Repository: safing/portmaster Branch: development Commit: 368822a17eff Files: 1599 Total size: 10.4 MB Directory structure: gitextract_qc0_tm9o/ ├── .angulardoc.json ├── .earthlyignore ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── config.yml │ │ ├── report-bug.md │ │ ├── report-compatibility.md │ │ └── suggest-feature.md │ ├── dependabot.yml │ ├── label-actions.yml │ └── workflows/ │ ├── angular.yml │ ├── go.yml │ ├── issues-first-greet.yml │ ├── issues-label-actions.yml │ ├── issues-stale.yml │ ├── kext.yml │ ├── release.yml │ ├── tauri.yml │ └── windows-dll.yml ├── .gitignore ├── .golangci.yml ├── .travis.yml ├── .vscode/ │ ├── launch.json │ └── settings.json ├── AUTHORS ├── CODE_OF_CONDUCT.md ├── Earthfile ├── LICENSE ├── README.md ├── TESTING.md ├── TRADEMARKS ├── assets/ │ ├── data/ │ │ ├── favicons/ │ │ │ ├── browserconfig.xml │ │ │ ├── head.html │ │ │ └── manifest.json │ │ ├── fonts/ │ │ │ ├── Roboto-300/ │ │ │ │ └── LICENSE.txt │ │ │ ├── Roboto-300italic/ │ │ │ │ └── LICENSE.txt │ │ │ ├── Roboto-500/ │ │ │ │ └── LICENSE.txt │ │ │ ├── Roboto-500italic/ │ │ │ │ └── LICENSE.txt │ │ │ ├── Roboto-700/ │ │ │ │ └── LICENSE.txt │ │ │ ├── Roboto-700italic/ │ │ │ │ └── LICENSE.txt │ │ │ ├── Roboto-italic/ │ │ │ │ └── LICENSE.txt │ │ │ ├── Roboto-regular/ │ │ │ │ └── LICENSE.txt │ │ │ ├── roboto-slimfix.css │ │ │ └── roboto.css │ │ ├── icons/ │ │ │ ├── README.md │ │ │ └── generate_ico.sh │ │ ├── img/ │ │ │ └── flags/ │ │ │ └── LICENSE.txt │ │ └── world-50m.json │ ├── icons.go │ ├── icons_default.go │ └── icons_windows.go ├── base/ │ ├── .gitignore │ ├── README.md │ ├── api/ │ │ ├── api_bridge.go │ │ ├── auth_wrapper.go │ │ ├── authentication.go │ │ ├── authentication_test.go │ │ ├── client/ │ │ │ ├── api.go │ │ │ ├── client.go │ │ │ ├── const.go │ │ │ ├── message.go │ │ │ └── websocket.go │ │ ├── config.go │ │ ├── database.go │ │ ├── doc.go │ │ ├── endpoints.go │ │ ├── endpoints_config.go │ │ ├── endpoints_debug.go │ │ ├── endpoints_meta.go │ │ ├── endpoints_test.go │ │ ├── enriched-response.go │ │ ├── init_test.go │ │ ├── main.go │ │ ├── module.go │ │ ├── request.go │ │ ├── router.go │ │ └── testclient/ │ │ ├── root/ │ │ │ └── index.html │ │ └── serve.go │ ├── apprise/ │ │ └── notify.go │ ├── config/ │ │ ├── basic_config.go │ │ ├── database.go │ │ ├── doc.go │ │ ├── expertise.go │ │ ├── get-safe.go │ │ ├── get.go │ │ ├── get_test.go │ │ ├── init_test.go │ │ ├── main.go │ │ ├── module.go │ │ ├── option.go │ │ ├── persistence.go │ │ ├── persistence_test.go │ │ ├── perspective.go │ │ ├── registry.go │ │ ├── registry_test.go │ │ ├── release.go │ │ ├── set.go │ │ ├── set_test.go │ │ ├── validate.go │ │ └── validity.go │ ├── container/ │ │ ├── container.go │ │ ├── container_test.go │ │ ├── doc.go │ │ └── serialization.go │ ├── database/ │ │ ├── accessor/ │ │ │ ├── accessor-json-bytes.go │ │ │ ├── accessor-json-string.go │ │ │ ├── accessor-struct.go │ │ │ ├── accessor.go │ │ │ └── accessor_test.go │ │ ├── boilerplate_test.go │ │ ├── controller.go │ │ ├── controllers.go │ │ ├── database.go │ │ ├── database_test.go │ │ ├── dbmodule/ │ │ │ ├── db.go │ │ │ └── maintenance.go │ │ ├── doc.go │ │ ├── errors.go │ │ ├── hook.go │ │ ├── hookbase.go │ │ ├── interface.go │ │ ├── interface_cache.go │ │ ├── interface_cache_test.go │ │ ├── iterator/ │ │ │ └── iterator.go │ │ ├── main.go │ │ ├── maintenance.go │ │ ├── migration/ │ │ │ ├── error.go │ │ │ └── migration.go │ │ ├── query/ │ │ │ ├── README.md │ │ │ ├── condition-and.go │ │ │ ├── condition-bool.go │ │ │ ├── condition-error.go │ │ │ ├── condition-exists.go │ │ │ ├── condition-float.go │ │ │ ├── condition-int.go │ │ │ ├── condition-not.go │ │ │ ├── condition-or.go │ │ │ ├── condition-regex.go │ │ │ ├── condition-string.go │ │ │ ├── condition-stringslice.go │ │ │ ├── condition.go │ │ │ ├── condition_test.go │ │ │ ├── operators.go │ │ │ ├── operators_test.go │ │ │ ├── parser.go │ │ │ ├── parser_test.go │ │ │ ├── query.go │ │ │ └── query_test.go │ │ ├── record/ │ │ │ ├── base.go │ │ │ ├── base_test.go │ │ │ ├── key.go │ │ │ ├── meta-bench_test.go │ │ │ ├── meta-gencode.go │ │ │ ├── meta-gencode_test.go │ │ │ ├── meta.colf │ │ │ ├── meta.gencode │ │ │ ├── meta.go │ │ │ ├── record.go │ │ │ ├── record_test.go │ │ │ ├── wrapper.go │ │ │ └── wrapper_test.go │ │ ├── registry.go │ │ ├── storage/ │ │ │ ├── badger/ │ │ │ │ ├── badger.go │ │ │ │ └── badger_test.go │ │ │ ├── bbolt/ │ │ │ │ ├── bbolt.go │ │ │ │ └── bbolt_test.go │ │ │ ├── errors.go │ │ │ ├── fstree/ │ │ │ │ ├── fstree.go │ │ │ │ └── fstree_test.go │ │ │ ├── hashmap/ │ │ │ │ ├── map.go │ │ │ │ └── map_test.go │ │ │ ├── injectbase.go │ │ │ ├── interface.go │ │ │ ├── sinkhole/ │ │ │ │ └── sinkhole.go │ │ │ ├── sqlite/ │ │ │ │ ├── bobgen.yaml │ │ │ │ ├── dberrors/ │ │ │ │ │ ├── bob_errors.bob.go │ │ │ │ │ ├── bob_main.bob_test.go │ │ │ │ │ └── records.bob.go │ │ │ │ ├── dbinfo/ │ │ │ │ │ ├── bob_types.bob.go │ │ │ │ │ └── records.bob.go │ │ │ │ ├── factory/ │ │ │ │ │ ├── bobfactory_context.bob.go │ │ │ │ │ ├── bobfactory_main.bob.go │ │ │ │ │ ├── bobfactory_main.bob_test.go │ │ │ │ │ ├── bobfactory_random.bob.go │ │ │ │ │ ├── bobfactory_random.bob_test.go │ │ │ │ │ └── records.bob.go │ │ │ │ ├── migrations/ │ │ │ │ │ ├── 0_settings.sql │ │ │ │ │ └── 1_initial.sql │ │ │ │ ├── migrations_config.yml │ │ │ │ ├── models/ │ │ │ │ │ ├── bob_joins.bob.go │ │ │ │ │ ├── bob_loaders.bob.go │ │ │ │ │ ├── bob_types.bob_test.go │ │ │ │ │ ├── bob_where.bob.go │ │ │ │ │ └── records.bob.go │ │ │ │ ├── prepared.go │ │ │ │ ├── prepared_test.go │ │ │ │ ├── schema.go │ │ │ │ ├── sqlite.go │ │ │ │ └── sqlite_test.go │ │ │ └── storages.go │ │ └── subscription.go │ ├── info/ │ │ └── version.go │ ├── log/ │ │ ├── formatting.go │ │ ├── formatting_unix.go │ │ ├── formatting_windows.go │ │ ├── input.go │ │ ├── logging.go │ │ ├── logging_test.go │ │ ├── output.go │ │ ├── slog.go │ │ ├── trace.go │ │ ├── trace_test.go │ │ └── writer.go │ ├── metrics/ │ │ ├── api.go │ │ ├── config.go │ │ ├── metric.go │ │ ├── metric_counter.go │ │ ├── metric_counter_fetching.go │ │ ├── metric_export.go │ │ ├── metric_gauge.go │ │ ├── metric_histogram.go │ │ ├── metrics_host.go │ │ ├── metrics_info.go │ │ ├── metrics_logs.go │ │ ├── metrics_runtime.go │ │ ├── module.go │ │ └── persistence.go │ ├── notifications/ │ │ ├── cleaner.go │ │ ├── config.go │ │ ├── database.go │ │ ├── doc.go │ │ ├── module-mirror.go │ │ ├── module.go │ │ └── notification.go │ ├── rng/ │ │ ├── doc.go │ │ ├── entropy.go │ │ ├── entropy_test.go │ │ ├── fullfeed.go │ │ ├── fullfeed_test.go │ │ ├── get.go │ │ ├── get_test.go │ │ ├── osfeeder.go │ │ ├── rng.go │ │ ├── rng_test.go │ │ └── tickfeeder.go │ ├── runtime/ │ │ ├── module.go │ │ ├── provider.go │ │ ├── registry.go │ │ ├── registry_test.go │ │ ├── singe_record_provider.go │ │ ├── storage.go │ │ └── trace_provider.go │ ├── template/ │ │ └── module.go │ └── utils/ │ ├── atomic.go │ ├── broadcastflag.go │ ├── call_limiter.go │ ├── call_limiter2.go │ ├── call_limiter_test.go │ ├── debug/ │ │ ├── debug.go │ │ ├── debug_android.go │ │ └── debug_default.go │ ├── fs.go │ ├── mimetypes.go │ ├── onceagain.go │ ├── onceagain_test.go │ ├── osdetail/ │ │ ├── colors_windows.go │ │ ├── command.go │ │ ├── dnscache_windows.go │ │ ├── errors.go │ │ ├── service_windows.go │ │ ├── shell_windows.go │ │ ├── svchost_windows.go │ │ ├── version_windows.go │ │ └── version_windows_test.go │ ├── permissions.go │ ├── permissions_windows.go │ ├── renameio/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── doc.go │ │ ├── example_test.go │ │ ├── symlink_test.go │ │ ├── tempfile.go │ │ ├── tempfile_linux_test.go │ │ ├── writefile.go │ │ └── writefile_test.go │ ├── safe.go │ ├── safe_test.go │ ├── slices.go │ ├── slices_test.go │ ├── stablepool.go │ ├── stablepool_test.go │ ├── structure.go │ ├── structure_test.go │ ├── uuid.go │ └── uuid_test.go ├── cmds/ │ ├── cmdbase/ │ │ ├── service.go │ │ ├── service_linux.go │ │ ├── service_windows.go │ │ ├── update.go │ │ └── version.go │ ├── integrationtest/ │ │ ├── main.go │ │ └── netstate.go │ ├── trafficgen/ │ │ ├── main.go │ │ └── pack │ └── winkext-test/ │ ├── main.go │ ├── main_linux.go │ └── pack ├── desktop/ │ ├── angular/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .vscode/ │ │ │ ├── launch.json │ │ │ └── tasks.json │ │ ├── README.md │ │ ├── angular.json │ │ ├── browser-extension-dev.config.ts │ │ ├── browser-extension.config.ts │ │ ├── docker.sh │ │ ├── e2e/ │ │ │ ├── protractor.conf.js │ │ │ ├── src/ │ │ │ │ ├── app.e2e-spec.ts │ │ │ │ └── app.po.ts │ │ │ └── tsconfig.json │ │ ├── karma.conf.js │ │ ├── package.json │ │ ├── projects/ │ │ │ ├── portmaster-chrome-extension/ │ │ │ │ ├── karma.conf.js │ │ │ │ ├── src/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── app-routing.module.ts │ │ │ │ │ │ ├── app.component.html │ │ │ │ │ │ ├── app.component.scss │ │ │ │ │ │ ├── app.component.ts │ │ │ │ │ │ ├── app.module.ts │ │ │ │ │ │ ├── domain-list/ │ │ │ │ │ │ │ ├── domain-list.component.html │ │ │ │ │ │ │ ├── domain-list.component.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── header/ │ │ │ │ │ │ │ ├── header.component.html │ │ │ │ │ │ │ ├── header.component.scss │ │ │ │ │ │ │ ├── header.component.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── interceptor.ts │ │ │ │ │ │ ├── request-interceptor.service.ts │ │ │ │ │ │ └── welcome/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── intro.component.html │ │ │ │ │ │ ├── intro.component.ts │ │ │ │ │ │ └── welcome.module.ts │ │ │ │ │ ├── assets/ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── background/ │ │ │ │ │ │ ├── commands.ts │ │ │ │ │ │ ├── tab-tracker.ts │ │ │ │ │ │ └── tab-utils.ts │ │ │ │ │ ├── background.ts │ │ │ │ │ ├── environments/ │ │ │ │ │ │ ├── environment.prod.ts │ │ │ │ │ │ └── environment.ts │ │ │ │ │ ├── index.html │ │ │ │ │ ├── main.ts │ │ │ │ │ ├── manifest.json │ │ │ │ │ ├── polyfills.ts │ │ │ │ │ ├── styles.scss │ │ │ │ │ └── test.ts │ │ │ │ ├── tsconfig.app.json │ │ │ │ └── tsconfig.spec.json │ │ │ ├── safing/ │ │ │ │ ├── portmaster-api/ │ │ │ │ │ ├── README.md │ │ │ │ │ ├── karma.conf.js │ │ │ │ │ ├── ng-package.json │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── lib/ │ │ │ │ │ │ │ ├── app-profile.service.ts │ │ │ │ │ │ │ ├── app-profile.types.ts │ │ │ │ │ │ │ ├── config.service.ts │ │ │ │ │ │ │ ├── config.types.ts │ │ │ │ │ │ │ ├── core.types.ts │ │ │ │ │ │ │ ├── debug-api.service.ts │ │ │ │ │ │ │ ├── features.ts │ │ │ │ │ │ │ ├── meta-api.service.ts │ │ │ │ │ │ │ ├── module.ts │ │ │ │ │ │ │ ├── netquery.service.ts │ │ │ │ │ │ │ ├── network.types.ts │ │ │ │ │ │ │ ├── platform-specific/ │ │ │ │ │ │ │ │ ├── tauri/ │ │ │ │ │ │ │ │ │ ├── tauri-http-interceptor.ts │ │ │ │ │ │ │ │ │ └── tauri-websocket-subject.ts │ │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ │ ├── portapi.service.ts │ │ │ │ │ │ │ ├── portapi.types.ts │ │ │ │ │ │ │ ├── spn.service.ts │ │ │ │ │ │ │ ├── spn.types.ts │ │ │ │ │ │ │ ├── utils.ts │ │ │ │ │ │ │ └── websocket.service.ts │ │ │ │ │ │ ├── public-api.ts │ │ │ │ │ │ └── test.ts │ │ │ │ │ ├── tsconfig.lib.json │ │ │ │ │ ├── tsconfig.lib.prod.json │ │ │ │ │ └── tsconfig.spec.json │ │ │ │ └── ui/ │ │ │ │ ├── .eslintrc.json │ │ │ │ ├── README.md │ │ │ │ ├── karma.conf.js │ │ │ │ ├── ng-package.json │ │ │ │ ├── package.json │ │ │ │ ├── src/ │ │ │ │ │ ├── lib/ │ │ │ │ │ │ ├── accordion/ │ │ │ │ │ │ │ ├── accordion-group.html │ │ │ │ │ │ │ ├── accordion-group.ts │ │ │ │ │ │ │ ├── accordion.html │ │ │ │ │ │ │ ├── accordion.module.ts │ │ │ │ │ │ │ ├── accordion.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── animations/ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── dialog/ │ │ │ │ │ │ │ ├── _confirm.dialog.scss │ │ │ │ │ │ │ ├── _dialog.scss │ │ │ │ │ │ │ ├── confirm.dialog.html │ │ │ │ │ │ │ ├── confirm.dialog.ts │ │ │ │ │ │ │ ├── dialog.animations.ts │ │ │ │ │ │ │ ├── dialog.container.ts │ │ │ │ │ │ │ ├── dialog.module.ts │ │ │ │ │ │ │ ├── dialog.ref.ts │ │ │ │ │ │ │ ├── dialog.service.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── dropdown/ │ │ │ │ │ │ │ ├── dropdown.html │ │ │ │ │ │ │ ├── dropdown.module.ts │ │ │ │ │ │ │ ├── dropdown.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── overlay-stepper/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── overlay-stepper-container.html │ │ │ │ │ │ │ ├── overlay-stepper-container.ts │ │ │ │ │ │ │ ├── overlay-stepper.module.ts │ │ │ │ │ │ │ ├── overlay-stepper.ts │ │ │ │ │ │ │ ├── refs.ts │ │ │ │ │ │ │ ├── step-outlet.ts │ │ │ │ │ │ │ └── step.ts │ │ │ │ │ │ ├── pagination/ │ │ │ │ │ │ │ ├── _pagination.scss │ │ │ │ │ │ │ ├── dynamic-items-paginator.ts │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── pagination.html │ │ │ │ │ │ │ ├── pagination.module.ts │ │ │ │ │ │ │ ├── pagination.ts │ │ │ │ │ │ │ └── snapshot-paginator.ts │ │ │ │ │ │ ├── select/ │ │ │ │ │ │ │ ├── _select.scss │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── item.ts │ │ │ │ │ │ │ ├── select.html │ │ │ │ │ │ │ ├── select.module.ts │ │ │ │ │ │ │ └── select.ts │ │ │ │ │ │ ├── tabs/ │ │ │ │ │ │ │ ├── _tab-group.scss │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── tab-group.html │ │ │ │ │ │ │ ├── tab-group.ts │ │ │ │ │ │ │ ├── tab.ts │ │ │ │ │ │ │ └── tabs.module.ts │ │ │ │ │ │ ├── tipup/ │ │ │ │ │ │ │ ├── _tipup.scss │ │ │ │ │ │ │ ├── anchor.ts │ │ │ │ │ │ │ ├── clone-node.ts │ │ │ │ │ │ │ ├── css-utils.ts │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── safe.pipe.ts │ │ │ │ │ │ │ ├── tipup-component.ts │ │ │ │ │ │ │ ├── tipup.html │ │ │ │ │ │ │ ├── tipup.module.ts │ │ │ │ │ │ │ ├── tipup.ts │ │ │ │ │ │ │ ├── translations.ts │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ ├── toggle-switch/ │ │ │ │ │ │ │ ├── _toggle-switch.scss │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── toggle-switch.html │ │ │ │ │ │ │ ├── toggle-switch.ts │ │ │ │ │ │ │ └── toggle.module.ts │ │ │ │ │ │ ├── tooltip/ │ │ │ │ │ │ │ ├── _tooltip-component.scss │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── tooltip-component.html │ │ │ │ │ │ │ ├── tooltip-component.ts │ │ │ │ │ │ │ ├── tooltip.module.ts │ │ │ │ │ │ │ └── tooltip.ts │ │ │ │ │ │ └── ui.module.ts │ │ │ │ │ ├── public-api.ts │ │ │ │ │ └── test.ts │ │ │ │ ├── theming.scss │ │ │ │ ├── tsconfig.lib.json │ │ │ │ ├── tsconfig.lib.prod.json │ │ │ │ └── tsconfig.spec.json │ │ │ └── tauri-builtin/ │ │ │ ├── src/ │ │ │ │ ├── app/ │ │ │ │ │ ├── app.component.html │ │ │ │ │ ├── app.component.ts │ │ │ │ │ └── app.config.ts │ │ │ │ ├── index.html │ │ │ │ ├── main.ts │ │ │ │ └── styles.scss │ │ │ └── tsconfig.app.json │ │ ├── proxy.json │ │ ├── src/ │ │ │ ├── app/ │ │ │ │ ├── app-routing.module.ts │ │ │ │ ├── app.component.html │ │ │ │ ├── app.component.scss │ │ │ │ ├── app.component.spec.ts │ │ │ │ ├── app.component.ts │ │ │ │ ├── app.module.ts │ │ │ │ ├── integration/ │ │ │ │ │ ├── browser.ts │ │ │ │ │ ├── electron.ts │ │ │ │ │ ├── factory.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── integration.ts │ │ │ │ │ └── taur-app.ts │ │ │ │ ├── intro/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── intro.module.ts │ │ │ │ │ ├── step-1-welcome/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── step-1-welcome.html │ │ │ │ │ │ └── step-1-welcome.ts │ │ │ │ │ ├── step-2-trackers/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── step-2-trackers.html │ │ │ │ │ │ └── step-2-trackers.ts │ │ │ │ │ ├── step-3-dns/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── step-3-dns.html │ │ │ │ │ │ └── step-3-dns.ts │ │ │ │ │ ├── step-4-tipups/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── step-4-tipups.html │ │ │ │ │ │ └── step-4-tipups.ts │ │ │ │ │ └── step.scss │ │ │ │ ├── layout/ │ │ │ │ │ ├── navigation/ │ │ │ │ │ │ ├── navigation.html │ │ │ │ │ │ ├── navigation.scss │ │ │ │ │ │ └── navigation.ts │ │ │ │ │ └── side-dash/ │ │ │ │ │ ├── side-dash.html │ │ │ │ │ ├── side-dash.scss │ │ │ │ │ └── side-dash.ts │ │ │ │ ├── package.json │ │ │ │ ├── pages/ │ │ │ │ │ ├── app-view/ │ │ │ │ │ │ ├── app-insights/ │ │ │ │ │ │ │ ├── app-insights.component.html │ │ │ │ │ │ │ └── app-insights.component.ts │ │ │ │ │ │ ├── app-view.html │ │ │ │ │ │ ├── app-view.scss │ │ │ │ │ │ ├── app-view.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── merge-profile-dialog/ │ │ │ │ │ │ │ ├── merge-profile-dialog.component.html │ │ │ │ │ │ │ └── merge-profile-dialog.component.ts │ │ │ │ │ │ ├── overview.html │ │ │ │ │ │ ├── overview.scss │ │ │ │ │ │ ├── overview.ts │ │ │ │ │ │ ├── qs-history/ │ │ │ │ │ │ │ ├── qs-history.component.html │ │ │ │ │ │ │ ├── qs-history.component.scss │ │ │ │ │ │ │ └── qs-history.component.ts │ │ │ │ │ │ ├── qs-internet/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── qs-internet.html │ │ │ │ │ │ │ └── qs-internet.ts │ │ │ │ │ │ ├── qs-select-exit/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── qs-select-exit.html │ │ │ │ │ │ │ ├── qs-select-exit.scss │ │ │ │ │ │ │ └── qs-select-exit.ts │ │ │ │ │ │ └── qs-use-spn/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── qs-use-spn.html │ │ │ │ │ │ └── qs-use-spn.ts │ │ │ │ │ ├── dashboard/ │ │ │ │ │ │ ├── dashboard-widget/ │ │ │ │ │ │ │ ├── dashboard-widget.component.html │ │ │ │ │ │ │ └── dashboard-widget.component.ts │ │ │ │ │ │ ├── dashboard.component.html │ │ │ │ │ │ ├── dashboard.component.scss │ │ │ │ │ │ ├── dashboard.component.ts │ │ │ │ │ │ └── feature-card/ │ │ │ │ │ │ ├── feature-card.component.html │ │ │ │ │ │ ├── feature-card.component.scss │ │ │ │ │ │ └── feature-card.component.ts │ │ │ │ │ ├── monitor/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── monitor.html │ │ │ │ │ │ ├── monitor.scss │ │ │ │ │ │ └── monitor.ts │ │ │ │ │ ├── page.scss │ │ │ │ │ ├── settings/ │ │ │ │ │ │ ├── settings.html │ │ │ │ │ │ ├── settings.scss │ │ │ │ │ │ └── settings.ts │ │ │ │ │ ├── spn/ │ │ │ │ │ │ ├── country-details/ │ │ │ │ │ │ │ ├── country-details.html │ │ │ │ │ │ │ ├── country-details.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── country-overlay/ │ │ │ │ │ │ │ ├── country-overlay.html │ │ │ │ │ │ │ ├── country-overlay.scss │ │ │ │ │ │ │ ├── country-overlay.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── map-legend/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── map-legend.html │ │ │ │ │ │ │ └── map-legend.ts │ │ │ │ │ │ ├── map-renderer/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── map-renderer.ts │ │ │ │ │ │ │ └── map-style.scss │ │ │ │ │ │ ├── map.service.ts │ │ │ │ │ │ ├── node-icon/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── node-icon.html │ │ │ │ │ │ │ ├── node-icon.scss │ │ │ │ │ │ │ └── node-icon.ts │ │ │ │ │ │ ├── pin-details/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── pin-details.html │ │ │ │ │ │ │ └── pin-details.ts │ │ │ │ │ │ ├── pin-list/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── pin-list.html │ │ │ │ │ │ │ └── pin-list.ts │ │ │ │ │ │ ├── pin-overlay/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── pin-overlay.html │ │ │ │ │ │ │ ├── pin-overlay.scss │ │ │ │ │ │ │ └── pin-overlay.ts │ │ │ │ │ │ ├── pin-route/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── pin-route.html │ │ │ │ │ │ │ ├── pin-route.scss │ │ │ │ │ │ │ └── pin-route.ts │ │ │ │ │ │ ├── spn-feature-carousel/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── spn-feature-carousel.html │ │ │ │ │ │ │ ├── spn-feature-carousel.scss │ │ │ │ │ │ │ └── spn-feature-carousel.ts │ │ │ │ │ │ ├── spn-page.html │ │ │ │ │ │ ├── spn-page.scss │ │ │ │ │ │ ├── spn-page.ts │ │ │ │ │ │ ├── spn.module.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ │ └── support/ │ │ │ │ │ ├── form/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── support-form.html │ │ │ │ │ │ ├── support-form.scss │ │ │ │ │ │ └── support-form.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── pages.ts │ │ │ │ │ ├── progress-dialog/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── progress-dialog.html │ │ │ │ │ │ └── progress-dialog.ts │ │ │ │ │ ├── support.html │ │ │ │ │ ├── support.scss │ │ │ │ │ └── support.ts │ │ │ │ ├── prompt-entrypoint/ │ │ │ │ │ ├── prompt-entrypoint.ts │ │ │ │ │ └── prompt.html │ │ │ │ ├── services/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── notifications.service.spec.ts │ │ │ │ │ ├── notifications.service.ts │ │ │ │ │ ├── notifications.types.ts │ │ │ │ │ ├── package.json │ │ │ │ │ ├── session-data.service.ts │ │ │ │ │ ├── status.service.spec.ts │ │ │ │ │ ├── status.service.ts │ │ │ │ │ ├── status.types.ts │ │ │ │ │ ├── supporthub.service.ts │ │ │ │ │ ├── ui-state.service.ts │ │ │ │ │ └── virtual-notification.ts │ │ │ │ └── shared/ │ │ │ │ ├── action-indicator/ │ │ │ │ │ ├── action-indicator.module.ts │ │ │ │ │ ├── action-indicator.service.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── indicator.html │ │ │ │ │ ├── indicator.scss │ │ │ │ │ └── indicator.ts │ │ │ │ ├── animations.ts │ │ │ │ ├── app-icon/ │ │ │ │ │ ├── app-icon-resolver.ts │ │ │ │ │ ├── app-icon.html │ │ │ │ │ ├── app-icon.module.ts │ │ │ │ │ ├── app-icon.scss │ │ │ │ │ ├── app-icon.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── basic-setting/ │ │ │ │ │ │ ├── basic-setting.html │ │ │ │ │ │ ├── basic-setting.scss │ │ │ │ │ │ ├── basic-setting.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── config-settings.html │ │ │ │ │ ├── config-settings.scss │ │ │ │ │ ├── config-settings.ts │ │ │ │ │ ├── config.module.ts │ │ │ │ │ ├── export-dialog/ │ │ │ │ │ │ ├── export-dialog.component.html │ │ │ │ │ │ └── export-dialog.component.ts │ │ │ │ │ ├── filter-lists/ │ │ │ │ │ │ ├── filter-list.html │ │ │ │ │ │ ├── filter-list.scss │ │ │ │ │ │ ├── filter-list.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── generic-setting/ │ │ │ │ │ │ ├── generic-setting.html │ │ │ │ │ │ ├── generic-setting.scss │ │ │ │ │ │ ├── generic-setting.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── import-dialog/ │ │ │ │ │ │ ├── cursor.ts │ │ │ │ │ │ ├── import-dialog.component.html │ │ │ │ │ │ ├── import-dialog.component.ts │ │ │ │ │ │ └── selection.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── ordererd-list/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── item.html │ │ │ │ │ │ ├── item.scss │ │ │ │ │ │ ├── item.ts │ │ │ │ │ │ ├── ordered-list.html │ │ │ │ │ │ ├── ordered-list.scss │ │ │ │ │ │ └── ordered-list.ts │ │ │ │ │ ├── rule-list/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── list-item.html │ │ │ │ │ │ ├── list-item.scss │ │ │ │ │ │ ├── list-item.ts │ │ │ │ │ │ ├── rule-list.html │ │ │ │ │ │ ├── rule-list.scss │ │ │ │ │ │ └── rule-list.ts │ │ │ │ │ ├── safe.pipe.ts │ │ │ │ │ └── subsystems.ts │ │ │ │ ├── count-indicator/ │ │ │ │ │ ├── count-indicator.html │ │ │ │ │ ├── count-indicator.module.ts │ │ │ │ │ ├── count-indicator.scss │ │ │ │ │ ├── count-indicator.ts │ │ │ │ │ ├── count.pipe.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── country-flag/ │ │ │ │ │ ├── country-flag.ts │ │ │ │ │ ├── country.module.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── edit-profile-dialog/ │ │ │ │ │ ├── edit-profile-dialog.html │ │ │ │ │ ├── edit-profile-dialog.scss │ │ │ │ │ ├── edit-profile-dialog.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── exit-screen/ │ │ │ │ │ ├── exit-screen.html │ │ │ │ │ ├── exit-screen.scss │ │ │ │ │ ├── exit-screen.ts │ │ │ │ │ ├── exit.service.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── expertise/ │ │ │ │ │ ├── expertise-directive.ts │ │ │ │ │ ├── expertise-switch.html │ │ │ │ │ ├── expertise-switch.scss │ │ │ │ │ ├── expertise-switch.ts │ │ │ │ │ ├── expertise.module.ts │ │ │ │ │ ├── expertise.service.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── external-link.directive.ts │ │ │ │ ├── feature-scout/ │ │ │ │ │ ├── feature-scout.html │ │ │ │ │ ├── feature-scout.scss │ │ │ │ │ ├── feature-scout.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── focus/ │ │ │ │ │ ├── focus.directive.ts │ │ │ │ │ ├── focus.module.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── fuzzySearch/ │ │ │ │ │ ├── fuse.service.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── search-pipe.ts │ │ │ │ ├── loading/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loading.html │ │ │ │ │ ├── loading.scss │ │ │ │ │ └── loading.ts │ │ │ │ ├── menu/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── menu-group.scss │ │ │ │ │ ├── menu-item.scss │ │ │ │ │ ├── menu-trigger.html │ │ │ │ │ ├── menu-trigger.scss │ │ │ │ │ ├── menu.html │ │ │ │ │ ├── menu.module.ts │ │ │ │ │ └── menu.ts │ │ │ │ ├── multi-switch/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── multi-switch.html │ │ │ │ │ ├── multi-switch.module.ts │ │ │ │ │ ├── multi-switch.scss │ │ │ │ │ ├── multi-switch.ts │ │ │ │ │ ├── switch-item.scss │ │ │ │ │ └── switch-item.ts │ │ │ │ ├── netquery/ │ │ │ │ │ ├── .eslintrc.json │ │ │ │ │ ├── add-to-filter/ │ │ │ │ │ │ ├── add-to-filter.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── circular-bar-chart/ │ │ │ │ │ │ └── circular-bar-chart.component.ts │ │ │ │ │ ├── combined-menu.pipe.ts │ │ │ │ │ ├── connection-details/ │ │ │ │ │ │ ├── conn-details.html │ │ │ │ │ │ ├── conn-details.scss │ │ │ │ │ │ ├── conn-details.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── connection-helper.service.ts │ │ │ │ │ ├── connection-row/ │ │ │ │ │ │ ├── conn-row.html │ │ │ │ │ │ ├── conn-row.scss │ │ │ │ │ │ ├── conn-row.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── line-chart/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── line-chart.ts │ │ │ │ │ ├── netquery.component.html │ │ │ │ │ ├── netquery.component.ts │ │ │ │ │ ├── netquery.module.ts │ │ │ │ │ ├── pipes/ │ │ │ │ │ │ ├── can-show.pipe.ts │ │ │ │ │ │ ├── can-use-rules.pipe.ts │ │ │ │ │ │ ├── country-name.pipe.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── is-blocked.pipe.ts │ │ │ │ │ │ └── location.pipe.ts │ │ │ │ │ ├── scope-label/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── scope-label.html │ │ │ │ │ │ └── scope-label.ts │ │ │ │ │ ├── search-overlay/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── search-overlay.html │ │ │ │ │ │ └── search-overlay.ts │ │ │ │ │ ├── searchbar/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── searchbar.html │ │ │ │ │ │ └── searchbar.ts │ │ │ │ │ ├── tag-bar/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── tag-bar.html │ │ │ │ │ │ └── tag-bar.ts │ │ │ │ │ ├── textql/ │ │ │ │ │ │ ├── helper.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── input.ts │ │ │ │ │ │ ├── lexer.ts │ │ │ │ │ │ ├── parser.ts │ │ │ │ │ │ └── token.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── network-scout/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── network-scout.html │ │ │ │ │ ├── network-scout.scss │ │ │ │ │ └── network-scout.ts │ │ │ │ ├── notification/ │ │ │ │ │ ├── notification.html │ │ │ │ │ ├── notification.scss │ │ │ │ │ └── notification.ts │ │ │ │ ├── notification-list/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── notification-list.component.html │ │ │ │ │ ├── notification-list.component.scss │ │ │ │ │ └── notification-list.component.ts │ │ │ │ ├── pipes/ │ │ │ │ │ ├── bytes.pipe.ts │ │ │ │ │ ├── common-pipes.module.ts │ │ │ │ │ ├── duration.pipe.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── round.pipe.ts │ │ │ │ │ ├── time-ago.pipe.ts │ │ │ │ │ ├── to-profile.pipe.ts │ │ │ │ │ └── to-seconds.pipe.ts │ │ │ │ ├── process-details-dialog/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── process-details-dialog.html │ │ │ │ │ ├── process-details-dialog.scss │ │ │ │ │ └── process-details-dialog.ts │ │ │ │ ├── prompt-list/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── prompt-list.component.html │ │ │ │ │ ├── prompt-list.component.scss │ │ │ │ │ └── prompt-list.component.ts │ │ │ │ ├── security-lock/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── security-lock.html │ │ │ │ │ ├── security-lock.scss │ │ │ │ │ └── security-lock.ts │ │ │ │ ├── spn-account-details/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── spn-account-details.html │ │ │ │ │ ├── spn-account-details.scss │ │ │ │ │ └── spn-account-details.ts │ │ │ │ ├── spn-login/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── spn-login.html │ │ │ │ │ ├── spn-login.scss │ │ │ │ │ └── spn-login.ts │ │ │ │ ├── spn-network-status/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── spn-network-status.html │ │ │ │ │ ├── spn-network-status.scss │ │ │ │ │ └── spn-network-status.ts │ │ │ │ ├── spn-status/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── spn-status.html │ │ │ │ │ └── spn-status.ts │ │ │ │ ├── text-placeholder/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── placeholder.scss │ │ │ │ │ └── placeholder.ts │ │ │ │ └── utils.ts │ │ │ ├── electron-app.d.ts │ │ │ ├── environments/ │ │ │ │ ├── environment.prod.ts │ │ │ │ └── environment.ts │ │ │ ├── i18n/ │ │ │ │ ├── helptexts.yaml │ │ │ │ └── helptexts.yaml.d.ts │ │ │ ├── index.html │ │ │ ├── main.ts │ │ │ ├── polyfills.ts │ │ │ ├── styles.scss │ │ │ ├── test.ts │ │ │ ├── theme/ │ │ │ │ ├── _breadcrumbs.scss │ │ │ │ ├── _button.scss │ │ │ │ ├── _card.scss │ │ │ │ ├── _colors.scss │ │ │ │ ├── _dialog.scss │ │ │ │ ├── _drag-n-drop.scss │ │ │ │ ├── _inputs.scss │ │ │ │ ├── _markdown.scss │ │ │ │ ├── _pill.scss │ │ │ │ ├── _scroll.scss │ │ │ │ ├── _search.scss │ │ │ │ ├── _table.scss │ │ │ │ ├── _tailwind.scss │ │ │ │ ├── _trust-level.scss │ │ │ │ ├── _typography.scss │ │ │ │ ├── _verdict.scss │ │ │ │ └── mixins/ │ │ │ │ └── _pill.scss │ │ │ └── theme.less │ │ ├── tailwind.config.js │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ └── tsconfig.spec.json │ └── tauri/ │ ├── .cargo/ │ │ └── config.toml │ ├── .vscode/ │ │ ├── launch.json │ │ └── tasks.json │ └── src-tauri/ │ ├── .gitignore │ ├── Cargo.toml │ ├── Cross.toml │ ├── README.md │ ├── build.rs │ ├── capabilities/ │ │ └── default.json │ ├── src/ │ │ ├── cli.rs │ │ ├── commands/ │ │ │ ├── mod.rs │ │ │ └── tauri_http.rs │ │ ├── config.rs │ │ ├── main.rs │ │ ├── portapi/ │ │ │ ├── client.rs │ │ │ ├── message.rs │ │ │ ├── mod.rs │ │ │ ├── models/ │ │ │ │ ├── config.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── notification.rs │ │ │ │ ├── spn.rs │ │ │ │ └── system_status_types.rs │ │ │ └── types.rs │ │ ├── portmaster/ │ │ │ ├── commands.rs │ │ │ ├── mod.rs │ │ │ ├── notifications.rs │ │ │ └── websocket.rs │ │ ├── service/ │ │ │ ├── manager.rs │ │ │ ├── mod.rs │ │ │ ├── status.rs │ │ │ ├── systemd.rs │ │ │ └── windows_service.rs │ │ ├── traymenu.rs │ │ ├── window.rs │ │ └── xdg/ │ │ └── mod.rs │ ├── tauri.conf.json5 │ └── templates/ │ ├── NSIS_Simple_Service_Plugin_Unicode_1.30/ │ │ ├── License.txt │ │ ├── Readme.txt │ │ └── Source/ │ │ ├── LSASecurityControl.pas │ │ ├── NSIS Plugins.groupproj │ │ ├── NSIS.pas │ │ ├── ServiceControl.pas │ │ ├── SimpleSC.dpr │ │ ├── SimpleSC.dproj │ │ └── SimpleSC.res │ ├── nsis/ │ │ └── install_hooks.nsh │ └── wix/ │ ├── CheckServiceStatus.vbs │ ├── Migration.vbs │ ├── files.wxs │ ├── main.wxs │ ├── main_original.wxs │ ├── migration.wxs │ └── old_service_check.wxs ├── go.mod ├── go.sum ├── packaging/ │ ├── README.md │ ├── linux/ │ │ ├── dev_helpers/ │ │ │ └── build_tauri.sh │ │ ├── portmaster-autostart.desktop │ │ ├── portmaster.desktop │ │ ├── portmaster.service │ │ ├── postinst │ │ ├── postrm │ │ └── readme.md │ └── windows/ │ ├── .gitkeep │ ├── dev_helpers/ │ │ ├── build_angular.ps1 │ │ └── build_tauri.ps1 │ ├── generate_windows_installers.ps1 │ └── sign_binaries_in_dist.ps1 ├── runtime/ │ └── .gitkeep ├── service/ │ ├── broadcasts/ │ │ ├── api.go │ │ ├── data.go │ │ ├── install_info.go │ │ ├── module.go │ │ ├── notify.go │ │ └── state.go │ ├── compat/ │ │ ├── api.go │ │ ├── callbacks.go │ │ ├── debug_default.go │ │ ├── debug_linux.go │ │ ├── debug_windows.go │ │ ├── iptables.go │ │ ├── iptables_test.go │ │ ├── module.go │ │ ├── notify.go │ │ ├── selfcheck.go │ │ ├── wfpstate.go │ │ └── wfpstate_test.go │ ├── config.go │ ├── configure/ │ │ └── updates.go │ ├── control/ │ │ ├── api.go │ │ ├── module.go │ │ └── pause.go │ ├── core/ │ │ ├── api.go │ │ ├── base/ │ │ │ ├── databases.go │ │ │ ├── logs.go │ │ │ ├── module.go │ │ │ └── profiling.go │ │ ├── config.go │ │ ├── core.go │ │ ├── events.go │ │ ├── os_default.go │ │ ├── os_windows.go │ │ ├── update_config.go │ │ └── update_versions.go │ ├── debug.go │ ├── debug_test.go │ ├── detection/ │ │ └── dga/ │ │ ├── lms.go │ │ └── lms_test.go │ ├── firewall/ │ │ ├── api.go │ │ ├── bypassing.go │ │ ├── config.go │ │ ├── dns.go │ │ ├── inspection/ │ │ │ └── inspection.go │ │ ├── interception/ │ │ │ ├── dnsmonitor/ │ │ │ │ ├── etwlink_windows.go │ │ │ │ ├── eventlistener.go │ │ │ │ ├── eventlistener_linux.go │ │ │ │ ├── eventlistener_windows.go │ │ │ │ ├── module.go │ │ │ │ └── varlinktypes.go │ │ │ ├── ebpf/ │ │ │ │ ├── bandwidth/ │ │ │ │ │ ├── bpf_bpfeb.go │ │ │ │ │ ├── bpf_bpfeb.o │ │ │ │ │ ├── bpf_bpfel.go │ │ │ │ │ ├── bpf_bpfel.o │ │ │ │ │ └── interface.go │ │ │ │ ├── connection_listener/ │ │ │ │ │ ├── bpf_bpfeb.go │ │ │ │ │ ├── bpf_bpfeb.o │ │ │ │ │ ├── bpf_bpfel.go │ │ │ │ │ ├── bpf_bpfel.o │ │ │ │ │ └── worker.go │ │ │ │ ├── exec/ │ │ │ │ │ ├── bpf_bpfeb.go │ │ │ │ │ ├── bpf_bpfeb.o │ │ │ │ │ ├── bpf_bpfel.go │ │ │ │ │ ├── bpf_bpfel.o │ │ │ │ │ └── exec.go │ │ │ │ └── programs/ │ │ │ │ ├── bandwidth.c │ │ │ │ ├── bpf/ │ │ │ │ │ ├── bpf_core_read.h │ │ │ │ │ ├── bpf_helper_defs.h │ │ │ │ │ ├── bpf_helpers.h │ │ │ │ │ └── bpf_tracing.h │ │ │ │ ├── exec.c │ │ │ │ ├── monitor.c │ │ │ │ ├── update.sh │ │ │ │ └── vmlinux-x86.h │ │ │ ├── interception_default.go │ │ │ ├── interception_linux.go │ │ │ ├── interception_windows.go │ │ │ ├── introspection.go │ │ │ ├── module.go │ │ │ ├── nfq/ │ │ │ │ ├── conntrack.go │ │ │ │ ├── nfq.go │ │ │ │ └── packet.go │ │ │ ├── nfqueue_linux.go │ │ │ ├── packet_tracer.go │ │ │ ├── windowskext/ │ │ │ │ ├── bandwidth_stats.go │ │ │ │ ├── doc.go │ │ │ │ ├── handler.go │ │ │ │ ├── kext.go │ │ │ │ ├── packet.go │ │ │ │ ├── service.go │ │ │ │ └── syscall.go │ │ │ └── windowskext2/ │ │ │ ├── doc.go │ │ │ ├── handler.go │ │ │ ├── kext.go │ │ │ ├── packet.go │ │ │ └── service.go │ │ ├── master.go │ │ ├── module.go │ │ ├── packet_handler.go │ │ ├── preauth.go │ │ ├── prompt.go │ │ └── tunnel.go │ ├── instance.go │ ├── integration/ │ │ ├── etw_windows.go │ │ ├── integration.go │ │ ├── integration_windows.go │ │ └── module.go │ ├── intel/ │ │ ├── block_reason.go │ │ ├── customlists/ │ │ │ ├── config.go │ │ │ ├── lists.go │ │ │ └── module.go │ │ ├── entity.go │ │ ├── entity_test.go │ │ ├── filterlists/ │ │ │ ├── bloom.go │ │ │ ├── cache_version.go │ │ │ ├── database.go │ │ │ ├── decoder.go │ │ │ ├── index.go │ │ │ ├── keys.go │ │ │ ├── lookup.go │ │ │ ├── module.go │ │ │ ├── module_test.go │ │ │ ├── record.go │ │ │ └── updater.go │ │ ├── geoip/ │ │ │ ├── country_info.go │ │ │ ├── country_info_test.go │ │ │ ├── database.go │ │ │ ├── init_test.go │ │ │ ├── location.go │ │ │ ├── location_test.go │ │ │ ├── lookup.go │ │ │ ├── lookup_test.go │ │ │ ├── module.go │ │ │ ├── regions.go │ │ │ └── regions_test.go │ │ └── resolver.go │ ├── interop/ │ │ ├── api.go │ │ ├── ivpn/ │ │ │ ├── ivpn.go │ │ │ └── notification.go │ │ ├── module.go │ │ └── verdict_handler.go │ ├── mgr/ │ │ ├── doc.go │ │ ├── events.go │ │ ├── group.go │ │ ├── group_ext.go │ │ ├── group_module.go │ │ ├── manager.go │ │ ├── sleepyticker.go │ │ ├── sleepyticker_test.go │ │ ├── states.go │ │ ├── worker.go │ │ ├── worker_info.go │ │ ├── worker_test.go │ │ ├── workermgr.go │ │ └── workermgr_test.go │ ├── nameserver/ │ │ ├── config.go │ │ ├── conflict.go │ │ ├── failing.go │ │ ├── metrics.go │ │ ├── module.go │ │ ├── nameserver.go │ │ ├── nsutil/ │ │ │ └── nsutil.go │ │ └── response.go │ ├── netenv/ │ │ ├── addresses_test.go │ │ ├── adresses.go │ │ ├── api.go │ │ ├── dbus_linux.go │ │ ├── dbus_linux_test.go │ │ ├── dialing.go │ │ ├── environment.go │ │ ├── environment_default.go │ │ ├── environment_linux.go │ │ ├── environment_linux_test.go │ │ ├── environment_test.go │ │ ├── environment_windows.go │ │ ├── environment_windows_test.go │ │ ├── icmp_listener.go │ │ ├── init_test.go │ │ ├── location.go │ │ ├── location_default.go │ │ ├── location_test.go │ │ ├── location_windows.go │ │ ├── main.go │ │ ├── main_test.go │ │ ├── network-change.go │ │ ├── notes.md │ │ ├── online-status.go │ │ ├── online-status_test.go │ │ ├── os_android.go │ │ └── os_default.go │ ├── netquery/ │ │ ├── active_chart_handler.go │ │ ├── bandwidth_chart_handler.go │ │ ├── database.go │ │ ├── manager.go │ │ ├── module_api.go │ │ ├── orm/ │ │ │ ├── decoder.go │ │ │ ├── decoder_test.go │ │ │ ├── encoder.go │ │ │ ├── encoder_test.go │ │ │ ├── query_runner.go │ │ │ ├── schema_builder.go │ │ │ └── schema_builder_test.go │ │ ├── query.go │ │ ├── query_handler.go │ │ ├── query_request.go │ │ ├── query_test.go │ │ └── runtime_query_runner.go │ ├── network/ │ │ ├── api.go │ │ ├── api_test.go │ │ ├── clean.go │ │ ├── connection.go │ │ ├── connection_android.go │ │ ├── connection_handler.go │ │ ├── connection_store.go │ │ ├── database.go │ │ ├── dns.go │ │ ├── iphelper/ │ │ │ ├── get.go │ │ │ ├── iphelper.go │ │ │ ├── tables.go │ │ │ └── tables_test.go │ │ ├── metrics.go │ │ ├── module.go │ │ ├── multicast.go │ │ ├── netutils/ │ │ │ ├── address.go │ │ │ ├── dns.go │ │ │ ├── dns_test.go │ │ │ ├── ip.go │ │ │ ├── ip_test.go │ │ │ └── tcpassembly.go │ │ ├── packet/ │ │ │ ├── bandwidth.go │ │ │ ├── const.go │ │ │ ├── info_only.go │ │ │ ├── packet.go │ │ │ ├── packetinfo.go │ │ │ └── parse.go │ │ ├── ports.go │ │ ├── proc/ │ │ │ ├── findpid.go │ │ │ ├── pids_by_user.go │ │ │ ├── tables.go │ │ │ └── tables_test.go │ │ ├── reference/ │ │ │ ├── ports.go │ │ │ └── protocols.go │ │ ├── socket/ │ │ │ └── socket.go │ │ ├── state/ │ │ │ ├── exists.go │ │ │ ├── info.go │ │ │ ├── lookup.go │ │ │ ├── system_default.go │ │ │ ├── system_linux.go │ │ │ ├── system_windows.go │ │ │ ├── tcp.go │ │ │ └── udp.go │ │ └── status.go │ ├── process/ │ │ ├── api.go │ │ ├── config.go │ │ ├── database.go │ │ ├── doc.go │ │ ├── executable.go │ │ ├── find.go │ │ ├── module.go │ │ ├── process.go │ │ ├── process_default.go │ │ ├── process_linux.go │ │ ├── process_windows.go │ │ ├── profile.go │ │ ├── special.go │ │ ├── tags/ │ │ │ ├── appimage_unix.go │ │ │ ├── flatpak_unix.go │ │ │ ├── interpreter_unix.go │ │ │ ├── net.go │ │ │ ├── snap_unix.go │ │ │ ├── svchost_windows.go │ │ │ └── winstore_windows.go │ │ └── tags.go │ ├── profile/ │ │ ├── active.go │ │ ├── api.go │ │ ├── binmeta/ │ │ │ ├── convert.go │ │ │ ├── find_default.go │ │ │ ├── find_linux.go │ │ │ ├── find_linux_test.go │ │ │ ├── find_windows.go │ │ │ ├── find_windows_test.go │ │ │ ├── icon.go │ │ │ ├── icons.go │ │ │ ├── ignore.go │ │ │ ├── locations_linux.go │ │ │ ├── name.go │ │ │ └── name_test.go │ │ ├── config-update.go │ │ ├── config.go │ │ ├── database.go │ │ ├── endpoints/ │ │ │ ├── annotations.go │ │ │ ├── endpoint-any.go │ │ │ ├── endpoint-asn.go │ │ │ ├── endpoint-continent.go │ │ │ ├── endpoint-country.go │ │ │ ├── endpoint-domain.go │ │ │ ├── endpoint-ip.go │ │ │ ├── endpoint-iprange.go │ │ │ ├── endpoint-lists.go │ │ │ ├── endpoint-scopes.go │ │ │ ├── endpoint.go │ │ │ ├── endpoint_test.go │ │ │ ├── endpoints.go │ │ │ ├── endpoints_test.go │ │ │ └── reason.go │ │ ├── fingerprint.go │ │ ├── fingerprint_test.go │ │ ├── framework.go │ │ ├── framework_test.go │ │ ├── get.go │ │ ├── merge.go │ │ ├── meta.go │ │ ├── migrations.go │ │ ├── module.go │ │ ├── profile-layered-provider.go │ │ ├── profile-layered.go │ │ ├── profile.go │ │ └── special.go │ ├── resolver/ │ │ ├── api.go │ │ ├── block-detection.go │ │ ├── compat.go │ │ ├── config.go │ │ ├── doc.go │ │ ├── failing.go │ │ ├── ipinfo.go │ │ ├── ipinfo_test.go │ │ ├── main.go │ │ ├── main_test.go │ │ ├── metrics.go │ │ ├── namerecord.go │ │ ├── namerecord_test.go │ │ ├── resolve.go │ │ ├── resolver-env.go │ │ ├── resolver-https.go │ │ ├── resolver-mdns.go │ │ ├── resolver-plain.go │ │ ├── resolver-tcp.go │ │ ├── resolver.go │ │ ├── resolver_test.go │ │ ├── resolvers.go │ │ ├── resolvers_test.go │ │ ├── reverse.go │ │ ├── reverse_test.go │ │ ├── rr_context.go │ │ ├── rrcache.go │ │ ├── rrcache_test.go │ │ ├── scopes.go │ │ └── test/ │ │ └── resolving.bash │ ├── status/ │ │ ├── module.go │ │ ├── notifications.go │ │ ├── security_level.go │ │ └── status.go │ ├── sync/ │ │ ├── module.go │ │ ├── profile.go │ │ ├── setting_single.go │ │ ├── settings.go │ │ └── util.go │ ├── ui/ │ │ ├── api.go │ │ ├── module.go │ │ └── serve.go │ └── updates/ │ ├── downloader.go │ ├── index.go │ ├── index_scan.go │ ├── module.go │ ├── updates_test.go │ └── upgrade.go ├── spn/ │ ├── TESTING.md │ ├── TRADEMARKS │ ├── access/ │ │ ├── account/ │ │ │ ├── auth.go │ │ │ ├── client.go │ │ │ ├── types.go │ │ │ └── view.go │ │ ├── api.go │ │ ├── client.go │ │ ├── client_test.go │ │ ├── database.go │ │ ├── features.go │ │ ├── module.go │ │ ├── module_test.go │ │ ├── notify.go │ │ ├── op_auth.go │ │ ├── storage.go │ │ ├── token/ │ │ │ ├── errors.go │ │ │ ├── module_test.go │ │ │ ├── pblind.go │ │ │ ├── pblind_gen_test.go │ │ │ ├── pblind_test.go │ │ │ ├── registry.go │ │ │ ├── request.go │ │ │ ├── request_test.go │ │ │ ├── scramble.go │ │ │ ├── scramble_gen_test.go │ │ │ ├── scramble_test.go │ │ │ ├── token.go │ │ │ └── token_test.go │ │ └── zones.go │ ├── cabin/ │ │ ├── config-public.go │ │ ├── database.go │ │ ├── identity.go │ │ ├── identity_test.go │ │ ├── keys.go │ │ ├── keys_test.go │ │ ├── module.go │ │ ├── module_test.go │ │ ├── verification.go │ │ └── verification_test.go │ ├── captain/ │ │ ├── api.go │ │ ├── bootstrap.go │ │ ├── client.go │ │ ├── config.go │ │ ├── establish.go │ │ ├── exceptions.go │ │ ├── gossip.go │ │ ├── hooks.go │ │ ├── intel.go │ │ ├── module.go │ │ ├── navigation.go │ │ ├── op_gossip.go │ │ ├── op_gossip_query.go │ │ ├── op_publish.go │ │ ├── piers.go │ │ ├── public.go │ │ └── status.go │ ├── conf/ │ │ ├── map.go │ │ ├── mode.go │ │ ├── networks.go │ │ └── version.go │ ├── crew/ │ │ ├── connect.go │ │ ├── metrics.go │ │ ├── module.go │ │ ├── module_test.go │ │ ├── op_connect.go │ │ ├── op_connect_test.go │ │ ├── op_ping.go │ │ ├── op_ping_test.go │ │ ├── policy.go │ │ └── sticky.go │ ├── debug.go │ ├── docks/ │ │ ├── bandwidth_test.go │ │ ├── controller.go │ │ ├── crane.go │ │ ├── crane_establish.go │ │ ├── crane_init.go │ │ ├── crane_netstate.go │ │ ├── crane_terminal.go │ │ ├── crane_test.go │ │ ├── crane_verify.go │ │ ├── cranehooks.go │ │ ├── hub_import.go │ │ ├── measurements.go │ │ ├── metrics.go │ │ ├── module.go │ │ ├── module_test.go │ │ ├── op_capacity.go │ │ ├── op_capacity_test.go │ │ ├── op_expand.go │ │ ├── op_latency.go │ │ ├── op_latency_test.go │ │ ├── op_sync_state.go │ │ ├── op_whoami.go │ │ ├── op_whoami_test.go │ │ ├── terminal_expansion.go │ │ └── terminal_expansion_test.go │ ├── hub/ │ │ ├── database.go │ │ ├── errors.go │ │ ├── format.go │ │ ├── format_test.go │ │ ├── hub.go │ │ ├── hub_test.go │ │ ├── intel.go │ │ ├── intel_override.go │ │ ├── measurements.go │ │ ├── status.go │ │ ├── transport.go │ │ ├── transport_test.go │ │ ├── truststores.go │ │ ├── update.go │ │ └── update_test.go │ ├── instance.go │ ├── navigator/ │ │ ├── api.go │ │ ├── api_route.go │ │ ├── costs.go │ │ ├── database.go │ │ ├── findnearest.go │ │ ├── findnearest_test.go │ │ ├── findroutes.go │ │ ├── findroutes_test.go │ │ ├── intel.go │ │ ├── map.go │ │ ├── map_stats.go │ │ ├── map_test.go │ │ ├── measurements.go │ │ ├── metrics.go │ │ ├── module.go │ │ ├── module_test.go │ │ ├── optimize.go │ │ ├── optimize_region.go │ │ ├── optimize_test.go │ │ ├── options.go │ │ ├── pin.go │ │ ├── pin_export.go │ │ ├── region.go │ │ ├── route.go │ │ ├── routing-profiles.go │ │ ├── sort.go │ │ ├── sort_test.go │ │ ├── state.go │ │ ├── state_test.go │ │ └── update.go │ ├── patrol/ │ │ ├── domains.go │ │ ├── domains_test.go │ │ ├── http.go │ │ └── module.go │ ├── ships/ │ │ ├── connection_test.go │ │ ├── http.go │ │ ├── http_info.go │ │ ├── http_info_page.html.tmpl │ │ ├── http_info_test.go │ │ ├── http_shared.go │ │ ├── http_shared_test.go │ │ ├── kcp.go │ │ ├── launch.go │ │ ├── masking.go │ │ ├── module.go │ │ ├── mtu.go │ │ ├── pier.go │ │ ├── registry.go │ │ ├── ship.go │ │ ├── tcp.go │ │ ├── testship.go │ │ ├── testship_test.go │ │ └── virtual_network.go │ ├── sluice/ │ │ ├── module.go │ │ ├── packet_listener.go │ │ ├── request.go │ │ ├── sluice.go │ │ ├── sluices.go │ │ └── udp_listener.go │ ├── spn.go │ ├── terminal/ │ │ ├── control_flow.go │ │ ├── defaults.go │ │ ├── errors.go │ │ ├── fmt.go │ │ ├── init.go │ │ ├── metrics.go │ │ ├── module.go │ │ ├── module_test.go │ │ ├── msg.go │ │ ├── msgtypes.go │ │ ├── operation.go │ │ ├── operation_base.go │ │ ├── operation_counter.go │ │ ├── permission.go │ │ ├── rate_limit.go │ │ ├── session.go │ │ ├── session_test.go │ │ ├── terminal.go │ │ ├── terminal_test.go │ │ ├── testing.go │ │ └── upstream.go │ ├── test │ ├── testing/ │ │ ├── README.md │ │ └── simple/ │ │ ├── README.md │ │ ├── clientsim.sh │ │ ├── config-template.json │ │ ├── docker-compose.yml │ │ ├── entrypoint.sh │ │ ├── inject-intel.sh │ │ ├── intel-client.yaml │ │ ├── intel-testnet.json │ │ ├── join.sh │ │ ├── reset-databases.sh │ │ ├── run.sh │ │ └── stop.sh │ ├── tools/ │ │ ├── Dockerfile │ │ ├── container-init.sh │ │ ├── install.sh │ │ ├── install.v2.sh │ │ ├── start-checksum.txt │ │ └── sysctl.conf │ └── unit/ │ ├── doc.go │ ├── scheduler.go │ ├── scheduler_stats.go │ ├── scheduler_test.go │ ├── unit.go │ ├── unit_debug.go │ └── unit_test.go ├── windows_core_dll/ │ ├── build.ps1 │ ├── dllmain.cpp │ ├── framework.h │ ├── pch.cpp │ ├── pch.h │ ├── windows_core_dll.sln │ ├── windows_core_dll.vcxproj │ ├── windows_core_dll.vcxproj.filters │ └── windows_core_dll.vcxproj.user └── windows_kext/ ├── .gitignore ├── PacketFlow.md ├── PortmasterKext64.inf ├── README.md ├── c_helper/ │ ├── ARM64/ │ │ └── c_helper.lib │ ├── c_helper.filters │ ├── c_helper.sln │ ├── c_helper.vcxproj │ ├── helper.c │ └── x64/ │ └── c_helper.lib ├── driver/ │ ├── .cargo/ │ │ └── config.toml │ ├── Cargo.toml │ ├── Makefile.toml │ ├── README.md │ ├── rust-toolchain │ └── src/ │ ├── ale_callouts.rs │ ├── array_holder.rs │ ├── bandwidth.rs │ ├── callouts.rs │ ├── common.rs │ ├── connection.rs │ ├── connection_cache.rs │ ├── connection_map.rs │ ├── device.rs │ ├── entry.rs │ ├── id_cache.rs │ ├── lib.rs │ ├── logger.rs │ ├── packet_callouts.rs │ ├── packet_util.rs │ └── stream_callouts.rs ├── kextinterface/ │ ├── command.go │ ├── info.go │ ├── ioctl.go │ ├── kext.go │ ├── kext_file.go │ ├── kext_file_test.go │ ├── protocol_test.go │ └── version.txt ├── link-dev.ps1 ├── protocol/ │ ├── Cargo.toml │ ├── README.md │ └── src/ │ ├── command.rs │ ├── info.rs │ └── lib.rs ├── test/ │ ├── BUILD_DEBUG.md │ ├── README.md │ └── build_test.ps1 ├── test_protocol.sh └── wdk/ ├── .cargo/ │ └── config.toml ├── Cargo.toml ├── README.md ├── build.rs ├── rust-analyzer.cargo.target ├── rust-toolchain └── src/ ├── allocator.rs ├── attributes.rs ├── consts.rs ├── debug.rs ├── driver.rs ├── error.rs ├── fast_mutex.rs ├── ffi.rs ├── filter_engine/ │ ├── callout.rs │ ├── callout_data.rs │ ├── classify.rs │ ├── connect_request.rs │ ├── ffi.rs │ ├── layer.rs │ ├── metadata.rs │ ├── mod.rs │ ├── net_buffer.rs │ ├── packet.rs │ ├── stream_data.rs │ └── transaction.rs ├── interface.rs ├── ioqueue.rs ├── irp_helpers.rs ├── lib.rs ├── rw_spin_lock.rs ├── spin_lock.rs └── utils.rs ================================================ FILE CONTENTS ================================================ ================================================ FILE: .angulardoc.json ================================================ { "repoId": "8f466ce7-4b75-4048-8b8a-cad5bf173aa0", "lastSync": 0 } ================================================ FILE: .earthlyignore ================================================ # Ignore angular outputs. desktop/angular/node_modules desktop/angular/dist desktop/angular/dist-lib desktop/angular/dist-extension desktop/angular/.angular # Ignore tauri outputs. desktop/tauri/src-tauri/target ####################### # Copy from .gitignore: # Compiled binaries # *.exe # dist/ # Dist dir # dist # Custom dev deops go.mod.* # vendor dir vendor # testing testing spn/testing/simple/testdata # Compiled Object files, Static and Dynamic libs (Shared Objects) *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof # Output of the go coverage tool, specifically when used with LiteIDE *.out # OS specifics .DS_Store # Custom dev scripts win_dev_* go.work go.work.sum ================================================ FILE: .gitattributes ================================================ # Treat all Go files in this repo as binary, with no git magic updating # line endings. Windows users contributing to Go will need to use a # modern version of git and editors capable of LF line endings. *.go -text diff=golang ================================================ FILE: .github/FUNDING.yml ================================================ github: safing ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ # Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser blank_issues_enabled: true # default: true contact_links: - name: "Ask Questions on Discord" url: https://safing.io/discord about: Get help from our great community - name: "Wiki and FAQ" url: https://wiki.safing.io/ about: Learn more about Portmaster in our Wiki - name: "Contribution Guideline" url: https://docs.safing.io/portmaster/guides/contribute about: Learn how to best contribute and make sure your work is aligned with Safing’s current goals and focus - name: "Code of Conduct" url: https://docs.safing.io/community/code-of-conduct about: Be nice to other community members ================================================ FILE: .github/ISSUE_TEMPLATE/report-bug.md ================================================ --- name: "🐞 Report a Bug" about: Report a bug encountered while using the Portmaster labels: bug --- **Pre-Submit Checklist**: - Check applicable sources for existing issues: - [Windows Known Issues](https://docs.safing.io/portmaster/install/windows#known-issues) - [Linux Known Issues](https://docs.safing.io/portmaster/install/linux#compatibility) - [Github Issues](https://github.com/safing/portmaster/issues?q=is%3Aissue+label%3Abug) **What happened**: **What did you expect to happen?**: **How did you reproduce it?**: **Debug Information**: ================================================ FILE: .github/ISSUE_TEMPLATE/report-compatibility.md ================================================ --- name: "📝 Make a Compatibility Report" about: Report Portmaster in/compatibility with Linux Distros, VPN Clients or general Software labels: "in/compatibility" --- **Pre-Submit Checklist**: - Check applicable sources for existing issues: - [Linux Compatibility](https://docs.safing.io/portmaster/install/linux#compatibility) - [VPN Compatibility](https://docs.safing.io/portmaster/install/status/vpn-compatibility) - [Github Issues](https://github.com/safing/portmaster/issues?q=is%3Aissue+label%3Ain%2Fcompatibility) **What worked?** **What did not work?** **Debug Information**: ================================================ FILE: .github/ISSUE_TEMPLATE/suggest-feature.md ================================================ --- name: "💡 Suggest an Improvement or Feature" about: Suggest an enhancement or a new feature for the Portmaster labels: suggestion --- **What would you like to add or change?**: **Why do you and others need this?**: ================================================ FILE: .github/dependabot.yml ================================================ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "gomod" directory: "/" schedule: interval: "daily" - package-ecosystem: "npm" directory: "/desktop/angular" schedule: interval: "daily" - package-ecosystem: "cargo" directory: "/desktop/tauri" schedule: interval: "daily" ================================================ FILE: .github/label-actions.yml ================================================ # Configuration for Label Actions - https://github.com/dessant/label-actions community support: comment: | Hey @{issue-author}, thank you for raising this issue with us. After a first review we noticed that this does not seem to be a technical issue, but rather a configuration issue or general question about how Portmaster works. Thus, we invite the community to help with configuration and/or answering this questions. If you are in a hurry or haven't received an answer, a good place to ask is in [our Discord community](https://discord.gg/safing). If your problem or question has been resolved or answered, please come back and give an update here for other users encountering the same and then close this issue. If you are a paying subscriber and want this issue to be checked out by Safing, please send us a message [on Discord](https://discord.gg/safing) or [via Email](mailto:support@safing.io) with your username and the link to this issue, so we can prioritize accordingly. needs debug info: comment: | Hey @{issue-author}, thank you for raising this issue with us. After a first review we noticed that we will require the Debug Info for further investigation. However, you haven't supplied any Debug Info in your report. Please [collect Debug Info](https://wiki.safing.io/en/FAQ/DebugInfo) from Portmaster _while_ the reported issue is present. in/compatibility: comment: | Hey @{issue-author}, thank you for reporting on a compatibility. We keep a list of compatible software and user provided guides for improving compatibility [in the wiki - please have a look there](https://wiki.safing.io/en/Portmaster/App/Compatibility). If you can't find your software in the list, then a good starting point is our guide on [How do I make software compatible with Portmaster](https://wiki.safing.io/en/FAQ/MakeSoftwareCompatibleWithPortmaster). If you have managed to establish compatibility with an application, please share your findings here. This will greatly help other users encountering the same issues. fixed: comment: | This issue has been fixed by the recently referenced commit or PR. However, the fix is not released yet. It is expected to go into the [Beta Release Channel](https://wiki.safing.io/en/FAQ/SwitchReleaseChannel) for testing within the next two weeks and will be available for everyone within the next four weeks. While this is the typical timeline we work with, things are subject to change. ================================================ FILE: .github/workflows/angular.yml ================================================ name: Angular on: push: paths: - 'desktop/angular/**' branches: - main - development - v1-legacy - v1-legacy-develop pull_request: paths: - 'desktop/angular/**' branches: - main - development - v1-legacy - v1-legacy-develop jobs: lint: name: Lint runs-on: ubuntu-latest defaults: run: working-directory: desktop/angular steps: - name: Check out code uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3 with: node-version: 18 - run: npm install - uses: sibiraj-s/action-eslint@bcf41bb9abce43cdbad51ab9b3da2eddaa17eab3 # v3 with: annotations: true extensions: 'ts,html' working-directory: desktop/angular test: name: Build runs-on: ubuntu-latest steps: - uses: earthly/actions-setup@43211c7a0eae5344d6d79fb4aaf209c8f8866203 # v1 with: version: v0.8.0 - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Log in to the Container registry uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build angular projects run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +angular-ci ================================================ FILE: .github/workflows/go.yml ================================================ name: Go on: push: paths: - '**.go' - 'cmds/**' - 'runtime/**' - 'service/**' - 'spn/**' branches: - main - development - v1-legacy - v1-legacy-develop pull_request: paths: - '**.go' - 'cmds/**' - 'runtime/**' - 'service/**' - 'spn/**' branches: - main - development - v1-legacy - v1-legacy-develop jobs: lint: name: Linter runs-on: ubuntu-latest steps: - name: Check out code uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Setup Go uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 with: go-version: '^1.21' cache: false - name: Run golangci-lint uses: golangci/golangci-lint-action@55c2c1448f86e01eaae002a5a3a9624417608d84 # v6 with: version: v1.64.6 only-new-issues: true args: -c ./.golangci.yml --timeout 15m - name: Run go vet run: go vet ./... test: name: Test & Build runs-on: ubuntu-latest steps: - uses: earthly/actions-setup@43211c7a0eae5344d6d79fb4aaf209c8f8866203 # v1 with: version: v0.8.0 - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Log in to the Container registry uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Run Go Tests & Build run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +go-ci ================================================ FILE: .github/workflows/issues-first-greet.yml ================================================ # This workflow responds to first time posters with a greeting message. # Docs: https://github.com/actions/first-interaction name: Greet New Users # This workflow is triggered when a new issue is created. on: issues: types: opened permissions: contents: read issues: write jobs: greet: runs-on: ubuntu-latest steps: - uses: actions/first-interaction@2ec0f0fd78838633cd1c1342e4536d49ef72be54 # v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} # Respond to first time issue raisers. issue-message: | Greetings and welcome to our community! As this is the first issue you opened here, we wanted to share some useful infos with you: - 🗣️ Our community on [Discord](https://discord.gg/safing) is super helpful and active. We also have an AI-enabled support bot that knows Portmaster well and can give you immediate help. - 📖 The [Wiki](https://wiki.safing.io/) answers all common questions and has many important details. If you can't find an answer there, let us know, so we can add anything that's missing. ================================================ FILE: .github/workflows/issues-label-actions.yml ================================================ # This workflow responds with a message when certain labels are added to an issue or PR. # Docs: https://github.com/dessant/label-actions name: Label Actions # This workflow is triggered when a label is added to an issue. on: issues: types: labeled permissions: contents: read issues: write jobs: action: runs-on: ubuntu-latest steps: - uses: dessant/label-actions@ade7bcd4c1b30de6ba8e556cc31301fd4f79ca65 # v3 with: github-token: ${{ secrets.GITHUB_TOKEN }} config-path: ".github/label-actions.yml" process-only: "issues" ================================================ FILE: .github/workflows/issues-stale.yml ================================================ # This workflow warns and then closes stale issues and PRs. # Docs: https://github.com/actions/stale name: Close Stale Issues on: schedule: - cron: "17 5 * * 1-5" # run at 5:17 (UTC) on Monday to Friday workflow_dispatch: permissions: contents: read issues: write jobs: stale: runs-on: ubuntu-latest steps: - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8 with: repo-token: ${{ secrets.GITHUB_TOKEN }} # Increase max operations. # When using GITHUB_TOKEN, the rate limit is 1,000 requests per hour per repository. operations-per-run: 500 # Handle stale issues stale-issue-label: 'stale' # Exemptions exempt-all-issue-assignees: true exempt-issue-labels: 'support,dependencies,pinned,security' # Mark as stale days-before-issue-stale: 63 # 2 months / 9 weeks stale-issue-message: | This issue has been automatically marked as inactive because it has not had activity in the past two months. If no further activity occurs, this issue will be automatically closed in one week in order to increase our focus on active topics. # Close days-before-issue-close: 7 # 1 week close-issue-message: | This issue has been automatically closed because it has not had recent activity. Thank you for your contributions. If the issue has not been resolved, you can [find more information in our Wiki](https://wiki.safing.io/) or [continue the conversation on our Discord](https://discord.gg/safing). # TODO: Handle stale PRs days-before-pr-stale: 36500 # 100 years - effectively disabled. ================================================ FILE: .github/workflows/kext.yml ================================================ name: Windows Kernel Extension on: push: paths: - 'windows_kext/**' branches: - main - development - v1-legacy - v1-legacy-develop pull_request: paths: - 'windows_kext/**' branches: - main - development - v1-legacy - v1-legacy-develop jobs: build: name: Build runs-on: ubuntu-latest steps: - uses: earthly/actions-setup@43211c7a0eae5344d6d79fb4aaf209c8f8866203 # v1 with: version: v0.8.0 - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Log in to the Container registry uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build Kernel Extension run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +kext-ci ================================================ FILE: .github/workflows/release.yml ================================================ name: Release v2.X on: push: branches: - main - development tags: - v* workflow_dispatch: jobs: release-prep: name: Prep runs-on: ubuntu-latest steps: - uses: earthly/actions-setup@43211c7a0eae5344d6d79fb4aaf209c8f8866203 # v1 with: version: v0.8.0 - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Log in to the Container registry uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build all artifacts run: earthly --remote-cache=ghcr.io/safing/build-cache --push +release-prep - name: Upload Dist uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: path: ./dist/ if-no-files-found: error installer-linux: #JOB DISABLED FOR NOW if: false name: Installer linux runs-on: ubuntu-latest needs: release-prep steps: - uses: earthly/actions-setup@43211c7a0eae5344d6d79fb4aaf209c8f8866203 # v1 with: version: v0.8.0 - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Log in to the Container registry uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build linux installers run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +installer-linux # --ci include --no-output flag - name: Upload Installers uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: path: ./dist/linux_amd64/ if-no-files-found: error installer-windows: #JOB DISABLED FOR NOW if: false name: Installer windows runs-on: windows-latest needs: release-prep steps: - name: Checkout Repository uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Download Dist uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: path: dist/ - name: Build windows artifacts run: powershell -NoProfile -File ./packaging/windows/generate_windows_installers.ps1 - name: Upload Installers uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: path: ./dist/windows_amd64/ if-no-files-found: error ================================================ FILE: .github/workflows/tauri.yml ================================================ name: Tauri on: push: paths: - 'desktop/tauri/**' branches: - main - development - v1-legacy - v1-legacy-develop pull_request: paths: - 'desktop/tauri/**' branches: - main - development - v1-legacy - v1-legacy-develop jobs: build: name: Build runs-on: ubuntu-latest steps: - uses: earthly/actions-setup@43211c7a0eae5344d6d79fb4aaf209c8f8866203 # v1 with: version: v0.8.0 - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Log in to the Container registry uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build tauri project run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +tauri-ci lint: name: Linter runs-on: ubuntu-latest steps: - uses: earthly/actions-setup@43211c7a0eae5344d6d79fb4aaf209c8f8866203 # v1 with: version: v0.8.0 - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Log in to the Container registry uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build tauri project run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +tauri-lint ================================================ FILE: .github/workflows/windows-dll.yml ================================================ name: Windows Portmaster Core DLL on: push: paths: - 'windows_core_dll/**' branches: - main - development - v1-legacy - v1-legacy-develop pull_request: paths: - 'windows_core_dll/**' branches: - main - development - v1-legacy - v1-legacy-develop workflow_dispatch: jobs: build: name: Build runs-on: windows-latest steps: - name: Checkout Repository uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Add msbuild to PATH uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2 - name: Build DLL run: msbuild windows_core_dll\windows_core_dll.sln -t:rebuild -property:Configuration=Release - name: Verify DLL shell: powershell run: | if (!(Test-Path "windows_core_dll/x64/Release/portmaster-core.dll")) { Write-Error "DLL build failed: portmaster-core.dll not found" exit 1 } - name: Upload artifacts uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: portmaster-core-dll path: windows_core_dll/x64/Release/portmaster-core.dll ================================================ FILE: .gitignore ================================================ # Compiled binaries *.exe dist/ # Dist dir dist packaging/_precompiled/ # Custom dev deops go.mod.* # vendor dir vendor # testing testdata # Compiled Object files, Static and Dynamic libs (Shared Objects) *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof # Output of the go coverage tool, specifically when used with LiteIDE *.out # OS specifics .DS_Store # Custom dev scripts win_dev_* go.work go.work.sum # Kext releases windows_kext/release/kext_release_*.zip windows_core_dll/.vs/windows_core_dll #windows_core_dll windows_core_dll/x64/ windows_core_dll/portmaster-core/x64/ #Tauri-generated files desktop/tauri/src-tauri/gen/ #Binaries used for installer gereneration for Windows desktop/tauri/src-tauri/binary/ desktop/tauri/src-tauri/intel/ windows_kext/test/_testcert/ windows_kext/test/_out/ windows_kext/test/_delme/ ================================================ FILE: .golangci.yml ================================================ # Docs: # https://golangci-lint.run/usage/linters/ linters: enable-all: true disable: - containedctx - contextcheck - cyclop - depguard - exhaustruct - forbidigo - funlen - gochecknoglobals - gochecknoinits - gocognit - gocyclo - gomoddirectives - interfacebloat - ireturn - lll - mnd - musttag - nestif - nilnil - nlreturn - noctx - nolintlint - nonamedreturns - perfsprint # TODO(ppacher): we should re-enanble this one to avoid costly fmt.* calls in the hot-path - revive - tagliatelle - testifylint - testpackage - varnamelen - whitespace - wrapcheck - wsl - gci - tenv # Deprecated linters-settings: revive: # See https://github.com/mgechev/revive#available-rules for details. enable-all-rules: true goimports: local-prefixes: github.com/safing godox: # report any comments starting with keywords, this is useful for TODO or FIXME comments that # might be left in the code accidentally and should be resolved before merging keywords: - FIXME gosec: # To specify a set of rules to explicitly exclude. # Available rules: https://github.com/securego/gosec#available-rules excludes: - G204 # Variables in commands. - G304 # Variables in file paths. - G505 # We need crypto/sha1 for non-security stuff. Using `nolint:` triggers another linter. issues: exclude-use-default: false exclude-rules: - text: "a blank import .*" linters: - golint - text: "ST1000: at least one file in a package should have a package comment.*" linters: - stylecheck ================================================ FILE: .travis.yml ================================================ language: go go: - 1.x os: - linux - windows branches: only: - master - develop - /^feature\/travis\/.+$/ # feature/travis/* - /^fix\/travis\/.+$/ # fix/travis/* addons: apt: update: true before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -y install libnetfilter-queue-dev; fi install: - go get -d -u github.com/golang/dep - go install github.com/golang/dep/cmd/dep - ./.ci-inject-internal-deps.sh - dep ensure - ./test install script: ./test --scripted ================================================ FILE: .vscode/launch.json ================================================ { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "portmaster-core", "type": "go", "request": "launch", "mode": "auto", "program": "${workspaceFolder}/cmds/portmaster-core", "windows": { "args": ["--bin-dir=C:\\Program Files\\Portmaster", "--log-stdout", "--log", "trace"] }, "linux": { "console": "integratedTerminal", // required to debug as root "asRoot": true, // required to debug as root "args": ["--bin-dir=/usr/lib/portmaster", "--log-stdout", "--log", "trace"] }, }, { "name": "portmaster-core (NO INTERCEPTION)", "type": "go", "request": "launch", "mode": "auto", "program": "${workspaceFolder}/cmds/portmaster-core", "windows": { "args": ["--bin-dir=C:\\Program Files\\Portmaster", "--log-stdout", "--log", "trace", "--disable-interception=true"] }, "linux": { "console": "integratedTerminal", // required to debug as root "asRoot": true, // required to debug as root "args": ["--bin-dir=/usr/lib/portmaster", "--log-stdout", "--log", "trace", "--disable-interception=true"] }, } ] } ================================================ FILE: .vscode/settings.json ================================================ { "cSpell.words": [ "netenv", "safing", "Warningf" ] } ================================================ FILE: AUTHORS ================================================ All files in this repository (unless otherwise noted) are authored, owned and copyrighted by Safing ICS Technologies GmbH (Austria). ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at coc@safing.io. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq ================================================ FILE: Earthfile ================================================ VERSION 0.8 # Custom argument: "custom_version" to manually set the version of the build (and ignore Git Tag value) # Usage example: earthly --build-arg custom_version="1.2.3" + ARG --global custom_version = "" ARG --global go_version = 1.24 ARG --global node_version = 18 ARG --global rust_version = 1.89 ARG --global tauri_version = "2.2.5" ARG --global golangci_lint_version = 1.64.6 ARG --global go_builder_image = "golang:${go_version}-alpine" ARG --global node_builder_image = "node:${node_version}" ARG --global rust_builder_image = "rust:${rust_version}-bookworm" ARG --global work_image = "alpine" ARG --global outputDir = "./dist" # Architectures: # The list of rust targets we support. They will be automatically converted # to GOOS, GOARCH and GOARM when building go binaries. See the +RUST_TO_GO_ARCH_STRING # helper method at the bottom of the file. # # Linux: # x86_64-unknown-linux-gnu # aarch64-unknown-linux-gnu # armv7-unknown-linux-gnueabihf # arm-unknown-linux-gnueabi # # Windows: # x86_64-pc-windows-gnu # aarch64-pc-windows-gnu # # Mac: # x86_64-apple-darwin # aarch64-apple-darwin # Import the earthly rust lib since it already provides some useful # build-targets and methods to initialize the rust toolchain. IMPORT github.com/earthly/lib/rust:3.0.2 AS rust build: # Build all Golang binaries: # ./dist/linux_amd64/portmaster-core # ./dist/linux_amd64/portmaster-start # ./dist/linux_arm64/portmaster-core # ./dist/linux_arm64/portmaster-start # ./dist/windows_amd64/portmaster-core.exe # ./dist/windows_amd64/portmaster-start.exe # ./dist/windows_arm64/portmaster-core.exe # ./dist/windows_arm64/portmaster-start.exe BUILD +go-build --GOOS="linux" --GOARCH="amd64" BUILD +go-build --GOOS="linux" --GOARCH="arm64" BUILD +go-build --GOOS="windows" --GOARCH="amd64" BUILD +go-build --GOOS="windows" --GOARCH="arm64" # Build the Angular UI: # ./dist/all/portmaster-ui.zip BUILD +angular-release # Build Tauri app binaries: # ./dist/linux_amd64/portmaster-app # ./dist/windows_amd64/portmaster-app BUILD +tauri-build --target="x86_64-unknown-linux-gnu" BUILD +tauri-build --target="x86_64-pc-windows-gnu" # TODO(vladimir): Build bundles # ./dist/linux_amd64/Portmaster-0.1.0-1.x86_64.rpm # ./dist/linux_amd64/Portmaster_0.1.0_amd64.deb # Bild Tauri bundle for Windows: # Build UI assets: # ./dist/all/assets.zip BUILD +assets build-spn: BUILD +go-build --CMDS="hub" --GOOS="linux" --GOARCH="amd64" BUILD +go-build --CMDS="hub" --GOOS="linux" --GOARCH="arm64" # TODO: Add other platforms go-ci: BUILD +go-build --GOOS="linux" --GOARCH="amd64" BUILD +go-build --GOOS="linux" --GOARCH="arm64" BUILD +go-build --GOOS="windows" --GOARCH="amd64" BUILD +go-build --GOOS="windows" --GOARCH="arm64" BUILD +go-test angular-ci: BUILD +angular-release tauri-ci: BUILD +tauri-build --target="x86_64-unknown-linux-gnu" BUILD +tauri-build --target="x86_64-pc-windows-gnu" kext-ci: BUILD +kext-build release: LOCALLY IF ! git diff --quiet RUN echo -e "\033[1;31m Refusing to release a dirty git repository. Please commit your local changes first! \033[0m" ; exit 1 END BUILD +build go-deps: FROM ${go_builder_image} WORKDIR /go-workdir # We need the git cli to extract version information for go-builds RUN apk add git # These cache dirs will be used in later test and build targets # to persist cached go packages. # # NOTE: cache only gets persisted on successful builds. A test # failure will prevent the go cache from being persisted. ENV GOCACHE = "/.go-cache" ENV GOMODCACHE = "/.go-mod-cache" # Copying only go.mod and go.sum means that the cache for this # target will only be busted when go.mod/go.sum change. This # means that we can cache the results of 'go mod download'. COPY go.mod . COPY go.sum . RUN go mod download # Explicitly cache here. SAVE IMAGE --cache-hint go-base: FROM +go-deps # Copy the full repo, as Go embeds whether the state is clean. COPY . . # Set version information: VERSION, SOURCE, BUILD_TIME and VERSION_SemVer DO +SET_VERSION_INFO # Explicitly cache here. SAVE IMAGE --cache-hint # updates all go dependencies and runs go mod tidy, saving go.mod and go.sum locally. go-update-deps: FROM +go-base RUN go get -u ./.. RUN go mod tidy SAVE ARTIFACT --keep-ts go.mod AS LOCAL go.mod SAVE ARTIFACT --keep-ts --if-exists go.sum AS LOCAL go.sum # mod-tidy runs 'go mod tidy', saving go.mod and go.sum locally. mod-tidy: FROM +go-base RUN go mod tidy SAVE ARTIFACT --keep-ts go.mod AS LOCAL go.mod SAVE ARTIFACT --keep-ts --if-exists go.sum AS LOCAL go.sum # go-build runs 'go build ./cmds/...', saving artifacts locally. # If --CMDS is not set, it defaults to building portmaster-start, portmaster-core and hub go-build: FROM +go-base # Arguments for cross-compilation. ARG GOOS=linux ARG GOARCH=amd64 ARG GOARM ARG CMDS=portmaster-core CACHE --sharing shared "$GOCACHE" CACHE --sharing shared "$GOMODCACHE" RUN mkdir /tmp/build # Fall back to build all binaries when none is specified. IF [ "${CMDS}" = "" ] LET CMDS=$(ls -1 "./cmds/") END # Build all go binaries from the specified in CMDS FOR bin IN $CMDS # Add special build options. IF [ "${GOOS}" = "windows" ] && [ "${bin}" = "portmaster-start" ] # Windows, portmaster-start ENV CGO_ENABLED = "1" ENV EXTRA_LD_FLAGS = "-H windowsgui" ELSE # Defaults ENV CGO_ENABLED = "0" ENV EXTRA_LD_FLAGS = "" END RUN --no-cache go build -ldflags="${EXTRA_LD_FLAGS} -X github.com/safing/portmaster/base/info.version=${VERSION} -X github.com/safing/portmaster/base/info.buildSource=${SOURCE} -X github.com/safing/portmaster/base/info.buildTime=${BUILD_TIME}" -o "/tmp/build/" ./cmds/${bin} END DO +GO_ARCH_STRING --goos="${GOOS}" --goarch="${GOARCH}" --goarm="${GOARM}" FOR bin IN $(ls -1 "/tmp/build/") SAVE ARTIFACT --keep-ts "/tmp/build/${bin}" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/${bin}" END SAVE ARTIFACT --keep-ts "/tmp/build/" ./output spn-image: # Use minimal image as base. FROM alpine # Copy the static executable. COPY (+go-build/output/portmaster-start --GOARCH=amd64 --GOOS=linux --CMDS=portmaster-start) /init/portmaster-start # Copy the init script COPY spn/tools/container-init.sh /init.sh # Run the hub. ENTRYPOINT ["/init.sh"] # Get version. LET version = "$(/init/portmaster-start version --short | tr ' ' -)" RUN echo "Version: ${version}" # Save dev image SAVE IMAGE "spn:latest" SAVE IMAGE "spn:${version}" SAVE IMAGE "ghcr.io/safing/spn:latest" SAVE IMAGE "ghcr.io/safing/spn:${version}" # Test one or more go packages. # Test are always run as -short, as "long" tests require a full desktop system. # Run `earthly +go-test` to test all packages # Run `earthly +go-test --PKG="service/firewall"` to only test a specific package. # Run `earthly +go-test --TESTFLAGS="-args arg1"` to add custom flags to go test (-args in this case) go-test: FROM +go-base ARG GOOS=linux ARG GOARCH=amd64 ARG GOARM ARG TESTFLAGS ARG PKG="..." CACHE --sharing shared "$GOCACHE" CACHE --sharing shared "$GOMODCACHE" FOR pkg IN $(go list -e "./${PKG}") RUN --no-cache go test -cover -short ${pkg} ${TESTFLAGS} END go-test-all: FROM ${work_image} ARG --required architectures FOR arch IN ${architectures} DO +RUST_TO_GO_ARCH_STRING --rustTarget="${arch}" BUILD +go-test --GOARCH="${GOARCH}" --GOOS="${GOOS}" --GOARM="${GOARM}" END go-lint: FROM +go-base RUN go install github.com/golangci/golangci-lint/cmd/golangci-lint@v${golangci_lint_version} RUN golangci-lint run -c ./.golangci.yml --timeout 15m --show-stats # Builds portmaster-start, portmaster-core, hub and notifier for all supported platforms go-release: FROM ${work_image} ARG --required architectures FOR arch IN ${architectures} DO +RUST_TO_GO_ARCH_STRING --rustTarget="${arch}" IF [ -z GOARCH ] RUN echo "Failed to extract GOARCH for ${arch}"; exit 1 END IF [ -z GOOS ] RUN echo "Failed to extract GOOS for ${arch}"; exit 1 END BUILD +go-build --GOARCH="${GOARCH}" --GOOS="${GOOS}" --GOARM="${GOARM}" END # Builds all binaries from the cmds/ folder for linux/windows AMD64 # Most utility binaries are never needed on other platforms. go-build-utils: BUILD +go-build --CMDS="" --GOARCH=amd64 --GOOS=linux BUILD +go-build --CMDS="" --GOARCH=amd64 --GOOS=windows # Prepares the angular project by installing dependencies angular-deps: FROM ${node_builder_image} WORKDIR /app/ui RUN apt update && apt install zip COPY desktop/angular/package.json . COPY desktop/angular/package-lock.json . RUN npm install # Copies the UI folder into the working container # and builds the shared libraries in the specified configuration (production or development) angular-base: FROM +angular-deps ARG configuration="production" COPY desktop/angular/ . # Remove symlink and copy assets directly. RUN rm ./assets # COPY assets/data ./assets # Do not include the assets folder into portmaster.zip, we use the assets.zip instead IF [ "${configuration}" = "production" ] RUN --no-cache npm run build-libs ELSE RUN --no-cache npm run build-libs:dev END # Explicitly cache here. SAVE IMAGE --cache-hint # Build an angualr project, zip it and save artifacts locally angular-project: ARG --required project ARG --required dist ARG configuration="production" ARG baseHref="/" FROM +angular-base --configuration="${configuration}" IF [ "${configuration}" = "production" ] ENV NODE_ENV="production" END RUN --no-cache ./node_modules/.bin/ng build --configuration ${configuration} --base-href ${baseHref} "${project}" RUN --no-cache cwd=$(pwd) && cd "${dist}" && zip -r "${cwd}/${project}.zip" ./ SAVE ARTIFACT --keep-ts "${dist}" "./output/${project}" # Save portmaster UI as local artifact. IF [ "${project}" = "portmaster" ] SAVE ARTIFACT --keep-ts "./${project}.zip" AS LOCAL ${outputDir}/all/${project}-ui.zip SAVE ARTIFACT --keep-ts "./${project}.zip" output/${project}.zip END # Build the angular projects (portmaster-UI and tauri-builtin) in dev mode angular-dev: BUILD +angular-project --project=portmaster --dist=./dist --configuration=development --baseHref=/ui/modules/portmaster/ BUILD +angular-project --project=tauri-builtin --dist=./dist/tauri-builtin --configuration=development --baseHref=/ # Build the angular projects (portmaster-UI and tauri-builtin) in production mode angular-release: BUILD +angular-project --project=portmaster --dist=./dist --configuration=production --baseHref=/ui/modules/portmaster/ BUILD +angular-project --project=tauri-builtin --dist=./dist/tauri-builtin --configuration=production --baseHref=/ assets: FROM ${work_image} RUN apk add zip WORKDIR /app/assets COPY --keep-ts ./assets/data . RUN zip -r -9 -db -X assets.zip * SAVE ARTIFACT --keep-ts "assets.zip" AS LOCAL "${outputDir}/all/assets.zip" # A base target for rust to prepare the build container rust-base: FROM ${rust_builder_image} RUN apt-get update -qq # Tools and libraries required for cross-compilation RUN apt-get install --no-install-recommends -qq \ autoconf \ autotools-dev \ libtool-bin \ clang \ cmake \ bsdmainutils \ gcc-multilib \ linux-libc-dev \ linux-libc-dev-amd64-cross \ build-essential \ curl \ wget \ file \ libsoup-3.0-dev \ libwebkit2gtk-4.1-dev \ gcc-mingw-w64-x86-64 \ zip # Install library dependencies for all supported architectures # required for succesfully linking. RUN apt-get install --no-install-recommends -qq \ libsoup-3.0-0 \ libwebkit2gtk-4.1-0 \ libssl3 \ libayatana-appindicator3-1 \ librsvg2-bin \ libgtk-3-0 \ libjavascriptcoregtk-4.1-0 \ libssl-dev \ libayatana-appindicator3-dev \ librsvg2-dev \ libgtk-3-dev \ libjavascriptcoregtk-4.1-dev # Add some required rustup components RUN rustup component add clippy RUN rustup component add rustfmt DO rust+INIT --keep_fingerprints=true # For now we need tauri-cli 2.0.0 for bulding DO rust+CARGO --args="install tauri-cli --version 2.2.7 --locked" # Explicitly cache here. SAVE IMAGE --cache-hint tauri-src: FROM +rust-base WORKDIR /app/tauri # --keep-ts is necessary to ensure that the timestamps of the source files # are preserved such that Rust's incremental compilation works correctly. COPY --keep-ts ./desktop/tauri/ . COPY assets/data ./../../assets/data COPY packaging ./../../packaging COPY (+angular-project/output/tauri-builtin --project=tauri-builtin --dist=./dist/tauri-builtin --configuration=production --baseHref="/") ./../angular/dist/tauri-builtin WORKDIR /app/tauri/src-tauri # Explicitly cache here. SAVE IMAGE --cache-hint tauri-build: FROM +tauri-src ARG --required target ARG output=".*/release/([^\./]+|([^\./]+\.(dll|exe)))" DO +RUST_TO_GO_ARCH_STRING --rustTarget="${target}" RUN echo "GOOS=${GOOS} GOARCH=${GOARCH} GOARM=${GOARM} GO_ARCH_STRING=${GO_ARCH_STRING}" DO rust+SET_CACHE_MOUNTS_ENV RUN rustup target add "${target}" # Build RUN --mount=$EARTHLY_RUST_TARGET_CACHE cargo tauri build --ci --target="${target}" --no-bundle DO rust+COPY_OUTPUT --output="${output}" # BUG(cross-compilation): # # The above command seems to correctly compile for all architectures we want to support but fails during # linking since the target libaries are not available for the requested platforms. Maybe we need to download # the, manually ... # # The earthly rust lib also has support for using cross-rs for cross-compilation but that fails due to the # fact that cross-rs base docker images used for building are heavily outdated (latest = ubunut:16.0, main = ubuntu:20.04) # which does not ship recent enough glib versions (our glib dependency needs glib>2.70 but ubunut:20.04 only ships 2.64) # # The following would use the CROSS function from the earthly lib, this # DO rust+CROSS --target="${target}" RUN echo output: $(ls -R "target/${target}/release") # Binaries SAVE ARTIFACT --if-exists --keep-ts "target/${target}/release/portmaster" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/portmaster" SAVE ARTIFACT --if-exists --keep-ts "target/${target}/release/portmaster.exe" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/portmaster.exe" SAVE ARTIFACT --if-exists --keep-ts "target/${target}/release/WebView2Loader.dll" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/WebView2Loader.dll" SAVE ARTIFACT --if-exists --keep-ts "target/${target}/release/portmaster" ./output/portmaster SAVE ARTIFACT --if-exists --keep-ts "target/${target}/release/portmaster.exe" ./output/portmaster.exe SAVE ARTIFACT --if-exists --keep-ts "target/${target}/release/WebView2Loader.dll" ./output/WebView2Loader.dll tauri-release: FROM ${work_image} ARG --required architectures FOR arch IN ${architectures} BUILD +tauri-build --target="${arch}" END tauri-lint: FROM +rust-base ARG target="x86_64-unknown-linux-gnu" WORKDIR /app # Copy static files that are embedded inside the executable. COPY --keep-ts ./assets ./assets # Copy all the rust code COPY --keep-ts ./desktop/tauri ./desktop/tauri # Create a empty ui dir so it will satisfy the build. RUN mkdir -p ./desktop/angular/dist/tauri-builtin SAVE IMAGE --cache-hint # Run the linter. WORKDIR /app/desktop/tauri/src-tauri RUN --mount=$EARTHLY_RUST_TARGET_CACHE cargo clippy --all-targets --all-features -- -D warnings release-prep: FROM +rust-base WORKDIR /app # Linux specific COPY (+tauri-build/output/portmaster --target="x86_64-unknown-linux-gnu") ./output/binary/linux_amd64/portmaster COPY (+go-build/output/portmaster-core --GOARCH=amd64 --GOOS=linux --CMDS=portmaster-core) ./output/binary/linux_amd64/portmaster-core # Windows specific COPY (+tauri-build/output/portmaster.exe --target="x86_64-pc-windows-gnu") ./output/binary/windows_amd64/portmaster.exe COPY (+tauri-build/output/WebView2Loader.dll --target="x86_64-pc-windows-gnu") ./output/binary/windows_amd64/WebView2Loader.dll COPY (+go-build/output/portmaster-core.exe --GOARCH=amd64 --GOOS=windows --CMDS=portmaster-core) ./output/binary/windows_amd64/portmaster-core.exe # All platforms COPY (+assets/assets.zip) ./output/binary/all/assets.zip COPY (+angular-project/output/portmaster.zip --project=portmaster --dist=./dist --configuration=production --baseHref=/ui/modules/portmaster/) ./output/binary/all/portmaster.zip # Build update manager COPY (+go-build/output/updatemgr --GOARCH=amd64 --GOOS=linux --CMDS=updatemgr) ./updatemgr # Get "portmaster-kext.sys" and "portmaster-core.dll" from current stable release RUN mkdir -p ./output/downloaded/windows_amd64 && ./updatemgr download https://updates.safing.io/stable.v3.json --platform windows_amd64 ./output/downloaded/windows_amd64 RUN find ./output/downloaded/windows_amd64 -type f ! -name "portmaster-kext.sys" ! -name "portmaster-core.dll" -delete # We are only interested in the KEXT and core DLL. Remove the rest. # Get intel artifacts RUN mkdir -p ./output/intel && ./updatemgr download https://updates.safing.io/intel.v3.json ./output/intel # Save all artifacts to output folder (on host) SAVE ARTIFACT --keep-ts "output/binary/all/*" AS LOCAL "${outputDir}/binary/all/" SAVE ARTIFACT --keep-ts "output/binary/linux_amd64/*" AS LOCAL "${outputDir}/binary/linux_amd64/" SAVE ARTIFACT --keep-ts "output/binary/windows_amd64/*" AS LOCAL "${outputDir}/binary/windows_amd64/" SAVE ARTIFACT --keep-ts "output/intel/*" AS LOCAL "${outputDir}/intel/" SAVE ARTIFACT --keep-ts "output/downloaded/*" AS LOCAL "${outputDir}/downloaded/" # KEXT and core DLL: artifacts from the current stable release # Save all artifacts to the container output folder so other containers can access it. SAVE ARTIFACT --keep-ts "output/binary/all/*" "output/binary/all/" SAVE ARTIFACT --keep-ts "output/binary/linux_amd64/*" "output/binary/linux_amd64/" SAVE ARTIFACT --keep-ts "output/binary/windows_amd64/*" "output/binary/windows_amd64/" SAVE ARTIFACT --keep-ts "output/intel/*" "output/intel/" SAVE ARTIFACT --keep-ts "output/downloaded/*" "output/downloaded/" # IMPORTANT: COPYING PRECOMPILED LOCAL FILES! # If "packaging/_precompiled" foledr exists, it's contents has priority to be used; it's files will overwrite the ones from the build! # Expected structure: # - packaging/_precompiled/binary/... # - packaging/_precompiled/intel # Careful! If there are any files in the '_precompiled/intel' folder, the final 'intel/index.json' may be broken due to incorrect hash values! COPY --if-exists --keep-ts ./packaging/_precompiled/binary ./packaging/precompiled/binary COPY --if-exists --keep-ts ./packaging/_precompiled/intel ./packaging/precompiled/intel IF --no-cache [ -d ./packaging/precompiled ] RUN --no-cache echo "[ !!! ATTENTION !!! ] PRECOMPILED FILES IN USE:" && find ./packaging/precompiled -type f; IF --no-cache [ -d ./packaging/precompiled/intel ] RUN --no-cache echo "[!!! ATTENTION !!!] ENSURE THAT 'intel/index.json' CONTAINS THE CORRECT HASHES!" END RUN --no-cache echo "Script paused. Press Enter to continue..." && read SAVE ARTIFACT --if-exists --keep-ts "packaging/precompiled/intel/*" AS LOCAL "${outputDir}/intel/" # save to host SAVE ARTIFACT --if-exists --keep-ts "packaging/precompiled/binary/*" AS LOCAL "${outputDir}/binary/" # save to host SAVE ARTIFACT --if-exists --keep-ts "packaging/precompiled/intel/*" "output/intel/" # save to container (so other containers can access it) SAVE ARTIFACT --if-exists --keep-ts "packaging/precompiled/binary/*" "output/binary/" # save to container (so other containers can access it) END installer-linux: FROM +rust-base # ARG --required target ARG target="x86_64-unknown-linux-gnu" WORKDIR /app/tauri COPY --keep-ts ./desktop/tauri/ . COPY assets/data ./../../assets/data COPY packaging ./../../packaging WORKDIR /app/tauri/src-tauri SAVE IMAGE --cache-hint DO +RUST_TO_GO_ARCH_STRING --rustTarget="${target}" # Build and copy the binaries RUN mkdir -p target/${target}/release COPY (+release-prep/output/binary/linux_amd64/portmaster) ./target/${target}/release/portmaster RUN mkdir -p binary COPY (+release-prep/output/binary/linux_amd64/portmaster-core) ./binary/portmaster-core COPY (+release-prep/output/binary/all/portmaster.zip) ./binary/portmaster.zip COPY (+release-prep/output/binary/all/assets.zip) ./binary/assets.zip # Download the intel data RUN mkdir -p intel COPY (+release-prep/output/intel/*) ./intel/ # Init version information: VERSION, SOURCE, BUILD_TIME and VERSION_SemVer DO +SET_VERSION_INFO # Set version in Cargo.toml if it's a valid SemVer (required for using in the installer file names) RUN if [ -n "$VERSION_SemVer" ]; then sed -i 's/^version = ".*"/version = "'"$VERSION_SemVer"'"/g' Cargo.toml; fi # build the installers RUN cargo tauri bundle --ci --target="${target}" # Installers SAVE ARTIFACT --if-exists --keep-ts "target/${target}/release/bundle/deb/*.deb" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/" SAVE ARTIFACT --if-exists --keep-ts "target/${target}/release/bundle/rpm/*.rpm" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/" all-artifacts: BUILD +release-prep BUILD +installer-linux kext-build: FROM ${rust_builder_image} # Install architecture target DO rust+INIT --keep_fingerprints=true # Build kext WORKDIR /app/kext # --keep-ts is necessary to ensure that the timestamps of the source files # are preserved such that Rust's incremental compilation works correctly. COPY --keep-ts ./windows_kext/ . # Add target architecture RUN rustup target add x86_64-pc-windows-msvc # Build using special earthly lib WORKDIR /app/kext/release DO rust+CARGO --args="run" SAVE ARTIFACT --keep-ts "portmaster-kext-release-bundle.zip" AS LOCAL "${outputDir}/windows_amd64/portmaster-kext-release-bundle.zip" # Takes GOOS, GOARCH and optionally GOARM and creates a string representation for file-names. # in the form of ${GOOS}_{GOARCH} if GOARM is empty, otherwise ${GOOS}_${GOARCH}v${GOARM}. # Thats the same format as expected and served by our update server. # # The result is available as GO_ARCH_STRING environment variable in the build context. GO_ARCH_STRING: FUNCTION ARG --required goos ARG --required goarch ARG goarm LET result = "${goos}_${goarch}" IF [ "${goarm}" != "" ] SET result = "${goos}_${goarch}v${goarm}" END ENV GO_ARCH_STRING="${result}" # Takes a rust target (--rustTarget) and extracts architecture and OS and arm version # and finally calls GO_ARCH_STRING. # # The result is available as GO_ARCH_STRING environment variable in the build context. # It also exports GOOS, GOARCH and GOARM environment variables. RUST_TO_GO_ARCH_STRING: FUNCTION ARG --required rustTarget LET goos="" IF [ -z "${rustTarget##*linux*}" ] SET goos="linux" ELSE IF [ -z "${rustTarget##*windows*}" ] SET goos="windows" ELSE IF [ -z "${rustTarget##*darwin*}" ] SET goos="darwin" ELSE RUN echo "GOOS not detected"; \ exit 1; END LET goarch="" LET goarm="" IF [ -z "${rustTarget##*x86_64*}" ] SET goarch="amd64" ELSE IF [ -z "${rustTarget##*arm*}" ] SET goarch="arm" SET goarm="6" IF [ -z "${rustTarget##*v7*}" ] SET goarm="7" END ELSE IF [ -z "${rustTarget##*aarch64*}" ] SET goarch="arm64" ELSE RUN echo "GOARCH not detected"; \ exit 1; END ENV GOOS="${goos}" ENV GOARCH="${goarch}" ENV GOARM="${goarm}" DO +GO_ARCH_STRING --goos="${goos}" --goarch="${goarch}" --goarm="${goarm}" # Takes an architecture or GOOS string and sets the BINEXT env var. BIN_EXT: FUNCTION ARG --required arch LET binext="" IF [ -z "${arch##*windows*}" ] SET binext=".exe" END ENV BINEXT="${goos}" # Function to set the version-related environment variables (variables: VERSION, SOURCE, BUILD_TIME and VERSION_SemVer) # Call example: # DO +SET_VERSION_INFO SET_VERSION_INFO: FUNCTION ARG gitDir="/tmp/git-info" # Check if already initialized and skip the rest if true IF [ -n "$BUILD_TIME" ] #RUN echo "Version info already initialized" ELSE # Make sure git is installed in the image RUN which git || apk add --no-cache git # Create a temporary directory for git information only RUN mkdir -p ${gitDir} # Copy only the .git directory to the temporary location COPY --dir .git ${gitDir}/.git # Check if custom version was provided via command line IF [ -n "$custom_version" ] ENV VERSION="${custom_version}" RUN echo "Using custom version from command line: $VERSION" ELSE # Get version from git tags without changing workdir LET version = "$(git --git-dir=${gitDir}/.git tag --points-at || true)" IF [ -z "${version}" ] LET dev_version = "$(git --git-dir=${gitDir}/.git describe --tags --first-parent --abbrev=0 || true)" IF [ -n "${dev_version}" ] SET version = "${dev_version}_dev_build" END END IF [ -z "${version}" ] SET version = "dev_build" END ENV VERSION="${version}" RUN echo "Version: $VERSION" END # Create cleaned version without 'v' prefix and without suffix starting with '_' # Only set VERSION_SemVer if it matches semantic versioning format LET version_clean = "$(echo "${VERSION}" | sed -E 's/^[vV]//' | sed -E 's/_.*$//')" IF [ $(echo "${version_clean}" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+([.-].*)?$') ] ENV VERSION_SemVer="${version_clean}" RUN echo "VERSION_SemVer: $VERSION_SemVer" ELSE RUN echo "VERSION_SemVer: [Empty - not a valid SemVer in Git Tag] - !!! WARNING !!!" END # Get source information without changing workdir LET source = "$( (git --git-dir=${gitDir}/.git remote -v | cut -f2 | cut -d" " -f1 | head -n 1) || echo "unknown" )" ENV SOURCE="${source}" RUN echo "Source: $SOURCE" # Get build time LET build_time = "$(date -u "+%Y-%m-%dT%H:%M:%SZ" || echo "unknown")" ENV BUILD_TIME = "${build_time}" RUN echo "Build Time: $BUILD_TIME" END ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: README.md ================================================ # Get Peace of Mind
with [Easy Privacy](https://safing.io/) Portmaster is a free and open-source application firewall that does the heavy lifting for you. Restore privacy and take back control over all your computer's network activity. With great defaults your privacy improves without any effort. And if you want to configure and control everything down to the last detail - Portmaster has you covered too. Developed in the EU 🇪🇺, Austria. __[Download for Free](https://safing.io/download/)__ __[About Us](https://safing.io/about/)__ ![Portmaster User Interface](https://safing.io/assets/img/page-specific/landing/portmaster-thumbnail.png?) _seen on:_ [](https://www.heise.de/tests/Datenschutz-Firewall-Portmaster-im-Test-9611687.html)     [![ghacks.net](https://safing.io/assets/img/external/ghacks.png)](https://www.ghacks.net/2022/11/08/portmaster-1-0-released-open-source-application-firewall/)     [![Techlore](https://safing.io/assets/img/external/techlore.png)](https://www.youtube.com/watch?v=E8cTRhGtmcM)     [![Lifehacker](https://safing.io/assets/img/external/logos/lifehacker.webp)](https://lifehacker.com/the-lesser-known-apps-everyone-should-install-on-a-new-1850223434) ## [Features](https://safing.io/features/) 1. Monitor All Network Activity 2. Full Control: Block Anything 3. Automatically Block Trackers & Malware 4. Set Global & Per‑App Settings 5. Secure DNS (Doh/DoT) 6. Record and Search Network Activity ([$](https://safing.io/pricing/)) 7. Per-App Bandwidth Usage ([$](https://safing.io/pricing/)) 8. [SPN, our Next-Gen Privacy Network](https://safing.io/spn/) ([$$](https://safing.io/pricing/)) # Technical Introduction Portmaster is a privacy suite for your Windows and Linux desktop. ### Base Technology - Portmaster integrates into network stack using nfqueue on Linux and a kernel driver (WFP) on Windows. - Packets are intercepted at the raw packet level - every packet is seen and can be stopped. - Ownership of connections is found using eBPF and `/proc` on Linux and a kernel driver and the IP Helper API (`iphlpapi.dll`) on Windows. - Most settings can be defined per app, which can be matched in different ways. - Support for special processes with weird or concealed paths/actors: - Snap, AppImage and Script support on Linux - Windows Store apps and svchost.exe system services support on Windows - Everything is 100% local on your device. (except the SPN, naturally) - Updates are fully signed and downloaded automatically. - Intelligence data (block lists, geoip) is downloaded and applied automatically. - The Portmaster Core Service runs as a system service, the UI elements (App, Notifier) run in user context. - The main UI still uses electron as a wrapper :/ - but this will change in the future. You can also open the UI in the browser ### Feature: Secure DNS - Portmaster intercepts "astray" DNS queries and reroutes them to itself for seamless integration. - DNS queries are resolved by the default or configured DoT/DoH resolvers. - Full support for split horizon and horizon validation to defend against rebinding attacks. ### Feature: Privacy Filter - Define allowed network scopes: Localhost, LAN, Internet, P2P, Inbound. - Easy rules based on Internet entities: Domain, IP, Country and more. - Filter Lists block common malware, ad, tracker domains etc. ### Feature: Network History ($) - Record connections and their details in a local database and search all of it later - Auto-delete old history or delete on demand ### Feature: Bandwidth Visibility ($) - Monitor bandwidth usage per connection and app ### Feature: SPN - Safing Privacy Network ($$) - A Privacy Network aimed at use cases "between" VPN and Tor. - Uses onion encryption over multiple hops just like Tor. - Routes are chosen to cover most distance within the network to increase privacy. - Exits are chosen near the destination server. This automatically geo-unblocks in many cases. - Exclude apps and domains/entities from using SPN. - Change routing algorithm and focus per app. - Nodes are hosted by Safing (company behind Portmaster) and the community. - Speeds are pretty decent (>100MBit/s). - Further Reading: [SPN Whitepaper](https://safing.io/files/whitepaper/Gate17.pdf) ## Documentation All details and guides in the dedicated [wiki](https://wiki.safing.io/) - [Getting Started](https://wiki.safing.io/en/Portmaster/App) - Install - [on Windows](https://wiki.safing.io/en/Portmaster/Install/Windows) - [on Linux](https://wiki.safing.io/en/Portmaster/Install/Linux) - [Contribute](https://wiki.safing.io/en/Contribute) - [VPN Compatibility](https://wiki.safing.io/en/Portmaster/App/Compatibility#vpn-compatibly) - [Software Compatibility](https://wiki.safing.io/en/Portmaster/App/Compatibility) - [Architecture](https://wiki.safing.io/en/Portmaster/Architecture) - [Settings Handbook](https://docs.safing.io/portmaster/settings) - [Portmaster Developer API](https://docs.safing.io/portmaster/api) # Build Portmaster Yourself (WIP) 1. [Install Earthly CLI](https://earthly.dev/get-earthly) 2. [Install Docker Engine](https://docs.docker.com/engine/install/) 3. Run `earthly +release` 4. Find artifacts in `./dist` ================================================ FILE: TESTING.md ================================================ # Testing Portmaster This page documents ways to test if Portmaster works as intended. ⚠ Work in Progress. Currently we are just collecting helpful things we find. ## Websites for Testing: - : Check - ICMP path MTU packet delivery - IP fragmented packet delivery ================================================ FILE: TRADEMARKS ================================================ The names "Safing", "Portmaster", "Gate17" and their logos are trademarks owned by Safing ICS Technologies GmbH (Austria). Although our code is free, it is very important that we strictly enforce our trademark rights, in order to be able to protect our users against people who use the marks to commit fraud. This means that, while you have considerable freedom to redistribute and modify our software, there are tight restrictions on your ability to use our names and logos in ways which fall in the domain of trademark law, even when built into binaries that we provide. This file is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Parts of it were taken from https://www.mozilla.org/en-US/foundation/licensing/. ================================================ FILE: assets/data/favicons/browserconfig.xml ================================================ #121213 ================================================ FILE: assets/data/favicons/head.html ================================================ ================================================ FILE: assets/data/favicons/manifest.json ================================================ { "name": "App", "icons": [ { "src": "\/assets\/favicons\/android-icon-36x36.png", "sizes": "36x36", "type": "image\/png", "density": "0.75" }, { "src": "\/assets\/favicons\/android-icon-48x48.png", "sizes": "48x48", "type": "image\/png", "density": "1.0" }, { "src": "\/assets\/favicons\/android-icon-72x72.png", "sizes": "72x72", "type": "image\/png", "density": "1.5" }, { "src": "\/assets\/favicons\/android-icon-96x96.png", "sizes": "96x96", "type": "image\/png", "density": "2.0" }, { "src": "\/assets\/favicons\/android-icon-144x144.png", "sizes": "144x144", "type": "image\/png", "density": "3.0" }, { "src": "\/assets\/favicons\/android-icon-192x192.png", "sizes": "192x192", "type": "image\/png", "density": "4.0" } ] } ================================================ FILE: assets/data/fonts/Roboto-300/LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: assets/data/fonts/Roboto-300italic/LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: assets/data/fonts/Roboto-500/LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: assets/data/fonts/Roboto-500italic/LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: assets/data/fonts/Roboto-700/LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: assets/data/fonts/Roboto-700italic/LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: assets/data/fonts/Roboto-italic/LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: assets/data/fonts/Roboto-regular/LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: assets/data/fonts/roboto-slimfix.css ================================================ @font-face { font-family: 'Roboto'; font-weight: 400; font-style: normal; src: url('/assets/fonts/Roboto-300/Roboto-300.eot'); src: url('/assets/fonts/Roboto-300/Roboto-300.eot?#iefix') format('embedded-opentype'), local('Roboto Light'), local('Roboto-300'), url('/assets/fonts/Roboto-300/Roboto-300.woff2') format('woff2'), url('/assets/fonts/Roboto-300/Roboto-300.woff') format('woff'), url('/assets/fonts/Roboto-300/Roboto-300.ttf') format('truetype'), url('/assets/fonts/Roboto-300/Roboto-300.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 500; font-style: normal; src: url('/assets/fonts/Roboto-regular/Roboto-regular.eot'); src: url('/assets/fonts/Roboto-regular/Roboto-regular.eot?#iefix') format('embedded-opentype'), local('Roboto'), local('Roboto-regular'), url('/assets/fonts/Roboto-regular/Roboto-regular.woff2') format('woff2'), url('/assets/fonts/Roboto-regular/Roboto-regular.woff') format('woff'), url('/assets/fonts/Roboto-regular/Roboto-regular.ttf') format('truetype'), url('/assets/fonts/Roboto-regular/Roboto-regular.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 700; font-style: normal; src: url('/assets/fonts/Roboto-500/Roboto-500.eot'); src: url('/assets/fonts/Roboto-500/Roboto-500.eot?#iefix') format('embedded-opentype'), local('Roboto Medium'), local('Roboto-500'), url('/assets/fonts/Roboto-500/Roboto-500.woff2') format('woff2'), url('/assets/fonts/Roboto-500/Roboto-500.woff') format('woff'), url('/assets/fonts/Roboto-500/Roboto-500.ttf') format('truetype'), url('/assets/fonts/Roboto-500/Roboto-500.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 900; font-style: normal; src: url('/assets/fonts/Roboto-700/Roboto-700.eot'); src: url('/assets/fonts/Roboto-700/Roboto-700.eot?#iefix') format('embedded-opentype'), local('Roboto Bold'), local('Roboto-700'), url('/assets/fonts/Roboto-700/Roboto-700.woff2') format('woff2'), url('/assets/fonts/Roboto-700/Roboto-700.woff') format('woff'), url('/assets/fonts/Roboto-700/Roboto-700.ttf') format('truetype'), url('/assets/fonts/Roboto-700/Roboto-700.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 400; font-style: italic; src: url('/assets/fonts/Roboto-300italic/Roboto-300italic.eot'); src: url('/assets/fonts/Roboto-300italic/Roboto-300italic.eot?#iefix') format('embedded-opentype'), local('Roboto Light Italic'), local('Roboto-300italic'), url('/assets/fonts/Roboto-300italic/Roboto-300italic.woff2') format('woff2'), url('/assets/fonts/Roboto-300italic/Roboto-300italic.woff') format('woff'), url('/assets/fonts/Roboto-300italic/Roboto-300italic.ttf') format('truetype'), url('/assets/fonts/Roboto-300italic/Roboto-300italic.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 500; font-style: italic; src: url('/assets/fonts/Roboto-italic/Roboto-italic.eot'); src: url('/assets/fonts/Roboto-italic/Roboto-italic.eot?#iefix') format('embedded-opentype'), local('Roboto Italic'), local('Roboto-italic'), url('/assets/fonts/Roboto-italic/Roboto-italic.woff2') format('woff2'), url('/assets/fonts/Roboto-italic/Roboto-italic.woff') format('woff'), url('/assets/fonts/Roboto-italic/Roboto-italic.ttf') format('truetype'), url('/assets/fonts/Roboto-italic/Roboto-italic.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 700; font-style: italic; src: url('/assets/fonts/Roboto-500italic/Roboto-500italic.eot'); src: url('/assets/fonts/Roboto-500italic/Roboto-500italic.eot?#iefix') format('embedded-opentype'), local('Roboto Medium Italic'), local('Roboto-500italic'), url('/assets/fonts/Roboto-500italic/Roboto-500italic.woff2') format('woff2'), url('/assets/fonts/Roboto-500italic/Roboto-500italic.woff') format('woff'), url('/assets/fonts/Roboto-500italic/Roboto-500italic.ttf') format('truetype'), url('/assets/fonts/Roboto-500italic/Roboto-500italic.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 900; font-style: italic; src: url('/assets/fonts/Roboto-700italic/Roboto-700italic.eot'); src: url('/assets/fonts/Roboto-700italic/Roboto-700italic.eot?#iefix') format('embedded-opentype'), local('Roboto Bold Italic'), local('Roboto-700italic'), url('/assets/fonts/Roboto-700italic/Roboto-700italic.woff2') format('woff2'), url('/assets/fonts/Roboto-700italic/Roboto-700italic.woff') format('woff'), url('/assets/fonts/Roboto-700italic/Roboto-700italic.ttf') format('truetype'), url('/assets/fonts/Roboto-700italic/Roboto-700italic.svg#Roboto') format('svg'); } ================================================ FILE: assets/data/fonts/roboto.css ================================================ @font-face { font-family: 'Roboto'; font-weight: 300; font-style: normal; src: url('/assets/fonts/Roboto-300/Roboto-300.eot'); src: url('/assets/fonts/Roboto-300/Roboto-300.eot?#iefix') format('embedded-opentype'), local('Roboto Light'), local('Roboto-300'), url('/assets/fonts/Roboto-300/Roboto-300.woff2') format('woff2'), url('/assets/fonts/Roboto-300/Roboto-300.woff') format('woff'), url('/assets/fonts/Roboto-300/Roboto-300.ttf') format('truetype'), url('/assets/fonts/Roboto-300/Roboto-300.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 400; font-style: normal; src: url('/assets/fonts/Roboto-regular/Roboto-regular.eot'); src: url('/assets/fonts/Roboto-regular/Roboto-regular.eot?#iefix') format('embedded-opentype'), local('Roboto'), local('Roboto-regular'), url('/assets/fonts/Roboto-regular/Roboto-regular.woff2') format('woff2'), url('/assets/fonts/Roboto-regular/Roboto-regular.woff') format('woff'), url('/assets/fonts/Roboto-regular/Roboto-regular.ttf') format('truetype'), url('/assets/fonts/Roboto-regular/Roboto-regular.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 500; font-style: normal; src: url('/assets/fonts/Roboto-500/Roboto-500.eot'); src: url('/assets/fonts/Roboto-500/Roboto-500.eot?#iefix') format('embedded-opentype'), local('Roboto Medium'), local('Roboto-500'), url('/assets/fonts/Roboto-500/Roboto-500.woff2') format('woff2'), url('/assets/fonts/Roboto-500/Roboto-500.woff') format('woff'), url('/assets/fonts/Roboto-500/Roboto-500.ttf') format('truetype'), url('/assets/fonts/Roboto-500/Roboto-500.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 700; font-style: normal; src: url('/assets/fonts/Roboto-700/Roboto-700.eot'); src: url('/assets/fonts/Roboto-700/Roboto-700.eot?#iefix') format('embedded-opentype'), local('Roboto Bold'), local('Roboto-700'), url('/assets/fonts/Roboto-700/Roboto-700.woff2') format('woff2'), url('/assets/fonts/Roboto-700/Roboto-700.woff') format('woff'), url('/assets/fonts/Roboto-700/Roboto-700.ttf') format('truetype'), url('/assets/fonts/Roboto-700/Roboto-700.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 300; font-style: italic; src: url('/assets/fonts/Roboto-300italic/Roboto-300italic.eot'); src: url('/assets/fonts/Roboto-300italic/Roboto-300italic.eot?#iefix') format('embedded-opentype'), local('Roboto Light Italic'), local('Roboto-300italic'), url('/assets/fonts/Roboto-300italic/Roboto-300italic.woff2') format('woff2'), url('/assets/fonts/Roboto-300italic/Roboto-300italic.woff') format('woff'), url('/assets/fonts/Roboto-300italic/Roboto-300italic.ttf') format('truetype'), url('/assets/fonts/Roboto-300italic/Roboto-300italic.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 400; font-style: italic; src: url('/assets/fonts/Roboto-italic/Roboto-italic.eot'); src: url('/assets/fonts/Roboto-italic/Roboto-italic.eot?#iefix') format('embedded-opentype'), local('Roboto Italic'), local('Roboto-italic'), url('/assets/fonts/Roboto-italic/Roboto-italic.woff2') format('woff2'), url('/assets/fonts/Roboto-italic/Roboto-italic.woff') format('woff'), url('/assets/fonts/Roboto-italic/Roboto-italic.ttf') format('truetype'), url('/assets/fonts/Roboto-italic/Roboto-italic.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 500; font-style: italic; src: url('/assets/fonts/Roboto-500italic/Roboto-500italic.eot'); src: url('/assets/fonts/Roboto-500italic/Roboto-500italic.eot?#iefix') format('embedded-opentype'), local('Roboto Medium Italic'), local('Roboto-500italic'), url('/assets/fonts/Roboto-500italic/Roboto-500italic.woff2') format('woff2'), url('/assets/fonts/Roboto-500italic/Roboto-500italic.woff') format('woff'), url('/assets/fonts/Roboto-500italic/Roboto-500italic.ttf') format('truetype'), url('/assets/fonts/Roboto-500italic/Roboto-500italic.svg#Roboto') format('svg'); } @font-face { font-family: 'Roboto'; font-weight: 700; font-style: italic; src: url('/assets/fonts/Roboto-700italic/Roboto-700italic.eot'); src: url('/assets/fonts/Roboto-700italic/Roboto-700italic.eot?#iefix') format('embedded-opentype'), local('Roboto Bold Italic'), local('Roboto-700italic'), url('/assets/fonts/Roboto-700italic/Roboto-700italic.woff2') format('woff2'), url('/assets/fonts/Roboto-700italic/Roboto-700italic.woff') format('woff'), url('/assets/fonts/Roboto-700italic/Roboto-700italic.ttf') format('truetype'), url('/assets/fonts/Roboto-700italic/Roboto-700italic.svg#Roboto') format('svg'); } ================================================ FILE: assets/data/icons/README.md ================================================ # .ICOs converted using https://www.icoconverter.com/ ================================================ FILE: assets/data/icons/generate_ico.sh ================================================ #!/bin/sh # Traymenu icons. Sometimes the wrong size is selected, so leave just one. convert pm_dark_green_512.png -resize 64x64 pm_dark_green_64.png convert pm_dark_blue_512.png -resize 64x64 pm_dark_blue_64.png convert pm_dark_red_512.png -resize 64x64 pm_dark_red_64.png convert pm_dark_yellow_512.png -resize 64x64 pm_dark_yellow_64.png convert pm_light_blue_512.png -resize 64x64 pm_light_blue_64.png convert pm_light_green_512.png -resize 64x64 pm_light_green_64.png convert pm_light_red_512.png -resize 64x64 pm_light_red_64.png convert pm_light_yellow_512.png -resize 64x64 pm_light_yellow_64.png convert pm_dark_512.png -colors 256 -define icon:auto-resize=64,48,32,16 pm_dark.ico convert pm_light_512.png -colors 256 -define icon:auto-resize=64,48,32,16 pm_light.ico ================================================ FILE: assets/data/img/flags/LICENSE.txt ================================================ Copyright (c) 2017 Go Squared Ltd. http://www.gosquared.com/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: assets/data/world-50m.json ================================================ {"type":"Topology","objects":{"land":{"type":"MultiPolygon","arcs":[[[0]],[[1]],[[2]],[[3]],[[4]],[[5]],[[6]],[[7]],[[8]],[[9]],[[10]],[[11]],[[12]],[[13]],[[14]],[[15]],[[16]],[[17]],[[18]],[[19]],[[20]],[[21]],[[22]],[[23]],[[24]],[[25]],[[26]],[[27]],[[28]],[[29]],[[30]],[[31]],[[32]],[[33]],[[34]],[[35]],[[36]],[[37]],[[38]],[[39]],[[40]],[[41]],[[42]],[[43]],[[44]],[[45]],[[46]],[[47]],[[48]],[[49]],[[50]],[[51]],[[52]],[[53]],[[54]],[[55]],[[56]],[[57]],[[58]],[[59]],[[60]],[[61]],[[62,63]],[[64,65,66]],[[67]],[[68,69,70,71,72]],[[73,74,75,76,77]],[[78]],[[79]],[[80]],[[81]],[[82]],[[83]],[[84]],[[85]],[[86]],[[87]],[[88]],[[89]],[[90]],[[91]],[[92]],[[93]],[[94]],[[95]],[[96]],[[97]],[[98,99]],[[100]],[[101]],[[102]],[[103]],[[104]],[[105]],[[106]],[[107]],[[108]],[[109]],[[110]],[[111]],[[112]],[[113]],[[114]],[[115,116]],[[117]],[[118]],[[119]],[[120]],[[121]],[[122]],[[123]],[[124]],[[125]],[[126]],[[127]],[[128]],[[129]],[[130]],[[131]],[[132]],[[133],[134]],[[135]],[[136]],[[137]],[[138]],[[139]],[[140]],[[141]],[[142]],[[143]],[[144]],[[145]],[[146]],[[147]],[[148]],[[149]],[[150]],[[151]],[[152]],[[153]],[[154]],[[155]],[[156]],[[157]],[[158]],[[159]],[[160]],[[161]],[[162]],[[163]],[[164]],[[165]],[[166]],[[167]],[[168]],[[169]],[[170]],[[171]],[[172]],[[173]],[[174]],[[175]],[[176]],[[177]],[[178]],[[179]],[[180]],[[181]],[[182]],[[183]],[[184]],[[185]],[[186]],[[187]],[[188]],[[189]],[[190]],[[191]],[[192]],[[193]],[[194]],[[195]],[[196]],[[197]],[[198]],[[199]],[[200]],[[201]],[[202]],[[203]],[[204]],[[205]],[[206,207,208,209,210,211,212]],[[213]],[[214]],[[215]],[[216]],[[217]],[[218]],[[219]],[[220]],[[221]],[[222]],[[223]],[[224]],[[225]],[[226]],[[227]],[[228]],[[229,230,231,232]],[[233],[234]],[[235]],[[236]],[[237]],[[238]],[[239]],[[240]],[[241]],[[242]],[[243]],[[244]],[[245]],[[246]],[[247]],[[248]],[[249]],[[250]],[[251]],[[252]],[[253]],[[254]],[[255]],[[256]],[[257]],[[258]],[[259]],[[260,261,262,263,264,265,266]],[[267]],[[268,269,270,271]],[[272]],[[273]],[[274,275,276,277,278,279,280,281,282,283]],[[284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306]],[[307]],[[308,309]],[[310]],[[311]],[[312]],[[313]],[[314]],[[315]],[[316]],[[317]],[[318]],[[319]],[[320]],[[321]],[[322]],[[323,324]],[[325]],[[326]],[[327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,908,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,930,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946],[947],[948],[949],[950],[951],[952,953,954,955],[956],[957,958],[959],[960],[961,962,963,964,965,966],[967],[968],[969],[970],[971],[972],[973],[974],[975,976],[977],[978],[979],[980],[981],[982],[983,984,985,986],[987],[988],[989],[990],[991],[992],[993],[994],[995],[996],[997],[998],[999,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014],[1015],[1016],[1017],[1018],[1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029],[1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094],[1095],[1096],[1097],[1098],[1099],[1100],[1101],[1102],[1103],[1104],[1105],[1106],[1107],[1108],[1109],[1110],[1111],[1112],[1113],[1114],[1115],[1116],[1117],[1118],[1119],[1120],[1121],[1122],[1123],[1124],[1125],[1126],[1127],[1128],[1129],[1130],[1131],[1132],[1133],[1134],[1135],[1136],[1137],[1138],[1139],[1140],[1141]],[[1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188,1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1202,1203,1204,1205,1206,1207,1208,1209,1210,1211,1212,1213,1214,1215,1216,1217,1218,1219,1220,1221,1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237,1238,1239,1240,1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1251,1252,1253,1254,1255,1256,1257,1258,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,1269,1270,1271,1272],[1273,1274,1275,1276,1277],[1278,1279,1280,1281,1282],[1283,1284,1285,1286,1287],[1288],[1289],[1290,1291,1292,1293],[1294,1295,1296,1297,1298,1299],[1300],[1301,1302,1303,1304],[1305],[1306,1307,1308,1309,1310,1311,1312,1313],[1314],[1315,1316,1317],[1318],[1319],[1320],[1321],[1322],[1323],[1324],[1325],[1326],[1327],[1328],[1329],[1330],[1331],[1332],[1333],[1334],[1335],[1336],[1337],[1338],[1339],[1340],[1341],[1342],[1343],[1344],[1345],[1346],[1347],[1348],[1349],[1350],[1351,1352,1353,1354],[1355],[1356],[1357],[1358],[1359],[1360,1361,1362,1363],[1364],[1365],[1366],[1367],[1368],[1369,1370,1371,1372,1373,1374,1375,1376],[1377],[1378],[1379,1380,1381,1382],[1383,1384,1385,1386],[1387],[1388],[1389],[1390],[1391],[1392],[1393],[1394],[1395],[1396,1397,1398,1399],[1400],[1401],[1402],[1403],[1404],[1405],[1406],[1407],[1408],[1409],[1410],[1411],[1412],[1413],[1414],[1415],[1416],[1417],[1418],[1419],[1420],[1421],[1422],[1423],[1424],[1425],[1426],[1427],[1428]],[[1429]],[[1430]],[[1431]],[[1432]],[[1433]],[[1434]],[[1435]],[[1436]],[[1437]],[[1438]],[[1439]],[[1440]],[[1441]],[[1442]],[[1443]],[[1444]],[[1445]],[[1446]],[[1447]],[[1448,1449]],[[1450]],[[1451]],[[1452]],[[1453]],[[1454]],[[1455]],[[1456,1457,1458]],[[1459]],[[1460]],[[1461]],[[1462]],[[1463]],[[1464]],[[1465]],[[1466]],[[1467]],[[1468]],[[1469]],[[1470]],[[1471],[1472]],[[1473,1474,1475,1476,1477,1478]],[[1479]],[[1480,1481,1482,1483,1484,1485,1486,1487,1488,1489]],[[1490,1491,1492,1493,1494,1495,1496,1497]],[[1498]],[[1499]],[[1500,1501]],[[1502]],[[1503,1504,1505]],[[1506]],[[1507,1508]],[[1509,1510,1511,1512]],[[1513,1514,1515,1516]],[[1517,1518,1519,1520]],[[1521,1522,1523]],[[1524,1525,1526]],[[1527,1528,1529,1530,1531,1532,1533]],[[1534]],[[1535]],[[1536,1537,1538,1539,1540]],[[1541]],[[1542]],[[1543]],[[1544,1545]],[[1546]],[[1547,1548,1549,1550,1551,1552]],[[1553]],[[1554]],[[1555,1556,1557,1558,1559,1560,1561,1562]],[[1563,1564,1565]],[[1566,1567,1568]],[[1569]],[[1570,1571,1572]],[[1573,1574,1575]],[[1576]],[[1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587]],[[1588]],[[1589,1590,1591,1592,1593,1594]],[[1595,1596,1597,1598,1599]],[[1600]],[[1601,1602,1603]],[[1604,1605,1606]],[[1607]],[[1608]],[[1609]],[[1610]],[[1611]],[[1612]],[[1613,1614,1615,1616]],[[1617,1618,1619,1620,1621]],[[1622]],[[1623,1624,1625]],[[1626,1627,1628]],[[1629]],[[1630,1631,1632]],[[1633]],[[1634,1635,1636]],[[1637,1638,1639,1640,1641,1642,1643,1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655,1656,1657,1658,1659,1660,1661,1662,1663,1664]],[[1665]],[[1666]],[[1667,1668,1669,1670,1671,1672,1673,1674,1675,1676,1677,1678,1679,1680,1681,1682,1683,1684,1685,1686,1687,1688]],[[1689]],[[1690,1691,1692,1693,1694,1695,1696,1697,1698,1699,1700,1701,1702]],[[1703,1704,1705]],[[1706,1707,1708]],[[1709]],[[1710,1711,1712,1713,1714,1715,1716,1717,1718,1719,1720]],[[1721]],[[1722,1723,1724,1725,1726,1727,1728,1729,1730,1731,1732,1733,1734,1735,1736,1737,1738,1739,1740,1741,1742,1743,1744,1745,1746,1747,1748,1749,1750,1751,1752,1753,1754,1755,1756,1757,1758,1759,1760,1761,1762,1763,1764,1765,1766,1767,1768,1769,1770,1771,1772,1773,1774,1775,1776,1777,1778,1779,1780,1781,1782,1783,1784,1785,1786,1787,1788,1789,1790,1791]],[[1792,1793,1794,1795,1796]],[[1797,1798,1799]],[[1800,1801]],[[1802,1803,1804,1805]],[[1806,1807]],[[1808]],[[1809]],[[1810]],[[1811,1812,1813,1814]],[[1815,1816,1817]],[[1818,1819]],[[1820,1821,1822,1823]],[[1824,1825,1826,1827,1828,1829,1830]],[[1831,1832,1833]],[[1834]],[[1835,1836,1837,1838,1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851]],[[1852]],[[1853]],[[1854,1855,1856,1857]],[[1858,1859,1860,1861,1862,1863,1864,1865]],[[1866,1867]],[[1868,1869,1870]],[[1871,1872]],[[1873,1874,1875,1876,1877,1878,1879,1880,1881,1882,1883,1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895,1896,1897,1898,1899,1900,1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931,1932,1933,1934,1935,1936,1937,1938,1939,1940,1941,1942,1943,1944,1945,1946,1947,1948,1949,1950,1951,1952,1953,1954,1955,1956,1957,1958,1959,1960,1961,1962,1963,1964,1965,1966,1967,1968,1969,1970,1971,1972,1973,1974,1975,1976,1977,1978,1979,1980,1981,1982,1983,1984,1985,1986,1987,1988,1989,1990],[1991],[1992]],[[1993]],[[1994,1995]],[[1996]],[[1997,1998,1999,2000]],[[2001]],[[2002]],[[2003]],[[2004]],[[2005,2006,2007,2008,2009,2010,2011]],[[2012,2013]],[[2014]],[[2015,2016]],[[2017]],[[2018]],[[2019]],[[2020]],[[2021]],[[2022]],[[2023]],[[2024]],[[2025]],[[2026]],[[2027,2028,2029,2030,2031,2032,2033,2034,2035,2036,2037,2038,2039,2040,2041,2042,2043,2044,2045,2046,2047,2048,2049,2050,2051,2052,2053,2054,2055,2056,2057,2058,2059,2060,2061,2062,2063,2064,2065,2066,2067,2068,2069,2070,2071,2072,2073]],[[2074,2075,2076,2077,2078,2079,2080,2081,2082,2083,2084,2085,2086,2087,2088,2089,2090]],[[2091,2092,2093,2094,2095,2096,2097,2098,2099,2100,2101,2102,2103,2104,2105,2106,2107,2108,2109,2110,2111,2112]],[[2113]],[[2114]],[[2115,2116,2117,2118]],[[2119]],[[2120,2121,2122,2123,2124,2125,2126,2127,2128,2129,2130]],[[2131,2132,2133,2134,2135,2136,2137,2138]],[[2139]],[[2140]],[[2141]],[[2142,2143,2144,2145,2146,2147,2148,2149,2150,2151,2152,2153,2154,2155,2156]],[[2157,2158,2159,2160,2161,2162,2163,2164,2165,2166,2167,2168,2169,2170,2171,2172,2173,2174,2175,2176,2177,2178,2179,2180,2181,2182,2183,2184,2185,2186,2187,2188,2189,2190,2191,2192,2193,2194,2195,2196],[2197]],[[2198]],[[2199]],[[2200]],[[2201,2202,2203,2204]],[[2205]],[[2206]],[[2207,2208]],[[2209]],[[2210]],[[2211]],[[2212,2213,2214]],[[2215,2216,2217,2218]],[[2219,2220,2221,2222,2223]],[[2224]],[[2225]],[[2226]],[[2227]]]},"countries":{"type":"GeometryCollection","geometries":[{"type":"Polygon","properties":{"admin":"Afghanistan","name":"Afghanistan","postal":"AF","pop_est":28400000,"iso_a2":"AF","iso_a3":"AFG"},"id":4,"arcs":[[2228,2229,2230,2231,2232,2233,2234]]},{"type":"MultiPolygon","properties":{"admin":"Angola","name":"Angola","postal":"AO","pop_est":12799293,"iso_a2":"AO","iso_a3":"AGO"},"id":24,"arcs":[[[2235,2236,1193,2237]],[[1195,2238,2239]]]},{"type":"Polygon","properties":{"admin":"Albania","name":"Albania","postal":"AL","pop_est":3639453,"iso_a2":"AL","iso_a3":"ALB"},"id":8,"arcs":[[2240,2241,2242,1236,2243,2244,1385,2245,2246]]},{"type":"Polygon","properties":{"admin":"United Arab Emirates","name":"United Arab Emirates","postal":"AE","pop_est":4798491,"iso_a2":"AE","iso_a3":"ARE"},"id":784,"arcs":[[1176,2247,2248,1174,2249]]},{"type":"MultiPolygon","properties":{"admin":"Argentina","name":"Argentina","postal":"AR","pop_est":40913584,"iso_a2":"AR","iso_a3":"ARG"},"id":32,"arcs":[[[31]],[[2250,115]],[[2251,2252,2253,670,2254,2255]]]},{"type":"Polygon","properties":{"admin":"Armenia","name":"Armenia","postal":"ARM","pop_est":2967004,"iso_a2":"AM","iso_a3":"ARM"},"id":51,"arcs":[[2256,2257,2258,2259,2260]]},{"type":"MultiPolygon","properties":{"admin":"Antarctica","name":"Antarctica","postal":"AQ","pop_est":3802,"iso_a2":"AQ","iso_a3":"ATA"},"id":10,"arcs":[[[2]],[[7]],[[8]],[[6]],[[5]],[[9]],[[11]],[[3]],[[10]],[[4]],[[0]],[[1]],[[12]],[[16]],[[17]],[[15]],[[14]],[[13]],[[19]],[[20]],[[18]],[[21]],[[22]],[[34]],[[33]],[[36]],[[37]],[[38]],[[35]],[[39]],[[40]],[[41]],[[42]],[[43]],[[32]],[[44]],[[23]],[[25]],[[24]]]},{"type":"Polygon","properties":{"admin":"French Southern and Antarctic Lands","name":"Fr. S. Antarctic Lands","postal":"TF","pop_est":140,"iso_a2":"TF","iso_a3":"ATF"},"id":260,"arcs":[[119]]},{"type":"MultiPolygon","properties":{"admin":"Australia","name":"Australia","postal":"AU","pop_est":21262641,"iso_a2":"AU","iso_a3":"AUS"},"id":36,"arcs":[[[132]],[[127]],[[128]],[[129]],[[135]],[[136]],[[148]],[[239]],[[242]],[[243]],[[233]]]},{"type":"Polygon","properties":{"admin":"Austria","name":"Austria","postal":"A","pop_est":8210281,"iso_a2":"AT","iso_a3":"AUT"},"id":40,"arcs":[[2261,2262,2263,2264,2265,2266,2267,1363,2268,2269,2270]]},{"type":"MultiPolygon","properties":{"admin":"Azerbaijan","name":"Azerbaijan","postal":"AZ","pop_est":8238672,"iso_a2":"AZ","iso_a3":"AZE"},"id":31,"arcs":[[[2271,2272,-2258]],[[1274,2273,-2261,2274,2275]]]},{"type":"Polygon","properties":{"admin":"Burundi","name":"Burundi","postal":"BI","pop_est":8988091,"iso_a2":"BI","iso_a3":"BDI"},"id":108,"arcs":[[2276,2277,1309,2278,2279,2280]]},{"type":"Polygon","properties":{"admin":"Belgium","name":"Belgium","postal":"B","pop_est":10414336,"iso_a2":"BE","iso_a3":"BEL"},"id":56,"arcs":[[2281,2282,2283,2284,2285,1249,2286,2287]]},{"type":"Polygon","properties":{"admin":"Benin","name":"Benin","postal":"BJ","pop_est":8791832,"iso_a2":"BJ","iso_a3":"BEN"},"id":204,"arcs":[[2288,1201,2289,2290,2291]]},{"type":"Polygon","properties":{"admin":"Burkina Faso","name":"Burkina Faso","postal":"BF","pop_est":15746232,"iso_a2":"BF","iso_a3":"BFA"},"id":854,"arcs":[[2292,-2291,2293,2294,2295,2296]]},{"type":"MultiPolygon","properties":{"admin":"Bangladesh","name":"Bangladesh","postal":"BD","pop_est":156050883,"iso_a2":"BD","iso_a3":"BGD"},"id":50,"arcs":[[[1430]],[[2297,1164,2298]]]},{"type":"Polygon","properties":{"admin":"Bulgaria","name":"Bulgaria","postal":"BG","pop_est":7204687,"iso_a2":"BG","iso_a3":"BGR"},"id":100,"arcs":[[1233,2299,2300,2301,2302,2303]]},{"type":"MultiPolygon","properties":{"admin":"The Bahamas","name":"Bahamas","postal":"BS","pop_est":309156,"iso_a2":"BS","iso_a3":"BHS"},"id":44,"arcs":[[[67]],[[1429]],[[1431]],[[1437]],[[1434]],[[1433]]]},{"type":"Polygon","properties":{"admin":"Bosnia and Herzegovina","name":"Bosnia and Herz.","postal":"BiH","pop_est":4613414,"iso_a2":"BA","iso_a3":"BIH"},"id":70,"arcs":[[2304,2305,2306,1239,2307]]},{"type":"Polygon","properties":{"admin":"Belarus","name":"Belarus","postal":"BY","pop_est":9648533,"iso_a2":"BY","iso_a3":"BLR"},"id":112,"arcs":[[2308,2309,2310,2311,2312]]},{"type":"Polygon","properties":{"admin":"Belize","name":"Belize","postal":"BZ","pop_est":307899,"iso_a2":"BZ","iso_a3":"BLZ"},"id":84,"arcs":[[2313,2314,657]]},{"type":"Polygon","properties":{"admin":"Bolivia","name":"Bolivia","postal":"BO","pop_est":9775246,"iso_a2":"BO","iso_a3":"BOL"},"id":68,"arcs":[[2315,-2256,2316,2317,2318,964,2319,2320,2321]]},{"type":"MultiPolygon","properties":{"admin":"Brazil","name":"Brazil","postal":"BR","pop_est":198739269,"iso_a2":"BR","iso_a3":"BRA"},"id":76,"arcs":[[[193]],[[194]],[[156]],[[247]],[[249]],[[251]],[[2322,2323,668,2324,2325,955,2326,2327,-2253,2328,2329,2330,-2322,2331,2332,2333,2334]]]},{"type":"MultiPolygon","properties":{"admin":"Brunei","name":"Brunei","postal":"BN","pop_est":388190,"iso_a2":"BN","iso_a3":"BRN"},"id":96,"arcs":[[[265,2335]],[[2336,2337,264]]]},{"type":"Polygon","properties":{"admin":"Bhutan","name":"Bhutan","postal":"BT","pop_est":691141,"iso_a2":"BT","iso_a3":"BTN"},"id":64,"arcs":[[2338,2339]]},{"type":"Polygon","properties":{"admin":"Botswana","name":"Botswana","postal":"BW","pop_est":1990876,"iso_a2":"BW","iso_a3":"BWA"},"id":72,"arcs":[[2340,2341,2342]]},{"type":"Polygon","properties":{"admin":"Central African Republic","name":"Central African Rep.","postal":"CF","pop_est":4511488,"iso_a2":"CF","iso_a3":"CAF"},"id":140,"arcs":[[2343,2344,2345,2346,2347,2348]]},{"type":"MultiPolygon","properties":{"admin":"Canada","name":"Canada","postal":"CA","pop_est":33487208,"iso_a2":"CA","iso_a3":"CAN"},"id":124,"arcs":[[[1500,1501]],[[2216,2217,2218,2215]],[[1490,1491,1492,1493,1494,1495,1496,1497]],[[1480,1481,1482,1483,1484,1485,1486,1487,1488,1489]],[[500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,2349,2350,2351,986,2352,2353,2354,2355,2356,2357,2358,2359]],[[1473,1474,1475,1476,1477,1478]],[[2142,2143,2144,2145,2146,2147,2148,2149,2150,2151,2152,2153,2154,2155,2156]],[[2157,2158,2159,2160,2161,2162,2163,2164,2165,2166,2167,2168,2169,2170,2171,2172,2173,2174,2175,2176,2177,2178,2179,2180,2181,2182,2183,2184,2185,2186,2187,2188,2189,2190,2191,2192,2193,2194,2195,2196]],[[2201,2202,2203,2204]],[[1513,1514,1515,1516]],[[1517,1518,1519,1520]],[[1524,1525,1526]],[[1527,1528,1529,1530,1531,1532,1533]],[[1563,1564,1565]],[[1994,1995]],[[1800,1801]],[[1854,1855,1856,1857]],[[1820,1821,1822,1823]],[[1858,1859,1860,1861,2360,1863,1864,1865]],[[1831,1832,1833]],[[1834]],[[1835,1836,1837,1838,1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851]],[[1868,1869,1870]],[[1871,1872]],[[1806,1807]],[[1815,1816,1817]],[[1802,1803,1804,1805]],[[1818,1819]],[[1811,1812,1813,1814]],[[2012,2013]],[[2015,2016]],[[2005,2006,2007,2008,2009,2010,2011]],[[2361,2362,2363,1000,1001,1002,1003,1004,1005,1006,1007,2364,2365,1027,1028,1029,1019,1020,2366,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,2367,2368,2369,2370,2371,2372,2373,2374,2375,2376,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,2377,2378,2379,2380,2381,2382,2383,2384,2385,2386,2387,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,908,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,930,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499]],[[2027,2028,2029,2030,2031,2032,2033,2034,2035,2036,2037,2038,2039,2040,2041,2042,2043,2044,2045,2046,2047,2048,2049,2050,2051,2052,2053,2054,2055,2056,2057,2058,2059,2060,2061,2062,2063,2064,2065,2066,2067,2068,2069,2070,2071,2072,2073]],[[2115,2116,2117,2118]],[[2131,2132,2133,2134,2135,2136,2137,2138]],[[1873,1874,1875,1876,1877,1878,1879,1880,1881,1882,1883,1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895,1896,1897,1898,1899,1900,1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931,1932,1933,1934,1935,1936,1937,1938,1939,1940,1941,1942,1943,1944,1945,1946,1947,1948,1949,1950,1951,1952,1953,1954,1955,1956,1957,1958,1959,1960,1961,1962,1963,1964,1965,1966,1967,1968,1969,1970,1971,1972,1973,1974,1975,1976,1977,1978,1979,1980,1981,1982,1983,1984,1985,1986,1987,1988,1989,1990]],[[2091,2092,2093,2094,2095,2096,2097,2098,2099,2100,2101,2102,2103,2104,2105,2106,2107,2108,2109,2110,2111,2112]],[[2026]],[[2120,2121,2122,2123,2124,2125,2126,2127,2128,2129,2130]],[[2074,2075,2076,2077,2078,2079,2080,2081,2082,2083,2084,2085,2086,2087,2088,2089,2090]],[[1613,1614,1615,1616]],[[1617,1618,1619,1620,1621]],[[1689]],[[1703,1704,1705]],[[1706,1707,1708]],[[1690,1691,1692,1693,1694,1695,1696,1697,1698,1699,1700,1701,1702]],[[1637,1638,1639,1640,1641,1642,1643,1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655,1656,1657,1658,1659,1660,1661,1662,1663,1664]],[[1667,1668,1669,1670,1671,1672,1673,1674,1675,1676,1677,1678,1679,1680,1681,1682,1683,1684,1685,1686,1687,1688]],[[1710,1711,1712,1713,1714,1715,1716,1717,1718,1719,1720]],[[1626,1627,1628]],[[1633]],[[1634,1635,1636]],[[1623,1624,1625]],[[1630,1631,1632]],[[267]],[[268,269,270,271]],[[274,275,276,277,278,279,280,281,282,283]],[[308,309]],[[284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306]],[[1722,1723,1724,1725,1726,1727,1728,1729,1730,1731,1732,1733,1734,1735,1736,1737,1738,1739,1740,1741,1742,1743,1744,1745,1746,1747,1748,1749,1750,1751,1752,1753,1754,1755,1756,1757,1758,1759,1760,1761,1762,1763,1764,1765,1766,1767,1768,1769,1770,1771,1772,1773,1774,1775,1776,1777,1778,1779,1780,1781,1782,1783,1784,1785,1786,1787,1788,1789,1790,1791]]]},{"type":"Polygon","properties":{"admin":"Switzerland","name":"Switzerland","postal":"CH","pop_est":7604467,"iso_a2":"CH","iso_a3":"CHE"},"id":756,"arcs":[[1362,-2268,2388,-2266,2389,2390,2391,2392,2393]]},{"type":"MultiPolygon","properties":{"admin":"Chile","name":"Chile","postal":"CL","pop_est":16601707,"iso_a2":"CL","iso_a3":"CHL"},"id":152,"arcs":[[[30]],[[29]],[[28]],[[27]],[[114]],[[113]],[[-2251,116]],[[149]],[[121]],[[117]],[[122]],[[120]],[[118]],[[123]],[[125]],[[124]],[[131]],[[-2255,671,2394,-2317]]]},{"type":"MultiPolygon","properties":{"admin":"China","name":"China","postal":"CN","pop_est":1338612970,"iso_a2":"CN","iso_a3":"CHN"},"id":156,"arcs":[[[101]],[[1441]],[[2395,1352,2396,2397,2398,1152,2399,1155,2400,1157,2401,2402,2403,2404,-2340,2405,2406,2407,2408,2409,2410,2411,2412,2413,2414,2415,2416,2417,2418,2419,-2229,2420,2421,2422,2423,2424,2425]]]},{"type":"Polygon","properties":{"admin":"Ivory Coast","name":"Côte d'Ivoire","postal":"CI","pop_est":20617068,"iso_a2":"CI","iso_a3":"CIV"},"id":384,"arcs":[[-2296,2426,1206,2427,2428,2429]]},{"type":"Polygon","properties":{"admin":"Cameroon","name":"Cameroon","postal":"CM","pop_est":18879301,"iso_a2":"CM","iso_a3":"CMR"},"id":120,"arcs":[[-2348,2430,2431,2432,1199,2433,2434,2435]]},{"type":"Polygon","properties":{"admin":"Democratic Republic of the Congo","name":"Dem. Rep. Congo","postal":"DRC","pop_est":68692542,"iso_a2":"CD","iso_a3":"COD"},"id":180,"arcs":[[2436,2437,2438,1316,2439,2440,1304,2441,2442,2443,2444,1290,2445,2446,-2280,2447,1311,2448,2449,-2238,1194,-2240,2450,-2346]]},{"type":"Polygon","properties":{"admin":"Republic of Congo","name":"Congo","postal":"CG","pop_est":4012809,"iso_a2":"CG","iso_a3":"COG"},"id":178,"arcs":[[-2451,-2239,1196,2451,-2431,-2347]]},{"type":"Polygon","properties":{"admin":"Colombia","name":"Colombia","postal":"CO","pop_est":45644023,"iso_a2":"CO","iso_a3":"COL"},"id":170,"arcs":[[2452,-2333,2453,2454,674,2455,663]]},{"type":"MultiPolygon","properties":{"admin":"Comoros","name":"Comoros","postal":"KM","pop_est":752438,"iso_a2":"KM","iso_a3":"COM"},"id":174,"arcs":[[[238]],[[240]]]},{"type":"MultiPolygon","properties":{"admin":"Cape Verde","name":"Cape Verde","postal":"CV","pop_est":429474,"iso_a2":"CV","iso_a3":"CPV"},"id":132,"arcs":[[[81]],[[83]],[[86]],[[85]]]},{"type":"Polygon","properties":{"admin":"Costa Rica","name":"Costa Rica","postal":"CR","pop_est":4253877,"iso_a2":"CR","iso_a3":"CRI"},"id":188,"arcs":[[661,2456,676,2457]]},{"type":"MultiPolygon","properties":{"admin":"Cuba","name":"Cuba","postal":"CU","pop_est":11451652,"iso_a2":"CU","iso_a3":"CUB"},"id":192,"arcs":[[[325]],[[326]]]},{"type":"Polygon","properties":{"admin":"Northern Cyprus","name":"N. Cyprus","postal":"CN","pop_est":265100,"iso_a2":"-99","iso_a3":"-99"},"id":-99,"arcs":[[2458,1449]]},{"type":"Polygon","properties":{"admin":"Cyprus","name":"Cyprus","postal":"CY","pop_est":531640,"iso_a2":"CY","iso_a3":"CYP"},"id":196,"arcs":[[-2459,1448]]},{"type":"Polygon","properties":{"admin":"Czech Republic","name":"Czech Rep.","postal":"CZ","pop_est":10211904,"iso_a2":"CZ","iso_a3":"CZE"},"id":203,"arcs":[[2459,2460,-2271,2461]]},{"type":"MultiPolygon","properties":{"admin":"Germany","name":"Germany","postal":"D","pop_est":82329758,"iso_a2":"DE","iso_a3":"DEU"},"id":276,"arcs":[[[2206]],[[2462,1256,2463,2464,-2462,-2270,2465,1361,-2394,-2393,-2392,2466,2467,-2284,2468,1252,2469,2470]]]},{"type":"Polygon","properties":{"admin":"Djibouti","name":"Djibouti","postal":"DJ","pop_est":516055,"iso_a2":"DJ","iso_a3":"DJI"},"id":262,"arcs":[[2471,2472,2473,1185]]},{"type":"Polygon","properties":{"admin":"Dominica","name":"Dominica","postal":"DM","pop_est":72660,"iso_a2":"DM","iso_a3":"DMA"},"id":212,"arcs":[[80]]},{"type":"MultiPolygon","properties":{"admin":"Denmark","name":"Denmark","postal":"DK","pop_est":5500510,"iso_a2":"DK","iso_a3":"DNK"},"id":208,"arcs":[[[1534]],[[1542]],[[1546]],[[1543]],[[2474,-2471,2475,1254]]]},{"type":"Polygon","properties":{"admin":"Dominican Republic","name":"Dominican Rep.","postal":"DO","pop_est":9650054,"iso_a2":"DO","iso_a3":"DOM"},"id":214,"arcs":[[2476,98]]},{"type":"Polygon","properties":{"admin":"Algeria","name":"Algeria","postal":"DZ","pop_est":34178188,"iso_a2":"DZ","iso_a3":"DZA"},"id":12,"arcs":[[2477,2478,2479,2480,2481,2482,2483,1220]]},{"type":"MultiPolygon","properties":{"admin":"Ecuador","name":"Ecuador","postal":"EC","pop_est":14573101,"iso_a2":"EC","iso_a3":"ECU"},"id":218,"arcs":[[[173]],[[195]],[[188]],[[2484,673,-2455]]]},{"type":"Polygon","properties":{"admin":"Egypt","name":"Egypt","postal":"EG","pop_est":83082869,"iso_a2":"EG","iso_a3":"EGY"},"id":818,"arcs":[[2485,2486,1182,2487,2488,1223]]},{"type":"MultiPolygon","properties":{"admin":"Eritrea","name":"Eritrea","postal":"ER","pop_est":5647168,"iso_a2":"ER","iso_a3":"ERI"},"id":232,"arcs":[[[1184,-2474,2489,2490]]]},{"type":"MultiPolygon","properties":{"admin":"Spain","name":"Spain","postal":"E","pop_est":40525002,"iso_a2":"ES","iso_a3":"ESP"},"id":724,"arcs":[[[1443]],[[1445]],[[1446]],[[1438]],[[1460]],[[1465]],[[2491,-2493,2493,1245,2494,1247]]]},{"type":"MultiPolygon","properties":{"admin":"Estonia","name":"Estonia","postal":"EST","pop_est":1299371,"iso_a2":"EE","iso_a3":"EST"},"id":233,"arcs":[[[1609]],[[1554]],[[2495,2496,1397,2497,2498,2499,1268]]]},{"type":"Polygon","properties":{"admin":"Ethiopia","name":"Ethiopia","postal":"ET","pop_est":85237338,"iso_a2":"ET","iso_a3":"ETH"},"id":231,"arcs":[[-2473,2500,2501,2502,2503,2504,-2490]]},{"type":"Polygon","properties":{"admin":"Finland","name":"Finland","postal":"FIN","pop_est":5250275,"iso_a2":"FI","iso_a3":"FIN"},"id":246,"arcs":[[2505,1270,2506,2507]]},{"type":"MultiPolygon","properties":{"admin":"Fiji","name":"Fiji","postal":"FJ","pop_est":944720,"iso_a2":"FJ","iso_a3":"FJI"},"id":242,"arcs":[[[142]],[[146]]]},{"type":"MultiPolygon","properties":{"admin":"Falkland Islands","name":"Falkland Is.","postal":"FK","pop_est":3140,"iso_a2":"FK","iso_a3":"FLK"},"id":238,"arcs":[[[151]],[[150]]]},{"type":"MultiPolygon","properties":{"admin":"France","name":"France","postal":"F","pop_est":64057792,"iso_a2":"FR","iso_a3":"FRA"},"id":250,"arcs":[[[139]],[[-2324,2508,667]],[[87]],[[82]],[[84]],[[1459]],[[2509,-2467,-2391,2510,2511,1244,-2494,-2513,-2492,1248,-2286]]]},{"type":"Polygon","properties":{"admin":"Faroe Islands","name":"Faeroe Is.","postal":"FO","pop_est":48856,"iso_a2":"FO","iso_a3":"FRO"},"id":234,"arcs":[[1853]]},{"type":"Polygon","properties":{"admin":"Gabon","name":"Gabon","postal":"GA","pop_est":1514993,"iso_a2":"GA","iso_a3":"GAB"},"id":266,"arcs":[[-2452,1197,2513,-2432]]},{"type":"MultiPolygon","properties":{"admin":"United Kingdom","name":"United Kingdom","postal":"GB","pop_est":62262000,"iso_a2":"GB","iso_a3":"GBR"},"id":826,"arcs":[[[2205]],[[2514,2208]],[[1553]],[[1569]],[[1600]],[[1607]],[[2209]],[[1993]]]},{"type":"Polygon","properties":{"admin":"Georgia","name":"Georgia","postal":"GE","pop_est":4615807,"iso_a2":"GE","iso_a3":"GEO"},"id":268,"arcs":[[-2275,-2260,2515,1229,2516]]},{"type":"Polygon","properties":{"admin":"Ghana","name":"Ghana","postal":"GH","pop_est":23832495,"iso_a2":"GH","iso_a3":"GHA"},"id":288,"arcs":[[2517,1203,2518,1205,-2427,-2295]]},{"type":"Polygon","properties":{"admin":"Guinea","name":"Guinea","postal":"GN","pop_est":10057975,"iso_a2":"GN","iso_a3":"GIN"},"id":324,"arcs":[[2519,-2429,2520,2521,1209,2522,2523]]},{"type":"Polygon","properties":{"admin":"Gambia","name":"Gambia","postal":"GM","pop_est":1782893,"iso_a2":"GM","iso_a3":"GMB"},"id":270,"arcs":[[1212,2524]]},{"type":"Polygon","properties":{"admin":"Guinea Bissau","name":"Guinea-Bissau","postal":"GW","pop_est":1533964,"iso_a2":"GW","iso_a3":"GNB"},"id":624,"arcs":[[1210,2525,-2523]]},{"type":"MultiPolygon","properties":{"admin":"Equatorial Guinea","name":"Eq. Guinea","postal":"GQ","pop_est":650702,"iso_a2":"GQ","iso_a3":"GNQ"},"id":226,"arcs":[[[1198,-2433,-2514]],[[106]]]},{"type":"MultiPolygon","properties":{"admin":"Greece","name":"Greece","postal":"GR","pop_est":10737428,"iso_a2":"GR","iso_a3":"GRC"},"id":300,"arcs":[[[1451]],[[1447]],[[1454]],[[1455]],[[1462]],[[1463]],[[1461]],[[1464]],[[1235,-2243,2526,-2301,2527]]]},{"type":"MultiPolygon","properties":{"admin":"Greenland","name":"Greenland","postal":"GL","pop_est":57600,"iso_a2":"GL","iso_a3":"GRL"},"id":304,"arcs":[[[2017]],[[2018]],[[2003]],[[2025]],[[1622]],[[1709]],[[307]],[[312]],[[2113]]]},{"type":"Polygon","properties":{"admin":"Guatemala","name":"Guatemala","postal":"GT","pop_est":13276517,"iso_a2":"GT","iso_a3":"GTM"},"id":320,"arcs":[[-2314,658,2528,2529,680,2530]]},{"type":"Polygon","properties":{"admin":"Guam","name":"Guam","postal":"GU","pop_est":178430,"iso_a2":"GU","iso_a3":"GUM"},"id":316,"arcs":[[88]]},{"type":"Polygon","properties":{"admin":"Guyana","name":"Guyana","postal":"GY","pop_est":772298,"iso_a2":"GY","iso_a3":"GUY"},"id":328,"arcs":[[2531,-2335,2532,665]]},{"type":"Polygon","properties":{"admin":"Hong Kong S.A.R.","name":"Hong Kong","postal":"HK","pop_est":7061200,"iso_a2":"HK","iso_a3":"HKG"},"id":344,"arcs":[[-2400,2533,1154]]},{"type":"Polygon","properties":{"admin":"Honduras","name":"Honduras","postal":"HN","pop_est":7792854,"iso_a2":"HN","iso_a3":"HND"},"id":340,"arcs":[[2534,678,2535,-2529,659]]},{"type":"MultiPolygon","properties":{"admin":"Croatia","name":"Croatia","postal":"HR","pop_est":4489409,"iso_a2":"HR","iso_a3":"HRV"},"id":191,"arcs":[[[-2307,2536,1238]],[[2537,-2308,1240,2538,2539]]]},{"type":"MultiPolygon","properties":{"admin":"Haiti","name":"Haiti","postal":"HT","pop_est":9035536,"iso_a2":"HT","iso_a3":"HTI"},"id":332,"arcs":[[[-2477,99]]]},{"type":"Polygon","properties":{"admin":"Hungary","name":"Hungary","postal":"HU","pop_est":9905596,"iso_a2":"HU","iso_a3":"HUN"},"id":348,"arcs":[[2540,2541,2542,-2540,2543,-2263,2544]]},{"type":"MultiPolygon","properties":{"admin":"Indonesia","name":"Indonesia","postal":"INDO","pop_est":240271522,"iso_a2":"ID","iso_a3":"IDN"},"id":360,"arcs":[[[245]],[[203]],[[2545,207,2546,2547,2548,210,2549,2550]],[[214]],[[219]],[[218]],[[223]],[[220]],[[216]],[[213]],[[217]],[[225]],[[226]],[[228]],[[153]],[[155]],[[227]],[[164]],[[167]],[[169]],[[157]],[[171]],[[158]],[[162]],[[159]],[[160]],[[175]],[[176]],[[177]],[[178]],[[179]],[[182]],[[181]],[[183]],[[180]],[[185]],[[184]],[[187]],[[190]],[[189]],[[192]],[[191]],[[2551,2552,2553,2554,232]],[[197]],[[196]],[[198]],[[248]],[[253]],[[254]],[[255]],[[256]],[[252]],[[257]],[[186]],[[102]],[[259]],[[103]],[[104]],[[107]],[[2555,261,2556]],[[105]],[[258]]]},{"type":"Polygon","properties":{"admin":"Isle of Man","name":"Isle of Man","postal":"IM","pop_est":76512,"iso_a2":"IM","iso_a3":"IMN"},"id":833,"arcs":[[2200]]},{"type":"MultiPolygon","properties":{"admin":"India","name":"India","postal":"IND","pop_est":1166079220,"iso_a2":"IN","iso_a3":"IND"},"id":356,"arcs":[[[111]],[[52]],[[92]],[[-2412,2557,-2410,2558,-2408,2559,-2406,-2339,-2405,2560,-2299,1165,2561,2562,2563,-2417,2564,-2415,2565,-2413]]]},{"type":"Polygon","properties":{"admin":"Ireland","name":"Ireland","postal":"IRL","pop_est":4203200,"iso_a2":"IE","iso_a3":"IRL"},"id":372,"arcs":[[2207,-2515]]},{"type":"MultiPolygon","properties":{"admin":"Iran","name":"Iran","postal":"IRN","pop_est":66429284,"iso_a2":"IR","iso_a3":"IRN"},"id":364,"arcs":[[[1435]],[[-2257,-2274,1275,2566,-2232,2567,2568,2569,1168,2570,2571,-2272]]]},{"type":"Polygon","properties":{"admin":"Iraq","name":"Iraq","postal":"IRQ","pop_est":31129225,"iso_a2":"IQ","iso_a3":"IRQ"},"id":368,"arcs":[[-2571,1169,2572,2573,2574,2575,2576]]},{"type":"Polygon","properties":{"admin":"Iceland","name":"Iceland","postal":"IS","pop_est":306694,"iso_a2":"IS","iso_a3":"ISL"},"id":352,"arcs":[[1852]]},{"type":"Polygon","properties":{"admin":"Israel","name":"Israel","postal":"IS","pop_est":7233701,"iso_a2":"IL","iso_a3":"ISR"},"id":376,"arcs":[[2577,2578,2579,2580,2581,1181,-2487,2582,1225,2583,2584]]},{"type":"MultiPolygon","properties":{"admin":"Italy","name":"Italy","postal":"I","pop_est":58126212,"iso_a2":"IT","iso_a3":"ITA"},"id":380,"arcs":[[[1452]],[[1466]],[[2585,1242,-2511,-2390,-2265]]]},{"type":"Polygon","properties":{"admin":"Jamaica","name":"Jamaica","postal":"J","pop_est":2825928,"iso_a2":"JM","iso_a3":"JAM"},"id":388,"arcs":[[97]]},{"type":"Polygon","properties":{"admin":"Jordan","name":"Jordan","postal":"J","pop_est":6342948,"iso_a2":"JO","iso_a3":"JOR"},"id":400,"arcs":[[2586,1180,-2582,-2581,-2580,2587,2588,-2578,2589,-2575]]},{"type":"MultiPolygon","properties":{"admin":"Japan","name":"Japan","postal":"J","pop_est":127078679,"iso_a2":"JP","iso_a3":"JPN"},"id":392,"arcs":[[[1432]],[[1444]],[[1467]],[[1470]],[[1450]],[[1453]],[[1471]],[[1499]]]},{"type":"Polygon","properties":{"admin":"Siachen Glacier","name":"Siachen Glacier","postal":"SG","pop_est":6000,"iso_a2":"-99","iso_a3":"-99"},"id":-99,"arcs":[[-2563,2590,-2419]]},{"type":"MultiPolygon","properties":{"admin":"Kazakhstan","name":"Kazakhstan","postal":"KZ","pop_est":15399437,"iso_a2":"KZ","iso_a3":"KAZ"},"id":398,"arcs":[[[2591,2592,2593,1372]],[[-2423,2594,2595,2596,1376,2597,2598,2599,1277,2600]]]},{"type":"Polygon","properties":{"admin":"Kenya","name":"Kenya","postal":"KE","pop_est":39002772,"iso_a2":"KE","iso_a3":"KEN"},"id":404,"arcs":[[2601,1188,2602,2603,1299,2604,2605,2606,-2503]]},{"type":"Polygon","properties":{"admin":"Kyrgyzstan","name":"Kyrgyzstan","postal":"KG","pop_est":5431747,"iso_a2":"KG","iso_a3":"KGZ"},"id":417,"arcs":[[-2422,2607,2608,-2595]]},{"type":"Polygon","properties":{"admin":"Cambodia","name":"Cambodia","postal":"KH","pop_est":14494293,"iso_a2":"KH","iso_a3":"KHM"},"id":116,"arcs":[[1159,2609,2610,2611]]},{"type":"MultiPolygon","properties":{"admin":"South Korea","name":"Korea","postal":"KR","pop_est":48508972,"iso_a2":"KR","iso_a3":"KOR"},"id":410,"arcs":[[[1469]],[[1150,2612]]]},{"type":"Polygon","properties":{"admin":"Kosovo","name":"Kosovo","postal":"KO","pop_est":1804838,"iso_a2":"-99","iso_a3":"-99"},"id":-99,"arcs":[[2613,-2241,2614,2615]]},{"type":"MultiPolygon","properties":{"admin":"Kuwait","name":"Kuwait","postal":"KW","pop_est":2691158,"iso_a2":"KW","iso_a3":"KWT"},"id":414,"arcs":[[[1442]],[[2616,-2573,1170]]]},{"type":"Polygon","properties":{"admin":"Laos","name":"Lao PDR","postal":"LA","pop_est":6834942,"iso_a2":"LA","iso_a3":"LAO"},"id":418,"arcs":[[2617,-2611,2618,2619,-2403]]},{"type":"Polygon","properties":{"admin":"Lebanon","name":"Lebanon","postal":"LB","pop_est":4017095,"iso_a2":"LB","iso_a3":"LBN"},"id":422,"arcs":[[-2584,1226,2620]]},{"type":"Polygon","properties":{"admin":"Liberia","name":"Liberia","postal":"LR","pop_est":3441790,"iso_a2":"LR","iso_a3":"LBR"},"id":430,"arcs":[[-2428,1207,2621,-2521]]},{"type":"Polygon","properties":{"admin":"Libya","name":"Libya","postal":"LY","pop_est":6310434,"iso_a2":"LY","iso_a3":"LBY"},"id":434,"arcs":[[-2489,2622,2623,2624,-2479,2625,1222]]},{"type":"Polygon","properties":{"admin":"Sri Lanka","name":"Sri Lanka","postal":"LK","pop_est":21324791,"iso_a2":"LK","iso_a3":"LKA"},"id":144,"arcs":[[108]]},{"type":"Polygon","properties":{"admin":"Lesotho","name":"Lesotho","postal":"LS","pop_est":2130819,"iso_a2":"LS","iso_a3":"LSO"},"id":426,"arcs":[[2626]]},{"type":"Polygon","properties":{"admin":"Lithuania","name":"Lithuania","postal":"LT","pop_est":3555179,"iso_a2":"LT","iso_a3":"LTU"},"id":440,"arcs":[[-2312,2627,2628,2629,1266,2630]]},{"type":"Polygon","properties":{"admin":"Luxembourg","name":"Luxembourg","postal":"L","pop_est":491775,"iso_a2":"LU","iso_a3":"LUX"},"id":442,"arcs":[[-2468,-2510,-2285]]},{"type":"Polygon","properties":{"admin":"Latvia","name":"Latvia","postal":"LV","pop_est":2231503,"iso_a2":"LV","iso_a3":"LVA"},"id":428,"arcs":[[2631,-2313,-2631,1267,-2500]]},{"type":"Polygon","properties":{"admin":"Morocco","name":"Morocco","postal":"MA","pop_est":34859364,"iso_a2":"MA","iso_a3":"MAR"},"id":504,"arcs":[[-2484,2632,2633,1217,2634,1219]]},{"type":"Polygon","properties":{"admin":"Moldova","name":"Moldova","postal":"MD","pop_est":4320748,"iso_a2":"MD","iso_a3":"MDA"},"id":498,"arcs":[[2635,2636]]},{"type":"Polygon","properties":{"admin":"Madagascar","name":"Madagascar","postal":"MG","pop_est":20653556,"iso_a2":"MG","iso_a3":"MDG"},"id":450,"arcs":[[235]]},{"type":"MultiPolygon","properties":{"admin":"Mexico","name":"Mexico","postal":"MX","pop_est":111211789,"iso_a2":"MX","iso_a3":"MEX"},"id":484,"arcs":[[[1439]],[[1440]],[[2637,2638,2639,2640,2641,2642,2643,2644,2645,2646,2647,2648,2649,2650,2651,2652,2653,2654,656,-2315,-2531,681,2655,2656,2657]]]},{"type":"Polygon","properties":{"admin":"Macedonia","name":"Macedonia","postal":"MK","pop_est":2066718,"iso_a2":"MK","iso_a3":"MKD"},"id":807,"arcs":[[-2302,-2527,-2242,-2614,2658]]},{"type":"Polygon","properties":{"admin":"Mali","name":"Mali","postal":"ML","pop_est":12666987,"iso_a2":"ML","iso_a3":"MLI"},"id":466,"arcs":[[2659,-2297,-2430,-2520,2660,2661,-2481]]},{"type":"MultiPolygon","properties":{"admin":"Myanmar","name":"Myanmar","postal":"MM","pop_est":48137741,"iso_a2":"MM","iso_a3":"MMR"},"id":104,"arcs":[[[-2620,2662,1163,-2298,-2561,-2404]]]},{"type":"Polygon","properties":{"admin":"Montenegro","name":"Montenegro","postal":"ME","pop_est":672180,"iso_a2":"ME","iso_a3":"MNE"},"id":499,"arcs":[[2663,-2615,-2247,2664,1383,2665,-2244,1237,-2537,-2306]]},{"type":"Polygon","properties":{"admin":"Mongolia","name":"Mongolia","postal":"MN","pop_est":3041142,"iso_a2":"MN","iso_a3":"MNG"},"id":496,"arcs":[[-2425,2666]]},{"type":"Polygon","properties":{"admin":"Mozambique","name":"Mozambique","postal":"MZ","pop_est":21669278,"iso_a2":"MZ","iso_a3":"MOZ"},"id":508,"arcs":[[2667,2668,2669,2670,2671,2672,2673,1284,2674,2675,1190]]},{"type":"Polygon","properties":{"admin":"Mauritania","name":"Mauritania","postal":"MR","pop_est":3129486,"iso_a2":"MR","iso_a3":"MRT"},"id":478,"arcs":[[2676,1214,2677,-2482,-2662]]},{"type":"Polygon","properties":{"admin":"Mauritius","name":"Mauritius","postal":"MU","pop_est":1284264,"iso_a2":"MU","iso_a3":"MUS"},"id":480,"arcs":[[141]]},{"type":"Polygon","properties":{"admin":"Malawi","name":"Malawi","postal":"MW","pop_est":14268711,"iso_a2":"MW","iso_a3":"MWI"},"id":454,"arcs":[[1287,2678,-2673,2679,2680]]},{"type":"MultiPolygon","properties":{"admin":"Malaysia","name":"Malaysia","postal":"MY","pop_est":25715819,"iso_a2":"MY","iso_a3":"MYS"},"id":458,"arcs":[[[1161,2681]],[[2682,-2557,262,2683,-2337,-2336,266]]]},{"type":"Polygon","properties":{"admin":"Namibia","name":"Namibia","postal":"NA","pop_est":2108665,"iso_a2":"NA","iso_a3":"NAM"},"id":516,"arcs":[[2684,-2343,2685,1192,-2237]]},{"type":"MultiPolygon","properties":{"admin":"New Caledonia","name":"New Caledonia","postal":"NC","pop_est":227436,"iso_a2":"NC","iso_a3":"NCL"},"id":540,"arcs":[[[140]],[[138]]]},{"type":"Polygon","properties":{"admin":"Niger","name":"Niger","postal":"NE","pop_est":15306252,"iso_a2":"NE","iso_a3":"NER"},"id":562,"arcs":[[2686,2687,-2292,-2293,-2660,-2480,-2625]]},{"type":"Polygon","properties":{"admin":"Nigeria","name":"Nigeria","postal":"NG","pop_est":149229090,"iso_a2":"NG","iso_a3":"NGA"},"id":566,"arcs":[[2688,-2434,1200,-2289,-2688]]},{"type":"Polygon","properties":{"admin":"Nicaragua","name":"Nicaragua","postal":"NI","pop_est":5891199,"iso_a2":"NI","iso_a3":"NIC"},"id":558,"arcs":[[660,-2458,677,-2535]]},{"type":"MultiPolygon","properties":{"admin":"Netherlands","name":"Netherlands","postal":"NL","pop_est":16715999,"iso_a2":"NL","iso_a3":"NLD"},"id":528,"arcs":[[[-2287,1250]],[[2224]],[[1251,-2469,-2283,2689,-2288],[1388]]]},{"type":"MultiPolygon","properties":{"admin":"Norway","name":"Norway","postal":"N","pop_est":4676305,"iso_a2":"NO","iso_a3":"NOR"},"id":578,"arcs":[[[1810]],[[1809]],[[2014]],[[2019]],[[2002]],[[2690,-2508,2691,1143]],[[2021]],[[1629]],[[1721]],[[313]]]},{"type":"Polygon","properties":{"admin":"Nepal","name":"Nepal","postal":"NP","pop_est":28563377,"iso_a2":"NP","iso_a3":"NPL"},"id":524,"arcs":[[-2560,-2407]]},{"type":"MultiPolygon","properties":{"admin":"New Zealand","name":"New Zealand","postal":"NZ","pop_est":4213418,"iso_a2":"NZ","iso_a3":"NZL"},"id":554,"arcs":[[[126]],[[45]],[[130]],[[133]]]},{"type":"MultiPolygon","properties":{"admin":"Oman","name":"Oman","postal":"OM","pop_est":3418085,"iso_a2":"OM","iso_a3":"OMN"},"id":512,"arcs":[[[78]],[[1177,2692,2693,-2248]],[[-2250,1175]]]},{"type":"Polygon","properties":{"admin":"Pakistan","name":"Pakistan","postal":"PK","pop_est":176242949,"iso_a2":"PK","iso_a3":"PAK"},"id":586,"arcs":[[-2591,-2562,1166,2694,-2569,-2230,-2420]]},{"type":"MultiPolygon","properties":{"admin":"Panama","name":"Panama","postal":"PA","pop_est":3360474,"iso_a2":"PA","iso_a3":"PAN"},"id":591,"arcs":[[[56]],[[-2456,675,-2457,662]]]},{"type":"Polygon","properties":{"admin":"Peru","name":"Peru","postal":"PE","pop_est":29546963,"iso_a2":"PE","iso_a3":"PER"},"id":604,"arcs":[[-2332,-2321,2695,966,2696,962,2697,-2318,-2395,672,-2485,-2454]]},{"type":"MultiPolygon","properties":{"admin":"Philippines","name":"Philippines","postal":"PH","pop_est":97976603,"iso_a2":"PH","iso_a3":"PHL"},"id":608,"arcs":[[[110]],[[109]],[[112]],[[59]],[[49]],[[60]],[[61]],[[58]],[[53]],[[54]],[[79]],[[55]],[[89]],[[90]],[[93]],[[94]],[[95]],[[96]]]},{"type":"MultiPolygon","properties":{"admin":"Papua New Guinea","name":"Papua New Guinea","postal":"PG","pop_est":6057263,"iso_a2":"PG","iso_a3":"PNG"},"id":598,"arcs":[[[244]],[[202]],[[200]],[[201]],[[204]],[[166]],[[163]],[[165]],[[170]],[[2698,-2554,2699,2700,230]],[[161]],[[172]],[[174]]]},{"type":"Polygon","properties":{"admin":"Poland","name":"Poland","postal":"PL","pop_est":38482919,"iso_a2":"PL","iso_a3":"POL"},"id":616,"arcs":[[2701,-2628,-2311,2702,2703,-2460,-2465,2704,1258,2705,-2707,1261]]},{"type":"Polygon","properties":{"admin":"Puerto Rico","name":"Puerto Rico","postal":"PR","pop_est":3971020,"iso_a2":"PR","iso_a3":"PRI"},"id":630,"arcs":[[100]]},{"type":"Polygon","properties":{"admin":"North Korea","name":"Dem. Rep. Korea","postal":"KP","pop_est":22665345,"iso_a2":"KP","iso_a3":"PRK"},"id":408,"arcs":[[2707,1149,-2613,1151,-2399]]},{"type":"MultiPolygon","properties":{"admin":"Portugal","name":"Portugal","postal":"P","pop_est":10707924,"iso_a2":"PT","iso_a3":"PRT"},"id":620,"arcs":[[[1468]],[[1246,-2495]]]},{"type":"Polygon","properties":{"admin":"Paraguay","name":"Paraguay","postal":"PY","pop_est":6995655,"iso_a2":"PY","iso_a3":"PRY"},"id":600,"arcs":[[-2331,-2330,-2329,-2252,-2316]]},{"type":"MultiPolygon","properties":{"admin":"Palestine","name":"Palestine","postal":"PAL","pop_est":4119083,"iso_a2":"PS","iso_a3":"PSE"},"id":275,"arcs":[[[-2588,-2579,-2589]]]},{"type":"MultiPolygon","properties":{"admin":"French Polynesia","name":"Fr. Polynesia","postal":"PF","pop_est":287032,"iso_a2":"PF","iso_a3":"PYF"},"id":258,"arcs":[[[143]]]},{"type":"Polygon","properties":{"admin":"Qatar","name":"Qatar","postal":"QA","pop_est":833285,"iso_a2":"QA","iso_a3":"QAT"},"id":634,"arcs":[[2708,1172]]},{"type":"Polygon","properties":{"admin":"Romania","name":"Romania","postal":"RO","pop_est":22215421,"iso_a2":"RO","iso_a3":"ROU"},"id":642,"arcs":[[2709,1232,-2304,2710,-2542,2711,-2636]]},{"type":"MultiPolygon","properties":{"admin":"Russia","name":"Russia","postal":"RUS","pop_est":140041247,"iso_a2":"RU","iso_a3":"RUS"},"id":643,"arcs":[[[1498]],[[1502]],[[1479]],[[2141]],[[2198]],[[1535]],[[2712,-2629,-2702,1262,2713,1264]],[[1541]],[[1608]],[[2714,1867]],[[1808]],[[2004]],[[2001]],[[2020]],[[2022]],[[1996]],[[2715,1998,2716,2000]],[[2024]],[[2023]],[[2114]],[[2139]],[[2119]],[[1610]],[[1611]],[[1612]],[[1666]],[[1665]],[[2140]],[[2717,1148,-2708,-2398,2718,1354,2719,-2426,-2667,-2424,-2601,1273,-2276,-2517,1230,2720,-2309,-2632,-2499,2721,1399,2722,-2496,1269,-2506,-2691,1144,2723,1146]],[[272]],[[310]],[[273]],[[311]],[[316]],[[317]],[[318]],[[314]],[[319]],[[321]],[[320]],[[315]],[[322]]]},{"type":"Polygon","properties":{"admin":"Rwanda","name":"Rwanda","postal":"RW","pop_est":10473282,"iso_a2":"RW","iso_a3":"RWA"},"id":646,"arcs":[[2724,-2281,-2447,2725,1292,2726,-2444,2727]]},{"type":"Polygon","properties":{"admin":"Western Sahara","name":"W. Sahara","postal":"WS","pop_est":-99,"iso_a2":"EH","iso_a3":"ESH"},"id":732,"arcs":[[-2483,-2678,1215,2728,-2633]]},{"type":"MultiPolygon","properties":{"admin":"Saudi Arabia","name":"Saudi Arabia","postal":"SA","pop_est":28686633,"iso_a2":"SA","iso_a3":"SAU"},"id":682,"arcs":[[[-2617,1171,-2709,1173,-2249,-2694,2729,1179,-2587,-2574]]]},{"type":"Polygon","properties":{"admin":"Sudan","name":"Sudan","postal":"SD","pop_est":25946220,"iso_a2":"SD","iso_a3":"SDN"},"id":729,"arcs":[[1183,-2491,-2505,2730,-2344,2731,-2623,-2488]]},{"type":"Polygon","properties":{"admin":"South Sudan","name":"S. Sudan","postal":"SS","pop_est":10625176,"iso_a2":"SS","iso_a3":"SSD"},"id":728,"arcs":[[-2504,-2607,2732,-2437,-2345,-2731]]},{"type":"Polygon","properties":{"admin":"Senegal","name":"Senegal","postal":"SN","pop_est":13711597,"iso_a2":"SN","iso_a3":"SEN"},"id":686,"arcs":[[-2661,-2524,-2526,1211,-2525,1213,-2677]]},{"type":"Polygon","properties":{"admin":"South Georgia and South Sandwich Islands","name":"S. Geo. and S. Sandw. Is.","postal":"GS","pop_est":30,"iso_a2":"GS","iso_a3":"SGS"},"id":239,"arcs":[[26]]},{"type":"MultiPolygon","properties":{"admin":"Solomon Islands","name":"Solomon Is.","postal":"SB","pop_est":595613,"iso_a2":"SB","iso_a3":"SLB"},"id":90,"arcs":[[[241]],[[246]],[[199]],[[215]],[[205]],[[221]],[[224]],[[222]],[[152]]]},{"type":"MultiPolygon","properties":{"admin":"Sierra Leone","name":"Sierra Leone","postal":"SL","pop_est":6440053,"iso_a2":"SL","iso_a3":"SLE"},"id":694,"arcs":[[[57]],[[-2622,1208,-2522]]]},{"type":"Polygon","properties":{"admin":"El Salvador","name":"El Salvador","postal":"SV","pop_est":7185218,"iso_a2":"SV","iso_a3":"SLV"},"id":222,"arcs":[[-2536,679,-2530]]},{"type":"Polygon","properties":{"admin":"Somaliland","name":"Somaliland","postal":"SL","pop_est":3500000,"iso_a2":"-99","iso_a3":"-99"},"id":-99,"arcs":[[2733,-2501,-2472,1186]]},{"type":"Polygon","properties":{"admin":"Somalia","name":"Somalia","postal":"SO","pop_est":9832017,"iso_a2":"SO","iso_a3":"SOM"},"id":706,"arcs":[[-2602,-2502,-2734,1187]]},{"type":"Polygon","properties":{"admin":"Republic of Serbia","name":"Serbia","postal":"RS","pop_est":7379339,"iso_a2":"RS","iso_a3":"SRB"},"id":688,"arcs":[[-2711,-2303,-2659,-2616,-2664,-2305,-2538,-2543]]},{"type":"Polygon","properties":{"admin":"Sao Tome and Principe","name":"São Tomé and Principe","postal":"ST","pop_est":212679,"iso_a2":"ST","iso_a3":"STP"},"id":678,"arcs":[[250]]},{"type":"Polygon","properties":{"admin":"Suriname","name":"Suriname","postal":"SR","pop_est":481267,"iso_a2":"SR","iso_a3":"SUR"},"id":740,"arcs":[[-2509,-2323,-2532,666]]},{"type":"Polygon","properties":{"admin":"Slovakia","name":"Slovakia","postal":"SK","pop_est":5463046,"iso_a2":"SK","iso_a3":"SVK"},"id":703,"arcs":[[2734,-2545,-2262,-2461,-2704]]},{"type":"Polygon","properties":{"admin":"Slovenia","name":"Slovenia","postal":"SLO","pop_est":2005692,"iso_a2":"SI","iso_a3":"SVN"},"id":705,"arcs":[[-2539,1241,-2586,-2264,-2544]]},{"type":"MultiPolygon","properties":{"admin":"Sweden","name":"Sweden","postal":"S","pop_est":9059651,"iso_a2":"SE","iso_a3":"SWE"},"id":752,"arcs":[[[1588]],[[1576]],[[1271,2735,1142,-2692,-2507]]]},{"type":"Polygon","properties":{"admin":"Swaziland","name":"Swaziland","postal":"SW","pop_est":1123913,"iso_a2":"SZ","iso_a3":"SWZ"},"id":748,"arcs":[[-2669,2736]]},{"type":"Polygon","properties":{"admin":"Syria","name":"Syria","postal":"SYR","pop_est":20178485,"iso_a2":"SY","iso_a3":"SYR"},"id":760,"arcs":[[-2576,-2590,-2585,-2621,1227,2737]]},{"type":"Polygon","properties":{"admin":"Chad","name":"Chad","postal":"TD","pop_est":10329208,"iso_a2":"TD","iso_a3":"TCD"},"id":148,"arcs":[[-2732,-2349,-2436,-2435,-2689,-2687,-2624]]},{"type":"Polygon","properties":{"admin":"Togo","name":"Togo","postal":"TG","pop_est":6019877,"iso_a2":"TG","iso_a3":"TGO"},"id":768,"arcs":[[-2290,1202,-2518,-2294]]},{"type":"MultiPolygon","properties":{"admin":"Thailand","name":"Thailand","postal":"TH","pop_est":65905410,"iso_a2":"TH","iso_a3":"THA"},"id":764,"arcs":[[[-2619,-2610,1160,-2682,1162,-2663]]]},{"type":"Polygon","properties":{"admin":"Tajikistan","name":"Tajikistan","postal":"TJ","pop_est":7349145,"iso_a2":"TJ","iso_a3":"TJK"},"id":762,"arcs":[[-2608,-2421,-2235,2738]]},{"type":"Polygon","properties":{"admin":"Turkmenistan","name":"Turkmenistan","postal":"TM","pop_est":4884887,"iso_a2":"TM","iso_a3":"TKM"},"id":795,"arcs":[[-2233,-2567,1276,-2600,2739,2740,1382,2741,2742]]},{"type":"MultiPolygon","properties":{"admin":"East Timor","name":"Timor-Leste","postal":"TL","pop_est":1131612,"iso_a2":"TL","iso_a3":"TLS"},"id":626,"arcs":[[[2743,2744,-2548]],[[2745,-2551,2746,212]]]},{"type":"Polygon","properties":{"admin":"Trinidad and Tobago","name":"Trinidad and Tobago","postal":"TT","pop_est":1310000,"iso_a2":"TT","iso_a3":"TTO"},"id":780,"arcs":[[48]]},{"type":"MultiPolygon","properties":{"admin":"Tunisia","name":"Tunisia","postal":"TN","pop_est":10486339,"iso_a2":"TN","iso_a3":"TUN"},"id":788,"arcs":[[[-2626,-2478,1221]]]},{"type":"MultiPolygon","properties":{"admin":"Turkey","name":"Turkey","postal":"TR","pop_est":76805524,"iso_a2":"TR","iso_a3":"TUR"},"id":792,"arcs":[[[-2516,-2259,-2273,-2572,-2577,-2738,1228]],[[1234,-2528,-2300]]]},{"type":"Polygon","properties":{"admin":"Taiwan","name":"Taiwan","postal":"TW","pop_est":22974347,"iso_a2":"TW","iso_a3":"TWN"},"id":158,"arcs":[[1436]]},{"type":"MultiPolygon","properties":{"admin":"United Republic of Tanzania","name":"Tanzania","postal":"TZ","pop_est":41048532,"iso_a2":"TZ","iso_a3":"TZA"},"id":834,"arcs":[[[154]],[[168]],[[2210]],[[2211]],[[2747,1297,2748,-2603,1189,-2676,2749,1286,-2681,2750,2751,1307,2752,-2277,-2725,2753]]]},{"type":"MultiPolygon","properties":{"admin":"Uganda","name":"Uganda","postal":"UG","pop_est":32369558,"iso_a2":"UG","iso_a3":"UGA"},"id":800,"arcs":[[[2754,1295,2755,-2754,-2728,-2443,2756,1302,2757,-2440,1317,2758,-2438,-2733,-2606]]]},{"type":"Polygon","properties":{"admin":"Ukraine","name":"Ukraine","postal":"UA","pop_est":45700395,"iso_a2":"UA","iso_a3":"UKR"},"id":804,"arcs":[[1231,-2710,-2637,-2712,-2541,-2735,-2703,-2310,-2721]]},{"type":"Polygon","properties":{"admin":"Uruguay","name":"Uruguay","postal":"UY","pop_est":3494382,"iso_a2":"UY","iso_a3":"URY"},"id":858,"arcs":[[2759,953,2760,-2325,669,-2254,-2328]]},{"type":"MultiPolygon","properties":{"admin":"United States of America","name":"United States","postal":"US","pop_est":313973000,"iso_a2":"US","iso_a3":"USA"},"id":840,"arcs":[[[73,74,75,76,77]],[[62,63]],[[64,65,66]],[[68,69,70,71,72]],[[323,324]],[[1456,1457,1458]],[[2212,2213,2214]],[[2220,2221,2222,2223,2219]],[[-2374,-2373,-2372,-2371,-2370,-2369,2761,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1030,1031,1032,1033,1034,1035,1036,1037,1038,-2367,1021,1022,1023,1024,1025,1026,-2366,2762,1009,1010,1011,1012,1013,1014,2763,-2363,-2362,-2360,-2359,-2358,-2357,-2356,-2355,-2354,2764,984,2765,-2351,-2350,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,-2655,-2654,-2653,-2652,-2651,-2650,-2649,-2648,-2647,-2646,-2645,-2644,-2643,-2642,-2641,-2640,-2639,-2638,-2658,-2657,-2656,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,-2377,-2376,-2375]],[[1506]],[[1503,1504,1505]],[[1507,1508]],[[2199]],[[1509,1510,1511,1512]],[[1521,1522,1523]],[[1536,1537,1538,1539,1540]],[[1544,1545]],[[1547,1548,1549,1550,1551,1552]],[[1555,1556,1557,1558,1559,1560,1561,1562]],[[1566,1567,1568]],[[1570,1571,1572]],[[1595,1596,1597,1598,1599]],[[1589,1590,1591,1592,1593,1594]],[[1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587]],[[1604,1605,1606]],[[1601,1602,1603]],[[1573,1574,1575]],[[1797,1798,1799]],[[1792,1793,1794,1795,1796]],[[1824,1825,1826,1827,1828,1829,1830]],[[867,868,869,870,871,872,-2388,-2387,-2386,-2385,-2384,-2383,-2382,-2381,-2380,-2379,-2378,745,746,2766,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866]]]},{"type":"MultiPolygon","properties":{"admin":"Uzbekistan","name":"Uzbekistan","postal":"UZ","pop_est":27606007,"iso_a2":"UZ","iso_a3":"UZB"},"id":860,"arcs":[[[2767,-2593,2768,1374,2769,-2596,-2609,-2739,-2234,-2743,2770,1380,2771,-2740,-2599,2772,1370]]]},{"type":"MultiPolygon","properties":{"admin":"Venezuela","name":"Venezuela","postal":"VE","pop_est":26814843,"iso_a2":"VE","iso_a3":"VEN"},"id":862,"arcs":[[[51]],[[-2533,-2334,-2453,664]]]},{"type":"MultiPolygon","properties":{"admin":"Vietnam","name":"Vietnam","postal":"VN","pop_est":86967524,"iso_a2":"VN","iso_a3":"VNM"},"id":704,"arcs":[[[50]],[[1158,-2612,-2618,-2402]]]},{"type":"MultiPolygon","properties":{"admin":"Vanuatu","name":"Vanuatu","postal":"VU","pop_est":218519,"iso_a2":"VU","iso_a3":"VUT"},"id":548,"arcs":[[[137]],[[144]],[[147]],[[145]],[[236]],[[237]]]},{"type":"MultiPolygon","properties":{"admin":"Samoa","name":"Samoa","postal":"WS","pop_est":219998,"iso_a2":"WS","iso_a3":"WSM"},"id":882,"arcs":[[[46]],[[47]]]},{"type":"MultiPolygon","properties":{"admin":"Yemen","name":"Yemen","postal":"YE","pop_est":23822783,"iso_a2":"YE","iso_a3":"YEM"},"id":887,"arcs":[[[91]],[[1178,-2730,-2693]]]},{"type":"Polygon","properties":{"admin":"South Africa","name":"South Africa","postal":"ZA","pop_est":49052489,"iso_a2":"ZA","iso_a3":"ZAF"},"id":710,"arcs":[[-2670,-2737,-2668,1191,-2686,-2342,2773],[-2627]]},{"type":"Polygon","properties":{"admin":"Zambia","name":"Zambia","postal":"ZM","pop_est":11862740,"iso_a2":"ZM","iso_a3":"ZMB"},"id":894,"arcs":[[2774,-2751,-2680,-2672,2775,2776,1279,2777,-2685,-2236,-2450,2778,1313]]},{"type":"Polygon","properties":{"admin":"Zimbabwe","name":"Zimbabwe","postal":"ZW","pop_est":12619600,"iso_a2":"ZW","iso_a3":"ZWE"},"id":716,"arcs":[[-2776,-2671,-2774,-2341,-2778,1280,2779,1282]]}]},"states":{"type":"GeometryCollection","geometries":[{"type":"Polygon","properties":{"iso_a2":"CA","name":"Alberta","postal":"AB","admin":"Canada"},"arcs":[[2780,2781,2782,2783]]},{"type":"MultiPolygon","properties":{"iso_a2":"CA","name":"British Columbia","postal":"BC","admin":"Canada"},"arcs":[[[2784,2785,2786,2787,2788,2789,2790,2791,2792,2793,2794,2795,2796,2797,2798]],[[2799,2800,2801,2802]],[[2803,2804,2805,2806]],[[2807,2808,2809]],[[2810,2811,2812,2813,2814,2815,2816]],[[2817,2818,2819,2820,2821,2822,2823,2824,2825,2826,2827,-2782,2828,2829,2830,2831,2832,2833,2834,2835,2836,2837,2838,2839,2840,2841,2842,2843,2844,2845,2846,2847,2848,2849,736,2850,2851,2852,2853,2854,2855,2856,2857]]]},{"type":"Polygon","properties":{"iso_a2":"CA","name":"Manitoba","postal":"MB","admin":"Canada"},"arcs":[[2858,2859,2860,2861,2862,2863,2864,2865,2866,2867,2868,2869,2870,2871,2872,2873,2874]]},{"type":"Polygon","properties":{"iso_a2":"CA","name":"New Brunswick","postal":"NB","admin":"Canada"},"arcs":[[2875,2876,2877,2878,2879,2880,2881,2882,2883,2884,2885,2886,2887,2888,2889,2890,2891]]},{"type":"MultiPolygon","properties":{"iso_a2":"CA","name":"Newfoundland and Labrador","postal":"NL","admin":"Canada"},"arcs":[[[2892,2893,2894,2895,2896,2897,2898,2899,2900,2901,2902,2903,2904,2905,2906,2907,2908,2909,2910,2911,2912,2913,2914,2915,2916,2917,2918,2919,2920,2921,2922,2923,2924,2925,2926,2927,2928,2929,2930,2931]],[[2932,2933,2934,2935,2936,2937,2938,2939,2940,2941,2942,2943,2944,2945,2946,2947,2948,2949,2950,2951,2952,2953,2954,2955,2956,2957,2958,2959,2960,2961,2962,2963,2964,2965,2966,2967,2968,2969,2970,2971,2972]]]},{"type":"MultiPolygon","properties":{"iso_a2":"CA","name":"Nova Scotia","postal":"NS","admin":"Canada"},"arcs":[[[-2876,2973,2974,2975,2976,2977,2978,2979,2980,2981,2982,2983,2984,2985,2986,2987,2988,2989,2990,2991]],[[2992,2993,2994,2995,2996,2997,2998,2999]]]},{"type":"MultiPolygon","properties":{"iso_a2":"CA","name":"Northwest Territories","postal":"NT","admin":"Canada"},"arcs":[[[3000,3001,-2783,-2828,3002,3003,3004,3005,3006,3007,3008,3009,3010,3011,3012,3013,3014,3015,3016,3017,3018,3019,3020,3021,3022,3023,3024,3025,3026]],[[3027,3028,3029,3030,3031,3032,3033,3034,3035,3036,3037,3038,3039,3040,3041,3042,3043,3044,3045,3046,3047,3048,3049,3050,3051]],[[3052,3053,3054,3055,3056,3057,3058,3059,3060,3061,3062,3063,3064,3065,3066,3067,3068]],[[3069]],[[3070,3071,3072,3073,3074,3075,3076,3077,3078,3079,3080,3081,3082,3083,3084,3085,3086,3087,3088]],[[3089,3090,3091,3092,3093,3094,3095,3096,3097,3098,3099]],[[3100,3101,3102]],[[3103,3104,3105]],[[3106]]]},{"type":"MultiPolygon","properties":{"iso_a2":"CA","name":"Nunavut","postal":"NU","admin":"Canada"},"arcs":[[[3107,3108,3109,3110]],[[3111,3112,3113]],[[3114,3115]],[[3116,3117]],[[3118,3119,3120,3121]],[[3122,3123,3124,3125]],[[3126,3127,3128,3129,3130,3131]],[[3132,3133,3134]],[[3135]],[[3136,3137,3138,3139,3140,3141,3142,3143,3144,3145,3146,3147,3148,3149,3150,3151,3152]],[[3153,3154,3155]],[[3156,3157]],[[3158,3159]],[[3160,3161,3162]],[[3163,3164,3165,3166]],[[3167,3168]],[[3169,3170,3171,3172]],[[3173,3174]],[[3175,3176]],[[3177,3178,3179,3180,3181,3182,3183]],[[-2863,-3001,3184,3185,3186,3187,3188,3189,3190,3191,3192,3193,3194,3195,3196,3197,3198,3199,3200,3201,3202,3203,3204,3205,3206,3207,3208,3209,3210,3211,3212,3213,3214,3215,3216,3217,3218,3219,3220,3221,3222,936,3223,3224,3225,3226,3227,3228,3229,3230,3231,3232,3233,3234,3235,3236,3237,3238,3239,3240,3241,3242,3243,3244,3245,3246,3247,3248,3249,3250,3251,3252,3253,3254,3255,3256,3257,3258,3259,3260,3261,3262,3263,3264,3265,3266,3267,3268,3269,3270,3271,3272,3273,3274,3275,3276,3277,3278,3279,3280]],[[-3028,3281,3282,3283,3284,3285,3286,3287,3288,3289,3290,3291,3292,3293,3294,3295,3296,3297,3298,3299,3300,3301,3302,3303,3304,3305]],[[3306,3307,3308,3309]],[[3310,3311,3312,3313,3314,3315,3316,3317]],[[3318,3319,3320,3321,3322,3323,3324,3325,3326,3327,3328,3329,3330,3331,3332,3333,3334,3335,3336,3337,3338,3339,3340,3341,3342,3343,3344,3345,3346,3347,3348,3349,3350,3351,3352,3353,3354,3355,3356,3357,3358,3359,3360,3361,3362,3363,3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3378,3379,3380,3381,3382,3383,3384,3385,3386,3387,3388,3389,3390,3391,3392,3393,3394,3395,3396,3397,3398,3399,3400,3401,3402,3403,3404,3405,3406,3407,3408,3409,3410,3411,3412,3413,3414,3415,3416,3417,3418,3419,3420,3421,3422,3423,3424,3425,3426,3427,3428,3429,3430,3431,3432,3433,3434,3435]],[[2091,3436,3437,3438,3439,3440,3441,3442,3443,3444,3445,3446,3447,3448,3449,3450,3451,3452,3453,3454,3455,3456]],[[3457]],[[3458,3459,3460,3461,3462,3463,3464,3465,3466,3467,3468]],[[3469,3470,3471,3472]],[[3473,3474,3475,3476,3477]],[[3478,3479,3480]],[[3481,3482,3483]],[[3484,3485,3486,3487,3488,3489,3490,3491,3492,3493,3494,3495,3496]],[[-3071,3497,3498,3499,3500,3501,3502,3503,3504,3505,3506,3507,3508]],[[3509,3510,3511,3512,3513,3514,3515,3516,3517,3518,3519,3520,3521,3522,3523,3524,3525,3526,3527,3528,3529,3530]],[[3531,3532,3533]],[[3534]],[[3535,3536,3537]],[[3538,3539,3540,3541]],[[3542,3543,3544,3545,3546,3547,3548,3549,3550,3551]],[[3552,3553]],[[3554,3555,3556,3557,3558,3559,3560,3561,3562,3563,3564,3565,3566,3567,3568,3569,3570,3571,3572,3573,3574,3575,3576]],[[3577,3578,3579,3580,3581,3582,3583,1729,3584,3585,3586,3587,3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,3637,3638,3639,3640,3641,3642,3643,3644,3645]]]},{"type":"MultiPolygon","properties":{"iso_a2":"CA","name":"Ontario","postal":"ON","admin":"Canada"},"arcs":[[[3646,3647,3648,3649]],[[3650,3651,3652]],[[3653,3654,3655,3656,3657,3658,3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685,3686,3687,3688,3689,3690,3691,3692,3693,3694,3695,3696,3697,3698,3699,3700,3701,3702,-2859,3703,3704,3705,3706,3707,3708,3709,3710,3711,3712,3713,3714,3715]]]},{"type":"Polygon","properties":{"iso_a2":"CA","name":"Prince Edward Island","postal":"PE","admin":"Canada"},"arcs":[[3716,3717,3718,3719,3720,3721,3722,3723,3724,3725]]},{"type":"MultiPolygon","properties":{"iso_a2":"CA","name":"Québec","postal":"QC","admin":"Canada"},"arcs":[[[3726,3727]],[[-2884,3728,3729,3730,3731,3732,3733,3734,3735,3736,3737,3738,3739,3740,3741,3742,3743,3744,3745,3746,3747]],[[1473,3748,3749,3750,3751,3752]],[[-2933,3753,3754,3755,3756,482,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769,3770,3771,3772,3773,-3654,3774,-3653,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784,3785,3786,3787,3788,3789,3790,3791,3792,3793,3794,3795,3796,3797,3798,3799,3800,3801,3802,3803,3804,3805,430,3806,3807,3808,3809,3810,3811,3812,3813,3814]]]},{"type":"Polygon","properties":{"iso_a2":"CA","name":"Saskatchewan","postal":"SK","admin":"Canada"},"arcs":[[3815,3816,-2784,-3002,-2862]]},{"type":"Polygon","properties":{"iso_a2":"CA","name":"Yukon","postal":"YT","admin":"Canada"},"arcs":[[-3003,-2827,3817,3818,3819,3820,3821]]},{"type":"MultiPolygon","properties":{"iso_a2":"US","name":"Alaska","postal":"AK","admin":"United States of America"},"arcs":[[[3822]],[[3823,3824,3825]],[[3826,3827]],[[3828,3829,3830,3831]],[[3832,3833,3834]],[[3835,3836,3837,3838,3839]],[[3840,3841]],[[3842,3843,3844,3845,1551,3846]],[[3847,3848,3849,3850,3851,3852,3853,3854]],[[3855,3856,3857]],[[3858,3859,3860]],[[3861,3862,3863,3864,3865]],[[3866,3867,3868,3869,3870,3871]],[[3872,3873,3874,3875,3876,3877,3878,3879,3880,3881,3882]],[[3883,3884,3885]],[[3886,3887,3888]],[[3889,3890,3891]],[[3892,3893,3894]],[[3895,3896,3897,3898,3899]],[[3900,3901,3902,3903,3904,3905,3906]],[[-3820,-3819,-3818,-2826,-2825,-2824,-2823,-2822,-2821,-2820,-2819,-2818,3907,3908,3909,3910,3911,3912,3913,3914,755,3915,3916,3917,3918,3919,3920,3921,3922,3923,3924,3925,3926,3927,3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939,3940,3941,3942,3943,3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959,3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,3972,3973,3974,3975,3976,3977,3978,3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,3989,3990,3991,3992,3993,3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005,4006,4007,4008,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021,4022,4023,4024,4025,4026,4027,4028,4029,4030,4031]]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Alabama","postal":"AL","admin":"United States of America"},"arcs":[[4032,4033,4034,4035,4036,4037,4038,4039]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Arkansas","postal":"AR","admin":"United States of America"},"arcs":[[4040,4041,4042,4043,4044,4045]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Arizona","postal":"AZ","admin":"United States of America"},"arcs":[[4046,4047,4048,4049,4050,4051,4052,4053]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"California","postal":"CA","admin":"United States of America"},"arcs":[[4054,4055,-4052,4056,4057,4058,4059,4060,4061,4062,4063,4064,4065,4066,4067,4068,4069,4070,4071,4072,4073]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Colorado","postal":"CO","admin":"United States of America"},"arcs":[[4074,4075,4076,4077,4078,4079]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Connecticut","postal":"CT","admin":"United States of America"},"arcs":[[4080,4081,4082,4083,4084]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Delaware","postal":"DE","admin":"United States of America"},"arcs":[[4085,4086,4087,4088,4089]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Florida","postal":"FL","admin":"United States of America"},"arcs":[[4090,4091,4092,4093,4094,4095,4096,4097,4098,4099,4100,4101,4102,4103,4104,4105,4106,4107,4108,4109,4110,622,4111,4112,4113,4114,4115,4116,-4040,4117]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Georgia","postal":"GA","admin":"United States of America"},"arcs":[[4118,4119,4120,-4118,-4039,4121,4122,4123]]},{"type":"MultiPolygon","properties":{"iso_a2":"US","name":"Hawaii","postal":"HI","admin":"United States of America"},"arcs":[[[4124,4125,4126,4127,4128]],[[4129,4130]],[[4131,4132,4133]],[[4134,4135,4136,4137,4138]],[[4139,4140]]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Iowa","postal":"IA","admin":"United States of America"},"arcs":[[4141,4142,4143,4144,4145,4146]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Idaho","postal":"ID","admin":"United States of America"},"arcs":[[4147,4148,4149,4150,4151,-2830,4152]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Illinois","postal":"IL","admin":"United States of America"},"arcs":[[4153,4154,4155,4156,4157,-4142,4158]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Indiana","postal":"IN","admin":"United States of America"},"arcs":[[4159,4160,-4156,4161,4162]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Kansas","postal":"KS","admin":"United States of America"},"arcs":[[4163,-4075,4164,4165]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Kentucky","postal":"KY","admin":"United States of America"},"arcs":[[4166,4167,4168,4169,-4157,-4161,4170]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Louisiana","postal":"LA","admin":"United States of America"},"arcs":[[4171,4172,4173,4174,4175,4176,4177,4178,4179,4180,4181,4182,4183,4184,-4043,4185]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Massachusetts","postal":"MA","admin":"United States of America"},"arcs":[[4186,-4085,4187,4188,4189,4190,4191,4192,4193,4194,4195,4196,4197]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Maryland","postal":"MD","admin":"United States of America"},"arcs":[[-4089,4198,4199,4200,4201,4202,4203,4204,4205,4206,4207,4208,4209,4210,4211]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Maine","postal":"ME","admin":"United States of America"},"arcs":[[-2881,-2880,4212,4213,4214,4215,4216,4217,4218,4219,4220,-3732,-3731,-3730,-3729,-2883,-2882]]},{"type":"MultiPolygon","properties":{"iso_a2":"US","name":"Michigan","postal":"MI","admin":"United States of America"},"arcs":[[[-3672,4221,4222,4223,-4163,4224,4225,4226,4227,4228,4229,4230,4231,4232,4233,4234,4235,4236,4237,4238,4239,4240,4241,4242]],[[4243,4244,4245,4246,4247,4248,4249,4250,4251,4252,4253,4254,4255,4256]],[[4257,4258,4259,4260,4261]]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Minnesota","postal":"MN","admin":"United States of America"},"arcs":[[4262,4263,4264,-4146,4265,4266,-2860,-3703,-3702,-3701,-3700,-3699,-3698]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Missouri","postal":"MO","admin":"United States of America"},"arcs":[[-4158,-4170,4267,-4046,4268,-4166,4269,-4143]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Mississippi","postal":"MS","admin":"United States of America"},"arcs":[[4270,4271,-4186,-4042,4272,-4037]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Montana","postal":"MT","admin":"United States of America"},"arcs":[[4273,4274,-4153,-2829,-2781,-3817,4275]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"North Carolina","postal":"NC","admin":"United States of America"},"arcs":[[4276,-4123,4277,4278,4279,4280,4281,4282,4283,4284,4285,4286,4287,4288,4289,4290]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"North Dakota","postal":"ND","admin":"United States of America"},"arcs":[[4291,-4276,-3816,-2861,-4267]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Nebraska","postal":"NE","admin":"United States of America"},"arcs":[[-4144,-4270,-4165,-4080,4292,4293]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"New Hampshire","postal":"NH","admin":"United States of America"},"arcs":[[4294,-4190,4295,-3734,-3733,-4221]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"New Jersey","postal":"NJ","admin":"United States of America"},"arcs":[[4296,4297,4298,4299,4300,4301]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"New Mexico","postal":"NM","admin":"United States of America"},"arcs":[[4302,4303,4304,4305,-4047,-4077,4306]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Nevada","postal":"NV","admin":"United States of America"},"arcs":[[-4053,-4056,4307,-4150,4308]]},{"type":"MultiPolygon","properties":{"iso_a2":"US","name":"New York","postal":"NY","admin":"United States of America"},"arcs":[[[4309,4310,4311]],[[4312,-4188,-4084,4313,-4298,4314,4315,4316,-3666,4317,4318,4319,4320,4321,4322,-3657,-3656,-3736]]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Ohio","postal":"OH","admin":"United States of America"},"arcs":[[4323,-4171,-4160,-4224,4324,4325,4326,4327,4328]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Oklahoma","postal":"OK","admin":"United States of America"},"arcs":[[-4045,4329,-4307,-4076,-4164,-4269]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Oregon","postal":"OR","admin":"United States of America"},"arcs":[[4330,-4151,-4308,-4055,4331,4332,4333,4334,4335,4336]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Pennsylvania","postal":"PA","admin":"United States of America"},"arcs":[[-4297,4337,-4090,-4212,4338,-4329,4339,-4315]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Rhode Island","postal":"RI","admin":"United States of America"},"arcs":[[4340,4341,-4081,-4187]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"South Carolina","postal":"SC","admin":"United States of America"},"arcs":[[4342,4343,4344,4345,4346,-4124,-4277]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"South Dakota","postal":"SD","admin":"United States of America"},"arcs":[[-4266,-4145,-4294,4347,-4274,-4292]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Tennessee","postal":"TN","admin":"United States of America"},"arcs":[[4348,-4278,-4122,-4038,-4273,-4041,-4268,-4169]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Texas","postal":"TX","admin":"United States of America"},"arcs":[[-4044,-4185,4349,4350,4351,4352,4353,4354,4355,4356,4357,4358,4359,4360,4361,4362,4363,4364,4365,4366,4367,4368,4369,4370,-4303,-4330]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Utah","postal":"UT","admin":"United States of America"},"arcs":[[4371,-4078,-4054,-4309,-4149]]},{"type":"MultiPolygon","properties":{"iso_a2":"US","name":"Virginia","postal":"VA","admin":"United States of America"},"arcs":[[[-4200,4372,4373,4374]],[[-4210,4375,4376,4377,4378,4379,4380,4381,4382,-4279,-4349,-4168,4383]]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Vermont","postal":"VT","admin":"United States of America"},"arcs":[[-4189,-4313,-3735,-4296]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Washington","postal":"WA","admin":"United States of America"},"arcs":[[-4331,4384,4385,4386,4387,4388,4389,4390,4391,4392,4393,4394,4395,4396,-2832,-2831,-4152]]},{"type":"MultiPolygon","properties":{"iso_a2":"US","name":"Wisconsin","postal":"WI","admin":"United States of America"},"arcs":[[[4397,4398,4399]],[[-4244,4400,4401,4402,4403,-4159,-4147,-4265,4404,4405,4406,4407]]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"West Virginia","postal":"WV","admin":"United States of America"},"arcs":[[-4211,-4384,-4167,-4324,-4339]]},{"type":"Polygon","properties":{"iso_a2":"US","name":"Wyoming","postal":"WY","admin":"United States of America"},"arcs":[[-4293,-4079,-4372,-4148,-4275,-4348]]}]},"cities":{"type":"GeometryCollection","geometries":[{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"San Bernardino","adm0name":"United States of America","adm1name":"California","iso_a2":"US"},"coordinates":[174161,706874]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Bridgeport","adm0name":"United States of America","adm1name":"Connecticut","iso_a2":"US"},"coordinates":[296661,748698]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Rochester","adm0name":"United States of America","adm1name":"New York","iso_a2":"US"},"coordinates":[284383,760490]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Manchester","adm0name":"United Kingdom","adm1name":"Manchester","iso_a2":"GB"},"coordinates":[493750,821690]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Gujranwala","adm0name":"Pakistan","adm1name":"Punjab","iso_a2":"PK"},"coordinates":[706063,695262]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Incheon","adm0name":"South Korea","adm1name":"Inch'on-gwangyoksi","iso_a2":"KR"},"coordinates":[851778,726754]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Benin City","adm0name":"Nigeria","adm1name":"Edo","iso_a2":"NG"},"coordinates":[515605,542293]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Xiamen","adm0name":"China","adm1name":"Fujian","iso_a2":"CN"},"coordinates":[827994,649581]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Nanchong","adm0name":"China","adm1name":"Sichuan","iso_a2":"CN"},"coordinates":[794799,687086]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Neijiang","adm0name":"China","adm1name":"Sichuan","iso_a2":"CN"},"coordinates":[791800,679976]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Nanyang","adm0name":"China","adm1name":"Henan","iso_a2":"CN"},"coordinates":[812577,700238]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Jinxi","adm0name":"China","adm1name":"Liaoning","iso_a2":"CN"},"coordinates":[835632,746152]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Yantai","adm0name":"China","adm1name":"Shandong","iso_a2":"CN"},"coordinates":[837216,727075]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Zaozhuang","adm0name":"China","adm1name":"Shandong","iso_a2":"CN"},"coordinates":[826578,711374]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Suzhou","adm0name":"China","adm1name":"Jiangsu","iso_a2":"CN"},"coordinates":[835050,690167]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Xuzhou","adm0name":"China","adm1name":"Jiangsu","iso_a2":"CN"},"coordinates":[825494,707819]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Wuxi","adm0name":"China","adm1name":"Jiangsu","iso_a2":"CN"},"coordinates":[834161,691823]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Jilin","adm0name":"China","adm1name":"Jilin","iso_a2":"CN"},"coordinates":[851522,764516]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Chandigarh","adm0name":"India","adm1name":"Chandigarh","iso_a2":"IN"},"coordinates":[713272,686728]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Jammu","adm0name":"India","adm1name":"Jammu and Kashmir","iso_a2":"IN"},"coordinates":[707901,698528]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Sholapur","adm0name":"India","adm1name":"Maharashtra","iso_a2":"IN"},"coordinates":[710827,609416]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Aurangabad","adm0name":"India","adm1name":"Maharashtra","iso_a2":"IN"},"coordinates":[709217,622600]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Nasik","adm0name":"India","adm1name":"Maharashtra","iso_a2":"IN"},"coordinates":[704938,623220]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Dispur","adm0name":"India","adm1name":"Assam","iso_a2":"IN"},"coordinates":[754907,659606]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Jullundur","adm0name":"India","adm1name":"Punjab","iso_a2":"IN"},"coordinates":[709907,690371]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Allahabad","adm0name":"India","adm1name":"Uttar Pradesh","iso_a2":"IN"},"coordinates":[727327,655536]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Moradabad","adm0name":"India","adm1name":"Uttar Pradesh","iso_a2":"IN"},"coordinates":[718763,675601]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Ghaziabad","adm0name":"India","adm1name":"Uttar Pradesh","iso_a2":"IN"},"coordinates":[715017,674526]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Agra","adm0name":"India","adm1name":"Uttar Pradesh","iso_a2":"IN"},"coordinates":[716703,665699]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Aligarh","adm0name":"India","adm1name":"Uttar Pradesh","iso_a2":"IN"},"coordinates":[716832,669974]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Meerut","adm0name":"India","adm1name":"Uttar Pradesh","iso_a2":"IN"},"coordinates":[715827,676540]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Dhanbad","adm0name":"India","adm1name":"Jharkhand","iso_a2":"IN"},"coordinates":[740049,645733]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Gwalior","adm0name":"India","adm1name":"Madhya Pradesh","iso_a2":"IN"},"coordinates":[717160,660127]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Vadodara","adm0name":"India","adm1name":"Dadra and Nagar Haveli","iso_a2":"IN"},"coordinates":[703271,636904]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Rajkot","adm0name":"India","adm1name":"Dadra and Nagar Haveli","iso_a2":"IN"},"coordinates":[696661,636904]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Durazno","adm0name":"Uruguay","adm1name":"Durazno","iso_a2":"UY"},"coordinates":[343027,306781]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"International Falls","adm0name":"United States of America","adm1name":"Minnesota","iso_a2":"US"},"coordinates":[240525,792652]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"St. Paul","adm0name":"United States of America","adm1name":"Minnesota","iso_a2":"US"},"coordinates":[241431,770986]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Billings","adm0name":"United States of America","adm1name":"Montana","iso_a2":"US"},"coordinates":[198500,775987]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Great Falls","adm0name":"United States of America","adm1name":"Montana","iso_a2":"US"},"coordinates":[190833,786130]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Missoula","adm0name":"United States of America","adm1name":"Montana","iso_a2":"US"},"coordinates":[183352,782409]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Minot","adm0name":"United States of America","adm1name":"North Dakota","iso_a2":"US"},"coordinates":[218622,790468]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Fargo","adm0name":"United States of America","adm1name":"North Dakota","iso_a2":"US"},"coordinates":[231140,782439]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Hilo","adm0name":"United States of America","adm1name":"Hawaii","iso_a2":"US"},"coordinates":[69194,621429]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Olympia","adm0name":"United States of America","adm1name":"Washington","iso_a2":"US"},"coordinates":[158613,783392]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Spokane","adm0name":"United States of America","adm1name":"Washington","iso_a2":"US"},"coordinates":[173833,787136]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Vancouver","adm0name":"United States of America","adm1name":"Washington","iso_a2":"US"},"coordinates":[159333,775052]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Flagstaff","adm0name":"United States of America","adm1name":"Arizona","iso_a2":"US"},"coordinates":[189860,713247]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Tucson","adm0name":"United States of America","adm1name":"Arizona","iso_a2":"US"},"coordinates":[191967,695526]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Santa Barbara","adm0name":"United States of America","adm1name":"California","iso_a2":"US"},"coordinates":[167444,708726]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Fresno","adm0name":"United States of America","adm1name":"California","iso_a2":"US"},"coordinates":[167297,722427]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Eureka","adm0name":"United States of America","adm1name":"California","iso_a2":"US"},"coordinates":[155146,746448]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Colorado Springs","adm0name":"United States of America","adm1name":"Colorado","iso_a2":"US"},"coordinates":[208911,734959]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Reno","adm0name":"United States of America","adm1name":"Nevada","iso_a2":"US"},"coordinates":[167166,738910]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Elko","adm0name":"United States of America","adm1name":"Nevada","iso_a2":"US"},"coordinates":[178438,746627]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Albuquerque","adm0name":"United States of America","adm1name":"New Mexico","iso_a2":"US"},"coordinates":[203773,712695]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Salem","adm0name":"United States of America","adm1name":"Oregon","iso_a2":"US"},"coordinates":[158267,770891]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Casper","adm0name":"United States of America","adm1name":"Wyoming","iso_a2":"US"},"coordinates":[204687,758678]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Topeka","adm0name":"United States of America","adm1name":"Kansas","iso_a2":"US"},"coordinates":[234250,736067]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Kansas City","adm0name":"United States of America","adm1name":"Missouri","iso_a2":"US"},"coordinates":[237205,736416]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Tulsa","adm0name":"United States of America","adm1name":"Oklahoma","iso_a2":"US"},"coordinates":[233527,718708]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Sioux Falls","adm0name":"United States of America","adm1name":"South Dakota","iso_a2":"US"},"coordinates":[231305,762727]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Shreveport","adm0name":"United States of America","adm1name":"Louisiana","iso_a2":"US"},"coordinates":[239528,697262]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Baton Rouge","adm0name":"United States of America","adm1name":"Louisiana","iso_a2":"US"},"coordinates":[246833,685164]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Ft. Worth","adm0name":"United States of America","adm1name":"Texas","iso_a2":"US"},"coordinates":[229610,698684]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Corpus Christi","adm0name":"United States of America","adm1name":"Texas","iso_a2":"US"},"coordinates":[229439,669078]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Austin","adm0name":"United States of America","adm1name":"Texas","iso_a2":"US"},"coordinates":[228487,684044]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Amarillo","adm0name":"United States of America","adm1name":"Texas","iso_a2":"US"},"coordinates":[217139,713436]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"El Paso","adm0name":"United States of America","adm1name":"Texas","iso_a2":"US"},"coordinates":[204133,693008]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Laredo","adm0name":"United States of America","adm1name":"Texas","iso_a2":"US"},"coordinates":[223591,667676]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Merida","adm0name":"Venezuela","adm1name":"Mérida","iso_a2":"VE"},"coordinates":[302417,554483]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Burlington","adm0name":"United States of America","adm1name":"Vermont","iso_a2":"US"},"coordinates":[296631,768212]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Montgomery","adm0name":"United States of America","adm1name":"Alabama","iso_a2":"US"},"coordinates":[260335,696442]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Tallahassee","adm0name":"United States of America","adm1name":"Florida","iso_a2":"US"},"coordinates":[265888,685117]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Orlando","adm0name":"United States of America","adm1name":"Florida","iso_a2":"US"},"coordinates":[273939,673635]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Jacksonville","adm0name":"United States of America","adm1name":"Florida","iso_a2":"US"},"coordinates":[273133,684418]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Savannah","adm0name":"United States of America","adm1name":"Georgia","iso_a2":"US"},"coordinates":[274695,694425]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Columbia","adm0name":"United States of America","adm1name":"South Carolina","iso_a2":"US"},"coordinates":[275277,706385]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Indianapolis","adm0name":"United States of America","adm1name":"Indiana","iso_a2":"US"},"coordinates":[260633,740225]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Wilmington","adm0name":"United States of America","adm1name":"North Carolina","iso_a2":"US"},"coordinates":[283486,707484]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Knoxville","adm0name":"United States of America","adm1name":"Tennessee","iso_a2":"US"},"coordinates":[266888,717820]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Richmond","adm0name":"United States of America","adm1name":"Virginia","iso_a2":"US"},"coordinates":[284855,727192]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Charleston","adm0name":"United States of America","adm1name":"West Virginia","iso_a2":"US"},"coordinates":[273243,731919]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Baltimore","adm0name":"United States of America","adm1name":"Maryland","iso_a2":"US"},"coordinates":[287161,737560]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Syracuse","adm0name":"United States of America","adm1name":"New York","iso_a2":"US"},"coordinates":[288472,759765]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Puerto Ayacucho","adm0name":"Venezuela","adm1name":"Amazonas","iso_a2":"VE"},"coordinates":[312156,538272]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Port-of-Spain","adm0name":"Trinidad and Tobago","adm1name":"Port of Spain","iso_a2":"TT"},"coordinates":[329119,567824]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Augusta","adm0name":"United States of America","adm1name":"Maine","iso_a2":"US"},"coordinates":[306167,767233]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Sault Ste. Marie","adm0name":"United States of America","adm1name":"Michigan","iso_a2":"US"},"coordinates":[265708,780176]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Atakpame","adm0name":"Togo","adm1name":"Plateaux","iso_a2":"TG"},"coordinates":[503110,549328]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Sousse","adm0name":"Tunisia","adm1name":"Sousse","iso_a2":"TN"},"coordinates":[529513,716990]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Taizz","adm0name":"Yemen","adm1name":"Ta`izz","iso_a2":"YE"},"coordinates":[622326,585327]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Sitka","adm0name":"United States of America","adm1name":"Alaska","iso_a2":"US"},"coordinates":[124090,842768]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Lvov","adm0name":"Ukraine","adm1name":"L'viv","iso_a2":"UA"},"coordinates":[566750,799962]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Odessa","adm0name":"Ukraine","adm1name":"Odessa","iso_a2":"UA"},"coordinates":[585299,780156]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Zhytomyr","adm0name":"Ukraine","adm1name":"Zhytomyr","iso_a2":"UA"},"coordinates":[579616,802395]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Dnipropetrovsk","adm0name":"Ukraine","adm1name":"Dnipropetrovs'k","iso_a2":"UA"},"coordinates":[597216,791946]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Donetsk","adm0name":"Ukraine","adm1name":"Donets'k","iso_a2":"UA"},"coordinates":[605077,789102]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Kharkiv","adm0name":"Ukraine","adm1name":"Kharkiv","iso_a2":"UA"},"coordinates":[600689,800951]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Turkmenbasy","adm0name":"Turkmenistan","adm1name":"Balkan","iso_a2":"TM"},"coordinates":[647137,741831]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Bukhara","adm0name":"Uzbekistan","adm1name":"Bukhoro","iso_a2":"UZ"},"coordinates":[678972,740392]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Nukus","adm0name":"Uzbekistan","adm1name":"Karakalpakstan","iso_a2":"UZ"},"coordinates":[665596,756329]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Turkmenabat","adm0name":"Turkmenistan","adm1name":"Chardzhou","iso_a2":"TM"},"coordinates":[676610,736423]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Mary","adm0name":"Turkmenistan","adm1name":"Mary","iso_a2":"TM"},"coordinates":[671759,727476]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Andijon","adm0name":"Uzbekistan","adm1name":"Andijon","iso_a2":"UZ"},"coordinates":[700944,746376]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Haiphong","adm0name":"Vietnam","adm1name":"Qu?ng Ninh","iso_a2":"VN"},"coordinates":[796328,628135]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Da Nang","adm0name":"Vietnam","adm1name":"Ðà N?ng","iso_a2":"VN"},"coordinates":[800693,599864]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Kabwe","adm0name":"Zambia","adm1name":"Central","iso_a2":"ZM"},"coordinates":[579027,419168]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Mufulira","adm0name":"Zambia","adm1name":"Copperbelt","iso_a2":"ZM"},"coordinates":[578499,430365]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Kitwe","adm0name":"Zambia","adm1name":"Copperbelt","iso_a2":"ZM"},"coordinates":[578388,428825]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Livingstone","adm0name":"Zambia","adm1name":"Southern","iso_a2":"ZM"},"coordinates":[571833,398907]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Chitungwiza","adm0name":"Zimbabwe","adm1name":"Harare","iso_a2":"ZW"},"coordinates":[586388,398077]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Douala","adm0name":"Cameroon","adm1name":"Littoral","iso_a2":"CM"},"coordinates":[526967,528784]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Birmingham","adm0name":"United Kingdom","adm1name":"West Midlands","iso_a2":"GB"},"coordinates":[494661,815614]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Belfast","adm0name":"United Kingdom","adm1name":"Belfast","iso_a2":"GB"},"coordinates":[483444,828192]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Izmir","adm0name":"Turkey","adm1name":"Izmir","iso_a2":"TR"},"coordinates":[575416,732442]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Bursa","adm0name":"Turkey","adm1name":"Bursa","iso_a2":"TR"},"coordinates":[580744,742892]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Samsun","adm0name":"Turkey","adm1name":"Samsun","iso_a2":"TR"},"coordinates":[600954,749278]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Konya","adm0name":"Turkey","adm1name":"Konya","iso_a2":"TR"},"coordinates":[590203,729117]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Adana","adm0name":"Turkey","adm1name":"Adana","iso_a2":"TR"},"coordinates":[598105,723904]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Gulu","adm0name":"Uganda","adm1name":"Aswa","iso_a2":"UG"},"coordinates":[589666,521187]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Kigali","adm0name":"Rwanda","adm1name":"Kigali City","iso_a2":"RW"},"coordinates":[583496,493155]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Cottica","adm0name":"Suriname","adm1name":"Sipaliwini","iso_a2":"SR"},"coordinates":[349352,527527]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Cordoba","adm0name":"Spain","adm1name":"Andalucía","iso_a2":"ES"},"coordinates":[486749,729135]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Maradi","adm0name":"Niger","adm1name":"Maradi","iso_a2":"NE"},"coordinates":[519712,584648]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Tahoua","adm0name":"Niger","adm1name":"Tahoua","iso_a2":"NE"},"coordinates":[514610,592991]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Constanta","adm0name":"Romania","adm1name":"Constanta","iso_a2":"RO"},"coordinates":[579472,766594]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Luleå","adm0name":"Sweden","adm1name":"Norrbotten","iso_a2":"SE"},"coordinates":[561551,893341]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Sundsvall","adm0name":"Sweden","adm1name":"Västernorrland","iso_a2":"SE"},"coordinates":[548101,874403]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Iasi","adm0name":"Romania","adm1name":"Iasi","iso_a2":"RO"},"coordinates":[576596,784163]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Surat Thani","adm0name":"Thailand","adm1name":"Surat Thani","iso_a2":"TH"},"coordinates":[775944,558926]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Chiang Mai","adm0name":"Thailand","adm1name":"Chiang Mai","iso_a2":"TH"},"coordinates":[774944,616096]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Nakhon Ratchasima","adm0name":"Thailand","adm1name":"Nakhon Ratchasima","iso_a2":"TH"},"coordinates":[783611,593584]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Mbabane","adm0name":"Swaziland","adm1name":"Hhohho","iso_a2":"SZ"},"coordinates":[586481,348805]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Piura","adm0name":"Peru","adm1name":"Piura","iso_a2":"PE"},"coordinates":[276028,473851]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Arequipa","adm0name":"Peru","adm1name":"Arequipa","iso_a2":"PE"},"coordinates":[301300,407449]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Chimbote","adm0name":"Peru","adm1name":"Ancash","iso_a2":"PE"},"coordinates":[281750,450982]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Pucallpa","adm0name":"Peru","adm1name":"Ucayali","iso_a2":"PE"},"coordinates":[292958,455136]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Iquitos","adm0name":"Peru","adm1name":"Loreto","iso_a2":"PE"},"coordinates":[296527,482500]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Huancayo","adm0name":"Peru","adm1name":"Junín","iso_a2":"PE"},"coordinates":[291110,433149]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Ciudad del Este","adm0name":"Paraguay","adm1name":"Alto Paraná","iso_a2":"PY"},"coordinates":[348289,353544]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Ponta Delgada","adm0name":"Portugal","adm1name":"Azores","iso_a2":"PT"},"coordinates":[428704,728355]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Vigo","adm0name":"Spain","adm1name":"Galicia","iso_a2":"ES"},"coordinates":[475750,754847]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Bilbao","adm0name":"Spain","adm1name":"País Vasco","iso_a2":"ES"},"coordinates":[491861,760950]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Kaolack","adm0name":"Senegal","adm1name":"Kaolack","iso_a2":"SM"},"coordinates":[455277,588548]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Kaedi","adm0name":"Senegal","adm1name":"Matam","iso_a2":"SM"},"coordinates":[462500,600397]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Geneina","adm0name":"Sudan","adm1name":"West Darfur","iso_a2":"SD"},"coordinates":[562333,584401]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Medina","adm0name":"Saudi Arabia","adm1name":"Al Madinah","iso_a2":"SA"},"coordinates":[609939,649878]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Tabuk","adm0name":"Saudi Arabia","adm1name":"Tabuk","iso_a2":"SA"},"coordinates":[601541,672875]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Juba","adm0name":"South Sudan","adm1name":"Central Equatoria","iso_a2":"SS"},"coordinates":[587722,533332]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Malakal","adm0name":"South Sudan","adm1name":"Upper Nile","iso_a2":"SS"},"coordinates":[587933,561218]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Omdurman","adm0name":"Sudan","adm1name":"Khartoum","iso_a2":"SD"},"coordinates":[590222,597237]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"El Obeid","adm0name":"Sudan","adm1name":"North Kurdufan","iso_a2":"SD"},"coordinates":[583935,582821]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"The Hague","adm0name":"Netherlands","adm1name":"Zuid-Holland","iso_a2":"NL"},"coordinates":[511860,813263]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Kristiansand","adm0name":"Norway","adm1name":"Vest-Agder","iso_a2":"NO"},"coordinates":[522222,849323]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Ljubljana","adm0name":"Slovenia","adm1name":"Osrednjeslovenska","iso_a2":"SI"},"coordinates":[540318,777570]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Bratislava","adm0name":"Slovakia","adm1name":"Bratislavský","iso_a2":"SK"},"coordinates":[547547,789979]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Hammerfest","adm0name":"Norway","adm1name":"Finnmark","iso_a2":"NO"},"coordinates":[565800,923346]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Doha","adm0name":"Qatar","adm1name":"Ad Dawhah","iso_a2":"QA"},"coordinates":[643147,654526]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Quetta","adm0name":"Pakistan","adm1name":"Baluchistan","iso_a2":"PK"},"coordinates":[686175,683766]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Larkana","adm0name":"Pakistan","adm1name":"Sind","iso_a2":"PK"},"coordinates":[689463,668005]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Springbok","adm0name":"South Africa","adm1name":"Northern Cape","iso_a2":"ZA"},"coordinates":[549675,328958]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Upington","adm0name":"South Africa","adm1name":"Northern Cape","iso_a2":"ZA"},"coordinates":[558972,336107]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Worcester","adm0name":"South Africa","adm1name":"Western Cape","iso_a2":"ZA"},"coordinates":[553999,305418]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"George","adm0name":"South Africa","adm1name":"Western Cape","iso_a2":"ZA"},"coordinates":[562360,303582]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Tete","adm0name":"Mozambique","adm1name":"Tete","iso_a2":"MZ"},"coordinates":[593277,408919]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Pemba","adm0name":"Mozambique","adm1name":"Cabo Delgado","iso_a2":"MZ"},"coordinates":[612589,427799]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Nampula","adm0name":"Mozambique","adm1name":"Nampula","iso_a2":"MZ"},"coordinates":[609147,415044]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Welkom","adm0name":"South Africa","adm1name":"Orange Free State","iso_a2":"ZA"},"coordinates":[574249,339011]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Xai-Xai","adm0name":"Mozambique","adm1name":"Gaza","iso_a2":"MZ"},"coordinates":[593443,356369]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Goroka","adm0name":"Papua New Guinea","adm1name":"Eastern Highlands","iso_a2":"PG"},"coordinates":[903848,468677]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Mt. Hagen","adm0name":"Papua New Guinea","adm1name":"Western Highlands","iso_a2":"PG"},"coordinates":[900601,469980]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Rabaul","adm0name":"Papua New Guinea","adm1name":"East New Britain","iso_a2":"PG"},"coordinates":[922620,479802]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Lae","adm0name":"Papua New Guinea","adm1name":"Morobe","iso_a2":"PG"},"coordinates":[908305,464828]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"David","adm0name":"Panama","adm1name":"Chiriquí","iso_a2":"PA"},"coordinates":[271018,554680]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Oujda","adm0name":"Morocco","adm1name":"Oriental","iso_a2":"MA"},"coordinates":[494694,710237]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Safi","adm0name":"Morocco","adm1name":"Doukkala - Abda","iso_a2":"MA"},"coordinates":[474333,696195]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Podgorica","adm0name":"Montenegro","adm1name":"Podgorica","iso_a2":"ME"},"coordinates":[553517,756305]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Quelimane","adm0name":"Mozambique","adm1name":"Zambezia","iso_a2":"MZ"},"coordinates":[602472,398787]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"East London","adm0name":"South Africa","adm1name":"Eastern Cape","iso_a2":"ZA"},"coordinates":[577416,309388]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Middelburg","adm0name":"South Africa","adm1name":"Eastern Cape","iso_a2":"ZA"},"coordinates":[569472,318097]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Naltchik","adm0name":"Russia","adm1name":"Kabardin-Balkar","iso_a2":"RU"},"coordinates":[621161,762420]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Stavropol","adm0name":"Russia","adm1name":"Stavropol'","iso_a2":"RU"},"coordinates":[616610,771613]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Ugolnye Kopi","adm0name":"Russia","adm1name":"Chukchi Autonomous Okrug","iso_a2":"RU"},"coordinates":[993610,888227]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Kaliningrad","adm0name":"Russia","adm1name":"Kaliningrad","iso_a2":"RU"},"coordinates":[556937,828785]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Pskov","adm0name":"Russia","adm1name":"Pskov","iso_a2":"RU"},"coordinates":[578694,847328]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Bryansk","adm0name":"Russia","adm1name":"Bryansk","iso_a2":"RU"},"coordinates":[595638,820254]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Smolensk","adm0name":"Russia","adm1name":"Smolensk","iso_a2":"RU"},"coordinates":[589020,829274]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Petrozavodsk","adm0name":"Russia","adm1name":"Karelia","iso_a2":"RU"},"coordinates":[595222,871144]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Tver","adm0name":"Russia","adm1name":"Tver'","iso_a2":"RU"},"coordinates":[599693,841581]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Vologda","adm0name":"Russia","adm1name":"Vologda","iso_a2":"RU"},"coordinates":[610888,855504]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Yaroslavl","adm0name":"Russia","adm1name":"Yaroslavl'","iso_a2":"RU"},"coordinates":[610749,846084]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Rostov","adm0name":"Russia","adm1name":"Rostov","iso_a2":"RU"},"coordinates":[610307,784568]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Sochi","adm0name":"Russia","adm1name":"Krasnodar","iso_a2":"RU"},"coordinates":[610360,762964]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Krasnodar","adm0name":"Russia","adm1name":"Krasnodar","iso_a2":"RU"},"coordinates":[608333,771435]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Penza","adm0name":"Russia","adm1name":"Penza","iso_a2":"RU"},"coordinates":[624999,819779]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Ryazan","adm0name":"Russia","adm1name":"Ryazan'","iso_a2":"RU"},"coordinates":[610333,828311]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Voronezh","adm0name":"Russia","adm1name":"Voronezh","iso_a2":"RU"},"coordinates":[609077,811201]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Magnitogorsk","adm0name":"Russia","adm1name":"Chelyabinsk","iso_a2":"RU"},"coordinates":[663833,821217]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Chelyabinsk","adm0name":"Russia","adm1name":"Chelyabinsk","iso_a2":"RU"},"coordinates":[670657,831492]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Vorkuta","adm0name":"Russia","adm1name":"Komi","iso_a2":"RU"},"coordinates":[677805,904617]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Kirov","adm0name":"Russia","adm1name":"Kirov","iso_a2":"RU"},"coordinates":[637971,851831]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Nizhny Tagil","adm0name":"Russia","adm1name":"Sverdlovsk","iso_a2":"RU"},"coordinates":[666597,847862]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Astrakhan","adm0name":"Russia","adm1name":"Astrakhan'","iso_a2":"RU"},"coordinates":[633486,779307]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Orenburg","adm0name":"Russia","adm1name":"Orenburg","iso_a2":"RU"},"coordinates":[653083,811485]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Saratov","adm0name":"Russia","adm1name":"Saratov","iso_a2":"RU"},"coordinates":[627855,810312]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Ulyanovsk","adm0name":"Russia","adm1name":"Ul'yanovsk","iso_a2":"RU"},"coordinates":[634471,826592]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Omsk","adm0name":"Russia","adm1name":"Omsk","iso_a2":"RU"},"coordinates":[703882,830514]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Tyumen","adm0name":"Russia","adm1name":"Tyumen'","iso_a2":"RU"},"coordinates":[682027,843241]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Novokuznetsk","adm0name":"Russia","adm1name":"Kemerovo","iso_a2":"RU"},"coordinates":[741986,823156]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Kemerovo","adm0name":"Russia","adm1name":"Kemerovo","iso_a2":"RU"},"coordinates":[739139,832576]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Groznyy","adm0name":"Russia","adm1name":"Chechnya","iso_a2":"RU"},"coordinates":[626940,761357]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Kandy","adm0name":"Sri Lanka","adm1name":"Kandy","iso_a2":"LK"},"coordinates":[724082,547847]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Sri Jawewardenepura Kotte","adm0name":"Sri Lanka","adm1name":"Colombo","iso_a2":"LK"},"coordinates":[722082,545596]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Daejeon","adm0name":"South Korea","adm1name":"Daejeon","iso_a2":"KR"},"coordinates":[853952,719997]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Gwangju","adm0name":"South Korea","adm1name":"Kwangju-gwangyoksi","iso_a2":"KR"},"coordinates":[852523,713097]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Busan","adm0name":"South Korea","adm1name":"Busan","iso_a2":"KR"},"coordinates":[858355,712648]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Zamboanga","adm0name":"Philippines","adm1name":"Zamboanga del Sur","iso_a2":"PH"},"coordinates":[839105,545725]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Laoag","adm0name":"Philippines","adm1name":"Ilocos Norte","iso_a2":"PH"},"coordinates":[834981,612535]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Baguio City","adm0name":"Philippines","adm1name":"Benguet","iso_a2":"PH"},"coordinates":[834915,602056]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"General Santos","adm0name":"Philippines","adm1name":"South Cotabato","iso_a2":"PH"},"coordinates":[847707,540920]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Ust-Ulimsk","adm0name":"Russia","adm1name":"Irkutsk","iso_a2":"RU"},"coordinates":[785092,848276]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Angarsk","adm0name":"Russia","adm1name":"Irkutsk","iso_a2":"RU"},"coordinates":[788666,816107]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Abakan","adm0name":"Russia","adm1name":"Krasnoyarsk","iso_a2":"RU"},"coordinates":[754013,822882]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Norilsk","adm0name":"Russia","adm1name":"Taymyr","iso_a2":"RU"},"coordinates":[745068,915519]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Khatanga","adm0name":"Russia","adm1name":"Taymyr","iso_a2":"RU"},"coordinates":[784624,931522]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Kyzyl","adm0name":"Russia","adm1name":"Tuva","iso_a2":"RU"},"coordinates":[762175,811051]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Ulan Ude","adm0name":"Russia","adm1name":"Buryat","iso_a2":"RU"},"coordinates":[798958,811752]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Blagoveshchensk","adm0name":"Russia","adm1name":"Amur","iso_a2":"RU"},"coordinates":[854259,802519]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Bukachacha","adm0name":"Russia","adm1name":"Chita","iso_a2":"RU"},"coordinates":[824768,818614]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Dalnegorsk","adm0name":"Russia","adm1name":"Primor'ye","iso_a2":"RU"},"coordinates":[876436,768575]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Ambarchik","adm0name":"Russia","adm1name":"Sakha (Yakutia)","iso_a2":"RU"},"coordinates":[950925,917361]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Batagay","adm0name":"Russia","adm1name":"Sakha (Yakutia)","iso_a2":"RU"},"coordinates":[873985,905542]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Chokurdakh","adm0name":"Russia","adm1name":"Sakha (Yakutia)","iso_a2":"RU"},"coordinates":[910817,923092]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Ust Nera","adm0name":"Russia","adm1name":"Sakha (Yakutia)","iso_a2":"RU"},"coordinates":[897777,887239]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Lensk","adm0name":"Russia","adm1name":"Sakha (Yakutia)","iso_a2":"RU"},"coordinates":[819296,864481]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Aldan","adm0name":"Russia","adm1name":"Sakha (Yakutia)","iso_a2":"RU"},"coordinates":[848303,851908]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Mirnyy","adm0name":"Russia","adm1name":"Sakha (Yakutia)","iso_a2":"RU"},"coordinates":[816558,875232]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Zhigansk","adm0name":"Russia","adm1name":"Sakha (Yakutia)","iso_a2":"RU"},"coordinates":[842697,900291]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Okhotsk","adm0name":"Russia","adm1name":"Khabarovsk","iso_a2":"RU"},"coordinates":[897824,856529]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Khabarovsk","adm0name":"Russia","adm1name":"Khabarovsk","iso_a2":"RU"},"coordinates":[875333,791786]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Okha","adm0name":"Russia","adm1name":"Sakhalin","iso_a2":"RU"},"coordinates":[897076,822113]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Yuzhno Sakhalinsk","adm0name":"Russia","adm1name":"Sakhalin","iso_a2":"RU"},"coordinates":[896500,782959]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Mexicali","adm0name":"Mexico","adm1name":"Baja California","iso_a2":"MX"},"coordinates":[179216,698162]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"La Paz","adm0name":"Mexico","adm1name":"Baja California Sur","iso_a2":"MX"},"coordinates":[193556,647733]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Torreon","adm0name":"Mexico","adm1name":"Coahuila","iso_a2":"MX"},"coordinates":[212716,656217]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Culiacan","adm0name":"Mexico","adm1name":"Sinaloa","iso_a2":"MX"},"coordinates":[201716,651833]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Nogales","adm0name":"Mexico","adm1name":"Sonora","iso_a2":"MX"},"coordinates":[191820,690182]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Hermosillo","adm0name":"Mexico","adm1name":"Sonora","iso_a2":"MX"},"coordinates":[191794,677112]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Guaymas","adm0name":"Mexico","adm1name":"Sonora","iso_a2":"MX"},"coordinates":[191972,670187]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"San Luis Potosi","adm0name":"Mexico","adm1name":"San Luis Potosí","iso_a2":"MX"},"coordinates":[219439,636074]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Matamoros","adm0name":"Mexico","adm1name":"Tamaulipas","iso_a2":"MX"},"coordinates":[229166,658042]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Nuevo Laredo","adm0name":"Mexico","adm1name":"Tamaulipas","iso_a2":"MX"},"coordinates":[223472,667639]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Colima","adm0name":"Mexico","adm1name":"Colima","iso_a2":"MX"},"coordinates":[211889,618644]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Campeche","adm0name":"Mexico","adm1name":"Campeche","iso_a2":"MX"},"coordinates":[248611,622199]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Oaxaca","adm0name":"Mexico","adm1name":"Oaxaca","iso_a2":"MX"},"coordinates":[231472,605923]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Leon","adm0name":"Mexico","adm1name":"Guanajuato","iso_a2":"MX"},"coordinates":[217495,630031]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Maiduguri","adm0name":"Nigeria","adm1name":"Borno","iso_a2":"NG"},"coordinates":[536550,574933]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Port Harcourt","adm0name":"Nigeria","adm1name":"Rivers","iso_a2":"NG"},"coordinates":[519466,533225]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Makurdi","adm0name":"Nigeria","adm1name":"Benue","iso_a2":"NG"},"coordinates":[523694,550513]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Ibadan","adm0name":"Nigeria","adm1name":"Oyo","iso_a2":"NG"},"coordinates":[510910,548451]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Ogbomosho","adm0name":"Nigeria","adm1name":"Oyo","iso_a2":"NG"},"coordinates":[511772,552894]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Warri","adm0name":"Nigeria","adm1name":"Delta","iso_a2":"NG"},"coordinates":[515999,537420]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Kaduna","adm0name":"Nigeria","adm1name":"Kaduna","iso_a2":"NG"},"coordinates":[520661,567054]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Gdansk","adm0name":"Poland","adm1name":"Pomeranian","iso_a2":"PL"},"coordinates":[551777,826770]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Kraków","adm0name":"Poland","adm1name":"Lesser Poland","iso_a2":"PL"},"coordinates":[555438,801306]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Dalandzadgad","adm0name":"Mongolia","adm1name":"Ömnögovi","iso_a2":"MN"},"coordinates":[790111,762926]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Wonsan","adm0name":"North Korea","adm1name":"Kangwon-do","iso_a2":"KP"},"coordinates":[853974,736721]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Sinuiju","adm0name":"North Korea","adm1name":"P'yongan-bukto","iso_a2":"KP"},"coordinates":[845613,742204]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Dund-Us","adm0name":"Mongolia","adm1name":"Hovd","iso_a2":"MN"},"coordinates":[754536,789189]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Choybalsan","adm0name":"Mongolia","adm1name":"Dornod","iso_a2":"MN"},"coordinates":[818071,789486]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Lüderitz","adm0name":"Namibia","adm1name":"Karas","iso_a2":"NA"},"coordinates":[542109,346842]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Walvis Bay","adm0name":"Namibia","adm1name":"Erongo","iso_a2":"NA"},"coordinates":[540292,368707]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Mwanza","adm0name":"Tanzania","adm1name":"Mwanza","iso_a2":"TZ"},"coordinates":[591472,489788]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Morogoro","adm0name":"Tanzania","adm1name":"Morogoro","iso_a2":"TZ"},"coordinates":[604610,464312]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Dodoma","adm0name":"Tanzania","adm1name":"Dodoma","iso_a2":"TZ"},"coordinates":[599305,468084]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Arusha","adm0name":"Tanzania","adm1name":"Arusha","iso_a2":"TZ"},"coordinates":[601861,484811]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Napier","adm0name":"New Zealand","adm1name":"Gisborne","iso_a2":"NZ"},"coordinates":[991429,270760]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Manukau","adm0name":"New Zealand","adm1name":"Auckland","iso_a2":"NZ"},"coordinates":[985790,285513]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Hamilton","adm0name":"New Zealand","adm1name":"Auckland","iso_a2":"NZ"},"coordinates":[986944,280950]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Blenheim","adm0name":"New Zealand","adm1name":"Marlborough","iso_a2":"NZ"},"coordinates":[983220,258728]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Dunedin","adm0name":"New Zealand","adm1name":"Otago","iso_a2":"NZ"},"coordinates":[973555,232904]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Bern","adm0name":"Switzerland","adm1name":"Bern","iso_a2":"CH"},"coordinates":[520741,782673]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Malmö","adm0name":"Sweden","adm1name":"Skåne","iso_a2":"SE"},"coordinates":[536203,834018]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Laayoune","adm0name":"Morocco","adm1name":"Laâyoune - Boujdour - Sakia El Hamra","iso_a2":"MA"},"coordinates":[463333,665566]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Ternate","adm0name":"Indonesia","adm1name":"Maluku Utara","iso_a2":"ID"},"coordinates":[853785,509415]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Ambon","adm0name":"Indonesia","adm1name":"Maluku","iso_a2":"ID"},"coordinates":[856110,482698]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Raba","adm0name":"Indonesia","adm1name":"Nusa Tenggara Barat","iso_a2":"ID"},"coordinates":[829907,454656]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Jayapura","adm0name":"Indonesia","adm1name":"Papua","iso_a2":"ID"},"coordinates":[890832,489710]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Florence","adm0name":"Italy","adm1name":"Toscana","iso_a2":"IT"},"coordinates":[531250,764090]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Catania","adm0name":"Italy","adm1name":"Sicily","iso_a2":"IT"},"coordinates":[541888,726884]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Pristina","adm0name":"Kosovo","adm1name":"Pristina","iso_a2":"-99"},"coordinates":[558793,757494]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Meru","adm0name":"Kenya","adm1name":"Eastern","iso_a2":"KE"},"coordinates":[604555,505072]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Eldoret","adm0name":"Kenya","adm1name":"Rift Valley","iso_a2":"KE"},"coordinates":[597971,507798]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Banda Aceh","adm0name":"Indonesia","adm1name":"Aceh","iso_a2":"ID"},"coordinates":[764777,537597]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"George Town","adm0name":"Malaysia","adm1name":"Pulau Pinang","iso_a2":"MY"},"coordinates":[778692,536790]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Zhangye","adm0name":"China","adm1name":"Gansu","iso_a2":"CN"},"coordinates":[779027,735356]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Wuwei","adm0name":"China","adm1name":"Gansu","iso_a2":"CN"},"coordinates":[785113,729420]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Dunhuang","adm0name":"China","adm1name":"Gansu","iso_a2":"CN"},"coordinates":[762949,742540]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Tianshui","adm0name":"China","adm1name":"Gansu","iso_a2":"CN"},"coordinates":[794216,709715]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Dulan","adm0name":"China","adm1name":"Gansu","iso_a2":"CN"},"coordinates":[772962,718985]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Golmud","adm0name":"China","adm1name":"Gansu","iso_a2":"CN"},"coordinates":[763564,720465]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Yulin","adm0name":"China","adm1name":"Guangxi","iso_a2":"CN"},"coordinates":[805966,638799]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Bose","adm0name":"China","adm1name":"Guangxi","iso_a2":"CN"},"coordinates":[796147,646309]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Wuzhou","adm0name":"China","adm1name":"Guangxi","iso_a2":"CN"},"coordinates":[809222,643823]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Lupanshui","adm0name":"China","adm1name":"Guizhou","iso_a2":"CN"},"coordinates":[791198,662286]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Quanzhou","adm0name":"China","adm1name":"Fujian","iso_a2":"CN"},"coordinates":[829382,652248]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Hefei","adm0name":"China","adm1name":"Anhui","iso_a2":"CN"},"coordinates":[825772,693423]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Suzhou","adm0name":"China","adm1name":"Anhui","iso_a2":"CN"},"coordinates":[824935,704004]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Zhanjiang","adm0name":"China","adm1name":"Guangdong","iso_a2":"CN"},"coordinates":[806605,630327]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Shaoguan","adm0name":"China","adm1name":"Guangdong","iso_a2":"CN"},"coordinates":[815499,651643]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Balikpapan","adm0name":"Indonesia","adm1name":"Kalimantan Timur","iso_a2":"ID"},"coordinates":[824527,497311]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Kuching","adm0name":"Malaysia","adm1name":"Sarawak","iso_a2":"MY"},"coordinates":[806472,513781]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Antsiranana","adm0name":"Madagascar","adm1name":"Antsiranana","iso_a2":"MG"},"coordinates":[636976,431985]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Fianarantsoa","adm0name":"Madagascar","adm1name":"Fianarantsoa","iso_a2":"MG"},"coordinates":[630786,377736]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Mahajanga","adm0name":"Madagascar","adm1name":"Mahajanga","iso_a2":"MG"},"coordinates":[628736,411881]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Toliara","adm0name":"Madagascar","adm1name":"Toliary","iso_a2":"MG"},"coordinates":[621360,366340]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Surakarta","adm0name":"Indonesia","adm1name":"Jawa Tengah","iso_a2":"ID"},"coordinates":[807846,459899]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Bandar Lampung","adm0name":"Indonesia","adm1name":"Lampung","iso_a2":"ID"},"coordinates":[792411,472559]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Tanjungpandan","adm0name":"Indonesia","adm1name":"Bangka-Belitung","iso_a2":"ID"},"coordinates":[799027,488425]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Malang","adm0name":"Indonesia","adm1name":"Jawa Timur","iso_a2":"ID"},"coordinates":[812800,457451]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Kupang","adm0name":"Indonesia","adm1name":"Nusa Tenggara Timur","iso_a2":"ID"},"coordinates":[843285,444414]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Parepare","adm0name":"Indonesia","adm1name":"Sulawesi Selatan","iso_a2":"ID"},"coordinates":[832314,480920]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Cuenca","adm0name":"Ecuador","adm1name":"Azuay","iso_a2":"EC"},"coordinates":[280555,487536]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Santa Cruz","adm0name":"Ecuador","adm1name":"Galápagos","iso_a2":"EC"},"coordinates":[249027,501557]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Puerto Limon","adm0name":"Costa Rica","adm1name":"Limón","iso_a2":"CR"},"coordinates":[269351,563962]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Santiago de Cuba","adm0name":"Cuba","adm1name":"Santiago de Cuba","iso_a2":"CU"},"coordinates":[289385,623354]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Santiago","adm0name":"Dominican Republic","adm1name":"Santiago","iso_a2":"DO"},"coordinates":[303694,620244]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Manizales","adm0name":"Colombia","adm1name":"Caldas","iso_a2":"CO"},"coordinates":[290222,534695]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Pasto","adm0name":"Colombia","adm1name":"Nariño","iso_a2":"CO"},"coordinates":[285330,511907]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Barranquilla","adm0name":"Colombia","adm1name":"Atlántico","iso_a2":"CO"},"coordinates":[292217,569660]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Roseau","adm0name":"Dominica","adm1name":"Saint George","iso_a2":"DM"},"coordinates":[329480,595367]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Mbandaka","adm0name":"Congo (Kinshasa)","adm1name":"Équateur","iso_a2":"CD"},"coordinates":[550722,504955]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Moundou","adm0name":"Chad","adm1name":"Logone Oriental","iso_a2":"TD"},"coordinates":[544694,555371]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Suez","adm0name":"Egypt","adm1name":"As Suways","iso_a2":"EG"},"coordinates":[590416,682480]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Bur Said","adm0name":"Egypt","adm1name":"Bur Sa`id","iso_a2":"EG"},"coordinates":[589694,689915]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"El Faiyum","adm0name":"Egypt","adm1name":"Al Fayyum","iso_a2":"EG"},"coordinates":[585666,678363]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Aswan","adm0name":"Egypt","adm1name":"Aswan","iso_a2":"EG"},"coordinates":[591385,647422]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Asyut","adm0name":"Egypt","adm1name":"Asyut","iso_a2":"EG"},"coordinates":[586610,665803]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Kisangani","adm0name":"Congo (Kinshasa)","adm1name":"Orientale","iso_a2":"CD"},"coordinates":[570055,507798]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Assab","adm0name":"Eritrea","adm1name":"Debubawi Keyih Bahri","iso_a2":"ER"},"coordinates":[618694,581794]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Djibouti","adm0name":"Djibouti","adm1name":"Djibouti","iso_a2":"DJ"},"coordinates":[619855,573411]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Dresden","adm0name":"Germany","adm1name":"Sachsen","iso_a2":"DE"},"coordinates":[538194,807160]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Xigaze","adm0name":"China","adm1name":"Xizang","iso_a2":"CN"},"coordinates":[746897,678007]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Shache","adm0name":"China","adm1name":"Xinjiang Uygur","iso_a2":"CN"},"coordinates":[714583,732371]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Yining","adm0name":"China","adm1name":"Xinjiang Uygur","iso_a2":"CN"},"coordinates":[725971,764800]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Altay","adm0name":"China","adm1name":"Xinjiang Uygur","iso_a2":"CN"},"coordinates":[744768,788301]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Putrajaya","adm0name":"Malaysia","adm1name":"Selangor","iso_a2":"MY"},"coordinates":[782504,521981]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Shizuishan","adm0name":"China","adm1name":"Ningxia Hui","iso_a2":"CN"},"coordinates":[796580,737152]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Yulin","adm0name":"China","adm1name":"Shaanxi","iso_a2":"CN"},"coordinates":[804814,731525]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Ankang","adm0name":"China","adm1name":"Shaanxi","iso_a2":"CN"},"coordinates":[802832,698328]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Houma","adm0name":"China","adm1name":"Shanxi","iso_a2":"CN"},"coordinates":[808916,715746]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Yueyang","adm0name":"China","adm1name":"Hunan","iso_a2":"CN"},"coordinates":[814160,678790]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Hengyang","adm0name":"China","adm1name":"Hunan","iso_a2":"CN"},"coordinates":[812744,663978]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Mianyang","adm0name":"China","adm1name":"Sichuan","iso_a2":"CN"},"coordinates":[791022,691171]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Xichang","adm0name":"China","adm1name":"Sichuan","iso_a2":"CN"},"coordinates":[784166,669891]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Baoshan","adm0name":"China","adm1name":"Yunnan","iso_a2":"CN"},"coordinates":[775415,653540]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Gejiu","adm0name":"China","adm1name":"Yunnan","iso_a2":"CN"},"coordinates":[786527,643230]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Shijianzhuang","adm0name":"China","adm1name":"Hebei","iso_a2":"CN"},"coordinates":[817993,730154]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Handan","adm0name":"China","adm1name":"Hebei","iso_a2":"CN"},"coordinates":[817993,721445]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Anshan","adm0name":"China","adm1name":"Liaoning","iso_a2":"CN"},"coordinates":[841494,748312]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Dalian","adm0name":"China","adm1name":"Liaoning","iso_a2":"CN"},"coordinates":[837855,735325]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Qingdao","adm0name":"China","adm1name":"Shandong","iso_a2":"CN"},"coordinates":[834244,718542]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Linyi","adm0name":"China","adm1name":"Shandong","iso_a2":"CN"},"coordinates":[828688,712559]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Huaiyin","adm0name":"China","adm1name":"Jiangsu","iso_a2":"CN"},"coordinates":[830633,703671]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Wenzhou","adm0name":"China","adm1name":"Zhejiang","iso_a2":"CN"},"coordinates":[835133,670731]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Ningbo","adm0name":"China","adm1name":"Zhejiang","iso_a2":"CN"},"coordinates":[837632,681751]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Fukuoka","adm0name":"Japan","adm1name":"Fukuoka","iso_a2":"JP"},"coordinates":[862243,703760]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Miyazaki","adm0name":"Japan","adm1name":"Miyazaki","iso_a2":"JP"},"coordinates":[865050,693815]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Naha","adm0name":"Japan","adm1name":"Okinawa","iso_a2":"JP"},"coordinates":[854647,659980]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Kochi","adm0name":"Japan","adm1name":"Kochi","iso_a2":"JP"},"coordinates":[870937,703556]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Gorontalo","adm0name":"Indonesia","adm1name":"Gorontalo","iso_a2":"ID"},"coordinates":[841861,507976]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Tongliao","adm0name":"China","adm1name":"Nei Mongol","iso_a2":"CN"},"coordinates":[839633,763153]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Hohhot","adm0name":"China","adm1name":"Nei Mongol","iso_a2":"CN"},"coordinates":[810161,746565]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Chifeng","adm0name":"China","adm1name":"Nei Mongol","iso_a2":"CN"},"coordinates":[830410,755155]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Ulanhot","adm0name":"China","adm1name":"Nei Mongol","iso_a2":"CN"},"coordinates":[839110,777716]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Hailar","adm0name":"China","adm1name":"Nei Mongol","iso_a2":"CN"},"coordinates":[832499,796200]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Jiamusi","adm0name":"China","adm1name":"Heilongjiang","iso_a2":"CN"},"coordinates":[862077,782171]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Beian","adm0name":"China","adm1name":"Heilongjiang","iso_a2":"CN"},"coordinates":[851338,790507]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Daqing","adm0name":"China","adm1name":"Heilongjiang","iso_a2":"CN"},"coordinates":[847216,780690]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Jixi","adm0name":"China","adm1name":"Heilongjiang","iso_a2":"CN"},"coordinates":[863800,773106]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Nagoya","adm0name":"Japan","adm1name":"Aichi","iso_a2":"JP"},"coordinates":[880313,713003]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Nagano","adm0name":"Japan","adm1name":"Nagano","iso_a2":"JP"},"coordinates":[883805,721849]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Kushiro","adm0name":"Japan","adm1name":"Hokkaido","iso_a2":"JP"},"coordinates":[901040,759320]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Hakodate","adm0name":"Japan","adm1name":"Hokkaido","iso_a2":"JP"},"coordinates":[890943,752329]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Kyoto","adm0name":"Japan","adm1name":"Kyoto","iso_a2":"JP"},"coordinates":[877077,712262]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Sendai","adm0name":"Japan","adm1name":"Miyagi","iso_a2":"JP"},"coordinates":[891720,731559]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Sakata","adm0name":"Japan","adm1name":"Yamagata","iso_a2":"JP"},"coordinates":[888471,735297]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Bandundu","adm0name":"Congo (Kinshasa)","adm1name":"Bandundu","iso_a2":"CD"},"coordinates":[548277,485107]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Kananga","adm0name":"Congo (Kinshasa)","adm1name":"Kasaï-Occidental","iso_a2":"CD"},"coordinates":[562216,469833]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Kasongo","adm0name":"Congo (Kinshasa)","adm1name":"Maniema","iso_a2":"CD"},"coordinates":[574055,478353]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Mbuji-Mayi","adm0name":"Congo (Kinshasa)","adm1name":"Kasaï-Oriental","iso_a2":"CD"},"coordinates":[565549,468293]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Kalemie","adm0name":"Congo (Kinshasa)","adm1name":"Katanga","iso_a2":"CD"},"coordinates":[581110,469566]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Butembo","adm0name":"Congo (Kinshasa)","adm1name":"Nord-Kivu","iso_a2":"CD"},"coordinates":[581332,505487]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Goma","adm0name":"Congo (Kinshasa)","adm1name":"Nord-Kivu","iso_a2":"CD"},"coordinates":[581171,494771]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Mzuzu","adm0name":"Malawi","adm1name":"Mzimba","iso_a2":"MW"},"coordinates":[594500,436823]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Blantyre","adm0name":"Malawi","adm1name":"Blantyre","iso_a2":"MW"},"coordinates":[597193,411170]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Quetzaltenango","adm0name":"Guatemala","adm1name":"Quezaltenango","iso_a2":"GT"},"coordinates":[245778,592576]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Banjul","adm0name":"The Gambia","adm1name":"Banjul","iso_a2":"GM"},"coordinates":[453912,584424]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Faridabad","adm0name":"India","adm1name":"Haryana","iso_a2":"IN"},"coordinates":[714762,673180]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Srinagar","adm0name":"India","adm1name":"Jammu and Kashmir","iso_a2":"IN"},"coordinates":[707813,706752]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Vijayawada","adm0name":"India","adm1name":"Andhra Pradesh","iso_a2":"IN"},"coordinates":[723966,602600]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Thiruvananthapuram","adm0name":"India","adm1name":"Kerala","iso_a2":"IN"},"coordinates":[713744,555086]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Kochi","adm0name":"India","adm1name":"Kerala","iso_a2":"IN"},"coordinates":[711727,564062]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Cuttack","adm0name":"India","adm1name":"Orissa","iso_a2":"IN"},"coordinates":[738583,625991]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Hubli","adm0name":"India","adm1name":"Karnataka","iso_a2":"IN"},"coordinates":[708674,595728]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Mangalore","adm0name":"India","adm1name":"Karnataka","iso_a2":"IN"},"coordinates":[707916,581143]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Mysore","adm0name":"India","adm1name":"Karnataka","iso_a2":"IN"},"coordinates":[712938,577658]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Gulbarga","adm0name":"India","adm1name":"Karnataka","iso_a2":"IN"},"coordinates":[713388,607506]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Kolhapur","adm0name":"India","adm1name":"Maharashtra","iso_a2":"IN"},"coordinates":[706166,603655]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Nanded","adm0name":"India","adm1name":"Maharashtra","iso_a2":"IN"},"coordinates":[714721,618289]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Akola","adm0name":"India","adm1name":"Maharashtra","iso_a2":"IN"},"coordinates":[713916,627412]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Guwahati","adm0name":"India","adm1name":"Assam","iso_a2":"IN"},"coordinates":[754911,659712]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Kayes","adm0name":"Congo (Brazzaville)","adm1name":"Bouenza","iso_a2":"CG"},"coordinates":[536888,479953]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Franceville","adm0name":"Gabon","adm1name":"Haut-Ogooué","iso_a2":"GA"},"coordinates":[537731,495041]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Bordeaux","adm0name":"France","adm1name":"Aquitaine","iso_a2":"FR"},"coordinates":[498342,770440]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Marseille","adm0name":"France","adm1name":"Provence-Alpes-Côte-d'Azur","iso_a2":"FR"},"coordinates":[514925,761198]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Le Havre","adm0name":"France","adm1name":"Haute-Normandie","iso_a2":"FR"},"coordinates":[500291,798007]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Gao","adm0name":"Mali","adm1name":"Gao","iso_a2":"ML"},"coordinates":[499860,601088]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Coihaique","adm0name":"Chile","adm1name":"Aisén del General Carlos Ibáñez del Campo","iso_a2":"CL"},"coordinates":[299806,234740]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Arica","adm0name":"Chile","adm1name":"Arica y Parinacota","iso_a2":"CL"},"coordinates":[304750,395115]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Copiapo","adm0name":"Chile","adm1name":"Atacama","iso_a2":"CL"},"coordinates":[304610,342624]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"La Serena","adm0name":"Chile","adm1name":"Coquimbo","iso_a2":"CL"},"coordinates":[302083,327576]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Los Angeles","adm0name":"Chile","adm1name":"Bío-Bío","iso_a2":"CL"},"coordinates":[299000,282787]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Narsarsuaq","adm0name":"Greenland","adm1name":"Kommune Kujalleq","iso_a2":"GL"},"coordinates":[373843,867096]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Sisimiut","adm0name":"Greenland","adm1name":"Qeqqata Kommunia","iso_a2":"GL"},"coordinates":[350925,901360]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Upernavik","adm0name":"Greenland","adm1name":"Qaasuitsup Kommunia","iso_a2":"GL"},"coordinates":[344051,935480]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Qaanaaq","adm0name":"Greenland","adm1name":"Qaasuitsup Kommunia","iso_a2":"GL"},"coordinates":[307410,963764]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Nouadhibou","adm0name":"Mauritania","adm1name":"Dakhlet Nouadhibou","iso_a2":"MR"},"coordinates":[452622,628538]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Kayes","adm0name":"Mali","adm1name":"Kayes","iso_a2":"ML"},"coordinates":[468221,590325]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Ayoun el Atrous","adm0name":"Mauritania","adm1name":"Hodh el Gharbi","iso_a2":"MR"},"coordinates":[473287,603457]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Segou","adm0name":"Mali","adm1name":"Ségou","iso_a2":"ML"},"coordinates":[482611,584342]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Skopje","adm0name":"Macedonia","adm1name":"Centar","iso_a2":"MK"},"coordinates":[559537,753544]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Al Jawf","adm0name":"Libya","adm1name":"Al Kufrah","iso_a2":"LY"},"coordinates":[564694,648089]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Tmassah","adm0name":"Libya","adm1name":"Murzuq","iso_a2":"LY"},"coordinates":[543888,660925]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Misratah","adm0name":"Libya","adm1name":"Misratah","iso_a2":"LY"},"coordinates":[541944,696550]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Zuwarah","adm0name":"Libya","adm1name":"An Nuqat al Khams","iso_a2":"LY"},"coordinates":[533553,699835]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Kirkuk","adm0name":"Iraq","adm1name":"At-Ta'mim","iso_a2":"IQ"},"coordinates":[623311,714871]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Mosul","adm0name":"Iraq","adm1name":"Ninawa","iso_a2":"IQ"},"coordinates":[619841,720053]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"An Najaf","adm0name":"Iraq","adm1name":"An-Najaf","iso_a2":"IQ"},"coordinates":[623153,694302]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Bahir Dar","adm0name":"Ethiopia","adm1name":"Amhara","iso_a2":"ET"},"coordinates":[603842,573441]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Mekele","adm0name":"Ethiopia","adm1name":"Tigray","iso_a2":"ET"},"coordinates":[609638,584697]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Dire Dawa","adm0name":"Ethiopia","adm1name":"Dire Dawa","iso_a2":"ET"},"coordinates":[616277,561532]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Rovaniemi","adm0name":"Finland","adm1name":"Lapland","iso_a2":"FI"},"coordinates":[571433,898693]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Vaasa","adm0name":"Finland","adm1name":"Western Finland","iso_a2":"FI"},"coordinates":[560000,878550]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Tampere","adm0name":"Finland","adm1name":"Pirkanmaa","iso_a2":"FI"},"coordinates":[565972,869071]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Aqtobe","adm0name":"Kazakhstan","adm1name":"Aqtöbe","iso_a2":"KZ"},"coordinates":[658805,802598]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Rudny","adm0name":"Kazakhstan","adm1name":"Qostanay","iso_a2":"KZ"},"coordinates":[675360,818432]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Qyzylorda","adm0name":"Kazakhstan","adm1name":"Qyzylorda","iso_a2":"KZ"},"coordinates":[681846,770133]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Atyrau","adm0name":"Kazakhstan","adm1name":"Atyrau","iso_a2":"KZ"},"coordinates":[644221,783834]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Ekibastuz","adm0name":"Kazakhstan","adm1name":"Pavlodar","iso_a2":"KZ"},"coordinates":[709222,811189]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Pavlodar","adm0name":"Kazakhstan","adm1name":"Pavlodar","iso_a2":"KZ"},"coordinates":[713750,814566]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Semey","adm0name":"Kazakhstan","adm1name":"East Kazakhstan","iso_a2":"KZ"},"coordinates":[722985,803517]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Oskemen","adm0name":"Kazakhstan","adm1name":"East Kazakhstan","iso_a2":"KZ"},"coordinates":[729486,800881]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Yazd","adm0name":"Iran","adm1name":"Yazd","iso_a2":"IR"},"coordinates":[651028,693826]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Ahvaz","adm0name":"Iran","adm1name":"Khuzestan","iso_a2":"IR"},"coordinates":[635327,690046]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Basra","adm0name":"Iraq","adm1name":"Al-Basrah","iso_a2":"IQ"},"coordinates":[632809,685504]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Bandar-e-Abbas","adm0name":"Iran","adm1name":"Hormozgan","iso_a2":"IR"},"coordinates":[656311,665886]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Hamadan","adm0name":"Iran","adm1name":"Hamadan","iso_a2":"IR"},"coordinates":[634763,710864]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Tabriz","adm0name":"Iran","adm1name":"East Azarbaijan","iso_a2":"IR"},"coordinates":[628608,730369]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Ludhiana","adm0name":"India","adm1name":"Punjab","iso_a2":"IN"},"coordinates":[710750,687959]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Kota","adm0name":"India","adm1name":"Rajasthan","iso_a2":"IN"},"coordinates":[710647,653906]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Jodhpur","adm0name":"India","adm1name":"Rajasthan","iso_a2":"IN"},"coordinates":[702818,660493]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Shymkent","adm0name":"Kazakhstan","adm1name":"South Kazakhstan","iso_a2":"KZ"},"coordinates":[693319,755440]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Taraz","adm0name":"Kazakhstan","adm1name":"Zhambyl","iso_a2":"KZ"},"coordinates":[698236,758876]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Lucknow","adm0name":"India","adm1name":"Uttar Pradesh","iso_a2":"IN"},"coordinates":[724758,663830]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Saharanpur","adm0name":"India","adm1name":"Uttar Pradesh","iso_a2":"IN"},"coordinates":[715416,682273]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Ranchi","adm0name":"India","adm1name":"Jharkhand","iso_a2":"IN"},"coordinates":[737021,643183]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Bhagalpur","adm0name":"India","adm1name":"Bihar","iso_a2":"IN"},"coordinates":[741610,654191]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Raipur","adm0name":"India","adm1name":"Chhattisgarh","iso_a2":"IN"},"coordinates":[726757,630534]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Jabalpur","adm0name":"India","adm1name":"Madhya Pradesh","iso_a2":"IN"},"coordinates":[722091,642028]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Indore","adm0name":"India","adm1name":"Madhya Pradesh","iso_a2":"IN"},"coordinates":[710730,639303]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Pondicherry","adm0name":"India","adm1name":"Puducherry","iso_a2":"IN"},"coordinates":[721749,575425]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Salem","adm0name":"India","adm1name":"Tamil Nadu","iso_a2":"IN"},"coordinates":[717160,573867]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Tiruchirappalli","adm0name":"India","adm1name":"Tamil Nadu","iso_a2":"IN"},"coordinates":[718577,568772]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Pointe-Noire","adm0name":"Congo (Brazzaville)","adm1name":"Kouilou","iso_a2":"CG"},"coordinates":[533000,476457]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Kankan","adm0name":"Guinea","adm1name":"Kankan","iso_a2":"GN"},"coordinates":[474138,566272]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Nzerekore","adm0name":"Guinea","adm1name":"Nzerekore","iso_a2":"GN"},"coordinates":[475472,550691]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Bouake","adm0name":"Ivory Coast","adm1name":"Vallée du Bandama","iso_a2":"CI"},"coordinates":[486027,550276]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"St.-Denis","adm0name":"France","adm1name":"La Réunion","iso_a2":"RE"},"coordinates":[654022,381021]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Rio Branco","adm0name":"Brazil","adm1name":"Acre","iso_a2":"BR"},"coordinates":[311666,445671]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"São Luís","adm0name":"Brazil","adm1name":"Maranhão","iso_a2":"BR"},"coordinates":[377034,489822]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Porto Velho","adm0name":"Brazil","adm1name":"Rondônia","iso_a2":"BR"},"coordinates":[322500,452878]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Alvorada","adm0name":"Brazil","adm1name":"Tocantins","iso_a2":"BR"},"coordinates":[363661,430839]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Corumba","adm0name":"Brazil","adm1name":"Mato Grosso do Sul","iso_a2":"BR"},"coordinates":[339861,392057]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Belo Horizonte","adm0name":"Brazil","adm1name":"Minas Gerais","iso_a2":"BR"},"coordinates":[378008,386743]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Montes Claros","adm0name":"Brazil","adm1name":"Minas Gerais","iso_a2":"BR"},"coordinates":[378166,405660]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Uberlandia","adm0name":"Brazil","adm1name":"Minas Gerais","iso_a2":"BR"},"coordinates":[365888,392745]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Colider","adm0name":"Brazil","adm1name":"Mato Grosso","iso_a2":"BR"},"coordinates":[345970,440630]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Alta Floresta","adm0name":"Brazil","adm1name":"Mato Grosso","iso_a2":"BR"},"coordinates":[344694,446065]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Cuiaba","adm0name":"Brazil","adm1name":"Mato Grosso","iso_a2":"BR"},"coordinates":[344203,412487]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Pelotas","adm0name":"Brazil","adm1name":"Rio Grande do Sul","iso_a2":"BR"},"coordinates":[354639,316616]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Caxias do Sul","adm0name":"Brazil","adm1name":"Rio Grande do Sul","iso_a2":"BR"},"coordinates":[357860,331842]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Ponta Grossa","adm0name":"Brazil","adm1name":"Paraná","iso_a2":"BR"},"coordinates":[360666,356072]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Teresina","adm0name":"Brazil","adm1name":"Piauí","iso_a2":"BR"},"coordinates":[381161,474543]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Maceio","adm0name":"Brazil","adm1name":"Alagoas","iso_a2":"BR"},"coordinates":[400745,447735]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Vitoria da Conquista","adm0name":"Brazil","adm1name":"Bahia","iso_a2":"BR"},"coordinates":[386555,416739]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Barreiras","adm0name":"Brazil","adm1name":"Bahia","iso_a2":"BR"},"coordinates":[375000,432794]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Vila Velha","adm0name":"Brazil","adm1name":"Espírito Santo","iso_a2":"BR"},"coordinates":[388005,384050]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Natal","adm0name":"Brazil","adm1name":"Rio Grande do Norte","iso_a2":"BR"},"coordinates":[402105,470485]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Thompson","adm0name":"Canada","adm1name":"Manitoba","iso_a2":"CA"},"coordinates":[228148,835005]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Brandon","adm0name":"Canada","adm1name":"Manitoba","iso_a2":"CA"},"coordinates":[222361,799952]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Fort Smith","adm0name":"Canada","adm1name":"Alberta","iso_a2":"CA"},"coordinates":[189212,860185]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Fort McMurray","adm0name":"Canada","adm1name":"Alberta","iso_a2":"CA"},"coordinates":[190601,840831]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Peace River","adm0name":"Canada","adm1name":"Alberta","iso_a2":"CA"},"coordinates":[174213,837869]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Fort St. John","adm0name":"Canada","adm1name":"British Columbia","iso_a2":"CA"},"coordinates":[164352,837967]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Iqaluit","adm0name":"Canada","adm1name":"Nunavut","iso_a2":"CA"},"coordinates":[309722,882404]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Cambridge Bay","adm0name":"Canada","adm1name":"Nunavut","iso_a2":"CA"},"coordinates":[208240,914197]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Kugluktuk","adm0name":"Canada","adm1name":"Nunavut","iso_a2":"CA"},"coordinates":[180207,906387]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Chesterfield Inlet","adm0name":"Canada","adm1name":"Nunavut","iso_a2":"CA"},"coordinates":[248055,879962]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Arviat","adm0name":"Canada","adm1name":"Nunavut","iso_a2":"CA"},"coordinates":[238726,866752]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Taloyoak","adm0name":"Canada","adm1name":"Nunavut","iso_a2":"CA"},"coordinates":[240185,916664]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Igloolik","adm0name":"Canada","adm1name":"Nunavut","iso_a2":"CA"},"coordinates":[272796,915024]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Dawson City","adm0name":"Canada","adm1name":"Yukon","iso_a2":"CA"},"coordinates":[112731,884277]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Timmins","adm0name":"Canada","adm1name":"Ontario","iso_a2":"CA"},"coordinates":[274074,791855]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"North Bay","adm0name":"Canada","adm1name":"Ontario","iso_a2":"CA"},"coordinates":[279305,779019]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Kuujjuarapik","adm0name":"Canada","adm1name":"Québec","iso_a2":"CA"},"coordinates":[283983,832229]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Kuujjuaq","adm0name":"Canada","adm1name":"Québec","iso_a2":"CA"},"coordinates":[310000,848928]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Sydney","adm0name":"Canada","adm1name":"Nova Scotia","iso_a2":"CA"},"coordinates":[332833,777634]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Labrador City","adm0name":"Canada","adm1name":"Newfoundland and Labrador","iso_a2":"CA"},"coordinates":[314122,818366]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Ebolowa","adm0name":"Cameroon","adm1name":"Sud","iso_a2":"CM"},"coordinates":[530972,521898]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Bambari","adm0name":"Central African Republic","adm1name":"Ouaka","iso_a2":"CF"},"coordinates":[557408,538854]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Venice","adm0name":"Italy","adm1name":"Veneto","iso_a2":"IT"},"coordinates":[534263,773916]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"El Calafate","adm0name":"Argentina","adm1name":"Santa Cruz","iso_a2":"AR"},"coordinates":[299166,206520]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"San Juan","adm0name":"Argentina","adm1name":"San Juan","iso_a2":"AR"},"coordinates":[309667,317800]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Rawson","adm0name":"Argentina","adm1name":"Chubut","iso_a2":"AR"},"coordinates":[319167,248188]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Neuquen","adm0name":"Argentina","adm1name":"Neuquén","iso_a2":"AR"},"coordinates":[310944,273959]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Trinidad","adm0name":"Bolivia","adm1name":"El Beni","iso_a2":"BO"},"coordinates":[319722,416838]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Santa Rosa","adm0name":"Argentina","adm1name":"La Pampa","iso_a2":"AR"},"coordinates":[321389,287763]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"San Carlos de Bariloche","adm0name":"Argentina","adm1name":"Río Negro","iso_a2":"AR"},"coordinates":[301944,260926]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Salta","adm0name":"Argentina","adm1name":"Salta","iso_a2":"AR"},"coordinates":[318286,357889]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Tucumán","adm0name":"Argentina","adm1name":"Tucumán","iso_a2":"AR"},"coordinates":[318837,345858]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Formosa","adm0name":"Argentina","adm1name":"Formosa","iso_a2":"AR"},"coordinates":[338380,349657]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Santa Fe","adm0name":"Argentina","adm1name":"Santa Fe","iso_a2":"AR"},"coordinates":[331416,317363]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Rosario","adm0name":"Argentina","adm1name":"Santa Fe","iso_a2":"AR"},"coordinates":[331477,309511]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Campinas","adm0name":"Brazil","adm1name":"São Paulo","iso_a2":"BR"},"coordinates":[369161,369059]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Sorocaba","adm0name":"Brazil","adm1name":"São Paulo","iso_a2":"BR"},"coordinates":[368139,365552]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Ribeirao Preto","adm0name":"Brazil","adm1name":"São Paulo","iso_a2":"BR"},"coordinates":[367139,379296]},{"type":"Point","properties":{"scalerank":4,"labelrank":1,"name":"Petrolina","adm0name":"Brazil","adm1name":"Pernambuco","iso_a2":"BR"},"coordinates":[387472,449145]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Bamenda","adm0name":"Cameroon","adm1name":"Nord-Ouest","iso_a2":"CM"},"coordinates":[528194,540027]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Garoua","adm0name":"Cameroon","adm1name":"Nord","iso_a2":"CM"},"coordinates":[537194,559815]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Herat","adm0name":"Afghanistan","adm1name":"Hirat","iso_a2":"AF"},"coordinates":[672694,708103]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Mazar-e Sharif","adm0name":"Afghanistan","adm1name":"Balkh","iso_a2":"AF"},"coordinates":[686388,722144]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Battambang","adm0name":"Cambodia","adm1name":"Batdâmbâng","iso_a2":"KH"},"coordinates":[786666,582327]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Siem Reap","adm0name":"Cambodia","adm1name":"Siemréab","iso_a2":"KH"},"coordinates":[788471,583907]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Malanje","adm0name":"Angola","adm1name":"Malanje","iso_a2":"AO"},"coordinates":[545389,448198]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Benguela","adm0name":"Angola","adm1name":"Benguela","iso_a2":"AO"},"coordinates":[537242,430198]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Lubango","adm0name":"Angola","adm1name":"Huíla","iso_a2":"AO"},"coordinates":[537471,416383]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Namibe","adm0name":"Angola","adm1name":"Namibe","iso_a2":"AO"},"coordinates":[533778,414725]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Tarija","adm0name":"Bolivia","adm1name":"Tarija","iso_a2":"BO"},"coordinates":[320138,377243]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Bridgetown","adm0name":"Barbados","adm1name":"Saint Michael","iso_a2":"BB"},"coordinates":[334399,582339]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Annaba","adm0name":"Algeria","adm1name":"Annaba","iso_a2":"DZ"},"coordinates":[521555,723448]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Parakou","adm0name":"Benin","adm1name":"Borgou","iso_a2":"BJ"},"coordinates":[507278,560052]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Porto-Novo","adm0name":"Benin","adm1name":"Ouémé","iso_a2":"BJ"},"coordinates":[507268,543127]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Constantine","adm0name":"Algeria","adm1name":"Constantine","iso_a2":"DZ"},"coordinates":[518332,720130]},{"type":"Point","properties":{"scalerank":4,"labelrank":6,"name":"Brest","adm0name":"Belarus","adm1name":"Brest","iso_a2":"BY"},"coordinates":[565833,813381]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Khulna","adm0name":"Bangladesh","adm1name":"Khulna","iso_a2":"BD"},"coordinates":[748772,640043]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Francistown","adm0name":"Botswana","adm1name":"Central","iso_a2":"BW"},"coordinates":[576388,379296]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Mahalapye","adm0name":"Botswana","adm1name":"Central","iso_a2":"BW"},"coordinates":[574500,367862]},{"type":"Point","properties":{"scalerank":4,"labelrank":7,"name":"Serowe","adm0name":"Botswana","adm1name":"Central","iso_a2":"BW"},"coordinates":[574194,372068]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Katherine","adm0name":"Australia","adm1name":"Northern Territory","iso_a2":"AU"},"coordinates":[867406,419010]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Busselton","adm0name":"Australia","adm1name":"Western Australia","iso_a2":"AU"},"coordinates":[820412,305321]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Mandurah","adm0name":"Australia","adm1name":"Western Australia","iso_a2":"AU"},"coordinates":[821519,312034]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Broome","adm0name":"Australia","adm1name":"Western Australia","iso_a2":"AU"},"coordinates":[839530,398304]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Kalgoorlie","adm0name":"Australia","adm1name":"Western Australia","iso_a2":"AU"},"coordinates":[837388,322627]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Albany","adm0name":"Australia","adm1name":"Western Australia","iso_a2":"AU"},"coordinates":[827476,297261]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Port Hedland","adm0name":"Australia","adm1name":"Western Australia","iso_a2":"AU"},"coordinates":[829460,384389]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Karratha","adm0name":"Australia","adm1name":"Western Australia","iso_a2":"AU"},"coordinates":[824638,381901]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Geraldton","adm0name":"Australia","adm1name":"Western Australia","iso_a2":"AU"},"coordinates":[818332,334291]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Griffith","adm0name":"Australia","adm1name":"New South Wales","iso_a2":"AU"},"coordinates":[905665,301567]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Orange","adm0name":"Australia","adm1name":"New South Wales","iso_a2":"AU"},"coordinates":[914166,307551]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Dubbo","adm0name":"Australia","adm1name":"New South Wales","iso_a2":"AU"},"coordinates":[912769,313595]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Armidale","adm0name":"Australia","adm1name":"New South Wales","iso_a2":"AU"},"coordinates":[921297,323948]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Broken Hill","adm0name":"Australia","adm1name":"New South Wales","iso_a2":"AU"},"coordinates":[892869,315431]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Port Lincoln","adm0name":"Australia","adm1name":"South Australia","iso_a2":"AU"},"coordinates":[877407,298942]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Whyalla","adm0name":"Australia","adm1name":"South Australia","iso_a2":"AU"},"coordinates":[882114,309062]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Portland","adm0name":"Australia","adm1name":"Victoria","iso_a2":"AU"},"coordinates":[893305,277573]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Bendigo","adm0name":"Australia","adm1name":"Victoria","iso_a2":"AU"},"coordinates":[900777,286934]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Wangaratta","adm0name":"Australia","adm1name":"Victoria","iso_a2":"AU"},"coordinates":[906388,289304]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Windorah","adm0name":"Australia","adm1name":"Queensland","iso_a2":"AU"},"coordinates":[896250,354039]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Mount Isa","adm0name":"Australia","adm1name":"Queensland","iso_a2":"AU"},"coordinates":[887471,381939]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Rockhampton","adm0name":"Australia","adm1name":"Queensland","iso_a2":"AU"},"coordinates":[918110,366299]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Cairns","adm0name":"Australia","adm1name":"Queensland","iso_a2":"AU"},"coordinates":[904897,404666]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Gold Coast","adm0name":"Australia","adm1name":"Queensland","iso_a2":"AU"},"coordinates":[926245,338350]},{"type":"Point","properties":{"scalerank":4,"labelrank":3,"name":"Devonport","adm0name":"Australia","adm1name":"Tasmania","iso_a2":"AU"},"coordinates":[906475,260673]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Bobo Dioulasso","adm0name":"Burkina Faso","adm1name":"Houet","iso_a2":"BF"},"coordinates":[488083,570952]},{"type":"Point","properties":{"scalerank":4,"labelrank":2,"name":"Rajshahi","adm0name":"Bangladesh","adm1name":"Rajshahi","iso_a2":"BD"},"coordinates":[746118,649137]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Mandalay","adm0name":"Myanmar","adm1name":"Mandalay","iso_a2":"MM"},"coordinates":[766897,634889]},{"type":"Point","properties":{"scalerank":4,"labelrank":5,"name":"Sittwe","adm0name":"Myanmar","adm1name":"Rakhine","iso_a2":"MM"},"coordinates":[758000,624035]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Bujumbura","adm0name":"Burundi","adm1name":"Bujumbura Mairie","iso_a2":"BI"},"coordinates":[581555,484715]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Pago Pago","adm0name":"American Samoa","iso_a2":"AS"},"coordinates":[25815,420136]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Kingstown","adm0name":"Saint Vincent and the Grenadines","iso_a2":"VC"},"coordinates":[329966,582614]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Castries","adm0name":"Saint Lucia","iso_a2":"LC"},"coordinates":[330555,587671]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Basseterre","adm0name":"Saint Kitts and Nevis","iso_a2":"KN"},"coordinates":[325786,607222]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Las Palmas","adm0name":"Spain","iso_a2":"ES"},"coordinates":[457138,671194]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Berbera","adm0name":"Somaliland","iso_a2":"-99"},"coordinates":[625045,566542]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Port Louis","adm0name":"Mauritius","iso_a2":"MU"},"coordinates":[659722,385241]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Gaza","adm0name":"Palestine","iso_a2":"PS"},"coordinates":[595680,691515]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Saint George's","adm0name":"Grenada","iso_a2":"GD"},"coordinates":[328495,576122]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Papeete","adm0name":"French Polynesia","iso_a2":"PF"},"coordinates":[84537,400842]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Manama","adm0name":"Bahrain","iso_a2":"BH"},"coordinates":[640508,660152]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Freeport","adm0name":"The Bahamas","iso_a2":"BS"},"coordinates":[281389,661912]},{"type":"Point","properties":{"scalerank":4,"labelrank":0,"name":"Saint John's","adm0name":"Antigua and Barbuda","iso_a2":"AG"},"coordinates":[328194,606132]},{"type":"Point","properties":{"scalerank":4,"labelrank":8,"name":"Taichung","adm0name":"Taiwan","adm1name":"Taichung City","iso_a2":"TW"},"coordinates":[835226,647805]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Kozhikode","adm0name":"India","adm1name":"Kerala","iso_a2":"IN"},"coordinates":[710466,571381]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Bhubaneshwar","adm0name":"India","adm1name":"Orissa","iso_a2":"IN"},"coordinates":[738403,624820]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Jamshedpur","adm0name":"India","adm1name":"Jharkhand","iso_a2":"IN"},"coordinates":[739431,639733]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Montevideo","adm0name":"Uruguay","adm1name":"Montevideo","iso_a2":"UY"},"coordinates":[343963,298213]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Helena","adm0name":"United States of America","adm1name":"Montana","iso_a2":"US"},"coordinates":[188791,780754]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Bismarck","adm0name":"United States of America","adm1name":"North Dakota","iso_a2":"US"},"coordinates":[220046,782031]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Boise","adm0name":"United States of America","adm1name":"Idaho","iso_a2":"US"},"coordinates":[177146,763074]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"San Jose","adm0name":"United States of America","adm1name":"California","iso_a2":"US"},"coordinates":[161523,725711]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Sacramento","adm0name":"United States of America","adm1name":"California","iso_a2":"US"},"coordinates":[162578,733265]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Las Vegas","adm0name":"United States of America","adm1name":"Nevada","iso_a2":"US"},"coordinates":[179939,719253]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Santa Fe","adm0name":"United States of America","adm1name":"New Mexico","iso_a2":"US"},"coordinates":[205729,716142]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Portland","adm0name":"United States of America","adm1name":"Oregon","iso_a2":"US"},"coordinates":[159217,774410]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Salt Lake City","adm0name":"United States of America","adm1name":"Utah","iso_a2":"US"},"coordinates":[189078,746298]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Cheyenne","adm0name":"United States of America","adm1name":"Wyoming","iso_a2":"US"},"coordinates":[208834,748449]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Des Moines","adm0name":"United States of America","adm1name":"Iowa","iso_a2":"US"},"coordinates":[239944,751055]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Omaha","adm0name":"United States of America","adm1name":"Nebraska","iso_a2":"US"},"coordinates":[233305,749041]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Oklahoma City","adm0name":"United States of America","adm1name":"Oklahoma","iso_a2":"US"},"coordinates":[229109,714869]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Pierre","adm0name":"United States of America","adm1name":"South Dakota","iso_a2":"US"},"coordinates":[221248,767575]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"San Antonio","adm0name":"United States of America","adm1name":"Texas","iso_a2":"US"},"coordinates":[226363,679425]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"San Cristobal","adm0name":"Venezuela","adm1name":"Táchira","iso_a2":"VE"},"coordinates":[299305,550750]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Valencia","adm0name":"Venezuela","adm1name":"Carabobo","iso_a2":"VE"},"coordinates":[311161,565336]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Jackson","adm0name":"United States of America","adm1name":"Mississippi","iso_a2":"US"},"coordinates":[249486,696070]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Raleigh","adm0name":"United States of America","adm1name":"North Carolina","iso_a2":"US"},"coordinates":[281542,716923]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Cleveland","adm0name":"United States of America","adm1name":"Ohio","iso_a2":"US"},"coordinates":[273064,750415]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Cincinnati","adm0name":"United States of America","adm1name":"Ohio","iso_a2":"US"},"coordinates":[265392,736741]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Nashville","adm0name":"United States of America","adm1name":"Tennessee","iso_a2":"US"},"coordinates":[258939,719016]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Memphis","adm0name":"United States of America","adm1name":"Tennessee","iso_a2":"US"},"coordinates":[249994,712796]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Norfolk","adm0name":"United States of America","adm1name":"Virginia","iso_a2":"US"},"coordinates":[288111,723033]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Milwaukee","adm0name":"United States of America","adm1name":"Wisconsin","iso_a2":"US"},"coordinates":[255773,759792]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Buffalo","adm0name":"United States of America","adm1name":"New York","iso_a2":"US"},"coordinates":[280884,758769]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Pittsburgh","adm0name":"United States of America","adm1name":"Pennsylvania","iso_a2":"US"},"coordinates":[277772,744254]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Ciudad Guayana","adm0name":"Venezuela","adm1name":"Bolívar","iso_a2":"VE"},"coordinates":[326055,554305]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Lome","adm0name":"Togo","adm1name":"Maritime","iso_a2":"TG"},"coordinates":[503390,541057]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Tunis","adm0name":"Tunisia","adm1name":"Tunis","iso_a2":"TN"},"coordinates":[528276,722753]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Kodiak","adm0name":"United States of America","adm1name":"Alaska","iso_a2":"US"},"coordinates":[76648,847091]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Cold Bay","adm0name":"United States of America","adm1name":"Alaska","iso_a2":"US"},"coordinates":[48014,831747]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Bethel","adm0name":"United States of America","adm1name":"Alaska","iso_a2":"US"},"coordinates":[50678,864884]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Point Hope","adm0name":"United States of America","adm1name":"Alaska","iso_a2":"US"},"coordinates":[36644,909640]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Barrow","adm0name":"United States of America","adm1name":"Alaska","iso_a2":"US"},"coordinates":[64476,927075]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Nome","adm0name":"United States of America","adm1name":"Alaska","iso_a2":"US"},"coordinates":[40538,886881]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Valdez","adm0name":"United States of America","adm1name":"Alaska","iso_a2":"US"},"coordinates":[93477,866914]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Juneau","adm0name":"United States of America","adm1name":"Alaska","iso_a2":"US"},"coordinates":[126611,850197]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Fairbanks","adm0name":"United States of America","adm1name":"Alaska","iso_a2":"US"},"coordinates":[89693,888841]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Prudhoe Bay","adm0name":"United States of America","adm1name":"Alaska","iso_a2":"US"},"coordinates":[87030,921160]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Sevastapol","adm0name":"Ukraine","adm1name":"Crimea","iso_a2":"UA"},"coordinates":[592958,768948]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Abu Dhabi","adm0name":"United Arab Emirates","adm1name":"Abu Dhabi","iso_a2":"AE"},"coordinates":[651018,649669]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Ashgabat","adm0name":"Turkmenistan","adm1name":"Ahal","iso_a2":"TM"},"coordinates":[662175,729550]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Samarqand","adm0name":"Uzbekistan","adm1name":"Samarkand","iso_a2":"UZ"},"coordinates":[685958,739740]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Lusaka","adm0name":"Zambia","adm1name":"Lusaka","iso_a2":"ZM"},"coordinates":[578559,413393]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Harare","adm0name":"Zimbabwe","adm1name":"Harare","iso_a2":"ZW"},"coordinates":[586230,399168]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Bulawayo","adm0name":"Zimbabwe","adm1name":"Bulawayo","iso_a2":"ZW"},"coordinates":[579388,385221]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Dili","adm0name":"East Timor","adm1name":"Dili","iso_a2":"TL"},"coordinates":[848831,454008]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Port Vila","adm0name":"Vanuatu","adm1name":"Shefa","iso_a2":"VU"},"coordinates":[967545,399657]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Tegucigalpa","adm0name":"Honduras","adm1name":"Francisco Morazán","iso_a2":"HN"},"coordinates":[257724,588276]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Georgetown","adm0name":"Guyana","adm1name":"East Berbice-Corentyne","iso_a2":"GY"},"coordinates":[338424,545015]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Reykjavík","adm0name":"Iceland","adm1name":"Suðurnes","iso_a2":"IS"},"coordinates":[439028,884771]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Port-au-Prince","adm0name":"Haiti","adm1name":"Ouest","iso_a2":"HT"},"coordinates":[299061,614574]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Glasgow","adm0name":"United Kingdom","adm1name":"Glasgow","iso_a2":"GB"},"coordinates":[488187,835753]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Kampala","adm0name":"Uganda","adm1name":"Kampala","iso_a2":"UG"},"coordinates":[590503,506605]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Aden","adm0name":"Yemen","adm1name":"`Adan","iso_a2":"YE"},"coordinates":[625025,580430]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Paramaribo","adm0name":"Suriname","adm1name":"Paramaribo","iso_a2":"SR"},"coordinates":[346758,539286]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Seville","adm0name":"Spain","adm1name":"Andalucía","iso_a2":"ES"},"coordinates":[483389,726322]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Zinder","adm0name":"Niger","adm1name":"Zinder","iso_a2":"NE"},"coordinates":[524953,586474]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Niamey","adm0name":"Niger","adm1name":"Niamey","iso_a2":"NE"},"coordinates":[505874,584808]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Port Sudan","adm0name":"Sudan","adm1name":"Red Sea","iso_a2":"SD"},"coordinates":[603378,620930]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Dushanbe","adm0name":"Tajikistan","adm1name":"Tadzhikistan Territories","iso_a2":"TJ"},"coordinates":[691038,733164]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Cusco","adm0name":"Peru","adm1name":"Cusco","iso_a2":"PE"},"coordinates":[300077,424589]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Tacna","adm0name":"Peru","adm1name":"Tacna","iso_a2":"PE"},"coordinates":[304861,398077]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Trujillo","adm0name":"Peru","adm1name":"La Libertad","iso_a2":"PE"},"coordinates":[280499,456610]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Ica","adm0name":"Peru","adm1name":"Ica","iso_a2":"PE"},"coordinates":[289651,421372]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Asuncion","adm0name":"Paraguay","adm1name":"Asunción","iso_a2":"PY"},"coordinates":[339879,354861]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Managua","adm0name":"Nicaragua","adm1name":"Managua","iso_a2":"NI"},"coordinates":[260359,576729]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Freetown","adm0name":"Sierra Leone","adm1name":"Western","iso_a2":"SL"},"coordinates":[463233,554909]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Agadez","adm0name":"Niger","adm1name":"Agadez","iso_a2":"NE"},"coordinates":[522174,605409]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Niyala","adm0name":"Sudan","adm1name":"South Darfur","iso_a2":"SD"},"coordinates":[569138,576166]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Wau","adm0name":"South Sudan","adm1name":"West Bahr-al-Ghazal","iso_a2":"SS"},"coordinates":[577750,550335]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Dongola","adm0name":"Sudan","adm1name":"Northern","iso_a2":"SD"},"coordinates":[584676,618269]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Kassala","adm0name":"Sudan","adm1name":"Kassala","iso_a2":"SD"},"coordinates":[601083,596309]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Tromsø","adm0name":"Norway","adm1name":"Troms","iso_a2":"NO"},"coordinates":[552755,917266]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Trondheim","adm0name":"Norway","adm1name":"Sør-Trøndelag","iso_a2":"NO"},"coordinates":[528934,880426]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Bergen","adm0name":"Norway","adm1name":"Hordaland","iso_a2":"NO"},"coordinates":[514790,862501]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Islamabad","adm0name":"Pakistan","adm1name":"F.C.T.","iso_a2":"PK"},"coordinates":[703235,704383]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Multan","adm0name":"Pakistan","adm1name":"Punjab","iso_a2":"PK"},"coordinates":[698480,683647]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Hyderabad","adm0name":"Pakistan","adm1name":"Sind","iso_a2":"PK"},"coordinates":[689924,655091]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Peshawar","adm0name":"Pakistan","adm1name":"N.W.F.P.","iso_a2":"PK"},"coordinates":[698702,706190]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Kathmandu","adm0name":"Nepal","adm1name":"Bhaktapur","iso_a2":"NP"},"coordinates":[736984,668935]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Nacala","adm0name":"Mozambique","adm1name":"Nampula","iso_a2":"MZ"},"coordinates":[613096,418702]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Bloemfontein","adm0name":"South Africa","adm1name":"Orange Free State","iso_a2":"ZA"},"coordinates":[572860,332197]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Pretoria","adm0name":"South Africa","adm1name":"Gauteng","iso_a2":"ZA"},"coordinates":[578409,352429]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Port Moresby","adm0name":"Papua New Guinea","adm1name":"Central","iso_a2":"PG"},"coordinates":[908867,448644]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Honiara","adm0name":"Solomon Islands","adm1name":"Guadalcanal","iso_a2":"SB"},"coordinates":[944304,448802]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Panama City","adm0name":"Panama","adm1name":"Panama","iso_a2":"PA"},"coordinates":[279069,557859]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Fez","adm0name":"Morocco","adm1name":"Fès - Boulemane","iso_a2":"MA"},"coordinates":[486104,706483]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Rabat","adm0name":"Morocco","adm1name":"Rabat - Salé - Zemmour - Zaer","iso_a2":"MA"},"coordinates":[481009,706298]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Marrakesh","adm0name":"Morocco","adm1name":"Marrakech - Tensift - Al Haouz","iso_a2":"MA"},"coordinates":[477772,692119]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Chisinau","adm0name":"Moldova","adm1name":"Chisinau","iso_a2":"MD"},"coordinates":[580159,783196]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Beira","adm0name":"Mozambique","adm1name":"Sofala","iso_a2":"MZ"},"coordinates":[596860,387294]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Port Elizabeth","adm0name":"South Africa","adm1name":"Eastern Cape","iso_a2":"ZA"},"coordinates":[571105,303474]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Maputo","adm0name":"Mozambique","adm1name":"Maputo","iso_a2":"MZ"},"coordinates":[590520,350958]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Tomsk","adm0name":"Russia","adm1name":"Tomsk","iso_a2":"RU"},"coordinates":[736041,839419]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Anadyr","adm0name":"Russia","adm1name":"Chukchi Autonomous Okrug","iso_a2":"RU"},"coordinates":[992986,888248]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Murmansk","adm0name":"Russia","adm1name":"Murmansk","iso_a2":"RU"},"coordinates":[591944,913327]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Archangel","adm0name":"Russia","adm1name":"Arkhangel'sk","iso_a2":"RU"},"coordinates":[612625,887289]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Nizhny Novgorod","adm0name":"Russia","adm1name":"Nizhegorod","iso_a2":"RU"},"coordinates":[622217,838471]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Volgograd","adm0name":"Russia","adm1name":"Volgograd","iso_a2":"RU"},"coordinates":[623605,793308]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Ufa","adm0name":"Russia","adm1name":"Bashkortostan","iso_a2":"RU"},"coordinates":[655660,829329]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Yekaterinburg","adm0name":"Russia","adm1name":"Sverdlovsk","iso_a2":"RU"},"coordinates":[668327,841534]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Samara","adm0name":"Russia","adm1name":"Samara","iso_a2":"RU"},"coordinates":[639303,819880]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Kazan","adm0name":"Russia","adm1name":"Tatarstan","iso_a2":"RU"},"coordinates":[636456,835017]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Surgut","adm0name":"Russia","adm1name":"Khanty-Mansiy","iso_a2":"RU"},"coordinates":[703958,867649]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Barnaul","adm0name":"Russia","adm1name":"Altay","iso_a2":"RU"},"coordinates":[732624,820816]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Novosibirsk","adm0name":"Russia","adm1name":"Novosibirsk","iso_a2":"RU"},"coordinates":[730438,830751]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Mogadishu","adm0name":"Somalia","adm1name":"Banaadir","iso_a2":"SO"},"coordinates":[626013,516973]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Muscat","adm0name":"Oman","adm1name":"Muscat","iso_a2":"OM"},"coordinates":[662758,644613]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Colombo","adm0name":"Sri Lanka","adm1name":"Colombo","iso_a2":"LK"},"coordinates":[721827,545785]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Cebu","adm0name":"Philippines","adm1name":"Cebu","iso_a2":"PH"},"coordinates":[844161,565868]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Iloilo","adm0name":"Philippines","adm1name":"Iloilo","iso_a2":"PH"},"coordinates":[840402,568139]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Davao","adm0name":"Philippines","adm1name":"Davao Del Sur","iso_a2":"PH"},"coordinates":[848966,546852]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Bratsk","adm0name":"Russia","adm1name":"Irkutsk","iso_a2":"RU"},"coordinates":[782263,837417]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Irkutsk","adm0name":"Russia","adm1name":"Irkutsk","iso_a2":"RU"},"coordinates":[789569,814685]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Krasnoyarsk","adm0name":"Russia","adm1name":"Krasnoyarsk","iso_a2":"RU"},"coordinates":[757955,836581]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Dickson","adm0name":"Russia","adm1name":"Taymyr","iso_a2":"RU"},"coordinates":[723736,940205]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Chita","adm0name":"Russia","adm1name":"Chita","iso_a2":"RU"},"coordinates":[815180,813115]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Vladivostok","adm0name":"Russia","adm1name":"Primor'ye","iso_a2":"RU"},"coordinates":[866416,760239]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Nizhneyansk","adm0name":"Russia","adm1name":"Sakha (Yakutia)","iso_a2":"RU"},"coordinates":[877962,927920]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Yakutsk","adm0name":"Russia","adm1name":"Sakha (Yakutia)","iso_a2":"RU"},"coordinates":[860374,872240]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Tiksi","adm0name":"Russia","adm1name":"Sakha (Yakutia)","iso_a2":"RU"},"coordinates":[857874,929067]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Magadan","adm0name":"Russia","adm1name":"Maga Buryatdan","iso_a2":"RU"},"coordinates":[918916,857666]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Tijuana","adm0name":"Mexico","adm1name":"Baja California","iso_a2":"MX"},"coordinates":[174772,697273]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Chihuahua","adm0name":"Mexico","adm1name":"Chihuahua","iso_a2":"MX"},"coordinates":[205314,674434]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Mazatlan","adm0name":"Mexico","adm1name":"Sinaloa","iso_a2":"MX"},"coordinates":[204388,642289]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Tampico","adm0name":"Mexico","adm1name":"Tamaulipas","iso_a2":"MX"},"coordinates":[228139,636832]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Acapulco","adm0name":"Mexico","adm1name":"Guerrero","iso_a2":"MX"},"coordinates":[222456,604544]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Veracruz","adm0name":"Mexico","adm1name":"Veracruz","iso_a2":"MX"},"coordinates":[232889,618332]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Tuxtla Gutierrez","adm0name":"Mexico","adm1name":"Chiapas","iso_a2":"MX"},"coordinates":[241250,603952]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Cancun","adm0name":"Mexico","adm1name":"Quintana Roo","iso_a2":"MX"},"coordinates":[258805,630138]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Merida","adm0name":"Mexico","adm1name":"Yucatán","iso_a2":"MX"},"coordinates":[251059,628944]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Enugu","adm0name":"Nigeria","adm1name":"Enugu","iso_a2":"NG"},"coordinates":[520833,542930]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Sokoto","adm0name":"Nigeria","adm1name":"Sokoto","iso_a2":"NG"},"coordinates":[514555,582090]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Perm","adm0name":"Russia","adm1name":"Perm'","iso_a2":"RU"},"coordinates":[656244,848347]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Erdenet","adm0name":"Mongolia","adm1name":"Orhon","iso_a2":"MN"},"coordinates":[789217,795331]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Ulaanbaatar","adm0name":"Mongolia","adm1name":"Ulaanbaatar","iso_a2":"MN"},"coordinates":[796984,788609]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Wellington","adm0name":"New Zealand","adm1name":"Manawatu-Wanganui","iso_a2":"NZ"},"coordinates":[985509,260037]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Mbeya","adm0name":"Tanzania","adm1name":"Mbeya","iso_a2":"TZ"},"coordinates":[592861,452049]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Windhoek","adm0name":"Namibia","adm1name":"Khomas","iso_a2":"NA"},"coordinates":[547454,371002]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Grootfontein","adm0name":"Namibia","adm1name":"Otjozondjupa","iso_a2":"NA"},"coordinates":[550323,388796]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Zanzibar","adm0name":"Tanzania","adm1name":"Zanzibar West","iso_a2":"TZ"},"coordinates":[608889,468223]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Christchurch","adm0name":"New Zealand","adm1name":"Canterbury","iso_a2":"NZ"},"coordinates":[979527,246796]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Valencia","adm0name":"Spain","adm1name":"Comunidad Valenciana","iso_a2":"ES"},"coordinates":[498883,738656]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Palana","adm0name":"Russia","adm1name":"Kamchatka","iso_a2":"RU"},"coordinates":[944305,854757]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Petropavlovsk Kamchatskiy","adm0name":"Russia","adm1name":"Kamchatka","iso_a2":"RU"},"coordinates":[940619,819080]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Abuja","adm0name":"Nigeria","adm1name":"Federal Capital Territory","iso_a2":"NG"},"coordinates":[520920,558542]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Padang","adm0name":"Indonesia","adm1name":"Sumatera Barat","iso_a2":"ID"},"coordinates":[778771,499041]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Bissau","adm0name":"Guinea Bissau","adm1name":"Bissau","iso_a2":"GW"},"coordinates":[456670,575011]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Palermo","adm0name":"Italy","adm1name":"Sicily","iso_a2":"IT"},"coordinates":[537078,730599]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Amman","adm0name":"Jordan","adm1name":"Amman","iso_a2":"JO"},"coordinates":[599808,694015]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Vilnius","adm0name":"Lithuania","adm1name":"Vilniaus","iso_a2":"LT"},"coordinates":[570324,828686]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Riga","adm0name":"Latvia","adm1name":"Riga","iso_a2":"LV"},"coordinates":[566943,842115]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Bishkek","adm0name":"Kyrgyzstan","adm1name":"Bishkek","iso_a2":"KG"},"coordinates":[707175,758728]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Jiayuguan","adm0name":"China","adm1name":"Gansu","iso_a2":"CN"},"coordinates":[773055,740629]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Xining","adm0name":"China","adm1name":"Gansu","iso_a2":"CN"},"coordinates":[782688,721682]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Guilin","adm0name":"China","adm1name":"Guangxi","iso_a2":"CN"},"coordinates":[806327,654499]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Huainan","adm0name":"China","adm1name":"Anhui","iso_a2":"CN"},"coordinates":[824938,698043]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Shantou","adm0name":"China","adm1name":"Guangdong","iso_a2":"CN"},"coordinates":[824077,643183]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Tarakan","adm0name":"Indonesia","adm1name":"Kalimantan Timur","iso_a2":"ID"},"coordinates":[826757,524268]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Mombasa","adm0name":"Kenya","adm1name":"Coast","iso_a2":"KE"},"coordinates":[610243,480793]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Maseru","adm0name":"Lesotho","adm1name":"Maseru","iso_a2":"LS"},"coordinates":[576342,331032]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Antananarivo","adm0name":"Madagascar","adm1name":"Antananarivo","iso_a2":"MG"},"coordinates":[631985,392658]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Semarang","adm0name":"Indonesia","adm1name":"Jawa Tengah","iso_a2":"ID"},"coordinates":[806717,463455]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Palembang","adm0name":"Indonesia","adm1name":"Sumatera Selatan","iso_a2":"ID"},"coordinates":[790966,487074]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Bandjarmasin","adm0name":"Indonesia","adm1name":"Kalimantan Selatan","iso_a2":"ID"},"coordinates":[818277,484989]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Ujungpandang","adm0name":"Indonesia","adm1name":"Sulawesi Selatan","iso_a2":"ID"},"coordinates":[831749,474277]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Lyon","adm0name":"France","adm1name":"Rhône-Alpes","iso_a2":"FR"},"coordinates":[513411,775891]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Quito","adm0name":"Ecuador","adm1name":"Pichincha","iso_a2":"EC"},"coordinates":[281939,503455]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"San Jose","adm0name":"Costa Rica","adm1name":"San José","iso_a2":"CR"},"coordinates":[266428,563588]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"San Salvador","adm0name":"El Salvador","adm1name":"San Salvador","iso_a2":"SV"},"coordinates":[252208,585953]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Kingston","adm0name":"Jamaica","adm1name":"Kingston","iso_a2":"JM"},"coordinates":[286756,611221]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Cartagena","adm0name":"Colombia","adm1name":"Bolívar","iso_a2":"CO"},"coordinates":[290232,566341]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Mitu","adm0name":"Colombia","adm1name":"Vaupés","iso_a2":"CO"},"coordinates":[305073,511816]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Bumba","adm0name":"Congo (Kinshasa)","adm1name":"Équateur","iso_a2":"CD"},"coordinates":[562388,517692]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Ndjamena","adm0name":"Chad","adm1name":"Hadjer-Lamis","iso_a2":"TD"},"coordinates":[541797,576492]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Abeche","adm0name":"Chad","adm1name":"Ouaddaï","iso_a2":"TD"},"coordinates":[557860,586711]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Malabo","adm0name":"Equatorial Guinea","adm1name":"Bioko Norte","iso_a2":"GQ"},"coordinates":[524398,526934]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Luxor","adm0name":"Egypt","adm1name":"Qina","iso_a2":"EG"},"coordinates":[590694,656975]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Asmara","adm0name":"Eritrea","adm1name":"Anseba","iso_a2":"ER"},"coordinates":[608148,595558]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Zagreb","adm0name":"Croatia","adm1name":"Grad Zagreb","iso_a2":"HR"},"coordinates":[544444,776057]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Tallinn","adm0name":"Estonia","adm1name":"Harju","iso_a2":"EE"},"coordinates":[568688,856830]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Lhasa","adm0name":"China","adm1name":"Xizang","iso_a2":"CN"},"coordinates":[753055,680348]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Hami","adm0name":"China","adm1name":"Xinjiang Uygur","iso_a2":"CN"},"coordinates":[759763,758443]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Hotan","adm0name":"China","adm1name":"Xinjiang Uygur","iso_a2":"CN"},"coordinates":[722018,724513]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Kashgar","adm0name":"China","adm1name":"Xinjiang Uygur","iso_a2":"CN"},"coordinates":[711027,738593]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Yinchuan","adm0name":"China","adm1name":"Ningxia Hui","iso_a2":"CN"},"coordinates":[795197,732631]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Pingxiang","adm0name":"China","adm1name":"Jiangxi","iso_a2":"CN"},"coordinates":[816244,668362]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Nagasaki","adm0name":"Japan","adm1name":"Nagasaki","iso_a2":"JP"},"coordinates":[860790,698831]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Qiqihar","adm0name":"China","adm1name":"Heilongjiang","iso_a2":"CN"},"coordinates":[844410,785222]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Kikwit","adm0name":"Congo (Kinshasa)","adm1name":"Bandundu","iso_a2":"CD"},"coordinates":[552361,474917]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Matadi","adm0name":"Congo (Kinshasa)","adm1name":"Bas-Congo","iso_a2":"CD"},"coordinates":[537360,470257]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Kolwezi","adm0name":"Congo (Kinshasa)","adm1name":"Katanga","iso_a2":"CD"},"coordinates":[570756,441226]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Lubumbashi","adm0name":"Congo (Kinshasa)","adm1name":"Katanga","iso_a2":"CD"},"coordinates":[576327,435531]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Lilongwe","adm0name":"Malawi","adm1name":"Lilongwe","iso_a2":"MW"},"coordinates":[593842,421873]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Guatemala","adm0name":"Guatemala","adm1name":"Guatemala","iso_a2":"GT"},"coordinates":[248530,591351]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Cayenne","adm0name":"France","adm1name":"Guinaa","iso_a2":"GF"},"coordinates":[354639,533942]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Libreville","adm0name":"Gabon","adm1name":"Estuaire","iso_a2":"GA"},"coordinates":[526271,507000]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Vishakhapatnam","adm0name":"India","adm1name":"Andhra Pradesh","iso_a2":"IN"},"coordinates":[731396,609769]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Suva","adm0name":"Fiji","adm1name":"Central","iso_a2":"FJ"},"coordinates":[995670,397289]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Port-Gentil","adm0name":"Gabon","adm1name":"Ogooué-Maritime","iso_a2":"GA"},"coordinates":[524389,500451]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Timbuktu","adm0name":"Mali","adm1name":"Timbuktu","iso_a2":"ML"},"coordinates":[491620,604050]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Punta Arenas","adm0name":"Chile","adm1name":"Magallanes y Antártica Chilena","iso_a2":"CL"},"coordinates":[302944,189744]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Iquique","adm0name":"Chile","adm1name":"Tarapacá","iso_a2":"CL"},"coordinates":[305194,384747]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Antofagasta","adm0name":"Chile","adm1name":"Antofagasta","iso_a2":"CL"},"coordinates":[304444,364604]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Valparaiso","adm0name":"Chile","adm1name":"Valparaíso","iso_a2":"CL"},"coordinates":[301047,308939]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Valdivia","adm0name":"Chile","adm1name":"Los Ríos","iso_a2":"CL"},"coordinates":[296541,268953]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Concepcion","adm0name":"Chile","adm1name":"Bío-Bío","iso_a2":"CL"},"coordinates":[297083,286520]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Puerto Montt","adm0name":"Chile","adm1name":"Los Lagos","iso_a2":"CL"},"coordinates":[297416,259030]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Nuuk","adm0name":"Greenland","adm1name":"Kommuneqarfik Sermersooq","iso_a2":"GL"},"coordinates":[356298,885057]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Nouakchott","adm0name":"Mauritania","adm1name":"Nouakchott","iso_a2":"MR"},"coordinates":[455623,611869]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Bamako","adm0name":"Mali","adm1name":"Bamako","iso_a2":"ML"},"coordinates":[477771,579673]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Atar","adm0name":"Mauritania","adm1name":"Adrar","iso_a2":"MR"},"coordinates":[463750,626267]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Djenne","adm0name":"Mali","adm1name":"Mopti","iso_a2":"ML"},"coordinates":[487360,587067]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Sabha","adm0name":"Libya","adm1name":"Sabha","iso_a2":"LY"},"coordinates":[540092,664874]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Banghazi","adm0name":"Libya","adm1name":"Benghazi","iso_a2":"LY"},"coordinates":[555735,695002]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Thessaloniki","adm0name":"Greece","adm1name":"Kentriki Makedonia","iso_a2":"GR"},"coordinates":[563564,745831]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Beirut","adm0name":"Lebanon","adm1name":"Beirut","iso_a2":"LB"},"coordinates":[598632,705401]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Tbilisi","adm0name":"Georgia","adm1name":"Tbilisi","iso_a2":"GE"},"coordinates":[624412,751926]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Gonder","adm0name":"Ethiopia","adm1name":"Amhara","iso_a2":"ET"},"coordinates":[604055,579425]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Astana","adm0name":"Kazakhstan","adm1name":"Aqmola","iso_a2":"KZ"},"coordinates":[698409,807937]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Qaraghandy","adm0name":"Kazakhstan","adm1name":"Qaraghandy","iso_a2":"KZ"},"coordinates":[703096,800258]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Almaty","adm0name":"Kazakhstan","adm1name":"Almaty","iso_a2":"KZ"},"coordinates":[713646,761406]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Isfahan","adm0name":"Iran","adm1name":"Esfahan","iso_a2":"IR"},"coordinates":[643606,698458]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Shiraz","adm0name":"Iran","adm1name":"Fars","iso_a2":"IR"},"coordinates":[646022,680270]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Amritsar","adm0name":"India","adm1name":"Punjab","iso_a2":"IN"},"coordinates":[707966,692178]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Varanasi","adm0name":"India","adm1name":"Uttar Pradesh","iso_a2":"IN"},"coordinates":[730549,654795]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Asansol","adm0name":"India","adm1name":"West Bengal","iso_a2":"IN"},"coordinates":[741614,645039]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Bhilai","adm0name":"India","adm1name":"Chhattisgarh","iso_a2":"IN"},"coordinates":[726197,630426]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Bhopal","adm0name":"India","adm1name":"Madhya Pradesh","iso_a2":"IN"},"coordinates":[715022,642472]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Madurai","adm0name":"India","adm1name":"Tamil Nadu","iso_a2":"IN"},"coordinates":[716994,563499]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Coimbatore","adm0name":"India","adm1name":"Tamil Nadu","iso_a2":"IN"},"coordinates":[713744,569897]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Vientiane","adm0name":"Laos","adm1name":"Vientiane [prefecture]","iso_a2":"LA"},"coordinates":[784999,611160]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Brazzaville","adm0name":"Congo (Brazzaville)","adm1name":"Pool","iso_a2":"CG"},"coordinates":[542451,479495]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Conakry","adm0name":"Guinea","adm1name":"Conakry","iso_a2":"GN"},"coordinates":[461993,561198]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Yamoussoukro","adm0name":"Ivory Coast","adm1name":"Lacs","iso_a2":"CI"},"coordinates":[485346,545112]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Cruzeiro do Sul","adm0name":"Brazil","adm1name":"Acre","iso_a2":"BR"},"coordinates":[298138,459513]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Leticia","adm0name":"Colombia","adm1name":"Amazonas","iso_a2":"CO"},"coordinates":[305679,479827]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Manaus","adm0name":"Brazil","adm1name":"Amazonas","iso_a2":"BR"},"coordinates":[333328,486363]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Caxias","adm0name":"Brazil","adm1name":"Maranhão","iso_a2":"BR"},"coordinates":[379583,476084]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Santarem","adm0name":"Brazil","adm1name":"Pará","iso_a2":"BR"},"coordinates":[348055,490302]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Maraba","adm0name":"Brazil","adm1name":"Pará","iso_a2":"BR"},"coordinates":[363566,473022]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Vilhena","adm0name":"Brazil","adm1name":"Rondônia","iso_a2":"BR"},"coordinates":[333009,429378]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Ji-Parana","adm0name":"Brazil","adm1name":"Rondônia","iso_a2":"BR"},"coordinates":[327870,440536]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Campo Grande","adm0name":"Brazil","adm1name":"Mato Grosso do Sul","iso_a2":"BR"},"coordinates":[348282,383573]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Florianopolis","adm0name":"Brazil","adm1name":"Santa Catarina","iso_a2":"BR"},"coordinates":[365216,341333]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Feira de Santana","adm0name":"Brazil","adm1name":"Bahia","iso_a2":"BR"},"coordinates":[391750,432142]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Winnipeg","adm0name":"Canada","adm1name":"Manitoba","iso_a2":"CA"},"coordinates":[230094,800247]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Churchill","adm0name":"Canada","adm1name":"Manitoba","iso_a2":"CA"},"coordinates":[238427,852873]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Regina","adm0name":"Canada","adm1name":"Saskatchewan","iso_a2":"CA"},"coordinates":[209397,803606]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Saskatoon","adm0name":"Canada","adm1name":"Saskatchewan","iso_a2":"CA"},"coordinates":[203694,813796]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Calgary","adm0name":"Canada","adm1name":"Alberta","iso_a2":"CA"},"coordinates":[183106,807367]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Prince Rupert","adm0name":"Canada","adm1name":"British Columbia","iso_a2":"CA"},"coordinates":[137973,826514]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Victoria","adm0name":"Canada","adm1name":"British Columbia","iso_a2":"CA"},"coordinates":[157361,791657]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Arctic Bay","adm0name":"Canada","adm1name":"Nunavut","iso_a2":"CA"},"coordinates":[263425,937400]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Resolute","adm0name":"Canada","adm1name":"Nunavut","iso_a2":"CA"},"coordinates":[236389,947175]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Repulse Bay","adm0name":"Canada","adm1name":"Nunavut","iso_a2":"CA"},"coordinates":[260325,898868]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Yellowknife","adm0name":"Canada","adm1name":"Northwest Territories","iso_a2":"CA"},"coordinates":[182230,874652]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Fort Good Hope","adm0name":"Canada","adm1name":"Northwest Territories","iso_a2":"CA"},"coordinates":[142685,897311]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Whitehorse","adm0name":"Canada","adm1name":"Yukon","iso_a2":"CA"},"coordinates":[124861,864430]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Boa Vista","adm0name":"Brazil","adm1name":"Roraima","iso_a2":"BR"},"coordinates":[331483,521401]},{"type":"Point","properties":{"scalerank":3,"labelrank":1,"name":"Macapá","adm0name":"Brazil","adm1name":"Amapá","iso_a2":"BR"},"coordinates":[358194,504913]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Ottawa","adm0name":"Canada","adm1name":"Ontario","iso_a2":"CA"},"coordinates":[289716,773798]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Fort Severn","adm0name":"Canada","adm1name":"Ontario","iso_a2":"CA"},"coordinates":[256528,836387]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Thunder Bay","adm0name":"Canada","adm1name":"Ontario","iso_a2":"CA"},"coordinates":[252014,791734]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Québec","adm0name":"Canada","adm1name":"Québec","iso_a2":"CA"},"coordinates":[302095,782218]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Halifax","adm0name":"Canada","adm1name":"Nova Scotia","iso_a2":"CA"},"coordinates":[323333,769244]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"St. John’s","adm0name":"Canada","adm1name":"Newfoundland and Labrador","iso_a2":"CA"},"coordinates":[353663,786632]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Nain","adm0name":"Canada","adm1name":"Newfoundland and Labrador","iso_a2":"CA"},"coordinates":[328650,839729]},{"type":"Point","properties":{"scalerank":3,"labelrank":2,"name":"Charlottetown","adm0name":"Canada","adm1name":"Prince Edward Island","iso_a2":"CA"},"coordinates":[324635,778719]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Ndele","adm0name":"Central African Republic","adm1name":"Bamingui-Bangoran","iso_a2":"CF"},"coordinates":[557369,554537]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Belgrade","adm0name":"Serbia","adm1name":"Grad Beograd","iso_a2":"RS"},"coordinates":[556849,770254]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Obo","adm0name":"Central African Republic","adm1name":"Haut-Mbomou","iso_a2":"CF"},"coordinates":[573611,536709]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Bandar Seri Begawan","adm0name":"Brunei","adm1name":"Brunei and Muara","iso_a2":"BN"},"coordinates":[819259,533648]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Puerto Deseado","adm0name":"Argentina","adm1name":"Santa Cruz","iso_a2":"AR"},"coordinates":[316944,221825]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Rio Gallegos","adm0name":"Argentina","adm1name":"Santa Cruz","iso_a2":"AR"},"coordinates":[307731,198818]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Comodoro Rivadavia","adm0name":"Argentina","adm1name":"Chubut","iso_a2":"AR"},"coordinates":[312500,232962]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Mendoza","adm0name":"Argentina","adm1name":"Mendoza","iso_a2":"AR"},"coordinates":[308837,309913]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Sucre","adm0name":"Bolivia","adm1name":"Chuquisaca","iso_a2":"BO"},"coordinates":[318723,391910]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Riberalta","adm0name":"Bolivia","adm1name":"El Beni","iso_a2":"BO"},"coordinates":[316388,439649]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Bahia Blanca","adm0name":"Argentina","adm1name":"Ciudad de Buenos Aires","iso_a2":"AR"},"coordinates":[327041,275203]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Mar del Plata","adm0name":"Argentina","adm1name":"Ciudad de Buenos Aires","iso_a2":"AR"},"coordinates":[340055,279587]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Córdoba","adm0name":"Argentina","adm1name":"Córdoba","iso_a2":"AR"},"coordinates":[321710,318701]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Posadas","adm0name":"Argentina","adm1name":"Misiones","iso_a2":"AR"},"coordinates":[344763,342637]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Belmopan","adm0name":"Belize","adm1name":"Cayo","iso_a2":"BZ"},"coordinates":[253425,606926]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Bangui","adm0name":"Central African Republic","adm1name":"Bangui","iso_a2":"CF"},"coordinates":[551550,530587]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Maroua","adm0name":"Cameroon","adm1name":"Extrême-Nord","iso_a2":"CM"},"coordinates":[539790,567490]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Yaounde","adm0name":"Cameroon","adm1name":"Centre","iso_a2":"CM"},"coordinates":[531985,527636]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Tirana","adm0name":"Albania","adm1name":"Durrës","iso_a2":"AL"},"coordinates":[555051,749560]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Yerevan","adm0name":"Armenia","adm1name":"Erevan","iso_a2":"AM"},"coordinates":[623642,742780]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Baku","adm0name":"Azerbaijan","adm1name":"Baki","iso_a2":"AZ"},"coordinates":[638500,744048]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Kandahar","adm0name":"Afghanistan","adm1name":"Kandahar","iso_a2":"AF"},"coordinates":[682485,691989]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Phnom Penh","adm0name":"Cambodia","adm1name":"Phnom Penh","iso_a2":"KH"},"coordinates":[791428,573156]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Menongue","adm0name":"Angola","adm1name":"Cuando Cubango","iso_a2":"AO"},"coordinates":[549166,417825]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Huambo","adm0name":"Angola","adm1name":"Huambo","iso_a2":"AO"},"coordinates":[543772,429192]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"La Paz","adm0name":"Bolivia","adm1name":"La Paz","iso_a2":"BO"},"coordinates":[310688,406987]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Santa Cruz","adm0name":"Bolivia","adm1name":"Santa Cruz","iso_a2":"BO"},"coordinates":[324366,399546]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Oran","adm0name":"Algeria","adm1name":"Oran","iso_a2":"DZ"},"coordinates":[498272,716291]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Cotonou","adm0name":"Benin","adm1name":"Ouémé","iso_a2":"BJ"},"coordinates":[506994,542646]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Tamanrasset","adm0name":"Algeria","adm1name":"Tamanghasset","iso_a2":"DZ"},"coordinates":[515341,639705]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Ghardaia","adm0name":"Algeria","adm1name":"Ghardaïa","iso_a2":"DZ"},"coordinates":[510194,697202]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Sofia","adm0name":"Bulgaria","adm1name":"Grad Sofiya","iso_a2":"BG"},"coordinates":[564762,757604]},{"type":"Point","properties":{"scalerank":3,"labelrank":6,"name":"Minsk","adm0name":"Belarus","adm1name":"Minsk","iso_a2":"BY"},"coordinates":[576568,824056]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Thimphu","adm0name":"Bhutan","adm1name":"Thimphu","iso_a2":"BT"},"coordinates":[748997,667480]},{"type":"Point","properties":{"scalerank":3,"labelrank":7,"name":"Gaborone","adm0name":"Botswana","adm1name":"South-East","iso_a2":"BW"},"coordinates":[571977,358701]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Darwin","adm0name":"Australia","adm1name":"Northern Territory","iso_a2":"AU"},"coordinates":[863471,431104]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Alice Springs","adm0name":"Australia","adm1name":"Northern Territory","iso_a2":"AU"},"coordinates":[871888,364302]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Canberra","adm0name":"Australia","adm1name":"Australian Capital Territory","iso_a2":"AU"},"coordinates":[914247,295685]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Newcastle","adm0name":"Australia","adm1name":"New South Wales","iso_a2":"AU"},"coordinates":[921708,310126]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Adelaide","adm0name":"Australia","adm1name":"South Australia","iso_a2":"AU"},"coordinates":[884994,297758]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Townsville","adm0name":"Australia","adm1name":"Queensland","iso_a2":"AU"},"coordinates":[907694,390672]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Brisbane","adm0name":"Australia","adm1name":"Queensland","iso_a2":"AU"},"coordinates":[925091,342073]},{"type":"Point","properties":{"scalerank":3,"labelrank":3,"name":"Hobart","adm0name":"Australia","adm1name":"Tasmania","iso_a2":"AU"},"coordinates":[909152,250854]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Ouagadougou","adm0name":"Burkina Faso","adm1name":"Kadiogo","iso_a2":"BF"},"coordinates":[495759,578016]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Sarajevo","adm0name":"Bosnia and Herzegovina","adm1name":"Sarajevo","iso_a2":"BA"},"coordinates":[551063,764505]},{"type":"Point","properties":{"scalerank":3,"labelrank":5,"name":"Naypyidaw","adm0name":"Myanmar","adm1name":"Mandalay","iso_a2":"MM"},"coordinates":[766990,621835]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"San Juan","adm0name":"Puerto Rico","iso_a2":"PR"},"coordinates":[316305,613964]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Stanley","adm0name":"Falkland Islands","iso_a2":"FK"},"coordinates":[339306,198423]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Hamilton","adm0name":"Bermuda","iso_a2":"BM"},"coordinates":[320044,696043]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Nukualofa","adm0name":"Tonga","iso_a2":"TO"},"coordinates":[13276,379483]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Hargeysa","adm0name":"Somaliland","iso_a2":"-99"},"coordinates":[622403,561355]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Victoria","adm0name":"Seychelles","iso_a2":"SC"},"coordinates":[654027,477366]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Sao Tome","adm0name":"Sao Tome and Principe","iso_a2":"ST"},"coordinates":[518703,506692]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Apia","adm0name":"Samoa","iso_a2":"WS"},"coordinates":[22948,422713]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Valletta","adm0name":"Malta","iso_a2":"MT"},"coordinates":[540318,717403]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Male","adm0name":"Maldives","iso_a2":"MV"},"coordinates":[704166,529403]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Jerusalem","adm0name":"Israel","adm1name":"Jerusalem","iso_a2":"IL"},"coordinates":[597795,692987]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Praia","adm0name":"Cape Verde","iso_a2":"CV"},"coordinates":[434676,593090]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Nassau","adm0name":"The Bahamas","iso_a2":"BS"},"coordinates":[285138,653322]},{"type":"Point","properties":{"scalerank":3,"labelrank":0,"name":"Nicosia","adm0name":"Cyprus","iso_a2":"CY"},"coordinates":[592684,713060]},{"type":"Point","properties":{"scalerank":3,"labelrank":8,"name":"Kaohsiung","adm0name":"Taiwan","adm1name":"Kaohsiung City","iso_a2":"TW"},"coordinates":[834073,638807]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Shenzhen","adm0name":"China","adm1name":"Guangdong","iso_a2":"CN"},"coordinates":[816999,638339]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Zibo","adm0name":"China","adm1name":"Shandong","iso_a2":"CN"},"coordinates":[827911,722749]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Minneapolis","adm0name":"United States of America","adm1name":"Minnesota","iso_a2":"US"},"coordinates":[240962,771210]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Honolulu","adm0name":"United States of America","adm1name":"Hawaii","iso_a2":"US"},"coordinates":[61500,630960]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Seattle","adm0name":"United States of America","adm1name":"Washington","iso_a2":"US"},"coordinates":[160161,786555]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Phoenix","adm0name":"United States of America","adm1name":"Arizona","iso_a2":"US"},"coordinates":[188689,703435]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"San Diego","adm0name":"United States of America","adm1name":"California","iso_a2":"US"},"coordinates":[174494,699169]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"St. Louis","adm0name":"United States of America","adm1name":"Missouri","iso_a2":"US"},"coordinates":[249328,733620]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"New Orleans","adm0name":"United States of America","adm1name":"Louisiana","iso_a2":"US"},"coordinates":[249883,682432]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Dallas","adm0name":"United States of America","adm1name":"Texas","iso_a2":"US"},"coordinates":[230995,699169]},{"type":"Point","properties":{"scalerank":2,"labelrank":6,"name":"Maracaibo","adm0name":"Venezuela","adm1name":"Zulia","iso_a2":"VE"},"coordinates":[300939,568298]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Boston","adm0name":"United States of America","adm1name":"Massachusetts","iso_a2":"US"},"coordinates":[302578,755511]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Tampa","adm0name":"United States of America","adm1name":"Florida","iso_a2":"US"},"coordinates":[270943,670299]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Philadelphia","adm0name":"United States of America","adm1name":"Pennsylvania","iso_a2":"US"},"coordinates":[291189,741707]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Detroit","adm0name":"United States of America","adm1name":"Michigan","iso_a2":"US"},"coordinates":[269217,755511]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Anchorage","adm0name":"United States of America","adm1name":"Alaska","iso_a2":"US"},"coordinates":[83611,867412]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Hanoi","adm0name":"Vietnam","adm1name":"Thái Nguyên","iso_a2":"VN"},"coordinates":[794022,629340]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Ho Chi Minh City","adm0name":"Vietnam","adm1name":"H? Chí Minh city","iso_a2":"VN"},"coordinates":[796369,568595]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Ankara","adm0name":"Turkey","adm1name":"Ankara","iso_a2":"TR"},"coordinates":[591284,741276]},{"type":"Point","properties":{"scalerank":2,"labelrank":6,"name":"Budapest","adm0name":"Hungary","adm1name":"Budapest","iso_a2":"HU"},"coordinates":[553003,786140]},{"type":"Point","properties":{"scalerank":2,"labelrank":6,"name":"Sanaa","adm0name":"Yemen","adm1name":"Amanat Al Asimah","iso_a2":"YE"},"coordinates":[622791,595697]},{"type":"Point","properties":{"scalerank":2,"labelrank":3,"name":"Barcelona","adm0name":"Spain","adm1name":"Cataluña","iso_a2":"ES"},"coordinates":[506059,749902]},{"type":"Point","properties":{"scalerank":2,"labelrank":6,"name":"Bucharest","adm0name":"Romania","adm1name":"Bucharest","iso_a2":"RO"},"coordinates":[572494,767972]},{"type":"Point","properties":{"scalerank":2,"labelrank":6,"name":"Aleppo","adm0name":"Syria","adm1name":"Aleppo (Halab)","iso_a2":"SY"},"coordinates":[603244,719372]},{"type":"Point","properties":{"scalerank":2,"labelrank":6,"name":"Damascus","adm0name":"Syria","adm1name":"Damascus","iso_a2":"SY"},"coordinates":[600827,703198]},{"type":"Point","properties":{"scalerank":2,"labelrank":7,"name":"Zürich","adm0name":"Switzerland","adm1name":"Zürich","iso_a2":"CH"},"coordinates":[523744,785429]},{"type":"Point","properties":{"scalerank":2,"labelrank":6,"name":"Lisbon","adm0name":"Portugal","adm1name":"Lisboa","iso_a2":"PT"},"coordinates":[474591,734139]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Khartoum","adm0name":"Sudan","adm1name":"Khartoum","iso_a2":"SD"},"coordinates":[590367,597079]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Jeddah","adm0name":"Saudi Arabia","adm1name":"Makkah","iso_a2":"SA"},"coordinates":[608936,632204]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Makkah","adm0name":"Saudi Arabia","adm1name":"Makkah","iso_a2":"SA"},"coordinates":[610605,631690]},{"type":"Point","properties":{"scalerank":2,"labelrank":7,"name":"Oslo","adm0name":"Norway","adm1name":"Oslo","iso_a2":"NO"},"coordinates":[529855,859702]},{"type":"Point","properties":{"scalerank":2,"labelrank":2,"name":"Lahore","adm0name":"Pakistan","adm1name":"Punjab","iso_a2":"PK"},"coordinates":[706522,691704]},{"type":"Point","properties":{"scalerank":2,"labelrank":2,"name":"Karachi","adm0name":"Pakistan","adm1name":"Sind","iso_a2":"PK"},"coordinates":[686077,652070]},{"type":"Point","properties":{"scalerank":2,"labelrank":3,"name":"Durban","adm0name":"South Africa","adm1name":"KwaZulu-Natal","iso_a2":"ZA"},"coordinates":[586050,327795]},{"type":"Point","properties":{"scalerank":2,"labelrank":2,"name":"St. Petersburg","adm0name":"Russia","adm1name":"City of St. Petersburg","iso_a2":"RU"},"coordinates":[584205,859834]},{"type":"Point","properties":{"scalerank":2,"labelrank":2,"name":"Guadalajara","adm0name":"Mexico","adm1name":"Jalisco","iso_a2":"MX"},"coordinates":[212967,627187]},{"type":"Point","properties":{"scalerank":2,"labelrank":2,"name":"Puebla","adm0name":"Mexico","adm1name":"Puebla","iso_a2":"MX"},"coordinates":[227216,617589]},{"type":"Point","properties":{"scalerank":2,"labelrank":2,"name":"Kano","adm0name":"Nigeria","adm1name":"Kano","iso_a2":"NG"},"coordinates":[523661,575822]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Warsaw","adm0name":"Poland","adm1name":"Masovian","iso_a2":"PL"},"coordinates":[558328,814281]},{"type":"Point","properties":{"scalerank":2,"labelrank":6,"name":"Pyongyang","adm0name":"North Korea","adm1name":"P'yongyang","iso_a2":"KP"},"coordinates":[849312,735898]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Dar es Salaam","adm0name":"Tanzania","adm1name":"Dar-Es-Salaam","iso_a2":"TZ"},"coordinates":[609072,464442]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Medan","adm0name":"Indonesia","adm1name":"Sumatera Utara","iso_a2":"ID"},"coordinates":[774021,525938]},{"type":"Point","properties":{"scalerank":2,"labelrank":8,"name":"Dublin","adm0name":"Ireland","adm1name":"Dublin","iso_a2":"IE"},"coordinates":[482636,820698]},{"type":"Point","properties":{"scalerank":2,"labelrank":8,"name":"Monrovia","adm0name":"Liberia","adm1name":"Montserrado","iso_a2":"LR"},"coordinates":[470001,542128]},{"type":"Point","properties":{"scalerank":2,"labelrank":3,"name":"Naples","adm0name":"Italy","adm1name":"Campania","iso_a2":"IT"},"coordinates":[539564,746684]},{"type":"Point","properties":{"scalerank":2,"labelrank":3,"name":"Milan","adm0name":"Italy","adm1name":"Lombardia","iso_a2":"IT"},"coordinates":[525564,774114]},{"type":"Point","properties":{"scalerank":2,"labelrank":6,"name":"Kuala Lumpur","adm0name":"Malaysia","adm1name":"Selangor","iso_a2":"MY"},"coordinates":[782494,523489]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Lanzhou","adm0name":"China","adm1name":"Gansu","iso_a2":"CN"},"coordinates":[788304,718341]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Nanning","adm0name":"China","adm1name":"Guangxi","iso_a2":"CN"},"coordinates":[800883,639925]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Guiyang","adm0name":"China","adm1name":"Guizhou","iso_a2":"CN"},"coordinates":[796439,662201]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Chongqing","adm0name":"China","adm1name":"Chongqing","iso_a2":"CN"},"coordinates":[796091,679885]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Fuzhou","adm0name":"China","adm1name":"Fujian","iso_a2":"CN"},"coordinates":[831382,659239]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Guangzhou","adm0name":"China","adm1name":"Guangdong","iso_a2":"CN"},"coordinates":[814786,641850]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Dongguan","adm0name":"China","adm1name":"Guangdong","iso_a2":"CN"},"coordinates":[815951,641281]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Bandung","adm0name":"Indonesia","adm1name":"Jawa Barat","iso_a2":"ID"},"coordinates":[798799,463554]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Surabaya","adm0name":"Indonesia","adm1name":"Jawa Timur","iso_a2":"ID"},"coordinates":[813191,461781]},{"type":"Point","properties":{"scalerank":2,"labelrank":7,"name":"Guayaquil","adm0name":"Ecuador","adm1name":"Guayas","iso_a2":"EC"},"coordinates":[277994,491576]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Medellin","adm0name":"Colombia","adm1name":"Antioquia","iso_a2":"CO"},"coordinates":[290064,541905]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Cali","adm0name":"Colombia","adm1name":"Valle del Cauca","iso_a2":"CO"},"coordinates":[287494,524872]},{"type":"Point","properties":{"scalerank":2,"labelrank":6,"name":"Havana","adm0name":"Cuba","adm1name":"Ciudad de la Habana","iso_a2":"CU"},"coordinates":[271205,641773]},{"type":"Point","properties":{"scalerank":2,"labelrank":3,"name":"Alexandria","adm0name":"Egypt","adm1name":"Al Iskandariyah","iso_a2":"EG"},"coordinates":[583188,689572]},{"type":"Point","properties":{"scalerank":2,"labelrank":3,"name":"Frankfurt","adm0name":"Germany","adm1name":"Hessen","iso_a2":"DE"},"coordinates":[524097,801532]},{"type":"Point","properties":{"scalerank":2,"labelrank":3,"name":"Hamburg","adm0name":"Germany","adm1name":"Hamburg","iso_a2":"DE"},"coordinates":[527772,821983]},{"type":"Point","properties":{"scalerank":2,"labelrank":3,"name":"Munich","adm0name":"Germany","adm1name":"Bayern","iso_a2":"DE"},"coordinates":[532147,789872]},{"type":"Point","properties":{"scalerank":2,"labelrank":7,"name":"Prague","adm0name":"Czech Republic","adm1name":"Prague","iso_a2":"CZ"},"coordinates":[540177,801445]},{"type":"Point","properties":{"scalerank":2,"labelrank":8,"name":"Kuwait","adm0name":"Kuwait","adm1name":"Al Kuwayt","iso_a2":"KW"},"coordinates":[633267,678728]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Xian","adm0name":"China","adm1name":"Shaanxi","iso_a2":"CN"},"coordinates":[802479,707789]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Taiyuan","adm0name":"China","adm1name":"Shanxi","iso_a2":"CN"},"coordinates":[812619,729117]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Wuhan","adm0name":"China","adm1name":"Hubei","iso_a2":"CN"},"coordinates":[817411,685898]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Changsha","adm0name":"China","adm1name":"Hunan","iso_a2":"CN"},"coordinates":[813800,671798]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Kunming","adm0name":"China","adm1name":"Yunnan","iso_a2":"CN"},"coordinates":[785216,653255]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Zhengzhou","adm0name":"China","adm1name":"Henan","iso_a2":"CN"},"coordinates":[815730,710633]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Shenyeng","adm0name":"China","adm1name":"Liaoning","iso_a2":"CN"},"coordinates":[842910,752400]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Jinan","adm0name":"China","adm1name":"Shandong","iso_a2":"CN"},"coordinates":[824980,722008]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Tianjin","adm0name":"China","adm1name":"Tianjin","iso_a2":"CN"},"coordinates":[825549,736553]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Nanchang","adm0name":"China","adm1name":"Jiangxi","iso_a2":"CN"},"coordinates":[821883,674642]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Nanjing","adm0name":"China","adm1name":"Jiangsu","iso_a2":"CN"},"coordinates":[829938,694608]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Hangzhou","adm0name":"China","adm1name":"Zhejiang","iso_a2":"CN"},"coordinates":[833799,683943]},{"type":"Point","properties":{"scalerank":2,"labelrank":2,"name":"Hiroshima","adm0name":"Japan","adm1name":"Hiroshima","iso_a2":"JP"},"coordinates":[867890,708458]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Changchun","adm0name":"China","adm1name":"Jilin","iso_a2":"CN"},"coordinates":[848161,764605]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Baotou","adm0name":"China","adm1name":"Nei Mongol","iso_a2":"CN"},"coordinates":[805055,745571]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Harbin","adm0name":"China","adm1name":"Heilongjiang","iso_a2":"CN"},"coordinates":[851800,775772]},{"type":"Point","properties":{"scalerank":2,"labelrank":2,"name":"Sapporo","adm0name":"Japan","adm1name":"Hokkaido","iso_a2":"JP"},"coordinates":[892605,759924]},{"type":"Point","properties":{"scalerank":2,"labelrank":8,"name":"Santo Domingo","adm0name":"Dominican Republic","adm1name":"Distrito Nacional","iso_a2":"DO"},"coordinates":[305828,614153]},{"type":"Point","properties":{"scalerank":2,"labelrank":6,"name":"Accra","adm0name":"Ghana","adm1name":"Greater Accra","iso_a2":"GH"},"coordinates":[499392,537610]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Delhi","adm0name":"India","adm1name":"Delhi","iso_a2":"IN"},"coordinates":[714522,674583]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Hyderabad","adm0name":"India","adm1name":"Andhra Pradesh","iso_a2":"IN"},"coordinates":[717993,607814]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Pune","adm0name":"India","adm1name":"Maharashtra","iso_a2":"IN"},"coordinates":[705133,614509]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Nagpur","adm0name":"India","adm1name":"Maharashtra","iso_a2":"IN"},"coordinates":[719688,630149]},{"type":"Point","properties":{"scalerank":2,"labelrank":7,"name":"Tripoli","adm0name":"Libya","adm1name":"Tajura' wa an Nawahi al Arba","iso_a2":"LY"},"coordinates":[536611,699587]},{"type":"Point","properties":{"scalerank":2,"labelrank":8,"name":"Tel Aviv-Yafo","adm0name":"Israel","adm1name":"Tel Aviv","iso_a2":"IL"},"coordinates":[596577,694785]},{"type":"Point","properties":{"scalerank":2,"labelrank":7,"name":"Helsinki","adm0name":"Finland","adm1name":"Southern Finland","iso_a2":"FI"},"coordinates":[569256,861236]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Mashhad","adm0name":"Iran","adm1name":"Razavi Khorasan","iso_a2":"IR"},"coordinates":[665466,719608]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Jaipur","adm0name":"India","adm1name":"Rajasthan","iso_a2":"IN"},"coordinates":[710577,664222]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Kanpur","adm0name":"India","adm1name":"Uttar Pradesh","iso_a2":"IN"},"coordinates":[723105,661490]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Patna","adm0name":"India","adm1name":"Bihar","iso_a2":"IN"},"coordinates":[736466,656543]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Chennai","adm0name":"India","adm1name":"Tamil Nadu","iso_a2":"IN"},"coordinates":[722994,582279]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Ahmedabad","adm0name":"India","adm1name":"Dadra and Nagar Haveli","iso_a2":"IN"},"coordinates":[701605,641169]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Surat","adm0name":"India","adm1name":"Dadra and Nagar Haveli","iso_a2":"IN"},"coordinates":[702327,630327]},{"type":"Point","properties":{"scalerank":2,"labelrank":8,"name":"København","adm0name":"Denmark","adm1name":"Hovedstaden","iso_a2":"DK"},"coordinates":[534893,834594]},{"type":"Point","properties":{"scalerank":2,"labelrank":8,"name":"Abidjan","adm0name":"Ivory Coast","adm1name":"Lagunes","iso_a2":"CI"},"coordinates":[488772,536247]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Belem","adm0name":"Brazil","adm1name":"Pará","iso_a2":"BR"},"coordinates":[365327,496138]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Brasilia","adm0name":"Brazil","adm1name":"Distrito Federal","iso_a2":"BR"},"coordinates":[366894,411221]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Porto Alegre","adm0name":"Brazil","adm1name":"Rio Grande do Sul","iso_a2":"BR"},"coordinates":[357772,326699]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Curitiba","adm0name":"Brazil","adm1name":"Paraná","iso_a2":"BR"},"coordinates":[362994,354129]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Fortaleza","adm0name":"Brazil","adm1name":"Ceará","iso_a2":"BR"},"coordinates":[392828,482512]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Salvador","adm0name":"Brazil","adm1name":"Bahia","iso_a2":"BR"},"coordinates":[393106,427888]},{"type":"Point","properties":{"scalerank":2,"labelrank":2,"name":"Edmonton","adm0name":"Canada","adm1name":"Alberta","iso_a2":"CA"},"coordinates":[184717,821983]},{"type":"Point","properties":{"scalerank":2,"labelrank":2,"name":"Montréal","adm0name":"Canada","adm1name":"Québec","iso_a2":"CA"},"coordinates":[295596,774292]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Goiania","adm0name":"Brazil","adm1name":"Goiás","iso_a2":"BR"},"coordinates":[363050,405672]},{"type":"Point","properties":{"scalerank":2,"labelrank":1,"name":"Recife","adm0name":"Brazil","adm1name":"Pernambuco","iso_a2":"BR"},"coordinates":[403006,456885]},{"type":"Point","properties":{"scalerank":2,"labelrank":8,"name":"Brussels","adm0name":"Belgium","adm1name":"Brussels","iso_a2":"BE"},"coordinates":[512031,805888]},{"type":"Point","properties":{"scalerank":2,"labelrank":2,"name":"Dhaka","adm0name":"Bangladesh","adm1name":"Dhaka","iso_a2":"BD"},"coordinates":[751129,645275]},{"type":"Point","properties":{"scalerank":2,"labelrank":6,"name":"Luanda","adm0name":"Angola","adm1name":"Luanda","iso_a2":"AO"},"coordinates":[536756,452367]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Algiers","adm0name":"Algeria","adm1name":"Alger","iso_a2":"DZ"},"coordinates":[508468,722530]},{"type":"Point","properties":{"scalerank":2,"labelrank":2,"name":"Chittagong","adm0name":"Bangladesh","adm1name":"Chittagong","iso_a2":"BD"},"coordinates":[754993,637022]},{"type":"Point","properties":{"scalerank":2,"labelrank":3,"name":"Perth","adm0name":"Australia","adm1name":"Western Australia","iso_a2":"AU"},"coordinates":[821772,315413]},{"type":"Point","properties":{"scalerank":2,"labelrank":5,"name":"Rangoon","adm0name":"Myanmar","adm1name":"Yangon","iso_a2":"MM"},"coordinates":[767123,604161]},{"type":"Point","properties":{"scalerank":1,"labelrank":1,"name":"San Francisco","adm0name":"United States of America","adm1name":"California","iso_a2":"US"},"coordinates":[159952,728479]},{"type":"Point","properties":{"scalerank":1,"labelrank":1,"name":"Denver","adm0name":"United States of America","adm1name":"Colorado","iso_a2":"US"},"coordinates":[208372,740162]},{"type":"Point","properties":{"scalerank":1,"labelrank":1,"name":"Houston","adm0name":"United States of America","adm1name":"Texas","iso_a2":"US"},"coordinates":[235161,681396]},{"type":"Point","properties":{"scalerank":1,"labelrank":1,"name":"Miami","adm0name":"United States of America","adm1name":"Florida","iso_a2":"US"},"coordinates":[277150,657506]},{"type":"Point","properties":{"scalerank":1,"labelrank":1,"name":"Atlanta","adm0name":"United States of America","adm1name":"Georgia","iso_a2":"US"},"coordinates":[265550,705153]},{"type":"Point","properties":{"scalerank":1,"labelrank":1,"name":"Chicago","adm0name":"United States of America","adm1name":"Illinois","iso_a2":"US"},"coordinates":[256244,752549]},{"type":"Point","properties":{"scalerank":1,"labelrank":6,"name":"Caracas","adm0name":"Venezuela","adm1name":"Distrito Capital","iso_a2":"VE"},"coordinates":[314114,566941]},{"type":"Point","properties":{"scalerank":1,"labelrank":5,"name":"Kiev","adm0name":"Ukraine","adm1name":"Kiev","iso_a2":"UA"},"coordinates":[584762,803519]},{"type":"Point","properties":{"scalerank":1,"labelrank":8,"name":"Dubai","adm0name":"United Arab Emirates","adm1name":"Dubay","iso_a2":"AE"},"coordinates":[653549,654203]},{"type":"Point","properties":{"scalerank":1,"labelrank":6,"name":"Tashkent","adm0name":"Uzbekistan","adm1name":"Tashkent","iso_a2":"UZ"},"coordinates":[692480,749478]},{"type":"Point","properties":{"scalerank":1,"labelrank":3,"name":"Madrid","adm0name":"Spain","adm1name":"Comunidad de Madrid","iso_a2":"ES"},"coordinates":[489762,744077]},{"type":"Point","properties":{"scalerank":1,"labelrank":7,"name":"Geneva","adm0name":"Switzerland","adm1name":"Genève","iso_a2":"CH"},"coordinates":[517055,778486]},{"type":"Point","properties":{"scalerank":1,"labelrank":7,"name":"Stockholm","adm0name":"Sweden","adm1name":"Stockholm","iso_a2":"SE"},"coordinates":[550264,856349]},{"type":"Point","properties":{"scalerank":1,"labelrank":5,"name":"Bangkok","adm0name":"Thailand","adm1name":"Bangkok Metropolis","iso_a2":"TH"},"coordinates":[779207,586190]},{"type":"Point","properties":{"scalerank":1,"labelrank":5,"name":"Lima","adm0name":"Peru","adm1name":"Lima","iso_a2":"PE"},"coordinates":[285967,433351]},{"type":"Point","properties":{"scalerank":1,"labelrank":8,"name":"Dakar","adm0name":"Senegal","adm1name":"Dakar","iso_a2":"SM"},"coordinates":[451458,591912]},{"type":"Point","properties":{"scalerank":1,"labelrank":3,"name":"Johannesburg","adm0name":"South Africa","adm1name":"Gauteng","iso_a2":"ZA"},"coordinates":[577855,349685]},{"type":"Point","properties":{"scalerank":1,"labelrank":8,"name":"Amsterdam","adm0name":"Netherlands","adm1name":"Noord-Holland","iso_a2":"NL"},"coordinates":[513651,814873]},{"type":"Point","properties":{"scalerank":1,"labelrank":5,"name":"Casablanca","adm0name":"Morocco","adm1name":"Grand Casablanca","iso_a2":"MA"},"coordinates":[478837,703790]},{"type":"Point","properties":{"scalerank":1,"labelrank":3,"name":"Seoul","adm0name":"South Korea","adm1name":"Seoul","iso_a2":"KR"},"coordinates":[852771,727288]},{"type":"Point","properties":{"scalerank":1,"labelrank":5,"name":"Manila","adm0name":"Philippines","adm1name":"Metropolitan Manila","iso_a2":"PH"},"coordinates":[836056,591250]},{"type":"Point","properties":{"scalerank":1,"labelrank":2,"name":"Monterrey","adm0name":"Mexico","adm1name":"Nuevo León","iso_a2":"MX"},"coordinates":[221300,656809]},{"type":"Point","properties":{"scalerank":1,"labelrank":8,"name":"Auckland","adm0name":"New Zealand","adm1name":"Auckland","iso_a2":"NZ"},"coordinates":[985452,286413]},{"type":"Point","properties":{"scalerank":1,"labelrank":3,"name":"Berlin","adm0name":"Germany","adm1name":"Berlin","iso_a2":"DE"},"coordinates":[537221,815891]},{"type":"Point","properties":{"scalerank":1,"labelrank":1,"name":"Urumqi","adm0name":"China","adm1name":"Xinjiang Uygur","iso_a2":"CN"},"coordinates":[743258,764249]},{"type":"Point","properties":{"scalerank":1,"labelrank":1,"name":"Chengdu","adm0name":"China","adm1name":"Sichuan","iso_a2":"CN"},"coordinates":[789078,686432]},{"type":"Point","properties":{"scalerank":1,"labelrank":2,"name":"Osaka","adm0name":"Japan","adm1name":"Osaka","iso_a2":"JP"},"coordinates":[876272,710604]},{"type":"Point","properties":{"scalerank":1,"labelrank":3,"name":"Kinshasa","adm0name":"Congo (Kinshasa)","adm1name":"Kinshasa City","iso_a2":"CD"},"coordinates":[542536,479077]},{"type":"Point","properties":{"scalerank":1,"labelrank":1,"name":"New Delhi","adm0name":"India","adm1name":"Delhi","iso_a2":"IN"},"coordinates":[714444,674156]},{"type":"Point","properties":{"scalerank":1,"labelrank":1,"name":"Bangalore","adm0name":"India","adm1name":"Karnataka","iso_a2":"IN"},"coordinates":[715438,581569]},{"type":"Point","properties":{"scalerank":1,"labelrank":6,"name":"Athens","adm0name":"Greece","adm1name":"Attiki","iso_a2":"GR"},"coordinates":[565920,729759]},{"type":"Point","properties":{"scalerank":1,"labelrank":5,"name":"Baghdad","adm0name":"Iraq","adm1name":"Baghdad","iso_a2":"IQ"},"coordinates":[623310,702242]},{"type":"Point","properties":{"scalerank":1,"labelrank":3,"name":"Addis Ababa","adm0name":"Ethiopia","adm1name":"Addis Ababa","iso_a2":"ET"},"coordinates":[607494,558246]},{"type":"Point","properties":{"scalerank":1,"labelrank":5,"name":"Tehran","adm0name":"Iran","adm1name":"Tehran","iso_a2":"IR"},"coordinates":[642839,716065]},{"type":"Point","properties":{"scalerank":1,"labelrank":2,"name":"Vancouver","adm0name":"Canada","adm1name":"British Columbia","iso_a2":"CA"},"coordinates":[157990,796647]},{"type":"Point","properties":{"scalerank":1,"labelrank":2,"name":"Toronto","adm0name":"Canada","adm1name":"Ontario","iso_a2":"CA"},"coordinates":[279383,763627]},{"type":"Point","properties":{"scalerank":1,"labelrank":3,"name":"Buenos Aires","adm0name":"Argentina","adm1name":"Ciudad de Buenos Aires","iso_a2":"AR"},"coordinates":[337779,299727]},{"type":"Point","properties":{"scalerank":1,"labelrank":5,"name":"Kabul","adm0name":"Afghanistan","adm1name":"Kabul","iso_a2":"AF"},"coordinates":[692169,709221]},{"type":"Point","properties":{"scalerank":1,"labelrank":7,"name":"Vienna","adm0name":"Austria","adm1name":"Wien","iso_a2":"AT"},"coordinates":[545457,790287]},{"type":"Point","properties":{"scalerank":1,"labelrank":3,"name":"Melbourne","adm0name":"Australia","adm1name":"Victoria","iso_a2":"AU"},"coordinates":[902702,280666]},{"type":"Point","properties":{"scalerank":1,"labelrank":8,"name":"Taipei","adm0name":"Taiwan","adm1name":"Taipei City","iso_a2":"TW"},"coordinates":[837688,653040]},{"type":"Point","properties":{"scalerank":0,"labelrank":1,"name":"Los Angeles","adm0name":"United States of America","adm1name":"California","iso_a2":"US"},"coordinates":[171717,706100]},{"type":"Point","properties":{"scalerank":0,"labelrank":1,"name":"Washington, D.C.","adm0name":"United States of America","adm1name":"District of Columbia","iso_a2":"US"},"coordinates":[286079,735187]},{"type":"Point","properties":{"scalerank":0,"labelrank":1,"name":"New York","adm0name":"United States of America","adm1name":"New York","iso_a2":"US"},"coordinates":[294495,746150]},{"type":"Point","properties":{"scalerank":0,"labelrank":5,"name":"London","adm0name":"United Kingdom","adm1name":"Westminster","iso_a2":"GB"},"coordinates":[499670,809838]},{"type":"Point","properties":{"scalerank":0,"labelrank":5,"name":"Istanbul","adm0name":"Turkey","adm1name":"Istanbul","iso_a2":"TR"},"coordinates":[580577,748253]},{"type":"Point","properties":{"scalerank":0,"labelrank":5,"name":"Riyadh","adm0name":"Saudi Arabia","adm1name":"Ar Riyad","iso_a2":"SA"},"coordinates":[629918,650712]},{"type":"Point","properties":{"scalerank":0,"labelrank":3,"name":"Cape Town","adm0name":"South Africa","adm1name":"Western Cape","iso_a2":"ZA"},"coordinates":[551202,303771]},{"type":"Point","properties":{"scalerank":0,"labelrank":2,"name":"Moscow","adm0name":"Russia","adm1name":"Moskva","iso_a2":"RU"},"coordinates":[604482,835030]},{"type":"Point","properties":{"scalerank":0,"labelrank":2,"name":"Mexico City","adm0name":"Mexico","adm1name":"Distrito Federal","iso_a2":"MX"},"coordinates":[224631,619915]},{"type":"Point","properties":{"scalerank":0,"labelrank":2,"name":"Lagos","adm0name":"Nigeria","adm1name":"Lagos","iso_a2":"NG"},"coordinates":[509415,542902]},{"type":"Point","properties":{"scalerank":0,"labelrank":3,"name":"Rome","adm0name":"Italy","adm1name":"Lazio","iso_a2":"IT"},"coordinates":[534670,752939]},{"type":"Point","properties":{"scalerank":0,"labelrank":1,"name":"Beijing","adm0name":"China","adm1name":"Beijing","iso_a2":"CN"},"coordinates":[823294,741286]},{"type":"Point","properties":{"scalerank":0,"labelrank":5,"name":"Nairobi","adm0name":"Kenya","adm1name":"Nairobi","iso_a2":"KE"},"coordinates":[602262,497125]},{"type":"Point","properties":{"scalerank":0,"labelrank":1,"name":"Jakarta","adm0name":"Indonesia","adm1name":"Jakarta Raya","iso_a2":"ID"},"coordinates":[796742,468148]},{"type":"Point","properties":{"scalerank":0,"labelrank":5,"name":"Bogota","adm0name":"Colombia","adm1name":"Bogota","iso_a2":"CO"},"coordinates":[294207,531960]},{"type":"Point","properties":{"scalerank":0,"labelrank":3,"name":"Cairo","adm0name":"Egypt","adm1name":"Al Qahirah","iso_a2":"EG"},"coordinates":[586799,682758]},{"type":"Point","properties":{"scalerank":0,"labelrank":1,"name":"Shanghai","adm0name":"China","adm1name":"Shanghai","iso_a2":"CN"},"coordinates":[837317,689669]},{"type":"Point","properties":{"scalerank":0,"labelrank":2,"name":"Tokyo","adm0name":"Japan","adm1name":"Tokyo","iso_a2":"JP"},"coordinates":[888192,716142]},{"type":"Point","properties":{"scalerank":0,"labelrank":1,"name":"Mumbai","adm0name":"India","adm1name":"Maharashtra","iso_a2":"IN"},"coordinates":[702374,617394]},{"type":"Point","properties":{"scalerank":0,"labelrank":3,"name":"Paris","adm0name":"France","adm1name":"Île-de-France","iso_a2":"FR"},"coordinates":[506476,794237]},{"type":"Point","properties":{"scalerank":0,"labelrank":3,"name":"Santiago","adm0name":"Chile","adm1name":"Región Metropolitana de Santiago","iso_a2":"CL"},"coordinates":[303697,306556]},{"type":"Point","properties":{"scalerank":0,"labelrank":1,"name":"Kolkata","adm0name":"India","adm1name":"West Bengal","iso_a2":"IN"},"coordinates":[745340,637999]},{"type":"Point","properties":{"scalerank":0,"labelrank":1,"name":"Rio de Janeiro","adm0name":"Brazil","adm1name":"Rio de Janeiro","iso_a2":"BR"},"coordinates":[379925,368910]},{"type":"Point","properties":{"scalerank":0,"labelrank":1,"name":"Sao Paulo","adm0name":"Brazil","adm1name":"São Paulo","iso_a2":"BR"},"coordinates":[370480,365156]},{"type":"Point","properties":{"scalerank":0,"labelrank":3,"name":"Sydney","adm0name":"Australia","adm1name":"New South Wales","iso_a2":"AU"},"coordinates":[919952,303771]},{"type":"Point","properties":{"scalerank":0,"labelrank":0,"name":"Singapore","adm0name":"Singapore","iso_a2":"SG"},"coordinates":[788482,512389]},{"type":"Point","properties":{"scalerank":0,"labelrank":0,"name":"Hong Kong","adm0name":"Hong Kong S.A.R.","iso_a2":"HK"},"coordinates":[817174,636873]}]}},"arcs":[[[85470,47819],[-4327,823],[2549,633],[1778,-1456]],[[135926,62848],[-3045,936],[3281,69],[-236,-1005]],[[54258,21344],[-9164,857],[3638,778],[5526,-1635]],[[965673,41772],[-3773,-978],[-481,1285],[4254,-307]],[[964121,46628],[6302,-1202],[-5445,-735],[-1844,-1198],[-1422,1933],[2409,1202]],[[405738,34787],[-7075,156],[2628,1209],[4447,-1365]],[[313161,34004],[-3575,90],[1434,1245],[2141,-1335]],[[334072,28722],[-2358,-3579],[-10237,1183],[-4755,2157],[12074,239],[-141,1922],[5030,1437],[387,-3359]],[[316184,30299],[-4542,3151],[4985,-464],[1416,-1955],[-1859,-732]],[[58185,31902],[-3469,-220],[-10900,3103],[279,1928],[2416,1619],[4480,-1060],[5441,-2972],[1753,-2398]],[[374381,37806],[4168,-45],[2102,-3902],[-1562,-4233],[-15721,-2674],[-1627,-838],[-12193,-508],[-513,1781],[2648,2728],[3047,-191],[5438,3920],[-1094,1165],[1427,4014],[3162,3306],[6265,1552],[3599,-570],[4781,-2400],[-384,-1842],[-3543,-1263]],[[304627,32657],[-4026,1396],[3443,3321],[8515,3087],[2085,-125],[-7008,-4897],[-3009,-2782]],[[146206,62619],[-2132,1749],[2510,-580],[-378,-1169]],[[149084,70536],[2927,-2396],[3427,-1125],[571,-2044],[-2879,102],[-6552,2931],[-99,2426],[2605,106]],[[442757,66979],[329,-3590],[-2221,2981],[1967,2192],[-75,-1583]],[[165121,67753],[789,-1381],[-2196,-2064],[-4990,-31],[652,2234],[-1354,1679],[7099,-437]],[[175726,65330],[-1773,486],[2764,1288],[-991,-1774]],[[167919,65653],[-704,1752],[2341,30],[-1637,-1782]],[[227524,78675],[1407,176],[346,-1810],[1641,1998],[2068,-264],[-1873,-2157],[3305,1133],[94,-2024],[-1323,-990],[-6544,175],[-4965,1629],[-5749,813],[380,889],[5676,880],[698,-1240],[3382,1673],[1457,-881]],[[293460,71648],[-612,-3038],[-3684,1650],[-324,1466],[1780,1570],[2508,-435],[332,-1213]],[[246776,71151],[-1415,3309],[2396,79],[-981,-3388]],[[492964,85456],[1224,-315],[-1920,-2052],[-2094,2867],[2790,-500]],[[295259,86241],[-1384,-1711],[-5476,-1234],[-900,1132],[5678,2100],[2082,-287]],[[331597,134082],[2154,-324],[-1032,-773],[-1122,1097]],[[346762,142020],[-615,875],[1989,-263],[-1374,-612]],[[338949,137923],[941,-643],[-3790,-1122],[890,1194],[1959,571]],[[396935,184408],[2158,-1100],[1466,-3035],[-797,-612],[-1169,2107],[-2754,2105],[1096,535]],[[301693,184601],[1024,-467],[-337,-1554],[-917,846],[-1320,-412],[-728,1500],[594,965],[1684,-878]],[[302801,179654],[1594,-243],[38,-1520],[-1488,614],[-144,1149]],[[306380,179352],[2229,-586],[1717,-1406],[651,-2298],[-3469,2828],[-319,-1650],[-1578,1759],[769,1353]],[[313667,177961],[-2751,-400],[-642,1425],[2933,19],[460,-1044]],[[320696,180552],[2039,-51],[-2284,-1052],[245,1103]],[[341609,129267],[-3451,-2313],[-2179,-2791],[726,-1717],[-1874,1058],[-1395,-508],[-3047,-3257],[-1787,-37],[349,-1175],[-1577,-865],[1081,-1407],[-1525,-1607],[1294,-1653],[2573,1177],[965,-347],[-1286,-2116],[-1121,1137],[-1232,-897],[-2240,348],[-63,-2623],[-1536,2467],[-1375,-83],[-903,-2230],[897,-1298],[-2587,397],[-947,-2416],[-1156,-666],[-545,-4764],[2065,-225],[-1544,-998],[584,-1459],[3231,-1115],[783,1786],[946,-3389],[2793,-3213],[1360,-3174],[-1158,-1450],[2425,-745],[-1267,-2429],[1800,194],[457,-2810],[-2251,-1870],[2070,555],[496,-1517],[-3393,-1171],[4270,-326],[-31,-2093],[1690,-5031],[-2665,-315],[-2575,1070],[1032,-2087],[2106,-663],[-67,-1532],[-2598,-957],[2929,-1237],[-631,-1407],[-3401,220],[853,-2498],[-2168,746],[-3378,-1584],[1927,-885],[-2913,-825],[2545,-940],[-8353,-3329],[-8150,-1998],[-2197,-1800],[-4731,-581],[-9638,1015],[-5338,-287],[2615,-3822],[2394,-1180],[7041,-689],[-1110,-1801],[-4335,-1679],[-8139,1406],[-7943,1116],[-2331,-791],[11134,-3252],[-1212,-1842],[-6731,-459],[-6434,2433],[-3207,-290],[1397,-1855],[4442,-1929],[2136,-2381],[1120,1072],[11082,-29],[1299,-1786],[-1462,-1638],[-8618,-553],[4123,-1006],[4913,426],[3252,-4193],[6131,-680],[5588,1767],[2163,-1407],[15247,-3939],[-3239,-2255],[2769,-2775],[11279,1090],[-5213,-2019],[4265,-3381],[-1082,-1441],[5760,-694],[6007,3662],[9354,3791],[15626,1826],[5841,-325],[229,-3344],[6962,1430],[6210,5761],[7394,2462],[4340,-1077],[5229,2449],[16661,2835],[14135,653],[-165,1725],[-15693,1016],[-1018,2583],[-7440,-388],[-9014,2692],[162,1813],[3037,3740],[2810,2439],[5591,1573],[8693,4678],[7985,2447],[4971,1126],[7877,497],[2629,1133],[6063,360],[-1235,1121],[4029,5380],[4517,-435],[3053,2783],[-3264,-47],[-2139,1786],[2563,3243],[3542,-156],[65,2310],[3025,-308],[4754,2204],[1497,3028],[-3305,1707],[-565,1328],[1912,346],[1926,-1356],[2601,2544],[-182,1210],[2412,-1467],[1623,-2955],[2591,748],[-445,3592],[5382,1348],[968,-853],[-1472,-2780],[9182,30],[2215,-667],[2630,994],[1458,-2647],[3828,3022],[4929,1792],[6954,1448],[6356,954],[766,818],[2351,-695],[1718,1719],[5727,-3230],[1383,-224],[3790,4224],[2103,-1715],[5512,114],[2161,712],[345,-1147],[3932,-847],[855,1483],[2122,-19],[191,-3609],[5015,349],[1762,3465],[1836,-1282],[-367,-993],[2070,-993],[2291,2403],[1200,-261],[1447,-2627],[4839,-755],[6702,2586],[3033,1676],[3821,440],[3451,1334],[1023,2230],[-1172,3259],[1539,2280],[2976,-77],[-1054,-2352],[2173,28],[2114,-3476],[3783,173],[1100,-939],[2860,-80],[1986,-1077],[2349,3438],[441,2716],[3525,2323],[3545,1323],[1141,1354],[5222,1297],[805,800],[3916,899],[446,2070],[2342,-835],[-794,-970],[4253,-1312],[-49,1621],[1636,1741],[-2095,1085],[2172,605],[3627,-1499],[-612,4443],[4311,2516],[4965,954],[3544,-340],[2118,-970],[4098,-3159],[-2888,-76],[682,-2060],[-657,-1724],[2047,1235],[2044,250],[3083,-1276],[1426,-1514],[3420,591],[6127,-1555],[2809,826],[12858,-2259],[3024,869],[1571,-4273],[-1244,-1616],[265,-2930],[-2009,-838],[489,-2924],[-2512,172],[-2528,-2582],[1869,-887],[3000,580],[637,-627],[-1048,-3579],[-2321,-2110],[-1681,-3625],[-2397,-7146],[2091,-539],[1804,1271],[1245,3381],[3153,833],[4783,4447],[1745,5434],[2376,1842],[172,1775],[3111,2091],[4116,-889],[1298,1881],[5351,3002],[2524,4686],[1560,940],[7581,2544],[3576,515],[3280,2894],[3405,-277],[6304,2209],[4773,-206],[3675,1306],[2922,562],[5252,-1077],[2432,1115],[1948,-767],[4332,778],[1719,-638],[1495,828],[1670,-1205],[5640,1853],[1626,2411],[3332,509],[3547,-728],[6766,-2503],[2178,-355],[1706,-1146],[4661,-1449],[3220,2279],[790,2650],[6090,1640],[1697,-769],[1742,-2551],[2704,-1189],[902,-1247],[-1004,-1521],[-3563,-1089],[99,-1358],[7463,2333],[6254,-577],[3917,954],[-3040,-1277],[-410,-1015],[6541,1657],[1931,1371],[5592,1533],[2498,-239],[2139,1638],[2221,-789],[2433,-3279],[2472,-402],[2240,459],[1400,3394],[3363,1643],[2442,-264],[4482,916],[2040,-1159],[307,-1183],[2951,2071],[3338,-1847],[933,588],[3474,-1210],[3060,-179],[1829,-837],[5758,-542],[1985,-1221],[2897,807],[502,-1269],[1973,-300],[-1887,-3867],[686,-625],[2608,1622],[2354,11],[2368,-2017],[738,-2395],[3786,-581],[7255,486],[195,-2248],[3909,205],[1488,-753],[1732,758],[222,2246],[1153,-404],[3668,-3594],[5447,-1684],[1254,758],[5142,-2021],[1183,-2684],[2298,-2028],[1076,-3019],[2395,-842],[-686,2039],[1765,1897],[1872,-1873],[2929,653],[6177,-911],[2618,-864],[1674,-2210],[5528,-2650],[759,1254],[1177,-2665],[-1764,-470],[-490,-3867],[-3692,1281],[3083,-2039],[-788,-1906],[-6637,-574],[1278,-1122],[-1721,-1230],[-3155,-287],[-1426,-1698],[-604,2942],[-1055,-1043],[72,-2782],[1465,-3349],[-4355,178],[-1305,813],[-1837,-2351],[-196,-2037],[-4508,-993],[2758,-413],[2533,-2617],[-146,-5329],[2379,-4958],[2227,-1783],[-1232,-2017],[1807,-543],[2192,1620],[683,-1562],[3875,-1260],[-6732,-503],[-5542,-1744],[-2450,2089],[539,-2873],[-2113,303],[-3595,-5214],[1840,-898],[-5515,-2446],[5469,-9],[-201,-5425],[6531,-3114],[2386,-1902],[-6658,-1793],[9622,802],[10723,-4212],[-1382,-1756],[7520,-437],[3056,-1235],[21397,-3698],[-975899,-1624],[13312,-1639],[11050,-486],[17983,-1689],[-1464,2223],[-18283,1673],[-3840,4281],[-7229,-28],[-4485,2139],[-13607,3256],[6236,312],[9455,-2260],[6113,167],[4921,-1581],[13891,-447],[5600,1164],[-697,1285],[6167,882],[6804,3147],[-4743,3015],[2114,1425],[-8545,2257],[1401,929],[23349,1550],[-6814,3241],[986,1206],[5287,469],[391,1749],[-6429,1364],[-4446,1801],[-8662,1639],[-3498,1952],[6044,2229],[-8259,352],[-3428,2496],[798,3681],[5561,304],[6393,-717],[1248,-1120],[4837,-59],[5559,-2202],[4339,1985],[-1156,2117],[8042,-2268],[-205,4378],[-4416,1840],[-3502,-326],[-2925,758],[3706,1547],[6763,-1896],[-1382,1923],[7801,3176],[3457,432],[4044,-1512],[-969,1186],[4255,1972],[5758,814],[2970,-378],[892,1797],[2406,863],[5241,-957],[3961,511],[6273,-746],[5355,1020],[6971,29],[4067,-346],[11702,700],[2836,1553],[9862,-371],[874,2764],[3562,-593],[-1672,-5291],[6706,1123],[-305,3096],[1738,484],[2426,-1059],[19,-2037],[-3230,-2505],[6086,-305],[7369,-943],[4771,1357],[8870,-72],[1859,-1745],[6893,1374],[-5321,1880],[986,2118],[-3148,174],[-1288,2736],[-2921,829],[990,1585],[3959,-834],[5803,865],[-3124,1236],[-7374,484],[-1943,2973],[2810,349],[-127,-1346],[12213,-273],[5167,-1634],[2544,527],[2751,-550],[5570,797],[3667,-834],[1737,2023],[2651,523],[594,1164],[2591,-981],[-625,-2195],[2829,220],[1008,-959],[3435,959],[2279,-1836],[7771,-2103],[2430,703],[129,2508],[2572,-588],[-294,2783],[2557,-861],[3094,-2761],[4326,565],[-551,-2273],[4877,1217],[3603,-362],[2964,1491],[11411,2029],[3206,1606],[2331,4409],[-1757,3338],[-123,2779],[-1066,3768],[-1431,2382],[-846,3480],[3712,118],[1102,1489],[-1154,1777],[1659,3678],[-991,1278],[1236,2948],[-2172,-119],[-18,2574],[1495,758],[1342,-1529],[-152,2974],[1887,538],[639,3049],[5054,3881],[-667,1785],[772,862],[1948,-628],[-169,1166],[4133,2002],[2133,3147],[3761,1497],[1772,1593],[4118,1929],[1025,-868]],[[291702,91617],[1526,-1313],[-1142,-1361],[-2438,493],[126,1270],[1928,911]],[[574603,87746],[-2366,49],[821,1781],[1545,-1830]],[[305413,94809],[1762,-2334],[3003,-7541],[263,-5724],[-2689,-4219],[-3706,-771],[-5067,-32],[-1997,1551],[1300,788],[4849,-542],[-2847,1455],[2991,1270],[-3995,1571],[-1857,-1661],[-1959,514],[-1008,-1985],[-3770,1728],[168,1561],[2502,-139],[568,1513],[5488,284],[-2358,1232],[3728,-107],[1456,949],[3788,421],[-3386,898],[-32,1334],[2036,1042],[1971,-218],[-1970,1411],[-2202,-217],[-2037,1346],[308,3237],[-875,2499],[4558,1257],[1016,-2371]],[[545062,89959],[-1454,1389],[2573,136],[-1119,-1525]],[[300040,91791],[-2198,317],[141,1374],[2057,-1691]],[[327784,91653],[-1583,3208],[2088,-1160],[-505,-2048]],[[311143,104968],[-2536,-1599],[-503,2021],[2074,3560],[1404,1054],[-383,-2416],[779,-682],[-835,-1938]],[[780503,115614],[-1975,438],[1703,1334],[272,-1772]],[[324498,122769],[956,-605],[-2508,-1556],[-1480,811],[2185,2590],[847,-1240]],[[326873,123038],[-1170,-280],[1161,2714],[751,-739],[-742,-1695]],[[339316,125233],[1657,-257],[-125,-1598],[-1602,-201],[-1462,1576],[1311,1947],[221,-1467]],[[345755,130448],[-2362,-1562],[-19,1204],[2381,358]],[[10618,245580],[-434,-1826],[-1428,1329],[1862,497]],[[23739,421499],[-1273,266],[-371,856],[1226,-132],[418,-990]],[[21295,424942],[302,-2008],[-863,75],[-674,1628],[1235,305]],[[330522,564757],[-2484,-386],[1130,1182],[96,1602],[-474,950],[1994,835],[-322,-1010],[60,-3173]],[[849139,563455],[-543,1210],[478,1904],[65,-3114]],[[789066,566277],[-128,-2142],[-468,2025],[596,117]],[[322640,570662],[-189,-1442],[-1347,557],[1536,885]],[[756952,567249],[-417,1163],[437,867],[-20,-2030]],[[846040,571919],[987,176],[231,-3479],[667,-3091],[-738,614],[108,-1981],[-683,799],[17,3634],[-948,842],[-319,3624],[678,-1138]],[[840266,573530],[1840,-471],[-394,-2483],[-685,-1734],[-1590,-1190],[-581,-975],[-68,2449],[388,4569],[-389,1507],[1479,-1672]],[[847887,578938],[822,-1994],[-83,-3834],[551,-2248],[-1307,-116],[-879,2448],[-91,1286],[-1388,2776],[-251,1928],[2626,-246]],[[273324,548160],[-691,714],[342,994],[349,-1708]],[[465204,548773],[-1181,797],[934,393],[247,-1190]],[[825863,554717],[108,1623],[1486,3123],[639,628],[2525,5965],[731,1306],[-72,1607],[667,2969],[71,-2329],[438,-2379],[-1110,-1777],[-260,-1130],[-1136,-858],[-965,-3911],[-1237,-2245],[-1885,-2592]],[[846093,562701],[-650,-930],[-1178,-37],[-329,1146],[988,1883],[1123,-643],[46,-1419]],[[842029,558417],[-1578,2481],[-421,1250],[167,1586],[1068,743],[-108,2469],[462,2268],[760,636],[863,-1263],[-1126,-5430],[406,-3005],[-493,-1735]],[[842694,560701],[45,3066],[903,3001],[909,4739],[35,-4076],[-275,-1594],[-858,-1756],[-759,-3380]],[[65314,628731],[1381,-1039]],[[66695,627692],[-1247,-825],[-134,1864]],[[63295,630407],[1393,-358]],[[64688,630049],[-410,-585]],[[64278,629464],[-983,943]],[[297147,630270],[-382,-1263],[-1379,-247],[383,1501],[1378,9]],[[61668,631836],[456,-883]],[[62124,630953],[-1320,65]],[[60804,631018],[-453,1580]],[[60351,632598],[863,688]],[[61214,633286],[454,-1450]],[[67829,617353],[-833,346],[-465,4026],[635,1565]],[[67166,623290],[-33,1550]],[[67133,624840],[1759,-1667],[1095,-2784]],[[69987,620389],[-1404,-1566]],[[68583,618823],[-754,-1470]],[[663116,624502],[-224,703],[675,2033],[185,-974],[-636,-1762]],[[833610,576804],[595,-920],[-843,-25],[248,945]],[[329773,595059],[-554,1636],[447,355],[107,-1991]],[[434877,593631],[-948,408],[71,1386],[877,-1794]],[[328918,599549],[-472,327],[31,1737],[544,-500],[-103,-1564]],[[436338,600914],[626,-404],[-355,-1084],[-271,1488]],[[329646,600873],[-541,-14],[141,1651],[400,-1637]],[[430083,605116],[-464,856],[993,22],[-529,-878]],[[433089,603196],[-652,-766],[-151,1094],[803,-328]],[[331038,590589],[-1093,1836],[836,-409],[257,-1427]],[[902059,583271],[-257,1004],[809,839],[-552,-1843]],[[843655,577513],[901,-1899],[14,-1270],[-1429,2631],[-1037,-1605],[218,3897],[1333,-1754]],[[839149,577913],[-474,-140],[616,1904],[-142,-1764]],[[649342,579583],[1178,161],[899,-659],[-1060,-1138],[-1475,-109],[-785,1131],[609,1082],[634,-468]],[[757562,573062],[-524,1998],[447,2022],[451,6775],[570,1110],[32,-1738],[-521,-1836],[288,-2392],[-743,-5939]],[[835289,584576],[1384,-280],[889,-1784],[50,-2921],[-845,-2484],[-874,1735],[-439,2714],[-1007,3242],[842,-222]],[[838651,584936],[555,-457],[-304,-1530],[-488,733],[237,1254]],[[845425,585481],[-874,183],[517,2455],[534,-1224],[-177,-1414]],[[836392,615002],[2067,-1895],[836,1133],[426,-497],[-414,-3828],[328,-2141],[695,-1602],[-1067,-5569],[-1499,-1490],[32,-1561],[-596,-2046],[842,-3478],[-130,-1516],[422,-2178],[1142,-1088],[-33,1291],[808,1032],[1015,-424],[1043,-2982],[247,1862],[1492,-1553],[-855,-911],[748,-2230],[-95,-941],[994,-443],[-230,-2777],[-507,727],[198,1343],[-1771,756],[-411,2356],[-1578,2760],[-353,-124],[575,-3753],[-1673,3171],[-819,884],[-1595,-1762],[-1008,1448],[-567,-475],[-55,2272],[847,1808],[-94,1319],[-846,980],[15,-2358],[-417,-177],[-991,2357],[-529,5845],[-340,1011],[170,1885],[915,-1652],[638,1030],[-234,1823],[287,2526],[-139,4044],[669,5152],[1395,636]],[[285385,614067],[2532,-1810],[386,-1412],[-872,-280],[-912,637],[-978,-1534],[-1564,963],[-848,1859],[-613,159],[215,1363],[954,439],[1700,-384]],[[300613,621536],[615,1050],[1677,110],[2291,-1646],[479,212],[604,-2208],[1151,169],[-829,-1243],[2608,-1262],[960,-1738],[-1061,-2329],[-594,1123],[-2322,211],[-1145,-1136],[-1283,500],[-1064,-373],[-1022,-3715],[-1035,2328]],[[300643,611589],[-810,1122],[-2268,-455],[-1413,589],[-1388,-1240],[-1484,1803],[532,1875],[1767,-831],[2228,-519],[1227,1423],[-1287,2350],[299,2189],[-1925,1289],[774,1452],[1335,-17],[1159,-926],[1224,-157]],[[316307,613993],[1390,-376],[23,-824],[-973,-1588],[-3405,118],[106,2992],[2859,-322]],[[808024,623158],[347,-1993],[-533,-578],[-694,-2287],[-336,-2512],[-2588,-3138],[-2272,1878],[-183,2207],[161,2551],[1619,2505],[107,849],[3787,1378],[585,-860]],[[782522,517031],[-293,-2149],[-423,2086],[716,63]],[[856816,516873],[-656,1455],[1067,1778],[59,-2210],[-470,-1023]],[[767954,518699],[-1820,1751],[-7,1543],[1698,-2375],[129,-919]],[[852267,528613],[-164,3048],[455,-1522],[-291,-1526]],[[524265,526983],[585,-775],[-673,-2393],[-637,243],[725,2925]],[[800877,526576],[-599,88],[-269,2003],[551,936],[531,-1270],[-214,-1757]],[[722171,562852],[753,-97],[1273,-2547],[1838,-5539],[175,-1852],[1217,-4921],[-35,-2293],[-622,-2820],[-715,-1092],[-1822,-1551],[-1270,182],[-477,849],[-655,4006],[-421,7325],[280,-93],[712,6195],[58,2856],[-289,1392]],[[839146,542801],[-790,1099],[694,752],[638,-603],[-542,-1248]],[[836553,540712],[645,-436],[-1280,-627],[635,1063]],[[760805,545187],[-572,2089],[486,138],[86,-2227]],[[850016,559939],[520,-263],[351,-2559],[-495,-1288],[621,-850],[195,-3857],[375,-921],[32,-2545],[-1083,-2341],[-7,-3217],[-1014,6065],[-1176,-3185],[520,-1955],[222,-2886],[-1056,-2052],[-54,2375],[-647,-963],[-2285,2149],[-375,1014],[-257,3491],[615,2386],[-662,1589],[-1321,849],[-283,-2372],[-818,1735],[-704,-1014],[-143,1144],[-816,-294],[-894,-3961],[-589,-213],[466,4990],[570,1291],[1832,1138],[59,1054],[1158,1806],[1152,-1603],[-267,-2218],[1235,1015],[704,2232],[778,-257],[381,2425],[757,-613],[191,938],[803,-73],[-236,3877],[297,533],[1348,-2596]],[[293373,191181],[2597,-1316],[-2276,373],[-321,943]],[[297435,187868],[1994,-1930],[-993,-1578],[-1099,46],[541,1191],[-1503,-469],[-26,1273],[-1476,1087],[1107,804],[1455,-424]],[[309361,192779],[809,-1466],[-416,-2138],[909,-269],[424,-1527],[1984,-2877],[2941,-2866],[2935,-857],[-464,-1184],[-3236,-913],[-1165,634],[-3583,637],[-1203,-214]],[[309296,179739],[-2314,-31],[-659,870],[-2149,-578],[-3916,1864],[3356,866],[-469,2753],[934,1521],[421,-2130],[-694,-110],[1280,-2214],[1186,435],[1358,-1491],[580,893],[-2622,1763],[-446,2062],[2212,1665],[-790,864],[-1260,-497],[-1027,1255],[2672,4235],[920,-1043],[1492,88]],[[291370,215386],[-1109,-2334],[-314,2042],[1423,292]],[[292868,216836],[-988,-204],[-764,3862],[1030,736],[722,-4394]],[[692179,213769],[973,802],[366,-1722],[1815,1223],[654,-847],[-471,-1377],[-1307,505],[-376,-838],[1464,-553],[-645,-741],[-2561,1059],[-1028,-720],[-37,3468],[799,2426],[354,-2685]],[[290249,215820],[-391,1050],[251,3053],[470,303],[647,-3572],[-977,-834]],[[291514,206740],[-1095,-280],[340,1964],[1252,-581],[-497,-1103]],[[293121,213543],[-329,-5088],[-1015,2806],[-294,-1892],[-1344,362],[1288,3087],[-272,1106],[1085,2241],[630,-293],[251,-2329]],[[293575,234020],[-1047,171],[1054,2902],[-7,-3073]],[[295179,241703],[-688,-593],[711,-3699],[-1033,-1222],[-1441,4013],[1446,1532],[547,1508],[458,-1539]],[[297260,239419],[-1010,-314],[-264,1138],[659,1814],[1232,-1269],[-617,-1369]],[[967067,227084],[268,-1231],[-1997,-1118],[728,3310],[1001,-961]],[[911110,269175],[825,-1352],[-531,-1637],[-941,2321],[647,668]],[[899799,267051],[58,3154],[534,-2192],[-592,-962]],[[882212,292985],[1250,-98],[-1663,-1893],[-1925,247],[-323,1684],[1931,926],[730,-866]],[[980875,260159],[1729,1721],[1569,-181],[-648,-2429],[594,-1843],[-2050,-4606],[-900,-2717],[-1351,-2240],[919,-3080],[-2461,-127],[-2048,-1421],[-856,-4987],[-1205,-4186],[211,-1106],[-992,-415],[-2036,-3618],[-1633,-468],[-1990,150],[-536,1439],[-1408,1004],[-2641,-29],[-607,2288],[1326,1658],[-712,-70],[810,2487],[3725,6172],[1222,534],[5375,6307],[1419,3113],[650,3597],[1456,2073],[358,2948],[1392,2542],[967,-1955],[352,-2555]],[[295074,247915],[-1706,677],[494,2092],[480,6418],[1414,-598],[158,-3377],[-884,-707],[980,-2078],[-936,-2427]],[[902895,263078],[668,100],[2874,-2332],[1867,1014],[1292,-55],[1427,1315],[901,-992],[24,-6475],[-244,415],[-804,-3569],[157,-3464],[-542,-373],[-590,2218],[-628,-479],[-1315,-4065],[-904,615],[-1403,-227],[-136,1013],[-1408,2663],[-802,4122],[882,-732],[-1669,4208],[-748,3930],[201,1828],[900,-678]],[[981302,297748],[1597,-542],[1323,-1306],[723,-3193],[-526,70],[1142,-3174],[-222,-3150],[1603,-901],[675,-1233],[-227,4300],[1154,-2856],[449,-3809],[2034,-1712],[1572,-600],[1870,2583],[1464,-813],[-746,-5090],[-810,-1013],[-187,-3065],[-1392,939],[-1260,-1697],[433,-1811],[-744,-2871],[-2387,-6253],[-1869,-2354],[-401,1145],[-1473,758],[1465,3956],[254,1969],[-680,1997],[-2986,2625],[-356,2012],[1644,1226],[554,1052],[327,3314],[591,2495],[-950,4187],[-1103,3587],[591,-649],[-25,2144],[-1320,1314],[77,-934],[-2301,5750],[205,1120],[-1346,3324],[716,-474],[848,-2367]],[[989070,274777],[-712,800],[-39,-1605],[751,805]],[[814396,350367],[-607,1599],[50,1558],[557,-3157]],[[925214,352158],[-109,3303],[566,1604],[301,-833],[-758,-4074]],[[970372,392507],[-965,408],[435,1423],[530,-1831]],[[956117,384770],[648,-214],[2100,-2884],[1308,-2951],[1780,-2193],[1775,-2683],[-468,-1694],[-1659,1700],[-224,785],[-2372,2554],[-874,1396],[-2105,4796],[91,1388]],[[654992,378293],[-1207,389],[-143,2190],[974,-13],[376,-2566]],[[965001,379352],[-1022,1410],[476,1475],[546,-2885]],[[660142,383355],[-745,-111],[7,1630],[752,1414],[377,-1319],[-391,-1614]],[[995222,401798],[865,-1656],[211,-2544],[-1411,-1003],[-868,-28],[-1461,1051],[-159,1268],[983,2383],[1840,529]],[[85218,399912],[-715,-265],[-90,1205],[805,-940]],[[967903,400788],[220,-1515],[-1018,519],[798,996]],[[967490,407932],[-1022,639],[653,871],[369,-1510]],[[999997,408927],[-1056,-2128],[-142,-1301],[1007,1350],[-8,-1333],[-1412,-367],[-603,556],[-1377,-1561],[-581,1115],[2929,3187],[1243,482]],[[967907,405311],[-737,-148],[49,1245],[688,-1097]],[[887521,406531],[-968,-999],[370,1625],[598,-626]],[[292158,198837],[809,-2144],[-889,-1651],[-704,2853],[784,942]],[[336527,200971],[2427,-678],[384,-1926],[-2348,-1344],[85,-965],[-1508,483],[-555,-1720],[-487,2267],[1420,1421],[582,2462]],[[332538,199833],[2828,204],[-1815,-3211],[-2125,-1297],[-764,777],[2009,1690],[-917,2454],[784,-617]],[[937461,461289],[-1069,40],[-1789,3604],[61,629],[1731,-2063],[1066,-2210]],[[816234,462622],[-1038,-670],[-1196,39],[-873,824],[395,1024],[3074,159],[-362,-1376]],[[609712,468136],[213,-1261],[-916,666],[-142,2038],[324,1239],[521,-2682]],[[873713,466550],[-503,-2207],[-824,269],[302,3514],[1025,-1576]],[[359296,503937],[-758,-1160],[-191,1316],[949,-156]],[[840680,473499],[-1007,-298],[240,3272],[925,881],[161,-1868],[-319,-1987]],[[856320,482947],[-826,-571],[106,1008],[720,-437]],[[852391,486423],[1017,-1796],[7,-1434],[-1509,-1129],[-1412,1447],[-421,2423],[2318,489]],[[860429,487739],[1734,-732],[1336,-3442],[-152,-1703],[-2182,2269],[-489,875],[-1044,-747],[-1990,904],[-923,-684],[-798,1532],[-587,-2065],[-118,1682],[890,2109],[3414,447],[909,-445]],[[924903,476538],[-792,1528],[-227,2983],[-1129,2898],[-3795,4698],[-2,829],[3762,-4961],[2325,-4120],[322,-1463],[-464,-2392]],[[823064,481800],[-583,-1101],[-209,2098],[275,2135],[424,524],[93,-3656]],[[933215,465101],[-662,-1042],[-1043,836],[-394,2453],[-1167,1996],[-146,3118],[1012,-1043],[1036,-3108],[989,-1396],[375,-1814]],[[874296,470906],[25,-2747],[-874,-973],[-800,1869],[1286,3441],[363,-1590]],[[929575,472530],[-298,1907],[520,-634],[-222,-1273]],[[911181,470199],[-679,1180],[820,-136],[-141,-1044]],[[869239,469751],[523,3503],[14,-1573],[-537,-1930]],[[610736,475651],[-34,-2071],[-573,-669],[74,2616],[533,124]],[[839007,472500],[-651,1077],[437,1070],[214,-2147]],[[921986,479261],[561,501],[801,-761],[-16,-2314],[-395,-1325],[-679,-290],[360,-2092],[-771,-1232],[-973,73],[-794,-2176],[-2224,-2112],[-2155,-83],[-751,1258],[-710,-295],[-2015,2149],[-157,1304],[1817,357],[683,-523],[1994,742],[229,2446],[170,-2335],[535,-632],[1817,662],[1038,2747],[957,456],[-353,3461],[1031,14]],[[842164,477754],[-552,-3760],[615,-520],[-1083,-2356],[-631,750],[504,1982],[501,4547],[646,-643]],[[917878,488947],[-752,10],[-567,1104],[738,531],[581,-1645]],[[277412,487103],[-256,1303],[874,163],[-618,-1466]],[[908520,493104],[924,-385],[-538,-929],[-1833,-158],[305,1390],[1142,82]],[[778958,485860],[-614,1162],[115,1206],[499,-2368]],[[800575,486958],[-639,-1329],[-331,803],[-677,-729],[143,3810],[1135,-182],[600,-1380],[-231,-993]],[[778344,488478],[-525,-466],[-9,1833],[534,-1367]],[[777341,490836],[-656,507],[221,1150],[435,-1657]],[[850152,490195],[-534,2216],[361,387],[173,-2603]],[[876315,495287],[2021,-375],[1920,-857],[-1845,-557],[-2058,1335],[-38,454]],[[862092,494702],[182,-1665],[-475,-452],[-1418,1072],[1711,1045]],[[850066,494114],[854,-196],[-2497,-684],[245,814],[1398,66]],[[847137,494613],[974,-620],[-2507,-1155],[-103,1882],[1636,-107]],[[855980,494879],[-1643,-402],[-471,495],[707,1851],[1407,-1944]],[[794571,494827],[889,-4713],[1257,-643],[-574,-1908],[153,-1045],[-1856,1463],[-591,3813],[-1814,822],[1257,3056],[901,129],[378,-974]],[[846913,510614],[-1282,-3109],[-1872,-978],[-1356,125],[-508,943],[-3446,-291],[-1156,345],[-1147,-315],[-868,432],[-976,-388],[-616,-1674],[-317,-2150],[234,-2688],[1167,-2307],[416,-1959],[1018,-216],[1349,3264],[1251,-460],[862,1044],[1691,11],[785,1093],[579,-461],[-5,-2107],[-913,781],[-672,-556],[-836,-2261],[-2188,-3051],[-1029,-493],[1389,-2284],[1528,-5150],[-404,-2487],[1734,-2894],[56,-1422],[-2176,-1132],[-113,-1490],[-1347,190],[-283,1057],[365,2893],[-1956,3181],[390,2304],[-178,2943],[-935,15],[-1110,-2282],[507,-3877],[-206,-2242],[160,-3149],[-391,-3133],[419,-2636],[-1329,80],[-651,-686],[-948,1591],[655,5931],[33,2307],[-566,3311],[-1181,-368],[-506,2257],[-80,2320],[857,1671],[638,3278],[-36,3089],[552,2971],[566,1339],[369,-1073],[-340,4582],[929,4627],[738,1722],[541,-982],[1098,2793],[1467,-441],[421,-868],[2347,-295],[1265,-996],[1071,462],[1581,-532],[1186,1090],[1988,4022],[679,-1179],[-958,-3002]],[[842256,497778],[70,-1292],[859,308],[-860,-1420],[-241,1615],[-723,-1675],[49,2398],[846,66]],[[246466,504866],[1312,-4604],[-294,-1117],[-1294,-453],[-241,1287],[937,1426],[-690,1611],[270,1850]],[[775454,494184],[-804,677],[-757,2759],[742,1672],[1106,-4219],[-287,-889]],[[804750,497722],[-785,-359],[134,1516],[651,-1157]],[[876063,500858],[1418,-441],[1338,-2182],[-736,-728],[-754,578],[-1266,2773]],[[863893,496923],[-610,356],[-303,1752],[1113,-50],[-200,-2058]],[[356020,496222],[796,4646],[811,642],[259,-743],[-321,-2103],[-1545,-2442]],[[362142,503359],[1422,389],[1469,-403],[579,-718],[-445,-2655],[-1081,-4037],[-677,412],[-170,-1107],[-758,522],[-829,-1652],[-1952,14],[-765,4607],[383,4370],[1103,926],[1721,-668]],[[249070,500146],[-577,563],[758,1137],[-181,-1700]],[[854352,502828],[105,-1727],[765,-1175],[-1161,15],[-381,2786],[672,101]],[[790205,502734],[322,-781],[-630,-1138],[-295,1157],[603,762]],[[863369,504693],[1288,-863],[-54,-1280],[-700,30],[-1065,1628],[185,-1249],[-1255,516],[161,640],[1440,578]],[[943750,449782],[612,-952],[1066,69],[1290,-2614],[-2681,423],[-644,1535],[357,1539]],[[918134,449344],[722,-423],[293,-1478],[-1271,254],[256,1647]],[[917625,448471],[-654,782],[584,579],[70,-1361]],[[919668,445353],[599,376],[-182,-1410],[-755,605],[-508,2272],[846,-1843]],[[833367,449176],[774,-1617],[591,-155],[913,-2154],[-1093,-1519],[-817,556],[-1511,2527],[-1433,394],[-351,1111],[630,800],[2297,57]],[[923974,451638],[933,-1480],[-685,253],[-248,1227]],[[946525,455461],[690,-1766],[-148,-1107],[595,-965],[453,-3719],[-1245,2564],[-770,4909],[425,84]],[[847805,449005],[-589,-957]],[[847216,448048],[-1585,-3456],[-1583,-1156],[-593,193],[-152,2040],[333,2085],[744,1433]],[[844380,449187],[613,692]],[[844993,449879],[1049,597]],[[846042,450476],[873,1108]],[[846915,451584],[381,648]],[[847296,452232],[420,1251],[1740,923],[2265,193],[963,852],[915,-645],[-1058,-1722],[-1479,-1436],[-2707,-1885],[-550,-758]],[[828450,455439],[2071,-118],[394,-1958],[-1809,-1116],[-277,1078],[-524,-983],[-3135,-1532],[-759,549],[130,2808],[916,979],[1117,-351],[664,-1687],[1191,707],[-1167,1480],[-165,1141],[1006,161],[347,-1158]],[[824001,453685],[-732,-1867],[-1396,611],[561,479],[-44,1822],[947,1382],[924,-1083],[-260,-1344]],[[937191,453094],[-491,880],[464,854],[27,-1734]],[[841063,453697],[-1913,-788],[-1230,-912],[-658,496],[-1054,-714],[-1346,792],[-1782,-330],[25,2443],[1923,1213],[2317,-1999],[1450,726],[823,-1005],[1988,2802],[76,-1050],[-619,-1674]],[[820688,456402],[712,-1494],[-1029,-1234],[-271,-1078],[-1095,2187],[-636,296],[-383,1535],[2702,-212]],[[844234,455707],[-1032,-1744],[-897,215],[1516,2015],[413,-486]],[[845239,455369],[-613,-1316],[-262,1170],[875,146]],[[846042,456487],[1526,-379],[-78,-877],[-1812,-544],[364,1800]],[[938231,455887],[155,-2192],[-726,2027],[-904,-266],[558,1956],[917,-1525]],[[944108,454156],[-2598,2924],[-1351,2939],[769,-353],[1042,-1774],[2038,-2508],[100,-1228]],[[885819,455018],[-923,472],[648,802],[275,-1274]],[[935243,457777],[-352,2068],[689,-882],[-337,-1186]],[[852224,459289],[-914,-1674],[-1010,394],[-861,-596],[492,1903],[661,-257],[1100,799],[532,-569]],[[884820,455700],[-665,-779],[-1690,-39],[-7,886],[895,3677],[800,1203],[1317,285],[610,-1811],[-564,-2151],[-696,-1271]],[[798260,469125],[814,-1233],[1745,-292],[1063,-3113],[4857,-929],[863,2814],[653,217],[506,-1382],[1072,123],[1520,-1452],[1255,-197],[709,-2239],[0,-1469],[1261,-982],[2284,505],[1038,-1556],[-159,-3019],[546,-2159],[-3695,2861],[-1596,-726],[-3247,617],[-2508,922],[-1579,1534],[-2103,1100],[-1501,224],[-804,-770],[-1484,432],[-1757,1495],[-2305,611],[178,1865],[-2877,1613],[843,1923],[226,2018],[574,1198],[2084,-1092],[614,1151],[910,-613]],[[864792,457323],[-664,798],[481,2337],[1065,2120],[-51,-3042],[-831,-2213]],[[890964,489271],[993,-25]],[[891957,489246],[3074,-2798],[1926,-1404],[1677,-655],[1409,-2089],[1283,-246],[1695,-3103],[685,-214],[1201,-2594],[-60,-3432],[1828,-1269],[1753,-1793],[951,-187],[1181,-2160],[121,-2056],[-2018,-351],[-440,-1227],[637,-2662],[1484,-2952],[1118,-1346],[334,-2670],[567,-832],[367,-2116],[1846,-114],[-124,-1990],[758,-1074],[1382,-431],[-589,-858],[313,-1227],[2203,-1447],[-614,-297],[558,-1248],[-909,-811],[-842,459],[-729,1329],[-3808,993],[-1707,683],[-1002,2343],[-1087,1699],[-147,1945],[-743,202],[-1842,5623],[-846,734],[-2097,891],[-304,1009],[-985,381],[-790,-1170],[-909,539],[123,-1601],[-1178,-336],[265,-1183],[-1441,-656],[-1074,231],[1120,-1198],[780,-1939],[-72,-943],[-1998,-2173],[-1160,934],[-3045,-303]],[[892036,450086],[-580,807]],[[891456,450893],[-2749,5829],[-1526,-521],[-1471,261],[645,3305],[-945,1989],[835,302],[-1245,1717],[734,310],[-1183,3050],[-396,2337],[-639,1618],[-15,1249],[-2698,3204],[-1307,626],[-1776,1705],[-2178,475],[-1226,1513],[-132,1425],[-1223,53],[-812,758],[-891,2687],[-1124,-4135],[-778,-193],[-596,2317],[322,905],[-329,1518],[-2167,2999],[721,641],[1373,-645],[1293,2081],[1161,-646],[822,925],[48,1712],[-2665,-1011],[-1819,180],[-789,1492],[-259,2552],[-1768,985],[-827,-187],[726,3374],[1519,898],[901,1479],[1379,565],[2355,-2176],[1394,-108],[757,-3354],[-393,-2432],[139,-2809],[845,-3775],[465,1751],[208,-2351],[392,145],[538,-2513],[1249,-70],[2102,4515],[407,1835],[1259,448],[910,1020],[-131,1094],[1895,2119],[2344,-1824],[3166,-3302],[2314,-577],[347,-956]],[[897718,433893],[-190,-2068],[809,-1993],[514,-4761],[-106,-1763],[577,-3600],[571,-676],[1420,1368],[486,-1543],[1777,-2670],[-45,-3161],[518,-3435],[-89,-2071],[1322,-3935],[622,-3347],[-260,-3778],[836,-1664],[-101,-1703],[512,-1408],[2604,-1773],[1381,-2909],[1902,-1635],[790,-1990],[-559,-587],[1448,-3229],[692,-2687],[394,-4022],[1052,-1736],[282,2288],[1291,-2338],[620,-101],[220,-5225],[1827,-3284],[1116,-1117],[630,-2350],[907,-1214],[551,-2367],[720,-1363],[18,-1520],[680,-1632],[-224,-2013],[91,-5276],[1274,-6198],[80,-3637],[-712,-2583],[-211,-3567],[-672,-3974],[-240,-5163],[-1068,-3619],[-247,-2331],[-1826,-2737],[-1448,-4028],[-32,-2048],[-889,-2194],[-750,-5218],[-341,-216],[-1034,-3669],[-653,-5995],[-76,-4047],[-1762,-1621],[-2878,-169],[-1071,-613],[-1337,-1688],[-1496,-2633],[-1568,-215],[484,-833],[-185,-1808],[-671,1658],[-2115,1957],[-290,1764],[-925,-1559],[445,2426],[-636,1134],[-1376,-1404],[748,-433],[-1565,-1495],[-1563,-2124],[-2575,2187],[-2463,1068],[-836,-546],[-1148,1698],[-1066,288],[-2342,4636],[204,3458],[-859,3350],[-1609,3057],[622,1383],[-1864,-1749],[-937,176],[907,3486],[-59,1545],[-1113,3518],[-1104,-5766],[-2245,-573],[363,1919],[1047,15],[285,4456],[1217,3448],[-221,2242],[390,631],[-560,1605],[-969,-2194],[-569,-2582],[-2241,-2373],[-1500,-3738],[300,-1676],[-975,25],[-1158,2132],[609,-8],[-735,3995],[-824,1661],[-271,1766],[-1361,967],[-558,2467],[372,1186],[-1897,2166],[-942,-5],[-1263,1348],[-1508,-302],[-1371,1842],[-1604,1188],[-1002,-641],[-1814,147],[-3288,-732],[-2440,-2155],[-2078,-1171],[-3896,-195],[-3219,-3470],[-1756,-1461],[-1322,-4189],[-1229,-900],[-1195,578],[-1741,-599],[-249,696],[-1823,282],[-2740,-808],[-1568,-68],[-1121,-2332],[-1542,-661],[-2111,-3003],[-1537,-658],[-2959,651],[-1472,1143],[-724,1593],[-1994,1601],[-40,4387],[521,-759],[927,664],[466,2005],[43,8877],[-1449,5252],[-507,3507],[-98,4636],[-919,3329],[-252,1948],[-1035,2739],[-380,4344],[-885,2960],[-1046,2550],[156,1847],[535,-2681],[753,1339],[-733,1383],[-538,2283],[883,-696],[1048,-3335],[348,617],[-4,2595],[-1510,5181],[-703,3207],[376,4164],[567,1864],[105,2338],[-311,2286],[765,4139],[459,654],[50,-3877],[655,839],[626,2366],[712,1222],[1659,1447],[1541,2733],[1933,2231],[1943,-400],[2202,2049],[1534,671],[981,1581],[1337,-255],[1695,763],[1896,1448],[837,1109],[871,2201],[945,3729],[1465,2606],[-344,406],[-214,3880],[755,2034],[801,1082],[695,2079],[476,-2525],[1169,-3898],[153,3037],[445,832],[-800,2235],[664,1766],[361,-1124],[752,613],[902,-334],[340,1312],[-542,2106],[161,1568],[863,1234],[868,-930],[-622,1668],[650,761],[785,-519],[-491,2400],[1114,1372],[863,-798],[815,3649],[1071,-942],[927,2470],[2137,-2672],[1464,-3298],[-361,-3423],[514,184],[-221,1513],[840,1511],[1613,-571],[438,-1634],[143,1711],[1021,-1590],[6,1711],[588,130],[-417,1503],[-889,1084],[920,2443],[359,2412],[1169,1604],[-255,2042],[1262,3119],[681,-752],[18,1130],[1160,1774],[407,-1239],[2264,538],[439,-646],[608,1540],[123,2288],[-553,933],[-949,-55],[-894,1359],[1253,399],[1166,-1786],[951,312],[445,-1498],[1997,-748],[924,-1041],[1371,137],[1355,-1405],[1957,2345],[-611,-1929],[653,-4],[897,-1668],[26,1789],[751,1031],[1131,-2324],[-1140,-2574],[-212,-2612],[-463,518],[-1019,-986],[173,-2997],[-295,-2031],[-1328,-3585],[348,-1436],[1874,-2387],[239,-987],[1148,-683],[2775,-3245],[1504,-2875],[2125,-1072],[662,-2544],[2188,-2215],[1320,462],[886,1245],[377,2369],[703,2183],[536,3416],[110,2750],[483,3251],[-286,3475],[200,1879],[-339,2105],[481,3189],[-89,1871],[852,832],[-644,2678],[730,2694],[603,5627],[800,1417],[1057,-3552],[99,-3048],[851,-789]],[[858420,409078],[-1074,-97],[-319,-2064],[567,-1663],[189,2004],[637,1820]],[[637606,431064],[1109,-3794],[656,-5734],[171,-4098],[687,-3872],[-760,-3406],[-879,2979],[-674,-648],[174,-3021],[329,-1060],[-177,-3313],[-633,-1291],[-284,-1859],[113,-3269],[-2419,-15161],[-712,-5281],[-1229,-6617],[-973,-8346],[-1057,-5407],[-1247,-2148],[-1583,-477],[-1807,-1972],[-1092,119],[-839,1238],[-1298,640],[-862,1365],[-966,3779],[-115,3649],[211,1258],[-901,3811],[-365,4959],[654,4105],[829,1050],[308,1857],[912,2880],[459,2711],[122,2923],[-583,2094],[-16,1982],[-536,2678],[-169,5314],[1228,4081],[152,2877],[1203,253],[716,1135],[1198,-57],[283,1058],[1754,594],[1529,2868],[418,-1144],[167,1615],[963,2647],[237,-1712],[797,2650],[-106,1037],[617,2426],[123,2158],[599,-730],[1503,2678],[316,1964],[-344,2755],[1169,2318],[920,-2088]],[[965034,409358],[1178,-2096],[-1076,-624],[-694,3969],[592,-1249]],[[963182,416876],[179,-1958],[737,1314],[344,-3259],[-1226,-862],[-641,4627],[607,138]],[[623545,433141],[79,-1630],[-791,1097],[712,533]],[[879762,422936],[499,-2898],[-1552,482],[248,2055],[805,361]],[[620738,434208],[-665,886],[462,2033],[203,-2919]],[[946044,434821],[-1591,1293],[-9,638],[1600,-1931]],[[862386,435524],[408,-814],[-1319,-47],[60,2056],[517,832],[334,-2027]],[[862828,437320],[1124,247],[681,856],[751,-1463],[-222,-895],[-1411,-2006],[-1219,1829],[-398,2387],[694,-955]],[[926487,436727],[-435,-494],[-488,1395],[923,-901]],[[841524,440086],[-287,873],[1460,1700],[130,-1045],[-1303,-1528]],[[949208,443178],[1084,-394],[743,-2189],[-694,-7],[-1626,1529],[-647,2145],[1140,-1084]],[[362655,504051],[-1073,110],[1194,895],[-121,-1005]],[[791051,503675],[-922,-79],[267,1226],[655,-1147]],[[361838,506306],[-280,-1548],[-1390,216],[185,1116],[1485,216]],[[518498,505431],[-531,633],[607,1049],[-76,-1682]],[[359927,505541],[-549,-502],[758,3125],[-209,-2623]],[[770781,513396],[1251,-2909],[-154,-2047],[-534,-192],[-1680,4913],[1117,235]],[[786187,509140],[-1594,847],[265,1426],[1329,-2273]],[[786573,509872],[-1291,1088],[245,662],[1046,-1750]],[[790514,511922],[-28,-2276],[-593,2071],[621,205]],[[784519,510583],[-552,2119],[596,-673],[-44,-1446]],[[784699,513362],[21,-760],[-1169,993],[-101,752],[1249,-985]],[[768033,535698],[974,267],[1957,-406],[1002,-1931],[945,-2757],[164,-1906],[3958,-5390],[2014,-5485],[1195,-1831],[-164,1744],[605,87],[1196,-3342],[856,-425],[1034,-2148],[867,-2841],[1056,-378],[602,-1324],[-1434,-1633],[2019,1648],[562,-85],[855,-2567],[-995,-1414],[7,-2025],[805,-2092],[1777,-899],[430,-4627],[916,-1621],[-491,-1733],[187,-1098],[806,1264],[1545,-797],[1284,-3639],[-397,-1800],[81,-2506],[-277,-1954],[156,-5016],[-197,-3951],[-549,-729],[-748,1481],[-744,-1161],[-1228,1334],[-105,-2276],[-2140,4887],[-2534,3608],[-1060,1887],[-1138,3276],[-1525,2560],[-1278,3432],[-751,2629],[20,1242],[-1025,3763],[-495,2800],[-1245,3038],[-729,2466],[-1219,1477],[-1006,6771],[-645,2414],[-2399,2703],[-305,2893],[-554,762],[-1174,3554],[-1456,1429],[-2639,5599],[-801,3096],[527,2043],[1237,-678],[811,-1304],[997,-385]],[[854812,509742],[412,-95],[768,2870],[1475,1516],[39,-2761],[-1121,-1360],[-106,-849],[974,-1088],[801,-1977],[-2546,1514],[-267,-1027],[251,-3239],[767,-2863],[-576,151],[-985,2750],[48,3140],[-426,1194],[145,2124],[-521,2392],[588,3506],[1124,2105],[-416,-2169],[347,-2969],[-997,-1883],[222,-982]],[[826676,529600],[-104,-224]],[[826572,529376],[-280,-510],[866,-2292],[-1682,-298],[502,-2638],[716,-766],[1268,-4423],[-771,-1724],[809,-1926],[1551,-2268],[961,-1995],[-1250,-999],[-940,360],[-792,1329],[33,-1584],[-494,-602],[-619,-2925],[-165,-3316],[277,-2649],[-895,-918],[-2680,-5089],[413,0],[292,-4300],[-491,-65],[-265,-3584],[-836,-2775],[-3508,-3405],[-466,4698],[-221,-623],[-1011,1202],[-795,-1050],[-516,1543],[-742,-301],[-859,1855],[-174,-1503],[-1030,-1264],[-876,471],[-1286,-1253],[2,2816],[-1264,732],[-1216,-814],[-989,1064],[-754,-556],[-633,6154],[-319,496],[165,2749],[-644,2296],[-1434,1654],[-415,2767],[377,1755],[-869,1922],[-108,2597],[472,4157],[841,2529],[696,622]],[[804524,516729],[987,-1836],[1014,12],[2081,-1888],[471,4377],[-72,1754],[559,-322],[0,1497],[790,1301],[2804,1284],[854,797],[1115,3173],[1328,2978],[360,2071]],[[816815,531927],[345,-11]],[[817160,531916],[1018,792],[1253,1764],[87,-727]],[[819518,533745],[315,0]],[[819833,533745],[652,196],[575,1549],[-452,1297],[513,1127],[536,-398],[949,3515],[990,2323],[708,2699],[454,-1881],[598,1832],[459,-1730],[978,-1204],[-79,-3157],[1074,667],[310,-1131],[1331,-1602],[1746,-1063],[-253,-1849],[-1278,-809],[-1143,147],[-209,-950],[1046,-1933],[-178,-828],[-1360,-665],[-742,518],[-382,-815]],[[190458,968527],[-4765,717],[7618,2063],[2826,-2488],[-5679,-292]],[[232764,969972],[3660,-1102],[-556,-2089],[-5284,-1107]],[[230584,965674],[-3516,3693]],[[227068,969367],[120,2224]],[[227188,971591],[5576,-1619]],[[785790,974253],[-1312,-2479],[3501,1864],[4092,-1962],[463,-1890],[-4426,-1432],[-6987,-393],[-3116,-1285],[-2208,374],[4412,4417],[890,2493],[3018,1337],[1673,-1044]],[[771316,979611],[-62,-2356],[2624,1727],[4069,-1630],[-1727,-5585],[-5233,-46],[-7679,1540],[-1590,1871],[-3189,551],[5324,3564],[7463,364]],[[215066,972034],[2424,1181],[5817,-2936],[-394,-1660]],[[222913,968619],[1625,-2642]],[[224538,965977],[-3944,205]],[[220594,966182],[-1356,1790],[-4603,1051]],[[214635,969023],[-4425,-602],[-1865,1475]],[[208345,969896],[3420,6]],[[211765,969902],[-1076,2786]],[[210689,972688],[-1849,-1082]],[[208840,971606],[-1995,1335],[411,1725]],[[207256,974666],[6871,-549],[939,-2083]],[[244762,985385],[3451,-3194]],[[248213,982191],[8245,-1313]],[[256458,980878],[-687,-1627]],[[255771,979251],[2624,-1204],[-882,-1861]],[[257513,976186],[4576,185]],[[262089,976371],[995,-2388]],[[263084,973983],[-6467,-3153]],[[256617,970830],[-3122,-546]],[[253495,970284],[-137,-2320]],[[253358,967964],[-3461,2456]],[[249897,970420],[1472,-2391]],[[251369,968029],[-6645,199]],[[244724,968228],[-6288,4486]],[[238436,972714],[7831,2173]],[[246267,974887],[-4106,527]],[[242161,975414],[-6338,-948]],[[235823,974466],[-4639,5012]],[[231184,979478],[5909,-516],[-4783,1448]],[[232310,980410],[2276,435]],[[234586,980845],[-1622,1924],[6125,-783]],[[239089,981986],[-4409,1652]],[[234680,983638],[680,964],[7938,1644]],[[243298,986246],[1464,-861]],[[451076,977642],[-3846,679],[4238,522],[-392,-1201]],[[225579,978561],[-137,-1445],[-3477,1075]],[[221965,978191],[1004,1336],[2610,-966]],[[757453,976808],[-4327,1302],[834,855],[6603,-857],[-3110,-1300]],[[658551,980752],[4,-1518],[-3767,60],[3763,1458]],[[375376,991018],[-5243,1567],[980,2038],[4581,-1724],[-318,-1881]],[[558049,980153],[3866,-1188],[5578,1843],[7120,-1187],[938,-1501],[-4326,-2983],[-4704,-1237],[-3006,1289],[-5568,-83],[-3296,1145],[3082,933],[-5720,72],[-1304,998],[3022,1109],[753,2051],[3565,-1261]],[[639661,984167],[3960,-1420],[-7799,-1886],[226,-1224],[-3736,-300],[-2745,1116],[10094,3714]],[[768129,985046],[3624,-1644],[-1824,-3301],[-9173,-1369],[-6524,2065],[4830,2564],[-548,1168],[7598,1730],[2017,-1213]],[[660989,979403],[-2448,2197],[3904,-173],[-1456,-2024]],[[631783,983731],[-3613,-2411],[-3435,975],[7048,1436]],[[672688,983619],[-3102,-2466],[-4852,610],[799,1748],[7155,108]],[[651996,985285],[8265,-1918],[-5505,-918],[-4572,1045],[1812,1791]],[[676038,982821],[-2371,721],[6338,2224],[1765,-1579],[-5732,-1366]],[[662839,984844],[660,-1553],[-4580,1408],[3920,145]],[[660583,987833],[-998,-2432],[-4817,313],[5815,2119]],[[57298,634654],[-1158,649],[1215,1053]],[[57355,636356],[-57,-1702]],[[270661,632517],[-809,-757],[-637,2059],[1022,586],[424,-1888]],[[272673,641945],[1831,-612],[1468,257],[2704,-2133],[1113,-1987],[1637,-242],[2916,-3373],[388,440],[2360,-3479],[2921,-1717],[-130,-1547],[2112,-491],[1026,-1577],[960,-547],[-237,-1259],[-2883,-1105],[-2411,572],[-4324,-795],[449,1343],[1249,1927],[-349,1400],[-2132,424],[-1371,2005],[-405,2736],[-518,612],[-1017,-391],[-2003,1124],[-1636,1901],[-1285,-63],[-2374,873],[-726,1111],[891,468],[-407,1258],[-2318,61],[-1782,-2763],[-1448,-313],[-362,-1345],[-1310,-989],[490,1766],[-97,1805],[879,1701],[2186,1786],[3212,1321],[733,-163]],[[241809,926906],[-91,-2428]],[[241718,924478],[2771,-3358]],[[244489,921120],[20,-1462]],[[244509,919658],[-2531,-2195]],[[241978,917463],[2711,-811],[3769,-159]],[[248458,916493],[-1896,-1297]],[[246562,915196],[2137,-2499]],[[248699,912697],[-293,-2305]],[[248406,910392],[1025,-1287],[1495,4485]],[[250926,913590],[1694,1491],[2820,-2692],[523,-3228],[-1372,126],[420,-3095]],[[255011,906192],[2581,-3447]],[[257592,902745],[2028,1968]],[[259620,904713],[1623,3296]],[[261243,908009],[1207,4132]],[[262450,912141],[1821,1802],[-1457,935]],[[262814,914878],[-79,3659],[3260,-87]],[[265995,918450],[1601,-800]],[[267596,917650],[3587,-343],[-1058,-874]],[[270125,916433],[3962,-2218]],[[274087,914215],[-1748,-1400]],[[272339,912815],[1879,-1341]],[[274218,911474],[-3531,-1249]],[[270687,910225],[1600,-3463]],[[272287,906762],[1962,-2382]],[[274249,904380],[-548,-2311],[-3261,-2857]],[[270440,899212],[-2449,-1296]],[[267991,897916],[-1103,1838],[-2571,2071]],[[264317,901825],[2833,-4376]],[[267150,897449],[-1813,-656]],[[265337,896793],[-2677,2121]],[[262660,898914],[-3309,-35]],[[259351,898879],[1640,-3014],[-3468,-3956],[-1884,-35],[-4943,3478]],[[250696,895352],[-3500,176],[3393,-1357],[2261,-2301]],[[252850,891870],[5407,-890]],[[258257,890980],[-703,-2203]],[[257554,888777],[-2293,-3809]],[[255261,884968],[-1977,-1132]],[[253284,883836],[-3751,-80]],[[249533,883756],[-215,-1996],[-1945,-320],[-2489,650],[3041,-2050]],[[247925,880040],[-345,-2403],[-1605,-840]],[[245975,876797],[-2534,90],[981,-1652]],[[244422,875235],[-1510,36]],[[242912,875271],[66,-2240]],[[242978,873031],[-2928,-1341]],[[240050,871690],[750,-1036]],[[240800,870654],[-2080,-2662]],[[238720,867992],[-21,-1061],[-1607,-4281]],[[237092,862650],[-386,-2742],[-7,-4061]],[[236699,855847],[319,-2357]],[[237018,853490],[1073,-913]],[[238091,852577],[-125,-2480]],[[237966,850097],[767,2741]],[[238733,852838],[2161,-22]],[[240894,852816],[2329,-8777]],[[243223,844039],[-794,-2022]],[[242429,842017],[4484,1823]],[[246913,843840],[1442,-99]],[[248355,843741],[2226,-1441]],[[250581,842300],[2340,-771]],[[252921,841529],[2426,-2274]],[[255347,839255],[1427,-2435]],[[256774,836820],[5235,-2697]],[[262009,834123],[1710,-1869]],[[263719,832254],[3196,172]],[[266915,832426],[3703,-983]],[[270618,831443],[995,-1986],[-552,-2712],[768,-3188]],[[271829,823557],[-331,-5075]],[[271498,818482],[1914,-3518]],[[273412,814964],[61,-774]],[[273473,814190],[2477,-2833]],[[275950,811357],[499,-2672],[1041,-145]],[[277490,808540],[1081,-979]],[[278571,807561],[1044,3024],[828,-973]],[[280443,809612],[381,-1561]],[[280824,808051],[478,1760],[-685,1399]],[[280617,811210],[1350,3072]],[[281967,814282],[-644,2225]],[[281323,816507],[-1082,6455]],[[280241,822962],[469,729]],[[280710,823691],[-2003,5079],[2100,1089],[2829,2104],[1572,1889]],[[285208,833852],[1874,3270]],[[287082,837122],[363,3553]],[[287445,840675],[-148,2809]],[[287297,843484],[-1622,4963]],[[285675,848447],[-3773,3931]],[[281902,852378],[157,1131]],[[282059,853509],[1939,3002]],[[283998,856511],[96,1753]],[[284094,858264],[1096,715]],[[285190,858979],[55,1456]],[[285245,860435],[-1331,3540]],[[283914,863975],[522,1098]],[[284436,865073],[-1607,-36]],[[282829,865037],[1853,4366]],[[284682,869403],[-1203,1219]],[[283479,870622],[-335,3516]],[[283144,874138],[1932,1287]],[[285076,875425],[4321,-1521]],[[289397,873904],[3253,-620]],[[292650,873284],[2822,1440]],[[295472,874724],[3900,-3689]],[[299372,871035],[2231,-3985],[3176,-535],[511,-1116],[1732,774],[-927,-6315]],[[306095,859858],[629,-1599],[-285,-1975]],[[306439,856284],[783,-23]],[[307222,856261],[-366,-2776]],[[306856,853485],[2936,-271]],[[309792,853214],[669,-2514]],[[310461,850700],[1845,-1100]],[[312306,849600],[2672,1987]],[[314978,851587],[2782,3329]],[[317760,854916],[556,4055]],[[318316,858971],[1319,2706]],[[319635,861677],[1421,-477],[-969,-944]],[[320087,860256],[1347,308]],[[321434,860564],[2066,-4332]],[[323500,856232],[94,-1290]],[[323594,854942],[1756,-2623]],[[325350,852319],[-1433,-1304],[2172,261]],[[326089,851276],[-1036,-2388]],[[325053,848888],[1374,360]],[[326427,849248],[1631,-1734]],[[328058,847514],[-192,-1478],[-1352,-888]],[[326514,845148],[1677,-478]],[[328191,844670],[-351,-790]],[[327840,843880],[1788,-1406],[-105,-1954]],[[329523,840520],[-1919,107]],[[327604,840627],[1770,-2004]],[[329374,838623],[-67,-2163],[1715,-223]],[[331022,836237],[1364,-1026]],[[332386,835211],[413,-1800]],[[332799,833411],[-1180,-2492]],[[331619,830919],[2384,1477]],[[334003,832396],[2116,-949]],[[336119,831447],[602,-1843]],[[336721,829604],[2272,222]],[[338993,829826],[1550,-1809]],[[340543,828017],[-2075,-1304]],[[338468,826713],[-1338,-1782]],[[337130,824931],[-3305,-1274]],[[333825,823657],[-1407,-3367]],[[332418,820290],[2798,2237]],[[335216,822527],[1861,1979],[2011,745]],[[339088,825251],[-733,738]],[[338355,825989],[2156,-387]],[[340511,825602],[780,-2198]],[[341291,823404],[-1090,-1138],[544,-774]],[[340745,821492],[1363,1602]],[[342108,823094],[2030,-901]],[[344138,822193],[867,-2225]],[[345005,819968],[-117,-4172]],[[344888,815796],[403,-2191]],[[345291,813605],[-3558,-4029],[-2204,-189],[-2058,-775]],[[337471,808612],[-1820,-3052]],[[335651,805560],[-2541,-3112]],[[333110,802448],[-3360,-312]],[[329750,802136],[-1208,-580]],[[328542,801556],[-541,763]],[[328001,802319],[-2211,408]],[[325790,802727],[-5979,-155]],[[319811,802572],[-1113,263]],[[318698,802835],[-3408,-641]],[[315290,802194],[-1238,-1292],[-1197,-3822]],[[312855,797080],[-1900,-543],[-2425,-2535]],[[308530,794002],[-2069,-3731]],[[306461,790271],[-2296,918],[2015,-1517]],[[306180,789672],[-362,-1575]],[[305818,788097],[-2223,-4102],[-1561,-2038]],[[302034,781957],[-1700,-646]],[[300334,781311],[-3059,-2827]],[[297275,778484],[-1377,-2793]],[[295898,775691],[-1559,-1400]],[[294339,774291],[178,-929]],[[294517,773362],[-2042,-2022]],[[292475,771340],[3197,2496]],[[295672,773836],[1107,3465]],[[296779,777301],[3496,3685],[1777,736]],[[302052,781722],[2060,1637]],[[304112,783359],[4257,7361]],[[308369,790720],[3962,3441]],[[312331,794161],[3840,2117],[1819,314]],[[317990,796592],[1909,-441]],[[319899,796151],[1596,-1599]],[[321495,794552],[-242,-2954]],[[321253,791598],[-2529,-2381],[-1854,993]],[[316870,790210],[-2160,-986]],[[314710,789224],[2375,-660]],[[317085,788564],[508,-1273]],[[317593,787291],[1847,891],[829,-721]],[[320269,787461],[-581,-2111]],[[319688,785350],[-1130,-1585]],[[318558,783765],[1354,-239],[-206,-1024]],[[319706,782502],[1012,-3836],[1737,-442]],[[322455,778224],[-390,-856],[2121,-1596]],[[324186,775772],[1645,-67],[605,-704]],[[326436,775001],[1465,1460]],[[327901,776461],[1287,-1074]],[[329188,775387],[1280,-2341]],[[330468,773046],[-4118,-2655]],[[326350,770391],[-2201,-1192]],[[324149,769199],[-827,242]],[[323322,769441],[-437,-1166]],[[322885,768275],[-662,939],[-2397,-4604]],[[319826,764610],[-2432,-1819]],[[317394,762791],[-1077,1499]],[[316317,764290],[73,3279]],[[316390,767569],[1610,2165]],[[318000,769734],[-380,163]],[[317620,769897],[3683,3252]],[[321303,773149],[-65,-1013]],[[321238,772136],[2739,1343],[-4291,59]],[[319686,773538],[1662,2730]],[[321348,776268],[-4361,-3630]],[[316987,772638],[-2744,-922]],[[314243,771716],[-701,605]],[[313542,772321],[-185,-2926]],[[313357,769395],[-1799,-588]],[[311558,768807],[-605,-1137]],[[310953,767670],[-1094,730]],[[309859,768400],[-227,-1475],[-1192,1038]],[[308440,767963],[-297,-1992]],[[308143,765971],[-2055,-1928]],[[306088,764043],[-436,491],[-2133,-4651]],[[303519,759883],[-7,-2374]],[[303512,757509],[-863,-2003]],[[302649,755506],[855,-606],[622,-2521]],[[304126,752379],[1425,135]],[[305551,752514],[147,-883]],[[305698,751631],[-1968,-846],[-122,1070]],[[303608,751855],[-1299,-1336]],[[302309,750519],[-615,1812],[-149,-2023]],[[301545,750308],[-2282,-960],[-1832,-39]],[[297431,749309],[-1829,-1560],[-1788,-2452],[-41,-899]],[[293773,744398],[789,-757],[-607,-3565],[-1718,-4294]],[[292237,735782],[-2027,2893]],[[290210,738675],[285,1774],[965,1148]],[[291460,741597],[-1426,-2030]],[[290034,739567],[543,-3247]],[[290577,736320],[990,-3492],[-1557,-5167]],[[290010,727661],[-1114,-2176]],[[288896,725485],[939,4088]],[[289835,729573],[-532,105]],[[289303,729678],[-106,2275],[-896,34]],[[288301,731987],[-146,1414]],[[288155,733401],[731,10]],[[288886,733411],[-652,3495]],[[288234,736906],[1008,1891],[-1523,-1693],[-223,-4105]],[[287496,732999],[130,-2125]],[[287626,730874],[-2186,1904]],[[285440,732778],[-200,-582]],[[285240,732196],[2122,-1790]],[[287362,730406],[793,-1190]],[[288155,729216],[2,-3179]],[[288157,726037],[-965,-204]],[[287192,725833],[910,-1599]],[[288102,724234],[-323,-965]],[[287779,723269],[1111,135],[324,-4366]],[[289214,719038],[-2343,-1292]],[[286871,717746],[2650,-342]],[[289521,717404],[-4,-1498]],[[289517,715906],[-1111,-1735]],[[288406,714171],[-996,914]],[[287410,715085],[-1228,-296],[1282,-1114]],[[287464,713675],[-11,-2922]],[[287453,710753],[-1714,-411]],[[285739,710342],[-1714,-2505]],[[284025,707837],[-491,-2046]],[[283534,705791],[-1328,-131],[-1210,-1146]],[[280996,704514],[-1078,-3193]],[[279918,701321],[-2201,-3349]],[[277717,697972],[-947,-706]],[[276770,697266],[-1416,-2791]],[[275354,694475],[-669,-895]],[[274685,693580],[-487,-3641],[-425,-381]],[[273773,689558],[-173,-2774]],[[273600,686784],[707,-5555]],[[274307,681229],[971,-4407]],[[275278,676822],[1044,-3340]],[[276322,673482],[-873,1609],[249,-2232]],[[275698,672859],[1451,-6955]],[[277149,665904],[514,-3782],[-327,-4090]],[[277336,658032],[-578,-3241]],[[276758,654791],[-1026,-1036]],[[275732,653755],[-1039,-109]],[[274693,653646],[-8,1358]],[[274685,655004],[-699,2748],[-974,901]],[[273012,658653],[-419,2677]],[[272593,661330],[-481,693]],[[272112,662023],[-76,2012]],[[272036,664035],[-620,-123]],[[271416,663912],[-960,3873]],[[270456,667785],[653,1842],[-497,729],[-226,-1423]],[[270386,668933],[-507,756]],[[269879,669689],[508,3791]],[[270387,673480],[25,2380]],[[270412,675860],[-1775,3343],[-1122,2809]],[[267515,682012],[-971,1054]],[[266544,683066],[-941,-1164]],[[265603,681902],[-2600,-1346]],[[263003,680556],[-97,1158]],[[262906,681714],[-1117,1726]],[[261789,683440],[-1941,1375]],[[259848,684815],[-3710,-636]],[[256138,684179],[-614,2384]],[[255524,686563],[-345,-1940]],[[255179,684623],[-2138,287]],[[253041,684910],[-1154,-414],[-744,-1062],[-1769,1264],[-522,-1416]],[[248852,683282],[661,-659]],[[249513,682623],[1216,846]],[[250729,683469],[1064,-2083],[-1018,-1191]],[[250775,680195],[576,-1180]],[[251351,679015],[1383,-1287]],[[252734,677728],[-939,-786],[-1454,2298],[-487,-158]],[[249854,679082],[-446,-1934]],[[249408,677148],[-463,1127]],[[248945,678275],[-1031,-974],[-1498,937],[127,998],[-1802,2243]],[[244741,681479],[-1021,-1654]],[[243720,679825],[-1140,239],[-1402,1077],[-1967,184]],[[239211,681325],[250,991]],[[239461,682316],[-849,-1818]],[[238612,680498],[-1885,-726]],[[236727,679772],[101,1198],[-781,-283]],[[236047,680687],[374,-1965]],[[236421,678722],[-1070,-2410]],[[235351,676312],[-1612,-1917]],[[233739,674395],[-2185,406],[609,-1490]],[[232163,673311],[-1703,-2153]],[[230460,671158],[-707,-2508]],[[229753,668650],[-739,-4166],[424,-3382]],[[229438,661102],[711,-2577]],[[230149,658525],[-218,-2228],[-784,-3382],[-447,-3701],[-527,-10459],[611,-6048],[1434,-5857],[1848,-4415],[463,-3117],[1301,-3490],[1776,-319],[1065,-1103],[700,-2013],[992,121],[1769,1394],[1854,226],[486,847],[2045,617],[474,-1453],[748,-83],[718,995],[-448,1572],[1937,2740],[128,2237],[516,1078],[65,3818],[364,2684],[1481,1571],[2614,827],[2076,1195],[2446,-1000],[340,1034],[845,-1184],[-112,-3179],[-990,-2239],[-684,-2400],[98,-1206],[-710,-1549],[730,-318],[-650,-1369],[438,-382],[-978,-6036],[-564,1513],[68,1863],[-733,-2171]],[[254734,614156],[551,-2078],[-485,-3032],[-115,-5789],[-1061,-2281],[-552,-2116]],[[253072,598860],[807,-749],[26,1103],[1016,-1311]],[[254921,597903],[1695,1071],[1975,-874],[1529,124],[1591,1301],[834,-612],[1417,535],[1150,-1113],[828,122],[1392,-3568],[317,877],[1358,-2223]],[[269007,593543],[-402,-1132],[318,-2737],[-624,-2035],[-431,-4006],[-30,-3870],[-489,-979],[205,-2829],[-412,-968],[492,-1297],[-601,-2026],[628,-2268]],[[267661,569396],[538,-2674],[1861,-4718],[596,-550]],[[270656,561454],[536,-878],[352,-2352],[1288,-440],[1182,-1047],[1434,632],[1976,1912],[1528,2298],[2980,-1135],[1171,-1007],[1968,-3424]],[[285071,556013],[1756,-3888],[-495,3387],[1788,2461],[392,1638],[1378,1095],[-113,1655],[650,5220],[1671,2955],[1084,-715],[149,-1326],[949,3409],[2071,-266],[1644,2467],[1241,1049],[387,1774],[1170,1371],[1256,-502],[347,-1712],[-507,-1093]],[[301889,574992],[-1770,-1729],[996,-4999],[-184,-1673],[-1245,-3722],[978,-2843],[206,-1559],[1080,314],[589,1319],[92,2119],[-927,3305],[-439,3051],[208,1099],[3436,2422],[965,423],[189,1349],[-1043,-281],[-261,1548],[785,1729],[482,-1080],[553,-3055],[1108,229],[1124,-514],[1192,-1604],[719,-3959],[745,-123],[2452,821],[2061,128],[1098,-2218],[2008,-1112],[773,166],[1840,2131],[904,594],[-1209,457],[2226,48],[2206,631],[2286,-52],[-1390,-1150],[-1482,-92],[576,-1175],[-96,-1641],[627,711],[540,-2329],[558,1196],[801,-1492],[673,957],[777,-1549],[1436,-1614],[-724,-1573],[-540,-2932],[-1031,-17],[874,-1108],[1397,1077],[1280,217],[896,-471]],[[333284,555367],[2271,-2812],[1593,-3133],[415,-1304],[-379,-4877],[552,2066],[1201,-387],[2201,-4080],[-13,-3251]],[[341125,537589],[625,2633],[2862,-1170],[309,985],[2763,158],[2165,-1069],[-283,-2660]],[[349566,536466],[858,2508],[1091,-1296],[1542,-820],[2337,-4193],[362,-1666],[395,796],[369,-3017]],[[356520,528778],[293,1479],[909,-1288],[465,-4809],[1038,-6348],[611,-2256],[1394,-1005],[162,-2944],[-1099,-1939],[-1450,-3930],[-1296,-1526],[-337,-1822],[-829,-2189],[-51,-1518],[-832,-2254],[-580,216],[-1208,-1121],[1991,-207],[2923,3845],[-63,-1052],[633,-3830],[797,-1504],[1122,1088],[777,-560],[1126,1153],[-583,-5125],[869,4031],[611,514],[780,2026],[687,-748],[37,2776],[930,2417],[1991,656],[1630,-906],[539,-1131],[1106,-360],[1596,-1875],[516,-50],[360,-2140],[702,1487],[1181,-1655],[316,-1818],[-347,-1900],[631,1216],[-794,-5771],[788,1172],[359,2424],[562,248],[-241,-1873],[722,1339],[1327,483],[208,746],[1232,-527],[1909,-1937],[1037,269],[1549,-1123],[2344,832],[1417,-390],[4135,-5071],[1186,-2956],[1174,-2226],[901,-716],[354,-1181],[1622,-1097],[1696,256],[1195,-446],[873,-2590],[687,-4899],[508,-5301],[-81,-4047],[-898,-5683],[-508,-1778],[-1408,-3208],[-1530,-4216],[-1498,-1993],[-1318,-4010],[-768,-3571],[-1531,-4409],[-720,-666],[-534,1971],[-446,-985],[46,-2116],[-706,-2612],[225,-3039],[-142,-3281],[497,-7164],[-679,-5328],[-251,-3272],[170,-2299],[-924,-1697],[-703,-3848],[111,-3780],[-837,-2749],[-1097,-4903],[-1092,-1994],[-717,-3552],[165,-2457],[-374,-972],[-1620,-1334],[-763,-1606],[-172,-2171],[-2544,-118],[-355,1444],[-384,-1587],[-1783,478],[-2142,-859],[187,-1295],[-1060,-636],[-1424,-2495],[-1411,42],[-2486,-2612],[-750,-1522],[-2054,-2987],[-907,-2483],[-554,856],[-804,-1300],[808,-627],[-400,-1295],[-369,-5255],[344,-2921],[-246,-2144],[61,-3067],[-497,-2961],[-1310,-1753],[-1319,-2915],[-798,-2593],[-739,-3702],[-890,-2796],[-1478,-3452],[-2465,-3759],[-66,1686],[1062,330],[1407,2576],[35,1309],[1311,2457],[120,2768],[-1420,-755],[-850,-4078],[-1413,-1962],[-614,-2972],[183,-1672],[-595,-1612],[-863,-4135],[-1995,-3581]],[[351748,304813],[-1152,-3781],[-1065,-1720],[-2038,-1553],[-2141,931],[-1235,-783],[-2048,1370],[-877,1329],[-1829,-148],[-1586,3347],[102,4325],[781,1710],[-330,2500]],[[338330,312340],[84,-2889],[-705,-902],[-340,-3270],[429,-3137],[-369,-611],[672,-2295],[1444,-1250],[1278,-1741],[402,-1881],[-542,-1270],[247,-2511],[1575,-1673],[72,-2517],[-2010,-5292],[-420,-2021],[-1756,-2074],[-4581,-2384],[-3566,-917],[-1362,-35],[-2145,865],[221,-2313],[670,-773],[-216,-2676],[-432,-414],[-389,-2729],[502,-1888],[-413,-1281],[-1567,-1296],[-2261,-240],[-2999,1993],[-779,-396],[319,-4067],[-113,-2387],[1520,-1779],[-164,-865],[1306,125],[-355,1048],[1203,618],[554,-1733],[-268,-2363],[-1217,-332],[-538,1713],[-906,242],[-1046,-1348],[1966,-1244],[-1851,-1924],[-828,-1993],[125,-2481],[-340,-2539],[-796,-1090],[25,-2053],[-1532,255],[-2087,-1733],[-1709,-4223],[-18,-2223],[2184,-3913],[2162,-521],[724,-1488],[-545,-2854],[345,-678],[-1620,-2377],[-1777,-1691],[-1811,-3667],[-274,-3627],[-1316,-1455],[-1140,2086],[683,-2402],[-1438,-1330],[-726,-3621],[476,-2683],[-919,-671],[1232,-918],[1315,-3804]],[[309879,194532],[-2215,896],[-888,-1280],[-3429,-2057],[-558,-5987],[-839,-617],[-2435,1488],[-365,2243],[1570,125],[1627,2293],[-647,481],[-2973,-2904],[-252,-1222],[-1400,1288],[1047,2929],[3235,851],[-3148,452],[-1327,-3229],[-1452,1405],[1123,769],[-2149,402],[-781,3089],[1281,-689],[417,927],[1519,-309],[1800,2170],[-2483,-1815],[-2259,2307],[741,371],[94,1697],[-2552,1590],[-778,2262],[1136,114],[115,1784],[1262,-2471],[13,1734],[-1234,1726],[822,1300],[119,2195],[1109,-116],[-1319,1322],[-43,3594],[585,2171],[-842,-216],[-306,2754],[2708,30],[-295,2006],[-625,-1623],[-1138,-89],[-845,1433],[1379,3080],[-431,2336],[-463,-578],[-1848,1693],[-1420,-61],[2034,2669],[-395,1687],[2525,640],[334,2069],[590,-172],[-712,-4058],[1074,1612],[-322,-3067],[413,474],[188,3084],[-379,1759],[1468,747],[-674,686],[229,1540],[1732,1446],[209,1764],[-1670,1586],[745,3182],[-290,1045],[620,2411],[258,4425],[985,-785],[-192,2682],[-901,428],[428,1477],[-959,686],[-630,-1405],[-1370,228],[-641,3698],[823,6137],[720,1737],[511,3346],[-850,5081],[188,1934],[-547,2025],[167,3022],[1071,128],[272,2835],[676,1765],[697,4767],[1111,2900],[614,5515],[940,3038],[-218,3303],[419,744],[474,3452],[-668,7212],[-21,4971],[747,1110],[235,2923],[-565,4285],[925,3250],[371,3854],[1128,8282],[-186,3230],[745,3623],[-358,3130],[240,5110],[265,1279],[-543,1170],[69,1845],[643,1234],[678,8031],[-304,4548],[136,5452],[-751,8647]],[[304393,396029],[-2552,3929],[-542,2300],[-1608,1728],[-990,1745],[-907,554],[-2864,2735],[-894,1424],[-2659,2965],[-1193,3037],[-1112,1575],[-1229,4565],[535,2060],[-1801,6912],[-890,1708],[-188,2352],[-1147,2225],[-286,2672],[-1248,4430],[-1602,8720],[-695,2411],[-1014,2220],[-1068,4555],[-968,2471],[-2866,3512],[140,1448],[581,316],[-792,3507],[164,825],[-633,2123],[148,2057],[1346,3503],[1317,2033]],[[276876,484646],[1119,1764],[533,3027],[-769,1335],[-772,-2091],[-1884,3066],[535,667],[-87,4107],[-280,1804],[968,1368],[199,2841],[969,2146],[301,2467],[-176,2219],[964,1156],[2338,1341],[111,1476]],[[280945,513339],[-460,998],[645,1333],[601,-444],[-112,3158],[1381,1074],[1250,2315],[1647,6128],[-973,872],[391,3918],[-320,4114],[-369,716],[792,1440],[-611,2350],[304,1942],[-1503,4294]],[[283608,547547],[-747,1863],[-699,3064],[855,1888],[-2702,3658],[-986,53],[-683,-919],[-175,-1513],[-1718,-1818],[-248,-1254],[1241,-3418],[-1188,-1334],[-1129,-325],[-868,3758],[-170,-1383],[-792,594],[-620,2467],[-1411,1027],[-1234,65],[-555,-1489]],[[269779,552531],[-786,3066],[-854,703],[495,-1782],[-1229,1235],[269,2494],[-717,1428],[-2121,2193],[-156,1498],[-1522,2116],[1046,-2581],[-633,-1417],[-556,1358],[-862,542],[-572,2936],[454,2055],[-669,904],[454,975]],[[261820,570254],[-601,1595],[-1412,2411],[-795,2479],[-2533,4425],[917,448]],[[257396,581612],[-422,2214],[-903,274]],[[256071,584100],[-324,-1295],[-2600,608],[-1141,1154],[-1462,486],[-809,1045]],[[249735,586098],[-1421,1141],[-1498,-20],[-1869,1793],[-1156,1879]],[[243791,590891],[-1594,3514],[-3075,5421],[-2071,878],[-664,1278],[-1566,-2624],[-2081,-1668],[-826,-244],[-1873,1525],[-1582,341],[-1067,1419],[-1060,583],[-672,1363],[-2579,1095],[-927,1190],[-2287,1659],[-2090,2672],[-686,1604],[-2368,833],[-2062,1555],[-1307,2980],[-2850,2850],[-1510,3949],[-520,2427],[1136,1146],[-645,1170],[710,2030],[78,2201],[-618,755],[-605,2191],[9,2008],[-405,1781],[-1696,3365],[-1896,4861],[-1229,2038],[43,765],[-1221,745],[-712,2132],[478,359],[-1987,2304],[-772,333],[395,1499],[-861,-835],[-523,797],[-113,1810],[627,1615],[-786,2400],[-756,-44],[-525,2230],[-1483,1442],[-383,1962],[238,1246],[-1643,609],[-973,2471],[-579,513],[-1338,3248],[-171,1485],[-1430,4241],[-1034,4787],[177,2286],[-1603,987],[-900,1680],[-560,-723],[-2178,2330],[399,-1502],[-257,-2907],[691,-3848],[-45,-1593],[770,-2416],[1714,-2742],[710,-2611],[818,-758],[310,-1700],[620,-519],[380,-3544],[1124,-1793],[904,-2632],[392,-2373],[187,1192],[627,-1020],[659,-3449],[112,-1989],[716,-1557],[991,-4374],[51,-2649],[811,-1428],[290,1446],[667,-1007],[1672,-4114],[-103,-1572],[-1293,-1949],[-452,709],[-769,3551],[-2934,4290],[-1815,3028],[47,3840],[-893,4299],[-1788,2188],[-375,2151],[-1230,-1333],[-673,1453],[-1679,1491],[-263,1261],[-1379,2434],[1296,-343],[748,527],[700,3278],[-269,1062],[-2357,4615],[-1888,2203],[-394,3242],[-501,657],[-184,2309],[-1667,4507],[115,1695],[-631,867],[-778,3175]],[[174644,697459],[-943,4516],[-1703,2527],[-916,129]],[[171082,704631],[-267,1620],[-1770,561]],[[169045,706812],[-1284,1813]],[[167761,708625],[-2431,318]],[[165330,708943],[-454,642]],[[164876,709585],[31,2941]],[[164907,712526],[-630,1712]],[[164277,714238],[-2825,5721],[-10,3601]],[[161442,723560],[-1429,1591]],[[160013,725151],[-330,3344],[1232,-1740]],[[160915,726755],[-875,2858],[1857,435]],[[161897,730048],[-2130,443],[-104,-1673]],[[159663,728818],[-1141,1357],[-525,2333]],[[157997,732508],[-1611,2713],[-511,5649],[-1220,2318]],[[154655,743188],[-132,1417]],[[154523,744605],[663,2836]],[[155186,747441],[179,2455]],[[155365,749896],[-796,4376]],[[154569,754272],[-513,4088],[1086,5207]],[[155142,763567],[249,6434]],[[155391,770001],[361,4735]],[[155752,774736],[49,3585]],[[155801,778321],[1918,-169]],[[157719,778152],[-677,696]],[[157042,778848],[-1689,49]],[[155353,778897],[-108,4478]],[[155245,783375],[-734,3693]],[[154511,787068],[-681,1455]],[[153830,788523],[-247,2821]],[[153583,791344],[2040,-1255]],[[155623,790089],[2642,-516]],[[158265,789573],[683,333]],[[158948,789906],[557,-5003]],[[159505,784903],[623,465],[-108,2659],[419,1127],[-1186,2693]],[[159253,791847],[344,1760]],[[159597,793607],[-677,1367]],[[158920,794974],[-724,0]],[[158196,794974],[-795,2762]],[[157401,797736],[-1545,209]],[[155856,797945],[-35,2883]],[[155821,800828],[-659,-1117]],[[155162,799711],[-753,-87],[-1027,1435]],[[153382,801059],[-768,2925]],[[152614,803984],[-3864,212],[1534,798]],[[150284,804994],[-1721,237]],[[148563,805231],[-319,1130]],[[148244,806361],[-1182,-282]],[[147062,806079],[-1807,1681]],[[145255,807760],[176,1939]],[[145431,809699],[-623,1758]],[[144808,811457],[214,3046]],[[145022,814503],[-863,-2968]],[[144159,811535],[-708,2195]],[[143451,813730],[699,4431]],[[144150,818161],[-721,-480]],[[143429,817681],[-797,2477],[-1191,731]],[[141441,820889],[-93,1622],[1590,-1307]],[[142938,821204],[-1159,2494]],[[141779,823698],[-903,-2657]],[[140876,821041],[-776,-838],[-2144,2799]],[[137956,823002],[811,2426],[-1073,1704],[2414,6170]],[[140108,833302],[-1178,-614]],[[138930,832688],[-111,3136]],[[138819,835824],[-33,-3497]],[[138786,832327],[-492,-1612]],[[138294,830715],[-1003,-1519]],[[137291,829196],[-763,226],[-550,2074]],[[135978,831496],[591,1033]],[[136569,832529],[-848,3942]],[[135721,836471],[-1788,-716]],[[133933,835755],[-554,-2023]],[[133379,833732],[-667,1102],[1054,2601]],[[133766,837435],[-2970,5257]],[[130796,842692],[-1535,739]],[[129261,843431],[-245,3098]],[[129016,846529],[-1818,3185],[-1265,901]],[[125933,850615],[-1300,2714],[-643,3415],[-387,-1286],[1259,-5305]],[[124862,850153],[-2288,517],[-760,2339]],[[121814,853009],[-2340,1455]],[[119474,854464],[2577,-3446],[-1447,-1231]],[[120604,849787],[-2671,1992]],[[117933,851779],[-2246,2998]],[[115687,854777],[-4019,2719]],[[111668,857496],[796,900],[-18,1888],[-1937,-1719]],[[110509,858565],[-1741,131]],[[108768,858696],[-2296,1310]],[[106472,860006],[-3544,753]],[[102928,860759],[-3338,-478],[-2094,1888]],[[97496,862169],[584,1979]],[[98080,864148],[-1548,-1712],[-1808,581]],[[94724,863017],[-2054,2483]],[[92670,865500],[155,1366]],[[92825,866866],[-2243,-1243],[-1708,299],[704,1484]],[[89578,867406],[-2237,-2466],[1647,-1883]],[[88988,863057],[-1297,-2937]],[[87691,860120],[-2679,691]],[[85012,860811],[-563,-1987]],[[84449,858824],[-2732,-1219]],[[81717,857605],[-980,-1869]],[[80737,855736],[-2232,-359],[-311,1290],[1251,652],[-1261,1574]],[[78184,858893],[1116,2491]],[[79300,861384],[265,3083],[2543,1781],[2357,-176]],[[84465,866072],[-980,1780],[-1852,41],[-3117,-2313]],[[78516,865580],[-46,-923],[-2510,-3061]],[[75960,861596],[-292,-1880]],[[75668,859716],[-1255,-464]],[[74413,859252],[-2436,-2840]],[[71977,856412],[-250,-1231]],[[71727,855181],[2343,-1763]],[[74070,853418],[-1904,-2162]],[[72166,851256],[-630,-1976]],[[71536,849280],[-2112,-849],[-2141,-2654]],[[67283,845777],[-1945,-1424]],[[65338,844353],[-420,-1884]],[[64918,842469],[-3444,-2160],[-1857,-1836]],[[59617,838473],[-700,-2064]],[[58917,836409],[-2649,-848]],[[56268,835561],[-2100,-1816],[-2726,-826]],[[51442,832919],[60,1370]],[[51502,834289],[-1708,-2902]],[[49794,831387],[-1299,613],[-369,-1458],[-1835,-933]],[[46291,829609],[156,1675]],[[46447,831284],[1035,437]],[[47482,831721],[2081,3103]],[[49563,834824],[2616,1788]],[[52179,836612],[2565,-1280]],[[54744,835332],[-686,1192],[658,1823]],[[54716,838347],[3644,3235]],[[58360,841582],[3480,4076]],[[61840,845658],[594,5174]],[[62434,850832],[1060,2703]],[[63494,853535],[-2444,-1407]],[[61050,852128],[-2094,1554]],[[58956,853682],[-36,-2735]],[[58920,850947],[-817,171],[-1633,2615]],[[56470,853733],[-694,-540]],[[55776,853193],[-1229,1370]],[[54547,854563],[-2774,-2261]],[[51773,852302],[-2177,-150]],[[49596,852152],[1169,889],[-831,2901]],[[49934,855942],[541,1805],[-1646,4120]],[[48829,861867],[788,2379],[-1521,-2468],[319,-1655],[-3712,-1083],[-2099,2944],[-1920,1407],[1524,2078]],[[42208,865469],[2985,-1789]],[[45193,863680],[860,991]],[[46053,864671],[-4875,1234]],[[41178,865905],[833,718]],[[42011,866623],[-2265,1262],[-1324,2079]],[[38422,869964],[1541,1295]],[[39963,871259],[-262,1369]],[[39701,872628],[1425,2211],[1153,45]],[[42279,874884],[-183,1894]],[[42096,876778],[2048,2730],[2081,-1279]],[[46225,878229],[2049,1303],[939,1561]],[[49213,881093],[3288,170],[891,1546]],[[53392,882809],[-581,2562]],[[52811,885371],[-1186,1629]],[[51625,887000],[1341,313]],[[52966,887313],[-707,2043],[-1591,-638]],[[50668,888718],[-2910,-2619]],[[47758,886099],[-2518,1268]],[[45240,887367],[-3294,-756]],[[41946,886611],[-3455,724]],[[38491,887335],[-757,2036]],[[37734,889371],[-1425,1365],[2031,880]],[[38340,891616],[-3351,690],[-1901,1397]],[[33088,893703],[2816,1300]],[[35904,895003],[4514,3156]],[[40418,898159],[4783,1224]],[[45201,899383],[-850,-2375]],[[44351,897008],[940,-780],[5219,-179]],[[50510,896049],[1003,1348],[-1036,531]],[[50477,897928],[-2165,3101]],[[48312,901029],[1639,-653]],[[49951,900376],[300,-1330]],[[50251,899046],[3496,-1106]],[[53747,897940],[1079,1182]],[[54826,899122],[-1672,583],[-1483,-705]],[[51671,899000],[-1273,880]],[[50398,899880],[651,1653]],[[51049,901533],[-3832,284]],[[47217,901817],[-1997,997]],[[45220,902814],[-1123,2436],[-3503,2600]],[[40594,907850],[-3890,1860],[1128,389],[476,2725]],[[38308,912824],[5296,304],[3170,2675],[580,2193],[2441,2391],[2993,846]],[[52788,921233],[4671,3400],[3655,-196],[4246,3331],[6320,-3593]],[[71680,924175],[2673,779],[2778,-724]],[[77131,924230],[-661,-929]],[[76470,923301],[3461,-1391]],[[79931,921910],[5432,486],[4343,-1680],[5230,-340],[1738,-896]],[[96674,919480],[5497,638],[5029,-2744]],[[107200,917374],[1127,-14]],[[108327,917360],[5056,-801],[2927,-2154]],[[116310,914405],[7684,-2700]],[[123994,911705],[-1429,1308],[514,2335]],[[123079,915348],[4976,1286],[-762,-1632]],[[127293,915002],[2808,1073]],[[130101,916075],[898,1283]],[[130999,917358],[4733,1519]],[[135732,918877],[1772,1400]],[[137504,920277],[2361,-862]],[[139865,919415],[-3643,-2166],[-2508,-490]],[[133714,916759],[-4211,-3927]],[[129503,912832],[1868,-424],[-35,1565]],[[131336,913973],[2585,2091],[2153,-21]],[[136074,916043],[118,-1301],[1714,2648],[3456,1339]],[[141362,918729],[384,-1004]],[[141746,917725],[3576,3246],[-853,1857]],[[144469,922828],[2368,-1982]],[[146837,920846],[1462,-3015],[3403,-2258]],[[151702,915573],[516,2842]],[[152218,918415],[2103,1669],[888,-2492]],[[155209,917592],[-837,-1840]],[[154372,915752],[2268,-13]],[[156640,915739],[1622,2564]],[[158262,918303],[4151,-203]],[[162413,918100],[3441,-2104],[3955,-969]],[[169809,915027],[2149,-1268],[5654,-1220],[1190,803]],[[178802,913342],[3382,-1855],[1248,-1543]],[[183432,909944],[-2225,-763],[-1858,-2181]],[[179349,907000],[4868,-1198]],[[184217,905802],[3463,-90]],[[187680,905712],[4527,874],[1954,948]],[[194161,907534],[1310,-1538]],[[195471,905996],[2882,-840]],[[198353,905156],[1843,-2750]],[[200196,902406],[-1574,-204]],[[198622,902202],[2182,-2087],[233,1559]],[[201037,901674],[1306,-719]],[[202343,900955],[-2266,5037]],[[200077,905992],[587,1778],[3713,998]],[[204377,908768],[1785,1931]],[[206162,910699],[-2297,-1002]],[[203865,909697],[-2809,-156]],[[201056,909541],[-318,-933]],[[200738,908608],[-2645,614]],[[198093,909222],[1036,1975]],[[199129,911197],[5970,1833],[1735,-1193]],[[206834,911837],[309,-1542]],[[207143,910295],[3430,-2530]],[[210573,907765],[1999,496]],[[212572,908261],[2172,-1797],[3159,-703]],[[217903,905761],[3052,868]],[[220955,906629],[3637,-686],[2041,494]],[[226633,906437],[2659,-1127]],[[229292,905310],[690,1410]],[[229982,906720],[-2511,285],[-1500,2729]],[[225971,909734],[3444,788],[1904,-2579]],[[231319,907943],[2097,1113]],[[233416,909056],[-1115,-4119]],[[232301,904937],[639,-1671]],[[232940,903266],[1171,265]],[[234111,903531],[1016,-1990]],[[235127,901541],[265,1670]],[[235392,903211],[-1089,2813]],[[234303,906024],[528,1682]],[[234831,907706],[1666,121],[3381,3503]],[[239878,911330],[-2552,1651]],[[237326,912981],[1336,1328]],[[238662,914309],[-527,1892],[-4941,2227]],[[233194,918428],[-1376,2940],[1852,1313]],[[233670,922681],[-1862,1539],[398,2754]],[[232206,926974],[2336,374],[-854,1401]],[[233688,928749],[1864,1958]],[[235552,930707],[1789,446]],[[237341,931153],[4468,-4247]],[[300051,207169],[-2273,162],[-1078,1268],[-288,-1355],[935,-2423],[-49,1561],[2753,787]],[[299149,209912],[125,889],[-1858,1013],[-276,-1382],[2009,-520]],[[302316,229071],[-1891,1251],[-1754,-1661],[-682,497],[-140,-2140],[1434,1704],[1323,512],[1710,-163]],[[309232,235614],[-815,831],[338,-2143],[477,1312]],[[297313,260123],[1023,603],[-694,1240],[-329,-1843]],[[352392,311289],[-108,-176]],[[352284,311113],[-1241,-2707],[224,-2392]],[[351267,306014],[47,-79]],[[351314,305935],[328,2335],[1093,2133],[674,-457],[498,2232],[-353,1780],[-1162,-2669]],[[327139,322385],[-161,1110],[-989,404],[-932,-1013],[701,-1336],[1381,835]],[[349258,362140],[-575,-4635],[-661,-1153],[536,-871],[-254,-1439]],[[348304,354042],[1138,1142],[-491,355],[329,3186],[-22,3415]],[[357177,383706],[1398,630],[-63,1361],[-1365,-992],[30,-999]],[[375077,393444],[-787,942],[-393,-2275],[1180,1333]],[[307956,408551],[363,-118]],[[308319,408433],[-123,-1621]],[[308196,406812],[119,-148]],[[308315,406664],[1162,1474],[-670,661],[92,1293],[-1567,2729]],[[307332,412821],[-125,120]],[[307207,412941],[-782,1344],[-698,-797],[-202,-2544],[1566,-1193],[-100,-903],[965,-297]],[[386332,449873],[-955,-620],[1,-1374],[-2042,457],[-987,-1134],[-18,-1639],[-573,-510],[-130,-2422],[-562,101],[197,-1444],[-1153,-2421],[1101,-162],[107,1778],[988,2462],[591,-1433],[-573,2822],[1189,2400],[2077,501],[251,1197],[713,268],[-222,1173]],[[361918,478518],[-11,-1010],[1199,-4197],[-183,2487],[-773,6122],[-664,-2804],[432,-598]],[[347518,533051],[241,742],[-889,541],[-578,-2791],[1280,431],[-54,1077]],[[264404,571429],[-1236,3095],[-1495,1921],[-282,-2397],[1061,-2885],[1724,-1068],[228,1334]],[[260823,577661],[-529,957],[-819,-406],[762,-1350],[586,799]],[[253117,597801],[-1417,-1326],[859,-263],[558,1589]],[[214693,624354],[-509,739],[-1329,-346],[1629,-807],[209,414]],[[229370,654923],[-756,-1152],[-164,-7372],[197,3327],[723,5197]],[[223732,665013],[790,-2918]],[[224522,662095],[147,1097],[-937,1821]],[[276031,664008],[-361,1749],[-824,-1134],[869,-1739],[316,1124]],[[178856,701850],[-936,1451],[-314,-514],[758,-1695],[492,758]],[[256497,710969],[1819,-777],[-1046,1001],[-773,-224]],[[184345,716995],[-1002,2446],[-638,-1149],[-571,1110],[-1069,-685],[1689,-686],[611,942],[980,-1978]],[[255397,721963],[-173,-1865],[581,-993],[-408,2858]],[[193423,729241],[-1653,-4426],[1470,3320],[183,1106]],[[311702,775814],[-211,-115]],[[311491,775699],[1149,-785]],[[312640,774914],[-32,205]],[[312608,775119],[-906,695]],[[276535,778914],[1280,-720],[1530,442],[-605,728],[-2205,-450]],[[287558,783985],[198,1824],[-1143,423],[945,-2247]],[[285352,786669],[38,-1448],[902,834],[-940,614]],[[305332,797621],[505,623],[-1163,1887],[-1107,-2007],[556,-676],[646,1365],[563,-1192]],[[255471,800710],[-1691,1871],[-706,-1305],[-186,-3291],[694,508],[1473,-716],[416,2933]],[[204952,786635],[-300,2367],[-1680,-1540],[-2177,-222],[2364,-334],[1329,1284],[464,-1555]],[[216258,786530],[2076,-365],[547,822],[-2660,-124],[-373,1705],[-374,-2070],[784,32]],[[292969,791821],[1197,-526],[-1333,1584],[62,966],[-2380,-2160],[2023,-479],[431,615]],[[299848,791779],[775,925],[-1541,348],[766,-1273]],[[241895,791656],[1342,-252],[-997,1806],[-1634,-469],[1900,-587],[-611,-498]],[[277277,793902],[869,-1058],[1584,-150],[-1498,1579],[-955,-371]],[[236909,794206],[1774,1729],[-1590,1716],[1090,153],[-791,2728],[-137,-1229],[-1568,-600],[125,-942],[1165,961],[-1591,-2895],[-35,-1486],[1558,-135]],[[289481,768262],[-51,62]],[[289430,768324],[-700,-759]],[[288730,767565],[-2596,-1733],[313,-777]],[[286447,765055],[-914,-398]],[[285533,764657],[-1043,963]],[[284490,765620],[-4103,-1205]],[[280387,764415],[-1412,-1521],[-570,-1532]],[[278405,761362],[1213,-722]],[[279618,760640],[345,264]],[[279963,760904],[445,254]],[[280408,761158],[2439,649]],[[282847,761807],[1794,-755],[1535,60]],[[286176,761112],[2069,1618],[-157,1767]],[[288088,764497],[604,887]],[[288692,765384],[-782,637]],[[287910,766021],[1571,2241]],[[296185,766268],[405,1760],[224,3722],[-656,-811],[27,-4671]],[[279310,767227],[793,829],[-583,1473],[-210,-2302]],[[221420,771418],[-171,4958],[-770,-1223],[788,-1524],[153,-2211]],[[188961,747632],[-2295,3939],[-448,-1716],[1643,-4008],[1100,1785]],[[269232,755176],[1352,232]],[[270584,755408],[-154,1442]],[[270430,756850],[-395,631]],[[270035,757481],[-1858,-5463],[2711,-2040]],[[270888,749978],[2103,669]],[[272991,750647],[1374,1537]],[[274365,752184],[2483,1602],[3126,3047],[921,1445]],[[280895,758278],[-523,1818]],[[280372,760096],[25,-1444]],[[280397,758652],[-3232,-520]],[[277165,758132],[-641,-953],[-2648,97],[-1340,-2209],[-1992,-1373],[-1399,271],[87,1211]],[[264522,776026],[3593,-2701]],[[268115,773325],[515,-1768],[-3,-2116]],[[268627,769441],[-101,-1826]],[[268526,767615],[-1523,-2444]],[[267003,765171],[399,-1990]],[[267402,763181],[979,1761]],[[268381,764942],[1210,848]],[[269591,765790],[788,-1261]],[[270379,764529],[684,-4957]],[[271063,759572],[1758,1833],[212,4762]],[[273033,766167],[1140,2761],[54,1145],[-1164,2733],[1113,-15]],[[274176,772791],[103,-1306]],[[274279,771485],[1011,-1465]],[[275290,770020],[1860,-1609]],[[277150,768411],[327,1762]],[[277477,770173],[1086,-270],[-1024,2062]],[[277539,771965],[124,1381]],[[277663,773346],[-857,337]],[[276806,773683],[-1300,3269],[-2164,92]],[[273342,777044],[-52,905]],[[273290,777949],[-3917,367],[-2962,1064]],[[266411,779380],[-204,1195]],[[266207,780575],[-931,-470]],[[265276,780105],[321,2503]],[[265597,782608],[-1074,776]],[[264523,783384],[492,1679],[-1166,1888],[443,1843]],[[264292,788794],[-2565,120],[-905,1422],[-812,3086]],[[260010,793422],[-2298,265]],[[257712,793687],[-2889,1172]],[[254823,794859],[85,-2231]],[[254908,792628],[-749,1398]],[[254159,794026],[-755,-1481]],[[253404,792545],[-1184,-783]],[[252220,791762],[-963,-2595]],[[251257,789167],[-159,-126]],[[251098,789041],[-3757,-2772]],[[247341,786269],[-3076,-4176],[1571,-312]],[[245836,781781],[1536,1111]],[[247372,782892],[581,-1569]],[[247953,781323],[744,-620],[3094,1764],[1957,2074]],[[253748,784541],[560,-2551]],[[254308,781990],[647,889]],[[254955,782879],[1606,-740],[777,-1790],[2002,-423],[1404,1385]],[[260744,781311],[3234,497]],[[263978,781808],[-185,-1570]],[[263793,780238],[1964,-86]],[[265757,780152],[279,-1823]],[[266036,778329],[689,-1315],[-1813,540],[-473,-1056]],[[264439,776498],[-1622,1388],[-3271,-1340]],[[259546,776546],[-1141,-944]],[[258405,775602],[-23,1149]],[[258382,776751],[-2355,-5774],[-386,-2314]],[[255641,768663],[1013,1747]],[[256654,770410],[742,-421]],[[257396,769989],[-1568,-9070],[335,-2911],[-45,-3211]],[[256118,754797],[1069,-3288],[943,77],[1224,1433]],[[259354,753019],[925,2999]],[[260279,756018],[215,2861]],[[260494,758879],[-889,4365]],[[259605,763244],[64,2586]],[[259669,765830],[620,1522]],[[260289,767352],[163,2308],[1724,2799],[187,-2494],[482,2993]],[[262845,772958],[1135,685]],[[263980,773643],[-40,2219]],[[263940,775862],[582,164]],[[304140,818688],[-1150,-344],[1394,-1772],[-244,2116]],[[65144,846949],[-1377,-1292],[582,-619],[795,1911]],[[66775,847565],[-2377,1168],[-43,-804],[2420,-364]],[[204838,846536],[-2252,-1307],[-60,-1422],[2764,1356],[-452,1373]],[[159718,836983],[-4078,-138],[-2403,3863],[2182,-4682],[2444,-3138],[-1641,2784],[123,902],[1602,-244],[1771,653]],[[294473,836638],[-896,1402],[-1408,66],[1583,-1987],[721,519]],[[287370,837457],[1688,160],[-1186,2129],[-502,-2289]],[[226959,845332],[-1977,-3354],[-895,360],[-623,-1640],[1940,770],[1679,1606],[709,1631],[-833,627]],[[217431,847801],[-739,1057],[-2498,-3777],[890,-3675],[-1994,-3118],[2283,1800],[1750,3923],[-717,578],[1529,2656],[-504,556]],[[224031,819892],[-1513,331],[-906,2234],[-20,2267],[993,1014],[-1359,95],[100,-3610],[-1444,-985],[2419,-1946],[1424,-267],[306,867]],[[288610,823079],[-1655,-222],[1641,2468],[-731,426],[-1104,-1648],[-2553,-1367],[1384,-1103],[3449,1143],[-435,-1247],[3825,2261],[-3126,-11],[-695,-700]],[[238232,823064],[293,934],[-2229,-387],[1936,-547]],[[307358,825126],[-1409,1693],[-785,-1418],[2194,-275]],[[249008,823252],[2079,32],[-1092,680],[-987,-712]],[[324512,825405],[-1589,-197],[-1719,3075],[-270,-2147],[-2602,-1459],[-1492,1564],[-1366,3216],[-533,-1128],[360,-2072],[598,1214],[2607,-3549],[-532,-2308],[1414,-406],[-675,2221],[1942,1945],[2048,-1578],[1809,1609]],[[239103,829583],[-1539,-201],[-796,-2306],[3100,2177],[-765,330]],[[210011,831510],[-625,870],[-1711,-523],[0,-1552],[2336,1205]],[[296716,831144],[381,-900],[2277,1985],[-2658,-1085]],[[200587,831811],[341,3160],[-1051,-1398],[710,-1762]],[[198441,839505],[801,-1889],[-1089,-3686],[1506,2928],[134,1892],[-1352,755]],[[72301,858817],[-990,131],[-4216,-1503],[284,-1203],[3138,692],[1784,1883]],[[189398,851653],[-1446,1288],[-156,-1182],[1679,-1461],[-77,1355]],[[214341,850290],[-320,1200],[-1828,-734],[-127,-2899],[1236,-540],[-55,1497],[1094,1476]],[[196575,855010],[3878,528],[1660,785],[-6410,1314],[-4764,-5137],[519,-1174],[1843,746],[1460,2225],[1814,713]],[[45922,862634],[-1068,118],[227,-1580],[841,1462]],[[237151,884822],[-4381,983],[1163,-1590],[3218,607]],[[200152,884438],[1232,-1804],[1100,618],[-602,1404],[-1730,-218]],[[197160,876811],[-4173,820],[-2413,-1321],[-2524,-3480],[-3534,-686],[-2077,2354],[-1088,-236],[-1597,1370],[-2081,700],[2129,-1535],[-52,-1359],[1818,-2182],[-2561,-761],[-1090,-3303],[-2260,868],[-1431,-1376],[5015,-1535],[4558,940],[432,1804],[1965,529],[2115,1386],[1725,2991],[2287,2467],[2858,615],[-4271,-195],[1696,1256],[3781,-660],[773,529]],[[227864,875283],[1455,-753],[-604,2622],[-2211,-210],[1360,-1659]],[[174114,879194],[-2171,2142],[-1464,-1382],[2469,-1326],[1166,566]],[[223316,877713],[1289,-177],[-572,1443],[-717,-1266]],[[216848,879267],[-278,-1756],[2068,-1532],[1129,1719],[-638,1275],[1133,2222],[-3414,-1928]],[[75167,922797],[-1865,997],[-1794,-452],[1671,-1071],[1988,526]],[[235418,889826],[-1894,-1],[1059,-1426],[835,1427]],[[170371,889803],[926,-1297],[190,2480],[-1558,109],[442,-1292]],[[183061,892047],[4868,-1480],[-3543,1706],[-1325,-226]],[[172660,898241],[-5222,-665],[-2009,496],[1003,1534],[2743,1510],[-2168,714],[-5842,-2330],[-8562,-2301],[683,-1816],[882,1255],[3065,-105],[1057,709],[4826,-967],[-4470,-3583],[97,-1239],[-1648,-959],[3904,-918],[1368,2385],[2457,1431],[313,-1871],[-2558,-2428],[670,-314],[4694,3443],[-1244,1057],[1024,1162],[4406,-528],[-460,1256],[1442,2062],[-451,1010]],[[205669,860170],[-968,611],[-187,-1600],[1155,989]],[[215141,862093],[1461,-2328],[380,1831],[-1041,2430],[-800,-1933]],[[180383,862136],[-2062,-446],[1417,-860],[645,1306]],[[211567,863464],[469,1666],[-2297,-644],[1828,-1022]],[[246263,806680],[1135,-86],[1416,976],[-493,565],[-2058,-1455]],[[227891,803055],[-791,2144],[-1435,1635],[524,1471],[-1651,3160],[-1053,-372],[1063,-1009],[428,-2393],[1539,-5501],[1376,865]],[[297500,807728],[-464,1449],[-2292,-2425],[-666,-3424],[3422,4400]],[[310402,809224],[-777,2114],[-1676,-587],[-28,-2263],[1127,-2089],[1084,1348],[270,1477]],[[223127,810497],[-459,3731],[448,3890],[-2325,1609],[-1629,-536],[773,-1941],[439,1346],[1102,-643],[766,-1808],[-463,-2670],[306,-2135],[1042,-843]],[[232297,805260],[230,3092],[-1539,3645],[-982,4778],[-2150,7742],[-137,2549],[-772,-2633],[1109,-1345],[-2860,764],[-863,-3896],[1196,-2873],[1726,-2635],[1012,-2317],[909,1444],[685,-1585],[-8,-2498],[522,1313],[897,-656],[-665,-1799],[140,-4678],[985,40],[565,1548]],[[549943,856209],[1470,468],[-621,-1769],[-4158,-3105],[-647,-4580],[-77,-4409],[-1475,-5009],[-3563,-524],[-1386,-1786],[-115,-2583],[-3578,87],[257,1673],[-1309,3550],[1046,1924],[-1283,1709],[-1907,4808],[-1288,4490],[-210,3570],[535,-247]],[[531634,854476],[-1539,872],[-736,2390],[-1083,-3423],[-1559,-375],[-4034,-4745],[-1945,-736],[-2530,608],[-2356,2371],[-263,2898],[1352,-846],[-409,2542],[1256,1551],[-2893,-2338],[-641,356],[481,2466],[2520,1121],[-1338,185],[2188,3226],[-1013,-363],[-1830,-3085],[-1066,-935],[369,5343],[-653,2776],[2710,469],[2933,-155],[-1065,684],[-3701,-584],[-893,1928],[933,308],[-1141,1334],[598,2662],[3360,2673],[3717,-152],[985,1100],[-3515,-417],[635,1522],[3033,790],[1331,1316],[-504,1315],[3607,530],[784,-1359],[2170,391],[-97,970],[1793,1065],[-178,1447],[-1033,-1651],[-2808,-1472],[-859,1617],[2641,3694],[2662,1931],[-96,1372],[1862,1204],[315,2306],[2350,3925],[257,2429],[2493,2835],[3683,755],[-2654,55],[1340,1886],[1399,-731],[-449,1862],[-1451,-535],[742,1664],[2665,1616],[702,-2026],[1087,4379],[2052,1029],[5018,5619],[6210,1572],[-214,1305],[3691,837],[1593,-2260],[337,1506],[2891,2692],[957,1816],[2788,-920],[-1374,-1782],[-638,-2626],[4064,4762],[217,-2979],[2696,2473],[115,1562],[2209,-687],[-629,-4073],[1232,2796],[1369,599],[5116,-3473],[-2828,-1055],[-3180,289],[2278,-998],[96,-1165],[3428,20]],[[585749,918146],[1619,-556],[1477,1564],[2659,-1195],[-2125,-461],[559,-1156],[3629,-1000],[6038,-702],[5202,-2960],[1943,-1993],[7045,-3805],[1090,-2984],[-472,-2272],[-1854,-2249],[-3423,-1864],[-2477,-401],[-8011,1964],[-1914,1276],[-4651,1378],[-610,1440],[-2875,442],[3672,-3732],[398,-1198],[2089,-617],[1871,-2137],[-1055,-2777],[1103,-2428],[183,-2524],[2160,-1076],[1994,-2224],[2992,-1123],[1747,1259],[-326,1744],[-2139,523],[-1820,2600],[985,1926],[1792,-380],[1337,-1361],[4857,-1786],[1908,1194],[-1796,3384],[51,1470],[2431,2165],[2178,948],[2041,2348],[2841,-617],[2420,-2411],[1067,3929],[-547,2535],[-1415,917],[1231,4391],[-157,1964],[-2125,1667],[4650,-181],[2261,-582],[2218,-3738],[-3227,-540],[-1637,-2410],[1730,-980],[1177,-1969],[1407,-313],[3232,1041],[608,3603],[2786,872],[5447,3665],[4181,1530],[4050,2297],[358,-3321],[-1862,-994],[4447,-389],[1546,2168],[1738,480],[3009,-562],[2906,1989],[2456,689],[870,-1586],[-754,-1742],[2218,-132],[-4,1685],[1648,134],[1234,1527],[-2119,3579],[2348,1544],[6515,-1044],[7565,-3785]],[[683568,913720],[921,-525]],[[684489,913195],[1628,-440],[3802,-3313],[2137,3770],[-1660,97],[-1492,3039],[-3338,1062],[1335,6394],[-1284,348],[264,2874],[3755,2373],[2138,5847],[1684,1349],[5153,96],[3644,-1317],[-521,-3626],[-1979,-3149],[1859,-2350],[330,-4111],[-641,-1080],[161,-7077],[791,-1571],[2044,-1426],[-1135,-2330],[36,-1874],[-4447,-6544],[-1700,-1258],[-2951,1762],[-2399,-339],[503,-1242],[3181,-1401],[4382,-565],[1390,1860],[3818,2575],[785,2481],[1930,2087],[-1050,3876],[523,1958],[5221,1346],[2165,-3014],[-178,-4094],[1390,-1121],[3465,-1],[-3706,964],[213,2598],[917,408],[-956,3814],[-4583,1967],[-3295,-856],[-2326,143],[-1159,3510],[2176,5162],[-3492,5133],[1626,2370],[3668,1777],[-571,3952],[1619,-92],[1033,-2964],[-1372,-2860],[235,-2794],[2162,-730],[4109,-301],[2772,-1030],[-1042,1614],[-2032,268],[-3247,1682],[-777,1866],[2331,726],[1887,-1131],[1894,653],[-2114,1421],[2809,1202],[2609,-86],[3724,-1726],[2079,-2032],[4097,15],[-190,-1948],[-1652,-948],[-179,-4244],[1696,2437],[448,-2219],[-968,-2149],[2928,1948],[-1624,3301],[1167,2908],[-3855,3810],[-3768,1485],[-882,3542],[205,2858],[8226,581],[8462,1349],[1218,-415],[-3340,-1963],[3592,723],[248,1562],[-2852,3226],[2908,-352],[-3997,1668],[2391,220],[2833,2650],[6982,2734],[9347,1558],[-1606,1308],[4455,456],[4166,-414],[1172,-1130],[6013,2082],[3232,-632],[-2833,2042],[5662,264],[405,2757],[5948,3767],[2455,616],[5222,-1431],[-1417,-1488],[-3284,-805],[7609,-399],[1356,-639],[-2197,-2093],[2737,-375],[1121,1235],[8576,27],[5992,-2794],[1261,-4744],[-2226,-2581],[-8568,-4106],[-4558,-3720],[-2580,-434],[-3005,-1853],[-1335,-2793],[2138,1794],[3535,200],[5847,1772],[2813,1531],[-3098,-48],[1413,1747],[5238,-1828],[3524,-363],[-719,-1115],[6058,1441],[8645,-669],[-55,-2033],[3667,-1586],[9472,-142],[1282,1412],[-880,2012],[3009,1315],[6012,-2489],[1330,1261],[5159,-2117],[-805,-1749],[1810,-1125],[-2311,-1007],[2759,-1301],[-1324,-1398],[-3143,2100],[5440,-7788],[3876,-2235],[1124,940],[3033,6073],[2145,-2577],[3546,-617],[3283,1443],[5443,-2391],[762,2010],[3031,-719],[2152,277],[-955,3003],[1372,1252],[-2661,-274],[1180,1971],[4106,538],[-1031,1795],[3759,-1002],[4040,-134],[7603,-1516],[-5788,-1087],[6736,258],[-2457,-569],[264,-2725],[4048,3445],[6221,-970],[1054,-1902],[-2313,-280],[2813,-1688],[4226,-1327],[2574,-2681],[3570,269],[5836,1278],[5950,-333],[4697,-2309],[773,-2014],[-483,-3109],[2995,-1057],[347,-3011],[1472,-1143],[-79,2810],[2330,1597],[4955,416],[982,-653],[6586,-647],[2067,1424],[1449,-965],[425,-1812],[2799,-1137],[831,-1739],[2578,233],[1271,1302],[-1147,3188],[-1171,256],[905,2850],[5757,-825],[1994,-856],[7863,216],[2269,-1270],[5343,-1533],[3200,-2392]],[[999999,913406],[0,-23201]],[[999999,890205],[-1534,-1453],[-2578,-1298],[-2142,676],[-1582,1760],[-307,-1347],[-2798,1032],[1860,-1991],[1824,884],[126,-1953],[1446,-1316],[767,842],[1169,-2365],[396,-2517],[1497,-2075],[663,-2979],[-1249,-2174],[-3060,1343],[-2019,308],[-7159,-3859],[-3033,-1372],[-1366,-1833],[-765,369],[-1287,-2412],[-4957,-3714],[-715,-2781],[-1023,602],[-2100,3133],[-3025,-131],[-3260,-1581],[-1757,-2575],[-543,633],[600,2995],[-3521,-2288],[-182,-1409],[-1784,1169],[-1657,-100],[-1028,-1222],[-381,-3154],[-746,-1674],[-2397,-3393],[-504,-2194],[1408,-1841],[698,1066],[1377,-1536],[-1207,-1950],[65,-3236],[1259,-732],[221,-2698],[-801,-1113],[-1164,1112],[1139,1715],[-1527,-727],[-1121,-1834],[-988,-4334],[1045,-3589],[-1055,-1299],[-2647,50],[-1940,-2088],[-641,-2401],[504,-3875],[-1220,639],[-2714,-2157],[-404,-3368],[-1000,-2934],[-3766,-4979],[-567,2028],[-496,7096],[-739,2945],[-1330,11009],[-181,2867],[449,4287],[739,3691],[2072,2708],[690,1859],[-515,1670],[1830,304],[600,1306],[1512,33],[2295,2362],[2252,4166],[2798,2961],[2496,3111],[694,1588],[2693,2150],[2048,793],[-435,644],[1255,1886],[561,5617],[1087,1057],[2217,139],[-3169,1200],[-2567,-862],[-895,-4499],[-1713,-767],[-4518,-5384],[-1646,-680],[570,2293],[-1635,-408],[258,1985],[1206,2972],[-2125,-438],[-1321,1202],[-2796,-1000],[-1669,269],[-2193,-1887],[-139,-1232],[-2157,-2935],[-2451,-2372],[-1884,-3219],[-397,-1806],[2825,-997],[-19,-1008],[-1950,157],[-1242,-836],[-1806,825],[-1329,-1633],[-1338,517],[-2983,-896],[-572,1229],[3166,835],[-2152,1781],[-2274,191],[-2846,1268],[-2349,-1410],[325,-1479],[-1824,779],[-2064,-863],[-2715,1116],[-1682,-1532],[-1047,1274],[-6562,-256],[-3241,-2195],[-752,-1507],[-2972,-3159],[-661,-2361],[-4957,-5024],[-2696,-4895],[-4212,-4663],[-2536,-2423],[-14,-1255],[1651,-875],[2626,220],[-217,-4839],[1211,104],[-164,1818],[2052,-1077],[-1703,-2178],[1435,-111],[2308,1531],[352,2969],[1733,-752],[1077,498],[1777,-2752],[2931,-3724],[-614,-999],[-31,-3833],[875,-1126],[-328,-1526],[-1207,-1782],[-680,-2297],[-587,-4066],[114,-5627],[-963,-6354],[-2217,-3770],[-1031,-2986],[-1152,-1933],[-694,-3043],[-1809,-4295],[-2450,-3835],[-1837,-4040],[-743,-685],[-1087,-3191],[-979,-1832],[-3949,-4122],[-1526,-788],[-1253,1060],[-1126,44],[17,2549],[-1231,-1294],[-199,949],[-1768,-3728],[-1247,180],[-62,-2097]],[[863019,755336],[-639,-4],[-1947,-3493],[-132,-5065],[-3900,-4866],[-2046,-1504],[-482,-3402],[1088,-733],[1634,-2729]],[[856595,733540],[678,-2651],[1990,-5341],[385,-3155],[-196,-4087],[472,-9],[-428,-3275],[-569,-1872],[-1953,-479],[-186,-1366],[-1133,898],[-892,-399],[-229,-1566],[-855,-1345],[-216,1729],[-971,-1873],[-1017,-739],[-741,2127],[721,146],[-647,2703],[548,2920],[636,722],[-493,2354],[-145,3126],[-752,1049],[1001,881],[1057,-672],[-588,1703],[-312,3485]],[[851760,728554],[-733,572],[-704,-803],[-965,1436],[-1143,-1543],[-1026,1224],[718,743],[-1544,429],[1046,2533],[673,643],[-424,1222],[700,2469],[-134,1412],[-1627,1371],[-380,-847],[-768,2304]],[[845449,741719],[-712,-966],[-2983,-992],[-1936,-1821],[-1902,-2969],[-1351,-790],[-159,1121],[1593,1113],[385,1646],[-1508,-11],[671,2726],[788,626],[1316,3503],[-1155,1779],[-1901,351],[-1932,-3972],[-2466,-1945],[-1018,-2930],[-869,-1431],[-1706,-589],[-713,946],[-712,-547],[-630,-3017],[1269,-2617],[2569,-833],[415,-2027],[-379,-2189],[1381,-1223],[3612,4201],[2473,-2213],[1156,406],[1696,-747],[-1091,-3371],[-949,745],[-2619,-2142],[-858,-2545],[-1740,-1820],[-1737,-3316],[-634,-3276],[2779,-2505],[843,-4073],[1016,-3683],[-48,-2104],[1521,-1715],[7,-982],[1191,-1815],[95,-1163],[-2480,983],[-1260,1401],[-933,-828],[1475,104],[2626,-3934],[604,-2386],[-974,-450],[-1471,-1675],[-490,-1206],[-1032,196],[510,-1508],[1461,998],[1440,-1911],[1125,-644],[-1601,-2286],[1208,719],[-65,-2790],[-557,719],[-749,-741],[603,-715],[-527,-2187],[353,-1628],[-1399,-451],[-1420,-4205],[-132,-1555],[-725,-1311],[-534,-2520],[-568,-363],[-161,1398],[-518,-1334],[676,-1700],[-1162,-1656],[433,-303],[-73,-3765],[-1240,274],[-658,-2876],[-756,-553],[-213,-1512],[-1314,277],[-86,-2257],[-1190,-2425],[-448,22],[-2325,-2883],[-441,-2417],[-608,210],[-2093,-1555],[-840,583],[-950,-1188],[-562,821],[-271,-1341],[-800,71]],[[817405,638260],[69,-246]],[[817474,638014],[-64,-1208],[-701,1282]],[[816709,638088],[-1100,2071],[2,1576],[-803,-1277],[616,-1884],[65,-1758]],[[815489,636816],[-446,-704]],[[815043,636112],[-2304,-2379],[-1784,431],[-948,-1721],[-1628,-281],[-1249,-1763],[-434,735],[-640,-2841],[919,-2016],[-1078,-1508],[-920,2121],[-308,3020],[693,2068],[-1075,340],[-1284,-579],[-1671,2751],[126,-1382],[-1535,-968]],[[799923,632140],[-1563,-1322],[-682,-1991],[-1336,305],[194,-1571],[-654,-2643],[-1483,-2073],[-1006,-5763],[740,-2748],[1697,-3294],[-56,-1344],[1948,-4868],[1816,-3409],[543,51],[1791,-5021],[409,-626],[732,-3921],[607,-5093],[-88,-3419],[422,-1916],[59,-2111],[-628,274],[-56,-5457],[-589,-2301],[-461,-124],[-1526,-2258],[-2805,-3175],[-708,1553],[-291,-1645],[-1216,-501],[892,-1077],[-527,-1521],[-1275,2144],[1030,-2372],[-360,-1571],[-1519,2634],[782,-1938],[155,-1640],[-1854,-1799],[-1073,-2749],[-956,-187],[208,5975],[687,1747],[-180,986],[-1012,607],[-659,1430]],[[790072,566398],[-1359,1039],[-1124,107],[527,1691],[-527,1520],[-1054,-1380],[-77,3240],[-531,1458]],[[785927,574073],[-944,2940],[-150,-555],[-1405,2504],[-863,933],[-774,-418],[-1616,567],[276,4250],[-852,529],[-1773,-996],[201,-1822],[-350,-2106],[70,-3077],[-1005,-4194],[-390,-3396],[-895,-3376],[-11,-3470],[647,-3083],[917,596],[502,-1193],[156,-2617],[885,-2386],[484,-4894],[-822,1693],[738,-3201],[1651,-1937],[1334,26],[837,-2314],[837,-1377]],[[783612,541699],[665,-416],[540,-1834],[1244,-2000],[1204,-3997],[147,-2707],[-296,-3698],[254,-1472],[-39,-3481],[1035,-2089],[1129,-5081],[-117,-2121],[-1338,502],[-596,-711],[-342,1283],[-1750,1832],[-3976,6100],[12,2182],[-1624,4224],[-280,4064],[-728,5542],[-25,2349],[-623,2712]],[[778108,542882],[-1175,2576],[-276,2837],[-662,98],[-855,3055],[-1311,2704],[-438,-983],[-508,1450],[370,5139],[920,5332]],[[774173,565090],[-389,-921],[-272,3797],[586,1843],[183,3583],[-292,869],[167,2884],[-335,5549],[-918,3387],[-266,-509],[-137,3044],[-800,4132],[-283,6023],[-350,853],[137,2596],[-716,387],[-549,3193],[-578,1513],[-171,-1697],[-796,-2767],[-2387,-2339],[-1038,-2644],[-156,1839],[-1084,-1273],[-138,2159],[-643,-1649],[162,2929],[-1378,-2265],[1014,9200],[-439,3746],[-943,3836],[-129,2596],[-321,-2297],[-622,754],[-590,2030],[922,-776],[481,1199],[-1766,3658],[-1001,98],[180,1793],[-956,-486],[-1107,2940]],[[756455,627897],[-745,2269],[-133,3021],[-509,3223],[-957,3887],[-914,-1604],[-571,-101],[-974,3181],[-444,-2263],[429,-2924],[-997,-2539],[-443,340],[384,1596],[-710,-793],[-200,2161],[-194,-2394],[-1273,-1554],[-722,898],[-118,1306]],[[747364,635607],[1,-2601],[-852,-413],[-287,3185],[-158,-2739],[-1466,204],[387,2639],[-1439,-2880],[-1605,-905],[-669,-1564],[322,-3179],[-625,-2292],[-1308,-2333],[-1957,-1342],[-320,1202],[-825,-1629],[774,34],[-1863,-2969],[-1852,-4934],[-1250,-1320],[-1266,-2730],[-1681,-1985],[-865,-2002],[-64,-2229],[-1380,-1365],[-1322,45],[-854,-3428],[-923,809],[-980,-1091],[-667,-3773],[311,-2939],[-150,-2166],[642,-5041],[-315,-3976],[-1031,-4156],[-290,-2450],[264,-2242],[-29,-5179],[-1243,-99],[-1096,-3690],[-46,-2456],[-1550,-969],[-637,-1268],[-367,-3000],[-1507,-1814],[-1530,1949],[-1148,2935],[-637,3255],[-227,2814],[289,-30],[-1178,5107],[-552,3422],[-1464,4122],[-1184,6042],[-277,3497],[-801,4900],[-1203,3437],[-48,1909],[-1266,3894],[-385,2403],[-504,6884],[-793,6287],[375,2003],[-475,-270],[-98,3224],[-366,1844],[593,4338],[-187,3282],[-557,2042],[1136,1408],[-1331,-18],[436,1632],[-1093,1287],[-748,-2169],[602,-1730],[-663,-2224],[-2752,-2469],[-848,9],[-1645,2098],[-3107,6530],[-70,1117],[814,-592],[2502,1702],[731,2356],[-2155,-1252],[-1191,530],[-1653,2023],[-620,2260],[998,1663],[-1505,-1512],[-194,1543]],[[689347,646059],[-1380,-275],[-997,2156],[-383,3443],[-1301,622],[-13,2164],[-750,2068],[-2080,-1304],[-2509,-284],[-326,-730],[-1409,885],[-1653,117],[-182,-843],[-2552,260],[-715,-710],[-1588,19]],[[671509,653647],[-584,340]],[[670925,653987],[-336,-554],[-2079,1067],[-2911,718],[-1583,83],[-689,813],[-1344,156],[-2721,1248],[-640,3435],[-339,3164],[-470,1093],[-1269,654],[-1961,-1320],[-2095,-2493],[-697,-283],[-1105,1112],[-1504,172],[-697,1289],[-2120,2252],[-599,1737],[-1237,1231],[-1012,122],[-1076,1697],[-1120,5518],[-557,497],[-71,1620],[-1335,2969],[-271,1643],[-1435,-1005],[-1391,1647],[-1410,-2041]],[[634851,682228],[-1577,121]],[[633274,682349],[457,-2431],[-1161,-922],[906,-364],[1086,-4814]],[[634562,673818],[920,-3459],[65,-1391],[1223,-1372],[466,-1847],[1614,-2085],[552,-2512],[-426,-1742],[1462,-6068],[685,-1762]],[[641123,651580],[-116,3883],[668,3180],[720,1018],[780,-1486],[-161,-2238],[324,-2232],[-483,-2842],[-445,-362]],[[642410,650501],[117,-1580],[718,-322]],[[643245,648599],[938,-1782],[958,59],[1103,944],[3459,-460],[1399,1192],[972,3153],[976,1370],[1179,2705],[1163,1752],[386,1592]],[[655778,659124],[971,1567],[-367,-4008]],[[656382,656683],[251,-3978]],[[656633,652705],[701,-3015],[1609,-3244],[3773,-1654],[2659,-6310],[800,-412],[-65,-1712],[-1190,-4272],[-1321,-2287],[-1171,-4182],[-1032,968],[-669,-1932],[-408,-3776],[268,-3494],[-1764,-678],[-1448,-1868],[-290,-2497],[-779,-1274],[-2198,-637],[-622,-1527],[112,-1209],[-643,-2030],[-2766,-198],[-1273,-1454],[-1457,-661]],[[647459,603350],[-2105,-2103],[-427,-1994],[121,-1786],[-1705,-1888],[-2991,-1769],[-1000,-1109],[-2270,-1263],[-838,-1074],[-1055,-2407],[-1884,-13],[-1618,-2289],[-1719,-1162],[-3143,-751],[-1718,-3098],[-1169,8],[-721,-877],[-1190,-312],[-1263,1318],[-676,2536],[141,2209],[-538,2199],[-189,3222],[-844,6515],[340,2236],[-112,2013]],[[618886,601711],[-279,2163],[-876,2284],[-248,1852],[-1511,2670],[-1446,4696],[-315,2393],[-992,3988],[-1884,3025],[-1298,1491],[-1444,4696],[148,1236],[-442,2149],[300,3028],[-246,2235],[-1996,6760],[-1025,1625],[-1046,630],[-1006,3130],[-89,2791],[-1751,4821],[-747,2903],[-1857,4962],[-1113,3569],[-1567,673],[454,2126],[475,5014]],[[597085,678621],[63,1193]],[[597148,679814],[-192,-460]],[[596956,679354],[-467,-1225],[-935,-7432],[-499,-1492],[-1277,1679],[-1424,3081],[-477,2994],[-985,2658],[-432,2679],[-572,-2033],[570,-1448],[185,-2335],[740,-2530],[1803,-3952],[7,-1722],[954,-3306],[183,-2372],[1684,-5675],[1747,-7204],[1638,-3184],[-675,-101],[-50,-2833],[486,-2940],[1477,-1881],[1783,-3744]],[[602420,635036],[154,-2431],[791,-2373],[-51,-6311],[153,-3192],[619,-4513],[1252,-1565],[777,-1816],[1133,-1448]],[[607248,611387],[839,-3424],[642,-4135],[434,-4787],[1352,-4717],[217,2046],[945,-2703],[2701,-2333],[1339,-3775],[1630,-2343],[428,-2222],[1103,-2063],[890,-922]],[[619768,580009],[814,-3073],[-382,-1306],[-1314,-1363],[-721,-1393],[1033,487],[929,-514]],[[620127,572847],[1686,-4239],[1482,-2098],[1546,39],[2427,2365],[2079,-533],[2333,2536],[1706,-205],[1820,1086],[734,-381]],[[635940,571417],[3254,1605],[1895,2692],[1285,-906],[-474,-2933],[157,-4022],[677,-1601],[-1262,-302],[-292,-5376],[-1098,-3454],[-908,-3824],[-697,-1405],[-252,-1795],[-1146,-3964],[-832,-4840],[-1111,-4024],[-1153,-3209],[-719,-2700],[-3046,-7176],[-2299,-4802],[-3141,-3941],[-1632,-2482],[-2403,-4558],[-4133,-9448],[-1242,-4279]],[[615368,494673],[-405,-1018],[-1381,-926],[91,-1009],[-773,-2048],[-1172,-882],[-297,-3331],[-1735,-7274],[-747,-1268]],[[608949,476917],[-1118,-7022],[152,-2688],[1662,-3242],[205,-861],[-716,-2926],[424,-2925],[-381,-2561],[409,-2957],[528,-1478],[396,-4278],[1888,-3258]],[[612398,442721],[412,-1168],[-581,-3972],[358,-3985],[-123,-2888],[260,-850],[-99,-4901],[280,-6374],[478,422],[47,-1919],[-603,-1920],[-164,-2121],[-1250,-2997],[-734,-2703],[-1673,-2115],[-439,-1068],[-2609,-1600],[-2502,-2944],[-2548,-6240],[-1877,-2196],[-1954,-3844],[-534,-55],[-141,-3859],[771,-1973],[792,-5004],[321,-4761],[307,1954],[227,-4967],[-569,-4947],[476,-156],[-288,-2054],[-784,-2193],[-1524,-1658],[-3500,-2605],[-1542,-2272],[-561,-2131],[718,-1564],[295,1093],[-191,-4536]],[[591350,345650],[-976,-8001],[-692,-2499],[-1410,-1869],[-1230,-2613],[-2907,-9432],[-3980,-7845],[-2765,-4500],[-2175,-2769],[-1800,-1412],[-1222,286],[-976,-1776],[-1765,222],[-488,-1157],[-3449,1089],[-882,-569],[-1984,421],[-856,-350],[-1269,-1798],[-2024,47],[-1473,-583],[-1415,-1911],[-1071,192],[-1491,2389],[-741,-83],[-63,1516],[-1269,-476],[314,1781],[-566,2762],[-1139,3520],[1110,1039],[167,3138],[-278,2251],[-1482,4286],[-1356,5446],[-664,4126],[-1396,4656]],[[545687,335174],[-2024,3861],[-1048,3432],[-1038,6330],[-341,3509],[-22,4103],[-932,4925],[-77,5455],[-196,1855],[340,1573],[-567,3037],[-968,2502],[-1452,5041],[-784,4337],[-1972,7452],[-1007,2286],[-889,3195],[-91,4457]],[[532619,402524],[211,3230],[-189,5168],[603,1172],[868,5904],[750,7107],[964,2430],[238,1493],[1205,1513],[1023,4192],[173,4493],[-351,2493],[-505,1261],[-917,4251],[-585,3881],[1001,2138],[54,1881],[-1434,6741],[-108,1642],[-839,2159],[-608,2949],[2127,1349]],[[536300,469971],[-1824,-720],[-550,1349]],[[533926,470600],[-101,2571],[-441,1898]],[[533384,475069],[-669,2598],[-1798,3848]],[[530917,481515],[-2174,5350],[-1634,2931],[-1279,3647],[-730,3520],[785,-1915],[568,200],[-1274,1777],[-676,3495],[876,800],[445,1316],[14,3790],[1375,-1447],[568,893],[-1264,598],[-615,1518],[814,145],[-75,2698]],[[526641,510831],[-569,636],[1169,4669],[-17,2234]],[[527224,518370],[410,4588],[-1090,4260],[510,326],[-711,1263],[-162,-852],[-1181,1003],[-228,2738],[-955,-164],[-51,1357]],[[523766,532889],[-895,902],[165,-2073],[-2802,-59],[-753,-890],[-2602,-632],[-1358,2112],[-568,2855],[7,1616],[-1458,3700],[-1193,1909],[-849,372],[-3944,-250]],[[507516,542451],[-3010,-903]],[[504506,541548],[-1210,-755]],[[503296,540793],[-658,-1653],[-1917,-314],[-1691,-1520],[-1246,-1624],[-2336,-1456],[-1009,-1294],[-1103,989],[-1987,944]],[[491349,534865],[76,235]],[[491425,535100],[187,14]],[[491612,535114],[-606,1212],[-305,-1213],[-2146,1061],[230,-471],[-2396,-544],[-344,387],[-2473,-1142],[-2803,-2207],[-1728,-1701]],[[479041,530496],[-1983,1414],[-2426,2753],[-3179,6061],[-1413,1377],[-177,918],[-1229,1322],[-600,1294]],[[468034,545635],[-627,1078],[-2091,1764],[-68,1655],[-1030,1131],[-210,1711],[-533,410],[-400,4945]],[[463075,558329],[65,719],[-1077,2776],[-91,1710],[-2047,1899],[-970,4048],[-743,50]],[[458212,569531],[-31,1196],[-940,446],[-116,4303],[-1068,-1067],[-387,1162],[-877,110],[-124,981],[-1091,1251]],[[453578,577913],[-143,4202]],[[453435,582115],[-172,1641],[746,-225],[3107,1067],[-1937,-207],[-848,-564],[-338,1387]],[[453993,585214],[-1143,4834],[-540,1407],[-1021,678],[1080,989],[843,2204],[856,3225]],[[454068,598551],[-2,2657],[526,3789],[744,3670],[134,2026],[-151,3752],[-357,2856],[-836,2125],[642,2519],[202,2611],[-609,2515],[-535,-108],[-705,2678],[-478,-1659]],[[452643,627982],[108,3383]],[[452751,631365],[217,3098]],[[452968,634463],[1591,4114],[411,2982],[1124,3861],[-259,562],[2390,4173],[508,1913],[170,3155],[1057,5033],[2329,2852],[888,4144]],[[463177,667252],[223,1310]],[[463400,668562],[630,1531],[2675,1275],[1544,1497],[970,1965],[1651,2081],[1760,4409],[516,1778],[40,2004],[-618,1602],[185,4187],[1281,3920],[283,2880],[1804,3642],[819,1109],[2053,1575],[1837,1948],[1522,4781],[1190,5982],[1797,693],[-167,-933],[1390,-2749],[1410,-709],[1768,702],[1353,-242],[650,996],[368,-1656],[1723,-140]],[[493834,712690],[851,-59],[1603,1600],[1163,1802],[1365,1144],[1317,231],[1297,2140],[2062,1528],[3711,480],[1053,1089],[2241,662],[2719,1],[1852,-1309],[2290,1557],[660,874],[1225,-985],[768,1024],[1961,-1398],[1851,479]],[[523823,723550],[3088,2388],[1412,-797],[600,-2808],[1782,2018],[202,-1175],[-1670,-3263],[181,-2584],[1149,-1501],[322,-2332],[-1626,-4120],[-1306,-1974],[262,-2142],[1566,-1988],[1005,287],[328,-1859],[839,-398]],[[531957,701302],[2153,-1916],[1316,-341],[1472,673],[2649,-1382],[767,-1009],[1843,-710],[507,-1371],[381,-2980],[582,-1365],[1159,-959],[1829,-295],[1577,-789],[2336,-1802],[2073,-2885],[986,-14],[1172,1187],[1215,3497],[-624,4378],[542,2377],[1388,2141],[2819,2116],[1532,-113],[2509,-1775],[544,-2399],[1420,-326],[922,-886],[1540,40],[947,-786],[349,-1352]],[[569862,692256],[644,-843],[1419,641],[3763,-1440],[1999,-1662],[1520,-278],[1548,-1304],[3675,3716],[1685,31],[140,763],[1312,-790],[1013,493],[-328,-1475],[919,-1183],[616,967],[777,-1110],[3609,665],[821,839]],[[594994,690286],[776,1554]],[[595770,691840],[558,1842],[1195,7038]],[[597523,700720],[1398,5619],[100,1280],[912,2257]],[[599933,709876],[-92,3523],[-496,2060],[356,2044]],[[599701,717503],[-227,2330],[1049,2068],[-388,1491],[-1421,-1858],[-2600,1111],[-2518,-3569],[-2500,-866],[-1158,875],[-989,2084],[-1859,1574],[-1968,383],[-446,-3290],[-2207,-910],[-1516,1425],[-292,1755],[-2040,702],[-1800,-814],[1629,2100],[-2481,-56],[517,855],[-878,1334],[-392,1769],[429,1724],[-2616,1769],[619,2087],[226,-1250],[1399,-17],[-930,1741],[694,1050],[-921,2402],[403,1604],[-1983,-566],[189,3096],[1547,2430],[1518,328],[530,-803],[4388,617],[-270,1223],[2464,637],[-1334,422],[-887,1174],[285,1265],[2143,-416],[1182,273],[1292,-664],[1236,135],[564,1259],[2357,2426],[2985,1706],[3804,-360],[711,631],[809,-1983],[2094,-273],[354,-1516],[918,-972],[745,598],[801,-1061],[3652,-1540],[2904,1078],[2330,-859],[1929,1482],[1529,1812]],[[615305,750685],[703,2681],[-762,4084],[-1812,2395],[-2384,2111]],[[611050,761956],[-3503,5143],[-1489,780],[-916,1654],[-1222,217],[-574,1401],[-1539,916],[807,967],[1961,517],[61,1641],[959,2333],[1327,253],[-2016,3233],[2041,163],[-174,885],[2375,1733],[-272,967],[-2726,-1051]],[[606150,783708],[-1863,-100],[-566,-935],[-2945,-1529],[-1257,-203],[-1519,-2044],[-138,955],[-1058,-1485],[481,-2897],[1487,-2311],[1701,843],[1124,-353],[-505,-1944],[-1453,-356],[-1105,552],[-1069,-1754],[-1030,27],[-2241,-2485],[-1276,983],[290,3224],[-1768,1484],[-1141,330],[3214,3218],[-1285,1355],[-2015,-546],[-1936,1428],[2217,1723],[-2905,291],[-2044,-667],[-1604,-4060],[-1715,-1092],[290,-2503]],[[582516,772857],[-412,-2468],[-1415,-508],[-1,996],[-1118,-3732],[-167,-3279]],[[579403,763866],[-333,-2091],[-921,37],[-569,-1241],[-112,-2585],[-1122,-1669],[1471,-2956]],[[577817,753361],[922,-2978],[1975,-1402],[-769,-1514],[-1690,631],[-1868,-637],[-671,-1694],[-1350,-1121],[-1581,-2504],[142,1418],[1495,1848],[-1907,-91],[-185,684]],[[572330,746001],[-2596,1587],[-865,-812],[-878,534],[-1096,-1325],[-888,141],[289,-1951],[-562,-1154],[-979,-43],[-1896,1653],[-103,-2717],[1934,-4432],[-1249,-179],[352,-1349],[-1025,-832],[1823,-1358],[1983,-2288],[246,-3350],[-1319,1783],[-1512,-783],[1258,-2596],[-910,-630],[-1211,1234],[748,-3118],[-33,-2888],[-564,1527],[-1122,-499],[-821,1937],[-522,-1728],[-860,2036],[-32,2726],[-1204,1855],[738,2029],[1170,779],[3043,-2191],[635,1290],[-2020,1555],[-2637,-694],[-998,375],[-926,3696],[-1330,1888],[-832,2265]],[[555559,739974],[-416,1979],[-1020,986],[-388,2442],[323,1843],[-57,2912],[380,2149],[-653,484]],[[553728,752769],[-2291,3340]],[[551437,756109],[-2361,2750]],[[549076,758859],[-229,244]],[[548847,759103],[-1894,2690],[-1415,895],[-1134,-140],[-2396,4366],[966,90],[-1599,2575],[-113,2217],[-1505,1523],[-964,-2975],[-934,1614],[-143,2422]],[[537716,774380],[394,419]],[[538110,774799],[-254,1086],[-3761,-1925],[-135,-1212],[827,-1620],[-764,-1455],[411,-2954],[819,-1357],[2425,-2509],[1239,-5224],[2377,-3774],[841,-702],[2209,32],[520,-1072],[-385,-1914],[3030,-2211],[2365,-2411],[1475,-3261],[-395,-1679],[-738,685],[-591,2033],[-2603,1054],[-1106,-3545],[188,-1308],[1436,-1530],[167,-2267],[-1710,-1678],[-38,-1811],[-1357,-2768],[-923,-16],[688,4582],[624,276],[-481,3522],[-920,3771],[-2060,1474],[-515,2544],[-1842,941],[-1025,2420],[-1791,48],[-1272,1338],[-2760,4846],[-947,804],[-1633,3039],[-1835,6419],[-2107,1774],[-1454,611],[-1901,-2982],[-1634,-900]],[[520814,764013],[-644,-421]],[[520170,763592],[-2132,-3121],[-1050,-574],[-1970,925],[-964,1280],[-1197,-341],[-1600,1220],[-2205,-2369],[-575,-1646],[443,-2868]],[[508920,756098],[102,-2884],[-3237,-3892],[-2916,-1335],[-3078,-7027],[-701,-2109],[340,-2709],[1129,-1798],[-1619,-1917],[-737,-1681],[-487,-3383],[-1404,-117],[-1306,-1945],[-1083,-2887],[-6054,-162],[-853,-1254],[-1382,-490],[-1261,-2357],[-1154,963],[-1254,4539],[-1089,1420],[-1449,-88]],[[479427,724985],[-1189,-1029],[-2121,685],[-1111,-528],[510,2361],[-186,6019],[-923,8],[215,1746],[-939,-71],[276,3599],[629,1210],[726,3773],[642,5036],[-618,4755],[280,646]],[[475618,753195],[240,1973],[-656,3107],[-856,1057],[1004,2118],[1736,621],[-23,833],[1553,1093],[1211,-1006],[4434,-72],[3174,-988],[2552,615],[1552,-876],[474,491],[1495,-749],[1507,470]],[[495015,761882],[860,927],[664,5901],[458,5762],[873,-1292],[-736,2528],[-319,3379],[-1778,1205],[-1140,3840],[615,875],[-1466,8],[210,941],[-4092,2172],[-1143,-86],[-1018,1283],[970,772],[-1087,2192],[4136,1783],[1499,-1801],[684,661],[2971,25],[-525,906],[-49,2351],[-759,2852],[1660,-21],[334,-1732],[2708,-540],[1613,898],[-640,1509],[2941,1748],[965,1505],[-38,2885],[926,1491],[1701,631]],[[507013,807440],[2292,1662]],[[509305,809102],[2434,52]],[[511739,809154],[-2159,914],[2038,411],[-655,1187],[1488,2953],[544,2967],[3844,3539],[2094,202],[1059,-942]],[[519992,820385],[243,2365],[2012,54],[1342,-1044],[352,2136],[998,304],[-73,3208],[-750,1921]],[[524116,829329],[-57,1149]],[[524059,830478],[-126,2562],[-1344,1075],[88,5966],[1409,-658],[280,1359],[-1356,754],[930,1534],[2599,718],[1134,2065],[1586,915],[-26,-2916],[-670,-3689],[1564,-587],[29,-1339],[-1493,-489],[-377,-2060],[-1645,-2204],[-381,-2688],[699,-660]],[[526959,830136],[112,-716]],[[527071,829420],[1105,-1889],[1633,-1020],[783,373],[-265,-2274],[1338,-301],[1977,1326],[1289,1771],[1259,-333],[1165,-1601],[767,73],[393,-1776],[1068,-720]],[[539583,823049],[660,-355]],[[540243,822694],[-380,1107]],[[539863,823801],[-496,92]],[[539367,823893],[109,450]],[[539476,824343],[5485,2015],[1038,1561],[1950,1041],[2949,643],[962,-2413],[851,-485],[1745,652]],[[554456,827357],[1028,2738],[1516,437],[1054,1728]],[[558054,832260],[-112,-611]],[[557942,831649],[-735,-1191],[1650,-280],[95,1022]],[[558952,831200],[37,968]],[[558989,832168],[-528,4734]],[[558461,836902],[70,4464],[1826,4428],[2294,908],[2035,-3759],[1789,-482],[1311,1874],[-224,3233]],[[567562,847568],[574,2866],[-2116,39],[-932,3317],[174,1629],[2461,1641],[2954,287],[182,699],[4070,-1117],[2883,200]],[[577812,857129],[4,1425],[2592,616],[556,1013],[2709,-748],[-1115,1906],[-1811,-23],[-1183,1090],[-362,1789],[-1987,-836]],[[577215,863361],[-1544,15],[-4404,-1218],[-3649,-1723],[-2442,-333],[-1657,1361],[-1123,-1106],[339,2081],[-3191,1279],[-210,2198],[682,3698],[-972,2359],[-423,3752],[1520,2466],[-294,978],[2152,629],[590,1999],[1990,1471],[2860,3668],[777,1693],[2028,351],[166,3667],[-3312,1931]],[[567098,894577],[-2925,-414],[-2045,636],[-312,-1452],[-1912,-1123],[42,-1465],[-1229,-2086],[1059,-2047],[-2102,-3527],[-3913,-2313],[-2077,-1772],[-398,-1673],[-1577,-387],[394,-1363],[-1296,-886],[-1222,-5185],[334,-5184],[1958,-658],[2309,-3023],[509,-1909],[-2795,-2357]],[[549900,856389],[-432,1148],[-1213,-340],[-2217,687],[-1193,-972],[2137,-11],[1086,-1029],[1875,337]],[[636756,779239],[-2981,-3555],[-1749,-1226],[-1238,-4225],[-913,-950],[1684,-3929],[410,-2866],[-127,-2813],[3082,-7052]],[[634924,752623],[1483,-3216],[333,-1632],[1526,-2620],[596,-43],[1043,-1761],[-1242,219],[-1021,-725],[-977,-6644],[-517,364],[-452,-1888],[50,-2251]],[[635746,732426],[589,-4549],[1081,-1013],[1835,-530],[1118,-2331],[1626,-1606],[1788,-759],[1189,43],[3289,1463],[1655,-299],[-155,3112]],[[649761,725957],[-252,3462],[172,5546],[-502,2047],[-1522,329],[219,2035],[1021,-365],[-322,2147],[-1636,19],[-457,2880],[583,3788],[560,-1262],[1306,-39],[412,-905],[1705,163],[924,1173],[-328,1791],[-1381,1931],[-690,3387],[-1895,16],[-540,-697],[-300,-4539],[-1022,3379]],[[645816,752243],[-89,1897],[374,3908],[-1766,535],[-958,1824],[-890,93],[17,1826],[-1308,4208],[-1388,787],[-93,1517],[1563,280],[1898,-579],[-1349,1662],[-49,1000],[1043,2237],[3098,241],[1859,-395],[-1185,1427],[1004,3666],[97,2829],[-706,1690],[-1202,215],[-1105,-895],[-2520,1603],[-2108,-1367],[-1165,-1452],[-1812,-682],[-320,-1079]],[[579890,406773],[-220,141]],[[579670,406914],[-1105,-546],[-671,-1286],[-690,-10],[-2147,-6749]],[[575057,398323],[576,1214]],[[575633,399537],[498,1027]],[[576131,400564],[826,2210],[951,844],[287,1338],[1698,181],[628,1169],[-631,467]],[[596829,424051],[8,920]],[[596837,424971],[-448,7467],[713,2757]],[[597102,435195],[-55,1606]],[[597047,436801],[-915,2270],[165,1707],[-397,4516],[-565,1769],[-903,1399],[-117,-964]],[[594315,447498],[86,-2168],[765,-2509],[-181,-705],[342,-6592],[-719,-2188],[41,-1803],[711,-3505],[-25,-2578],[835,-2199],[-59,-2417],[712,855],[1187,-2077],[-633,3686],[-548,753]],[[583319,439012],[-188,1258],[-1007,-1653],[65,-1400],[914,550],[216,1245]],[[590316,457091],[-1354,2401],[-631,-116],[447,-1971],[1538,-314]],[[581104,494978],[-351,228],[-450,-2385],[-164,-2606]],[[580139,490215],[167,89]],[[580306,490304],[1242,2470],[-300,1659]],[[581248,494433],[-144,545]],[[594373,505981],[-82,25]],[[594291,506006],[-1283,3],[-822,1245],[-139,-1043],[-2578,-1442],[-719,-834],[122,-1120],[-754,-2779],[125,-863]],[[588243,499173],[42,-439]],[[588285,498734],[229,-485],[-558,-4978],[429,-5130],[1171,3050],[1584,-1374],[713,620],[968,-717],[1092,2009],[-1480,404],[689,756],[-629,442],[819,952],[209,1512],[615,-40],[118,1925]],[[594254,497680],[365,913]],[[594619,498593],[45,2690],[383,818],[1656,983],[-266,1090],[-1202,-1532],[-841,2220],[-21,1119]],[[550098,499048],[149,1643],[-772,44],[623,-1687]],[[582340,501712],[626,1212]],[[582966,502924],[-236,1073]],[[582730,503997],[-387,11]],[[582343,504008],[-409,-359],[-361,-2723],[767,786]],[[580912,453401],[-726,882],[-1388,-3483],[-94,-2068],[1006,317],[1202,4352]],[[586662,453459],[-41,293]],[[586621,453752],[-1610,6282],[-212,3670],[-1086,2711],[-477,-209],[-675,1517],[627,2047],[-544,2923],[168,1754],[-569,1196],[94,2477]],[[582337,478120],[31,410]],[[582368,478530],[-801,3341],[-183,2968]],[[581384,484839],[-285,99]],[[581099,484938],[-369,-5798],[532,1280],[-362,-2874],[-21,-3186],[709,-2918],[-506,-1768],[893,-4769],[1766,-3432],[410,-3397],[794,-1827]],[[584945,456249],[-19,-482]],[[584926,455767],[-303,-1397],[928,-402],[684,-1335],[427,826]],[[593018,514450],[-202,523],[-1153,-1089],[-1262,-151],[1988,-1771],[-373,1661],[1002,827]],[[586953,517904],[-123,-430]],[[586830,517474],[-2165,-4070],[-5,-1348]],[[584660,512056],[322,-1170],[1043,2878],[1196,2183],[-268,1957]],[[605708,543686],[-517,-515],[-73,-2474],[733,2227],[-143,762]],[[500755,544260],[92,2515],[-132,4775],[-564,-668],[-1498,2472],[-1767,4265],[904,-3637],[1675,-2135],[-283,-1706],[850,-738],[478,-1638],[-200,-2189],[-850,-921],[-1222,72],[2049,-2460],[468,1993]],[[601901,519752],[-90,1900],[-625,906],[-572,2734],[-131,6867],[-621,-468],[-287,-4607],[330,-2413],[1272,-3754],[308,-1698],[416,533]],[[513014,566127],[-597,1548],[-7,-3130],[530,-1266],[74,2848]],[[790472,578337],[-362,1765],[-571,235],[-705,2460],[-782,165],[298,-1542],[1254,-2269],[868,-814]],[[540322,581615],[423,1277],[-906,-197],[-187,-1535],[670,455]],[[603570,574593],[283,-961],[568,2872],[-365,1103],[-1012,-223],[-261,-2127],[787,-664]],[[785773,614477],[-978,844],[245,-1141],[733,297]],[[589539,639047],[271,-551],[1877,3887],[-268,2056],[-1140,-4893],[-926,179],[-1993,-3425],[-939,-3194],[577,837],[905,2959],[1140,2091],[496,54]],[[752585,676072],[-1025,1705],[79,-2453],[946,748]],[[824182,677333],[-1536,690],[124,2955],[-646,-2607],[368,-1217],[1202,-1253],[488,1432]],[[835004,688893],[-392,-39],[-672,2672],[-642,-830],[-27,-1781],[1157,-714],[576,692]],[[750960,685556],[1857,1866],[-196,502],[-1819,-456],[158,-1912]],[[632245,686535],[-2077,1346],[-107,-1133],[2184,-213]],[[738577,687953],[-1352,630],[-67,-767],[1247,-724],[172,861]],[[748104,693080],[-970,734],[-1117,-439],[1587,-1436],[500,1141]],[[831685,699001],[-162,1684],[-748,-1323],[910,-361]],[[749194,703001],[333,-1261],[388,1327],[-721,-66]],[[621884,698374],[-912,2321],[-189,-2590],[862,-635],[239,904]],[[741047,689360],[-98,1041],[-960,-2790],[1058,1749]],[[731194,691121],[-361,1115],[-731,-517],[1092,-598]],[[827614,691459],[-1739,1048],[424,-1368],[1315,320]],[[770743,711929],[-1115,128],[456,-1016],[659,888]],[[771908,711114],[-346,1481],[-599,-1257],[945,-224]],[[779868,722801],[-1328,2011],[-885,400],[-850,-1816],[857,-1378],[2071,-529],[135,1312]],[[626850,728317],[-640,381],[-219,2714],[-698,0],[719,-6148],[475,-543],[895,1727],[-532,1869]],[[619412,731894],[916,848],[-73,1920],[-2128,-401],[-480,-1454],[1470,-136],[295,-777]],[[592937,734903],[-423,1618],[-409,-2100],[1042,-1478],[390,1123],[-600,837]],[[750778,745730],[-534,277],[670,-3123],[-136,2846]],[[624971,744746],[897,-2169],[941,273],[-774,1684],[-1064,212]],[[688863,749129],[401,-1223],[890,346],[-1291,877]],[[829457,701243],[710,1093],[-1035,1086],[-870,-1953],[24,-1928],[1171,1702]],[[620169,704535],[573,1320],[-1115,1822],[542,-3142]],[[868915,771622],[-4,576]],[[868911,772198],[-637,875],[-1411,-56]],[[866863,773017],[-184,-432]],[[866679,772585],[179,-2794],[823,-1261],[1234,3092]],[[728146,775844],[-613,3476],[-1063,599],[-286,-2349],[1962,-1726]],[[719418,781676],[-2373,-910],[-4556,241],[-1862,1081],[-1109,-477],[-2226,87],[-1475,-1921],[-313,-1431],[-1495,-3136],[1878,-3895],[-53,3183],[525,674],[-32,2094],[1371,615],[324,1592],[1433,1380],[576,-606],[1742,282],[2404,-597],[904,269],[1633,-929],[2424,160],[922,2078],[-642,166]],[[598330,772830],[-1552,3481],[-238,1599],[-2940,519],[1851,-1185],[1771,-2191],[248,-1615],[860,-608]],[[519093,779803],[-793,572],[-699,-995],[1492,423]],[[598060,786080],[-477,2063],[-370,-1496],[-2947,-274],[-691,-4032],[1396,3312],[1118,595],[1023,-674],[948,506]],[[527029,786288],[-240,234]],[[526789,786522],[-1282,617]],[[525507,787139],[1032,-944]],[[526539,786195],[490,93]],[[825635,792558],[1620,3199],[-490,1285],[-1798,-2969],[668,-1515]],[[757740,790146],[-2251,-998],[622,-1428],[1629,2426]],[[734765,788675],[-2596,2000],[-262,2671],[340,1138],[1998,1271],[-1664,2431],[446,1097],[-1459,-559],[2041,-2300],[-1745,-2249],[-101,-3485],[-1101,-120],[3342,-3218],[761,1323]],[[760553,795780],[-1500,966],[-1096,-395],[1480,-1392],[1116,821]],[[592130,795130],[-1395,3067],[-92,-920],[-2031,776],[2414,-2351],[1104,-572]],[[662809,774782],[-63,-283]],[[662746,774499],[-880,-3290],[-81,-2711],[618,-374],[836,1696],[-121,4023]],[[663118,773843],[104,375]],[[663222,774218],[640,1702],[1079,-1224],[186,-2401]],[[665127,772295],[-179,-732]],[[664948,771563],[-498,-1754],[1691,-3336],[620,1333],[58,2571]],[[666819,770377],[23,2136]],[[666842,772513],[-175,2227],[-643,9],[218,1988],[1582,-394],[2240,2499],[162,813],[-1538,1592],[-1140,68],[-663,-1693],[1804,3],[-214,-1962],[-2400,103],[-1211,-2325],[-46,1648],[-1706,-751],[-303,-1556]],[[742708,752661],[-166,982],[-1185,542],[-376,-1675],[1727,151]],[[703585,752117],[-1682,832],[-17,-1401],[1699,569]],[[660075,754430],[-215,659]],[[659860,755089],[-770,252],[-497,-1984]],[[658593,753357],[-112,-1155]],[[658481,752202],[812,-1186],[1311,750],[-529,2664]],[[553592,755111],[-214,-628]],[[553378,754483],[495,-417]],[[553873,754066],[-86,1194]],[[553787,755260],[-195,-149]],[[717506,757030],[-149,861],[-2533,-379],[-3085,-1239],[102,-788],[1829,-813],[1647,-51],[2189,2409]],[[516297,816124],[-1241,1845],[-1057,-3142],[1265,-546],[1033,1843]],[[703851,819125],[51,1726],[-1161,44],[1110,-1770]],[[634409,825632],[1183,-1358],[145,-1724],[943,-1659],[1879,699],[-2208,287],[365,2372],[1415,1722],[-1964,-1244],[-1308,1336],[874,1313],[401,3009],[1077,272],[1377,1644],[3786,1378],[-3237,-375],[-2032,-734],[-1385,-1939],[-318,-1994],[-914,-462],[-79,-2543]],[[554900,827208],[1780,1468],[-1158,167],[-1782,-2619],[1160,984]],[[787591,822480],[-1194,3030],[438,1154],[-296,2720],[582,2473],[-947,5896],[-1558,234],[-2102,-1663],[761,-2664],[18,2978],[1444,772],[1153,-552],[976,-4508],[-665,-3079],[514,-1751],[-596,-2483],[754,-2311],[277,-3377],[441,3131]],[[713185,829568],[-35,-687],[2794,-326],[857,1577],[-1329,746],[-2287,-1310]],[[607446,848975],[857,2297],[-657,304],[-3077,3613],[-399,-1849],[1374,100],[216,-1895],[-2564,1819],[3079,-4609],[1171,220]],[[587585,849699],[-646,1460],[-1172,-1555],[1818,95]],[[576897,854139],[-443,162]],[[576454,854301],[-1548,-1193],[1826,-4688]],[[576732,848420],[152,25]],[[576884,848445],[395,2763],[-382,2931]],[[541480,852979],[-1184,-1323],[-1071,-3768],[344,-681],[1911,5772]],[[545566,856089],[-2918,-151],[1284,-796],[1634,947]],[[538995,854750],[20,1368],[-2508,170],[280,-2440],[-1181,897],[-539,-4145],[1156,1315],[2108,700],[664,2135]],[[574388,886199],[1329,-1605],[1852,1547],[-3181,58]],[[808666,875704],[-89,-3371],[864,42],[70,3529],[-604,2084],[1769,920],[-988,-1704],[1908,-641],[736,1839],[-1092,1421],[-2668,-1308],[-1701,923],[-963,1693],[843,-3241],[1968,-830],[-53,-1356]],[[601163,868433],[-2027,3839],[141,2843],[-3354,2343],[246,-1205],[2030,-1123],[560,-1699],[-1394,-144],[-1142,1644],[-29,-4945],[2298,-1770],[1032,-2898],[1572,2012],[67,1103]],[[575103,870033],[-456,1418],[-1619,-283],[2075,-1135]],[[565308,868591],[687,2172],[1572,1448],[-398,753],[-1313,-1732],[-548,-2641]],[[574860,877079],[-457,-1440],[-2353,-868],[458,2157],[-1308,-1179],[806,-1433],[-2040,-5057],[865,-1794],[573,2204],[-180,2069],[654,1513],[3076,2356],[-94,1472]],[[581993,872761],[1135,1841],[-1847,695],[192,-2281],[-3509,461],[-859,2011],[1692,22],[-591,2050],[-2474,1261],[823,-1749],[-36,-2598],[978,-1509],[2521,-1952],[-1514,-494],[-577,-1386],[-660,1288],[-1517,-2391],[2228,-540],[-349,-980],[2557,1064],[-381,2511],[1023,-550],[1885,1843],[-720,1383]],[[597257,880953],[-1849,2047],[1683,-3970],[166,1923]],[[592892,880587],[-374,-949],[2109,-325],[-1735,1274]],[[583315,879692],[-2504,1663],[836,-2316],[1169,-221],[1433,-1873],[-934,2747]],[[590325,892960],[-1483,2117],[-1322,-558],[2805,-1559]],[[550002,895031],[-92,1328],[-1727,-154],[1819,-1174]],[[580190,897575],[-2219,-1640],[1544,169],[675,1471]],[[755379,910475],[-7289,-911],[1729,-560],[6655,1098],[-1095,373]],[[744889,916455],[-2083,2995],[881,-2910],[1202,-85]],[[780618,942300],[1406,549],[-1870,3023],[3704,-1323],[-412,1551],[8865,1674],[-2125,1828],[-648,-1651],[-2725,-820],[-4020,-206],[-4747,1302],[1105,-1681],[-2744,-1047],[3870,-856],[341,-2343]],[[579019,915436],[-3570,-2388],[840,-960],[2478,660],[252,2688]],[[748524,915745],[1750,953],[4056,-718],[-3761,1228],[-3777,-1505],[1732,42]],[[589764,898704],[-271,1975],[-1256,137],[1527,-2112]],[[584338,890689],[964,-760],[2410,692],[-3374,68]],[[603422,861159],[2045,-467],[-643,1444],[-1402,-977]],[[582879,867448],[1589,-1766],[1945,-4630],[961,-1418],[679,1946],[2225,-351],[1233,2762],[-1159,2814],[-2642,1894],[-2011,2000],[-2820,-3251]],[[779963,809216],[-1131,409],[-518,-5760],[697,440],[952,4911]],[[756434,802727],[791,-1756],[2112,1637],[-1302,2231],[-1826,-1453],[225,-659]],[[692465,805016],[-1413,-1035],[-246,-1466],[1570,-68],[-579,1015],[668,1554]],[[805177,833755],[-802,1378],[-1061,-1398],[-62,-2025],[-3079,-7966],[-1466,-2430],[-1286,-1172],[-1723,-3464],[-1088,-858],[-2485,-3589],[-3818,-1032],[1665,-1374],[1211,-59],[2699,1251],[1080,1513],[354,2173],[4538,2709],[1981,3088],[750,253],[-264,2743],[1133,362],[866,1690],[-183,1343],[1040,6864]],[[292109,640353],[-1063,1606],[183,1013],[880,-2619]],[[752159,635584],[-729,-143],[459,1554],[-494,3009],[472,-131],[544,-1945],[-252,-2344]],[[284284,648382],[233,-3023],[-549,79],[-635,2768],[951,176]],[[856273,662620],[-854,-1163],[-828,-2143],[211,2010],[1460,2654],[11,-1358]],[[285484,658185],[54,3465],[-910,2455],[1298,-2211],[-442,-3709]],[[281964,663072],[1525,90],[-2020,-1408],[495,1318]],[[656077,664210],[-649,-1303],[-1687,-315],[2408,2105],[-72,-487]],[[836135,638730],[-470,-4119],[-718,2555],[-710,1103],[-704,3598],[241,3313],[1308,4559],[1141,3284],[1536,1437],[932,-1787],[-805,-4998],[-450,-4183],[-505,-2710],[-796,-2052]],[[284045,651095],[-4,-1445],[-833,-1043],[-1084,2015],[766,2339],[512,370],[643,-2236]],[[450460,673524],[-463,1570],[712,168],[-249,-1738]],[[188325,676558],[-210,-1398],[-654,463],[122,1897],[742,-962]],[[185678,676836],[-1180,2138],[-19,947],[1070,-1606],[129,-1479]],[[838507,691292],[-1462,897],[355,668],[1107,-1565]],[[634098,680225],[-539,1034],[287,1066],[252,-2100]],[[457220,671475],[32,-1616],[-892,-536],[76,2191],[784,-39]],[[859589,671840],[-799,241],[1458,1587],[-659,-1828]],[[454626,672853],[-899,-2209],[-687,1970],[2184,1117],[-598,-878]],[[460564,671605],[674,3389],[352,-926],[-279,-1965],[-747,-498]],[[577340,717578],[-351,1436],[1432,1552],[-400,-2182],[-681,-806]],[[594456,712459],[-2952,-2899],[-1368,910],[-367,1326],[1099,1290]],[[590868,713086],[636,1300],[1436,-323],[2790,1526],[-1447,-1786],[173,-1344]],[[874812,707856],[-735,35],[935,1481],[-200,-1516]],[[566256,715245],[1279,-1017],[1135,362],[2095,-703],[748,-969],[1175,429],[212,-1009],[-4012,-653],[-253,916],[-2292,930],[-934,1004],[847,710]],[[543268,731152],[-1325,-4513],[541,-2638],[-505,-1929],[-1697,657],[-997,1807],[-659,-18],[-4083,4261],[830,2152],[467,-878],[715,921],[1449,-1123],[2290,265],[944,747],[2030,289]],[[884288,728792],[-272,1022],[715,1904],[198,-1484],[-641,-1442]],[[557256,732117],[414,-1853],[-980,1577],[566,276]],[[572483,731138],[-689,2109],[873,-197],[-184,-1912]],[[300269,747979],[-3587,-2408]],[[296682,745571],[-2327,-92]],[[294355,745479],[1273,1664],[4641,836]],[[526334,758316],[195,-4003],[-1012,-4413],[-1052,1205],[-672,4558],[409,1138],[2132,1515]],[[508736,740452],[844,-171],[-1045,-2725],[-1949,1847],[2188,2117],[-38,-1068]],[[555771,738334],[-1191,1739],[527,556],[664,-2295]],[[565042,735526],[1899,-1704],[491,-2672],[-1436,1073],[-1407,2370],[-1061,411],[1514,522]],[[573361,737722],[483,-1765],[-1176,-33],[-879,1032],[1572,766]],[[570660,741596],[-224,-1038],[-831,1135],[1055,-97]],[[511926,740758],[-1253,797],[1065,332],[188,-1129]],[[526755,746921],[481,-2266],[-523,-6785],[-363,-1273],[-1194,591],[-486,-1933],[-647,81],[-640,1653],[359,3754],[-211,2686],[-782,2127],[96,1550],[965,-374],[1824,2411],[1121,-2222]],[[864373,703794],[1134,295],[355,-889],[-481,-1352],[998,-116],[222,-2433],[-679,-1487],[-1097,-7039],[-1811,-2308],[289,1504],[-373,2662],[-184,-3198],[-1078,671],[337,1834],[-355,2899],[1238,3131],[-717,2804],[-708,73],[133,-1502],[-967,-746],[-685,3027],[222,762],[1957,1597],[331,1186],[1304,221],[615,-1596]],[[452247,699446],[1382,-656],[-1327,-214],[-55,870]],[[850907,701549],[29,1403],[1566,324],[-78,-1031],[-1517,-696]],[[873214,707667],[778,-176],[282,-2406],[-1005,-1257],[-543,-2139],[-1527,1562],[-961,-894],[-858,-3069],[-931,-471],[-408,913],[-229,3044],[-909,-535],[1549,2073],[392,1792],[971,-386],[1245,532],[344,1305],[1027,717],[783,-605]],[[892303,749827],[628,190],[20,-4701],[929,-1896],[502,-2646],[-214,-4344],[-674,-808],[-530,-3381],[-997,-393],[-501,-2300],[301,-2858],[-190,-2756],[-946,-2958],[-16,-2628],[701,-1980],[-1157,-1272],[-114,-1441],[-1378,-2177],[-261,2353],[722,1444],[-699,697],[-552,-3057],[-1074,805],[-454,-2600],[-690,-1303],[-97,2107],[-626,661],[-1078,-2904],[-1794,403],[-1338,-483],[592,1124],[-1084,198],[-91,1503],[-886,-2259],[891,-2099],[-1455,-872],[-1149,-3644],[-1287,-50],[-771,2042],[-209,2313],[791,1256],[-1791,1569],[-1477,-400],[-667,-1010],[-2294,-1332],[-2512,-447],[-255,-2300],[-1127,1264],[-2282,-452],[237,2469],[971,122],[1395,1856],[2964,4650],[1259,-311],[2327,477],[2667,1231],[424,-1313],[981,-132],[1153,1567],[-247,1321],[1922,4452],[404,3792],[1331,829],[-1107,-2074],[257,-1984],[905,-396],[477,1074],[2238,1581],[1570,3706],[1432,1769],[1766,7515],[51,2004],[-679,761],[567,2590],[-254,1680],[914,1246],[372,2494],[671,-204],[143,-1790],[1221,-65],[226,2141],[-1109,-621],[379,2173],[812,-788]],[[877937,714367],[-468,-2295],[903,1565],[-435,730]],[[328330,795571],[-1162,-87]],[[327168,795484],[-3740,1896]],[[323428,797380],[-886,1532]],[[322542,798912],[-1542,1007],[857,675]],[[321857,800594],[3536,-1399],[2892,-2500]],[[328285,796695],[45,-1124]],[[915797,775121],[-60,1171],[1897,2214],[568,-30],[-2405,-3355]],[[323107,780571],[1533,-828]],[[324640,779743],[2683,385]],[[327323,780128],[389,-389],[-2374,-2489]],[[325338,777250],[-485,1590]],[[324853,778840],[-622,-690]],[[324231,778150],[-1339,1448],[-978,163]],[[321914,779761],[-770,1278]],[[321144,781039],[1096,2492]],[[322240,783531],[-262,-1695]],[[321978,781836],[1129,-1265]],[[331391,775898],[539,2552]],[[331930,778450],[1778,-263]],[[333708,778187],[103,-1152]],[[333811,777035],[-1550,-1839]],[[332261,775196],[-2494,-479]],[[329767,774717],[-588,2178],[1737,5066]],[[330916,781961],[1283,1226]],[[332199,783187],[213,-1397],[-681,-3528],[-1340,-2777],[1000,413]],[[906131,768341],[1001,-340],[-1266,-1152],[-1459,-2374],[1724,3866]],[[899511,766086],[2706,-1047],[1512,2332],[-671,-3374],[666,-2736],[1367,494],[-911,-1254],[-2429,-1347],[-1837,-389],[-1500,-2739],[-536,-2481],[-2024,1527],[-1823,1903],[-1236,-191],[-1169,-1213],[-1404,1287],[-459,-1334],[2322,-3135],[-1364,62],[-1420,-2324],[-426,908],[314,1993],[-799,2812],[196,1550],[1653,2374],[-261,1500],[2511,-614],[281,2626],[686,2232],[382,4129],[-553,2604],[985,2095],[2129,-4091],[1623,-2502],[1489,-1657]],[[295648,774097],[-1094,-164]],[[294554,773933],[1345,1560],[-251,-1396]],[[912776,773199],[-1906,-1939],[-710,-78],[-1251,-2510],[-861,-884],[969,2677],[1775,2189],[1984,545]],[[9463,811999],[432,-667]],[[9895,811332],[-1456,-891]],[[8439,810441],[1024,1558]],[[6513,810872],[1645,1335],[-236,-1097],[-1409,-238]],[[13068,812920],[2748,1149]],[[15816,814069],[-104,-820],[-2644,-329]],[[33432,820758],[-3061,-3029]],[[30371,817729],[1969,3695]],[[32340,821424],[1032,595]],[[33372,822019],[60,-1261]],[[134017,819872],[828,-2926]],[[134845,816946],[-375,-732]],[[134470,816214],[1025,-2515],[-2620,3729],[58,1281],[-1119,819]],[[131814,819528],[2203,344]],[[142910,818356],[118,-2495]],[[143028,815861],[-470,-1356],[-187,2806]],[[142371,817311],[-426,-530],[-772,2038]],[[141173,818819],[401,1553],[1336,-2016]],[[34659,820350],[-795,281],[1868,1201]],[[35732,821832],[440,2586]],[[36172,824418],[1857,-1573],[-1281,-1312],[-2089,-1183]],[[139308,819707],[-1857,2230]],[[137451,821937],[1590,-639]],[[139041,821298],[267,-1591]],[[131512,825393],[1448,-552]],[[132960,824841],[1295,634]],[[134255,825475],[-953,-5192]],[[133302,820283],[-2316,1437]],[[130986,821720],[-577,1603]],[[130409,823323],[11,2256]],[[130420,825579],[1092,-186]],[[531559,829920],[1050,-499],[-783,-1059],[-1172,856],[905,702]],[[883167,831111],[738,-350],[-1514,-2253],[-551,1304],[318,1916],[1009,-617]],[[45900,830448],[327,-1453]],[[46227,828995],[-4071,-1875]],[[42156,827120],[-178,1117],[994,1619]],[[42972,829856],[1839,938]],[[44811,830794],[1089,-346]],[[962916,829609],[-14,-859],[-2483,3557],[1457,103],[1040,-2801]],[[541910,830692],[-1121,476],[225,1152],[896,-1628]],[[534913,835213],[-983,-1888],[-980,-4110],[-579,2453],[-1021,105],[-854,3064],[1378,1314],[958,-1456],[130,1602],[1978,569],[-27,-1653]],[[129708,833783],[-959,-1626]],[[128749,832157],[44,1600],[915,26]],[[529569,834175],[390,-2823],[-2213,177],[-356,2087],[2179,559]],[[136169,833460],[-581,-1676]],[[135588,831784],[-721,1199]],[[134867,832983],[-874,-1440],[-232,1485]],[[133761,833028],[614,2461]],[[134375,835489],[988,732]],[[135363,836221],[806,-2761]],[[482975,836075],[-490,-1917],[-389,1335],[879,582]],[[563676,853234],[-1059,-811],[-1350,1503],[1475,815],[934,-1507]],[[128983,838496],[1009,-115]],[[129992,838381],[2860,-4972]],[[132852,833409],[-1162,-96]],[[131690,833313],[1708,-1515],[-438,-2939]],[[132960,828859],[-1800,1990]],[[131160,830849],[-892,1846]],[[130268,832695],[195,1361],[-1797,1157]],[[128666,835213],[317,3283]],[[280734,838063],[-2959,-1979]],[[277775,836084],[2024,3959]],[[279799,840043],[935,-1980]],[[132963,836150],[-1464,799]],[[131499,836949],[779,2491]],[[132278,839440],[868,-1507],[-183,-1783]],[[483950,838526],[-1108,-330],[205,2116],[903,-1786]],[[127916,837243],[-665,-301]],[[127251,836942],[-512,4513]],[[126739,841455],[641,555],[1123,-1671],[-587,-3096]],[[76620,850469],[855,-1179],[-1868,-861]],[[75607,848429],[-1666,423],[1500,1950]],[[75441,850802],[1179,-333]],[[552990,847363],[-730,-768],[261,-1825],[-2115,-2831],[-27,3770],[1113,1623],[1498,31]],[[75283,847292],[1304,11]],[[76587,847303],[590,-1474]],[[77177,845829],[-2940,-2078]],[[74237,843751],[-1341,-2179],[-1352,1686]],[[71544,843258],[-47,-1370]],[[71497,841888],[-1237,2510]],[[70260,844398],[475,1327]],[[70735,845725],[1404,422]],[[72139,846147],[349,1121]],[[72488,847268],[1055,-526],[1011,1426]],[[74554,848168],[729,-876]],[[545912,838208],[-323,1649],[1703,4598],[257,-149],[-1637,-6098]],[[125084,844493],[969,-3752]],[[126053,840741],[-93,-2907]],[[125960,837834],[-614,2625],[-1265,896],[363,1217]],[[124444,842572],[-838,1284],[-863,-1390]],[[122743,842466],[69,1825]],[[122812,844291],[1226,1278],[1046,-1076]],[[129538,842431],[1145,-729]],[[130683,841702],[-719,-2463]],[[129964,839239],[-1084,-3]],[[128880,839236],[-1046,3232]],[[127834,842468],[1704,-37]],[[482931,845403],[1218,-1399],[-676,-1326],[-2254,2354],[1428,1237],[284,-866]],[[125887,849293],[1223,-106],[-74,-1536]],[[127036,847651],[948,-3245]],[[127984,844406],[-1849,-1451],[291,2312],[-1170,4625],[631,-599]],[[123296,848287],[1697,351]],[[124993,848638],[197,-3377]],[[125190,845261],[-1573,1073],[-539,-1435],[-2435,3271],[685,1462],[1486,294],[482,-1639]],[[482781,850488],[-630,-2027],[-1476,-1606],[-153,2835],[2151,1624],[108,-826]],[[954541,851909],[350,2440],[2254,1221],[120,-1988],[-2724,-1673]],[[562826,852016],[1876,-816],[-1563,-1498],[-1507,-452],[-904,2031],[2098,735]],[[891694,943123],[-2524,1095],[2077,531],[447,-1626]],[[814964,945499],[-1680,-1810],[-3553,1529],[1614,1161],[3619,-880]],[[907764,951248],[4547,253],[119,-836],[4524,-315],[1507,-1627],[-2915,-1019],[-4179,314],[-5400,2207],[1797,1023]],[[210778,949266],[-2132,660]],[[208646,949926],[1503,1671]],[[210149,951597],[1956,-1581]],[[212105,950016],[-1327,-750]],[[240159,949216],[-12,-1995]],[[240147,947221],[-3196,-291]],[[236951,946930],[-5173,2064],[2469,3189]],[[234247,952183],[3455,383]],[[237702,952566],[2457,-3350]],[[449998,951464],[1151,-2456],[-3013,53],[-514,1881],[2376,522]],[[183799,965371],[-3325,1261]],[[180474,966632],[1941,652]],[[182415,967284],[1384,-1913]],[[250463,962485],[-3651,709],[-5,1308]],[[246807,964502],[2715,-79]],[[249522,964423],[941,-1938]],[[560022,970354],[4190,-3580],[4959,-1392],[-5833,-2848],[-192,1561],[-4675,-583],[1676,2859],[-3585,3503],[3460,480]],[[193893,964006],[-4872,-1067]],[[189021,962939],[-3367,1103],[-63,2262]],[[185591,966304],[5502,1043],[4304,-54],[-3358,-1451],[1854,-1836]],[[209605,962901],[-1869,-922],[-2364,3218],[4233,-2296]],[[234765,965592],[5282,-126]],[[240047,965466],[176,-1756],[-6854,57]],[[233369,963767],[1396,1825]],[[200410,955317],[-468,-1498]],[[199942,953819],[3136,-133]],[[203078,953686],[1376,1646]],[[204454,955332],[2543,-1863]],[[206997,953469],[-1060,-3283]],[[205937,950186],[-3586,-1567],[-4660,816]],[[197691,949435],[-5861,-2524],[-4385,-1315]],[[187445,945596],[-2762,79]],[[184683,945675],[-1718,1990]],[[182965,947665],[3602,1240],[3235,261]],[[189802,949166],[1770,1228],[-7438,-937]],[[184134,949457],[-847,2167]],[[183287,951624],[-1209,-2052]],[[182078,949572],[-3547,-710]],[[178531,948862],[-5198,1799]],[[173333,950661],[1238,1192]],[[174571,951853],[2992,119]],[[177563,951972],[2654,1261],[-5287,-618]],[[174930,952615],[1909,1655]],[[176839,954270],[-755,1141],[2858,2156]],[[178942,957567],[3852,83]],[[182794,957650],[1029,-1449]],[[183823,956201],[3128,-31],[4569,-3869]],[[191520,952301],[5461,-249],[-1970,2112]],[[195011,954164],[1016,1458]],[[196027,955622],[-2333,1824],[2588,2032]],[[196282,959478],[2421,-133],[-127,-1769]],[[198576,957576],[1834,-2259]],[[889023,953962],[2131,-1174],[1859,3000],[2709,-1385],[3404,-235],[4359,-1648],[-1033,-1876],[-2398,-1329],[-3584,1736],[758,1909],[-2545,5],[1474,-3125],[1420,-965],[-1820,-888],[-1349,1012],[-4776,-855],[-1839,585],[-1407,-1713],[-2797,835],[-2429,1933],[141,3707],[4586,2650],[3136,-2179]],[[877634,951478],[-1379,-120],[685,2700],[694,-2580]],[[238068,960381],[3611,-1730]],[[241679,958651],[4696,358],[5273,-2913]],[[251648,956096],[-5202,-173]],[[246446,955923],[5762,-2357],[-1225,-1167],[2026,-659]],[[253009,951740],[4609,971],[3302,-685]],[[260920,952026],[5935,1877]],[[266855,953903],[4940,71]],[[271795,953974],[5088,-1196]],[[276883,952778],[1910,-2546]],[[278793,950232],[-2009,-875],[2656,-794]],[[279440,948563],[-2434,-1991]],[[277006,946572],[-4253,-622]],[[272753,945950],[-9035,772]],[[263718,946722],[-425,-523],[-8913,-145]],[[254380,946054],[231,1722]],[[254611,947776],[-4179,-1399],[-5881,1449]],[[244551,947826],[-1293,3277]],[[243258,951103],[995,1846],[-2842,4124],[-6062,-532]],[[235349,956541],[-4365,2738]],[[230984,959279],[243,1454]],[[231227,960733],[3111,545]],[[234338,961278],[3730,-897]],[[168348,952708],[4562,2934],[350,-868],[-2743,-2669],[-2169,603]],[[228608,957739],[1014,-6202]],[[229622,951537],[-940,-1733]],[[228682,949804],[-2484,-698]],[[226198,949106],[-4627,-9]],[[221571,949097],[-1380,2007]],[[220191,951104],[3167,1830]],[[223358,952934],[-8195,-840]],[[215163,952094],[1662,2193]],[[216825,954287],[235,3289]],[[217060,957576],[5106,-2959]],[[222166,954617],[-2419,3175]],[[219747,957792],[6056,1294]],[[225803,959086],[2805,-1347]],[[216034,955063],[-3020,-1484],[-2878,2477]],[[210136,956056],[4908,588]],[[215044,956644],[990,-1581]],[[211047,958429],[2699,-788]],[[213746,957641],[-3387,-733]],[[210359,956908],[688,1521]],[[448381,955226],[-1397,2299],[981,1254],[416,-3553]],[[179024,963052],[-2040,-1550]],[[176984,961502],[181,-2906],[-2593,-1855]],[[174572,956741],[-2406,880]],[[172166,957621],[666,2001]],[[172832,959622],[-2809,-1607]],[[170023,958015],[-1046,-2290],[-989,1266],[-1080,-2852]],[[166908,954139],[-3074,957]],[[163834,955096],[-3836,-451]],[[159998,954645],[97,2707],[2089,239],[7010,5116]],[[169194,962707],[5031,50],[2544,1359]],[[176769,964116],[2255,-1064]],[[546629,978121],[3249,-1200],[3148,-3242],[2981,-67],[3406,-2402],[-4500,-696],[-3693,-3541],[-4830,-8566],[-6149,3672],[-1155,2163],[8228,1383],[-501,675],[-6247,-859],[-2565,1550],[8601,1910],[16,1855],[-3792,-1128],[-263,1824],[-2377,-626],[475,-1545],[-4133,-1051],[-3823,2839],[1391,1114],[-1930,2245],[-1033,-911],[-950,3951],[6037,-660],[2550,-2047],[1767,2719],[4725,-4843],[-1329,4150],[2696,1334]],[[306975,996546],[8048,-431]],[[315023,996115],[-2237,-1635]],[[312786,994480],[6922,1379],[5055,-1988]],[[324763,993871],[4467,-581]],[[329230,993290],[-1943,-2511]],[[327287,990779],[-6660,-1834],[-5699,-695]],[[314928,988250],[-4700,-2105]],[[310228,986145],[7173,1381]],[[317401,987526],[699,-1242]],[[318100,986284],[-8741,-3591]],[[309359,982693],[-7659,-5431],[-5790,-32],[17,-1548]],[[295927,975682],[-4981,-439]],[[290946,975243],[-4554,541]],[[286392,975784],[6573,-2724],[-11248,133]],[[281717,973193],[1942,-786]],[[283659,972407],[4519,382]],[[288178,972789],[5063,-1675]],[[293241,971114],[-1237,-1062]],[[292004,970052],[-4153,-140],[3278,-1146]],[[291129,968766],[-1868,-1883],[-5963,-378]],[[283298,966505],[-177,-2530]],[[283121,963975],[-2203,-1105]],[[280918,962870],[-6244,-373]],[[274674,962497],[4934,-659]],[[279608,961838],[334,-1317]],[[279942,960521],[2589,247]],[[282531,960768],[12,-2408],[-6683,-2339]],[[275860,956021],[-1334,1992]],[[274526,958013],[-3776,1247],[824,-1525]],[[271574,957735],[-4590,-75]],[[266984,957660],[-3488,-880]],[[263496,956780],[-2707,772]],[[260789,957552],[-6334,-177]],[[254455,957375],[-3261,515]],[[251194,957890],[195,1984],[3060,1641]],[[254449,961515],[4295,418],[-3453,3228]],[[255291,965161],[2992,1025]],[[258283,966186],[3971,-2555]],[[262254,963631],[2361,-592]],[[264615,963039],[2664,1016]],[[267279,964055],[-4194,157]],[[263085,964212],[-717,2184]],[[262368,966396],[1453,2279]],[[263821,968675],[-3315,-1370]],[[260506,967305],[827,1550]],[[261333,968855],[-4533,-984],[2067,3540]],[[258867,971411],[5011,818]],[[263878,972229],[4812,-841]],[[268690,971388],[2313,790]],[[271003,972178],[-3158,889],[-4206,3309],[-3697,1380]],[[259942,977756],[316,2809]],[[260258,980565],[7176,-535],[5189,-3001]],[[272623,977029],[2349,-174],[-5420,3465]],[[269552,980320],[8084,1485]],[[277636,981805],[8891,2071]],[[286527,983876],[-4724,256],[733,1459],[-7556,-3038]],[[274980,982553],[-8525,-584],[-9038,672]],[[257417,982641],[-4421,805]],[[252996,983446],[1412,1150]],[[254408,984596],[-4262,1025],[4079,2322]],[[254225,987943],[-8122,80]],[[246103,988023],[2535,1771]],[[248638,989794],[7920,1232]],[[256558,991026],[7206,-606]],[[263764,990420],[-4267,1210],[4678,1554]],[[264175,993184],[15200,-3524]],[[279375,989660],[-8407,3393]],[[270968,993053],[4004,2085],[6281,-591]],[[281253,994547],[-3906,1373]],[[277347,995920],[20398,1008],[9230,-382]],[[38512,862457],[1128,-411],[383,-2376]],[[40023,859670],[-1431,-816],[-2868,1381]],[[35724,860235],[-825,1173]],[[34899,861408],[1666,62]],[[36565,861470],[1947,987]],[[89622,859078],[1542,3229]],[[91164,862307],[539,-616]],[[91703,861691],[-2081,-2613]],[[319909,868276],[-1665,1681]],[[318244,869957],[1784,75],[-119,-1756]],[[291239,908966],[74,-4127]],[[291313,904839],[-1814,-1504],[-3402,-98]],[[286097,903237],[-836,2602],[1571,3111]],[[286832,908950],[2427,625],[1980,-609]],[[295495,906299],[-2644,266],[-369,1412],[3531,-575]],[[296013,907402],[-518,-1103]],[[639625,914604],[-1775,-1932],[-2665,-750],[-1078,1820],[979,2346],[1650,444],[2889,-1928]],[[543778,910905],[1580,1867],[528,-1442],[-1685,-1444],[-4597,-1176],[3208,2518],[196,2533],[1138,1390],[-368,-4246]],[[542241,913167],[42,-1936],[-1948,99],[1906,1837]],[[279970,912589],[-542,459]],[[279428,913048],[2248,2652]],[[281676,915700],[1021,-396]],[[282697,915304],[-2727,-2715]],[[259456,906015],[-1011,2159]],[[258445,908174],[1496,493]],[[259941,908667],[-485,-2652]],[[291997,909646],[-1442,1047],[1157,724]],[[291712,911417],[285,-1771]],[[304619,875284],[-1192,285]],[[303427,875569],[-1024,1666]],[[302403,877235],[1923,-856]],[[304326,876379],[293,-1095]],[[23714,881749],[2868,349]],[[26582,882098],[2786,-2077],[1975,-223]],[[31343,879798],[-377,-826]],[[30966,878972],[-2572,-459]],[[28394,878513],[-2080,1691]],[[26314,880204],[-3512,269]],[[22802,880473],[912,1276]],[[284615,879658],[-1122,-1024]],[[283493,878634],[-1569,1996]],[[281924,880630],[2232,-120],[459,-852]],[[285098,881443],[641,554],[1267,-1706],[-1908,1152]],[[264112,891353],[853,1104],[7118,-4758]],[[272083,887699],[1039,-2557],[-630,-1075]],[[272492,884067],[2983,348]],[[275475,884415],[1463,-1942]],[[276938,882473],[-2067,-1781]],[[274871,880692],[-3699,1453]],[[271172,882145],[-248,1304]],[[270924,883449],[-2853,1020]],[[268071,884469],[-650,-1693]],[[267421,882776],[-2513,-2986]],[[264908,879790],[-2396,-1008]],[[262512,878782],[-860,3361],[-2894,-777]],[[258758,881366],[-950,574]],[[257808,881940],[2603,2753],[-340,2541]],[[260071,887234],[1146,6745]],[[261217,893979],[1131,1269]],[[262348,895248],[1764,-3895]],[[456824,897085],[1909,906],[-723,-1653],[769,-1905],[3246,-1371],[250,-2408],[-2684,-4130],[-4204,-1983],[-1611,-1456],[-3266,-903],[-2327,-1815],[-4289,883],[-2913,2249],[-3778,-581],[-379,1079],[1907,308],[1139,1934],[-2279,2353],[-4331,405],[5928,1098],[-1615,1054],[1748,1308],[-2939,788],[-2771,-1024],[-1542,550],[953,1536],[2296,-57],[-1255,1892],[1995,-517],[1593,521],[-1873,1701],[1927,432],[2831,-2396],[-70,-3268],[840,-1229],[1083,2319],[792,-516],[272,2739],[2485,-1546],[220,1797],[1681,552],[1885,-2007],[-550,1940],[2074,-1144],[1103,1413],[1125,-422],[577,1867],[1543,402],[1228,-1695]],[[481580,873383],[-254,-1407],[-1249,1749],[1503,-342]],[[279041,874472],[614,-2284]],[[279655,872188],[-957,-2261],[-1657,1029]],[[277041,870956],[677,3109]],[[277718,874065],[1323,407]],[[272221,877686],[-315,-1789]],[[271906,875897],[-2506,-2620]],[[269400,873277],[-1897,-294]],[[267503,872983],[-173,848]],[[267330,873831],[-420,723]],[[266910,874554],[36,302]],[[266946,874856],[1453,2538]],[[268399,877394],[3822,292]],[[0,890205],[0,1449]],[[0,891654],[0,21752],[3127,-1359],[505,-1232],[9297,-5142],[1377,-1952],[-210,-4298],[1476,-1654],[1079,2575],[-1228,1921],[2133,247],[1253,-1043],[3980,-218],[4310,-4517],[1295,-155],[-2118,-1646],[-349,-1445],[-2180,1024],[1101,-1448],[-2368,-319],[-2434,1096],[1587,-1516],[-3,-2233],[-2184,-1017],[-28,-3431],[-2755,898],[-880,1118],[-2993,976],[-1273,1235],[-666,2726],[-2674,845],[-3483,-763],[-2214,5101],[-1880,-1943],[1199,-2969],[-1799,-2663]],[[264792,893213],[-1282,1457]],[[263510,894670],[549,1112]],[[264059,895782],[733,-2569]],[[267428,894527],[-1090,-148]],[[266338,894379],[-803,2127],[1893,-1979]],[[259474,925417],[4151,836]],[[263625,926253],[624,-889]],[[264249,925364],[584,3462]],[[264833,928826],[-3477,2372]],[[261356,931198],[1405,1352],[2929,-960],[-2749,2184]],[[262941,933774],[-843,2092],[1739,3324],[3435,482]],[[267272,939672],[3118,1852],[3482,-563],[3142,-5266],[-2651,-2571]],[[274363,933124],[1030,-2371],[2268,2860]],[[277661,933613],[3730,-253],[2703,-1016]],[[284094,932344],[-1734,2489],[4047,714]],[[286407,935547],[4442,-1421]],[[290849,934126],[1086,-2253]],[[291935,931873],[1695,-297]],[[293630,931576],[-308,-2239]],[[293322,929337],[4172,32],[4572,-1873]],[[302066,927496],[-664,-1520],[1952,-12]],[[303354,925964],[381,-1376]],[[303735,924588],[-1724,-2195]],[[302011,922393],[3684,2042]],[[305695,924435],[4421,-1908]],[[310116,922527],[-1557,-1872]],[[308559,920655],[485,-1573],[1732,2211]],[[310776,921293],[2102,-1660]],[[312878,919633],[395,-1800]],[[313273,917833],[-2479,139]],[[310794,917972],[-1108,-1048]],[[309686,916924],[4839,-1425]],[[314525,915499],[-89,-1090]],[[314436,914409],[-3154,565]],[[311282,914974],[518,-1241]],[[311800,913733],[-795,-2890]],[[311005,910843],[3678,-624]],[[314683,910219],[-325,-1362]],[[314358,908857],[1718,384],[-559,-2228],[3991,824]],[[319508,907837],[2280,-2491]],[[321788,905346],[889,-2126]],[[322677,903220],[2211,-172],[-1837,-2445]],[[323051,900603],[4814,1165],[1858,-2195]],[[329723,899573],[-1564,-1989]],[[328159,897584],[-1918,557]],[[326241,898141],[1560,-2201]],[[327801,895940],[-1663,-5]],[[326138,895935],[-191,-2337]],[[325947,893598],[-1885,-138],[-747,-4080]],[[323315,889380],[-803,1074]],[[322512,890454],[-1832,43]],[[320680,890497],[-2351,3836]],[[318329,894333],[1103,1858]],[[319432,896191],[-2282,-478]],[[317150,895713],[-2671,3310]],[[314479,899023],[-1444,83],[-11,-1576],[-1548,1105]],[[311476,898635],[1904,-2700]],[[313380,895935],[-2983,-568],[3164,-2952],[-578,-496]],[[312983,891919],[1831,-2268],[2023,-521]],[[316837,889130],[2400,-2661]],[[319237,886469],[-265,-2421]],[[318972,884048],[1365,0]],[[320337,884048],[456,-4527],[-1882,2964]],[[318911,882485],[396,-3137]],[[319307,879348],[1016,-1969]],[[320323,877379],[-1331,179]],[[318992,877558],[313,-1697]],[[319305,875861],[-3261,2732],[-529,-474]],[[315515,878119],[-2126,1645]],[[313389,879764],[-1982,2540]],[[311407,882304],[474,-1842]],[[311881,880462],[-2142,1793],[-1159,-131]],[[308580,882124],[2138,-3146],[1293,-466]],[[312011,878512],[4710,-5241]],[[316721,873271],[-768,-2018]],[[315953,871253],[-3287,1676]],[[312666,872929],[-2607,497],[-1955,1008]],[[308104,874434],[-1285,2010],[-1920,112]],[[304899,876556],[-2826,1654],[-1998,2293],[1436,489],[-2131,1165]],[[299380,882157],[-3390,4234]],[[295990,886391],[-4091,2183]],[[291899,888574],[767,-1392],[-6156,-1892],[-2597,767]],[[283913,886057],[-1065,1485]],[[282848,887542],[219,1905]],[[283067,889447],[2041,1524]],[[285108,890971],[95,1520]],[[285203,892491],[4162,-1339],[333,524]],[[289698,891676],[5994,1005]],[[295692,892681],[-543,1668],[-1910,2206],[3424,3318],[2726,3289]],[[299389,903162],[-3020,6597]],[[296369,909759],[-937,-434]],[[295432,909325],[-894,1684],[-1267,6],[-1375,2388],[-4128,-1721]],[[287768,911682],[-427,1878]],[[287341,913560],[1676,127]],[[289017,913687],[463,1704]],[[289480,915391],[-1725,727]],[[287755,916118],[-292,1438]],[[287463,917556],[-2996,958]],[[284467,918514],[-697,2378]],[[283770,920892],[-1223,-106]],[[282547,920786],[-2176,2219],[-781,-1370],[1272,-2340],[-2018,-490]],[[278844,918805],[-5399,1283]],[[273445,920088],[-1608,-1600]],[[271837,918488],[-2646,964]],[[269191,919452],[-2133,-244]],[[267058,919208],[-6842,1081]],[[260216,920289],[-839,1516]],[[259377,921805],[-3546,-884]],[[255831,920921],[-2632,1606]],[[253199,922527],[-1688,3192]],[[251511,925719],[4475,-695]],[[255986,925024],[1957,398]],[[257943,925422],[-2033,1167],[-3353,470]],[[252557,927059],[-2129,1211]],[[250428,928270],[-498,2703]],[[249930,930973],[453,2745]],[[250383,933718],[1662,3893],[4288,3874]],[[256333,941485],[2641,658]],[[258974,942143],[4608,-153]],[[263582,941990],[377,-672],[-3088,-2574],[-1508,-2308]],[[259363,936436],[1647,-6515]],[[261010,929921],[2814,-2475]],[[263824,927446],[-4350,-2029]],[[301559,899263],[867,-1573],[-276,-2234],[1790,1987],[2847,-459],[185,2188],[-2702,1022],[-1976,1627],[-735,-2558]],[[303757,887946],[-344,2276],[-2683,2000],[626,-2975],[-1076,-1051],[1111,-705],[1617,1086],[749,-631]],[[496366,863369],[710,-551],[-621,-1955],[-1077,995],[988,1511]],[[310717,863514],[897,-667]],[[311614,862847],[-1518,-1158],[621,1825]],[[996837,924325],[82,2397],[3080,1817],[0,-3227],[-3162,-987]],[[0,925312],[0,1133]],[[0,926445],[0,1276]],[[0,927721],[0,818]],[[0,928539],[4572,-51],[2283,-1576],[-804,-1158],[-4681,-855],[-1370,413]],[[970001,916943],[-3922,1519],[1581,1060],[2825,-789],[-484,-1790]],[[565112,924262],[-3005,-1783],[-1012,843],[4017,940]],[[429354,924887],[84,-1591],[-2421,-1176],[-919,587],[-3593,-589],[525,2626],[2036,-204],[3214,1072],[1074,-725]],[[948519,912918],[-918,1240],[595,1534],[323,-2774]],[[232499,915545],[1985,-3018]],[[234484,912527],[-2266,-2158]],[[232218,910369],[-2974,431]],[[229244,910800],[-2357,1773],[-3112,444]],[[223775,913017],[-41,1265],[2777,1204]],[[226511,915486],[522,2488],[1326,635]],[[228359,918609],[4140,-3064]],[[286124,914356],[-898,1615]],[[285226,915971],[1805,-297],[-907,-1318]],[[548619,917037],[1392,-541],[-149,-1818],[-2193,-1020],[-442,1990],[1392,1389]],[[277839,916524],[-2223,991]],[[275616,917515],[3289,791],[-1066,-1782]],[[358295,916778],[-935,1790],[1865,-37],[-930,-1753]],[[353524,919101],[1905,-814],[-187,-1885],[-4071,-1377],[-685,1680],[-3040,1027],[1658,1353],[-1370,1466],[1233,757],[2768,-568],[1789,-1639]],[[553486,919822],[936,-570],[-2243,-2318],[-1209,1120],[2398,2841],[118,-1073]],[[667917,919042],[-28,-1237],[-4132,989],[-1333,2215],[1479,1176],[4014,-3143]],[[475129,924399],[1694,1784],[948,-586],[-2642,-1198]],[[647614,926786],[-329,-1618],[-2148,1873],[2477,-255]],[[720837,935555],[-2348,1009],[1411,1197],[937,-2206]],[[715645,933003],[-2018,38],[1869,1974],[2184,-880],[-2035,-1132]],[[347175,935965],[-1408,-1320],[-1920,892],[3328,428]],[[223930,942411],[4686,1360],[-1247,-1303],[-3439,-57]],[[182061,934716],[2661,676]],[[184722,935392],[811,1698]],[[185533,937090],[5384,-1584]],[[190917,935506],[-1737,-2119]],[[189180,933387],[2097,55],[2596,1753]],[[193873,935195],[-1264,2056],[1812,-146],[3639,-2869],[1355,-4433]],[[199415,929803],[2513,851],[-1083,1508]],[[200845,932162],[-1507,5667]],[[199338,937829],[581,1439]],[[199919,939268],[2995,-431],[3162,-1572]],[[206076,937265],[4064,-9341]],[[210140,927924],[-611,-1954]],[[209529,925970],[1711,-2023]],[[211240,923947],[2511,-637],[4131,-3081],[1444,-144]],[[219326,920085],[298,-2344],[-3608,753]],[[216016,918494],[-1075,-1723]],[[214941,916771],[-2050,794]],[[212891,917565],[747,-2805]],[[213638,914760],[1787,1566],[1309,-410],[329,-2270],[-2883,-1187]],[[214180,912459],[-6141,574]],[[208039,913033],[240,953]],[[208279,913986],[-3115,478]],[[205164,914464],[-1439,1645],[-2169,-2592]],[[201556,913517],[-4184,-1436],[-6569,-1290]],[[190803,910791],[-5047,-284]],[[185756,910507],[-1359,2040]],[[184397,912547],[-214,2113]],[[184183,914660],[-4069,413]],[[180114,915073],[-3763,947]],[[176351,916020],[-1640,2248],[-87,1754]],[[174624,920022],[7064,1258],[5428,-517]],[[187116,920763],[2793,495]],[[189909,921258],[-5902,2263]],[[184007,923521],[-6204,-619]],[[177803,922902],[-4434,256]],[[173369,923158],[-1880,1534],[1250,1600],[5340,1323]],[[178079,927615],[846,975]],[[178925,928590],[-5934,-923]],[[172991,927667],[-54,1592],[-3127,163]],[[169810,929422],[-213,1770]],[[169597,931192],[2032,1642]],[[171629,932834],[-448,1607]],[[171181,934441],[2286,1760]],[[173467,936201],[8093,3209]],[[181560,939410],[1318,-609]],[[182878,938801],[152,-2422]],[[183030,936379],[-969,-1663]],[[167398,943794],[1680,-502]],[[169078,943292],[1633,1284]],[[170711,944576],[2859,-77]],[[173570,944499],[5567,-3631]],[[179137,940868],[177,-1066]],[[179314,939802],[-9763,-4471]],[[169551,935331],[-1239,-1918],[-2145,-876],[-1221,-4188]],[[164946,928349],[-3139,-361]],[[161807,927988],[-3299,-2114],[-2974,3493]],[[155534,929367],[-4875,2725]],[[150659,932092],[2154,2668]],[[152813,934760],[419,2893]],[[153232,937653],[2887,4099]],[[156119,941752],[-2498,3437]],[[153621,945189],[9391,1077]],[[163012,946266],[4869,-1760]],[[167881,944506],[-483,-712]],[[222216,942806],[2345,-1271]],[[224561,941535],[4378,925]],[[228939,942460],[1612,-1309],[-753,-1657]],[[229798,939494],[-3234,-2290]],[[226564,937204],[2670,-48]],[[229234,937156],[880,-2070]],[[230114,935086],[1713,331]],[[231827,935417],[-705,-2280]],[[231122,933137],[507,-2844]],[[231629,930293],[-2691,-1209]],[[228938,929084],[-2435,850]],[[226503,929934],[723,-1969]],[[227226,927965],[-2690,-436],[-3965,4650]],[[220571,932179],[-3138,964],[-2749,2773]],[[214684,935916],[2198,1623]],[[216882,937539],[1588,-1840]],[[218470,935699],[2405,158]],[[220875,935857],[1078,1127]],[[221953,936984],[-951,1726]],[[221002,938710],[-2810,1045]],[[218192,939755],[1488,2218]],[[219680,941973],[2536,833]],[[416796,999793],[11551,-1800],[-17216,-1041],[13863,-107],[5218,547],[1814,-1672],[8193,-1670],[-6503,-1827],[-15882,-746],[-643,-1219],[12951,271],[2377,-1778],[3304,1841],[4904,337],[532,-2213],[-5350,-4552],[3170,731],[5321,3046],[7111,-987],[5278,2582],[9341,-1093],[1845,-1333],[-8263,-3915],[-6270,-1125],[2287,-863],[-2586,-1359],[-7113,351],[-1971,-2692],[2375,-711],[580,-3145],[-5225,-3538],[-2199,-4529],[2458,718],[3811,-1143],[-3306,-592],[1247,-1484],[5257,-908],[-475,-2589],[-6755,644],[-2598,-1858],[1279,-1832],[3648,-268],[1651,-2733],[232,-3126],[-2942,500],[-3608,-2031],[2378,485],[879,-1926],[1739,1463],[2112,-2937],[-401,-1158],[-4890,-1025],[-3346,1038],[-3,-1320],[5467,-1275],[-396,-2105],[-4654,-1321],[-2230,452],[-3249,2478],[-1760,-1500],[-5450,-2312],[6017,1789],[1912,-146],[5174,-2843],[-714,-4733],[-4933,2246],[-1558,3193],[-5632,-1907],[5240,906],[290,-2555],[7519,-4104],[-1509,-1447],[2086,-132],[637,-5640],[-3244,-526],[-3059,698],[-2140,3960],[-3676,2064],[-3393,-233],[3748,-549],[43,-1519],[-2710,-1382],[-4404,337],[649,-1826],[-1310,-1317],[5647,-474],[-2924,-1613],[5642,1355],[6546,-1414],[2469,67],[-2028,-1901],[-6039,-3225],[267,-565],[-3469,-2743],[-8078,-2390],[-1720,76],[-2062,-1148],[-2246,63],[-2522,1829],[453,-2643],[-2757,-2160],[-2624,-5335],[-3019,-2818],[-1886,1131],[658,-1785],[-2081,-1831],[-1898,240],[-1920,-1649],[-686,691],[1972,3639],[-1281,-370],[-2458,-3774],[-4318,-605],[1705,-1076],[-1877,-1730],[-2308,309],[2506,-3679],[-1665,-1529],[642,-2942],[-1384,-1252],[-163,-1422],[-2641,-3435],[-2454,155],[2191,-899],[-468,-2463],[587,-1751],[-593,-1039],[-1093,-5417],[-1607,-1911],[480,-2273],[-2762,-1358],[-934,1081],[-2572,1116],[-3,1434],[-1851,1012],[548,3349],[-2848,-2160],[-3696,234],[-1991,2497],[-1273,3631],[1532,1122],[-2208,-480],[195,1387],[-2127,1425],[-197,2066],[-2996,4860],[-664,3334],[1323,2105],[3083,849],[-1667,556],[-1887,-1033],[-1449,-2396],[-474,1168],[-708,6194],[-2363,785],[257,2271],[-794,421],[3711,2719],[2153,2273],[-4558,-3851],[-1924,-512],[-56,1536],[1657,2447],[-2350,1829],[238,1675],[3145,1964],[4125,-671],[591,1008],[-3823,180],[-3862,-1706],[1621,3903],[4318,-907],[1083,1603],[-3253,-633],[-2791,468],[955,1857],[2046,535],[1450,-906],[1476,1135],[1470,5839],[1190,1712],[-5452,264],[-2135,1439],[-2753,710],[-1435,1645],[1014,716],[3789,-413],[3548,-1843],[411,4026],[-4531,361],[717,1905],[-1918,459],[-104,1606],[-1349,-2317],[-3815,-190],[-920,1184],[978,2866],[-795,2032],[2254,1115],[146,1368],[-2652,1424],[1098,885],[-2241,1752],[485,1999],[-2158,1918],[1252,1823],[-6522,5086],[243,1799],[-7941,2908],[-5733,946],[-4092,-989],[-5953,-123],[1056,-1033],[-4094,531],[-3710,1967],[3502,1650],[-6161,769],[-1171,2180],[5235,118],[1074,872],[6118,-369],[-839,2375],[-7389,-1268],[-7538,2783],[-2092,1526],[1174,1835],[9450,2092],[4198,1538],[4207,92],[1522,1232],[2431,4719],[-3516,-668],[-3697,843],[404,1461],[8623,3812],[2314,-1012],[431,1970],[4076,-501],[557,3324],[5483,1849],[12599,2042],[3178,-1718],[2240,1542],[5225,-2527],[3759,136],[-4024,3211],[5913,-324],[9917,-3416],[2107,119],[816,3076],[-3649,2116],[11439,316],[-9982,634],[-1074,781],[6681,1464],[4748,-970],[2620,1371],[5774,-1975],[1251,2883],[21874,470]],[[653666,939029],[3082,-635],[-960,-2440],[-2022,-1922],[-162,-3137],[2071,-3494],[2366,-2480],[2029,-1174],[-1332,-828],[-7069,539],[-3382,1145],[2145,1494],[-2200,2464],[-4309,-297],[-1038,1691],[399,1744],[1860,347],[3105,4663],[-478,1367],[4461,1423],[1434,-470]],[[202996,940045],[854,1278]],[[203850,941323],[3059,416],[2400,-897]],[[209309,940842],[183,-1543]],[[209492,939299],[-2102,-2601],[-4394,3347]],[[894957,942510],[3218,-1938],[410,-1911],[-5262,383],[-3378,1020],[1956,2267],[3056,179]],[[241192,944080],[2634,-1117]],[[243826,942963],[3152,218],[2037,-834]],[[249015,942347],[-4898,-6603],[-5815,18]],[[238302,935762],[1822,-1989]],[[240124,933773],[-1340,-2325]],[[238784,931448],[-3209,-8]],[[235575,931440],[-985,4468]],[[234590,935908],[-237,5414]],[[234353,941322],[2598,-189]],[[236951,941133],[-951,2135]],[[236000,943268],[5192,812]],[[279063,941080],[3474,67]],[[282537,941147],[3000,-985]],[[285537,940162],[2547,-2480]],[[288084,937682],[-308,-1542],[-3987,451]],[[283789,936591],[-4624,-835]],[[279165,935756],[-1897,2777]],[[277268,938533],[-1780,924]],[[275488,939457],[171,2234],[3404,-611]],[[696316,937765],[-2094,-63],[637,2135],[2197,413],[1905,-2017],[-2645,-468]],[[688236,956383],[-4119,-1504],[-13684,-3964],[-2374,-2429],[-3890,-2353],[-1573,-51],[-259,-2192],[-4105,-4516],[-1482,-411],[-3954,928],[-1964,-611],[-1491,2461],[2444,1146],[2609,3958],[2546,1951],[1636,2529],[1442,-252],[3540,3042],[5725,1283],[721,1248],[4916,-268],[8032,2230],[4644,2338],[2641,-439],[1151,-2138],[-3152,-1986]],[[933113,802729],[-1883,-1229],[-69,1204],[1283,609],[1157,2200],[-488,-2784]],[[146674,804734],[4765,-1918],[2331,-5262],[1797,-1212]],[[155567,796342],[1386,-3803],[-272,-1472]],[[156681,791067],[-3041,1562],[-1198,969]],[[152442,793598],[753,1585]],[[153195,795183],[-1696,-517]],[[151499,794666],[-511,1450]],[[150988,796116],[-1665,1522],[-289,1292]],[[149034,798930],[-2506,2827]],[[146528,801757],[-1706,-61]],[[144822,801696],[-116,1881]],[[144706,803577],[1165,-240],[57,1057]],[[145928,804394],[-1647,-501]],[[144281,803893],[-798,1456]],[[143483,805349],[1189,689]],[[144672,806038],[2002,-1304]],[[345948,810042],[-1590,-1232],[443,-2495],[-2285,-5023]],[[342516,801292],[-356,-2642]],[[342160,798650],[1598,2823]],[[343758,801473],[238,-888]],[[343996,800585],[1754,338]],[[345750,800923],[-1417,-1733]],[[344333,799190],[-131,-1497],[2445,178]],[[346647,797871],[-104,-1672]],[[346543,796199],[2153,1955],[16,-1114],[1405,593],[940,-712]],[[351057,796921],[128,-1069]],[[351185,795852],[-1091,-2574],[744,-160]],[[350838,793118],[-1027,-1546]],[[349811,791572],[2807,1423]],[[352618,792995],[-218,-1523],[-1316,-1152]],[[351084,790320],[-700,-2418]],[[350384,787902],[716,-812]],[[351100,787090],[892,1988],[1158,682]],[[353150,789760],[-860,-2725]],[[352290,787035],[147,-1173],[945,1863],[357,-1304]],[[353739,786421],[-1155,-5143],[-1519,-7]],[[351065,781271],[-55,2711]],[[351010,783982],[-1493,-1524]],[[349517,782458],[846,3001]],[[350363,785459],[-896,2801]],[[349467,788260],[-1030,-2871]],[[348437,785389],[-817,58]],[[347620,785447],[-1275,-2839]],[[346345,782608],[-1470,-189],[18,1172],[964,527]],[[345857,784118],[1727,2431],[-1380,533],[-190,-946],[-1187,171]],[[344827,786307],[12,1712]],[[344839,788019],[-1010,-875]],[[343829,787144],[-2031,-574]],[[341798,786570],[-3835,606]],[[337963,787176],[-2177,-629]],[[335786,786547],[-431,2517]],[[335355,789064],[2616,3120]],[[337971,792184],[-1073,450],[869,2880]],[[337767,795514],[1177,861]],[[338944,796375],[-99,1854],[1994,6850],[1521,3414]],[[342360,808493],[2013,1738],[1575,-189]],[[341569,796584],[-2717,-3564],[704,54],[2013,3510]],[[896558,826971],[596,-1498],[-161,-2055],[849,-2951],[278,-4044],[-467,-3138],[834,-3629],[1001,-7042],[1267,-5754],[969,-2942],[-1376,2332],[-1092,614],[-1743,-671],[-1474,-6675],[-49,-1980],[1246,-3053],[590,-2534],[744,-254],[264,-2318],[-414,-1968],[-414,3143],[-1957,840],[-1391,-4644],[-686,3163],[579,4083],[-207,2651],[603,2523],[-875,4365],[766,4852],[-197,5604],[377,4192],[-1346,3044],[-171,3179],[396,1674],[56,4643],[1952,641],[500,2656],[-1032,2280],[1185,671]],[[980032,818789],[1734,-952],[-1392,-593],[-1224,1101],[882,444]],[[487744,825735],[-1037,-665],[769,1798],[268,-1133]],[[275745,817216],[-3632,1793]],[[272113,819009],[533,807]],[[272646,819816],[1977,116]],[[274623,819932],[1122,-2716]],[[488342,820617],[-491,-1109],[-540,1495],[1031,-386]],[[538081,826905],[-958,-811],[-522,1768],[684,919],[796,-1876]],[[482727,825162],[530,-6881],[-827,-4031],[-3340,-876],[-381,-705],[-3192,-2340],[-2838,-601],[710,1220],[-1503,-526],[1450,1622],[-1348,-612],[-819,579],[1685,2259],[-401,1894],[1116,1253],[599,1874],[-2271,2671],[724,3240],[-556,963],[4197,-98],[1145,2367],[-1752,239],[1359,2756],[2065,281],[868,-603]],[[479947,831107],[3027,743],[1519,-3282],[-68,-2316],[-1698,-1090]],[[491362,851389],[-286,-1151],[-2160,-2146],[-401,-2258],[2032,772],[3691,-35],[823,-1236],[-2263,-5523],[-1728,-1051],[1561,-389],[-1972,-1722],[2120,-2],[1256,-736],[1366,-1971],[1010,-4719],[3354,-3886],[-337,-570],[1560,-5105],[-861,-1507],[2805,316],[1670,-1216],[249,-1687],[-1311,-3695],[-1451,-685],[-183,-2033],[2024,-139],[-48,-1073],[-1215,-1517],[-2098,-966],[-2751,15],[-1754,779],[-1720,-1740],[-2676,672],[-1126,-500],[-1080,-2388],[-1053,958],[-1543,-594],[-1381,-1595],[-325,1332],[2109,3140],[1097,2442],[2921,99],[1953,3174],[-2388,-2076],[-3632,2057],[-838,-660],[-1000,1505],[2442,1879],[1119,2040],[-336,2214],[-1616,-648],[1589,2446],[2625,1041],[669,2003],[160,2634],[-829,-293],[-1120,2013],[375,2939],[-1455,-1083],[-3271,454],[1275,3814],[-598,1172],[132,2086],[-1505,-1666],[-1061,-2414],[440,4103],[1170,4164],[-1289,-1339],[-1334,1102],[1585,3049],[-128,3843],[1667,2259],[-162,1526],[5593,679],[-157,-707]],[[589039,490923],[-572,-747],[-84,1557],[656,-810]],[[592152,491925],[-899,584],[683,564],[216,-1148]],[[258356,772744],[-801,-2405]],[[257555,770339],[-323,707]],[[257232,771046],[1124,1698]],[[268828,776662],[1113,441]],[[269941,777103],[234,-736],[2217,752]],[[272392,777119],[160,-2629]],[[272552,774490],[-3724,2172]],[[256275,785735],[-1336,-1321]],[[254939,784414],[-604,-1184]],[[254335,783230],[-490,967]],[[253845,784197],[615,1271]],[[254460,785468],[1815,267]],[[516113,815548],[-838,-971],[-918,455],[1358,1238],[398,-722]],[[189596,872516],[-2362,-2950],[-1044,1470],[3406,1480]],[[581880,871836],[-1354,-975],[-795,1658],[1719,282],[430,-965]],[[309615,809192],[-402,-1402],[-1162,1505],[715,1124],[849,-1227]],[[708031,725294],[-1442,-438],[470,-803]],[[707059,724053],[-1499,-1179],[-646,387],[-3185,-349],[-2784,-2329],[-1209,-2336],[673,-1235],[536,-3855],[-1819,-3866],[239,-2848],[-1766,-588],[-1170,600],[-352,-913],[1156,-3132],[-1011,-1519],[-1163,-548],[-722,-3475],[105,-2943],[-1140,-1792],[-754,999],[-1212,0],[-1619,-1756],[443,-963],[-1252,-747],[-1008,520],[-1464,-2331],[-612,-6378],[-3004,-1636],[-1595,30],[-1174,-1023],[-1475,629],[-3031,-532],[-4536,2668]],[[669009,681613],[724,1598]],[[669733,683211],[1889,4168],[-344,3262],[-2332,668],[24,4468],[-747,5264],[990,2176],[-1128,792],[-70,2701],[1122,1331],[-454,1178],[625,803],[864,5722]],[[670172,715744],[779,-959],[1226,-83],[900,-1617],[2080,1629],[144,2209],[2094,1148],[1802,1945],[848,4688],[2051,706],[584,1884],[2103,-1308]],[[684783,725986],[1519,-81],[1917,-963]],[[688219,724942],[857,-1318],[2480,2224],[846,-1284],[631,2634],[2109,659],[-102,1541],[1845,3152],[1047,-885],[63,-2302],[760,87],[-331,-4773],[457,-2338],[568,-228],[3915,4231],[1415,61],[80,-1108],[1417,1088],[1110,-124],[645,-965]],[[566573,440308],[223,-3162],[-210,-1365],[56,-4659],[-303,-2233],[224,-1122],[-5511,-74],[2,-17504],[475,-3801],[429,-547],[2988,-5635]],[[564946,400206],[-5455,-2133],[-1865,-113],[-979,784],[-3657,413],[-996,678],[-893,1800],[-7308,58],[-5077,5],[-1484,2257],[-840,238],[-1537,-1453],[-1483,262],[-753,-478]],[[536300,469971],[3696,-165],[5324,160],[1118,-2226],[-24,-1364],[765,-4655],[1532,-4849],[3103,828],[1910,-181],[519,4871],[369,636],[2606,604],[25,-2030],[3176,-164],[252,-684],[-171,-2633],[321,-2818],[-184,-4902],[75,-2523],[948,-2644],[303,-3855],[-358,-1191],[280,-1789],[784,819],[1655,-111],[676,583],[1205,-221],[368,841]],[[533384,475069],[1017,2282],[1379,1031],[533,-1124]],[[536313,477258],[-1726,-2587],[145,-3699],[-806,-372]],[[555733,756786],[1170,-1918],[225,-2072]],[[557128,752796],[-215,-3561],[700,-2177],[620,-328]],[[558233,746730],[186,-1133],[-1038,-3206],[-962,-818],[-289,-1931],[-571,332]],[[553728,752769],[-3,1422]],[[553725,754191],[148,-125]],[[553787,755260],[-14,-11]],[[553773,755249],[822,2019],[1138,-482]],[[656633,652705],[-901,-1424],[-745,766],[-96,-3705],[623,-1063],[-1215,-426],[-109,-1581],[-858,-4087],[-39,-1959]],[[653293,639226],[-226,-489],[-7081,1844],[-2674,6790],[-67,1228]],[[655778,659124],[179,-2205],[425,-236]],[[309296,179739],[65,13040]],[[325969,372995],[1214,-2244],[794,-2648],[3023,-4732],[2632,-1395],[1443,-2135],[2799,-2994],[1510,-1049],[718,-1999],[-1777,-5378],[32,-1472],[-1251,-3354],[102,-701],[1213,243],[4809,-1661],[758,1376],[1249,-553],[416,1569],[2054,2949],[410,2035],[172,4341]],[[348289,353193],[1281,314],[732,-864],[611,-3295],[-464,-5309],[-1358,-1791],[-1524,-1041],[-627,-1585],[-1733,-1999],[-1389,-3158],[-1981,-5081],[-1862,-3513]],[[339975,325871],[-732,-2389],[172,-1585],[-992,-6008],[-93,-3549]],[[309879,194532],[-49,393],[-4164,1672],[-5440,110],[-1359,2659],[188,5089],[-2258,-334],[-967,3631],[-209,3213],[319,1594],[906,79],[427,1919],[1020,1089],[17,1621],[876,1719],[-625,2090],[478,2273],[1225,1724],[-98,2195],[680,1497],[-501,2476],[678,1225],[-318,2221],[1090,2064],[-1676,2601],[1933,168],[135,1907],[-1687,344],[388,2687],[-624,2900],[343,1619],[-1014,1048],[61,4098],[1010,1166],[-418,2671],[-58,5681],[658,2112],[-342,939],[775,3402],[316,3654],[1317,1465],[-213,4131],[-387,1652],[137,3839],[-205,1604],[379,1895],[1808,2737],[-182,4358],[501,3515],[661,2560],[554,453],[-116,2920],[207,2652],[-556,73],[-416,4738],[-1154,5345],[182,2495],[995,4195],[570,486],[79,3490],[-275,2637],[552,1308],[475,4086],[1341,2896],[911,4568],[1390,745],[-654,3019],[463,2161],[-516,3958],[600,2332],[-493,1506],[866,2641],[2483,2122],[965,6117],[-517,1064]],[[313347,369511],[1342,3587],[963,607],[403,1844],[1247,-1760],[1982,-19],[1256,-746],[778,-3548],[970,4473],[438,398],[2709,48],[534,-1400]],[[629140,735218],[-1045,-171]],[[628095,735047],[-1011,4059],[-1605,45],[-392,1153],[-731,-365]],[[624356,739939],[-1331,1995],[-1609,748],[-391,1871],[426,1405],[-786,2296]],[[620665,748254],[4338,1089]],[[625003,749343],[1628,-2630],[-587,-1238],[1635,-2395],[-1069,-1518],[1728,-2269],[490,-1257],[312,-2818]],[[547091,792639],[-243,-1257],[783,-2256]],[[547631,789126],[-224,-1768],[-1433,236],[-271,-4387],[-1001,-852]],[[544702,782355],[-376,-1098],[-2658,-307],[-1381,-1238],[-2232,611]],[[538055,780323],[-3644,1082],[-608,2248],[-2569,-631],[-609,-1058],[-1590,402]],[[529035,782366],[-2424,1140]],[[526611,783506],[-146,1264]],[[526465,784770],[74,1425]],[[527029,786288],[-98,96]],[[526931,786384],[1715,-1361],[327,1349],[1529,-847],[3412,1897],[1324,-290],[912,-1134],[-555,4045],[2391,2146],[388,1445]],[[538374,793634],[651,-975],[1784,-19],[780,2279],[3014,-1357],[1168,269],[1320,-1192]],[[628095,735047],[-1763,761],[-1840,3816]],[[624492,739624],[-136,315]],[[635746,732426],[-767,-144],[-1582,2417],[608,947],[-294,1975],[517,514],[-907,1688],[-619,-210],[-3562,-4395]],[[625003,749343],[777,940],[1422,-1334],[1847,-913],[596,1283],[-1362,2193],[688,1386]],[[628971,752898],[888,-464],[1421,-2948],[1667,-606],[1977,3743]],[[584871,490497],[-360,-1430],[252,-1635],[737,-400],[28,-1715],[-1015,-1862],[-771,-2941],[-1390,-2187]],[[582352,478327],[16,203]],[[581384,484839],[-244,85]],[[581140,484924],[38,1702],[-583,1975]],[[580595,488601],[135,697],[909,-1220],[1328,546],[172,2233],[1732,-360]],[[515815,805530],[552,-132]],[[516367,805398],[282,-12]],[[516649,805386],[1030,-2573],[-689,-1157]],[[516990,801656],[-1035,-1192],[126,-2260]],[[516081,798204],[-784,-162],[-1776,1643],[-21,2060],[-1901,-1041],[-3,1696],[-1410,464],[-1556,2693],[-742,-400],[-875,2283]],[[509305,809102],[1534,-1008],[900,1060]],[[511739,809154],[770,523],[2704,-1124],[973,-945],[-371,-2078]],[[509987,574011],[-300,-1782],[636,-1871],[328,-2798],[-718,-2009],[-52,-2138],[-645,-764],[-778,-4115],[-751,-209],[-246,-6960],[246,-6885],[-191,-2029]],[[504506,541548],[432,461],[-556,2327],[131,1836],[-69,12162],[-488,1392],[-262,4218],[-1528,2148],[335,3754]],[[502501,569846],[1462,2689],[1538,-170],[1135,2836]],[[506636,575201],[-65,1924],[1423,864],[1993,-3978]],[[500603,593059],[-148,-2454],[1262,-4703],[1619,-2049],[-591,43],[-3,-1913],[1605,-2408],[1413,465],[423,-1468],[-374,-1115],[827,-2256]],[[502501,569846],[-1157,-7],[-1535,732]],[[499809,570571],[-641,304],[-1117,-1054],[-5912,56],[-236,-2406],[356,-1128],[57,-4407],[195,-1047]],[[492511,560889],[-336,-329],[-1131,2782],[-1816,-3],[-1262,-1476],[-1772,1684],[-361,1846],[-1177,1093]],[[484656,566486],[92,3651],[530,969],[32,3685],[1362,1210],[1026,1810],[-145,1982],[705,720],[-284,1927],[772,1561],[1320,-1116],[762,513],[287,2323],[781,40],[120,1607],[1158,1915],[955,-626],[389,1707],[571,175],[1995,1976],[803,1352],[1457,69],[1259,-877]],[[757152,634925],[157,-3980],[-836,791],[-419,-869],[401,-2970]],[[747364,635607],[-862,7959],[-482,1409],[461,3297],[-1633,1510],[-339,842],[351,1699],[454,-195],[397,1817],[1205,35],[-336,1754],[-880,497],[-1021,1860],[1204,3729],[457,-1339],[2409,-1697],[192,1247],[566,-1625],[-24,-3768],[1737,-875],[4473,69],[1163,-1335],[-603,-290],[-521,-3085],[-519,-1061],[-1416,-603],[-574,-2565],[430,-3295],[845,-739],[884,3110],[-23,1075],[879,-15],[321,-4470],[361,-1443],[232,-4191]],[[577817,753361],[-1332,-286],[-666,940],[-1888,-679],[-818,-1471]],[[573113,751865],[-708,-257],[248,-1412],[-2511,-1133],[-2036,1830],[-2453,-982],[-1998,-299]],[[563655,749612],[249,2255],[-469,1639],[-1369,1898]],[[562066,755404],[499,753],[-158,2377],[1417,2048],[-1173,1579],[-372,3276],[790,1365]],[[563069,766802],[899,-947],[-305,-1443],[849,234],[6313,-1203],[1996,1993],[2420,949],[2215,-1068],[460,-976],[1487,-475]],[[552797,770541],[949,71],[-547,-3427],[1200,-1535],[-941,-464],[675,-1549],[-816,-1009]],[[553317,762628],[-466,-1427],[-979,-366],[-640,-1554],[-21,-2421]],[[551211,756860],[-2135,1999]],[[548847,759103],[-865,3006],[-1556,1974],[-1387,2584],[-233,1533],[-1094,1730],[143,2448],[1404,-1008],[659,1230],[3561,-820],[2361,-4],[957,-1235]],[[578188,837332],[1797,-1187],[1612,-22],[297,-1505],[2088,951],[1870,-1630],[197,-3078],[-497,-1583],[895,-799],[785,-2681],[1079,-829],[-105,-1454],[1933,-697],[706,-2112],[-1562,-1453],[-2012,622],[-442,-1063],[768,-1294],[117,-2880],[517,-1252]],[[588231,813386],[-2174,-324],[-1244,-2665],[32,-1963],[-1066,1260],[-2262,-563],[-585,1389],[-951,-633],[-1693,578],[-2538,33],[-356,822],[-3380,955],[-4343,-272],[-2101,-2070]],[[565570,809933],[131,3095],[-1266,1283],[1800,2413],[118,2152],[-1118,5405]],[[565235,824281],[3565,206],[2164,2116],[867,3481],[2545,2096],[-883,411],[377,1926]],[[573870,834517],[1275,965],[1457,-188],[1586,2038]],[[253072,598860],[-954,23],[211,11377]],[[252329,610260],[78,924],[908,-31],[788,2846],[631,157]],[[338445,385253],[-57,2053],[-2529,3151],[-2546,-67],[-4860,-2061],[-445,-2429],[-998,-3004],[-1,-2984],[-1040,-6917]],[[313347,369511],[-1901,-7],[-303,4537],[-550,2598],[-29,1886],[-936,2231],[94,1845],[-681,910],[130,4369],[654,1708],[-1404,2753],[-349,5437],[-609,636],[-549,2589]],[[306914,401003],[-317,1812],[518,663],[1114,3294]],[[308229,406772],[86,-108]],[[307332,412821],[-12,12]],[[307320,412833],[534,1615],[-562,1622],[388,2167],[985,2360],[-538,3056],[252,1105],[13,3652],[815,2240],[-2481,9184]],[[306726,439834],[2028,-352],[336,-660],[915,614],[907,1871],[972,119],[387,1049],[1308,1404],[1060,1739],[1295,885],[2410,673],[230,-3202],[-352,-1975],[323,-2598],[5,-2456],[915,-3174],[1331,-1634],[258,-1119],[2319,-469],[664,-955],[775,65],[839,-1944],[1637,-809],[1073,-2321],[1980,213],[1585,-1778],[89,-2340],[488,-2570],[71,-2786],[-861,-56],[947,-2259],[185,-4679],[4549,-350],[535,261],[-348,-2168],[208,-3460],[1565,-1647],[718,-4544],[-629,-4749],[-920,-3931],[752,-1393],[-830,-1096]],[[343103,516223],[1286,-592],[290,1169],[-594,1540],[537,1287],[571,-655],[2088,1135],[1007,-1605]],[[348288,518502],[1350,-1219],[1007,1385],[2230,-1015],[734,1068],[1972,7928],[939,2129]],[[351748,304813],[-452,1152]],[[351296,305965],[18,-30]],[[352392,311289],[-83,-134]],[[352309,311155],[-1203,1592],[-444,2051],[-1275,1195],[-1020,2192],[-1852,1538],[-841,2071],[-1243,-1204],[-476,2670],[-1824,3088],[-1060,-1043],[-1096,566]],[[348289,353193],[15,849]],[[348304,354042],[381,1259],[573,6839]],[[349258,362140],[-996,1501],[-992,-960],[-1066,-98],[-477,2430],[-221,5388],[-643,2156],[-946,156],[-570,1117],[-1506,-1058],[-2830,960],[349,6583],[-915,4938]],[[306726,439834],[-1061,129],[-1199,-762],[-695,286],[15,9077],[-1669,-2890],[-2622,-223],[-548,2924],[-2307,585],[654,2478],[-1597,3834],[-629,2426],[153,912],[-783,1342],[783,1462],[-105,2390],[1438,2025],[292,1301],[-278,1457],[710,2747],[258,3034],[523,329],[2372,3334],[2420,912],[483,1049],[1097,138],[460,-895],[759,385]],[[305650,479620],[1571,18018],[-742,4221],[-1120,2035],[47,4251],[1616,897],[827,-561],[31,1355],[-551,1185],[-1363,-27],[10,3846],[4644,65],[-48,1584],[567,-1389],[1362,2105],[410,-131],[727,-2787],[-10,-2401],[605,77]],[[314233,511963],[1595,-2791],[1723,1371],[578,-1731],[1027,2469],[1034,861],[1713,2168],[220,1690],[1782,1884],[13,1122],[-1832,671],[31,1638],[-503,2388],[-6,2267],[-1658,3821],[1562,-545],[650,-1251],[2019,-41],[906,-1945],[567,468],[145,2044],[838,822],[715,-345],[1664,1122],[761,1357],[770,109],[1107,2721],[-382,1229]],[[331272,535536],[1666,218],[421,-924],[-439,-3256],[1237,-901],[423,-2652],[-843,-2050],[65,-1412],[-453,-3906],[664,-2463],[-3,-2213],[1459,-3108],[1024,-1021],[974,479],[475,1795],[2073,691],[1321,1836],[784,-787],[983,361]],[[819833,533745],[518,-3074],[-610,57],[-223,3017]],[[819518,533745],[-778,-1076],[260,-1925],[-644,-2187],[-1513,3369]],[[816843,531926],[317,-10]],[[754532,669180],[-103,-1199],[1100,-637],[230,-3171],[-1116,-669],[-2589,-179],[-1094,703],[-1617,-1119],[-2517,1540],[94,2101]],[[746920,666550],[1793,4688],[1234,1207],[1020,-398],[12,-970],[2014,-627],[411,574],[1055,-708],[73,-1136]],[[570163,399300],[-97,-721],[1492,-4348],[1131,-5268],[1417,-2100],[1509,-1499],[164,-1972],[1164,-308],[-84,-3161],[1045,-3014],[2755,-1412],[193,-1507],[716,-760]],[[581568,373230],[-652,-114],[-806,-1586],[-1749,-1260],[-888,-2253],[-2510,-3737],[-422,-3177],[-1064,-2025],[-1499,-976],[-912,-5088],[-390,-641],[-1932,-610],[-2373,1283],[-1744,1980],[-1075,-1133],[-663,-3633],[-1526,-3016],[-1235,-1623],[-2518,31],[-314,2400],[523,1652],[-61,1477],[-1244,5248],[-1013,1499]],[[555501,357928],[-9,16450],[2760,0],[9,21810],[2881,712],[3024,1120],[552,-106],[783,-2521],[2162,2813],[477,-441],[1051,1370],[972,165]],[[563500,569410],[1256,-3150],[928,-3348],[-66,-2857],[-429,-1338],[192,-1771],[1694,-890]],[[567075,556056],[401,-2217],[1560,-911],[1095,-2447],[-159,-1216],[1941,-2692],[1314,-2545],[-148,-1067],[571,-2287],[1581,-1732],[889,-3956]],[[576120,534986],[-801,526],[-814,-803],[-3603,1479],[-766,-1703],[-1343,-560],[-1239,380],[-2507,-1961],[-837,437],[-1000,-535],[-927,-3032],[-2042,868],[-415,-217],[-2721,1291],[-921,2174],[-1166,1538],[-849,227],[-1201,-1399],[-1392,-3755],[119,-4616]],[[551695,525325],[-378,856],[-871,-729],[-2008,1094],[-2124,-885],[-492,-1933],[-77,-2234],[-792,-3328]],[[544953,518166],[-333,3783],[-801,1295],[-1795,4145],[-295,3150],[-871,1819],[-406,3640],[150,3467],[-516,1028],[856,1428],[1407,5829],[651,1541]],[[543000,549291],[1013,-287],[1483,1234],[463,1078],[665,-1864],[2402,2563],[2617,458],[1436,3527],[-612,1384],[714,748],[1453,29],[1871,629],[1198,1650],[1363,3371],[1283,2322],[-54,1234],[935,1469],[1252,1028],[1018,-454]],[[313542,772321],[-966,631]],[[312576,772952],[63,1967]],[[312639,774919],[-31,200]],[[311702,775814],[-38,-22]],[[311664,775792],[-16,7865],[-1191,1559],[-1648,-845]],[[308809,784371],[-1151,1538]],[[307658,785909],[-2124,-4467]],[[305534,781442],[-802,-4756],[-1671,-3814]],[[303061,772872],[-1193,164]],[[301868,773036],[-528,-1674]],[[301340,771362],[-8865,-22]],[[267330,873831],[-420,723]],[[292475,771340],[-1307,-619]],[[291168,770721],[-1718,-2421]],[[289450,768300],[-20,24]],[[279963,760904],[422,241]],[[280385,761145],[-13,-1049]],[[270430,756850],[633,2722]],[[251257,789167],[-84,-67]],[[251173,789100],[-3508,1179]],[[247665,790279],[-1884,-843]],[[245781,789436],[-4105,3279]],[[241676,792715],[-1975,-511]],[[239701,792204],[-2537,1286]],[[237164,793490],[-255,716]],[[236909,794206],[-395,2614],[-834,385],[-19,-2240],[-6577,10],[-10660,-1]],[[218424,794974],[-4738,0],[-7106,0],[-10660,0],[-8290,0],[-5922,0],[-5922,0],[-8292,0]],[[167494,794974],[-8574,0]],[[138819,835824],[-202,1310]],[[138617,837134],[-4105,2900]],[[134512,840034],[-691,-52]],[[133821,839982],[-576,2586],[-4969,9944],[-3121,3456]],[[125155,855968],[-298,1719]],[[124857,857687],[-1180,1271],[-2349,-1115]],[[121328,857843],[-714,-2681],[-2388,-1476]],[[118226,853686],[-430,1914]],[[117796,855600],[-4423,5079],[295,1541],[-2484,-951]],[[111184,861269],[-2857,694]],[[108327,861963],[0,55397]],[[526465,784770],[146,-1264]],[[529035,782366],[-61,-1865],[-955,295],[-410,-1411],[-1912,-444],[-826,-2706],[-1620,3644],[-1618,-3101],[-2130,24]],[[519503,776802],[-732,2903],[-913,87],[-951,-1680],[-73,1667],[2612,5298],[146,989],[1562,611]],[[521154,786677],[3515,378]],[[524669,787055],[685,84]],[[525354,787139],[153,0]],[[304393,396029],[1367,827],[206,2976],[948,1171]],[[868915,771709],[-4,489]],[[866863,773017],[-131,-309]],[[866732,772708],[-479,546],[-1125,-2031],[-1011,-439],[480,-4967],[17,-3784],[-536,-3144],[-1788,-1038],[284,-1135]],[[862574,756716],[-796,2111],[-950,631],[-496,-3100],[-1128,-364],[-1084,-2223],[-2440,-301],[683,-2516],[-499,-1028],[-2588,842],[-767,1479],[-841,-830],[-307,-1676],[-1392,-2686],[-3055,-2636],[-1465,-2700]],[[817405,638260],[-696,-172]],[[815489,636816],[-446,-704]],[[799923,632140],[-474,813],[-1252,-215],[-958,1686],[-952,506],[-354,2468],[678,2272],[-662,767],[-1942,85],[-1576,2503],[-1141,-1238],[-192,-1334],[-1177,-1227],[-883,286],[-386,-1268],[-819,1444],[-414,-1094],[-475,990],[-819,-1845],[-1356,1706],[-1082,-2143]],[[783687,637302],[-1267,492],[-408,-1236],[589,-2531],[-88,-4007],[-1335,436],[-237,2037]],[[780941,632493],[-51,1058],[-1637,-1706],[-879,29],[-657,1413],[-169,1934],[-2013,580],[534,4142],[-123,1605],[-1325,565],[-88,2566],[-420,1288],[425,1474],[-2270,-149],[-1256,-915],[350,1302],[-442,2138],[800,4503],[981,2031],[1258,1375],[295,4483],[-224,5860],[-1139,537],[-395,2839],[-1558,2179],[-599,-1731]],[[770339,671893],[-2000,1433],[-891,-283],[831,2083],[-404,1700],[-759,-835],[494,1778],[-846,1406],[-1443,-1426],[-266,-901],[-1807,720],[-407,809],[-2002,-3017],[-1933,-1258],[-242,-1117],[-1160,-1512],[-104,-1174],[-1907,-1295],[-961,176]],[[746920,666550],[-396,1219],[277,2055],[-632,1322],[-1420,-1311]],[[744749,669835],[-2690,-191],[-1631,1463],[-406,-928],[-915,918],[-347,-920],[-765,2068],[-1544,229],[101,1636],[-1235,20],[-1349,1873],[-354,1826],[-1438,-215],[-1189,2542],[-837,419],[-1932,2558],[-321,1254],[-1739,64],[-450,-1448],[-680,422]],[[725028,683425],[-912,1483],[-1363,910],[-741,1898]],[[722012,687716],[-22,32]],[[721990,687748],[-1520,1101]],[[720470,688849],[-85,152]],[[720385,689001],[-645,1760],[-875,-646],[-201,3519]],[[718664,693634],[-916,3746],[865,457],[606,-1415],[834,846]],[[720053,697268],[-8,373]],[[720045,697641],[-226,3602]],[[719819,701243],[-63,322]],[[719756,701565],[-863,1620],[-136,3483],[605,833],[-833,1717],[-1080,805],[-749,3537],[-560,1383]],[[716140,714943],[-31,68]],[[716109,715011],[-981,-120],[-1888,1102]],[[713240,715993],[-598,1335],[-1121,-344],[-675,1538],[193,1741],[-372,1584],[-1156,524],[-215,1038],[-2237,644]],[[708031,725294],[631,913],[-623,1278],[-415,5383],[-1298,887],[-1322,-313],[-17,2341],[-455,2647]],[[704532,738430],[786,934],[214,2587],[2361,1788],[66,880],[1994,662],[261,-1774],[2230,851],[954,3157],[3611,553],[664,1753],[2586,2436],[2562,1479],[-18,934]],[[722803,754670],[-124,2817],[1040,1232],[-414,1005],[1099,702],[-1196,5543],[279,3844],[-1273,303],[172,1240],[2205,727],[2330,1304],[826,-1111],[1360,-226],[288,1889],[-711,459],[1953,9870],[2740,-1276],[1807,11],[332,-841],[1941,1380],[477,1133],[-363,3916],[621,2780],[2222,852],[566,2845],[1582,456]],[[742562,795524],[1366,453]],[[743928,795977],[-198,-1664],[657,-1934],[1493,-1010],[1474,-2263],[1426,8],[2089,-1942],[509,-2316],[1038,-1959],[455,-2521],[-89,-2922],[-945,-3025],[599,-1951],[1963,-707],[3343,-242],[2414,-798],[2932,-3260],[1773,-431],[1561,-6349],[1315,-2879],[2278,411],[6283,-1313],[1434,647],[4806,-1253],[719,-1481],[3056,-1244],[1773,-1508],[2185,744],[0,-1293],[1345,-374],[597,844],[4370,3263],[3892,939],[3533,51],[2659,1883],[1686,3363],[2571,2192],[-1475,3886],[609,2724],[768,1404],[1427,-35],[515,-833],[2750,-1019],[1232,1167],[1352,2500],[3233,555],[1555,2001],[894,2926],[2140,427],[2709,2104],[1488,256],[2396,-915],[532,1493],[-519,1731],[-1748,2986],[-1621,1955],[-2027,23],[-1161,-1989],[-1639,1288],[-1471,-68],[-924,-1014],[-946,1529],[1100,4410],[2026,6721]],[[824119,799896],[3306,-1839],[1606,1961],[2246,1315],[-268,2012],[2508,7077],[1708,2206],[-70,3518],[-1635,391],[75,915],[1693,2279],[4538,1855],[3898,153],[2976,-2233],[730,413],[1594,-956],[1844,-3806],[1368,-5297],[332,-2403],[1061,-2324],[-2,-1506],[789,-1449],[-243,-1989],[1381,-1805],[1956,186],[1156,-1410],[1050,159],[1939,-2946],[992,-180],[697,-3080],[-256,-1266],[808,-2584],[2173,-65],[2158,521],[402,1058],[2116,889],[2291,1637],[751,-306],[523,-3592],[-1623,-2448],[96,-1032],[-932,-3727],[-15,-1488],[-1876,-4461],[-201,-2157],[-844,-383]],[[492511,560889],[-141,-2202],[406,-1832],[263,-3506],[-789,-1640],[-545,-4307],[-694,-2356],[98,-2719],[662,-4178],[468,-254],[-61,-2649],[-566,-132]],[[479041,530496],[-66,4321],[385,1445],[-67,3062],[-952,792],[-254,1539],[-1986,1617],[752,1741],[100,1614],[-527,2870]],[[476426,549497],[707,-10],[598,3484],[-665,645],[53,1196],[1544,-268],[-750,2230],[480,1742],[-327,1985],[-670,473],[2,3118],[405,832]],[[477803,564924],[915,1570],[1926,-1488],[763,1026],[109,1819],[685,-498],[406,898],[55,-2635],[575,-500],[530,1153],[889,217]],[[544953,518166],[-344,-3518],[-883,1414],[-2331,576],[-1162,845],[-3307,40]],[[536926,517523],[-203,562],[-5200,257],[-55,-784]],[[531468,517558],[-3747,1],[-497,811]],[[523766,532889],[681,2620],[720,4809],[1666,3097],[333,1352],[1010,1400],[941,-623],[344,1018],[1184,-2164],[336,-1540],[1106,1537],[79,1135],[782,1348],[-261,923],[690,1881],[604,4103],[473,1856],[1119,1724],[342,3198],[683,671],[262,2942],[738,3370],[991,3170],[1854,2087],[187,3651],[-300,1123],[-893,507],[-371,4116]],[[539066,582200],[1256,-585]],[[540322,581615],[681,-1920],[889,-4800],[-143,-4336],[684,-4480],[1052,-2071],[-2275,-392],[-1646,226],[-739,-1708],[986,-2891],[2178,-3828],[908,-4180],[103,-1944]],[[576120,534986],[1069,-2752],[1122,-1744],[654,-155],[832,1072],[1179,-692],[883,1325],[576,-148],[1439,-3584],[871,-867],[917,-2043]],[[585662,525398],[-235,-2660],[258,-1154],[-328,-2320],[1484,-1752]],[[586841,517512],[-11,-38]],[[584660,512056],[-1486,-2485],[-23,-1897],[-698,-3669]],[[582453,504005],[-110,3]],[[582340,501712],[2,5]],[[582342,501717],[-184,-5222]],[[582158,496495],[-988,-1767]],[[581170,494728],[-66,250]],[[580139,490215],[21,11]],[[580160,490226],[435,-1625]],[[581140,484924],[-41,14]],[[584945,456249],[-9,-232]],[[584936,456017],[-4664,-1572],[55,-1274],[-1437,-3106],[637,-3593],[25,-4964],[-783,-4821],[349,-1950],[1616,-3180],[1009,-488],[367,1355],[654,279],[0,-7331],[-670,852],[-1499,-710],[-1824,5254],[-2290,1699],[-936,3496],[-686,-1740],[-981,-434],[-1584,486],[-1880,1582],[-83,2288],[-2734,-798],[-42,1776],[-982,1185]],[[536313,477258],[950,-1200],[751,881],[88,1388],[622,-179],[1160,1097],[145,-3151],[826,-299],[2478,5041],[757,573],[762,2785],[196,2570],[-6,5051],[904,2000],[1205,4149],[845,831],[1317,2669],[-80,1609],[454,3031],[41,5237],[432,2469],[40,2835],[1163,5398],[332,3282]],[[530917,481515],[1039,2346],[958,-1045],[236,2240],[-1101,2855],[188,2927],[1275,-414],[1061,489],[-40,2376],[1004,-17],[551,-2260],[1314,-486],[747,1522],[425,-1938],[557,-8],[824,3418],[199,2825],[-125,2613],[194,2096],[-1723,2459],[68,2335],[563,2047],[964,1630],[-704,3310],[-916,287],[-1603,-1053],[-309,2412],[363,3042]],[[301889,574992],[-1773,-1158],[-807,-2784],[-548,-487],[-1176,-3691],[-381,-4160],[-972,-3331],[828,194],[728,-892],[363,-2852],[692,-1455],[-74,-5493],[654,-501],[875,-2251],[2443,24],[995,524],[1555,-858],[1822,-4758],[2687,128],[2511,505],[357,-1281],[-1071,-4473],[-84,-4524],[538,-3807],[973,-2657],[-1454,-3099],[600,-588],[1133,-2390],[930,-6914]],[[305650,479620],[-1038,2498],[-1099,196],[1837,6109],[-61,546],[-2274,2604],[-1340,-684],[-988,1074],[-644,-1030],[-1142,-606],[-1366,121],[-742,772],[-118,2654],[-832,813],[-466,2631],[-1617,1649],[-477,2310],[-1066,2255],[-1341,553]],[[290876,504085],[-1653,1527],[-1198,1762],[-510,-1262],[-1182,196],[-1396,926],[-125,1254],[-2346,2427],[-1521,2424]],[[283608,547547],[469,2853],[404,-994],[1085,2544],[-784,3116],[289,947]],[[270656,561454],[-1045,-756],[-1,-2305],[553,-642],[-488,-1252],[154,-1699],[-450,-815],[400,-1454]],[[261820,570254],[343,725],[1978,-1417],[578,633],[980,-428],[500,-1182],[992,-220],[470,1031]],[[594456,712459],[-1330,-157],[-394,735],[-1864,49]],[[541137,806029],[512,920],[1002,-1200],[2959,-1412],[-582,-888],[1302,-1932],[863,826],[-304,1127],[2763,-2695],[1910,-550],[749,-2184]],[[552311,798041],[-1865,-1501],[-1117,-2187],[-1584,-162],[-654,-1552]],[[538374,793634],[-3286,4113],[-987,3444],[489,1821],[5323,3251],[1224,-234]],[[527054,829528],[17,-108]],[[539583,823049],[24,-14]],[[539607,823035],[433,-2642],[-766,-2078],[1335,-2395],[370,-2647],[-419,-1477],[1152,-3435],[-575,-2332]],[[526931,786384],[-142,138]],[[521154,786677],[-87,2795],[705,3387],[823,2000],[-1899,1057],[-1987,51],[-1086,1730]],[[517623,797697],[397,2049],[-1030,1910]],[[516649,805386],[-276,1385],[830,2990],[-680,1620],[2204,880],[815,2780],[450,5344]],[[524116,829329],[-32,661]],[[524084,829990],[2970,-462]],[[620127,572847],[-898,-2965]],[[619229,569882],[-1014,483],[-2109,-595],[-89,3606],[1700,5198]],[[617717,578574],[810,-533],[1241,1968]],[[526959,830136],[95,-608]],[[524084,829990],[-25,488]],[[300643,611589],[18,1790],[-662,1520],[714,800],[239,2357],[-339,3480]],[[523823,723550],[-1021,-2619],[388,-754],[-286,-2947],[412,-3949],[-412,-2784],[-2033,-3872],[-38,-1469],[642,-3341],[1333,-2025],[340,-2270],[1974,-2792],[1318,-10918]],[[526440,683810],[-579,-677],[917,-2836],[562,-3966],[-75,-2410],[279,-4589],[-468,-2695],[408,-2861],[-97,-1753],[-1023,-1293],[-119,-1579],[1534,-4355],[76,-1665],[633,-2726],[1195,-235],[2363,-1543],[1198,-4579]],[[533244,644048],[-5754,-7235],[-6708,-8434],[-4570,-8259],[-4469,-1992]],[[511743,618128],[-2297,-915],[-819,958],[417,1545],[-146,2244],[-2215,1625],[-519,1089],[-1483,774],[-207,1050],[-1236,1551],[-57,1687],[-7683,10714],[-8895,12352]],[[486603,652802],[-6023,7672],[-4701,5897]],[[475879,666371],[0,2195]],[[475879,668566],[64,6293],[2709,3738],[1639,1633],[1277,-334],[374,1424],[2922,876],[1335,3012],[1793,1383],[1939,2174],[-580,782],[19,2750],[2248,1021],[240,1234],[1340,518],[3259,-243],[582,2247],[-1234,2425],[-470,2613],[-323,8491],[-1178,2087]],[[290876,504085],[-949,-96],[1008,-2562],[38,-2349],[-440,163],[-452,-3596],[-1442,-3565],[-1637,-2546],[-3282,-2482],[-1346,-2462],[-207,-2249],[-721,-3252],[-19,-1403],[-1084,-2536],[-707,372],[-854,2801],[-1392,942],[-678,-993],[-351,2335],[919,1136],[-404,2903]],[[594994,690286],[131,-677]],[[595125,689609],[1831,-10255]],[[602420,635036],[-6380,-3],[-8723,-3],[-5194,-4],[-6367,2],[-6367,2]],[[569389,635030],[0,42574],[-705,6331],[687,3116],[-336,3308],[827,1897]],[[617717,578574],[-1184,2464],[-520,1787],[-1117,1871],[-1648,3819],[-1522,1699],[-1916,625],[-927,-339],[-344,881],[-1583,-1207],[-1723,2535],[-869,-4166],[-872,1805],[-647,-1077],[-1389,-90]],[[601456,589181],[-271,5185],[264,700],[1089,6197],[-73,1946],[337,2573],[1117,16],[1032,2348],[1308,751],[989,2490]],[[495015,761882],[1066,-991],[-194,-1001],[3281,-1456],[717,-807],[1868,3],[182,921],[2032,-1477]],[[504739,756526],[-772,548]],[[504739,756526],[906,-888],[1720,-77],[1555,537]],[[479427,724985],[-272,2406],[1337,2720],[-890,2445],[959,3549],[-485,467],[-1009,3118],[1386,311],[629,3727],[-328,3946],[1989,3098],[-917,832],[-211,1599],[-1469,229],[-712,-873],[-2281,368],[32,1409],[-1567,-1141]],[[577812,857129],[-955,-2975]],[[576857,854154],[-403,147]],[[576732,848420],[54,9]],[[576786,848429],[-809,-2889]],[[575977,845540],[-2276,17],[-1504,1820],[-2445,1334],[-2190,-1143]],[[619229,569882],[-731,-2239],[506,-2478],[944,-1914],[836,-2966],[1501,-2331],[8209,-5859],[2778,0]],[[633272,552095],[-4320,-8885],[-4118,-9392],[-2643,228],[-2398,-1813],[-928,-2088],[-2132,-913],[-389,-949]],[[616344,528283],[-1842,-203],[-1266,1953],[-2564,-2498],[-966,-2342],[-3912,1141],[-3279,4519],[-2288,226],[-886,2123],[-50,3175],[-1324,879]],[[597967,537256],[-518,1071],[-1031,5849],[-1796,3350],[-1106,2638],[-1222,531],[-593,1130],[616,2636],[1997,279],[392,822],[-45,5209]],[[594661,560771],[593,3930],[-44,2389],[822,2086],[999,-91],[503,5639],[1343,4270],[1421,1120],[291,3227],[495,2103],[372,3737]],[[580460,913634],[-1375,-3161],[595,-1770],[1830,-758],[1765,-2211],[-2478,-4251],[2267,-5213],[534,-2425],[-767,-669],[-598,-3557],[1302,-1205],[98,-2364],[1099,-2046],[-1388,-1619],[3269,-3194],[981,-1912],[-690,-1883],[-4432,-6052],[-5257,-5983]],[[567098,894577],[-1122,2286],[658,3670],[-1445,3788],[474,2989],[-2379,2586],[-2181,768],[-3820,3059]],[[557283,913723],[2777,1385],[2192,-3263],[2536,-420],[1473,929],[3020,-1259],[2242,2351],[731,3925],[1427,1554],[3790,869],[3477,-2312],[533,-1175],[-1021,-2673]],[[348288,518502],[595,797],[574,2112],[-22,1898],[591,2674],[-1001,2752],[-274,2553],[-7,3131],[822,2047]],[[516081,798204],[1542,-507]],[[519503,776802],[-598,-1279],[964,-1831],[-1459,-1676],[1119,-2377],[-521,-1220],[345,-1367],[1860,-682],[-399,-2357]],[[520814,764013],[-644,-421]],[[503967,757074],[772,-548]],[[526641,510831],[4846,-191],[-19,6918]],[[482727,825162],[-1199,-177],[-1111,2071],[-849,-1701],[-2120,1737],[2499,4015]],[[620665,748254],[-1810,2705],[-893,-734],[-2657,460]],[[611050,761956],[478,888],[1383,-212],[2590,-1865],[2329,30],[3788,-2827],[485,-1069],[2538,1124],[2379,-1667],[-247,-1599],[2198,-1861]],[[499809,570571],[30,-2873],[1218,-2007],[-410,-4908],[822,-623],[-112,-3003],[-323,-546],[877,-2696],[-290,-939],[142,-4693],[-305,-2978],[588,-2360],[1250,-2152]],[[491349,534865],[76,235]],[[468362,578206],[-313,-1219],[547,-1085],[1034,1124],[710,-1811],[1118,1855],[1262,-1008],[1284,1262],[-104,1239],[980,-369],[624,-2843],[-10,-1476],[1151,-1700],[-713,-2077],[907,-267],[295,-3275],[669,-1632]],[[476426,549497],[-616,595],[-504,-2347],[-633,-278],[-962,1185],[263,1325],[-414,4186],[-695,1117],[-1431,-293]],[[471434,554987],[-1191,-888],[588,2087],[-528,3713],[-1431,3931],[-1959,90],[-1640,-775],[-706,-2895],[-756,-1599],[-736,-322]],[[458212,569531],[1002,3368],[1159,897],[1480,451],[-15,1621],[-586,998],[669,797],[-58,2140]],[[461863,579803],[1795,-239],[197,-924],[2002,-886],[2505,452]],[[453993,585214],[2924,-6],[1115,1338],[2174,-1917],[967,326],[361,-1234],[-1109,-589],[-2512,1900],[-375,-951],[-1467,-420],[-56,-999],[-2263,-14],[-317,-533]],[[453578,577913],[3158,803],[1052,1123],[4075,-36]],[[558233,746730],[1699,113],[983,1413],[1567,66],[1173,1290]],[[573113,751865],[844,-1865],[-817,-966],[1,-1684],[-811,-1349]],[[254921,597903],[-2078,-3474],[-683,-1639],[139,-1535],[-529,-1131]],[[251770,590124],[-2061,-3444],[26,-582]],[[243791,590891],[444,3133],[-311,1461],[1252,4439],[3582,15],[83,1886],[-815,1878],[-1942,3246],[1158,-21],[10,3342],[5077,-10]],[[341125,537589],[-378,-3130],[-1056,-173],[-954,-4853],[616,-2938],[787,-1914],[683,144],[261,-2929],[1404,-5014],[615,-559]],[[331272,535536],[-1763,4177],[689,1820],[-47,2846],[1188,437],[897,1049],[193,1117],[-855,457],[-239,1704],[571,1863],[1337,1424],[558,1495],[-517,1442]],[[817405,638260],[69,-246]],[[269007,593543],[-716,89],[-1256,-1266],[-1862,-954],[-897,1045],[-836,-1686],[-130,-1264],[-1607,-2769],[-704,1219],[-811,-1660],[-1115,-39],[64,-2666],[-968,-1908],[-773,-72]],[[256071,584100],[275,2450],[-1210,1034],[-921,-788],[-1772,3057],[-673,271]],[[551211,756860],[226,-751]],[[552514,776837],[644,-4357],[-361,-1939]],[[537716,774380],[2200,-210],[456,970],[869,-1054],[1368,-2],[-173,1574],[966,601],[31,2172],[2445,1773]],[[545878,780204],[2472,-3252],[1114,-952],[1531,-221],[1519,1058]],[[561477,791492],[1770,-1752],[299,-963]],[[563546,788777],[-1628,-1299],[-3475,-8801],[-2216,-792]],[[556227,777885],[-1975,276],[-1738,-1324]],[[545878,780204],[-1176,2151]],[[547631,789126],[1707,-1397],[2673,100],[188,1263],[3074,777],[1209,973],[434,1370],[2671,151],[876,-1269],[1014,398]],[[847411,448364],[-195,-316]],[[844380,449187],[165,186]],[[844545,449373],[683,-512],[450,1407]],[[845678,450268],[364,208]],[[846915,451584],[90,154]],[[847005,451738],[631,-1069],[-477,-428],[252,-1877]],[[890964,489271],[628,-15]],[[891592,489256],[0,-1148]],[[891592,488108],[4,-20988],[-314,-2334],[315,-979],[3,-13114]],[[891600,450693],[-144,200]],[[826595,529426],[-23,-50]],[[804524,516729],[69,-2445],[2367,-4460],[1200,920],[2311,-106],[857,853],[298,1752],[806,711],[1298,47],[126,-651],[1760,-1311],[779,1175],[1787,195],[791,3039],[-123,1602],[1091,1616],[-258,1883],[1023,1145],[310,2437],[7,2921],[910,2429],[3346,-69],[1316,-986]],[[720385,689001],[85,-152]],[[721990,687748],[22,-32]],[[725028,683425],[-1281,-1568],[-817,-2823],[-512,-3514],[3051,-2934],[1899,-2772],[375,277],[2070,-2339],[1547,-877],[1497,41],[728,672],[1442,-1141],[209,-1527],[1688,-1777],[765,585],[469,-1185],[1747,-387],[931,-826],[874,713],[754,-1156],[2132,414],[296,1746],[-492,2424],[349,4364]],[[770339,671893],[36,-1660],[-1211,-1741],[385,-3210],[-1035,1405],[-1677,-724],[-434,-1009],[-2157,-2663],[10,-3294],[-1606,-4726],[426,-1154],[-1152,-4306],[-459,-2639],[-2279,862],[299,-2014],[-137,-3255],[-559,-596],[-238,-1859],[232,-2121],[-549,-2112],[-765,754],[-317,-906]],[[689347,646059],[1553,636],[11,1783],[2308,44],[436,-595],[2308,1455],[470,-1068],[911,960],[10,1704],[-1099,4356],[-10,1446],[-1066,234],[-457,1206],[157,3326],[-1907,1973],[272,2193],[1601,3995],[720,1043],[927,-1754],[3147,1384],[1310,4676],[1560,1641],[1328,5365],[1188,942],[250,2026],[1223,2714],[815,836],[-320,895],[-22,3124],[638,1397],[1429,1135],[221,823],[-1877,1420],[15,1414],[-857,66],[-142,1321],[-859,1484],[433,1569],[-414,1666],[682,1196],[-824,170],[-389,1816],[420,1944],[942,663],[3914,-1554],[921,988],[1538,391],[1261,2216]],[[714023,712724],[2086,2287]],[[716109,715011],[31,-68]],[[719756,701565],[63,-322]],[[720045,697641],[8,-373]],[[649761,725957],[2308,938],[918,2374],[1397,1168],[1806,-156],[589,1043],[2091,-196],[395,-1341],[3056,-2082],[1055,266],[1350,-1024],[724,-1965],[1390,-1280],[774,-1927],[2162,29],[396,-6060]],[[669733,683211],[-724,-1598]],[[669009,681613],[1319,-2879],[846,-3442],[742,-1452],[2424,-2041],[1,-5639],[1122,13],[384,-758],[-381,-2719],[-2024,-619],[-556,-1209],[-1026,-679],[-559,-2805],[-224,-3357]],[[671077,654027],[-152,-40]],[[634851,682228],[-455,1586],[-1022,1395],[-12,3106],[-920,74],[0,2359],[418,2334],[-1274,3728],[-694,254],[-2067,2741],[-735,168],[92,1611],[-740,2253],[-1340,2139],[113,2632],[668,2271],[1266,1950],[-452,2349],[545,1756],[-1233,96],[-1005,1058],[-918,3026],[-739,3652]],[[624347,724766],[-566,3567],[-972,969],[609,2658],[-1132,6047],[1017,265],[549,2052],[640,-700]],[[633274,682349],[-850,668],[-1551,-795],[-580,-2511],[-1040,-2615]],[[629253,677096],[-486,-193],[-4555,770],[-5163,7712],[-2176,3466],[-4737,5087],[-3399,1099]],[[608737,695037],[283,1342],[-527,842],[-789,5208]],[[607704,702429],[5322,5687],[826,574],[577,2014],[60,3076],[383,2087],[-301,2565],[475,2614],[1033,489],[1584,3030]],[[617663,724565],[1155,1560],[2949,-879],[491,533],[746,-1987],[1343,974]],[[599409,698654],[-656,-2011]],[[598753,696643],[-995,823],[-659,-2213],[688,-2435],[-919,-2092],[1605,489]],[[598473,691215],[-77,-912]],[[598396,690303],[-61,-562]],[[598335,689741],[107,-581],[-830,-4216],[-464,-5130]],[[595125,689609],[645,2231]],[[597523,700720],[841,-48],[1272,2110]],[[599636,702782],[123,-2857],[-350,-1271]],[[538055,780323],[-894,-1532],[707,-500],[-175,-2032],[417,-1460]],[[608737,695037],[-509,-768],[-5566,-2982],[2838,-5874],[-963,-1106],[-456,-1886],[-1984,-764],[-775,-2197],[-1280,-1805],[-2957,966]],[[598473,691215],[301,1695]],[[598774,692910],[-21,3733]],[[599409,698654],[1624,-2062],[1240,-413],[5431,6250]],[[714023,712724],[-783,3269]],[[665127,772295],[-32,-131]],[[665095,772164],[-1906,1936]],[[663189,774100],[33,118]],[[722803,754670],[-801,1322],[-1197,263],[-1010,1885],[-1673,527],[-7596,404],[-428,-701],[-1633,532],[-2329,1990],[-1814,-1407],[-176,-3518],[-2055,1356],[-2601,1092],[-1556,-525],[-860,-2873]],[[697074,755017],[-1475,-1007],[-890,-1529],[-2863,-2687],[-1335,-2907],[46,-1282],[-1536,885],[-311,2294],[-3406,-103],[-586,4833],[-1359,59],[252,5841],[-825,-674],[-853,2568],[-1641,2395],[-1284,-969],[-3434,455],[-3380,-805],[-2304,4008],[-424,1334],[-2646,2687]],[[666820,770413],[22,2100]],[[662809,774782],[-59,-262]],[[662750,774520],[-391,14],[-6872,-3247],[5,-21758]],[[655492,749529],[-1200,-353],[-822,1158],[-960,2731],[-2175,2465],[-2419,-766],[-2100,-2521]],[[636756,779239],[-1728,1359],[-14,1181],[984,52],[-2360,5751],[-2271,-26],[-800,3220],[-954,757],[116,2330],[866,1735],[-590,1592],[528,2877],[929,2493],[1053,619],[2024,-3256],[1136,1095],[-606,3552],[1940,1416],[485,1372],[2080,1221],[1520,2605],[1529,-1504],[2740,1220],[667,-1182],[2131,4],[1954,-2175],[1690,-2696],[214,2002],[2264,-2348],[710,2],[1927,2473],[1445,270],[1196,-1044],[1102,1201],[1445,-166],[1457,-2187],[2580,-666],[396,1287],[2742,-615],[1243,981],[543,2184],[-617,1257],[-2495,1239],[-1109,1928],[1680,1033],[859,1445],[-492,2073],[681,1350],[2574,-171],[112,973],[-2265,1062],[933,1399],[-1543,583],[984,2533],[1653,-609],[3181,941],[3853,1653],[1935,-118],[887,1534],[2071,261],[4085,1215],[3567,3064],[3347,-1346],[1544,845],[1243,-4181],[-257,-2293],[676,-320],[2591,675],[375,-1824],[606,1007],[2935,-1213],[-776,-699],[-77,-2116],[1353,980],[1368,-783],[279,946],[3347,2717],[3279,1993],[-726,-2961],[3135,-3338],[2142,-4388],[2758,-6785],[1437,-4257],[1216,1017],[68,1404],[1193,582],[538,-1853],[1096,-1356],[2856,-73],[2398,1581],[1633,-1302],[1050,-3172],[1851,-1053],[840,-2737],[2470,-594],[1203,654],[1968,-3103]],[[616344,528283],[-1506,-4598],[-1048,-2293],[39,-21831],[1509,-4159],[30,-729]],[[608949,476917],[-3957,6031],[-524,1269],[98,2458],[-9967,11867]],[[594599,498542],[20,51]],[[594373,505981],[-1,1]],[[594372,505982],[1410,4908],[850,1118],[493,2445],[-165,4955],[-1272,4051],[-153,3128],[-633,720],[-525,2413]],[[594377,529720],[3590,7536]],[[704532,738430],[-2755,-373],[-1139,-1057],[-1178,403],[-932,1944],[-2048,-1128],[-348,896],[-3639,-235],[26,2629],[1833,1384],[1346,-906],[1407,1123]],[[697105,743110],[963,285],[1077,-797],[1219,1696],[629,-219],[2098,2277],[-1368,579],[-1267,1718],[-794,126],[-593,2051],[-713,-2400],[-1739,749],[-1671,1830],[1836,2655],[1074,849],[-782,508]],[[785927,574073],[-548,2269],[52,1994],[-711,1444],[-499,5154],[631,271],[1005,3264],[807,1161],[4388,564],[819,-1187],[304,704]],[[792175,589711],[464,-1402],[503,276],[1036,-1373],[612,738],[-406,1742],[1453,1393],[884,-1561],[1943,2313]],[[798664,591837],[-522,-3427],[761,-4081],[-362,-2414],[223,-2905],[-450,-1656],[-651,98],[-635,-1182],[-1435,-765],[-4,-1485],[-1129,357],[-429,-729],[13,-2018],[866,-1671],[-11,-1288],[-1136,1156],[-2035,-611],[-477,-2088],[-1179,-730]],[[851760,728554],[1487,3097],[2416,23],[932,1866]],[[559895,755010],[-1396,-451],[-1371,-1763]],[[555733,756786],[778,1663]],[[556511,758449],[1164,2552],[1743,-3005],[1006,-484],[-529,-2502]],[[634562,673818],[-2142,-58],[-662,2704],[-2505,632]],[[783687,637302],[1263,-2814],[314,-1435],[706,114],[-273,-2461],[704,-2217],[1472,-1153],[1160,1446],[1475,-1745],[-598,-1216],[697,-396],[859,-2112],[-1060,-2414],[-1430,383],[-377,-1986],[2278,-3179],[1196,-903],[-169,-1190],[1035,-1753],[647,-2467],[2253,-4643],[538,-2933],[1945,-2465],[-640,-1425],[1354,-3242],[-480,-1631],[108,-1628]],[[792175,589711],[812,1089],[104,4922],[303,2009],[-600,1703],[-997,1024],[-824,2887],[182,3867],[-1371,3054],[-1037,2981],[-1836,530],[-658,-2251],[-1207,-1156],[-1432,2235],[-2767,-4331],[-546,618],[568,2664],[-174,2213],[655,3377],[-207,3384],[-1629,-287],[-632,1518],[404,1970],[-626,1761],[-543,-410]],[[778117,625082],[353,2451],[876,561],[533,2889],[1062,1510]],[[599933,709876],[1269,-93],[422,-2324],[-1785,-3280],[-203,-1397]],[[468034,545635],[666,1931],[1723,3121],[213,1847],[503,512],[295,1941]],[[569389,635030],[-2,-11809],[-2776,-39],[0,-2958]],[[566611,620224],[-5322,5606],[-6655,7008],[-3992,4205],[-6242,6574],[-2792,-2660]],[[541608,640957],[-2079,-2238],[-2082,3328],[-4203,2001]],[[526440,683810],[1046,935],[892,2346],[-281,4032],[1976,3654],[1885,1973],[-1,4552]],[[579824,326379],[-958,-270],[-1038,-2931],[-737,251],[-1012,1683],[-936,3862],[675,857],[1225,3432],[2472,2123],[1877,-3010],[248,-1066],[-813,-3847],[-1003,-1084]],[[565235,824281],[-87,1206],[-1909,1264]],[[563239,826751],[181,2854],[-734,1307],[-1374,27],[-2324,1188]],[[558988,832127],[1,41]],[[558461,836902],[2884,1994],[4801,-459],[855,-385],[2174,793],[463,-1171],[1433,-416],[2799,-2741]],[[575977,845540],[1236,-1251],[-437,-2793],[1288,-1777],[124,-2387]],[[475879,668566],[-373,0],[63,-3174],[-1718,-191],[-894,-1348],[-1434,0],[-1865,886],[-1305,-753],[152,-1481],[-1056,-3135],[-868,-434],[-1112,-7111],[-1750,-2545],[-694,-2488],[-1278,-1128],[-695,-2251],[-556,-6520],[-1138,-2662],[-583,-2430],[-2528,237],[-3478,-415]],[[452769,631623],[199,2840]],[[463177,667252],[223,1310]],[[578367,773986],[-313,3094],[402,2835],[-479,3122],[-2042,3919],[-989,3053],[-1005,621]],[[573941,790630],[2584,1290],[1693,-1419],[2595,-1556],[186,-3080],[1082,-1236],[63,-1676],[849,-800],[-111,-2835],[-1921,1046],[-608,-609],[57,-2217],[-2043,-3552]],[[187598,692927],[3952,-2631]],[[191550,690296],[7854,31]],[[199404,690327],[7,2665]],[[199411,692992],[4885,-54],[549,-1336]],[[204845,691602],[2722,-4369]],[[207567,687233],[825,-955],[1319,-5737]],[[209711,680541],[1093,-1727]],[[210804,678814],[2369,-2281]],[[213173,676533],[1089,1522]],[[214262,678055],[364,2286]],[[214626,680341],[1292,1347],[1947,-368],[1471,-2067]],[[219336,679253],[1056,-2321]],[[220392,676932],[1008,-4389]],[[221400,672543],[2196,-4617]],[[223596,667926],[136,-2913]],[[223732,665013],[790,-2918]],[[224522,662095],[178,-694],[2848,-2266],[2011,-1149]],[[229559,657986],[590,539]],[[174644,697459],[6675,1079]],[[181319,698538],[-308,-1227]],[[181011,697311],[6587,-4384]],[[559895,755010],[2171,394]],[[511743,618128],[19,-12717],[-314,-3783],[-679,-3570],[-1035,-2363],[-6123,-498],[-945,-1691],[-2063,-447]],[[468362,578206],[-2,3185],[-680,2535],[-546,-320],[-619,1879],[98,3398],[-581,1493],[-145,2076]],[[465887,592452],[488,-377],[644,1480],[313,2550],[847,1184],[1409,-2810],[699,1609],[2098,-290],[2123,725],[10179,1],[424,4660],[-747,1693],[-1105,20579],[-1576,29341],[4920,5]],[[778117,625082],[-645,639],[-1198,-364],[119,-1039],[-1336,-864],[-289,-1593],[-1882,-487],[-623,348],[-550,-1715],[-308,-3129],[133,-1843],[-747,-750],[409,-1208],[446,-3608],[1795,-4180],[109,-1443],[944,-3266],[-627,-771],[-75,-3834],[-1040,-1182],[153,-2307],[900,-2694],[1010,-1837],[564,-1974],[-81,-3633],[825,-3291],[584,-4543],[-1180,-4004],[-1202,-2633],[-152,-2787]],[[553317,762628],[992,-1902],[2202,-2277]],[[553773,755249],[-181,-138]],[[553378,754483],[347,-292]],[[743928,795977],[1051,1713],[2266,126],[1793,1450],[-28,1099],[2809,1892],[4721,3802],[2079,-1541],[3189,-282],[1010,-3156],[1380,-523],[2057,458],[3149,-770],[619,-901],[2486,2057],[489,2697],[-1262,2679],[338,2151],[2628,4555],[2857,-2143],[1521,-174],[2533,-1620],[2029,-588],[492,-4553],[1097,-1172],[2636,-1472],[4864,1984],[2318,-1001],[1370,48],[1450,-1915],[1985,-383],[238,-1960],[1612,-1606],[1731,71],[2675,-974],[1744,-25],[1414,1124],[2064,405],[2710,1136],[302,1073],[3146,2827],[1240,-241],[1475,-1687],[1232,-405],[1158,772],[1524,-1108]],[[591350,345650],[-2148,58]],[[589202,345708],[-146,4865],[-311,359]],[[588745,350932],[104,8869],[-517,3368],[-706,2428],[-716,6400]],[[586910,371997],[3009,6323],[296,3684],[542,1166],[928,3806],[-637,2873],[-169,2291],[768,3807],[-125,9758],[-1958,1562],[-843,118],[-700,1272],[-1254,1129],[-2218,168],[-116,2086]],[[584433,412040],[-456,3868],[1226,1014],[7024,4773]],[[592227,421695],[1207,-3286],[1934,945],[479,-1123],[99,-4142],[-813,-3497],[409,-1846],[2486,-5319],[-342,3180],[531,2368],[1103,605],[382,6911],[-127,1308],[-1666,4587],[-1075,2219]],[[596834,424605],[3,366]],[[597102,435195],[7,928]],[[597109,436123],[1865,-23],[429,765],[1128,-1290],[909,-270],[983,859],[1390,-825],[2232,2558],[876,-797],[842,1092],[1463,630],[1853,1788],[1319,2111]],[[465887,592452],[-1606,2569],[-1936,5341],[-869,24],[-1199,2560],[-1918,573],[-2160,-1137],[-1308,274],[-823,-4105]],[[452643,627982],[233,3099],[10967,28],[-217,6885],[-200,1523],[374,1464],[1143,1606],[1658,1163],[20,14976],[9261,0],[-3,7645]],[[596829,424051],[5,554]],[[592227,421695],[-583,-52],[-889,2440],[821,2283],[150,3522],[1045,834],[-404,2235],[-72,3422],[426,2234],[-329,1566],[1105,1795],[-362,2108],[-604,1165],[-321,2439],[-766,1297]],[[591444,448983],[1391,-1188],[1480,-297]],[[778108,542882],[160,1362],[1714,-1455],[721,-1088],[-199,-2794],[366,-795],[1229,1605],[883,-488],[630,2470]],[[826676,529600],[-81,-174]],[[816815,531927],[28,-1]],[[564946,400206],[2484,945],[1826,-369],[907,-1482]],[[555501,357928],[0,-21769],[-859,-312],[-1416,-2576],[-897,412],[-2044,-15],[-1819,1028],[-173,2044],[-915,1908],[-835,-2494],[-856,-980]],[[541608,640957],[537,-6364],[26,-2362],[1182,-3371],[-56,-1309],[1005,-2549],[-594,-2364],[-724,-17748],[-3074,-6862],[-2554,-8113],[439,-4006]],[[537795,585909],[-785,-200],[-1858,-2039],[-533,-1380],[-2920,1540],[-1258,106],[-2151,-601],[-1580,-2722],[-2403,578],[-1821,2269],[-851,277],[-2033,-2001],[-702,637],[-1161,2938],[-2484,1595],[-695,-685],[-1162,15],[-1878,-1789],[-554,-4045],[-837,-1452],[-142,-4939]],[[537795,585909],[1271,-3709]],[[516367,805398],[-552,132]],[[585749,918146],[-25,-1452],[-1759,565],[-3505,-3625]],[[557283,913723],[-1404,-95],[563,-1581],[-971,-2356],[-4420,1221],[-1283,-3540],[-1645,823],[-3325,-4017],[767,-2197],[-2724,-3348],[169,-1089],[-2613,-1047],[-176,-4904],[-2304,-4266],[1187,-696],[-325,-2666],[-1836,360],[-1770,-796],[-1840,-3844],[606,-1724],[-288,-2421],[525,-1815],[-412,-3346],[2015,-2183],[-549,-1811],[-1080,-260],[818,-3270],[-285,-2038],[-2237,-3048],[-105,-3947],[-707,654]],[[647459,603350],[-990,3862],[-2087,10047]],[[644382,617259],[8332,5923],[1844,11884],[-1265,4160]],[[671509,653647],[-432,380]],[[307320,412833],[-113,108]],[[307956,408551],[363,-118]],[[308196,406812],[33,-40]],[[892036,450086],[-436,607]],[[891592,488108],[0,1148]],[[891592,489256],[365,-10]],[[554456,827357],[1677,-227],[7106,-379]],[[565570,809933],[1390,-3987],[-370,-2577],[-725,-194],[-2951,-4966],[446,-3071],[-753,308]],[[562607,795446],[-2497,2010],[-2844,-120],[-2387,-1110],[-875,2330],[-812,-1171],[-881,656]],[[539607,823035],[636,-341]],[[539863,823801],[-381,71]],[[539476,824343],[6,-471]],[[862574,756716],[445,-1380]],[[642410,650501],[-838,-197],[-449,1276]],[[578367,773986],[1523,-1281],[1786,1099],[840,-947]],[[563069,766802],[-576,2775],[-1124,-973],[-2042,1895],[372,1543],[-1993,2145],[2,1573],[-1481,2125]],[[563546,788777],[905,815],[2709,-1058],[1114,148],[874,-1263],[1585,1143],[1941,484],[1267,1584]],[[558952,831200],[36,927]],[[558054,832260],[-112,-611]],[[0,890205],[0,1449]],[[0,925312],[0,1133]],[[0,927721],[0,818]],[[999999,913406],[0,-23201]],[[866732,772708],[-53,-123]],[[868915,771622],[0,87]],[[606150,783708],[203,2771],[1703,1754],[2321,-62],[617,2513],[-870,1909],[956,1541],[-840,928],[1065,1139],[-109,2350],[-695,-147],[-1683,1682],[-712,-185],[-1833,1349],[-588,-784],[-1733,2911],[-2232,-1198],[-3355,1957],[-277,2988],[-688,945],[-2306,240],[-314,2579],[769,599],[-1841,3344],[-3409,-214],[-625,-1153],[-1443,-78]],[[576786,848429],[98,16]],[[576897,854139],[-40,15]],[[683568,913720],[921,-525]],[[584749,498394],[841,-2938],[45,-4593],[-764,-366]],[[580160,490226],[146,78]],[[581248,494433],[-78,295]],[[582158,496495],[981,-486],[1194,2342],[416,43]],[[452751,631365],[18,258]],[[644382,617259],[-7737,-2221],[-2834,-2751],[-1646,-4198],[-383,-1993],[-1295,-939],[-815,1867],[-1033,-221],[-2510,524],[-718,638],[-2756,-171],[-664,-438],[-1386,1135],[-631,-929],[-72,-3969],[-1016,-1882]],[[594661,560771],[-520,4],[189,2270],[-186,2095],[-2000,3858],[-275,4392],[351,3708],[-1326,34],[41,-1264],[-1846,-18],[731,-1722],[191,-3900],[-1309,-2341],[-772,-2615],[-1195,-2500],[-1348,-335],[-2046,3168],[-1104,-1258],[-368,-1756],[-1315,-939],[-431,-1683],[-2210,15],[-453,1606],[-2254,84],[-1453,-522],[-1833,4011],[-259,1290],[-2031,-751],[-783,-3075],[-703,-5260],[-1069,-1311]],[[563500,569410],[173,2519],[-1017,1924],[-567,5870],[-1464,771],[1119,3194],[-335,2374],[1118,2352],[-357,2507],[804,1019],[726,2604],[5,2198],[475,1004],[2440,460],[-9,22018]],[[594377,529720],[-1352,-2756],[-1367,741],[-1837,-1031],[-499,-1055],[-995,1627],[-883,-724],[-910,623],[-872,-1747]],[[635940,571417],[-2,-10703],[-2666,-8619]],[[562607,795446],[-1130,-3954]],[[549900,856389],[43,-180]],[[589202,345708],[-328,130],[-101,-2893],[-1358,61],[-1128,1086],[-748,2062],[25,2078],[1122,3377],[578,574],[1481,-1251]],[[599701,717503],[725,-490],[616,1999],[726,372],[-276,1323],[337,2045],[2160,-943],[2098,1530],[1597,-1235],[1639,-69],[1833,856],[1915,1610],[2249,-51],[2092,1110],[251,-995]],[[688219,724942],[154,1865],[1332,3234],[108,1214],[-792,2556],[155,1735],[-1186,275],[-908,1384],[1026,2247],[2067,-502],[1288,3553],[967,366],[-191,2183],[577,1366],[830,-831],[2024,2171],[860,-1681],[-1023,-1695],[751,-1495],[847,223]],[[655492,749529],[2891,-348],[-150,3513],[346,518]],[[658579,753212],[-98,-1010]],[[660075,754430],[-26,79]],[[660049,754509],[1142,1924],[1269,-1012],[-929,1844],[1216,891],[2394,-2837],[1131,-26],[290,-2019],[638,-711],[-285,-2577],[1015,-1053],[2427,-157],[467,479],[1128,-1080],[2077,-7318],[4200,-5361],[4028,-4236],[679,178],[2145,-1994],[-298,-3458]],[[844545,449373],[448,506]],[[844993,449879],[685,389]],[[847805,449005],[-394,-641]],[[847005,451738],[291,494]],[[588280,498780],[5,-46]],[[594254,497680],[345,862]],[[597109,436123],[-62,678]],[[591444,448983],[-1352,1488],[-1363,606],[-1133,2019],[-969,612]],[[586627,453708],[-6,44]],[[582337,478120],[15,207]],[[584749,498394],[930,386],[2601,0]],[[594372,505982],[-81,24]],[[588243,499173],[37,-393]],[[582342,501717],[624,1207]],[[582730,503997],[-277,8]],[[586953,517904],[-112,-392]],[[352309,311155],[-25,-42]],[[351267,306014],[29,-49]],[[251173,789100],[-75,-59]],[[280385,761145],[23,13]],[[289481,768262],[-31,38]],[[311664,775792],[-173,-93]],[[312640,774914],[-1,5]],[[138294,830715],[-1003,-1519]],[[663118,773843],[71,257]],[[665095,772164],[-147,-601]],[[666819,770377],[1,36]],[[660049,754509],[-189,580]],[[658593,753357],[-14,-145]],[[662750,774520],[-4,-21]],[[581568,373230],[829,282],[2214,-1082],[1267,227],[1032,-660]],[[586662,453459],[-35,249]],[[584433,412040],[-2524,-318],[-1700,-2010],[-319,-2939]],[[579890,406773],[-220,141]],[[575057,398323],[-669,-492],[-1240,665],[-1306,-134],[-1679,938]],[[584936,456017],[-10,-250]],[[575633,399537],[498,1027]],[[194446,794974],[-11287,0]],[[183159,794974],[-1817,3409],[182,2469],[-384,2214],[-1158,1154],[-2198,3412],[-1879,3917],[-663,-456],[-1133,2619],[-1142,1346],[-1360,18],[-273,1708],[-1344,2838],[-2555,1459],[-771,2631],[2,36479]],[[166666,860191],[12153,0],[5208,0],[10417,0]],[[194444,860191],[2,-65217]],[[146674,804734],[4763,-1918],[2332,-5262],[1798,-1212]],[[155567,796342],[1385,-3802],[-271,-1473]],[[156681,791067],[-4239,2531]],[[152442,793598],[753,1585]],[[153195,795183],[-1696,-517]],[[151499,794666],[-511,1450]],[[150988,796116],[-1665,1521],[-289,1293]],[[149034,798930],[-2506,2827]],[[146528,801757],[-1706,-61]],[[144822,801696],[-116,1881]],[[144706,803577],[1164,-240],[58,1057]],[[145928,804394],[-1647,-501]],[[144281,803893],[-798,1456]],[[143483,805349],[1189,689]],[[144672,806038],[2002,-1304]],[[134017,819872],[828,-2926]],[[134845,816946],[-375,-732]],[[134470,816214],[1025,-2515],[-2622,3729],[60,1281],[-1119,819]],[[131814,819528],[2203,344]],[[142910,818356],[118,-2495]],[[143028,815861],[-470,-1357],[-187,2807]],[[142371,817311],[-427,-530],[-771,2038]],[[141173,818819],[401,1551],[1336,-2014]],[[139308,819707],[-1857,2230]],[[137451,821937],[1590,-639]],[[139041,821298],[267,-1591]],[[131512,825393],[1448,-552]],[[132960,824841],[1295,634]],[[134255,825475],[-953,-5192]],[[133302,820283],[-2316,1437]],[[130986,821720],[-577,1603]],[[130409,823323],[11,2256]],[[130420,825579],[1092,-186]],[[138819,835824],[-202,1310]],[[138617,837134],[-4105,2900]],[[134512,840034],[-691,-52]],[[133821,839982],[-576,2586],[-4971,9944],[-3119,3456]],[[125155,855968],[-298,1719]],[[124857,857687],[-1180,1273],[-2349,-1117]],[[121328,857843],[-713,-2681],[-2389,-1476]],[[118226,853686],[-430,1914]],[[117796,855600],[-4064,4594]],[[113732,860194],[7956,-3],[7932,0],[5287,0],[7932,0],[5287,0],[7932,0]],[[156058,860191],[10608,0]],[[183159,794974],[-5516,0]],[[177643,794974],[-2753,0]],[[174890,794974],[-7396,0]],[[167494,794974],[-8574,0]],[[158920,794974],[-724,0]],[[158196,794974],[-795,2762]],[[157401,797736],[-1545,209]],[[155856,797945],[-35,2883]],[[155821,800828],[-659,-1117]],[[155162,799711],[-1780,1348]],[[153382,801059],[-768,2925]],[[152614,803984],[-3864,212],[1534,798]],[[150284,804994],[-1721,237]],[[148563,805231],[-319,1130]],[[148244,806361],[-1182,-282]],[[147062,806079],[-1807,1681]],[[145255,807760],[176,1939]],[[145431,809699],[-623,1758]],[[144808,811457],[214,3046]],[[145022,814503],[-863,-2968]],[[144159,811535],[-708,2195]],[[143451,813730],[699,4431]],[[143429,817681],[-797,2476],[-1191,732]],[[141441,820889],[-93,1622],[1590,-1307]],[[142938,821204],[-1159,2494]],[[141779,823698],[-903,-2657]],[[140876,821041],[-776,-838],[-2144,2799]],[[137956,823002],[813,2426],[-1076,1704],[2415,6170]],[[140108,833302],[-1178,-614]],[[138930,832688],[-111,3136]],[[252921,841529],[-12978,-18385],[-4259,-5483],[-5,-20456]],[[235679,797205],[-18,-2238],[-5733,8]],[[229928,794975],[-4397,-1],[-7107,0]],[[218424,794974],[-987,22745],[-773,17686],[-2,24786]],[[216662,860191],[11248,0],[8834,3]],[[236744,860194],[-45,-4347]],[[236699,855847],[319,-2357]],[[237018,853490],[1073,-913]],[[238091,852577],[-125,-2480]],[[237966,850097],[767,2741]],[[238733,852838],[2161,-22]],[[240894,852816],[2329,-8777]],[[243223,844039],[-794,-2022]],[[242429,842017],[4484,1823]],[[246913,843840],[1442,-99]],[[248355,843741],[2226,-1441]],[[250581,842300],[2340,-771]],[[322136,777316],[-788,-1048]],[[321348,776268],[-4361,-3630]],[[316987,772638],[-2744,-922]],[[314243,771716],[-701,605]],[[313542,772321],[-966,631]],[[312576,772952],[64,1967],[-976,873]],[[311664,775792],[-17,7865],[-1191,1559],[-1647,-845]],[[308809,784371],[-625,541]],[[308184,784912],[1874,1744],[3,2015],[2125,410],[689,-850],[1835,993]],[[314710,789224],[2375,-660]],[[317085,788564],[508,-1273]],[[317593,787291],[1846,893],[830,-723]],[[320269,787461],[-581,-2111]],[[319688,785350],[-1130,-1585]],[[318558,783765],[1355,-239],[-207,-1024]],[[319706,782502],[1012,-3837],[1737,-441]],[[322455,778224],[-319,-908]],[[345948,810042],[-1590,-1233],[641,-1748],[-2483,-5769]],[[342516,801292],[-356,-2642]],[[342160,798650],[1598,2823]],[[343758,801473],[238,-888]],[[343996,800585],[1754,338]],[[345750,800923],[-1417,-1733]],[[344333,799190],[-131,-1498],[2445,179]],[[346647,797871],[-104,-1672]],[[346543,796199],[2153,1954],[2361,-1232]],[[351057,796921],[128,-1069]],[[351185,795852],[-1633,-2094],[1286,-640]],[[350838,793118],[-1027,-1546]],[[349811,791572],[2807,1423]],[[352618,792995],[-218,-1524],[-1316,-1151]],[[351084,790320],[-700,-2418]],[[350384,787902],[716,-812]],[[351100,787090],[892,1987],[1158,683]],[[353150,789760],[-860,-2725]],[[352290,787035],[147,-1173],[945,1861],[357,-1302]],[[353739,786421],[-1156,-5144],[-1518,-6]],[[351065,781271],[-55,2711]],[[351010,783982],[-1493,-1524]],[[349517,782458],[846,3001]],[[350363,785459],[-896,2801]],[[349467,788260],[-1030,-2871]],[[348437,785389],[-817,58]],[[347620,785447],[-1275,-2839]],[[346345,782608],[-1314,-228],[-138,1210],[964,528]],[[345857,784118],[1728,2431],[-1380,533],[-190,-946],[-1188,171]],[[344827,786307],[12,1712]],[[344839,788019],[-1010,-875]],[[343829,787144],[-2031,-574]],[[341798,786570],[-3835,606]],[[337963,787176],[-2177,-629]],[[335786,786547],[-431,2517]],[[335355,789064],[2616,3120]],[[337971,792184],[-1072,450],[868,2880]],[[337767,795514],[1177,861]],[[338944,796375],[-100,1854],[1994,6850],[1522,3414]],[[342360,808493],[2013,1739],[1575,-190]],[[341388,809491],[-2,3304],[-11295,-1],[-6776,0],[-1264,2706],[1811,1369],[-728,2441],[-1408,-1692],[133,-3724],[-444,-2714],[-2352,2525],[-2496,-269],[-1188,1375],[453,3291],[-2132,-841],[361,3400],[-1442,1614],[-834,2561],[683,1201],[-302,1601],[1306,653],[-798,2486],[2184,-1408],[-342,3138],[3051,-3456],[3708,-111],[1638,-676],[667,3290],[1109,1021],[-2479,4488],[-299,3517],[580,1175],[793,4957],[-1188,351],[-906,2275],[1454,1721],[-619,1078],[1438,533],[-3487,1171],[1153,410],[-143,3137],[-1037,72],[424,2165],[-596,853],[738,1519]],[[320515,861997],[-428,-1741]],[[320087,860256],[1347,308]],[[321434,860564],[2066,-4332]],[[323500,856232],[94,-1290]],[[323594,854942],[1756,-2623]],[[325350,852319],[-1433,-1302],[2172,259]],[[326089,851276],[-1036,-2388]],[[325053,848888],[1374,360]],[[326427,849248],[1631,-1734]],[[328058,847514],[-191,-1478],[-1353,-888]],[[326514,845148],[1677,-478]],[[328191,844670],[-351,-790]],[[327840,843880],[1788,-1407],[-105,-1953]],[[329523,840520],[-1919,107]],[[327604,840627],[1770,-2004]],[[329374,838623],[-68,-2163],[1716,-223]],[[331022,836237],[1364,-1026]],[[332386,835211],[413,-1800]],[[332799,833411],[-1180,-2492]],[[331619,830919],[2384,1477]],[[334003,832396],[2116,-949]],[[336119,831447],[602,-1843]],[[336721,829604],[2272,222]],[[338993,829826],[1550,-1809]],[[340543,828017],[-2075,-1304]],[[338468,826713],[-1338,-1782]],[[337130,824931],[-3305,-1274]],[[333825,823657],[-1407,-3367]],[[332418,820290],[2798,2237]],[[335216,822527],[1861,1980],[2011,744]],[[339088,825251],[-733,738]],[[338355,825989],[2156,-387]],[[340511,825602],[780,-2198]],[[341291,823404],[-1089,-1138],[543,-774]],[[340745,821492],[1363,1602]],[[342108,823094],[2030,-901]],[[344138,822193],[867,-2225]],[[345005,819968],[-117,-4172]],[[344888,815796],[403,-2191]],[[345291,813605],[-3903,-4114]],[[322136,777316],[2050,-1544]],[[324186,775772],[1645,-68],[605,-703]],[[326436,775001],[1465,1460]],[[327901,776461],[1287,-1074]],[[329188,775387],[1280,-2341]],[[330468,773046],[-4118,-2655]],[[326350,770391],[-2201,-1192]],[[324149,769199],[-827,242]],[[323322,769441],[-437,-1166]],[[322885,768275],[-662,938],[-2397,-4603]],[[319826,764610],[-2432,-1819]],[[317394,762791],[-1077,1499]],[[316317,764290],[73,3279]],[[316390,767569],[1610,2165]],[[318000,769734],[-380,163]],[[317620,769897],[3683,3252]],[[321303,773149],[-65,-1013]],[[321238,772136],[2739,1342],[-4291,60]],[[319686,773538],[1662,2730]],[[331391,775898],[539,2552]],[[331930,778450],[1778,-263]],[[333708,778187],[103,-1152]],[[333811,777035],[-1550,-1839]],[[332261,775196],[-2494,-479]],[[329767,774717],[-587,2177],[1736,5067]],[[330916,781961],[1283,1226]],[[332199,783187],[212,-1397],[-681,-3528],[-1467,-1348],[128,-1430],[1000,414]],[[164773,916863],[10,-9267],[10642,-6944],[4258,-2777],[7806,-5092],[5096,-31],[3202,-3521],[1510,-733],[12107,-2039],[7265,-1224],[-7,-25044]],[[216662,860191],[-12497,0],[-9721,0]],[[156058,860191],[-2168,3915],[46,1723],[-2210,-955],[-4198,24],[-508,3401],[-2558,2023],[-1576,2404],[-1967,292],[161,2057],[-1411,1885],[248,1402],[-1401,1233],[803,1241],[-2265,2753],[-1240,2668],[-4151,2518],[686,1260],[-1134,1103],[1576,2209],[-1164,2556],[-2708,-394],[-129,3572],[-1167,2600],[-5751,2],[-120,3019],[-768,1376],[6,6805]],[[120990,912883],[3004,-1178]],[[123994,911705],[-1429,1307],[514,2336]],[[123079,915348],[4976,1286],[-762,-1632]],[[127293,915002],[2808,1073]],[[130101,916075],[898,1283]],[[130999,917358],[4733,1519]],[[135732,918877],[1772,1400]],[[137504,920277],[2361,-862]],[[139865,919415],[-3643,-2167],[-2508,-489]],[[133714,916759],[-4211,-3927]],[[129503,912832],[1868,-425],[-35,1566]],[[131336,913973],[2585,2089],[2153,-19]],[[136074,916043],[119,-1301],[1714,2648],[3455,1339]],[[141362,918729],[384,-1004]],[[141746,917725],[3576,3246],[-853,1857]],[[144469,922828],[2368,-1982]],[[146837,920846],[1462,-3015],[3403,-2258]],[[151702,915573],[516,2842]],[[152218,918415],[2102,1669],[889,-2492]],[[155209,917592],[-837,-1840]],[[154372,915752],[2268,-13]],[[156640,915739],[1622,2564]],[[158262,918303],[4151,-203]],[[162413,918100],[2360,-1237]],[[194439,937095],[5,-17659],[-8004,0],[-11594,6],[552,-2089]],[[175398,917353],[-774,2669]],[[174624,920022],[7063,1258],[5429,-517]],[[187116,920763],[2793,495]],[[189909,921258],[-5902,2263]],[[184007,923521],[-6204,-619]],[[177803,922902],[-4434,256]],[[173369,923158],[-1880,1533],[1249,1601],[5341,1323]],[[178079,927615],[846,975]],[[178925,928590],[-5934,-923]],[[172991,927667],[-54,1592],[-3127,163]],[[169810,929422],[-213,1770]],[[169597,931192],[2032,1642]],[[171629,932834],[-448,1607]],[[171181,934441],[2286,1760]],[[173467,936201],[8093,3209]],[[181560,939410],[1318,-609]],[[182878,938801],[152,-2422]],[[183030,936379],[-969,-1663]],[[182061,934716],[2661,676]],[[184722,935392],[811,1698]],[[185533,937090],[5384,-1584]],[[190917,935506],[-1737,-2119]],[[189180,933387],[4693,1808]],[[193873,935195],[-1266,2056],[1832,-156]],[[167398,943794],[1680,-502]],[[169078,943292],[1633,1284]],[[170711,944576],[2859,-77]],[[173570,944499],[5567,-3631]],[[179137,940868],[177,-1066]],[[179314,939802],[-9763,-4471]],[[169551,935331],[-1240,-1918],[-2144,-875],[-1221,-4189]],[[164946,928349],[-3139,-361]],[[161807,927988],[-3297,-2114],[-2976,3493]],[[155534,929367],[-4875,2725]],[[150659,932092],[2154,2668]],[[152813,934760],[419,2893]],[[153232,937653],[2887,4099]],[[156119,941752],[-2498,3437]],[[153621,945189],[9391,1077]],[[163012,946266],[4869,-1760]],[[167881,944506],[-483,-712]],[[168348,952708],[4561,2933],[-1599,-3156],[-2962,223]],[[194437,952245],[0,-4077]],[[194437,948168],[-6992,-2572]],[[187445,945596],[-2762,79]],[[184683,945675],[-1718,1990]],[[182965,947665],[3601,1240],[3236,261]],[[189802,949166],[1770,1229],[-7438,-938]],[[184134,949457],[-847,2167]],[[183287,951624],[-1209,-2052]],[[182078,949572],[-3547,-710]],[[178531,948862],[-5198,1799]],[[173333,950661],[1238,1192]],[[174571,951853],[2992,119]],[[177563,951972],[2654,1260],[-5287,-617]],[[174930,952615],[1909,1655]],[[176839,954270],[-756,1141],[2859,2156]],[[178942,957567],[3852,83]],[[182794,957650],[1029,-1449]],[[183823,956201],[3128,-30],[4569,-3870]],[[191520,952301],[2917,-56]],[[179024,963052],[-2040,-1550]],[[176984,961502],[181,-2907],[-2593,-1854]],[[174572,956741],[-2406,880]],[[172166,957621],[666,2001]],[[172832,959622],[-2809,-1607]],[[170023,958015],[-1047,-2290],[-986,1266],[-1082,-2852]],[[166908,954139],[-3074,957]],[[163834,955096],[-3836,-451]],[[159998,954645],[97,2708],[2088,238],[7011,5116]],[[169194,962707],[5030,50],[2545,1359]],[[176769,964116],[2255,-1064]],[[183799,965371],[-3325,1261]],[[180474,966632],[1941,652]],[[182415,967284],[1384,-1913]],[[193893,964006],[-4872,-1067]],[[189021,962939],[-3367,1102],[-63,2263]],[[185591,966304],[8843,1052],[-541,-3350]],[[190458,968527],[-4765,716],[7619,2064],[1122,-2577],[-3976,-203]],[[275745,817216],[-3632,1793]],[[272113,819009],[533,807]],[[272646,819816],[1977,116]],[[274623,819932],[1122,-2716]],[[280734,838063],[-2959,-1979]],[[277775,836084],[2024,3959]],[[279799,840043],[935,-1980]],[[310717,863514],[897,-667]],[[311614,862847],[-1153,-1236],[256,1903]],[[319909,868276],[-1665,1681]],[[318244,869957],[1785,75],[-120,-1756]],[[279041,874472],[614,-2284]],[[279655,872188],[-958,-2261],[-1656,1029]],[[277041,870956],[677,3109]],[[277718,874065],[1323,407]],[[304619,875284],[-1192,285]],[[303427,875569],[-1024,1666]],[[302403,877235],[1923,-856]],[[304326,876379],[293,-1095]],[[272221,877686],[-315,-1789]],[[271906,875897],[-2506,-2620]],[[269400,873277],[-1897,-294]],[[267503,872983],[-557,1873]],[[266946,874856],[1453,2538]],[[268399,877394],[3822,292]],[[284615,879658],[-1122,-1024]],[[283493,878634],[-1569,1996]],[[281924,880630],[1751,115],[940,-1087]],[[285098,881443],[642,556],[1267,-1708],[-1909,1152]],[[264112,891353],[853,1103],[7118,-4757]],[[272083,887699],[1039,-2559],[-630,-1073]],[[272492,884067],[2983,348]],[[275475,884415],[1463,-1942]],[[276938,882473],[-2067,-1781]],[[274871,880692],[-3699,1453]],[[271172,882145],[-248,1304]],[[270924,883449],[-2853,1020]],[[268071,884469],[-650,-1693]],[[267421,882776],[-2513,-2986]],[[264908,879790],[-2396,-1008]],[[262512,878782],[-858,3361],[-2896,-777]],[[258758,881366],[-950,574]],[[257808,881940],[2603,2752],[-340,2542]],[[260071,887234],[1146,6745]],[[261217,893979],[1131,1269]],[[262348,895248],[1764,-3895]],[[264792,893213],[-1282,1457]],[[263510,894670],[549,1112]],[[264059,895782],[733,-2569]],[[267428,894527],[-1090,-148]],[[266338,894379],[-802,2127],[1892,-1979]],[[295495,906299],[-2644,265],[-370,1413],[3532,-575]],[[296013,907402],[-518,-1103]],[[259456,906015],[-1011,2159]],[[258445,908174],[1496,493]],[[259941,908667],[-485,-2652]],[[291239,908966],[74,-4127]],[[291313,904839],[-1813,-1504],[-3403,-98]],[[286097,903237],[-836,2600],[1571,3113]],[[286832,908950],[2957,540],[1450,-524]],[[291997,909646],[-1443,1047],[1158,724]],[[291712,911417],[285,-1771]],[[279970,912589],[-542,459]],[[279428,913048],[2248,2652]],[[281676,915700],[1021,-396]],[[282697,915304],[-2727,-2715]],[[286124,914356],[-898,1615]],[[285226,915971],[1591,-74],[-693,-1541]],[[277839,916524],[-2223,991]],[[275616,917515],[3743,655],[-1520,-1646]],[[232499,915545],[1985,-3018]],[[234484,912527],[-2266,-2158]],[[232218,910369],[-2974,431]],[[229244,910800],[-5469,2217]],[[223775,913017],[-41,1263],[2777,1206]],[[226511,915486],[521,2488],[1327,635]],[[228359,918609],[975,-1297],[3165,-1767]],[[164773,916863],[2302,-1330],[2734,-506]],[[169809,915027],[2148,-1269],[5655,-1220],[1190,804]],[[178802,913342],[3380,-1855],[1250,-1543]],[[183432,909944],[-2225,-764],[-1858,-2180]],[[179349,907000],[4868,-1198]],[[184217,905802],[3463,-90]],[[187680,905712],[4529,875],[1952,947]],[[194161,907534],[1310,-1538]],[[195471,905996],[2882,-840]],[[198353,905156],[1843,-2750]],[[200196,902406],[-1574,-204]],[[198622,902202],[2183,-2087],[232,1559]],[[201037,901674],[1306,-719]],[[202343,900955],[-2266,5037]],[[200077,905992],[587,1779],[3713,997]],[[204377,908768],[1785,1931]],[[206162,910699],[-2297,-1002]],[[203865,909697],[-2809,-156]],[[201056,909541],[-318,-933]],[[200738,908608],[-2645,614]],[[198093,909222],[1036,1975]],[[199129,911197],[5969,1833],[1736,-1193]],[[206834,911837],[309,-1542]],[[207143,910295],[3430,-2530]],[[210573,907765],[1999,496]],[[212572,908261],[2172,-1799],[3159,-701]],[[217903,905761],[3052,868]],[[220955,906629],[3637,-687],[2041,495]],[[226633,906437],[2659,-1127]],[[229292,905310],[690,1410]],[[229982,906720],[-2512,285],[-1499,2729]],[[225971,909734],[3444,787],[1904,-2578]],[[231319,907943],[2097,1113]],[[233416,909056],[-1115,-4119]],[[232301,904937],[639,-1671]],[[232940,903266],[1171,265]],[[234111,903531],[1016,-1990]],[[235127,901541],[265,1670]],[[235392,903211],[-1089,2813]],[[234831,907706],[1666,120],[3381,3504]],[[239878,911330],[-2552,1651]],[[237326,912981],[1336,1328]],[[238662,914309],[-525,1891],[-4943,2228]],[[233194,918428],[-1377,2939],[1853,1314]],[[233670,922681],[-1862,1538],[398,2755]],[[232206,926974],[2338,374],[-856,1401]],[[233688,928749],[1864,1958]],[[235552,930707],[1789,446]],[[237341,931153],[4468,-4247]],[[241809,926906],[-91,-2428]],[[241718,924478],[2771,-3358]],[[244489,921120],[20,-1462]],[[244509,919658],[-2531,-2195]],[[241978,917463],[2711,-812],[3769,-158]],[[248458,916493],[-1896,-1297]],[[246562,915196],[2137,-2499]],[[248699,912697],[-293,-2305]],[[248406,910392],[1109,-1212],[1411,4410]],[[250926,913590],[1694,1490],[2821,-2691],[413,-3339],[-1262,237],[419,-3095]],[[255011,906192],[2581,-3447]],[[257592,902745],[2028,1968]],[[259620,904713],[1623,3296]],[[261243,908009],[1207,4132]],[[262450,912141],[1821,1801],[-1457,936]],[[262814,914878],[-78,3659],[3259,-87]],[[265995,918450],[1601,-800]],[[267596,917650],[3586,-343],[-1057,-874]],[[270125,916433],[3962,-2218]],[[274087,914215],[-1748,-1400]],[[272339,912815],[1879,-1341]],[[274218,911474],[-3531,-1249]],[[270687,910225],[1600,-3463]],[[272287,906762],[1962,-2382]],[[274249,904380],[-548,-2310],[-3261,-2858]],[[270440,899212],[-2449,-1296]],[[267991,897916],[-1103,1836],[-2571,2073]],[[264317,901825],[2833,-4376]],[[267150,897449],[-1813,-656]],[[265337,896793],[-2677,2121]],[[262660,898914],[-3309,-35]],[[259351,898879],[1640,-3015],[-3467,-3955],[-1885,-36],[-4943,3479]],[[250696,895352],[-3501,175],[3393,-1356],[2262,-2301]],[[252850,891870],[5407,-890]],[[258257,890980],[-703,-2203]],[[257554,888777],[-2293,-3809]],[[255261,884968],[-1977,-1132]],[[253284,883836],[-3751,-80]],[[249533,883756],[-215,-1995],[-1573,-362],[-2862,691],[3042,-2050]],[[247925,880040],[-86,-2251],[-1864,-992]],[[245975,876797],[-2534,90],[981,-1652]],[[244422,875235],[-1510,36]],[[242912,875271],[66,-2240]],[[242978,873031],[-2928,-1341]],[[240050,871690],[750,-1036]],[[240800,870654],[-2080,-2662]],[[238720,867992],[-21,-1061],[-1607,-4281]],[[237092,862650],[-348,-2456]],[[194439,937095],[3463,-2553],[1513,-4739]],[[199415,929803],[2511,850],[-1081,1509]],[[200845,932162],[-1507,5667]],[[199338,937829],[581,1439]],[[199919,939268],[2995,-430],[3162,-1573]],[[206076,937265],[4064,-9341]],[[210140,927924],[-611,-1954]],[[209529,925970],[1711,-2023]],[[211240,923947],[2511,-638],[4131,-3081],[1444,-143]],[[219326,920085],[299,-2344],[-3609,753]],[[216016,918494],[-1075,-1723]],[[214941,916771],[-2050,794]],[[212891,917565],[747,-2805]],[[213638,914760],[2608,1633],[818,-2747],[-2884,-1187]],[[214180,912459],[-6141,574]],[[208039,913033],[240,953]],[[208279,913986],[-3115,478]],[[205164,914464],[-1441,1644],[-2167,-2591]],[[201556,913517],[-4184,-1435],[-6569,-1291]],[[190803,910791],[-5047,-284]],[[185756,910507],[-1359,2040]],[[184397,912547],[-214,2113]],[[184183,914660],[-4069,413]],[[180114,915073],[-3763,947]],[[176351,916020],[-953,1333]],[[202996,940045],[854,1278]],[[203850,941323],[3059,415],[2400,-896]],[[209309,940842],[183,-1543]],[[209492,939299],[-1963,-2571],[-4533,3317]],[[279063,941080],[3474,67]],[[282537,941147],[3000,-985]],[[285537,940162],[2547,-2480]],[[288084,937682],[-308,-1543],[-3987,452]],[[283789,936591],[-4624,-835]],[[279165,935756],[-1897,2777]],[[277268,938533],[-1780,924]],[[275488,939457],[171,2235],[3404,-612]],[[259474,925417],[4151,836]],[[263625,926253],[624,-889]],[[264249,925364],[584,3462]],[[264833,928826],[-3477,2372]],[[261356,931198],[1405,1353],[2929,-962],[-2749,2185]],[[262941,933774],[-843,2091],[1740,3325],[3434,482]],[[267272,939672],[3118,1852],[3481,-563],[3144,-5266],[-2652,-2571]],[[274363,933124],[1030,-2372],[2268,2861]],[[277661,933613],[3730,-255],[2703,-1014]],[[284094,932344],[-2036,2147],[4349,1056]],[[286407,935547],[4442,-1421]],[[290849,934126],[1086,-2253]],[[291935,931873],[1695,-297]],[[293630,931576],[-308,-2239]],[[293322,929337],[4172,31],[4572,-1872]],[[302066,927496],[-663,-1520],[1951,-12]],[[303354,925964],[381,-1376]],[[303735,924588],[-1724,-2195]],[[302011,922393],[3684,2042]],[[305695,924435],[4421,-1908]],[[310116,922527],[-1557,-1872]],[[308559,920655],[485,-1575],[1732,2213]],[[310776,921293],[2102,-1660]],[[312878,919633],[395,-1800]],[[313273,917833],[-2479,139]],[[310794,917972],[-1108,-1048]],[[309686,916924],[4839,-1425]],[[314525,915499],[-89,-1090]],[[314436,914409],[-3154,565]],[[311282,914974],[518,-1241]],[[311800,913733],[-795,-2890]],[[311005,910843],[3678,-624]],[[314683,910219],[-325,-1362]],[[314358,908857],[1719,384],[-560,-2229],[3991,825]],[[319508,907837],[2280,-2491]],[[321788,905346],[889,-2126]],[[322677,903220],[2211,-173],[-1837,-2444]],[[323051,900603],[4814,1166],[1858,-2196]],[[329723,899573],[-1564,-1989]],[[328159,897584],[-1918,557]],[[326241,898141],[1560,-2201]],[[327801,895940],[-1663,-5]],[[326138,895935],[-191,-2337]],[[325947,893598],[-1885,-137],[-747,-4081]],[[323315,889380],[-803,1074]],[[322512,890454],[-1832,43]],[[320680,890497],[-2351,3836]],[[318329,894333],[1103,1858]],[[319432,896191],[-2282,-478]],[[317150,895713],[-2671,3310]],[[314479,899023],[-1455,-1493],[-1548,1105]],[[311476,898635],[1904,-2700]],[[313380,895935],[-2982,-568],[3163,-2952],[-578,-496]],[[312983,891919],[1832,-2268],[2022,-521]],[[316837,889130],[2400,-2661]],[[319237,886469],[-265,-2421]],[[318972,884048],[1365,0]],[[320337,884048],[456,-4526],[-1882,2963]],[[318911,882485],[396,-3137]],[[319307,879348],[1016,-1969]],[[320323,877379],[-1331,179]],[[318992,877558],[313,-1697]],[[319305,875861],[-3261,2731],[-529,-473]],[[315515,878119],[-2126,1645]],[[313389,879764],[-1982,2540]],[[311407,882304],[474,-1842]],[[311881,880462],[-2142,1793],[-1159,-131]],[[308580,882124],[2138,-3145],[1293,-467]],[[312011,878512],[4710,-5241]],[[316721,873271],[-768,-2018]],[[315953,871253],[-3287,1676]],[[312666,872929],[-2607,498],[-1955,1007]],[[308104,874434],[-1285,2011],[-1920,111]],[[304899,876556],[-2826,1653],[-1998,2296],[1436,487],[-2131,1165]],[[299380,882157],[-3390,4234]],[[295990,886391],[-4091,2183]],[[291899,888574],[767,-1391],[-5788,-1869],[-2965,743]],[[283913,886057],[-1065,1485]],[[282848,887542],[219,1905]],[[283067,889447],[2041,1524]],[[285108,890971],[95,1520]],[[285203,892491],[4162,-1340],[333,525]],[[289698,891676],[5994,1005]],[[295692,892681],[-542,1668],[-1911,2205],[3203,3175],[2947,3433]],[[299389,903162],[-3020,6597]],[[296369,909759],[-937,-434]],[[295432,909325],[-2788,2445],[-574,2124],[-4302,-2212]],[[287768,911682],[-427,1878]],[[287341,913560],[1676,127]],[[289017,913687],[463,1704]],[[289480,915391],[-1725,727]],[[287755,916118],[-292,1438]],[[287463,917556],[-2996,958]],[[284467,918514],[-697,2378]],[[283770,920892],[-1223,-106]],[[282547,920786],[-2176,2217],[-781,-1369],[1272,-2338],[-2018,-491]],[[278844,918805],[-5399,1283]],[[273445,920088],[-1608,-1600]],[[271837,918488],[-2646,964]],[[269191,919452],[-2133,-244]],[[267058,919208],[-6842,1081]],[[260216,920289],[-839,1516]],[[259377,921805],[-3546,-884]],[[255831,920921],[-2632,1606]],[[253199,922527],[-1688,3192]],[[251511,925719],[4475,-695]],[[255986,925024],[1957,398]],[[257943,925422],[-2033,1166],[-3353,471]],[[252557,927059],[-2129,1211]],[[250428,928270],[-498,2703]],[[249930,930973],[453,2745]],[[250383,933718],[1662,3892],[4288,3875]],[[256333,941485],[2641,658]],[[258974,942143],[4608,-153]],[[263582,941990],[-4219,-5554]],[[259363,936436],[1647,-6515]],[[261010,929921],[2814,-2475]],[[263824,927446],[-4350,-2029]],[[224561,941535],[4378,925]],[[228939,942460],[1612,-1311],[-753,-1655]],[[229798,939494],[-3234,-2290]],[[226564,937204],[2670,-48]],[[229234,937156],[880,-2070]],[[230114,935086],[1713,331]],[[231827,935417],[-705,-2280]],[[231122,933137],[507,-2844]],[[231629,930293],[-2691,-1209]],[[228938,929084],[-2435,850]],[[226503,929934],[723,-1969]],[[227226,927965],[-2690,-437],[-3965,4651]],[[220571,932179],[-3137,964],[-2750,2773]],[[214684,935916],[2198,1623]],[[216882,937539],[1588,-1840]],[[218470,935699],[2405,158]],[[220875,935857],[1078,1127]],[[221953,936984],[-951,1726]],[[221002,938710],[-2810,1045]],[[218192,939755],[1488,2218]],[[219680,941973],[2536,833]],[[223930,942411],[4686,1359],[-1589,-1423],[-3097,64]],[[241192,944080],[2634,-1117]],[[243826,942963],[5189,-616]],[[249015,942347],[-4898,-6604],[-5815,19]],[[238302,935762],[1822,-1989]],[[240124,933773],[-1340,-2325]],[[238784,931448],[-3209,-8]],[[235575,931440],[-985,4468]],[[234590,935908],[-237,5414]],[[234353,941322],[2598,-189]],[[236951,941133],[-951,2135]],[[236000,943268],[5192,812]],[[210778,949266],[-2132,660]],[[208646,949926],[1503,1671]],[[210149,951597],[1956,-1581]],[[212105,950016],[-1327,-750]],[[240159,949216],[-12,-1995]],[[240147,947221],[-3196,-291]],[[236951,946930],[-5173,2063],[2469,3190]],[[234247,952183],[3455,383]],[[237702,952566],[2457,-3350]],[[216034,955063],[-3020,-1485],[-2878,2478]],[[210136,956056],[4908,588]],[[215044,956644],[990,-1581]],[[211047,958429],[2699,-788]],[[213746,957641],[-3387,-733]],[[210359,956908],[688,1521]],[[228608,957739],[1014,-6202]],[[229622,951537],[-940,-1733]],[[228682,949804],[-2484,-698]],[[226198,949106],[-4627,-9]],[[221571,949097],[-1380,2007]],[[220191,951104],[3167,1830]],[[223358,952934],[-8195,-840]],[[215163,952094],[1662,2193]],[[216825,954287],[235,3289]],[[217060,957576],[5106,-2959]],[[222166,954617],[-2419,3175]],[[219747,957792],[6056,1294]],[[225803,959086],[2805,-1347]],[[194437,952245],[2939,947],[-2365,972]],[[195011,954164],[1016,1458]],[[196027,955622],[-1592,2195],[1847,1661]],[[196282,959478],[2420,-133],[-126,-1769]],[[198576,957576],[1834,-2259]],[[200410,955317],[-468,-1498]],[[199942,953819],[3136,-133]],[[203078,953686],[1376,1646]],[[204454,955332],[2543,-1863]],[[206997,953469],[-1060,-3283]],[[205937,950186],[-3585,-1567],[-4661,816]],[[197691,949435],[-3254,-1267]],[[238068,960381],[3611,-1730]],[[241679,958651],[4696,357],[5273,-2912]],[[251648,956096],[-5202,-173]],[[246446,955923],[5762,-2358],[-1225,-1167],[2026,-658]],[[253009,951740],[4609,970],[3302,-684]],[[260920,952026],[5935,1877]],[[266855,953903],[4940,71]],[[271795,953974],[5088,-1196]],[[276883,952778],[1910,-2546]],[[278793,950232],[-2009,-876],[2656,-793]],[[279440,948563],[-2434,-1991]],[[277006,946572],[-4253,-622]],[[272753,945950],[-9035,772]],[[263718,946722],[-2484,-641],[-6854,-27]],[[254380,946054],[231,1722]],[[254611,947776],[-4179,-1400],[-5881,1450]],[[244551,947826],[-1293,3277]],[[243258,951103],[995,1844],[-2842,4126],[-6062,-532]],[[235349,956541],[-4365,2738]],[[230984,959279],[243,1454]],[[231227,960733],[3111,545]],[[234338,961278],[3730,-897]],[[250463,962485],[-3651,710],[-5,1307]],[[246807,964502],[2715,-79]],[[249522,964423],[941,-1938]],[[209605,962901],[-1869,-923],[-2364,3220],[4233,-2297]],[[234765,965592],[5282,-126]],[[240047,965466],[176,-1756],[-6854,57]],[[233369,963767],[1396,1825]],[[232764,969972],[3660,-1103],[-556,-2089],[-5284,-1106]],[[230584,965674],[-3516,3693]],[[227068,969367],[120,2224]],[[227188,971591],[5576,-1619]],[[215066,972034],[2424,1183],[5817,-2939],[-394,-1659]],[[222913,968619],[1625,-2642]],[[224538,965977],[-3944,205]],[[220594,966182],[-1356,1790],[-4603,1051]],[[214635,969023],[-4425,-603],[-1865,1476]],[[208345,969896],[3420,6]],[[211765,969902],[-1076,2786]],[[210689,972688],[-1849,-1082]],[[208840,971606],[-1995,1336],[411,1724]],[[207256,974666],[5449,-48],[2361,-2584]],[[225579,978561],[-137,-1446],[-3477,1076]],[[221965,978191],[1003,1336],[2611,-966]],[[244762,985385],[3451,-3194]],[[248213,982191],[8245,-1313]],[[256458,980878],[-687,-1627]],[[255771,979251],[2624,-1206],[-882,-1859]],[[257513,976186],[4576,185]],[[262089,976371],[995,-2388]],[[263084,973983],[-6467,-3153]],[[256617,970830],[-3122,-546]],[[253495,970284],[-137,-2320]],[[253358,967964],[-3461,2456]],[[249897,970420],[1472,-2391]],[[251369,968029],[-6645,199]],[[244724,968228],[-6288,4486]],[[238436,972714],[7831,2173]],[[246267,974887],[-4106,527]],[[242161,975414],[-6338,-948]],[[235823,974466],[-4639,5012]],[[231184,979478],[5911,-516],[-4785,1448]],[[232310,980410],[2276,435]],[[234586,980845],[-1622,1924],[6125,-783]],[[239089,981986],[-4409,1652]],[[234680,983638],[680,965],[7938,1643]],[[243298,986246],[1464,-861]],[[306975,996546],[8048,-431]],[[315023,996115],[-2237,-1635]],[[312786,994480],[6923,1379],[5054,-1988]],[[324763,993871],[4467,-581]],[[329230,993290],[-1943,-2511]],[[327287,990779],[-6660,-1835],[-5699,-694]],[[314928,988250],[-4700,-2105]],[[317401,987526],[699,-1242]],[[318100,986284],[-8741,-3591]],[[309359,982693],[-7659,-5433],[-5791,-30],[18,-1548]],[[295927,975682],[-4981,-439]],[[290946,975243],[-4554,541]],[[286392,975784],[6574,-2724],[-11249,133]],[[281717,973193],[1942,-786]],[[283659,972407],[4519,382]],[[288178,972789],[5063,-1675]],[[293241,971114],[-1237,-1062]],[[292004,970052],[-4271,-198],[3396,-1088]],[[291129,968766],[-1868,-1884],[-5963,-377]],[[283298,966505],[-177,-2530]],[[283121,963975],[-2203,-1105]],[[280918,962870],[-6244,-373]],[[274674,962497],[4934,-659]],[[279608,961838],[334,-1317]],[[279942,960521],[2589,247]],[[282531,960768],[12,-2409],[-6683,-2338]],[[275860,956021],[-1334,1992]],[[274526,958013],[-3776,1247],[824,-1525]],[[271574,957735],[-4590,-75]],[[266984,957660],[-3488,-880]],[[263496,956780],[-2707,772]],[[260789,957552],[-6334,-177]],[[254455,957375],[-3261,515]],[[251194,957890],[196,1984],[3059,1641]],[[254449,961515],[4294,418],[-3452,3228]],[[255291,965161],[2992,1025]],[[258283,966186],[3971,-2555]],[[262254,963631],[2361,-592]],[[264615,963039],[2664,1016]],[[267279,964055],[-4194,157]],[[263085,964212],[-717,2184]],[[262368,966396],[1453,2279]],[[263821,968675],[-3315,-1370]],[[260506,967305],[827,1550]],[[261333,968855],[-4533,-984],[2067,3540]],[[258867,971411],[5011,818]],[[263878,972229],[4812,-841]],[[268690,971388],[2313,790]],[[271003,972178],[-3159,889],[-4205,3308],[-3697,1381]],[[259942,977756],[316,2809]],[[260258,980565],[7176,-537],[5189,-2999]],[[272623,977029],[2348,-174],[-5419,3465]],[[269552,980320],[8084,1485]],[[277636,981805],[8891,2071]],[[286527,983876],[-4725,256],[735,1458],[-7557,-3037]],[[274980,982553],[-8526,-584],[-9037,672]],[[257417,982641],[-4421,805]],[[252996,983446],[1412,1150]],[[254408,984596],[-4262,1024],[4079,2323]],[[254225,987943],[-8122,80]],[[246103,988023],[2535,1771]],[[248638,989794],[7920,1232]],[[256558,991026],[7206,-606]],[[263764,990420],[-4267,1212],[4678,1552]],[[264175,993184],[15200,-3524]],[[279375,989660],[-8407,3393]],[[270968,993053],[4003,2085],[6282,-591]],[[281253,994547],[-3906,1373]],[[277347,995920],[20398,1007],[9230,-381]],[[269941,777103],[157,-702],[2294,718]],[[272392,777119],[160,-2629]],[[272552,774490],[-3724,2172]],[[268828,776662],[1113,441]],[[279110,806663],[-539,898]],[[278571,807561],[536,2530]],[[279107,810091],[3,-3428]],[[279112,806382],[18,-19952],[412,-3135],[1622,-3722],[3051,-1226],[1248,-1677],[1138,-142],[1139,-2271],[1415,-451],[2888,1315],[1299,-440],[156,-2093]],[[293498,772588],[-1023,-1248]],[[292475,771340],[-1307,-619]],[[291168,770721],[-1718,-2421]],[[289450,768300],[-720,-735]],[[288730,767565],[-2939,-1148],[656,-1362]],[[286447,765055],[-914,-398]],[[285533,764657],[-1043,963]],[[284490,765620],[-4103,-1205]],[[280387,764415],[-1412,-1521],[-570,-1532]],[[278405,761362],[1213,-722]],[[279618,760640],[767,504]],[[280385,761144],[-13,-1048]],[[280372,760096],[25,-1444]],[[280397,758652],[-3232,-520]],[[277165,758132],[-641,-954],[-2648,99],[-1284,-2203],[-2162,-1310],[-1286,200],[88,1212]],[[269232,755176],[1352,232]],[[270584,755408],[-154,1442]],[[270430,756850],[633,2721]],[[271063,759571],[1757,1834],[213,4762]],[[273033,766167],[1195,3039],[-1165,3598],[1113,-13]],[[274176,772791],[103,-1306]],[[274279,771485],[1011,-1465]],[[275290,770020],[1860,-1609]],[[277150,768411],[327,1762]],[[277477,770173],[1085,-270],[-1023,2062]],[[277539,771965],[124,1381]],[[277663,773346],[-857,337]],[[276806,773683],[-1300,3268],[-2164,93]],[[273342,777044],[-52,905]],[[273290,777949],[-3917,366],[-2962,1065]],[[266411,779380],[-204,1195]],[[266207,780575],[-931,-470]],[[265276,780105],[321,2503]],[[265597,782608],[-1074,776]],[[264523,783384],[493,1680],[-1167,1886],[443,1844]],[[264292,788794],[-2563,120],[-908,1422],[-811,3086]],[[260010,793422],[-2298,265]],[[257712,793687],[-2889,1172]],[[254823,794859],[85,-2231]],[[254908,792628],[-749,1398]],[[254159,794026],[-755,-1481]],[[253404,792545],[-1184,-783]],[[252220,791762],[-1047,-2662]],[[251173,789100],[-3508,1179]],[[247665,790279],[-1884,-843]],[[245781,789436],[-4105,3279]],[[241676,792715],[-1975,-511]],[[239701,792204],[-2537,1286]],[[237164,793490],[-650,3328],[-835,387]],[[252921,841529],[2426,-2274]],[[255347,839255],[1427,-2435]],[[256774,836820],[5235,-2697]],[[262009,834123],[1710,-1869]],[[263719,832254],[3196,172]],[[266915,832426],[3703,-983]],[[270618,831443],[994,-1986],[-551,-2711],[768,-3189]],[[271829,823557],[-331,-5075]],[[271498,818482],[1914,-3518]],[[273412,814964],[61,-774]],[[273473,814190],[2477,-2833]],[[275950,811357],[499,-2673],[1041,-144]],[[277490,808540],[1622,-2158]],[[323107,780571],[1533,-828]],[[324640,779743],[2683,385]],[[327323,780128],[388,-389],[-2373,-2489]],[[325338,777250],[-485,1590]],[[324853,778840],[-622,-690]],[[324231,778150],[-1339,1447],[-978,164]],[[321914,779761],[-770,1278]],[[321144,781039],[1096,2492]],[[322240,783531],[-262,-1695]],[[321978,781836],[1129,-1265]],[[295648,774097],[-1094,-164]],[[294554,773933],[1345,1559],[-251,-1395]],[[308184,784912],[-526,997]],[[307658,785909],[-2124,-4467]],[[305534,781442],[-802,-4757],[-1671,-3813]],[[303061,772872],[-518,187]],[[302543,773059],[-675,-23]],[[301868,773036],[-528,-1674]],[[301340,771362],[-5096,-12]],[[296244,771350],[-3769,-10]],[[292475,771340],[3197,2496]],[[295672,773836],[1107,3465]],[[296779,777301],[3496,3684],[1777,737]],[[302052,781722],[2060,1637]],[[304112,783359],[4257,7361]],[[308369,790720],[3962,3441]],[[312331,794161],[3840,2116],[1819,315]],[[317990,796592],[1909,-441]],[[319899,796151],[1596,-1599]],[[321495,794552],[-242,-2954]],[[321253,791598],[-2530,-2381],[-1853,993]],[[316870,790210],[-2160,-986]],[[327168,795484],[-3740,1896]],[[323428,797380],[-886,1532]],[[322542,798912],[-1543,1007],[858,675]],[[321857,800594],[3536,-1400],[2892,-2499]],[[328285,796695],[45,-1124]],[[341388,809491],[-3917,-879]],[[337471,808612],[-1820,-3052]],[[335651,805560],[-2541,-3112]],[[333110,802448],[-3360,-312]],[[328542,801556],[-541,763]],[[328001,802319],[-2211,408]],[[325790,802727],[-5979,-155]],[[319811,802572],[-1113,263]],[[318698,802835],[-3408,-641]],[[315290,802194],[-1238,-1291],[-1197,-3823]],[[312855,797080],[-1901,-543],[-2424,-2535]],[[308530,794002],[-2069,-3731]],[[306461,790271],[-2297,918],[2016,-1517]],[[306180,789672],[-362,-1575]],[[305818,788097],[-2223,-4104],[-1561,-2036]],[[302034,781957],[-1700,-646]],[[300334,781311],[-3059,-2827]],[[297275,778484],[-1377,-2793]],[[295898,775691],[-1559,-1400]],[[294339,774291],[178,-929]],[[294517,773362],[-1019,-774]],[[279112,806382],[-2,281]],[[279107,810091],[1336,-479]],[[280443,809612],[381,-1561]],[[280824,808051],[477,1760],[-684,1399]],[[280617,811210],[1350,3072]],[[281967,814282],[-644,2225]],[[281323,816507],[-1082,6455]],[[280241,822962],[469,729]],[[280710,823691],[-2003,5078],[2101,1090],[2827,2104],[1573,1889]],[[285208,833852],[1874,3270]],[[287082,837122],[363,3553]],[[287445,840675],[-148,2809]],[[287297,843484],[-1622,4963]],[[285675,848447],[-3773,3931]],[[281902,852378],[157,1131]],[[282059,853509],[1939,3002]],[[283998,856511],[96,1753]],[[284094,858264],[1096,715]],[[285190,858979],[55,1456]],[[285245,860435],[-1331,3540]],[[283914,863975],[522,1098]],[[284436,865073],[-1607,-36]],[[282829,865037],[1853,4366]],[[284682,869403],[-1203,1219]],[[283479,870622],[-335,3516]],[[283144,874138],[1932,1287]],[[285076,875425],[4321,-1521]],[[289397,873904],[3253,-620]],[[292650,873284],[2822,1440]],[[295472,874724],[3900,-3689]],[[299372,871035],[2231,-3984],[3177,-536],[510,-1116],[1732,775],[-927,-6316]],[[306095,859858],[628,-1598],[-284,-1976]],[[307222,856261],[-366,-2776]],[[306856,853485],[2936,-271]],[[309792,853214],[669,-2514]],[[310461,850700],[1845,-1100]],[[312306,849600],[2672,1987]],[[314978,851587],[2782,3329]],[[317760,854916],[556,4055]],[[318316,858971],[1319,2706]],[[319635,861677],[880,320]],[[218424,794974],[-7407,0]],[[211017,794974],[-9175,0],[-7396,0]],[[113732,860194],[-65,2027],[-2483,-952]],[[111184,861269],[-2857,694]],[[108327,861963],[0,55397]],[[108327,917360],[5057,-801],[2926,-2154]],[[116310,914405],[4680,-1522]],[[6513,810872],[1645,1335],[-238,-1097],[-1407,-238]],[[9463,811999],[432,-667]],[[9895,811332],[-1456,-891]],[[8439,810441],[1024,1558]],[[13068,812920],[2748,1149]],[[15816,814069],[-1032,-1074],[-1716,-75]],[[33432,820758],[-3061,-3029]],[[30371,817729],[1969,3695]],[[32340,821424],[1032,595]],[[33372,822019],[60,-1261]],[[34659,820350],[-795,281],[1868,1201]],[[35732,821832],[440,2586]],[[36172,824418],[2075,-180],[-1499,-2705],[-2089,-1183]],[[45900,830448],[327,-1453]],[[46227,828995],[-4071,-1875]],[[42156,827120],[-178,1117],[994,1619]],[[42972,829856],[1839,938]],[[44811,830794],[1089,-346]],[[129708,833783],[-959,-1626]],[[128749,832157],[44,1600],[915,26]],[[136169,833460],[-581,-1676]],[[135588,831784],[-721,1199]],[[134867,832983],[-875,-1440],[-231,1485]],[[133761,833028],[614,2461]],[[135363,836221],[806,-2761]],[[128983,838496],[1009,-115]],[[129992,838381],[2860,-4972]],[[132852,833409],[-1162,-96]],[[131690,833313],[1708,-1515],[-438,-2939]],[[132960,828859],[-1800,1990]],[[131160,830849],[-892,1846]],[[130268,832695],[193,1360],[-1795,1158]],[[128666,835213],[317,3283]],[[132963,836150],[-1464,799]],[[131499,836949],[779,2491]],[[132278,839440],[685,-3290]],[[127916,837243],[-665,-301]],[[127251,836942],[-512,4513]],[[126739,841455],[1067,36],[374,-1556],[-264,-2692]],[[129538,842431],[1145,-729]],[[130683,841702],[-719,-2463]],[[129964,839239],[-1084,-3]],[[128880,839236],[-1046,3232]],[[127834,842468],[1704,-37]],[[125084,844493],[969,-3752]],[[126053,840741],[-93,-2907]],[[125960,837834],[-614,2624],[-1265,897],[363,1217]],[[124444,842572],[-838,1283],[-863,-1389]],[[122743,842466],[69,1825]],[[122812,844291],[1226,1279],[1046,-1077]],[[75283,847292],[1304,11]],[[76587,847303],[590,-1474]],[[77177,845829],[-2940,-2078]],[[74237,843751],[-1341,-2179],[-1352,1686]],[[71544,843258],[-47,-1370]],[[71497,841888],[-1237,2510]],[[70260,844398],[475,1327]],[[70735,845725],[1404,422]],[[72139,846147],[349,1121]],[[72488,847268],[1156,-527],[910,1427]],[[74554,848168],[729,-876]],[[123296,848287],[1697,351]],[[124993,848638],[197,-3377]],[[125190,845261],[-1573,1074],[-541,-1436],[-2434,3271],[684,1462],[1644,151],[326,-1496]],[[125887,849293],[1223,-104],[-74,-1538]],[[127036,847651],[948,-3245]],[[127984,844406],[-1851,-1451],[292,2312],[-1240,5017],[702,-991]],[[76620,850469],[853,-1179],[-1866,-861]],[[75607,848429],[-1668,423],[1502,1950]],[[75441,850802],[1179,-333]],[[89622,859078],[1542,3229]],[[91164,862307],[539,-616]],[[91703,861691],[-2081,-2613]],[[38512,862457],[1126,-411],[385,-2376]],[[40023,859670],[-1431,-816],[-2868,1381]],[[35724,860235],[-825,1173]],[[34899,861408],[1666,62]],[[36565,861470],[1947,987]],[[23714,881749],[2868,349]],[[26582,882098],[2784,-2077],[1977,-223]],[[31343,879798],[-377,-826]],[[30966,878972],[-2572,-459]],[[28394,878513],[-2080,1691]],[[26314,880204],[-3512,269]],[[22802,880473],[912,1276]],[[138819,835824],[-33,-3497]],[[138786,832327],[-1496,-3131],[-762,226],[-550,2074]],[[135978,831496],[591,1033]],[[136569,832529],[-848,3942]],[[135721,836471],[-1788,-716]],[[133933,835755],[-554,-2023]],[[133379,833732],[-667,1102],[1054,2601]],[[133766,837435],[-2970,5257]],[[129261,843431],[-245,3098]],[[129016,846529],[-1819,3187],[-1264,899]],[[125933,850615],[-1300,2714],[-645,3415],[-384,-1286],[1258,-5305]],[[124862,850153],[-2289,517],[-759,2339]],[[121814,853009],[-2340,1455]],[[119474,854464],[2577,-3447],[-1447,-1230]],[[120604,849787],[-2671,1992]],[[117933,851779],[-2246,2998]],[[115687,854777],[-4019,2719]],[[111668,857496],[1302,1960],[-524,830],[-1937,-1721]],[[110509,858565],[-1741,131]],[[108768,858696],[-2296,1310]],[[106472,860006],[-3544,753]],[[102928,860759],[-3338,-478],[-2094,1888]],[[97496,862169],[584,1979]],[[98080,864148],[-1548,-1711],[-1808,580]],[[94724,863017],[-2054,2483]],[[92670,865500],[155,1366]],[[92825,866866],[-2245,-1243],[-1707,299],[705,1484]],[[89578,867406],[-2239,-2466],[1649,-1883]],[[88988,863057],[-1297,-2937]],[[87691,860120],[-2679,691]],[[85012,860811],[-563,-1987]],[[84449,858824],[-2732,-1219]],[[81717,857605],[-980,-1869]],[[80737,855736],[-2234,-359],[-309,1290],[1251,652],[-1261,1574]],[[78184,858893],[1116,2491]],[[79300,861384],[265,3084],[2541,1780],[2359,-176]],[[84465,866072],[-980,1780],[-1853,41],[-3116,-2313]],[[78516,865580],[-46,-924],[-2510,-3060]],[[75960,861596],[-292,-1880]],[[75668,859716],[-1255,-464]],[[74413,859252],[-2436,-2840]],[[71977,856412],[-250,-1231]],[[71727,855181],[2343,-1763]],[[74070,853418],[-1904,-2162]],[[72166,851256],[-630,-1976]],[[71536,849280],[-2112,-851],[-2141,-2652]],[[67283,845777],[-1945,-1424]],[[65338,844353],[-420,-1884]],[[64918,842469],[-3444,-2160],[-1857,-1836]],[[59617,838473],[-700,-2064]],[[58917,836409],[-2649,-848]],[[56268,835561],[-2100,-1816],[-2726,-826]],[[51442,832919],[60,1370]],[[51502,834289],[-1708,-2902]],[[49794,831387],[-1300,613],[-368,-1458],[-1835,-933]],[[46291,829609],[156,1675]],[[46447,831284],[1035,437]],[[47482,831721],[2081,3103]],[[49563,834824],[2616,1788]],[[52179,836612],[2565,-1280]],[[54744,835332],[-655,948],[627,2067]],[[54716,838347],[3644,3235]],[[58360,841582],[3480,4076]],[[61840,845658],[594,5174]],[[62434,850832],[1060,2703]],[[63494,853535],[-2444,-1407]],[[61050,852128],[-2094,1554]],[[58956,853682],[-36,-2735]],[[58920,850947],[-817,171],[-1633,2615]],[[56470,853733],[-694,-540]],[[55776,853193],[-1229,1370]],[[54547,854563],[-2774,-2261]],[[51773,852302],[-2177,-150]],[[49596,852152],[1169,889],[-831,2901]],[[49934,855942],[541,1805],[-1646,4120]],[[48829,861867],[786,2379],[-1519,-2469],[317,-1654],[-3710,-1084],[-2099,2945],[-1921,1407],[1525,2078]],[[42208,865469],[2985,-1789]],[[45193,863680],[860,991]],[[46053,864671],[-4875,1234]],[[41178,865905],[833,718]],[[42011,866623],[-2265,1263],[-1324,2078]],[[38422,869964],[1541,1295]],[[39963,871259],[-262,1369]],[[39701,872628],[1425,2210],[1153,46]],[[42279,874884],[-183,1894]],[[42096,876778],[2050,2730],[2079,-1279]],[[46225,878229],[2048,1303],[940,1561]],[[49213,881093],[3286,170],[893,1546]],[[53392,882809],[-581,2562]],[[52811,885371],[-1186,1629]],[[51625,887000],[1341,313]],[[52966,887313],[-708,2043],[-1590,-638]],[[50668,888718],[-2910,-2619]],[[47758,886099],[-2518,1268]],[[45240,887367],[-3294,-756]],[[41946,886611],[-3455,724]],[[38491,887335],[-757,2036]],[[37734,889371],[-1426,1365],[2032,880]],[[38340,891616],[-3353,690],[-1899,1397]],[[33088,893703],[2816,1300]],[[35904,895003],[4514,3156]],[[40418,898159],[4783,1224]],[[45201,899383],[-850,-2375]],[[44351,897008],[938,-782],[5221,-177]],[[50510,896049],[1001,1348],[-1034,531]],[[50477,897928],[-2165,3101]],[[48312,901029],[1639,-653]],[[49951,900376],[300,-1330]],[[50251,899046],[3496,-1106]],[[53747,897940],[1079,1182]],[[54826,899122],[-1671,583],[-1484,-705]],[[51671,899000],[-1273,880]],[[50398,899880],[651,1653]],[[51049,901533],[-3832,284]],[[47217,901817],[-1997,997]],[[45220,902814],[-1125,2436],[-3501,2600]],[[40594,907850],[-3890,1860],[1128,388],[476,2726]],[[38308,912824],[5295,304],[3169,2674],[582,2193],[2440,2392],[2994,846]],[[52788,921233],[4671,3400],[3655,-197],[4246,3333],[6320,-3594]],[[71680,924175],[2673,778],[2778,-723]],[[77131,924230],[-661,-929]],[[76470,923301],[3461,-1391]],[[79931,921910],[5430,486],[4345,-1681],[5228,-339],[1740,-896]],[[96674,919480],[5497,637],[5029,-2743]],[[107200,917374],[1127,-14]],[[256972,684688],[-834,-509]],[[256138,684179],[-614,2384]],[[255524,686563],[-345,-1940]],[[255179,684623],[-734,25]],[[254445,684648],[-241,9003],[1116,18025],[-246,391]],[[255074,712067],[-48,154],[7130,-143]],[[262156,712078],[1175,-12113],[753,-4205],[-540,-2169],[324,-5206]],[[263868,688385],[-7184,-6],[478,-1950],[-190,-1741]],[[250820,718007],[-663,-1911],[-553,-3346],[-420,-671]],[[249184,712079],[-949,-3479],[-1208,-2604],[-383,-1769],[162,-3909]],[[246806,700318],[-8032,-23]],[[238774,700295],[-17,3214],[-1213,556]],[[237544,704065],[125,10302],[-498,6598]],[[237171,720965],[6963,0],[5415,0],[240,-1210],[-848,-1801],[1879,53]],[[197092,723926],[-3,-33609]],[[197089,690317],[-5539,-21]],[[191550,690296],[-3952,2631]],[[187598,692927],[-6587,4384]],[[181011,697311],[308,1227]],[[181319,698538],[688,750],[-632,1942],[544,4349],[1065,2267],[-681,1197],[-666,2977]],[[181637,712020],[55,2143],[-347,4338],[1868,573],[8,4872]],[[183221,723946],[5202,-7],[8669,-13]],[[154920,753549],[5885,-2],[5859,0]],[[166664,753547],[2,-17775],[5205,-7749],[5485,-8879],[4281,-7124]],[[181319,698538],[-6676,-1079],[-942,4515],[-1702,2528],[-917,129]],[[171082,704631],[-267,1621],[-1770,560]],[[169045,706812],[-1284,1813]],[[167761,708625],[-2431,318]],[[165330,708943],[-454,642]],[[164876,709585],[31,2941]],[[164907,712526],[-630,1712]],[[164277,714238],[-2826,5721],[-9,3601]],[[161442,723560],[-1429,1591]],[[160013,725151],[-331,3345],[1233,-1741]],[[160915,726755],[-875,2857],[1857,436]],[[161897,730048],[-2131,443],[-103,-1673]],[[159663,728818],[-1141,1356],[-525,2334]],[[157997,732508],[-1611,2714],[-511,5649],[-1220,2317]],[[154655,743188],[-132,1417]],[[154523,744605],[663,2836]],[[155186,747441],[179,2455]],[[155365,749896],[-445,3653]],[[216598,741701],[33,-17775]],[[216631,723926],[-2745,0]],[[213886,723926],[-5248,0],[-7347,0],[-4199,0]],[[197092,723926],[1,23698]],[[197093,747624],[8724,0],[5234,0]],[[211051,747624],[5546,-1],[1,-5922]],[[300553,753615],[-115,-4008]],[[300438,749607],[-3007,-298]],[[297431,749309],[-1960,-1738]],[[295471,747571],[416,6302]],[[295887,753873],[4666,-258]],[[290497,740602],[-463,-1035]],[[290034,739567],[543,-3247]],[[290577,736320],[985,-3774]],[[291562,732546],[-1860,-4],[-215,7508]],[[289487,740050],[1010,552]],[[273600,686784],[707,-5555]],[[274307,681229],[971,-4407]],[[275278,676822],[1044,-3340]],[[276322,673482],[-873,1608],[249,-2231]],[[275698,672859],[1451,-6955]],[[277149,665904],[514,-3783],[-327,-4089]],[[277336,658032],[-578,-3241]],[[276758,654791],[-1026,-1036]],[[275732,653755],[-1039,-109]],[[274693,653646],[-8,1358]],[[274685,655004],[-699,2747],[-974,902]],[[273012,658653],[-419,2677]],[[272593,661330],[-481,693]],[[272112,662023],[-76,2012]],[[272036,664035],[-620,-123]],[[271416,663912],[-960,3873]],[[270456,667785],[653,1841],[-497,729],[-226,-1422]],[[270386,668933],[-507,756]],[[269879,669689],[508,3791]],[[270387,673480],[25,2380]],[[270412,675860],[-1775,3344],[-1122,2808]],[[266544,683066],[-941,-1164]],[[265603,681902],[-2600,-1346]],[[263003,680556],[-97,1158]],[[262906,681714],[-1117,1726]],[[261789,683440],[-1941,1375]],[[259848,684815],[-2876,-127]],[[263868,688385],[343,-1663],[7345,-921],[494,-954],[351,2459],[1199,-522]],[[275354,694475],[-669,-895]],[[274685,693580],[-486,-3642],[-426,-380]],[[273773,689558],[-173,-2774]],[[262156,712078],[3609,-75]],[[265765,712003],[3360,78]],[[269125,712081],[-669,-1736],[1411,-1608],[718,-2485],[1621,-2930],[1433,-3479],[1153,-4894],[562,-474]],[[67829,617353],[-833,347],[-465,4024],[635,1566]],[[67166,623290],[-33,1550]],[[67133,624840],[1759,-1668],[1095,-2783]],[[69987,620389],[-1404,-1566]],[[68583,618823],[-754,-1470]],[[65314,628731],[1381,-1039]],[[66695,627692],[-1247,-826],[-134,1865]],[[63295,630407],[1393,-358]],[[64688,630049],[-410,-585]],[[64278,629464],[-983,943]],[[61668,631836],[456,-883]],[[62124,630953],[-1320,65]],[[60804,631018],[-453,1580]],[[60351,632598],[863,688]],[[61214,633286],[454,-1450]],[[57298,634654],[-1158,648],[1215,1054]],[[57355,636356],[-57,-1702]],[[248192,756583],[1342,-2445],[-500,-2774],[-1946,-1558],[262,-2094],[-1356,-3769]],[[245994,743943],[-804,1453],[-5606,-69],[-5598,-66]],[[233986,745261],[-894,7225],[-1095,4087]],[[231997,756573],[-391,1293],[467,4571]],[[232073,762437],[4516,2],[9952,6]],[[246541,762445],[488,-4229],[1163,-1633]],[[191524,768349],[3,-14800]],[[191527,753549],[-8312,2]],[[183215,753551],[-8261,-4]],[[174954,753547],[-10,10711],[301,2067],[-806,1652],[1813,6046],[79,1571],[-1045,1660]],[[175286,777254],[-356,2530],[-40,15190]],[[177643,794974],[-1,-5855],[875,-1768],[87,-1592],[981,-1046],[2077,-3560],[696,44],[-495,-6470],[643,-521],[986,1149],[1678,-5175],[942,-1941],[2134,476],[2032,-94],[450,1184],[796,-1456]],[[256077,756489],[41,-1692]],[[256118,754797],[767,-2866]],[[256885,751931],[-25,-16700],[-293,-2170],[-659,-1467],[-469,-2998]],[[255439,728596],[-182,-1509],[-1076,-1007],[64,-1635],[-1596,560],[-300,-1130]],[[252349,723875],[-924,1307],[-101,2558],[-1806,2515],[-558,1426],[687,3098],[-1491,918],[-710,2366],[-1407,2606],[-45,3274]],[[248192,756583],[7885,-94]],[[264455,751775],[-71,-15460]],[[264384,736315],[58,-1614],[-1789,-808],[-38,-905],[-1792,-3190],[-586,1018],[-650,-1461],[-890,350],[-756,-1013],[-642,701],[-1860,-797]],[[256885,751931],[1830,194]],[[258715,752125],[5739,-4],[1,-346]],[[237172,723926],[-5135,0],[-6419,0],[-8987,0]],[[216598,741701],[7569,0],[6405,0],[4575,3]],[[235147,741704],[1158,-829],[-355,-2089],[1210,-2279],[12,-12581]],[[270520,732502],[66,-2065],[858,-2444],[875,-874]],[[272319,727119],[-1990,-2379],[-1302,-2226],[-1437,-932]],[[267590,721582],[-175,-108],[-7714,462],[-3699,-124],[-611,-854],[-3865,1]],[[251526,720959],[817,1278],[6,1638]],[[264384,736315],[1288,-249],[363,-1094],[1493,-1278],[2248,360],[744,-1552]],[[251331,683591],[-1957,1106],[-522,-1415]],[[248852,683282],[661,-659]],[[249513,682623],[1216,846]],[[250729,683469],[1064,-2084],[-1018,-1190]],[[250775,680195],[576,-1180]],[[251351,679015],[1383,-1287]],[[252734,677728],[-939,-786],[-1454,2297],[-487,-157]],[[249854,679082],[-446,-1934]],[[249408,677148],[-463,1127]],[[248945,678275],[-1032,-973],[-1497,937],[127,996],[-1802,2244]],[[244741,681479],[-1021,-1654]],[[243720,679825],[-1141,239],[-1400,1077],[-1968,184]],[[239211,681325],[250,991]],[[239461,682316],[205,3442],[527,2891],[-1425,5646],[6,6000]],[[246806,700318],[0,-2316],[561,-2280],[-941,-2110],[-921,-3596],[-107,-1630],[5336,-4],[-277,-1602],[874,-3189]],[[302128,751805],[-426,1839],[-1149,-29]],[[295887,753873],[631,4128]],[[296518,758001],[2185,-130]],[[298703,757871],[3159,-165],[1454,1032]],[[303316,758738],[196,-1229]],[[303512,757509],[-863,-2003]],[[302649,755506],[856,-606],[621,-2521]],[[304126,752379],[1425,135]],[[305551,752514],[147,-883]],[[305698,751631],[-1968,-847],[-122,1071]],[[303608,751855],[-1299,-1336]],[[302309,750519],[-181,1286]],[[291562,732546],[-940,-2552]],[[290622,729994],[-787,-421]],[[289835,729573],[-532,105]],[[289303,729678],[-105,2275],[-897,34]],[[288301,731987],[-146,1414]],[[288155,733401],[731,10]],[[288886,733411],[-652,3495]],[[288234,736906],[1007,1892],[-1521,-1694],[-224,-4105]],[[287496,732999],[130,-2125]],[[287626,730874],[-2186,1904]],[[285440,732778],[587,2337]],[[286027,735115],[-1935,2708]],[[284092,737823],[-357,1501],[-905,509],[-873,-902],[-784,552],[-1973,-2463],[29,3033]],[[279229,740053],[10258,-3]],[[313542,772321],[-185,-2926]],[[313357,769395],[-1799,-588]],[[311558,768807],[-605,-1137]],[[310953,767670],[-1094,730]],[[309859,768400],[-228,-1475],[-1191,1038]],[[308440,767963],[-297,-1992]],[[308143,765971],[-2055,-1928]],[[306088,764043],[-436,492],[-2133,-4652]],[[303519,759883],[-618,1892],[-358,11284]],[[270430,756850],[-395,631]],[[270035,757481],[-1853,-5444]],[[268182,752037],[-3727,-262]],[[258715,752125],[639,894]],[[259354,753019],[925,2999]],[[260279,756018],[215,2861]],[[260494,758879],[-889,4365]],[[259605,763244],[64,2586]],[[259669,765830],[620,1522]],[[260289,767352],[164,2308],[1723,2801],[186,-2496],[483,2993]],[[262845,772958],[1135,685]],[[263980,773643],[-40,2219]],[[263940,775862],[582,164]],[[264522,776026],[3593,-2701]],[[268115,773325],[512,-3884]],[[268627,769441],[-101,-1826]],[[268526,767615],[-1523,-2444]],[[267003,765171],[399,-1990]],[[267402,763181],[979,1761]],[[268381,764942],[1210,848]],[[269591,765790],[788,-1261]],[[270379,764529],[684,-4958]],[[256650,771959],[-721,1537],[157,1886],[-952,1536],[-5464,2348],[-802,1488]],[[248868,780754],[2923,1713],[1957,2074]],[[253748,784541],[560,-2551]],[[254308,781990],[647,889]],[[254955,782879],[1606,-741],[777,-1790],[2002,-424],[1404,1387]],[[260744,781311],[3234,497]],[[263978,781808],[-185,-1570]],[[263793,780238],[1964,-86]],[[265757,780152],[279,-1823]],[[266036,778329],[906,-1200],[-1723,81],[-780,-712]],[[264439,776498],[-1881,1381],[-3012,-1333]],[[259546,776546],[-1141,-944]],[[258405,775602],[-23,1149]],[[258382,776751],[-1732,-4792]],[[254939,784414],[-604,-1184]],[[254335,783230],[-490,967]],[[253845,784197],[615,1271]],[[254460,785468],[1815,267]],[[256275,785735],[-1336,-1321]],[[251173,789100],[-3832,-2831]],[[247341,786269],[-3194,-4511]],[[244147,781758],[-513,-602],[-3,-3414],[-1114,-1040],[-553,-1861],[516,-598],[-256,-4170],[3935,-4735],[382,-2893]],[[232073,762437],[0,10641],[-1088,1770],[803,2055]],[[231788,776903],[-640,4178],[-200,5571],[-742,3466],[-278,4857]],[[251526,720959],[-706,-2952]],[[237171,720965],[1,2961]],[[235147,741704],[-1161,3557]],[[254445,684648],[-1404,262]],[[253041,684910],[-1710,-1319]],[[249184,712079],[5890,-12]],[[211080,776905],[-72,-5581]],[[211008,771324],[-4871,0],[-8525,0],[-6087,0],[-1,-2975]],[[211017,794974],[63,-18069]],[[281766,705417],[-3026,5452],[-3141,189],[-842,1937],[-3524,272],[-2108,-1186]],[[265765,712003],[104,1260],[1155,1860],[2447,1692],[285,896],[2323,1014],[882,1378],[208,1511]],[[273169,721614],[3063,-141],[6030,-100],[6720,-109]],[[288982,721264],[232,-2226]],[[289214,719038],[-2343,-1292]],[[286871,717746],[2650,-342]],[[289521,717404],[-4,-1498]],[[289517,715906],[-1111,-1735]],[[288406,714171],[-996,914]],[[287410,715085],[-1228,-297],[1282,-1113]],[[287464,713675],[-11,-2922]],[[287453,710753],[-1714,-411]],[[285739,710342],[-1714,-2505]],[[284025,707837],[-491,-2046]],[[283534,705791],[-1768,-374]],[[231788,776903],[-9060,0],[-7118,0],[-4530,2]],[[211051,747624],[-20,11852]],[[211031,759476],[9614,0],[5906,-2],[827,-881],[2514,49],[2105,-2069]],[[303519,759883],[-203,-1145]],[[298703,757871],[-230,926],[457,3807],[983,4571],[967,885],[460,3302]],[[291460,741597],[943,1013],[-1262,2615],[184,2389],[1177,2122]],[[292502,749736],[2192,-2162]],[[294694,747574],[-921,-3176]],[[293773,744398],[789,-758],[-607,-3565],[-1718,-4293]],[[292237,735782],[-2027,2893]],[[290210,738675],[284,1774],[966,1148]],[[213888,720966],[-116,-3],[-66,-26647],[-10007,-11],[619,-1378]],[[204318,692927],[-4907,65]],[[199411,692992],[-7,-2665]],[[199404,690327],[-2315,-10]],[[213886,723926],[2,-2960]],[[166664,753547],[8290,0]],[[183215,753551],[6,-29605]],[[300269,747979],[-3587,-2408]],[[296682,745571],[-2327,-92]],[[294355,745479],[1273,1665],[4641,835]],[[296244,771350],[86,-4365],[-195,-4089],[400,-151],[-17,-4744]],[[295471,747571],[-991,-1425],[214,1428]],[[292502,749736],[-899,1159],[-913,2639],[-4595,6],[-7659,10],[0,1627]],[[278436,755177],[2459,3101]],[[280895,758278],[-523,1818]],[[280385,761144],[2462,663]],[[282847,761807],[1794,-755],[1535,60]],[[286176,761112],[2069,1617],[-157,1768]],[[288088,764497],[604,887]],[[288692,765384],[-782,637]],[[287910,766021],[1540,2279]],[[276336,745528],[-1234,-6373],[-1928,-1773],[-626,-2238],[-329,693],[-881,-3149],[-818,-186]],[[268182,752037],[2706,-2059]],[[270888,749978],[2103,669]],[[272991,750647],[1374,1537]],[[274365,752184],[1967,1298]],[[276332,753482],[4,-7954]],[[237544,704065],[-2094,1826],[-1955,-437],[-1037,-1017],[-1841,1344],[-393,-1204],[-1503,1512],[-506,-676],[-711,1594],[-1098,-333],[-1924,869],[-483,1308],[-756,-401],[-1019,1174],[-9,11341],[-8327,1]],[[157719,778152],[775,-229],[601,-2614],[1453,-490],[1500,753],[1715,-468],[1927,510],[3699,1630],[5897,10]],[[154920,753549],[-351,723]],[[154569,754272],[-513,4087],[1086,5208]],[[155142,763567],[249,6434]],[[155391,770001],[361,4735]],[[155752,774736],[49,3585]],[[155801,778321],[1918,-169]],[[291460,741597],[-963,-995]],[[279229,740053],[-2895,-2],[2,5477]],[[276332,753482],[2104,1695]],[[302128,751805],[-583,-1497]],[[301545,750308],[-1107,-701]],[[281766,705417],[-770,-903]],[[280996,704514],[-1078,-3193]],[[279918,701321],[-2201,-3349]],[[277717,697972],[-947,-706]],[[276770,697266],[-1416,-2791]],[[211031,759476],[-23,11848]],[[267590,721582],[5579,32]],[[239461,682316],[-849,-1818]],[[238612,680498],[-1885,-726]],[[236727,679772],[101,1197],[-781,-282]],[[236047,680687],[374,-1965]],[[236421,678722],[-1070,-2410]],[[235351,676312],[-1612,-1917]],[[233739,674395],[-2185,405],[609,-1489]],[[232163,673311],[-1703,-2153]],[[230460,671158],[-707,-2508]],[[229753,668650],[-739,-4166],[424,-3382]],[[229438,661102],[711,-2578],[-590,-538]],[[229559,657986],[-2011,1148],[-2847,2266],[-933,3494],[-172,3032]],[[223596,667926],[-2196,4617]],[[221400,672543],[-1008,4389]],[[220392,676932],[-2171,4196],[-2508,523],[-1087,-1310]],[[214626,680341],[-364,-2286]],[[214262,678055],[-1089,-1522]],[[213173,676533],[-2369,2281]],[[210804,678814],[-1093,1727]],[[209711,680541],[-1319,5736],[-825,956]],[[207567,687233],[-2722,4369]],[[204845,691602],[-527,1325]],[[191527,753549],[0,-5925],[5566,0]],[[290622,729994],[-612,-2333]],[[290010,727661],[-1114,-2176]],[[288896,725485],[939,4088]],[[286027,735115],[-787,-2919]],[[285240,732196],[2122,-1790]],[[287362,730406],[793,-1190]],[[288155,729216],[2,-3179]],[[288157,726037],[-965,-204]],[[287192,725833],[910,-1599]],[[288102,724234],[-323,-965]],[[287779,723269],[1111,136],[92,-2141]],[[272319,727119],[1112,-1954],[1725,545],[1743,1231],[86,992],[1910,5332],[993,-596],[553,1856],[1683,2198],[314,1724],[1351,-1816],[303,1192]],[[157719,778152],[-677,696]],[[157042,778848],[-1689,49]],[[155353,778897],[-108,4478]],[[155245,783375],[-734,3693]],[[154511,787068],[-681,1455]],[[153830,788523],[-247,2821]],[[153583,791344],[2040,-1255]],[[155623,790089],[2642,-516]],[[158265,789573],[683,333]],[[158948,789906],[557,-5003]],[[159505,784903],[623,464],[-109,2660],[419,1128],[-1185,2692]],[[159253,791847],[344,1760]],[[159597,793607],[-677,1367]],[[258356,772744],[-801,-2405]],[[257555,770339],[-323,707]],[[257232,771046],[1124,1698]],[[256650,771959],[-1009,-3296]],[[255641,768663],[1013,1747]],[[256654,770410],[742,-421]],[[257396,769989],[-1568,-9070],[249,-4430]],[[244147,781758],[1689,23]],[[245836,781781],[1536,1111]],[[247372,782892],[581,-1569]],[[247953,781323],[915,-569]]],"transform":{"scale":[0.00036000036000036,0.00016879196566696583],"translate":[-180,-85.19218750000006]}} ================================================ FILE: assets/icons.go ================================================ package assets import ( _ "embed" ) //go:embed data/icons/pm_light_512.png var PNG []byte ================================================ FILE: assets/icons_default.go ================================================ //go:build !windows package assets import ( "bytes" _ "embed" "fmt" "image" "image/png" "golang.org/x/image/draw" "github.com/safing/portmaster/base/log" ) // Colored Icon IDs. const ( GreenID = 0 YellowID = 1 RedID = 2 BlueID = 3 ) // Icons. var ( //go:embed data/icons/pm_light_green_512.png GreenPNG []byte //go:embed data/icons/pm_light_yellow_512.png YellowPNG []byte //go:embed data/icons/pm_light_red_512.png RedPNG []byte //go:embed data/icons/pm_light_blue_512.png BluePNG []byte // ColoredIcons holds all the icons as .PNGs. ColoredIcons [4][]byte ) func init() { setColoredIcons() } func setColoredIcons() { ColoredIcons = [4][]byte{ GreenID: GreenPNG, YellowID: YellowPNG, RedID: RedPNG, BlueID: BluePNG, } } // ScaleColoredIconsTo scales all colored icons to the given size. // It must be called before any colored icons are used. // It does nothing on Windows. func ScaleColoredIconsTo(pixelSize int) { // Scale colored icons only. GreenPNG = quickScalePNG(GreenPNG, pixelSize) YellowPNG = quickScalePNG(YellowPNG, pixelSize) RedPNG = quickScalePNG(RedPNG, pixelSize) BluePNG = quickScalePNG(BluePNG, pixelSize) // Repopulate colored icons. setColoredIcons() } func quickScalePNG(imgData []byte, pixelSize int) []byte { scaledImage, err := scalePNGTo(imgData, pixelSize) if err != nil { log.Warningf("failed to scale image (using original): %s", err) return imgData } return scaledImage } func scalePNGTo(imgData []byte, pixelSize int) ([]byte, error) { img, err := png.Decode(bytes.NewReader(imgData)) if err != nil { return nil, fmt.Errorf("failed to decode image: %w", err) } // Return data unprocessed if image already has the correct size. if img.Bounds().Dx() == pixelSize { return imgData, nil } // Scale image to given size. rectangle := image.Rect(0, 0, pixelSize, pixelSize) scaledImage := image.NewRGBA(rectangle) draw.CatmullRom.Scale(scaledImage, rectangle, img, img.Bounds(), draw.Over, nil) // Encode scaled image. scaledImgBuffer := new(bytes.Buffer) err = png.Encode(scaledImgBuffer, scaledImage) if err != nil { return nil, fmt.Errorf("failed to encode image: %w", err) } return scaledImgBuffer.Bytes(), nil } ================================================ FILE: assets/icons_windows.go ================================================ package assets import ( _ "embed" ) // Colored Icon IDs. const ( GreenID = 0 YellowID = 1 RedID = 2 BlueID = 3 ) // Icons. var ( //go:embed data/icons/pm_light_green_256.png GreenICO []byte //go:embed data/icons/pm_light_yellow_256.png YellowICO []byte //go:embed data/icons/pm_light_red_256.png RedICO []byte //go:embed data/icons/pm_light_blue_256.png BlueICO []byte // ColoredIcons holds all the icons as .ICOs ColoredIcons = [4][]byte{ GreenID: GreenICO, YellowID: YellowICO, RedID: RedICO, BlueID: BlueICO, } ) // ScaleColoredIconsTo scales all colored icons to the given size. // It must be called before any colored icons are used. // It does nothing on Windows. func ScaleColoredIconsTo(pixelSize int) {} ================================================ FILE: base/.gitignore ================================================ portbase apitest misc go.mod.* vendor go.work go.work.sum ================================================ FILE: base/README.md ================================================ > **Check out our main project at [safing/portmaster](https://github.com/safing/portmaster)** # Portbase Portbase helps you quickly take off with your project. It gives you all the basic needs you would have for a service (_not_ tool!). Here is what is included: - `log`: really fast and beautiful logging - `modules`: a multi stage, dependency aware boot process for your software, also manages tasks - `config`: simple, live updating and extremely fast configuration storage - `info`: easily tag your builds with versions, commit hashes, and so on - `formats`: some handy data encoding libs - `rng`: a feedable CSPRNG for great randomness - `database`: intelligent and syncable database with hooks and easy integration with structs, uses buckets with different backends - `api`: a websocket interface to the database, can be extended with custom http handlers Before you continue, a word about this project. It was created to hold the base code for both Portmaster and Gate17. This is also what it will be developed for. If you have a great idea on how to improve portbase, please, by all means, raise an issue and tell us about it, but please also don't be surprised or offended if we ask you to create your own fork to do what you need. Portbase isn't for everyone, it's quite specific to our needs, but we decided to make it easily available to others. Portbase is actively maintained, please raise issues. ## log The main goal of this logging package is to be as fast as possible. Logs are sent to a channel only with minimal processing beforehand, so that the service can continue with the important work and write the logs later. Second, is beauty, both in form what information is provided and how. You can use flags to change the log level on a source file basis. ## modules requires `log` packages may register themselves as modules, to take part in the multi stage boot and coordinated shutdown. Registering only requires a name/key and the `prep()`, `start()` and `stop()` functions. This is how modules are booted: - `init()` available: ~~flags~~, ~~config~~, ~~logging~~, ~~dependencies~~ - register flags (with the stdlib `flag` library) - register module - `module.prep()` available: flags, ~~config~~, ~~logging~~, ~~dependencies~~ - react to flags - register config variables - if an error occurs, return it - return ErrCleanExit for a clean, successful exit. (eg. you only printed a version) - `module.start()` available: flags, config, logging, dependencies - start tasks and workers - do not log errors while starting, but return them - `module.stop()` available: flags, config, logging, dependencies - stop all work (ie. goroutines) - do not log errors while stopping, but return them You can start tasks and workers from your module that are then integrated into the module system and will allow for insights and better control of them in the future. ## config requires `log` The config package stores the configuration in json strings. This may sound a bit weird, but it's very practical. There are three layers of configuration - in order of priority: user configuration, default configuration and the fallback values supplied when registering a config variable. When using config variables, you get a function that checks if your config variable is still up to date every time. If it did not change, it's _extremely_ fast. But if it, it will fetch the current value, which takes a short while, but does not happen often. // This is how you would get a string config variable function. myVar := GetAsString("my_config_var", "default") // You then use myVar() directly every time, except when you must guarantee the same value between two calls if myVar() != "default" { log.Infof("my_config_var is set to %s", myVar()) } // no error handling needed! :) WARNING: While these config variable functions are _extremely_ fast, they are _NOT_ thread/goroutine safe! (Use the `Concurrent` wrapper for that!) ## info Info provides a easy way to store your version and build information within the binary. If you use the `build` script to build the program, it will automatically set build information so that you can easily find out when and from which commit a binary was built. The `build` script extracts information from the host and the git repo and then calls `go build` with some additional arguments. ## formats/varint This is just a convenience wrapper around `encoding/binary`, because we use varints a lot. ## formats/dsd requires `formats/varint` DSD stands for dynamically structured data. In short, this a generic packer that reacts to the supplied data type. - structs are usually json encoded - []bytes and strings stay the same This makes it easier / more efficient to store different data types in a k/v data storage. ## rng requires `log`, `config` This package provides a CSPRNG based on the [Fortuna](https://en.wikipedia.org/wiki/Fortuna_(PRNG)) CSPRNG, devised by Bruce Schneier and Niels Ferguson. Implemented by Jochen Voss, published [on Github](https://github.com/seehuhn/fortuna). Only the Generator is used from the `fortuna` package. The feeding system implemented here is configurable and is focused with efficiency in mind. While you can feed the RNG yourself, it has two feeders by default: - It starts with a seed from `crypto/rand` and periodically reseeds from there - A really simple tickfeeder which extracts entropy from the internal go scheduler using goroutines and is meant to be used under load. ## database requires `log` _introduction to be written_ ## api requires `log`, `database`, `config` _introduction to be written_ ## The main program If you build everything with modules, your main program should be similar to this - just use an empty import for the modules you need: import ( "os" "os/signal" "syscall" "github.com/safing/portmaster/base/info" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/modules" // include packages here _ "path/to/my/custom/module" ) func main() { // Set Info info.Set("MySoftware", "1.0.0") // Start err := modules.Start() if err != nil { if err == modules.ErrCleanExit { os.Exit(0) } else { os.Exit(1) } } // Shutdown // catch interrupt for clean shutdown signalCh := make(chan os.Signal) signal.Notify( signalCh, os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, ) select { case <-signalCh: log.Warning("main: program was interrupted") modules.Shutdown() case <-modules.ShuttingDown(): } } ================================================ FILE: base/api/api_bridge.go ================================================ package api import ( "bytes" "fmt" "net/http" "net/http/httptest" "net/url" "path" "strings" "sync" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" ) const ( endpointBridgeRemoteAddress = "websocket-bridge" apiDatabaseName = "api" ) func registerEndpointBridgeDB() error { if _, err := database.Register(&database.Database{ Name: apiDatabaseName, Description: "API Bridge", StorageType: "injected", }); err != nil { return err } _, err := database.InjectDatabase("api", &endpointBridgeStorage{}) return err } type endpointBridgeStorage struct { storage.InjectBase } // EndpointBridgeRequest holds a bridged request API request. type EndpointBridgeRequest struct { record.Base sync.Mutex Method string Path string Query map[string]string Data []byte MimeType string } // EndpointBridgeResponse holds a bridged request API response. type EndpointBridgeResponse struct { record.Base sync.Mutex MimeType string Body string } // Get returns a database record. func (ebs *endpointBridgeStorage) Get(key string) (record.Record, error) { if key == "" { return nil, database.ErrNotFound } return callAPI(&EndpointBridgeRequest{ Method: http.MethodGet, Path: key, }) } // Get returns the metadata of a database record. func (ebs *endpointBridgeStorage) GetMeta(key string) (*record.Meta, error) { // This interface is an API, always return a fresh copy. m := &record.Meta{} m.Update() return m, nil } // Put stores a record in the database. func (ebs *endpointBridgeStorage) Put(r record.Record) (record.Record, error) { if r.DatabaseKey() == "" { return nil, database.ErrNotFound } // Prepare data. var ebr *EndpointBridgeRequest if r.IsWrapped() { // Only allocate a new struct, if we need it. ebr = &EndpointBridgeRequest{} err := record.Unwrap(r, ebr) if err != nil { return nil, err } } else { var ok bool ebr, ok = r.(*EndpointBridgeRequest) if !ok { return nil, fmt.Errorf("record not of type *EndpointBridgeRequest, but %T", r) } } // Override path with key to mitigate sneaky stuff. ebr.Path = r.DatabaseKey() return callAPI(ebr) } // ReadOnly returns whether the database is read only. func (ebs *endpointBridgeStorage) ReadOnly() bool { return false } func callAPI(ebr *EndpointBridgeRequest) (record.Record, error) { // Add API prefix to path. requestURL := path.Join(apiV1Path, ebr.Path) // Check if path is correct. (Defense in depth) if !strings.HasPrefix(requestURL, apiV1Path) { return nil, fmt.Errorf("bridged request for %q violates scope", ebr.Path) } // Apply default Method. if ebr.Method == "" { if len(ebr.Data) > 0 { ebr.Method = http.MethodPost } else { ebr.Method = http.MethodGet } } // Build URL. u, err := url.ParseRequestURI(requestURL) if err != nil { return nil, fmt.Errorf("failed to build bridged request url: %w", err) } // Build query values. if ebr.Query != nil && len(ebr.Query) > 0 { query := url.Values{} for k, v := range ebr.Query { query.Set(k, v) } u.RawQuery = query.Encode() } // Create request and response objects. r := httptest.NewRequest(ebr.Method, u.String(), bytes.NewBuffer(ebr.Data)) r.RemoteAddr = endpointBridgeRemoteAddress if ebr.MimeType != "" { r.Header.Set("Content-Type", ebr.MimeType) } w := httptest.NewRecorder() // Let the API handle the request. server.Handler.ServeHTTP(w, r) switch w.Code { case 200: // Everything okay, continue. case 500: // A Go error was returned internally. // We can safely return this as an error. return nil, fmt.Errorf("bridged api call failed: %s", w.Body.String()) default: return nil, fmt.Errorf("bridged api call returned unexpected error code %d", w.Code) } response := &EndpointBridgeResponse{ MimeType: w.Header().Get("Content-Type"), Body: w.Body.String(), } response.SetKey(apiDatabaseName + ":" + ebr.Path) response.UpdateMeta() return response, nil } ================================================ FILE: base/api/auth_wrapper.go ================================================ package api import "net/http" // WrapInAuthHandler wraps a simple http.HandlerFunc into a handler that // exposes the required API permissions for this handler. func WrapInAuthHandler(fn http.HandlerFunc, read, write Permission) http.Handler { return &wrappedAuthenticatedHandler{ HandlerFunc: fn, read: read, write: write, } } type wrappedAuthenticatedHandler struct { http.HandlerFunc read Permission write Permission } // ReadPermission returns the read permission for the handler. func (wah *wrappedAuthenticatedHandler) ReadPermission(r *http.Request) Permission { return wah.read } // WritePermission returns the write permission for the handler. func (wah *wrappedAuthenticatedHandler) WritePermission(r *http.Request) Permission { return wah.write } ================================================ FILE: base/api/authentication.go ================================================ package api import ( "encoding/base64" "errors" "fmt" "net/http" "net/url" "strings" "sync" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/mgr" ) const ( sessionCookieName = "Portmaster-API-Token" sessionCookieTTL = 5 * time.Minute ) var ( apiKeys = make(map[string]*AuthToken) apiKeysLock sync.Mutex authFnSet = abool.New() authFn AuthenticatorFunc sessions = make(map[string]*session) sessionsLock sync.Mutex // ErrAPIAccessDeniedMessage should be wrapped by errors returned by // AuthenticatorFunc in order to signify a blocked request, including a error // message for the user. This is an empty message on purpose, as to allow the // function to define the full text of the error shown to the user. ErrAPIAccessDeniedMessage = errors.New("") ) // Permission defines an API requests permission. type Permission int8 const ( // NotFound declares that the operation does not exist. NotFound Permission = -2 // Dynamic declares that the operation requires permission to be processed, // but anyone can execute the operation, as it reacts to permissions itself. Dynamic Permission = -1 // NotSupported declares that the operation is not supported. NotSupported Permission = 0 // PermitAnyone declares that anyone can execute the operation without any // authentication. PermitAnyone Permission = 1 // PermitUser declares that the operation may be executed by authenticated // third party applications that are categorized as representing a simple // user and is limited in access. PermitUser Permission = 2 // PermitAdmin declares that the operation may be executed by authenticated // third party applications that are categorized as representing an // administrator and has broad in access. PermitAdmin Permission = 3 // PermitSelf declares that the operation may only be executed by the // software itself and its own (first party) components. PermitSelf Permission = 4 ) // AuthenticatorFunc is a function that can be set as the authenticator for the // API endpoint. If none is set, all requests will have full access. // The returned AuthToken represents the permissions that the request has. type AuthenticatorFunc func(r *http.Request, s *http.Server) (*AuthToken, error) // AuthToken represents either a set of required or granted permissions. // All attributes must be set when the struct is built and must not be changed // later. Functions may be called at any time. // The Write permission implicitly also includes reading. type AuthToken struct { Read Permission Write Permission ValidUntil *time.Time } type session struct { sync.Mutex token *AuthToken validUntil time.Time } // Expired returns whether the session has expired. func (sess *session) Expired() bool { sess.Lock() defer sess.Unlock() return time.Now().After(sess.validUntil) } // Refresh refreshes the validity of the session with the given TTL. func (sess *session) Refresh(ttl time.Duration) { sess.Lock() defer sess.Unlock() sess.validUntil = time.Now().Add(ttl) } // AuthenticatedHandler defines the handler interface to specify custom // permission for an API handler. The returned permission is the required // permission for the request to proceed. type AuthenticatedHandler interface { ReadPermission(r *http.Request) Permission WritePermission(r *http.Request) Permission } // SetAuthenticator sets an authenticator function for the API endpoint. If none is set, all requests will be permitted. func SetAuthenticator(fn AuthenticatorFunc) error { if module.online.Load() { return ErrAuthenticationImmutable } if !authFnSet.SetToIf(false, true) { return ErrAuthenticationAlreadySet } authFn = fn return nil } func authenticateRequest(w http.ResponseWriter, r *http.Request, targetHandler http.Handler, readMethod bool) *AuthToken { tracer := log.Tracer(r.Context()) // Get required permission for target handler. requiredPermission := PermitSelf if authdHandler, ok := targetHandler.(AuthenticatedHandler); ok { if readMethod { requiredPermission = authdHandler.ReadPermission(r) } else { requiredPermission = authdHandler.WritePermission(r) } } // Check if we need to do any authentication at all. switch requiredPermission { //nolint:exhaustive case NotFound: // Not found. tracer.Debug("api: no API endpoint registered for this path") http.Error(w, "Not found.", http.StatusNotFound) return nil case NotSupported: // A read or write permission can be marked as not supported. tracer.Trace("api: authenticated handler reported: not supported") http.Error(w, "Method not allowed.", http.StatusMethodNotAllowed) return nil case PermitAnyone: // Don't process permissions, as we don't need them. tracer.Tracef("api: granted %s access to public handler", r.RemoteAddr) return &AuthToken{ Read: PermitAnyone, Write: PermitAnyone, } case Dynamic: // Continue processing permissions, but treat as PermitAnyone. requiredPermission = PermitAnyone } // The required permission must match the request permission values after // handling the specials. if requiredPermission < PermitAnyone || requiredPermission > PermitSelf { tracer.Warningf( "api: handler returned invalid permission: %s (%d)", requiredPermission, requiredPermission, ) http.Error(w, "Internal server error during authentication.", http.StatusInternalServerError) return nil } // Authenticate request. token, handled := checkAuth(w, r, requiredPermission > PermitAnyone) switch { case handled: return nil case token == nil: // Use default permissions. token = &AuthToken{ Read: PermitAnyone, Write: PermitAnyone, } } // Get effective permission for request. var requestPermission Permission if readMethod { requestPermission = token.Read } else { requestPermission = token.Write } // Check for valid request permission. if requestPermission < PermitAnyone || requestPermission > PermitSelf { tracer.Warningf( "api: authenticator returned invalid permission: %s (%d)", requestPermission, requestPermission, ) http.Error(w, "Internal server error during authentication.", http.StatusInternalServerError) return nil } // Check permission. if requestPermission < requiredPermission { // If the token is strictly public, return an authentication request. if token.Read == PermitAnyone && token.Write == PermitAnyone { w.Header().Set( "WWW-Authenticate", `Bearer realm="Portmaster API" domain="/"`, ) http.Error(w, "Authorization required.", http.StatusUnauthorized) return nil } // Otherwise just inform of insufficient permissions. http.Error(w, "Insufficient permissions.", http.StatusForbidden) return nil } tracer.Tracef("api: granted %s access to protected handler", r.RemoteAddr) // Make a copy of the AuthToken in order mitigate the handler poisoning the // token, as changes would apply to future requests. return &AuthToken{ Read: token.Read, Write: token.Write, } } func checkAuth(w http.ResponseWriter, r *http.Request, authRequired bool) (token *AuthToken, handled bool) { // Return highest possible permissions in dev mode. if devMode() { return &AuthToken{ Read: PermitSelf, Write: PermitSelf, }, false } // Database Bridge Access. if r.RemoteAddr == endpointBridgeRemoteAddress { return &AuthToken{ Read: dbCompatibilityPermission, Write: dbCompatibilityPermission, }, false } // Check for valid API key. token = checkAPIKey(r) if token != nil { return token, false } // Check for valid session cookie. token = checkSessionCookie(r) if token != nil { return token, false } // Check if an external authentication method is available. if !authFnSet.IsSet() { return nil, false } // Authenticate externally. token, err := authFn(r, server) if err != nil { // Check if the authentication process failed internally. if !errors.Is(err, ErrAPIAccessDeniedMessage) { log.Tracer(r.Context()).Errorf("api: authenticator failed: %s", err) http.Error(w, "Internal server error during authentication.", http.StatusInternalServerError) return nil, true } // Return authentication failure message if authentication is required. if authRequired { log.Tracer(r.Context()).Warningf("api: denying api access from %s", r.RemoteAddr) http.Error(w, err.Error(), http.StatusForbidden) return nil, true } return nil, false } // Abort if no token is returned. if token == nil { return nil, false } // Create session cookie for authenticated request. err = createSession(w, r, token) if err != nil { log.Tracer(r.Context()).Warningf("api: failed to create session: %s", err) } return token, false } func checkAPIKey(r *http.Request) *AuthToken { // Get API key from request. key := r.Header.Get("Authorization") if key == "" { return nil } // Parse API key. switch { case strings.HasPrefix(key, "Bearer "): key = strings.TrimPrefix(key, "Bearer ") case strings.HasPrefix(key, "Basic "): user, pass, _ := r.BasicAuth() key = user + pass default: log.Tracer(r.Context()).Tracef( "api: provided api key type %s is unsupported", strings.Split(key, " ")[0], ) return nil } apiKeysLock.Lock() defer apiKeysLock.Unlock() // Check if the provided API key exists. token, ok := apiKeys[key] if !ok { log.Tracer(r.Context()).Tracef( "api: provided api key %s... is unknown", key[:4], ) return nil } // Abort if the token is expired. if token.ValidUntil != nil && time.Now().After(*token.ValidUntil) { log.Tracer(r.Context()).Warningf("api: denying api access from %s using expired token", r.RemoteAddr) return nil } return token } func updateAPIKeys() { apiKeysLock.Lock() defer apiKeysLock.Unlock() log.Debug("api: importing possibly updated API keys from config") // Delete current keys. for k := range apiKeys { delete(apiKeys, k) } // whether or not we found expired API keys that should be removed // from the setting hasExpiredKeys := false // a list of valid API keys. Used when hasExpiredKeys is set to true. // in that case we'll update the setting to only contain validAPIKeys validAPIKeys := []string{} // Parse new keys. for _, key := range configuredAPIKeys() { u, err := url.Parse(key) if err != nil { log.Errorf("api: failed to parse configured API key %s: %s", key, err) continue } if u.Path == "" { log.Errorf("api: malformed API key %s: missing path section", key) continue } // Create token with default permissions. token := &AuthToken{ Read: PermitAnyone, Write: PermitAnyone, } // Update with configured permissions. q := u.Query() // Parse read permission. readPermission, err := parseAPIPermission(q.Get("read")) if err != nil { log.Errorf("api: invalid API key %s: %s", key, err) continue } token.Read = readPermission // Parse write permission. writePermission, err := parseAPIPermission(q.Get("write")) if err != nil { log.Errorf("api: invalid API key %s: %s", key, err) continue } token.Write = writePermission expireStr := q.Get("expires") if expireStr != "" { validUntil, err := time.Parse(time.RFC3339, expireStr) if err != nil { log.Errorf("api: invalid API key %s: %s", key, err) continue } // continue to the next token if this one is already invalid if time.Now().After(validUntil) { // mark the key as expired so we'll remove it from the setting afterwards hasExpiredKeys = true continue } token.ValidUntil = &validUntil } // Save token. apiKeys[u.Path] = token validAPIKeys = append(validAPIKeys, key) } if hasExpiredKeys { module.mgr.Go("api key cleanup", func(ctx *mgr.WorkerCtx) error { if err := config.SetConfigOption(CfgAPIKeys, validAPIKeys); err != nil { log.Errorf("api: failed to remove expired API keys: %s", err) } else { log.Infof("api: removed expired API keys from %s", CfgAPIKeys) } return nil }) } } func checkSessionCookie(r *http.Request) *AuthToken { // Get session cookie from request. c, err := r.Cookie(sessionCookieName) if err != nil { return nil } // Check if session cookie is registered. sessionsLock.Lock() sess, ok := sessions[c.Value] sessionsLock.Unlock() if !ok { log.Tracer(r.Context()).Tracef("api: provided session cookie %s is unknown", c.Value) return nil } // Check if session is still valid. if sess.Expired() { log.Tracer(r.Context()).Tracef("api: provided session cookie %s has expired", c.Value) return nil } // Refresh session and return. sess.Refresh(sessionCookieTTL) log.Tracer(r.Context()).Tracef("api: session cookie %s is valid, refreshing", c.Value) return sess.token } func createSession(w http.ResponseWriter, r *http.Request, token *AuthToken) error { // Generate new session key. secret, err := rng.Bytes(32) // 256 bit if err != nil { return err } sessionKey := base64.RawURLEncoding.EncodeToString(secret) // Set token cookie in response. http.SetCookie(w, &http.Cookie{ Name: sessionCookieName, Value: sessionKey, Path: "/", HttpOnly: true, SameSite: http.SameSiteStrictMode, }) // Create session. sess := &session{ token: token, } sess.Refresh(sessionCookieTTL) // Save session. sessionsLock.Lock() defer sessionsLock.Unlock() sessions[sessionKey] = sess log.Tracer(r.Context()).Debug("api: issued session cookie") return nil } func cleanSessions(_ *mgr.WorkerCtx) error { sessionsLock.Lock() defer sessionsLock.Unlock() for sessionKey, sess := range sessions { if sess.Expired() { delete(sessions, sessionKey) } } return nil } func deleteSession(sessionKey string) { sessionsLock.Lock() defer sessionsLock.Unlock() delete(sessions, sessionKey) } func getEffectiveMethod(r *http.Request) (eMethod string, readMethod bool, ok bool) { method := r.Method // Get CORS request method if OPTIONS request. if r.Method == http.MethodOptions { method = r.Header.Get("Access-Control-Request-Method") if method == "" { return "", false, false } } switch method { case http.MethodGet, http.MethodHead: return http.MethodGet, true, true case http.MethodPost, http.MethodPut, http.MethodDelete: return method, false, true default: return "", false, false } } func parseAPIPermission(s string) (Permission, error) { switch strings.ToLower(s) { case "", "anyone": return PermitAnyone, nil case "user": return PermitUser, nil case "admin": return PermitAdmin, nil default: return PermitAnyone, fmt.Errorf("invalid permission: %s", s) } } func (p Permission) String() string { switch p { case NotSupported: return "NotSupported" case Dynamic: return "Dynamic" case PermitAnyone: return "PermitAnyone" case PermitUser: return "PermitUser" case PermitAdmin: return "PermitAdmin" case PermitSelf: return "PermitSelf" case NotFound: return "NotFound" default: return "Unknown" } } // Role returns a string representation of the permission role. func (p Permission) Role() string { switch p { case PermitAnyone: return "Anyone" case PermitUser: return "User" case PermitAdmin: return "Admin" case PermitSelf: return "Self" case Dynamic, NotFound, NotSupported: return "Invalid" default: return "Invalid" } } ================================================ FILE: base/api/authentication_test.go ================================================ package api import ( "errors" "fmt" "net/http" "testing" "github.com/stretchr/testify/assert" ) var testToken = new(AuthToken) func testAuthenticator(r *http.Request, s *http.Server) (*AuthToken, error) { switch { case testToken.Read == -127 || testToken.Write == -127: return nil, errors.New("test error") case testToken.Read == -128 || testToken.Write == -128: return nil, fmt.Errorf("%wdenied", ErrAPIAccessDeniedMessage) default: return testToken, nil } } type testAuthHandler struct { Read Permission Write Permission } func (ah *testAuthHandler) ReadPermission(r *http.Request) Permission { return ah.Read } func (ah *testAuthHandler) WritePermission(r *http.Request) Permission { return ah.Write } func (ah *testAuthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Check if request is as expected. ar := GetAPIRequest(r) switch { case ar == nil: http.Error(w, "ar == nil", http.StatusInternalServerError) case ar.AuthToken == nil: http.Error(w, "ar.AuthToken == nil", http.StatusInternalServerError) default: http.Error(w, "auth success", http.StatusOK) } } func makeAuthTestPath(reading bool, p Permission) string { if reading { return fmt.Sprintf("/test/auth/read/%s", p) } return fmt.Sprintf("/test/auth/write/%s", p) } func TestPermissions(t *testing.T) { t.Parallel() testHandler := &mainHandler{ mux: mainMux, } // Define permissions that need testing. permissionsToTest := []Permission{ NotSupported, PermitAnyone, PermitUser, PermitAdmin, PermitSelf, Dynamic, NotFound, 100, // Test a too high value. -100, // Test a too low value. -127, // Simulate authenticator failure. -128, // Simulate authentication denied message. } // Register test handlers. for _, p := range permissionsToTest { RegisterHandler(makeAuthTestPath(true, p), &testAuthHandler{Read: p}) RegisterHandler(makeAuthTestPath(false, p), &testAuthHandler{Write: p}) } // Test all the combinations. for _, requestPerm := range permissionsToTest { for _, handlerPerm := range permissionsToTest { for _, method := range []string{ http.MethodGet, http.MethodHead, http.MethodPost, http.MethodPut, http.MethodDelete, } { // Set request permission for test requests. _, reading, _ := getEffectiveMethod(&http.Request{Method: method}) if reading { testToken.Read = requestPerm testToken.Write = NotSupported } else { testToken.Read = NotSupported testToken.Write = requestPerm } // Evaluate expected result. var expectSuccess bool switch { case handlerPerm == PermitAnyone: // This is fast-tracked. There are not additional checks. expectSuccess = true case handlerPerm == Dynamic: // This is turned into PermitAnyone in the authenticator. // But authentication is still processed and the result still gets // sanity checked! if requestPerm >= PermitAnyone && requestPerm <= PermitSelf { expectSuccess = true } // Another special case is when the handler requires permission to be // processed but the authenticator fails to authenticate the request. // In this case, a fallback token with PermitAnyone is used. if requestPerm == -128 { // -128 is used to simulate a permission denied message. expectSuccess = true } case handlerPerm <= NotSupported: // Invalid handler permission. case handlerPerm > PermitSelf: // Invalid handler permission. case requestPerm <= NotSupported: // Invalid request permission. case requestPerm > PermitSelf: // Invalid request permission. case requestPerm < handlerPerm: // Valid, but insufficient request permission. default: expectSuccess = true } if expectSuccess { // Test for success. if !assert.HTTPBodyContains( t, testHandler.ServeHTTP, method, makeAuthTestPath(reading, handlerPerm), nil, "auth success", ) { t.Errorf( "%s with %s (%d) to handler %s (%d)", method, requestPerm, requestPerm, handlerPerm, handlerPerm, ) } } else { // Test for error. if !assert.HTTPError(t, testHandler.ServeHTTP, method, makeAuthTestPath(reading, handlerPerm), nil, ) { t.Errorf( "%s with %s (%d) to handler %s (%d)", method, requestPerm, requestPerm, handlerPerm, handlerPerm, ) } } } } } } func TestPermissionDefinitions(t *testing.T) { t.Parallel() if NotSupported != 0 { t.Fatalf("NotSupported must be zero, was %v", NotSupported) } } ================================================ FILE: base/api/client/api.go ================================================ package client // Get sends a get command to the API. func (c *Client) Get(key string, handleFunc func(*Message)) *Operation { op := c.NewOperation(handleFunc) op.Send(msgRequestGet, key, nil) return op } // Query sends a query command to the API. func (c *Client) Query(query string, handleFunc func(*Message)) *Operation { op := c.NewOperation(handleFunc) op.Send(msgRequestQuery, query, nil) return op } // Sub sends a sub command to the API. func (c *Client) Sub(query string, handleFunc func(*Message)) *Operation { op := c.NewOperation(handleFunc) op.Send(msgRequestSub, query, nil) return op } // Qsub sends a qsub command to the API. func (c *Client) Qsub(query string, handleFunc func(*Message)) *Operation { op := c.NewOperation(handleFunc) op.Send(msgRequestQsub, query, nil) return op } // Create sends a create command to the API. func (c *Client) Create(key string, value interface{}, handleFunc func(*Message)) *Operation { op := c.NewOperation(handleFunc) op.Send(msgRequestCreate, key, value) return op } // Update sends an update command to the API. func (c *Client) Update(key string, value interface{}, handleFunc func(*Message)) *Operation { op := c.NewOperation(handleFunc) op.Send(msgRequestUpdate, key, value) return op } // Insert sends an insert command to the API. func (c *Client) Insert(key string, value interface{}, handleFunc func(*Message)) *Operation { op := c.NewOperation(handleFunc) op.Send(msgRequestInsert, key, value) return op } // Delete sends a delete command to the API. func (c *Client) Delete(key string, handleFunc func(*Message)) *Operation { op := c.NewOperation(handleFunc) op.Send(msgRequestDelete, key, nil) return op } ================================================ FILE: base/api/client/client.go ================================================ package client import ( "fmt" "sync" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" ) const ( backOffTimer = 1 * time.Second offlineSignal uint8 = 0 onlineSignal uint8 = 1 ) // The Client enables easy interaction with the API. type Client struct { sync.Mutex server string onlineSignal chan struct{} offlineSignal chan struct{} shutdownSignal chan struct{} lastSignal uint8 send chan *Message resend chan *Message recv chan *Message operations map[string]*Operation nextOpID uint64 lastError string } // NewClient returns a new Client. func NewClient(server string) *Client { c := &Client{ server: server, onlineSignal: make(chan struct{}), offlineSignal: make(chan struct{}), shutdownSignal: make(chan struct{}), lastSignal: offlineSignal, send: make(chan *Message, 100), resend: make(chan *Message, 1), recv: make(chan *Message, 100), operations: make(map[string]*Operation), } go c.handler() return c } // Connect connects to the API once. func (c *Client) Connect() error { defer c.signalOffline() err := c.wsConnect() if err != nil && err.Error() != c.lastError { log.Errorf("client: error connecting to Portmaster: %s", err) c.lastError = err.Error() } return err } // StayConnected calls Connect again whenever the connection is lost. func (c *Client) StayConnected() { log.Infof("client: connecting to Portmaster at %s", c.server) _ = c.Connect() for { select { case <-time.After(backOffTimer): log.Infof("client: reconnecting...") _ = c.Connect() case <-c.shutdownSignal: return } } } // Shutdown shuts the client down. func (c *Client) Shutdown() { select { case <-c.shutdownSignal: default: close(c.shutdownSignal) } } func (c *Client) signalOnline() { c.Lock() defer c.Unlock() if c.lastSignal == offlineSignal { log.Infof("client: went online") c.offlineSignal = make(chan struct{}) close(c.onlineSignal) c.lastSignal = onlineSignal // resend unsent request for _, op := range c.operations { if op.resuscitationEnabled.IsSet() && op.request.sent != nil && op.request.sent.SetToIf(true, false) { op.client.send <- op.request log.Infof("client: resuscitated %s %s %s", op.request.OpID, op.request.Type, op.request.Key) } } } } func (c *Client) signalOffline() { c.Lock() defer c.Unlock() if c.lastSignal == onlineSignal { log.Infof("client: went offline") c.onlineSignal = make(chan struct{}) close(c.offlineSignal) c.lastSignal = offlineSignal // signal offline status to operations for _, op := range c.operations { op.handle(&Message{ OpID: op.ID, Type: MsgOffline, }) } } } // Online returns a closed channel read if the client is connected to the API. func (c *Client) Online() <-chan struct{} { c.Lock() defer c.Unlock() return c.onlineSignal } // Offline returns a closed channel read if the client is not connected to the API. func (c *Client) Offline() <-chan struct{} { c.Lock() defer c.Unlock() return c.offlineSignal } func (c *Client) handler() { for { select { case m := <-c.recv: if m == nil { return } c.Lock() op, ok := c.operations[m.OpID] c.Unlock() if ok { log.Tracef("client: [%s] received %s msg: %s", m.OpID, m.Type, m.Key) op.handle(m) } else { log.Tracef("client: received message for unknown operation %s", m.OpID) } case <-c.shutdownSignal: return } } } // Operation represents a single operation by a client. type Operation struct { ID string request *Message client *Client handleFunc func(*Message) handler chan *Message resuscitationEnabled *abool.AtomicBool } func (op *Operation) handle(m *Message) { if op.handleFunc != nil { op.handleFunc(m) } else { select { case op.handler <- m: default: log.Warningf("client: handler channel of operation %s overflowed", op.ID) } } } // Cancel the operation. func (op *Operation) Cancel() { op.client.Lock() defer op.client.Unlock() delete(op.client.operations, op.ID) close(op.handler) } // Send sends a request to the API. func (op *Operation) Send(command, text string, data interface{}) { op.request = &Message{ OpID: op.ID, Type: command, Key: text, Value: data, sent: abool.NewBool(false), } log.Tracef("client: [%s] sending %s msg: %s", op.request.OpID, op.request.Type, op.request.Key) op.client.send <- op.request } // EnableResuscitation will resend the request after reconnecting to the API. func (op *Operation) EnableResuscitation() { op.resuscitationEnabled.Set() } // NewOperation returns a new operation. func (c *Client) NewOperation(handleFunc func(*Message)) *Operation { c.Lock() defer c.Unlock() c.nextOpID++ op := &Operation{ ID: fmt.Sprintf("#%d", c.nextOpID), client: c, handleFunc: handleFunc, handler: make(chan *Message, 100), resuscitationEnabled: abool.NewBool(false), } c.operations[op.ID] = op return op } ================================================ FILE: base/api/client/const.go ================================================ package client // Message Types. const ( msgRequestGet = "get" msgRequestQuery = "query" msgRequestSub = "sub" msgRequestQsub = "qsub" msgRequestCreate = "create" msgRequestUpdate = "update" msgRequestInsert = "insert" msgRequestDelete = "delete" MsgOk = "ok" MsgError = "error" MsgDone = "done" MsgSuccess = "success" MsgUpdate = "upd" MsgNew = "new" MsgDelete = "del" MsgWarning = "warning" MsgOffline = "offline" // special message type for signaling the handler that the connection was lost apiSeperator = "|" ) var apiSeperatorBytes = []byte(apiSeperator) ================================================ FILE: base/api/client/message.go ================================================ package client import ( "bytes" "errors" "github.com/tevino/abool" "github.com/safing/structures/container" "github.com/safing/structures/dsd" ) // ErrMalformedMessage is returned when a malformed message was encountered. var ErrMalformedMessage = errors.New("malformed message") // Message is an API message. type Message struct { OpID string Type string Key string RawValue []byte Value interface{} sent *abool.AtomicBool } // ParseMessage parses the given raw data and returns a Message. func ParseMessage(data []byte) (*Message, error) { parts := bytes.SplitN(data, apiSeperatorBytes, 4) if len(parts) < 2 { return nil, ErrMalformedMessage } m := &Message{ OpID: string(parts[0]), Type: string(parts[1]), } switch m.Type { case MsgOk, MsgUpdate, MsgNew: // parse key and data // 127|ok|| // 127|upd|| // 127|new|| if len(parts) != 4 { return nil, ErrMalformedMessage } m.Key = string(parts[2]) m.RawValue = parts[3] case MsgDelete: // parse key // 127|del| if len(parts) != 3 { return nil, ErrMalformedMessage } m.Key = string(parts[2]) case MsgWarning, MsgError: // parse message // 127|error| // 127|warning| // error with single record, operation continues if len(parts) != 3 { return nil, ErrMalformedMessage } m.Key = string(parts[2]) case MsgDone, MsgSuccess: // nothing more to do // 127|success // 127|done } return m, nil } // Pack serializes a message into a []byte slice. func (m *Message) Pack() ([]byte, error) { c := container.New([]byte(m.OpID), apiSeperatorBytes, []byte(m.Type)) if m.Key != "" { c.Append(apiSeperatorBytes) c.Append([]byte(m.Key)) if len(m.RawValue) > 0 { c.Append(apiSeperatorBytes) c.Append(m.RawValue) } else if m.Value != nil { var err error m.RawValue, err = dsd.Dump(m.Value, dsd.JSON) if err != nil { return nil, err } c.Append(apiSeperatorBytes) c.Append(m.RawValue) } } return c.CompileData(), nil } ================================================ FILE: base/api/client/websocket.go ================================================ package client import ( "fmt" "sync" "github.com/gorilla/websocket" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" ) type wsState struct { wsConn *websocket.Conn wg sync.WaitGroup failing *abool.AtomicBool failSignal chan struct{} } func (c *Client) wsConnect() error { state := &wsState{ failing: abool.NewBool(false), failSignal: make(chan struct{}), } var err error state.wsConn, _, err = websocket.DefaultDialer.Dial(fmt.Sprintf("ws://%s/api/database/v1", c.server), nil) if err != nil { return err } c.signalOnline() state.wg.Add(2) go c.wsReader(state) go c.wsWriter(state) // wait for end of connection select { case <-state.failSignal: case <-c.shutdownSignal: state.Error("") } _ = state.wsConn.Close() state.wg.Wait() return nil } func (c *Client) wsReader(state *wsState) { defer state.wg.Done() for { _, data, err := state.wsConn.ReadMessage() log.Tracef("client: read message") if err != nil { if !websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) { state.Error(fmt.Sprintf("client: read error: %s", err)) } else { state.Error("client: connection closed by server") } return } log.Tracef("client: received message: %s", string(data)) m, err := ParseMessage(data) if err != nil { log.Warningf("client: failed to parse message: %s", err) } else { select { case c.recv <- m: case <-state.failSignal: return } } } } func (c *Client) wsWriter(state *wsState) { defer state.wg.Done() for { select { case <-state.failSignal: return case m := <-c.resend: data, err := m.Pack() if err == nil { err = state.wsConn.WriteMessage(websocket.BinaryMessage, data) } if err != nil { state.Error(fmt.Sprintf("client: write error: %s", err)) return } log.Tracef("client: sent message: %s", string(data)) if m.sent != nil { m.sent.Set() } case m := <-c.send: data, err := m.Pack() if err == nil { err = state.wsConn.WriteMessage(websocket.BinaryMessage, data) } if err != nil { c.resend <- m state.Error(fmt.Sprintf("client: write error: %s", err)) return } log.Tracef("client: sent message: %s", string(data)) if m.sent != nil { m.sent.Set() } } } } func (state *wsState) Error(message string) { if state.failing.SetToIf(false, true) { close(state.failSignal) if message != "" { log.Warning(message) } } } ================================================ FILE: base/api/config.go ================================================ package api import ( "flag" "github.com/safing/portmaster/base/config" ) // Config Keys. const ( CfgDefaultListenAddressKey = "core/listenAddress" CfgAPIKeys = "core/apiKeys" ) var ( listenAddressFlag string listenAddressConfig config.StringOption defaultListenAddress string configuredAPIKeys config.StringArrayOption devMode config.BoolOption ) func init() { flag.StringVar( &listenAddressFlag, "api-address", "", "set api listen address; configuration is stronger", ) } func getDefaultListenAddress() string { // check if overridden if listenAddressFlag != "" { return listenAddressFlag } // return internal default return defaultListenAddress } func registerConfig() error { err := config.Register(&config.Option{ Name: "API Listen Address", Key: CfgDefaultListenAddressKey, Description: "Defines the IP address and port on which the internal API listens.", OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelDeveloper, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: getDefaultListenAddress(), ValidationRegex: "^([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}:[0-9]{1,5}|\\[[:0-9A-Fa-f]+\\]:[0-9]{1,5})$", RequiresRestart: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: 513, config.CategoryAnnotation: "Development", }, }) if err != nil { return err } listenAddressConfig = config.GetAsString(CfgDefaultListenAddressKey, getDefaultListenAddress()) err = config.Register(&config.Option{ Name: "API Keys", Key: CfgAPIKeys, Description: "Define API keys for privileged access to the API. Every entry is a separate API key with respective permissions. Format is `?read=&write=`. Permissions are `anyone`, `user` and `admin`, and may be omitted.", Sensitive: true, OptType: config.OptTypeStringArray, ExpertiseLevel: config.ExpertiseLevelDeveloper, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: []string{}, Annotations: config.Annotations{ config.DisplayOrderAnnotation: 514, config.CategoryAnnotation: "Development", }, }) if err != nil { return err } configuredAPIKeys = config.GetAsStringArray(CfgAPIKeys, []string{}) devMode = config.Concurrent.GetAsBool(config.CfgDevModeKey, false) return nil } // SetDefaultAPIListenAddress sets the default listen address for the API. func SetDefaultAPIListenAddress(address string) { defaultListenAddress = address } ================================================ FILE: base/api/database.go ================================================ package api import ( "bytes" "errors" "fmt" "net/http" "sync" "github.com/gorilla/websocket" "github.com/tevino/abool" "github.com/tidwall/gjson" "github.com/tidwall/sjson" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/structures/container" "github.com/safing/structures/dsd" "github.com/safing/structures/varint" ) const ( dbMsgTypeOk = "ok" dbMsgTypeError = "error" dbMsgTypeDone = "done" dbMsgTypeSuccess = "success" dbMsgTypeUpd = "upd" dbMsgTypeNew = "new" dbMsgTypeDel = "del" dbMsgTypeWarning = "warning" dbAPISeperator = "|" emptyString = "" ) var ( dbAPISeperatorBytes = []byte(dbAPISeperator) dbCompatibilityPermission = PermitAdmin ) func init() { RegisterHandler("/api/database/v1", WrapInAuthHandler( startDatabaseWebsocketAPI, // Default to admin read/write permissions until the database gets support // for api permissions. dbCompatibilityPermission, dbCompatibilityPermission, )) } // DatabaseAPI is a generic database API interface. type DatabaseAPI struct { queriesLock sync.Mutex queries map[string]*iterator.Iterator subsLock sync.Mutex subs map[string]*database.Subscription shutdownSignal chan struct{} shuttingDown *abool.AtomicBool db *database.Interface sendBytes func(data []byte) } // DatabaseWebsocketAPI is a database websocket API interface. type DatabaseWebsocketAPI struct { DatabaseAPI sendQueue chan []byte conn *websocket.Conn } func allowAnyOrigin(r *http.Request) bool { return true } // CreateDatabaseAPI creates a new database interface. func CreateDatabaseAPI(sendFunction func(data []byte)) DatabaseAPI { return DatabaseAPI{ queries: make(map[string]*iterator.Iterator), subs: make(map[string]*database.Subscription), shutdownSignal: make(chan struct{}), shuttingDown: abool.NewBool(false), db: database.NewInterface(nil), sendBytes: sendFunction, } } func startDatabaseWebsocketAPI(w http.ResponseWriter, r *http.Request) { upgrader := websocket.Upgrader{ CheckOrigin: allowAnyOrigin, ReadBufferSize: 1024, WriteBufferSize: 65536, } wsConn, err := upgrader.Upgrade(w, r, nil) if err != nil { errMsg := fmt.Sprintf("could not upgrade: %s", err) log.Error(errMsg) http.Error(w, errMsg, http.StatusBadRequest) return } newDBAPI := &DatabaseWebsocketAPI{ DatabaseAPI: DatabaseAPI{ queries: make(map[string]*iterator.Iterator), subs: make(map[string]*database.Subscription), shutdownSignal: make(chan struct{}), shuttingDown: abool.NewBool(false), db: database.NewInterface(nil), }, sendQueue: make(chan []byte, 100), conn: wsConn, } newDBAPI.sendBytes = func(data []byte) { newDBAPI.sendQueue <- data } module.mgr.Go("database api handler", newDBAPI.handler) module.mgr.Go("database api writer", newDBAPI.writer) log.Tracer(r.Context()).Infof("api request: init websocket %s %s", r.RemoteAddr, r.RequestURI) } func (api *DatabaseWebsocketAPI) handler(_ *mgr.WorkerCtx) error { defer func() { _ = api.shutdown(nil) }() for { _, msg, err := api.conn.ReadMessage() if err != nil { return api.shutdown(err) } api.Handle(msg) } } func (api *DatabaseWebsocketAPI) writer(ctx *mgr.WorkerCtx) error { defer func() { _ = api.shutdown(nil) }() var data []byte var err error for { select { // prioritize direct writes case data = <-api.sendQueue: if len(data) == 0 { return nil } case <-ctx.Done(): return nil case <-api.shutdownSignal: return nil } // log.Tracef("api: sending %s", string(*msg)) err = api.conn.WriteMessage(websocket.BinaryMessage, data) if err != nil { return api.shutdown(err) } } } func (api *DatabaseWebsocketAPI) shutdown(err error) error { // Check if we are the first to shut down. if !api.shuttingDown.SetToIf(false, true) { return nil } // Check the given error. if err != nil { if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseAbnormalClosure, ) { log.Infof("api: websocket connection to %s closed", api.conn.RemoteAddr()) } else { log.Warningf("api: websocket connection error with %s: %s", api.conn.RemoteAddr(), err) } } // Trigger shutdown. close(api.shutdownSignal) _ = api.conn.Close() return nil } // Handle handles a message for the database API. func (api *DatabaseAPI) Handle(msg []byte) { // 123|get| // 123|ok|| // 123|error| // 124|query| // 124|ok|| // 124|done // 124|error| // 124|warning| // error with single record, operation continues // 124|cancel // 125|sub| // 125|upd|| // 125|new|| // 127|del| // 125|warning| // error with single record, operation continues // 125|cancel // 127|qsub| // 127|ok|| // 127|done // 127|error| // 127|upd|| // 127|new|| // 127|del| // 127|warning| // error with single record, operation continues // 127|cancel // 128|create|| // 128|success // 128|error| // 129|update|| // 129|success // 129|error| // 130|insert|| // 130|success // 130|error| // 131|delete| // 131|success // 131|error| parts := bytes.SplitN(msg, []byte("|"), 3) // Handle special command "cancel" if len(parts) == 2 && string(parts[1]) == "cancel" { // 124|cancel // 125|cancel // 127|cancel go api.handleCancel(parts[0]) return } if len(parts) != 3 { api.send(nil, dbMsgTypeError, "bad request: malformed message", nil) return } switch string(parts[1]) { case "get": // 123|get| go api.handleGet(parts[0], string(parts[2])) case "query": // 124|query| go api.handleQuery(parts[0], string(parts[2])) case "sub": // 125|sub| go api.handleSub(parts[0], string(parts[2])) case "qsub": // 127|qsub| go api.handleQsub(parts[0], string(parts[2])) case "create", "update", "insert": // split key and payload dataParts := bytes.SplitN(parts[2], []byte("|"), 2) if len(dataParts) != 2 { api.send(nil, dbMsgTypeError, "bad request: malformed message", nil) return } switch string(parts[1]) { case "create": // 128|create|| go api.handlePut(parts[0], string(dataParts[0]), dataParts[1], true) case "update": // 129|update|| go api.handlePut(parts[0], string(dataParts[0]), dataParts[1], false) case "insert": // 130|insert|| go api.handleInsert(parts[0], string(dataParts[0]), dataParts[1]) } case "delete": // 131|delete| go api.handleDelete(parts[0], string(parts[2])) default: api.send(parts[0], dbMsgTypeError, "bad request: unknown method", nil) } } func (api *DatabaseAPI) send(opID []byte, msgType string, msgOrKey string, data []byte) { c := container.New(opID) c.Append(dbAPISeperatorBytes) c.Append([]byte(msgType)) if msgOrKey != emptyString { c.Append(dbAPISeperatorBytes) c.Append([]byte(msgOrKey)) } if len(data) > 0 { c.Append(dbAPISeperatorBytes) c.Append(data) } api.sendBytes(c.CompileData()) } func (api *DatabaseAPI) handleGet(opID []byte, key string) { // 123|get| // 123|ok|| // 123|error| var data []byte r, err := api.db.Get(key) if err == nil { data, err = MarshalRecord(r, true) } if err != nil { api.send(opID, dbMsgTypeError, err.Error(), nil) return } api.send(opID, dbMsgTypeOk, r.Key(), data) } func (api *DatabaseAPI) handleQuery(opID []byte, queryText string) { // 124|query| // 124|ok|| // 124|done // 124|warning| // 124|error| // 124|warning| // error with single record, operation continues // 124|cancel var err error q, err := query.ParseQuery(queryText) if err != nil { api.send(opID, dbMsgTypeError, err.Error(), nil) return } api.processQuery(opID, q) } func (api *DatabaseAPI) processQuery(opID []byte, q *query.Query) (ok bool) { it, err := api.db.Query(q) if err != nil { api.send(opID, dbMsgTypeError, err.Error(), nil) return false } // Save query iterator. api.queriesLock.Lock() api.queries[string(opID)] = it api.queriesLock.Unlock() // Remove query iterator after it ended. defer func() { api.queriesLock.Lock() defer api.queriesLock.Unlock() delete(api.queries, string(opID)) }() for { select { case <-api.shutdownSignal: // cancel query and return it.Cancel() return false case r := <-it.Next: // process query feed if r != nil { // process record data, err := MarshalRecord(r, true) if err != nil { api.send(opID, dbMsgTypeWarning, err.Error(), nil) continue } api.send(opID, dbMsgTypeOk, r.Key(), data) } else { // sub feed ended if it.Err() != nil { api.send(opID, dbMsgTypeError, it.Err().Error(), nil) return false } api.send(opID, dbMsgTypeDone, emptyString, nil) return true } } } } // func (api *DatabaseWebsocketAPI) runQuery() func (api *DatabaseAPI) handleSub(opID []byte, queryText string) { // 125|sub| // 125|upd|| // 125|new|| // 125|delete| // 125|warning| // error with single record, operation continues // 125|cancel var err error q, err := query.ParseQuery(queryText) if err != nil { api.send(opID, dbMsgTypeError, err.Error(), nil) return } sub, ok := api.registerSub(opID, q) if !ok { return } api.processSub(opID, sub) } func (api *DatabaseAPI) registerSub(opID []byte, q *query.Query) (sub *database.Subscription, ok bool) { var err error sub, err = api.db.Subscribe(q) if err != nil { api.send(opID, dbMsgTypeError, err.Error(), nil) return nil, false } return sub, true } func (api *DatabaseAPI) processSub(opID []byte, sub *database.Subscription) { // Save subscription. api.subsLock.Lock() api.subs[string(opID)] = sub api.subsLock.Unlock() // Remove subscription after it ended. defer func() { api.subsLock.Lock() defer api.subsLock.Unlock() delete(api.subs, string(opID)) }() for { select { case <-api.shutdownSignal: // cancel sub and return _ = sub.Cancel() return case r := <-sub.Feed: // process sub feed if r != nil { // process record data, err := MarshalRecord(r, true) if err != nil { api.send(opID, dbMsgTypeWarning, err.Error(), nil) continue } // TODO: use upd, new and delete msgTypes r.Lock() isDeleted := r.Meta().IsDeleted() isNew := r.Meta().Created == r.Meta().Modified r.Unlock() switch { case isDeleted: api.send(opID, dbMsgTypeDel, r.Key(), nil) case isNew: api.send(opID, dbMsgTypeNew, r.Key(), data) default: api.send(opID, dbMsgTypeUpd, r.Key(), data) } } else { // sub feed ended api.send(opID, dbMsgTypeDone, "", nil) return } } } } func (api *DatabaseAPI) handleQsub(opID []byte, queryText string) { // 127|qsub| // 127|ok|| // 127|done // 127|error| // 127|upd|| // 127|new|| // 127|delete| // 127|warning| // error with single record, operation continues // 127|cancel var err error q, err := query.ParseQuery(queryText) if err != nil { api.send(opID, dbMsgTypeError, err.Error(), nil) return } sub, ok := api.registerSub(opID, q) if !ok { return } ok = api.processQuery(opID, q) if !ok { return } api.processSub(opID, sub) } func (api *DatabaseAPI) handleCancel(opID []byte) { api.cancelQuery(opID) api.cancelSub(opID) } func (api *DatabaseAPI) cancelQuery(opID []byte) { api.queriesLock.Lock() defer api.queriesLock.Unlock() // Get subscription from api. it, ok := api.queries[string(opID)] if !ok { // Fail silently as quries end by themselves when finished. return } // End query. it.Cancel() // The query handler will end the communication with a done message. } func (api *DatabaseAPI) cancelSub(opID []byte) { api.subsLock.Lock() defer api.subsLock.Unlock() // Get subscription from api. sub, ok := api.subs[string(opID)] if !ok { api.send(opID, dbMsgTypeError, "could not find subscription", nil) return } // End subscription. err := sub.Cancel() if err != nil { api.send(opID, dbMsgTypeError, fmt.Sprintf("failed to cancel subscription: %s", err), nil) } // The subscription handler will end the communication with a done message. } func (api *DatabaseAPI) handlePut(opID []byte, key string, data []byte, create bool) { // 128|create|| // 128|success // 128|error| // 129|update|| // 129|success // 129|error| if len(data) < 2 { api.send(opID, dbMsgTypeError, "bad request: malformed message", nil) return } // TODO - staged for deletion: remove transition code // if data[0] != dsd.JSON { // typedData := make([]byte, len(data)+1) // typedData[0] = dsd.JSON // copy(typedData[1:], data) // data = typedData // } r, err := record.NewWrapper(key, nil, data[0], data[1:]) if err != nil { api.send(opID, dbMsgTypeError, err.Error(), nil) return } if create { err = api.db.PutNew(r) } else { err = api.db.Put(r) } if err != nil { api.send(opID, dbMsgTypeError, err.Error(), nil) return } api.send(opID, dbMsgTypeSuccess, emptyString, nil) } func (api *DatabaseAPI) handleInsert(opID []byte, key string, data []byte) { // 130|insert|| // 130|success // 130|error| r, err := api.db.Get(key) if err != nil { api.send(opID, dbMsgTypeError, err.Error(), nil) return } acc := r.GetAccessor(r) result := gjson.ParseBytes(data) anythingPresent := false var insertError error result.ForEach(func(key gjson.Result, value gjson.Result) bool { anythingPresent = true if !key.Exists() { insertError = errors.New("values must be in a map") return false } if key.Type != gjson.String { insertError = errors.New("keys must be strings") return false } if !value.Exists() { insertError = errors.New("non-existent value") return false } insertError = acc.Set(key.String(), value.Value()) return insertError == nil }) if insertError != nil { api.send(opID, dbMsgTypeError, insertError.Error(), nil) return } if !anythingPresent { api.send(opID, dbMsgTypeError, "could not find any valid values", nil) return } err = api.db.Put(r) if err != nil { api.send(opID, dbMsgTypeError, err.Error(), nil) return } api.send(opID, dbMsgTypeSuccess, emptyString, nil) } func (api *DatabaseAPI) handleDelete(opID []byte, key string) { // 131|delete| // 131|success // 131|error| err := api.db.Delete(key) if err != nil { api.send(opID, dbMsgTypeError, err.Error(), nil) return } api.send(opID, dbMsgTypeSuccess, emptyString, nil) } // MarshalRecord locks and marshals the given record, additionally adding // metadata and returning it as json. func MarshalRecord(r record.Record, withDSDIdentifier bool) ([]byte, error) { r.Lock() defer r.Unlock() // Pour record into JSON. jsonData, err := r.Marshal(r, dsd.JSON) if err != nil { return nil, err } // Remove JSON identifier for manual editing. jsonData = bytes.TrimPrefix(jsonData, varint.Pack8(dsd.JSON)) // Add metadata. jsonData, err = sjson.SetBytes(jsonData, "_meta", r.Meta()) if err != nil { return nil, err } // Add database key. jsonData, err = sjson.SetBytes(jsonData, "_meta.Key", r.Key()) if err != nil { return nil, err } // Add JSON identifier again. if withDSDIdentifier { formatID := varint.Pack8(dsd.JSON) finalData := make([]byte, 0, len(formatID)+len(jsonData)) finalData = append(finalData, formatID...) finalData = append(finalData, jsonData...) return finalData, nil } return jsonData, nil } ================================================ FILE: base/api/doc.go ================================================ /* Package api provides an API for integration with other components of the same software package and also third party components. It provides direct database access as well as a simpler way to register API endpoints. You can of course also register raw `http.Handler`s directly. Optional authentication guards registered handlers. This is achieved by attaching functions to the `http.Handler`s that are registered, which allow them to specify the required permissions for the handler. The permissions are divided into the roles and assume a single user per host. The Roles are User, Admin and Self. User roles are expected to have mostly read access and react to notifications or system events, like a system tray program. The Admin role is meant for advanced components that also change settings, but are restricted so they cannot break the software. Self is reserved for internal use with full access. */ package api ================================================ FILE: base/api/endpoints.go ================================================ package api import ( "bytes" "errors" "fmt" "io" "net/http" "sort" "strconv" "strings" "sync" "github.com/gorilla/mux" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/structures/dsd" ) // Endpoint describes an API Endpoint. // Path and at least one permission are required. // As is exactly one function. type Endpoint struct { //nolint:maligned // Name is the human reabable name of the endpoint. Name string // Description is the human readable description and documentation of the endpoint. Description string // Parameters is the parameter documentation. Parameters []Parameter `json:",omitempty"` // Path describes the URL path of the endpoint. Path string // MimeType defines the content type of the returned data. MimeType string // Read defines the required read permission. Read Permission `json:",omitempty"` // ReadMethod sets the required read method for the endpoint. // Available methods are: // GET: Returns data only, no action is taken, nothing is changed. // If omitted, defaults to GET. // // This field is currently being introduced and will only warn and not deny // access if the write method does not match. ReadMethod string `json:",omitempty"` // Write defines the required write permission. Write Permission `json:",omitempty"` // WriteMethod sets the required write method for the endpoint. // Available methods are: // POST: Create a new resource; Change a status; Execute a function // PUT: Update an existing resource // DELETE: Remove an existing resource // If omitted, defaults to POST. // // This field is currently being introduced and will only warn and not deny // access if the write method does not match. WriteMethod string `json:",omitempty"` // ActionFunc is for simple actions with a return message for the user. ActionFunc ActionFunc `json:"-"` // DataFunc is for returning raw data that the caller for further processing. DataFunc DataFunc `json:"-"` // StructFunc is for returning any kind of struct. StructFunc StructFunc `json:"-"` // RecordFunc is for returning a database record. It will be properly locked // and marshalled including metadata. RecordFunc RecordFunc `json:"-"` // HandlerFunc is the raw http handler. HandlerFunc http.HandlerFunc `json:"-"` } // Parameter describes a parameterized variation of an endpoint. type Parameter struct { Method string Field string Value string Description string } // HTTPStatusProvider is an interface for errors to provide a custom HTTP // status code. type HTTPStatusProvider interface { HTTPStatus() int } // HTTPStatusError represents an error with an HTTP status code. type HTTPStatusError struct { err error code int } // Error returns the error message. func (e *HTTPStatusError) Error() string { return e.err.Error() } // Unwrap return the wrapped error. func (e *HTTPStatusError) Unwrap() error { return e.err } // HTTPStatus returns the HTTP status code this error. func (e *HTTPStatusError) HTTPStatus() int { return e.code } // ErrorWithStatus adds the HTTP status code to the error. func ErrorWithStatus(err error, code int) error { return &HTTPStatusError{ err: err, code: code, } } type ( // ActionFunc is for simple actions with a return message for the user. ActionFunc func(ar *Request) (msg string, err error) // DataFunc is for returning raw data that the caller for further processing. DataFunc func(ar *Request) (data []byte, err error) // StructFunc is for returning any kind of struct. StructFunc func(ar *Request) (i interface{}, err error) // RecordFunc is for returning a database record. It will be properly locked // and marshalled including metadata. RecordFunc func(ar *Request) (r record.Record, err error) ) // MIME Types. const ( MimeTypeJSON string = "application/json" MimeTypeText string = "text/plain" apiV1Path = "/api/v1/" ) func init() { RegisterHandler(apiV1Path+"{endpointPath:.+}", &endpointHandler{}) } var ( endpoints = make(map[string]*Endpoint) endpointsMux = mux.NewRouter() endpointsLock sync.RWMutex // ErrInvalidEndpoint is returned when an invalid endpoint is registered. ErrInvalidEndpoint = errors.New("endpoint is invalid") // ErrAlreadyRegistered is returned when there already is an endpoint with // the same path registered. ErrAlreadyRegistered = errors.New("an endpoint for this path is already registered") ) func getAPIContext(r *http.Request) (apiEndpoint *Endpoint, apiRequest *Request) { // Get request context and check if we already have an action cached. apiRequest = GetAPIRequest(r) if apiRequest == nil { return nil, nil } var ok bool apiEndpoint, ok = apiRequest.HandlerCache.(*Endpoint) if ok { return apiEndpoint, apiRequest } endpointsLock.RLock() defer endpointsLock.RUnlock() // Get handler for request. // Gorilla does not support handling this on our own very well. // See github.com/gorilla/mux.ServeHTTP for reference. var match mux.RouteMatch var handler http.Handler if endpointsMux.Match(r, &match) { handler = match.Handler apiRequest.Route = match.Route // Add/Override variables instead of replacing. for k, v := range match.Vars { apiRequest.URLVars[k] = v } } else { return nil, apiRequest } apiEndpoint, ok = handler.(*Endpoint) if ok { // Cache for next operation. apiRequest.HandlerCache = apiEndpoint } return apiEndpoint, apiRequest } // RegisterEndpoint registers a new endpoint. An error will be returned if it // does not pass the sanity checks. func RegisterEndpoint(e Endpoint) error { if err := e.check(); err != nil { return fmt.Errorf("%w: %w", ErrInvalidEndpoint, err) } endpointsLock.Lock() defer endpointsLock.Unlock() _, ok := endpoints[e.Path] if ok { return ErrAlreadyRegistered } endpoints[e.Path] = &e endpointsMux.Handle(apiV1Path+e.Path, &e) return nil } // GetEndpointByPath returns the endpoint registered with the given path. func GetEndpointByPath(path string) (*Endpoint, error) { endpointsLock.Lock() defer endpointsLock.Unlock() endpoint, ok := endpoints[path] if !ok { return nil, fmt.Errorf("no registered endpoint on path: %q", path) } return endpoint, nil } func (e *Endpoint) check() error { // Check path. if strings.TrimSpace(e.Path) == "" { return errors.New("path is missing") } // Check permissions. if e.Read < Dynamic || e.Read > PermitSelf { return errors.New("invalid read permission") } if e.Write < Dynamic || e.Write > PermitSelf { return errors.New("invalid write permission") } // Check methods. if e.Read != NotSupported { switch e.ReadMethod { case http.MethodGet: // All good. case "": // Set to default. e.ReadMethod = http.MethodGet default: return errors.New("invalid read method") } } else { e.ReadMethod = "" } if e.Write != NotSupported { switch e.WriteMethod { case http.MethodPost, http.MethodPut, http.MethodDelete: // All good. case "": // Set to default. e.WriteMethod = http.MethodPost default: return errors.New("invalid write method") } } else { e.WriteMethod = "" } // Check functions. var defaultMimeType string fnCnt := 0 if e.ActionFunc != nil { fnCnt++ defaultMimeType = MimeTypeText } if e.DataFunc != nil { fnCnt++ defaultMimeType = MimeTypeText } if e.StructFunc != nil { fnCnt++ defaultMimeType = MimeTypeJSON } if e.RecordFunc != nil { fnCnt++ defaultMimeType = MimeTypeJSON } if e.HandlerFunc != nil { fnCnt++ defaultMimeType = MimeTypeText } if fnCnt != 1 { return errors.New("only one function may be set") } // Set default mime type. if e.MimeType == "" { e.MimeType = defaultMimeType } return nil } // ExportEndpoints exports the registered endpoints. The returned data must be // treated as immutable. func ExportEndpoints() []*Endpoint { endpointsLock.RLock() defer endpointsLock.RUnlock() // Copy the map into a slice. eps := make([]*Endpoint, 0, len(endpoints)) for _, ep := range endpoints { eps = append(eps, ep) } sort.Sort(sortByPath(eps)) return eps } type sortByPath []*Endpoint func (eps sortByPath) Len() int { return len(eps) } func (eps sortByPath) Less(i, j int) bool { return eps[i].Path < eps[j].Path } func (eps sortByPath) Swap(i, j int) { eps[i], eps[j] = eps[j], eps[i] } type endpointHandler struct{} var _ AuthenticatedHandler = &endpointHandler{} // Compile time interface check. // ReadPermission returns the read permission for the handler. func (eh *endpointHandler) ReadPermission(r *http.Request) Permission { apiEndpoint, _ := getAPIContext(r) if apiEndpoint != nil { return apiEndpoint.Read } return NotFound } // WritePermission returns the write permission for the handler. func (eh *endpointHandler) WritePermission(r *http.Request) Permission { apiEndpoint, _ := getAPIContext(r) if apiEndpoint != nil { return apiEndpoint.Write } return NotFound } // ServeHTTP handles the http request. func (eh *endpointHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { apiEndpoint, apiRequest := getAPIContext(r) if apiEndpoint == nil || apiRequest == nil { http.NotFound(w, r) return } apiEndpoint.ServeHTTP(w, r) } // ServeHTTP handles the http request. func (e *Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { _, apiRequest := getAPIContext(r) if apiRequest == nil { http.NotFound(w, r) return } // Return OPTIONS request before starting to handle normal requests. if r.Method == http.MethodOptions { w.WriteHeader(http.StatusNoContent) return } eMethod, readMethod, ok := getEffectiveMethod(r) if !ok { http.Error(w, "unsupported method for the actions API", http.StatusMethodNotAllowed) return } if readMethod { if eMethod != e.ReadMethod { log.Tracer(r.Context()).Warningf( "api: method %q does not match required read method %q%s", r.Method, e.ReadMethod, " - this will be an error and abort the request in the future", ) } } else { if eMethod != e.WriteMethod { log.Tracer(r.Context()).Warningf( "api: method %q does not match required write method %q%s", r.Method, e.WriteMethod, " - this will be an error and abort the request in the future", ) } } switch eMethod { case http.MethodGet, http.MethodDelete: // Nothing to do for these. case http.MethodPost, http.MethodPut: // Read body data. inputData, ok := readBody(w, r) if !ok { return } apiRequest.InputData = inputData // restore request body for any http.HandlerFunc below r.Body = io.NopCloser(bytes.NewReader(inputData)) default: // Defensive. http.Error(w, "unsupported method for the actions API", http.StatusMethodNotAllowed) return } // Add response headers to request struct so that the endpoint can work with them. apiRequest.ResponseHeader = w.Header() // Execute action function and get response data var responseData []byte var err error switch { case e.ActionFunc != nil: var msg string msg, err = e.ActionFunc(apiRequest) if !strings.HasSuffix(msg, "\n") { msg += "\n" } if err == nil { responseData = []byte(msg) } case e.DataFunc != nil: responseData, err = e.DataFunc(apiRequest) case e.StructFunc != nil: var v interface{} v, err = e.StructFunc(apiRequest) if err == nil && v != nil { var mimeType string responseData, mimeType, _, err = dsd.MimeDump(v, r.Header.Get("Accept")) if err == nil { w.Header().Set("Content-Type", mimeType) } } case e.RecordFunc != nil: var rec record.Record rec, err = e.RecordFunc(apiRequest) if err == nil && r != nil { responseData, err = MarshalRecord(rec, false) } case e.HandlerFunc != nil: e.HandlerFunc(w, r) return default: http.Error(w, "missing handler", http.StatusInternalServerError) return } // Check for handler error. if err != nil { var statusProvider HTTPStatusProvider if errors.As(err, &statusProvider) { http.Error(w, err.Error(), statusProvider.HTTPStatus()) } else { http.Error(w, err.Error(), http.StatusInternalServerError) } return } // Return no content if there is none, or if request is HEAD. if len(responseData) == 0 || r.Method == http.MethodHead { w.WriteHeader(http.StatusNoContent) return } // Set content type if not yet set. if w.Header().Get("Content-Type") == "" { w.Header().Set("Content-Type", e.MimeType+"; charset=utf-8") } // Write response. w.Header().Set("Content-Length", strconv.Itoa(len(responseData))) w.WriteHeader(http.StatusOK) _, err = w.Write(responseData) if err != nil { log.Tracer(r.Context()).Warningf("api: failed to write response: %s", err) } } func readBody(w http.ResponseWriter, r *http.Request) (inputData []byte, ok bool) { // Check for too long content in order to prevent death. if r.ContentLength > 20000000 { // 20MB http.Error(w, "too much input data", http.StatusRequestEntityTooLarge) return nil, false } // Read and close body. inputData, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "failed to read body"+err.Error(), http.StatusInternalServerError) return nil, false } return inputData, true } ================================================ FILE: base/api/endpoints_config.go ================================================ package api import ( "github.com/safing/portmaster/base/config" ) func registerConfigEndpoints() error { if err := RegisterEndpoint(Endpoint{ Path: "config/options", Read: PermitAnyone, MimeType: MimeTypeJSON, StructFunc: listConfig, Name: "Export Configuration Options", Description: "Returns a list of all registered configuration options and their metadata. This does not include the current active or default settings.", }); err != nil { return err } return nil } func listConfig(ar *Request) (i interface{}, err error) { return config.ExportOptions(), nil } ================================================ FILE: base/api/endpoints_debug.go ================================================ package api import ( "bytes" "context" "errors" "fmt" "net/http" "os" "runtime/pprof" "strings" "time" "github.com/safing/portmaster/base/info" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils/debug" ) func registerDebugEndpoints() error { if err := RegisterEndpoint(Endpoint{ Path: "ping", Read: PermitAnyone, ActionFunc: ping, Name: "Ping", Description: "Pong.", }); err != nil { return err } if err := RegisterEndpoint(Endpoint{ Path: "ready", Read: PermitAnyone, ActionFunc: ready, Name: "Ready", Description: "Check if Portmaster has completed starting and is ready.", }); err != nil { return err } if err := RegisterEndpoint(Endpoint{ Path: "debug/stack", Read: PermitAnyone, DataFunc: getStack, Name: "Get Goroutine Stack", Description: "Returns the current goroutine stack.", }); err != nil { return err } if err := RegisterEndpoint(Endpoint{ Path: "debug/stack/print", Read: PermitAnyone, ActionFunc: printStack, Name: "Print Goroutine Stack", Description: "Prints the current goroutine stack to stdout.", }); err != nil { return err } if err := RegisterEndpoint(Endpoint{ Path: "debug/cpu", MimeType: "application/octet-stream", Read: PermitAnyone, DataFunc: handleCPUProfile, Name: "Get CPU Profile", Description: strings.ReplaceAll(`Gather and return the CPU profile. This data needs to gathered over a period of time, which is specified using the duration parameter. You can easily view this data in your browser with this command (with Go installed): "go tool pprof -http :8888 http://127.0.0.1:817/api/v1/debug/cpu" `, `"`, "`"), Parameters: []Parameter{{ Method: http.MethodGet, Field: "duration", Value: "10s", Description: "Specify the formatting style. The default is simple markdown formatting.", }}, }); err != nil { return err } if err := RegisterEndpoint(Endpoint{ Path: "debug/heap", MimeType: "application/octet-stream", Read: PermitAnyone, DataFunc: handleHeapProfile, Name: "Get Heap Profile", Description: strings.ReplaceAll(`Gather and return the heap memory profile. You can easily view this data in your browser with this command (with Go installed): "go tool pprof -http :8888 http://127.0.0.1:817/api/v1/debug/heap" `, `"`, "`"), }); err != nil { return err } if err := RegisterEndpoint(Endpoint{ Path: "debug/allocs", MimeType: "application/octet-stream", Read: PermitAnyone, DataFunc: handleAllocsProfile, Name: "Get Allocs Profile", Description: strings.ReplaceAll(`Gather and return the memory allocation profile. You can easily view this data in your browser with this command (with Go installed): "go tool pprof -http :8888 http://127.0.0.1:817/api/v1/debug/allocs" `, `"`, "`"), }); err != nil { return err } if err := RegisterEndpoint(Endpoint{ Path: "debug/info", Read: PermitAnyone, DataFunc: debugInfo, Name: "Get Debug Information", Description: "Returns debugging information, including the version and platform info, errors, logs and the current goroutine stack.", Parameters: []Parameter{{ Method: http.MethodGet, Field: "style", Value: "github", Description: "Specify the formatting style. The default is simple markdown formatting.", }}, }); err != nil { return err } return nil } // ping responds with pong. func ping(ar *Request) (msg string, err error) { return "Pong.", nil } // ready checks if Portmaster has completed starting. func ready(ar *Request) (msg string, err error) { if module.instance.Ready() { return "", ErrorWithStatus(errors.New("portmaster is not ready, reload (F5) to try again"), http.StatusTooEarly) } return "Portmaster is ready.", nil } // getStack returns the current goroutine stack. func getStack(_ *Request) (data []byte, err error) { buf := &bytes.Buffer{} err = pprof.Lookup("goroutine").WriteTo(buf, 1) if err != nil { return nil, err } return buf.Bytes(), nil } // printStack prints the current goroutine stack to stderr. func printStack(_ *Request) (msg string, err error) { _, err = fmt.Fprint(log.GlobalWriter, "===== PRINTING STACK =====\n") if err == nil { err = pprof.Lookup("goroutine").WriteTo(os.Stderr, 1) } if err == nil { _, err = fmt.Fprint(log.GlobalWriter, "===== END OF STACK =====\n") } if err != nil { return "", err } return "stack printed to stdout", nil } // handleCPUProfile returns the CPU profile. func handleCPUProfile(ar *Request) (data []byte, err error) { // Parse duration. duration := 10 * time.Second if durationOption := ar.Request.URL.Query().Get("duration"); durationOption != "" { parsedDuration, err := time.ParseDuration(durationOption) if err != nil { return nil, fmt.Errorf("failed to parse duration: %w", err) } duration = parsedDuration } // Indicate download and filename. ar.ResponseHeader.Set( "Content-Disposition", fmt.Sprintf(`attachment; filename="portmaster-cpu-profile_v%s.pprof"`, info.Version()), ) // Start CPU profiling. buf := new(bytes.Buffer) if err := pprof.StartCPUProfile(buf); err != nil { return nil, fmt.Errorf("failed to start cpu profile: %w", err) } // Wait for the specified duration. select { case <-time.After(duration): case <-ar.Context().Done(): pprof.StopCPUProfile() return nil, context.Canceled } // Stop CPU profiling and return data. pprof.StopCPUProfile() return buf.Bytes(), nil } // handleHeapProfile returns the Heap profile. func handleHeapProfile(ar *Request) (data []byte, err error) { // Indicate download and filename. ar.ResponseHeader.Set( "Content-Disposition", fmt.Sprintf(`attachment; filename="portmaster-memory-heap-profile_v%s.pprof"`, info.Version()), ) buf := new(bytes.Buffer) if err := pprof.Lookup("heap").WriteTo(buf, 0); err != nil { return nil, fmt.Errorf("failed to write heap profile: %w", err) } return buf.Bytes(), nil } // handleAllocsProfile returns the Allocs profile. func handleAllocsProfile(ar *Request) (data []byte, err error) { // Indicate download and filename. ar.ResponseHeader.Set( "Content-Disposition", fmt.Sprintf(`attachment; filename="portmaster-memory-allocs-profile_v%s.pprof"`, info.Version()), ) buf := new(bytes.Buffer) if err := pprof.Lookup("allocs").WriteTo(buf, 0); err != nil { return nil, fmt.Errorf("failed to write allocs profile: %w", err) } return buf.Bytes(), nil } // debugInfo returns the debugging information for support requests. func debugInfo(ar *Request) (data []byte, err error) { // Create debug information helper. di := new(debug.Info) di.Style = ar.Request.URL.Query().Get("style") // Add debug information. di.AddVersionInfo() di.AddPlatformInfo(ar.Context()) di.AddLastUnexpectedLogs() di.AddGoroutineStack() // Return data. return di.Bytes(), nil } ================================================ FILE: base/api/endpoints_meta.go ================================================ package api import ( "encoding/json" "errors" "net/http" ) func registerMetaEndpoints() error { if err := RegisterEndpoint(Endpoint{ Path: "endpoints", Read: PermitAnyone, MimeType: MimeTypeJSON, DataFunc: listEndpoints, Name: "Export API Endpoints", Description: "Returns a list of all registered endpoints and their metadata.", }); err != nil { return err } if err := RegisterEndpoint(Endpoint{ Path: "auth/permissions", Read: Dynamic, StructFunc: permissions, Name: "View Current Permissions", Description: "Returns the current permissions assigned to the request.", }); err != nil { return err } if err := RegisterEndpoint(Endpoint{ Path: "auth/bearer", Read: Dynamic, HandlerFunc: authBearer, Name: "Request HTTP Bearer Auth", Description: "Returns an HTTP Bearer Auth request, if not authenticated.", }); err != nil { return err } if err := RegisterEndpoint(Endpoint{ Path: "auth/basic", Read: Dynamic, HandlerFunc: authBasic, Name: "Request HTTP Basic Auth", Description: "Returns an HTTP Basic Auth request, if not authenticated.", }); err != nil { return err } if err := RegisterEndpoint(Endpoint{ Path: "auth/reset", Read: PermitAnyone, HandlerFunc: authReset, Name: "Reset Authenticated Session", Description: "Resets authentication status internally and in the browser.", }); err != nil { return err } return nil } func listEndpoints(ar *Request) (data []byte, err error) { data, err = json.Marshal(ExportEndpoints()) return } func permissions(ar *Request) (i interface{}, err error) { if ar.AuthToken == nil { return nil, errors.New("authentication token missing") } return struct { Read Permission Write Permission ReadRole string WriteRole string }{ Read: ar.AuthToken.Read, Write: ar.AuthToken.Write, ReadRole: ar.AuthToken.Read.Role(), WriteRole: ar.AuthToken.Write.Role(), }, nil } func authBearer(w http.ResponseWriter, r *http.Request) { // Check if authenticated by checking read permission. ar := GetAPIRequest(r) if ar.AuthToken.Read != PermitAnyone { TextResponse(w, r, "Authenticated.") return } // Respond with desired authentication header. w.Header().Set( "WWW-Authenticate", `Bearer realm="Portmaster API" domain="/"`, ) http.Error(w, "Authorization required.", http.StatusUnauthorized) } func authBasic(w http.ResponseWriter, r *http.Request) { // Check if authenticated by checking read permission. ar := GetAPIRequest(r) if ar.AuthToken.Read != PermitAnyone { TextResponse(w, r, "Authenticated.") return } // Respond with desired authentication header. w.Header().Set( "WWW-Authenticate", `Basic realm="Portmaster API" domain="/"`, ) http.Error(w, "Authorization required.", http.StatusUnauthorized) } func authReset(w http.ResponseWriter, r *http.Request) { // Get session cookie from request and delete session if exists. c, err := r.Cookie(sessionCookieName) if err == nil { deleteSession(c.Value) } // Delete session and cookie. http.SetCookie(w, &http.Cookie{ Name: sessionCookieName, MaxAge: -1, // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0' }) // Request client to also reset all data. w.Header().Set("Clear-Site-Data", "*") // Set HTTP Auth Realm without requesting authorization. w.Header().Set("WWW-Authenticate", `None realm="Portmaster API"`) // Reply with 401 Unauthorized in order to clear HTTP Basic Auth data. http.Error(w, "Session deleted.", http.StatusUnauthorized) } ================================================ FILE: base/api/endpoints_test.go ================================================ package api import ( "errors" "sync" "testing" "github.com/stretchr/testify/assert" "github.com/safing/portmaster/base/database/record" ) const ( successMsg = "endpoint api success" failedMsg = "endpoint api failed" ) type actionTestRecord struct { record.Base sync.Mutex Msg string } func TestEndpoints(t *testing.T) { t.Parallel() testHandler := &mainHandler{ mux: mainMux, } // ActionFn assert.NoError(t, RegisterEndpoint(Endpoint{ Path: "test/action", Read: PermitAnyone, ActionFunc: func(_ *Request) (msg string, err error) { return successMsg, nil }, })) assert.HTTPBodyContains(t, testHandler.ServeHTTP, "GET", apiV1Path+"test/action", nil, successMsg) assert.NoError(t, RegisterEndpoint(Endpoint{ Path: "test/action-err", Read: PermitAnyone, ActionFunc: func(_ *Request) (msg string, err error) { return "", errors.New(failedMsg) }, })) assert.HTTPBodyContains(t, testHandler.ServeHTTP, "GET", apiV1Path+"test/action-err", nil, failedMsg) // DataFn assert.NoError(t, RegisterEndpoint(Endpoint{ Path: "test/data", Read: PermitAnyone, DataFunc: func(_ *Request) (data []byte, err error) { return []byte(successMsg), nil }, })) assert.HTTPBodyContains(t, testHandler.ServeHTTP, "GET", apiV1Path+"test/data", nil, successMsg) assert.NoError(t, RegisterEndpoint(Endpoint{ Path: "test/data-err", Read: PermitAnyone, DataFunc: func(_ *Request) (data []byte, err error) { return nil, errors.New(failedMsg) }, })) assert.HTTPBodyContains(t, testHandler.ServeHTTP, "GET", apiV1Path+"test/data-err", nil, failedMsg) // StructFn assert.NoError(t, RegisterEndpoint(Endpoint{ Path: "test/struct", Read: PermitAnyone, StructFunc: func(_ *Request) (i interface{}, err error) { return &actionTestRecord{ Msg: successMsg, }, nil }, })) assert.HTTPBodyContains(t, testHandler.ServeHTTP, "GET", apiV1Path+"test/struct", nil, successMsg) assert.NoError(t, RegisterEndpoint(Endpoint{ Path: "test/struct-err", Read: PermitAnyone, StructFunc: func(_ *Request) (i interface{}, err error) { return nil, errors.New(failedMsg) }, })) assert.HTTPBodyContains(t, testHandler.ServeHTTP, "GET", apiV1Path+"test/struct-err", nil, failedMsg) // RecordFn assert.NoError(t, RegisterEndpoint(Endpoint{ Path: "test/record", Read: PermitAnyone, RecordFunc: func(_ *Request) (r record.Record, err error) { r = &actionTestRecord{ Msg: successMsg, } r.CreateMeta() return r, nil }, })) assert.HTTPBodyContains(t, testHandler.ServeHTTP, "GET", apiV1Path+"test/record", nil, successMsg) assert.NoError(t, RegisterEndpoint(Endpoint{ Path: "test/record-err", Read: PermitAnyone, RecordFunc: func(_ *Request) (r record.Record, err error) { return nil, errors.New(failedMsg) }, })) assert.HTTPBodyContains(t, testHandler.ServeHTTP, "GET", apiV1Path+"test/record-err", nil, failedMsg) } func TestActionRegistration(t *testing.T) { t.Parallel() assert.Error(t, RegisterEndpoint(Endpoint{})) assert.Error(t, RegisterEndpoint(Endpoint{ Path: "test/err", Read: NotFound, })) assert.Error(t, RegisterEndpoint(Endpoint{ Path: "test/err", Read: PermitSelf + 1, })) assert.Error(t, RegisterEndpoint(Endpoint{ Path: "test/err", Write: NotFound, })) assert.Error(t, RegisterEndpoint(Endpoint{ Path: "test/err", Write: PermitSelf + 1, })) assert.Error(t, RegisterEndpoint(Endpoint{ Path: "test/err", })) assert.Error(t, RegisterEndpoint(Endpoint{ Path: "test/err", ActionFunc: func(_ *Request) (msg string, err error) { return successMsg, nil }, DataFunc: func(_ *Request) (data []byte, err error) { return []byte(successMsg), nil }, })) assert.NoError(t, RegisterEndpoint(Endpoint{ Path: "test/err", ActionFunc: func(_ *Request) (msg string, err error) { return successMsg, nil }, })) } ================================================ FILE: base/api/enriched-response.go ================================================ package api import ( "bufio" "errors" "net" "net/http" "github.com/safing/portmaster/base/log" ) // LoggingResponseWriter is a wrapper for http.ResponseWriter for better request logging. type LoggingResponseWriter struct { ResponseWriter http.ResponseWriter Request *http.Request Status int } // NewLoggingResponseWriter wraps a http.ResponseWriter. func NewLoggingResponseWriter(w http.ResponseWriter, r *http.Request) *LoggingResponseWriter { return &LoggingResponseWriter{ ResponseWriter: w, Request: r, } } // Header wraps the original Header method. func (lrw *LoggingResponseWriter) Header() http.Header { return lrw.ResponseWriter.Header() } // Write wraps the original Write method. func (lrw *LoggingResponseWriter) Write(b []byte) (int, error) { return lrw.ResponseWriter.Write(b) } // WriteHeader wraps the original WriteHeader method to extract information. func (lrw *LoggingResponseWriter) WriteHeader(code int) { lrw.Status = code lrw.ResponseWriter.WriteHeader(code) } // Hijack wraps the original Hijack method, if available. func (lrw *LoggingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { hijacker, ok := lrw.ResponseWriter.(http.Hijacker) if ok { c, b, err := hijacker.Hijack() if err != nil { return nil, nil, err } log.Tracer(lrw.Request.Context()).Infof("api request: %s HIJ %s", lrw.Request.RemoteAddr, lrw.Request.RequestURI) return c, b, nil } return nil, nil, errors.New("response does not implement http.Hijacker") } // RequestLogger is a logging middleware. func RequestLogger(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Tracer(r.Context()).Tracef("api request: %s ___ %s", r.RemoteAddr, r.RequestURI) lrw := NewLoggingResponseWriter(w, r) next.ServeHTTP(lrw, r) if lrw.Status != 0 { // request may have been hijacked log.Tracer(r.Context()).Infof("api request: %s %d %s", lrw.Request.RemoteAddr, lrw.Status, lrw.Request.RequestURI) } }) } ================================================ FILE: base/api/init_test.go ================================================ package api import ( "testing" "github.com/safing/portmaster/base/config" ) type testInstance struct { config *config.Config } var _ instance = &testInstance{} func (stub *testInstance) Config() *config.Config { return stub.config } func (stub *testInstance) SetCmdLineOperation(f func() error) {} func (stub *testInstance) Ready() bool { return true } func TestMain(m *testing.M) { SetDefaultAPIListenAddress("0.0.0.0:8080") instance := &testInstance{} var err error module, err = New(instance) if err != nil { panic(err) } err = SetAuthenticator(testAuthenticator) if err != nil { panic(err) } m.Run() } ================================================ FILE: base/api/main.go ================================================ package api import ( "encoding/json" "errors" "flag" "os" "time" "github.com/safing/portmaster/service/mgr" ) var exportEndpoints bool // API Errors. var ( ErrAuthenticationAlreadySet = errors.New("the authentication function has already been set") ErrAuthenticationImmutable = errors.New("the authentication function can only be set before the api has started") ) func init() { flag.BoolVar(&exportEndpoints, "export-api-endpoints", false, "export api endpoint registry and exit") } func prep() error { // Register endpoints. if err := registerConfig(); err != nil { return err } if err := registerDebugEndpoints(); err != nil { return err } if err := registerConfigEndpoints(); err != nil { return err } if err := registerMetaEndpoints(); err != nil { return err } if exportEndpoints { module.instance.SetCmdLineOperation(exportEndpointsCmd) return mgr.ErrExecuteCmdLineOp } if getDefaultListenAddress() == "" { return errors.New("no default listen address for api available") } return nil } func start() error { startServer() updateAPIKeys() module.instance.Config().EventConfigChange.AddCallback("update API keys", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { updateAPIKeys() return false, nil }) // start api auth token cleaner if authFnSet.IsSet() { _ = module.mgr.Repeat("clean api sessions", 5*time.Minute, cleanSessions) } return registerEndpointBridgeDB() } func stop() error { return stopServer() } func exportEndpointsCmd() error { data, err := json.MarshalIndent(ExportEndpoints(), "", " ") if err != nil { return err } _, err = os.Stdout.Write(data) return err } ================================================ FILE: base/api/module.go ================================================ package api import ( "errors" "sync/atomic" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/mgr" ) // API is the HTTP/Websockets API module. type API struct { mgr *mgr.Manager instance instance online atomic.Bool } func (api *API) Manager() *mgr.Manager { return api.mgr } // Start starts the module. func (api *API) Start() error { if err := start(); err != nil { return err } api.online.Store(true) return nil } // Stop stops the module. func (api *API) Stop() error { defer api.online.Store(false) return stop() } var ( shimLoaded atomic.Bool module *API ) // New returns a new UI module. func New(instance instance) (*API, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("API") module = &API{ mgr: m, instance: instance, } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface { Config() *config.Config SetCmdLineOperation(f func() error) Ready() bool } ================================================ FILE: base/api/request.go ================================================ package api import ( "fmt" "net/http" "github.com/gorilla/mux" "github.com/safing/portmaster/base/log" ) // Request is a support struct to pool more request related information. type Request struct { // Request is the http request. *http.Request // InputData contains the request body for write operations. InputData []byte // Route of this request. Route *mux.Route // URLVars contains the URL variables extracted by the gorilla mux. URLVars map[string]string // AuthToken is the request-side authentication token assigned. AuthToken *AuthToken // ResponseHeader holds the response header. ResponseHeader http.Header // HandlerCache can be used by handlers to cache data between handlers within a request. HandlerCache interface{} } // apiRequestContextKey is a key used for the context key/value storage. type apiRequestContextKey struct{} // RequestContextKey is the key used to add the API request to the context. var RequestContextKey = apiRequestContextKey{} // GetAPIRequest returns the API Request of the given http request. func GetAPIRequest(r *http.Request) *Request { ar, ok := r.Context().Value(RequestContextKey).(*Request) if ok { return ar } return nil } // TextResponse writes a text response. func TextResponse(w http.ResponseWriter, r *http.Request, text string) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Header().Set("X-Content-Type-Options", "nosniff") w.WriteHeader(http.StatusOK) _, err := fmt.Fprintln(w, text) if err != nil { log.Tracer(r.Context()).Warningf("api: failed to write text response: %s", err) } } ================================================ FILE: base/api/router.go ================================================ package api import ( "context" "errors" "fmt" "net/http" "net/url" "path" "runtime/debug" "strings" "sync" "time" "github.com/gorilla/mux" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/mgr" ) // EnableServer defines if the HTTP server should be started. var EnableServer = true var ( // mainMux is the main mux router. mainMux = mux.NewRouter() // server is the main server. server = &http.Server{ ReadHeaderTimeout: 10 * time.Second, } handlerLock sync.RWMutex allowedDevCORSOrigins = []string{ "127.0.0.1", "localhost", } ) // RegisterHandler registers a handler with the API endpoint. func RegisterHandler(path string, handler http.Handler) *mux.Route { handlerLock.Lock() defer handlerLock.Unlock() return mainMux.Handle(path, handler) } // RegisterHandleFunc registers a handle function with the API endpoint. func RegisterHandleFunc(path string, handleFunc func(http.ResponseWriter, *http.Request)) *mux.Route { handlerLock.Lock() defer handlerLock.Unlock() return mainMux.HandleFunc(path, handleFunc) } func startServer() { // Check if server is enabled. if !EnableServer { return } // Configure server. server.Addr = listenAddressConfig() server.Handler = &mainHandler{ // TODO: mainMux should not be modified anymore. mux: mainMux, } // Start server manager. module.mgr.Go("http server manager", serverManager) } func stopServer() error { // Check if server is enabled. if !EnableServer { return nil } if server.Addr != "" { return server.Shutdown(context.Background()) } return nil } // Serve starts serving the API endpoint. func serverManager(ctx *mgr.WorkerCtx) error { // start serving log.Infof("api: starting to listen on %s", server.Addr) backoffDuration := 10 * time.Second for { err := module.mgr.Do("http server", func(ctx *mgr.WorkerCtx) error { err := server.ListenAndServe() // return on shutdown error if errors.Is(err, http.ErrServerClosed) { return nil } return err }) if err == nil { return nil } // log error and restart log.Errorf("api: http endpoint failed: %s - restarting in %s", err, backoffDuration) time.Sleep(backoffDuration) } } type mainHandler struct { mux *mux.Router } func (mh *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { _ = module.mgr.Do("http request", func(_ *mgr.WorkerCtx) error { return mh.handle(w, r) }) } func (mh *mainHandler) handle(w http.ResponseWriter, r *http.Request) error { // Setup context trace logging. ctx, tracer := log.AddTracer(r.Context()) // Add request context. apiRequest := &Request{ Request: r, } ctx = context.WithValue(ctx, RequestContextKey, apiRequest) // Add context back to request. r = r.WithContext(ctx) lrw := NewLoggingResponseWriter(w, r) tracer.Tracef("api request: %s ___ %s %s", r.RemoteAddr, lrw.Request.Method, r.RequestURI) defer func() { // Log request status. if lrw.Status != 0 { // If lrw.Status is 0, the request may have been hijacked. tracer.Debugf("api request: %s %d %s %s", lrw.Request.RemoteAddr, lrw.Status, lrw.Request.Method, lrw.Request.RequestURI) } tracer.Submit() }() // Add security headers. w.Header().Set("Referrer-Policy", "same-origin") w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("X-Frame-Options", "deny") w.Header().Set("X-XSS-Protection", "1; mode=block") w.Header().Set("X-DNS-Prefetch-Control", "off") // Add CSP Header in production mode. if !devMode() { w.Header().Set( "Content-Security-Policy", "default-src 'self'; "+ "connect-src https://*.safing.io 'self'; "+ "style-src 'self' 'unsafe-inline'; "+ "img-src 'self' data: blob:", ) } // Check Cross-Origin Requests. origin := r.Header.Get("Origin") isPreflighCheck := false if origin != "" { // Parse origin URL. originURL, err := url.Parse(origin) if err != nil { tracer.Warningf("api: denied request from %s: failed to parse origin header: %s", r.RemoteAddr, err) http.Error(lrw, "Invalid Origin.", http.StatusForbidden) return nil } // Check if the Origin matches the Host. switch { case originURL.Host == r.Host: // Origin (with port) matches Host. case originURL.Hostname() == r.Host: // Origin (without port) matches Host. case originURL.Scheme == "chrome-extension": // Allow access for the browser extension // TODO(ppacher): // This currently allows access from any browser extension. // Can we reduce that to only our browser extension? // Also, what do we need to support Firefox? case devMode() && utils.StringInSlice(allowedDevCORSOrigins, originURL.Hostname()): // We are in dev mode and the request is coming from the allowed // development origins. default: // Origin and Host do NOT match! tracer.Warningf("api: denied request from %s: Origin (`%s`) and Host (`%s`) do not match", r.RemoteAddr, origin, r.Host) http.Error(lrw, "Cross-Origin Request Denied.", http.StatusForbidden) return nil // If the Host header has a port, and the Origin does not, requests will // also end up here, as we cannot properly check for equality. } // Add Cross-Site Headers now as we need them in any case now. w.Header().Set("Access-Control-Allow-Origin", origin) w.Header().Set("Access-Control-Allow-Methods", "*") w.Header().Set("Access-Control-Allow-Headers", "*") w.Header().Set("Access-Control-Allow-Credentials", "true") w.Header().Set("Access-Control-Expose-Headers", "*") w.Header().Set("Access-Control-Max-Age", "60") w.Header().Add("Vary", "Origin") // if there's a Access-Control-Request-Method header this is a Preflight check. // In that case, we will just check if the preflighMethod is allowed and then return // success here if preflighMethod := r.Header.Get("Access-Control-Request-Method"); r.Method == http.MethodOptions && preflighMethod != "" { isPreflighCheck = true } } // Clean URL. cleanedRequestPath := cleanRequestPath(r.URL.Path) // If the cleaned URL differs from the original one, redirect to there. if r.URL.Path != cleanedRequestPath { redirURL := *r.URL redirURL.Path = cleanedRequestPath http.Redirect(lrw, r, redirURL.String(), http.StatusMovedPermanently) return nil } // Get handler for request. // Gorilla does not support handling this on our own very well. // See github.com/gorilla/mux.ServeHTTP for reference. var match mux.RouteMatch var handler http.Handler if mh.mux.Match(r, &match) { handler = match.Handler apiRequest.Route = match.Route apiRequest.URLVars = match.Vars } switch { case match.MatchErr == nil: // All good. case errors.Is(match.MatchErr, mux.ErrMethodMismatch): http.Error(lrw, "Method not allowed.", http.StatusMethodNotAllowed) return nil default: tracer.Debug("api: no handler registered for this path") http.Error(lrw, "Not found.", http.StatusNotFound) return nil } // Be sure that URLVars always is a map. if apiRequest.URLVars == nil { apiRequest.URLVars = make(map[string]string) } // Check method. _, readMethod, ok := getEffectiveMethod(r) if !ok { http.Error(lrw, "Method not allowed.", http.StatusMethodNotAllowed) return nil } // At this point we know the method is allowed and there's a handler for the request. // If this is just a CORS-Preflight, we'll accept the request with StatusOK now. // There's no point in trying to authenticate the request because the Browser will // not send authentication along a preflight check. if isPreflighCheck && handler != nil { lrw.WriteHeader(http.StatusOK) return nil } // Check authentication. apiRequest.AuthToken = authenticateRequest(lrw, r, handler, readMethod) if apiRequest.AuthToken == nil { // Authenticator already replied. return nil } // Check if we have a handler. if handler == nil { http.Error(lrw, "Not found.", http.StatusNotFound) return nil } // Format panics in handler. defer func() { if panicValue := recover(); panicValue != nil { // Log failure. log.Errorf("api: handler panic: %s", panicValue) // Respond with a server error. if devMode() { http.Error( lrw, fmt.Sprintf( "Internal Server Error: %s\n\n%s", panicValue, debug.Stack(), ), http.StatusInternalServerError, ) } else { http.Error(lrw, "Internal Server Error.", http.StatusInternalServerError) } } }() // Handle with registered handler. handler.ServeHTTP(lrw, r) return nil } // cleanRequestPath cleans and returns a request URL. func cleanRequestPath(requestPath string) string { // If the request URL is empty, return a request for "root". if requestPath == "" || requestPath == "/" { return "/" } // If the request URL does not start with a slash, prepend it. if !strings.HasPrefix(requestPath, "/") { requestPath = "/" + requestPath } // Clean path to remove any relative parts. cleanedRequestPath := path.Clean(requestPath) // Because path.Clean removes a trailing slash, we need to add it back here // if the original URL had one. if strings.HasSuffix(requestPath, "/") { cleanedRequestPath += "/" } return cleanedRequestPath } ================================================ FILE: base/api/testclient/root/index.html ================================================ yeeee ================================================ FILE: base/api/testclient/serve.go ================================================ package testclient import ( "net/http" "github.com/safing/portmaster/base/api" ) func init() { api.RegisterHandler("/test/", http.StripPrefix("/test/", http.FileServer(http.Dir("./api/testclient/root/")))) } ================================================ FILE: base/apprise/notify.go ================================================ package apprise import ( "bytes" "context" "encoding/json" "errors" "fmt" "io" "net/http" "sync" "github.com/safing/portmaster/base/utils" ) // Notifier sends messsages to an Apprise API. type Notifier struct { // URL defines the Apprise API endpoint. URL string // DefaultType defines the default message type. DefaultType MsgType // DefaultTag defines the default message tag. DefaultTag string // DefaultFormat defines the default message format. DefaultFormat MsgFormat // AllowUntagged defines if untagged messages are allowed, // which are sent to all configured apprise endpoints. AllowUntagged bool client *http.Client clientLock sync.Mutex } // Message represents the message to be sent to the Apprise API. type Message struct { // Title is an optional title to go along with the body. Title string `json:"title,omitempty"` // Body is the main message content. This is the only required field. Body string `json:"body"` // Type defines the message type you want to send as. // The valid options are info, success, warning, and failure. // If no type is specified then info is the default value used. Type MsgType `json:"type,omitempty"` // Tag is used to notify only those tagged accordingly. // Use a comma (,) to OR your tags and a space ( ) to AND them. Tag string `json:"tag,omitempty"` // Format optionally identifies the text format of the data you're feeding Apprise. // The valid options are text, markdown, html. // The default value if nothing is specified is text. Format MsgFormat `json:"format,omitempty"` } // MsgType defines the message type. type MsgType string // Message Types. const ( TypeInfo MsgType = "info" TypeSuccess MsgType = "success" TypeWarning MsgType = "warning" TypeFailure MsgType = "failure" ) // MsgFormat defines the message format. type MsgFormat string // Message Formats. const ( FormatText MsgFormat = "text" FormatMarkdown MsgFormat = "markdown" FormatHTML MsgFormat = "html" ) type errorResponse struct { Error string `json:"error"` } // Send sends a message to the Apprise API. func (n *Notifier) Send(ctx context.Context, m *Message) error { // Check if the message has a body. if m.Body == "" { return errors.New("the message must have a body") } // Apply notifier defaults. n.applyDefaults(m) // Check if the message is tagged. if m.Tag == "" && !n.AllowUntagged { return errors.New("the message must have a tag") } // Marshal the message to JSON. payload, err := json.Marshal(m) if err != nil { return fmt.Errorf("failed to marshal message: %w", err) } // Create request. request, err := http.NewRequestWithContext(ctx, http.MethodPost, n.URL, bytes.NewReader(payload)) if err != nil { return fmt.Errorf("failed to create request: %w", err) } request.Header.Set("Content-Type", "application/json") // Send message to API. resp, err := n.getClient().Do(request) if err != nil { return fmt.Errorf("failed to send message: %w", err) } defer resp.Body.Close() //nolint:errcheck,gosec switch resp.StatusCode { case http.StatusOK, http.StatusCreated, http.StatusNoContent, http.StatusAccepted: return nil default: // Try to tease body contents. if body, err := io.ReadAll(resp.Body); err == nil && len(body) > 0 { // Try to parse json response. errorResponse := &errorResponse{} if err := json.Unmarshal(body, errorResponse); err == nil && errorResponse.Error != "" { return fmt.Errorf("failed to send message: apprise returned %q with an error message: %s", resp.Status, errorResponse.Error) } return fmt.Errorf("failed to send message: %s (body teaser: %s)", resp.Status, utils.SafeFirst16Bytes(body)) } return fmt.Errorf("failed to send message: %s", resp.Status) } } func (n *Notifier) applyDefaults(m *Message) { if m.Type == "" { m.Type = n.DefaultType } if m.Tag == "" { m.Tag = n.DefaultTag } if m.Format == "" { m.Format = n.DefaultFormat } } // SetClient sets a custom http client for accessing the Apprise API. func (n *Notifier) SetClient(client *http.Client) { n.clientLock.Lock() defer n.clientLock.Unlock() n.client = client } func (n *Notifier) getClient() *http.Client { n.clientLock.Lock() defer n.clientLock.Unlock() // Create client if needed. if n.client == nil { n.client = &http.Client{} } return n.client } ================================================ FILE: base/config/basic_config.go ================================================ package config import ( "flag" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" ) // Configuration Keys. var ( CfgDevModeKey = "core/devMode" defaultDevMode bool CfgLogLevel = "core/log/level" defaultLogLevel = log.InfoLevel.String() logLevel StringOption ) func init() { flag.BoolVar(&defaultDevMode, "devmode", false, "enable development mode; configuration is stronger") } func registerBasicOptions() error { // Get the default log level from the log package. defaultLogLevel = log.GetLogLevel().Name() // Register logging setting. // The log package cannot do that, as it would trigger and import loop. if err := Register(&Option{ Name: "Log Level", Key: CfgLogLevel, Description: "Configure the logging level.", OptType: OptTypeString, ExpertiseLevel: ExpertiseLevelDeveloper, ReleaseLevel: ReleaseLevelStable, DefaultValue: defaultLogLevel, Annotations: Annotations{ DisplayOrderAnnotation: 513, DisplayHintAnnotation: DisplayHintOneOf, CategoryAnnotation: "Development", }, PossibleValues: []PossibleValue{ { Name: "Critical", Value: "critical", Description: "The critical level only logs errors that lead to a partial, but imminent failure.", }, { Name: "Error", Value: "error", Description: "The error level logs errors that potentially break functionality. Everything logged by the critical level is included here too.", }, { Name: "Warning", Value: "warning", Description: "The warning level logs minor errors and worse. Everything logged by the error level is included here too.", }, { Name: "Info", Value: "info", Description: "The info level logs the main events that are going on and are interesting to the user. Everything logged by the warning level is included here too.", }, { Name: "Debug", Value: "debug", Description: "The debug level logs some additional debugging details. Everything logged by the info level is included here too.", }, { Name: "Trace", Value: "trace", Description: "The trace level logs loads of detailed information as well as operation and request traces. Everything logged by the debug level is included here too.", }, }, }); err != nil { return err } logLevel = GetAsString(CfgLogLevel, defaultLogLevel) // Register to hook to update the log level. module.EventConfigChange.AddCallback("update log level", setLogLevel) return Register(&Option{ Name: "Development Mode", Key: CfgDevModeKey, Description: "In Development Mode, security restrictions are lifted/softened to enable unrestricted access for debugging and testing purposes.", OptType: OptTypeBool, ExpertiseLevel: ExpertiseLevelDeveloper, ReleaseLevel: ReleaseLevelStable, DefaultValue: defaultDevMode, Annotations: Annotations{ DisplayOrderAnnotation: 512, CategoryAnnotation: "Development", }, }) } func loadLogLevel() error { return setDefaultConfigOption(CfgLogLevel, log.GetLogLevel().Name(), false) } func setLogLevel(_ *mgr.WorkerCtx, _ struct{}) (cancel bool, err error) { log.SetLogLevel(log.ParseLevel(logLevel())) return false, nil } ================================================ FILE: base/config/database.go ================================================ package config import ( "errors" "sort" "strings" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" "github.com/safing/portmaster/base/log" ) var dbController *database.Controller // StorageInterface provices a storage.Interface to the configuration manager. type StorageInterface struct { storage.InjectBase } // Get returns a database record. func (s *StorageInterface) Get(key string) (record.Record, error) { opt, err := GetOption(key) if err != nil { return nil, storage.ErrNotFound } return opt.Export() } // Put stores a record in the database. func (s *StorageInterface) Put(r record.Record) (record.Record, error) { if r.Meta().Deleted > 0 { return r, setConfigOption(r.DatabaseKey(), nil, false) } acc := r.GetAccessor(r) if acc == nil { return nil, errors.New("invalid data") } val, ok := acc.Get("Value") if !ok || val == nil { err := setConfigOption(r.DatabaseKey(), nil, false) if err != nil { return nil, err } return s.Get(r.DatabaseKey()) } option, err := GetOption(r.DatabaseKey()) if err != nil { return nil, err } var value interface{} switch option.OptType { case OptTypeString: value, ok = acc.GetString("Value") case OptTypeStringArray: value, ok = acc.GetStringArray("Value") case OptTypeInt: value, ok = acc.GetInt("Value") case OptTypeBool: value, ok = acc.GetBool("Value") case optTypeAny: ok = false } if !ok { return nil, errors.New("received invalid value in \"Value\"") } if err := setConfigOption(r.DatabaseKey(), value, false); err != nil { return nil, err } return option.Export() } // Delete deletes a record from the database. func (s *StorageInterface) Delete(key string) error { return setConfigOption(key, nil, false) } // Query returns a an iterator for the supplied query. func (s *StorageInterface) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { optionsLock.RLock() defer optionsLock.RUnlock() it := iterator.New() var opts []*Option for _, opt := range options { if strings.HasPrefix(opt.Key, q.DatabaseKeyPrefix()) { opts = append(opts, opt) } } go s.processQuery(it, opts) return it, nil } func (s *StorageInterface) processQuery(it *iterator.Iterator, opts []*Option) { sort.Sort(sortByKey(opts)) for _, opt := range opts { r, err := opt.Export() if err != nil { it.Finish(err) return } it.Next <- r } it.Finish(nil) } // ReadOnly returns whether the database is read only. func (s *StorageInterface) ReadOnly() bool { return false } func registerAsDatabase() error { _, err := database.Register(&database.Database{ Name: "config", Description: "Configuration Manager", StorageType: "injected", }) if err != nil { return err } controller, err := database.InjectDatabase("config", &StorageInterface{}) if err != nil { return err } dbController = controller return nil } // handleOptionUpdate updates the expertise and release level options, // if required, and eventually pushes a update for the option. // The caller must hold the option lock. func handleOptionUpdate(option *Option, push bool) { if expertiseLevelOptionFlag.IsSet() && option == expertiseLevelOption { updateExpertiseLevel() } if releaseLevelOptionFlag.IsSet() && option == releaseLevelOption { updateReleaseLevel() } if push { pushUpdate(option) } } // pushUpdate pushes an database update notification for option. // The caller must hold the option lock. func pushUpdate(option *Option) { r, err := option.export() if err != nil { log.Errorf("failed to export option to push update: %s", err) } else { dbController.PushUpdate(r) } } ================================================ FILE: base/config/doc.go ================================================ // Package config provides a versatile configuration management system. package config ================================================ FILE: base/config/expertise.go ================================================ package config import ( "sync/atomic" "github.com/tevino/abool" ) // ExpertiseLevel allows to group settings by user expertise. // It's useful if complex or technical settings should be hidden // from the average user while still allowing experts and developers // to change deep configuration settings. type ExpertiseLevel uint8 // Expertise Level constants. const ( ExpertiseLevelUser ExpertiseLevel = 0 ExpertiseLevelExpert ExpertiseLevel = 1 ExpertiseLevelDeveloper ExpertiseLevel = 2 ExpertiseLevelNameUser = "user" ExpertiseLevelNameExpert = "expert" ExpertiseLevelNameDeveloper = "developer" expertiseLevelKey = "core/expertiseLevel" ) var ( expertiseLevelOption *Option expertiseLevel = new(int32) expertiseLevelOptionFlag = abool.New() ) func init() { registerExpertiseLevelOption() } func registerExpertiseLevelOption() { expertiseLevelOption = &Option{ Name: "UI Mode", Key: expertiseLevelKey, Description: "Control the default amount of settings and information shown. Hidden settings are still in effect. Can be changed temporarily in the top right corner.", OptType: OptTypeString, ExpertiseLevel: ExpertiseLevelUser, ReleaseLevel: ReleaseLevelStable, DefaultValue: ExpertiseLevelNameUser, Annotations: Annotations{ DisplayOrderAnnotation: -16, DisplayHintAnnotation: DisplayHintOneOf, CategoryAnnotation: "User Interface", }, PossibleValues: []PossibleValue{ { Name: "Simple Interface", Value: ExpertiseLevelNameUser, Description: "Hide complex settings and information.", }, { Name: "Advanced Interface", Value: ExpertiseLevelNameExpert, Description: "Show technical details.", }, { Name: "Developer Interface", Value: ExpertiseLevelNameDeveloper, Description: "Developer mode. Please be careful!", }, }, } err := Register(expertiseLevelOption) if err != nil { panic(err) } expertiseLevelOptionFlag.Set() } func updateExpertiseLevel() { // get value value := expertiseLevelOption.activeFallbackValue if expertiseLevelOption.activeValue != nil { value = expertiseLevelOption.activeValue } if expertiseLevelOption.activeDefaultValue != nil { value = expertiseLevelOption.activeDefaultValue } // set atomic value switch value.stringVal { case ExpertiseLevelNameUser: atomic.StoreInt32(expertiseLevel, int32(ExpertiseLevelUser)) case ExpertiseLevelNameExpert: atomic.StoreInt32(expertiseLevel, int32(ExpertiseLevelExpert)) case ExpertiseLevelNameDeveloper: atomic.StoreInt32(expertiseLevel, int32(ExpertiseLevelDeveloper)) default: atomic.StoreInt32(expertiseLevel, int32(ExpertiseLevelUser)) } } // GetExpertiseLevel returns the current active expertise level. func GetExpertiseLevel() uint8 { return uint8(atomic.LoadInt32(expertiseLevel)) } ================================================ FILE: base/config/get-safe.go ================================================ package config import "sync" type safe struct{} // Concurrent makes concurrency safe get methods available. var Concurrent = &safe{} // GetAsString returns a function that returns the wanted string with high performance. func (cs *safe) GetAsString(name string, fallback string) StringOption { valid := getValidityFlag() option, valueCache := getValueCache(name, nil, OptTypeString) value := fallback if valueCache != nil { value = valueCache.stringVal } var lock sync.Mutex return func() string { lock.Lock() defer lock.Unlock() if !valid.IsSet() { valid = getValidityFlag() option, valueCache = getValueCache(name, option, OptTypeString) if valueCache != nil { value = valueCache.stringVal } else { value = fallback } } return value } } // GetAsStringArray returns a function that returns the wanted string with high performance. func (cs *safe) GetAsStringArray(name string, fallback []string) StringArrayOption { valid := getValidityFlag() option, valueCache := getValueCache(name, nil, OptTypeStringArray) value := fallback if valueCache != nil { value = valueCache.stringArrayVal } var lock sync.Mutex return func() []string { lock.Lock() defer lock.Unlock() if !valid.IsSet() { valid = getValidityFlag() option, valueCache = getValueCache(name, option, OptTypeStringArray) if valueCache != nil { value = valueCache.stringArrayVal } else { value = fallback } } return value } } // GetAsInt returns a function that returns the wanted int with high performance. func (cs *safe) GetAsInt(name string, fallback int64) IntOption { valid := getValidityFlag() option, valueCache := getValueCache(name, nil, OptTypeInt) value := fallback if valueCache != nil { value = valueCache.intVal } var lock sync.Mutex return func() int64 { lock.Lock() defer lock.Unlock() if !valid.IsSet() { valid = getValidityFlag() option, valueCache = getValueCache(name, option, OptTypeInt) if valueCache != nil { value = valueCache.intVal } else { value = fallback } } return value } } // GetAsBool returns a function that returns the wanted int with high performance. func (cs *safe) GetAsBool(name string, fallback bool) BoolOption { valid := getValidityFlag() option, valueCache := getValueCache(name, nil, OptTypeBool) value := fallback if valueCache != nil { value = valueCache.boolVal } var lock sync.Mutex return func() bool { lock.Lock() defer lock.Unlock() if !valid.IsSet() { valid = getValidityFlag() option, valueCache = getValueCache(name, option, OptTypeBool) if valueCache != nil { value = valueCache.boolVal } else { value = fallback } } return value } } ================================================ FILE: base/config/get.go ================================================ package config import ( "github.com/safing/portmaster/base/log" ) type ( // StringOption defines the returned function by GetAsString. StringOption func() string // StringArrayOption defines the returned function by GetAsStringArray. StringArrayOption func() []string // IntOption defines the returned function by GetAsInt. IntOption func() int64 // BoolOption defines the returned function by GetAsBool. BoolOption func() bool ) func getValueCache(name string, option *Option, requestedType OptionType) (*Option, *valueCache) { // get option if option == nil { var err error option, err = GetOption(name) if err != nil { log.Errorf("config: request for unregistered option: %s", name) return nil, nil } } // Check the option type, no locking required as // OptType is immutable once it is set if requestedType != option.OptType { log.Errorf("config: bad type: requested %s as %s, but is %s", name, getTypeName(requestedType), getTypeName(option.OptType)) return option, nil } option.Lock() defer option.Unlock() // check release level if option.ReleaseLevel <= getReleaseLevel() && option.activeValue != nil { return option, option.activeValue } if option.activeDefaultValue != nil { return option, option.activeDefaultValue } return option, option.activeFallbackValue } // GetAsString returns a function that returns the wanted string with high performance. func GetAsString(name string, fallback string) StringOption { valid := getValidityFlag() option, valueCache := getValueCache(name, nil, OptTypeString) value := fallback if valueCache != nil { value = valueCache.stringVal } return func() string { if !valid.IsSet() { valid = getValidityFlag() option, valueCache = getValueCache(name, option, OptTypeString) if valueCache != nil { value = valueCache.stringVal } else { value = fallback } } return value } } // GetAsStringArray returns a function that returns the wanted string with high performance. func GetAsStringArray(name string, fallback []string) StringArrayOption { valid := getValidityFlag() option, valueCache := getValueCache(name, nil, OptTypeStringArray) value := fallback if valueCache != nil { value = valueCache.stringArrayVal } return func() []string { if !valid.IsSet() { valid = getValidityFlag() option, valueCache = getValueCache(name, option, OptTypeStringArray) if valueCache != nil { value = valueCache.stringArrayVal } else { value = fallback } } return value } } // GetAsInt returns a function that returns the wanted int with high performance. func GetAsInt(name string, fallback int64) IntOption { valid := getValidityFlag() option, valueCache := getValueCache(name, nil, OptTypeInt) value := fallback if valueCache != nil { value = valueCache.intVal } return func() int64 { if !valid.IsSet() { valid = getValidityFlag() option, valueCache = getValueCache(name, option, OptTypeInt) if valueCache != nil { value = valueCache.intVal } else { value = fallback } } return value } } // GetAsBool returns a function that returns the wanted int with high performance. func GetAsBool(name string, fallback bool) BoolOption { valid := getValidityFlag() option, valueCache := getValueCache(name, nil, OptTypeBool) value := fallback if valueCache != nil { value = valueCache.boolVal } return func() bool { if !valid.IsSet() { valid = getValidityFlag() option, valueCache = getValueCache(name, option, OptTypeBool) if valueCache != nil { value = valueCache.boolVal } else { value = fallback } } return value } } /* func getAndFindValue(key string) interface{} { optionsLock.RLock() option, ok := options[key] optionsLock.RUnlock() if !ok { log.Errorf("config: request for unregistered option: %s", key) return nil } return option.findValue() } */ /* // findValue finds the preferred value in the user or default config. func (option *Option) findValue() interface{} { // lock option option.Lock() defer option.Unlock() if option.ReleaseLevel <= getReleaseLevel() && option.activeValue != nil { return option.activeValue } if option.activeDefaultValue != nil { return option.activeDefaultValue } return option.DefaultValue } */ ================================================ FILE: base/config/get_test.go ================================================ package config import ( "encoding/json" "fmt" "testing" "github.com/safing/portmaster/base/log" ) func parseAndReplaceConfig(jsonData string) error { m, err := JSONToMap([]byte(jsonData)) if err != nil { return err } validationErrors, _ := ReplaceConfig(m) if len(validationErrors) > 0 { return fmt.Errorf("%d errors, first: %w", len(validationErrors), validationErrors[0]) } return nil } func parseAndReplaceDefaultConfig(jsonData string) error { m, err := JSONToMap([]byte(jsonData)) if err != nil { return err } validationErrors, _ := ReplaceDefaultConfig(m) if len(validationErrors) > 0 { return fmt.Errorf("%d errors, first: %w", len(validationErrors), validationErrors[0]) } return nil } func quickRegister(t *testing.T, key string, optType OptionType, defaultValue interface{}) { t.Helper() err := Register(&Option{ Name: key, Key: key, Description: "test config", ReleaseLevel: ReleaseLevelStable, ExpertiseLevel: ExpertiseLevelUser, OptType: optType, DefaultValue: defaultValue, }) if err != nil { t.Fatal(err) } } func TestGet(t *testing.T) { //nolint:paralleltest // reset options = make(map[string]*Option) err := log.Start("info", true, "") if err != nil { t.Fatal(err) } quickRegister(t, "monkey", OptTypeString, "c") quickRegister(t, "zebras/zebra", OptTypeStringArray, []string{"a", "b"}) quickRegister(t, "elephant", OptTypeInt, -1) quickRegister(t, "hot", OptTypeBool, false) quickRegister(t, "cold", OptTypeBool, true) err = parseAndReplaceConfig(` { "monkey": "a", "zebras": { "zebra": ["black", "white"] }, "elephant": 2, "hot": true, "cold": false } `) if err != nil { t.Fatal(err) } err = parseAndReplaceDefaultConfig(` { "monkey": "b", "snake": "0", "elephant": 0 } `) if err != nil { t.Fatal(err) } monkey := GetAsString("monkey", "none") if monkey() != "a" { t.Errorf("monkey should be a, is %s", monkey()) } zebra := GetAsStringArray("zebras/zebra", []string{}) if len(zebra()) != 2 || zebra()[0] != "black" || zebra()[1] != "white" { t.Errorf("zebra should be [\"black\", \"white\"], is %v", zebra()) } elephant := GetAsInt("elephant", -1) if elephant() != 2 { t.Errorf("elephant should be 2, is %d", elephant()) } hot := GetAsBool("hot", false) if !hot() { t.Errorf("hot should be true, is %v", hot()) } cold := GetAsBool("cold", true) if cold() { t.Errorf("cold should be false, is %v", cold()) } err = parseAndReplaceConfig(` { "monkey": "3" } `) if err != nil { t.Fatal(err) } if monkey() != "3" { t.Errorf("monkey should be 0, is %s", monkey()) } if elephant() != 0 { t.Errorf("elephant should be 0, is %d", elephant()) } zebra() hot() // concurrent GetAsString("monkey", "none")() GetAsStringArray("zebras/zebra", []string{})() GetAsInt("elephant", -1)() GetAsBool("hot", false)() // perspective // load data pLoaded := make(map[string]interface{}) err = json.Unmarshal([]byte(`{ "monkey": "a", "zebras": { "zebra": ["black", "white"] }, "elephant": 2, "hot": true, "cold": false }`), &pLoaded) if err != nil { t.Fatal(err) } // create p, err := NewPerspective(pLoaded) if err != nil { t.Fatal(err) } monkeyVal, ok := p.GetAsString("monkey") if !ok || monkeyVal != "a" { t.Errorf("[perspective] monkey should be a, is %+v", monkeyVal) } zebraVal, ok := p.GetAsStringArray("zebras/zebra") if !ok || len(zebraVal) != 2 || zebraVal[0] != "black" || zebraVal[1] != "white" { t.Errorf("[perspective] zebra should be [\"black\", \"white\"], is %+v", zebraVal) } elephantVal, ok := p.GetAsInt("elephant") if !ok || elephantVal != 2 { t.Errorf("[perspective] elephant should be 2, is %+v", elephantVal) } hotVal, ok := p.GetAsBool("hot") if !ok || !hotVal { t.Errorf("[perspective] hot should be true, is %+v", hotVal) } coldVal, ok := p.GetAsBool("cold") if !ok || coldVal { t.Errorf("[perspective] cold should be false, is %+v", coldVal) } } func TestReleaseLevel(t *testing.T) { //nolint:paralleltest // reset options = make(map[string]*Option) registerReleaseLevelOption() // setup subsystemOption := &Option{ Name: "test subsystem", Key: "subsystem/test", Description: "test config", ReleaseLevel: ReleaseLevelStable, ExpertiseLevel: ExpertiseLevelUser, OptType: OptTypeBool, DefaultValue: false, } err := Register(subsystemOption) if err != nil { t.Fatal(err) } err = SetConfigOption("subsystem/test", true) if err != nil { t.Fatal(err) } testSubsystem := GetAsBool("subsystem/test", false) // test option level stable subsystemOption.ReleaseLevel = ReleaseLevelStable err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable) if err != nil { t.Fatal(err) } if !testSubsystem() { t.Error("should be active") } err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta) if err != nil { t.Fatal(err) } if !testSubsystem() { t.Error("should be active") } err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental) if err != nil { t.Fatal(err) } if !testSubsystem() { t.Error("should be active") } // test option level beta subsystemOption.ReleaseLevel = ReleaseLevelBeta err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable) if err != nil { t.Fatal(err) } if testSubsystem() { t.Errorf("should be inactive: opt=%d system=%d", subsystemOption.ReleaseLevel, getReleaseLevel()) } err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta) if err != nil { t.Fatal(err) } if !testSubsystem() { t.Error("should be active") } err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental) if err != nil { t.Fatal(err) } if !testSubsystem() { t.Error("should be active") } // test option level experimental subsystemOption.ReleaseLevel = ReleaseLevelExperimental err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable) if err != nil { t.Fatal(err) } if testSubsystem() { t.Error("should be inactive") } err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta) if err != nil { t.Fatal(err) } if testSubsystem() { t.Error("should be inactive") } err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental) if err != nil { t.Fatal(err) } if !testSubsystem() { t.Error("should be active") } } func BenchmarkGetAsStringCached(b *testing.B) { // reset options = make(map[string]*Option) // Setup err := parseAndReplaceConfig(`{ "monkey": "banana" }`) if err != nil { b.Fatal(err) } monkey := GetAsString("monkey", "no banana") // Reset timer for precise results b.ResetTimer() // Start benchmark for range b.N { monkey() } } func BenchmarkGetAsStringRefetch(b *testing.B) { // Setup err := parseAndReplaceConfig(`{ "monkey": "banana" }`) if err != nil { b.Fatal(err) } // Reset timer for precise results b.ResetTimer() // Start benchmark for range b.N { getValueCache("monkey", nil, OptTypeString) } } func BenchmarkGetAsIntCached(b *testing.B) { // Setup err := parseAndReplaceConfig(`{ "elephant": 1 }`) if err != nil { b.Fatal(err) } elephant := GetAsInt("elephant", -1) // Reset timer for precise results b.ResetTimer() // Start benchmark for range b.N { elephant() } } func BenchmarkGetAsIntRefetch(b *testing.B) { // Setup err := parseAndReplaceConfig(`{ "elephant": 1 }`) if err != nil { b.Fatal(err) } // Reset timer for precise results b.ResetTimer() // Start benchmark for range b.N { getValueCache("elephant", nil, OptTypeInt) } } ================================================ FILE: base/config/init_test.go ================================================ package config import ( "fmt" "os" "testing" ) type testInstance struct { dataDir string } var _ instance = testInstance{} func (stub testInstance) DataDir() string { return stub.dataDir } func (stub testInstance) SetCmdLineOperation(f func() error) {} func newTestInstance(testName string) (*testInstance, error) { testDir, err := os.MkdirTemp("", fmt.Sprintf("portmaster-%s", testName)) if err != nil { return nil, fmt.Errorf("failed to make tmp dir: %w", err) } return &testInstance{ dataDir: testDir, }, nil } func TestMain(m *testing.M) { instance, err := newTestInstance("test-config") if err != nil { panic(fmt.Errorf("failed to create test instance: %w", err)) } defer func() { _ = os.RemoveAll(instance.DataDir()) }() module, err = New(instance) if err != nil { panic(fmt.Errorf("failed to initialize module: %w", err)) } m.Run() } func TestConfigPersistence(t *testing.T) { //nolint:paralleltest err := SaveConfig() if err != nil { t.Fatal(err) } err = loadConfig(true) if err != nil { t.Fatal(err) } } ================================================ FILE: base/config/main.go ================================================ package config import ( "encoding/json" "errors" "flag" "fmt" "io/fs" "os" "path/filepath" "sort" "sync/atomic" "github.com/safing/portmaster/base/utils/debug" "github.com/safing/portmaster/service/mgr" ) // ChangeEvent is the name of the config change event. const ChangeEvent = "config change" var exportConfig bool func init() { flag.BoolVar(&exportConfig, "export-config-options", false, "export configuration registry and exit") } func prep() error { if exportConfig { module.instance.SetCmdLineOperation(exportConfigCmd) return mgr.ErrExecuteCmdLineOp } return registerBasicOptions() } // true - when config is initialized (from disk). So no new options can be registered anymore. var cfgInitialized atomic.Bool func start() error { configFilePath = filepath.Join(module.instance.DataDir(), "config.json") // Load log level from log package after it started. err := loadLogLevel() if err != nil { return err } err = registerAsDatabase() if err != nil && !errors.Is(err, fs.ErrNotExist) { return err } // mark config as loaded, so that options can react on this in their registration and prevent registration of options after loading. cfgInitialized.Store(true) err = loadConfig(false) if err != nil && !errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("failed to load config file: %w", err) } return nil } func exportConfigCmd() error { // Reset the metrics instance name option, as the default // is set to the current hostname. // Config key copied from metrics.CfgOptionInstanceKey. option, err := GetOption("core/metrics/instance") if err == nil { option.DefaultValue = "" } data, err := json.MarshalIndent(ExportOptions(), "", " ") if err != nil { return err } _, err = os.Stdout.Write(data) return err } // AddToDebugInfo adds all changed global config options to the given debug.Info. func AddToDebugInfo(di *debug.Info) { var lines []string // Collect all changed settings. _ = ForEachOption(func(opt *Option) error { opt.Lock() defer opt.Unlock() if opt.ReleaseLevel <= getReleaseLevel() && opt.activeValue != nil { if opt.Sensitive { lines = append(lines, fmt.Sprintf("%s: [redacted]", opt.Key)) } else { lines = append(lines, fmt.Sprintf("%s: %v", opt.Key, opt.activeValue.getData(opt))) } } return nil }) sort.Strings(lines) // Add data as section. di.AddSection( fmt.Sprintf("Config: %d", len(lines)), debug.UseCodeSection|debug.AddContentLineBreaks, lines..., ) } // GetActiveConfigValues returns a map with the active config values. func GetActiveConfigValues() map[string]interface{} { values := make(map[string]interface{}) // Collect active values from options. _ = ForEachOption(func(opt *Option) error { opt.Lock() defer opt.Unlock() if opt.ReleaseLevel <= getReleaseLevel() && opt.activeValue != nil { values[opt.Key] = opt.activeValue.getData(opt) } return nil }) return values } ================================================ FILE: base/config/module.go ================================================ package config import ( "errors" "sync/atomic" "github.com/safing/portmaster/service/mgr" ) // Config provides configuration mgmt. type Config struct { mgr *mgr.Manager instance instance EventConfigChange *mgr.EventMgr[struct{}] } // Manager returns the module's manager. func (u *Config) Manager() *mgr.Manager { return u.mgr } // Start starts the module. func (u *Config) Start() error { return start() } // Stop stops the module. func (u *Config) Stop() error { return nil } var ( module *Config shimLoaded atomic.Bool ) // New returns a new Config module. func New(instance instance) (*Config, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Config") module = &Config{ mgr: m, instance: instance, EventConfigChange: mgr.NewEventMgr[struct{}](ChangeEvent, m), } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface { DataDir() string SetCmdLineOperation(f func() error) } ================================================ FILE: base/config/option.go ================================================ package config import ( "encoding/json" "fmt" "reflect" "regexp" "sync" "github.com/mitchellh/copystructure" "github.com/tidwall/sjson" "github.com/safing/portmaster/base/database/record" "github.com/safing/structures/dsd" ) // OptionType defines the value type of an option. type OptionType uint8 // Various attribute options. Use ExternalOptType for extended types in the frontend. const ( optTypeAny OptionType = 0 OptTypeString OptionType = 1 OptTypeStringArray OptionType = 2 OptTypeInt OptionType = 3 OptTypeBool OptionType = 4 ) func getTypeName(t OptionType) string { switch t { case optTypeAny: return "any" case OptTypeString: return "string" case OptTypeStringArray: return "[]string" case OptTypeInt: return "int" case OptTypeBool: return "bool" default: return "unknown" } } // PossibleValue defines a value that is possible for // a configuration setting. type PossibleValue struct { // Name is a human readable name of the option. Name string // Description is a human readable description of // this value. Description string // Value is the actual value of the option. The type // must match the option's value type. Value interface{} } // Annotations can be attached to configuration options to // provide hints for user interfaces or other systems working // or setting configuration options. // Annotation keys should follow the below format to ensure // future well-known annotation additions do not conflict // with vendor/product/package specific annoations. // // Format: :: //. type Annotations map[string]interface{} // MigrationFunc is a function that migrates a config option value. type MigrationFunc func(option *Option, value any) any // Well known annotations defined by this package. const ( // DisplayHintAnnotation provides a hint for the user // interface on how to render an option. // The value of DisplayHintAnnotation is expected to // be a string. See DisplayHintXXXX constants below // for a list of well-known display hint annotations. DisplayHintAnnotation = "safing/portbase:ui:display-hint" // DisplayOrderAnnotation provides a hint for the user // interface in which order settings should be displayed. // The value of DisplayOrderAnnotations is expected to be // an number (int). DisplayOrderAnnotation = "safing/portbase:ui:order" // UnitAnnotations defines the SI unit of an option (if any). UnitAnnotation = "safing/portbase:ui:unit" // CategoryAnnotations can provide an additional category // to each settings. This category can be used by a user // interface to group certain options together. // User interfaces should treat a CategoryAnnotation, if // supported, with higher priority as a DisplayOrderAnnotation. CategoryAnnotation = "safing/portbase:ui:category" // SubsystemAnnotation can be used to mark an option as part // of a module subsystem. SubsystemAnnotation = "safing/portbase:module:subsystem" // StackableAnnotation can be set on configuration options that // stack on top of the default (or otherwise related) options. // The value of StackableAnnotaiton is expected to be a boolean but // may be extended to hold references to other options in the // future. StackableAnnotation = "safing/portbase:options:stackable" // RestartPendingAnnotation is automatically set on a configuration option // that requires a restart and has been changed. // The value must always be a boolean with value "true". RestartPendingAnnotation = "safing/portbase:options:restart-pending" // QuickSettingAnnotation can be used to add quick settings to // a configuration option. A quick setting can support the user // by switching between pre-configured values. // The type of a quick-setting annotation is []QuickSetting or QuickSetting. QuickSettingsAnnotation = "safing/portbase:ui:quick-setting" // RequiresAnnotation can be used to mark another option as a // requirement. The type of RequiresAnnotation is []ValueRequirement // or ValueRequirement. RequiresAnnotation = "safing/portbase:config:requires" // RequiresFeatureIDAnnotation can be used to mark a setting as only available // when the user has a certain feature ID in the subscription plan. // The type is []string or string. RequiresFeatureIDAnnotation = "safing/portmaster:ui:config:requires-feature" // SettablePerAppAnnotation can be used to mark a setting as settable per-app and // is a boolean. SettablePerAppAnnotation = "safing/portmaster:settable-per-app" // RequiresUIReloadAnnotation can be used to inform the UI that changing the value // of the annotated setting requires a full reload of the user interface. // The value of this annotation does not matter as the sole presence of // the annotation key is enough. Though, users are advised to set the value // of this annotation to true. RequiresUIReloadAnnotation = "safing/portmaster:ui:requires-reload" ) // QuickSettingsAction defines the action of a quick setting. type QuickSettingsAction string const ( // QuickReplace replaces the current setting with the one from // the quick setting. QuickReplace = QuickSettingsAction("replace") // QuickMergeTop merges the value of the quick setting with the // already configured one adding new values on the top. Merging // is only supported for OptTypeStringArray. QuickMergeTop = QuickSettingsAction("merge-top") // QuickMergeBottom merges the value of the quick setting with the // already configured one adding new values at the bottom. Merging // is only supported for OptTypeStringArray. QuickMergeBottom = QuickSettingsAction("merge-bottom") ) // QuickSetting defines a quick setting for a configuration option and // should be used together with the QuickSettingsAnnotation. type QuickSetting struct { // Name is the name of the quick setting. Name string // Value is the value that the quick-setting configures. It must match // the expected value type of the annotated option. Value interface{} // Action defines the action of the quick setting. Action QuickSettingsAction } // ValueRequirement defines a requirement on another configuration option. type ValueRequirement struct { // Key is the key of the configuration option that is required. Key string // Value that is required. Value interface{} } // Values for the DisplayHintAnnotation. const ( // DisplayHintOneOf is used to mark an option // as a "select"-style option. That is, only one of // the supported values may be set. This option makes // only sense together with the PossibleValues property // of Option. DisplayHintOneOf = "one-of" // DisplayHintOrdered is used to mark a list option as ordered. // That is, the order of items is important and a user interface // is encouraged to provide the user with re-ordering support // (like drag'n'drop). DisplayHintOrdered = "ordered" // DisplayHintFilePicker is used to mark the option as being a file, which // should give the option to use a file picker to select a local file from disk. DisplayHintFilePicker = "file-picker" ) // Option describes a configuration option. type Option struct { sync.Mutex // Name holds the name of the configuration options. // It should be human readable and is mainly used for // presentation purposes. // Name is considered immutable after the option has // been created. Name string // Key holds the database path for the option. It should // follow the path format `category/sub/key`. // Key is considered immutable after the option has // been created. Key string // Description holds a human readable description of the // option and what is does. The description should be short. // Use the Help property for a longer support text. // Description is considered immutable after the option has // been created. Description string // Help may hold a long version of the description providing // assistance with the configuration option. // Help is considered immutable after the option has // been created. Help string // Sensitive signifies that the configuration values may contain sensitive // content, such as authentication keys. Sensitive bool // OptType defines the type of the option. // OptType is considered immutable after the option has // been created. OptType OptionType // ExpertiseLevel can be used to set the required expertise // level for the option to be displayed to a user. // ExpertiseLevel is considered immutable after the option has // been created. ExpertiseLevel ExpertiseLevel // ReleaseLevel is used to mark the stability of the option. // ReleaseLevel is considered immutable after the option has // been created. ReleaseLevel ReleaseLevel // RequiresRestart should be set to true if a modification of // the options value requires a restart of the whole application // to take effect. // RequiresRestart is considered immutable after the option has // been created. RequiresRestart bool // DefaultValue holds the default value of the option. Note that // this value can be overwritten during runtime (see activeDefaultValue // and activeFallbackValue). // DefaultValue is considered immutable after the option has // been created. DefaultValue interface{} // ValidationRegex may contain a regular expression used to validate // the value of option. If the option type is set to OptTypeStringArray // the validation regex is applied to all entries of the string slice. // Note that it is recommended to keep the validation regex simple so // it can also be used in other languages (mainly JavaScript) to provide // a better user-experience by pre-validating the expression. // ValidationRegex is considered immutable after the option has // been created. ValidationRegex string // ValidationFunc may contain a function to validate more complex values. // The error is returned beyond the scope of this package and may be // displayed to a user. ValidationFunc func(value interface{}) error `json:"-"` // PossibleValues may be set to a slice of values that are allowed // for this configuration setting. Note that PossibleValues makes most // sense when ExternalOptType is set to HintOneOf // PossibleValues is considered immutable after the option has // been created. PossibleValues []PossibleValue `json:",omitempty"` // Annotations adds additional annotations to the configuration options. // See documentation of Annotations for more information. // Annotations is considered mutable and setting/reading annotation keys // must be performed while the option is locked. Annotations Annotations // Migrations holds migration functions that are given the raw option value // before any validation is run. The returned value is then used. Migrations []MigrationFunc `json:"-"` activeValue *valueCache // runtime value (loaded from config file or set by user) activeDefaultValue *valueCache // runtime default value (may be set internally) activeFallbackValue *valueCache // default value from option registration compiledRegex *regexp.Regexp } // AddAnnotation adds the annotation key to option if it's not already set. func (option *Option) AddAnnotation(key string, value interface{}) { option.Lock() defer option.Unlock() if option.Annotations == nil { option.Annotations = make(Annotations) } if _, ok := option.Annotations[key]; ok { return } option.Annotations[key] = value } // SetAnnotation sets the value of the annotation key overwritting an // existing value if required. func (option *Option) SetAnnotation(key string, value interface{}) { option.Lock() defer option.Unlock() option.setAnnotation(key, value) } // setAnnotation sets the value of the annotation key overwritting an // existing value if required. Does not lock the Option. func (option *Option) setAnnotation(key string, value interface{}) { if option.Annotations == nil { option.Annotations = make(Annotations) } option.Annotations[key] = value } // GetAnnotation returns the value of the annotation key. func (option *Option) GetAnnotation(key string) (interface{}, bool) { option.Lock() defer option.Unlock() if option.Annotations == nil { return nil, false } val, ok := option.Annotations[key] return val, ok } // AnnotationEquals returns whether the annotation of the given key matches the // given value. func (option *Option) AnnotationEquals(key string, value any) bool { option.Lock() defer option.Unlock() if option.Annotations == nil { return false } setValue, ok := option.Annotations[key] if !ok { return false } return reflect.DeepEqual(value, setValue) } // copyOrNil returns a copy of the option, or nil if copying failed. func (option *Option) copyOrNil() *Option { copied, err := copystructure.Copy(option) if err != nil { return nil } return copied.(*Option) //nolint:forcetypeassert } // IsSetByUser returns whether the option has been set by the user. func (option *Option) IsSetByUser() bool { option.Lock() defer option.Unlock() return option.activeValue != nil } // UserValue returns the value set by the user or nil if the value has not // been changed from the default. func (option *Option) UserValue() any { option.Lock() defer option.Unlock() if option.activeValue == nil { return nil } return option.activeValue.getData(option) } // ValidateValue checks if the given value is valid for the option. func (option *Option) ValidateValue(value any) error { option.Lock() defer option.Unlock() value = migrateValue(option, value) if _, err := validateValue(option, value); err != nil { return err } return nil } // Export expors an option to a Record. func (option *Option) Export() (record.Record, error) { option.Lock() defer option.Unlock() return option.export() } func (option *Option) export() (record.Record, error) { data, err := json.Marshal(option) if err != nil { return nil, err } if option.activeValue != nil { data, err = sjson.SetBytes(data, "Value", option.activeValue.getData(option)) if err != nil { return nil, err } } if option.activeDefaultValue != nil { data, err = sjson.SetBytes(data, "DefaultValue", option.activeDefaultValue.getData(option)) if err != nil { return nil, err } } r, err := record.NewWrapper(fmt.Sprintf("config:%s", option.Key), nil, dsd.JSON, data) if err != nil { return nil, err } r.SetMeta(&record.Meta{}) return r, nil } type sortByKey []*Option func (opts sortByKey) Len() int { return len(opts) } func (opts sortByKey) Less(i, j int) bool { return opts[i].Key < opts[j].Key } func (opts sortByKey) Swap(i, j int) { opts[i], opts[j] = opts[j], opts[i] } ================================================ FILE: base/config/persistence.go ================================================ package config import ( "encoding/json" "fmt" "os" "path" "strings" "sync" "github.com/safing/portmaster/base/log" ) var ( configFilePath string loadedConfigValidationErrors []*ValidationError loadedConfigValidationErrorsLock sync.Mutex ) // GetLoadedConfigValidationErrors returns the encountered validation errors // from the last time loading config from disk. func GetLoadedConfigValidationErrors() []*ValidationError { loadedConfigValidationErrorsLock.Lock() defer loadedConfigValidationErrorsLock.Unlock() return loadedConfigValidationErrors } func loadConfig(requireValidConfig bool) error { // check if persistence is configured if configFilePath == "" { return nil } // read config file data, err := os.ReadFile(configFilePath) if err != nil { return err } // convert to map newValues, err := JSONToMap(data) if err != nil { return err } validationErrors, _ := ReplaceConfig(newValues) if requireValidConfig && len(validationErrors) > 0 { return fmt.Errorf("encountered %d validation errors during config loading", len(validationErrors)) } // Save validation errors. loadedConfigValidationErrorsLock.Lock() defer loadedConfigValidationErrorsLock.Unlock() loadedConfigValidationErrors = validationErrors return nil } // SaveConfig saves the current configuration to file. // It will acquire a read-lock on the global options registry // lock and must lock each option! func SaveConfig() error { optionsLock.RLock() defer optionsLock.RUnlock() // check if persistence is configured if configFilePath == "" { return nil } // extract values activeValues := make(map[string]interface{}) for key, option := range options { // we cannot immedately unlock the option afger // getData() because someone could lock and change it // while we are marshaling the value (i.e. for string slices). // We NEED to keep the option locks until we finsihed. option.Lock() defer option.Unlock() if option.activeValue != nil { activeValues[key] = option.activeValue.getData(option) } } // convert to JSON data, err := MapToJSON(activeValues) if err != nil { log.Errorf("config: failed to save config: %s", err) return err } // write file return os.WriteFile(configFilePath, data, 0o0600) } // JSONToMap parses and flattens a hierarchical json object. func JSONToMap(jsonData []byte) (map[string]interface{}, error) { loaded := make(map[string]interface{}) err := json.Unmarshal(jsonData, &loaded) if err != nil { return nil, err } return Flatten(loaded), nil } // Flatten returns a flattened copy of the given hierarchical config. func Flatten(config map[string]interface{}) (flattenedConfig map[string]interface{}) { flattenedConfig = make(map[string]interface{}) flattenMap(flattenedConfig, config, "") return flattenedConfig } func flattenMap(rootMap, subMap map[string]interface{}, subKey string) { for key, entry := range subMap { // get next level key subbedKey := path.Join(subKey, key) // check for next subMap nextSub, ok := entry.(map[string]interface{}) if ok { flattenMap(rootMap, nextSub, subbedKey) } else { // only set if not on root level rootMap[subbedKey] = entry } } } // MapToJSON expands a flattened map and returns it as json. func MapToJSON(config map[string]interface{}) ([]byte, error) { return json.MarshalIndent(Expand(config), "", " ") } // Expand returns a hierarchical copy of the given flattened config. func Expand(flattenedConfig map[string]interface{}) (config map[string]interface{}) { config = make(map[string]interface{}) for key, entry := range flattenedConfig { PutValueIntoHierarchicalConfig(config, key, entry) } return config } // PutValueIntoHierarchicalConfig injects a configuration entry into an hierarchical config map. Conflicting entries will be replaced. func PutValueIntoHierarchicalConfig(config map[string]interface{}, key string, value interface{}) { parts := strings.Split(key, "/") // create/check maps for all parts except the last one subMap := config for i, part := range parts { if i == len(parts)-1 { // do not process the last part, // which is not a map, but the value key itself break } var nextSubMap map[string]interface{} // get value value, ok := subMap[part] if !ok { // create new map and assign it nextSubMap = make(map[string]interface{}) subMap[part] = nextSubMap } else { nextSubMap, ok = value.(map[string]interface{}) if !ok { // create new map and assign it nextSubMap = make(map[string]interface{}) subMap[part] = nextSubMap } } // assign for next parts loop subMap = nextSubMap } // assign value to last submap subMap[parts[len(parts)-1]] = value } // CleanFlattenedConfig removes all inexistent configuration options from the given flattened config map. func CleanFlattenedConfig(flattenedConfig map[string]interface{}) { optionsLock.RLock() defer optionsLock.RUnlock() for key := range flattenedConfig { _, ok := options[key] if !ok { delete(flattenedConfig, key) } } } // CleanHierarchicalConfig removes all inexistent configuration options from the given hierarchical config map. func CleanHierarchicalConfig(config map[string]interface{}) { optionsLock.RLock() defer optionsLock.RUnlock() cleanSubMap(config, "") } func cleanSubMap(subMap map[string]interface{}, subKey string) (empty bool) { var foundValid int for key, value := range subMap { value, ok := value.(map[string]interface{}) if ok { // we found another section isEmpty := cleanSubMap(value, path.Join(subKey, key)) if isEmpty { delete(subMap, key) } else { foundValid++ } continue } // we found an option value if strings.Contains(key, "/") { delete(subMap, key) } else { _, ok := options[path.Join(subKey, key)] if ok { foundValid++ } else { delete(subMap, key) } } } return foundValid == 0 } ================================================ FILE: base/config/persistence_test.go ================================================ package config import ( "bytes" "encoding/json" "testing" ) var ( jsonData = `{ "a": "b", "c": { "d": "e", "f": "g", "h": { "i": "j", "k": "l", "m": { "n": "o" } } }, "p": "q" }` jsonBytes = []byte(jsonData) mapData = map[string]interface{}{ "a": "b", "p": "q", "c/d": "e", "c/f": "g", "c/h/i": "j", "c/h/k": "l", "c/h/m/n": "o", } ) func TestJSONMapConversion(t *testing.T) { t.Parallel() // convert to json j, err := MapToJSON(mapData) if err != nil { t.Fatal(err) } // check if to json matches if !bytes.Equal(jsonBytes, j) { t.Errorf("json does not match, got %s", j) } // convert to map m, err := JSONToMap(jsonBytes) if err != nil { t.Fatal(err) } // and back j2, err := MapToJSON(m) if err != nil { t.Fatal(err) } // check if double convert matches if !bytes.Equal(jsonBytes, j2) { t.Errorf("json does not match, got %s", j) } } func TestConfigCleaning(t *testing.T) { t.Parallel() // load configFlat, err := JSONToMap(jsonBytes) if err != nil { t.Fatal(err) } // clean everything CleanFlattenedConfig(configFlat) if len(configFlat) != 0 { t.Errorf("should be empty: %+v", configFlat) } // load manuall for hierarchical config configHier := make(map[string]interface{}) err = json.Unmarshal(jsonBytes, &configHier) if err != nil { t.Fatal(err) } // clean everything CleanHierarchicalConfig(configHier) if len(configHier) != 0 { t.Errorf("should be empty: %+v", configHier) } } ================================================ FILE: base/config/perspective.go ================================================ package config import ( "fmt" "github.com/safing/portmaster/base/log" ) // Perspective is a view on configuration data without interfering with the configuration system. type Perspective struct { config map[string]*perspectiveOption } type perspectiveOption struct { option *Option valueCache *valueCache } // NewPerspective parses the given config and returns it as a new perspective. func NewPerspective(config map[string]interface{}) (*Perspective, error) { // flatten config structure config = Flatten(config) perspective := &Perspective{ config: make(map[string]*perspectiveOption), } var firstErr error var errCnt int optionsLock.RLock() optionsLoop: for key, option := range options { // get option key from config configValue, ok := config[key] if !ok { continue } // migrate value configValue = migrateValue(option, configValue) // validate value valueCache, err := validateValue(option, configValue) if err != nil { errCnt++ if firstErr == nil { firstErr = err } continue optionsLoop } // add to perspective perspective.config[key] = &perspectiveOption{ option: option, valueCache: valueCache, } } optionsLock.RUnlock() if firstErr != nil { if errCnt > 0 { return perspective, fmt.Errorf("encountered %d errors, first was: %w", errCnt, firstErr) } return perspective, firstErr } return perspective, nil } func (p *Perspective) getPerspectiveValueCache(name string, requestedType OptionType) *valueCache { // get option pOption, ok := p.config[name] if !ok { // check if option exists at all if _, err := GetOption(name); err != nil { log.Errorf("config: request for unregistered option: %s", name) } return nil } // check type if requestedType != pOption.option.OptType && requestedType != optTypeAny { log.Errorf("config: bad type: requested %s as %s, but is %s", name, getTypeName(requestedType), getTypeName(pOption.option.OptType)) return nil } // check release level if pOption.option.ReleaseLevel > getReleaseLevel() { return nil } return pOption.valueCache } // Has returns whether the given option is set in the perspective. func (p *Perspective) Has(name string) bool { valueCache := p.getPerspectiveValueCache(name, optTypeAny) return valueCache != nil } // GetAsString returns a function that returns the wanted string with high performance. func (p *Perspective) GetAsString(name string) (value string, ok bool) { valueCache := p.getPerspectiveValueCache(name, OptTypeString) if valueCache != nil { return valueCache.stringVal, true } return "", false } // GetAsStringArray returns a function that returns the wanted string with high performance. func (p *Perspective) GetAsStringArray(name string) (value []string, ok bool) { valueCache := p.getPerspectiveValueCache(name, OptTypeStringArray) if valueCache != nil { return valueCache.stringArrayVal, true } return nil, false } // GetAsInt returns a function that returns the wanted int with high performance. func (p *Perspective) GetAsInt(name string) (value int64, ok bool) { valueCache := p.getPerspectiveValueCache(name, OptTypeInt) if valueCache != nil { return valueCache.intVal, true } return 0, false } // GetAsBool returns a function that returns the wanted int with high performance. func (p *Perspective) GetAsBool(name string) (value bool, ok bool) { valueCache := p.getPerspectiveValueCache(name, OptTypeBool) if valueCache != nil { return valueCache.boolVal, true } return false, false } ================================================ FILE: base/config/registry.go ================================================ package config import ( "fmt" "regexp" "sort" "strings" "sync" ) var ( optionsLock sync.RWMutex options = make(map[string]*Option) ) // ForEachOption calls fn for each defined option. If fn returns // and error the iteration is stopped and the error is returned. // Note that ForEachOption does not guarantee a stable order of // iteration between multiple calles. ForEachOption does NOT lock // opt when calling fn. func ForEachOption(fn func(opt *Option) error) error { optionsLock.RLock() defer optionsLock.RUnlock() for _, opt := range options { if err := fn(opt); err != nil { return err } } return nil } // ExportOptions exports the registered options. The returned data must be // treated as immutable. // The data does not include the current active or default settings. func ExportOptions() []*Option { optionsLock.RLock() defer optionsLock.RUnlock() // Copy the map into a slice. opts := make([]*Option, 0, len(options)) for _, opt := range options { opts = append(opts, opt) } sort.Sort(sortByKey(opts)) return opts } // GetOption returns the option with name or an error // if the option does not exist. The caller should lock // the returned option itself for further processing. func GetOption(name string) (*Option, error) { optionsLock.RLock() defer optionsLock.RUnlock() opt, ok := options[name] if !ok { return nil, fmt.Errorf("option %q does not exist", name) } return opt, nil } // Register registers a new configuration option. // Note: Option must be registered before initial loading config from disk. func Register(option *Option) error { if option.Name == "" { return fmt.Errorf("failed to register option: please set option.Name") } if option.Key == "" { return fmt.Errorf("failed to register option: please set option.Key") } if option.Description == "" { return fmt.Errorf("failed to register option: please set option.Description") } if option.OptType == 0 { return fmt.Errorf("failed to register option: please set option.OptType") } if cfgInitialized.Load() { return fmt.Errorf("cannot register option '%s': config already initialized", option.Key) } if option.ValidationRegex == "" && option.PossibleValues != nil { values := make([]string, len(option.PossibleValues)) for idx, val := range option.PossibleValues { values[idx] = fmt.Sprintf("%v", val.Value) } option.ValidationRegex = fmt.Sprintf("^(%s)$", strings.Join(values, "|")) } var err error if option.ValidationRegex != "" { option.compiledRegex, err = regexp.Compile(option.ValidationRegex) if err != nil { return fmt.Errorf("config: could not compile option.ValidationRegex: %w", err) } } var vErr *ValidationError option.activeFallbackValue, vErr = validateValue(option, option.DefaultValue) if vErr != nil { return fmt.Errorf("config: invalid default value: %w", vErr) } optionsLock.Lock() defer optionsLock.Unlock() options[option.Key] = option return nil } ================================================ FILE: base/config/registry_test.go ================================================ package config import ( "testing" ) func TestRegistry(t *testing.T) { //nolint:paralleltest // reset options = make(map[string]*Option) if err := Register(&Option{ Name: "name", Key: "key", Description: "description", ReleaseLevel: ReleaseLevelStable, ExpertiseLevel: ExpertiseLevelUser, OptType: OptTypeString, DefaultValue: "water", ValidationRegex: "^(banana|water)$", }); err != nil { t.Error(err) } if err := Register(&Option{ Name: "name", Key: "key", Description: "description", ReleaseLevel: ReleaseLevelStable, ExpertiseLevel: ExpertiseLevelUser, OptType: 0, DefaultValue: "default", ValidationRegex: "^[A-Z][a-z]+$", }); err == nil { t.Error("should fail") } if err := Register(&Option{ Name: "name", Key: "key", Description: "description", ReleaseLevel: ReleaseLevelStable, ExpertiseLevel: ExpertiseLevelUser, OptType: OptTypeString, DefaultValue: "default", ValidationRegex: "[", }); err == nil { t.Error("should fail") } } ================================================ FILE: base/config/release.go ================================================ package config import ( "sync/atomic" "github.com/tevino/abool" ) // ReleaseLevel is used to define the maturity of a // configuration setting. type ReleaseLevel uint8 // Release Level constants. const ( ReleaseLevelStable ReleaseLevel = 0 ReleaseLevelBeta ReleaseLevel = 1 ReleaseLevelExperimental ReleaseLevel = 2 ReleaseLevelNameStable = "stable" ReleaseLevelNameBeta = "beta" ReleaseLevelNameExperimental = "experimental" releaseLevelKey = "core/releaseLevel" ) var ( releaseLevel = new(int32) releaseLevelOption *Option releaseLevelOptionFlag = abool.New() ) func init() { registerReleaseLevelOption() } func registerReleaseLevelOption() { releaseLevelOption = &Option{ Name: "Feature Stability", Key: releaseLevelKey, Description: `May break things. Decide if you want to experiment with unstable features. "Beta" has been tested roughly by the Safing team while "Experimental" is really raw. When "Beta" or "Experimental" are disabled, their settings use the default again.`, OptType: OptTypeString, ExpertiseLevel: ExpertiseLevelDeveloper, ReleaseLevel: ReleaseLevelStable, DefaultValue: ReleaseLevelNameStable, Annotations: Annotations{ DisplayOrderAnnotation: -8, DisplayHintAnnotation: DisplayHintOneOf, CategoryAnnotation: "Updates", }, PossibleValues: []PossibleValue{ { Name: "Stable", Value: ReleaseLevelNameStable, Description: "Only show stable features.", }, { Name: "Beta", Value: ReleaseLevelNameBeta, Description: "Show stable and beta features.", }, { Name: "Experimental", Value: ReleaseLevelNameExperimental, Description: "Show all features", }, }, } err := Register(releaseLevelOption) if err != nil { panic(err) } releaseLevelOptionFlag.Set() } func updateReleaseLevel() { // get value value := releaseLevelOption.activeFallbackValue if releaseLevelOption.activeValue != nil { value = releaseLevelOption.activeValue } if releaseLevelOption.activeDefaultValue != nil { value = releaseLevelOption.activeDefaultValue } // set atomic value switch value.stringVal { case ReleaseLevelNameStable: atomic.StoreInt32(releaseLevel, int32(ReleaseLevelStable)) case ReleaseLevelNameBeta: atomic.StoreInt32(releaseLevel, int32(ReleaseLevelBeta)) case ReleaseLevelNameExperimental: atomic.StoreInt32(releaseLevel, int32(ReleaseLevelExperimental)) default: atomic.StoreInt32(releaseLevel, int32(ReleaseLevelStable)) } } func getReleaseLevel() ReleaseLevel { return ReleaseLevel(atomic.LoadInt32(releaseLevel)) } ================================================ FILE: base/config/set.go ================================================ package config import ( "errors" "sync" "github.com/tevino/abool" ) var ( // ErrInvalidJSON is returned by SetConfig and SetDefaultConfig if they receive invalid json. ErrInvalidJSON = errors.New("json string invalid") // ErrInvalidOptionType is returned by SetConfigOption and SetDefaultConfigOption if given an unsupported option type. ErrInvalidOptionType = errors.New("invalid option value type") validityFlag = abool.NewBool(true) validityFlagLock sync.RWMutex ) // getValidityFlag returns a flag that signifies if the configuration has been changed. This flag must not be changed, only read. func getValidityFlag() *abool.AtomicBool { validityFlagLock.RLock() defer validityFlagLock.RUnlock() return validityFlag } // signalChanges marks the configs validtityFlag as dirty and eventually // triggers a config change event. func signalChanges() { // reset validity flag validityFlagLock.Lock() validityFlag.SetTo(false) validityFlag = abool.NewBool(true) validityFlagLock.Unlock() module.EventConfigChange.Submit(struct{}{}) } // ValidateConfig validates the given configuration and returns all validation // errors as well as whether the given configuration contains unknown keys. func ValidateConfig(newValues map[string]interface{}) (validationErrors []*ValidationError, requiresRestart bool, containsUnknown bool) { // RLock the options because we are not adding or removing // options from the registration but rather only checking the // options value which is guarded by the option's lock itself. optionsLock.RLock() defer optionsLock.RUnlock() var checked int for key, option := range options { newValue, ok := newValues[key] if ok { checked++ func() { option.Lock() defer option.Unlock() newValue = migrateValue(option, newValue) _, err := validateValue(option, newValue) if err != nil { validationErrors = append(validationErrors, err) } if option.RequiresRestart { requiresRestart = true } }() } } return validationErrors, requiresRestart, checked < len(newValues) } // ReplaceConfig sets the (prioritized) user defined config. func ReplaceConfig(newValues map[string]interface{}) (validationErrors []*ValidationError, requiresRestart bool) { // RLock the options because we are not adding or removing // options from the registration but rather only update the // options value which is guarded by the option's lock itself. optionsLock.RLock() defer optionsLock.RUnlock() for key, option := range options { newValue, ok := newValues[key] func() { option.Lock() defer option.Unlock() option.activeValue = nil if ok { newValue = migrateValue(option, newValue) valueCache, err := validateValue(option, newValue) if err == nil { option.activeValue = valueCache } else { validationErrors = append(validationErrors, err) } } handleOptionUpdate(option, true) if option.RequiresRestart { requiresRestart = true } }() } signalChanges() return validationErrors, requiresRestart } // ReplaceDefaultConfig sets the (fallback) default config. func ReplaceDefaultConfig(newValues map[string]interface{}) (validationErrors []*ValidationError, requiresRestart bool) { // RLock the options because we are not adding or removing // options from the registration but rather only update the // options value which is guarded by the option's lock itself. optionsLock.RLock() defer optionsLock.RUnlock() for key, option := range options { newValue, ok := newValues[key] func() { option.Lock() defer option.Unlock() option.activeDefaultValue = nil if ok { newValue = migrateValue(option, newValue) valueCache, err := validateValue(option, newValue) if err == nil { option.activeDefaultValue = valueCache } else { validationErrors = append(validationErrors, err) } } handleOptionUpdate(option, true) if option.RequiresRestart { requiresRestart = true } }() } signalChanges() return validationErrors, requiresRestart } // SetConfigOption sets a single value in the (prioritized) user defined config. func SetConfigOption(key string, value any) error { return setConfigOption(key, value, true) } func setConfigOption(key string, value any, push bool) (err error) { option, err := GetOption(key) if err != nil { return err } option.Lock() if value == nil { option.activeValue = nil } else { value = migrateValue(option, value) valueCache, vErr := validateValue(option, value) if vErr == nil { option.activeValue = valueCache } else { err = vErr } } // Add the "restart pending" annotation if the settings requires a restart. if option.RequiresRestart { option.setAnnotation(RestartPendingAnnotation, true) } handleOptionUpdate(option, push) option.Unlock() if err != nil { return err } // finalize change, activate triggers signalChanges() return SaveConfig() } // SetDefaultConfigOption sets a single value in the (fallback) default config. func SetDefaultConfigOption(key string, value interface{}) error { return setDefaultConfigOption(key, value, true) } func setDefaultConfigOption(key string, value interface{}, push bool) (err error) { option, err := GetOption(key) if err != nil { return err } option.Lock() if value == nil { option.activeDefaultValue = nil } else { value = migrateValue(option, value) valueCache, vErr := validateValue(option, value) if vErr == nil { option.activeDefaultValue = valueCache } else { err = vErr } } // Add the "restart pending" annotation if the settings requires a restart. if option.RequiresRestart { option.setAnnotation(RestartPendingAnnotation, true) } handleOptionUpdate(option, push) option.Unlock() if err != nil { return err } // finalize change, activate triggers signalChanges() // Do not save the configuration, as it only saves the active values, not the // active default value. return nil } ================================================ FILE: base/config/set_test.go ================================================ //nolint:goconst package config import "testing" func TestLayersGetters(t *testing.T) { //nolint:paralleltest // reset options = make(map[string]*Option) mapData, err := JSONToMap([]byte(` { "monkey": "1", "elephant": 2, "zebras": { "zebra": ["black", "white"], "weird_zebra": ["black", -1] }, "env": { "hot": true } } `)) if err != nil { t.Fatal(err) } validationErrors, _ := ReplaceConfig(mapData) if len(validationErrors) > 0 { t.Fatalf("%d errors, first: %s", len(validationErrors), validationErrors[0].Error()) } // Test missing values missingString := GetAsString("missing", "fallback") if missingString() != "fallback" { t.Error("expected fallback value: fallback") } missingStringArray := GetAsStringArray("missing", []string{"fallback"}) if len(missingStringArray()) != 1 || missingStringArray()[0] != "fallback" { t.Error("expected fallback value: [fallback]") } missingInt := GetAsInt("missing", -1) if missingInt() != -1 { t.Error("expected fallback value: -1") } missingBool := GetAsBool("missing", false) if missingBool() { t.Error("expected fallback value: false") } // Test value mismatch notString := GetAsString("elephant", "fallback") if notString() != "fallback" { t.Error("expected fallback value: fallback") } notStringArray := GetAsStringArray("elephant", []string{"fallback"}) if len(notStringArray()) != 1 || notStringArray()[0] != "fallback" { t.Error("expected fallback value: [fallback]") } mixedStringArray := GetAsStringArray("zebras/weird_zebra", []string{"fallback"}) if len(mixedStringArray()) != 1 || mixedStringArray()[0] != "fallback" { t.Error("expected fallback value: [fallback]") } notInt := GetAsInt("monkey", -1) if notInt() != -1 { t.Error("expected fallback value: -1") } notBool := GetAsBool("monkey", false) if notBool() { t.Error("expected fallback value: false") } } func TestLayersSetters(t *testing.T) { //nolint:paralleltest // reset options = make(map[string]*Option) _ = Register(&Option{ Name: "name", Key: "monkey", Description: "description", ReleaseLevel: ReleaseLevelStable, ExpertiseLevel: ExpertiseLevelUser, OptType: OptTypeString, DefaultValue: "banana", ValidationRegex: "^(banana|water)$", }) _ = Register(&Option{ Name: "name", Key: "zebras/zebra", Description: "description", ReleaseLevel: ReleaseLevelStable, ExpertiseLevel: ExpertiseLevelUser, OptType: OptTypeStringArray, DefaultValue: []string{"black", "white"}, ValidationRegex: "^[a-z]+$", }) _ = Register(&Option{ Name: "name", Key: "elephant", Description: "description", ReleaseLevel: ReleaseLevelStable, ExpertiseLevel: ExpertiseLevelUser, OptType: OptTypeInt, DefaultValue: 2, ValidationRegex: "", }) _ = Register(&Option{ Name: "name", Key: "hot", Description: "description", ReleaseLevel: ReleaseLevelStable, ExpertiseLevel: ExpertiseLevelUser, OptType: OptTypeBool, DefaultValue: true, ValidationRegex: "", }) // correct types if err := SetConfigOption("monkey", "banana"); err != nil { t.Error(err) } if err := SetConfigOption("zebras/zebra", []string{"black", "white"}); err != nil { t.Error(err) } if err := SetDefaultConfigOption("elephant", 2); err != nil { t.Error(err) } if err := SetDefaultConfigOption("hot", true); err != nil { t.Error(err) } // incorrect types if err := SetConfigOption("monkey", []string{"black", "white"}); err == nil { t.Error("should fail") } if err := SetConfigOption("zebras/zebra", 2); err == nil { t.Error("should fail") } if err := SetDefaultConfigOption("elephant", true); err == nil { t.Error("should fail") } if err := SetDefaultConfigOption("hot", "banana"); err == nil { t.Error("should fail") } if err := SetDefaultConfigOption("hot", []byte{0}); err == nil { t.Error("should fail") } // validation fail if err := SetConfigOption("monkey", "dirt"); err == nil { t.Error("should fail") } if err := SetConfigOption("zebras/zebra", []string{"Element649"}); err == nil { t.Error("should fail") } // unregistered checking if err := SetConfigOption("invalid", "banana"); err == nil { t.Error("should fail") } if err := SetConfigOption("invalid", []string{"black", "white"}); err == nil { t.Error("should fail") } if err := SetConfigOption("invalid", 2); err == nil { t.Error("should fail") } if err := SetConfigOption("invalid", true); err == nil { t.Error("should fail") } if err := SetConfigOption("invalid", []byte{0}); err == nil { t.Error("should fail") } // delete if err := SetConfigOption("monkey", nil); err != nil { t.Error(err) } if err := SetDefaultConfigOption("elephant", nil); err != nil { t.Error(err) } if err := SetDefaultConfigOption("invalid_delete", nil); err == nil { t.Error("should fail") } } ================================================ FILE: base/config/validate.go ================================================ package config import ( "errors" "fmt" "math" "reflect" "github.com/safing/portmaster/base/log" ) type valueCache struct { stringVal string stringArrayVal []string intVal int64 boolVal bool } func (vc *valueCache) getData(opt *Option) interface{} { switch opt.OptType { case OptTypeBool: return vc.boolVal case OptTypeInt: return vc.intVal case OptTypeString: return vc.stringVal case OptTypeStringArray: return vc.stringArrayVal case optTypeAny: return nil default: return nil } } // isAllowedPossibleValue checks if value is defined as a PossibleValue // in opt. If there are not possible values defined value is considered // allowed and nil is returned. isAllowedPossibleValue ensure the actual // value is an allowed primitiv value by using reflection to convert // value and each PossibleValue to a comparable primitiv if possible. // In case of complex value types isAllowedPossibleValue uses // reflect.DeepEqual as a fallback. func isAllowedPossibleValue(opt *Option, value interface{}) error { if opt.PossibleValues == nil { return nil } for _, val := range opt.PossibleValues { compareAgainst := val.Value valueType := reflect.TypeOf(value) // loading int's from the configuration JSON does not preserve the correct type // as we get float64 instead. Make sure to convert them before. if reflect.TypeOf(val.Value).ConvertibleTo(valueType) { compareAgainst = reflect.ValueOf(val.Value).Convert(valueType).Interface() } if compareAgainst == value { return nil } if reflect.DeepEqual(val.Value, value) { return nil } } return errors.New("value is not allowed") } // migrateValue runs all value migrations. func migrateValue(option *Option, value any) any { for _, migration := range option.Migrations { newValue := migration(option, value) if newValue != value { log.Debugf("config: migrated %s value from %v to %v", option.Key, value, newValue) } value = newValue } return value } // validateValue ensures that value matches the expected type of option. // It does not create a copy of the value! func validateValue(option *Option, value interface{}) (*valueCache, *ValidationError) { //nolint:gocyclo if option.OptType != OptTypeStringArray { if err := isAllowedPossibleValue(option, value); err != nil { return nil, &ValidationError{ Option: option.copyOrNil(), Err: err, } } } var validated *valueCache switch v := value.(type) { case string: if option.OptType != OptTypeString { return nil, invalid(option, "expected type %s, got type %T", getTypeName(option.OptType), v) } if option.compiledRegex != nil { if !option.compiledRegex.MatchString(v) { return nil, invalid(option, "did not match validation regex") } } validated = &valueCache{stringVal: v} case []interface{}: vConverted := make([]string, len(v)) for pos, entry := range v { s, ok := entry.(string) if !ok { return nil, invalid(option, "entry #%d is not a string", pos+1) } vConverted[pos] = s } // Call validation function again with converted value. var vErr *ValidationError validated, vErr = validateValue(option, vConverted) if vErr != nil { return nil, vErr } case []string: if option.OptType != OptTypeStringArray { return nil, invalid(option, "expected type %s, got type %T", getTypeName(option.OptType), v) } if option.compiledRegex != nil { for pos, entry := range v { if !option.compiledRegex.MatchString(entry) { return nil, invalid(option, "entry #%d did not match validation regex", pos+1) } if err := isAllowedPossibleValue(option, entry); err != nil { return nil, invalid(option, "entry #%d is not allowed", pos+1) } } } validated = &valueCache{stringArrayVal: v} case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, float32, float64: // uint64 is omitted, as it does not fit in a int64 if option.OptType != OptTypeInt { return nil, invalid(option, "expected type %s, got type %T", getTypeName(option.OptType), v) } if option.compiledRegex != nil { // we need to use %v here so we handle float and int correctly. if !option.compiledRegex.MatchString(fmt.Sprintf("%v", v)) { return nil, invalid(option, "did not match validation regex") } } switch v := value.(type) { case int: validated = &valueCache{intVal: int64(v)} case int8: validated = &valueCache{intVal: int64(v)} case int16: validated = &valueCache{intVal: int64(v)} case int32: validated = &valueCache{intVal: int64(v)} case int64: validated = &valueCache{intVal: v} case uint: validated = &valueCache{intVal: int64(v)} case uint8: validated = &valueCache{intVal: int64(v)} case uint16: validated = &valueCache{intVal: int64(v)} case uint32: validated = &valueCache{intVal: int64(v)} case float32: // convert if float has no decimals if math.Remainder(float64(v), 1) == 0 { validated = &valueCache{intVal: int64(v)} } else { return nil, invalid(option, "failed to convert float32 to int64") } case float64: // convert if float has no decimals if math.Remainder(v, 1) == 0 { validated = &valueCache{intVal: int64(v)} } else { return nil, invalid(option, "failed to convert float64 to int64") } default: return nil, invalid(option, "internal error") } case bool: if option.OptType != OptTypeBool { return nil, invalid(option, "expected type %s, got type %T", getTypeName(option.OptType), v) } validated = &valueCache{boolVal: v} default: return nil, invalid(option, "invalid option value type: %T", value) } // Check if there is an additional function to validate the value. if option.ValidationFunc != nil { var err error switch option.OptType { case optTypeAny: err = errors.New("internal error") case OptTypeString: err = option.ValidationFunc(validated.stringVal) case OptTypeStringArray: err = option.ValidationFunc(validated.stringArrayVal) case OptTypeInt: err = option.ValidationFunc(validated.intVal) case OptTypeBool: err = option.ValidationFunc(validated.boolVal) } if err != nil { return nil, &ValidationError{ Option: option.copyOrNil(), Err: err, } } } return validated, nil } // ValidationError error holds details about a config option value validation error. type ValidationError struct { Option *Option Err error } // Error returns the formatted error. func (ve *ValidationError) Error() string { return fmt.Sprintf("validation of %s failed: %s", ve.Option.Key, ve.Err) } // Unwrap returns the wrapped error. func (ve *ValidationError) Unwrap() error { return ve.Err } func invalid(option *Option, format string, a ...interface{}) *ValidationError { return &ValidationError{ Option: option.copyOrNil(), Err: fmt.Errorf(format, a...), } } ================================================ FILE: base/config/validity.go ================================================ package config import ( "github.com/tevino/abool" ) // ValidityFlag is a flag that signifies if the configuration has been changed. It is not safe for concurrent use. type ValidityFlag struct { flag *abool.AtomicBool } // NewValidityFlag returns a flag that signifies if the configuration has been changed. // It always starts out as invalid. Refresh to start with the current value. func NewValidityFlag() *ValidityFlag { vf := &ValidityFlag{ flag: abool.New(), } return vf } // IsValid returns if the configuration is still valid. func (vf *ValidityFlag) IsValid() bool { return vf.flag.IsSet() } // Refresh refreshes the flag and makes it reusable. func (vf *ValidityFlag) Refresh() { validityFlagLock.RLock() defer validityFlagLock.RUnlock() vf.flag = validityFlag } ================================================ FILE: base/container/container.go ================================================ package container import ( "errors" "io" "github.com/safing/structures/varint" ) // Container is []byte sclie on steroids, allowing for quick data appending, prepending and fetching. type Container struct { compartments [][]byte offset int err error } // Data Handling // NewContainer is DEPRECATED, please use New(), it's the same thing. func NewContainer(data ...[]byte) *Container { return &Container{ compartments: data, } } // New creates a new container with an optional initial []byte slice. Data will NOT be copied. func New(data ...[]byte) *Container { return &Container{ compartments: data, } } // Prepend prepends data. Data will NOT be copied. func (c *Container) Prepend(data []byte) { if c.offset < 1 { c.renewCompartments() } c.offset-- c.compartments[c.offset] = data } // Append appends the given data. Data will NOT be copied. func (c *Container) Append(data []byte) { c.compartments = append(c.compartments, data) } // PrependNumber prepends a number (varint encoded). func (c *Container) PrependNumber(n uint64) { c.Prepend(varint.Pack64(n)) } // AppendNumber appends a number (varint encoded). func (c *Container) AppendNumber(n uint64) { c.compartments = append(c.compartments, varint.Pack64(n)) } // PrependInt prepends an int (varint encoded). func (c *Container) PrependInt(n int) { c.Prepend(varint.Pack64(uint64(n))) } // AppendInt appends an int (varint encoded). func (c *Container) AppendInt(n int) { c.compartments = append(c.compartments, varint.Pack64(uint64(n))) } // AppendAsBlock appends the length of the data and the data itself. Data will NOT be copied. func (c *Container) AppendAsBlock(data []byte) { c.AppendNumber(uint64(len(data))) c.Append(data) } // PrependAsBlock prepends the length of the data and the data itself. Data will NOT be copied. func (c *Container) PrependAsBlock(data []byte) { c.Prepend(data) c.PrependNumber(uint64(len(data))) } // AppendContainer appends another Container. Data will NOT be copied. func (c *Container) AppendContainer(data *Container) { c.compartments = append(c.compartments, data.compartments...) } // AppendContainerAsBlock appends another Container (length and data). Data will NOT be copied. func (c *Container) AppendContainerAsBlock(data *Container) { c.AppendNumber(uint64(data.Length())) c.compartments = append(c.compartments, data.compartments...) } // HoldsData returns true if the Container holds any data. func (c *Container) HoldsData() bool { for i := c.offset; i < len(c.compartments); i++ { if len(c.compartments[i]) > 0 { return true } } return false } // Length returns the full length of all bytes held by the container. func (c *Container) Length() (length int) { for i := c.offset; i < len(c.compartments); i++ { length += len(c.compartments[i]) } return } // Replace replaces all held data with a new data slice. Data will NOT be copied. func (c *Container) Replace(data []byte) { c.compartments = [][]byte{data} } // CompileData concatenates all bytes held by the container and returns it as one single []byte slice. Data will NOT be copied and is NOT consumed. func (c *Container) CompileData() []byte { if len(c.compartments) != 1 { newBuf := make([]byte, c.Length()) copyBuf := newBuf for i := c.offset; i < len(c.compartments); i++ { copy(copyBuf, c.compartments[i]) copyBuf = copyBuf[len(c.compartments[i]):] } c.compartments = [][]byte{newBuf} c.offset = 0 } return c.compartments[0] } // Get returns the given amount of bytes. Data MAY be copied and IS consumed. func (c *Container) Get(n int) ([]byte, error) { buf := c.Peek(n) if len(buf) < n { return nil, errors.New("container: not enough data to return") } c.skip(len(buf)) return buf, nil } // GetAll returns all data. Data MAY be copied and IS consumed. func (c *Container) GetAll() []byte { // TODO: Improve. buf := c.Peek(c.Length()) c.skip(len(buf)) return buf } // GetAsContainer returns the given amount of bytes in a new container. Data will NOT be copied and IS consumed. func (c *Container) GetAsContainer(n int) (*Container, error) { newC := c.PeekContainer(n) if newC == nil { return nil, errors.New("container: not enough data to return") } c.skip(n) return newC, nil } // GetMax returns as much as possible, but the given amount of bytes at maximum. Data MAY be copied and IS consumed. func (c *Container) GetMax(n int) []byte { buf := c.Peek(n) c.skip(len(buf)) return buf } // WriteToSlice copies data to the give slice until it is full, or the container is empty. It returns the bytes written and if the container is now empty. Data IS copied and IS consumed. func (c *Container) WriteToSlice(slice []byte) (n int, containerEmptied bool) { for i := c.offset; i < len(c.compartments); i++ { copy(slice, c.compartments[i]) if len(slice) < len(c.compartments[i]) { // only part was copied n += len(slice) c.compartments[i] = c.compartments[i][len(slice):] c.checkOffset() return n, false } // all was copied n += len(c.compartments[i]) slice = slice[len(c.compartments[i]):] c.compartments[i] = nil c.offset = i + 1 } c.checkOffset() return n, true } // WriteAllTo writes all the data to the given io.Writer. Data IS NOT copied (but may be by writer) and IS NOT consumed. func (c *Container) WriteAllTo(writer io.Writer) error { for i := c.offset; i < len(c.compartments); i++ { written := 0 for written < len(c.compartments[i]) { n, err := writer.Write(c.compartments[i][written:]) if err != nil { return err } written += n } } return nil } func (c *Container) clean() { if c.offset > 100 { c.renewCompartments() } } func (c *Container) renewCompartments() { baseLength := len(c.compartments) - c.offset + 5 newCompartments := make([][]byte, baseLength, baseLength+5) copy(newCompartments[5:], c.compartments[c.offset:]) c.compartments = newCompartments c.offset = 4 } func (c *Container) carbonCopy() *Container { newC := &Container{ compartments: make([][]byte, len(c.compartments)), offset: c.offset, err: c.err, } copy(newC.compartments, c.compartments) return newC } func (c *Container) checkOffset() { if c.offset >= len(c.compartments) { c.offset = len(c.compartments) / 2 } } // Block Handling // PrependLength prepends the current full length of all bytes in the container. func (c *Container) PrependLength() { c.Prepend(varint.Pack64(uint64(c.Length()))) } // Peek returns the given amount of bytes. Data MAY be copied and IS NOT consumed. func (c *Container) Peek(n int) []byte { // Check requested length. if n <= 0 { return nil } // Check if the first slice holds enough data. if len(c.compartments[c.offset]) >= n { return c.compartments[c.offset][:n] } // Start gathering data. slice := make([]byte, n) copySlice := slice n = 0 for i := c.offset; i < len(c.compartments); i++ { copy(copySlice, c.compartments[i]) if len(copySlice) <= len(c.compartments[i]) { n += len(copySlice) return slice[:n] } n += len(c.compartments[i]) copySlice = copySlice[len(c.compartments[i]):] } return slice[:n] } // PeekContainer returns the given amount of bytes in a new container. Data will NOT be copied and IS NOT consumed. func (c *Container) PeekContainer(n int) (newC *Container) { // Check requested length. if n < 0 { return nil } else if n == 0 { return &Container{} } newC = &Container{} for i := c.offset; i < len(c.compartments); i++ { if n >= len(c.compartments[i]) { newC.compartments = append(newC.compartments, c.compartments[i]) n -= len(c.compartments[i]) } else { newC.compartments = append(newC.compartments, c.compartments[i][:n]) n = 0 } } if n > 0 { return nil } return newC } func (c *Container) skip(n int) { for i := c.offset; i < len(c.compartments); i++ { if len(c.compartments[i]) <= n { n -= len(c.compartments[i]) c.offset = i + 1 c.compartments[i] = nil if n == 0 { c.checkOffset() return } } else { c.compartments[i] = c.compartments[i][n:] c.checkOffset() return } } c.checkOffset() } // GetNextBlock returns the next block of data defined by a varint. Data MAY be copied and IS consumed. func (c *Container) GetNextBlock() ([]byte, error) { blockSize, err := c.GetNextN64() if err != nil { return nil, err } return c.Get(int(blockSize)) } // GetNextBlockAsContainer returns the next block of data as a Container defined by a varint. Data will NOT be copied and IS consumed. func (c *Container) GetNextBlockAsContainer() (*Container, error) { blockSize, err := c.GetNextN64() if err != nil { return nil, err } return c.GetAsContainer(int(blockSize)) } // GetNextN8 parses and returns a varint of type uint8. func (c *Container) GetNextN8() (uint8, error) { buf := c.Peek(2) num, n, err := varint.Unpack8(buf) if err != nil { return 0, err } c.skip(n) return num, nil } // GetNextN16 parses and returns a varint of type uint16. func (c *Container) GetNextN16() (uint16, error) { buf := c.Peek(3) num, n, err := varint.Unpack16(buf) if err != nil { return 0, err } c.skip(n) return num, nil } // GetNextN32 parses and returns a varint of type uint32. func (c *Container) GetNextN32() (uint32, error) { buf := c.Peek(5) num, n, err := varint.Unpack32(buf) if err != nil { return 0, err } c.skip(n) return num, nil } // GetNextN64 parses and returns a varint of type uint64. func (c *Container) GetNextN64() (uint64, error) { buf := c.Peek(10) num, n, err := varint.Unpack64(buf) if err != nil { return 0, err } c.skip(n) return num, nil } ================================================ FILE: base/container/container_test.go ================================================ package container import ( "bytes" "testing" "github.com/safing/portmaster/base/utils" ) var ( testData = []byte("The quick brown fox jumps over the lazy dog") testDataSplitted = [][]byte{ []byte("T"), []byte("he"), []byte(" qu"), []byte("ick "), []byte("brown"), []byte(" fox j"), []byte("umps ov"), []byte("er the l"), []byte("azy dog"), } ) func TestContainerDataHandling(t *testing.T) { t.Parallel() c1 := New(utils.DuplicateBytes(testData)) c1c := c1.carbonCopy() c2 := New() for range len(testData) { oneByte := make([]byte, 1) c1c.WriteToSlice(oneByte) c2.Append(oneByte) } c2c := c2.carbonCopy() c3 := New() for i := len(c2c.compartments) - 1; i >= c2c.offset; i-- { c3.Prepend(c2c.compartments[i]) } c3c := c3.carbonCopy() d4 := make([]byte, len(testData)*2) n, _ := c3c.WriteToSlice(d4) d4 = d4[:n] c3c = c3.carbonCopy() d5 := make([]byte, len(testData)) for i := range len(testData) { c3c.WriteToSlice(d5[i : i+1]) } c6 := New() c6.Replace(testData) c7 := New(testDataSplitted[0]) for i := 1; i < len(testDataSplitted); i++ { c7.Append(testDataSplitted[i]) } c8 := New(testDataSplitted...) for range 110 { c8.Prepend(nil) } c8.clean() c9 := c8.PeekContainer(len(testData)) c10 := c9.PeekContainer(len(testData) - 1) c10.Append(testData[len(testData)-1:]) compareMany(t, testData, c1.CompileData(), c2.CompileData(), c3.CompileData(), d4, d5, c6.CompileData(), c7.CompileData(), c8.CompileData(), c9.CompileData(), c10.CompileData()) } func compareMany(t *testing.T, reference []byte, other ...[]byte) { t.Helper() for i, cmp := range other { if !bytes.Equal(reference, cmp) { t.Errorf("sample %d does not match reference: sample is '%s'", i+1, string(cmp)) } } } func TestDataFetching(t *testing.T) { t.Parallel() c1 := New(utils.DuplicateBytes(testData)) data := c1.GetMax(1) if string(data[0]) != "T" { t.Errorf("failed to GetMax(1), got %s, expected %s", string(data), "T") } _, err := c1.Get(1000) if err == nil { t.Error("should fail") } _, err = c1.GetAsContainer(1000) if err == nil { t.Error("should fail") } } func TestBlocks(t *testing.T) { t.Parallel() c1 := New(utils.DuplicateBytes(testData)) c1.PrependLength() n, err := c1.GetNextN8() if err != nil { t.Errorf("GetNextN8() failed: %s", err) } if n != 43 { t.Errorf("n should be 43, was %d", n) } c1.PrependLength() n2, err := c1.GetNextN16() if err != nil { t.Errorf("GetNextN16() failed: %s", err) } if n2 != 43 { t.Errorf("n should be 43, was %d", n2) } c1.PrependLength() n3, err := c1.GetNextN32() if err != nil { t.Errorf("GetNextN32() failed: %s", err) } if n3 != 43 { t.Errorf("n should be 43, was %d", n3) } c1.PrependLength() n4, err := c1.GetNextN64() if err != nil { t.Errorf("GetNextN64() failed: %s", err) } if n4 != 43 { t.Errorf("n should be 43, was %d", n4) } } func TestContainerBlockHandling(t *testing.T) { t.Parallel() c1 := New(utils.DuplicateBytes(testData)) c1.PrependLength() c1.AppendAsBlock(testData) c1c := c1.carbonCopy() c2 := New(nil) for range c1.Length() { oneByte := make([]byte, 1) c1c.WriteToSlice(oneByte) c2.Append(oneByte) } c3 := New(testDataSplitted[0]) for i := 1; i < len(testDataSplitted); i++ { c3.Append(testDataSplitted[i]) } c3.PrependLength() d1, err := c1.GetNextBlock() if err != nil { t.Errorf("GetNextBlock failed: %s", err) } d2, err := c1.GetNextBlock() if err != nil { t.Errorf("GetNextBlock failed: %s", err) } d3, err := c2.GetNextBlock() if err != nil { t.Errorf("GetNextBlock failed: %s", err) } d4, err := c2.GetNextBlock() if err != nil { t.Errorf("GetNextBlock failed: %s", err) } d5, err := c3.GetNextBlock() if err != nil { t.Errorf("GetNextBlock failed: %s", err) } compareMany(t, testData, d1, d2, d3, d4, d5) } func TestContainerMisc(t *testing.T) { t.Parallel() c1 := New() d1 := c1.CompileData() if len(d1) > 0 { t.Fatalf("empty container should not hold any data") } } func TestDeprecated(t *testing.T) { t.Parallel() NewContainer(utils.DuplicateBytes(testData)) } ================================================ FILE: base/container/doc.go ================================================ // Package container gives you a []byte slice on steroids, allowing for quick data appending, prepending and fetching as well as transparent error transportation. // // A Container is basically a [][]byte slice that just appends new []byte slices and only copies things around when necessary. // // Byte slices added to the Container are not changed or appended, to not corrupt any other data that may be before and after the given slice. // If interested, consider the following example to understand why this is important: // // package main // // import ( // "fmt" // ) // // func main() { // a := []byte{0, 1,2,3,4,5,6,7,8,9} // fmt.Printf("a: %+v\n", a) // fmt.Printf("\nmaking changes...\n(we are not changing a directly)\n\n") // b := a[2:6] // c := append(b, 10, 11) // fmt.Printf("b: %+v\n", b) // fmt.Printf("c: %+v\n", c) // fmt.Printf("a: %+v\n", a) // } // // run it here: https://play.golang.org/p/xu1BXT3QYeE package container ================================================ FILE: base/container/serialization.go ================================================ package container import ( "encoding/json" ) // MarshalJSON serializes the container as a JSON byte array. func (c *Container) MarshalJSON() ([]byte, error) { return json.Marshal(c.CompileData()) } // UnmarshalJSON unserializes a container from a JSON byte array. func (c *Container) UnmarshalJSON(data []byte) error { var raw []byte if err := json.Unmarshal(data, &raw); err != nil { return err } c.compartments = [][]byte{raw} return nil } ================================================ FILE: base/database/accessor/accessor-json-bytes.go ================================================ package accessor import ( "github.com/tidwall/gjson" "github.com/tidwall/sjson" ) // JSONBytesAccessor is a json string with get functions. type JSONBytesAccessor struct { json *[]byte } // NewJSONBytesAccessor adds the Accessor interface to a JSON bytes string. func NewJSONBytesAccessor(json *[]byte) *JSONBytesAccessor { return &JSONBytesAccessor{ json: json, } } // Set sets the value identified by key. func (ja *JSONBytesAccessor) Set(key string, value interface{}) error { result := gjson.GetBytes(*ja.json, key) if result.Exists() { err := checkJSONValueType(result, key, value) if err != nil { return err } } newJSON, err := sjson.SetBytes(*ja.json, key, value) if err != nil { return err } *ja.json = newJSON return nil } // Get returns the value found by the given json key and whether it could be successfully extracted. func (ja *JSONBytesAccessor) Get(key string) (value interface{}, ok bool) { result := gjson.GetBytes(*ja.json, key) if !result.Exists() { return nil, false } return result.Value(), true } // GetString returns the string found by the given json key and whether it could be successfully extracted. func (ja *JSONBytesAccessor) GetString(key string) (value string, ok bool) { result := gjson.GetBytes(*ja.json, key) if !result.Exists() || result.Type != gjson.String { return emptyString, false } return result.String(), true } // GetStringArray returns the []string found by the given json key and whether it could be successfully extracted. func (ja *JSONBytesAccessor) GetStringArray(key string) (value []string, ok bool) { result := gjson.GetBytes(*ja.json, key) if !result.Exists() && !result.IsArray() { return nil, false } slice := result.Array() sliceCopy := make([]string, len(slice)) for i, res := range slice { if res.Type == gjson.String { sliceCopy[i] = res.String() } else { return nil, false } } return sliceCopy, true } // GetInt returns the int found by the given json key and whether it could be successfully extracted. func (ja *JSONBytesAccessor) GetInt(key string) (value int64, ok bool) { result := gjson.GetBytes(*ja.json, key) if !result.Exists() || result.Type != gjson.Number { return 0, false } return result.Int(), true } // GetFloat returns the float found by the given json key and whether it could be successfully extracted. func (ja *JSONBytesAccessor) GetFloat(key string) (value float64, ok bool) { result := gjson.GetBytes(*ja.json, key) if !result.Exists() || result.Type != gjson.Number { return 0, false } return result.Float(), true } // GetBool returns the bool found by the given json key and whether it could be successfully extracted. func (ja *JSONBytesAccessor) GetBool(key string) (value bool, ok bool) { result := gjson.GetBytes(*ja.json, key) switch { case !result.Exists(): return false, false case result.Type == gjson.True: return true, true case result.Type == gjson.False: return false, true default: return false, false } } // Exists returns the whether the given key exists. func (ja *JSONBytesAccessor) Exists(key string) bool { result := gjson.GetBytes(*ja.json, key) return result.Exists() } // Type returns the accessor type as a string. func (ja *JSONBytesAccessor) Type() string { return "JSONBytesAccessor" } ================================================ FILE: base/database/accessor/accessor-json-string.go ================================================ package accessor import ( "fmt" "github.com/tidwall/gjson" "github.com/tidwall/sjson" ) // JSONAccessor is a json string with get functions. type JSONAccessor struct { json *string } // NewJSONAccessor adds the Accessor interface to a JSON string. func NewJSONAccessor(json *string) *JSONAccessor { return &JSONAccessor{ json: json, } } // Set sets the value identified by key. func (ja *JSONAccessor) Set(key string, value interface{}) error { result := gjson.Get(*ja.json, key) if result.Exists() { err := checkJSONValueType(result, key, value) if err != nil { return err } } newJSON, err := sjson.Set(*ja.json, key, value) if err != nil { return err } *ja.json = newJSON return nil } func checkJSONValueType(jsonValue gjson.Result, key string, value interface{}) error { switch value.(type) { case string: if jsonValue.Type != gjson.String { return fmt.Errorf("tried to set field %s (%s) to a %T value", key, jsonValue.Type.String(), value) } case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: if jsonValue.Type != gjson.Number { return fmt.Errorf("tried to set field %s (%s) to a %T value", key, jsonValue.Type.String(), value) } case bool: if jsonValue.Type != gjson.True && jsonValue.Type != gjson.False { return fmt.Errorf("tried to set field %s (%s) to a %T value", key, jsonValue.Type.String(), value) } case []string: if !jsonValue.IsArray() { return fmt.Errorf("tried to set field %s (%s) to a %T value", key, jsonValue.Type.String(), value) } } return nil } // Get returns the value found by the given json key and whether it could be successfully extracted. func (ja *JSONAccessor) Get(key string) (value interface{}, ok bool) { result := gjson.Get(*ja.json, key) if !result.Exists() { return nil, false } return result.Value(), true } // GetString returns the string found by the given json key and whether it could be successfully extracted. func (ja *JSONAccessor) GetString(key string) (value string, ok bool) { result := gjson.Get(*ja.json, key) if !result.Exists() || result.Type != gjson.String { return emptyString, false } return result.String(), true } // GetStringArray returns the []string found by the given json key and whether it could be successfully extracted. func (ja *JSONAccessor) GetStringArray(key string) (value []string, ok bool) { result := gjson.Get(*ja.json, key) if !result.Exists() && !result.IsArray() { return nil, false } slice := result.Array() sliceCopy := make([]string, len(slice)) for i, res := range slice { if res.Type == gjson.String { sliceCopy[i] = res.String() } else { return nil, false } } return sliceCopy, true } // GetInt returns the int found by the given json key and whether it could be successfully extracted. func (ja *JSONAccessor) GetInt(key string) (value int64, ok bool) { result := gjson.Get(*ja.json, key) if !result.Exists() || result.Type != gjson.Number { return 0, false } return result.Int(), true } // GetFloat returns the float found by the given json key and whether it could be successfully extracted. func (ja *JSONAccessor) GetFloat(key string) (value float64, ok bool) { result := gjson.Get(*ja.json, key) if !result.Exists() || result.Type != gjson.Number { return 0, false } return result.Float(), true } // GetBool returns the bool found by the given json key and whether it could be successfully extracted. func (ja *JSONAccessor) GetBool(key string) (value bool, ok bool) { result := gjson.Get(*ja.json, key) switch { case !result.Exists(): return false, false case result.Type == gjson.True: return true, true case result.Type == gjson.False: return false, true default: return false, false } } // Exists returns the whether the given key exists. func (ja *JSONAccessor) Exists(key string) bool { result := gjson.Get(*ja.json, key) return result.Exists() } // Type returns the accessor type as a string. func (ja *JSONAccessor) Type() string { return "JSONAccessor" } ================================================ FILE: base/database/accessor/accessor-struct.go ================================================ package accessor import ( "errors" "fmt" "reflect" ) // StructAccessor is a json string with get functions. type StructAccessor struct { object reflect.Value } // NewStructAccessor adds the Accessor interface to a JSON string. func NewStructAccessor(object interface{}) *StructAccessor { return &StructAccessor{ object: reflect.ValueOf(object).Elem(), } } // Set sets the value identified by key. func (sa *StructAccessor) Set(key string, value interface{}) error { field := sa.object.FieldByName(key) if !field.IsValid() { return errors.New("struct field does not exist") } if !field.CanSet() { return fmt.Errorf("field %s or struct is immutable", field.String()) } newVal := reflect.ValueOf(value) // set directly if type matches if newVal.Kind() == field.Kind() { field.Set(newVal) return nil } // handle special cases switch field.Kind() { // nolint:exhaustive // ints case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var newInt int64 switch newVal.Kind() { // nolint:exhaustive case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: newInt = newVal.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: newInt = int64(newVal.Uint()) default: return fmt.Errorf("tried to set field %s (%s) to a %s value", key, field.Kind().String(), newVal.Kind().String()) } if field.OverflowInt(newInt) { return fmt.Errorf("setting field %s (%s) to %d would overflow", key, field.Kind().String(), newInt) } field.SetInt(newInt) // uints case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: var newUint uint64 switch newVal.Kind() { // nolint:exhaustive case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: newUint = uint64(newVal.Int()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: newUint = newVal.Uint() default: return fmt.Errorf("tried to set field %s (%s) to a %s value", key, field.Kind().String(), newVal.Kind().String()) } if field.OverflowUint(newUint) { return fmt.Errorf("setting field %s (%s) to %d would overflow", key, field.Kind().String(), newUint) } field.SetUint(newUint) // floats case reflect.Float32, reflect.Float64: switch newVal.Kind() { // nolint:exhaustive case reflect.Float32, reflect.Float64: field.SetFloat(newVal.Float()) default: return fmt.Errorf("tried to set field %s (%s) to a %s value", key, field.Kind().String(), newVal.Kind().String()) } default: return fmt.Errorf("tried to set field %s (%s) to a %s value", key, field.Kind().String(), newVal.Kind().String()) } return nil } // Get returns the value found by the given json key and whether it could be successfully extracted. func (sa *StructAccessor) Get(key string) (value interface{}, ok bool) { field := sa.object.FieldByName(key) if !field.IsValid() || !field.CanInterface() { return nil, false } return field.Interface(), true } // GetString returns the string found by the given json key and whether it could be successfully extracted. func (sa *StructAccessor) GetString(key string) (value string, ok bool) { field := sa.object.FieldByName(key) if !field.IsValid() || field.Kind() != reflect.String { return "", false } return field.String(), true } // GetStringArray returns the []string found by the given json key and whether it could be successfully extracted. func (sa *StructAccessor) GetStringArray(key string) (value []string, ok bool) { field := sa.object.FieldByName(key) if !field.IsValid() || field.Kind() != reflect.Slice || !field.CanInterface() { return nil, false } v := field.Interface() slice, ok := v.([]string) if !ok { return nil, false } return slice, true } // GetInt returns the int found by the given json key and whether it could be successfully extracted. func (sa *StructAccessor) GetInt(key string) (value int64, ok bool) { field := sa.object.FieldByName(key) if !field.IsValid() { return 0, false } switch field.Kind() { // nolint:exhaustive case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return field.Int(), true case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return int64(field.Uint()), true default: return 0, false } } // GetFloat returns the float found by the given json key and whether it could be successfully extracted. func (sa *StructAccessor) GetFloat(key string) (value float64, ok bool) { field := sa.object.FieldByName(key) if !field.IsValid() { return 0, false } switch field.Kind() { // nolint:exhaustive case reflect.Float32, reflect.Float64: return field.Float(), true default: return 0, false } } // GetBool returns the bool found by the given json key and whether it could be successfully extracted. func (sa *StructAccessor) GetBool(key string) (value bool, ok bool) { field := sa.object.FieldByName(key) if !field.IsValid() || field.Kind() != reflect.Bool { return false, false } return field.Bool(), true } // Exists returns the whether the given key exists. func (sa *StructAccessor) Exists(key string) bool { field := sa.object.FieldByName(key) return field.IsValid() } // Type returns the accessor type as a string. func (sa *StructAccessor) Type() string { return "StructAccessor" } ================================================ FILE: base/database/accessor/accessor.go ================================================ package accessor const ( emptyString = "" ) // Accessor provides an interface to supply the query matcher a method to retrieve values from an object. type Accessor interface { Get(key string) (value interface{}, ok bool) GetString(key string) (value string, ok bool) GetStringArray(key string) (value []string, ok bool) GetInt(key string) (value int64, ok bool) GetFloat(key string) (value float64, ok bool) GetBool(key string) (value bool, ok bool) Exists(key string) bool Set(key string, value interface{}) error Type() string } ================================================ FILE: base/database/accessor/accessor_test.go ================================================ //nolint:maligned,unparam package accessor import ( "encoding/json" "testing" "github.com/safing/portmaster/base/utils" ) type TestStruct struct { S string A []string I int I8 int8 I16 int16 I32 int32 I64 int64 UI uint UI8 uint8 UI16 uint16 UI32 uint32 UI64 uint64 F32 float32 F64 float64 B bool } var ( testStruct = &TestStruct{ S: "banana", A: []string{"black", "white"}, I: 42, I8: 42, I16: 42, I32: 42, I64: 42, UI: 42, UI8: 42, UI16: 42, UI32: 42, UI64: 42, F32: 42.42, F64: 42.42, B: true, } testJSONBytes, _ = json.Marshal(testStruct) //nolint:errchkjson testJSON = string(testJSONBytes) ) func testGetString(t *testing.T, acc Accessor, key string, shouldSucceed bool, expectedValue string) { t.Helper() v, ok := acc.GetString(key) switch { case !ok && shouldSucceed: t.Errorf("%s failed to get string with key %s", acc.Type(), key) case ok && !shouldSucceed: t.Errorf("%s should have failed to get string with key %s, it returned %v", acc.Type(), key, v) } if v != expectedValue { t.Errorf("%s returned an unexpected value: wanted %v, got %v", acc.Type(), expectedValue, v) } } func testGetStringArray(t *testing.T, acc Accessor, key string, shouldSucceed bool, expectedValue []string) { t.Helper() v, ok := acc.GetStringArray(key) switch { case !ok && shouldSucceed: t.Errorf("%s failed to get []string with key %s", acc.Type(), key) case ok && !shouldSucceed: t.Errorf("%s should have failed to get []string with key %s, it returned %v", acc.Type(), key, v) } if !utils.StringSliceEqual(v, expectedValue) { t.Errorf("%s returned an unexpected value: wanted %v, got %v", acc.Type(), expectedValue, v) } } func testGetInt(t *testing.T, acc Accessor, key string, shouldSucceed bool, expectedValue int64) { t.Helper() v, ok := acc.GetInt(key) switch { case !ok && shouldSucceed: t.Errorf("%s failed to get int with key %s", acc.Type(), key) case ok && !shouldSucceed: t.Errorf("%s should have failed to get int with key %s, it returned %v", acc.Type(), key, v) } if v != expectedValue { t.Errorf("%s returned an unexpected value: wanted %v, got %v", acc.Type(), expectedValue, v) } } func testGetFloat(t *testing.T, acc Accessor, key string, shouldSucceed bool, expectedValue float64) { t.Helper() v, ok := acc.GetFloat(key) switch { case !ok && shouldSucceed: t.Errorf("%s failed to get float with key %s", acc.Type(), key) case ok && !shouldSucceed: t.Errorf("%s should have failed to get float with key %s, it returned %v", acc.Type(), key, v) } if int64(v) != int64(expectedValue) { t.Errorf("%s returned an unexpected value: wanted %v, got %v", acc.Type(), expectedValue, v) } } func testGetBool(t *testing.T, acc Accessor, key string, shouldSucceed bool, expectedValue bool) { t.Helper() v, ok := acc.GetBool(key) switch { case !ok && shouldSucceed: t.Errorf("%s failed to get bool with key %s", acc.Type(), key) case ok && !shouldSucceed: t.Errorf("%s should have failed to get bool with key %s, it returned %v", acc.Type(), key, v) } if v != expectedValue { t.Errorf("%s returned an unexpected value: wanted %v, got %v", acc.Type(), expectedValue, v) } } func testExists(t *testing.T, acc Accessor, key string, shouldSucceed bool) { t.Helper() ok := acc.Exists(key) switch { case !ok && shouldSucceed: t.Errorf("%s should report key %s as existing", acc.Type(), key) case ok && !shouldSucceed: t.Errorf("%s should report key %s as non-existing", acc.Type(), key) } } func testSet(t *testing.T, acc Accessor, key string, shouldSucceed bool, valueToSet interface{}) { t.Helper() err := acc.Set(key, valueToSet) switch { case err != nil && shouldSucceed: t.Errorf("%s failed to set %s to %+v: %s", acc.Type(), key, valueToSet, err) case err == nil && !shouldSucceed: t.Errorf("%s should have failed to set %s to %+v", acc.Type(), key, valueToSet) } } func TestAccessor(t *testing.T) { t.Parallel() // Test interface compliance. accs := []Accessor{ NewJSONAccessor(&testJSON), NewJSONBytesAccessor(&testJSONBytes), NewStructAccessor(testStruct), } // get for _, acc := range accs { testGetString(t, acc, "S", true, "banana") testGetStringArray(t, acc, "A", true, []string{"black", "white"}) testGetInt(t, acc, "I", true, 42) testGetInt(t, acc, "I8", true, 42) testGetInt(t, acc, "I16", true, 42) testGetInt(t, acc, "I32", true, 42) testGetInt(t, acc, "I64", true, 42) testGetInt(t, acc, "UI", true, 42) testGetInt(t, acc, "UI8", true, 42) testGetInt(t, acc, "UI16", true, 42) testGetInt(t, acc, "UI32", true, 42) testGetInt(t, acc, "UI64", true, 42) testGetFloat(t, acc, "F32", true, 42.42) testGetFloat(t, acc, "F64", true, 42.42) testGetBool(t, acc, "B", true, true) } // set for _, acc := range accs { testSet(t, acc, "S", true, "coconut") testSet(t, acc, "A", true, []string{"green", "blue"}) testSet(t, acc, "I", true, uint32(44)) testSet(t, acc, "I8", true, uint64(44)) testSet(t, acc, "I16", true, uint8(44)) testSet(t, acc, "I32", true, uint16(44)) testSet(t, acc, "I64", true, 44) testSet(t, acc, "UI", true, 44) testSet(t, acc, "UI8", true, int64(44)) testSet(t, acc, "UI16", true, int32(44)) testSet(t, acc, "UI32", true, int8(44)) testSet(t, acc, "UI64", true, int16(44)) testSet(t, acc, "F32", true, 44.44) testSet(t, acc, "F64", true, 44.44) testSet(t, acc, "B", true, false) } // get again to check if new values were set for _, acc := range accs { testGetString(t, acc, "S", true, "coconut") testGetStringArray(t, acc, "A", true, []string{"green", "blue"}) testGetInt(t, acc, "I", true, 44) testGetInt(t, acc, "I8", true, 44) testGetInt(t, acc, "I16", true, 44) testGetInt(t, acc, "I32", true, 44) testGetInt(t, acc, "I64", true, 44) testGetInt(t, acc, "UI", true, 44) testGetInt(t, acc, "UI8", true, 44) testGetInt(t, acc, "UI16", true, 44) testGetInt(t, acc, "UI32", true, 44) testGetInt(t, acc, "UI64", true, 44) testGetFloat(t, acc, "F32", true, 44.44) testGetFloat(t, acc, "F64", true, 44.44) testGetBool(t, acc, "B", true, false) } // failures for _, acc := range accs { testSet(t, acc, "S", false, true) testSet(t, acc, "S", false, false) testSet(t, acc, "S", false, 1) testSet(t, acc, "S", false, 1.1) testSet(t, acc, "A", false, "1") testSet(t, acc, "A", false, true) testSet(t, acc, "A", false, false) testSet(t, acc, "A", false, 1) testSet(t, acc, "A", false, 1.1) testSet(t, acc, "I", false, "1") testSet(t, acc, "I8", false, "1") testSet(t, acc, "I16", false, "1") testSet(t, acc, "I32", false, "1") testSet(t, acc, "I64", false, "1") testSet(t, acc, "UI", false, "1") testSet(t, acc, "UI8", false, "1") testSet(t, acc, "UI16", false, "1") testSet(t, acc, "UI32", false, "1") testSet(t, acc, "UI64", false, "1") testSet(t, acc, "F32", false, "1.1") testSet(t, acc, "F64", false, "1.1") testSet(t, acc, "B", false, "false") testSet(t, acc, "B", false, 1) testSet(t, acc, "B", false, 1.1) } // get again to check if values werent changed when an error occurred for _, acc := range accs { testGetString(t, acc, "S", true, "coconut") testGetStringArray(t, acc, "A", true, []string{"green", "blue"}) testGetInt(t, acc, "I", true, 44) testGetInt(t, acc, "I8", true, 44) testGetInt(t, acc, "I16", true, 44) testGetInt(t, acc, "I32", true, 44) testGetInt(t, acc, "I64", true, 44) testGetInt(t, acc, "UI", true, 44) testGetInt(t, acc, "UI8", true, 44) testGetInt(t, acc, "UI16", true, 44) testGetInt(t, acc, "UI32", true, 44) testGetInt(t, acc, "UI64", true, 44) testGetFloat(t, acc, "F32", true, 44.44) testGetFloat(t, acc, "F64", true, 44.44) testGetBool(t, acc, "B", true, false) } // test existence for _, acc := range accs { testExists(t, acc, "S", true) testExists(t, acc, "A", true) testExists(t, acc, "I", true) testExists(t, acc, "I8", true) testExists(t, acc, "I16", true) testExists(t, acc, "I32", true) testExists(t, acc, "I64", true) testExists(t, acc, "UI", true) testExists(t, acc, "UI8", true) testExists(t, acc, "UI16", true) testExists(t, acc, "UI32", true) testExists(t, acc, "UI64", true) testExists(t, acc, "F32", true) testExists(t, acc, "F64", true) testExists(t, acc, "B", true) } // test non-existence for _, acc := range accs { testExists(t, acc, "X", false) } } ================================================ FILE: base/database/boilerplate_test.go ================================================ package database import ( "fmt" "sync" "github.com/safing/portmaster/base/database/record" ) type Example struct { record.Base sync.Mutex Name string Score int } var exampleDB = NewInterface(&Options{ Internal: true, Local: true, }) // GetExample gets an Example from the database. func GetExample(key string) (*Example, error) { r, err := exampleDB.Get(key) if err != nil { return nil, err } // unwrap if r.IsWrapped() { // only allocate a new struct, if we need it newExample := &Example{} err = record.Unwrap(r, newExample) if err != nil { return nil, err } return newExample, nil } // or adjust type newExample, ok := r.(*Example) if !ok { return nil, fmt.Errorf("record not of type *Example, but %T", r) } return newExample, nil } func (e *Example) Save() error { return exampleDB.Put(e) } func (e *Example) SaveAs(key string) error { e.SetKey(key) return exampleDB.PutNew(e) } func NewExample(key, name string, score int) *Example { newExample := &Example{ Name: name, Score: score, } newExample.SetKey(key) return newExample } ================================================ FILE: base/database/controller.go ================================================ package database import ( "context" "errors" "sync" "time" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" ) // A Controller takes care of all the extra database logic. type Controller struct { database *Database storage storage.Interface shadowDelete bool hooksLock sync.RWMutex hooks []*RegisteredHook subscriptionLock sync.RWMutex subscriptions []*Subscription } // newController creates a new controller for a storage. func newController(database *Database, storageInt storage.Interface, shadowDelete bool) *Controller { return &Controller{ database: database, storage: storageInt, shadowDelete: shadowDelete, } } // ReadOnly returns whether the storage is read only. func (c *Controller) ReadOnly() bool { return c.storage.ReadOnly() } // Injected returns whether the storage is injected. func (c *Controller) Injected() bool { return c.storage.Injected() } // Get returns the record with the given key. func (c *Controller) Get(key string) (record.Record, error) { if shuttingDown.IsSet() { return nil, ErrShuttingDown } if err := c.runPreGetHooks(key); err != nil { return nil, err } r, err := c.storage.Get(key) if err != nil { // replace not found error if errors.Is(err, storage.ErrNotFound) { return nil, ErrNotFound } return nil, err } r.Lock() defer r.Unlock() r, err = c.runPostGetHooks(r) if err != nil { return nil, err } if !r.Meta().CheckValidity() { return nil, ErrNotFound } return r, nil } // GetMeta returns the metadata of the record with the given key. func (c *Controller) GetMeta(key string) (*record.Meta, error) { if shuttingDown.IsSet() { return nil, ErrShuttingDown } var m *record.Meta var err error if metaDB, ok := c.storage.(storage.MetaHandler); ok { m, err = metaDB.GetMeta(key) if err != nil { // replace not found error if errors.Is(err, storage.ErrNotFound) { return nil, ErrNotFound } return nil, err } } else { r, err := c.storage.Get(key) if err != nil { // replace not found error if errors.Is(err, storage.ErrNotFound) { return nil, ErrNotFound } return nil, err } m = r.Meta() } if !m.CheckValidity() { return nil, ErrNotFound } return m, nil } // Put saves a record in the database, executes any registered // pre-put hooks and finally send an update to all subscribers. // The record must be locked and secured from concurrent access // when calling Put(). func (c *Controller) Put(r record.Record) (err error) { if shuttingDown.IsSet() { return ErrShuttingDown } if c.ReadOnly() { return ErrReadOnly } r, err = c.runPrePutHooks(r) if err != nil { return err } if !c.shadowDelete && r.Meta().IsDeleted() { // Immediate delete. err = c.storage.Delete(r.DatabaseKey()) } else { // Put or shadow delete. r, err = c.storage.Put(r) } if err != nil { return err } if r == nil { return errors.New("storage returned nil record after successful put operation") } c.notifySubscribers(r) return nil } // PutMany stores many records in the database. It does not // process any hooks or update subscriptions. Use with care! func (c *Controller) PutMany() (chan<- record.Record, <-chan error) { if shuttingDown.IsSet() { errs := make(chan error, 1) errs <- ErrShuttingDown return make(chan record.Record), errs } if c.ReadOnly() { errs := make(chan error, 1) errs <- ErrReadOnly return make(chan record.Record), errs } if batcher, ok := c.storage.(storage.Batcher); ok { return batcher.PutMany(c.shadowDelete) } errs := make(chan error, 1) errs <- ErrNotImplemented return make(chan record.Record), errs } // Query executes the given query on the database. func (c *Controller) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { if shuttingDown.IsSet() { return nil, ErrShuttingDown } it, err := c.storage.Query(q, local, internal) if err != nil { return nil, err } return it, nil } // PushUpdate pushes a record update to subscribers. // The caller must hold the record's lock when calling // PushUpdate. func (c *Controller) PushUpdate(r record.Record) { if c != nil { if shuttingDown.IsSet() { return } c.notifySubscribers(r) } } func (c *Controller) addSubscription(sub *Subscription) { if shuttingDown.IsSet() { return } c.subscriptionLock.Lock() defer c.subscriptionLock.Unlock() c.subscriptions = append(c.subscriptions, sub) } // Maintain runs the Maintain method on the storage. func (c *Controller) Maintain(ctx context.Context) error { if shuttingDown.IsSet() { return ErrShuttingDown } if maintainer, ok := c.storage.(storage.Maintainer); ok { return maintainer.Maintain(ctx) } return nil } // MaintainThorough runs the MaintainThorough method on the // storage. func (c *Controller) MaintainThorough(ctx context.Context) error { if shuttingDown.IsSet() { return ErrShuttingDown } if maintainer, ok := c.storage.(storage.Maintainer); ok { return maintainer.MaintainThorough(ctx) } return nil } // MaintainRecordStates runs the record state lifecycle // maintenance on the storage. func (c *Controller) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time) error { if shuttingDown.IsSet() { return ErrShuttingDown } return c.storage.MaintainRecordStates(ctx, purgeDeletedBefore, c.shadowDelete) } // Purge deletes all records that match the given query. // It returns the number of successful deletes and an error. func (c *Controller) Purge(ctx context.Context, q *query.Query, local, internal bool) (int, error) { if shuttingDown.IsSet() { return 0, ErrShuttingDown } if purger, ok := c.storage.(storage.Purger); ok { return purger.Purge(ctx, q, local, internal, c.shadowDelete) } return 0, ErrNotImplemented } // PurgeOlderThan deletes all records last updated before the given time. // It returns the number of successful deletes and an error. func (c *Controller) PurgeOlderThan(ctx context.Context, prefix string, purgeBefore time.Time, local, internal bool) (int, error) { if shuttingDown.IsSet() { return 0, ErrShuttingDown } if purger, ok := c.storage.(storage.PurgeOlderThan); ok { return purger.PurgeOlderThan(ctx, prefix, purgeBefore, local, internal, c.shadowDelete) } return 0, ErrNotImplemented } // Shutdown shuts down the storage. func (c *Controller) Shutdown() error { return c.storage.Shutdown() } // notifySubscribers notifies all subscribers that are interested // in r. r must be locked when calling notifySubscribers. // Any subscriber that is not blocking on it's feed channel will // be skipped. func (c *Controller) notifySubscribers(r record.Record) { c.subscriptionLock.RLock() defer c.subscriptionLock.RUnlock() for _, sub := range c.subscriptions { if r.Meta().CheckPermission(sub.local, sub.internal) && sub.q.Matches(r) { select { case sub.Feed <- r: default: } } } } func (c *Controller) runPreGetHooks(key string) error { c.hooksLock.RLock() defer c.hooksLock.RUnlock() for _, hook := range c.hooks { if !hook.h.UsesPreGet() { continue } if !hook.q.MatchesKey(key) { continue } if err := hook.h.PreGet(key); err != nil { return err } } return nil } func (c *Controller) runPostGetHooks(r record.Record) (record.Record, error) { c.hooksLock.RLock() defer c.hooksLock.RUnlock() var err error for _, hook := range c.hooks { if !hook.h.UsesPostGet() { continue } if !hook.q.Matches(r) { continue } r, err = hook.h.PostGet(r) if err != nil { return nil, err } } return r, nil } func (c *Controller) runPrePutHooks(r record.Record) (record.Record, error) { c.hooksLock.RLock() defer c.hooksLock.RUnlock() var err error for _, hook := range c.hooks { if !hook.h.UsesPrePut() { continue } if !hook.q.Matches(r) { continue } r, err = hook.h.PrePut(r) if err != nil { return nil, err } } return r, nil } ================================================ FILE: base/database/controllers.go ================================================ package database import ( "errors" "fmt" "sync" "github.com/safing/portmaster/base/database/storage" ) // StorageTypeInjected is the type of injected databases. const StorageTypeInjected = "injected" var ( controllers = make(map[string]*Controller) controllersLock sync.RWMutex ) func getController(name string) (*Controller, error) { if !initialized.IsSet() { return nil, errors.New("database not initialized") } // return database if already started controllersLock.RLock() controller, ok := controllers[name] controllersLock.RUnlock() if ok { return controller, nil } controllersLock.Lock() defer controllersLock.Unlock() if shuttingDown.IsSet() { return nil, ErrShuttingDown } // get db registration registeredDB, err := getDatabase(name) if err != nil { return nil, fmt.Errorf("could not start database %s: %w", name, err) } // Check if database is injected. if registeredDB.StorageType == StorageTypeInjected { return nil, fmt.Errorf("database storage is not injected") } // get location dbLocation, err := getLocation(name, registeredDB.StorageType) if err != nil { return nil, fmt.Errorf("could not start database %s (type %s): %w", name, registeredDB.StorageType, err) } // start database storageInt, err := storage.StartDatabase(name, registeredDB.StorageType, dbLocation) if err != nil { return nil, fmt.Errorf("could not start database %s (type %s): %w", name, registeredDB.StorageType, err) } controller = newController(registeredDB, storageInt, registeredDB.ShadowDelete) controllers[name] = controller return controller, nil } // InjectDatabase injects an already running database into the system. func InjectDatabase(name string, storageInt storage.Interface) (*Controller, error) { controllersLock.Lock() defer controllersLock.Unlock() if shuttingDown.IsSet() { return nil, ErrShuttingDown } _, ok := controllers[name] if ok { return nil, fmt.Errorf(`database "%s" already loaded`, name) } registryLock.Lock() defer registryLock.Unlock() // check if database is registered registeredDB, ok := registry[name] if !ok { return nil, fmt.Errorf("database %q not registered", name) } if registeredDB.StorageType != StorageTypeInjected { return nil, fmt.Errorf("database not of type %q", StorageTypeInjected) } controller := newController(registeredDB, storageInt, false) controllers[name] = controller return controller, nil } // Withdraw withdraws an injected database, but leaves the database registered. func (c *Controller) Withdraw() { if c != nil && c.Injected() { controllersLock.Lock() defer controllersLock.Unlock() delete(controllers, c.database.Name) } } ================================================ FILE: base/database/database.go ================================================ package database import ( "time" ) // Database holds information about a registered database. type Database struct { Name string Description string StorageType string ShadowDelete bool // Whether deleted records should be kept until purged. Registered time.Time LastUpdated time.Time LastLoaded time.Time } // Loaded updates the LastLoaded timestamp. func (db *Database) Loaded() { db.LastLoaded = time.Now().Round(time.Second) } // Updated updates the LastUpdated timestamp. func (db *Database) Updated() { db.LastUpdated = time.Now().Round(time.Second) } ================================================ FILE: base/database/database_test.go ================================================ package database import ( "context" "errors" "fmt" "log" "os" "reflect" "runtime/pprof" "testing" "time" q "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" _ "github.com/safing/portmaster/base/database/storage/badger" _ "github.com/safing/portmaster/base/database/storage/bbolt" _ "github.com/safing/portmaster/base/database/storage/fstree" _ "github.com/safing/portmaster/base/database/storage/hashmap" _ "github.com/safing/portmaster/base/database/storage/sqlite" ) func TestMain(m *testing.M) { testDir, err := os.MkdirTemp("", "portbase-database-testing-") if err != nil { panic(err) } err = Initialize(testDir) if err != nil { panic(err) } exitCode := m.Run() // Clean up the test directory. // Do not defer, as we end this function with a os.Exit call. _ = os.RemoveAll(testDir) os.Exit(exitCode) } func makeKey(dbName, key string) string { return fmt.Sprintf("%s:%s", dbName, key) } func testDatabase(t *testing.T, storageType string, shadowDelete bool) { //nolint:maintidx,thelper t.Run(fmt.Sprintf("TestStorage_%s_%v", storageType, shadowDelete), func(t *testing.T) { dbName := fmt.Sprintf("testing-%s-%v", storageType, shadowDelete) fmt.Println(dbName) _, err := Register(&Database{ Name: dbName, Description: fmt.Sprintf("Unit Test Database for %s", storageType), StorageType: storageType, ShadowDelete: shadowDelete, }) if err != nil { t.Fatal(err) } dbController, err := getController(dbName) if err != nil { t.Fatal(err) } // hook hook, err := RegisterHook(q.New(dbName).MustBeValid(), &HookBase{}) if err != nil { t.Fatal(err) } // interface db := NewInterface(&Options{ Local: true, Internal: true, }) // sub sub, err := db.Subscribe(q.New(dbName).MustBeValid()) if err != nil { t.Fatal(err) } A := NewExample(dbName+":A", "Herbert", 411) err = A.Save() if err != nil { t.Fatal(err) } B := NewExample(makeKey(dbName, "B"), "Fritz", 347) err = B.Save() if err != nil { t.Fatal(err) } C := NewExample(makeKey(dbName, "C"), "Norbert", 217) err = C.Save() if err != nil { t.Fatal(err) } exists, err := db.Exists(makeKey(dbName, "A")) if err != nil { t.Fatal(err) } if !exists { t.Fatalf("record %s should exist!", makeKey(dbName, "A")) } A1, err := GetExample(makeKey(dbName, "A")) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(A, A1) { log.Fatalf("A and A1 mismatch, A1: %v", A1) } cnt := countRecords(t, db, q.New(dbName).Where( q.And( q.Where("Name", q.EndsWith, "bert"), q.Where("Score", q.GreaterThan, 100), ), )) if cnt != 2 { t.Fatalf("expected two records, got %d", cnt) } // test putmany if _, ok := dbController.storage.(storage.Batcher); ok { batchPut := db.PutMany(dbName) records := []record.Record{A, B, C, nil} // nil is to signify finish for _, r := range records { err = batchPut(r) if err != nil { t.Fatal(err) } } } // test maintenance if _, ok := dbController.storage.(storage.Maintainer); ok { now := time.Now().UTC() nowUnix := now.Unix() // we start with 3 records without expiry cnt := countRecords(t, db, q.New(dbName)) if cnt != 3 { t.Fatalf("expected three records, got %d", cnt) } // delete entry A.Meta().Deleted = nowUnix - 61 err = A.Save() if err != nil { t.Fatal(err) } // expire entry B.Meta().Expires = nowUnix - 1 err = B.Save() if err != nil { t.Fatal(err) } // one left cnt = countRecords(t, db, q.New(dbName)) if cnt != 1 { t.Fatalf("expected one record, got %d", cnt) } // run maintenance err = dbController.MaintainRecordStates(t.Context(), now.Add(-60*time.Second)) if err != nil { t.Fatal(err) } // one left cnt = countRecords(t, db, q.New(dbName)) if cnt != 1 { t.Fatalf("expected one record, got %d", cnt) } // check status individually _, err = dbController.storage.Get("A") if !errors.Is(err, storage.ErrNotFound) { t.Errorf("A should be deleted and purged, err=%s", err) } if !shadowDelete { // previous call MaintainRecordStates() must purge all expired and deleted records _, err := dbController.storage.Get("B") if !errors.Is(err, storage.ErrNotFound) { t.Errorf("B should be deleted and purged, err=%s", err) } } else { B1, err := dbController.storage.Get("B") if err != nil { t.Fatalf("should exist: %s, original meta: %+v", err, B.Meta()) } if B1.Meta().Deleted == 0 { t.Errorf("B should be deleted") } } // delete last entry C.Meta().Deleted = nowUnix - 1 err = C.Save() if err != nil { t.Fatal(err) } // run maintenance // Since previous call MaintainRecordStates() saved actual timestamp for deleted records, // use 'now + 1sec' just to guarantee that time is bigger) err = dbController.MaintainRecordStates(t.Context(), time.Now().Add(time.Second).UTC()) if err != nil { t.Fatal(err) } // check status individually B2, err := dbController.storage.Get("B") if err == nil { t.Errorf("B should be deleted and purged, meta: %+v", B2.Meta()) } else if !errors.Is(err, storage.ErrNotFound) { t.Errorf("B should be deleted and purged, err=%s", err) } C2, err := dbController.storage.Get("C") if err == nil { t.Errorf("C should be deleted and purged, meta: %+v", C2.Meta()) } else if !errors.Is(err, storage.ErrNotFound) { t.Errorf("C should be deleted and purged, err=%s", err) } // none left cnt = countRecords(t, db, q.New(dbName)) if cnt != 0 { t.Fatalf("expected no records, got %d", cnt) } } err = hook.Cancel() if err != nil { t.Fatal(err) } err = sub.Cancel() if err != nil { t.Fatal(err) } }) } func TestDatabaseSystem(t *testing.T) { //nolint:tparallel t.Parallel() // panic after 10 seconds, to check for locks finished := make(chan struct{}) defer close(finished) go func() { select { case <-finished: case <-time.After(10 * time.Second): fmt.Println("===== TAKING TOO LONG - PRINTING STACK TRACES =====") _ = pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) os.Exit(1) } }() for _, shadowDelete := range []bool{false, true} { testDatabase(t, "sqlite", shadowDelete) testDatabase(t, "bbolt", shadowDelete) testDatabase(t, "hashmap", shadowDelete) testDatabase(t, "fstree", shadowDelete) // testDatabase(t, "badger", shadowDelete) // TODO: Fix badger tests } err := MaintainRecordStates(context.TODO()) if err != nil { t.Fatal(err) } err = Maintain(context.TODO()) if err != nil { t.Fatal(err) } err = MaintainThorough(context.TODO()) if err != nil { t.Fatal(err) } err = Shutdown() if err != nil { t.Fatal(err) } } func countRecords(t *testing.T, db *Interface, query *q.Query) int { t.Helper() _, err := query.Check() if err != nil { t.Fatal(err) } it, err := db.Query(query) if err != nil { t.Fatal(err) } cnt := 0 for range it.Next { cnt++ } if it.Err() != nil { t.Fatal(it.Err()) } return cnt } ================================================ FILE: base/database/dbmodule/db.go ================================================ package dbmodule import ( "errors" "path/filepath" "sync/atomic" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/service/mgr" ) type DBModule struct { mgr *mgr.Manager instance instance } func (dbm *DBModule) Manager() *mgr.Manager { return dbm.mgr } func (dbm *DBModule) Start() error { return start() } func (dbm *DBModule) Stop() error { return stop() } var databasesRootDir string // SetDatabaseLocation sets the location of the database for initialization. Supply either a path or dir structure. func SetDatabaseLocation(dir string) { if databasesRootDir == "" { databasesRootDir = dir } } func prep() error { SetDatabaseLocation(filepath.Join(module.instance.DataDir(), "databases")) if databasesRootDir == "" { return errors.New("database location not specified") } return nil } func start() error { startMaintenanceTasks() return nil } func stop() error { return database.Shutdown() } var ( module *DBModule shimLoaded atomic.Bool ) func New(instance instance) (*DBModule, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("DBModule") module = &DBModule{ mgr: m, instance: instance, } if err := prep(); err != nil { return nil, err } err := database.Initialize(databasesRootDir) if err != nil { return nil, err } return module, nil } type instance interface { DataDir() string } ================================================ FILE: base/database/dbmodule/maintenance.go ================================================ package dbmodule import ( "time" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" ) func startMaintenanceTasks() { _ = module.mgr.Repeat("basic maintenance", 10*time.Minute, maintainBasic) _ = module.mgr.Repeat("thorough maintenance", 1*time.Hour, maintainThorough) _ = module.mgr.Repeat("record maintenance", 1*time.Hour, maintainRecords) } func maintainBasic(ctx *mgr.WorkerCtx) error { log.Infof("database: running Maintain") return database.Maintain(ctx.Ctx()) } func maintainThorough(ctx *mgr.WorkerCtx) error { log.Infof("database: running MaintainThorough") return database.MaintainThorough(ctx.Ctx()) } func maintainRecords(ctx *mgr.WorkerCtx) error { log.Infof("database: running MaintainRecordStates") return database.MaintainRecordStates(ctx.Ctx()) } ================================================ FILE: base/database/doc.go ================================================ /* Package database provides a universal interface for interacting with the database. # A Lazy Database The database system can handle Go structs as well as serialized data by the dsd package. While data is in transit within the system, it does not know which form it currently has. Only when it reaches its destination, it must ensure that it is either of a certain type or dump it. # Record Interface The database system uses the Record interface to transparently handle all types of structs that get saved in the database. Structs include the Base struct to fulfill most parts of the Record interface. Boilerplate Code: type Example struct { record.Base sync.Mutex Name string Score int } var ( db = database.NewInterface(nil) ) // GetExample gets an Example from the database. func GetExample(key string) (*Example, error) { r, err := db.Get(key) if err != nil { return nil, err } // unwrap if r.IsWrapped() { // only allocate a new struct, if we need it new := &Example{} err = record.Unwrap(r, new) if err != nil { return nil, err } return new, nil } // or adjust type new, ok := r.(*Example) if !ok { return nil, fmt.Errorf("record not of type *Example, but %T", r) } return new, nil } func (e *Example) Save() error { return db.Put(e) } func (e *Example) SaveAs(key string) error { e.SetKey(key) return db.PutNew(e) } */ package database ================================================ FILE: base/database/errors.go ================================================ package database import ( "errors" ) // Errors. var ( ErrNotFound = errors.New("database entry not found") ErrPermissionDenied = errors.New("access to database record denied") ErrReadOnly = errors.New("database is read only") ErrShuttingDown = errors.New("database system is shutting down") ErrNotImplemented = errors.New("not implemented by this storage") ) ================================================ FILE: base/database/hook.go ================================================ package database import ( "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" ) // Hook can be registered for a database query and // will be executed at certain points during the life // cycle of a database record. type Hook interface { // UsesPreGet should return true if the hook's PreGet // should be called prior to loading a database record // from the underlying storage. UsesPreGet() bool // PreGet is called before a database record is loaded from // the underlying storage. A PreGet hookd may be used to // implement more advanced access control on database keys. PreGet(dbKey string) error // UsesPostGet should return true if the hook's PostGet // should be called after loading a database record from // the underlying storage. UsesPostGet() bool // PostGet is called after a record has been loaded form the // underlying storage and may perform additional mutation // or access check based on the records data. // The passed record is already locked by the database system // so users can safely access all data of r. PostGet(r record.Record) (record.Record, error) // UsesPrePut should return true if the hook's PrePut method // should be called prior to saving a record in the database. UsesPrePut() bool // PrePut is called prior to saving (creating or updating) a // record in the database storage. It may be used to perform // extended validation or mutations on the record. // The passed record is already locked by the database system // so users can safely access all data of r. PrePut(r record.Record) (record.Record, error) } // RegisteredHook is a registered database hook. type RegisteredHook struct { q *query.Query h Hook } // RegisterHook registers a hook for records matching the given // query in the database. func RegisterHook(q *query.Query, hook Hook) (*RegisteredHook, error) { _, err := q.Check() if err != nil { return nil, err } c, err := getController(q.DatabaseName()) if err != nil { return nil, err } rh := &RegisteredHook{ q: q, h: hook, } c.hooksLock.Lock() defer c.hooksLock.Unlock() c.hooks = append(c.hooks, rh) return rh, nil } // Cancel unregisteres the hook from the database. Once // Cancel returned the hook's methods will not be called // anymore for updates that matched the registered query. func (h *RegisteredHook) Cancel() error { c, err := getController(h.q.DatabaseName()) if err != nil { return err } c.hooksLock.Lock() defer c.hooksLock.Unlock() for key, hook := range c.hooks { if hook.q == h.q { c.hooks = append(c.hooks[:key], c.hooks[key+1:]...) return nil } } return nil } ================================================ FILE: base/database/hookbase.go ================================================ package database import ( "github.com/safing/portmaster/base/database/record" ) // HookBase implements the Hook interface and provides dummy functions to reduce boilerplate. type HookBase struct{} // UsesPreGet implements the Hook interface and returns false. func (b *HookBase) UsesPreGet() bool { return false } // UsesPostGet implements the Hook interface and returns false. func (b *HookBase) UsesPostGet() bool { return false } // UsesPrePut implements the Hook interface and returns false. func (b *HookBase) UsesPrePut() bool { return false } // PreGet implements the Hook interface. func (b *HookBase) PreGet(dbKey string) error { return nil } // PostGet implements the Hook interface. func (b *HookBase) PostGet(r record.Record) (record.Record, error) { return r, nil } // PrePut implements the Hook interface. func (b *HookBase) PrePut(r record.Record) (record.Record, error) { return r, nil } ================================================ FILE: base/database/interface.go ================================================ package database import ( "context" "errors" "fmt" "sync" "time" "github.com/bluele/gcache" "github.com/tevino/abool" "github.com/safing/portmaster/base/database/accessor" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" ) const ( getDBFromKey = "" ) // Interface provides a method to access the database with attached options. type Interface struct { options *Options cache gcache.Cache writeCache map[string]record.Record writeCacheLock sync.Mutex triggerCacheWrite chan struct{} } // Options holds options that may be set for an Interface instance. type Options struct { // Local specifies if the interface is used by an actor on the local device. // Setting both the Local and Internal flags will bring performance // improvements because less checks are needed. Local bool // Internal specifies if the interface is used by an actor within the // software. Setting both the Local and Internal flags will bring performance // improvements because less checks are needed. Internal bool // AlwaysMakeSecret will have the interface mark all saved records as secret. // This means that they will be only accessible by an internal interface. AlwaysMakeSecret bool // AlwaysMakeCrownjewel will have the interface mark all saved records as // crown jewels. This means that they will be only accessible by a local // interface. AlwaysMakeCrownjewel bool // AlwaysSetRelativateExpiry will have the interface set a relative expiry, // based on the current time, on all saved records. AlwaysSetRelativateExpiry int64 // AlwaysSetAbsoluteExpiry will have the interface set an absolute expiry on // all saved records. AlwaysSetAbsoluteExpiry int64 // CacheSize defines that a cache should be used for this interface and // defines it's size. // Caching comes with an important caveat: If database records are changed // from another interface, the cache will not be invalidated for these // records. It will therefore serve outdated data until that record is // evicted from the cache. CacheSize int // DelayCachedWrites defines a database name for which cache writes should // be cached and batched. The database backend must support the Batcher // interface. This option is only valid if used with a cache. // Additionally, this may only be used for internal and local interfaces. // Please note that this means that other interfaces will not be able to // guarantee to serve the latest record if records are written this way. DelayCachedWrites string } // Apply applies options to the record metadata. func (o *Options) Apply(r record.Record) { r.UpdateMeta() if o.AlwaysMakeSecret { r.Meta().MakeSecret() } if o.AlwaysMakeCrownjewel { r.Meta().MakeCrownJewel() } if o.AlwaysSetAbsoluteExpiry > 0 { r.Meta().SetAbsoluteExpiry(o.AlwaysSetAbsoluteExpiry) } else if o.AlwaysSetRelativateExpiry > 0 { r.Meta().SetRelativateExpiry(o.AlwaysSetRelativateExpiry) } } // HasAllPermissions returns whether the options specify the highest possible // permissions for operations. func (o *Options) HasAllPermissions() bool { return o.Local && o.Internal } // hasAccessPermission checks if the interface options permit access to the // given record, locking the record for accessing it's attributes. func (o *Options) hasAccessPermission(r record.Record) bool { // Check if the options specify all permissions, which makes checking the // record unnecessary. if o.HasAllPermissions() { return true } r.Lock() defer r.Unlock() // Check permissions against record. return r.Meta().CheckPermission(o.Local, o.Internal) } // NewInterface returns a new Interface to the database. func NewInterface(opts *Options) *Interface { if opts == nil { opts = &Options{} } newIface := &Interface{ options: opts, } if opts.CacheSize > 0 { cacheBuilder := gcache.New(opts.CacheSize).ARC() if opts.DelayCachedWrites != "" { cacheBuilder.EvictedFunc(newIface.cacheEvictHandler) newIface.writeCache = make(map[string]record.Record, opts.CacheSize/2) newIface.triggerCacheWrite = make(chan struct{}) } newIface.cache = cacheBuilder.Build() } return newIface } // Exists return whether a record with the given key exists. func (i *Interface) Exists(key string) (bool, error) { _, err := i.Get(key) if err != nil { switch { case errors.Is(err, ErrNotFound): return false, nil case errors.Is(err, ErrPermissionDenied): return true, nil default: return false, err } } return true, nil } // Get return the record with the given key. func (i *Interface) Get(key string) (record.Record, error) { r, _, err := i.getRecord(getDBFromKey, key, false) return r, err } func (i *Interface) getRecord(dbName string, dbKey string, mustBeWriteable bool) (r record.Record, db *Controller, err error) { //nolint:unparam if dbName == "" { dbName, dbKey = record.ParseKey(dbKey) } db, err = getController(dbName) if err != nil { return nil, nil, err } if mustBeWriteable && db.ReadOnly() { return nil, db, ErrReadOnly } r = i.checkCache(dbName + ":" + dbKey) if r != nil { if !i.options.hasAccessPermission(r) { return nil, db, ErrPermissionDenied } return r, db, nil } r, err = db.Get(dbKey) if err != nil { return nil, db, err } if !i.options.hasAccessPermission(r) { return nil, db, ErrPermissionDenied } r.Lock() ttl := r.Meta().GetRelativeExpiry() r.Unlock() i.updateCache( r, false, // writing false, // remove ttl, // expiry ) return r, db, nil } func (i *Interface) getMeta(dbName string, dbKey string, mustBeWriteable bool) (m *record.Meta, db *Controller, err error) { //nolint:unparam if dbName == "" { dbName, dbKey = record.ParseKey(dbKey) } db, err = getController(dbName) if err != nil { return nil, nil, err } if mustBeWriteable && db.ReadOnly() { return nil, db, ErrReadOnly } r := i.checkCache(dbName + ":" + dbKey) if r != nil { if !i.options.hasAccessPermission(r) { return nil, db, ErrPermissionDenied } return r.Meta(), db, nil } m, err = db.GetMeta(dbKey) if err != nil { return nil, db, err } if !m.CheckPermission(i.options.Local, i.options.Internal) { return nil, db, ErrPermissionDenied } return m, db, nil } // InsertValue inserts a value into a record. func (i *Interface) InsertValue(key string, attribute string, value interface{}) error { r, db, err := i.getRecord(getDBFromKey, key, true) if err != nil { return err } r.Lock() defer r.Unlock() var acc accessor.Accessor if r.IsWrapped() { wrapper, ok := r.(*record.Wrapper) if !ok { return errors.New("record is malformed (reports to be wrapped but is not of type *record.Wrapper)") } acc = accessor.NewJSONBytesAccessor(&wrapper.Data) } else { acc = accessor.NewStructAccessor(r) } err = acc.Set(attribute, value) if err != nil { return fmt.Errorf("failed to set value with %s: %w", acc.Type(), err) } i.options.Apply(r) return db.Put(r) } // Put saves a record to the database. func (i *Interface) Put(r record.Record) (err error) { // get record or only database var db *Controller if !i.options.HasAllPermissions() { _, db, err = i.getMeta(r.DatabaseName(), r.DatabaseKey(), true) if err != nil && !errors.Is(err, ErrNotFound) { return err } } else { db, err = getController(r.DatabaseName()) if err != nil { return err } } // Check if database is read only. if db.ReadOnly() { return ErrReadOnly } r.Lock() i.options.Apply(r) remove := r.Meta().IsDeleted() ttl := r.Meta().GetRelativeExpiry() r.Unlock() // The record may not be locked when updating the cache. written := i.updateCache(r, true, remove, ttl) if written { return nil } r.Lock() defer r.Unlock() return db.Put(r) } // PutNew saves a record to the database as a new record (ie. with new timestamps). func (i *Interface) PutNew(r record.Record) (err error) { // get record or only database var db *Controller if !i.options.HasAllPermissions() { _, db, err = i.getMeta(r.DatabaseName(), r.DatabaseKey(), true) if err != nil && !errors.Is(err, ErrNotFound) { return err } } else { db, err = getController(r.DatabaseName()) if err != nil { return err } } // Check if database is read only. if db.ReadOnly() { return ErrReadOnly } r.Lock() if r.Meta() != nil { r.Meta().Reset() } i.options.Apply(r) remove := r.Meta().IsDeleted() ttl := r.Meta().GetRelativeExpiry() r.Unlock() // The record may not be locked when updating the cache. written := i.updateCache(r, true, remove, ttl) if written { return nil } r.Lock() defer r.Unlock() return db.Put(r) } // PutMany stores many records in the database. // Warning: This is nearly a direct database access and omits many things: // - Record locking // - Hooks // - Subscriptions // - Caching // Use with care. func (i *Interface) PutMany(dbName string) (put func(record.Record) error) { interfaceBatch := make(chan record.Record, 100) // permission check if !i.options.HasAllPermissions() { return func(r record.Record) error { return ErrPermissionDenied } } // get database db, err := getController(dbName) if err != nil { return func(r record.Record) error { return err } } // Check if database is read only. if db.ReadOnly() { return func(r record.Record) error { return ErrReadOnly } } // start database access dbBatch, errs := db.PutMany() finished := abool.New() var internalErr error // interface options proxy go func() { defer close(dbBatch) // signify that we are finished for { select { case r := <-interfaceBatch: // finished? if r == nil { return } // apply options i.options.Apply(r) // pass along dbBatch <- r case <-time.After(1 * time.Second): // bail out internalErr = errors.New("timeout: putmany unused for too long") finished.Set() return } } }() return func(r record.Record) error { // finished? if finished.IsSet() { // check for internal error if internalErr != nil { return internalErr } // check for previous error select { case err := <-errs: return err default: return errors.New("batch is closed") } } // finish? if r == nil { finished.Set() interfaceBatch <- nil // signify that we are finished // do not close, as this fn could be called again with nil. return <-errs } // check record scope if r.DatabaseName() != dbName { return errors.New("record out of database scope") } // submit select { case interfaceBatch <- r: return nil case err := <-errs: return err } } } // SetAbsoluteExpiry sets an absolute record expiry. func (i *Interface) SetAbsoluteExpiry(key string, time int64) error { r, db, err := i.getRecord(getDBFromKey, key, true) if err != nil { return err } r.Lock() defer r.Unlock() i.options.Apply(r) r.Meta().SetAbsoluteExpiry(time) return db.Put(r) } // SetRelativateExpiry sets a relative (self-updating) record expiry. func (i *Interface) SetRelativateExpiry(key string, duration int64) error { r, db, err := i.getRecord(getDBFromKey, key, true) if err != nil { return err } r.Lock() defer r.Unlock() i.options.Apply(r) r.Meta().SetRelativateExpiry(duration) return db.Put(r) } // MakeSecret marks the record as a secret, meaning interfacing processes, such as an UI, are denied access to the record. func (i *Interface) MakeSecret(key string) error { r, db, err := i.getRecord(getDBFromKey, key, true) if err != nil { return err } r.Lock() defer r.Unlock() i.options.Apply(r) r.Meta().MakeSecret() return db.Put(r) } // MakeCrownJewel marks a record as a crown jewel, meaning it will only be accessible locally. func (i *Interface) MakeCrownJewel(key string) error { r, db, err := i.getRecord(getDBFromKey, key, true) if err != nil { return err } r.Lock() defer r.Unlock() i.options.Apply(r) r.Meta().MakeCrownJewel() return db.Put(r) } // Delete deletes a record from the database. func (i *Interface) Delete(key string) error { r, db, err := i.getRecord(getDBFromKey, key, true) if err != nil { return err } // Check if database is read only. if db.ReadOnly() { return ErrReadOnly } i.options.Apply(r) r.Meta().Delete() return db.Put(r) } // Query executes the given query on the database. // Will not see data that is in the write cache, waiting to be written. // Use with care with caching. func (i *Interface) Query(q *query.Query) (*iterator.Iterator, error) { _, err := q.Check() if err != nil { return nil, err } db, err := getController(q.DatabaseName()) if err != nil { return nil, err } // TODO: Finish caching system integration. // Flush the cache before we query the database. // i.FlushCache() return db.Query(q, i.options.Local, i.options.Internal) } // Purge deletes all records that match the given query. It returns the number // of successful deletes and an error. func (i *Interface) Purge(ctx context.Context, q *query.Query) (int, error) { _, err := q.Check() if err != nil { return 0, err } db, err := getController(q.DatabaseName()) if err != nil { return 0, err } // Check if database is read only before we add to the cache. if db.ReadOnly() { return 0, ErrReadOnly } return db.Purge(ctx, q, i.options.Local, i.options.Internal) } // PurgeOlderThan deletes all records last updated before the given time. // It returns the number of successful deletes and an error. func (i *Interface) PurgeOlderThan(ctx context.Context, prefix string, purgeBefore time.Time) (int, error) { dbName, dbKeyPrefix := record.ParseKey(prefix) if dbName == "" { return 0, errors.New("unknown database") } db, err := getController(dbName) if err != nil { return 0, err } // Check if database is read only before we add to the cache. if db.ReadOnly() { return 0, ErrReadOnly } return db.PurgeOlderThan(ctx, dbKeyPrefix, purgeBefore, i.options.Local, i.options.Internal) } // Subscribe subscribes to updates matching the given query. func (i *Interface) Subscribe(q *query.Query) (*Subscription, error) { _, err := q.Check() if err != nil { return nil, err } c, err := getController(q.DatabaseName()) if err != nil { return nil, err } sub := &Subscription{ q: q, local: i.options.Local, internal: i.options.Internal, Feed: make(chan record.Record, 1000), } c.addSubscription(sub) return sub, nil } ================================================ FILE: base/database/interface_cache.go ================================================ package database import ( "errors" "time" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" ) // DelayedCacheWriter must be run by the caller of an interface that uses delayed cache writing. func (i *Interface) DelayedCacheWriter(wc *mgr.WorkerCtx) error { // Check if the DelayedCacheWriter should be run at all. if i.options.CacheSize <= 0 || i.options.DelayCachedWrites == "" { return errors.New("delayed cache writer is not applicable to this database interface") } // Check if backend support the Batcher interface. batchPut := i.PutMany(i.options.DelayCachedWrites) // End batchPut immediately and check for an error. err := batchPut(nil) if err != nil { return err } // percentThreshold defines the minimum percentage of entries in the write cache in relation to the cache size that need to be present in order for flushing the cache to the database storage. percentThreshold := 25 thresholdWriteTicker := time.NewTicker(5 * time.Second) forceWriteTicker := time.NewTicker(5 * time.Minute) for { // Wait for trigger for writing the cache. select { case <-wc.Done(): // The caller is shutting down, flush the cache to storage and exit. i.flushWriteCache(0) return nil case <-i.triggerCacheWrite: // An entry from the cache was evicted that was also in the write cache. // This makes it likely that other entries that are also present in the // write cache will be evicted soon. Flush the write cache to storage // immediately in order to reduce single writes. i.flushWriteCache(0) case <-thresholdWriteTicker.C: // Often check if the write cache has filled up to a certain degree and // flush it to storage before we start evicting to-be-written entries and // slow down the hot path again. i.flushWriteCache(percentThreshold) case <-forceWriteTicker.C: // Once in a while, flush the write cache to storage no matter how much // it is filled. We don't want entries lingering around in the write // cache forever. This also reduces the amount of data loss in the event // of a total crash. i.flushWriteCache(0) } } } // ClearCache clears the read cache. func (i *Interface) ClearCache() { // Check if cache is in use. if i.cache == nil { return } // Clear all cache entries. i.cache.Purge() } // FlushCache writes (and thus clears) the write cache. func (i *Interface) FlushCache() { // Check if write cache is in use. if i.options.DelayCachedWrites != "" { return } i.flushWriteCache(0) } func (i *Interface) flushWriteCache(percentThreshold int) { i.writeCacheLock.Lock() defer i.writeCacheLock.Unlock() // Check if there is anything to do. if len(i.writeCache) == 0 { return } // Check if we reach the given threshold for writing to storage. if (len(i.writeCache)*100)/i.options.CacheSize < percentThreshold { return } // Write the full cache in a batch operation. batchPut := i.PutMany(i.options.DelayCachedWrites) for _, r := range i.writeCache { err := batchPut(r) if err != nil { log.Warningf("database: failed to write write-cached entry to %q database: %s", i.options.DelayCachedWrites, err) } } // Finish batch. err := batchPut(nil) if err != nil { log.Warningf("database: failed to finish flushing write cache to %q database: %s", i.options.DelayCachedWrites, err) } // Optimized map clearing following the Go1.11 recommendation. for key := range i.writeCache { delete(i.writeCache, key) } } // cacheEvictHandler is run by the cache for every entry that gets evicted // from the cache. func (i *Interface) cacheEvictHandler(keyData, _ interface{}) { // Transform the key into a string. key, ok := keyData.(string) if !ok { return } // Check if the evicted record is one that is to be written. // Lock the write cache until the end of the function. // The read cache is locked anyway for the whole duration. i.writeCacheLock.Lock() defer i.writeCacheLock.Unlock() r, ok := i.writeCache[key] if ok { delete(i.writeCache, key) } if !ok { return } // Write record to database in order to mitigate race conditions where the record would appear // as non-existent for a short duration. db, err := getController(r.DatabaseName()) if err != nil { log.Warningf("database: failed to write evicted cache entry %q: database %q does not exist", key, r.DatabaseName()) return } r.Lock() defer r.Unlock() err = db.Put(r) if err != nil { log.Warningf("database: failed to write evicted cache entry %q to database: %s", key, err) } // Finally, trigger writing the full write cache because a to-be-written // entry was just evicted from the cache, and this makes it likely that more // to-be-written entries will be evicted shortly. select { case i.triggerCacheWrite <- struct{}{}: default: } } func (i *Interface) checkCache(key string) record.Record { // Check if cache is in use. if i.cache == nil { return nil } // Check if record exists in cache. cacheVal, err := i.cache.Get(key) if err == nil { r, ok := cacheVal.(record.Record) if ok { return r } } return nil } // updateCache updates an entry in the interface cache. The given record may // not be locked, as updating the cache might write an (unrelated) evicted // record to the database in the process. If this happens while the // DelayedCacheWriter flushes the write cache with the same record present, // this will deadlock. func (i *Interface) updateCache(r record.Record, write bool, remove bool, ttl int64) (written bool) { // Check if cache is in use. if i.cache == nil { return false } // Check if record should be deleted if remove { // Remove entry from cache. i.cache.Remove(r.Key()) // Let write through to database storage. return false } // Update cache with record. if ttl >= 0 { _ = i.cache.SetWithExpire( r.Key(), r, time.Duration(ttl)*time.Second, ) } else { _ = i.cache.Set( r.Key(), r, ) } // Add record to write cache instead if: // 1. The record is being written. // 2. Write delaying is active. // 3. Write delaying is active for the database of this record. if write && r.DatabaseName() == i.options.DelayCachedWrites { i.writeCacheLock.Lock() defer i.writeCacheLock.Unlock() i.writeCache[r.Key()] = r return true } return false } ================================================ FILE: base/database/interface_cache_test.go ================================================ package database import ( "fmt" "strconv" "sync" "testing" "github.com/safing/portmaster/service/mgr" ) func benchmarkCacheWriting(b *testing.B, storageType string, cacheSize int, sampleSize int, delayWrites bool) { //nolint:gocognit,gocyclo,thelper b.Run(fmt.Sprintf("CacheWriting_%s_%d_%d_%v", storageType, cacheSize, sampleSize, delayWrites), func(b *testing.B) { // Setup Benchmark. // Create database. dbName := fmt.Sprintf("cache-w-benchmark-%s-%d-%d-%v", storageType, cacheSize, sampleSize, delayWrites) _, err := Register(&Database{ Name: dbName, Description: fmt.Sprintf("Cache Benchmark Database for %s", storageType), StorageType: storageType, }) if err != nil { b.Fatal(err) } // Create benchmark interface. options := &Options{ Local: true, Internal: true, CacheSize: cacheSize, } if cacheSize > 0 && delayWrites { options.DelayCachedWrites = dbName } db := NewInterface(options) // Start m := mgr.New("Cache writing benchmark test") var wg sync.WaitGroup if cacheSize > 0 && delayWrites { wg.Add(1) m.Go("Cache writing benchmark worker", func(wc *mgr.WorkerCtx) error { err := db.DelayedCacheWriter(wc) if err != nil { panic(err) } wg.Done() return nil }) } // Start Benchmark. b.ResetTimer() for i := range b.N { testRecordID := i % sampleSize r := NewExample( dbName+":"+strconv.Itoa(testRecordID), "A", 1, ) err = db.Put(r) if err != nil { b.Fatal(err) } } // End cache writer and wait m.Cancel() wg.Wait() }) } func benchmarkCacheReadWrite(b *testing.B, storageType string, cacheSize int, sampleSize int, delayWrites bool) { //nolint:gocognit,gocyclo,thelper b.Run(fmt.Sprintf("CacheReadWrite_%s_%d_%d_%v", storageType, cacheSize, sampleSize, delayWrites), func(b *testing.B) { // Setup Benchmark. // Create database. dbName := fmt.Sprintf("cache-rw-benchmark-%s-%d-%d-%v", storageType, cacheSize, sampleSize, delayWrites) _, err := Register(&Database{ Name: dbName, Description: fmt.Sprintf("Cache Benchmark Database for %s", storageType), StorageType: storageType, }) if err != nil { b.Fatal(err) } // Create benchmark interface. options := &Options{ Local: true, Internal: true, CacheSize: cacheSize, } if cacheSize > 0 && delayWrites { options.DelayCachedWrites = dbName } db := NewInterface(options) // Start m := mgr.New("Cache read/write benchmark test") var wg sync.WaitGroup if cacheSize > 0 && delayWrites { wg.Add(1) m.Go("Cache read/write benchmark worker", func(wc *mgr.WorkerCtx) error { err := db.DelayedCacheWriter(wc) if err != nil { panic(err) } wg.Done() return nil }) } // Start Benchmark. b.ResetTimer() writing := true for i := range b.N { testRecordID := i % sampleSize key := dbName + ":" + strconv.Itoa(testRecordID) if i > 0 && testRecordID == 0 { writing = !writing // switch between reading and writing every samplesize } if writing { r := NewExample(key, "A", 1) err = db.Put(r) } else { _, err = db.Get(key) } if err != nil { b.Fatal(err) } } // End cache writer and wait m.Cancel() wg.Wait() }) } func BenchmarkCache(b *testing.B) { for _, storageType := range []string{"bbolt", "hashmap"} { benchmarkCacheWriting(b, storageType, 32, 8, false) benchmarkCacheWriting(b, storageType, 32, 8, true) benchmarkCacheWriting(b, storageType, 32, 1024, false) benchmarkCacheWriting(b, storageType, 32, 1024, true) benchmarkCacheWriting(b, storageType, 512, 1024, false) benchmarkCacheWriting(b, storageType, 512, 1024, true) benchmarkCacheReadWrite(b, storageType, 32, 8, false) benchmarkCacheReadWrite(b, storageType, 32, 8, true) benchmarkCacheReadWrite(b, storageType, 32, 1024, false) benchmarkCacheReadWrite(b, storageType, 32, 1024, true) benchmarkCacheReadWrite(b, storageType, 512, 1024, false) benchmarkCacheReadWrite(b, storageType, 512, 1024, true) } } ================================================ FILE: base/database/iterator/iterator.go ================================================ package iterator import ( "sync" "github.com/tevino/abool" "github.com/safing/portmaster/base/database/record" ) // Iterator defines the iterator structure. type Iterator struct { Next chan record.Record Done chan struct{} errLock sync.Mutex err error doneClosed *abool.AtomicBool } // New creates a new Iterator. func New() *Iterator { return &Iterator{ Next: make(chan record.Record, 10), Done: make(chan struct{}), doneClosed: abool.NewBool(false), } } // Finish is called be the storage to signal the end of the query results. func (it *Iterator) Finish(err error) { close(it.Next) if it.doneClosed.SetToIf(false, true) { close(it.Done) } it.errLock.Lock() defer it.errLock.Unlock() it.err = err } // Cancel is called by the iteration consumer to cancel the running query. func (it *Iterator) Cancel() { if it.doneClosed.SetToIf(false, true) { close(it.Done) } } // Err returns the iterator error, if exists. func (it *Iterator) Err() error { it.errLock.Lock() defer it.errLock.Unlock() return it.err } ================================================ FILE: base/database/main.go ================================================ package database import ( "errors" "fmt" "path/filepath" "github.com/safing/portmaster/base/utils" "github.com/tevino/abool" ) var ( initialized = abool.NewBool(false) shuttingDown = abool.NewBool(false) shutdownSignal = make(chan struct{}) rootDir string ) // Initialize initializes the database at the specified location. func Initialize(databasesRootDir string) error { if initialized.SetToIf(false, true) { rootDir = databasesRootDir // ensure root and databases dirs err := utils.EnsureDirectory(rootDir, utils.AdminOnlyExecPermission) if err != nil { return fmt.Errorf("failed to create/check database dir %q: %w", rootDir, err) } return nil } return errors.New("database already initialized") } // Shutdown shuts down the whole database system. func Shutdown() (err error) { if shuttingDown.SetToIf(false, true) { close(shutdownSignal) } else { return } controllersLock.RLock() defer controllersLock.RUnlock() for _, c := range controllers { err = c.Shutdown() if err != nil { return } } return } // getLocation returns the storage location for the given name and type. func getLocation(name, storageType string) (string, error) { location := filepath.Join(rootDir, name, storageType) // Make sure location exists. err := utils.EnsureDirectory(location, utils.AdminOnlyExecPermission) if err != nil { return "", fmt.Errorf("failed to create/check database dir %q: %w", location, err) } return location, nil } ================================================ FILE: base/database/maintenance.go ================================================ package database import ( "context" "time" ) // Maintain runs the Maintain method on all storages. func Maintain(ctx context.Context) (err error) { // copy, as we might use the very long all := duplicateControllers() for _, c := range all { err = c.Maintain(ctx) if err != nil { return } } return } // MaintainThorough runs the MaintainThorough method on all storages. func MaintainThorough(ctx context.Context) (err error) { // copy, as we might use the very long all := duplicateControllers() for _, c := range all { err = c.MaintainThorough(ctx) if err != nil { return } } return } // MaintainRecordStates runs record state lifecycle maintenance on all storages. func MaintainRecordStates(ctx context.Context) (err error) { // delete immediately for now // TODO: increase purge threshold when starting to sync DBs purgeDeletedBefore := time.Now().UTC() // copy, as we might use the very long all := duplicateControllers() for _, c := range all { err = c.MaintainRecordStates(ctx, purgeDeletedBefore) if err != nil { return } } return } func duplicateControllers() (all []*Controller) { controllersLock.RLock() defer controllersLock.RUnlock() all = make([]*Controller, 0, len(controllers)) for _, c := range controllers { all = append(all, c) } return } ================================================ FILE: base/database/migration/error.go ================================================ package migration import "errors" // DiagnosticStep describes one migration step in the Diagnostics. type DiagnosticStep struct { Version string Description string } // Diagnostics holds a detailed error report about a failed migration. type Diagnostics struct { //nolint:errname // Message holds a human readable message of the encountered // error. Message string // Wrapped must be set to the underlying error that was encountered // while preparing or executing migrations. Wrapped error // StartOfMigration is set to the version of the database before // any migrations are applied. StartOfMigration string // LastSuccessfulMigration is set to the version of the database // which has been applied successfully before the error happened. LastSuccessfulMigration string // TargetVersion is set to the version of the database that the // migration run aimed for. That is, it's the last available version // added to the registry. TargetVersion string // ExecutionPlan is a list of migration steps that were planned to // be executed. ExecutionPlan []DiagnosticStep // FailedMigration is the description of the migration that has // failed. FailedMigration string } // Error returns a string representation of the migration error. func (err *Diagnostics) Error() string { msg := "" if err.FailedMigration != "" { msg = err.FailedMigration + ": " } if err.Message != "" { msg += err.Message + ": " } msg += err.Wrapped.Error() return msg } // Unwrap returns the actual error that happened when executing // a migration. It implements the interface required by the stdlib // errors package to support errors.Is() and errors.As(). func (err *Diagnostics) Unwrap() error { if u := errors.Unwrap(err.Wrapped); u != nil { return u } return err.Wrapped } ================================================ FILE: base/database/migration/migration.go ================================================ package migration import ( "context" "errors" "fmt" "sort" "sync" "time" "github.com/hashicorp/go-version" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/structures/dsd" ) // MigrateFunc is called when a migration should be applied to the // database. It receives the current version (from) and the target // version (to) of the database and a dedicated interface for // interacting with data stored in the DB. // A dedicated log.ContextTracer is added to ctx for each migration // run. type MigrateFunc func(ctx context.Context, from, to *version.Version, dbInterface *database.Interface) error // Migration represents a registered data-migration that should be applied to // some database. Migrations are stacked on top and executed in order of increasing // version number (see Version field). type Migration struct { // Description provides a short human-readable description of the // migration. Description string // Version should hold the version of the database/subsystem after // the migration has been applied. Version string // MigrateFuc is executed when the migration should be performed. MigrateFunc MigrateFunc } // Registry holds a migration stack. type Registry struct { key string lock sync.Mutex migrations []Migration } // New creates a new migration registry. // The key should be the name of the database key that is used to store // the version of the last successfully applied migration. func New(key string) *Registry { return &Registry{ key: key, } } // Add adds one or more migrations to reg. func (reg *Registry) Add(migrations ...Migration) error { reg.lock.Lock() defer reg.lock.Unlock() for _, m := range migrations { if _, err := version.NewSemver(m.Version); err != nil { return fmt.Errorf("migration %q: invalid version %s: %w", m.Description, m.Version, err) } reg.migrations = append(reg.migrations, m) } return nil } // Migrate migrates the database by executing all registered // migration in order of increasing version numbers. The error // returned, if not nil, is always of type *Diagnostics. func (reg *Registry) Migrate(ctx context.Context) (err error) { reg.lock.Lock() defer reg.lock.Unlock() start := time.Now() log.Infof("migration: migration of %s started", reg.key) defer func() { if err != nil { log.Errorf("migration: migration of %s failed after %s: %s", reg.key, time.Since(start), err) } else { log.Infof("migration: migration of %s finished after %s", reg.key, time.Since(start)) } }() db := database.NewInterface(&database.Options{ Local: true, Internal: true, }) startOfMigration, err := reg.getLatestSuccessfulMigration(db) if err != nil { return err } execPlan, diag, err := reg.getExecutionPlan(startOfMigration) if err != nil { return err } if len(execPlan) == 0 { return nil } diag.TargetVersion = execPlan[len(execPlan)-1].Version // finally, apply our migrations lastAppliedMigration := startOfMigration for _, m := range execPlan { target, _ := version.NewSemver(m.Version) // we can safely ignore the error here migrationCtx, tracer := log.AddTracer(ctx) if err := m.MigrateFunc(migrationCtx, lastAppliedMigration, target, db); err != nil { diag.Wrapped = err diag.FailedMigration = m.Description tracer.Errorf("migration: migration for %s failed: %s - %s", reg.key, target.String(), m.Description) tracer.Submit() return diag } lastAppliedMigration = target diag.LastSuccessfulMigration = lastAppliedMigration.String() if err := reg.saveLastSuccessfulMigration(db, target); err != nil { diag.Message = "failed to persist migration status" diag.Wrapped = err diag.FailedMigration = m.Description } tracer.Infof("migration: applied migration for %s: %s - %s", reg.key, target.String(), m.Description) tracer.Submit() } // all migrations have been applied successfully, we're done here return nil } func (reg *Registry) getLatestSuccessfulMigration(db *database.Interface) (*version.Version, error) { // find the latest version stored in the database rec, err := db.Get(reg.key) if errors.Is(err, database.ErrNotFound) { return nil, nil } if err != nil { return nil, &Diagnostics{ Message: "failed to query database for migration status", Wrapped: err, } } // Unwrap the record to get the actual database r, ok := rec.(*record.Wrapper) if !ok { return nil, &Diagnostics{ Wrapped: errors.New("expected wrapped database record"), } } sv, err := version.NewSemver(string(r.Data)) if err != nil { return nil, &Diagnostics{ Message: "failed to parse version stored in migration status record", Wrapped: err, } } return sv, nil } func (reg *Registry) saveLastSuccessfulMigration(db *database.Interface, ver *version.Version) error { r := &record.Wrapper{ Data: []byte(ver.String()), Format: dsd.RAW, } r.SetKey(reg.key) return db.Put(r) } func (reg *Registry) getExecutionPlan(startOfMigration *version.Version) ([]Migration, *Diagnostics, error) { // create a look-up map for migrations indexed by their semver created a // list of version (sorted by increasing number) that we use as our execution // plan. lm := make(map[string]Migration) versions := make(version.Collection, 0, len(reg.migrations)) for _, m := range reg.migrations { ver, err := version.NewSemver(m.Version) if err != nil { return nil, nil, &Diagnostics{ Message: "failed to parse version of migration", Wrapped: err, FailedMigration: m.Description, } } lm[ver.String()] = m // use .String() for a normalized string representation versions = append(versions, ver) } sort.Sort(versions) diag := new(Diagnostics) if startOfMigration != nil { diag.StartOfMigration = startOfMigration.String() } // prepare our diagnostics and the execution plan execPlan := make([]Migration, 0, len(versions)) for _, ver := range versions { // skip an migration that has already been applied. if startOfMigration != nil && startOfMigration.GreaterThanOrEqual(ver) { continue } m := lm[ver.String()] diag.ExecutionPlan = append(diag.ExecutionPlan, DiagnosticStep{ Description: m.Description, Version: ver.String(), }) execPlan = append(execPlan, m) } return execPlan, diag, nil } ================================================ FILE: base/database/query/README.md ================================================ # Query ## Control Flow - Grouping with `(` and `)` - Chaining with `and` and `or` - _NO_ mixing! Be explicit and use grouping. - Negation with `not` - in front of expression for group: `not (...)` - inside expression for clause: `name not matches "^King "` ## Selectors Supported by all feeders: - root level field: `field` - sub level field: `field.sub` - array/slice/map access: `map.0` - array/slice/map length: `map.#` Please note that some feeders may have other special characters. It is advised to only use alphanumeric characters for keys. ## Operators | Name | Textual | Req. Type | Internal Type | Compared with | |-------------------------|--------------------|-----------|---------------|---------------------------| | Equals | `==` | int | int64 | `==` | | GreaterThan | `>` | int | int64 | `>` | | GreaterThanOrEqual | `>=` | int | int64 | `>=` | | LessThan | `<` | int | int64 | `<` | | LessThanOrEqual | `<=` | int | int64 | `<=` | | FloatEquals | `f==` | float | float64 | `==` | | FloatGreaterThan | `f>` | float | float64 | `>` | | FloatGreaterThanOrEqual | `f>=` | float | float64 | `>=` | | FloatLessThan | `f<` | float | float64 | `<` | | FloatLessThanOrEqual | `f<=` | float | float64 | `<=` | | SameAs | `sameas`, `s==` | string | string | `==` | | Contains | `contains`, `co` | string | string | `strings.Contains()` | | StartsWith | `startswith`, `sw` | string | string | `strings.HasPrefix()` | | EndsWith | `endswith`, `ew` | string | string | `strings.HasSuffix()` | | In | `in` | string | string | for loop with `==` | | Matches | `matches`, `re` | string | string | `regexp.Regexp.Matches()` | | Is | `is` | bool* | bool | `==` | | Exists | `exists`, `ex` | any | n/a | n/a | \*accepts strings: 1, t, T, true, True, TRUE, 0, f, F, false, False, FALSE ## Escaping If you need to use a control character within a value (ie. not for controlling), escape it with `\`. It is recommended to wrap a word into parenthesis instead of escaping control characters, when possible. | Location | Characters to be escaped | |---|---| | Within parenthesis (`"`) | `"`, `\` | | Everywhere else | `(`, `)`, `"`, `\`, `\t`, `\r`, `\n`, ` ` (space) | ================================================ FILE: base/database/query/condition-and.go ================================================ package query import ( "fmt" "strings" "github.com/safing/portmaster/base/database/accessor" ) // And combines multiple conditions with a logical _AND_ operator. func And(conditions ...Condition) Condition { return &andCond{ conditions: conditions, } } type andCond struct { conditions []Condition } func (c *andCond) complies(acc accessor.Accessor) bool { for _, cond := range c.conditions { if !cond.complies(acc) { return false } } return true } func (c *andCond) check() (err error) { for _, cond := range c.conditions { err = cond.check() if err != nil { return err } } return nil } func (c *andCond) string() string { all := make([]string, 0, len(c.conditions)) for _, cond := range c.conditions { all = append(all, cond.string()) } return fmt.Sprintf("(%s)", strings.Join(all, " and ")) } ================================================ FILE: base/database/query/condition-bool.go ================================================ package query import ( "errors" "fmt" "strconv" "github.com/safing/portmaster/base/database/accessor" ) type boolCondition struct { key string operator uint8 value bool } func newBoolCondition(key string, operator uint8, value interface{}) *boolCondition { var parsedValue bool switch v := value.(type) { case bool: parsedValue = v case string: var err error parsedValue, err = strconv.ParseBool(v) if err != nil { return &boolCondition{ key: fmt.Sprintf("could not parse \"%s\" to bool: %s", v, err), operator: errorPresent, } } default: return &boolCondition{ key: fmt.Sprintf("incompatible value %v for int64", value), operator: errorPresent, } } return &boolCondition{ key: key, operator: operator, value: parsedValue, } } func (c *boolCondition) complies(acc accessor.Accessor) bool { comp, ok := acc.GetBool(c.key) if !ok { return false } switch c.operator { case Is: return comp == c.value default: return false } } func (c *boolCondition) check() error { if c.operator == errorPresent { return errors.New(c.key) } return nil } func (c *boolCondition) string() string { return fmt.Sprintf("%s %s %t", escapeString(c.key), getOpName(c.operator), c.value) } ================================================ FILE: base/database/query/condition-error.go ================================================ package query import ( "github.com/safing/portmaster/base/database/accessor" ) type errorCondition struct { err error } func newErrorCondition(err error) *errorCondition { return &errorCondition{ err: err, } } func (c *errorCondition) complies(acc accessor.Accessor) bool { return false } func (c *errorCondition) check() error { return c.err } func (c *errorCondition) string() string { return "[ERROR]" } ================================================ FILE: base/database/query/condition-exists.go ================================================ package query import ( "errors" "fmt" "github.com/safing/portmaster/base/database/accessor" ) type existsCondition struct { key string operator uint8 } func newExistsCondition(key string, operator uint8) *existsCondition { return &existsCondition{ key: key, operator: operator, } } func (c *existsCondition) complies(acc accessor.Accessor) bool { return acc.Exists(c.key) } func (c *existsCondition) check() error { if c.operator == errorPresent { return errors.New(c.key) } return nil } func (c *existsCondition) string() string { return fmt.Sprintf("%s %s", escapeString(c.key), getOpName(c.operator)) } ================================================ FILE: base/database/query/condition-float.go ================================================ package query import ( "errors" "fmt" "strconv" "github.com/safing/portmaster/base/database/accessor" ) type floatCondition struct { key string operator uint8 value float64 } func newFloatCondition(key string, operator uint8, value interface{}) *floatCondition { var parsedValue float64 switch v := value.(type) { case int: parsedValue = float64(v) case int8: parsedValue = float64(v) case int16: parsedValue = float64(v) case int32: parsedValue = float64(v) case int64: parsedValue = float64(v) case uint: parsedValue = float64(v) case uint8: parsedValue = float64(v) case uint16: parsedValue = float64(v) case uint32: parsedValue = float64(v) case float32: parsedValue = float64(v) case float64: parsedValue = v case string: var err error parsedValue, err = strconv.ParseFloat(v, 64) if err != nil { return &floatCondition{ key: fmt.Sprintf("could not parse %s to float64: %s", v, err), operator: errorPresent, } } default: return &floatCondition{ key: fmt.Sprintf("incompatible value %v for float64", value), operator: errorPresent, } } return &floatCondition{ key: key, operator: operator, value: parsedValue, } } func (c *floatCondition) complies(acc accessor.Accessor) bool { comp, ok := acc.GetFloat(c.key) if !ok { return false } switch c.operator { case FloatEquals: return comp == c.value case FloatGreaterThan: return comp > c.value case FloatGreaterThanOrEqual: return comp >= c.value case FloatLessThan: return comp < c.value case FloatLessThanOrEqual: return comp <= c.value default: return false } } func (c *floatCondition) check() error { if c.operator == errorPresent { return errors.New(c.key) } return nil } func (c *floatCondition) string() string { return fmt.Sprintf("%s %s %g", escapeString(c.key), getOpName(c.operator), c.value) } ================================================ FILE: base/database/query/condition-int.go ================================================ package query import ( "errors" "fmt" "strconv" "github.com/safing/portmaster/base/database/accessor" ) type intCondition struct { key string operator uint8 value int64 } func newIntCondition(key string, operator uint8, value interface{}) *intCondition { var parsedValue int64 switch v := value.(type) { case int: parsedValue = int64(v) case int8: parsedValue = int64(v) case int16: parsedValue = int64(v) case int32: parsedValue = int64(v) case int64: parsedValue = v case uint: parsedValue = int64(v) case uint8: parsedValue = int64(v) case uint16: parsedValue = int64(v) case uint32: parsedValue = int64(v) case string: var err error parsedValue, err = strconv.ParseInt(v, 10, 64) if err != nil { return &intCondition{ key: fmt.Sprintf("could not parse %s to int64: %s (hint: use \"sameas\" to compare strings)", v, err), operator: errorPresent, } } default: return &intCondition{ key: fmt.Sprintf("incompatible value %v for int64", value), operator: errorPresent, } } return &intCondition{ key: key, operator: operator, value: parsedValue, } } func (c *intCondition) complies(acc accessor.Accessor) bool { comp, ok := acc.GetInt(c.key) if !ok { return false } switch c.operator { case Equals: return comp == c.value case GreaterThan: return comp > c.value case GreaterThanOrEqual: return comp >= c.value case LessThan: return comp < c.value case LessThanOrEqual: return comp <= c.value default: return false } } func (c *intCondition) check() error { if c.operator == errorPresent { return errors.New(c.key) } return nil } func (c *intCondition) string() string { return fmt.Sprintf("%s %s %d", escapeString(c.key), getOpName(c.operator), c.value) } ================================================ FILE: base/database/query/condition-not.go ================================================ package query import ( "fmt" "strings" "github.com/safing/portmaster/base/database/accessor" ) // Not negates the supplied condition. func Not(c Condition) Condition { return ¬Cond{ notC: c, } } type notCond struct { notC Condition } func (c *notCond) complies(acc accessor.Accessor) bool { return !c.notC.complies(acc) } func (c *notCond) check() error { return c.notC.check() } func (c *notCond) string() string { next := c.notC.string() if strings.HasPrefix(next, "(") { return fmt.Sprintf("not %s", c.notC.string()) } splitted := strings.Split(next, " ") return strings.Join(append([]string{splitted[0], "not"}, splitted[1:]...), " ") } ================================================ FILE: base/database/query/condition-or.go ================================================ package query import ( "fmt" "strings" "github.com/safing/portmaster/base/database/accessor" ) // Or combines multiple conditions with a logical _OR_ operator. func Or(conditions ...Condition) Condition { return &orCond{ conditions: conditions, } } type orCond struct { conditions []Condition } func (c *orCond) complies(acc accessor.Accessor) bool { for _, cond := range c.conditions { if cond.complies(acc) { return true } } return false } func (c *orCond) check() (err error) { for _, cond := range c.conditions { err = cond.check() if err != nil { return err } } return nil } func (c *orCond) string() string { all := make([]string, 0, len(c.conditions)) for _, cond := range c.conditions { all = append(all, cond.string()) } return fmt.Sprintf("(%s)", strings.Join(all, " or ")) } ================================================ FILE: base/database/query/condition-regex.go ================================================ package query import ( "errors" "fmt" "regexp" "github.com/safing/portmaster/base/database/accessor" ) type regexCondition struct { key string operator uint8 regex *regexp.Regexp } func newRegexCondition(key string, operator uint8, value interface{}) *regexCondition { switch v := value.(type) { case string: r, err := regexp.Compile(v) if err != nil { return ®exCondition{ key: fmt.Sprintf("could not compile regex \"%s\": %s", v, err), operator: errorPresent, } } return ®exCondition{ key: key, operator: operator, regex: r, } default: return ®exCondition{ key: fmt.Sprintf("incompatible value %v for string", value), operator: errorPresent, } } } func (c *regexCondition) complies(acc accessor.Accessor) bool { comp, ok := acc.GetString(c.key) if !ok { return false } switch c.operator { case Matches: return c.regex.MatchString(comp) default: return false } } func (c *regexCondition) check() error { if c.operator == errorPresent { return errors.New(c.key) } return nil } func (c *regexCondition) string() string { return fmt.Sprintf("%s %s %s", escapeString(c.key), getOpName(c.operator), escapeString(c.regex.String())) } ================================================ FILE: base/database/query/condition-string.go ================================================ package query import ( "errors" "fmt" "strings" "github.com/safing/portmaster/base/database/accessor" ) type stringCondition struct { key string operator uint8 value string } func newStringCondition(key string, operator uint8, value interface{}) *stringCondition { switch v := value.(type) { case string: return &stringCondition{ key: key, operator: operator, value: v, } default: return &stringCondition{ key: fmt.Sprintf("incompatible value %v for string", value), operator: errorPresent, } } } func (c *stringCondition) complies(acc accessor.Accessor) bool { comp, ok := acc.GetString(c.key) if !ok { return false } switch c.operator { case SameAs: return c.value == comp case Contains: return strings.Contains(comp, c.value) case StartsWith: return strings.HasPrefix(comp, c.value) case EndsWith: return strings.HasSuffix(comp, c.value) default: return false } } func (c *stringCondition) check() error { if c.operator == errorPresent { return errors.New(c.key) } return nil } func (c *stringCondition) string() string { return fmt.Sprintf("%s %s %s", escapeString(c.key), getOpName(c.operator), escapeString(c.value)) } ================================================ FILE: base/database/query/condition-stringslice.go ================================================ package query import ( "fmt" "strings" "github.com/safing/portmaster/base/database/accessor" "github.com/safing/portmaster/base/utils" ) type stringSliceCondition struct { key string operator uint8 value []string } func newStringSliceCondition(key string, operator uint8, value interface{}) *stringSliceCondition { switch v := value.(type) { case string: parsedValue := strings.Split(v, ",") if len(parsedValue) < 2 { return &stringSliceCondition{ key: v, operator: errorPresent, } } return &stringSliceCondition{ key: key, operator: operator, value: parsedValue, } case []string: return &stringSliceCondition{ key: key, operator: operator, value: v, } default: return &stringSliceCondition{ key: fmt.Sprintf("incompatible value %v for []string", value), operator: errorPresent, } } } func (c *stringSliceCondition) complies(acc accessor.Accessor) bool { comp, ok := acc.GetString(c.key) if !ok { return false } switch c.operator { case In: return utils.StringInSlice(c.value, comp) default: return false } } func (c *stringSliceCondition) check() error { if c.operator == errorPresent { return fmt.Errorf("could not parse \"%s\" to []string", c.key) } return nil } func (c *stringSliceCondition) string() string { return fmt.Sprintf("%s %s %s", escapeString(c.key), getOpName(c.operator), escapeString(strings.Join(c.value, ","))) } ================================================ FILE: base/database/query/condition.go ================================================ package query import ( "fmt" "github.com/safing/portmaster/base/database/accessor" ) // Condition is an interface to provide a common api to all condition types. type Condition interface { complies(acc accessor.Accessor) bool check() error string() string } // Operators. const ( Equals uint8 = iota // int GreaterThan // int GreaterThanOrEqual // int LessThan // int LessThanOrEqual // int FloatEquals // float FloatGreaterThan // float FloatGreaterThanOrEqual // float FloatLessThan // float FloatLessThanOrEqual // float SameAs // string Contains // string StartsWith // string EndsWith // string In // stringSlice Matches // regex Is // bool: accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE Exists // any errorPresent uint8 = 255 ) // Where returns a condition to add to a query. func Where(key string, operator uint8, value interface{}) Condition { switch operator { case Equals, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual: return newIntCondition(key, operator, value) case FloatEquals, FloatGreaterThan, FloatGreaterThanOrEqual, FloatLessThan, FloatLessThanOrEqual: return newFloatCondition(key, operator, value) case SameAs, Contains, StartsWith, EndsWith: return newStringCondition(key, operator, value) case In: return newStringSliceCondition(key, operator, value) case Matches: return newRegexCondition(key, operator, value) case Is: return newBoolCondition(key, operator, value) case Exists: return newExistsCondition(key, operator) default: return newErrorCondition(fmt.Errorf("no operator with ID %d", operator)) } } ================================================ FILE: base/database/query/condition_test.go ================================================ package query import "testing" func testSuccess(t *testing.T, c Condition) { t.Helper() err := c.check() if err != nil { t.Errorf("failed: %s", err) } } func TestInterfaces(t *testing.T) { t.Parallel() testSuccess(t, newIntCondition("banana", Equals, uint(1))) testSuccess(t, newIntCondition("banana", Equals, uint8(1))) testSuccess(t, newIntCondition("banana", Equals, uint16(1))) testSuccess(t, newIntCondition("banana", Equals, uint32(1))) testSuccess(t, newIntCondition("banana", Equals, int(1))) testSuccess(t, newIntCondition("banana", Equals, int8(1))) testSuccess(t, newIntCondition("banana", Equals, int16(1))) testSuccess(t, newIntCondition("banana", Equals, int32(1))) testSuccess(t, newIntCondition("banana", Equals, int64(1))) testSuccess(t, newIntCondition("banana", Equals, "1")) testSuccess(t, newFloatCondition("banana", FloatEquals, uint(1))) testSuccess(t, newFloatCondition("banana", FloatEquals, uint8(1))) testSuccess(t, newFloatCondition("banana", FloatEquals, uint16(1))) testSuccess(t, newFloatCondition("banana", FloatEquals, uint32(1))) testSuccess(t, newFloatCondition("banana", FloatEquals, int(1))) testSuccess(t, newFloatCondition("banana", FloatEquals, int8(1))) testSuccess(t, newFloatCondition("banana", FloatEquals, int16(1))) testSuccess(t, newFloatCondition("banana", FloatEquals, int32(1))) testSuccess(t, newFloatCondition("banana", FloatEquals, int64(1))) testSuccess(t, newFloatCondition("banana", FloatEquals, float32(1))) testSuccess(t, newFloatCondition("banana", FloatEquals, float64(1))) testSuccess(t, newFloatCondition("banana", FloatEquals, "1.1")) testSuccess(t, newStringCondition("banana", SameAs, "coconut")) testSuccess(t, newRegexCondition("banana", Matches, "coconut")) testSuccess(t, newStringSliceCondition("banana", FloatEquals, []string{"banana", "coconut"})) testSuccess(t, newStringSliceCondition("banana", FloatEquals, "banana,coconut")) } func testCondError(t *testing.T, c Condition) { t.Helper() err := c.check() if err == nil { t.Error("should fail") } } func TestConditionErrors(t *testing.T) { t.Parallel() // test invalid value types testCondError(t, newBoolCondition("banana", Is, 1)) testCondError(t, newFloatCondition("banana", FloatEquals, true)) testCondError(t, newIntCondition("banana", Equals, true)) testCondError(t, newStringCondition("banana", SameAs, 1)) testCondError(t, newRegexCondition("banana", Matches, 1)) testCondError(t, newStringSliceCondition("banana", Matches, 1)) // test error presence testCondError(t, newBoolCondition("banana", errorPresent, true)) testCondError(t, And(newBoolCondition("banana", errorPresent, true))) testCondError(t, Or(newBoolCondition("banana", errorPresent, true))) testCondError(t, newExistsCondition("banana", errorPresent)) testCondError(t, newFloatCondition("banana", errorPresent, 1.1)) testCondError(t, newIntCondition("banana", errorPresent, 1)) testCondError(t, newStringCondition("banana", errorPresent, "coconut")) testCondError(t, newRegexCondition("banana", errorPresent, "coconut")) } func TestWhere(t *testing.T) { t.Parallel() c := Where("", 254, nil) err := c.check() if err == nil { t.Error("should fail") } } ================================================ FILE: base/database/query/operators.go ================================================ package query var ( operatorNames = map[string]uint8{ "==": Equals, ">": GreaterThan, ">=": GreaterThanOrEqual, "<": LessThan, "<=": LessThanOrEqual, "f==": FloatEquals, "f>": FloatGreaterThan, "f>=": FloatGreaterThanOrEqual, "f<": FloatLessThan, "f<=": FloatLessThanOrEqual, "sameas": SameAs, "s==": SameAs, "contains": Contains, "co": Contains, "startswith": StartsWith, "sw": StartsWith, "endswith": EndsWith, "ew": EndsWith, "in": In, "matches": Matches, "re": Matches, "is": Is, "exists": Exists, "ex": Exists, } primaryNames = make(map[uint8]string) ) func init() { for opName, opID := range operatorNames { name, ok := primaryNames[opID] if ok { if len(name) < len(opName) { primaryNames[opID] = opName } } else { primaryNames[opID] = opName } } } func getOpName(operator uint8) string { name, ok := primaryNames[operator] if ok { return name } return "[unknown]" } ================================================ FILE: base/database/query/operators_test.go ================================================ package query import "testing" func TestGetOpName(t *testing.T) { t.Parallel() if getOpName(254) != "[unknown]" { t.Error("unexpected output") } } ================================================ FILE: base/database/query/parser.go ================================================ package query import ( "errors" "fmt" "regexp" "strconv" "strings" ) type snippet struct { text string globalPosition int } // ParseQuery parses a plaintext query. Special characters (that must be escaped with a '\') are: `\()` and any whitespaces. // //nolint:gocognit func ParseQuery(query string) (*Query, error) { snippets, err := extractSnippets(query) if err != nil { return nil, err } snippetsPos := 0 getSnippet := func() (*snippet, error) { // order is important, as parseAndOr will always consume one additional snippet. snippetsPos++ if snippetsPos > len(snippets) { return nil, fmt.Errorf("unexpected end at position %d", len(query)) } return snippets[snippetsPos-1], nil } remainingSnippets := func() int { return len(snippets) - snippetsPos } // check for query word queryWord, err := getSnippet() if err != nil { return nil, err } if queryWord.text != "query" { return nil, errors.New("queries must start with \"query\"") } // get prefix prefix, err := getSnippet() if err != nil { return nil, err } q := New(prefix.text) for remainingSnippets() > 0 { command, err := getSnippet() if err != nil { return nil, err } switch command.text { case "where": if q.where != nil { return nil, fmt.Errorf("duplicate \"%s\" clause found at position %d", command.text, command.globalPosition) } // parse conditions condition, err := parseAndOr(getSnippet, remainingSnippets, true) if err != nil { return nil, err } // go one back, as parseAndOr had to check if its done snippetsPos-- q.Where(condition) case "orderby": if q.orderBy != "" { return nil, fmt.Errorf("duplicate \"%s\" clause found at position %d", command.text, command.globalPosition) } orderBySnippet, err := getSnippet() if err != nil { return nil, err } q.OrderBy(orderBySnippet.text) case "limit": if q.limit != 0 { return nil, fmt.Errorf("duplicate \"%s\" clause found at position %d", command.text, command.globalPosition) } limitSnippet, err := getSnippet() if err != nil { return nil, err } limit, err := strconv.ParseUint(limitSnippet.text, 10, 31) if err != nil { return nil, fmt.Errorf("could not parse integer (%s) at position %d", limitSnippet.text, limitSnippet.globalPosition) } q.Limit(int(limit)) case "offset": if q.offset != 0 { return nil, fmt.Errorf("duplicate \"%s\" clause found at position %d", command.text, command.globalPosition) } offsetSnippet, err := getSnippet() if err != nil { return nil, err } offset, err := strconv.ParseUint(offsetSnippet.text, 10, 31) if err != nil { return nil, fmt.Errorf("could not parse integer (%s) at position %d", offsetSnippet.text, offsetSnippet.globalPosition) } q.Offset(int(offset)) default: return nil, fmt.Errorf("unknown clause \"%s\" at position %d", command.text, command.globalPosition) } } return q.Check() } func extractSnippets(text string) (snippets []*snippet, err error) { skip := false start := -1 inParenthesis := false var pos int var char rune for pos, char = range text { // skip if skip { skip = false continue } if char == '\\' { skip = true } // wait for parenthesis to be overs if inParenthesis { if char == '"' { snippets = append(snippets, &snippet{ text: prepToken(text[start+1 : pos]), globalPosition: start + 1, }) start = -1 inParenthesis = false } continue } // handle segments switch char { case '\t', '\n', '\r', ' ', '(', ')': if start >= 0 { snippets = append(snippets, &snippet{ text: prepToken(text[start:pos]), globalPosition: start + 1, }) start = -1 } default: if start == -1 { start = pos } } // handle special segment characters switch char { case '(', ')': snippets = append(snippets, &snippet{ text: text[pos : pos+1], globalPosition: pos + 1, }) case '"': if start < pos { return nil, fmt.Errorf("parenthesis ('\"') may not be used within words, please escape with '\\' (position: %d)", pos+1) } inParenthesis = true } } // add last if start >= 0 { snippets = append(snippets, &snippet{ text: prepToken(text[start : pos+1]), globalPosition: start + 1, }) } return snippets, nil } //nolint:gocognit func parseAndOr(getSnippet func() (*snippet, error), remainingSnippets func() int, rootCondition bool) (Condition, error) { var ( isOr = false typeSet = false wrapInNot = false expectingMore = true conditions []Condition ) for { if !expectingMore && rootCondition && remainingSnippets() == 0 { // advance snippetsPos by one, as it will be set back by 1 _, _ = getSnippet() if len(conditions) == 1 { return conditions[0], nil } if isOr { return Or(conditions...), nil } return And(conditions...), nil } firstSnippet, err := getSnippet() if err != nil { return nil, err } if !expectingMore && rootCondition { switch firstSnippet.text { case "orderby", "limit", "offset": if len(conditions) == 1 { return conditions[0], nil } if isOr { return Or(conditions...), nil } return And(conditions...), nil } } switch firstSnippet.text { case "(": condition, err := parseAndOr(getSnippet, remainingSnippets, false) if err != nil { return nil, err } if wrapInNot { conditions = append(conditions, Not(condition)) wrapInNot = false } else { conditions = append(conditions, condition) } expectingMore = true case ")": if len(conditions) == 1 { return conditions[0], nil } if isOr { return Or(conditions...), nil } return And(conditions...), nil case "and": if typeSet && isOr { return nil, fmt.Errorf("you may not mix \"and\" and \"or\" (position: %d)", firstSnippet.globalPosition) } isOr = false typeSet = true expectingMore = true case "or": if typeSet && !isOr { return nil, fmt.Errorf("you may not mix \"and\" and \"or\" (position: %d)", firstSnippet.globalPosition) } isOr = true typeSet = true expectingMore = true case "not": wrapInNot = true expectingMore = true default: condition, err := parseCondition(firstSnippet, getSnippet) if err != nil { return nil, err } if wrapInNot { conditions = append(conditions, Not(condition)) wrapInNot = false } else { conditions = append(conditions, condition) } expectingMore = false } } } func parseCondition(firstSnippet *snippet, getSnippet func() (*snippet, error)) (Condition, error) { wrapInNot := false // get operator name opName, err := getSnippet() if err != nil { return nil, err } // negate? if opName.text == "not" { wrapInNot = true opName, err = getSnippet() if err != nil { return nil, err } } // get operator operator, ok := operatorNames[opName.text] if !ok { return nil, fmt.Errorf("unknown operator at position %d", opName.globalPosition) } // don't need a value for "exists" if operator == Exists { if wrapInNot { return Not(Where(firstSnippet.text, operator, nil)), nil } return Where(firstSnippet.text, operator, nil), nil } // get value value, err := getSnippet() if err != nil { return nil, err } if wrapInNot { return Not(Where(firstSnippet.text, operator, value.text)), nil } return Where(firstSnippet.text, operator, value.text), nil } var escapeReplacer = regexp.MustCompile(`\\([^\\])`) // prepToken removes surrounding parenthesis and escape characters. func prepToken(text string) string { return escapeReplacer.ReplaceAllString(strings.Trim(text, "\""), "$1") } // escapeString correctly escapes a snippet for printing. func escapeString(token string) string { // check if token contains characters that need to be escaped if strings.ContainsAny(token, "()\"\\\t\r\n ") { // put the token in parenthesis and only escape \ and " return fmt.Sprintf("\"%s\"", strings.ReplaceAll(token, "\"", "\\\"")) } return token } ================================================ FILE: base/database/query/parser_test.go ================================================ package query import ( "reflect" "testing" "github.com/davecgh/go-spew/spew" ) func TestExtractSnippets(t *testing.T) { t.Parallel() text1 := `query test: where ( "bananas" > 100 and monkeys.# <= "12")or(coconuts < 10 "and" area > 50) or name sameas Julian or name matches ^King\ ` result1 := []*snippet{ {text: "query", globalPosition: 1}, {text: "test:", globalPosition: 7}, {text: "where", globalPosition: 13}, {text: "(", globalPosition: 19}, {text: "bananas", globalPosition: 21}, {text: ">", globalPosition: 31}, {text: "100", globalPosition: 33}, {text: "and", globalPosition: 37}, {text: "monkeys.#", globalPosition: 41}, {text: "<=", globalPosition: 51}, {text: "12", globalPosition: 54}, {text: ")", globalPosition: 58}, {text: "or", globalPosition: 59}, {text: "(", globalPosition: 61}, {text: "coconuts", globalPosition: 62}, {text: "<", globalPosition: 71}, {text: "10", globalPosition: 73}, {text: "and", globalPosition: 76}, {text: "area", globalPosition: 82}, {text: ">", globalPosition: 87}, {text: "50", globalPosition: 89}, {text: ")", globalPosition: 91}, {text: "or", globalPosition: 93}, {text: "name", globalPosition: 96}, {text: "sameas", globalPosition: 101}, {text: "Julian", globalPosition: 108}, {text: "or", globalPosition: 115}, {text: "name", globalPosition: 118}, {text: "matches", globalPosition: 123}, {text: "^King ", globalPosition: 131}, } snippets, err := extractSnippets(text1) if err != nil { t.Errorf("failed to extract snippets: %s", err) } if !reflect.DeepEqual(result1, snippets) { t.Errorf("unexpected results:") for _, el := range snippets { t.Errorf("%+v", el) } } // t.Error(spew.Sprintf("%v", treeElement)) } func testParsing(t *testing.T, queryText string, expectedResult *Query) { t.Helper() _, err := expectedResult.Check() if err != nil { t.Errorf("failed to create query: %s", err) return } q, err := ParseQuery(queryText) if err != nil { t.Errorf("failed to parse query: %s", err) return } if queryText != q.Print() { t.Errorf("string match failed: %s", q.Print()) return } if !reflect.DeepEqual(expectedResult, q) { t.Error("deepqual match failed.") t.Error("got:") t.Error(spew.Sdump(q)) t.Error("expected:") t.Error(spew.Sdump(expectedResult)) } } func TestParseQuery(t *testing.T) { t.Parallel() text1 := `query test: where (bananas > 100 and monkeys.# <= 12) or not (coconuts < 10 and area not > 50) or name sameas Julian or name matches "^King " orderby name limit 10 offset 20` result1 := New("test:").Where(Or( And( Where("bananas", GreaterThan, 100), Where("monkeys.#", LessThanOrEqual, 12), ), Not(And( Where("coconuts", LessThan, 10), Not(Where("area", GreaterThan, 50)), )), Where("name", SameAs, "Julian"), Where("name", Matches, "^King "), )).OrderBy("name").Limit(10).Offset(20) testParsing(t, text1, result1) testParsing(t, `query test: orderby name`, New("test:").OrderBy("name")) testParsing(t, `query test: limit 10`, New("test:").Limit(10)) testParsing(t, `query test: offset 10`, New("test:").Offset(10)) testParsing(t, `query test: where banana matches ^ban`, New("test:").Where(Where("banana", Matches, "^ban"))) testParsing(t, `query test: where banana exists`, New("test:").Where(Where("banana", Exists, nil))) testParsing(t, `query test: where banana not exists`, New("test:").Where(Not(Where("banana", Exists, nil)))) // test all operators testParsing(t, `query test: where banana == 1`, New("test:").Where(Where("banana", Equals, 1))) testParsing(t, `query test: where banana > 1`, New("test:").Where(Where("banana", GreaterThan, 1))) testParsing(t, `query test: where banana >= 1`, New("test:").Where(Where("banana", GreaterThanOrEqual, 1))) testParsing(t, `query test: where banana < 1`, New("test:").Where(Where("banana", LessThan, 1))) testParsing(t, `query test: where banana <= 1`, New("test:").Where(Where("banana", LessThanOrEqual, 1))) testParsing(t, `query test: where banana f== 1.1`, New("test:").Where(Where("banana", FloatEquals, 1.1))) testParsing(t, `query test: where banana f> 1.1`, New("test:").Where(Where("banana", FloatGreaterThan, 1.1))) testParsing(t, `query test: where banana f>= 1.1`, New("test:").Where(Where("banana", FloatGreaterThanOrEqual, 1.1))) testParsing(t, `query test: where banana f< 1.1`, New("test:").Where(Where("banana", FloatLessThan, 1.1))) testParsing(t, `query test: where banana f<= 1.1`, New("test:").Where(Where("banana", FloatLessThanOrEqual, 1.1))) testParsing(t, `query test: where banana sameas banana`, New("test:").Where(Where("banana", SameAs, "banana"))) testParsing(t, `query test: where banana contains banana`, New("test:").Where(Where("banana", Contains, "banana"))) testParsing(t, `query test: where banana startswith banana`, New("test:").Where(Where("banana", StartsWith, "banana"))) testParsing(t, `query test: where banana endswith banana`, New("test:").Where(Where("banana", EndsWith, "banana"))) testParsing(t, `query test: where banana in banana,coconut`, New("test:").Where(Where("banana", In, []string{"banana", "coconut"}))) testParsing(t, `query test: where banana matches banana`, New("test:").Where(Where("banana", Matches, "banana"))) testParsing(t, `query test: where banana is true`, New("test:").Where(Where("banana", Is, true))) testParsing(t, `query test: where banana exists`, New("test:").Where(Where("banana", Exists, nil))) // special testParsing(t, `query test: where banana not exists`, New("test:").Where(Not(Where("banana", Exists, nil)))) } func testParseError(t *testing.T, queryText string, expectedErrorString string) { t.Helper() _, err := ParseQuery(queryText) if err == nil { t.Errorf("should fail to parse: %s", queryText) return } if err.Error() != expectedErrorString { t.Errorf("unexpected error for query: %s\nwanted: %s\n got: %s", queryText, expectedErrorString, err) } } func TestParseErrors(t *testing.T) { t.Parallel() // syntax testParseError(t, `query`, `unexpected end at position 5`) testParseError(t, `query test: where`, `unexpected end at position 17`) testParseError(t, `query test: where (`, `unexpected end at position 19`) testParseError(t, `query test: where )`, `unknown clause ")" at position 19`) testParseError(t, `query test: where not`, `unexpected end at position 21`) testParseError(t, `query test: where banana`, `unexpected end at position 24`) testParseError(t, `query test: where banana >`, `unexpected end at position 26`) testParseError(t, `query test: where banana nope`, `unknown operator at position 26`) testParseError(t, `query test: where banana exists or`, `unexpected end at position 34`) testParseError(t, `query test: where banana exists and`, `unexpected end at position 35`) testParseError(t, `query test: where banana exists and (`, `unexpected end at position 37`) testParseError(t, `query test: where banana exists and banana is true or`, `you may not mix "and" and "or" (position: 52)`) testParseError(t, `query test: where banana exists or banana is true and`, `you may not mix "and" and "or" (position: 51)`) // testParseError(t, `query test: where banana exists and (`, ``) // value parsing error testParseError(t, `query test: where banana == banana`, `could not parse banana to int64: strconv.ParseInt: parsing "banana": invalid syntax (hint: use "sameas" to compare strings)`) testParseError(t, `query test: where banana f== banana`, `could not parse banana to float64: strconv.ParseFloat: parsing "banana": invalid syntax`) testParseError(t, `query test: where banana in banana`, `could not parse "banana" to []string`) testParseError(t, `query test: where banana matches [banana`, "could not compile regex \"[banana\": error parsing regexp: missing closing ]: `[banana`") testParseError(t, `query test: where banana is great`, `could not parse "great" to bool: strconv.ParseBool: parsing "great": invalid syntax`) } ================================================ FILE: base/database/query/query.go ================================================ package query import ( "fmt" "strings" "github.com/safing/portmaster/base/database/accessor" "github.com/safing/portmaster/base/database/record" ) // Example: // q.New("core:/", // q.Where("a", q.GreaterThan, 0), // q.Where("b", q.Equals, 0), // q.Or( // q.Where("c", q.StartsWith, "x"), // q.Where("d", q.Contains, "y") // ) // ) // Query contains a compiled query. type Query struct { checked bool dbName string dbKeyPrefix string where Condition orderBy string limit int offset int } // New creates a new query with the supplied prefix. func New(prefix string) *Query { dbName, dbKeyPrefix := record.ParseKey(prefix) return &Query{ dbName: dbName, dbKeyPrefix: dbKeyPrefix, } } // Where adds filtering. func (q *Query) Where(condition Condition) *Query { q.where = condition return q } // Limit limits the number of returned results. func (q *Query) Limit(limit int) *Query { q.limit = limit return q } // Offset sets the query offset. func (q *Query) Offset(offset int) *Query { q.offset = offset return q } // OrderBy orders the results by the given key. func (q *Query) OrderBy(key string) *Query { q.orderBy = key return q } // Check checks for errors in the query. func (q *Query) Check() (*Query, error) { if q.checked { return q, nil } // check condition if q.where != nil { err := q.where.check() if err != nil { return nil, err } } q.checked = true return q, nil } // MustBeValid checks for errors in the query and panics if there is an error. func (q *Query) MustBeValid() *Query { _, err := q.Check() if err != nil { panic(err) } return q } // IsChecked returns whether they query was checked. func (q *Query) IsChecked() bool { return q.checked } // MatchesKey checks whether the query matches the supplied database key (key without database prefix). func (q *Query) MatchesKey(dbKey string) bool { return strings.HasPrefix(dbKey, q.dbKeyPrefix) } // HasWhereCondition returns whether the query has a "where" condition set. func (q *Query) HasWhereCondition() bool { return q.where != nil } // MatchesRecord checks whether the query matches the supplied database record (value only). func (q *Query) MatchesRecord(r record.Record) bool { if q.where == nil { return true } acc := r.GetAccessor(r) if acc == nil { return false } return q.where.complies(acc) } // MatchesAccessor checks whether the query matches the supplied accessor (value only). func (q *Query) MatchesAccessor(acc accessor.Accessor) bool { if q.where == nil { return true } return q.where.complies(acc) } // Matches checks whether the query matches the supplied database record. func (q *Query) Matches(r record.Record) bool { if !q.MatchesKey(r.DatabaseKey()) { return false } return q.MatchesRecord(r) } // Print returns the string representation of the query. func (q *Query) Print() string { var where string if q.where != nil { where = q.where.string() if where != "" { if strings.HasPrefix(where, "(") { where = where[1 : len(where)-1] } where = fmt.Sprintf(" where %s", where) } } var orderBy string if q.orderBy != "" { orderBy = fmt.Sprintf(" orderby %s", q.orderBy) } var limit string if q.limit > 0 { limit = fmt.Sprintf(" limit %d", q.limit) } var offset string if q.offset > 0 { offset = fmt.Sprintf(" offset %d", q.offset) } return fmt.Sprintf("query %s:%s%s%s%s%s", q.dbName, q.dbKeyPrefix, where, orderBy, limit, offset) } // DatabaseName returns the name of the database. func (q *Query) DatabaseName() string { return q.dbName } // DatabaseKeyPrefix returns the key prefix for the database. func (q *Query) DatabaseKeyPrefix() string { return q.dbKeyPrefix } ================================================ FILE: base/database/query/query_test.go ================================================ //nolint:unparam package query import ( "testing" "github.com/safing/portmaster/base/database/record" "github.com/safing/structures/dsd" ) // copied from https://github.com/tidwall/gjson/blob/master/gjson_test.go var testJSON = `{"age":100, "name":{"here":"B\\\"R"}, "noop":{"what is a wren?":"a bird"}, "happy":true,"immortal":false, "items":[1,2,3,{"tags":[1,2,3],"points":[[1,2],[3,4]]},4,5,6,7], "arr":["1",2,"3",{"hello":"world"},"4",5], "vals":[1,2,3,{"sadf":sdf"asdf"}],"name":{"first":"tom","last":null}, "created":"2014-05-16T08:28:06.989Z", "loggy":{ "programmers": [ { "firstName": "Brett", "lastName": "McLaughlin", "email": "aaaa", "tag": "good" }, { "firstName": "Jason", "lastName": "Hunter", "email": "bbbb", "tag": "bad" }, { "firstName": "Elliotte", "lastName": "Harold", "email": "cccc", "tag":, "good" }, { "firstName": 1002.3, "age": 101 } ] }, "lastly":{"yay":"final"}, "temperature": 120.413 }` func testQuery(t *testing.T, r record.Record, shouldMatch bool, condition Condition) { t.Helper() q := New("test:").Where(condition).MustBeValid() // fmt.Printf("%s\n", q.Print()) matched := q.Matches(r) switch { case !matched && shouldMatch: t.Errorf("should match: %s", q.Print()) case matched && !shouldMatch: t.Errorf("should not match: %s", q.Print()) } } func TestQuery(t *testing.T) { t.Parallel() // if !gjson.Valid(testJSON) { // t.Fatal("test json is invalid") // } r, err := record.NewWrapper("", nil, dsd.JSON, []byte(testJSON)) if err != nil { t.Fatal(err) } testQuery(t, r, true, Where("age", Equals, 100)) testQuery(t, r, true, Where("age", GreaterThan, uint8(99))) testQuery(t, r, true, Where("age", GreaterThanOrEqual, 99)) testQuery(t, r, true, Where("age", GreaterThanOrEqual, 100)) testQuery(t, r, true, Where("age", LessThan, 101)) testQuery(t, r, true, Where("age", LessThanOrEqual, "101")) testQuery(t, r, true, Where("age", LessThanOrEqual, 100)) testQuery(t, r, true, Where("temperature", FloatEquals, 120.413)) testQuery(t, r, true, Where("temperature", FloatGreaterThan, 120)) testQuery(t, r, true, Where("temperature", FloatGreaterThanOrEqual, 120)) testQuery(t, r, true, Where("temperature", FloatGreaterThanOrEqual, 120.413)) testQuery(t, r, true, Where("temperature", FloatLessThan, 121)) testQuery(t, r, true, Where("temperature", FloatLessThanOrEqual, "121")) testQuery(t, r, true, Where("temperature", FloatLessThanOrEqual, "120.413")) testQuery(t, r, true, Where("lastly.yay", SameAs, "final")) testQuery(t, r, true, Where("lastly.yay", Contains, "ina")) testQuery(t, r, true, Where("lastly.yay", StartsWith, "fin")) testQuery(t, r, true, Where("lastly.yay", EndsWith, "nal")) testQuery(t, r, true, Where("lastly.yay", In, "draft,final")) testQuery(t, r, true, Where("lastly.yay", In, "final,draft")) testQuery(t, r, true, Where("happy", Is, true)) testQuery(t, r, true, Where("happy", Is, "true")) testQuery(t, r, true, Where("happy", Is, "t")) testQuery(t, r, true, Not(Where("happy", Is, "0"))) testQuery(t, r, true, And( Where("happy", Is, "1"), Not(Or( Where("happy", Is, false), Where("happy", Is, "f"), )), )) testQuery(t, r, true, Where("happy", Exists, nil)) testQuery(t, r, true, Where("created", Matches, "^2014-[0-9]{2}-[0-9]{2}T")) } ================================================ FILE: base/database/record/base.go ================================================ package record import ( "errors" "github.com/safing/portmaster/base/database/accessor" "github.com/safing/portmaster/base/log" "github.com/safing/structures/container" "github.com/safing/structures/dsd" ) // TODO(ppacher): // we can reduce the record.Record interface a lot by moving // most of those functions that require the Record as it's first // parameter to static package functions // (i.e. Marshal, MarshalRecord, GetAccessor, ...). // We should also consider given Base a GetBase() *Base method // that returns itself. This way we can remove almost all Base // only methods from the record.Record interface. That is, we can // remove all those CreateMeta, UpdateMeta, ... stuff from the // interface definition (not the actual functions!). This would make // the record.Record interface slim and only provide methods that // most users actually need. All those database/storage related methods // can still be accessed by using GetBase().XXX() instead. We can also // expose the dbName and dbKey and meta properties directly which would // make a nice JSON blob when marshalled. // Base provides a quick way to comply with the Model interface. type Base struct { dbName string dbKey string meta *Meta } // SetKey sets the key on the database record. The key may only be set once and // future calls to SetKey will be ignored. If you want to copy/move the record // to another database key, you will need to create a copy and assign a new key. // A key must be set before the record is used in any database operation. func (b *Base) SetKey(key string) { if !b.KeyIsSet() { b.dbName, b.dbKey = ParseKey(key) } else { log.Errorf("database: key is already set: tried to replace %q with %q", b.Key(), key) } } // ResetKey resets the database name and key. // Use with caution! func (b *Base) ResetKey() { b.dbName = "" b.dbKey = "" } // Key returns the key of the database record. // As the key must be set before any usage and can only be set once, this // function may be used without locking the record. func (b *Base) Key() string { return b.dbName + ":" + b.dbKey } // KeyIsSet returns true if the database key is set. // As the key must be set before any usage and can only be set once, this // function may be used without locking the record. func (b *Base) KeyIsSet() bool { return b.dbName != "" } // DatabaseName returns the name of the database. // As the key must be set before any usage and can only be set once, this // function may be used without locking the record. func (b *Base) DatabaseName() string { return b.dbName } // DatabaseKey returns the database key of the database record. // As the key must be set before any usage and can only be set once, this // function may be used without locking the record. func (b *Base) DatabaseKey() string { return b.dbKey } // Meta returns the metadata object for this record. func (b *Base) Meta() *Meta { return b.meta } // CreateMeta sets a default metadata object for this record. func (b *Base) CreateMeta() { b.meta = &Meta{} } // UpdateMeta creates the metadata if it does not exist and updates it. func (b *Base) UpdateMeta() { if b.meta == nil { b.CreateMeta() } b.meta.Update() } // SetMeta sets the metadata on the database record, it should only be called after loading the record. Use MoveTo to save the record with another key. func (b *Base) SetMeta(meta *Meta) { b.meta = meta } // Marshal marshals the format and data. func (b *Base) Marshal(self Record, format uint8) ([]byte, error) { if b.Meta() == nil { return nil, errors.New("missing meta") } if b.Meta().Deleted > 0 { return nil, nil } dumped, err := dsd.Dump(self, format) if err != nil { return nil, err } return dumped, nil } // MarshalDataOnly marshals the data only. func (b *Base) MarshalDataOnly(self Record, format uint8) ([]byte, error) { if b.Meta() == nil { return nil, errors.New("missing meta") } if b.Meta().Deleted > 0 { return nil, nil } return dsd.DumpWithoutIdentifier(self, format, "") } // MarshalRecord marshals the data, format and metadata. func (b *Base) MarshalRecord(self Record) ([]byte, error) { if b.Meta() == nil { return nil, errors.New("missing meta") } // version c := container.New([]byte{1}) // meta encoding metaSection, err := dsd.Dump(b.meta, dsd.GenCode) if err != nil { return nil, err } c.AppendAsBlock(metaSection) // data dataSection, err := b.Marshal(self, dsd.JSON) if err != nil { return nil, err } c.Append(dataSection) return c.CompileData(), nil } // IsWrapped returns whether the record is a Wrapper. func (b *Base) IsWrapped() bool { return false } // GetAccessor returns an accessor for this record, if available. func (b *Base) GetAccessor(self Record) accessor.Accessor { return accessor.NewStructAccessor(self) } ================================================ FILE: base/database/record/base_test.go ================================================ package record import "testing" func TestBaseRecord(t *testing.T) { t.Parallel() // check model interface compliance var m Record b := &TestRecord{} m = b _ = m } ================================================ FILE: base/database/record/key.go ================================================ package record import ( "strings" ) // ParseKey splits a key into it's database name and key parts. func ParseKey(key string) (dbName, dbKey string) { splitted := strings.SplitN(key, ":", 2) if len(splitted) < 2 { return splitted[0], "" } return splitted[0], strings.Join(splitted[1:], ":") } ================================================ FILE: base/database/record/meta-bench_test.go ================================================ package record // Benchmark: // BenchmarkAllocateBytes-8 2000000000 0.76 ns/op // BenchmarkAllocateStruct1-8 2000000000 0.76 ns/op // BenchmarkAllocateStruct2-8 2000000000 0.79 ns/op // BenchmarkMetaSerializeContainer-8 1000000 1703 ns/op // BenchmarkMetaUnserializeContainer-8 2000000 950 ns/op // BenchmarkMetaSerializeVarInt-8 3000000 457 ns/op // BenchmarkMetaUnserializeVarInt-8 20000000 62.9 ns/op // BenchmarkMetaSerializeWithXDR2-8 1000000 2360 ns/op // BenchmarkMetaUnserializeWithXDR2-8 500000 3189 ns/op // BenchmarkMetaSerializeWithColfer-8 10000000 237 ns/op // BenchmarkMetaUnserializeWithColfer-8 20000000 51.7 ns/op // BenchmarkMetaSerializeWithCodegen-8 50000000 23.7 ns/op // BenchmarkMetaUnserializeWithCodegen-8 100000000 18.9 ns/op // BenchmarkMetaSerializeWithDSDJSON-8 1000000 2398 ns/op // BenchmarkMetaUnserializeWithDSDJSON-8 300000 6264 ns/op import ( "testing" "time" "github.com/safing/structures/container" "github.com/safing/structures/dsd" "github.com/safing/structures/varint" ) var testMeta = &Meta{ Created: time.Now().Unix(), Modified: time.Now().Unix(), Expires: time.Now().Unix(), Deleted: time.Now().Unix(), secret: true, cronjewel: true, } func BenchmarkAllocateBytes(b *testing.B) { for range b.N { _ = make([]byte, 33) } } func BenchmarkAllocateStruct1(b *testing.B) { for range b.N { var newMeta Meta _ = newMeta } } func BenchmarkAllocateStruct2(b *testing.B) { for range b.N { _ = Meta{} } } func BenchmarkMetaSerializeContainer(b *testing.B) { // Start benchmark for range b.N { c := container.New() c.AppendNumber(uint64(testMeta.Created)) c.AppendNumber(uint64(testMeta.Modified)) c.AppendNumber(uint64(testMeta.Expires)) c.AppendNumber(uint64(testMeta.Deleted)) switch { case testMeta.secret && testMeta.cronjewel: c.AppendNumber(3) case testMeta.secret: c.AppendNumber(1) case testMeta.cronjewel: c.AppendNumber(2) default: c.AppendNumber(0) } } } func BenchmarkMetaUnserializeContainer(b *testing.B) { // Setup c := container.New() c.AppendNumber(uint64(testMeta.Created)) c.AppendNumber(uint64(testMeta.Modified)) c.AppendNumber(uint64(testMeta.Expires)) c.AppendNumber(uint64(testMeta.Deleted)) switch { case testMeta.secret && testMeta.cronjewel: c.AppendNumber(3) case testMeta.secret: c.AppendNumber(1) case testMeta.cronjewel: c.AppendNumber(2) default: c.AppendNumber(0) } encodedData := c.CompileData() // Reset timer for precise results b.ResetTimer() // Start benchmark for range b.N { var newMeta Meta var err error var num uint64 c := container.New(encodedData) num, err = c.GetNextN64() newMeta.Created = int64(num) if err != nil { b.Errorf("could not decode: %s", err) return } num, err = c.GetNextN64() newMeta.Modified = int64(num) if err != nil { b.Errorf("could not decode: %s", err) return } num, err = c.GetNextN64() newMeta.Expires = int64(num) if err != nil { b.Errorf("could not decode: %s", err) return } num, err = c.GetNextN64() newMeta.Deleted = int64(num) if err != nil { b.Errorf("could not decode: %s", err) return } flags, err := c.GetNextN8() if err != nil { b.Errorf("could not decode: %s", err) return } switch flags { case 3: newMeta.secret = true newMeta.cronjewel = true case 2: newMeta.cronjewel = true case 1: newMeta.secret = true case 0: default: b.Errorf("invalid flag value: %d", flags) return } } } func BenchmarkMetaSerializeVarInt(b *testing.B) { // Start benchmark for range b.N { encoded := make([]byte, 33) offset := 0 data := varint.Pack64(uint64(testMeta.Created)) for _, part := range data { encoded[offset] = part offset++ } data = varint.Pack64(uint64(testMeta.Modified)) for _, part := range data { encoded[offset] = part offset++ } data = varint.Pack64(uint64(testMeta.Expires)) for _, part := range data { encoded[offset] = part offset++ } data = varint.Pack64(uint64(testMeta.Deleted)) for _, part := range data { encoded[offset] = part offset++ } switch { case testMeta.secret && testMeta.cronjewel: encoded[offset] = 3 case testMeta.secret: encoded[offset] = 1 case testMeta.cronjewel: encoded[offset] = 2 default: encoded[offset] = 0 } } } func BenchmarkMetaUnserializeVarInt(b *testing.B) { // Setup encoded := make([]byte, 33) offset := 0 data := varint.Pack64(uint64(testMeta.Created)) for _, part := range data { encoded[offset] = part offset++ } data = varint.Pack64(uint64(testMeta.Modified)) for _, part := range data { encoded[offset] = part offset++ } data = varint.Pack64(uint64(testMeta.Expires)) for _, part := range data { encoded[offset] = part offset++ } data = varint.Pack64(uint64(testMeta.Deleted)) for _, part := range data { encoded[offset] = part offset++ } switch { case testMeta.secret && testMeta.cronjewel: encoded[offset] = 3 case testMeta.secret: encoded[offset] = 1 case testMeta.cronjewel: encoded[offset] = 2 default: encoded[offset] = 0 } offset++ encodedData := encoded[:offset] // Reset timer for precise results b.ResetTimer() // Start benchmark for range b.N { var newMeta Meta offset = 0 num, n, err := varint.Unpack64(encodedData) if err != nil { b.Error(err) return } testMeta.Created = int64(num) offset += n num, n, err = varint.Unpack64(encodedData[offset:]) if err != nil { b.Error(err) return } testMeta.Modified = int64(num) offset += n num, n, err = varint.Unpack64(encodedData[offset:]) if err != nil { b.Error(err) return } testMeta.Expires = int64(num) offset += n num, n, err = varint.Unpack64(encodedData[offset:]) if err != nil { b.Error(err) return } testMeta.Deleted = int64(num) offset += n switch encodedData[offset] { case 3: newMeta.secret = true newMeta.cronjewel = true case 2: newMeta.cronjewel = true case 1: newMeta.secret = true case 0: default: b.Errorf("invalid flag value: %d", encodedData[offset]) return } } } func BenchmarkMetaSerializeWithCodegen(b *testing.B) { for range b.N { _, err := testMeta.GenCodeMarshal(nil) if err != nil { b.Errorf("failed to serialize with codegen: %s", err) return } } } func BenchmarkMetaUnserializeWithCodegen(b *testing.B) { // Setup encodedData, err := testMeta.GenCodeMarshal(nil) if err != nil { b.Errorf("failed to serialize with codegen: %s", err) return } // Reset timer for precise results b.ResetTimer() // Start benchmark for range b.N { var newMeta Meta _, err := newMeta.GenCodeUnmarshal(encodedData) if err != nil { b.Errorf("failed to unserialize with codegen: %s", err) return } } } func BenchmarkMetaSerializeWithDSDJSON(b *testing.B) { for range b.N { _, err := dsd.Dump(testMeta, dsd.JSON) if err != nil { b.Errorf("failed to serialize with DSD/JSON: %s", err) return } } } func BenchmarkMetaUnserializeWithDSDJSON(b *testing.B) { // Setup encodedData, err := dsd.Dump(testMeta, dsd.JSON) if err != nil { b.Errorf("failed to serialize with DSD/JSON: %s", err) return } // Reset timer for precise results b.ResetTimer() // Start benchmark for range b.N { var newMeta Meta _, err := dsd.Load(encodedData, &newMeta) if err != nil { b.Errorf("failed to unserialize with DSD/JSON: %s", err) return } } } ================================================ FILE: base/database/record/meta-gencode.go ================================================ package record import ( "fmt" ) // GenCodeSize returns the size of the gencode marshalled byte slice. func (m *Meta) GenCodeSize() (s int) { s += 34 return } // GenCodeMarshal gencode marshalls Meta into the given byte array, or a new one if its too small. func (m *Meta) GenCodeMarshal(buf []byte) ([]byte, error) { size := m.GenCodeSize() { if cap(buf) >= size { buf = buf[:size] } else { buf = make([]byte, size) } } i := uint64(0) { buf[0+0] = byte(m.Created >> 0) buf[1+0] = byte(m.Created >> 8) buf[2+0] = byte(m.Created >> 16) buf[3+0] = byte(m.Created >> 24) buf[4+0] = byte(m.Created >> 32) buf[5+0] = byte(m.Created >> 40) buf[6+0] = byte(m.Created >> 48) buf[7+0] = byte(m.Created >> 56) } { buf[0+8] = byte(m.Modified >> 0) buf[1+8] = byte(m.Modified >> 8) buf[2+8] = byte(m.Modified >> 16) buf[3+8] = byte(m.Modified >> 24) buf[4+8] = byte(m.Modified >> 32) buf[5+8] = byte(m.Modified >> 40) buf[6+8] = byte(m.Modified >> 48) buf[7+8] = byte(m.Modified >> 56) } { buf[0+16] = byte(m.Expires >> 0) buf[1+16] = byte(m.Expires >> 8) buf[2+16] = byte(m.Expires >> 16) buf[3+16] = byte(m.Expires >> 24) buf[4+16] = byte(m.Expires >> 32) buf[5+16] = byte(m.Expires >> 40) buf[6+16] = byte(m.Expires >> 48) buf[7+16] = byte(m.Expires >> 56) } { buf[0+24] = byte(m.Deleted >> 0) buf[1+24] = byte(m.Deleted >> 8) buf[2+24] = byte(m.Deleted >> 16) buf[3+24] = byte(m.Deleted >> 24) buf[4+24] = byte(m.Deleted >> 32) buf[5+24] = byte(m.Deleted >> 40) buf[6+24] = byte(m.Deleted >> 48) buf[7+24] = byte(m.Deleted >> 56) } { if m.secret { buf[32] = 1 } else { buf[32] = 0 } } { if m.cronjewel { buf[33] = 1 } else { buf[33] = 0 } } return buf[:i+34], nil } // GenCodeUnmarshal gencode unmarshalls Meta and returns the bytes read. func (m *Meta) GenCodeUnmarshal(buf []byte) (uint64, error) { if len(buf) < m.GenCodeSize() { return 0, fmt.Errorf("insufficient data: got %d out of %d bytes", len(buf), m.GenCodeSize()) } i := uint64(0) { m.Created = 0 | (int64(buf[0+0]) << 0) | (int64(buf[1+0]) << 8) | (int64(buf[2+0]) << 16) | (int64(buf[3+0]) << 24) | (int64(buf[4+0]) << 32) | (int64(buf[5+0]) << 40) | (int64(buf[6+0]) << 48) | (int64(buf[7+0]) << 56) } { m.Modified = 0 | (int64(buf[0+8]) << 0) | (int64(buf[1+8]) << 8) | (int64(buf[2+8]) << 16) | (int64(buf[3+8]) << 24) | (int64(buf[4+8]) << 32) | (int64(buf[5+8]) << 40) | (int64(buf[6+8]) << 48) | (int64(buf[7+8]) << 56) } { m.Expires = 0 | (int64(buf[0+16]) << 0) | (int64(buf[1+16]) << 8) | (int64(buf[2+16]) << 16) | (int64(buf[3+16]) << 24) | (int64(buf[4+16]) << 32) | (int64(buf[5+16]) << 40) | (int64(buf[6+16]) << 48) | (int64(buf[7+16]) << 56) } { m.Deleted = 0 | (int64(buf[0+24]) << 0) | (int64(buf[1+24]) << 8) | (int64(buf[2+24]) << 16) | (int64(buf[3+24]) << 24) | (int64(buf[4+24]) << 32) | (int64(buf[5+24]) << 40) | (int64(buf[6+24]) << 48) | (int64(buf[7+24]) << 56) } { m.secret = buf[32] == 1 } { m.cronjewel = buf[33] == 1 } return i + 34, nil } ================================================ FILE: base/database/record/meta-gencode_test.go ================================================ package record import ( "reflect" "testing" "time" ) var genCodeTestMeta = &Meta{ Created: time.Now().Unix(), Modified: time.Now().Unix(), Expires: time.Now().Unix(), Deleted: time.Now().Unix(), secret: true, cronjewel: true, } func TestGenCode(t *testing.T) { t.Parallel() encoded, err := genCodeTestMeta.GenCodeMarshal(nil) if err != nil { t.Fatal(err) } newMeta := &Meta{} _, err = newMeta.GenCodeUnmarshal(encoded) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(genCodeTestMeta, newMeta) { t.Errorf("objects are not equal, got: %v", newMeta) } } ================================================ FILE: base/database/record/meta.colf ================================================ package record type course struct { Created int64 Modified int64 Expires int64 Deleted int64 Secret bool Cronjewel bool } ================================================ FILE: base/database/record/meta.gencode ================================================ struct Meta { Created int64 Modified int64 Expires int64 Deleted int64 Secret bool Cronjewel bool } ================================================ FILE: base/database/record/meta.go ================================================ package record import "time" // Meta holds metadata about the record. type Meta struct { Created int64 Modified int64 Expires int64 Deleted int64 secret bool // secrets must not be sent to the UI, only synced between nodes cronjewel bool // crownjewels must never leave the instance, but may be read by the UI } // SetAbsoluteExpiry sets an absolute expiry time (in seconds), that is not affected when the record is updated. func (m *Meta) SetAbsoluteExpiry(seconds int64) { m.Expires = seconds m.Deleted = 0 } // SetRelativateExpiry sets a relative expiry time (ie. TTL in seconds) that is automatically updated whenever the record is updated/saved. func (m *Meta) SetRelativateExpiry(seconds int64) { if seconds >= 0 { m.Deleted = -seconds } } // GetAbsoluteExpiry returns the absolute expiry time. func (m *Meta) GetAbsoluteExpiry() int64 { return m.Expires } // GetRelativeExpiry returns the current relative expiry time - ie. seconds until expiry. // A negative value signifies that the record does not expire. func (m *Meta) GetRelativeExpiry() int64 { if m.Expires == 0 { return -1 } abs := m.Expires - time.Now().Unix() if abs < 0 { return 0 } return abs } // MakeCrownJewel marks the database records as a crownjewel, meaning that it will not be sent/synced to other devices. func (m *Meta) MakeCrownJewel() { m.cronjewel = true } // IsCrownJewel returns whether the database record is marked as a crownjewel. func (m *Meta) IsCrownJewel() bool { return m.cronjewel } // MakeSecret sets the database record as secret, meaning that it may only be used internally, and not by interfacing processes, such as the UI. func (m *Meta) MakeSecret() { m.secret = true } // IsSecret returns whether the database record is marked as a secret. func (m *Meta) IsSecret() bool { return m.secret } // Update updates the internal meta states and should be called before writing the record to the database. func (m *Meta) Update() { now := time.Now().Unix() m.Modified = now if m.Created == 0 { m.Created = now } if m.Deleted < 0 { m.Expires = now - m.Deleted } } // Reset resets all metadata, except for the secret and crownjewel status. func (m *Meta) Reset() { m.Created = 0 m.Modified = 0 m.Expires = 0 m.Deleted = 0 } // Delete marks the record as deleted. func (m *Meta) Delete() { m.Deleted = time.Now().Unix() } // IsDeleted returns whether the record is deleted. func (m *Meta) IsDeleted() bool { return m.Deleted > 0 } // CheckValidity checks whether the database record is valid. func (m *Meta) CheckValidity() (valid bool) { if m == nil { return false } switch { case m.Deleted > 0: return false case m.Expires > 0 && m.Expires < time.Now().Unix(): return false default: return true } } // CheckPermission checks whether the database record may be accessed with the following scope. func (m *Meta) CheckPermission(local, internal bool) (permitted bool) { if m == nil { return false } switch { case !local && m.cronjewel: return false case !internal && m.secret: return false default: return true } } // Duplicate returns a new copy of Meta. func (m *Meta) Duplicate() *Meta { return &Meta{ Created: m.Created, Modified: m.Modified, Expires: m.Expires, Deleted: m.Deleted, secret: m.secret, cronjewel: m.cronjewel, } } ================================================ FILE: base/database/record/record.go ================================================ package record import ( "github.com/safing/portmaster/base/database/accessor" ) // Record provides an interface for uniformally handling database records. type Record interface { SetKey(key string) // test:config Key() string // test:config KeyIsSet() bool DatabaseName() string // test DatabaseKey() string // config // Metadata. Meta() *Meta SetMeta(meta *Meta) CreateMeta() UpdateMeta() // Serialization. Marshal(self Record, format uint8) ([]byte, error) MarshalDataOnly(self Record, format uint8) ([]byte, error) MarshalRecord(self Record) ([]byte, error) GetAccessor(self Record) accessor.Accessor // Locking. Lock() Unlock() // Wrapping. IsWrapped() bool } ================================================ FILE: base/database/record/record_test.go ================================================ package record import ( "sync" ) type TestRecord struct { Base sync.Mutex } ================================================ FILE: base/database/record/wrapper.go ================================================ package record import ( "errors" "fmt" "sync" "github.com/safing/portmaster/base/database/accessor" "github.com/safing/structures/container" "github.com/safing/structures/dsd" "github.com/safing/structures/varint" ) // Wrapper wraps raw data and implements the Record interface. type Wrapper struct { Base sync.Mutex Format uint8 Data []byte } // NewRawWrapper returns a record wrapper for the given data, including metadata. This is normally only used by storage backends when loading records. func NewRawWrapper(database, key string, data []byte) (*Wrapper, error) { version, offset, err := varint.Unpack8(data) if err != nil { return nil, err } if version != 1 { return nil, fmt.Errorf("incompatible record version: %d", version) } metaSection, n, err := varint.GetNextBlock(data[offset:]) if err != nil { return nil, fmt.Errorf("could not get meta section: %w", err) } offset += n newMeta := &Meta{} _, err = dsd.Load(metaSection, newMeta) if err != nil { return nil, fmt.Errorf("could not unmarshal meta section: %w", err) } var format uint8 = dsd.RAW if !newMeta.IsDeleted() { format, n, err = varint.Unpack8(data[offset:]) if err != nil { return nil, fmt.Errorf("could not get dsd format: %w", err) } offset += n } return &Wrapper{ Base{ database, key, newMeta, }, sync.Mutex{}, format, data[offset:], }, nil } // NewWrapper returns a new record wrapper for the given data. func NewWrapper(key string, meta *Meta, format uint8, data []byte) (*Wrapper, error) { dbName, dbKey := ParseKey(key) return &Wrapper{ Base{ dbName: dbName, dbKey: dbKey, meta: meta, }, sync.Mutex{}, format, data, }, nil } // NewWrapperFromDatabase returns a new record wrapper for the given data. func NewWrapperFromDatabase(dbName, dbKey string, meta *Meta, format uint8, data []byte) (*Wrapper, error) { return &Wrapper{ Base{ dbName: dbName, dbKey: dbKey, meta: meta, }, sync.Mutex{}, format, data, }, nil } // Marshal marshals the format and data. func (w *Wrapper) Marshal(r Record, format uint8) ([]byte, error) { if w.Meta() == nil { return nil, errors.New("missing meta") } if w.Meta().Deleted > 0 { return nil, nil } if format != dsd.AUTO && format != w.Format { return nil, errors.New("could not dump model, wrapped object format mismatch") } data := make([]byte, len(w.Data)+1) data[0] = w.Format copy(data[1:], w.Data) return data, nil } // MarshalDataOnly marshals the data only. func (w *Wrapper) MarshalDataOnly(self Record, format uint8) ([]byte, error) { if w.Meta() == nil { return nil, errors.New("missing meta") } if w.Meta().Deleted > 0 { return nil, nil } if format != dsd.AUTO && format != w.Format { return nil, errors.New("could not dump model, wrapped object format mismatch") } return w.Data, nil } // MarshalRecord marshals the data, format and metadata. func (w *Wrapper) MarshalRecord(r Record) ([]byte, error) { // Duplication necessary, as the version from Base would call Base.Marshal instead of Wrapper.Marshal if w.Meta() == nil { return nil, errors.New("missing meta") } // version c := container.New([]byte{1}) // meta metaSection, err := dsd.Dump(w.meta, dsd.GenCode) if err != nil { return nil, err } c.AppendAsBlock(metaSection) // data dataSection, err := w.Marshal(r, dsd.AUTO) if err != nil { return nil, err } c.Append(dataSection) return c.CompileData(), nil } // IsWrapped returns whether the record is a Wrapper. func (w *Wrapper) IsWrapped() bool { return true } // Unwrap unwraps data into a record. func Unwrap(wrapped, r Record) error { wrapper, ok := wrapped.(*Wrapper) if !ok { return fmt.Errorf("cannot unwrap %T", wrapped) } err := dsd.LoadAsFormat(wrapper.Data, wrapper.Format, r) if err != nil { return fmt.Errorf("failed to unwrap %T: %w", r, err) } r.SetKey(wrapped.Key()) r.SetMeta(wrapped.Meta()) return nil } // GetAccessor returns an accessor for this record, if available. func (w *Wrapper) GetAccessor(self Record) accessor.Accessor { if w.Format == dsd.JSON && len(w.Data) > 0 { return accessor.NewJSONBytesAccessor(&w.Data) } return nil } ================================================ FILE: base/database/record/wrapper_test.go ================================================ package record import ( "bytes" "testing" "github.com/safing/structures/dsd" ) func TestWrapper(t *testing.T) { t.Parallel() // check model interface compliance var m Record w := &Wrapper{} m = w _ = m // create test data testData := []byte(`{"a": "b"}`) encodedTestData := []byte(`J{"a": "b"}`) // test wrapper wrapper, err := NewWrapper("test:a", &Meta{}, dsd.JSON, testData) if err != nil { t.Fatal(err) } if wrapper.Format != dsd.JSON { t.Error("format mismatch") } if !bytes.Equal(testData, wrapper.Data) { t.Error("data mismatch") } encoded, err := wrapper.Marshal(wrapper, dsd.JSON) if err != nil { t.Fatal(err) } if !bytes.Equal(encodedTestData, encoded) { t.Error("marshal mismatch") } wrapper.SetMeta(&Meta{}) wrapper.meta.Update() raw, err := wrapper.MarshalRecord(wrapper) if err != nil { t.Fatal(err) } wrapper2, err := NewRawWrapper("test", "a", raw) if err != nil { t.Fatal(err) } if !bytes.Equal(testData, wrapper2.Data) { t.Error("marshal mismatch") } } ================================================ FILE: base/database/registry.go ================================================ package database import ( "errors" "fmt" "regexp" "sync" "time" ) var ( registry = make(map[string]*Database) registryLock sync.Mutex nameConstraint = regexp.MustCompile("^[A-Za-z0-9_-]{3,}$") ) // Register registers a new database. // If the database is already registered, only // the description and the primary API will be // updated and the effective object will be returned. func Register(db *Database) (*Database, error) { registryLock.Lock() defer registryLock.Unlock() registeredDB, ok := registry[db.Name] if ok { // update database if registeredDB.Description != db.Description { registeredDB.Description = db.Description } if registeredDB.ShadowDelete != db.ShadowDelete { registeredDB.ShadowDelete = db.ShadowDelete } } else { // register new database if !nameConstraint.MatchString(db.Name) { return nil, errors.New("database name must only contain alphanumeric and `_-` characters and must be at least 3 characters long") } now := time.Now().Round(time.Second) db.Registered = now db.LastUpdated = now db.LastLoaded = time.Time{} registry[db.Name] = db } if ok { return registeredDB, nil } return nil, nil } func getDatabase(name string) (*Database, error) { registryLock.Lock() defer registryLock.Unlock() registeredDB, ok := registry[name] if !ok { return nil, fmt.Errorf(`database "%s" not registered`, name) } registeredDB.Loaded() return registeredDB, nil } ================================================ FILE: base/database/storage/badger/badger.go ================================================ package badger import ( "context" "errors" "fmt" "time" "github.com/dgraph-io/badger" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" "github.com/safing/portmaster/base/log" ) // Badger database made pluggable for portbase. type Badger struct { name string db *badger.DB } func init() { _ = storage.Register("badger", NewBadger) } // NewBadger opens/creates a badger database. func NewBadger(name, location string) (storage.Interface, error) { opts := badger.DefaultOptions(location) db, err := badger.Open(opts) if errors.Is(err, badger.ErrTruncateNeeded) { // clean up after crash log.Warningf("database/storage: truncating corrupted value log of badger database %s: this may cause data loss", name) opts.Truncate = true db, err = badger.Open(opts) } if err != nil { return nil, err } return &Badger{ name: name, db: db, }, nil } // Get returns a database record. func (b *Badger) Get(key string) (record.Record, error) { var item *badger.Item err := b.db.View(func(txn *badger.Txn) error { var err error item, err = txn.Get([]byte(key)) if err != nil { if errors.Is(err, badger.ErrKeyNotFound) { return storage.ErrNotFound } return err } return nil }) if err != nil { return nil, err } // return err if deleted or expired if item.IsDeletedOrExpired() { return nil, storage.ErrNotFound } data, err := item.ValueCopy(nil) if err != nil { return nil, err } m, err := record.NewRawWrapper(b.name, string(item.Key()), data) if err != nil { return nil, err } return m, nil } // GetMeta returns the metadata of a database record. func (b *Badger) GetMeta(key string) (*record.Meta, error) { // TODO: Replace with more performant variant. r, err := b.Get(key) if err != nil { return nil, err } return r.Meta(), nil } // Put stores a record in the database. func (b *Badger) Put(r record.Record) (record.Record, error) { data, err := r.MarshalRecord(r) if err != nil { return nil, err } err = b.db.Update(func(txn *badger.Txn) error { return txn.Set([]byte(r.DatabaseKey()), data) }) if err != nil { return nil, err } return r, nil } // Delete deletes a record from the database. func (b *Badger) Delete(key string) error { return b.db.Update(func(txn *badger.Txn) error { err := txn.Delete([]byte(key)) if err != nil && !errors.Is(err, badger.ErrKeyNotFound) { return err } return nil }) } // Query returns a an iterator for the supplied query. func (b *Badger) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { _, err := q.Check() if err != nil { return nil, fmt.Errorf("invalid query: %w", err) } queryIter := iterator.New() go b.queryExecutor(queryIter, q, local, internal) return queryIter, nil } //nolint:gocognit func (b *Badger) queryExecutor(queryIter *iterator.Iterator, q *query.Query, local, internal bool) { err := b.db.View(func(txn *badger.Txn) error { it := txn.NewIterator(badger.DefaultIteratorOptions) defer it.Close() prefix := []byte(q.DatabaseKeyPrefix()) for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { item := it.Item() var data []byte err := item.Value(func(val []byte) error { data = val return nil }) if err != nil { return err } r, err := record.NewRawWrapper(b.name, string(item.Key()), data) if err != nil { return err } if !r.Meta().CheckValidity() { continue } if !r.Meta().CheckPermission(local, internal) { continue } if q.MatchesRecord(r) { copiedData, err := item.ValueCopy(nil) if err != nil { return err } newWrapper, err := record.NewRawWrapper(b.name, r.DatabaseKey(), copiedData) if err != nil { return err } select { case <-queryIter.Done: return nil case queryIter.Next <- newWrapper: default: select { case queryIter.Next <- newWrapper: case <-queryIter.Done: return nil case <-time.After(1 * time.Minute): return errors.New("query timeout") } } } } return nil }) queryIter.Finish(err) } // ReadOnly returns whether the database is read only. func (b *Badger) ReadOnly() bool { return false } // Injected returns whether the database is injected. func (b *Badger) Injected() bool { return false } // Maintain runs a light maintenance operation on the database. func (b *Badger) Maintain(_ context.Context) error { _ = b.db.RunValueLogGC(0.7) return nil } // MaintainThorough runs a thorough maintenance operation on the database. func (b *Badger) MaintainThorough(_ context.Context) (err error) { for err == nil { err = b.db.RunValueLogGC(0.7) } return nil } // MaintainRecordStates maintains records states in the database. func (b *Badger) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time, shadowDelete bool) error { // TODO: implement MaintainRecordStates return nil } // Shutdown shuts down the database. func (b *Badger) Shutdown() error { return b.db.Close() } ================================================ FILE: base/database/storage/badger/badger_test.go ================================================ package badger import ( "context" "os" "reflect" "sync" "testing" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" ) var ( // Compile time interface checks. _ storage.Interface = &Badger{} _ storage.Maintainer = &Badger{} ) type TestRecord struct { //nolint:maligned record.Base sync.Mutex S string I int I8 int8 I16 int16 I32 int32 I64 int64 UI uint UI8 uint8 UI16 uint16 UI32 uint32 UI64 uint64 F32 float32 F64 float64 B bool } func TestBadger(t *testing.T) { t.Parallel() testDir, err := os.MkdirTemp("", "testing-") if err != nil { t.Fatal(err) } defer func() { _ = os.RemoveAll(testDir) // clean up }() // start db, err := NewBadger("test", testDir) if err != nil { t.Fatal(err) } a := &TestRecord{ S: "banana", I: 42, I8: 42, I16: 42, I32: 42, I64: 42, UI: 42, UI8: 42, UI16: 42, UI32: 42, UI64: 42, F32: 42.42, F64: 42.42, B: true, } a.SetMeta(&record.Meta{}) a.Meta().Update() a.SetKey("test:A") // put record _, err = db.Put(a) if err != nil { t.Fatal(err) } // get and compare r1, err := db.Get("A") if err != nil { t.Fatal(err) } a1 := &TestRecord{} err = record.Unwrap(r1, a1) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(a, a1) { t.Fatalf("mismatch, got %v", a1) } // test query q := query.New("").MustBeValid() it, err := db.Query(q, true, true) if err != nil { t.Fatal(err) } cnt := 0 for range it.Next { cnt++ } if it.Err() != nil { t.Fatal(err) } if cnt != 1 { t.Fatalf("unexpected query result count: %d", cnt) } // delete err = db.Delete("A") if err != nil { t.Fatal(err) } // check if its gone _, err = db.Get("A") if err == nil { t.Fatal("should fail") } // maintenance maintainer, ok := db.(storage.Maintainer) if ok { err = maintainer.Maintain(context.TODO()) if err != nil { t.Fatal(err) } err = maintainer.MaintainThorough(context.TODO()) if err != nil { t.Fatal(err) } } else { t.Fatal("should implement Maintainer") } // shutdown err = db.Shutdown() if err != nil { t.Fatal(err) } } ================================================ FILE: base/database/storage/bbolt/bbolt.go ================================================ package bbolt import ( "bytes" "context" "errors" "fmt" "path/filepath" "time" "go.etcd.io/bbolt" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" ) var bucketName = []byte{0} // BBolt database made pluggable for portbase. type BBolt struct { name string db *bbolt.DB } func init() { _ = storage.Register("bbolt", NewBBolt) } // NewBBolt opens/creates a bbolt database. func NewBBolt(name, location string) (storage.Interface, error) { // Create options for bbolt database. dbFile := filepath.Join(location, "db.bbolt") dbOptions := &bbolt.Options{ Timeout: 1 * time.Second, } // Open/Create database, retry if there is a timeout. db, err := bbolt.Open(dbFile, 0o0600, dbOptions) for i := 0; i < 5 && err != nil; i++ { // Try again if there is an error. db, err = bbolt.Open(dbFile, 0o0600, dbOptions) } if err != nil { return nil, err } // Create bucket err = db.Update(func(tx *bbolt.Tx) error { _, err := tx.CreateBucketIfNotExists(bucketName) if err != nil { return err } return nil }) if err != nil { return nil, err } return &BBolt{ name: name, db: db, }, nil } // Get returns a database record. func (b *BBolt) Get(key string) (record.Record, error) { var r record.Record err := b.db.View(func(tx *bbolt.Tx) error { // get value from db value := tx.Bucket(bucketName).Get([]byte(key)) if value == nil { return storage.ErrNotFound } // copy data duplicate := make([]byte, len(value)) copy(duplicate, value) // create record var txErr error r, txErr = record.NewRawWrapper(b.name, key, duplicate) if txErr != nil { return txErr } return nil }) if err != nil { return nil, err } return r, nil } // GetMeta returns the metadata of a database record. func (b *BBolt) GetMeta(key string) (*record.Meta, error) { // TODO: Replace with more performant variant. r, err := b.Get(key) if err != nil { return nil, err } return r.Meta(), nil } // Put stores a record in the database. func (b *BBolt) Put(r record.Record) (record.Record, error) { data, err := r.MarshalRecord(r) if err != nil { return nil, err } err = b.db.Update(func(tx *bbolt.Tx) error { txErr := tx.Bucket(bucketName).Put([]byte(r.DatabaseKey()), data) if txErr != nil { return txErr } return nil }) if err != nil { return nil, err } return r, nil } // PutMany stores many records in the database. func (b *BBolt) PutMany(shadowDelete bool) (chan<- record.Record, <-chan error) { batch := make(chan record.Record, 100) errs := make(chan error, 1) go func() { err := b.db.Batch(func(tx *bbolt.Tx) error { bucket := tx.Bucket(bucketName) for r := range batch { txErr := b.batchPutOrDelete(bucket, shadowDelete, r) if txErr != nil { return txErr } } return nil }) errs <- err }() return batch, errs } func (b *BBolt) batchPutOrDelete(bucket *bbolt.Bucket, shadowDelete bool, r record.Record) (err error) { r.Lock() defer r.Unlock() if !shadowDelete && r.Meta().IsDeleted() { // Immediate delete. err = bucket.Delete([]byte(r.DatabaseKey())) } else { // Put or shadow delete. var data []byte data, err = r.MarshalRecord(r) if err == nil { err = bucket.Put([]byte(r.DatabaseKey()), data) } } return err } // Delete deletes a record from the database. func (b *BBolt) Delete(key string) error { err := b.db.Update(func(tx *bbolt.Tx) error { txErr := tx.Bucket(bucketName).Delete([]byte(key)) if txErr != nil { return txErr } return nil }) if err != nil { return err } return nil } // Query returns a an iterator for the supplied query. func (b *BBolt) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { _, err := q.Check() if err != nil { return nil, fmt.Errorf("invalid query: %w", err) } queryIter := iterator.New() go b.queryExecutor(queryIter, q, local, internal) return queryIter, nil } func (b *BBolt) queryExecutor(queryIter *iterator.Iterator, q *query.Query, local, internal bool) { prefix := []byte(q.DatabaseKeyPrefix()) err := b.db.View(func(tx *bbolt.Tx) error { // Create a cursor for iteration. c := tx.Bucket(bucketName).Cursor() // Iterate over items in sorted key order. This starts from the // first key/value pair and updates the k/v variables to the // next key/value on each iteration. // // The loop finishes at the end of the cursor when a nil key is returned. for key, value := c.Seek(prefix); key != nil; key, value = c.Next() { // if we don't match the prefix anymore, exit if !bytes.HasPrefix(key, prefix) { return nil } // wrap value iterWrapper, err := record.NewRawWrapper(b.name, string(key), value) if err != nil { return err } // check validity / access if !iterWrapper.Meta().CheckValidity() { continue } if !iterWrapper.Meta().CheckPermission(local, internal) { continue } // check if matches & send if q.MatchesRecord(iterWrapper) { // copy data duplicate := make([]byte, len(value)) copy(duplicate, value) newWrapper, err := record.NewRawWrapper(b.name, iterWrapper.DatabaseKey(), duplicate) if err != nil { return err } select { case <-queryIter.Done: return nil case queryIter.Next <- newWrapper: default: select { case <-queryIter.Done: return nil case queryIter.Next <- newWrapper: case <-time.After(1 * time.Second): return errors.New("query timeout") } } } } return nil }) queryIter.Finish(err) } // ReadOnly returns whether the database is read only. func (b *BBolt) ReadOnly() bool { return false } // Injected returns whether the database is injected. func (b *BBolt) Injected() bool { return false } // MaintainRecordStates maintains records states in the database. func (b *BBolt) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time, shadowDelete bool) error { //nolint:gocognit now := time.Now().Unix() purgeThreshold := purgeDeletedBefore.Unix() return b.db.Update(func(tx *bbolt.Tx) error { bucket := tx.Bucket(bucketName) // Create a cursor for iteration. c := bucket.Cursor() for key, value := c.First(); key != nil; key, value = c.Next() { // check if context is cancelled select { case <-ctx.Done(): return nil default: } // wrap value wrapper, err := record.NewRawWrapper(b.name, string(key), value) if err != nil { return err } // check if we need to do maintenance meta := wrapper.Meta() switch { case meta.Deleted == 0 && meta.Expires > 0 && meta.Expires < now: if shadowDelete { // mark as deleted meta.Deleted = meta.Expires deleted, err := wrapper.MarshalRecord(wrapper) if err != nil { return err } err = bucket.Put(key, deleted) if err != nil { return err } // Cursor repositioning is required after modifying data. // While the documentation states that this is also required after a // delete, this actually makes the cursor skip a record with the // following c.Next() call of the loop. // Docs/Issue: https://github.com/boltdb/bolt/issues/426#issuecomment-141982984 c.Seek(key) continue } // Immediately delete expired entries if shadowDelete is disabled. fallthrough case meta.Deleted > 0 && (!shadowDelete || meta.Deleted < purgeThreshold): // delete from storage err = c.Delete() if err != nil { return err } } } return nil }) } // Purge deletes all records that match the given query. It returns the number of successful deletes and an error. func (b *BBolt) Purge(ctx context.Context, q *query.Query, local, internal, shadowDelete bool) (int, error) { //nolint:gocognit prefix := []byte(q.DatabaseKeyPrefix()) var cnt int var done bool for !done { err := b.db.Update(func(tx *bbolt.Tx) error { // Create a cursor for iteration. bucket := tx.Bucket(bucketName) c := bucket.Cursor() for key, value := c.Seek(prefix); key != nil; key, value = c.Next() { // Check if context has been cancelled. select { case <-ctx.Done(): done = true return nil default: } // Check if we still match the key prefix, if not, exit. if !bytes.HasPrefix(key, prefix) { done = true return nil } // Wrap the value in a new wrapper to access the metadata. wrapper, err := record.NewRawWrapper(b.name, string(key), value) if err != nil { return err } // Check if we have permission for this record. if !wrapper.Meta().CheckPermission(local, internal) { continue } // Check if record is already deleted. if wrapper.Meta().IsDeleted() { continue } // Check if the query matches this record. if !q.MatchesRecord(wrapper) { continue } // Delete record. if shadowDelete { // Shadow delete. wrapper.Meta().Delete() deleted, err := wrapper.MarshalRecord(wrapper) if err != nil { return err } err = bucket.Put(key, deleted) if err != nil { return err } // Cursor repositioning is required after modifying data. // While the documentation states that this is also required after a // delete, this actually makes the cursor skip a record with the // following c.Next() call of the loop. // Docs/Issue: https://github.com/boltdb/bolt/issues/426#issuecomment-141982984 c.Seek(key) } else { // Immediate delete. err = c.Delete() if err != nil { return err } } // Work in batches of 1000 changes in order to enable other operations in between. cnt++ if cnt%1000 == 0 { return nil } } done = true return nil }) if err != nil { return cnt, err } } return cnt, nil } // Shutdown shuts down the database. func (b *BBolt) Shutdown() error { return b.db.Close() } ================================================ FILE: base/database/storage/bbolt/bbolt_test.go ================================================ package bbolt import ( "context" "os" "reflect" "sync" "testing" "time" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" ) var ( // Compile time interface checks. _ storage.Interface = &BBolt{} _ storage.Batcher = &BBolt{} _ storage.Purger = &BBolt{} ) type TestRecord struct { //nolint:maligned record.Base sync.Mutex S string I int I8 int8 I16 int16 I32 int32 I64 int64 UI uint UI8 uint8 UI16 uint16 UI32 uint32 UI64 uint64 F32 float32 F64 float64 B bool } func TestBBolt(t *testing.T) { t.Parallel() testDir, err := os.MkdirTemp("", "testing-") if err != nil { t.Fatal(err) } defer func() { _ = os.RemoveAll(testDir) // clean up }() // start db, err := NewBBolt("test", testDir) if err != nil { t.Fatal(err) } a := &TestRecord{ S: "banana", I: 42, I8: 42, I16: 42, I32: 42, I64: 42, UI: 42, UI8: 42, UI16: 42, UI32: 42, UI64: 42, F32: 42.42, F64: 42.42, B: true, } a.SetMeta(&record.Meta{}) a.Meta().Update() a.SetKey("test:A") // put record _, err = db.Put(a) if err != nil { t.Fatal(err) } // get and compare r1, err := db.Get("A") if err != nil { t.Fatal(err) } a1 := &TestRecord{} err = record.Unwrap(r1, a1) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(a, a1) { t.Fatalf("mismatch, got %v", a1) } // setup query test records qA := &TestRecord{} qA.SetKey("test:path/to/A") qA.CreateMeta() qB := &TestRecord{} qB.SetKey("test:path/to/B") qB.CreateMeta() qC := &TestRecord{} qC.SetKey("test:path/to/C") qC.CreateMeta() qZ := &TestRecord{} qZ.SetKey("test:z") qZ.CreateMeta() // put _, err = db.Put(qA) if err == nil { _, err = db.Put(qB) } if err == nil { _, err = db.Put(qC) } if err == nil { _, err = db.Put(qZ) } if err != nil { t.Fatal(err) } // test query q := query.New("test:path/to/").MustBeValid() it, err := db.Query(q, true, true) if err != nil { t.Fatal(err) } cnt := 0 for range it.Next { cnt++ } if it.Err() != nil { t.Fatal(it.Err()) } if cnt != 3 { t.Fatalf("unexpected query result count: %d", cnt) } // delete err = db.Delete("A") if err != nil { t.Fatal(err) } // check if its gone _, err = db.Get("A") if err == nil { t.Fatal("should fail") } // maintenance err = db.MaintainRecordStates(context.TODO(), time.Now(), true) if err != nil { t.Fatal(err) } // maintenance err = db.MaintainRecordStates(context.TODO(), time.Now(), false) if err != nil { t.Fatal(err) } // purging purger, ok := db.(storage.Purger) if ok { n, err := purger.Purge(context.TODO(), query.New("test:path/to/").MustBeValid(), true, true, false) if err != nil { t.Fatal(err) } if n != 3 { t.Fatalf("unexpected purge delete count: %d", n) } } else { t.Fatal("should implement Purger") } // test query q = query.New("test").MustBeValid() it, err = db.Query(q, true, true) if err != nil { t.Fatal(err) } cnt = 0 for range it.Next { cnt++ } if it.Err() != nil { t.Fatal(it.Err()) } if cnt != 1 { t.Fatalf("unexpected query result count: %d", cnt) } // shutdown err = db.Shutdown() if err != nil { t.Fatal(err) } } ================================================ FILE: base/database/storage/errors.go ================================================ package storage import "errors" // Errors for storages. var ( ErrNotFound = errors.New("storage entry not found") ErrRecordMalformed = errors.New("record is malformed") ) ================================================ FILE: base/database/storage/fstree/fstree.go ================================================ /* Package fstree provides a dead simple file-based database storage backend. It is primarily meant for easy testing or storing big files that can easily be accesses directly, without datastore. */ package fstree import ( "context" "errors" "fmt" "io/fs" "os" "path/filepath" "runtime" "strings" "time" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" "github.com/safing/portmaster/base/utils/renameio" ) const ( defaultFileMode = os.FileMode(0o0644) defaultDirMode = os.FileMode(0o0755) onWindows = runtime.GOOS == "windows" ) // FSTree database storage. type FSTree struct { name string basePath string } func init() { _ = storage.Register("fstree", NewFSTree) } // NewFSTree returns a (new) FSTree database. func NewFSTree(name, location string) (storage.Interface, error) { basePath, err := filepath.Abs(location) if err != nil { return nil, fmt.Errorf("fstree: failed to validate path %s: %w", location, err) } file, err := os.Stat(basePath) if err != nil { if errors.Is(err, fs.ErrNotExist) { err = os.MkdirAll(basePath, defaultDirMode) if err != nil { return nil, fmt.Errorf("fstree: failed to create directory %s: %w", basePath, err) } } else { return nil, fmt.Errorf("fstree: failed to stat path %s: %w", basePath, err) } } else { if !file.IsDir() { return nil, fmt.Errorf("fstree: provided database path (%s) is a file", basePath) } } return &FSTree{ name: name, basePath: basePath, }, nil } func (fst *FSTree) buildFilePath(key string, checkKeyLength bool) (string, error) { // check key length if checkKeyLength && len(key) < 1 { return "", fmt.Errorf("fstree: key too short: %s", key) } // build filepath dstPath := filepath.Join(fst.basePath, key) // Join also calls Clean() if !strings.HasPrefix(dstPath, fst.basePath) { return "", fmt.Errorf("fstree: key integrity check failed, compiled path is %s", dstPath) } // return return dstPath, nil } // Get returns a database record. func (fst *FSTree) Get(key string) (record.Record, error) { dstPath, err := fst.buildFilePath(key, true) if err != nil { return nil, err } data, err := os.ReadFile(dstPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil, storage.ErrNotFound } return nil, fmt.Errorf("fstree: failed to read file %s: %w", dstPath, err) } r, err := record.NewRawWrapper(fst.name, key, data) if err != nil { return nil, err } return r, nil } // GetMeta returns the metadata of a database record. func (fst *FSTree) GetMeta(key string) (*record.Meta, error) { // TODO: Replace with more performant variant. r, err := fst.Get(key) if err != nil { return nil, err } return r.Meta(), nil } // Put stores a record in the database. func (fst *FSTree) Put(r record.Record) (record.Record, error) { dstPath, err := fst.buildFilePath(r.DatabaseKey(), true) if err != nil { return nil, err } data, err := r.MarshalRecord(r) if err != nil { return nil, err } err = writeFile(dstPath, data, defaultFileMode) if err != nil { // create dir and try again err = os.MkdirAll(filepath.Dir(dstPath), defaultDirMode) if err != nil { return nil, fmt.Errorf("fstree: failed to create directory %s: %w", filepath.Dir(dstPath), err) } err = writeFile(dstPath, data, defaultFileMode) if err != nil { return nil, fmt.Errorf("fstree: could not write file %s: %w", dstPath, err) } } return r, nil } // Delete deletes a record from the database. func (fst *FSTree) Delete(key string) error { dstPath, err := fst.buildFilePath(key, true) if err != nil { return err } // remove entry err = os.Remove(dstPath) if err != nil { return fmt.Errorf("fstree: could not delete %s: %w", dstPath, err) } return nil } // Query returns a an iterator for the supplied query. func (fst *FSTree) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { _, err := q.Check() if err != nil { return nil, fmt.Errorf("invalid query: %w", err) } walkPrefix, err := fst.buildFilePath(q.DatabaseKeyPrefix(), false) if err != nil { return nil, err } fileInfo, err := os.Stat(walkPrefix) var walkRoot string switch { case err == nil && fileInfo.IsDir(): walkRoot = walkPrefix case err == nil: walkRoot = filepath.Dir(walkPrefix) case errors.Is(err, fs.ErrNotExist): walkRoot = filepath.Dir(walkPrefix) default: // err != nil return nil, fmt.Errorf("fstree: could not stat query root %s: %w", walkPrefix, err) } queryIter := iterator.New() go fst.queryExecutor(walkRoot, queryIter, q, local, internal) return queryIter, nil } func (fst *FSTree) queryExecutor(walkRoot string, queryIter *iterator.Iterator, q *query.Query, local, internal bool) { err := filepath.Walk(walkRoot, func(path string, info os.FileInfo, err error) error { if err != nil { return fmt.Errorf("fstree: error in walking fs: %w", err) } if info.IsDir() { // skip dir if not in scope if !strings.HasPrefix(path, fst.basePath) { return filepath.SkipDir } // continue return nil } // still in scope? if !strings.HasPrefix(path, fst.basePath) { return nil } // read file data, err := os.ReadFile(path) if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil } return fmt.Errorf("fstree: failed to read file %s: %w", path, err) } // parse key, err := filepath.Rel(fst.basePath, path) if err != nil { return fmt.Errorf("fstree: failed to extract key from filepath %s: %w", path, err) } r, err := record.NewRawWrapper(fst.name, key, data) if err != nil { return fmt.Errorf("fstree: failed to load file %s: %w", path, err) } if !r.Meta().CheckValidity() { // record is not valid return nil } if !r.Meta().CheckPermission(local, internal) { // no permission to access return nil } // check if matches, then send if q.MatchesRecord(r) { select { case queryIter.Next <- r: case <-queryIter.Done: case <-time.After(1 * time.Second): return errors.New("fstree: query buffer full, timeout") } } return nil }) queryIter.Finish(err) } // ReadOnly returns whether the database is read only. func (fst *FSTree) ReadOnly() bool { return false } // Injected returns whether the database is injected. func (fst *FSTree) Injected() bool { return false } // MaintainRecordStates maintains records states in the database. func (fst *FSTree) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time, shadowDelete bool) error { // TODO: implement MaintainRecordStates return nil } // Shutdown shuts down the database. func (fst *FSTree) Shutdown() error { return nil } // writeFile mirrors os.WriteFile, replacing an existing file with the same // name atomically. This is not atomic on Windows, but still an improvement. // TODO: Replace with github.com/google/renamio.WriteFile as soon as it is fixed on Windows. // TODO: This has become a wont-fix. Explore other options. // This function is forked from https://github.com/google/renameio/blob/a368f9987532a68a3d676566141654a81aa8100b/writefile.go. func writeFile(filename string, data []byte, perm os.FileMode) error { t, err := renameio.TempFile("", filename) if err != nil { return err } defer t.Cleanup() //nolint:errcheck // Set permissions before writing data, in case the data is sensitive. // TODO(vladimir): to set permissions on windows we need the full path of the file. err = t.Chmod(perm) if err != nil { return err } if _, err := t.Write(data); err != nil { return err } return t.CloseAtomicallyReplace() } ================================================ FILE: base/database/storage/fstree/fstree_test.go ================================================ package fstree import "github.com/safing/portmaster/base/database/storage" // Compile time interface checks. var _ storage.Interface = &FSTree{} ================================================ FILE: base/database/storage/hashmap/map.go ================================================ package hashmap import ( "context" "errors" "fmt" "sync" "time" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" ) // HashMap storage. type HashMap struct { name string db map[string]record.Record dbLock sync.RWMutex } func init() { _ = storage.Register("hashmap", NewHashMap) } // NewHashMap creates a hashmap database. func NewHashMap(name, location string) (storage.Interface, error) { return &HashMap{ name: name, db: make(map[string]record.Record), }, nil } // Get returns a database record. func (hm *HashMap) Get(key string) (record.Record, error) { hm.dbLock.RLock() defer hm.dbLock.RUnlock() r, ok := hm.db[key] if !ok { return nil, storage.ErrNotFound } return r, nil } // GetMeta returns the metadata of a database record. func (hm *HashMap) GetMeta(key string) (*record.Meta, error) { // TODO: Replace with more performant variant. r, err := hm.Get(key) if err != nil { return nil, err } return r.Meta(), nil } // Put stores a record in the database. func (hm *HashMap) Put(r record.Record) (record.Record, error) { hm.dbLock.Lock() defer hm.dbLock.Unlock() hm.db[r.DatabaseKey()] = r return r, nil } // PutMany stores many records in the database. func (hm *HashMap) PutMany(shadowDelete bool) (chan<- record.Record, <-chan error) { hm.dbLock.Lock() defer hm.dbLock.Unlock() // we could lock for every record, but we want to have the same behaviour // as the other storage backends, especially for testing. batch := make(chan record.Record, 100) errs := make(chan error, 1) // start handler go func() { for r := range batch { hm.batchPutOrDelete(shadowDelete, r) } errs <- nil }() return batch, errs } func (hm *HashMap) batchPutOrDelete(shadowDelete bool, r record.Record) { r.Lock() defer r.Unlock() hm.dbLock.Lock() defer hm.dbLock.Unlock() if !shadowDelete && r.Meta().IsDeleted() { delete(hm.db, r.DatabaseKey()) } else { hm.db[r.DatabaseKey()] = r } } // Delete deletes a record from the database. func (hm *HashMap) Delete(key string) error { hm.dbLock.Lock() defer hm.dbLock.Unlock() delete(hm.db, key) return nil } // Query returns a an iterator for the supplied query. func (hm *HashMap) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { _, err := q.Check() if err != nil { return nil, fmt.Errorf("invalid query: %w", err) } queryIter := iterator.New() go hm.queryExecutor(queryIter, q, local, internal) return queryIter, nil } func (hm *HashMap) queryExecutor(queryIter *iterator.Iterator, q *query.Query, local, internal bool) { hm.dbLock.RLock() defer hm.dbLock.RUnlock() var err error mapLoop: for key, record := range hm.db { record.Lock() if !q.MatchesKey(key) || !q.MatchesRecord(record) || !record.Meta().CheckValidity() || !record.Meta().CheckPermission(local, internal) { record.Unlock() continue } record.Unlock() select { case <-queryIter.Done: break mapLoop case queryIter.Next <- record: default: select { case <-queryIter.Done: break mapLoop case queryIter.Next <- record: case <-time.After(1 * time.Second): err = errors.New("query timeout") break mapLoop } } } queryIter.Finish(err) } // ReadOnly returns whether the database is read only. func (hm *HashMap) ReadOnly() bool { return false } // Injected returns whether the database is injected. func (hm *HashMap) Injected() bool { return false } // MaintainRecordStates maintains records states in the database. func (hm *HashMap) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time, shadowDelete bool) error { hm.dbLock.Lock() defer hm.dbLock.Unlock() now := time.Now().Unix() purgeThreshold := purgeDeletedBefore.Unix() for key, record := range hm.db { // check if context is cancelled select { case <-ctx.Done(): return nil default: } meta := record.Meta() switch { case meta.Deleted == 0 && meta.Expires > 0 && meta.Expires < now: if shadowDelete { // mark as deleted record.Lock() meta.Deleted = meta.Expires record.Unlock() continue } // Immediately delete expired entries if shadowDelete is disabled. fallthrough case meta.Deleted > 0 && (!shadowDelete || meta.Deleted < purgeThreshold): // delete from storage delete(hm.db, key) } } return nil } // Shutdown shuts down the database. func (hm *HashMap) Shutdown() error { return nil } ================================================ FILE: base/database/storage/hashmap/map_test.go ================================================ package hashmap import ( "reflect" "sync" "testing" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" ) var ( // Compile time interface checks. _ storage.Interface = &HashMap{} _ storage.Batcher = &HashMap{} ) type TestRecord struct { //nolint:maligned record.Base sync.Mutex S string I int I8 int8 I16 int16 I32 int32 I64 int64 UI uint UI8 uint8 UI16 uint16 UI32 uint32 UI64 uint64 F32 float32 F64 float64 B bool } func TestHashMap(t *testing.T) { t.Parallel() // start db, err := NewHashMap("test", "") if err != nil { t.Fatal(err) } a := &TestRecord{ S: "banana", I: 42, I8: 42, I16: 42, I32: 42, I64: 42, UI: 42, UI8: 42, UI16: 42, UI32: 42, UI64: 42, F32: 42.42, F64: 42.42, B: true, } a.SetMeta(&record.Meta{}) a.Meta().Update() a.SetKey("test:A") // put record _, err = db.Put(a) if err != nil { t.Fatal(err) } // get and compare a1, err := db.Get("A") if err != nil { t.Fatal(err) } if !reflect.DeepEqual(a, a1) { t.Fatalf("mismatch, got %v", a1) } // setup query test records qA := &TestRecord{} qA.SetKey("test:path/to/A") qA.CreateMeta() qB := &TestRecord{} qB.SetKey("test:path/to/B") qB.CreateMeta() qC := &TestRecord{} qC.SetKey("test:path/to/C") qC.CreateMeta() qZ := &TestRecord{} qZ.SetKey("test:z") qZ.CreateMeta() // put _, err = db.Put(qA) if err == nil { _, err = db.Put(qB) } if err == nil { _, err = db.Put(qC) } if err == nil { _, err = db.Put(qZ) } if err != nil { t.Fatal(err) } // test query q := query.New("test:path/to/").MustBeValid() it, err := db.Query(q, true, true) if err != nil { t.Fatal(err) } cnt := 0 for range it.Next { cnt++ } if it.Err() != nil { t.Fatal(it.Err()) } if cnt != 3 { t.Fatalf("unexpected query result count: %d", cnt) } // delete err = db.Delete("A") if err != nil { t.Fatal(err) } // check if its gone _, err = db.Get("A") if err == nil { t.Fatal("should fail") } // shutdown err = db.Shutdown() if err != nil { t.Fatal(err) } } ================================================ FILE: base/database/storage/injectbase.go ================================================ package storage import ( "context" "errors" "time" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" ) // ErrNotImplemented is returned when a function is not implemented by a storage. var ErrNotImplemented = errors.New("not implemented") // InjectBase is a dummy base structure to reduce boilerplate code for injected storage interfaces. type InjectBase struct{} // Compile time interface check. var _ Interface = &InjectBase{} // Get returns a database record. func (i *InjectBase) Get(key string) (record.Record, error) { return nil, ErrNotImplemented } // Put stores a record in the database. func (i *InjectBase) Put(m record.Record) (record.Record, error) { return nil, ErrNotImplemented } // Delete deletes a record from the database. func (i *InjectBase) Delete(key string) error { return ErrNotImplemented } // Query returns a an iterator for the supplied query. func (i *InjectBase) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { return nil, ErrNotImplemented } // ReadOnly returns whether the database is read only. func (i *InjectBase) ReadOnly() bool { return true } // Injected returns whether the database is injected. func (i *InjectBase) Injected() bool { return true } // MaintainRecordStates maintains records states in the database. func (i *InjectBase) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time, shadowDelete bool) error { return nil } // Shutdown shuts down the database. func (i *InjectBase) Shutdown() error { return nil } ================================================ FILE: base/database/storage/interface.go ================================================ package storage import ( "context" "time" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" ) // Interface defines the database storage API. type Interface interface { // Primary Interface Get(key string) (record.Record, error) Put(m record.Record) (record.Record, error) Delete(key string) error Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) // Information and Control ReadOnly() bool Injected() bool Shutdown() error // Mandatory Record Maintenance MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time, shadowDelete bool) error } // MetaHandler defines the database storage API for backends that support optimized fetching of only the metadata. type MetaHandler interface { GetMeta(key string) (*record.Meta, error) } // Maintainer defines the database storage API for backends that require regular maintenance. type Maintainer interface { Maintain(ctx context.Context) error MaintainThorough(ctx context.Context) error } // Batcher defines the database storage API for backends that support batch operations. type Batcher interface { PutMany(shadowDelete bool) (batch chan<- record.Record, errs <-chan error) } // Purger defines the database storage API for backends that support the purge operation. type Purger interface { Purge(ctx context.Context, q *query.Query, local, internal, shadowDelete bool) (int, error) } // PurgeOlderThan defines the database storage API for backends that support the PurgeOlderThan operation. type PurgeOlderThan interface { PurgeOlderThan(ctx context.Context, prefix string, purgeBefore time.Time, local, internal, shadowDelete bool) (int, error) } ================================================ FILE: base/database/storage/sinkhole/sinkhole.go ================================================ package sinkhole import ( "context" "errors" "time" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" ) // Sinkhole is a dummy storage. type Sinkhole struct { name string } var ( // Compile time interface checks. _ storage.Interface = &Sinkhole{} _ storage.Maintainer = &Sinkhole{} _ storage.Batcher = &Sinkhole{} ) func init() { _ = storage.Register("sinkhole", NewSinkhole) } // NewSinkhole creates a dummy database. func NewSinkhole(name, location string) (storage.Interface, error) { return &Sinkhole{ name: name, }, nil } // Exists returns whether an entry with the given key exists. func (s *Sinkhole) Exists(key string) (bool, error) { return false, nil } // Get returns a database record. func (s *Sinkhole) Get(key string) (record.Record, error) { return nil, storage.ErrNotFound } // GetMeta returns the metadata of a database record. func (s *Sinkhole) GetMeta(key string) (*record.Meta, error) { return nil, storage.ErrNotFound } // Put stores a record in the database. func (s *Sinkhole) Put(r record.Record) (record.Record, error) { return r, nil } // PutMany stores many records in the database. func (s *Sinkhole) PutMany(shadowDelete bool) (chan<- record.Record, <-chan error) { batch := make(chan record.Record, 100) errs := make(chan error, 1) // start handler go func() { for range batch { // discard everything } errs <- nil }() return batch, errs } // Delete deletes a record from the database. func (s *Sinkhole) Delete(key string) error { return nil } // Query returns a an iterator for the supplied query. func (s *Sinkhole) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { return nil, errors.New("query not implemented by sinkhole") } // ReadOnly returns whether the database is read only. func (s *Sinkhole) ReadOnly() bool { return false } // Injected returns whether the database is injected. func (s *Sinkhole) Injected() bool { return false } // Maintain runs a light maintenance operation on the database. func (s *Sinkhole) Maintain(ctx context.Context) error { return nil } // MaintainThorough runs a thorough maintenance operation on the database. func (s *Sinkhole) MaintainThorough(ctx context.Context) error { return nil } // MaintainRecordStates maintains records states in the database. func (s *Sinkhole) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time, shadowDelete bool) error { return nil } // Shutdown shuts down the database. func (s *Sinkhole) Shutdown() error { return nil } ================================================ FILE: base/database/storage/sqlite/bobgen.yaml ================================================ sqlite: dsn: "testdata/schema.db" except: migrations: no_factory: true ================================================ FILE: base/database/storage/sqlite/dberrors/bob_errors.bob.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package dberrors import ( "errors" "fmt" "strings" sqliteDriver "modernc.org/sqlite" ) // ErrUniqueConstraint captures all unique constraint errors by explicitly leaving `s` empty. var ErrUniqueConstraint = &UniqueConstraintError{s: ""} type UniqueConstraintError struct { // schema is the schema where the unique constraint is defined. schema string // table is the name of the table where the unique constraint is defined. table string // columns are the columns constituting the unique constraint. columns []string // s is a string uniquely identifying the constraint in the raw error message returned from the database. s string } func (e *UniqueConstraintError) Error() string { return e.s } func (e *UniqueConstraintError) Is(target error) bool { var err *sqliteDriver.Error if !errors.As(target, &err) { return false } // 1555 is for Primary Key Constraint // 2067 is for Unique Constraint if err.Code() != 1555 && err.Code() != 2067 { return false } for _, col := range e.columns { if !strings.Contains(err.Error(), fmt.Sprintf("%s.%s", e.table, col)) { return false } } return true } ================================================ FILE: base/database/storage/sqlite/dberrors/bob_main.bob_test.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package dberrors import "github.com/stephenafamo/bob" // Set the testDB to enable tests that use the database var testDB bob.Transactor[bob.Tx] ================================================ FILE: base/database/storage/sqlite/dberrors/records.bob.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package dberrors var RecordErrors = &recordErrors{ ErrUniquePkMainRecords: &UniqueConstraintError{ schema: "", table: "records", columns: []string{"key"}, s: "pk_main_records", }, } type recordErrors struct { ErrUniquePkMainRecords *UniqueConstraintError } ================================================ FILE: base/database/storage/sqlite/dbinfo/bob_types.bob.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package dbinfo import "github.com/aarondl/opt/null" type Table[Cols columns, Idxs indexes, FKs foreignKeys, U uniques, C checks] struct { Schema string Name string Columns Cols Indexes Idxs PrimaryKey *constraint ForeignKeys FKs Uniques U Checks C Comment string } type columns interface { AsSlice() []column } type column struct { Name string DBType string Default string Comment string Nullable bool Generated bool AutoIncr bool } type indexes interface { AsSlice() []index } type index struct { Type string Name string Columns []indexColumn Unique bool Comment string Partial bool } type indexColumn struct { Name string Desc null.Val[bool] IsExpression bool } type constraint struct { Name string Columns []string Comment string } type foreignKeys interface { AsSlice() []foreignKey } type foreignKey struct { constraint ForeignTable string ForeignColumns []string } type uniques interface { AsSlice() []constraint } type checks interface { AsSlice() []check } type check struct { constraint Expression string } ================================================ FILE: base/database/storage/sqlite/dbinfo/records.bob.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package dbinfo import "github.com/aarondl/opt/null" var Records = Table[ recordColumns, recordIndexes, recordForeignKeys, recordUniques, recordChecks, ]{ Schema: "", Name: "records", Columns: recordColumns{ Key: column{ Name: "key", DBType: "TEXT", Default: "", Comment: "", Nullable: false, Generated: false, AutoIncr: false, }, Format: column{ Name: "format", DBType: "SMALLINT", Default: "NULL", Comment: "", Nullable: true, Generated: false, AutoIncr: false, }, Value: column{ Name: "value", DBType: "BLOB", Default: "NULL", Comment: "", Nullable: true, Generated: false, AutoIncr: false, }, Created: column{ Name: "created", DBType: "BIGINT", Default: "", Comment: "", Nullable: false, Generated: false, AutoIncr: false, }, Modified: column{ Name: "modified", DBType: "BIGINT", Default: "", Comment: "", Nullable: false, Generated: false, AutoIncr: false, }, Expires: column{ Name: "expires", DBType: "BIGINT", Default: "0", Comment: "", Nullable: false, Generated: false, AutoIncr: false, }, Deleted: column{ Name: "deleted", DBType: "BIGINT", Default: "0", Comment: "", Nullable: false, Generated: false, AutoIncr: false, }, Secret: column{ Name: "secret", DBType: "BOOLEAN", Default: "FALSE", Comment: "", Nullable: false, Generated: false, AutoIncr: false, }, Crownjewel: column{ Name: "crownjewel", DBType: "BOOLEAN", Default: "FALSE", Comment: "", Nullable: false, Generated: false, AutoIncr: false, }, }, Indexes: recordIndexes{ SqliteAutoindexRecords1: index{ Type: "pk", Name: "sqlite_autoindex_records_1", Columns: []indexColumn{ { Name: "key", Desc: null.FromCond(false, true), IsExpression: false, }, }, Unique: true, Comment: "", Partial: false, }, }, PrimaryKey: &constraint{ Name: "pk_main_records", Columns: []string{"key"}, Comment: "", }, Comment: "", } type recordColumns struct { Key column Format column Value column Created column Modified column Expires column Deleted column Secret column Crownjewel column } func (c recordColumns) AsSlice() []column { return []column{ c.Key, c.Format, c.Value, c.Created, c.Modified, c.Expires, c.Deleted, c.Secret, c.Crownjewel, } } type recordIndexes struct { SqliteAutoindexRecords1 index } func (i recordIndexes) AsSlice() []index { return []index{ i.SqliteAutoindexRecords1, } } type recordForeignKeys struct{} func (f recordForeignKeys) AsSlice() []foreignKey { return []foreignKey{} } type recordUniques struct{} func (u recordUniques) AsSlice() []constraint { return []constraint{} } type recordChecks struct{} func (c recordChecks) AsSlice() []check { return []check{} } ================================================ FILE: base/database/storage/sqlite/factory/bobfactory_context.bob.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package factory import "context" type contextKey string // Relationship Contexts for records var recordWithParentsCascadingCtx = newContextual[bool]("recordWithParentsCascading") // Contextual is a convienience wrapper around context.WithValue and context.Value type contextual[V any] struct { key contextKey } func newContextual[V any](key string) contextual[V] { return contextual[V]{key: contextKey(key)} } func (k contextual[V]) WithValue(ctx context.Context, val V) context.Context { return context.WithValue(ctx, k.key, val) } func (k contextual[V]) Value(ctx context.Context) (V, bool) { v, ok := ctx.Value(k.key).(V) return v, ok } ================================================ FILE: base/database/storage/sqlite/factory/bobfactory_main.bob.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package factory import ( "context" "github.com/aarondl/opt/null" models "github.com/safing/portmaster/base/database/storage/sqlite/models" ) type Factory struct { baseRecordMods RecordModSlice } func New() *Factory { return &Factory{} } func (f *Factory) NewRecord(mods ...RecordMod) *RecordTemplate { return f.NewRecordWithContext(context.Background(), mods...) } func (f *Factory) NewRecordWithContext(ctx context.Context, mods ...RecordMod) *RecordTemplate { o := &RecordTemplate{f: f} if f != nil { f.baseRecordMods.Apply(ctx, o) } RecordModSlice(mods).Apply(ctx, o) return o } func (f *Factory) FromExistingRecord(m *models.Record) *RecordTemplate { o := &RecordTemplate{f: f, alreadyPersisted: true} o.Key = func() string { return m.Key } o.Format = func() null.Val[int64] { return m.Format } o.Value = func() null.Val[[]byte] { return m.Value } o.Created = func() int64 { return m.Created } o.Modified = func() int64 { return m.Modified } o.Expires = func() int64 { return m.Expires } o.Deleted = func() int64 { return m.Deleted } o.Secret = func() bool { return m.Secret } o.Crownjewel = func() bool { return m.Crownjewel } return o } func (f *Factory) ClearBaseRecordMods() { f.baseRecordMods = nil } func (f *Factory) AddBaseRecordMod(mods ...RecordMod) { f.baseRecordMods = append(f.baseRecordMods, mods...) } ================================================ FILE: base/database/storage/sqlite/factory/bobfactory_main.bob_test.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package factory import ( "context" "testing" ) func TestCreateRecord(t *testing.T) { if testDB == nil { t.Skip("skipping test, no DSN provided") } ctx, cancel := context.WithCancel(t.Context()) t.Cleanup(cancel) tx, err := testDB.Begin(ctx) if err != nil { t.Fatalf("Error starting transaction: %v", err) } defer func() { if err := tx.Rollback(ctx); err != nil { t.Fatalf("Error rolling back transaction: %v", err) } }() if _, err := New().NewRecordWithContext(ctx).Create(ctx, tx); err != nil { t.Fatalf("Error creating Record: %v", err) } } ================================================ FILE: base/database/storage/sqlite/factory/bobfactory_random.bob.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package factory import ( "strconv" "strings" "github.com/jaswdr/faker/v2" ) var defaultFaker = faker.New() func random___byte(f *faker.Faker, limits ...string) []byte { if f == nil { f = &defaultFaker } return []byte(random_string(f, limits...)) } func random_bool(f *faker.Faker, limits ...string) bool { if f == nil { f = &defaultFaker } return f.Bool() } func random_int64(f *faker.Faker, limits ...string) int64 { if f == nil { f = &defaultFaker } return f.Int64() } func random_string(f *faker.Faker, limits ...string) string { if f == nil { f = &defaultFaker } val := strings.Join(f.Lorem().Words(f.IntBetween(1, 5)), " ") if len(limits) == 0 { return val } limitInt, _ := strconv.Atoi(limits[0]) if limitInt > 0 && limitInt < len(val) { val = val[:limitInt] } return val } ================================================ FILE: base/database/storage/sqlite/factory/bobfactory_random.bob_test.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package factory import ( "bytes" "testing" "github.com/stephenafamo/bob" ) // Set the testDB to enable tests that use the database var testDB bob.Transactor[bob.Tx] func TestRandom___byte(t *testing.T) { t.Parallel() val1 := random___byte(nil) val2 := random___byte(nil) if bytes.Equal(val1, val2) { t.Fatalf("random___byte() returned the same value twice: %v", val1) } } func TestRandom_int64(t *testing.T) { t.Parallel() val1 := random_int64(nil) val2 := random_int64(nil) if val1 == val2 { t.Fatalf("random_int64() returned the same value twice: %v", val1) } } func TestRandom_string(t *testing.T) { t.Parallel() val1 := random_string(nil) val2 := random_string(nil) if val1 == val2 { t.Fatalf("random_string() returned the same value twice: %v", val1) } } ================================================ FILE: base/database/storage/sqlite/factory/records.bob.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package factory import ( "context" "testing" "github.com/aarondl/opt/null" "github.com/aarondl/opt/omit" "github.com/aarondl/opt/omitnull" "github.com/jaswdr/faker/v2" models "github.com/safing/portmaster/base/database/storage/sqlite/models" "github.com/stephenafamo/bob" ) type RecordMod interface { Apply(context.Context, *RecordTemplate) } type RecordModFunc func(context.Context, *RecordTemplate) func (f RecordModFunc) Apply(ctx context.Context, n *RecordTemplate) { f(ctx, n) } type RecordModSlice []RecordMod func (mods RecordModSlice) Apply(ctx context.Context, n *RecordTemplate) { for _, f := range mods { f.Apply(ctx, n) } } // RecordTemplate is an object representing the database table. // all columns are optional and should be set by mods type RecordTemplate struct { Key func() string Format func() null.Val[int64] Value func() null.Val[[]byte] Created func() int64 Modified func() int64 Expires func() int64 Deleted func() int64 Secret func() bool Crownjewel func() bool f *Factory alreadyPersisted bool } // Apply mods to the RecordTemplate func (o *RecordTemplate) Apply(ctx context.Context, mods ...RecordMod) { for _, mod := range mods { mod.Apply(ctx, o) } } // setModelRels creates and sets the relationships on *models.Record // according to the relationships in the template. Nothing is inserted into the db func (t RecordTemplate) setModelRels(o *models.Record) {} // BuildSetter returns an *models.RecordSetter // this does nothing with the relationship templates func (o RecordTemplate) BuildSetter() *models.RecordSetter { m := &models.RecordSetter{} if o.Key != nil { val := o.Key() m.Key = omit.From(val) } if o.Format != nil { val := o.Format() m.Format = omitnull.FromNull(val) } if o.Value != nil { val := o.Value() m.Value = omitnull.FromNull(val) } if o.Created != nil { val := o.Created() m.Created = omit.From(val) } if o.Modified != nil { val := o.Modified() m.Modified = omit.From(val) } if o.Expires != nil { val := o.Expires() m.Expires = omit.From(val) } if o.Deleted != nil { val := o.Deleted() m.Deleted = omit.From(val) } if o.Secret != nil { val := o.Secret() m.Secret = omit.From(val) } if o.Crownjewel != nil { val := o.Crownjewel() m.Crownjewel = omit.From(val) } return m } // BuildManySetter returns an []*models.RecordSetter // this does nothing with the relationship templates func (o RecordTemplate) BuildManySetter(number int) []*models.RecordSetter { m := make([]*models.RecordSetter, number) for i := range m { m[i] = o.BuildSetter() } return m } // Build returns an *models.Record // Related objects are also created and placed in the .R field // NOTE: Objects are not inserted into the database. Use RecordTemplate.Create func (o RecordTemplate) Build() *models.Record { m := &models.Record{} if o.Key != nil { m.Key = o.Key() } if o.Format != nil { m.Format = o.Format() } if o.Value != nil { m.Value = o.Value() } if o.Created != nil { m.Created = o.Created() } if o.Modified != nil { m.Modified = o.Modified() } if o.Expires != nil { m.Expires = o.Expires() } if o.Deleted != nil { m.Deleted = o.Deleted() } if o.Secret != nil { m.Secret = o.Secret() } if o.Crownjewel != nil { m.Crownjewel = o.Crownjewel() } o.setModelRels(m) return m } // BuildMany returns an models.RecordSlice // Related objects are also created and placed in the .R field // NOTE: Objects are not inserted into the database. Use RecordTemplate.CreateMany func (o RecordTemplate) BuildMany(number int) models.RecordSlice { m := make(models.RecordSlice, number) for i := range m { m[i] = o.Build() } return m } func ensureCreatableRecord(m *models.RecordSetter) { if !(m.Key.IsValue()) { val := random_string(nil) m.Key = omit.From(val) } if !(m.Created.IsValue()) { val := random_int64(nil) m.Created = omit.From(val) } if !(m.Modified.IsValue()) { val := random_int64(nil) m.Modified = omit.From(val) } } // insertOptRels creates and inserts any optional the relationships on *models.Record // according to the relationships in the template. // any required relationship should have already exist on the model func (o *RecordTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.Record) error { var err error return err } // Create builds a record and inserts it into the database // Relations objects are also inserted and placed in the .R field func (o *RecordTemplate) Create(ctx context.Context, exec bob.Executor) (*models.Record, error) { var err error opt := o.BuildSetter() ensureCreatableRecord(opt) m, err := models.Records.Insert(opt).One(ctx, exec) if err != nil { return nil, err } if err := o.insertOptRels(ctx, exec, m); err != nil { return nil, err } return m, err } // MustCreate builds a record and inserts it into the database // Relations objects are also inserted and placed in the .R field // panics if an error occurs func (o *RecordTemplate) MustCreate(ctx context.Context, exec bob.Executor) *models.Record { m, err := o.Create(ctx, exec) if err != nil { panic(err) } return m } // CreateOrFail builds a record and inserts it into the database // Relations objects are also inserted and placed in the .R field // It calls `tb.Fatal(err)` on the test/benchmark if an error occurs func (o *RecordTemplate) CreateOrFail(ctx context.Context, tb testing.TB, exec bob.Executor) *models.Record { tb.Helper() m, err := o.Create(ctx, exec) if err != nil { tb.Fatal(err) return nil } return m } // CreateMany builds multiple records and inserts them into the database // Relations objects are also inserted and placed in the .R field func (o RecordTemplate) CreateMany(ctx context.Context, exec bob.Executor, number int) (models.RecordSlice, error) { var err error m := make(models.RecordSlice, number) for i := range m { m[i], err = o.Create(ctx, exec) if err != nil { return nil, err } } return m, nil } // MustCreateMany builds multiple records and inserts them into the database // Relations objects are also inserted and placed in the .R field // panics if an error occurs func (o RecordTemplate) MustCreateMany(ctx context.Context, exec bob.Executor, number int) models.RecordSlice { m, err := o.CreateMany(ctx, exec, number) if err != nil { panic(err) } return m } // CreateManyOrFail builds multiple records and inserts them into the database // Relations objects are also inserted and placed in the .R field // It calls `tb.Fatal(err)` on the test/benchmark if an error occurs func (o RecordTemplate) CreateManyOrFail(ctx context.Context, tb testing.TB, exec bob.Executor, number int) models.RecordSlice { tb.Helper() m, err := o.CreateMany(ctx, exec, number) if err != nil { tb.Fatal(err) return nil } return m } // Record has methods that act as mods for the RecordTemplate var RecordMods recordMods type recordMods struct{} func (m recordMods) RandomizeAllColumns(f *faker.Faker) RecordMod { return RecordModSlice{ RecordMods.RandomKey(f), RecordMods.RandomFormat(f), RecordMods.RandomValue(f), RecordMods.RandomCreated(f), RecordMods.RandomModified(f), RecordMods.RandomExpires(f), RecordMods.RandomDeleted(f), RecordMods.RandomSecret(f), RecordMods.RandomCrownjewel(f), } } // Set the model columns to this value func (m recordMods) Key(val string) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Key = func() string { return val } }) } // Set the Column from the function func (m recordMods) KeyFunc(f func() string) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Key = f }) } // Clear any values for the column func (m recordMods) UnsetKey() RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Key = nil }) } // Generates a random value for the column using the given faker // if faker is nil, a default faker is used func (m recordMods) RandomKey(f *faker.Faker) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Key = func() string { return random_string(f) } }) } // Set the model columns to this value func (m recordMods) Format(val null.Val[int64]) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Format = func() null.Val[int64] { return val } }) } // Set the Column from the function func (m recordMods) FormatFunc(f func() null.Val[int64]) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Format = f }) } // Clear any values for the column func (m recordMods) UnsetFormat() RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Format = nil }) } // Generates a random value for the column using the given faker // if faker is nil, a default faker is used // The generated value is sometimes null func (m recordMods) RandomFormat(f *faker.Faker) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Format = func() null.Val[int64] { if f == nil { f = &defaultFaker } val := random_int64(f) return null.From(val) } }) } // Generates a random value for the column using the given faker // if faker is nil, a default faker is used // The generated value is never null func (m recordMods) RandomFormatNotNull(f *faker.Faker) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Format = func() null.Val[int64] { if f == nil { f = &defaultFaker } val := random_int64(f) return null.From(val) } }) } // Set the model columns to this value func (m recordMods) Value(val null.Val[[]byte]) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Value = func() null.Val[[]byte] { return val } }) } // Set the Column from the function func (m recordMods) ValueFunc(f func() null.Val[[]byte]) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Value = f }) } // Clear any values for the column func (m recordMods) UnsetValue() RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Value = nil }) } // Generates a random value for the column using the given faker // if faker is nil, a default faker is used // The generated value is sometimes null func (m recordMods) RandomValue(f *faker.Faker) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Value = func() null.Val[[]byte] { if f == nil { f = &defaultFaker } val := random___byte(f) return null.From(val) } }) } // Generates a random value for the column using the given faker // if faker is nil, a default faker is used // The generated value is never null func (m recordMods) RandomValueNotNull(f *faker.Faker) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Value = func() null.Val[[]byte] { if f == nil { f = &defaultFaker } val := random___byte(f) return null.From(val) } }) } // Set the model columns to this value func (m recordMods) Created(val int64) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Created = func() int64 { return val } }) } // Set the Column from the function func (m recordMods) CreatedFunc(f func() int64) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Created = f }) } // Clear any values for the column func (m recordMods) UnsetCreated() RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Created = nil }) } // Generates a random value for the column using the given faker // if faker is nil, a default faker is used func (m recordMods) RandomCreated(f *faker.Faker) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Created = func() int64 { return random_int64(f) } }) } // Set the model columns to this value func (m recordMods) Modified(val int64) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Modified = func() int64 { return val } }) } // Set the Column from the function func (m recordMods) ModifiedFunc(f func() int64) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Modified = f }) } // Clear any values for the column func (m recordMods) UnsetModified() RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Modified = nil }) } // Generates a random value for the column using the given faker // if faker is nil, a default faker is used func (m recordMods) RandomModified(f *faker.Faker) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Modified = func() int64 { return random_int64(f) } }) } // Set the model columns to this value func (m recordMods) Expires(val int64) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Expires = func() int64 { return val } }) } // Set the Column from the function func (m recordMods) ExpiresFunc(f func() int64) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Expires = f }) } // Clear any values for the column func (m recordMods) UnsetExpires() RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Expires = nil }) } // Generates a random value for the column using the given faker // if faker is nil, a default faker is used func (m recordMods) RandomExpires(f *faker.Faker) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Expires = func() int64 { return random_int64(f) } }) } // Set the model columns to this value func (m recordMods) Deleted(val int64) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Deleted = func() int64 { return val } }) } // Set the Column from the function func (m recordMods) DeletedFunc(f func() int64) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Deleted = f }) } // Clear any values for the column func (m recordMods) UnsetDeleted() RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Deleted = nil }) } // Generates a random value for the column using the given faker // if faker is nil, a default faker is used func (m recordMods) RandomDeleted(f *faker.Faker) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Deleted = func() int64 { return random_int64(f) } }) } // Set the model columns to this value func (m recordMods) Secret(val bool) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Secret = func() bool { return val } }) } // Set the Column from the function func (m recordMods) SecretFunc(f func() bool) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Secret = f }) } // Clear any values for the column func (m recordMods) UnsetSecret() RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Secret = nil }) } // Generates a random value for the column using the given faker // if faker is nil, a default faker is used func (m recordMods) RandomSecret(f *faker.Faker) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Secret = func() bool { return random_bool(f) } }) } // Set the model columns to this value func (m recordMods) Crownjewel(val bool) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Crownjewel = func() bool { return val } }) } // Set the Column from the function func (m recordMods) CrownjewelFunc(f func() bool) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Crownjewel = f }) } // Clear any values for the column func (m recordMods) UnsetCrownjewel() RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Crownjewel = nil }) } // Generates a random value for the column using the given faker // if faker is nil, a default faker is used func (m recordMods) RandomCrownjewel(f *faker.Faker) RecordMod { return RecordModFunc(func(_ context.Context, o *RecordTemplate) { o.Crownjewel = func() bool { return random_bool(f) } }) } func (m recordMods) WithParentsCascading() RecordMod { return RecordModFunc(func(ctx context.Context, o *RecordTemplate) { if isDone, _ := recordWithParentsCascadingCtx.Value(ctx); isDone { return } ctx = recordWithParentsCascadingCtx.WithValue(ctx, true) }) } ================================================ FILE: base/database/storage/sqlite/migrations/0_settings.sql ================================================ -- +migrate Up -- SQL in section 'Up' is executed when this migration is applied PRAGMA auto_vacuum = INCREMENTAL; -- https://sqlite.org/pragma.html#pragma_auto_vacuum -- +migrate Down -- SQL section 'Down' is executed when this migration is rolled back PRAGMA auto_vacuum = NONE; -- https://sqlite.org/pragma.html#pragma_auto_vacuum ================================================ FILE: base/database/storage/sqlite/migrations/1_initial.sql ================================================ -- +migrate Up -- SQL in section 'Up' is executed when this migration is applied CREATE TABLE records ( key TEXT PRIMARY KEY, format SMALLINT, value BLOB, created BIGINT NOT NULL, modified BIGINT NOT NULL, expires BIGINT DEFAULT 0 NOT NULL, deleted BIGINT DEFAULT 0 NOT NULL, secret BOOLEAN DEFAULT FALSE NOT NULL, crownjewel BOOLEAN DEFAULT FALSE NOT NULL ); -- +migrate Down -- SQL section 'Down' is executed when this migration is rolled back DROP TABLE records; ================================================ FILE: base/database/storage/sqlite/migrations_config.yml ================================================ development: dialect: sqlite3 datasource: testdata/schema.db dir: migrations table: migrations ================================================ FILE: base/database/storage/sqlite/models/bob_joins.bob.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package models import ( "hash/maphash" "github.com/stephenafamo/bob" "github.com/stephenafamo/bob/clause" "github.com/stephenafamo/bob/dialect/sqlite/dialect" ) var ( SelectJoins = getJoins[*dialect.SelectQuery] UpdateJoins = getJoins[*dialect.UpdateQuery] ) type joinSet[Q interface{ aliasedAs(string) Q }] struct { InnerJoin Q LeftJoin Q RightJoin Q } func (j joinSet[Q]) AliasedAs(alias string) joinSet[Q] { return joinSet[Q]{ InnerJoin: j.InnerJoin.aliasedAs(alias), LeftJoin: j.LeftJoin.aliasedAs(alias), RightJoin: j.RightJoin.aliasedAs(alias), } } type joins[Q dialect.Joinable] struct{} func buildJoinSet[Q interface{ aliasedAs(string) Q }, C any, F func(C, string) Q](c C, f F) joinSet[Q] { return joinSet[Q]{ InnerJoin: f(c, clause.InnerJoin), LeftJoin: f(c, clause.LeftJoin), RightJoin: f(c, clause.RightJoin), } } func getJoins[Q dialect.Joinable]() joins[Q] { return joins[Q]{} } type modAs[Q any, C interface{ AliasedAs(string) C }] struct { c C f func(C) bob.Mod[Q] } func (m modAs[Q, C]) Apply(q Q) { m.f(m.c).Apply(q) } func (m modAs[Q, C]) AliasedAs(alias string) bob.Mod[Q] { m.c = m.c.AliasedAs(alias) return m } func randInt() int64 { out := int64(new(maphash.Hash).Sum64()) if out < 0 { return -out % 10000 } return out % 10000 } ================================================ FILE: base/database/storage/sqlite/models/bob_loaders.bob.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package models import ( "context" "database/sql" "errors" "fmt" "github.com/stephenafamo/bob" "github.com/stephenafamo/bob/dialect/sqlite/dialect" "github.com/stephenafamo/bob/orm" ) var Preload = getPreloaders() type preloaders struct{} func getPreloaders() preloaders { return preloaders{} } var ( SelectThenLoad = getThenLoaders[*dialect.SelectQuery]() InsertThenLoad = getThenLoaders[*dialect.InsertQuery]() UpdateThenLoad = getThenLoaders[*dialect.UpdateQuery]() ) type thenLoaders[Q orm.Loadable] struct{} func getThenLoaders[Q orm.Loadable]() thenLoaders[Q] { return thenLoaders[Q]{} } func thenLoadBuilder[Q orm.Loadable, T any](name string, f func(context.Context, bob.Executor, T, ...bob.Mod[*dialect.SelectQuery]) error) func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] { return func(queryMods ...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] { return func(ctx context.Context, exec bob.Executor, retrieved any) error { loader, isLoader := retrieved.(T) if !isLoader { return fmt.Errorf("object %T cannot load %q", retrieved, name) } err := f(ctx, exec, loader, queryMods...) // Don't cause an issue due to missing relationships if errors.Is(err, sql.ErrNoRows) { return nil } return err } } } ================================================ FILE: base/database/storage/sqlite/models/bob_types.bob_test.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package models import "github.com/stephenafamo/bob" // Set the testDB to enable tests that use the database var testDB bob.Transactor[bob.Tx] // Make sure the type Record runs hooks after queries var _ bob.HookableType = &Record{} ================================================ FILE: base/database/storage/sqlite/models/bob_where.bob.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package models import ( "github.com/stephenafamo/bob/clause" "github.com/stephenafamo/bob/dialect/sqlite" "github.com/stephenafamo/bob/dialect/sqlite/dialect" ) var ( SelectWhere = Where[*dialect.SelectQuery]() UpdateWhere = Where[*dialect.UpdateQuery]() DeleteWhere = Where[*dialect.DeleteQuery]() OnConflictWhere = Where[*clause.ConflictClause]() // Used in ON CONFLICT DO UPDATE ) func Where[Q sqlite.Filterable]() struct { Records recordWhere[Q] } { return struct { Records recordWhere[Q] }{ Records: buildRecordWhere[Q](Records.Columns), } } ================================================ FILE: base/database/storage/sqlite/models/records.bob.go ================================================ // Code generated by BobGen sqlite v0.41.1. DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package models import ( "context" "io" "github.com/aarondl/opt/null" "github.com/aarondl/opt/omit" "github.com/aarondl/opt/omitnull" "github.com/stephenafamo/bob" "github.com/stephenafamo/bob/dialect/sqlite" "github.com/stephenafamo/bob/dialect/sqlite/dialect" "github.com/stephenafamo/bob/dialect/sqlite/dm" "github.com/stephenafamo/bob/dialect/sqlite/sm" "github.com/stephenafamo/bob/dialect/sqlite/um" "github.com/stephenafamo/bob/expr" ) // Record is an object representing the database table. type Record struct { Key string `db:"key,pk" ` Format null.Val[int64] `db:"format" ` Value null.Val[[]byte] `db:"value" ` Created int64 `db:"created" ` Modified int64 `db:"modified" ` Expires int64 `db:"expires" ` Deleted int64 `db:"deleted" ` Secret bool `db:"secret" ` Crownjewel bool `db:"crownjewel" ` } // RecordSlice is an alias for a slice of pointers to Record. // This should almost always be used instead of []*Record. type RecordSlice []*Record // Records contains methods to work with the records table var Records = sqlite.NewTablex[*Record, RecordSlice, *RecordSetter]("", "records", buildRecordColumns("records")) // RecordsQuery is a query on the records table type RecordsQuery = *sqlite.ViewQuery[*Record, RecordSlice] func buildRecordColumns(alias string) recordColumns { return recordColumns{ ColumnsExpr: expr.NewColumnsExpr( "key", "format", "value", "created", "modified", "expires", "deleted", "secret", "crownjewel", ).WithParent("records"), tableAlias: alias, Key: sqlite.Quote(alias, "key"), Format: sqlite.Quote(alias, "format"), Value: sqlite.Quote(alias, "value"), Created: sqlite.Quote(alias, "created"), Modified: sqlite.Quote(alias, "modified"), Expires: sqlite.Quote(alias, "expires"), Deleted: sqlite.Quote(alias, "deleted"), Secret: sqlite.Quote(alias, "secret"), Crownjewel: sqlite.Quote(alias, "crownjewel"), } } type recordColumns struct { expr.ColumnsExpr tableAlias string Key sqlite.Expression Format sqlite.Expression Value sqlite.Expression Created sqlite.Expression Modified sqlite.Expression Expires sqlite.Expression Deleted sqlite.Expression Secret sqlite.Expression Crownjewel sqlite.Expression } func (c recordColumns) Alias() string { return c.tableAlias } func (recordColumns) AliasedAs(alias string) recordColumns { return buildRecordColumns(alias) } // RecordSetter is used for insert/upsert/update operations // All values are optional, and do not have to be set // Generated columns are not included type RecordSetter struct { Key omit.Val[string] `db:"key,pk" ` Format omitnull.Val[int64] `db:"format" ` Value omitnull.Val[[]byte] `db:"value" ` Created omit.Val[int64] `db:"created" ` Modified omit.Val[int64] `db:"modified" ` Expires omit.Val[int64] `db:"expires" ` Deleted omit.Val[int64] `db:"deleted" ` Secret omit.Val[bool] `db:"secret" ` Crownjewel omit.Val[bool] `db:"crownjewel" ` } func (s RecordSetter) SetColumns() []string { vals := make([]string, 0, 9) if s.Key.IsValue() { vals = append(vals, "key") } if !s.Format.IsUnset() { vals = append(vals, "format") } if !s.Value.IsUnset() { vals = append(vals, "value") } if s.Created.IsValue() { vals = append(vals, "created") } if s.Modified.IsValue() { vals = append(vals, "modified") } if s.Expires.IsValue() { vals = append(vals, "expires") } if s.Deleted.IsValue() { vals = append(vals, "deleted") } if s.Secret.IsValue() { vals = append(vals, "secret") } if s.Crownjewel.IsValue() { vals = append(vals, "crownjewel") } return vals } func (s RecordSetter) Overwrite(t *Record) { if s.Key.IsValue() { t.Key = s.Key.MustGet() } if !s.Format.IsUnset() { t.Format = s.Format.MustGetNull() } if !s.Value.IsUnset() { t.Value = s.Value.MustGetNull() } if s.Created.IsValue() { t.Created = s.Created.MustGet() } if s.Modified.IsValue() { t.Modified = s.Modified.MustGet() } if s.Expires.IsValue() { t.Expires = s.Expires.MustGet() } if s.Deleted.IsValue() { t.Deleted = s.Deleted.MustGet() } if s.Secret.IsValue() { t.Secret = s.Secret.MustGet() } if s.Crownjewel.IsValue() { t.Crownjewel = s.Crownjewel.MustGet() } } func (s *RecordSetter) Apply(q *dialect.InsertQuery) { q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { return Records.BeforeInsertHooks.RunHooks(ctx, exec, s) }) if len(q.TableRef.Columns) == 0 { q.TableRef.Columns = s.SetColumns() if len(q.TableRef.Columns) == 0 { q.TableRef.Columns = []string{"key"} } } q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { vals := make([]bob.Expression, 0, 9) if s.Key.IsValue() { vals = append(vals, sqlite.Arg(s.Key.MustGet())) } if !s.Format.IsUnset() { vals = append(vals, sqlite.Arg(s.Format.MustGetNull())) } if !s.Value.IsUnset() { vals = append(vals, sqlite.Arg(s.Value.MustGetNull())) } if s.Created.IsValue() { vals = append(vals, sqlite.Arg(s.Created.MustGet())) } if s.Modified.IsValue() { vals = append(vals, sqlite.Arg(s.Modified.MustGet())) } if s.Expires.IsValue() { vals = append(vals, sqlite.Arg(s.Expires.MustGet())) } if s.Deleted.IsValue() { vals = append(vals, sqlite.Arg(s.Deleted.MustGet())) } if s.Secret.IsValue() { vals = append(vals, sqlite.Arg(s.Secret.MustGet())) } if s.Crownjewel.IsValue() { vals = append(vals, sqlite.Arg(s.Crownjewel.MustGet())) } if len(vals) == 0 { vals = append(vals, sqlite.Arg(nil)) } return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") })) } func (s RecordSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { return um.Set(s.Expressions()...) } func (s RecordSetter) Expressions(prefix ...string) []bob.Expression { exprs := make([]bob.Expression, 0, 9) if s.Key.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ sqlite.Quote(append(prefix, "key")...), sqlite.Arg(s.Key), }}) } if !s.Format.IsUnset() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ sqlite.Quote(append(prefix, "format")...), sqlite.Arg(s.Format), }}) } if !s.Value.IsUnset() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ sqlite.Quote(append(prefix, "value")...), sqlite.Arg(s.Value), }}) } if s.Created.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ sqlite.Quote(append(prefix, "created")...), sqlite.Arg(s.Created), }}) } if s.Modified.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ sqlite.Quote(append(prefix, "modified")...), sqlite.Arg(s.Modified), }}) } if s.Expires.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ sqlite.Quote(append(prefix, "expires")...), sqlite.Arg(s.Expires), }}) } if s.Deleted.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ sqlite.Quote(append(prefix, "deleted")...), sqlite.Arg(s.Deleted), }}) } if s.Secret.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ sqlite.Quote(append(prefix, "secret")...), sqlite.Arg(s.Secret), }}) } if s.Crownjewel.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ sqlite.Quote(append(prefix, "crownjewel")...), sqlite.Arg(s.Crownjewel), }}) } return exprs } // FindRecord retrieves a single record by primary key // If cols is empty Find will return all columns. func FindRecord(ctx context.Context, exec bob.Executor, KeyPK string, cols ...string) (*Record, error) { if len(cols) == 0 { return Records.Query( sm.Where(Records.Columns.Key.EQ(sqlite.Arg(KeyPK))), ).One(ctx, exec) } return Records.Query( sm.Where(Records.Columns.Key.EQ(sqlite.Arg(KeyPK))), sm.Columns(Records.Columns.Only(cols...)), ).One(ctx, exec) } // RecordExists checks the presence of a single record by primary key func RecordExists(ctx context.Context, exec bob.Executor, KeyPK string) (bool, error) { return Records.Query( sm.Where(Records.Columns.Key.EQ(sqlite.Arg(KeyPK))), ).Exists(ctx, exec) } // AfterQueryHook is called after Record is retrieved from the database func (o *Record) AfterQueryHook(ctx context.Context, exec bob.Executor, queryType bob.QueryType) error { var err error switch queryType { case bob.QueryTypeSelect: ctx, err = Records.AfterSelectHooks.RunHooks(ctx, exec, RecordSlice{o}) case bob.QueryTypeInsert: ctx, err = Records.AfterInsertHooks.RunHooks(ctx, exec, RecordSlice{o}) case bob.QueryTypeUpdate: ctx, err = Records.AfterUpdateHooks.RunHooks(ctx, exec, RecordSlice{o}) case bob.QueryTypeDelete: ctx, err = Records.AfterDeleteHooks.RunHooks(ctx, exec, RecordSlice{o}) } return err } // primaryKeyVals returns the primary key values of the Record func (o *Record) primaryKeyVals() bob.Expression { return sqlite.Arg(o.Key) } func (o *Record) pkEQ() dialect.Expression { return sqlite.Quote("records", "key").EQ(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { return o.primaryKeyVals().WriteSQL(ctx, w, d, start) })) } // Update uses an executor to update the Record func (o *Record) Update(ctx context.Context, exec bob.Executor, s *RecordSetter) error { v, err := Records.Update(s.UpdateMod(), um.Where(o.pkEQ())).One(ctx, exec) if err != nil { return err } *o = *v return nil } // Delete deletes a single Record record with an executor func (o *Record) Delete(ctx context.Context, exec bob.Executor) error { _, err := Records.Delete(dm.Where(o.pkEQ())).Exec(ctx, exec) return err } // Reload refreshes the Record using the executor func (o *Record) Reload(ctx context.Context, exec bob.Executor) error { o2, err := Records.Query( sm.Where(Records.Columns.Key.EQ(sqlite.Arg(o.Key))), ).One(ctx, exec) if err != nil { return err } *o = *o2 return nil } // AfterQueryHook is called after RecordSlice is retrieved from the database func (o RecordSlice) AfterQueryHook(ctx context.Context, exec bob.Executor, queryType bob.QueryType) error { var err error switch queryType { case bob.QueryTypeSelect: ctx, err = Records.AfterSelectHooks.RunHooks(ctx, exec, o) case bob.QueryTypeInsert: ctx, err = Records.AfterInsertHooks.RunHooks(ctx, exec, o) case bob.QueryTypeUpdate: ctx, err = Records.AfterUpdateHooks.RunHooks(ctx, exec, o) case bob.QueryTypeDelete: ctx, err = Records.AfterDeleteHooks.RunHooks(ctx, exec, o) } return err } func (o RecordSlice) pkIN() dialect.Expression { if len(o) == 0 { return sqlite.Raw("NULL") } return sqlite.Quote("records", "key").In(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { pkPairs := make([]bob.Expression, len(o)) for i, row := range o { pkPairs[i] = row.primaryKeyVals() } return bob.ExpressSlice(ctx, w, d, start, pkPairs, "", ", ", "") })) } // copyMatchingRows finds models in the given slice that have the same primary key // then it first copies the existing relationships from the old model to the new model // and then replaces the old model in the slice with the new model func (o RecordSlice) copyMatchingRows(from ...*Record) { for i, old := range o { for _, new := range from { if new.Key != old.Key { continue } o[i] = new break } } } // UpdateMod modifies an update query with "WHERE primary_key IN (o...)" func (o RecordSlice) UpdateMod() bob.Mod[*dialect.UpdateQuery] { return bob.ModFunc[*dialect.UpdateQuery](func(q *dialect.UpdateQuery) { q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { return Records.BeforeUpdateHooks.RunHooks(ctx, exec, o) }) q.AppendLoader(bob.LoaderFunc(func(ctx context.Context, exec bob.Executor, retrieved any) error { var err error switch retrieved := retrieved.(type) { case *Record: o.copyMatchingRows(retrieved) case []*Record: o.copyMatchingRows(retrieved...) case RecordSlice: o.copyMatchingRows(retrieved...) default: // If the retrieved value is not a Record or a slice of Record // then run the AfterUpdateHooks on the slice _, err = Records.AfterUpdateHooks.RunHooks(ctx, exec, o) } return err })) q.AppendWhere(o.pkIN()) }) } // DeleteMod modifies an delete query with "WHERE primary_key IN (o...)" func (o RecordSlice) DeleteMod() bob.Mod[*dialect.DeleteQuery] { return bob.ModFunc[*dialect.DeleteQuery](func(q *dialect.DeleteQuery) { q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { return Records.BeforeDeleteHooks.RunHooks(ctx, exec, o) }) q.AppendLoader(bob.LoaderFunc(func(ctx context.Context, exec bob.Executor, retrieved any) error { var err error switch retrieved := retrieved.(type) { case *Record: o.copyMatchingRows(retrieved) case []*Record: o.copyMatchingRows(retrieved...) case RecordSlice: o.copyMatchingRows(retrieved...) default: // If the retrieved value is not a Record or a slice of Record // then run the AfterDeleteHooks on the slice _, err = Records.AfterDeleteHooks.RunHooks(ctx, exec, o) } return err })) q.AppendWhere(o.pkIN()) }) } func (o RecordSlice) UpdateAll(ctx context.Context, exec bob.Executor, vals RecordSetter) error { if len(o) == 0 { return nil } _, err := Records.Update(vals.UpdateMod(), o.UpdateMod()).All(ctx, exec) return err } func (o RecordSlice) DeleteAll(ctx context.Context, exec bob.Executor) error { if len(o) == 0 { return nil } _, err := Records.Delete(o.DeleteMod()).Exec(ctx, exec) return err } func (o RecordSlice) ReloadAll(ctx context.Context, exec bob.Executor) error { if len(o) == 0 { return nil } o2, err := Records.Query(sm.Where(o.pkIN())).All(ctx, exec) if err != nil { return err } o.copyMatchingRows(o2...) return nil } type recordWhere[Q sqlite.Filterable] struct { Key sqlite.WhereMod[Q, string] Format sqlite.WhereNullMod[Q, int64] Value sqlite.WhereNullMod[Q, []byte] Created sqlite.WhereMod[Q, int64] Modified sqlite.WhereMod[Q, int64] Expires sqlite.WhereMod[Q, int64] Deleted sqlite.WhereMod[Q, int64] Secret sqlite.WhereMod[Q, bool] Crownjewel sqlite.WhereMod[Q, bool] } func (recordWhere[Q]) AliasedAs(alias string) recordWhere[Q] { return buildRecordWhere[Q](buildRecordColumns(alias)) } func buildRecordWhere[Q sqlite.Filterable](cols recordColumns) recordWhere[Q] { return recordWhere[Q]{ Key: sqlite.Where[Q, string](cols.Key), Format: sqlite.WhereNull[Q, int64](cols.Format), Value: sqlite.WhereNull[Q, []byte](cols.Value), Created: sqlite.Where[Q, int64](cols.Created), Modified: sqlite.Where[Q, int64](cols.Modified), Expires: sqlite.Where[Q, int64](cols.Expires), Deleted: sqlite.Where[Q, int64](cols.Deleted), Secret: sqlite.Where[Q, bool](cols.Secret), Crownjewel: sqlite.Where[Q, bool](cols.Crownjewel), } } ================================================ FILE: base/database/storage/sqlite/prepared.go ================================================ package sqlite import ( "context" "fmt" "strconv" "github.com/stephenafamo/bob" "github.com/stephenafamo/bob/dialect/sqlite/im" "github.com/stephenafamo/bob/expr" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" "github.com/safing/portmaster/base/database/storage/sqlite/models" "github.com/safing/structures/dsd" ) var UsePreparedStatements bool = true // PutMany stores many records in the database. func (db *SQLite) putManyWithPreparedStmts(shadowDelete bool) (chan<- record.Record, <-chan error) { batch := make(chan record.Record, 100) errs := make(chan error, 1) // Simulate upsert with custom selection on conflict. rawQuery, _, err := models.Records.Insert( im.Into("records", "key", "format", "value", "created", "modified", "expires", "deleted", "secret", "crownjewel"), im.Values(expr.Arg("key"), expr.Arg("format"), expr.Arg("value"), expr.Arg("created"), expr.Arg("modified"), expr.Arg("expires"), expr.Arg("deleted"), expr.Arg("secret"), expr.Arg("crownjewel")), im.OnConflict("key").DoUpdate( im.SetExcluded("format", "value", "created", "modified", "expires", "deleted", "secret", "crownjewel"), ), ).Build(db.ctx) if err != nil { errs <- err return batch, errs } // Start transaction. tx, err := db.bob.BeginTx(db.ctx, nil) if err != nil { errs <- err return batch, errs } // Create prepared statement WITHIN TRANSACTION. preparedStmt, err := tx.PrepareContext(db.ctx, rawQuery) if err != nil { errs <- err return batch, errs } // start handler go func() { // Read all put records. writeBatch: for { select { case r := <-batch: if r != nil { // Write record. err := writeWithPreparedStatement(db.ctx, &preparedStmt, r) if err != nil { errs <- err break writeBatch } } else { // Finalize transcation. errs <- tx.Commit(db.ctx) return } case <-db.ctx.Done(): break writeBatch } } // Rollback transaction. errs <- tx.Rollback(db.ctx) }() return batch, errs } func writeWithPreparedStatement(ctx context.Context, pStmt *bob.StdPrepared, r record.Record) error { r.Lock() defer r.Unlock() // default serialization format - JSON format := uint8(dsd.JSON) // For wrapped records, check the required format if r.IsWrapped() { wrapper, ok := r.(*record.Wrapper) if !ok { return fmt.Errorf("%w: reports to be wrapped but is not of type *record.Wrapper", storage.ErrRecordMalformed) } format, ok = dsd.ValidateSerializationFormat(wrapper.Format) if !ok { return dsd.ErrIncompatibleFormat } } // Serialize. data, err := r.MarshalDataOnly(r, format) if err != nil { return err } // Get Meta. m := r.Meta() // Insert. if len(data) > 0 { format := strconv.Itoa(int(format)) _, err = pStmt.ExecContext( ctx, r.DatabaseKey(), format, data, m.Created, m.Modified, m.Expires, m.Deleted, m.IsSecret(), m.IsCrownJewel(), ) } else { _, err = pStmt.ExecContext( ctx, r.DatabaseKey(), nil, nil, m.Created, m.Modified, m.Expires, m.Deleted, m.IsSecret(), m.IsCrownJewel(), ) } return err } ================================================ FILE: base/database/storage/sqlite/prepared_test.go ================================================ package sqlite import ( "strconv" "testing" ) func BenchmarkPutMany(b *testing.B) { // Configure prepared statement usage. origSetting := UsePreparedStatements UsePreparedStatements = false defer func() { UsePreparedStatements = origSetting }() // Run benchmark. benchPutMany(b) } func BenchmarkPutManyPreparedStmt(b *testing.B) { // Configure prepared statement usage. origSetting := UsePreparedStatements UsePreparedStatements = true defer func() { UsePreparedStatements = origSetting }() // Run benchmark. benchPutMany(b) } func benchPutMany(b *testing.B) { //nolint:thelper // Start database. testDir := b.TempDir() db, err := openSQLite("test", testDir, false) if err != nil { b.Fatal(err) } defer func() { // shutdown err = db.Shutdown() if err != nil { b.Fatal(err) } }() // Start benchmarking. b.ResetTimer() // Benchmark PutMany. records, errs := db.PutMany(false) for i := range b.N { // Create test record. newTestRecord := &TestRecord{ S: "banana", I: 42, I8: 42, I16: 42, I32: 42, I64: 42, UI: 42, UI8: 42, UI16: 42, UI32: 42, UI64: 42, F32: 42.42, F64: 42.42, B: true, } newTestRecord.UpdateMeta() newTestRecord.SetKey("test:" + strconv.Itoa(i)) select { case records <- newTestRecord: case err := <-errs: b.Fatal(err) } } // Finalize. close(records) err = <-errs if err != nil { b.Fatal(err) } } ================================================ FILE: base/database/storage/sqlite/schema.go ================================================ package sqlite // Base command for sql-migrate: //go:generate -command migrate go tool github.com/rubenv/sql-migrate/sql-migrate // Run missing migrations: //go:generate migrate up --config=migrations_config.yml // Redo last migration: // x go:generate migrate redo --config=migrations_config.yml // Undo all migrations: // x go:generate migrate down --config=migrations_config.yml // Generate models with bob: //go:generate go tool github.com/stephenafamo/bob/gen/bobgen-sqlite import ( "embed" migrate "github.com/rubenv/sql-migrate" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage/sqlite/models" ) //go:embed migrations/* var dbMigrations embed.FS func getMigrations() migrate.EmbedFileSystemMigrationSource { return migrate.EmbedFileSystemMigrationSource{ FileSystem: dbMigrations, Root: "migrations", } } func getMeta(r *models.Record) *record.Meta { meta := &record.Meta{ Created: r.Created, Modified: r.Modified, Expires: r.Expires, Deleted: r.Deleted, } if r.Secret { meta.MakeSecret() } if r.Crownjewel { meta.MakeCrownJewel() } return meta } ================================================ FILE: base/database/storage/sqlite/sqlite.go ================================================ package sqlite import ( "context" "database/sql" "errors" "fmt" "path/filepath" "sync" "time" "github.com/aarondl/opt/omit" "github.com/aarondl/opt/omitnull" migrate "github.com/rubenv/sql-migrate" sqldblogger "github.com/simukti/sqldb-logger" "github.com/stephenafamo/bob" "github.com/stephenafamo/bob/dialect/sqlite" "github.com/stephenafamo/bob/dialect/sqlite/im" "github.com/stephenafamo/bob/dialect/sqlite/um" _ "modernc.org/sqlite" "github.com/safing/portmaster/base/database/accessor" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" "github.com/safing/portmaster/base/database/storage/sqlite/models" "github.com/safing/portmaster/base/log" "github.com/safing/structures/dsd" ) // Errors. var ( ErrQueryTimeout = errors.New("query timeout") ) // SQLite storage. type SQLite struct { name string db *sql.DB bob bob.DB wg sync.WaitGroup ctx context.Context cancelCtx context.CancelFunc } func init() { _ = storage.Register("sqlite", func(name, location string) (storage.Interface, error) { return NewSQLite(name, location) }) } // NewSQLite creates a sqlite database. func NewSQLite(name, location string) (*SQLite, error) { return openSQLite(name, location, false) } // openSQLite creates a sqlite database. func openSQLite(name, location string, printStmts bool) (*SQLite, error) { dbFile := filepath.Join(location, "db.sqlite") // Open database file. // Default settings: // _time_format = YYYY-MM-DDTHH:MM:SS.SSS // _txlock = deferred db, err := sql.Open("sqlite", dbFile) if err != nil { return nil, fmt.Errorf("open sqlite: %w", err) } // Enable statement printing. if printStmts { db = sqldblogger.OpenDriver(dbFile, db.Driver(), &statementLogger{}) } // Set other settings. pragmas := []string{ "PRAGMA journal_mode=WAL;", // Corruption safe write ahead log for txs. "PRAGMA synchronous=NORMAL;", // Best for WAL. "PRAGMA cache_size=-10000;", // 10MB Cache. "PRAGMA busy_timeout=3000;", // 3s (3000ms) timeout for locked tables. } for _, pragma := range pragmas { _, err := db.Exec(pragma) if err != nil { return nil, fmt.Errorf("failed to init sqlite with %s: %w", pragma, err) } } // Limit concurrent writes to avoid "SQLITE_BUSY" errors on large workloads. // SQLite typically allows only one writer at a time anyway. // In WAL mode, concurrent readers can still function, // but we keep a single writer to reduce lock contention. db.SetMaxOpenConns(1) // Only 1 open connection can be active db.SetMaxIdleConns(1) // Maintain at most 1 idle connection in the pool db.SetConnMaxLifetime(0) // Keep the single connection alive indefinitely // Run migrations on database. n, err := migrate.Exec(db, "sqlite3", getMigrations(), migrate.Up) if err != nil { return nil, fmt.Errorf("migrate sqlite: %w", err) } log.Debugf("database/sqlite: ran %d migrations on %s database", n, name) // Return as bob database. ctx, cancelCtx := context.WithCancel(context.Background()) return &SQLite{ name: name, db: db, bob: bob.NewDB(db), ctx: ctx, cancelCtx: cancelCtx, }, nil } // Get returns a database record. func (db *SQLite) Get(key string) (record.Record, error) { db.wg.Add(1) defer db.wg.Done() // Get record from database. r, err := models.FindRecord(db.ctx, db.bob, key) if err != nil { return nil, fmt.Errorf("%w: %w", storage.ErrNotFound, err) } // Return data in wrapper. return record.NewWrapperFromDatabase( db.name, key, getMeta(r), uint8(r.Format.GetOrZero()), //nolint:gosec // Values are within uint8. r.Value.GetOrZero(), ) } // GetMeta returns the metadata of a database record. func (db *SQLite) GetMeta(key string) (*record.Meta, error) { r, err := db.Get(key) if err != nil { return nil, err } return r.Meta(), nil } // Put stores a record in the database. func (db *SQLite) Put(r record.Record) (record.Record, error) { return db.putRecord(r, nil) } func (db *SQLite) putRecord(r record.Record, tx *bob.Tx) (record.Record, error) { db.wg.Add(1) defer db.wg.Done() // Lock record if in a transaction. if tx != nil { r.Lock() defer r.Unlock() } // default serialization format - JSON format := uint8(dsd.JSON) // For wrapped records, check the required format if r.IsWrapped() { wrapper, ok := r.(*record.Wrapper) if !ok { return nil, fmt.Errorf("%w: reports to be wrapped but is not of type *record.Wrapper", storage.ErrRecordMalformed) } format, ok = dsd.ValidateSerializationFormat(wrapper.Format) if !ok { return nil, dsd.ErrIncompatibleFormat } } // Serialize. data, err := r.MarshalDataOnly(r, format) if err != nil { return nil, err } // Prepare for setter. setFormat := omitnull.From(int64(format)) setData := omitnull.From(data) if len(data) == 0 { setFormat.Null() setData.Null() } // Create structure for insert. m := r.Meta() setter := models.RecordSetter{ Key: omit.From(r.DatabaseKey()), Format: setFormat, Value: setData, Created: omit.From(m.Created), Modified: omit.From(m.Modified), Expires: omit.From(m.Expires), Deleted: omit.From(m.Deleted), Secret: omit.From(m.IsSecret()), Crownjewel: omit.From(m.IsCrownJewel()), } // Simulate upsert with custom selection on conflict. dbQuery := models.Records.Insert( &setter, im.OnConflict("key").DoUpdate( im.SetExcluded("format", "value", "created", "modified", "expires", "deleted", "secret", "crownjewel"), ), ) // Execute in transaction or directly. if tx != nil { _, err = dbQuery.Exec(db.ctx, tx) } else { _, err = dbQuery.Exec(db.ctx, db.bob) } if err != nil { return nil, err } return r, nil } // PutMany stores many records in the database. func (db *SQLite) PutMany(shadowDelete bool) (chan<- record.Record, <-chan error) { db.wg.Add(1) defer db.wg.Done() // Check if we should use prepared statement optimized inserting. if UsePreparedStatements { return db.putManyWithPreparedStmts(shadowDelete) } batch := make(chan record.Record, 100) errs := make(chan error, 1) tx, err := db.bob.BeginTx(db.ctx, nil) if err != nil { errs <- err return batch, errs } // start handler go func() { // Read all put records. writeBatch: for { select { case r := <-batch: if r != nil { // Write record. _, err := db.putRecord(r, &tx) if err != nil { errs <- err break writeBatch } } else { // Finalize transcation. errs <- tx.Commit(db.ctx) return } case <-db.ctx.Done(): break writeBatch } } // Rollback transaction. errs <- tx.Rollback(db.ctx) }() return batch, errs } // Delete deletes a record from the database. func (db *SQLite) Delete(key string) error { db.wg.Add(1) defer db.wg.Done() toDelete := &models.Record{Key: key} return toDelete.Delete(db.ctx, db.bob) } // Query returns a an iterator for the supplied query. func (db *SQLite) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { db.wg.Add(1) defer db.wg.Done() _, err := q.Check() if err != nil { return nil, fmt.Errorf("invalid query: %w", err) } queryIter := iterator.New() go db.queryExecutor(queryIter, q, local, internal) return queryIter, nil } func (db *SQLite) queryExecutor(queryIter *iterator.Iterator, q *query.Query, local, internal bool) { db.wg.Add(1) defer db.wg.Done() // Build query. var recordQuery *sqlite.ViewQuery[*models.Record, models.RecordSlice] if q.DatabaseKeyPrefix() != "" { recordQuery = models.Records.View.Query( models.SelectWhere.Records.Key.Like(q.DatabaseKeyPrefix() + "%"), ) } else { recordQuery = models.Records.View.Query() } // Get cursor to go over all records in the query. cursor, err := models.RecordsQuery.Cursor(recordQuery, db.ctx, db.bob) if err != nil { queryIter.Finish(err) return } defer func() { _ = cursor.Close() }() recordsLoop: for cursor.Next() { // Get next record r, cErr := cursor.Get() if cErr != nil { err = fmt.Errorf("cursor error: %w", cErr) break recordsLoop } // Check if key matches. if !q.MatchesKey(r.Key) { continue recordsLoop } // Check Meta. m := getMeta(r) if !m.CheckValidity() || !m.CheckPermission(local, internal) { continue recordsLoop } // Check Data. if q.HasWhereCondition() { if r.Format.IsNull() || r.Value.IsNull() { continue recordsLoop } jsonData := string(r.Value.GetOrZero()) jsonAccess := accessor.NewJSONAccessor(&jsonData) if !q.MatchesAccessor(jsonAccess) { continue recordsLoop } } // Build database record. matched, _ := record.NewWrapperFromDatabase( db.name, r.Key, m, uint8(r.Format.GetOrZero()), //nolint:gosec // Values are within uint8. r.Value.GetOrZero(), ) select { case <-queryIter.Done: break recordsLoop case queryIter.Next <- matched: default: select { case <-queryIter.Done: break recordsLoop case queryIter.Next <- matched: case <-time.After(1 * time.Second): err = ErrQueryTimeout break recordsLoop } } } queryIter.Finish(err) } // Purge deletes all records that match the given query. It returns the number of successful deletes and an error. func (db *SQLite) Purge(ctx context.Context, q *query.Query, local, internal, shadowDelete bool) (int, error) { db.wg.Add(1) defer db.wg.Done() // Optimize for local and internal queries without where clause and without shadow delete. if local && internal && !shadowDelete && !q.HasWhereCondition() { // First count entries (SQLite does not support affected rows) n, err := models.Records.Query( models.SelectWhere.Records.Key.Like(q.DatabaseKeyPrefix()+"%"), ).Count(db.ctx, db.bob) if err != nil || n == 0 { return int(n), err } // Delete entries. _, err = models.Records.Delete( models.DeleteWhere.Records.Key.Like(q.DatabaseKeyPrefix()+"%"), ).Exec(db.ctx, db.bob) return int(n), err } // Optimize for local and internal queries without where clause, but with shadow delete. if local && internal && shadowDelete && !q.HasWhereCondition() { // First count entries (SQLite does not support affected rows) n, err := models.Records.Query( models.SelectWhere.Records.Key.Like(q.DatabaseKeyPrefix()+"%"), ).Count(db.ctx, db.bob) if err != nil || n == 0 { return int(n), err } // Mark purged records as deleted. now := time.Now().Unix() _, err = models.Records.Update( um.SetCol("format").ToArg(nil), um.SetCol("value").ToArg(nil), um.SetCol("deleted").ToArg(now), models.UpdateWhere.Records.Key.Like(q.DatabaseKeyPrefix()+"%"), ).Exec(db.ctx, db.bob) return int(n), err } // Otherwise, iterate over all entries and delete matching ones. // TODO: Non-local, non-internal or content matching queries are not supported at the moment. return 0, storage.ErrNotImplemented } // PurgeOlderThan deletes all records last updated before the given time. It returns the number of successful deletes and an error. func (db *SQLite) PurgeOlderThan(ctx context.Context, prefix string, purgeBefore time.Time, local, internal, shadowDelete bool) (int, error) { db.wg.Add(1) defer db.wg.Done() purgeBeforeInt := purgeBefore.Unix() // Optimize for local and internal queries without where clause and without shadow delete. if local && internal && !shadowDelete { // First count entries (SQLite does not support affected rows) n, err := models.Records.Query( models.SelectWhere.Records.Key.Like(prefix+"%"), models.SelectWhere.Records.Modified.LT(purgeBeforeInt), ).Count(db.ctx, db.bob) if err != nil || n == 0 { return int(n), err } // Delete entries. _, err = models.Records.Delete( models.DeleteWhere.Records.Key.Like(prefix+"%"), models.DeleteWhere.Records.Modified.LT(purgeBeforeInt), ).Exec(db.ctx, db.bob) return int(n), err } // Optimize for local and internal queries without where clause, but with shadow delete. if local && internal && shadowDelete { // First count entries (SQLite does not support affected rows) n, err := models.Records.Query( models.SelectWhere.Records.Key.Like(prefix+"%"), models.SelectWhere.Records.Modified.LT(purgeBeforeInt), ).Count(db.ctx, db.bob) if err != nil || n == 0 { return int(n), err } // Mark purged records as deleted. now := time.Now().Unix() _, err = models.Records.Update( um.SetCol("format").ToArg(nil), um.SetCol("value").ToArg(nil), um.SetCol("deleted").ToArg(now), models.UpdateWhere.Records.Key.Like(prefix+"%"), models.UpdateWhere.Records.Modified.LT(purgeBeforeInt), ).Exec(db.ctx, db.bob) return int(n), err } // TODO: Non-local or non-internal queries are not supported at the moment. return 0, storage.ErrNotImplemented } // ReadOnly returns whether the database is read only. func (db *SQLite) ReadOnly() bool { return false } // Injected returns whether the database is injected. func (db *SQLite) Injected() bool { return false } // MaintainRecordStates maintains records states in the database. func (db *SQLite) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time, shadowDelete bool) error { db.wg.Add(1) defer db.wg.Done() now := time.Now().Unix() purgeThreshold := purgeDeletedBefore.Unix() // Option 1: Using shadow delete. if shadowDelete { // Mark expired records as deleted. _, err := models.Records.Update( um.SetCol("format").ToArg(nil), um.SetCol("value").ToArg(nil), um.SetCol("deleted").ToArg(now), models.UpdateWhere.Records.Deleted.EQ(0), models.UpdateWhere.Records.Expires.GT(0), models.UpdateWhere.Records.Expires.LT(now), ).Exec(db.ctx, db.bob) if err != nil { return fmt.Errorf("failed to shadow delete expired records: %w", err) } // Purge deleted records before threshold. _, err = models.Records.Delete( models.DeleteWhere.Records.Deleted.GT(0), models.DeleteWhere.Records.Deleted.LT(purgeThreshold), ).Exec(db.ctx, db.bob) if err != nil { return fmt.Errorf("failed to purge deleted records (before threshold): %w", err) } return nil } // Option 2: Immediate delete. // Delete expired record. _, err := models.Records.Delete( models.DeleteWhere.Records.Expires.GT(0), models.DeleteWhere.Records.Expires.LT(now), ).Exec(db.ctx, db.bob) if err != nil { return fmt.Errorf("failed to delete expired records: %w", err) } // Delete shadow deleted records. _, err = models.Records.Delete( models.DeleteWhere.Records.Deleted.GT(0), ).Exec(db.ctx, db.bob) if err != nil { return fmt.Errorf("failed to purge deleted records: %w", err) } return nil } func (db *SQLite) Maintain(ctx context.Context) error { db.wg.Add(1) defer db.wg.Done() // Remove up to about 100KB of SQLite pages from the freelist on every run. // (Assuming 4KB page size.) _, err := db.db.ExecContext(ctx, "PRAGMA incremental_vacuum(25);") return err } func (db *SQLite) MaintainThorough(ctx context.Context) error { db.wg.Add(1) defer db.wg.Done() // Remove all pages from the freelist. _, err := db.db.ExecContext(ctx, "PRAGMA incremental_vacuum;") return err } // Shutdown shuts down the database. func (db *SQLite) Shutdown() error { db.wg.Wait() db.cancelCtx() return db.bob.Close() } type statementLogger struct{} func (sl statementLogger) Log(ctx context.Context, level sqldblogger.Level, msg string, data map[string]interface{}) { fmt.Printf("SQL: %s --- %+v\n", msg, data) } ================================================ FILE: base/database/storage/sqlite/sqlite_test.go ================================================ package sqlite import ( "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" ) var ( // Compile time interface checks. _ storage.Interface = &SQLite{} _ storage.Batcher = &SQLite{} _ storage.Purger = &SQLite{} ) type TestRecord struct { //nolint:maligned record.Base sync.Mutex S string I int I8 int8 I16 int16 I32 int32 I64 int64 UI uint UI8 uint8 UI16 uint16 UI32 uint32 UI64 uint64 F32 float32 F64 float64 B bool } func TestSQLite(t *testing.T) { t.Parallel() // start testDir := t.TempDir() db, err := openSQLite("test", testDir, true) if err != nil { t.Fatal(err) } defer func() { // shutdown err = db.Shutdown() if err != nil { t.Fatal(err) } }() a := &TestRecord{ S: "banana", I: 42, I8: 42, I16: 42, I32: 42, I64: 42, UI: 42, UI8: 42, UI16: 42, UI32: 42, UI64: 42, F32: 42.42, F64: 42.42, B: true, } a.SetMeta(&record.Meta{}) a.Meta().Update() a.SetKey("test:A") // put record _, err = db.Put(a) if err != nil { t.Fatal(err) } // get and compare r1, err := db.Get("A") if err != nil { t.Fatal(err) } a1 := &TestRecord{} err = record.Unwrap(r1, a1) if err != nil { t.Fatal(err) } assert.Equal(t, a, a1, "struct must match") // setup query test records qA := &TestRecord{} qA.SetKey("test:path/to/A") qA.UpdateMeta() qB := &TestRecord{} qB.SetKey("test:path/to/B") qB.UpdateMeta() // Set creation/modification in the past. qB.Meta().Created = time.Now().Add(-time.Hour).Unix() qB.Meta().Modified = time.Now().Add(-time.Hour).Unix() qC := &TestRecord{} qC.SetKey("test:path/to/C") qC.UpdateMeta() // Set expiry in the past. qC.Meta().Expires = time.Now().Add(-time.Hour).Unix() qZ := &TestRecord{} qZ.SetKey("test:z") qZ.UpdateMeta() put, errs := db.PutMany(false) put <- qA put <- qB put <- qC put <- qZ close(put) err = <-errs if err != nil { t.Fatal(err) } // test query q := query.New("test:path/to/").MustBeValid() it, err := db.Query(q, true, true) if err != nil { t.Fatal(err) } cnt := 0 for range it.Next { cnt++ } if it.Err() != nil { t.Fatal(it.Err()) } if cnt != 2 { // Note: One is expired. t.Fatalf("unexpected query result count: %d", cnt) } // delete err = db.Delete("A") if err != nil { t.Fatal(err) } // check if its gone _, err = db.Get("A") if err == nil { t.Fatal("should fail") } // purge older than n, err := db.PurgeOlderThan(t.Context(), "path/to/", time.Now().Add(-30*time.Minute), true, true, false) if err != nil { t.Fatal(err) } if n != 1 { t.Fatalf("unexpected purge older than delete count: %d", n) } // maintenance err = db.MaintainRecordStates(t.Context(), time.Now().Add(-time.Minute), true) if err != nil { t.Fatal(err) } // maintenance err = db.MaintainRecordStates(t.Context(), time.Now(), false) if err != nil { t.Fatal(err) } // purge n, err = db.Purge(t.Context(), query.New("test:path/to/").MustBeValid(), true, true, true) if err != nil { t.Fatal(err) } if n != 1 { t.Fatalf("unexpected purge delete count: %d", n) } // Maintenance err = db.Maintain(t.Context()) if err != nil { t.Fatalf("Maintain: %s", err) } err = db.MaintainThorough(t.Context()) if err != nil { t.Fatalf("MaintainThorough: %s", err) } // test query q = query.New("test").MustBeValid() it, err = db.Query(q, true, true) if err != nil { t.Fatal(err) } cnt = 0 for range it.Next { cnt++ } if it.Err() != nil { t.Fatal(it.Err()) } if cnt != 1 { t.Fatalf("unexpected query result count: %d", cnt) } } ================================================ FILE: base/database/storage/storages.go ================================================ package storage import ( "errors" "fmt" "sync" ) // A Factory creates a new database of it's type. type Factory func(name, location string) (Interface, error) var ( storages = make(map[string]Factory) storagesLock sync.Mutex ) // Register registers a new storage type. func Register(name string, factory Factory) error { storagesLock.Lock() defer storagesLock.Unlock() _, ok := storages[name] if ok { return errors.New("factory for this type already exists") } storages[name] = factory return nil } // CreateDatabase starts a new database with the given name and storageType at location. func CreateDatabase(name, storageType, location string) (Interface, error) { return nil, nil } // StartDatabase starts a new database with the given name and storageType at location. func StartDatabase(name, storageType, location string) (Interface, error) { storagesLock.Lock() defer storagesLock.Unlock() factory, ok := storages[storageType] if !ok { return nil, fmt.Errorf("storage type %s not registered", storageType) } return factory(name, location) } ================================================ FILE: base/database/subscription.go ================================================ package database import ( "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" ) // Subscription is a database subscription for updates. type Subscription struct { q *query.Query local bool internal bool Feed chan record.Record } // Cancel cancels the subscription. func (s *Subscription) Cancel() error { c, err := getController(s.q.DatabaseName()) if err != nil { return err } c.subscriptionLock.Lock() defer c.subscriptionLock.Unlock() for key, sub := range c.subscriptions { if sub.q == s.q { c.subscriptions = append(c.subscriptions[:key], c.subscriptions[key+1:]...) close(s.Feed) // this close is guarded by the controllers subscriptionLock. return nil } } return nil } ================================================ FILE: base/info/version.go ================================================ package info import ( "errors" "fmt" "os" "runtime" "runtime/debug" "strings" "sync" ) var ( name string license string version = "dev build" versionNumber = "0.0.0" buildSource = "unknown" buildTime = "unknown" info *Info loadInfo sync.Once ) func init() { // Replace space placeholders. buildSource = strings.ReplaceAll(buildSource, "_", " ") buildTime = strings.ReplaceAll(buildTime, "_", " ") // Convert version string from git tag to expected format. version = strings.TrimSpace(strings.ReplaceAll(strings.TrimPrefix(version, "v"), "_", " ")) versionNumber = strings.TrimSpace(strings.TrimSuffix(version, "dev build")) if versionNumber == "" { versionNumber = "0.0.0" } // Get build info. buildInfo, _ := debug.ReadBuildInfo() buildSettings := make(map[string]string) for _, setting := range buildInfo.Settings { buildSettings[setting.Key] = setting.Value } // Add "dev build" to version if repo is dirty. if buildSettings["vcs.modified"] == "true" && !strings.HasSuffix(version, "dev build") { version += " dev build" } } // Info holds the programs meta information. type Info struct { //nolint:maligned Name string Version string VersionNumber string License string Source string BuildTime string CGO bool Commit string CommitTime string Dirty bool debug.BuildInfo } // Set sets meta information via the main routine. This should be the first thing your program calls. func Set(setName string, setVersion string, setLicenseName string) { name = setName license = setLicenseName if setVersion != "" { version = setVersion versionNumber = setVersion } } // GetInfo returns all the meta information about the program. func GetInfo() *Info { loadInfo.Do(func() { buildInfo, _ := debug.ReadBuildInfo() buildSettings := make(map[string]string) for _, setting := range buildInfo.Settings { buildSettings[setting.Key] = setting.Value } info = &Info{ Name: name, Version: version, VersionNumber: versionNumber, License: license, Source: buildSource, BuildTime: buildTime, CGO: buildSettings["CGO_ENABLED"] == "1", Commit: buildSettings["vcs.revision"], CommitTime: buildSettings["vcs.time"], Dirty: buildSettings["vcs.modified"] == "true", BuildInfo: *buildInfo, } if info.Commit == "" { info.Commit = "unknown" } if info.CommitTime == "" { info.CommitTime = "unknown" } }) return info } // Version returns the annotated version. func Version() string { return version } // VersionNumber returns the version number only. func VersionNumber() string { return versionNumber } // FullVersion returns the full and detailed version string. func FullVersion() string { info := GetInfo() builder := new(strings.Builder) // Name and version. builder.WriteString(fmt.Sprintf("%s %s\n", info.Name, version)) // Build info. cgoInfo := "-cgo" if info.CGO { cgoInfo = "+cgo" } builder.WriteString(fmt.Sprintf("\nbuilt with %s (%s %s) for %s/%s\n", runtime.Version(), runtime.Compiler, cgoInfo, runtime.GOOS, runtime.GOARCH)) builder.WriteString(fmt.Sprintf(" at %s\n", info.BuildTime)) // Commit info. dirtyInfo := "clean" if info.Dirty { dirtyInfo = "dirty" } builder.WriteString(fmt.Sprintf("\ncommit %s (%s)\n", info.Commit, dirtyInfo)) builder.WriteString(fmt.Sprintf(" at %s\n", info.CommitTime)) builder.WriteString(fmt.Sprintf(" from %s\n", info.Source)) builder.WriteString(fmt.Sprintf("\nLicensed under the %s license.", license)) return builder.String() } // CondensedVersion returns the rather complete, but condensed version string. func CondensedVersion() string { info := GetInfo() cgoInfo := "-cgo" if info.CGO { cgoInfo = "+cgo" } dirtyInfo := "clean" if info.Dirty { dirtyInfo = "dirty" } return fmt.Sprintf( "%s %s (%s/%s; built with %s [%s %s] from %s [%s] at %s)", info.Name, version, runtime.GOOS, runtime.GOARCH, runtime.Version(), runtime.Compiler, cgoInfo, info.Commit, dirtyInfo, info.CommitTime, ) } // CheckVersion checks if the metadata is ok. func CheckVersion() error { switch { case strings.HasSuffix(os.Args[0], ".test"): return nil // testing on linux/darwin case strings.HasSuffix(os.Args[0], ".test.exe"): return nil // testing on windows default: // check version information if name == "" || license == "" { return errors.New("must call SetInfo() before calling CheckVersion()") } } return nil } ================================================ FILE: base/log/formatting.go ================================================ package log import ( "fmt" "time" ) var counter uint16 const ( maxCount uint16 = 999 timeFormat string = "2006-01-02 15:04:05.000" ) func (s Severity) String() string { switch s { case TraceLevel: return "TRC" case DebugLevel: return "DBG" case InfoLevel: return "INF" case WarningLevel: return "WRN" case ErrorLevel: return "ERR" case CriticalLevel: return "CRT" default: return "NON" } } func formatLine(line *logLine, duplicates uint64, useColor bool) string { var colorStart, colorEnd, colorDim, colorEndDim string if useColor { colorStart = line.level.color() colorEnd = endColor() colorDim = dimColor() colorEndDim = endDimColor() } counter++ var fLine string if line.line == 0 { fLine = fmt.Sprintf( "%s%s%s %s%s%s %s? %s %03d%s%s %s", colorDim, line.timestamp.Format(timeFormat), colorEndDim, colorStart, line.level.String(), colorEnd, colorDim, rightArrow, counter, formatDuplicates(duplicates), colorEndDim, line.msg, ) } else { fLen := len(line.file) fPartStart := fLen - 10 if fPartStart < 0 { fPartStart = 0 } fLine = fmt.Sprintf( "%s%s%s %s%s%s %s%s:%03d %s %03d%s%s %s", colorDim, line.timestamp.Format(timeFormat), colorEndDim, colorStart, line.level.String(), colorEnd, colorDim, line.file[fPartStart:], line.line, rightArrow, counter, formatDuplicates(duplicates), colorEndDim, line.msg, ) } if line.tracer != nil { // append full trace time if len(line.tracer.logs) > 0 { fLine += fmt.Sprintf(" Σ=%s", line.timestamp.Sub(line.tracer.logs[0].timestamp)) } // append all trace actions var d time.Duration for i, action := range line.tracer.logs { // set color if useColor { colorStart = action.level.color() } // set filename length fLen := len(action.file) fPartStart := fLen - 10 if fPartStart < 0 { fPartStart = 0 } // format if i == len(line.tracer.logs)-1 { // last d = line.timestamp.Sub(action.timestamp) } else { d = line.tracer.logs[i+1].timestamp.Sub(action.timestamp) } fLine += fmt.Sprintf( "\n%s%23s%s %s%s%s %s%s:%03d %s%s %s", colorDim, d, colorEndDim, colorStart, action.level.String(), colorEnd, colorDim, action.file[fPartStart:], action.line, rightArrow, colorEndDim, action.msg, ) } } if counter >= maxCount { counter = 0 } return fLine } func formatDuplicates(duplicates uint64) string { if duplicates == 0 { return "" } return fmt.Sprintf(" [%dx]", duplicates+1) } ================================================ FILE: base/log/formatting_unix.go ================================================ //go:build !windows package log const ( rightArrow = "▶" leftArrow = "◀" ) const ( colorDim = "\033[2m" colorEndDim = "\033[22m" colorRed = "\033[91m" colorYellow = "\033[93m" colorBlue = "\033[34m" colorMagenta = "\033[35m" colorCyan = "\033[36m" colorGreen = "\033[92m" // Saved for later: // colorBlack = "\033[30m" //. // colorGreen = "\033[32m" //. // colorWhite = "\033[37m" //. ) func (s Severity) color() string { switch s { case DebugLevel: return colorCyan case InfoLevel: return colorGreen case WarningLevel: return colorYellow case ErrorLevel: return colorRed case CriticalLevel: return colorMagenta case TraceLevel: return "" default: return "" } } func endColor() string { return "\033[0m" } func blueColor() string { return colorBlue } func dimColor() string { return colorDim } func endDimColor() string { return colorEndDim } ================================================ FILE: base/log/formatting_windows.go ================================================ package log import ( "github.com/safing/portmaster/base/utils/osdetail" ) const ( rightArrow = ">" leftArrow = "<" ) const ( colorDim = "\033[2m" colorEndDim = "\033[22m" colorRed = "\033[91m" colorYellow = "\033[93m" colorBlue = "\033[34m" colorMagenta = "\033[35m" colorCyan = "\033[36m" colorGreen = "\033[92m" // colorBlack = "\033[30m" // colorGreen = "\033[32m" // colorWhite = "\033[37m" ) var ( colorsSupported bool ) func init() { colorsSupported = osdetail.EnableColorSupport() } func (s Severity) color() string { if colorsSupported { switch s { case DebugLevel: return colorCyan case InfoLevel: return colorGreen case WarningLevel: return colorYellow case ErrorLevel: return colorRed case CriticalLevel: return colorMagenta default: return "" } } return "" } func endColor() string { if colorsSupported { return "\033[0m" } return "" } func blueColor() string { if colorsSupported { return colorBlue } return "" } func dimColor() string { if colorsSupported { return colorDim } return "" } func endDimColor() string { if colorsSupported { return colorEndDim } return "" } ================================================ FILE: base/log/input.go ================================================ package log import ( "fmt" "runtime" "sync/atomic" "time" ) var ( warnLogLines = new(uint64) errLogLines = new(uint64) critLogLines = new(uint64) ) func log(level Severity, msg string, tracer *ContextTracer) { if !started.IsSet() { // a bit resource intense, but keeps logs before logging started. // TODO: create option to disable logging go func() { <-startedSignal log(level, msg, tracer) }() return } // Check log level. if uint32(level) < atomic.LoadUint32(logLevel) { return } // get time now := time.Now() // get file and line _, file, line, ok := runtime.Caller(2) if !ok { file = "" line = 0 } else { if len(file) > 3 { file = file[:len(file)-3] } else { file = "" } } // create log object log := &logLine{ msg: msg, tracer: tracer, level: level, timestamp: now, file: file, line: line, } // send log to processing select { case logBuffer <- log: default: forceEmptyingLoop: // force empty buffer until we can send to it for { select { case forceEmptyingOfBuffer <- struct{}{}: case logBuffer <- log: break forceEmptyingLoop } } } // wake up writer if necessary if logsWaitingFlag.SetToIf(false, true) { select { case logsWaiting <- struct{}{}: default: } } } func fastcheck(level Severity) bool { return uint32(level) >= atomic.LoadUint32(logLevel) } // Trace is used to log tiny steps. Log traces to context if you can! func Trace(msg string) { if fastcheck(TraceLevel) { log(TraceLevel, msg, nil) } } // Tracef is used to log tiny steps. Log traces to context if you can! func Tracef(format string, things ...interface{}) { if fastcheck(TraceLevel) { log(TraceLevel, fmt.Sprintf(format, things...), nil) } } // Debug is used to log minor errors or unexpected events. These occurrences are usually not worth mentioning in itself, but they might hint at a bigger problem. func Debug(msg string) { if fastcheck(DebugLevel) { log(DebugLevel, msg, nil) } } // Debugf is used to log minor errors or unexpected events. These occurrences are usually not worth mentioning in itself, but they might hint at a bigger problem. func Debugf(format string, things ...interface{}) { if fastcheck(DebugLevel) { log(DebugLevel, fmt.Sprintf(format, things...), nil) } } // Info is used to log mildly significant events. Should be used to inform about somewhat bigger or user affecting events that happen. func Info(msg string) { if fastcheck(InfoLevel) { log(InfoLevel, msg, nil) } } // Infof is used to log mildly significant events. Should be used to inform about somewhat bigger or user affecting events that happen. func Infof(format string, things ...interface{}) { if fastcheck(InfoLevel) { log(InfoLevel, fmt.Sprintf(format, things...), nil) } } // Warning is used to log (potentially) bad events, but nothing broke (even a little) and there is no need to panic yet. func Warning(msg string) { atomic.AddUint64(warnLogLines, 1) if fastcheck(WarningLevel) { log(WarningLevel, msg, nil) } } // Warningf is used to log (potentially) bad events, but nothing broke (even a little) and there is no need to panic yet. func Warningf(format string, things ...interface{}) { atomic.AddUint64(warnLogLines, 1) if fastcheck(WarningLevel) { log(WarningLevel, fmt.Sprintf(format, things...), nil) } } // Error is used to log errors that break or impair functionality. The task/process may have to be aborted and tried again later. The system is still operational. Maybe User/Admin should be informed. func Error(msg string) { atomic.AddUint64(errLogLines, 1) if fastcheck(ErrorLevel) { log(ErrorLevel, msg, nil) } } // Errorf is used to log errors that break or impair functionality. The task/process may have to be aborted and tried again later. The system is still operational. func Errorf(format string, things ...interface{}) { atomic.AddUint64(errLogLines, 1) if fastcheck(ErrorLevel) { log(ErrorLevel, fmt.Sprintf(format, things...), nil) } } // Critical is used to log events that completely break the system. Operation cannot continue. User/Admin must be informed. func Critical(msg string) { atomic.AddUint64(critLogLines, 1) if fastcheck(CriticalLevel) { log(CriticalLevel, msg, nil) } } // Criticalf is used to log events that completely break the system. Operation cannot continue. User/Admin must be informed. func Criticalf(format string, things ...interface{}) { atomic.AddUint64(critLogLines, 1) if fastcheck(CriticalLevel) { log(CriticalLevel, fmt.Sprintf(format, things...), nil) } } // TotalWarningLogLines returns the total amount of warning log lines since // start of the program. func TotalWarningLogLines() uint64 { return atomic.LoadUint64(warnLogLines) } // TotalErrorLogLines returns the total amount of error log lines since start // of the program. func TotalErrorLogLines() uint64 { return atomic.LoadUint64(errLogLines) } // TotalCriticalLogLines returns the total amount of critical log lines since // start of the program. func TotalCriticalLogLines() uint64 { return atomic.LoadUint64(critLogLines) } ================================================ FILE: base/log/logging.go ================================================ package log import ( "fmt" "log/slog" "os" "strings" "sync" "sync/atomic" "time" "github.com/tevino/abool" ) // concept /* - Logging function: - check if file-based levelling enabled - if yes, check if level is active on this file - check if level is active - send data to backend via big buffered channel - Backend: - wait until there is time for writing logs - write logs - configurable if logged to folder (buffer + rollingFileAppender) and/or console - console: log everything above INFO to stderr - Channel overbuffering protection: - if buffer is full, trigger write - Anti-Importing-Loop: - everything imports logging - logging is configured by main module and is supplied access to configuration and taskmanager */ // Severity describes a log level. type Severity uint32 func (s Severity) toSLogLevel() slog.Level { // Convert to slog level. switch s { case TraceLevel: return slog.LevelDebug case DebugLevel: return slog.LevelDebug case InfoLevel: return slog.LevelInfo case WarningLevel: return slog.LevelWarn case ErrorLevel: return slog.LevelError case CriticalLevel: return slog.LevelError } // Failed to convert, return default log level return slog.LevelWarn } // Message describes a log level message and is implemented // by logLine. type Message interface { Text() string Severity() Severity Time() time.Time File() string LineNumber() int } type logLine struct { msg string tracer *ContextTracer level Severity timestamp time.Time file string line int } func (ll *logLine) Text() string { return ll.msg } func (ll *logLine) Severity() Severity { return ll.level } func (ll *logLine) Time() time.Time { return ll.timestamp } func (ll *logLine) File() string { return ll.file } func (ll *logLine) LineNumber() int { return ll.line } func (ll *logLine) Equal(ol *logLine) bool { switch { case ll.msg != ol.msg: return false case ll.tracer != nil || ol.tracer != nil: return false case ll.file != ol.file: return false case ll.line != ol.line: return false case ll.level != ol.level: return false } return true } // Log Levels. const ( TraceLevel Severity = 1 DebugLevel Severity = 2 InfoLevel Severity = 3 WarningLevel Severity = 4 ErrorLevel Severity = 5 CriticalLevel Severity = 6 ) var ( logBuffer chan *logLine forceEmptyingOfBuffer = make(chan struct{}) logLevelInt = uint32(InfoLevel) logLevel = &logLevelInt logsWaiting = make(chan struct{}, 1) logsWaitingFlag = abool.NewBool(false) shutdownFlag = abool.NewBool(false) shutdownSignal = make(chan struct{}) shutdownWaitGroup sync.WaitGroup initializing = abool.NewBool(false) started = abool.NewBool(false) startedSignal = make(chan struct{}) ) // GetLogLevel returns the current log level. func GetLogLevel() Severity { return Severity(atomic.LoadUint32(logLevel)) } // SetLogLevel sets a new log level. Only effective after Start(). func SetLogLevel(level Severity) { atomic.StoreUint32(logLevel, uint32(level)) // Setup slog here for the transition period. setupSLog(level) } // Name returns the name of the log level. func (s Severity) Name() string { switch s { case TraceLevel: return "trace" case DebugLevel: return "debug" case InfoLevel: return "info" case WarningLevel: return "warning" case ErrorLevel: return "error" case CriticalLevel: return "critical" default: return "none" } } // ParseLevel returns the level severity of a log level name. func ParseLevel(level string) Severity { switch strings.ToLower(level) { case "trace": return 1 case "debug": return 2 case "info": return 3 case "warning": return 4 case "error": return 5 case "critical": return 6 } return 0 } // Start starts the logging system. Must be called in order to see logs. func Start(level string, logToStdout bool, logDir string) error { if !initializing.SetToIf(false, true) { return nil } // Parse log level argument. initialLogLevel := InfoLevel if level != "" { initialLogLevel = ParseLevel(level) if initialLogLevel == 0 { fmt.Fprintf(os.Stderr, "log warning: invalid log level %q, falling back to level info\n", level) initialLogLevel = InfoLevel } } // Setup writer. if logToStdout { GlobalWriter = NewStdoutWriter() } else { // Create file log writer. var err error GlobalWriter, err = NewFileWriter(logDir) if err != nil { return fmt.Errorf("failed to initialize log file: %w", err) } } // Init logging systems. SetLogLevel(initialLogLevel) logBuffer = make(chan *logLine, 1024) if !schedulingEnabled { close(writeTrigger) } startWriter() started.Set() close(startedSignal) // Delete all logs older than one month. if !logToStdout { err := CleanOldLogs(logDir, 30*24*time.Hour) if err != nil { Errorf("log: failed to clean old log files: %s", err) } } return nil } // Shutdown writes remaining log lines and then stops the log system. func Shutdown() { if shutdownFlag.SetToIf(false, true) { close(shutdownSignal) } shutdownWaitGroup.Wait() GlobalWriter.Close() } ================================================ FILE: base/log/logging_test.go ================================================ package log import ( "fmt" "testing" "time" ) func init() { err := Start("info", true, "") if err != nil { panic(fmt.Sprintf("start failed: %s", err)) } } func TestLogging(t *testing.T) { t.Parallel() // skip if testing.Short() { t.Skip() } // set levels (static random) SetLogLevel(WarningLevel) SetLogLevel(InfoLevel) SetLogLevel(ErrorLevel) SetLogLevel(DebugLevel) SetLogLevel(CriticalLevel) SetLogLevel(TraceLevel) // log Trace("Trace") Debug("Debug") Info("Info") Warning("Warning") Error("Error") Critical("Critical") // logf Tracef("Trace %s", "f") Debugf("Debug %s", "f") Infof("Info %s", "f") Warningf("Warning %s", "f") Errorf("Error %s", "f") Criticalf("Critical %s", "f") // play with levels SetLogLevel(CriticalLevel) Warning("Warning") SetLogLevel(TraceLevel) // log invalid level log(0xFF, "msg", nil) // wait logs to be written time.Sleep(1 * time.Millisecond) // do not really shut down, we may need logging for other tests // ShutdownLogging() } ================================================ FILE: base/log/output.go ================================================ package log import ( "fmt" "runtime/debug" "sync" "time" "github.com/safing/portmaster/base/info" ) // Adapter is used to write logs. type Adapter interface { // Write is called for each log message. WriteMessage(msg Message, duplicates uint64) } var ( schedulingEnabled = false writeTrigger = make(chan struct{}) ) // EnableScheduling enables external scheduling of the logger. This will require to manually trigger writes via TriggerWrite whenever logs should be written. Please note that full buffers will also trigger writing. Must be called before Start() to have an effect. func EnableScheduling() { if !initializing.IsSet() { schedulingEnabled = true } } // TriggerWriter triggers log output writing. func TriggerWriter() { if started.IsSet() && schedulingEnabled { select { case writeTrigger <- struct{}{}: default: } } } // TriggerWriterChannel returns the channel to trigger log writing. Returned channel will close if EnableScheduling() is not called correctly. func TriggerWriterChannel() chan struct{} { return writeTrigger } func startWriter() { if GlobalWriter.isStdout { fmt.Fprintf(GlobalWriter, "%s%s%s %sBOF %s%s\n", dimColor(), time.Now().Format(timeFormat), endDimColor(), blueColor(), rightArrow, endColor(), ) } else { fmt.Fprintf(GlobalWriter, "%s BOF %s\n", time.Now().Format(timeFormat), rightArrow, ) } writeVersion() shutdownWaitGroup.Add(1) go writerManager() } func writeVersion() { if GlobalWriter.isStdout { fmt.Fprintf(GlobalWriter, "%s%s%s running %s%s%s\n", dimColor(), time.Now().Format(timeFormat), endDimColor(), blueColor(), info.CondensedVersion(), endColor()) } else { fmt.Fprintf(GlobalWriter, "%s running %s\n", time.Now().Format(timeFormat), info.CondensedVersion()) } } func writerManager() { defer shutdownWaitGroup.Done() for { err := writer() if err != nil { Errorf("log: writer failed: %s", err) } else { return } } } func writer() error { var err error defer func() { // recover from panic panicVal := recover() if panicVal != nil { _, err = fmt.Fprintf(GlobalWriter, "%s", panicVal) // write stack to stderr fmt.Fprintf( GlobalWriter, `===== Error Report ===== Message: %s StackTrace: %s ===== End of Report ===== `, err, string(debug.Stack()), ) } }() var currentLine *logLine var duplicates uint64 for { // reset currentLine = nil duplicates = 0 // wait until logs need to be processed select { case <-logsWaiting: // normal process logsWaitingFlag.UnSet() case <-forceEmptyingOfBuffer: // log buffer is full! case <-shutdownSignal: // shutting down finalizeWriting() return err } // wait for timeslot to log select { case <-writeTrigger: // normal process case <-forceEmptyingOfBuffer: // log buffer is full! case <-shutdownSignal: // shutting down finalizeWriting() return err } // write all the logs! writeLoop: for { select { case nextLine := <-logBuffer: // first line we process, just assign to currentLine if currentLine == nil { currentLine = nextLine continue writeLoop } // we now have currentLine and nextLine // if currentLine and nextLine are equal, do not print, just increase counter and continue if nextLine.Equal(currentLine) { duplicates++ continue writeLoop } // if currentLine and line are _not_ equal, output currentLine GlobalWriter.WriteMessage(currentLine, duplicates) // add to unexpected logs addUnexpectedLogs(currentLine) // reset duplicate counter duplicates = 0 // set new currentLine currentLine = nextLine default: break writeLoop } } // write final line if currentLine != nil { GlobalWriter.WriteMessage(currentLine, duplicates) // add to unexpected logs addUnexpectedLogs(currentLine) } // back down a little select { case <-time.After(10 * time.Millisecond): case <-shutdownSignal: finalizeWriting() return err } } } func finalizeWriting() { for { select { case line := <-logBuffer: GlobalWriter.WriteMessage(line, 0) case <-time.After(10 * time.Millisecond): if GlobalWriter.isStdout { fmt.Fprintf(GlobalWriter, "%s%s%s %sEOF %s%s\n", dimColor(), time.Now().Format(timeFormat), endDimColor(), blueColor(), leftArrow, endColor(), ) } else { fmt.Fprintf(GlobalWriter, "%s EOF %s\n", time.Now().Format(timeFormat), leftArrow, ) } return } } } // Last Unexpected Logs var ( lastUnexpectedLogs [10]string lastUnexpectedLogsIndex int lastUnexpectedLogsLock sync.Mutex ) func addUnexpectedLogs(line *logLine) { // Add main line. if line.level >= WarningLevel { addUnexpectedLogLine(line) return } // Check for unexpected lines in the tracer. if line.tracer != nil { for _, traceLine := range line.tracer.logs { if traceLine.level >= WarningLevel { // Add full trace. addUnexpectedLogLine(line) return } } } } func addUnexpectedLogLine(line *logLine) { lastUnexpectedLogsLock.Lock() defer lastUnexpectedLogsLock.Unlock() // Format line and add to logs. lastUnexpectedLogs[lastUnexpectedLogsIndex] = formatLine(line, 0, false) // Increase index and wrap back to start. lastUnexpectedLogsIndex = (lastUnexpectedLogsIndex + 1) % len(lastUnexpectedLogs) } // GetLastUnexpectedLogs returns the last 10 log lines of level Warning an up. func GetLastUnexpectedLogs() []string { lastUnexpectedLogsLock.Lock() defer lastUnexpectedLogsLock.Unlock() // Make a copy and return. logsLen := len(lastUnexpectedLogs) start := lastUnexpectedLogsIndex logsCopy := make([]string, 0, logsLen) // Loop from mid-to-mid. for i := start; i < start+logsLen; i++ { if lastUnexpectedLogs[i%logsLen] != "" { logsCopy = append(logsCopy, lastUnexpectedLogs[i%logsLen]) } } return logsCopy } ================================================ FILE: base/log/slog.go ================================================ package log import ( "io" "log/slog" "os" "runtime" "github.com/lmittmann/tint" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" ) func setupSLog(level Severity) { // TODO: Changes in the log level are not yet reflected onto the slog handlers in the modules. // Set highest possible level, so it can be changed in runtime. handlerLogLevel := level.toSLogLevel() // Create handler depending on OS. var logHandler slog.Handler switch runtime.GOOS { case "windows": logHandler = tint.NewHandler( windowsColoring(GlobalWriter), // Enable coloring on Windows. &tint.Options{ AddSource: true, Level: handlerLogLevel, TimeFormat: timeFormat, NoColor: !( /* Color: */ GlobalWriter.IsStdout() && isatty.IsTerminal(GlobalWriter.file.Fd())), }, ) case "linux": logHandler = tint.NewHandler(GlobalWriter, &tint.Options{ AddSource: true, Level: handlerLogLevel, TimeFormat: timeFormat, NoColor: !( /* Color: */ GlobalWriter.IsStdout() && isatty.IsTerminal(GlobalWriter.file.Fd())), }) default: logHandler = tint.NewHandler(os.Stdout, &tint.Options{ AddSource: true, Level: handlerLogLevel, TimeFormat: timeFormat, NoColor: true, }) } // Set as default logger. slog.SetDefault(slog.New(logHandler)) } func windowsColoring(lw *LogWriter) io.Writer { if lw.IsStdout() { return colorable.NewColorable(lw.file) } return lw } ================================================ FILE: base/log/trace.go ================================================ package log import ( "context" "fmt" "runtime" "sync" "sync/atomic" "time" ) // ContextTracerKey is the key used for the context key/value storage. type ContextTracerKey struct{} // ContextTracer is attached to a context in order bind logs to a context. type ContextTracer struct { sync.Mutex logs []*logLine } var key = ContextTracerKey{} // AddTracer adds a ContextTracer to the returned Context. Will return a nil ContextTracer if logging level is not set to trace. Will return a nil ContextTracer if one already exists. Will return a nil ContextTracer in case of an error. Will return a nil context if nil. func AddTracer(ctx context.Context) (context.Context, *ContextTracer) { if ctx != nil && fastcheck(TraceLevel) { // Check log level. if atomic.LoadUint32(logLevel) > uint32(TraceLevel) { return ctx, nil } // check for existing tracer _, ok := ctx.Value(key).(*ContextTracer) if !ok { // add and return new tracer tracer := &ContextTracer{} return context.WithValue(ctx, key, tracer), tracer } } return ctx, nil } // Tracer returns the ContextTracer previously added to the given Context. func Tracer(ctx context.Context) *ContextTracer { if ctx != nil { tracer, ok := ctx.Value(key).(*ContextTracer) if ok { return tracer } } return nil } // Submit collected logs on the context for further processing/outputting. Does nothing if called on a nil ContextTracer. func (tracer *ContextTracer) Submit() { if tracer == nil { return } if !started.IsSet() { // a bit resource intense, but keeps logs before logging started. // TODO: create option to disable logging go func() { <-startedSignal tracer.Submit() }() return } if len(tracer.logs) == 0 { return } // extract last line as main line mainLine := tracer.logs[len(tracer.logs)-1] tracer.logs = tracer.logs[:len(tracer.logs)-1] // create log object log := &logLine{ msg: mainLine.msg, tracer: tracer, level: mainLine.level, timestamp: mainLine.timestamp, file: mainLine.file, line: mainLine.line, } // send log to processing select { case logBuffer <- log: default: forceEmptyingLoop: // force empty buffer until we can send to it for { select { case forceEmptyingOfBuffer <- struct{}{}: case logBuffer <- log: break forceEmptyingLoop } } } // wake up writer if necessary if logsWaitingFlag.SetToIf(false, true) { logsWaiting <- struct{}{} } } func (tracer *ContextTracer) log(level Severity, msg string) { // get file and line _, file, line, ok := runtime.Caller(2) if !ok { file = "" line = 0 } else { if len(file) > 3 { file = file[:len(file)-3] } else { file = "" } } tracer.Lock() defer tracer.Unlock() tracer.logs = append(tracer.logs, &logLine{ timestamp: time.Now(), level: level, msg: msg, file: file, line: line, }) } // Trace is used to log tiny steps. Log traces to context if you can! func (tracer *ContextTracer) Trace(msg string) { switch { case tracer != nil: tracer.log(TraceLevel, msg) case fastcheck(TraceLevel): log(TraceLevel, msg, nil) } } // Tracef is used to log tiny steps. Log traces to context if you can! func (tracer *ContextTracer) Tracef(format string, things ...interface{}) { switch { case tracer != nil: tracer.log(TraceLevel, fmt.Sprintf(format, things...)) case fastcheck(TraceLevel): log(TraceLevel, fmt.Sprintf(format, things...), nil) } } // Debug is used to log minor errors or unexpected events. These occurrences are usually not worth mentioning in itself, but they might hint at a bigger problem. func (tracer *ContextTracer) Debug(msg string) { switch { case tracer != nil: tracer.log(DebugLevel, msg) case fastcheck(DebugLevel): log(DebugLevel, msg, nil) } } // Debugf is used to log minor errors or unexpected events. These occurrences are usually not worth mentioning in itself, but they might hint at a bigger problem. func (tracer *ContextTracer) Debugf(format string, things ...interface{}) { switch { case tracer != nil: tracer.log(DebugLevel, fmt.Sprintf(format, things...)) case fastcheck(DebugLevel): log(DebugLevel, fmt.Sprintf(format, things...), nil) } } // Info is used to log mildly significant events. Should be used to inform about somewhat bigger or user affecting events that happen. func (tracer *ContextTracer) Info(msg string) { switch { case tracer != nil: tracer.log(InfoLevel, msg) case fastcheck(InfoLevel): log(InfoLevel, msg, nil) } } // Infof is used to log mildly significant events. Should be used to inform about somewhat bigger or user affecting events that happen. func (tracer *ContextTracer) Infof(format string, things ...interface{}) { switch { case tracer != nil: tracer.log(InfoLevel, fmt.Sprintf(format, things...)) case fastcheck(InfoLevel): log(InfoLevel, fmt.Sprintf(format, things...), nil) } } // Warning is used to log (potentially) bad events, but nothing broke (even a little) and there is no need to panic yet. func (tracer *ContextTracer) Warning(msg string) { switch { case tracer != nil: tracer.log(WarningLevel, msg) case fastcheck(WarningLevel): log(WarningLevel, msg, nil) } } // Warningf is used to log (potentially) bad events, but nothing broke (even a little) and there is no need to panic yet. func (tracer *ContextTracer) Warningf(format string, things ...interface{}) { switch { case tracer != nil: tracer.log(WarningLevel, fmt.Sprintf(format, things...)) case fastcheck(WarningLevel): log(WarningLevel, fmt.Sprintf(format, things...), nil) } } // Error is used to log errors that break or impair functionality. The task/process may have to be aborted and tried again later. The system is still operational. Maybe User/Admin should be informed. func (tracer *ContextTracer) Error(msg string) { switch { case tracer != nil: tracer.log(ErrorLevel, msg) case fastcheck(ErrorLevel): log(ErrorLevel, msg, nil) } } // Errorf is used to log errors that break or impair functionality. The task/process may have to be aborted and tried again later. The system is still operational. func (tracer *ContextTracer) Errorf(format string, things ...interface{}) { switch { case tracer != nil: tracer.log(ErrorLevel, fmt.Sprintf(format, things...)) case fastcheck(ErrorLevel): log(ErrorLevel, fmt.Sprintf(format, things...), nil) } } // Critical is used to log events that completely break the system. Operation connot continue. User/Admin must be informed. func (tracer *ContextTracer) Critical(msg string) { switch { case tracer != nil: tracer.log(CriticalLevel, msg) case fastcheck(CriticalLevel): log(CriticalLevel, msg, nil) } } // Criticalf is used to log events that completely break the system. Operation connot continue. User/Admin must be informed. func (tracer *ContextTracer) Criticalf(format string, things ...interface{}) { switch { case tracer != nil: tracer.log(CriticalLevel, fmt.Sprintf(format, things...)) case fastcheck(CriticalLevel): log(CriticalLevel, fmt.Sprintf(format, things...), nil) } } ================================================ FILE: base/log/trace_test.go ================================================ package log import ( "context" "testing" "time" ) func TestContextTracer(t *testing.T) { t.Parallel() // skip if testing.Short() { t.Skip() } ctx, tracer := AddTracer(context.Background()) _ = Tracer(ctx) tracer.Trace("api: request received, checking security") time.Sleep(1 * time.Millisecond) tracer.Trace("login: logging in user") time.Sleep(1 * time.Millisecond) tracer.Trace("database: fetching requested resources") time.Sleep(10 * time.Millisecond) tracer.Warning("database: partial failure") time.Sleep(10 * time.Microsecond) tracer.Trace("renderer: rendering output") time.Sleep(1 * time.Millisecond) tracer.Trace("api: returning request") tracer.Trace("api: completed request") tracer.Submit() time.Sleep(100 * time.Millisecond) } ================================================ FILE: base/log/writer.go ================================================ package log import ( "fmt" "os" "path/filepath" "sync" "time" ) // GlobalWriter is the global log writer. var GlobalWriter *LogWriter = nil type LogWriter struct { writeLock sync.Mutex isStdout bool file *os.File } // NewStdoutWriter creates a new log writer thet will write to the stdout. func NewStdoutWriter() *LogWriter { return &LogWriter{ file: os.Stdout, isStdout: true, } } // NewFileWriter creates a new log writer that will write to a file. The file path will be /2006-01-02_15-04-05.log (with current date and time) func NewFileWriter(dir string) (*LogWriter, error) { // Make sure log dir exists, if not, create with strict permission, as logs can contain sensitive data. _ = os.MkdirAll(dir, 0o700) // Open new log file. logFile := time.Now().UTC().Format("2006-01-02_15-04-05") + ".log" file, err := os.Create(filepath.Join(dir, logFile)) if err != nil { return nil, err } return &LogWriter{ file: file, isStdout: false, }, nil } // Write writes the buffer to the writer. func (l *LogWriter) Write(buf []byte) (int, error) { if l == nil { return 0, fmt.Errorf("log writer not initialized") } // No need to lock in stdout context. if !l.isStdout { l.writeLock.Lock() defer l.writeLock.Unlock() } return l.file.Write(buf) } // WriteMessage writes the message to the writer. func (l *LogWriter) WriteMessage(msg Message, duplicates uint64) { if l == nil { return } // No need to lock in stdout context. if !l.isStdout { l.writeLock.Lock() defer l.writeLock.Unlock() } fmt.Fprintln(l.file, formatLine(msg.(*logLine), duplicates, l.isStdout)) } // IsStdout returns true if writer was initialized with stdout. func (l *LogWriter) IsStdout() bool { return l != nil && l.isStdout } // Close closes the writer. func (l *LogWriter) Close() { if l != nil && !l.isStdout { _ = l.file.Close() } } // CleanOldLogs deletes all log files in given directory that are older than the given threshold. func CleanOldLogs(dir string, threshold time.Duration) error { // Get current log file name. var currentLogFile string if GlobalWriter != nil && GlobalWriter.file != nil { currentLogFile = GlobalWriter.file.Name() } // Read dir entries. files, err := os.ReadDir(dir) if err != nil { return fmt.Errorf("failed to read dir: %w", err) } // Remove files older than threshold deleteOlderThan := time.Now().Add(-threshold) for _, f := range files { // Skip directories and the current log file. if f.IsDir() || f.Name() == currentLogFile { continue } // Delete log files. if fileInfo, err := f.Info(); err == nil { if fileInfo.ModTime().Before(deleteOlderThan) { _ = os.Remove(filepath.Join(dir, f.Name())) } } } return nil } ================================================ FILE: base/metrics/api.go ================================================ package metrics import ( "bytes" "context" "fmt" "io" "net/http" "time" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" ) func registerAPI() error { api.RegisterHandler("/metrics", &metricsAPI{}) if err := api.RegisterEndpoint(api.Endpoint{ Name: "Export Registered Metrics", Description: "List all registered metrics with their metadata.", Path: "metrics/list", Read: api.Dynamic, StructFunc: func(ar *api.Request) (any, error) { return ExportMetrics(ar.AuthToken.Read), nil }, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Export Metric Values", Description: "List all exportable metric values.", Path: "metrics/values", Read: api.Dynamic, Parameters: []api.Parameter{{ Method: http.MethodGet, Field: "internal-only", Description: "Specify to only return metrics with an alternative internal ID.", }}, StructFunc: func(ar *api.Request) (any, error) { return ExportValues( ar.AuthToken.Read, ar.Request.URL.Query().Has("internal-only"), ), nil }, }); err != nil { return err } return nil } type metricsAPI struct{} func (m *metricsAPI) ReadPermission(*http.Request) api.Permission { return api.Dynamic } func (m *metricsAPI) WritePermission(*http.Request) api.Permission { return api.NotSupported } func (m *metricsAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Get API Request for permission and query. ar := api.GetAPIRequest(r) if ar == nil { http.Error(w, "Missing API Request.", http.StatusInternalServerError) return } // Get expertise level from query. expertiseLevel := config.ExpertiseLevelDeveloper switch ar.Request.URL.Query().Get("level") { case config.ExpertiseLevelNameUser: expertiseLevel = config.ExpertiseLevelUser case config.ExpertiseLevelNameExpert: expertiseLevel = config.ExpertiseLevelExpert case config.ExpertiseLevelNameDeveloper: expertiseLevel = config.ExpertiseLevelDeveloper } w.Header().Set("Content-Type", "text/plain; version=0.0.4; charset=utf-8") w.WriteHeader(http.StatusOK) WriteMetrics(w, ar.AuthToken.Read, expertiseLevel) } // WriteMetrics writes all metrics that match the given permission and // expertiseLevel to the given writer. func WriteMetrics(w io.Writer, permission api.Permission, expertiseLevel config.ExpertiseLevel) { registryLock.RLock() defer registryLock.RUnlock() // Write all matching metrics. for _, metric := range registry { if permission >= metric.Opts().Permission && expertiseLevel >= metric.Opts().ExpertiseLevel { metric.WritePrometheus(w) } } } func writeMetricsTo(ctx context.Context, url string) error { // First, collect metrics into buffer. buf := &bytes.Buffer{} WriteMetrics(buf, api.PermitSelf, config.ExpertiseLevelDeveloper) // Check if there is something to send. if buf.Len() == 0 { log.Debugf("metrics: not pushing metrics, nothing to send") return nil } // Create request req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, buf) if err != nil { return fmt.Errorf("failed to create request: %w", err) } // Send. resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer func() { _ = resp.Body.Close() }() // Check return status. if resp.StatusCode >= 200 && resp.StatusCode <= 299 { return nil } // Get and return error. body, _ := io.ReadAll(resp.Body) return fmt.Errorf( "got %s while writing metrics to %s: %s", resp.Status, url, body, ) } func metricsWriter(ctx *mgr.WorkerCtx) error { pushURL := pushOption() module.metricTicker = mgr.NewSleepyTicker(1*time.Minute, 0) defer module.metricTicker.Stop() for { select { case <-ctx.Done(): return nil case <-module.metricTicker.Wait(): err := writeMetricsTo(ctx.Ctx(), pushURL) if err != nil { return err } } } } ================================================ FILE: base/metrics/config.go ================================================ package metrics import ( "flag" "os" "strings" "github.com/safing/portmaster/base/config" ) // Configuration Keys. var ( CfgOptionInstanceKey = "core/metrics/instance" instanceOption config.StringOption cfgOptionInstanceOrder = 0 CfgOptionCommentKey = "core/metrics/comment" commentOption config.StringOption cfgOptionCommentOrder = 0 CfgOptionPushKey = "core/metrics/push" pushOption config.StringOption cfgOptionPushOrder = 0 instanceFlag string defaultInstance string commentFlag string pushFlag string ) func init() { hostname, err := os.Hostname() if err == nil { hostname = strings.ReplaceAll(hostname, "-", "") if prometheusFormat.MatchString(hostname) { defaultInstance = hostname } } flag.StringVar(&instanceFlag, "metrics-instance", defaultInstance, "set the default metrics instance label for all metrics") flag.StringVar(&commentFlag, "metrics-comment", "", "set the default metrics comment label") flag.StringVar(&pushFlag, "push-metrics", "", "set default URL to push prometheus metrics to") } func prepConfig() error { err := config.Register(&config.Option{ Name: "Metrics Instance Name", Key: CfgOptionInstanceKey, Description: "Define the prometheus instance label for all exported metrics. Please note that changing the metrics instance name will reset persisted metrics.", Sensitive: true, OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: instanceFlag, RequiresRestart: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionInstanceOrder, config.CategoryAnnotation: "Metrics", }, ValidationRegex: "^(" + prometheusBaseFormt + ")?$", }) if err != nil { return err } instanceOption = config.Concurrent.GetAsString(CfgOptionInstanceKey, instanceFlag) err = config.Register(&config.Option{ Name: "Metrics Comment Label", Key: CfgOptionCommentKey, Description: "Define a metrics comment label, which is added to the info metric.", Sensitive: true, OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: commentFlag, RequiresRestart: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionCommentOrder, config.CategoryAnnotation: "Metrics", }, }) if err != nil { return err } commentOption = config.Concurrent.GetAsString(CfgOptionCommentKey, commentFlag) err = config.Register(&config.Option{ Name: "Push Prometheus Metrics", Key: CfgOptionPushKey, Description: "Push metrics to this URL in the prometheus format.", Sensitive: true, OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: pushFlag, RequiresRestart: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionPushOrder, config.CategoryAnnotation: "Metrics", }, }) if err != nil { return err } pushOption = config.Concurrent.GetAsString(CfgOptionPushKey, pushFlag) return nil } ================================================ FILE: base/metrics/metric.go ================================================ package metrics import ( "fmt" "io" "regexp" "sort" "strings" vm "github.com/VictoriaMetrics/metrics" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" ) // PrometheusFormatRequirement is required format defined by prometheus for // metric and label names. const ( prometheusBaseFormt = "[a-zA-Z_][a-zA-Z0-9_]*" PrometheusFormatRequirement = "^" + prometheusBaseFormt + "$" ) var prometheusFormat = regexp.MustCompile(PrometheusFormatRequirement) // Metric represents one or more metrics. type Metric interface { ID() string LabeledID() string Opts() *Options WritePrometheus(w io.Writer) } type metricBase struct { Identifier string Labels map[string]string LabeledIdentifier string Options *Options set *vm.Set } // Options can be used to set advanced metric settings. type Options struct { // Name defines an optional human readable name for the metric. Name string // InternalID specifies an alternative internal ID that will be used when // exposing the metric via the API in a structured format. InternalID string // AlertLimit defines an upper limit that triggers an alert. AlertLimit float64 // AlertTimeframe defines an optional timeframe in seconds for which the // AlertLimit should be interpreted in. AlertTimeframe float64 // Permission defines the permission that is required to read the metric. Permission api.Permission // ExpertiseLevel defines the expertise level that the metric is meant for. ExpertiseLevel config.ExpertiseLevel // Persist enabled persisting the metric on shutdown and loading the previous // value at start. This is only supported for counters. Persist bool } func newMetricBase(id string, labels map[string]string, opts Options) (*metricBase, error) { // Check formats. if !prometheusFormat.MatchString(strings.ReplaceAll(id, "/", "_")) { return nil, fmt.Errorf("metric name %q must match %s", id, PrometheusFormatRequirement) } for labelName := range labels { if !prometheusFormat.MatchString(labelName) { return nil, fmt.Errorf("metric label name %q must match %s", labelName, PrometheusFormatRequirement) } } // Check permission. if opts.Permission < api.PermitAnyone { // Default to PermitUser. opts.Permission = api.PermitUser } // Ensure that labels is a map. if labels == nil { labels = make(map[string]string) } // Create metric base. base := &metricBase{ Identifier: id, Labels: labels, Options: &opts, set: vm.NewSet(), } base.LabeledIdentifier = base.buildLabeledID() return base, nil } // ID returns the given ID of the metric. func (m *metricBase) ID() string { return m.Identifier } // LabeledID returns the Prometheus-compatible labeled ID of the metric. func (m *metricBase) LabeledID() string { return m.LabeledIdentifier } // Opts returns the metric options. They may not be modified. func (m *metricBase) Opts() *Options { return m.Options } // WritePrometheus writes the metric in the prometheus format to the given writer. func (m *metricBase) WritePrometheus(w io.Writer) { m.set.WritePrometheus(w) } func (m *metricBase) buildLabeledID() string { // Because we use the namespace and the global flags here, we need to flag // them as immutable. registryLock.Lock() defer registryLock.Unlock() firstMetricRegistered = true // Build ID from Identifier. metricID := strings.TrimSpace(strings.ReplaceAll(m.Identifier, "/", "_")) // Add namespace to ID. if metricNamespace != "" { metricID = metricNamespace + "_" + metricID } // Return now if no labels are defined. if len(globalLabels) == 0 && len(m.Labels) == 0 { return metricID } // Add global labels to the custom ones, if they don't exist yet. for labelName, labelValue := range globalLabels { if _, ok := m.Labels[labelName]; !ok { m.Labels[labelName] = labelValue } } // Render labels into a slice and sort them in order to make the labeled ID // reproducible. labels := make([]string, 0, len(m.Labels)) for labelName, labelValue := range m.Labels { labels = append(labels, fmt.Sprintf("%s=%q", labelName, labelValue)) } sort.Strings(labels) // Return fully labaled ID. return fmt.Sprintf("%s{%s}", metricID, strings.Join(labels, ",")) } // Split metrics into sets, according to the API Auth Levels, which will also correspond to the UI Mode levels. SPN // nodes will also allow public access to metrics with the permission "PermitAnyone". // Save "life-long" metrics on shutdown and load them at start. // Generate the correct metric name and labels. // Expose metrics via http, but also via the runtime DB in order to push metrics to the UI. // The UI will have to parse the prometheus metrics format and will not be able to immediately present historical data, // but data will have to be built. // Provide the option to push metrics to a prometheus push gateway, this is especially helpful when gathering data from // loads of SPN nodes. ================================================ FILE: base/metrics/metric_counter.go ================================================ package metrics import ( vm "github.com/VictoriaMetrics/metrics" ) // Counter is a counter metric. type Counter struct { *metricBase *vm.Counter } // NewCounter registers a new counter metric. func NewCounter(id string, labels map[string]string, opts *Options) (*Counter, error) { // Ensure that there are options. if opts == nil { opts = &Options{} } // Make base. base, err := newMetricBase(id, labels, *opts) if err != nil { return nil, err } // Create metric struct. m := &Counter{ metricBase: base, } // Create metric in set m.Counter = m.set.NewCounter(m.LabeledID()) // Register metric. err = register(m) if err != nil { return nil, err } // Load state. m.loadState() return m, nil } // CurrentValue returns the current counter value. func (c *Counter) CurrentValue() uint64 { return c.Get() } ================================================ FILE: base/metrics/metric_counter_fetching.go ================================================ package metrics import ( "fmt" "io" vm "github.com/VictoriaMetrics/metrics" ) // FetchingCounter is a counter metric that fetches the values via a function call. type FetchingCounter struct { *metricBase counter *vm.Counter fetchCnt func() uint64 } // NewFetchingCounter registers a new fetching counter metric. func NewFetchingCounter(id string, labels map[string]string, fn func() uint64, opts *Options) (*FetchingCounter, error) { // Check if a fetch function is provided. if fn == nil { return nil, fmt.Errorf("%w: no fetch function provided", ErrInvalidOptions) } // Ensure that there are options. if opts == nil { opts = &Options{} } // Make base. base, err := newMetricBase(id, labels, *opts) if err != nil { return nil, err } // Create metric struct. m := &FetchingCounter{ metricBase: base, fetchCnt: fn, } // Create metric in set m.counter = m.set.NewCounter(m.LabeledID()) // Register metric. err = register(m) if err != nil { return nil, err } return m, nil } // CurrentValue returns the current counter value. func (fc *FetchingCounter) CurrentValue() uint64 { return fc.fetchCnt() } // WritePrometheus writes the metric in the prometheus format to the given writer. func (fc *FetchingCounter) WritePrometheus(w io.Writer) { fc.counter.Set(fc.fetchCnt()) fc.metricBase.set.WritePrometheus(w) } ================================================ FILE: base/metrics/metric_export.go ================================================ package metrics import ( "github.com/safing/portmaster/base/api" ) // UIntMetric is an interface for special functions of uint metrics. type UIntMetric interface { CurrentValue() uint64 } // FloatMetric is an interface for special functions of float metrics. type FloatMetric interface { CurrentValue() float64 } // MetricExport is used to export a metric and its current value. type MetricExport struct { Metric CurrentValue any } // ExportMetrics exports all registered metrics. func ExportMetrics(requestPermission api.Permission) []*MetricExport { registryLock.RLock() defer registryLock.RUnlock() export := make([]*MetricExport, 0, len(registry)) for _, metric := range registry { // Check permission. if requestPermission < metric.Opts().Permission { continue } // Add metric with current value. export = append(export, &MetricExport{ Metric: metric, CurrentValue: getCurrentValue(metric), }) } return export } // ExportValues exports the values of all supported metrics. func ExportValues(requestPermission api.Permission, internalOnly bool) map[string]any { registryLock.RLock() defer registryLock.RUnlock() export := make(map[string]any, len(registry)) for _, metric := range registry { // Check permission. if requestPermission < metric.Opts().Permission { continue } // Get Value. v := getCurrentValue(metric) if v == nil { continue } // Get ID. var id string switch { case metric.Opts().InternalID != "": id = metric.Opts().InternalID case internalOnly: continue default: id = metric.LabeledID() } // Add to export export[id] = v } return export } func getCurrentValue(metric Metric) any { if m, ok := metric.(UIntMetric); ok { return m.CurrentValue() } if m, ok := metric.(FloatMetric); ok { return m.CurrentValue() } return nil } ================================================ FILE: base/metrics/metric_gauge.go ================================================ package metrics import ( vm "github.com/VictoriaMetrics/metrics" ) // Gauge is a gauge metric. type Gauge struct { *metricBase *vm.Gauge } // NewGauge registers a new gauge metric. func NewGauge(id string, labels map[string]string, fn func() float64, opts *Options) (*Gauge, error) { // Ensure that there are options. if opts == nil { opts = &Options{} } // Make base. base, err := newMetricBase(id, labels, *opts) if err != nil { return nil, err } // Create metric struct. m := &Gauge{ metricBase: base, } // Create metric in set m.Gauge = m.set.NewGauge(m.LabeledID(), fn) // Register metric. err = register(m) if err != nil { return nil, err } return m, nil } // CurrentValue returns the current gauge value. func (g *Gauge) CurrentValue() float64 { return g.Get() } ================================================ FILE: base/metrics/metric_histogram.go ================================================ package metrics import ( vm "github.com/VictoriaMetrics/metrics" ) // Histogram is a histogram metric. type Histogram struct { *metricBase *vm.Histogram } // NewHistogram registers a new histogram metric. func NewHistogram(id string, labels map[string]string, opts *Options) (*Histogram, error) { // Ensure that there are options. if opts == nil { opts = &Options{} } // Make base. base, err := newMetricBase(id, labels, *opts) if err != nil { return nil, err } // Create metric struct. m := &Histogram{ metricBase: base, } // Create metric in set m.Histogram = m.set.NewHistogram(m.LabeledID()) // Register metric. err = register(m) if err != nil { return nil, err } return m, nil } ================================================ FILE: base/metrics/metrics_host.go ================================================ package metrics import ( "runtime" "sync" "time" "github.com/shirou/gopsutil/disk" "github.com/shirou/gopsutil/load" "github.com/shirou/gopsutil/mem" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/log" ) const hostStatTTL = 1 * time.Second func registerHostMetrics() (err error) { // Register load average metrics. _, err = NewGauge("host/load/avg/1", nil, getFloat64HostStat(LoadAvg1), &Options{Name: "Host Load Avg 1min", Permission: api.PermitUser}) if err != nil { return err } _, err = NewGauge("host/load/avg/5", nil, getFloat64HostStat(LoadAvg5), &Options{Name: "Host Load Avg 5min", Permission: api.PermitUser}) if err != nil { return err } _, err = NewGauge("host/load/avg/15", nil, getFloat64HostStat(LoadAvg15), &Options{Name: "Host Load Avg 15min", Permission: api.PermitUser}) if err != nil { return err } // Register memory usage metrics. _, err = NewGauge("host/mem/total", nil, getUint64HostStat(MemTotal), &Options{Name: "Host Memory Total", Permission: api.PermitUser}) if err != nil { return err } _, err = NewGauge("host/mem/used", nil, getUint64HostStat(MemUsed), &Options{Name: "Host Memory Used", Permission: api.PermitUser}) if err != nil { return err } _, err = NewGauge("host/mem/available", nil, getUint64HostStat(MemAvailable), &Options{Name: "Host Memory Available", Permission: api.PermitUser}) if err != nil { return err } _, err = NewGauge("host/mem/used/percent", nil, getFloat64HostStat(MemUsedPercent), &Options{Name: "Host Memory Used in Percent", Permission: api.PermitUser}) if err != nil { return err } // Register disk usage metrics. _, err = NewGauge("host/disk/total", nil, getUint64HostStat(DiskTotal), &Options{Name: "Host Disk Total", Permission: api.PermitUser}) if err != nil { return err } _, err = NewGauge("host/disk/used", nil, getUint64HostStat(DiskUsed), &Options{Name: "Host Disk Used", Permission: api.PermitUser}) if err != nil { return err } _, err = NewGauge("host/disk/free", nil, getUint64HostStat(DiskFree), &Options{Name: "Host Disk Free", Permission: api.PermitUser}) if err != nil { return err } _, err = NewGauge("host/disk/used/percent", nil, getFloat64HostStat(DiskUsedPercent), &Options{Name: "Host Disk Used in Percent", Permission: api.PermitUser}) if err != nil { return err } return nil } func getUint64HostStat(getStat func() (uint64, bool)) func() float64 { return func() float64 { val, _ := getStat() return float64(val) } } func getFloat64HostStat(getStat func() (float64, bool)) func() float64 { return func() float64 { val, _ := getStat() return val } } var ( loadAvg *load.AvgStat loadAvgExpires time.Time loadAvgLock sync.Mutex ) func getLoadAvg() *load.AvgStat { loadAvgLock.Lock() defer loadAvgLock.Unlock() // Return cache if still valid. if time.Now().Before(loadAvgExpires) { return loadAvg } // Refresh. var err error loadAvg, err = load.Avg() if err != nil { log.Warningf("metrics: failed to get load avg: %s", err) loadAvg = nil } loadAvgExpires = time.Now().Add(hostStatTTL) return loadAvg } // LoadAvg1 returns the 1-minute average system load. func LoadAvg1() (loadAvg float64, ok bool) { if stat := getLoadAvg(); stat != nil { return stat.Load1 / float64(runtime.NumCPU()), true } return 0, false } // LoadAvg5 returns the 5-minute average system load. func LoadAvg5() (loadAvg float64, ok bool) { if stat := getLoadAvg(); stat != nil { return stat.Load5 / float64(runtime.NumCPU()), true } return 0, false } // LoadAvg15 returns the 15-minute average system load. func LoadAvg15() (loadAvg float64, ok bool) { if stat := getLoadAvg(); stat != nil { return stat.Load15 / float64(runtime.NumCPU()), true } return 0, false } var ( memStat *mem.VirtualMemoryStat memStatExpires time.Time memStatLock sync.Mutex ) func getMemStat() *mem.VirtualMemoryStat { memStatLock.Lock() defer memStatLock.Unlock() // Return cache if still valid. if time.Now().Before(memStatExpires) { return memStat } // Refresh. var err error memStat, err = mem.VirtualMemory() if err != nil { log.Warningf("metrics: failed to get load avg: %s", err) memStat = nil } memStatExpires = time.Now().Add(hostStatTTL) return memStat } // MemTotal returns the total system memory. func MemTotal() (total uint64, ok bool) { if stat := getMemStat(); stat != nil { return stat.Total, true } return 0, false } // MemUsed returns the used system memory. func MemUsed() (used uint64, ok bool) { if stat := getMemStat(); stat != nil { return stat.Used, true } return 0, false } // MemAvailable returns the available system memory. func MemAvailable() (available uint64, ok bool) { if stat := getMemStat(); stat != nil { return stat.Available, true } return 0, false } // MemUsedPercent returns the percent of used system memory. func MemUsedPercent() (usedPercent float64, ok bool) { if stat := getMemStat(); stat != nil { return stat.UsedPercent, true } return 0, false } var ( diskStat *disk.UsageStat diskStatExpires time.Time diskStatLock sync.Mutex ) func getDiskStat() *disk.UsageStat { diskStatLock.Lock() defer diskStatLock.Unlock() // Return cache if still valid. if time.Now().Before(diskStatExpires) { return diskStat } // Refresh. var err error diskStat, err = disk.Usage(module.instance.DataDir()) if err != nil { log.Warningf("metrics: failed to get load avg: %s", err) diskStat = nil } diskStatExpires = time.Now().Add(hostStatTTL) return diskStat } // DiskTotal returns the total disk space (from the program's data root). func DiskTotal() (total uint64, ok bool) { if stat := getDiskStat(); stat != nil { return stat.Total, true } return 0, false } // DiskUsed returns the used disk space (from the program's data root). func DiskUsed() (used uint64, ok bool) { if stat := getDiskStat(); stat != nil { return stat.Used, true } return 0, false } // DiskFree returns the available disk space (from the program's data root). func DiskFree() (free uint64, ok bool) { if stat := getDiskStat(); stat != nil { return stat.Free, true } return 0, false } // DiskUsedPercent returns the percent of used disk space (from the program's data root). func DiskUsedPercent() (usedPercent float64, ok bool) { if stat := getDiskStat(); stat != nil { return stat.UsedPercent, true } return 0, false } ================================================ FILE: base/metrics/metrics_info.go ================================================ package metrics import ( "runtime" "strings" "sync/atomic" "github.com/safing/portmaster/base/info" ) var reportedStart atomic.Bool func registerInfoMetric() error { meta := info.GetInfo() _, err := NewGauge( "info", map[string]string{ "version": checkUnknown(meta.Version), "commit": checkUnknown(meta.Commit), "build_date": checkUnknown(meta.BuildTime), "build_source": checkUnknown(meta.Source), "go_os": runtime.GOOS, "go_arch": runtime.GOARCH, "go_version": runtime.Version(), "go_compiler": runtime.Compiler, "comment": commentOption(), }, func() float64 { // Report as 0 the first time in order to detect (re)starts. if reportedStart.CompareAndSwap(false, true) { return 0 } return 1 }, nil, ) return err } func checkUnknown(s string) string { if strings.Contains(s, "unknown") { return "unknown" } return s } ================================================ FILE: base/metrics/metrics_logs.go ================================================ package metrics import ( "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/log" ) func registerLogMetrics() (err error) { _, err = NewFetchingCounter( "logs/warning/total", nil, log.TotalWarningLogLines, &Options{ Name: "Total Warning Log Lines", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = NewFetchingCounter( "logs/error/total", nil, log.TotalErrorLogLines, &Options{ Name: "Total Error Log Lines", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = NewFetchingCounter( "logs/critical/total", nil, log.TotalCriticalLogLines, &Options{ Name: "Total Critical Log Lines", Permission: api.PermitUser, }, ) if err != nil { return err } return nil } ================================================ FILE: base/metrics/metrics_runtime.go ================================================ package metrics import ( "bufio" "bytes" "fmt" "io" "strings" vm "github.com/VictoriaMetrics/metrics" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" ) func registerRuntimeMetric() error { runtimeBase, err := newMetricBase("_runtime", nil, Options{ Name: "Golang Runtime", Permission: api.PermitAdmin, ExpertiseLevel: config.ExpertiseLevelDeveloper, }) if err != nil { return err } return register(&runtimeMetrics{ metricBase: runtimeBase, }) } type runtimeMetrics struct { *metricBase } func (r *runtimeMetrics) WritePrometheus(w io.Writer) { // If there nothing to change, just write directly to w. if metricNamespace == "" && len(globalLabels) == 0 { vm.WriteProcessMetrics(w) return } // Write metrics to buffer. buf := new(bytes.Buffer) vm.WriteProcessMetrics(buf) // Add namespace and label per line. scanner := bufio.NewScanner(buf) scanner.Split(bufio.ScanLines) for scanner.Scan() { line := scanner.Text() // Add namespace, if set. if metricNamespace != "" { line = metricNamespace + "_" + line } // Add global labels, if set. if len(globalLabels) > 0 { // Find where to insert. mergeWithExisting := true insertAt := strings.Index(line, "{") + 1 if insertAt <= 0 { mergeWithExisting = false insertAt = strings.Index(line, " ") if insertAt < 0 { continue } } // Write new line directly to w. fmt.Fprint(w, line[:insertAt]) if !mergeWithExisting { fmt.Fprint(w, "{") } labelsAdded := 0 for labelKey, labelValue := range globalLabels { fmt.Fprintf(w, "%s=%q", labelKey, labelValue) // Add separator if not last label. labelsAdded++ if labelsAdded < len(globalLabels) { fmt.Fprint(w, ", ") } } if mergeWithExisting { fmt.Fprint(w, ", ") } else { fmt.Fprint(w, "}") } fmt.Fprintln(w, line[insertAt:]) } } // Check if there was an error in the scanner. if scanner.Err() != nil { log.Warningf("metrics: failed to scan go process metrics: %s", scanner.Err()) } } ================================================ FILE: base/metrics/module.go ================================================ package metrics import ( "errors" "fmt" "sort" "sync" "sync/atomic" "github.com/safing/portmaster/service/mgr" ) type Metrics struct { mgr *mgr.Manager instance instance metricTicker *mgr.SleepyTicker } func (met *Metrics) Manager() *mgr.Manager { return met.mgr } func (met *Metrics) Start() error { return start() } func (met *Metrics) Stop() error { return stop() } func (met *Metrics) SetSleep(enabled bool) { if met.metricTicker != nil { met.metricTicker.SetSleep(enabled) } } var ( module *Metrics shimLoaded atomic.Bool registry []Metric registryLock sync.RWMutex readyToRegister bool firstMetricRegistered bool metricNamespace string globalLabels = make(map[string]string) // ErrAlreadyStarted is returned when an operation is only valid before the // first metric is registered, and is called after. ErrAlreadyStarted = errors.New("can only be changed before first metric is registered") // ErrAlreadyRegistered is returned when a metric with the same ID is // registered again. ErrAlreadyRegistered = errors.New("metric already registered") // ErrAlreadySet is returned when a value is already set and cannot be changed. ErrAlreadySet = errors.New("already set") // ErrInvalidOptions is returned when invalid options where provided. ErrInvalidOptions = errors.New("invalid options") ) func start() error { // Add metric instance name as global variable if set. if instanceOption() != "" { if err := AddGlobalLabel("instance", instanceOption()); err != nil { return err } } // Mark registry as ready to register metrics. func() { registryLock.Lock() defer registryLock.Unlock() readyToRegister = true }() if err := registerInfoMetric(); err != nil { return err } if err := registerRuntimeMetric(); err != nil { return err } if err := registerHostMetrics(); err != nil { return err } if err := registerLogMetrics(); err != nil { return err } if pushOption() != "" { module.mgr.Go("metric pusher", metricsWriter) } return nil } func stop() error { // Wait until the metrics pusher is done, as it may have started reporting // and may report a higher number than we store to disk. For persistent // metrics it can then happen that the first report is lower than the // previous report, making prometheus think that all that happened since the // last report, due to the automatic restart detection. // The registry is read locked when writing metrics. // Write lock the registry to make sure all writes are finished. registryLock.Lock() registryLock.Unlock() //nolint:staticcheck storePersistentMetrics() return nil } func register(m Metric) error { registryLock.Lock() defer registryLock.Unlock() // Check if metric ID is already registered. for _, registeredMetric := range registry { if m.LabeledID() == registeredMetric.LabeledID() { return ErrAlreadyRegistered } if m.Opts().InternalID != "" && m.Opts().InternalID == registeredMetric.Opts().InternalID { return fmt.Errorf("%w with this internal ID", ErrAlreadyRegistered) } } // Add new metric to registry and sort it. registry = append(registry, m) sort.Sort(byLabeledID(registry)) // Check if we can already register. if !readyToRegister { return fmt.Errorf("registering metric %q too early", m.ID()) } // Set flag that first metric is now registered. firstMetricRegistered = true return nil } // SetNamespace sets the namespace for all metrics. It is prefixed to all // metric IDs. // It must be set before any metric is registered. // Does not affect golang runtime metrics. func SetNamespace(namespace string) error { // Lock registry and check if a first metric is already registered. registryLock.Lock() defer registryLock.Unlock() if firstMetricRegistered { return ErrAlreadyStarted } // Check if the namespace is already set. if metricNamespace != "" { return ErrAlreadySet } metricNamespace = namespace return nil } // AddGlobalLabel adds a global label to all metrics. // Global labels must be added before any metric is registered. // Does not affect golang runtime metrics. func AddGlobalLabel(name, value string) error { // Lock registry and check if a first metric is already registered. registryLock.Lock() defer registryLock.Unlock() if firstMetricRegistered { return ErrAlreadyStarted } // Check format. if !prometheusFormat.MatchString(name) { return fmt.Errorf("metric label name %q must match %s", name, PrometheusFormatRequirement) } globalLabels[name] = value return nil } type byLabeledID []Metric func (r byLabeledID) Len() int { return len(r) } func (r byLabeledID) Less(i, j int) bool { return r[i].LabeledID() < r[j].LabeledID() } func (r byLabeledID) Swap(i, j int) { r[i], r[j] = r[j], r[i] } func New(instance instance) (*Metrics, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Metrics") module = &Metrics{ mgr: m, instance: instance, } if err := prepConfig(); err != nil { return nil, err } if err := registerAPI(); err != nil { return nil, err } return module, nil } type instance interface { DataDir() string } ================================================ FILE: base/metrics/persistence.go ================================================ package metrics import ( "errors" "fmt" "sync" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" ) var ( storage *metricsStorage storageKey string storageInit = abool.New() storageLoaded = abool.New() db = database.NewInterface(&database.Options{ Local: true, Internal: true, }) // ErrAlreadyInitialized is returned when trying to initialize an option // more than once or if the time window for initializing is over. ErrAlreadyInitialized = errors.New("already initialized") ) type metricsStorage struct { sync.Mutex record.Base Start time.Time Counters map[string]uint64 } // EnableMetricPersistence enables metric persistence for metrics that opted // for it. They given key is the database key where the metric data will be // persisted. // This call also directly loads the stored data from the database. // The returned error is only about loading the metrics, not about enabling // persistence. // May only be called once. func EnableMetricPersistence(key string) error { // Check if already initialized. if !storageInit.SetToIf(false, true) { return ErrAlreadyInitialized } // Set storage key. storageKey = key // Load metrics from storage. var err error storage, err = getMetricsStorage(storageKey) switch { case err == nil: // Continue. case errors.Is(err, database.ErrNotFound): return nil default: return err } storageLoaded.Set() // Load saved state for all counter metrics. registryLock.RLock() defer registryLock.RUnlock() for _, m := range registry { counter, ok := m.(*Counter) if ok { counter.loadState() } } return nil } func (c *Counter) loadState() { // Check if we can and should load the state. if !storageLoaded.IsSet() || !c.Opts().Persist { return } c.Set(storage.Counters[c.LabeledID()]) } func storePersistentMetrics() { // Check if persistence is enabled. if !storageInit.IsSet() || storageKey == "" { return } // Create new storage. newStorage := &metricsStorage{ // TODO: This timestamp should be taken from previous save, if possible. Start: time.Now(), Counters: make(map[string]uint64), } newStorage.SetKey(storageKey) // Copy values from previous version. if storageLoaded.IsSet() { newStorage.Start = storage.Start } registryLock.RLock() defer registryLock.RUnlock() // Export all counter metrics. for _, m := range registry { if m.Opts().Persist { counter, ok := m.(*Counter) if ok { newStorage.Counters[m.LabeledID()] = counter.Get() } } } // Save to database. err := db.Put(newStorage) if err != nil { log.Warningf("metrics: failed to save metrics storage to db: %s", err) } } func getMetricsStorage(key string) (*metricsStorage, error) { r, err := db.Get(key) if err != nil { return nil, err } // unwrap if r.IsWrapped() { // only allocate a new struct, if we need it newStorage := &metricsStorage{} err = record.Unwrap(r, newStorage) if err != nil { return nil, err } return newStorage, nil } // or adjust type newStorage, ok := r.(*metricsStorage) if !ok { return nil, fmt.Errorf("record not of type *metricsStorage, but %T", r) } return newStorage, nil } ================================================ FILE: base/notifications/cleaner.go ================================================ package notifications import ( "time" "github.com/safing/portmaster/service/mgr" ) func cleaner(ctx *mgr.WorkerCtx) error { //nolint:unparam // Conforms to worker interface ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for { select { case <-ctx.Done(): return nil case <-ticker.C: deleteExpiredNotifs() } } } func deleteExpiredNotifs() { // Get a copy of the notification map. notsCopy := getNotsCopy() // Delete all expired notifications. for _, n := range notsCopy { if n.isExpired() { n.delete(true) } } } func (n *Notification) isExpired() bool { n.Lock() defer n.Unlock() return n.Expires > 0 && n.Expires < time.Now().Unix() } func getNotsCopy() []*Notification { notsLock.RLock() defer notsLock.RUnlock() notsCopy := make([]*Notification, 0, len(nots)) for _, n := range nots { notsCopy = append(notsCopy, n) } return notsCopy } ================================================ FILE: base/notifications/config.go ================================================ package notifications import ( "github.com/safing/portmaster/base/config" ) // Configuration Keys. var ( CfgUseSystemNotificationsKey = "core/useSystemNotifications" useSystemNotifications config.BoolOption ) func registerConfig() error { if err := config.Register(&config.Option{ Name: "Desktop Notifications", Key: CfgUseSystemNotificationsKey, Description: "In addition to showing notifications in the Portmaster App, also send them to the Desktop. This requires the Portmaster Notifier to be running.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelUser, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: true, // TODO: turn off by default on unsupported systems Annotations: config.Annotations{ config.DisplayOrderAnnotation: -15, config.CategoryAnnotation: "User Interface", }, }); err != nil { return err } useSystemNotifications = config.Concurrent.GetAsBool(CfgUseSystemNotificationsKey, true) return nil } ================================================ FILE: base/notifications/database.go ================================================ package notifications import ( "errors" "fmt" "strings" "sync" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" "github.com/safing/portmaster/base/log" ) var ( nots = make(map[string]*Notification) notsLock sync.RWMutex dbController *database.Controller ) // Storage interface errors. var ( ErrInvalidData = errors.New("invalid data, must be a notification object") ErrInvalidPath = errors.New("invalid path") ErrNoDelete = errors.New("notifications may not be deleted, they must be handled") ) // StorageInterface provices a storage.Interface to the configuration manager. type StorageInterface struct { storage.InjectBase } func registerAsDatabase() error { _, err := database.Register(&database.Database{ Name: "notifications", Description: "Notifications", StorageType: "injected", }) if err != nil { return err } controller, err := database.InjectDatabase("notifications", &StorageInterface{}) if err != nil { return err } dbController = controller return nil } // Get returns a database record. func (s *StorageInterface) Get(key string) (record.Record, error) { // Get EventID from key. if !strings.HasPrefix(key, "all/") { return nil, storage.ErrNotFound } key = strings.TrimPrefix(key, "all/") // Get notification from storage. n, ok := getNotification(key) if !ok { return nil, storage.ErrNotFound } return n, nil } func getNotification(eventID string) (n *Notification, ok bool) { notsLock.RLock() defer notsLock.RUnlock() n, ok = nots[eventID] return } // Query returns a an iterator for the supplied query. func (s *StorageInterface) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { it := iterator.New() go s.processQuery(q, it) // TODO: check local and internal return it, nil } func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) { // Get a copy of the notification map. notsCopy := getNotsCopy() // send all notifications for _, n := range notsCopy { if inQuery(n, q) { select { case it.Next <- n: case <-it.Done: // make sure we don't leak this goroutine if the iterator get's cancelled return } } } it.Finish(nil) } func inQuery(n *Notification, q *query.Query) bool { n.lock.Lock() defer n.lock.Unlock() switch { case n.Meta().IsDeleted(): return false case !q.MatchesKey(n.DatabaseKey()): return false case !q.MatchesRecord(n): return false } return true } // Put stores a record in the database. func (s *StorageInterface) Put(r record.Record) (record.Record, error) { // record is already locked! key := r.DatabaseKey() n, err := EnsureNotification(r) if err != nil { return nil, ErrInvalidData } // transform key if strings.HasPrefix(key, "all/") { key = strings.TrimPrefix(key, "all/") } else { return nil, ErrInvalidPath } return applyUpdate(n, key) } func applyUpdate(n *Notification, key string) (*Notification, error) { // separate goroutine in order to correctly lock notsLock existing, ok := getNotification(key) // ignore if already deleted if !ok || existing.Meta().IsDeleted() { // this is a completely new notification // we pass pushUpdate==false because the storage // controller will push an update on put anyway. n.save(false) return n, nil } // Save when we're finished, if needed. save := false defer func() { if save { existing.save(false) } }() existing.Lock() defer existing.Unlock() if existing.State == Executed { return existing, fmt.Errorf("action already executed") } // check if the notification has been marked as // "executed externally". if n.State == Executed { log.Tracef("notifications: action for %s executed externally", n.EventID) existing.State = Executed save = true // in case the action has been executed immediately by the // sender we may need to update the SelectedActionID. // Though, we guard the assignments with value check // so partial updates that only change the // State property do not overwrite existing values. if n.SelectedActionID != "" { existing.SelectedActionID = n.SelectedActionID } } if n.SelectedActionID != "" && existing.State == Active { log.Tracef("notifications: selected action for %s: %s", n.EventID, n.SelectedActionID) existing.selectAndExecuteAction(n.SelectedActionID) save = true } return existing, nil } // Delete deletes a record from the database. func (s *StorageInterface) Delete(key string) error { // Get EventID from key. if !strings.HasPrefix(key, "all/") { return storage.ErrNotFound } key = strings.TrimPrefix(key, "all/") // Get notification from storage. n, ok := getNotification(key) if !ok { return storage.ErrNotFound } n.delete(true) return nil } // ReadOnly returns whether the database is read only. func (s *StorageInterface) ReadOnly() bool { return false } // EnsureNotification ensures that the given record is a Notification and returns it. func EnsureNotification(r record.Record) (*Notification, error) { // unwrap if r.IsWrapped() { // only allocate a new struct, if we need it n := &Notification{} err := record.Unwrap(r, n) if err != nil { return nil, err } return n, nil } // or adjust type n, ok := r.(*Notification) if !ok { return nil, fmt.Errorf("record not of type *Notification, but %T", r) } return n, nil } ================================================ FILE: base/notifications/doc.go ================================================ /* Package notifications provides a notification system. # Notification Lifecycle 1. Create Notification with an ID and Message. 2. Set possible actions and save it. 3. When the user responds, the action is executed. Example // create notification n := notifications.New("update-available", "A new update is available. Restart to upgrade.") // set actions and save n.AddAction("later", "Later").AddAction("restart", "Restart now!").Save() // wait for user action selectedAction := <-n.Response() switch selectedAction { case "later": log.Infof("user wants to upgrade later.") case "restart": log.Infof("user wants to restart now.") } */ package notifications ================================================ FILE: base/notifications/module-mirror.go ================================================ package notifications import ( "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" ) // SyncWithState syncs the notification to a state in the given state mgr. // The state will be removed when the notification is removed. func (n *Notification) SyncWithState(state *mgr.StateMgr) { if state == nil { log.Warningf("notifications: invalid usage: cannot attach %s to nil module", n.EventID) return } n.lock.Lock() defer n.lock.Unlock() if n.Meta().IsDeleted() { log.Warningf("notifications: cannot attach module to deleted notification %s", n.EventID) return } if n.State != Active { log.Warningf("notifications: cannot attach module to inactive notification %s", n.EventID) return } if n.belongsTo != nil { log.Warningf("notifications: cannot override attached module for notification %s", n.EventID) return } // Attach module. n.belongsTo = state // Create state with same ID. state.Add(mgr.State{ ID: n.EventID, Name: n.Title, Message: n.Message, Type: notifTypeToStateType(n.Type), Data: n.EventData, }) } func notifTypeToStateType(notifType Type) mgr.StateType { switch notifType { case Info: return mgr.StateTypeHint case Warning: return mgr.StateTypeWarning case Prompt: return mgr.StateTypeUndefined case Error: return mgr.StateTypeError default: return mgr.StateTypeUndefined } } ================================================ FILE: base/notifications/module.go ================================================ package notifications import ( "errors" "fmt" "sync/atomic" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/mgr" ) type Notifications struct { mgr *mgr.Manager instance instance states *mgr.StateMgr } func (n *Notifications) Manager() *mgr.Manager { return n.mgr } func (n *Notifications) States() *mgr.StateMgr { return n.states } func (n *Notifications) Start() error { return start() } func (n *Notifications) Stop() error { return nil } // NotifyInfo is a helper method for quickly showing an info notification. // The notification will be activated immediately. // If the provided id is empty, an id will derived from msg. // ShowOnSystem is disabled. // If no actions are defined, a default "OK" (ID:"ack") action will be added. func (n *Notifications) NotifyInfo(id, title, msg string, actions ...Action) *Notification { return NotifyInfo(id, title, msg, actions...) } // NotifyWarn is a helper method for quickly showing a warning notification // The notification will be activated immediately. // If the provided id is empty, an id will derived from msg. // ShowOnSystem is enabled. // If no actions are defined, a default "OK" (ID:"ack") action will be added. func (n *Notifications) NotifyWarn(id, title, msg string, actions ...Action) *Notification { return NotifyWarn(id, title, msg, actions...) } // NotifyError is a helper method for quickly showing an error notification. // The notification will be activated immediately. // If the provided id is empty, an id will derived from msg. // ShowOnSystem is enabled. // If no actions are defined, a default "OK" (ID:"ack") action will be added. func (n *Notifications) NotifyError(id, title, msg string, actions ...Action) *Notification { return NotifyError(id, title, msg, actions...) } // NotifyPrompt is a helper method for quickly showing a prompt notification. // The notification will be activated immediately. // If the provided id is empty, an id will derived from msg. // ShowOnSystem is disabled. // If no actions are defined, a default "OK" (ID:"ack") action will be added. func (n *Notifications) NotifyPrompt(id, title, msg string, actions ...Action) *Notification { return NotifyPrompt(id, title, msg, actions...) } // Notify sends the given notification. func (n *Notifications) Notify(notification *Notification) *Notification { return Notify(notification) } func prep() error { return registerConfig() } func start() error { err := registerAsDatabase() if err != nil { return err } showConfigLoadingErrors() module.mgr.Go("cleaner", cleaner) return nil } func showConfigLoadingErrors() { validationErrors := config.GetLoadedConfigValidationErrors() if len(validationErrors) == 0 { return } // Trigger a module error for more awareness. module.states.Add(mgr.State{ ID: "config:validation-errors-on-load", Name: "Invalid Settings", Message: "Some current settings are invalid. Please update them and restart the Portmaster.", Type: mgr.StateTypeError, }) // Send one notification per invalid setting. for _, validationError := range config.GetLoadedConfigValidationErrors() { NotifyError( fmt.Sprintf("config:validation-error:%s", validationError.Option.Key), fmt.Sprintf("Invalid Setting for %s", validationError.Option.Name), fmt.Sprintf(`Your current setting for %s is invalid: %s Please update the setting and restart the Portmaster, until then the default value is used.`, validationError.Option.Name, validationError.Err.Error(), ), Action{ Text: "Change", Type: ActionTypeOpenSetting, Payload: &ActionTypeOpenSettingPayload{ Key: validationError.Option.Key, }, }, ) } } var ( module *Notifications shimLoaded atomic.Bool ) func New(instance instance) (*Notifications, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Notifications") module = &Notifications{ mgr: m, instance: instance, states: mgr.NewStateMgr(m), } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface{} ================================================ FILE: base/notifications/notification.go ================================================ package notifications import ( "context" "fmt" "sync" "time" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/mgr" ) // Type describes the type of a notification. type Type uint8 // Notification types. const ( Info Type = 0 Warning Type = 1 Prompt Type = 2 Error Type = 3 ) // State describes the state of a notification. type State string // NotificationActionFn defines the function signature for notification action // functions. type NotificationActionFn func(context.Context, *Notification) error // Possible notification states. // State transitions can only happen from top to bottom. const ( // Active describes a notification that is active, no expired and, // if actions are available, still waits for the user to select an // action. Active State = "active" // Responded describes a notification where the user has already // selected which action to take but that action is still to be // performed. Responded State = "responded" // Executes describes a notification where the user has selected // and action and that action has been performed. Executed State = "executed" ) // Notification represents a notification that is to be delivered to the user. type Notification struct { //nolint:maligned record.Base // EventID is used to identify a specific notification. It consists of // the module name and a per-module unique event id. // The following format is recommended: // : EventID string // GUID is a unique identifier for each notification instance. That is // two notifications with the same EventID must still have unique GUIDs. // The GUID is mainly used for system (Windows) integration and is // automatically populated by the notification package. Average users // don't need to care about this field. GUID string // Type is the notification type. It can be one of Info, Warning or Prompt. Type Type // Title is an optional and very short title for the message that gives a // hint about what the notification is about. Title string // Category is an optional category for the notification that allows for // tagging and grouping notifications by category. Category string // Message is the default message shown to the user if no localized version // of the notification is available. Note that the message should already // have any paramerized values replaced. Message string // ShowOnSystem specifies if the notification should be also shown on the // operating system. Notifications shown on the operating system level are // more focus-intrusive and should only be used for important notifications. // If the configuration option "Desktop Notifications" is switched off, this // will be forced to false on the first save. ShowOnSystem bool // EventData contains an additional payload for the notification. This payload // may contain contextual data and may be used by a localization framework // to populate the notification message template. // If EventData implements sync.Locker it will be locked and unlocked together with the // notification. Otherwise, EventData is expected to be immutable once the // notification has been saved and handed over to the notification or database package. EventData interface{} // Expires holds the unix epoch timestamp at which the notification expires // and can be cleaned up. // Users can safely ignore expired notifications and should handle expiry the // same as deletion. Expires int64 // State describes the current state of a notification. See State for // a list of available values and their meaning. State State // AvailableActions defines a list of actions that a user can choose from. AvailableActions []*Action // SelectedActionID is updated to match the ID of one of the AvailableActions // based on the user selection. SelectedActionID string // belongsTo holds the state this notification belongs to. The notification // lifecycle will be mirrored to the specified failure status. belongsTo *mgr.StateMgr lock sync.Mutex actionFunction NotificationActionFn // call function to process action actionTrigger chan string // and/or send to a channel expiredTrigger chan struct{} // closed on expire } // Action describes an action that can be taken for a notification. type Action struct { // ID specifies a unique ID for the action. If an action is selected, the ID // is written to SelectedActionID and the notification is saved. // If the action type is not ActionTypeNone, the ID may be empty, signifying // that this action is merely additional and selecting it does not dismiss the // notification. ID string // Text on the button. Text string // Type specifies the action type. Implementing interfaces should only // display action types they can handle. Type ActionType // Payload holds additional data for special action types. Payload interface{} // Visibility specifies where the action should be visible. Default is always visible. Visibility ActionVisibility } type ActionVisibility string const ( // Default visibility, action is always visible. ActionVisibilityDefault ActionVisibility = "" // default visibility // Visible only in extended view // (when user clicks on notification to read full message) ActionVisibilityDetailed ActionVisibility = "detailed" // Visible only in the UI app, never on the system level (if ShowOnSystem is true). ActionVisibilityInAppOnly ActionVisibility = "in-app-only" ) // ActionType defines a specific type of action. type ActionType string // Action Types. const ( ActionTypeNone = "" // Report selected ID back to backend. ActionTypeOpenURL = "open-url" // Open external URL ActionTypeOpenPage = "open-page" // Payload: Page ID ActionTypeOpenSetting = "open-setting" // Payload: See struct definition below. ActionTypeOpenProfile = "open-profile" // Payload: Scoped Profile ID ActionTypeInjectEvent = "inject-event" // Payload: Event ID ActionTypeWebhook = "call-webhook" // Payload: See struct definition below. ) // ActionTypeOpenSettingPayload defines the payload for the OpenSetting Action Type. type ActionTypeOpenSettingPayload struct { // Key is the key of the setting. Key string // Profile is the scoped ID of the profile. // Leaving this empty opens the global settings. Profile string } // ActionTypeWebhookPayload defines the payload for the WebhookPayload Action Type. type ActionTypeWebhookPayload struct { // HTTP Method to use. Defaults to "GET", or "POST" if a Payload is supplied. Method string // URL to call. // If the URL is relative, prepend the current API endpoint base path. // If the URL is absolute, send request to the Portmaster. URL string // Payload holds arbitrary payload data. Payload interface{} // ResultAction defines what should be done with successfully returned data. // Must one of: // - `ignore`: do nothing (default) // - `display`: the result is a human readable message, display it in a success message. ResultAction string } // Get returns the notification identifed by the given id or nil if it doesn't exist. func Get(id string) *Notification { notsLock.RLock() defer notsLock.RUnlock() n, ok := nots[id] if ok { return n } return nil } // Delete deletes the notification with the given id. func Delete(id string) { // Delete notification in defer to enable deferred unlocking. var n *Notification var ok bool defer func() { if ok { n.Delete() } }() notsLock.Lock() defer notsLock.Unlock() n, ok = nots[id] } // NotifyInfo is a helper method for quickly showing an info notification. // The notification will be activated immediately. // If the provided id is empty, an id will derived from msg. // ShowOnSystem is disabled. // If no actions are defined, a default "OK" (ID:"ack") action will be added. func NotifyInfo(id, title, msg string, actions ...Action) *Notification { return notify(Info, id, title, msg, false, actions...) } // NotifyWarn is a helper method for quickly showing a warning notification // The notification will be activated immediately. // If the provided id is empty, an id will derived from msg. // ShowOnSystem is enabled. // If no actions are defined, a default "OK" (ID:"ack") action will be added. func NotifyWarn(id, title, msg string, actions ...Action) *Notification { return notify(Warning, id, title, msg, true, actions...) } // NotifyError is a helper method for quickly showing an error notification. // The notification will be activated immediately. // If the provided id is empty, an id will derived from msg. // ShowOnSystem is enabled. // If no actions are defined, a default "OK" (ID:"ack") action will be added. func NotifyError(id, title, msg string, actions ...Action) *Notification { return notify(Error, id, title, msg, true, actions...) } // NotifyPrompt is a helper method for quickly showing a prompt notification. // The notification will be activated immediately. // If the provided id is empty, an id will derived from msg. // ShowOnSystem is disabled. // If no actions are defined, a default "OK" (ID:"ack") action will be added. func NotifyPrompt(id, title, msg string, actions ...Action) *Notification { return notify(Prompt, id, title, msg, false, actions...) } func notify(nType Type, id, title, msg string, showOnSystem bool, actions ...Action) *Notification { // Process actions. var acts []*Action if len(actions) == 0 { // Create ack action if there are no defined actions. acts = []*Action{ { ID: "ack", Text: "OK", }, } } else { // Reference given actions for notification. acts = make([]*Action, len(actions)) for index := range actions { a := actions[index] acts[index] = &a } } return Notify(&Notification{ EventID: id, Type: nType, Title: title, Message: msg, ShowOnSystem: showOnSystem, AvailableActions: acts, }) } // Notify sends the given notification. func Notify(n *Notification) *Notification { // While this function is very similar to Save(), it is much nicer to use in // order to just fire off one notification, as it does not require some more // uncommon Go syntax. n.save(true) return n } // Save saves the notification. func (n *Notification) Save() { n.save(true) } // save saves the notification to the internal storage. It locks the // notification, so it must not be locked when save is called. func (n *Notification) save(pushUpdate bool) { var id string // Save notification after pre-save processing. defer func() { if id != "" { // Lock and save to notification storage. notsLock.Lock() defer notsLock.Unlock() nots[id] = n } }() // We do not access EventData here, so it is enough to just lock the // notification itself. n.lock.Lock() defer n.lock.Unlock() // Check if required data is present. if n.Title == "" && n.Message == "" { log.Warning("notifications: ignoring notification without Title or Message") return } // Derive EventID from Message if not given. if n.EventID == "" { n.EventID = fmt.Sprintf( "unknown:%s", utils.DerivedInstanceUUID(n.Message).String(), ) } // Save ID for deletion id = n.EventID // Generate random GUID if not set. if n.GUID == "" { n.GUID = utils.RandomUUID(n.EventID).String() } // Make sure we always have a notification state assigned. if n.State == "" { n.State = Active } // Initialize on first save. if !n.KeyIsSet() { // Set database key. n.SetKey(fmt.Sprintf("notifications:all/%s", n.EventID)) // Check if notifications should be shown on the system at all. if !useSystemNotifications() { n.ShowOnSystem = false } } // Update meta data. n.UpdateMeta() // Push update via the database system if needed. if pushUpdate { log.Tracef("notifications: pushing update for %s to subscribers", n.Key()) dbController.PushUpdate(n) } } // SetActionFunction sets a trigger function to be executed when the user reacted on the notification. // The provided function will be started as its own goroutine and will have to lock everything it accesses, even the provided notification. func (n *Notification) SetActionFunction(fn NotificationActionFn) *Notification { n.lock.Lock() defer n.lock.Unlock() n.actionFunction = fn return n } // Response waits for the user to respond to the notification and returns the selected action. func (n *Notification) Response() <-chan string { n.lock.Lock() defer n.lock.Unlock() if n.actionTrigger == nil { n.actionTrigger = make(chan string) } return n.actionTrigger } // Update updates/resends a notification if it was not already responded to. func (n *Notification) Update(expires int64) { // Save when we're finished, if needed. save := false defer func() { if save { n.save(true) } }() n.lock.Lock() defer n.lock.Unlock() // Don't update if notification isn't active. if n.State != Active { return } // Don't update too quickly. if n.Meta().Modified > time.Now().Add(-10*time.Second).Unix() { return } // Update expiry and save. n.Expires = expires save = true } // Delete (prematurely) cancels and deletes a notification. func (n *Notification) Delete() { // Dismiss notification. func() { n.lock.Lock() defer n.lock.Unlock() if n.actionTrigger != nil { close(n.actionTrigger) n.actionTrigger = nil } }() n.delete(true) } // delete deletes the notification from the internal storage. It locks the // notification, so it must not be locked when delete is called. func (n *Notification) delete(pushUpdate bool) { var id string // Delete notification after processing deletion. defer func() { // Lock and delete from notification storage. notsLock.Lock() defer notsLock.Unlock() delete(nots, id) }() // We do not access EventData here, so it is enough to just lock the // notification itself. n.lock.Lock() defer n.lock.Unlock() // Check if notification is already deleted. if n.Meta().IsDeleted() { return } // Save ID for deletion id = n.EventID // Mark notification as deleted. n.Meta().Delete() // Close expiry channel if available. if n.expiredTrigger != nil { close(n.expiredTrigger) n.expiredTrigger = nil } // Push update via the database system if needed. if pushUpdate { dbController.PushUpdate(n) } // Remove the connected state. if n.belongsTo != nil { n.belongsTo.Remove(n.EventID) } } // Expired notifies the caller when the notification has expired. func (n *Notification) Expired() <-chan struct{} { n.lock.Lock() defer n.lock.Unlock() if n.expiredTrigger == nil { n.expiredTrigger = make(chan struct{}) } return n.expiredTrigger } // selectAndExecuteAction sets the user response and executes/triggers the action, if possible. func (n *Notification) selectAndExecuteAction(id string) { if n.State != Active { return } n.State = Responded n.SelectedActionID = id executed := false if n.actionFunction != nil { module.mgr.Go("notification action execution", func(ctx *mgr.WorkerCtx) error { return n.actionFunction(ctx.Ctx(), n) }) executed = true } if n.actionTrigger != nil { // satisfy all listeners (if they are listening) // TODO(ppacher): if we miss to notify the waiter here (because // nobody is listeing on actionTrigger) we wil likely // never be able to execute the action again (simply because // we won't try). May consider replacing the single actionTrigger // channel with a per-listener (buffered) one so we just send // the value and close the channel. triggerAll: for { select { case n.actionTrigger <- n.SelectedActionID: executed = true case <-time.After(100 * time.Millisecond): // mitigate race conditions break triggerAll } } } if executed { n.State = Executed // n.resolveModuleFailure() } } // Lock locks the Notification. If EventData is set and // implements sync.Locker it is locked as well. Users that // want to replace the EventData on a notification must // ensure to unlock the current value on their own. If the // new EventData implements sync.Locker as well, it must // be locked prior to unlocking the notification. func (n *Notification) Lock() { n.lock.Lock() if locker, ok := n.EventData.(sync.Locker); ok { locker.Lock() } } // Unlock unlocks the Notification and the EventData, if // it implements sync.Locker. See Lock() for more information // on how to replace and work with EventData. func (n *Notification) Unlock() { n.lock.Unlock() if locker, ok := n.EventData.(sync.Locker); ok { locker.Unlock() } } ================================================ FILE: base/rng/doc.go ================================================ // Package rng provides a feedable CSPRNG. // // CSPRNG used is fortuna: github.com/seehuhn/fortuna // By default the CSPRNG is fed by two sources: // - It starts with a seed from `crypto/rand` and periodically reseeds from there // - A really simple tickfeeder which extracts entropy from the internal go scheduler using goroutines and is meant to be used under load. // // The RNG can also be easily fed with additional sources. package rng ================================================ FILE: base/rng/entropy.go ================================================ package rng import ( "encoding/binary" "github.com/tevino/abool" "github.com/safing/portmaster/service/mgr" "github.com/safing/structures/container" ) const ( minFeedEntropy = 256 ) var rngFeeder = make(chan []byte) // The Feeder is used to feed entropy to the RNG. type Feeder struct { input chan *entropyData entropy int64 needsEntropy *abool.AtomicBool buffer *container.Container } type entropyData struct { data []byte entropy int } // NewFeeder returns a new entropy Feeder. func NewFeeder() *Feeder { newFeeder := &Feeder{ input: make(chan *entropyData), needsEntropy: abool.NewBool(true), buffer: container.New(), } module.mgr.Go("feeder", newFeeder.run) return newFeeder } // NeedsEntropy returns whether the feeder is currently gathering entropy. func (f *Feeder) NeedsEntropy() bool { return f.needsEntropy.IsSet() } // SupplyEntropy supplies entropy to the Feeder, it will block until the Feeder has read from it. func (f *Feeder) SupplyEntropy(data []byte, entropy int) { f.input <- &entropyData{ data: data, entropy: entropy, } } // SupplyEntropyIfNeeded supplies entropy to the Feeder, but will not block if no entropy is currently needed. func (f *Feeder) SupplyEntropyIfNeeded(data []byte, entropy int) { if f.needsEntropy.IsSet() { return } select { case f.input <- &entropyData{ data: data, entropy: entropy, }: default: } } // SupplyEntropyAsInt supplies entropy to the Feeder, it will block until the Feeder has read from it. func (f *Feeder) SupplyEntropyAsInt(n int64, entropy int) { b := make([]byte, 8) binary.LittleEndian.PutUint64(b, uint64(n)) f.SupplyEntropy(b, entropy) } // SupplyEntropyAsIntIfNeeded supplies entropy to the Feeder, but will not block if no entropy is currently needed. func (f *Feeder) SupplyEntropyAsIntIfNeeded(n int64, entropy int) { if f.needsEntropy.IsSet() { // avoid allocating a slice if possible b := make([]byte, 8) binary.LittleEndian.PutUint64(b, uint64(n)) f.SupplyEntropyIfNeeded(b, entropy) } } // CloseFeeder stops the feed processing - the responsible goroutine exits. The input channel is closed and the feeder may not be used anymore in any way. func (f *Feeder) CloseFeeder() { close(f.input) } func (f *Feeder) run(ctx *mgr.WorkerCtx) error { defer f.needsEntropy.UnSet() for { // gather f.needsEntropy.Set() gather: for { select { case newEntropy := <-f.input: // check if feed has been closed if newEntropy == nil { return nil } // append to buffer f.buffer.Append(newEntropy.data) f.entropy += int64(newEntropy.entropy) if f.entropy >= minFeedEntropy { break gather } case <-ctx.Done(): return nil } } // feed f.needsEntropy.UnSet() select { case rngFeeder <- f.buffer.CompileData(): case <-ctx.Done(): return nil } f.buffer = container.New() } } ================================================ FILE: base/rng/entropy_test.go ================================================ package rng import ( "testing" "time" ) func TestFeeder(t *testing.T) { t.Parallel() // wait for start / first round to complete time.Sleep(1 * time.Millisecond) f := NewFeeder() // go through all functions f.NeedsEntropy() f.SupplyEntropy([]byte{0}, 0) f.SupplyEntropyAsInt(0, 0) f.SupplyEntropyIfNeeded([]byte{0}, 0) f.SupplyEntropyAsIntIfNeeded(0, 0) // fill entropy f.SupplyEntropyAsInt(0, 65535) // check blocking calls waitOne := make(chan struct{}) go func() { f.SupplyEntropy([]byte{0}, 0) close(waitOne) }() select { case <-waitOne: t.Error("call does not block!") case <-time.After(10 * time.Millisecond): } waitTwo := make(chan struct{}) go func() { f.SupplyEntropyAsInt(0, 0) close(waitTwo) }() select { case <-waitTwo: t.Error("call does not block!") case <-time.After(10 * time.Millisecond): } // check non-blocking calls waitThree := make(chan struct{}) go func() { f.SupplyEntropyIfNeeded([]byte{0}, 0) close(waitThree) }() select { case <-waitThree: case <-time.After(10 * time.Millisecond): t.Error("call blocks!") } waitFour := make(chan struct{}) go func() { f.SupplyEntropyAsIntIfNeeded(0, 0) close(waitFour) }() select { case <-waitFour: case <-time.After(10 * time.Millisecond): t.Error("call blocks!") } } ================================================ FILE: base/rng/fullfeed.go ================================================ package rng import ( "time" "github.com/safing/portmaster/service/mgr" ) func getFullFeedDuration() time.Duration { // full feed every 5x time of reseedAfterSeconds secsUntilFullFeed := reseedAfterSeconds * 5 // full feed at most once every ten minutes if secsUntilFullFeed < 600 { secsUntilFullFeed = 600 } return time.Duration(secsUntilFullFeed) * time.Second } func fullFeeder(ctx *mgr.WorkerCtx) error { fullFeedDuration := getFullFeedDuration() for { select { case <-time.After(fullFeedDuration): rngLock.Lock() feedAll: for { select { case data := <-rngFeeder: rng.Reseed(data) default: break feedAll } } rngLock.Unlock() case <-ctx.Done(): return nil } } } ================================================ FILE: base/rng/fullfeed_test.go ================================================ package rng import ( "testing" ) func TestFullFeeder(t *testing.T) { t.Parallel() for range 10 { go func() { rngFeeder <- []byte{0} }() } } ================================================ FILE: base/rng/get.go ================================================ package rng import ( "encoding/binary" "errors" "io" "math" "time" ) const ( reseedAfterSeconds = 600 // ten minutes reseedAfterBytes = 1048576 // one megabyte ) var ( // Reader provides a global instance to read from the RNG. Reader io.Reader rngBytesRead uint64 rngLastFeed = time.Now() ) // reader provides an io.Reader interface. type reader struct{} func init() { Reader = reader{} } func checkEntropy() (err error) { if !rngReady { return errors.New("RNG is not ready yet") } if rngBytesRead > reseedAfterBytes || int(time.Since(rngLastFeed).Seconds()) > reseedAfterSeconds { select { case r := <-rngFeeder: rng.Reseed(r) rngBytesRead = 0 rngLastFeed = time.Now() case <-time.After(1 * time.Second): return errors.New("failed to get new entropy") } } return nil } // Read reads random bytes into the supplied byte slice. func Read(b []byte) (n int, err error) { rngLock.Lock() defer rngLock.Unlock() if err := checkEntropy(); err != nil { return 0, err } return copy(b, rng.PseudoRandomData(uint(len(b)))), nil } // Read implements the io.Reader interface. func (r reader) Read(b []byte) (n int, err error) { return Read(b) } // Bytes allocates a new byte slice of given length and fills it with random data. func Bytes(n int) ([]byte, error) { rngLock.Lock() defer rngLock.Unlock() if err := checkEntropy(); err != nil { return nil, err } return rng.PseudoRandomData(uint(n)), nil } // Number returns a random number from 0 to (incl.) max. func Number(max uint64) (uint64, error) { secureLimit := math.MaxUint64 - (math.MaxUint64 % max) max++ for { randomBytes, err := Bytes(8) if err != nil { return 0, err } candidate := binary.LittleEndian.Uint64(randomBytes) if candidate < secureLimit { return candidate % max, nil } } } ================================================ FILE: base/rng/get_test.go ================================================ package rng import ( "testing" ) func TestNumberRandomness(t *testing.T) { t.Parallel() // skip in automated tests t.Logf("Integer number bias test deactivated, as it sometimes triggers.") t.SkipNow() if testing.Short() { t.Skip() } var subjects uint64 = 10 var testSize uint64 = 10000 results := make([]uint64, int(subjects)) for range int(subjects * testSize) { n, err := Number(subjects - 1) if err != nil { t.Fatal(err) return } results[int(n)]++ } // catch big mistakes in the number function, eg. massive % bias lowerMargin := testSize - testSize/50 upperMargin := testSize + testSize/50 for subject, result := range results { if result < lowerMargin || result > upperMargin { t.Errorf("subject %d is outside of margins: %d", subject, result) } } t.Fatal(results) } ================================================ FILE: base/rng/osfeeder.go ================================================ package rng import ( "crypto/rand" "fmt" "github.com/safing/portmaster/service/mgr" ) func osFeeder(ctx *mgr.WorkerCtx) error { entropyBytes := minFeedEntropy / 8 feeder := NewFeeder() defer feeder.CloseFeeder() for { // gather osEntropy := make([]byte, entropyBytes) n, err := rand.Read(osEntropy) if err != nil { return fmt.Errorf("could not read entropy from os: %w", err) } if n != entropyBytes { return fmt.Errorf("could not read enough entropy from os: got only %d bytes instead of %d", n, entropyBytes) } // feed select { case feeder.input <- &entropyData{ data: osEntropy, entropy: entropyBytes * 8, }: case <-ctx.Done(): return nil } } } ================================================ FILE: base/rng/rng.go ================================================ package rng import ( "crypto/aes" "crypto/cipher" "crypto/rand" "errors" "fmt" "sync" "sync/atomic" "github.com/aead/serpent" "github.com/seehuhn/fortuna" "github.com/safing/portmaster/service/mgr" ) // Rng is a random number generator. type Rng struct { mgr *mgr.Manager instance instance } var ( rng *fortuna.Generator rngLock sync.Mutex rngReady = false rngCipher = "aes" // Possible values: "aes", "serpent". ) func newCipher(key []byte) (cipher.Block, error) { switch rngCipher { case "aes": return aes.NewCipher(key) case "serpent": return serpent.NewCipher(key) default: return nil, fmt.Errorf("unknown or unsupported cipher: %s", rngCipher) } } // Manager returns the module manager. func (r *Rng) Manager() *mgr.Manager { return r.mgr } // Start starts the module. func (r *Rng) Start() error { rngLock.Lock() defer rngLock.Unlock() rng = fortuna.NewGenerator(newCipher) if rng == nil { return errors.New("failed to initialize rng") } // add another (async) OS rng seed r.mgr.Go("initial rng feed", func(_ *mgr.WorkerCtx) error { // get entropy from OS osEntropy := make([]byte, minFeedEntropy/8) _, err := rand.Read(osEntropy) if err != nil { return fmt.Errorf("could not read entropy from os: %w", err) } // feed rngLock.Lock() rng.Reseed(osEntropy) rngLock.Unlock() return nil }) // mark as ready rngReady = true // random source: OS r.mgr.Go("os rng feeder", osFeeder) // random source: goroutine ticks r.mgr.Go("tick rng feeder", tickFeeder) // full feeder r.mgr.Go("full feeder", fullFeeder) return nil } // Stop stops the module. func (r *Rng) Stop() error { return nil } var ( module *Rng shimLoaded atomic.Bool ) // New returns a new rng. func New(instance instance) (*Rng, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Rng") module = &Rng{ mgr: m, instance: instance, } return module, nil } type instance interface{} ================================================ FILE: base/rng/rng_test.go ================================================ package rng import ( "testing" ) func init() { var err error module, err = New(struct{}{}) if err != nil { panic(err) } err = module.Start() if err != nil { panic(err) } } func TestRNG(t *testing.T) { t.Parallel() key := make([]byte, 16) rngCipher = "aes" _, err := newCipher(key) if err != nil { t.Errorf("failed to create aes cipher: %s", err) } rngCipher = "serpent" _, err = newCipher(key) if err != nil { t.Errorf("failed to create serpent cipher: %s", err) } b := make([]byte, 32) _, err = Read(b) if err != nil { t.Errorf("Read failed: %s", err) } _, err = Reader.Read(b) if err != nil { t.Errorf("Read failed: %s", err) } _, err = Bytes(32) if err != nil { t.Errorf("Bytes failed: %s", err) } _, err = Number(100) if err != nil { t.Errorf("Number failed: %s", err) } } ================================================ FILE: base/rng/tickfeeder.go ================================================ package rng import ( "encoding/binary" "time" "github.com/safing/portmaster/service/mgr" ) func getTickFeederTickDuration() time.Duration { // be ready in 1/10 time of reseedAfterSeconds msecsAvailable := reseedAfterSeconds * 100 // ex.: reseed after 10 minutes: msecsAvailable = 60000 // have full entropy after 5 minutes // one tick generates 0,125 bits of entropy ticksNeeded := minFeedEntropy * 8 // ex.: minimum entropy is 256: ticksNeeded = 2048 // msces between ticks tickMsecs := msecsAvailable / ticksNeeded // ex.: tickMsecs = 29(,296875) // use a minimum of 10 msecs per tick for good entropy // it would take 21 seconds to get full 256 bits of entropy with 10msec ticks if tickMsecs < 10 { tickMsecs = 10 } return time.Duration(tickMsecs) * time.Millisecond } // tickFeeder is a really simple entropy feeder that adds the least significant bit of the current nanosecond unixtime to its pool every time it 'ticks'. // The more work the program does, the better the quality, as the internal schedular cannot immediately run the goroutine when it's ready. func tickFeeder(ctx *mgr.WorkerCtx) error { var value int64 var pushes int feeder := NewFeeder() defer feeder.CloseFeeder() ticker := time.NewTicker(getTickFeederTickDuration()) defer ticker.Stop() for { select { case <-ticker.C: case <-ctx.Done(): return nil } // add tick value value = (value << 1) | (time.Now().UnixNano() % 2) pushes++ if pushes >= 64 { // convert to []byte b := make([]byte, 8) binary.LittleEndian.PutUint64(b, uint64(value)) // reset pushes = 0 // feed select { case feeder.input <- &entropyData{ data: b, entropy: 8, }: case <-ctx.Done(): return nil } } } } ================================================ FILE: base/runtime/module.go ================================================ package runtime import ( "errors" "sync/atomic" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/service/mgr" ) // DefaultRegistry is the default registry // that is used by the module-level API. var DefaultRegistry = NewRegistry() type Runtime struct { mgr *mgr.Manager instance instance } func (r *Runtime) Manager() *mgr.Manager { return r.mgr } func (r *Runtime) Start() error { _, err := database.Register(&database.Database{ Name: "runtime", Description: "Runtime database", StorageType: "injected", ShadowDelete: false, }) if err != nil { return err } if err := DefaultRegistry.InjectAsDatabase("runtime"); err != nil { return err } return nil } func (r *Runtime) Stop() error { return nil } // Register is like Registry.Register but uses // the package DefaultRegistry. func Register(key string, provider ValueProvider) (PushFunc, error) { return DefaultRegistry.Register(key, provider) } var ( module *Runtime shimLoaded atomic.Bool ) func New(instance instance) (*Runtime, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Runtime") module = &Runtime{ mgr: m, instance: instance, } return module, nil } type instance interface{} ================================================ FILE: base/runtime/provider.go ================================================ package runtime import ( "errors" "github.com/safing/portmaster/base/database/record" ) var ( // ErrReadOnly should be returned from ValueProvider.Set if a // runtime record is considered read-only. ErrReadOnly = errors.New("runtime record is read-only") // ErrWriteOnly should be returned from ValueProvider.Get if // a runtime record is considered write-only. ErrWriteOnly = errors.New("runtime record is write-only") ) type ( // PushFunc is returned when registering a new value provider // and can be used to inform the database system about the // availability of a new runtime record value. Similar to // database.Controller.PushUpdate, the caller must hold // the lock for each record passed to PushFunc. PushFunc func(...record.Record) // ValueProvider provides access to a runtime-computed // database record. ValueProvider interface { // Set is called when the value is set from outside. // If the runtime value is considered read-only ErrReadOnly // should be returned. It is guaranteed that the key of // the record passed to Set is prefixed with the key used // to register the value provider. Set(r record.Record) (record.Record, error) // Get should return one or more records that match keyOrPrefix. // keyOrPrefix is guaranteed to be at least the prefix used to // register the ValueProvider. Get(keyOrPrefix string) ([]record.Record, error) } // SimpleValueSetterFunc is a convenience type for implementing a // write-only value provider. SimpleValueSetterFunc func(record.Record) (record.Record, error) // SimpleValueGetterFunc is a convenience type for implementing a // read-only value provider. SimpleValueGetterFunc func(keyOrPrefix string) ([]record.Record, error) ) // Set implements ValueProvider.Set and calls fn. func (fn SimpleValueSetterFunc) Set(r record.Record) (record.Record, error) { return fn(r) } // Get implements ValueProvider.Get and returns ErrWriteOnly. func (SimpleValueSetterFunc) Get(_ string) ([]record.Record, error) { return nil, ErrWriteOnly } // Set implements ValueProvider.Set and returns ErrReadOnly. func (SimpleValueGetterFunc) Set(r record.Record) (record.Record, error) { return nil, ErrReadOnly } // Get implements ValueProvider.Get and calls fn. func (fn SimpleValueGetterFunc) Get(keyOrPrefix string) ([]record.Record, error) { return fn(keyOrPrefix) } // Compile time checks. var ( _ ValueProvider = SimpleValueGetterFunc(nil) _ ValueProvider = SimpleValueSetterFunc(nil) ) ================================================ FILE: base/runtime/registry.go ================================================ package runtime import ( "errors" "fmt" "strings" "sync" "github.com/armon/go-radix" "golang.org/x/sync/errgroup" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" "github.com/safing/portmaster/base/log" ) var ( // ErrKeyTaken is returned when trying to register // a value provider at database key or prefix that // is already occupied by another provider. ErrKeyTaken = errors.New("runtime key or prefix already used") // ErrKeyUnmanaged is returned when a Put operation // on an unmanaged key is performed. ErrKeyUnmanaged = errors.New("runtime key not managed by any provider") // ErrInjected is returned by Registry.InjectAsDatabase // if the registry has already been injected. ErrInjected = errors.New("registry already injected") ) // Registry keeps track of registered runtime // value providers and exposes them via an // injected database. Users normally just need // to use the defaul registry provided by this // package but may consider creating a dedicated // runtime registry on their own. Registry uses // a radix tree for value providers and their // chosen database key/prefix. type Registry struct { l sync.RWMutex providers *radix.Tree dbController *database.Controller dbName string } // keyedValueProvider simply wraps a value provider with it's // registration prefix. type keyedValueProvider struct { ValueProvider key string } // NewRegistry returns a new registry. func NewRegistry() *Registry { return &Registry{ providers: radix.New(), } } func isPrefixKey(key string) bool { return strings.HasSuffix(key, "/") } // DatabaseName returns the name of the database where the // registry has been injected. It returns an empty string // if InjectAsDatabase has not been called. func (r *Registry) DatabaseName() string { r.l.RLock() defer r.l.RUnlock() return r.dbName } // InjectAsDatabase injects the registry as the storage // database for name. func (r *Registry) InjectAsDatabase(name string) error { r.l.Lock() defer r.l.Unlock() if r.dbController != nil { return ErrInjected } ctrl, err := database.InjectDatabase(name, r.asStorage()) if err != nil { return err } r.dbName = name r.dbController = ctrl return nil } // Register registers a new value provider p under keyOrPrefix. The // returned PushFunc can be used to send update notitifcations to // database subscribers. Note that keyOrPrefix must end in '/' to be // accepted as a prefix. func (r *Registry) Register(keyOrPrefix string, p ValueProvider) (PushFunc, error) { r.l.Lock() defer r.l.Unlock() // search if there's a provider registered for a prefix // that matches or is equal to keyOrPrefix. key, _, ok := r.providers.LongestPrefix(keyOrPrefix) if ok && (isPrefixKey(key) || key == keyOrPrefix) { return nil, fmt.Errorf("%w: found provider on %s", ErrKeyTaken, key) } // if keyOrPrefix is a prefix there must not be any provider // registered for a key that matches keyOrPrefix. if isPrefixKey(keyOrPrefix) { foundProvider := "" r.providers.WalkPrefix(keyOrPrefix, func(s string, _ interface{}) bool { foundProvider = s return true }) if foundProvider != "" { return nil, fmt.Errorf("%w: found provider on %s", ErrKeyTaken, foundProvider) } } r.providers.Insert(keyOrPrefix, &keyedValueProvider{ ValueProvider: TraceProvider(p), key: keyOrPrefix, }) log.Tracef("runtime: registered new provider at %s", keyOrPrefix) return func(records ...record.Record) { r.l.RLock() defer r.l.RUnlock() if r.dbController == nil { return } for _, rec := range records { r.dbController.PushUpdate(rec) } }, nil } // Get returns the runtime value that is identified by key. // It implements the storage.Interface. func (r *Registry) Get(key string) (record.Record, error) { provider := r.getMatchingProvider(key) if provider == nil { return nil, database.ErrNotFound } records, err := provider.Get(key) if err != nil { // instead of returning ErrWriteOnly to the database interface // we wrap it in ErrNotFound so the records effectively gets // hidden. if errors.Is(err, ErrWriteOnly) { return nil, database.ErrNotFound } return nil, err } // Get performs an exact match so filter out // and values that do not match key. for _, r := range records { if r.DatabaseKey() == key { return r, nil } } return nil, database.ErrNotFound } // Put stores the record m in the runtime database. Note that // ErrReadOnly is returned if there's no value provider responsible // for m.Key(). func (r *Registry) Put(m record.Record) (record.Record, error) { provider := r.getMatchingProvider(m.DatabaseKey()) if provider == nil { // if there's no provider for the given value // return ErrKeyUnmanaged. return nil, ErrKeyUnmanaged } res, err := provider.Set(m) if err != nil { return nil, err } return res, nil } // Query performs a query on the runtime registry returning all // records across all value providers that match q. // Query implements the storage.Storage interface. func (r *Registry) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { if _, err := q.Check(); err != nil { return nil, fmt.Errorf("invalid query: %w", err) } searchPrefix := q.DatabaseKeyPrefix() providers := r.collectProviderByPrefix(searchPrefix) if len(providers) == 0 { return nil, fmt.Errorf("%w: for key %s", ErrKeyUnmanaged, searchPrefix) } iter := iterator.New() grp := new(errgroup.Group) for idx := range providers { p := providers[idx] grp.Go(func() (err error) { defer recovery(&err) key := p.key if len(searchPrefix) > len(key) { key = searchPrefix } records, err := p.Get(key) if err != nil { if errors.Is(err, ErrWriteOnly) { return nil } return err } for _, r := range records { r.Lock() var ( matchesKey = q.MatchesKey(r.DatabaseKey()) isValid = r.Meta().CheckValidity() isAllowed = r.Meta().CheckPermission(local, internal) allowed = matchesKey && isValid && isAllowed ) if allowed { allowed = q.MatchesRecord(r) } r.Unlock() if !allowed { log.Tracef("runtime: not sending %s for query %s. matchesKey=%v isValid=%v isAllowed=%v", r.DatabaseKey(), searchPrefix, matchesKey, isValid, isAllowed) continue } select { case iter.Next <- r: case <-iter.Done: return nil } } return nil }) } go func() { err := grp.Wait() iter.Finish(err) }() return iter, nil } func (r *Registry) getMatchingProvider(key string) *keyedValueProvider { r.l.RLock() defer r.l.RUnlock() providerKey, provider, ok := r.providers.LongestPrefix(key) if !ok { return nil } if !isPrefixKey(providerKey) && providerKey != key { return nil } return provider.(*keyedValueProvider) //nolint:forcetypeassert } func (r *Registry) collectProviderByPrefix(prefix string) []*keyedValueProvider { r.l.RLock() defer r.l.RUnlock() // if there's a LongestPrefix provider that's the only one // we need to ask if _, p, ok := r.providers.LongestPrefix(prefix); ok { return []*keyedValueProvider{p.(*keyedValueProvider)} //nolint:forcetypeassert } var providers []*keyedValueProvider r.providers.WalkPrefix(prefix, func(key string, p interface{}) bool { providers = append(providers, p.(*keyedValueProvider)) //nolint:forcetypeassert return false }) return providers } // GetRegistrationKeys returns a list of all provider registration // keys or prefixes. func (r *Registry) GetRegistrationKeys() []string { r.l.RLock() defer r.l.RUnlock() var keys []string r.providers.Walk(func(key string, p interface{}) bool { keys = append(keys, key) return false }) return keys } // asStorage returns a storage.Interface compatible struct // that is backed by r. func (r *Registry) asStorage() storage.Interface { return &storageWrapper{ Registry: r, } } func recovery(err *error) { if x := recover(); x != nil { if e, ok := x.(error); ok { *err = e return } *err = fmt.Errorf("%v", x) } } ================================================ FILE: base/runtime/registry_test.go ================================================ package runtime import ( "errors" "sync" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" ) type testRecord struct { record.Base sync.Mutex Value string } func makeTestRecord(key, value string) record.Record { r := &testRecord{Value: value} r.CreateMeta() r.SetKey("runtime:" + key) return r } type testProvider struct { k string r []record.Record } func (tp *testProvider) Get(key string) ([]record.Record, error) { return tp.r, nil } func (tp *testProvider) Set(r record.Record) (record.Record, error) { return nil, errors.New("not implemented") } func getTestRegistry(t *testing.T) *Registry { t.Helper() r := NewRegistry() providers := []testProvider{ { k: "p1/", r: []record.Record{ makeTestRecord("p1/f1/v1", "p1.1"), makeTestRecord("p1/f2/v2", "p1.2"), makeTestRecord("p1/v3", "p1.3"), }, }, { k: "p2/f1", r: []record.Record{ makeTestRecord("p2/f1/v1", "p2.1"), makeTestRecord("p2/f1/f2/v2", "p2.2"), makeTestRecord("p2/f1/v3", "p2.3"), }, }, } for idx := range providers { p := providers[idx] _, err := r.Register(p.k, &p) require.NoError(t, err) } return r } func TestRegistryGet(t *testing.T) { t.Parallel() var ( r record.Record err error ) reg := getTestRegistry(t) r, err = reg.Get("p1/f1/v1") require.NoError(t, err) require.NotNil(t, r) assert.Equal(t, "p1.1", r.(*testRecord).Value) //nolint:forcetypeassert r, err = reg.Get("p1/v3") require.NoError(t, err) require.NotNil(t, r) assert.Equal(t, "p1.3", r.(*testRecord).Value) //nolint:forcetypeassert r, err = reg.Get("p1/v4") require.Error(t, err) assert.Nil(t, r) r, err = reg.Get("no-provider/foo") require.Error(t, err) assert.Nil(t, r) } func TestRegistryQuery(t *testing.T) { t.Parallel() reg := getTestRegistry(t) q := query.New("runtime:p") iter, err := reg.Query(q, true, true) require.NoError(t, err) require.NotNil(t, iter) var records []record.Record //nolint:prealloc for r := range iter.Next { records = append(records, r) } assert.Len(t, records, 6) q = query.New("runtime:p1/f") iter, err = reg.Query(q, true, true) require.NoError(t, err) require.NotNil(t, iter) records = nil for r := range iter.Next { records = append(records, r) } assert.Len(t, records, 2) } func TestRegistryRegister(t *testing.T) { t.Parallel() r := NewRegistry() cases := []struct { inp string err bool }{ {"runtime:foo/bar/bar", false}, {"runtime:foo/bar/bar2", false}, {"runtime:foo/bar", false}, {"runtime:foo/bar", true}, // already used {"runtime:foo/bar/", true}, // cannot register a prefix if there are providers below {"runtime:foo/baz/", false}, {"runtime:foo/baz2/", false}, {"runtime:foo/baz3", false}, {"runtime:foo/baz/bar", true}, } for _, c := range cases { _, err := r.Register(c.inp, nil) if c.err { assert.Error(t, err, c.inp) } else { assert.NoError(t, err, c.inp) } } } ================================================ FILE: base/runtime/singe_record_provider.go ================================================ package runtime import "github.com/safing/portmaster/base/database/record" // singleRecordReader is a convenience type for read-only exposing // a single record.Record. Note that users must lock the whole record // themself before performing any manipulation on the record. type singleRecordReader struct { record.Record } // ProvideRecord returns a ValueProvider the exposes read-only // access to r. Users of ProvideRecord need to ensure the lock // the whole record before performing modifications on it. // // Example: // // type MyValue struct { // record.Base // Value string // } // r := new(MyValue) // pushUpdate, _ := runtime.Register("my/key", ProvideRecord(r)) // r.Lock() // r.Value = "foobar" // pushUpdate(r) // r.Unlock() func ProvideRecord(r record.Record) ValueProvider { return &singleRecordReader{r} } // Set implements ValueProvider.Set and returns ErrReadOnly. func (sr *singleRecordReader) Set(_ record.Record) (record.Record, error) { return nil, ErrReadOnly } // Get implements ValueProvider.Get and returns the wrapped record.Record // but only if keyOrPrefix exactly matches the records database key. func (sr *singleRecordReader) Get(keyOrPrefix string) ([]record.Record, error) { if keyOrPrefix != sr.Record.DatabaseKey() { return nil, nil } return []record.Record{sr.Record}, nil } ================================================ FILE: base/runtime/storage.go ================================================ package runtime import ( "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" ) // storageWrapper is a simple wrapper around storage.InjectBase and // Registry and make sure the supported methods are handled by // the registry rather than the InjectBase defaults. // storageWrapper is mainly there to keep the method landscape of // Registry as small as possible. type storageWrapper struct { storage.InjectBase Registry *Registry } func (sw *storageWrapper) Get(key string) (record.Record, error) { return sw.Registry.Get(key) } func (sw *storageWrapper) Put(r record.Record) (record.Record, error) { return sw.Registry.Put(r) } func (sw *storageWrapper) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { return sw.Registry.Query(q, local, internal) } func (sw *storageWrapper) ReadOnly() bool { return false } ================================================ FILE: base/runtime/trace_provider.go ================================================ package runtime import ( "time" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" ) // traceValueProvider can be used to wrap an // existing value provider to trace an calls to // their Set and Get methods. type traceValueProvider struct { ValueProvider } // TraceProvider returns a new ValueProvider that wraps // vp but traces all Set and Get methods calls. func TraceProvider(vp ValueProvider) ValueProvider { return &traceValueProvider{vp} } func (tvp *traceValueProvider) Set(r record.Record) (res record.Record, err error) { defer func(start time.Time) { log.Tracef("runtime: setting record %q: duration=%s err=%v", r.Key(), time.Since(start), err) }(time.Now()) return tvp.ValueProvider.Set(r) } func (tvp *traceValueProvider) Get(keyOrPrefix string) (records []record.Record, err error) { defer func(start time.Time) { log.Tracef("runtime: loading records %q: duration=%s err=%v #records=%d", keyOrPrefix, time.Since(start), err, len(records)) }(time.Now()) return tvp.ValueProvider.Get(keyOrPrefix) } ================================================ FILE: base/template/module.go ================================================ package template import ( "time" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/mgr" ) // Template showcases the usage of the module system. type Template struct { i instance m *mgr.Manager states *mgr.StateMgr EventRecordAdded *mgr.EventMgr[string] EventRecordDeleted *mgr.EventMgr[string] specialWorkerMgr *mgr.WorkerMgr } type instance interface{} // New returns a new template. func New(instance instance) (*Template, error) { m := mgr.New("template") t := &Template{ i: instance, m: m, states: m.NewStateMgr(), EventRecordAdded: mgr.NewEventMgr[string]("record added", m), EventRecordDeleted: mgr.NewEventMgr[string]("record deleted", m), specialWorkerMgr: m.NewWorkerMgr("special worker", serviceWorker, nil), } // register options err := config.Register(&config.Option{ Name: "language", Key: "template/language", Description: "Sets the language for the template [TEMPLATE]", OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelUser, // default ReleaseLevel: config.ReleaseLevelStable, // default RequiresRestart: false, // default DefaultValue: "en", ValidationRegex: "^[a-z]{2}$", }) if err != nil { return nil, err } return t, nil } // Manager returns the module manager. func (t *Template) Manager() *mgr.Manager { return t.m } // States returns the module states. func (t *Template) States() *mgr.StateMgr { return t.states } // Start starts the module. func (t *Template) Start() error { t.m.Go("worker", serviceWorker) t.specialWorkerMgr.Delay(10 * time.Minute) return nil } // Stop stops the module. func Stop() error { return nil } func serviceWorker(w *mgr.WorkerCtx) error { for { select { case <-time.After(1 * time.Second): err := do() if err != nil { return err } case <-w.Done(): return nil } } } func do() error { return nil } ================================================ FILE: base/utils/atomic.go ================================================ package utils import ( "errors" "fmt" "io" "io/fs" "os" "github.com/safing/portmaster/base/utils/renameio" ) // AtomicFileOptions holds additional options for manipulating // the behavior of CreateAtomic and friends. type AtomicFileOptions struct { // Mode is the file mode for the new file. If // 0, the file mode will be set to 0600. Mode os.FileMode // TempDir is the path to the temp-directory // that should be used. If empty, it defaults // to the system temp. TempDir string } // CreateAtomic creates or overwrites a file at dest atomically using // data from r. Atomic means that even in case of a power outage, // dest will never be a zero-length file. It will always either contain // the previous data (or not exist) or the new data but never anything // in between. func CreateAtomic(dest string, r io.Reader, opts *AtomicFileOptions) error { if opts == nil { opts = &AtomicFileOptions{} } tmpFile, err := renameio.TempFile(opts.TempDir, dest) if err != nil { return fmt.Errorf("failed to create temp file: %w", err) } defer tmpFile.Cleanup() //nolint:errcheck if opts.Mode != 0 { if err := tmpFile.Chmod(opts.Mode); err != nil { return fmt.Errorf("failed to update mode bits of temp file: %w", err) } } if _, err := io.Copy(tmpFile, r); err != nil { return fmt.Errorf("failed to copy source file: %w", err) } if err := tmpFile.CloseAtomicallyReplace(); err != nil { return fmt.Errorf("failed to rename temp file to %q", dest) } return nil } // CopyFileAtomic is like CreateAtomic but copies content from // src to dest. If opts.Mode is 0 CopyFileAtomic tries to set // the file mode of src to dest. func CopyFileAtomic(dest string, src string, opts *AtomicFileOptions) error { if opts == nil { opts = &AtomicFileOptions{} } if opts.Mode == 0 { stat, err := os.Stat(src) if err != nil { return err } opts.Mode = stat.Mode() } f, err := os.Open(src) if err != nil { return err } defer func() { _ = f.Close() }() return CreateAtomic(dest, f, opts) } // ReplaceFileAtomic replaces the file at dest with the content from src. // If dest exists it's file mode copied and used for the replacement. If // not, dest will get the same file mode as src. See CopyFileAtomic and // CreateAtomic for more information. func ReplaceFileAtomic(dest string, src string, opts *AtomicFileOptions) error { if opts == nil { opts = &AtomicFileOptions{} } if opts.Mode == 0 { stat, err := os.Stat(dest) if err == nil { opts.Mode = stat.Mode() } else if !errors.Is(err, fs.ErrNotExist) { return err } } return CopyFileAtomic(dest, src, opts) } ================================================ FILE: base/utils/broadcastflag.go ================================================ package utils import ( "sync" "github.com/tevino/abool" ) // BroadcastFlag is a simple system to broadcast a flag value. type BroadcastFlag struct { flag *abool.AtomicBool signal chan struct{} lock sync.Mutex } // Flag receives changes from its broadcasting flag. // A Flag must only be used in one goroutine and is not concurrency safe, // but fast. type Flag struct { flag *abool.AtomicBool signal chan struct{} broadcaster *BroadcastFlag } // NewBroadcastFlag returns a new BroadcastFlag. // In the initial state, the flag is not set and the signal does not trigger. func NewBroadcastFlag() *BroadcastFlag { return &BroadcastFlag{ flag: abool.New(), signal: make(chan struct{}), lock: sync.Mutex{}, } } // NewFlag returns a new Flag that listens to this broadcasting flag. // In the initial state, the flag is set and the signal triggers. // You can call Refresh immediately to get the current state from the // broadcasting flag. func (bf *BroadcastFlag) NewFlag() *Flag { newFlag := &Flag{ flag: abool.NewBool(true), signal: make(chan struct{}), broadcaster: bf, } close(newFlag.signal) return newFlag } // NotifyAndReset notifies all flags of this broadcasting flag and resets the // internal broadcast flag state. func (bf *BroadcastFlag) NotifyAndReset() { bf.lock.Lock() defer bf.lock.Unlock() // Notify all flags of the change. bf.flag.Set() close(bf.signal) // Reset bf.flag = abool.New() bf.signal = make(chan struct{}) } // Signal returns a channel that waits for the flag to be set. This does not // reset the Flag itself, you'll need to call Refresh for that. func (f *Flag) Signal() <-chan struct{} { return f.signal } // IsSet returns whether the flag was set since the last Refresh. // This does not reset the Flag itself, you'll need to call Refresh for that. func (f *Flag) IsSet() bool { return f.flag.IsSet() } // Refresh fetches the current state from the broadcasting flag. func (f *Flag) Refresh() { f.broadcaster.lock.Lock() defer f.broadcaster.lock.Unlock() // Copy current flag and signal from the broadcasting flag. f.flag = f.broadcaster.flag f.signal = f.broadcaster.signal } ================================================ FILE: base/utils/call_limiter.go ================================================ package utils import ( "sync" "sync/atomic" "time" ) // CallLimiter bundles concurrent calls and optionally limits how fast a function is called. type CallLimiter struct { pause time.Duration inLock sync.Mutex lastExec time.Time waiters atomic.Int32 outLock sync.Mutex } // NewCallLimiter returns a new call limiter. // Set minPause to zero to disable the minimum pause between calls. func NewCallLimiter(minPause time.Duration) *CallLimiter { return &CallLimiter{ pause: minPause, } } // Do executes the given function. // All concurrent calls to Do are bundled and return when f() finishes. // Waits until the minimum pause is over before executing f() again. func (l *CallLimiter) Do(f func()) { // Wait for the previous waiters to exit. l.inLock.Lock() // Defer final unlock to safeguard from panics. defer func() { // Execution is finished - leave. // If we are the last waiter, let the next batch in. if l.waiters.Add(-1) == 0 { l.inLock.Unlock() } }() // Check if we are the first waiter. if l.waiters.Add(1) == 1 { // Take the lead on this execution run. l.lead(f) } else { // We are not the first waiter, let others in. l.inLock.Unlock() } // Wait for execution to complete. l.outLock.Lock() l.outLock.Unlock() //nolint:staticcheck // Last statement is in defer above. } func (l *CallLimiter) lead(f func()) { // Make all others wait while we execute the function. l.outLock.Lock() // Unlock in lock until execution is finished. l.inLock.Unlock() // Transition from out lock to in lock when done. defer func() { // Update last execution time. l.lastExec = time.Now().UTC() // Stop newcomers from waiting on previous execution. l.inLock.Lock() // Allow waiters to leave. l.outLock.Unlock() }() // Wait for the minimum duration between executions. if l.pause > 0 { sinceLastExec := time.Since(l.lastExec) if sinceLastExec < l.pause { time.Sleep(l.pause - sinceLastExec) } } // Execute. f() } ================================================ FILE: base/utils/call_limiter2.go ================================================ package utils import ( "sync" "sync/atomic" "time" ) // CallLimiter2 bundles concurrent calls and optionally limits how fast a function is called. type CallLimiter2 struct { pause time.Duration slot atomic.Int64 slotWait sync.RWMutex executing atomic.Bool lastExec time.Time } // NewCallLimiter2 returns a new call limiter. // Set minPause to zero to disable the minimum pause between calls. func NewCallLimiter2(minPause time.Duration) *CallLimiter2 { return &CallLimiter2{ pause: minPause, } } // Do executes the given function. // All concurrent calls to Do are bundled and return when f() finishes. // Waits until the minimum pause is over before executing f() again. func (l *CallLimiter2) Do(f func()) { // Get ticket number. slot := l.slot.Load() // Check if we can execute. if l.executing.CompareAndSwap(false, true) { // Make others wait. l.slotWait.Lock() defer l.slotWait.Unlock() // Execute and return. l.waitAndExec(f) return } // Wait for slot to end and check if slot is done. for l.slot.Load() == slot { time.Sleep(100 * time.Microsecond) l.slotWait.RLock() l.slotWait.RUnlock() //nolint:staticcheck } } func (l *CallLimiter2) waitAndExec(f func()) { defer func() { // Update last exec time. l.lastExec = time.Now().UTC() // Enable next execution first. l.executing.Store(false) // Move to next slot aftewards to prevent wait loops. l.slot.Add(1) }() // Wait for the minimum duration between executions. if l.pause > 0 { sinceLastExec := time.Since(l.lastExec) if sinceLastExec < l.pause { time.Sleep(l.pause - sinceLastExec) } } // Execute. f() } ================================================ FILE: base/utils/call_limiter_test.go ================================================ package utils import ( "sync" "sync/atomic" "testing" "time" "github.com/tevino/abool" ) func TestCallLimiter(t *testing.T) { t.Parallel() pause := 10 * time.Millisecond oa := NewCallLimiter2(pause) executed := abool.New() var testWg sync.WaitGroup // One execution should gobble up the whole batch. // We are doing this without sleep in function, so dummy exec first to trigger first pause. oa.Do(func() {}) // Start for range 10 { testWg.Add(100) for range 100 { go func() { oa.Do(func() { if !executed.SetToIf(false, true) { t.Errorf("concurrent execution!") } }) testWg.Done() }() } testWg.Wait() // Check if function was executed at least once. if executed.IsNotSet() { t.Errorf("no execution!") } executed.UnSet() // reset check } // Wait for 2x pause to reset. time.Sleep(2 * pause) // Continuous use with re-execution. // Choose values so that about 10 executions are expected var execs uint32 testWg.Add(100) for range 100 { go func() { oa.Do(func() { atomic.AddUint32(&execs, 1) time.Sleep(10 * time.Millisecond) }) testWg.Done() }() // Start one goroutine every 1ms. time.Sleep(1 * time.Millisecond) } testWg.Wait() if execs <= 5 { t.Errorf("unexpected low exec count: %d", execs) } if execs >= 15 { t.Errorf("unexpected high exec count: %d", execs) } // Wait for 2x pause to reset. time.Sleep(2 * pause) // Check if the limiter correctly handles panics. testWg.Add(100) for range 100 { go func() { defer func() { _ = recover() testWg.Done() }() oa.Do(func() { time.Sleep(1 * time.Millisecond) panic("test") }) }() time.Sleep(100 * time.Microsecond) } testWg.Wait() } ================================================ FILE: base/utils/debug/debug.go ================================================ package debug import ( "bytes" "fmt" "runtime/pprof" "time" "github.com/safing/portmaster/base/info" "github.com/safing/portmaster/base/log" ) // Info gathers debugging information and stores everything in a buffer in // order to write it to somewhere later. It directly inherits a bytes.Buffer, // so you can also use all these functions too. type Info struct { bytes.Buffer Style string } // InfoFlag defines possible options for adding sections to a Info. type InfoFlag int const ( // NoFlags does nothing. NoFlags InfoFlag = 0 // UseCodeSection wraps the section content in a markdown code section. UseCodeSection InfoFlag = 1 // AddContentLineBreaks adds a line breaks after each line of content, // except for the last. AddContentLineBreaks InfoFlag = 2 ) func useCodeSection(flags InfoFlag) bool { return flags&UseCodeSection > 0 } func addContentLineBreaks(flags InfoFlag) bool { return flags&AddContentLineBreaks > 0 } // AddSection adds a debug section to the Info. The result is directly // written into the buffer. func (di *Info) AddSection(name string, flags InfoFlag, content ...string) { // Check if we need a spacer. if di.Len() > 0 { _, _ = di.WriteString("\n\n") } // Write section to buffer. // Write section header. if di.Style == "github" { _, _ = di.WriteString(fmt.Sprintf("
\n%s\n\n", name)) } else { _, _ = di.WriteString(fmt.Sprintf("**%s**:\n\n", name)) } // Write section content. if useCodeSection(flags) { // Write code header: Needs one empty line between previous data. _, _ = di.WriteString("```\n") } for i, part := range content { _, _ = di.WriteString(part) if addContentLineBreaks(flags) && i < len(content)-1 { _, _ = di.WriteString("\n") } } if useCodeSection(flags) { // Write code footer: Needs one empty line between next data. _, _ = di.WriteString("\n```\n") } // Write section header. if di.Style == "github" { _, _ = di.WriteString("\n
") } } // AddVersionInfo adds version information from the info pkg. func (di *Info) AddVersionInfo() { di.AddSection( "Version "+info.Version(), UseCodeSection, info.FullVersion(), ) } // AddGoroutineStack adds the current goroutine stack. func (di *Info) AddGoroutineStack() { buf := new(bytes.Buffer) err := pprof.Lookup("goroutine").WriteTo(buf, 1) if err != nil { di.AddSection( "Goroutine Stack", NoFlags, fmt.Sprintf("Failed to get: %s", err), ) return } // Add section. di.AddSection( "Goroutine Stack", UseCodeSection, buf.String(), ) } // AddLastUnexpectedLogs adds the last 10 unexpected log lines, if any. func (di *Info) AddLastUnexpectedLogs() { lines := log.GetLastUnexpectedLogs() // Check if there is anything at all. if len(lines) == 0 { di.AddSection("No Unexpected Logs", NoFlags) return } di.AddSection( "Unexpected Logs", UseCodeSection|AddContentLineBreaks, append( lines, fmt.Sprintf("%s CURRENT TIME", time.Now().Format("060102 15:04:05.000")), )..., ) } ================================================ FILE: base/utils/debug/debug_android.go ================================================ package debug // TODO: Re-enable Android interfaces. // Deactived for transition to new module system. // import ( // "context" // "fmt" // "github.com/safing/portmaster-android/go/app_interface" // ) // // AddPlatformInfo adds OS and platform information. // func (di *Info) AddPlatformInfo(_ context.Context) { // // Get information from the system. // info, err := app_interface.GetPlatformInfo() // if err != nil { // di.AddSection( // "Platform Information", // NoFlags, // fmt.Sprintf("Failed to get: %s", err), // ) // return // } // // Add section. // di.AddSection( // fmt.Sprintf("Platform: Android"), // UseCodeSection|AddContentLineBreaks, // fmt.Sprintf("SDK: %d", info.SDK), // fmt.Sprintf("Device: %s %s (%s)", info.Manufacturer, info.Brand, info.Board), // fmt.Sprintf("App: %s: %s %s", info.ApplicationID, info.VersionName, info.BuildType)) // } ================================================ FILE: base/utils/debug/debug_default.go ================================================ //go:build !android package debug import ( "context" "fmt" "github.com/shirou/gopsutil/host" ) // AddPlatformInfo adds OS and platform information. func (di *Info) AddPlatformInfo(ctx context.Context) { // Get information from the system. info, err := host.InfoWithContext(ctx) if err != nil { di.AddSection( "Platform Information", NoFlags, fmt.Sprintf("Failed to get: %s", err), ) return } // Check if we want to add virtulization information. var virtInfo string if info.VirtualizationRole == "guest" { if info.VirtualizationSystem != "" { virtInfo = fmt.Sprintf("VM: %s", info.VirtualizationSystem) } else { virtInfo = "VM: unidentified" } } // Add section. di.AddSection( fmt.Sprintf("Platform: %s %s", info.Platform, info.PlatformVersion), UseCodeSection|AddContentLineBreaks, fmt.Sprintf("System: %s %s (%s) %s", info.Platform, info.OS, info.PlatformFamily, info.PlatformVersion), fmt.Sprintf("Kernel: %s %s", info.KernelVersion, info.KernelArch), virtInfo, ) } ================================================ FILE: base/utils/fs.go ================================================ package utils import ( "errors" "fmt" "io/fs" "log/slog" "os" "runtime" ) const isWindows = runtime.GOOS == "windows" // EnsureDirectory ensures that the given directory exists and that is has the given permissions set. // If path is a file, it is deleted and a directory created. func EnsureDirectory(path string, perm FSPermission) error { if !perm.IsExecPermission() { slog.Warn("utils: setting not executable permission for directory", "dir", path) } // open path f, err := os.Stat(path) if err == nil { // file exists if f.IsDir() { // directory exists, check permissions if isWindows { // Ignore windows permission error. For none admin users it will always fail. _ = SetFilePermission(path, perm) return nil } else if f.Mode().Perm() != perm.AsUnixPermission() { return SetFilePermission(path, perm) } return nil } err = os.Remove(path) if err != nil { return fmt.Errorf("could not remove file %s to place dir: %w", path, err) } } // file does not exist (or has been deleted) if err == nil || errors.Is(err, fs.ErrNotExist) { err = os.MkdirAll(path, perm.AsUnixPermission()) if err != nil { return fmt.Errorf("could not create dir %s: %w", path, err) } // Set permissions. err = SetFilePermission(path, perm) // Ignore windows permission error. For none admin users it will always fail. if !isWindows { return err } return nil } // other error opening path return fmt.Errorf("failed to access %s: %w", path, err) } // PathExists returns whether the given path (file or dir) exists. func PathExists(path string) bool { _, err := os.Stat(path) return err == nil || errors.Is(err, fs.ErrExist) } ================================================ FILE: base/utils/mimetypes.go ================================================ package utils import "strings" // Do not depend on the OS for mimetypes. // A Windows update screwed us over here and broke all the automatic mime // typing via Go in April 2021. // MimeTypeByExtension returns a mimetype for the given file name extension, // which must including the leading dot. // If the extension is not known, the call returns with ok=false and, // additionally, a default "application/octet-stream" mime type is returned. func MimeTypeByExtension(ext string) (mimeType string, ok bool) { mimeType, ok = mimeTypes[strings.ToLower(ext)] if ok { return } return defaultMimeType, false } var ( defaultMimeType = "application/octet-stream" mimeTypes = map[string]string{ ".7z": "application/x-7z-compressed", ".atom": "application/atom+xml", ".css": "text/css; charset=utf-8", ".csv": "text/csv; charset=utf-8", ".deb": "application/x-debian-package", ".epub": "application/epub+zip", ".es": "application/ecmascript", ".flv": "video/x-flv", ".gif": "image/gif", ".gz": "application/gzip", ".htm": "text/html; charset=utf-8", ".html": "text/html; charset=utf-8", ".jpeg": "image/jpeg", ".jpg": "image/jpeg", ".js": "text/javascript; charset=utf-8", ".json": "application/json; charset=utf-8", ".m3u": "audio/mpegurl", ".m4a": "audio/mpeg", ".md": "text/markdown; charset=utf-8", ".mjs": "text/javascript; charset=utf-8", ".mov": "video/quicktime", ".mp3": "audio/mpeg", ".mp4": "video/mp4", ".mpeg": "video/mpeg", ".mpg": "video/mpeg", ".ogg": "audio/ogg", ".ogv": "video/ogg", ".otf": "font/otf", ".pdf": "application/pdf", ".png": "image/png", ".qt": "video/quicktime", ".rar": "application/rar", ".rtf": "application/rtf", ".svg": "image/svg+xml", ".tar": "application/x-tar", ".tiff": "image/tiff", ".ts": "video/MP2T", ".ttc": "font/collection", ".ttf": "font/ttf", ".txt": "text/plain; charset=utf-8", ".wasm": "application/wasm", ".wav": "audio/x-wav", ".webm": "video/webm", ".webp": "image/webp", ".woff": "font/woff", ".woff2": "font/woff2", ".xml": "text/xml; charset=utf-8", ".xz": "application/x-xz", ".yaml": "application/yaml; charset=utf-8", ".yml": "application/yaml; charset=utf-8", ".zip": "application/zip", } ) ================================================ FILE: base/utils/onceagain.go ================================================ package utils // This file is forked from https://github.com/golang/go/blob/bc593eac2dc63d979a575eccb16c7369a5ff81e0/src/sync/once.go. import ( "sync" "sync/atomic" ) // OnceAgain is an object that will perform only one action "in flight". It's // basically the same as sync.Once, but is automatically reused when the // function was executed and everyone who waited has left. // Important: This is somewhat racy when used heavily as it only resets _after_ // everyone who waited has left. So, while some goroutines are waiting to be // activated again to leave the waiting state, other goroutines will call Do() // without executing the function again. type OnceAgain struct { // done indicates whether the action has been performed. // It is first in the struct because it is used in the hot path. // The hot path is inlined at every call site. // Placing done first allows more compact instructions on some architectures (amd64/x86), // and fewer instructions (to calculate offset) on other architectures. done uint32 // Number of waiters waiting for the function to finish. The last waiter resets done. waiters int32 m sync.Mutex } // Do calls the function f if and only if Do is being called for the // first time for this instance of Once. In other words, given // // var once Once // // if once.Do(f) is called multiple times, only the first call will invoke f, // even if f has a different value in each invocation. A new instance of // Once is required for each function to execute. // // Do is intended for initialization that must be run exactly once. Since f // is niladic, it may be necessary to use a function literal to capture the // arguments to a function to be invoked by Do: // // config.once.Do(func() { config.init(filename) }) // // Because no call to Do returns until the one call to f returns, if f causes // Do to be called, it will deadlock. // // If f panics, Do considers it to have returned; future calls of Do return // without calling f. func (o *OnceAgain) Do(f func()) { // Note: Here is an incorrect implementation of Do: // // if atomic.CompareAndSwapUint32(&o.done, 0, 1) { // f() // } // // Do guarantees that when it returns, f has finished. // This implementation would not implement that guarantee: // given two simultaneous calls, the winner of the cas would // call f, and the second would return immediately, without // waiting for the first's call to f to complete. // This is why the slow path falls back to a mutex, and why // the atomic.StoreUint32 must be delayed until after f returns. if atomic.LoadUint32(&o.done) == 0 { // Outlined slow-path to allow inlining of the fast-path. o.doSlow(f) } } func (o *OnceAgain) doSlow(f func()) { atomic.AddInt32(&o.waiters, 1) defer func() { if atomic.AddInt32(&o.waiters, -1) == 0 { atomic.StoreUint32(&o.done, 0) // reset } }() o.m.Lock() defer o.m.Unlock() if o.done == 0 { defer atomic.StoreUint32(&o.done, 1) f() } } ================================================ FILE: base/utils/onceagain_test.go ================================================ package utils import ( "sync" "sync/atomic" "testing" "time" "github.com/tevino/abool" ) func TestOnceAgain(t *testing.T) { t.Parallel() oa := OnceAgain{} executed := abool.New() var testWg sync.WaitGroup // One execution should gobble up the whole batch. for range 10 { testWg.Add(100) for range 100 { go func() { oa.Do(func() { if !executed.SetToIf(false, true) { t.Errorf("concurrent execution!") } time.Sleep(10 * time.Millisecond) }) testWg.Done() }() } testWg.Wait() executed.UnSet() // reset check } // Continuous use with re-execution. // Choose values so that about 10 executions are expected var execs uint32 testWg.Add(100) for range 100 { go func() { oa.Do(func() { atomic.AddUint32(&execs, 1) time.Sleep(10 * time.Millisecond) }) testWg.Done() }() time.Sleep(1 * time.Millisecond) } testWg.Wait() if execs <= 8 { t.Errorf("unexpected low exec count: %d", execs) } if execs >= 12 { t.Errorf("unexpected high exec count: %d", execs) } } ================================================ FILE: base/utils/osdetail/colors_windows.go ================================================ package osdetail import ( "sync" "golang.org/x/sys/windows" ) var ( colorSupport bool colorSupportChecked bool checkingColorSupport sync.Mutex ) // EnableColorSupport tries to enable color support for cmd on windows and returns whether it is enabled. func EnableColorSupport() bool { checkingColorSupport.Lock() defer checkingColorSupport.Unlock() if !colorSupportChecked { colorSupport = enableColorSupport() colorSupportChecked = true } return colorSupport } func enableColorSupport() bool { if IsAtLeastWindowsNTVersionWithDefault("10", false) { // check if windows.Stdout is file if windows.GetFileInformationByHandle(windows.Stdout, &windows.ByHandleFileInformation{}) == nil { return false } var mode uint32 err := windows.GetConsoleMode(windows.Stdout, &mode) if err == nil { if mode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 { mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING err = windows.SetConsoleMode(windows.Stdout, mode) if err != nil { return false } } return true } } return false } ================================================ FILE: base/utils/osdetail/command.go ================================================ package osdetail import ( "bytes" "errors" "os/exec" "strings" ) // RunCmd runs the given command and run error checks on the output. func RunCmd(command ...string) (output []byte, err error) { // Create command to execute. var cmd *exec.Cmd switch len(command) { case 0: return nil, errors.New("no command supplied") case 1: cmd = exec.Command(command[0]) default: cmd = exec.Command(command[0], command[1:]...) } // Create and assign output buffers. var stdoutBuf bytes.Buffer var stderrBuf bytes.Buffer cmd.Stdout = &stdoutBuf cmd.Stderr = &stderrBuf // Run command and collect output. err = cmd.Run() stdout, stderr := stdoutBuf.Bytes(), stderrBuf.Bytes() if err != nil { return nil, err } // Command might not return an error, but just write to stdout instead. if len(stderr) > 0 { return nil, errors.New(strings.SplitN(string(stderr), "\n", 2)[0]) } // Debugging output: // fmt.Printf("command stdout: %s\n", stdout) // fmt.Printf("command stderr: %s\n", stderr) // Finalize stdout. cleanedOutput := bytes.TrimSpace(stdout) if len(cleanedOutput) == 0 { return nil, ErrEmptyOutput } return cleanedOutput, nil } ================================================ FILE: base/utils/osdetail/dnscache_windows.go ================================================ package osdetail import ( "os/exec" ) // EnableDNSCache enables the Windows Service "DNS Client" by setting the registry value "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Dnscache" to 2 (Automatic). // A reboot is required for this setting to take effect. func EnableDNSCache() error { return exec.Command("reg", "add", "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\Dnscache", "/v", "Start", "/t", "REG_DWORD", "/d", "2", "/f").Run() } // DisableDNSCache disables the Windows Service "DNS Client" by setting the registry value "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Dnscache" to 4 (Disabled). // A reboot is required for this setting to take effect. func DisableDNSCache() error { return exec.Command("reg", "add", "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\Dnscache", "/v", "Start", "/t", "REG_DWORD", "/d", "4", "/f").Run() } ================================================ FILE: base/utils/osdetail/errors.go ================================================ package osdetail import "errors" var ( // ErrNotSupported is returned when an operation is not supported on the current platform. ErrNotSupported = errors.New("not supported") // ErrNotFound is returned when the desired data is not found. ErrNotFound = errors.New("not found") // ErrEmptyOutput is a special error that is returned when an operation has no error, but also returns to data. ErrEmptyOutput = errors.New("command succeeded with empty output") ) ================================================ FILE: base/utils/osdetail/service_windows.go ================================================ package osdetail import ( "errors" "fmt" "os/exec" "strings" "time" ) // Service Status const ( StatusUnknown uint8 = iota StatusRunningStoppable StatusRunningNotStoppable StatusStartPending StatusStopPending StatusStopped ) // Exported errors var ( ErrServiceNotStoppable = errors.New("the service is not stoppable") ) // GetServiceStatus returns the current status of a Windows Service (limited implementation). func GetServiceStatus(name string) (status uint8, err error) { output, err := exec.Command("sc", "query", name).Output() if err != nil { return StatusUnknown, fmt.Errorf("failed to query service: %s", err) } outputString := string(output) switch { case strings.Contains(outputString, "RUNNING"): if strings.Contains(outputString, "NOT_STOPPABLE") { return StatusRunningNotStoppable, nil } return StatusRunningStoppable, nil case strings.Contains(outputString, "STOP_PENDING"): return StatusStopPending, nil case strings.Contains(outputString, "STOPPED"): return StatusStopped, nil case strings.Contains(outputString, "START_PENDING"): return StatusStopPending, nil } return StatusUnknown, errors.New("unknown service status") } // StopService stops a Windows Service. func StopService(name string) (err error) { pendingCnt := 0 for { // get status status, err := GetServiceStatus(name) if err != nil { return err } switch status { case StatusRunningStoppable: err := exec.Command("sc", "stop", name).Run() if err != nil { return fmt.Errorf("failed to stop service: %s", err) } case StatusRunningNotStoppable: return ErrServiceNotStoppable case StatusStartPending, StatusStopPending: pendingCnt++ if pendingCnt > 50 { return errors.New("service stuck in pending status (5s)") } case StatusStopped: return nil } time.Sleep(100 * time.Millisecond) } } // SartService starts a Windows Service. func SartService(name string) (err error) { pendingCnt := 0 for { // get status status, err := GetServiceStatus(name) if err != nil { return err } switch status { case StatusRunningStoppable, StatusRunningNotStoppable: return nil case StatusStartPending, StatusStopPending: pendingCnt++ if pendingCnt > 50 { return errors.New("service stuck in pending status (5s)") } case StatusStopped: err := exec.Command("sc", "start", name).Run() if err != nil { return fmt.Errorf("failed to stop service: %s", err) } } time.Sleep(100 * time.Millisecond) } } ================================================ FILE: base/utils/osdetail/shell_windows.go ================================================ package osdetail import ( "bytes" "errors" ) // RunPowershellCmd runs a powershell command and returns its output. func RunPowershellCmd(script string) (output []byte, err error) { // Create command to execute. return RunCmd( "powershell.exe", "-ExecutionPolicy", "Bypass", "-NoProfile", "-NonInteractive", "[System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8\n"+script, ) } const outputSeparator = "pwzzhtuvpwdgozhzbnjj" // RunTerminalCmd runs a Windows cmd command and returns its output. // It sets the output of the cmd to UTF-8 in order to avoid encoding errors. func RunTerminalCmd(command ...string) (output []byte, err error) { output, err = RunCmd(append([]string{ "cmd.exe", "/c", "chcp", // Set output encoding... "65001", // ...to UTF-8. "&", "echo", outputSeparator, "&", }, command..., )...) if err != nil { return nil, err } // Find correct start of output and shift start. index := bytes.IndexAny(output, outputSeparator+"\r\n") if index < 0 { return nil, errors.New("failed to post-process output: could not find output separator") } output = output[index+len(outputSeparator)+2:] return output, nil } ================================================ FILE: base/utils/osdetail/svchost_windows.go ================================================ package osdetail import ( "bufio" "bytes" "errors" "fmt" "os/exec" "strconv" "strings" "sync" ) var ( serviceNames map[int32][]string serviceNamesLock sync.Mutex ) // Errors var ( ErrServiceNotFound = errors.New("no service with the given PID was found") ) // GetServiceNames returns all service names assosicated with a svchost.exe process on Windows. func GetServiceNames(pid int32) ([]string, error) { serviceNamesLock.Lock() defer serviceNamesLock.Unlock() if serviceNames != nil { names, ok := serviceNames[pid] if ok { return names, nil } } serviceNames, err := GetAllServiceNames() if err != nil { return nil, err } names, ok := serviceNames[pid] if ok { return names, nil } return nil, ErrServiceNotFound } // GetAllServiceNames returns a list of service names assosicated with svchost.exe processes on Windows. func GetAllServiceNames() (map[int32][]string, error) { output, err := exec.Command("tasklist", "/svc", "/fi", "imagename eq svchost.exe").Output() if err != nil { return nil, fmt.Errorf("failed to get svchost tasklist: %s", err) } // file scanner scanner := bufio.NewScanner(bytes.NewReader(output)) scanner.Split(bufio.ScanLines) // skip output header for scanner.Scan() { if strings.HasPrefix(scanner.Text(), "=") { break } } var ( pid int32 services []string collection = make(map[int32][]string) ) for scanner.Scan() { // get fields of line fields := strings.Fields(scanner.Text()) // check fields length if len(fields) == 0 { continue } // new entry if fields[0] == "svchost.exe" { // save old entry if pid != 0 { collection[pid] = services } // reset PID pid = 0 services = make([]string, 0, len(fields)) // check fields length if len(fields) < 3 { continue } // get pid i, err := strconv.ParseInt(fields[1], 10, 32) if err != nil { continue } pid = int32(i) // skip used fields fields = fields[2:] } // add service names for _, field := range fields { services = append(services, strings.Trim(strings.TrimSpace(field), ",")) } } if pid != 0 { // save last entry collection[pid] = services } return collection, nil } ================================================ FILE: base/utils/osdetail/version_windows.go ================================================ package osdetail import ( "fmt" "strings" "sync" "github.com/hashicorp/go-version" "github.com/shirou/gopsutil/host" ) var ( // versionRe = regexp.MustCompile(`[0-9\.]+`) windowsNTVersion string windowsNTVersionForCmp *version.Version fetching sync.Mutex fetched bool ) // WindowsNTVersion returns the current Windows version. func WindowsNTVersion() (string, error) { var err error fetching.Lock() defer fetching.Unlock() if !fetched { _, _, windowsNTVersion, err = host.PlatformInformation() windowsNTVersion = strings.SplitN(windowsNTVersion, " ", 2)[0] if err != nil { return "", fmt.Errorf("failed to obtain Windows-Version: %s", err) } windowsNTVersionForCmp, err = version.NewVersion(windowsNTVersion) if err != nil { return "", fmt.Errorf("failed to parse Windows-Version %s: %s", windowsNTVersion, err) } fetched = true } return windowsNTVersion, err } // IsAtLeastWindowsNTVersion returns whether the current WindowsNT version is at least the given version or newer. func IsAtLeastWindowsNTVersion(v string) (bool, error) { _, err := WindowsNTVersion() if err != nil { return false, err } versionForCmp, err := version.NewVersion(v) if err != nil { return false, err } return windowsNTVersionForCmp.GreaterThanOrEqual(versionForCmp), nil } // IsAtLeastWindowsNTVersionWithDefault is like IsAtLeastWindowsNTVersion(), but keeps the Error and returns the default Value in Errorcase func IsAtLeastWindowsNTVersionWithDefault(v string, defaultValue bool) bool { val, err := IsAtLeastWindowsNTVersion(v) if err != nil { return defaultValue } return val } // IsAtLeastWindowsVersion returns whether the current Windows version is at least the given version or newer. func IsAtLeastWindowsVersion(v string) (bool, error) { var NTVersion string switch v { case "7": NTVersion = "6.1" case "8": NTVersion = "6.2" case "8.1": NTVersion = "6.3" case "10": NTVersion = "10" default: return false, fmt.Errorf("failed to compare Windows-Version: Windows %s is unknown", v) } return IsAtLeastWindowsNTVersion(NTVersion) } // IsAtLeastWindowsVersionWithDefault is like IsAtLeastWindowsVersion(), but keeps the Error and returns the default Value in Errorcase func IsAtLeastWindowsVersionWithDefault(v string, defaultValue bool) bool { val, err := IsAtLeastWindowsVersion(v) if err != nil { return defaultValue } return val } ================================================ FILE: base/utils/osdetail/version_windows_test.go ================================================ package osdetail import "testing" func TestWindowsNTVersion(t *testing.T) { if str, err := WindowsNTVersion(); str == "" || err != nil { t.Fatalf("failed to obtain windows version: %s", err) } } func TestIsAtLeastWindowsNTVersion(t *testing.T) { ret, err := IsAtLeastWindowsNTVersion("6") if err != nil { t.Fatalf("failed to compare windows versions: %s", err) } if !ret { t.Fatalf("WindowsNTVersion is less than 6 (Vista)") } } func TestIsAtLeastWindowsVersion(t *testing.T) { ret, err := IsAtLeastWindowsVersion("7") if err != nil { t.Fatalf("failed to compare windows versions: %s", err) } if !ret { t.Fatalf("WindowsVersion is less than 7") } } ================================================ FILE: base/utils/permissions.go ================================================ //go:build !windows package utils import "os" // SetFilePermission sets the permission of a file or directory. func SetFilePermission(path string, perm FSPermission) error { return os.Chmod(path, perm.AsUnixPermission()) } ================================================ FILE: base/utils/permissions_windows.go ================================================ //go:build windows package utils import ( "github.com/hectane/go-acl" "golang.org/x/sys/windows" ) var ( systemSID *windows.SID adminsSID *windows.SID usersSID *windows.SID ) func init() { // Initialize Security ID for all need groups. // Reference: https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers var err error systemSID, err = windows.StringToSid("S-1-5-18") // SYSTEM (Local System) if err != nil { panic(err) } adminsSID, err = windows.StringToSid("S-1-5-32-544") // Administrators if err != nil { panic(err) } usersSID, err = windows.StringToSid("S-1-5-32-545") // Users if err != nil { panic(err) } } // SetFilePermission sets the permission of a file or directory. func SetFilePermission(path string, perm FSPermission) error { switch perm { case AdminOnlyPermission, AdminOnlyExecPermission: // Set only admin rights, remove all others. acl.Apply( path, true, false, acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, systemSID), acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, adminsSID), ) case PublicReadPermission, PublicReadExecPermission: // Set admin rights and read/execute rights for users, remove all others. acl.Apply( path, true, false, acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, systemSID), acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, adminsSID), acl.GrantSid(windows.GENERIC_READ|windows.GENERIC_EXECUTE, usersSID), ) case PublicWritePermission, PublicWriteExecPermission: // Set full control to admin and regular users. Guest users will not have access. acl.Apply( path, true, false, acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, systemSID), acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, adminsSID), acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, usersSID), ) } return nil } ================================================ FILE: base/utils/renameio/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: base/utils/renameio/README.md ================================================ This is a fork of the github.com/google/renameio Go package at commit 353f8196982447d8b12c64f69530e657331e3dbc. The inital commit of this package will carry the original package contents. The Original License is the Apache License in Version 2.0 and the copyright of the forked package is held by Google Inc. Any changes are recorded in the git history, which is part of this project. --- The `renameio` Go package provides a way to atomically create or replace a file or symbolic link. ## Atomicity vs durability `renameio` concerns itself *only* with atomicity, i.e. making sure applications never see unexpected file content (a half-written file, or a 0-byte file). As a practical example, consider https://manpages.debian.org/: if there is a power outage while the site is updating, we are okay with losing the manpages which were being rendered at the time of the power outage. They will be added in a later run of the software. We are not okay with having a manpage replaced by a 0-byte file under any circumstances, though. ## Advantages of this package There are other packages for atomically replacing files, and sometimes ad-hoc implementations can be found in programs. A naive approach to the problem is to create a temporary file followed by a call to `os.Rename()`. However, there are a number of subtleties which make the correct sequence of operations hard to identify: * The temporary file should be removed when an error occurs, but a remove must not be attempted if the rename succeeded, as a new file might have been created with the same name. This renders a throwaway `defer os.Remove(t.Name())` insufficient; state must be kept. * The temporary file must be created on the same file system (same mount point) for the rename to work, but the TMPDIR environment variable should still be respected, e.g. to direct temporary files into a separate directory outside of the webserver’s document root but on the same file system. * On POSIX operating systems, the [`fsync`](https://manpages.debian.org/stretch/manpages-dev/fsync.2) system call must be used to ensure that the `os.Rename()` call will not result in a 0-length file. This package attempts to get all of these details right, provides an intuitive, yet flexible API and caters to use-cases where high performance is required. ## Disclaimer This is not an official Google product (experimental or otherwise), it is just code that happens to be owned by Google. This project is not affiliated with the Go project. ================================================ FILE: base/utils/renameio/doc.go ================================================ // Package renameio provides a way to atomically create or replace a file or // symbolic link. // // Caveat: this package requires the file system rename(2) implementation to be // atomic. Notably, this is not the case when using NFS with multiple clients: // https://stackoverflow.com/a/41396801 package renameio ================================================ FILE: base/utils/renameio/example_test.go ================================================ package renameio_test import ( "fmt" "log" "github.com/safing/portmaster/base/utils/renameio" ) func ExampleTempFile_justone() { //nolint:testableexamples persist := func(temperature float64) error { t, err := renameio.TempFile("", "/srv/www/metrics.txt") if err != nil { return err } defer func() { _ = t.Cleanup() }() if _, err := fmt.Fprintf(t, "temperature_degc %f\n", temperature); err != nil { return err } return t.CloseAtomicallyReplace() } // Thanks to the write package, a webserver exposing /srv/www never // serves an incomplete or missing file. if err := persist(31.2); err != nil { log.Fatal(err) } } func ExampleTempFile_many() { //nolint:testableexamples // Prepare for writing files to /srv/www, effectively caching calls to // TempDir which TempFile would otherwise need to make. dir := renameio.TempDir("/srv/www") persist := func(temperature float64) error { t, err := renameio.TempFile(dir, "/srv/www/metrics.txt") if err != nil { return err } defer func() { _ = t.Cleanup() }() if _, err := fmt.Fprintf(t, "temperature_degc %f\n", temperature); err != nil { return err } return t.CloseAtomicallyReplace() } // Imagine this was an endless loop, reading temperature sensor values. // Thanks to the write package, a webserver exposing /srv/www never // serves an incomplete or missing file. for { if err := persist(31.2); err != nil { log.Fatal(err) } } } ================================================ FILE: base/utils/renameio/symlink_test.go ================================================ //go:build darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris || windows package renameio import ( "bytes" "os" "path/filepath" "testing" ) func TestSymlink(t *testing.T) { t.Parallel() d, err := os.MkdirTemp("", "test-renameio-testsymlink") if err != nil { t.Fatal(err) } t.Cleanup(func() { _ = os.RemoveAll(d) }) want := []byte("Hello World") if err := os.WriteFile(filepath.Join(d, "hello.txt"), want, 0o0600); err != nil { t.Fatal(err) } for range 2 { if err := Symlink("hello.txt", filepath.Join(d, "hi.txt")); err != nil { t.Fatal(err) } got, err := os.ReadFile(filepath.Join(d, "hi.txt")) if err != nil { t.Fatal(err) } if !bytes.Equal(got, want) { t.Fatalf("unexpected content: got %q, want %q", string(got), string(want)) } } } ================================================ FILE: base/utils/renameio/tempfile.go ================================================ package renameio import ( "errors" "io/fs" "os" "path/filepath" ) // TempDir checks whether os.TempDir() can be used as a temporary directory for // later atomically replacing files within dest. If no (os.TempDir() resides on // a different mount point), dest is returned. // // Note that the returned value ceases to be valid once either os.TempDir() // changes (e.g. on Linux, once the TMPDIR environment variable changes) or the // file system is unmounted. func TempDir(dest string) string { return tempDir("", filepath.Join(dest, "renameio-TempDir")) } func tempDir(dir, dest string) string { if dir != "" { return dir // caller-specified directory always wins } // Chose the destination directory as temporary directory so that we // definitely can rename the file, for which both temporary and destination // file need to point to the same mount point. fallback := filepath.Dir(dest) // The user might have overridden the os.TempDir() return value by setting // the TMPDIR environment variable. tmpdir := os.TempDir() testsrc, err := os.CreateTemp(tmpdir, "."+filepath.Base(dest)) if err != nil { return fallback } cleanup := true defer func() { if cleanup { _ = os.Remove(testsrc.Name()) } }() _ = testsrc.Close() testdest, err := os.CreateTemp(filepath.Dir(dest), "."+filepath.Base(dest)) if err != nil { return fallback } defer func() { _ = os.Remove(testdest.Name()) }() _ = testdest.Close() if err := os.Rename(testsrc.Name(), testdest.Name()); err != nil { return fallback } cleanup = false // testsrc no longer exists return tmpdir } // PendingFile is a pending temporary file, waiting to replace the destination // path in a call to CloseAtomicallyReplace. type PendingFile struct { *os.File path string done bool closed bool } // Cleanup is a no-op if CloseAtomicallyReplace succeeded, and otherwise closes // and removes the temporary file. func (t *PendingFile) Cleanup() error { if t.done { return nil } // An error occurred. Close and remove the tempfile. Errors are returned for // reporting, there is nothing the caller can recover here. var closeErr error if !t.closed { closeErr = t.Close() } if err := os.Remove(t.Name()); err != nil { return err } return closeErr } // CloseAtomicallyReplace closes the temporary file and atomically replaces // the destination file with it, i.e., a concurrent open(2) call will either // open the file previously located at the destination path (if any), or the // just written file, but the file will always be present. func (t *PendingFile) CloseAtomicallyReplace() error { // Even on an ordered file system (e.g. ext4 with data=ordered) or file // systems with write barriers, we cannot skip the fsync(2) call as per // Theodore Ts'o (ext2/3/4 lead developer): // // > data=ordered only guarantees the avoidance of stale data (e.g., the previous // > contents of a data block showing up after a crash, where the previous data // > could be someone's love letters, medical records, etc.). Without the fsync(2) // > a zero-length file is a valid and possible outcome after the rename. if err := t.Sync(); err != nil { return err } t.closed = true if err := t.Close(); err != nil { return err } if err := os.Rename(t.Name(), t.path); err != nil { return err } t.done = true return nil } // TempFile wraps os.CreateTemp for the use case of atomically creating or // replacing the destination file at path. // // If dir is the empty string, TempDir(filepath.Base(path)) is used. If you are // going to write a large number of files to the same file system, store the // result of TempDir(filepath.Base(path)) and pass it instead of the empty // string. // // The file's permissions will be 0600 by default. You can change these by // explicitly calling Chmod on the returned PendingFile. func TempFile(dir, path string) (*PendingFile, error) { f, err := os.CreateTemp(tempDir(dir, path), "."+filepath.Base(path)) if err != nil { return nil, err } return &PendingFile{File: f, path: path}, nil } // Symlink wraps os.Symlink, replacing an existing symlink with the same name // atomically (os.Symlink fails when newname already exists, at least on Linux). func Symlink(oldname, newname string) error { // Fast path: if newname does not exist yet, we can skip the whole dance // below. if err := os.Symlink(oldname, newname); err == nil || !errors.Is(err, fs.ErrExist) { return err } // We need to use os.MkdirTemp, as we cannot overwrite a os.CreateTemp, // and removing+symlinking creates a TOCTOU race. d, err := os.MkdirTemp(filepath.Dir(newname), "."+filepath.Base(newname)) if err != nil { return err } cleanup := true defer func() { if cleanup { _ = os.RemoveAll(d) } }() symlink := filepath.Join(d, "tmp.symlink") if err := os.Symlink(oldname, symlink); err != nil { return err } if err := os.Rename(symlink, newname); err != nil { return err } cleanup = false return os.RemoveAll(d) } ================================================ FILE: base/utils/renameio/tempfile_linux_test.go ================================================ //go:build linux package renameio import ( "os" "path/filepath" "syscall" "testing" ) func TestTempDir(t *testing.T) { t.Parallel() if tmpdir, ok := os.LookupEnv("TMPDIR"); ok { t.Cleanup(func() { _ = os.Setenv("TMPDIR", tmpdir) // restore }) } else { t.Cleanup(func() { _ = os.Unsetenv("TMPDIR") // restore }) } mount1, err := os.MkdirTemp("", "test-renameio-testtempdir1") if err != nil { t.Fatal(err) } t.Cleanup(func() { _ = os.RemoveAll(mount1) }) mount2, err := os.MkdirTemp("", "test-renameio-testtempdir2") if err != nil { t.Fatal(err) } t.Cleanup(func() { _ = os.RemoveAll(mount2) }) if err := syscall.Mount("tmpfs", mount1, "tmpfs", 0, ""); err != nil { t.Skipf("cannot mount tmpfs on %s: %v", mount1, err) } t.Cleanup(func() { _ = syscall.Unmount(mount1, 0) }) if err := syscall.Mount("tmpfs", mount2, "tmpfs", 0, ""); err != nil { t.Skipf("cannot mount tmpfs on %s: %v", mount2, err) } t.Cleanup(func() { _ = syscall.Unmount(mount2, 0) }) tests := []struct { name string dir string path string TMPDIR string want string }{ { name: "implicit TMPDIR", path: filepath.Join(os.TempDir(), "foo.txt"), want: os.TempDir(), }, { name: "explicit TMPDIR", path: filepath.Join(mount1, "foo.txt"), TMPDIR: mount1, want: mount1, }, { name: "explicit unsuitable TMPDIR", path: filepath.Join(mount1, "foo.txt"), TMPDIR: mount2, want: mount1, }, { name: "nonexistant TMPDIR", path: filepath.Join(mount1, "foo.txt"), TMPDIR: "/nonexistant", want: mount1, }, { name: "caller-specified", dir: "/overridden", path: filepath.Join(mount1, "foo.txt"), TMPDIR: "/nonexistant", want: "/overridden", }, } for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { t.Parallel() if testCase.TMPDIR == "" { _ = os.Unsetenv("TMPDIR") } else { _ = os.Setenv("TMPDIR", testCase.TMPDIR) } if got := tempDir(testCase.dir, testCase.path); got != testCase.want { t.Fatalf("tempDir(%q, %q): got %q, want %q", testCase.dir, testCase.path, got, testCase.want) } }) } } ================================================ FILE: base/utils/renameio/writefile.go ================================================ package renameio import ( "os" "runtime" "github.com/hectane/go-acl" ) // WriteFile mirrors os.WriteFile, replacing an existing file with the same // name atomically. func WriteFile(filename string, data []byte, perm os.FileMode) error { t, err := TempFile("", filename) if err != nil { return err } defer func() { _ = t.Cleanup() }() // Set permissions before writing data, in case the data is sensitive. if runtime.GOOS == "windows" { err = acl.Chmod(t.path, perm) } else { err = t.Chmod(perm) } if err != nil { return err } if _, err := t.Write(data); err != nil { return err } return t.CloseAtomicallyReplace() } ================================================ FILE: base/utils/renameio/writefile_test.go ================================================ //go:build darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris || windows package renameio import ( "bytes" "os" "path/filepath" "testing" ) func TestWriteFile(t *testing.T) { t.Parallel() d, err := os.MkdirTemp("", "test-renameio-testwritefile") if err != nil { t.Fatal(err) } defer func() { _ = os.RemoveAll(d) }() filename := filepath.Join(d, "hello.sh") wantData := []byte("#!/bin/sh\necho \"Hello World\"\n") wantPerm := os.FileMode(0o0600) if err := WriteFile(filename, wantData, wantPerm); err != nil { t.Fatal(err) } gotData, err := os.ReadFile(filename) if err != nil { t.Fatal(err) } if !bytes.Equal(gotData, wantData) { t.Errorf("got data %v, want data %v", gotData, wantData) } fi, err := os.Stat(filename) if err != nil { t.Fatal(err) } if gotPerm := fi.Mode() & os.ModePerm; gotPerm != wantPerm { t.Errorf("got permissions 0%o, want permissions 0%o", gotPerm, wantPerm) } } ================================================ FILE: base/utils/safe.go ================================================ package utils import ( "encoding/hex" "strings" ) // SafeFirst16Bytes return the first 16 bytes of the given data in safe form. func SafeFirst16Bytes(data []byte) string { if len(data) == 0 { return "" } return strings.TrimPrefix( strings.SplitN(hex.Dump(data), "\n", 2)[0], "00000000 ", ) } // SafeFirst16Chars return the first 16 characters of the given data in safe form. func SafeFirst16Chars(s string) string { return SafeFirst16Bytes([]byte(s)) } ================================================ FILE: base/utils/safe_test.go ================================================ package utils import ( "testing" "github.com/stretchr/testify/assert" ) func TestSafeFirst16(t *testing.T) { t.Parallel() assert.Equal(t, "47 6f 20 69 73 20 61 6e 20 6f 70 65 6e 20 73 6f |Go is an open so|", SafeFirst16Bytes([]byte("Go is an open source programming language.")), ) assert.Equal(t, "47 6f 20 69 73 20 61 6e 20 6f 70 65 6e 20 73 6f |Go is an open so|", SafeFirst16Chars("Go is an open source programming language."), ) assert.Equal(t, "", SafeFirst16Bytes(nil), ) assert.Equal(t, "", SafeFirst16Chars(""), ) } ================================================ FILE: base/utils/slices.go ================================================ package utils // IndexOfString returns the index of given string and -1 if its not part of the slice. func IndexOfString(a []string, s string) int { for i, entry := range a { if entry == s { return i } } return -1 } // StringInSlice returns whether the given string is in the string slice. func StringInSlice(a []string, s string) bool { return IndexOfString(a, s) >= 0 } // RemoveFromStringSlice removes the given string from the slice and returns a new slice. func RemoveFromStringSlice(a []string, s string) []string { i := IndexOfString(a, s) if i > 0 { a = append(a[:i], a[i+1:]...) } return a } // DuplicateStrings returns a new copy of the given string slice. func DuplicateStrings(a []string) []string { b := make([]string, len(a)) copy(b, a) return b } // StringSliceEqual returns whether the given string slices are equal. func StringSliceEqual(a []string, b []string) bool { if len(a) != len(b) { return false } for i, v := range a { if v != b[i] { return false } } return true } // DuplicateBytes returns a new copy of the given byte slice. func DuplicateBytes(a []byte) []byte { b := make([]byte, len(a)) copy(b, a) return b } ================================================ FILE: base/utils/slices_test.go ================================================ package utils import ( "bytes" "testing" ) var ( stringTestSlice = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"} stringTestSlice2 = []string{"a", "x", "x", "x", "x", "x", "x", "x", "x", "j"} stringTestSlice3 = []string{"a", "x"} byteTestSlice = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ) func TestStringInSlice(t *testing.T) { t.Parallel() if !StringInSlice(stringTestSlice, "a") { t.Fatal("string reported not in slice (1), but it is") } if !StringInSlice(stringTestSlice, "d") { t.Fatal("string reported not in slice (2), but it is") } if !StringInSlice(stringTestSlice, "j") { t.Fatal("string reported not in slice (3), but it is") } if StringInSlice(stringTestSlice, "0") { t.Fatal("string reported in slice (1), but is not") } if StringInSlice(stringTestSlice, "x") { t.Fatal("string reported in slice (2), but is not") } if StringInSlice(stringTestSlice, "k") { t.Fatal("string reported in slice (3), but is not") } } func TestRemoveFromStringSlice(t *testing.T) { t.Parallel() test1 := DuplicateStrings(stringTestSlice) test1 = RemoveFromStringSlice(test1, "b") if StringInSlice(test1, "b") { t.Fatal("string reported in slice, but was removed") } if len(test1) != len(stringTestSlice)-1 { t.Fatalf("new string slice length not as expected: is %d, should be %d\nnew slice is %v", len(test1), len(stringTestSlice)-1, test1) } RemoveFromStringSlice(test1, "b") } func TestDuplicateStrings(t *testing.T) { t.Parallel() a := DuplicateStrings(stringTestSlice) if !StringSliceEqual(a, stringTestSlice) { t.Fatal("copied string slice is not equal") } a[0] = "x" if StringSliceEqual(a, stringTestSlice) { t.Fatal("copied string slice is not a real copy") } } func TestStringSliceEqual(t *testing.T) { t.Parallel() if !StringSliceEqual(stringTestSlice, stringTestSlice) { t.Fatal("strings are equal, but are reported as not") } if StringSliceEqual(stringTestSlice, stringTestSlice2) { t.Fatal("strings are not equal (1), but are reported as equal") } if StringSliceEqual(stringTestSlice, stringTestSlice3) { t.Fatal("strings are not equal (1), but are reported as equal") } } func TestDuplicateBytes(t *testing.T) { t.Parallel() a := DuplicateBytes(byteTestSlice) if !bytes.Equal(a, byteTestSlice) { t.Fatal("copied bytes slice is not equal") } a[0] = 0xff if bytes.Equal(a, byteTestSlice) { t.Fatal("copied bytes slice is not a real copy") } } ================================================ FILE: base/utils/stablepool.go ================================================ package utils import "sync" // A StablePool is a drop-in replacement for sync.Pool that is slower, but // predictable. // A StablePool is a set of temporary objects that may be individually saved and // retrieved. // // In contrast to sync.Pool, items are not removed automatically. Every item // will be returned at some point. Items are returned in a FIFO manner in order // to evenly distribute usage of a set of items. // // A StablePool is safe for use by multiple goroutines simultaneously and must // not be copied after first use. type StablePool struct { lock sync.Mutex pool []interface{} cnt int getIndex int putIndex int // New optionally specifies a function to generate // a value when Get would otherwise return nil. // It may not be changed concurrently with calls to Get. New func() interface{} } // Put adds x to the pool. func (p *StablePool) Put(x interface{}) { if x == nil { return } p.lock.Lock() defer p.lock.Unlock() // check if pool is full (or unitialized) if p.cnt == len(p.pool) { p.pool = append(p.pool, x) p.cnt++ p.putIndex = p.cnt return } // correct putIndex p.putIndex %= len(p.pool) // iterate the whole pool once to find a free spot stopAt := p.putIndex - 1 for i := p.putIndex; i != stopAt; i = (i + 1) % len(p.pool) { if p.pool[i] == nil { p.pool[i] = x p.cnt++ p.putIndex = i + 1 return } } } // Get returns the next item from the Pool, removes it from the Pool, and // returns it to the caller. // In contrast to sync.Pool, Get never ignores the pool. // Callers should not assume any relation between values passed to Put and // the values returned by Get. // // If Get would otherwise return nil and p.New is non-nil, Get returns // the result of calling p.New. func (p *StablePool) Get() interface{} { p.lock.Lock() defer p.lock.Unlock() // check if pool is empty if p.cnt == 0 { if p.New != nil { return p.New() } return nil } // correct getIndex p.getIndex %= len(p.pool) // iterate the whole pool to find an item stopAt := p.getIndex - 1 for i := p.getIndex; i != stopAt; i = (i + 1) % len(p.pool) { if p.pool[i] != nil { x := p.pool[i] p.pool[i] = nil p.cnt-- p.getIndex = i + 1 return x } } // if we ever get here, return a new item if p.New != nil { return p.New() } return nil } // Size returns the amount of items the pool currently holds. func (p *StablePool) Size() int { p.lock.Lock() defer p.lock.Unlock() return p.cnt } // Max returns the amount of items the pool held at maximum. func (p *StablePool) Max() int { p.lock.Lock() defer p.lock.Unlock() return len(p.pool) } ================================================ FILE: base/utils/stablepool_test.go ================================================ package utils import ( "fmt" "sync" "testing" "time" "github.com/stretchr/testify/assert" ) func TestStablePoolRealWorld(t *testing.T) { t.Parallel() // "real world" simulation cnt := 0 testPool := &StablePool{ New: func() interface{} { cnt++ return cnt }, } var testWg sync.WaitGroup var testWorkerWg sync.WaitGroup // for i := 0; i < 100; i++ { // cnt++ // testPool.Put(cnt) // } for range 100 { // block round testWg.Add(1) // add workers testWorkerWg.Add(100) for j := range 100 { go func() { // wait for round to start testWg.Wait() // get value x := testPool.Get() // fmt.Println(x) // "work" time.Sleep(5 * time.Microsecond) // re-insert 99% if j%100 > 0 { testPool.Put(x) } // mark as finished testWorkerWg.Done() }() } // start round testWg.Done() // wait for round to finish testWorkerWg.Wait() } t.Logf("real world simulation: cnt=%d p.cnt=%d p.max=%d\n", cnt, testPool.Size(), testPool.Max()) assert.GreaterOrEqual(t, 200, cnt, "should not use more than 200 values") assert.GreaterOrEqual(t, 100, testPool.Max(), "pool should have at most this max size") // optimal usage test optPool := &StablePool{} for range 1000 { for j := range 100 { optPool.Put(j) } for k := range 100 { assert.Equal(t, k, optPool.Get(), "should match") } } assert.Equal(t, 100, optPool.Max(), "pool should have exactly this max size") } func TestStablePoolFuzzing(t *testing.T) { t.Parallel() // fuzzing test fuzzPool := &StablePool{} var fuzzWg sync.WaitGroup var fuzzWorkerWg sync.WaitGroup // start goroutines and wait fuzzWg.Add(1) for i := range 1000 { fuzzWorkerWg.Add(2) go func() { fuzzWg.Wait() fuzzPool.Put(i) fuzzWorkerWg.Done() }() go func() { fuzzWg.Wait() fmt.Print(fuzzPool.Get()) fuzzWorkerWg.Done() }() } // kick off fuzzWg.Done() // wait for all to finish fuzzWorkerWg.Wait() } func TestStablePoolBreaking(t *testing.T) { t.Parallel() // try to break it breakPool := &StablePool{} for range 10 { for j := range 100 { breakPool.Put(nil) breakPool.Put(j) breakPool.Put(nil) } for k := range 100 { assert.Equal(t, k, breakPool.Get(), "should match") } } } ================================================ FILE: base/utils/structure.go ================================================ package utils import ( "fmt" "io/fs" "path/filepath" "strings" "sync" ) type FSPermission uint8 const ( AdminOnlyPermission FSPermission = iota AdminOnlyExecPermission PublicReadPermission PublicReadExecPermission PublicWritePermission PublicWriteExecPermission ) // AsUnixDirExecPermission return the corresponding unix permission for a directory or executable. func (perm FSPermission) AsUnixPermission() fs.FileMode { switch perm { case AdminOnlyPermission: return 0o600 case AdminOnlyExecPermission: return 0o700 case PublicReadPermission: return 0o644 case PublicReadExecPermission: return 0o755 case PublicWritePermission: return 0o666 case PublicWriteExecPermission: return 0o777 } return 0 } func (perm FSPermission) IsExecPermission() bool { switch perm { case AdminOnlyExecPermission, PublicReadExecPermission, PublicWriteExecPermission: return true } return false } // DirStructure represents a directory structure with permissions that should be enforced. type DirStructure struct { sync.Mutex Path string Dir string Perm FSPermission Parent *DirStructure Children map[string]*DirStructure } // NewDirStructure returns a new DirStructure. func NewDirStructure(path string, perm FSPermission) *DirStructure { return &DirStructure{ Path: path, Perm: perm, Children: make(map[string]*DirStructure), } } // ChildDir adds a new child DirStructure and returns it. Should the child already exist, the existing child is returned and the permissions are updated. func (ds *DirStructure) ChildDir(dirName string, perm FSPermission) (child *DirStructure) { ds.Lock() defer ds.Unlock() // if exists, update child, ok := ds.Children[dirName] if ok { child.Perm = perm return child } // create new newDir := &DirStructure{ Path: filepath.Join(ds.Path, dirName), Dir: dirName, Perm: perm, Parent: ds, Children: make(map[string]*DirStructure), } ds.Children[dirName] = newDir return newDir } // Ensure ensures that the specified directory structure (from the first parent on) exists. func (ds *DirStructure) Ensure() error { return ds.EnsureAbsPath(ds.Path) } // EnsureRelPath ensures that the specified directory structure (from the first parent on) and the given relative path (to the DirStructure) exists. func (ds *DirStructure) EnsureRelPath(dirPath string) error { return ds.EnsureAbsPath(filepath.Join(ds.Path, dirPath)) } // EnsureRelDir ensures that the specified directory structure (from the first parent on) and the given relative path (to the DirStructure) exists. func (ds *DirStructure) EnsureRelDir(dirNames ...string) error { return ds.EnsureAbsPath(filepath.Join(append([]string{ds.Path}, dirNames...)...)) } // EnsureAbsPath ensures that the specified directory structure (from the first parent on) and the given absolute path exists. // If the given path is outside the DirStructure, an error will be returned. func (ds *DirStructure) EnsureAbsPath(dirPath string) error { // always start at the top if ds.Parent != nil { return ds.Parent.EnsureAbsPath(dirPath) } // check if root if dirPath == ds.Path { return ds.ensure(nil) } // check scope slashedPath := ds.Path // add slash to end if !strings.HasSuffix(slashedPath, string(filepath.Separator)) { slashedPath += string(filepath.Separator) } // check if given path is in scope if !strings.HasPrefix(dirPath, slashedPath) { return fmt.Errorf(`path "%s" is outside of DirStructure scope`, dirPath) } // get relative path relPath, err := filepath.Rel(ds.Path, dirPath) if err != nil { return fmt.Errorf("failed to get relative path: %w", err) } // split to path elements pathDirs := strings.Split(filepath.ToSlash(relPath), "/") // start checking return ds.ensure(pathDirs) } func (ds *DirStructure) ensure(pathDirs []string) error { ds.Lock() defer ds.Unlock() // check current dir err := EnsureDirectory(ds.Path, ds.Perm) if err != nil { return err } if len(pathDirs) == 0 { // we reached the end! return nil } child, ok := ds.Children[pathDirs[0]] if !ok { // we have reached the end of the defined dir structure // ensure all remaining dirs dirPath := ds.Path for _, dir := range pathDirs { dirPath = filepath.Join(dirPath, dir) err := EnsureDirectory(dirPath, ds.Perm) if err != nil { return err } } return nil } // we got a child, continue return child.ensure(pathDirs[1:]) } ================================================ FILE: base/utils/structure_test.go ================================================ //go:build !windows package utils import ( "fmt" "os" "path/filepath" "strings" ) func ExampleDirStructure() { // output: // / [755] // /repo [777] // /repo/b [755] // /repo/b/c [777] // /repo/b/d [755] // /repo/b/d/e [755] // /repo/b/d/f [755] // /repo/b/d/f/g [755] // /repo/b/d/f/g/h [755] // /secret [700] basePath, err := os.MkdirTemp("", "") if err != nil { fmt.Println(err) return } ds := NewDirStructure(basePath, PublicReadExecPermission) secret := ds.ChildDir("secret", AdminOnlyExecPermission) repo := ds.ChildDir("repo", PublicWriteExecPermission) _ = repo.ChildDir("a", AdminOnlyExecPermission) b := repo.ChildDir("b", PublicReadExecPermission) c := b.ChildDir("c", PublicWriteExecPermission) err = ds.Ensure() if err != nil { fmt.Println(err) } err = c.Ensure() if err != nil { fmt.Println(err) } err = secret.Ensure() if err != nil { fmt.Println(err) } err = b.EnsureRelDir("d", "e") if err != nil { fmt.Println(err) } err = b.EnsureRelPath("d/f/g/h") if err != nil { fmt.Println(err) } _ = filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error { if err == nil { dir := strings.TrimPrefix(path, basePath) if dir == "" { dir = "/" } fmt.Printf("%s [%o]\n", dir, info.Mode().Perm()) } return nil }) } ================================================ FILE: base/utils/uuid.go ================================================ package utils import ( "encoding/binary" "time" "github.com/gofrs/uuid" ) var ( constantUUID = uuid.Must(uuid.FromString("e8dba9f7-21e2-4c82-96cb-6586922c6422")) instanceUUID = RandomUUID("instance") ) // RandomUUID returns a new random UUID with optionally provided ns. func RandomUUID(ns string) uuid.UUID { randUUID, err := uuid.NewV4() switch { case err != nil: // fallback // should practically never happen return uuid.NewV5(uuidFromTime(), ns) case ns != "": // mix ns into the UUID return uuid.NewV5(randUUID, ns) default: return randUUID } } // DerivedUUID returns a new UUID that is derived from the input only, and therefore is always reproducible. func DerivedUUID(input string) uuid.UUID { return uuid.NewV5(constantUUID, input) } // DerivedInstanceUUID returns a new UUID that is derived from the input, but is unique per instance (execution) and therefore is only reproducible with the same process. func DerivedInstanceUUID(input string) uuid.UUID { return uuid.NewV5(instanceUUID, input) } func uuidFromTime() uuid.UUID { var timeUUID uuid.UUID binary.LittleEndian.PutUint64(timeUUID[:], uint64(time.Now().UnixNano())) return timeUUID } ================================================ FILE: base/utils/uuid_test.go ================================================ package utils import ( "testing" "time" "github.com/gofrs/uuid" ) func TestUUID(t *testing.T) { t.Parallel() // check randomness a := RandomUUID("") a2 := RandomUUID("") if a.String() == a2.String() { t.Error("should not match") } // check with input b := RandomUUID("b") b2 := RandomUUID("b") if b.String() == b2.String() { t.Error("should not match") } // check with long input c := RandomUUID("TG8UkxS+4rVrDxHtDAaNab1CBpygzmX1g5mJA37jbQ5q2uE4rVrDxHtDAaNab1CBpygzmX1g5mJA37jbQ5q2uE") c2 := RandomUUID("TG8UkxS+4rVrDxHtDAaNab1CBpygzmX1g5mJA37jbQ5q2uE4rVrDxHtDAaNab1CBpygzmX1g5mJA37jbQ5q2uE") if c.String() == c2.String() { t.Error("should not match") } // check for nanosecond precision d := uuidFromTime() time.Sleep(2 * time.Nanosecond) d2 := uuidFromTime() if d.String() == d2.String() { t.Error("should not match") } // check mixing timeUUID := uuidFromTime() e := uuid.NewV5(timeUUID, "e") e2 := uuid.NewV5(timeUUID, "e2") if e.String() == e2.String() { t.Error("should not match") } // check deriving f := DerivedUUID("f") f2 := DerivedUUID("f") f3 := DerivedUUID("f3") if f.String() != f2.String() { t.Error("should match") } if f.String() == f3.String() { t.Error("should not match") } // check instance deriving g := DerivedInstanceUUID("g") g2 := DerivedInstanceUUID("g") g3 := DerivedInstanceUUID("g3") if g.String() != g2.String() { t.Error("should match") } if g.String() == g3.String() { t.Error("should not match") } } ================================================ FILE: cmds/cmdbase/service.go ================================================ package cmdbase import ( "context" "errors" "fmt" "io" "log/slog" "os" "os/exec" "runtime" "runtime/pprof" "time" "github.com/spf13/cobra" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service" "github.com/safing/portmaster/service/mgr" ) var ( RebootOnRestart bool PrintStackOnExit bool ) type SystemService interface { Run() IsService() bool RestartService() error } type ServiceInstance interface { Ready() bool Start() error Stop() error Restart() Shutdown() Ctx() context.Context IsShuttingDown() bool ShuttingDown() <-chan struct{} ShutdownCtx() context.Context IsShutDown() bool ShutdownComplete() <-chan struct{} ExitCode() int ShouldRestartIsSet() bool CommandLineOperationIsSet() bool CommandLineOperationExecute() error } var ( SvcFactory func(*service.ServiceConfig) (ServiceInstance, error) SvcConfig *service.ServiceConfig ) func RunService(cmd *cobra.Command, args []string) { if SvcFactory == nil || SvcConfig == nil { fmt.Fprintln(os.Stderr, "internal error: service not set up in cmdbase") os.Exit(1) } // Start logging. // Note: Must be created before the service instance, so that they use the right logger. err := log.Start(SvcConfig.LogLevel, SvcConfig.LogToStdout, SvcConfig.LogDir) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(4) } // Create instance. // Instance modules might request a cmdline execution of a function. var execCmdLine bool instance, err := SvcFactory(SvcConfig) switch { case err == nil: // Continue case errors.Is(err, mgr.ErrExecuteCmdLineOp): execCmdLine = true default: fmt.Printf("error creating an instance: %s\n", err) os.Exit(2) } // Execute module command line operation, if requested or available. switch { case !execCmdLine: // Run service. case !instance.CommandLineOperationIsSet(): fmt.Println("command line operation execution requested, but not set") os.Exit(3) default: // Run the function and exit. fmt.Println("executing cmdline op") err = instance.CommandLineOperationExecute() if err != nil { fmt.Fprintf(os.Stderr, "command line operation failed: %s\n", err) os.Exit(3) } os.Exit(0) } // START // Create system service. service := NewSystemService(instance) // Start instance via system service manager. go func() { service.Run() }() // SHUTDOWN // Wait for shutdown to be started. <-instance.ShuttingDown() // Wait for shutdown to be finished. select { case <-instance.ShutdownComplete(): // Print stack on shutdown, if enabled. if PrintStackOnExit { printStackTo(log.GlobalWriter, "PRINTING STACK ON EXIT") } case <-time.After(3 * time.Minute): printStackTo(log.GlobalWriter, "PRINTING STACK - TAKING TOO LONG FOR SHUTDOWN") } // Check if restart was triggered and send start service command if true. if instance.ShouldRestartIsSet() && service.IsService() { // Check if we should reboot instead. var rebooting bool if RebootOnRestart { // Trigger system reboot and record success. rebooting = triggerSystemReboot() if !rebooting { log.Warningf("updates: rebooting failed, only restarting service instead") } } // Restart service if not rebooting. if !rebooting { if err := service.RestartService(); err != nil { slog.Error("failed to restart service", "err", err) } } } // Stop logging. log.Shutdown() // Give a small amount of time for everything to settle: // - All logs written. // - Restart command started, if needed. // - Windows service manager notified. time.Sleep(100 * time.Millisecond) // Exit os.Exit(instance.ExitCode()) } func printStackTo(writer io.Writer, msg string) { _, err := fmt.Fprintf(writer, "===== %s =====\n", msg) if err == nil { err = pprof.Lookup("goroutine").WriteTo(writer, 1) } if err != nil { slog.Error("failed to write stack trace", "err", err) } } func triggerSystemReboot() (success bool) { switch runtime.GOOS { case "linux": err := exec.Command("systemctl", "reboot").Run() if err != nil { log.Errorf("updates: triggering reboot with systemctl failed: %s", err) return false } default: log.Warningf("updates: rebooting is not support on %s", runtime.GOOS) return false } return true } ================================================ FILE: cmds/cmdbase/service_linux.go ================================================ package cmdbase import ( "fmt" "log/slog" "os" "os/exec" "os/signal" "syscall" processInfo "github.com/shirou/gopsutil/process" "github.com/safing/portmaster/base/log" ) type LinuxSystemService struct { instance ServiceInstance } func NewSystemService(instance ServiceInstance) *LinuxSystemService { return &LinuxSystemService{instance: instance} } func (s *LinuxSystemService) Run() { // Start instance. err := s.instance.Start() if err != nil { slog.Error("failed to start", "err", err) // Print stack on start failure, if enabled. if PrintStackOnExit { printStackTo(log.GlobalWriter, "PRINTING STACK ON START FAILURE") } os.Exit(1) } // Subscribe to signals. signalCh := make(chan os.Signal, 1) signal.Notify( signalCh, os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGUSR1, ) // Wait for shutdown signal. wait: for { select { case <-s.instance.ShuttingDown(): break wait case sig := <-signalCh: // Only print and continue to wait if SIGUSR1 if sig == syscall.SIGUSR1 { printStackTo(log.GlobalWriter, "PRINTING STACK ON REQUEST") continue wait } else { // Trigger shutdown. fmt.Printf(" \n", sig) // CLI output. slog.Warn("received stop signal", "signal", sig) s.instance.Shutdown() break wait } } } // Wait for shutdown to finish. // Catch signals during shutdown. // Force exit after 5 interrupts. forceCnt := 5 for { select { case <-s.instance.ShutdownComplete(): return case sig := <-signalCh: if sig != syscall.SIGUSR1 { forceCnt-- if forceCnt > 0 { fmt.Printf(" again, but already shutting down - %d more to force\n", sig, forceCnt) } else { printStackTo(log.GlobalWriter, "PRINTING STACK ON FORCED EXIT") os.Exit(1) } } } } } func (s *LinuxSystemService) RestartService() error { // Check if user defined custom command for restarting the service. restartCommand, exists := os.LookupEnv("PORTMASTER_RESTART_COMMAND") // Run the service restart var cmd *exec.Cmd if exists && restartCommand != "" { slog.Debug("running custom restart command", "command", restartCommand) cmd = exec.Command("sh", "-c", restartCommand) } else { cmd = exec.Command("systemctl", "restart", "portmaster") } if err := cmd.Start(); err != nil { return fmt.Errorf("failed run restart command: %w", err) } return nil } func (s *LinuxSystemService) IsService() bool { // Get own process ID pid := os.Getpid() // Get parent process ID. currentProcess, err := processInfo.NewProcess(int32(pid)) //nolint:gosec if err != nil { return false } ppid, err := currentProcess.Ppid() if err != nil { return false } // Check if the parent process ID is 1 == init system return ppid == 1 } ================================================ FILE: cmds/cmdbase/service_windows.go ================================================ package cmdbase // Based on the official Go examples from // https://github.com/golang/sys/blob/master/windows/svc/example // by The Go Authors. // Original LICENSE (sha256sum: 2d36597f7117c38b006835ae7f537487207d8ec407aa9d9980794b2030cbc067) can be found in vendor/pkg cache directory. import ( "fmt" "log/slog" "os" "os/exec" "os/signal" "syscall" "golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc/debug" "github.com/safing/portmaster/base/log" ) const serviceName = "PortmasterCore" type WindowsSystemService struct { instance ServiceInstance } func NewSystemService(instance ServiceInstance) *WindowsSystemService { return &WindowsSystemService{instance: instance} } func (s *WindowsSystemService) Run() { svcRun := svc.Run // Check if we are running interactively. isService, err := svc.IsWindowsService() switch { case err != nil: slog.Warn("failed to determine if running interactively", "err", err) slog.Warn("continuing without service integration (no real service)") svcRun = debug.Run case !isService: slog.Warn("running interactively, switching to debug execution (no real service)") svcRun = debug.Run } // Run service client. err = svcRun(serviceName, s) if err != nil { slog.Error("service execution failed", "err", err) os.Exit(1) } // Execution continues in s.Execute(). } func (s *WindowsSystemService) Execute(args []string, changeRequests <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { // Tell service manager we are starting. changes <- svc.Status{State: svc.StartPending} // Start instance. err := s.instance.Start() if err != nil { fmt.Printf("failed to start: %s\n", err) // Print stack on start failure, if enabled. if PrintStackOnExit { printStackTo(log.GlobalWriter, "PRINTING STACK ON START FAILURE") } // Notify service manager we stopped again. changes <- svc.Status{State: svc.Stopped} // Relay exit code to service manager. return false, 1 } // Tell service manager we are up and running! changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown} // Subscribe to signals. // Docs: https://pkg.go.dev/os/signal?GOOS=windows signalCh := make(chan os.Signal, 4) signal.Notify( signalCh, // Windows ^C (Control-C) or ^BREAK (Control-Break). // Completely prevents kill. os.Interrupt, // Windows CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT. // Does not prevent kill, but gives a little time to stop service. syscall.SIGTERM, ) isShuttingDown := false // Wait for shutdown signal. waitSignal: for { select { case sig := <-signalCh: // Trigger shutdown. fmt.Printf(" \n", sig) // CLI output. slog.Warn("received stop signal", "signal", sig) break waitSignal case c := <-changeRequests: switch c.Cmd { case svc.Interrogate: changes <- c.CurrentStatus case svc.Stop, svc.Shutdown: fmt.Printf(" \n", serviceCmdName(c.Cmd)) // CLI output. slog.Warn("received service shutdown command", "cmd", c.Cmd) break waitSignal default: slog.Error("unexpected service control request", "cmd", serviceCmdName(c.Cmd)) } case <-s.instance.ShuttingDown(): isShuttingDown = true break waitSignal } } // Trigger shutdown, // but only if we are not already shutting down. if !isShuttingDown { s.instance.Shutdown() } // Notify the service host that service is in shutting down state. changes <- svc.Status{State: svc.StopPending} // Wait for shutdown to finish. // Catch signals during shutdown. // Force exit after 5 interrupts. forceCnt := 5 waitShutdown: for { select { case <-s.instance.ShutdownComplete(): break waitShutdown case sig := <-signalCh: forceCnt-- if forceCnt > 0 { fmt.Printf(" but already shutting down - %d more to force\n", sig, forceCnt) } else { printStackTo(log.GlobalWriter, "PRINTING STACK ON FORCED EXIT") os.Exit(1) } case c := <-changeRequests: switch c.Cmd { case svc.Interrogate: changes <- c.CurrentStatus case svc.Stop, svc.Shutdown: forceCnt-- if forceCnt > 0 { fmt.Printf(" but already shutting down - %d more to force\n", serviceCmdName(c.Cmd), forceCnt) } else { printStackTo(log.GlobalWriter, "PRINTING STACK ON FORCED EXIT") os.Exit(1) } default: slog.Error("unexpected service control request", "cmd", serviceCmdName(c.Cmd)) } } } // Notify service manager. changes <- svc.Status{State: svc.Stopped} return false, 0 } func (s *WindowsSystemService) IsService() bool { isService, err := svc.IsWindowsService() if err != nil { return false } return isService } func (s *WindowsSystemService) RestartService() error { // Script that wait for portmaster service status to change to stop // and then sends a start command for the same service. command := ` $serviceName = "PortmasterCore" while ((Get-Service -Name $serviceName).Status -ne 'Stopped') { Start-Sleep -Seconds 1 } sc.exe start $serviceName` // Create the command to execute the PowerShell script cmd := exec.Command("powershell.exe", "-Command", command) // Start the command. The script will continue even after the parent process exits. err := cmd.Start() if err != nil { return err } return nil } func serviceCmdName(cmd svc.Cmd) string { switch cmd { case svc.Stop: return "Stop" case svc.Pause: return "Pause" case svc.Continue: return "Continue" case svc.Interrogate: return "Interrogate" case svc.Shutdown: return "Shutdown" case svc.ParamChange: return "ParamChange" case svc.NetBindAdd: return "NetBindAdd" case svc.NetBindRemove: return "NetBindRemove" case svc.NetBindEnable: return "NetBindEnable" case svc.NetBindDisable: return "NetBindDisable" case svc.DeviceEvent: return "DeviceEvent" case svc.HardwareProfileChange: return "HardwareProfileChange" case svc.PowerEvent: return "PowerEvent" case svc.SessionChange: return "SessionChange" case svc.PreShutdown: return "PreShutdown" default: return "Unknown Command" } } ================================================ FILE: cmds/cmdbase/update.go ================================================ package cmdbase import ( "fmt" "log/slog" "github.com/spf13/cobra" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service" "github.com/safing/portmaster/service/ui" "github.com/safing/portmaster/service/updates" ) var UpdateCmd = &cobra.Command{ Use: "update", Short: "Force an update of all components.", RunE: update, } func update(cmd *cobra.Command, args []string) error { // Finalize config. err := SvcConfig.Init() if err != nil { return fmt.Errorf("internal configuration error: %w", err) } // Force logging to stdout. SvcConfig.LogToStdout = true // Start logging. _ = log.Start(SvcConfig.LogLevel, SvcConfig.LogToStdout, SvcConfig.LogDir) defer log.Shutdown() // Create updaters. instance := &updateDummyInstance{} binaryUpdateConfig, intelUpdateConfig, err := service.MakeUpdateConfigs(SvcConfig) if err != nil { return fmt.Errorf("init updater config: %w", err) } binaryUpdates, err := updates.New(instance, "Binary Updater", *binaryUpdateConfig) if err != nil { return fmt.Errorf("configure binary updates: %w", err) } intelUpdates, err := updates.New(instance, "Intel Updater", *intelUpdateConfig) if err != nil { return fmt.Errorf("configure intel updates: %w", err) } // Force update all. binErr := binaryUpdates.ForceUpdate() if binErr != nil { slog.Error("binary update failed", "err", binErr) } intelErr := intelUpdates.ForceUpdate() if intelErr != nil { slog.Error("intel update failed", "err", intelErr) } // Return error. if binErr != nil { return fmt.Errorf("binary update failed: %w", binErr) } if intelErr != nil { return fmt.Errorf("intel update failed: %w", intelErr) } return nil } type updateDummyInstance struct{} func (udi *updateDummyInstance) Restart() {} func (udi *updateDummyInstance) Shutdown() {} func (udi *updateDummyInstance) Notifications() *notifications.Notifications { return nil } func (udi *updateDummyInstance) UI() *ui.UI { return nil } ================================================ FILE: cmds/cmdbase/version.go ================================================ package cmdbase import ( "fmt" "github.com/spf13/cobra" "github.com/safing/portmaster/base/info" ) var VersionCmd = &cobra.Command{ Use: "version", Short: "Show version and related metadata.", RunE: Version, } func Version(cmd *cobra.Command, args []string) error { fmt.Println(info.FullVersion()) return nil } ================================================ FILE: cmds/integrationtest/main.go ================================================ package main import ( "os" "github.com/spf13/cobra" ) var rootCmd = &cobra.Command{ Use: "integrationtest", Short: "A simple tool to test system integrations", } func main() { if err := rootCmd.Execute(); err != nil { os.Exit(1) } } ================================================ FILE: cmds/integrationtest/netstate.go ================================================ package main import ( "fmt" "time" processInfo "github.com/shirou/gopsutil/process" "github.com/spf13/cobra" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/network/socket" "github.com/safing/portmaster/service/network/state" ) func init() { rootCmd.AddCommand(netStateCmd) netStateCmd.AddCommand(netStateMonitorCmd) } var ( netStateCmd = &cobra.Command{ Use: "netstate", Short: "Print current network state as received from the system", RunE: netState, } netStateMonitorCmd = &cobra.Command{ Use: "monitor", Short: "Monitor the network state and print any new connections", RunE: netStateMonitor, } seen = make(map[string]bool) ) func netState(cmd *cobra.Command, args []string) error { tables := state.GetInfo() for _, s := range tables.TCP4Connections { checkAndPrintConnectionInfoIfNew(packet.IPv4, packet.TCP, s) } for _, s := range tables.TCP4Listeners { checkAndPrintBindInfoIfNew(packet.IPv4, packet.TCP, s) } for _, s := range tables.TCP6Connections { checkAndPrintConnectionInfoIfNew(packet.IPv6, packet.TCP, s) } for _, s := range tables.TCP6Listeners { checkAndPrintBindInfoIfNew(packet.IPv6, packet.TCP, s) } for _, s := range tables.UDP4Binds { checkAndPrintBindInfoIfNew(packet.IPv6, packet.UDP, s) } for _, s := range tables.UDP6Binds { checkAndPrintBindInfoIfNew(packet.IPv6, packet.UDP, s) } return nil } func netStateMonitor(cmd *cobra.Command, args []string) error { for { err := netState(cmd, args) if err != nil { return err } time.Sleep(10 * time.Millisecond) } } func checkAndPrintConnectionInfoIfNew(ipv packet.IPVersion, p packet.IPProtocol, s *socket.ConnectionInfo) { // Build connection string. c := fmt.Sprintf( "%s %s %s:%d <-> %s:%d", ipv, p, s.Local.IP, s.Local.Port, s.Remote.IP, s.Remote.Port, ) checkAndPrintSocketInfoIfNew(c, s) } func checkAndPrintBindInfoIfNew(ipv packet.IPVersion, p packet.IPProtocol, s *socket.BindInfo) { // Build connection string. c := fmt.Sprintf( "%s %s bind %s:%d", ipv, p, s.Local.IP, s.Local.Port, ) checkAndPrintSocketInfoIfNew(c, s) } func checkAndPrintSocketInfoIfNew(c string, s socket.Info) { // Return if connection was already seen. if _, ok := seen[c]; ok { return } // Otherwise, add as seen. seen[c] = true // Check if we have the PID. _, _, err := state.CheckPID(s, false) // Print result. if err == nil { pInfo, err := processInfo.NewProcess(int32(s.GetPID())) if err != nil { fmt.Printf("%s %d no binary: %s\n", c, s.GetPID(), err) } else { exe, _ := pInfo.Exe() fmt.Printf("%s %d %s\n", c, s.GetPID(), exe) } } else { fmt.Printf("%s %d (err: %s)\n", c, s.GetPID(), err) } } ================================================ FILE: cmds/trafficgen/main.go ================================================ package main import ( "flag" "fmt" "net/http" "os" "time" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" ) const dnsResolver = "1.1.1.1:53" var ( url string lookup string n int waitMsec int ) func init() { flag.StringVar(&url, "url", "", "send HTTP HEAD requests to this url") flag.StringVar(&lookup, "lookup", "", fmt.Sprintf("query %s for this domains", dnsResolver)) flag.IntVar(&n, "n", 10, "how many requests to make") flag.IntVar(&waitMsec, "w", 100, "how many ms to wait between requests") } func main() { // Parse flags flag.Parse() if url == "" && lookup == "" { flag.Usage() os.Exit(1) } // Start logging. err := log.Start("trace", true, "") if err != nil { fmt.Printf("failed to start logging: %s\n", err) os.Exit(1) } defer log.Shutdown() log.Info("starting traffic generator") // Execute requests waitDuration := time.Duration(waitMsec) * time.Millisecond for i := 1; i <= n; i++ { makeHTTPRequest(i) lookupDomain(i) time.Sleep(waitDuration) } } func makeHTTPRequest(i int) { if url == "" { return } // Create a new client so that the connection won't be shared with other requests. client := http.Client{ CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }, } start := time.Now() resp, err := client.Head(url) if err != nil { log.Errorf("http request #%d failed after %s: %s", i, time.Since(start).Round(time.Millisecond), err) return } defer func() { _ = resp.Body.Close() }() log.Infof("http response #%d after %s: %d", i, time.Since(start).Round(time.Millisecond), resp.StatusCode) } func lookupDomain(i int) { if lookup == "" { return } // Create DNS query. dnsQuery := new(dns.Msg) dnsQuery.SetQuestion(dns.Fqdn(lookup), dns.TypeA) // Send request. start := time.Now() reply, err := dns.Exchange(dnsQuery, dnsResolver) if err != nil { log.Errorf("dns request #%d failed after %s: %s", i, time.Since(start).Round(time.Millisecond), err) return } log.Infof("dns response #%d after %s: %s", i, time.Since(start).Round(time.Millisecond), dns.RcodeToString[reply.Rcode]) } ================================================ FILE: cmds/trafficgen/pack ================================================ #!/bin/bash baseDir="$( cd "$(dirname "$0")" && pwd )" cd "$baseDir" echo "building..." GOOS=windows go build dstFile="../../../portmaster-windows-kext/install/MINGW/amd64/trafficgen.exe" if [[ -d $(dirname "$dstFile") ]]; then # make path absolute dstFile="$(cd "$(dirname "$dstFile")" && pwd)/$(basename "$dstFile")" # copy echo "copying to $dstFile" cp trafficgen.exe "$dstFile" else echo "not copying to $dstFile (dir does not exist)" fi ================================================ FILE: cmds/winkext-test/main.go ================================================ //go:build windows // +build windows package main import ( "context" "flag" "fmt" "os" "os/signal" "path/filepath" "syscall" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/firewall/interception/windowskext" "github.com/safing/portmaster/service/network/packet" ) var ( packets chan packet.Packet shutdownCh = make(chan struct{}) getPayload bool rerouteDNS bool permanentAccept bool maxPackets int ) func init() { flag.BoolVar(&getPayload, "get-payload", false, "get payload of handled packets") flag.BoolVar(&rerouteDNS, "reroute-dns", false, "reroute dns to own IP") flag.BoolVar(&permanentAccept, "permanent-accept", false, "permanent-accept packets") flag.IntVar(&maxPackets, "max-packets", 0, "handle specified amount of packets, then exit") } func main() { flag.Parse() // check parameter count if flag.NArg() != 2 { fmt.Printf("usage: %s [options] \n", os.Args[0]) flag.Usage() os.Exit(1) } // logging err := log.Start("info", true, "") if err != nil { fmt.Printf("failed to start logging: %s\n", err) os.Exit(1) } defer log.Shutdown() log.SetLogLevel(log.TraceLevel) log.Info("starting windows kext test program") // Check paths. dllPath, err := filepath.Abs(flag.Arg(0)) if err == nil { _, err = os.Stat(dllPath) } if err != nil { log.Criticalf("cannot find .dll: %s\n", err) return } log.Infof("using .dll at %s", dllPath) sysPath, err := filepath.Abs(flag.Arg(1)) if err == nil { _, err = os.Stat(sysPath) } if err != nil { log.Criticalf("cannot find .sys: %s", err) return } log.Infof("using .sys at %s", sysPath) // init err = windowskext.Init(sysPath) if err != nil { log.Criticalf("failed to init kext: %s", err) return } // start err = windowskext.Start() if err != nil { log.Criticalf("failed to start kext: %s", err) return } packets = make(chan packet.Packet, 1000) go windowskext.Handler(context.TODO(), packets) go handlePackets() // catch interrupt for clean shutdown signalCh := make(chan os.Signal, 1) signal.Notify( signalCh, os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, ) select { case <-signalCh: fmt.Println(" ") log.Warning("program was interrupted, shutting down") case <-shutdownCh: log.Warningf("shutting down") } // stop err = windowskext.Stop() if err != nil { log.Criticalf("failed to stop kext: %s", err) } log.Info("shutdown complete") } func handlePackets() { var err error var handledPackets int for { pkt := <-packets if pkt == nil { log.Infof("stopped handling packets") return } log.Infof("received packet: %s", pkt) handledPackets++ if getPayload { data := pkt.Payload() log.Infof("payload is: %x", data) } // reroute dns requests to nameserver if rerouteDNS { if pkt.IsOutbound() && !pkt.Info().Src.Equal(pkt.Info().Dst) && pkt.Info().DstPort == 53 { log.Infof("rerouting %s", pkt) err = pkt.RerouteToNameserver() if err != nil { log.Errorf("failed to reroute: %s", err) } continue } } // accept all log.Infof("accepting %s", pkt) if permanentAccept { err = pkt.PermanentAccept() } else { err = pkt.Accept() } if err != nil { log.Errorf("failed to accept: %s", err) } if maxPackets > 0 && handledPackets > maxPackets { log.Infof("max-packets (%d) reached", maxPackets) close(shutdownCh) return } } } ================================================ FILE: cmds/winkext-test/main_linux.go ================================================ //go:build linux // +build linux package main import "log" func main() { log.Fatalf("winkext-test not supported on linux") } ================================================ FILE: cmds/winkext-test/pack ================================================ #!/bin/bash baseDir="$( cd "$(dirname "$0")" && pwd )" cd "$baseDir" echo "building..." GOOS=windows go build dstFile="../../../portmaster-windows-kext/install/MINGW/amd64/winkext-test.exe" if [[ -d $(dirname "$dstFile") ]]; then # make path absolute dstFile="$(cd "$(dirname "$dstFile")" && pwd)/$(basename "$dstFile")" # copy echo "copying to $dstFile" cp winkext-test.exe "$dstFile" else echo "not copying to $dstFile (dir does not exist)" fi ================================================ FILE: desktop/angular/.eslintrc.json ================================================ { "root": true, "ignorePatterns": [ "projects/**/*" ], "parserOptions": { "tsconfigRootDir": "desktop/angular" }, "overrides": [ { "files": [ "*.ts" ], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:@angular-eslint/recommended", "plugin:@angular-eslint/template/process-inline-templates" ], "rules": { "@angular-eslint/directive-selector": [ "error", { "type": "attribute", "prefix": "app", "style": "camelCase" } ], "@angular-eslint/component-selector": [ "error", { "type": "element", "prefix": "app", "style": "kebab-case" } ], "@typescript-eslint/no-explicit-any": "off" } }, { "files": [ "*.html" ], "extends": [ "plugin:@angular-eslint/template/recommended", "plugin:@angular-eslint/template/accessibility" ], "rules": { "@angular-eslint/template/click-events-have-key-events": "off", "@angular-eslint/template/interactive-supports-focus": "off" } } ] } ================================================ FILE: desktop/angular/.gitignore ================================================ node_modules dist dist-extension dist-lib .angular ================================================ FILE: desktop/angular/.vscode/launch.json ================================================ { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Run UI in Chrome (npm run start)", "type": "chrome", "request": "launch", "preLaunchTask": "debugproj", "postDebugTask": "killnode", "url": "http://localhost:4200/", "webRoot": "${workspaceFolder}", "sourceMapPathOverrides": { "webpack:/*": "${webRoot}/*", "/./*": "${webRoot}/*", "/src/*": "${webRoot}/*", "/*": "*", "/./~/*": "${webRoot}/node_modules/*" } }, ] } ================================================ FILE: desktop/angular/.vscode/tasks.json ================================================ { "version": "2.0.0", "tasks": [ { "label": "ui-library-watch", "type": "npm", "script": "build-ui:dev:watch", "isBackground": true, "presentation": { "echo": true, "reveal": "always", "focus": false, "panel": "dedicated", "group": "dev-libraries" }, "group": { "kind": "build" }, "problemMatcher": { "owner": "typescript", "source": "ts", "fileLocation": ["relative", "${workspaceFolder}"], "pattern": "$tsc", "background": { "activeOnStart": true, "beginsPattern": "Building Angular Package", "endsPattern": "Compilation complete\\. Watching for file changes\\.\\.\\." } } }, { "label": "api-library-watch", "type": "npm", "script": "build-api:dev:watch", "isBackground": true, "presentation": { "echo": true, "reveal": "always", "focus": false, "panel": "dedicated", "group": "dev-libraries" }, "group": { "kind": "build" }, "problemMatcher": { "owner": "typescript", "source": "ts", "fileLocation": ["relative", "${workspaceFolder}"], "pattern": "$tsc", "background": { "activeOnStart": true, "beginsPattern": "Building Angular Package", "endsPattern": "Compilation complete\\. Watching for file changes\\.\\.\\." } } }, { "label": "build-libs-first", "type": "npm", "script": "build-libs:dev", "isBackground": false, "presentation": { "echo": true, "reveal": "always", "focus": false, "panel": "shared" }, "group": { "kind": "build" }, "problemMatcher": [] }, { "label": "debugproj", "dependsOrder": "sequence", "dependsOn": [ "build-libs-first", "ui-library-watch", "api-library-watch", "main-app-with-polling" ], "isBackground": true, "presentation": { "echo": false, "reveal": "never", "focus": false, "panel": "shared" }, "group": { "kind": "build", "isDefault": true }, "problemMatcher": [] }, { "label": "main-app-with-polling", "type": "npm", "script": "serve-with-lib-watch", "isBackground": true, "presentation": { "echo": true, "reveal": "always", "focus": true, "panel": "dedicated", "group": "dev-main" }, "group": { "kind": "build" }, "problemMatcher": { "owner": "typescript", "source": "ts", "applyTo": "closedDocuments", "fileLocation": ["relative", "${workspaceFolder}"], "pattern": "$tsc", "background": { "activeOnStart": true, "beginsPattern": "Generating browser application bundles", "endsPattern": "Compiled successfully\\.|Failed to compile\\." } } }, { "label": "killnode", "type": "process", "windows": { "command": "taskkill", "args": ["/F", "/IM", "node.exe"] }, "osx":{ "command": "killall", "args": ["node"] }, "linux":{ "command": "killall", "args": ["node"] }, "presentation": { "echo": true, "reveal": "always", "focus": false, "panel": "shared" }, "group": { "kind": "build" }, "isBackground": false, "problemMatcher": [] } ] } ================================================ FILE: desktop/angular/README.md ================================================ # Portmaster Welcome to the new Portmaster User-Interface. It's based on Angular and is built, unit and e2e tested using `@angular/cli`. ## Running locally This section explains how to prepare your Ubuntu machine to build and test the new Portmaster User-Interface. It's recommended to use a virtual machine but running it on bare metal will work as well. You can use the new Portmaster UI as well as the old one in parallel so you can simply switch back when something is still missing or buggy. 1. **Prepare your tooling** There's a simple dockerized way to build and test the new UI. Just make sure to have docker installed: ```bash sudo apt update sudo apt install -y docker.io git sudo systemctl enable --now docker sudo gpasswd -a $USER docker ``` 2. **Portmaster installation** Next, make sure to install the Portmaster using the official .deb installer from [here](https://updates.safing.io/latest/linux_amd64/packages/portmaster-installer.deb). See the [Wiki](https://github.com/safing/portmaster/wiki/Linux) for more information. Once the Portmaster is installed we need to add two new configuration flags. Execute the following: ```bash echo 'PORTMASTER_ARGS="--experimental-nfqueue --devmode"' | sudo tee /etc/default/portmaster sudo systemctl daemon-reload sudo systemctl restart portmaster ``` 3. **Build and run the new UI** Now, clone this repository and execute the `docker.sh` script: ```bash # Clone the repository git clone https://github.com/safing/portmaster-ui # Enter the repo and checkout the correct branch cd portmaster-ui git checkout feature/new-ui # Enter the directory and run docker.sh cd modules/portmaster sudo bash ./docker.sh ``` Finally open your browser and point it to http://localhost:8080. ## Hacking Quick Start Although everything should work in the docker container as well, for the best development experience it's recommended to install `@angular/cli` locally. It's highly recommended to: - Use [VSCode](https://code.visualstudio.com/) (or it's oss or server-side variant) with - the official [Angular Language Service](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template) extension - the [Tailwind CSS Extension Pack](https://marketplace.visualstudio.com/items?itemName=andrewmcodes.tailwindcss-extension-pack) extension - the [formate: CSS/LESS/SCSS formatter](https://github.com/mblander/formate) extension ### Folder Structure From the project root (the folder containing this [README.md](./)) there are only two folders with the following content and structure: - **`src/`** contains the actual application sources: - **`app/`** contains the actual application sources (components, services, uni tests ...) - **`layout/`** contains components that form the overall application layout. For example the navigation bar and the side dash are located there. - **`pages/`** contains the different pages of the application. A page is something that is associated with a dedicated application route and is rendered at the applications main content. - **`services/`** contains shared services (like PortAPI and friends) - **`shared/`** contains shared components that are likely used accross other components or pages. - **`widgets/`** contains widgets and their settings components for the application side dash. - **`debug/`** contains a debug sidebar component - **`assets/`** contains static assets that must be shipped seperately. - **`environments/`** contains build and production related environment settings (those are handled by `@angular/cli` automatically, see [angular.json](angular.json)) - **`e2e/`** contains end-to-end testing sources. ### Development server Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. In development mode (that is, you don't pass `--prod`) the UI expects portmaster running at `ws://127.0.0.1:817/api/database/v1`. See [environment](./src/app/environments/environment.ts). ### Code scaffolding Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. ### Build Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. ### Running unit tests Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). ### Running end-to-end tests Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). ### Further help To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). ================================================ FILE: desktop/angular/angular.json ================================================ { "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "portmaster": { "projectType": "application", "schematics": { "@schematics/angular:component": { "style": "scss" }, "@schematics/angular:application": { "strict": true } }, "root": "", "sourceRoot": "src", "prefix": "app", "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "dist", "index": "src/index.html", "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", "aot": true, "assets": [ "src/favicon.ico", "src/assets" ], "styles": [ "src/theme.less", "src/styles.scss", "node_modules/prismjs/themes/prism-okaidia.css", "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.css" ], "stylePreprocessorOptions": { "includePaths": [ "dist-lib/" ] }, "scripts": [ "node_modules/marked/marked.min.js", "node_modules/emoji-toolkit/lib/js/joypixels.min.js", "node_modules/prismjs/prism.js", "node_modules/prismjs/components/prism-yaml.min.js", "node_modules/prismjs/components/prism-json.min.js", "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.js" ], "vendorChunk": true, "extractLicenses": false, "buildOptimizer": false, "sourceMap": true, "optimization": false, "namedChunks": true }, "configurations": { "development": {}, "production": { "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ], "optimization": { "scripts": true, "styles": { "minify": true, "inlineCritical": false } }, "outputHashing": "all", "sourceMap": false, "namedChunks": false, "extractLicenses": true, "vendorChunk": true, "buildOptimizer": true, "budgets": [ { "type": "initial", "maximumWarning": "4mb", "maximumError": "16mb" }, { "type": "anyComponentStyle", "maximumWarning": "4mb", "maximumError": "16mb" } ] } } }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { "browserTarget": "portmaster:build" }, "configurations": { "production": { "browserTarget": "portmaster:build:production" } } }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { "browserTarget": "portmaster:build" } }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { "main": "src/test.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.spec.json", "karmaConfig": "karma.conf.js", "assets": [ "src/favicon.ico", "src/assets" ], "styles": [ "src/styles.scss" ], "scripts": [] } }, "e2e": { "builder": "@angular-devkit/build-angular:protractor", "options": { "protractorConfig": "e2e/protractor.conf.js", "devServerTarget": "portmaster:serve" }, "configurations": { "production": { "devServerTarget": "portmaster:serve:production" } } }, "lint": { "builder": "@angular-eslint/builder:lint", "options": { "lintFilePatterns": [ "src/**/*.ts", "src/**/*.html" ] } } } }, "@safing/ui": { "projectType": "library", "root": "projects/safing/ui", "sourceRoot": "projects/safing/ui/src", "prefix": "lib", "architect": { "build": { "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "project": "projects/safing/ui/ng-package.json" }, "configurations": { "production": { "tsConfig": "projects/safing/ui/tsconfig.lib.prod.json" }, "development": { "tsConfig": "projects/safing/ui/tsconfig.lib.json" } }, "defaultConfiguration": "production" }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { "main": "projects/safing/ui/src/test.ts", "tsConfig": "projects/safing/ui/tsconfig.spec.json", "karmaConfig": "projects/safing/ui/karma.conf.js" } }, "lint": { "builder": "@angular-eslint/builder:lint", "options": { "lintFilePatterns": [ "projects/safing/ui/**/*.ts", "projects/safing/ui/**/*.html" ] } } } }, "portmaster-chrome-extension": { "projectType": "application", "schematics": { "@schematics/angular:component": { "style": "scss" } }, "root": "projects/portmaster-chrome-extension", "sourceRoot": "projects/portmaster-chrome-extension/src", "prefix": "app", "architect": { "build": { "builder": "@angular-builders/custom-webpack:browser", "options": { "customWebpackConfig": { "path": "./browser-extension.config.ts" }, "outputPath": "dist-extension", "index": "projects/portmaster-chrome-extension/src/index.html", "main": "projects/portmaster-chrome-extension/src/main.ts", "polyfills": "projects/portmaster-chrome-extension/src/polyfills.ts", "tsConfig": "projects/portmaster-chrome-extension/tsconfig.app.json", "inlineStyleLanguage": "scss", "assets": [ "projects/portmaster-chrome-extension/src/favicon.ico", "projects/portmaster-chrome-extension/src/assets", "projects/portmaster-chrome-extension/src/manifest.json" ], "styles": [ "projects/portmaster-chrome-extension/src/styles.scss" ], "scripts": [], "optimization": { "styles": { "inlineCritical": false } }, "outputHashing": "none" }, "configurations": { "production": { "budgets": [ { "type": "initial", "maximumWarning": "500kb", "maximumError": "1mb" }, { "type": "anyComponentStyle", "maximumWarning": "2kb", "maximumError": "4kb" } ], "fileReplacements": [ { "replace": "projects/portmaster-chrome-extension/src/environments/environment.ts", "with": "projects/portmaster-chrome-extension/src/environments/environment.prod.ts" } ], "outputHashing": "none" }, "development": { "customWebpackConfig": { "path": "./browser-extension-dev.config.ts" }, "buildOptimizer": false, "optimization": false, "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true } }, "defaultConfiguration": "production" }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", "configurations": { "production": { "browserTarget": "portmaster-chrome-extension:build:production" }, "development": { "browserTarget": "portmaster-chrome-extension:build:development" } }, "defaultConfiguration": "development" }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { "browserTarget": "portmaster-chrome-extension:build" } }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { "main": "projects/portmaster-chrome-extension/src/test.ts", "polyfills": "projects/portmaster-chrome-extension/src/polyfills.ts", "tsConfig": "projects/portmaster-chrome-extension/tsconfig.spec.json", "karmaConfig": "projects/portmaster-chrome-extension/karma.conf.js", "inlineStyleLanguage": "scss", "assets": [ "projects/portmaster-chrome-extension/src/favicon.ico", "projects/portmaster-chrome-extension/src/assets" ], "styles": [ "projects/portmaster-chrome-extension/src/styles.scss" ], "scripts": [] } } } }, "@safing/portmaster-api": { "projectType": "library", "root": "projects/safing/portmaster-api", "sourceRoot": "projects/safing/portmaster-api/src", "prefix": "lib", "architect": { "build": { "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "project": "projects/safing/portmaster-api/ng-package.json" }, "configurations": { "production": { "tsConfig": "projects/safing/portmaster-api/tsconfig.lib.prod.json" }, "development": { "tsConfig": "projects/safing/portmaster-api/tsconfig.lib.json" } }, "defaultConfiguration": "production" }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { "main": "projects/safing/portmaster-api/src/test.ts", "tsConfig": "projects/safing/portmaster-api/tsconfig.spec.json", "karmaConfig": "projects/safing/portmaster-api/karma.conf.js" } } } }, "tauri-builtin": { "projectType": "application", "schematics": { "@schematics/angular:component": { "skipTests": true, "style": "scss", "standalone": true }, "@schematics/angular:class": { "skipTests": true }, "@schematics/angular:directive": { "skipTests": true, "standalone": true }, "@schematics/angular:guard": { "skipTests": true }, "@schematics/angular:interceptor": { "skipTests": true }, "@schematics/angular:pipe": { "skipTests": true, "standalone": true }, "@schematics/angular:resolver": { "skipTests": true }, "@schematics/angular:service": { "skipTests": true } }, "root": "projects/tauri-builtin", "sourceRoot": "projects/tauri-builtin/src", "prefix": "app", "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "dist/tauri-builtin", "index": "projects/tauri-builtin/src/index.html", "main": "projects/tauri-builtin/src/main.ts", "polyfills": [ "zone.js" ], "tsConfig": "projects/tauri-builtin/tsconfig.app.json", "assets": [ "projects/tauri-builtin/src/favicon.ico", "projects/tauri-builtin/src/assets" ], "styles": [ "projects/tauri-builtin/src/styles.scss" ], "inlineStyleLanguage": "scss", "stylePreprocessorOptions": { "includePaths": [ "dist-lib/" ] }, "scripts": [] }, "configurations": { "production": { "budgets": [ { "type": "initial", "maximumWarning": "500kb", "maximumError": "1mb" }, { "type": "anyComponentStyle", "maximumWarning": "2kb", "maximumError": "4kb" } ], "outputHashing": "all" }, "development": { "buildOptimizer": false, "optimization": false, "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true } }, "defaultConfiguration": "production" }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", "configurations": { "production": { "browserTarget": "tauri-builtin:build:production" }, "development": { "browserTarget": "tauri-builtin:build:development" } }, "defaultConfiguration": "development" }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { "browserTarget": "tauri-builtin:build" } } } } }, "cli": { "analytics": false }, "schematics": { "@angular-eslint/schematics:application": { "setParserOptionsProject": true }, "@angular-eslint/schematics:library": { "setParserOptionsProject": true } } } ================================================ FILE: desktop/angular/browser-extension-dev.config.ts ================================================ import type { Configuration } from 'webpack'; const ExtensionReloader = require('webpack-ext-reloader'); const config = require('./browser-extension.config'); module.exports = { ...config, mode: 'development', plugins: [ new ExtensionReloader({ reloadPage: true, // Force the reload of the page also entries: { // The entries used for the content/background scripts or extension pages background: 'background', } }) ] } as Configuration; ================================================ FILE: desktop/angular/browser-extension.config.ts ================================================ import type { Configuration } from 'webpack'; module.exports = { entry: { background: { import: 'projects/portmaster-chrome-extension/src/background.ts', runtime: false } }, } as Configuration; ================================================ FILE: desktop/angular/docker.sh ================================================ #!/bin/bash # cd to script dir baseDir="$( cd "$(dirname "$0")" && pwd )" cd "$baseDir" # get base dir for mounting mnt="$( cd ../.. && pwd )" # run container and start dev server docker run \ -ti \ --rm \ -v $mnt:/portmaster \ -w /portmaster/desktop/angular \ -p 8081:8080 \ node:latest \ npm start -- --host 0.0.0.0 --port 8080 ================================================ FILE: desktop/angular/e2e/protractor.conf.js ================================================ // @ts-check // Protractor configuration file, see link for more information // https://github.com/angular/protractor/blob/master/lib/config.ts const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); /** * @type { import("protractor").Config } */ exports.config = { allScriptsTimeout: 11000, specs: [ './src/**/*.e2e-spec.ts' ], capabilities: { browserName: 'chrome' }, directConnect: true, baseUrl: 'http://localhost:4200/', framework: 'jasmine', jasmineNodeOpts: { showColors: true, defaultTimeoutInterval: 30000, print: function() {} }, onPrepare() { require('ts-node').register({ project: require('path').join(__dirname, './tsconfig.json') }); jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: StacktraceOption.PRETTY } })); } }; ================================================ FILE: desktop/angular/e2e/src/app.e2e-spec.ts ================================================ import { AppPage } from './app.po'; import { browser, logging } from 'protractor'; describe('workspace-project App', () => { let page: AppPage; beforeEach(() => { page = new AppPage(); }); it('should display welcome message', () => { page.navigateTo(); expect(page.getTitleText()).toEqual('portmaster app is running!'); }); afterEach(async () => { // Assert that there are no errors emitted from the browser const logs = await browser.manage().logs().get(logging.Type.BROWSER); expect(logs).not.toContain(jasmine.objectContaining({ level: logging.Level.SEVERE, } as logging.Entry)); }); }); ================================================ FILE: desktop/angular/e2e/src/app.po.ts ================================================ import { browser, by, element } from 'protractor'; export class AppPage { navigateTo(): Promise { return browser.get(browser.baseUrl) as Promise; } getTitleText(): Promise { return element(by.css('app-root .content span')).getText() as Promise; } } ================================================ FILE: desktop/angular/e2e/tsconfig.json ================================================ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/e2e", "module": "commonjs", "target": "es2018", "types": [ "jasmine", "jasminewd2", "node" ] } } ================================================ FILE: desktop/angular/karma.conf.js ================================================ // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html module.exports = function (config) { config.set({ basePath: '', frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('karma-coverage-istanbul-reporter'), require('@angular-devkit/build-angular/plugins/karma') ], client: { clearContext: false // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, './coverage/portmaster'), reports: ['html', 'lcovonly', 'text-summary'], fixWebpackSourcePaths: true }, reporters: ['progress', 'kjhtml'], port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, restartOnFileChange: true }); }; ================================================ FILE: desktop/angular/package.json ================================================ { "name": "portmaster", "version": "2.1.8", "scripts": { "ng": "ng", "start": "npm install && npm run build-libs:dev && ng serve --proxy-config ./proxy.json", "build-libs": "cross-env NODE_ENV=production ng build --configuration production @safing/ui && cross-env NODE_ENV=production ng build --configuration production @safing/portmaster-api", "build-libs:dev": "ng build --configuration development @safing/ui && ng build --configuration development @safing/portmaster-api", "build-ui:dev:watch": "ng build --configuration development @safing/ui --watch", "build-api:dev:watch": "ng build --configuration development @safing/portmaster-api --watch", "serve-with-lib-watch": "ng serve --proxy-config ./proxy.json --poll=2000", "serve": "npm run build-libs:dev && ng serve --proxy-config ./proxy.json", "build:dev": "npm run build-libs:dev && ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e", "zip-dist": "node pack.js", "chrome-extension": "cross-env NODE_ENV=production ng build --configuration production portmaster-chrome-extension", "chrome-extension:dev": "ng build --configuration development portmaster-chrome-extension --watch", "build": "npm run build-libs && NODE_ENV=production ng build --configuration production --base-href /ui/modules/portmaster/", "build-tauri": "npm run build-libs && cross-env NODE_ENV=production ng build --configuration production tauri-builtin", "serve-tauri-builtin": "ng serve tauri-builtin --port 4100", "serve-app": "ng serve --port 4200 --proxy-config ./proxy.json", "tauri-dev": "npm install && run-s build-libs:dev && run-p serve-app serve-tauri-builtin" }, "private": true, "dependencies": { "@angular/animations": "^16.0.1", "@angular/cdk": "^16.0.1", "@angular/common": "^16.0.1", "@angular/compiler": "^16.0.1", "@angular/core": "^16.0.1", "@angular/forms": "^16.0.1", "@angular/localize": "^16.0.1", "@angular/platform-browser": "^16.0.1", "@angular/platform-browser-dynamic": "^16.0.1", "@angular/router": "^16.0.1", "@ctrl/tinycolor": "^4.1.0", "@fortawesome/angular-fontawesome": "^0.13.0", "@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/free-brands-svg-icons": "^6.4.0", "@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-solid-svg-icons": "^6.4.0", "@tauri-apps/api": ">=2.1.1", "@tauri-apps/plugin-cli": ">=2.0.0", "@tauri-apps/plugin-clipboard-manager": ">=2.0.0", "@tauri-apps/plugin-dialog": ">=2.0.0", "@tauri-apps/plugin-notification": ">=2.0.0", "@tauri-apps/plugin-os": ">=2.0.0", "@tauri-apps/plugin-shell": "^2.0.1", "@tauri-apps/plugin-websocket": "^2.3.0", "autoprefixer": "^10.4.14", "d3": "^7.8.4", "data-urls": "^5.0.0", "emoji-toolkit": "^7.0.1", "fuse.js": "^6.6.2", "ng-zorro-antd": "^16.1.0", "ngx-markdown": "^16.0.0", "postcss": "^8.4.23", "prismjs": "^1.29.0", "psl": "^1.9.0", "rxjs": "~7.8.1", "topojson-client": "^3.1.0", "topojson-simplify": "^3.0.3", "tslib": "^2.5.0", "whatwg-encoding": "^3.1.1", "zone.js": "^0.13.0" }, "devDependencies": { "@angular-builders/custom-webpack": "^16.0.0-beta.1", "@angular-devkit/build-angular": "^16.0.1", "@angular-eslint/builder": "16.0.1", "@angular-eslint/eslint-plugin": "16.0.1", "@angular-eslint/eslint-plugin-template": "16.0.1", "@angular-eslint/schematics": "16.0.1", "@angular-eslint/template-parser": "16.0.1", "@angular/cli": "^16.0.1", "@angular/compiler-cli": "^16.0.1", "@fullhuman/postcss-purgecss": "^5.0.0", "@types/chrome": "^0.0.236", "@types/d3": "^7.4.0", "@types/data-urls": "^3.0.4", "@types/jasmine": "^4.3.1", "@types/jasminewd2": "~2.0.10", "@types/node": "^20.1.5", "@types/psl": "^1.1.0", "@types/topojson-client": "^3.1.1", "@types/topojson-simplify": "^3.0.1", "@types/whatwg-encoding": "^2.0.3", "@typescript-eslint/eslint-plugin": "^5.59.6", "@typescript-eslint/parser": "^5.59.6", "cross-env": "^7.0.3", "eslint": "^8.40.0", "jasmine-core": "^5.0.0", "jasmine-spec-reporter": "^7.0.0", "js-yaml-loader": "^1.2.2", "ng-packagr": "^16.0.1", "npm-run-all": "^4.1.5", "postcss-import": "^15.1.0", "postcss-loader": "^7.3.0", "postcss-scss": "^4.0.6", "protractor": "~7.0.0", "tailwindcss": "^3.3.2", "ts-node": "^10.9.1", "typescript": "4.9", "webpack-bundle-analyzer": "^4.8.0", "webpack-ext-reloader": "^1.1.9", "zip-a-folder": "^1.1.5" } } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/karma.conf.js ================================================ // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html module.exports = function (config) { config.set({ basePath: '', frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('karma-coverage'), require('@angular-devkit/build-angular/plugins/karma') ], client: { jasmine: { // you can add configuration options for Jasmine here // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html // for example, you can disable the random execution with `random: false` // or set a specific seed with `seed: 4321` }, clearContext: false // leave Jasmine Spec Runner output visible in browser }, jasmineHtmlReporter: { suppressAll: true // removes the duplicated traces }, coverageReporter: { dir: require('path').join(__dirname, '../../coverage/portmaster-chrome-extension'), subdir: '.', reporters: [ { type: 'html' }, { type: 'text-summary' } ] }, reporters: ['progress', 'kjhtml'], port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, restartOnFileChange: true }); }; ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/app-routing.module.ts ================================================ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ExtDomainListComponent } from './domain-list'; import { IntroComponent } from './welcome/intro.component'; const routes: Routes = [ { path: '', pathMatch: 'full', component: ExtDomainListComponent }, { path: 'authorize', pathMatch: 'prefix', component: IntroComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/app.component.html ================================================ ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/app.component.scss ================================================ :host { @apply bg-background text-white flex flex-col w-96 h-96; } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/app.component.ts ================================================ import { HttpErrorResponse } from '@angular/common/http'; import { Component, OnInit } from '@angular/core'; import { NavigationEnd, Router } from '@angular/router'; import { MetaAPI, MyProfileResponse, retryPipeline } from '@safing/portmaster-api'; import { catchError, filter, throwError } from 'rxjs'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], }) export class AppComponent implements OnInit { isAuthorizeView = false; constructor( private metaapi: MetaAPI, private router: Router, ) { } profile: MyProfileResponse | null = null; ngOnInit(): void { this.router.events .pipe( filter(event => event instanceof NavigationEnd) ) .subscribe(event => { if (event instanceof NavigationEnd) { this.isAuthorizeView = event.url.includes("/authorize") } }) this.metaapi.myProfile() .pipe( catchError(err => { if (err instanceof HttpErrorResponse && err.status === 403) { this.router.navigate(['/authorize']) } return throwError(() => err) }), retryPipeline() ) .subscribe({ next: profile => { this.profile = profile; console.log(this.profile); } }) } } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/app.module.ts ================================================ import { OverlayModule } from '@angular/cdk/overlay'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { PortmasterAPIModule } from '@safing/portmaster-api'; import { TabModule } from '@safing/ui'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { ExtDomainListComponent } from './domain-list'; import { ExtHeaderComponent } from './header'; import { AuthIntercepter as AuthInterceptor } from './interceptor'; import { WelcomeModule } from './welcome'; @NgModule({ declarations: [ AppComponent, ExtDomainListComponent, ExtHeaderComponent, ], imports: [ BrowserModule, AppRoutingModule, HttpClientModule, PortmasterAPIModule.forRoot(), TabModule, WelcomeModule, OverlayModule, ], providers: [ { provide: HTTP_INTERCEPTORS, multi: true, useClass: AuthInterceptor, } ], bootstrap: [AppComponent] }) export class AppModule { } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/domain-list/domain-list.component.html ================================================
  • {{ req.domain }}
    {{ req.lastConn.extra_data?.reason?.Msg }}
================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/domain-list/domain-list.component.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from "@angular/core"; import { Netquery, NetqueryConnection } from "@safing/portmaster-api"; import { ListRequests, NotifyRequests } from "../../background/commands"; import { Request } from '../../background/tab-tracker'; interface DomainRequests { domain: string; requests: Request[]; latestIsBlocked: boolean; lastConn?: NetqueryConnection; } @Component({ selector: 'ext-domain-list', templateUrl: './domain-list.component.html', changeDetection: ChangeDetectionStrategy.OnPush, styles: [ ` :host { @apply flex flex-grow flex-col overflow-auto; } ` ] }) export class ExtDomainListComponent implements OnInit { requests: DomainRequests[] = []; constructor( private netquery: Netquery, private cdr: ChangeDetectorRef ) { } ngOnInit() { // setup listening for requests sent from our background script const self = this; chrome.runtime.onMessage.addListener((msg: NotifyRequests) => { if (typeof msg !== 'object') { console.error('Received invalid message from background script') return; } console.log(`DEBUG: received command ${msg.type} from background script`) switch (msg.type) { case 'notifyRequests': self.updateRequests(msg.requests); break; default: console.error('Received unknown command from background script') } }) this.loadRequests(); } updateRequests(req: Request[]) { let m = new Map(); this.requests.forEach(obj => { obj.requests = []; m.set(obj.domain, obj); }); req.forEach(r => { let obj = m.get(r.domain); if (!obj) { obj = { domain: r.domain, requests: [], latestIsBlocked: false } m.set(r.domain, obj) } obj.requests.push(r); }) this.requests = []; Array.from(m.keys()).sort() .map(key => m.get(key)!) .forEach(obj => { this.requests.push(obj) this.netquery.query({ query: { domain: obj.domain, }, orderBy: [ { field: 'started', desc: true, } ], page: 0, pageSize: 1, }) .subscribe(result => { if (!result[0]) { return; } obj.latestIsBlocked = !result[0].allowed; obj.lastConn = result[0] as NetqueryConnection; }) }) this.cdr.detectChanges(); } private loadRequests() { const cmd: ListRequests = { type: 'listRequests', tabId: 'current' } const self = this; chrome.runtime.sendMessage(cmd, (response: any) => { if (Array.isArray(response)) { self.updateRequests(response) return; } console.error(response); }) } } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/domain-list/index.ts ================================================ export * from './domain-list.component'; ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/header/header.component.html ================================================
Secure
================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/header/header.component.scss ================================================ svg { transform: scale(0.95); path { top: 0px; left: 0px; transform-origin: center center; } .shield-one { transform: scale(.62); } .shield-two { animation-delay: -1.2s; opacity: .6; transform: scale(.8); } .shield-three { animation-delay: -2.5s; opacity: .4; transform: scale(1); } .shield-ok { transform: scale(.62); } } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/header/header.component.ts ================================================ import { ChangeDetectionStrategy, Component } from "@angular/core"; @Component({ selector: 'ext-header', templateUrl: './header.component.html', changeDetection: ChangeDetectionStrategy.OnPush, styleUrls: ['./header.component.scss'] }) export class ExtHeaderComponent { } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/header/index.ts ================================================ export * from './header.component'; ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/interceptor.ts ================================================ import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { BehaviorSubject, filter, Observable, switchMap } from "rxjs"; @Injectable() export class AuthIntercepter implements HttpInterceptor { /** Used to delay requests until we loaded the access token from the extension storage. */ private loaded$ = new BehaviorSubject(false); /** Holds the access token required to talk to the Portmaster API. */ private token: string | null = null; constructor() { // make sure we use the new access token once we get one. chrome.storage.onChanged.addListener(changes => { this.token = changes['key'].newValue || null; }) // try to read the current access token from the extension storage. chrome.storage.local.get('key', obj => { this.token = obj.key || null; console.log("got token", this.token) this.loaded$.next(true); }) chrome.runtime.sendMessage({ type: 'listRequests', tabId: 'current' }, (response: any) => { console.log(response); }) } intercept(req: HttpRequest, next: HttpHandler): Observable> { return this.loaded$.pipe( filter(loaded => loaded), switchMap(() => { if (!!this.token) { req = req.clone({ headers: req.headers.set("Authorization", "Bearer " + this.token) }) } return next.handle(req) }) ) } } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/request-interceptor.service.ts ================================================ import { Injectable } from "@angular/core"; import { Subject } from "rxjs"; @Injectable({ providedIn: 'root' }) export class RequestInterceptorService { /** Used to emit when a new URL was requested */ private onUrlRequested$ = new Subject(); /** Used to emit when a URL has likely been blocked by the portmaster */ private onUrlBlocked$ = new Subject(); /** Emits when a new URL was requested */ get onUrlRequested() { return this.onUrlRequested$.asObservable(); } /** Emits when a new URL was likely blocked by the portmaster */ get onUrlBlocked() { return this.onUrlBlocked$.asObservable(); } constructor() { this.registerCallbacks() } private registerCallbacks() { const filter = { urls: [ "http://*/*", "https://*/*", ] }; chrome.webRequest.onBeforeRequest.addListener(details => this.onUrlRequested$.next(details), filter) chrome.webRequest.onErrorOccurred.addListener(details => { if (details.error !== "net::ERR_ADDRESS_UNREACHABLE") { // we don't care about errors other than UNREACHABLE because that's error caused // by the portmaster. return; } this.onUrlBlocked$.next(details); }, filter) } } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/welcome/index.ts ================================================ export * from './welcome.module'; ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/welcome/intro.component.html ================================================

Welcome to the Portmaster Browser Extension

This extension adds direct support for Portmaster to your Browser. For that, it needs to get access to the Portmaster on your system. For security reasons, you first need to authorize the Browser Extension to talk to the Portmaster.

Waiting for Authorization

Please open the Portmaster and approve the authorization request.
================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/welcome/intro.component.ts ================================================ import { Component } from "@angular/core"; import { Router } from "@angular/router"; import { MetaAPI } from "@safing/portmaster-api"; import { Subject, takeUntil } from "rxjs"; @Component({ templateUrl: './intro.component.html', styles: [ ` :host { @apply flex flex-col h-full; } ` ] }) export class IntroComponent { private cancelRequest$ = new Subject(); state: 'authorizing' | 'failed' | '' = ''; constructor( private meta: MetaAPI, private router: Router, ) { } authorizeExtension() { // cancel any pending request this.cancelRequest$.next(); this.state = 'authorizing'; this.meta.requestApplicationAccess("Portmaster Browser Extension") .pipe(takeUntil(this.cancelRequest$)) .subscribe({ next: token => { chrome.storage.local.set(token); console.log(token); this.router.navigate(['/']) }, error: err => { this.state = 'failed'; } }) } } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/app/welcome/welcome.module.ts ================================================ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { OverlayStepperModule } from "@safing/ui"; import { IntroComponent } from "./intro.component"; @NgModule({ imports: [ CommonModule, OverlayStepperModule, ], declarations: [ IntroComponent, ], exports: [ IntroComponent, ] }) export class WelcomeModule { } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/assets/.gitkeep ================================================ ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/background/commands.ts ================================================ import { Request } from "./tab-tracker"; export interface ListRequests { type: 'listRequests'; domain?: string; tabId: number | 'current'; } export interface NotifyRequests { type: 'notifyRequests', requests: Request[]; } export type CallRequest = ListRequests; ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/background/tab-tracker.ts ================================================ import { deepClone } from "@safing/portmaster-api"; export interface Request { /** The ID assigned by the browser */ id: string; /** The domain this request was for */ domain: string; /** The timestamp in milliseconds since epoch at which the request was initiated */ time: number; /** Whether or not this request errored with net::ERR_ADDRESS_UNREACHABLE */ isUnreachable: boolean; } /** * TabTracker tracks requests to domains made by a single browser tab. */ export class TabTracker { /** A list of requests observed for this tab order by time they have been initiated */ private requests: Request[] = []; /** A lookup map for requests to specific domains */ private byDomain = new Map(); /** A lookup map for requests by the chrome request ID */ private byRequestId = new Map; constructor(public readonly tabId: number) { } /** Returns an array of all requests observed in this tab. */ allRequests(): Request[] { return deepClone(this.requests) } /** Returns a list of requests that have been observed for domain */ forDomain(domain: string): Request[] { if (!domain.endsWith(".")) { domain += "." } return this.byDomain.get(domain) || []; } /** Call to add the details of a web-request to this tab-tracker */ trackRequest(details: chrome.webRequest.WebRequestDetails) { // If this is the wrong tab ID ignore the request details if (details.tabId !== this.tabId) { console.error(`TabTracker.trackRequest: called with wrong tab ID. Expected ${this.tabId} but got ${details.tabId}`) return; } // if the type of the request is for the main_frame the user switched to a new website. // In that case, we can wipe out all currently stored requests as the user will likely not // care anymore. if (details.type === "main_frame") { this.clearState(); } // get the domain of the request normalized to contain the trailing dot. let domain = new URL(details.url).host; if (!domain.endsWith(".")) { domain += "." } const req: Request = { id: details.requestId, domain: domain, time: details.timeStamp, isUnreachable: false, // we don't actually know that yet } this.requests.push(req); this.byRequestId.set(req.id, req) // Add the request to the by-domain lookup map let byDomainRequests = this.byDomain.get(req.domain); if (!byDomainRequests) { byDomainRequests = []; this.byDomain.set(req.domain, byDomainRequests) } byDomainRequests.push(req) console.log(`DEBUG: observed request ${req.id} to ${req.domain}`) } /** Call to notify the tab-tracker of a request error */ trackError(errorDetails: chrome.webRequest.WebResponseErrorDetails) { // we only care about net::ERR_ADDRESS_UNREACHABLE here because that's how the // Portmaster blocks the request. // TODO(ppacher): docs say we must not rely on that value so we should figure out a better // way to detect if the error is caused by the Portmaster. if (errorDetails.error !== "net::ERR_ADDRESS_UNREACHABLE") { return; } // the the previsouly observed request by the request ID. const req = this.byRequestId.get(errorDetails.requestId) if (!req) { console.error("TabTracker.trackError: request has not been observed before") return } // make sure the error details actually happend for the observed tab. if (errorDetails.tabId !== this.tabId) { console.error(`TabTracker.trackRequest: called with wrong tab ID. Expected ${this.tabId} but got ${errorDetails.tabId}`) return; } // mark the request as unreachable. req.isUnreachable = true; console.log(`DEBUG: marked request ${req.id} to ${req.domain} as unreachable`) } /** Clears the current state of the tab tracker */ private clearState() { this.requests = []; this.byDomain = new Map(); this.byRequestId = new Map(); } } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/background/tab-utils.ts ================================================ /** Queries and returns the currently active tab */ export function getCurrentTab(): Promise { return new Promise((resolve) => { chrome.tabs.query({ active: true, lastFocusedWindow: true }, ([tab]) => { resolve(tab); }) }) } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/background.ts ================================================ import { debounceTime, Subject } from "rxjs"; import { CallRequest, ListRequests, NotifyRequests } from "./background/commands"; import { Request, TabTracker } from "./background/tab-tracker"; import { getCurrentTab } from "./background/tab-utils"; export class BackgroundService { /** a lookup map for tab trackers by tab-id */ private trackers = new Map(); /** used to signal the pop-up that new requests arrived */ private notifyRequests = new Subject(); constructor() { // register a navigation-completed listener. This is fired when the user switches to a new website // by entering it in the browser address bar. chrome.webNavigation.onCompleted.addListener((details) => { console.log("event: webNavigation.onCompleted", details); }) // request event listeners for new requests and errors that occured for them. // We only care about http and https here. const filter = { urls: [ 'http://*/*', 'https://*/*' ] } chrome.webRequest.onBeforeRequest.addListener(details => this.handleOnBeforeRequest(details), filter) chrome.webRequest.onErrorOccurred.addListener(details => this.handleOnErrorOccured(details), filter) // make sure we can communicate with the extension popup chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => this.handleMessage(msg, sender, sendResponse)) // set-up signalling of new requests to the pop-up this.notifyRequests .pipe(debounceTime(500)) .subscribe(async () => { const currentTab = await getCurrentTab(); if (!!currentTab && !!currentTab.id) { const msg: NotifyRequests = { type: 'notifyRequests', requests: this.mustGetTab({ tabId: currentTab.id }).allRequests() } chrome.runtime.sendMessage(msg) } }) } /** Callback for messages sent by the popup */ private handleMessage(msg: CallRequest, sender: chrome.runtime.MessageSender, sendResponse: (msg: any) => void) { console.log(`DEBUG: got message from ${sender.origin} (tab=${sender.tab?.id})`) if (typeof msg !== 'object') { console.error(`Received invalid message from popup`, msg) return; } let response: Promise; switch (msg.type) { case 'listRequests': response = this.handleListRequests(msg) break; default: response = Promise.reject("unknown command") } response .then(res => { console.log(`DEBUG: sending response for command ${msg.type}`, res) sendResponse(res); }) .catch(err => { console.error(`Failed to handle command ${msg.type}`, err) sendResponse({ type: 'error', details: err }); }) } /** Returns a list of all observed requests based on the filter in msg. */ private async handleListRequests(msg: ListRequests): Promise { if (msg.tabId === 'current') { const currentID = (await getCurrentTab()).id if (!currentID) { return []; } msg.tabId = currentID; } const tracker = this.mustGetTab({ tabId: msg.tabId as number }) if (!!msg.domain) { return tracker.forDomain(msg.domain) } return tracker.allRequests() } /** Callback for chrome.webRequest.onBeforeRequest */ private handleOnBeforeRequest(details: chrome.webRequest.WebRequestDetails) { this.mustGetTab(details).trackRequest(details) this.notifyRequests.next(); } /** Callback for chrome.webRequest.onErrorOccured */ private handleOnErrorOccured(details: chrome.webRequest.WebResponseErrorDetails) { this.mustGetTab(details).trackError(details); this.notifyRequests.next(); } /** Returns the tab-tracker for tabId. Creates a new tracker if none exists. */ private mustGetTab({ tabId }: { tabId: number }): TabTracker { let tracker = this.trackers.get(tabId); if (!tracker) { tracker = new TabTracker(tabId) this.trackers.set(tabId, tracker) } return tracker; } } /** start the background service once we got successfully installed. */ chrome.runtime.onInstalled.addListener(() => { new BackgroundService() }); ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/environments/environment.prod.ts ================================================ export const environment = { production: false }; ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/environments/environment.ts ================================================ // This file can be replaced during build by using the `fileReplacements` array. // `ng build` replaces `environment.ts` with `environment.prod.ts`. // The list of file replacements can be found in `angular.json`. export const environment = { production: false }; /* * For easier debugging in development mode, you can import the following file * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. * * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/index.html ================================================ PortmasterChromeExtension ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/main.ts ================================================ import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); } platformBrowserDynamic().bootstrapModule(AppModule) .catch(err => console.error(err)); ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/manifest.json ================================================ { "name": "Portmaster Browser Extension", "version": "0.1", "description": "Browser Extension for even better Portmaster integration", "manifest_version": 2, "permissions": [ "activeTab", "storage", "webRequest", "webNavigation", "*://*/*" ], "browser_action": { "default_popup": "index.html", "default_icon": { "128": "assets/icon_128.png" } }, "background": { "scripts": ["background.js"], "persistent": true } } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/polyfills.ts ================================================ /** * This file includes polyfills needed by Angular and is loaded before the app. * You can add your own extra polyfills to this file. * * This file is divided into 2 sections: * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. * 2. Application imports. Files imported after ZoneJS that should be loaded before your main * file. * * The current setup is for so-called "evergreen" browsers; the last versions of browsers that * automatically update themselves. This includes recent versions of Safari, Chrome (including * Opera), Edge on the desktop, and iOS and Chrome on mobile. * * Learn more in https://angular.io/guide/browser-support */ /*************************************************************************************************** * BROWSER POLYFILLS */ /** * By default, zone.js will patch all possible macroTask and DomEvents * user can disable parts of macroTask/DomEvents patch by setting following flags * because those flags need to be set before `zone.js` being loaded, and webpack * will put import in the top of bundle, so user need to create a separate file * in this directory (for example: zone-flags.ts), and put the following flags * into that file, and then add the following code before importing zone.js. * import './zone-flags'; * * The flags allowed in zone-flags.ts are listed here. * * The following flags will work for all browsers. * * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames * * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js * with the following flag, it will bypass `zone.js` patch for IE/Edge * * (window as any).__Zone_enable_cross_context_check = true; * */ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS */ ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/styles.scss ================================================ /* You can add global styles to this file, and also import other style files */ @import 'tailwindcss/base'; @import 'tailwindcss/components'; @import 'tailwindcss/utilities'; @import '@angular/cdk/overlay-prebuilt'; ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/src/test.ts ================================================ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, platformBrowserDynamicTesting(), ); ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/tsconfig.app.json ================================================ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/app", "types": [ "chrome" ] }, "files": [ "src/main.ts", "src/polyfills.ts", "src/background.ts" ], "include": [ "src/**/*.d.ts" ] } ================================================ FILE: desktop/angular/projects/portmaster-chrome-extension/tsconfig.spec.json ================================================ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/spec", "types": [ "jasmine" ] }, "files": [ "src/test.ts", "src/polyfills.ts" ], "include": [ "src/**/*.spec.ts", "src/**/*.d.ts" ] } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/README.md ================================================ # PortmasterApi This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.0. ## Code scaffolding Run `ng generate component component-name --project portmaster-api` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project portmaster-api`. > Note: Don't forget to add `--project portmaster-api` or else it will be added to the default project in your `angular.json` file. ## Build Run `ng build portmaster-api` to build the project. The build artifacts will be stored in the `dist/` directory. ## Publishing After building your library with `ng build portmaster-api`, go to the dist folder `cd dist/portmaster-api` and run `npm publish`. ## Running unit tests Run `ng test portmaster-api` to execute the unit tests via [Karma](https://karma-runner.github.io). ## Further help To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. ================================================ FILE: desktop/angular/projects/safing/portmaster-api/karma.conf.js ================================================ // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html module.exports = function (config) { config.set({ basePath: '', frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('karma-coverage'), require('@angular-devkit/build-angular/plugins/karma') ], client: { jasmine: { // you can add configuration options for Jasmine here // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html // for example, you can disable the random execution with `random: false` // or set a specific seed with `seed: 4321` }, clearContext: false // leave Jasmine Spec Runner output visible in browser }, jasmineHtmlReporter: { suppressAll: true // removes the duplicated traces }, coverageReporter: { dir: require('path').join(__dirname, '../../../coverage/safing/portmaster-api'), subdir: '.', reporters: [ { type: 'html' }, { type: 'text-summary' } ] }, reporters: ['progress', 'kjhtml'], port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, restartOnFileChange: true }); }; ================================================ FILE: desktop/angular/projects/safing/portmaster-api/ng-package.json ================================================ { "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", "dest": "../../../dist-lib/safing/portmaster-api", "lib": { "entryFile": "src/public-api.ts" } } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/package.json ================================================ { "name": "@safing/portmaster-api", "version": "0.0.1", "peerDependencies": { "@angular/common": "^14.0.0", "@angular/core": "^14.0.0" }, "dependencies": { "tslib": "^2.3.0" }, "devDependencies": { "@types/jasmine": "^4.0.3" } } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/app-profile.service.ts ================================================ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Inject, Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { filter, finalize, map, mergeMap, share, take } from 'rxjs/operators'; import { AppProfile, FlatConfigObject, LayeredProfile, TagDescription, flattenProfileConfig, } from './app-profile.types'; import { PORTMASTER_HTTP_API_ENDPOINT, PortapiService, } from './portapi.service'; import { Process } from './portapi.types'; @Injectable() export class AppProfileService { private watchedProfiles = new Map>(); constructor( private portapi: PortapiService, private http: HttpClient, @Inject(PORTMASTER_HTTP_API_ENDPOINT) private httpAPI: string ) { } /** * Returns the database key of a profile. * * @param source The source of the profile. * @param id The profile ID. */ getKey(source: string, id: string): string; /** * Returns the database key of a profile * * @param p The app-profile itself.. */ getKey(p: AppProfile): string; getKey(idOrSourceOrProfile: string | AppProfile, id?: string): string { if (typeof idOrSourceOrProfile === 'object') { return this.getKey(idOrSourceOrProfile.Source, idOrSourceOrProfile.ID); } let key = idOrSourceOrProfile; if (!!id) { key = `core:profiles/${idOrSourceOrProfile}/${id}`; } return key; } /** * Load an application profile. * * @param sourceAndId The full profile ID including source */ getAppProfile(sourceAndId: string): Observable; /** * Load an application profile. * * @param source The source of the profile * @param id The ID of the profile */ getAppProfile(source: string, id: string): Observable; getAppProfile( sourceOrSourceAndID: string, id?: string ): Observable { let source = sourceOrSourceAndID; if (id !== undefined) { source += '/' + id; } const key = `core:profiles/${source}`; if (this.watchedProfiles.has(key)) { return this.watchedProfiles.get(key)!.pipe(take(1)); } return this.getAppProfileFromKey(key); } setProfileIcon( content: string | ArrayBuffer, mimeType: string ): Observable<{ filename: string }> { return this.http.post<{ filename: string }>( `${this.httpAPI}/v1/profile/icon`, content, { headers: new HttpHeaders({ 'Content-Type': mimeType, }), } ); } /** * Loads an application profile by it's database key. * * @param key The key of the application profile. */ getAppProfileFromKey(key: string): Observable { return this.portapi.get(key); } /** * Loads the global-configuration profile. */ globalConfig(): Observable { return this.getAppProfile('special', 'global-config').pipe( map((profile) => flattenProfileConfig(profile.Config)) ); } /** Returns all possible process tags. */ tagDescriptions(): Observable { return this.http .get<{ Tags: TagDescription[] }>(`${this.httpAPI}/v1/process/tags`) .pipe(map((result) => result.Tags)); } /** * Watches an application profile for changes. * * @param source The source of the profile * @param id The ID of the profile */ watchAppProfile(sourceAndId: string): Observable; /** * Watches an application profile for changes. * * @param source The source of the profile * @param id The ID of the profile */ watchAppProfile(source: string, id: string): Observable; watchAppProfile(sourceAndId: string, id?: string): Observable { let key = ''; if (id === undefined) { key = sourceAndId; if (!key.startsWith('core:profiles/')) { key = `core:profiles/${key}`; } } else { key = `core:profiles/${sourceAndId}/${id}`; } if (this.watchedProfiles.has(key)) { return this.watchedProfiles.get(key)!; } const stream = this.portapi.get(key).pipe( mergeMap(() => this.portapi.watch(key)), finalize(() => { console.log( 'watchAppProfile: removing cached profile stream for ' + key ); this.watchedProfiles.delete(key); }), share({ connector: () => new BehaviorSubject(null), resetOnRefCountZero: true, }), filter((profile) => profile !== null) ) as Observable; this.watchedProfiles.set(key, stream); return stream; } /** @deprecated use saveProfile instead */ saveLocalProfile(profile: AppProfile): Observable { return this.saveProfile(profile); } /** * Save an application profile. * * @param profile The profile to save */ saveProfile(profile: AppProfile): Observable { profile.LastEdited = Math.floor(new Date().getTime() / 1000); return this.portapi.update( `core:profiles/${profile.Source}/${profile.ID}`, profile ); } /** * Watch all application profiles */ watchProfiles(): Observable { return this.portapi.watchAll('core:profiles/'); } watchLayeredProfile(source: string, id: string): Observable; /** * Watches the layered runtime profile for a given application * profile. * * @param profile The app profile */ watchLayeredProfile(profile: AppProfile): Observable; watchLayeredProfile( profileOrSource: string | AppProfile, id?: string ): Observable { if (typeof profileOrSource == 'object') { id = profileOrSource.ID; profileOrSource = profileOrSource.Source; } const key = `runtime:layeredProfile/${profileOrSource}/${id}`; return this.portapi.watch(key); } /** * Loads the layered runtime profile for a given application * profile. * * @param profile The app profile */ getLayeredProfile(profile: AppProfile): Observable { const key = `runtime:layeredProfile/${profile.Source}/${profile.ID}`; return this.portapi.get(key); } /** * Delete an application profile. * * @param profile The profile to delete */ deleteProfile(profile: AppProfile): Observable { return this.portapi.delete(`core:profiles/${profile.Source}/${profile.ID}`); } getProcessesByProfile(profileOrId: AppProfile | string): Observable { if (typeof profileOrId === 'object') { profileOrId = profileOrId.Source + "/" + profileOrId.ID } return this.http.get(`${this.httpAPI}/v1/process/list/by-profile/${profileOrId}`) } getProcessByPid(pid: number): Observable { return this.http.get(`${this.httpAPI}/v1/process/group-leader/${pid}`) } } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/app-profile.types.ts ================================================ import { BaseSetting, OptionValueType, SettingValueType } from './config.types'; import { SecurityLevel } from './core.types'; import { Record } from './portapi.types'; export interface ConfigMap { [key: string]: ConfigObject; } export type ConfigObject = OptionValueType | ConfigMap; export interface FlatConfigObject { [key: string]: OptionValueType; } export interface LayeredProfile extends Record { // LayerIDs is a list of all profiles that are used // by this layered profile. Profiles are evaluated in // order. LayerIDs: string[]; // The current revision counter of the layered profile. RevisionCounter: number; } export enum FingerprintType { Tag = 'tag', Cmdline = 'cmdline', Env = 'env', Path = 'path', } export enum FingerpringOperation { Equal = 'equals', Prefix = 'prefix', Regex = 'regex', } export interface Fingerprint { Type: FingerprintType; Key: string; Operation: FingerpringOperation; Value: string; } export interface TagDescription { ID: string; Name: string; Description: string; } export interface Icon { Type: '' | 'database' | 'path' | 'api'; Source: '' | 'user' | 'import' | 'core' | 'ui'; Value: string; } export interface AppProfile extends Record { ID: string; LinkedPath: string; // deprecated PresentationPath: string; Fingerprints: Fingerprint[]; Created: number; LastEdited: number; Config?: ConfigMap; Description: string; Warning: string; WarningLastUpdated: string; Homepage: string; Icons: Icon[]; Name: string; Internal: boolean; SecurityLevel: SecurityLevel; Source: 'local'; } // flattenProfileConfig returns a flat version of a nested ConfigMap where each property // can be used as the database key for the associated setting. export function flattenProfileConfig( p?: ConfigMap, prefix = '' ): FlatConfigObject { if (p === null || p === undefined) { return {} } let result: FlatConfigObject = {}; Object.keys(p).forEach((key) => { const childPrefix = prefix === '' ? key : `${prefix}/${key}`; const prop = p[key]; if (isConfigMap(prop)) { const flattened = flattenProfileConfig(prop, childPrefix); result = mergeObjects(result, flattened); return; } result[childPrefix] = prop; }); return result; } /** * Returns the current value (or null) of a setting stored in a config * map by path. * * @param obj The ConfigMap object * @param path The path of the setting separated by foward slashes. */ export function getAppSetting( obj: ConfigMap | null | undefined, path: string ): T | null { if (obj === null || obj === undefined) { return null } const parts = path.split('/'); let iter = obj; for (let idx = 0; idx < parts.length; idx++) { const propName = parts[idx]; if (iter[propName] === undefined) { return null; } const value = iter[propName]; if (idx === parts.length - 1) { return value as T; } if (!isConfigMap(value)) { return null; } iter = value; } return null; } export function getActualValue>( s: S ): SettingValueType { if (s.Value !== undefined) { return s.Value; } if (s.GlobalDefault !== undefined) { return s.GlobalDefault; } return s.DefaultValue; } /** * Sets the value of a settings inside the nested config object. * * @param obj THe config object * @param path The path of the setting * @param value The new value to set. */ export function setAppSetting(obj: ConfigObject, path: string, value: any) { const parts = path.split('/'); if (typeof obj !== 'object' || Array.isArray(obj)) { return; } let iter = obj; for (let idx = 0; idx < parts.length; idx++) { const propName = parts[idx]; if (idx === parts.length - 1) { if (value === undefined) { delete iter[propName]; } else { iter[propName] = value; } return; } if (iter[propName] === undefined) { iter[propName] = {}; } iter = iter[propName] as ConfigMap; } } /** Typeguard to ensure v is a ConfigMap */ function isConfigMap(v: any): v is ConfigMap { return typeof v === 'object' && !Array.isArray(v); } /** * Returns a new flat-config object that contains values from both * parameters. * * @param a The first config object * @param b The second config object */ function mergeObjects( a: FlatConfigObject, b: FlatConfigObject ): FlatConfigObject { var res: FlatConfigObject = {}; Object.keys(a).forEach((key) => { res[key] = a[key]; }); Object.keys(b).forEach((key) => { res[key] = b[key]; }); return res; } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/config.service.ts ================================================ import { Injectable, TrackByFunction } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { distinctUntilChanged, filter, map, share, toArray } from 'rxjs/operators'; import { BaseSetting, BoolSetting, OptionType, Setting, SettingValueType } from './config.types'; import { PortapiService } from './portapi.service'; @Injectable({ providedIn: 'root' }) export class ConfigService { networkRatingEnabled$: Observable; /** * A {@link TrackByFunction} for tracking settings. */ static trackBy: TrackByFunction = (_: number, obj: Setting) => obj.Name; readonly trackBy = ConfigService.trackBy; /** configPrefix is the database key prefix for the config db */ readonly configPrefix = "config:"; constructor(private portapi: PortapiService) { this.networkRatingEnabled$ = this.watch("core/enableNetworkRating") .pipe( share({ connector: () => new BehaviorSubject(false) }), ) } /** * Loads a configuration setting from the database. * * @param key The key of the configuration setting. */ get(key: string): Observable { return this.portapi.get(this.configPrefix + key); } /** * Returns all configuration settings that match query. Note that in * contrast to {@link PortAPI} settings values are collected into * an array before being emitted. This allows simple usage in *ngFor * and friends. * * @param query The query used to search for configuration settings. */ query(query: string): Observable { return this.portapi.query(this.configPrefix + query) .pipe( map(setting => setting.data), toArray() ); } /** * Save a setting. * * @param s The setting to save. Note that the new value should already be set to {@property Value}. */ save(s: Setting): Observable; /** * Save a setting. * * @param key The key of the configuration setting * @param value The new value of the setting. */ save(key: string, value: any): Observable; // save is overloaded, see above. save(s: Setting | string, v?: any): Observable { if (typeof s === 'string') { return this.portapi.update(this.configPrefix + s, { Key: s, Value: v, }); } return this.portapi.update(this.configPrefix + s.Key, s); } /** * Watch a configuration setting. * * @param key The key of the setting to watch. */ watch(key: string): Observable> { return this.portapi.qsub, any>>(this.configPrefix + key) .pipe( filter(value => value.key === this.configPrefix + key), // qsub does a query so filter for our key. map(value => value.data), map(value => value.Value !== undefined ? value.Value : value.DefaultValue), distinctUntilChanged(), ) } /** * Tests if a value is valid for a given option. * * @param spec The option specification (as returned by get()). * @param value The value that should be tested. */ validate(spec: S, value: SettingValueType) { if (!spec.ValidationRegex) { return; } const re = new RegExp(spec.ValidationRegex); switch (spec.OptType) { case OptionType.Int: case OptionType.Bool: // todo(ppacher): do we validate that? return case OptionType.String: if (!re.test(value as string)) { throw new Error(`${value} does not match ${spec.ValidationRegex}`) } return; case OptionType.StringArray: (value as string[]).forEach(v => { if (!re.test(v as string)) { throw new Error(`${value} does not match ${spec.ValidationRegex}`) } }); return } } } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/config.types.ts ================================================ import { FeatureID } from './features'; import { Record } from './portapi.types'; import { deepClone } from './utils'; /** * ExpertiseLevel defines all available expertise levels. */ export enum ExpertiseLevel { User = 'user', Expert = 'expert', Developer = 'developer', } export enum ExpertiseLevelNumber { user = 0, expert = 1, developer = 2 } export function getExpertiseLevelNumber(lvl: ExpertiseLevel): ExpertiseLevelNumber { switch (lvl) { case ExpertiseLevel.User: return ExpertiseLevelNumber.user; case ExpertiseLevel.Expert: return ExpertiseLevelNumber.expert; case ExpertiseLevel.Developer: return ExpertiseLevelNumber.developer } } /** * OptionType defines the type of an option as stored in * the backend. Note that ExternalOptionHint may be used * to request a different visual representation and edit * menu on a per-option basis. */ export enum OptionType { String = 1, StringArray = 2, Int = 3, Bool = 4, } /** * Converts an option type to it's string representation. * * @param opt The option type to convert */ export function optionTypeName(opt: OptionType): string { switch (opt) { case OptionType.String: return 'string'; case OptionType.StringArray: return '[]string'; case OptionType.Int: return 'int' case OptionType.Bool: return 'bool' } } /** The actual type an option value can be */ export type OptionValueType = string | string[] | number | boolean; /** Type-guard for string option types */ export function isStringType(opt: OptionType, vt: OptionValueType): vt is string { return opt === OptionType.String; } /** Type-guard for string-array option types */ export function isStringArrayType(opt: OptionType, vt: OptionValueType): vt is string[] { return opt === OptionType.StringArray; } /** Type-guard for number option types */ export function isNumberType(opt: OptionType, vt: OptionValueType): vt is number { return opt === OptionType.Int; } /** Type-guard for boolean option types */ export function isBooleanType(opt: OptionType, vt: OptionValueType): vt is boolean { return opt === OptionType.Bool; } /** * ReleaseLevel defines the available release and maturity * levels. */ export enum ReleaseLevel { Stable = 0, Beta = 1, Experimental = 2, } export function releaseLevelFromName(name: 'stable' | 'beta' | 'experimental'): ReleaseLevel { switch (name) { case 'stable': return ReleaseLevel.Stable; case 'beta': return ReleaseLevel.Beta; case 'experimental': return ReleaseLevel.Experimental; } } /** * releaseLevelName returns a string representation of the * release level. * * @args level The release level to convert. */ export function releaseLevelName(level: ReleaseLevel): string { switch (level) { case ReleaseLevel.Stable: return 'stable' case ReleaseLevel.Beta: return 'beta' case ReleaseLevel.Experimental: return 'experimental' } } /** * ExternalOptionHint tells the UI to use a different visual * representation and edit menu that the options value would * imply. */ export enum ExternalOptionHint { SecurityLevel = 'security level', EndpointList = 'endpoint list', FilterList = 'filter list', OneOf = 'one-of', OrderedList = 'ordered' } /** A list of well-known option annotation keys. */ export enum WellKnown { DisplayHint = "safing/portbase:ui:display-hint", Order = "safing/portbase:ui:order", Unit = "safing/portbase:ui:unit", Category = "safing/portbase:ui:category", Subsystem = "safing/portbase:module:subsystem", Stackable = "safing/portbase:options:stackable", QuickSetting = "safing/portbase:ui:quick-setting", Requires = "safing/portbase:config:requires", RestartPending = "safing/portbase:options:restart-pending", EndpointListVerdictNames = "safing/portmaster:ui:endpoint-list:verdict-names", RequiresFeatureID = "safing/portmaster:ui:config:requires-feature", RequiresUIReload = "safing/portmaster:ui:requires-reload", } /** * Annotations describes the annoations object of a configuration * setting. Well-known annotations are stricktly typed. */ export interface Annotations { // Well known option annoations and their // types. [WellKnown.DisplayHint]?: ExternalOptionHint; [WellKnown.Order]?: number; [WellKnown.Unit]?: string; [WellKnown.Category]?: string; [WellKnown.Subsystem]?: string; [WellKnown.Stackable]?: true; [WellKnown.QuickSetting]?: QuickSetting | QuickSetting[] | CountrySelectionQuickSetting | CountrySelectionQuickSetting[]; [WellKnown.Requires]?: ValueRequirement | ValueRequirement[]; [WellKnown.RequiresFeatureID]?: FeatureID | FeatureID[]; [WellKnown.RequiresUIReload]?: unknown, // Any thing else... [key: string]: any; } export interface PossilbeValue { /** Name is the name of the value and should be displayed */ Name: string; /** Description may hold an additional description of the value */ Description: string; /** Value is the actual value expected by the portmaster */ Value: T; } export interface QuickSetting { // Name is the name of the quick setting. Name: string; // Value is the value that the quick-setting configures. It must match // the expected value type of the annotated option. Value: T; // Action defines the action of the quick setting. Action: 'replace' | 'merge-top' | 'merge-bottom'; } export interface CountrySelectionQuickSetting extends QuickSetting { // Filename of the flag to be used. // In most cases this will be the 2-letter country code, but there are also special flags. FlagID: string; } export interface ValueRequirement { // Key is the configuration key of the required setting. Key: string; // Value is the required value of the linked setting. Value: any; } /** * BaseSetting describes the general shape of a portbase config setting. */ export interface BaseSetting extends Record { // Value is the value of a setting. Value?: T; // DefaultValue is the default value of a setting. DefaultValue: T; // Description is a short description. Description?: string; // ExpertiseLevel defines the required expertise level for // this setting to show up. ExpertiseLevel: ExpertiseLevelNumber; // Help may contain a longer help text for this option. Help?: string; // Key is the database key. Key: string; // Name is the name of the option. Name: string; // OptType is the option's basic type. OptType: O; // Annotations holds option specific annotations. Annotations: Annotations; // ReleaseLevel defines the release level of the feature // or settings changed by this option. ReleaseLevel: ReleaseLevel; // RequiresRestart may be set to true if the service requires // a restart after this option has been changed. RequiresRestart?: boolean; // ValidateRegex defines the regex used to validate this option. // The regex is used in Golang but is expected to be valid in // JavaScript as well. ValidationRegex?: string; PossibleValues?: PossilbeValue[]; // GlobalDefault holds the global default value and is used in the app settings // This property is NOT defined inside the portmaster! GlobalDefault?: T; } export type IntSetting = BaseSetting; export type StringSetting = BaseSetting; export type StringArraySetting = BaseSetting; export type BoolSetting = BaseSetting; /** * Apply a quick setting to a value. * * @param current The current value of the setting. * @param qs The quick setting to apply. */ export function applyQuickSetting(current: V | null, qs: QuickSetting): V | null { if (qs.Action === 'replace' || !qs.Action) { return deepClone(qs.Value); } if ((!Array.isArray(current) && current !== null) || !Array.isArray(qs.Value)) { console.warn(`Tried to ${qs.Action} quick-setting on non-array type`); return current; } const clone = deepClone(current); let missing: any[] = []; qs.Value.forEach(val => { if (clone.includes(val)) { return } missing.push(val); }); if (qs.Action === 'merge-bottom') { return clone.concat(missing) as V; } return missing.concat(clone) as V; } /** * Parses the ValidationRegex of a setting and returns a list * of supported values. * * @param s The setting to extract support values from. */ export function parseSupportedValues(s: S): SettingValueType[] { if (!s.ValidationRegex) { return []; } const values = s.ValidationRegex.match(/\w+/gmi); const result: SettingValueType[] = []; let converter: (s: string) => any; switch (s.OptType) { case OptionType.Bool: converter = s => s === 'true'; break; case OptionType.Int: converter = s => +s; break; case OptionType.String: case OptionType.StringArray: converter = s => s break } values?.forEach(val => { result.push(converter(val)) }); return result; } /** * isDefaultValue checks if value is the settings default value. * It supports all available settings type and fallsback to use * JSON encoded string comparision (JS JSON.stringify is stable). */ export function isDefaultValue(value: T | undefined | null, defaultValue: T): boolean { if (value === undefined) { return true; } const isObject = typeof value === 'object'; const isDefault = isObject ? JSON.stringify(value) === JSON.stringify(defaultValue) : value === defaultValue; return isDefault; } /** * SettingValueType is used to infer the type of a settings from it's default value. * Use like this: * * validate(spec: S, value SettingValueType) { ... } */ export type SettingValueType = S extends { DefaultValue: infer T } ? T : any; export type Setting = IntSetting | StringSetting | StringArraySetting | BoolSetting; ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/core.types.ts ================================================ import { TrackByFunction } from '@angular/core'; export enum SecurityLevel { Off = 0, Normal = 1, High = 2, Extreme = 4, } export enum RiskLevel { Off = 'off', Auto = 'auto', Low = 'low', Medium = 'medium', High = 'high' } /** Interface capturing any object that has an ID member. */ export interface Identifyable { ID: string | number; } /** A TrackByFunction for all Identifyable objects. */ export const trackById: TrackByFunction = (_: number, obj: Identifyable) => { return obj.ID; } export function getEnumKey(enumLike: any, value: string | number): string { if (typeof value === 'string') { return value.toLowerCase() } return (enumLike[value] as string).toLowerCase() } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/debug-api.service.ts ================================================ import { HttpClient } from '@angular/common/http'; import { Inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { PORTMASTER_HTTP_API_ENDPOINT } from './portapi.service'; @Injectable({ providedIn: 'root', }) export class DebugAPI { constructor( private http: HttpClient, @Inject(PORTMASTER_HTTP_API_ENDPOINT) private httpAPI: string, ) { } ping(): Observable { return this.http.get(`${this.httpAPI}/v1/ping`, { responseType: 'text' }) } ready(): Observable { return this.http.get(`${this.httpAPI}/v1/ready`, { responseType: 'text' }) } getStack(): Observable { return this.http.get(`${this.httpAPI}/v1/debug/stack`, { responseType: 'text' }) } getDebugInfo(style = 'github'): Observable { return this.http.get(`${this.httpAPI}/v1/debug/info`, { params: { style, }, responseType: 'text', }) } getCoreDebugInfo(style = 'github'): Observable { return this.http.get(`${this.httpAPI}/v1/debug/core`, { params: { style, }, responseType: 'text', }) } getProfileDebugInfo(source: string, id: string, style = 'github'): Observable { return this.http.get(`${this.httpAPI}/v1/debug/network`, { params: { profile: `${source}/${id}`, style, }, responseType: 'text', }) } } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/features.ts ================================================ export enum FeatureID { None = "", SPN = "spn", PrioritySupport = "support", History = "history", Bandwidth = "bw-vis", VPNCompat = "vpn-compat", } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/meta-api.service.ts ================================================ import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http'; import { Inject, Injectable, Optional } from '@angular/core'; import { Observable, of, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { PORTMASTER_HTTP_API_ENDPOINT } from './portapi.service'; export interface MetaEndpointParameter { Method: string; Field: string; Value: string; Description: string; } export interface MetaEndpoint { Path: string; MimeType: string; Read: number; Write: number; Name: string; Description: string; Parameters: MetaEndpointParameter[]; } export interface AuthPermission { Read: number; Write: number; ReadRole: string; WriteRole: string; } export interface MyProfileResponse { profile: string; source: string; name: string; } export interface AuthKeyResponse { key: string; validUntil: string; } @Injectable() export class MetaAPI { constructor( private http: HttpClient, @Inject(PORTMASTER_HTTP_API_ENDPOINT) @Optional() private httpEndpoint: string, ) { if (!this.httpEndpoint) { this.httpEndpoint = `http://localhost:817/api`; console.warn("[portmaster-api: MetaAPI] No HTTP API endpoint provided, using default: " + this.httpEndpoint); } } listEndpoints(): Observable { return this.http.get(`${this.httpEndpoint}/v1/endpoints`) } permissions(): Observable { return this.http.get(`${this.httpEndpoint}/v1/auth/permissions`) } myProfile(): Observable { return this.http.get(`${this.httpEndpoint}/v1/app/profile`) } requestApplicationAccess(appName: string, read: 'user' | 'admin' = 'user', write: 'user' | 'admin' = 'user'): Observable { let params = new HttpParams() .set("app-name", appName) .set("read", read) .set("write", write) return this.http.get(`${this.httpEndpoint}/v1/app/auth`, { params }) } login(bearer: string): Observable; login(username: string, password: string): Observable; login(usernameOrBearer: string, password?: string): Observable { let login: Observable; if (!!password) { login = this.http.get(`${this.httpEndpoint}/v1/auth/basic`, { headers: { 'Authorization': `Basic ${btoa(usernameOrBearer + ":" + password)}` } }) } else { login = this.http.get(`${this.httpEndpoint}/v1/auth/bearer`, { headers: { 'Authorization': `Bearer ${usernameOrBearer}` } }) } return login.pipe( map(() => true), catchError(err => { if (err instanceof HttpErrorResponse) { if (err.status === 401) { return of(false); } } return throwError(() => err) }) ) } logout(): Observable { return this.http.get(`${this.httpEndpoint}/v1/auth/reset`); } } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/module.ts ================================================ import { ModuleWithProviders, NgModule } from "@angular/core"; import { AppProfileService } from "./app-profile.service"; import { ConfigService } from "./config.service"; import { DebugAPI } from "./debug-api.service"; import { MetaAPI } from "./meta-api.service"; import { Netquery } from "./netquery.service"; import { PortapiService, PORTMASTER_HTTP_API_ENDPOINT, PORTMASTER_WS_API_ENDPOINT } from "./portapi.service"; import { SPNService } from "./spn.service"; import { WebsocketService } from "./websocket.service"; import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { TauriHttpInterceptor } from "./platform-specific/tauri/tauri-http-interceptor"; import { IsTauriEnvironment } from "./platform-specific/utils"; export interface ModuleConfig { httpAPI?: string; websocketAPI?: string; } // Factory function to provide the appropriate HTTP client configuration // // This function determines the appropriate HTTP client configuration based on the runtime environment. // If the application is running in a Tauri environment, it uses the TauriHttpInterceptor to ensure // that all HTTP requests are made from the application binary instead of the WebView instance. // This allows for more direct and controlled communication with the Portmaster API. // In other environments (e.g., browser, Electron), the standard HttpClient is used without any interceptors. export function HttpClientProviderFactory() { if (IsTauriEnvironment()) { console.log("[portmaster-api] Running under Tauri - using TauriHttpClient"); return provideHttpClient( withInterceptors([TauriHttpInterceptor]) ); } else { console.log("[portmaster-api] Running in browser - using default HttpClient"); return provideHttpClient(); } } @NgModule({}) export class PortmasterAPIModule { /** * Configures a module with additional providers. * * @param cfg The module configuration defining the Portmaster HTTP and Websocket API endpoints. */ static forRoot(cfg: ModuleConfig = {}): ModuleWithProviders { if (!cfg.httpAPI) { cfg.httpAPI = `http://${window.location.host}/api`; console.warn("[portmaster-api] No HTTP API endpoint provided, using default: " + cfg.httpAPI); } if (!cfg.websocketAPI) { cfg.websocketAPI = `ws://${window.location.host}/api/database/v1`; console.warn("[portmaster-api] No WebSocket API endpoint provided, using default: " + cfg.websocketAPI); } return { ngModule: PortmasterAPIModule, providers: [ HttpClientProviderFactory(), PortapiService, WebsocketService, MetaAPI, ConfigService, AppProfileService, DebugAPI, Netquery, SPNService, { provide: PORTMASTER_HTTP_API_ENDPOINT, useValue: cfg.httpAPI, }, { provide: PORTMASTER_WS_API_ENDPOINT, useValue: cfg.websocketAPI } ] } } } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/netquery.service.ts ================================================ import { HttpClient, HttpParams, HttpResponse } from "@angular/common/http"; import { Inject, Injectable } from "@angular/core"; import { Observable, forkJoin, of } from "rxjs"; import { catchError, map, mergeMap } from "rxjs/operators"; import { AppProfileService } from "./app-profile.service"; import { AppProfile } from "./app-profile.types"; import { DNSContext, IPScope, Reason, TLSContext, TunnelContext, Verdict } from "./network.types"; import { PORTMASTER_HTTP_API_ENDPOINT, PortapiService } from "./portapi.service"; import { Container } from "postcss"; export interface FieldSelect { field: string; } export interface FieldAsSelect { $field: { field: string; as: string; } } export interface Count { $count: { field: string; distinct?: boolean; as?: string; } } export interface Sum { $sum: { condition: Condition; as: string; distinct?: boolean; } | { field: string; as: string; distinct?: boolean; } } export interface Min { $min: { condition: Condition; as: string; distinct?: boolean; } | { field: string; as: string; distinct?: boolean; } } export interface Distinct { $distinct: string; } export type Select = FieldSelect | FieldAsSelect | Count | Distinct | Sum | Min; export interface Equal { $eq: any; } export interface NotEqual { $ne: any; } export interface Like { $like: string; } export interface In { $in: any[]; } export interface NotIn { $notin: string[]; } export interface Greater { $gt: number; } export interface GreaterOrEqual { $ge: number; } export interface Less { $lt: number; } export interface LessOrEqual { $le: number; } export type Matcher = Equal | NotEqual | Like | In | NotIn | Greater | GreaterOrEqual | Less | LessOrEqual; export interface OrderBy { field: string; desc?: boolean; } export interface Condition { [key: string]: string | Matcher | (string | Matcher)[]; } export interface TextSearch { fields: string[]; value: string; } export enum Database { Live = "main", History = "history" } export interface Query { select?: string | Select | (Select | string)[]; query?: Condition; orderBy?: string | OrderBy | (OrderBy | string)[]; textSearch?: TextSearch; groupBy?: string[]; pageSize?: number; page?: number; databases?: Database[]; } export interface NetqueryConnection { id: string; allowed: boolean | null; profile: string; path: string; type: 'dns' | 'ip'; external: boolean; ip_version: number; ip_protocol: number; local_ip: string; local_port: number; remote_ip: string; remote_port: number; domain: string; country: string; asn: number; as_owner: string; latitude: number; longitude: number; scope: IPScope; verdict: Verdict; started: string; ended: string; tunneled: boolean; encrypted: boolean; internal: boolean; direction: 'inbound' | 'outbound'; profile_revision: number; exit_node?: string; extra_data?: { pid?: number; processCreatedAt?: number; cname?: string[]; blockedByLists?: string[]; blockedEntities?: string[]; reason?: Reason; tunnel?: TunnelContext; dns?: DNSContext; tls?: TLSContext; }; profile_name: string; active: boolean; bytes_received: number; bytes_sent: number; } export interface ChartResult { timestamp: number; value: number; countBlocked: number; } export interface QueryResult extends Partial { [key: string]: any; } export interface Identities { exit_node: string; count: number; } export interface IProfileStats { ID: string; Name: string; size: number; empty: boolean; identities: Identities[]; countAllowed: number; countUnpermitted: number; countAliveConnections: number; bytes_sent: number; bytes_received: number; } type BatchResponse = { [key in keyof T]: QueryResult[] } interface BatchRequest { [key: string]: Query } interface BandwidthBaseResult { timestamp: number; incoming: number; outgoing: number; } export type ConnKeys = keyof NetqueryConnection export type BandwidthChartResult = { [key in K]: NetqueryConnection[K]; } & BandwidthBaseResult export type ProfileBandwidthChartResult = BandwidthChartResult<'profile'>; export type ConnectionBandwidthChartResult = BandwidthChartResult<'id'>; @Injectable({ providedIn: 'root' }) export class Netquery { constructor( private http: HttpClient, private profileService: AppProfileService, private portapi: PortapiService, @Inject(PORTMASTER_HTTP_API_ENDPOINT) private httpAPI: string, ) { } query(query: Query, origin: string): Observable { return this.http.post<{ results: QueryResult[] }>(`${this.httpAPI}/v1/netquery/query`, query, { params: new HttpParams().set("origin", origin) }) .pipe(map(res => res.results || [])); } batch(queries: T): Observable> { return this.http.post>(`${this.httpAPI}/v1/netquery/query/batch`, queries) } cleanProfileHistory(profileIDs: string | string[]): Observable> { return this.http.post(`${this.httpAPI}/v1/netquery/history/clear`, { profileIDs: Array.isArray(profileIDs) ? profileIDs : [profileIDs] }, { observe: 'response', responseType: 'text', reportProgress: false, } ) } profileBandwidthChart(profile?: string[], interval?: number): Observable<{ [profile: string]: ProfileBandwidthChartResult[] }> { const cond: Condition = {} if (!!profile) { cond['profile'] = profile } return this.bandwidthChart(cond, ['profile'], interval) .pipe( map(results => { const obj: { [connId: string]: ProfileBandwidthChartResult[] } = {}; results?.forEach(row => { const arr = obj[row.profile] || [] arr.push(row) obj[row.profile] = arr }) return obj }) ) } bandwidthChart(query: Condition, groupBy?: K[], interval?: number): Observable[]> { return this.http.post<{ results: BandwidthChartResult[] }>(`${this.httpAPI}/v1/netquery/charts/bandwidth`, { interval, groupBy, query, }) .pipe( map(response => response.results), ) } connectionBandwidthChart(connIds: string[], interval?: number): Observable<{ [connId: string]: ConnectionBandwidthChartResult[] }> { const cond: Condition = {} if (!!connIds) { cond['id'] = connIds } return this.bandwidthChart(cond, ['id'], interval) .pipe( map(results => { const obj: { [connId: string]: ConnectionBandwidthChartResult[] } = {}; results?.forEach(row => { const arr = obj[row.id] || [] arr.push(row) obj[row.id] = arr }) return obj }) ) } activeConnectionChart(cond: Condition, textSearch?: TextSearch): Observable { return this.http.post<{ results: ChartResult[] }>(`${this.httpAPI}/v1/netquery/charts/connection-active`, { query: cond, textSearch, }) .pipe(map(res => { const now = new Date(); let data: ChartResult[] = []; let lastPoint: ChartResult | null = { timestamp: Math.floor(now.getTime() / 1000 - 600), value: 0, countBlocked: 0, }; res.results?.forEach(point => { if (!!lastPoint && lastPoint.timestamp < (point.timestamp - 10)) { for (let i = lastPoint.timestamp; i < point.timestamp; i += 10) { data.push({ timestamp: i, value: 0, countBlocked: 0, }) } } data.push(point); lastPoint = point; }) const lastPointTs = Math.round(now.getTime() / 1000); if (!!lastPoint && lastPoint.timestamp < (lastPointTs - 20)) { for (let i = lastPoint.timestamp; i < lastPointTs; i += 20) { data.push({ timestamp: i, value: 0, countBlocked: 0 }) } } return data; })); } getActiveProfileIDs(): Observable { return this.query({ select: [ 'profile', ], groupBy: [ 'profile', ], }, 'get-active-profile-ids').pipe( map(result => { return result.map(res => res.profile!); }) ) } getActiveProfiles(): Observable { return this.getActiveProfileIDs() .pipe( mergeMap(profiles => forkJoin(profiles.map(pid => this.profileService.getAppProfile(pid)))) ) } getProfileStats(query?: Condition): Observable { let profileCache = new Map(); return this.batch({ verdicts: { select: [ 'profile', 'verdict', { $count: { field: '*', as: 'totalCount' } }, ], groupBy: [ 'profile', 'verdict', ], query: query, }, conns: { select: [ 'profile', { $count: { field: '*', as: 'totalCount' } }, { $count: { field: 'ended', as: 'countEnded' } }, { $sum: { field: 'bytes_sent', as: 'bytes_sent' } }, { $sum: { field: 'bytes_received', as: 'bytes_received' } }, ], groupBy: [ 'profile', ], query: query, }, identities: { select: [ 'profile', 'exit_node', { $count: { field: '*', as: 'totalCount' } } ], groupBy: [ 'profile', 'exit_node', ], query: { ...query, exit_node: { $ne: "", }, }, } }).pipe( map(result => { let statsMap = new Map(); const getOrCreate = (id: string) => { let stats = statsMap.get(id) || { ID: id, Name: 'Deleted', countAliveConnections: 0, countAllowed: 0, countUnpermitted: 0, empty: true, identities: [], size: 0, bytes_received: 0, bytes_sent: 0 }; statsMap.set(id, stats); return stats; } result.verdicts?.forEach(res => { const stats = getOrCreate(res.profile!); switch (res.verdict) { case Verdict.Accept: case Verdict.RerouteToNs: case Verdict.RerouteToTunnel: case Verdict.Undeterminable: stats.size += res.totalCount stats.countAllowed += res.totalCount; break; case Verdict.Block: case Verdict.Drop: case Verdict.Failed: case Verdict.Undecided: stats.size += res.totalCount stats.countUnpermitted += res.totalCount; break; } stats.empty = stats.size == 0; }) result.conns?.forEach(res => { const stats = getOrCreate(res.profile!); stats.countAliveConnections = res.totalCount - res.countEnded; stats.bytes_received += res.bytes_received!; stats.bytes_sent += res.bytes_sent!; }) result.identities?.forEach(res => { const stats = getOrCreate(res.profile!); let ident = stats.identities.find(value => value.exit_node === res.exit_node) if (!ident) { ident = { count: 0, exit_node: res.exit_node!, } stats.identities.push(ident); } ident.count += res.totalCount; }) return Array.from(statsMap.values()) }), mergeMap(stats => { return forkJoin(stats.map(p => { if (profileCache.has(p.ID)) { return of(profileCache.get(p.ID)!); } return this.profileService.getAppProfile(p.ID) .pipe(catchError(err => { return of(null) })) })) .pipe( map((profiles: (AppProfile | null)[]) => { profileCache = new Map(); let lm = new Map(); stats.forEach(stat => lm.set(stat.ID, stat)); profiles .forEach(p => { if (!p) { return } profileCache.set(`${p.Source}/${p.ID}`, p) let stat = lm.get(`${p.Source}/${p.ID}`) if (!stat) { return; } stat.Name = p.Name }) return Array.from(lm.values()) }) ) }) ) } } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/network.types.ts ================================================ import { Record } from './portapi.types'; export enum Verdict { Undecided = 0, Undeterminable = 1, Accept = 2, Block = 3, Drop = 4, RerouteToNs = 5, RerouteToTunnel = 6, Failed = 7 } export enum IPProtocol { ICMP = 1, IGMP = 2, TCP = 6, UDP = 17, ICMPv6 = 58, UDPLite = 136, RAW = 255, // TODO(ppacher): what is RAW used for? } export enum IPVersion { V4 = 4, V6 = 6, } export enum IPScope { Invalid = -1, Undefined = 0, HostLocal = 1, LinkLocal = 2, SiteLocal = 3, Global = 4, LocalMulticast = 5, GlobalMulitcast = 6 } let globalScopes = new Set([IPScope.GlobalMulitcast, IPScope.Global]) let localScopes = new Set([IPScope.SiteLocal, IPScope.LinkLocal, IPScope.LocalMulticast]) // IsGlobalScope returns true if scope represents a globally // routed destination. export function IsGlobalScope(scope: IPScope): scope is IPScope.GlobalMulitcast | IPScope.Global { return globalScopes.has(scope); } // IsLocalScope returns true if scope represents a locally // routed destination. export function IsLANScope(scope: IPScope): scope is IPScope.SiteLocal | IPScope.LinkLocal | IPScope.LocalMulticast { return localScopes.has(scope); } // IsLocalhost returns true if scope represents localhost. export function IsLocalhost(scope: IPScope): scope is IPScope.HostLocal { return scope === IPScope.HostLocal; } const deniedVerdicts = new Set([ Verdict.Drop, Verdict.Block, ]) // IsDenied returns true if the verdict v represents a // deny or block decision. export function IsDenied(v: Verdict): boolean { return deniedVerdicts.has(v); } export interface CountryInfo { Code: string; Name: string; Center: GeoCoordinates; Continent: ContinentInfo; } export interface ContinentInfo { Code: string; Region: string; Name: string; } export interface GeoCoordinates { AccuracyRadius: number; Latitude: number; Longitude: number; } export const UnknownLocation: GeoCoordinates = { AccuracyRadius: 0, Latitude: 0, Longitude: 0 } export interface IntelEntity { // Protocol is the IP protocol used to connect/communicate // the the described entity. Protocol: IPProtocol; // Port is the remote port number used. Port: number; // Domain is the domain name of the entity. This may either // be the domain name used in the DNS request or the // named returned from reverse PTR lookup. Domain: string; // CNAME is a list of CNAMEs that have been used // to resolve this entity. CNAME: string[] | null; // IP is the IP address of the entity. IP: string; // IPScope holds the classification of the IP address. IPScope: IPScope; // Country holds the country of residence of the IP address. Country: string; // ASN holds the number of the autonoumous system that operates // the IP. ASN: number; // ASOrg holds the AS owner name. ASOrg: string; // Coordinates contains the geographic coordinates of the entity. Coordinates: GeoCoordinates | null; // BlockedByLists holds a list of filter list IDs that // would have blocked the entity. BlockedByLists: string[] | null; // BlockedEntities holds a list of entities that have been // blocked by filter lists. Those entities can be ASNs, domains, // CNAMEs, IPs or Countries. BlockedEntities: string[] | null; // ListOccurences maps the blocked entity (see BlockedEntities) // to a list of filter-list IDs that contains it. ListOccurences: { [key: string]: string[] } | null; } export enum ScopeIdentifier { IncomingHost = "IH", IncomingLAN = "IL", IncomingInternet = "II", IncomingInvalid = "IX", PeerHost = "PH", PeerLAN = "PL", PeerInternet = "PI", PeerInvalid = "PX" } export const ScopeTranslation: { [key: string]: string } = { [ScopeIdentifier.IncomingHost]: "Device-Local Incoming", [ScopeIdentifier.IncomingLAN]: "LAN Incoming", [ScopeIdentifier.IncomingInternet]: "Internet Incoming", [ScopeIdentifier.PeerHost]: "Device-Local Outgoing", [ScopeIdentifier.PeerLAN]: "LAN Peer-to-Peer", [ScopeIdentifier.PeerInternet]: "Internet Peer-to-Peer", [ScopeIdentifier.IncomingInvalid]: "N/A", [ScopeIdentifier.PeerInvalid]: "N/A", } export interface ProcessContext { BinaryPath: string; ProcessName: string; ProfileName: string; PID: number; Profile: string; Source: string } // Reason justifies the decision on a connection // verdict. export interface Reason { // Msg holds a human readable message of the reason. Msg: string; // OptionKey, if available, holds the key of the // configuration option that caused the verdict. OptionKey: string; // Profile holds the profile the option setting has // been configured in. Profile: string; // Context may holds additional data about the reason. Context: any; } export enum ConnectionType { Undefined = 0, IPConnection = 1, DNSRequest = 2 } export function IsDNSRequest(t: ConnectionType): t is ConnectionType.DNSRequest { return t === ConnectionType.DNSRequest; } export function IsIPConnection(t: ConnectionType): t is ConnectionType.IPConnection { return t === ConnectionType.IPConnection; } export interface DNSContext { Domain: string; ServedFromCache: boolean; RequestingNew: boolean; IsBackup: boolean; Filtered: boolean; FilteredEntries: string[], // RR Question: 'A' | 'AAAA' | 'MX' | 'TXT' | 'SOA' | 'SRV' | 'PTR' | 'NS' | string; RCode: 'NOERROR' | 'SERVFAIL' | 'NXDOMAIN' | 'REFUSED' | string; Modified: string; Expires: string; } export interface TunnelContext { Path: TunnelNode[]; PathCost: number; RoutingAlg: 'default'; } export interface GeoIPInfo { IP: string; Country: string; ASN: number; ASOwner: string; } export interface TunnelNode { ID: string; Name: string; IPv4?: GeoIPInfo; IPv6?: GeoIPInfo; } export interface CertInfo { Subject: string; Issuer: string; AlternateNames: string[]; NotBefore: dateType; NotAfter: dateType; } export interface TLSContext { Version: string; VersionRaw: number; SNI: string; Chain: CertInfo[][]; } export interface Connection extends Record { // ID is a unique ID for the connection. ID: string; // Type defines the connection type. Type: ConnectionType; // TLS may holds additional data for the TLS // session. TLS: TLSContext | null; // DNSContext holds additional data about the DNS request for // this connection. DNSContext: DNSContext | null; // TunnelContext holds additional data about the SPN tunnel used for // the connection. TunnelContext: TunnelContext | null; // Scope defines the scope of the connection. It's an somewhat // weired field that may contain a ScopeIdentifier or a string. // In case of a string it may eventually be interpreted as a // domain name. Scope: ScopeIdentifier | string; // IPVersion is the version of the IP protocol used. IPVersion: IPVersion; // Inbound is true if the connection is incoming to // hte local system. Inbound: boolean; // IPProtocol is the protocol used by the connection. IPProtocol: IPProtocol; // LocalIP is the local IP address that is involved into // the connection. LocalIP: string; // LocalIPScope holds the classification of the local IP // address; LocalIPScope: IPScope; // LocalPort is the local port that is involved into the // connection. LocalPort: number; // Entity describes the remote entity that is part of the // connection. Entity: IntelEntity; // Verdict defines the final verdict. Verdict: Verdict; // Reason is the reason justifying the verdict of the connection. Reason: Reason; // Started holds the number of seconds in UNIX epoch time at which // the connection was initiated. Started: number; // End dholds the number of seconds in UNIX epoch time at which // the connection was considered terminated. Ended: number; // Tunneled is set to true if the connection was tunneled through the // SPN. Tunneled: boolean; // VerdictPermanent is set to true if the connection was marked and // handed back to the operating system. VerdictPermanent: boolean; // Inspecting is set to true if the connection is being inspected. Inspecting: boolean; // Encrypted is set to true if the connection is estimated as being // encrypted. Interpreting this field must be done with care! Encrypted: boolean; // Internal is set to true if this connection is done by the Portmaster // or any associated helper processes/binaries itself. Internal: boolean; // ProcessContext holds additional information about the process // that initated the connection. ProcessContext: ProcessContext; // ProfileRevisionCounter is used to track changes to the process // profile. ProfileRevisionCounter: number; } export interface ReasonContext { [key: string]: any; } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/platform-specific/tauri/tauri-http-interceptor.ts ================================================ import { HttpEvent, HttpHandlerFn, HttpRequest, HttpResponse, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; import { from, Observable, switchMap, map, catchError, throwError } from 'rxjs'; import { invoke } from '@tauri-apps/api/core' import { inject } from '@angular/core'; import { PORTMASTER_HTTP_API_ENDPOINT } from '../../portapi.service'; /** * TauriHttpInterceptor intercepts HTTP requests and routes them through Tauri's `@tauri-apps/plugin-http` API. * * This allows HTTP requests to be executed from the Tauri application binary instead of the WebView, * enabling more secure and direct communication with external APIs. * * The interceptor handles various response types (e.g., JSON, text, blob, arraybuffer) and ensures * that headers and response data are properly mapped to Angular's HttpResponse format. * * References: * - https://angular.dev/guide/http/interceptors */ export function TauriHttpInterceptor(req: HttpRequest, next: HttpHandlerFn): Observable> { const httpApiEndpoint = inject(PORTMASTER_HTTP_API_ENDPOINT); if (httpApiEndpoint && !req.url.startsWith(httpApiEndpoint)) { // If the request URL does not start with the configured HTTP API endpoint, skip interception console.log('[TauriHttpInterceptor] Non-API request, skipping interception:', req.url); return next(req); } const fetchOptions: RequestInit = { method: req.method, headers: req.headers.keys().reduce((acc: Record, key) => { acc[key] = req.headers.get(key) || ''; return acc; }, {}), body: getRequestBody(req), }; //console.log('[TauriHttpInterceptor] Fetching:', req.url, "Headers:", fetchOptions.headers); return from(send_tauri_http_request(req.url, fetchOptions)).pipe( switchMap(response => { // Copy all response headers const headerMap: Record = {}; response.headers.forEach((value: string, key: string) => { headerMap[key] = value; }); const headers = new HttpHeaders(headerMap); // Check if response status is ok (2xx) if (!response.ok) { // Get the error content return from(response.text()).pipe( map(errorText => { throw new HttpErrorResponse({ error: errorText, headers: headers, status: response.status, statusText: response.statusText, url: req.url }); }) ); } // Get the response type from the request const responseType = req.responseType || 'json'; // Helper function to create HttpResponse from body const createResponse = (body: any): HttpEvent => { return new HttpResponse({ body, status: response.status, headers: headers, url: req.url }) as HttpEvent; }; switch (responseType) { case 'text': return from(response.text()).pipe(map(createResponse)); case 'arraybuffer': return from(response.arrayBuffer()).pipe(map(createResponse)); case 'blob': return from(response.blob()).pipe( map(blob => { const contentType = response.headers.get('content-type') || ''; // Create a new blob with the proper MIME type if (contentType && (!blob.type || blob.type === 'application/octet-stream')) { const typedBlob = new Blob([blob], { type: contentType }); return createResponse(typedBlob); } return createResponse(blob); }) ); case 'json': default: return from(response.text()).pipe( map(body => { let parsedBody: any; try { // Only attempt to parse as JSON if we have content // and either explicitly requested JSON or content-type is JSON if (body && (responseType === 'json' || (response.headers.get('content-type') || '').includes('application/json'))) { parsedBody = JSON.parse(body); } else { parsedBody = body; } } catch (e) { console.warn('[TauriHttpInterceptor] Failed to parse JSON response:', e); parsedBody = body; } return createResponse(parsedBody); }) ); } }), catchError(error => { console.error('[TauriHttpInterceptor] Request failed:', error); // If it's already an HttpErrorResponse, just return it if (error instanceof HttpErrorResponse) { return throwError(() => error); } // Otherwise create a new HttpErrorResponse with available information return throwError(() => new HttpErrorResponse({ error: error.message || 'Unknown error occurred', status: error.status || 0, statusText: error.statusText || 'Unknown Error', url: req.url, headers: error.headers ? new HttpHeaders(error.headers) : new HttpHeaders() })); }) ); } function getRequestBody(req: HttpRequest): any { if (!req.body) { return undefined; } // Handle different body types properly if (req.body instanceof FormData || req.body instanceof Blob || req.body instanceof ArrayBuffer || req.body instanceof URLSearchParams) { return req.body; } // Default to JSON stringify for object data return JSON.stringify(req.body); } export async function send_tauri_http_request( url: string, init: RequestInit = {} ): Promise { // Extract method, headers, and body buffer const method = init.method || 'GET'; const headers = [...(init.headers instanceof Headers ? (() => { const headerArray: [string, string][] = []; init.headers.forEach((value, key) => headerArray.push([key, value])); return headerArray; })() : Object.entries(init.headers || {}))]; let body: Uint8Array | undefined; if (init.body) { if (typeof init.body === 'string') { // Most efficient way to convert a string to Uint8Array body = new TextEncoder().encode(init.body); } else if (init.body instanceof ArrayBuffer) { body = new Uint8Array(init.body); } else if (init.body instanceof Uint8Array) { body = init.body; } else if (init.body instanceof Blob) { // Efficiently read Blob data body = new Uint8Array(await init.body.arrayBuffer()); } else if (init.body instanceof URLSearchParams) { body = new TextEncoder().encode(init.body.toString()); } else { // Fallback for other types, though the inefficient path is kept for unsupported types // This path should ideally be avoided by handling types in getRequestBody. console.warn('[TauriHttpInterceptor] Using inefficient body conversion for unknown type.'); body = new Uint8Array(await new Response(init.body as any).arrayBuffer()); } } const res = await invoke<{ status: number; status_text: string; headers: [string, string][]; body: number[]; }>('send_tauri_http_request', { url, opts: { method, headers, body: body ? Array.from(body) : undefined } }); return new Response(new Uint8Array(res.body), { status: res.status, statusText: res.status_text, headers: res.headers, }); } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/platform-specific/tauri/tauri-websocket-subject.ts ================================================ import WebSocket, { Message } from '@tauri-apps/plugin-websocket'; import { Subject, Observable, merge, mergeMap, throwError } from 'rxjs'; import { WebSocketSubject, WebSocketSubjectConfig } from 'rxjs/webSocket'; const LOG_PREFIX = '[tauri_ws]'; const PING_INTERVAL_MS = 10000; // Send a ping every PING_INTERVAL_MS milliseconds const PONG_TIMEOUT_MS = 5000; // Wait PONG_TIMEOUT_MS milliseconds for a pong response /** * Creates a WebSocket connection using the Tauri WebSocket API and wraps it in an RxJS WebSocketSubject-compatible interface. * * @template T - The type of messages sent and received through the WebSocket. * @param {WebSocketSubjectConfig} opts - Configuration options for the WebSocket connection. * @returns {WebSocketSubject} - An RxJS WebSocketSubject-compatible object for interacting with the WebSocket. * @throws {Error} If the `serializer` or `deserializer` functions are not provided. * * @example * const wsSubject = createTauriWsConnection({ * url: 'ws://example.com', * serializer: JSON.stringify, * deserializer: JSON.parse, * openObserver: { next: () => console.log('Connection opened') }, * closeObserver: { next: () => console.log('Connection closed') }, * closingObserver: { next: () => console.log('Connection closing') }, * }, ngZone); */ export function createTauriWsConnection(opts: WebSocketSubjectConfig): WebSocketSubject { if (!opts.serializer) throw new Error(`${LOG_PREFIX} Messages Serializer not provided!`); if (!opts.deserializer) throw new Error(`${LOG_PREFIX} Messages Deserializer not provided!`); const serializer = opts.serializer; const deserializer = opts.deserializer; let wsConnection: WebSocket | null = null; const messageSubject = new Subject(); const errorSubject = new Subject(); // Added for error propagation // Combined stream with both messages and errors const observable$ = merge( messageSubject.asObservable(), errorSubject.pipe( mergeMap(err => throwError(() => err)) ) ); ////////////////////////////////////////////////////////////// // Track subscriptions ////////////////////////////////////////////////////////////// let subscriptionCount = 0; // Wrapper with subscription tracking const trackedObservable$ = new Observable(subscriber => { subscriptionCount++; // If this is the first subscription, connect to WebSocket if (subscriptionCount === 1) { connect(); } const subscription = observable$.subscribe({ next: value => subscriber.next(value), error: err => subscriber.error(err), complete: () => subscriber.complete() }); // Cleanup function - called when unsubscribed return () => { subscriptionCount--; subscription.unsubscribe(); // If this was the last subscription, close the WebSocket connection if (subscriptionCount === 0) { disconnect(); } }; }); ////////////////////////////////////////////////////////////// // Function to establish a WebSocket connection ////////////////////////////////////////////////////////////// let listenerRemovalFn: (() => void) | null = null; // Store the removal function for the ws listener const connect = (): void => { console.log(`${LOG_PREFIX} Connecting to WebSocket: ${opts.url}`); WebSocket.connect(opts.url) .then((ws) => { wsConnection = ws; lastMessageReceivedTime = 0; console.log(`${LOG_PREFIX} Connection established`); opts.openObserver?.next(undefined as unknown as Event); listenerRemovalFn = ws.addListener(messagesListener); startHealthChecks(); }) .catch((error: Error) => { console.error(`${LOG_PREFIX} Connection failed:`, error); errorSubject.next(error); }); }; const disconnect = (): void => { stopHealthChecks(); if (listenerRemovalFn) { try { listenerRemovalFn(); } catch (err) { console.error(`${LOG_PREFIX} Error removing listener:`, err); } listenerRemovalFn = null; // Clear the reference } const currentWs = wsConnection; wsConnection = null; if (!currentWs) return; console.log(`${LOG_PREFIX} Closing WebSocket connection.`); opts.closeObserver?.next(undefined as unknown as CloseEvent); currentWs.disconnect().catch(err => console.warn(`${LOG_PREFIX} Error closing connection:`, err)); } ////////////////////////////////////////////////////////////// // Function to check if connection alive ////////////////////////////////////////////////////////////// let healthCheckIntervalId: ReturnType | null = null; let pongTimeoutId: ReturnType | null = null; const startHealthChecks = () => { stopHealthChecks(); // Ensure no multiple intervals are running healthCheckIntervalId = setInterval(() => { if (!wsConnection) { stopHealthChecks(); return; } if (pongTimeoutId) { // Ping already in flight, waiting for pong. return; } wsConnection.send({ type: 'Ping', data: [] }) .then(() => { pongTimeoutId = setTimeout(() => { console.error(`${LOG_PREFIX} No Pong received. Connection is likely dead.`); errorSubject.next(new Error('Connection timed out')); stopHealthChecks(); }, PONG_TIMEOUT_MS); }) .catch(err => { console.error(`${LOG_PREFIX} Ping send failed:`, err); errorSubject.next(new Error(`Ping send failed: ${err}`)); stopHealthChecks(); }); }, PING_INTERVAL_MS); }; const stopHealthChecks = () => { if (healthCheckIntervalId) { clearInterval(healthCheckIntervalId); healthCheckIntervalId = null; } if (pongTimeoutId) { clearTimeout(pongTimeoutId); pongTimeoutId = null; } }; ////////////////////////////////////////////////////////////// // Track last message received time to detect inactivity timeout // If no message received for INACTIVITY_TIMEOUT_MS, // assume connection was paused due to OS hibernation or similar. ////////////////////////////////////////////////////////////// const INACTIVITY_TIMEOUT_MS = 1000 * 60 * 15; // 15 minutes inactivity timeout let lastMessageReceivedTime: number = 0; const checkReceivedMessageTime = () => { if ((Date.now() - lastMessageReceivedTime) > INACTIVITY_TIMEOUT_MS && lastMessageReceivedTime>0) { console.error(`${LOG_PREFIX} Inactivity timeout reached. Assuming connection was paused.`); errorSubject.next(new Error('Inactivity timeout')); } lastMessageReceivedTime = Date.now(); } ////////////////////////////////////////////////////////////// // Messages listener ////////////////////////////////////////////////////////////// const messagesListener = (message: Message) => { checkReceivedMessageTime(); // Update last message received time try { switch (message.type) { case 'Text': try { const deserializedMessage = deserializer({ data: message.data as string } as any); messageSubject.next(deserializedMessage); } catch (err) { console.error(`${LOG_PREFIX} Error deserializing text message:`, err); } break; case 'Binary': try { const uint8Array = new Uint8Array(message.data as number[]); const deserializedMessage = deserializer({ data: uint8Array.buffer } as any); messageSubject.next(deserializedMessage); } catch (err) { console.error(`${LOG_PREFIX} Error deserializing binary message:`, err); } break; case 'Close': console.warn(`${LOG_PREFIX} Connection closed by server: ${message}`); errorSubject.next(new Error(`Connection closed by server: ${message}`)); break; case 'Ping': break; case 'Pong': // Pong received, clear the timeout. if (pongTimeoutId) { clearTimeout(pongTimeoutId); pongTimeoutId = null; } break; // All other message types are unexpected. Proceed with reconnect. default: console.warn(`${LOG_PREFIX} Received unexpected message: '${message}'`); break; } } catch (error) { console.error(`${LOG_PREFIX} Error processing message: `, error); } } ////////////////////////////////////////////////////////////// // RxJS WebSocketSubject-compatible interface ////////////////////////////////////////////////////////////// const webSocketSubject = { asObservable: () => trackedObservable$, next: (message: T) => { if (!wsConnection) { errorSubject.next(new Error('Connection not established')); return; } try { const serializedMessage = serializer(message); // 'string' type is enough here, since default serializer for portmaster message returns string if (typeof serializedMessage !== 'string') throw new Error('Serialized message is not a string'); wsConnection?.send(serializedMessage).catch((err: Error) => { console.error(`${LOG_PREFIX} Error sending message:`, err); errorSubject.next(err); }); } catch (error) { console.error(`${LOG_PREFIX} Error serializing message:`, error); return; } }, complete: () => { if (wsConnection) { opts.closingObserver?.next(); disconnect(); } messageSubject.complete(); }, subscribe: trackedObservable$.subscribe.bind(trackedObservable$), pipe: trackedObservable$.pipe.bind(trackedObservable$), }; return webSocketSubject as unknown as WebSocketSubject; } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/platform-specific/utils.ts ================================================ // Simple function to detect if the app is running in a Tauri environment export function IsTauriEnvironment(): boolean { return '__TAURI__' in window; } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/portapi.service.ts ================================================ import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http'; import { Inject, Injectable, InjectionToken, isDevMode, NgZone, } from '@angular/core'; import { BehaviorSubject, Observable, Observer, of } from 'rxjs'; import { concatMap, delay, filter, map, retryWhen, takeWhile, tap, bufferTime, } from 'rxjs/operators'; import { WebSocketSubject } from 'rxjs/webSocket'; import { DataReply, deserializeMessage, DoneReply, ImportResult, InspectedActiveRequest, isCancellable, isDataReply, ProfileImportResult, Record, ReplyMessage, Requestable, RequestMessage, RequestType, RetryableOpts, retryPipeline, serializeMessage, WatchOpts, } from './portapi.types'; import { WebsocketService } from './websocket.service'; export const PORTMASTER_WS_API_ENDPOINT = new InjectionToken( 'PortmasterWebsocketEndpoint' ); export const PORTMASTER_HTTP_API_ENDPOINT = new InjectionToken( 'PortmasterHttpApiEndpoint' ); export const RECONNECT_INTERVAL = 2000; let uniqueRequestId = 0; interface PendingMethod { observer: Observer; request: RequestMessage; } @Injectable() export class PortapiService { /** The actual websocket connection, auto-(re)connects on subscription */ private ws$: WebSocketSubject | null; /** used to emit changes to our "connection state" */ private connectedSubject = new BehaviorSubject(false); /** A map to multiplex websocket messages to the actual observer/initator */ private _streams$ = new Map>>(); /** Map to keep track of "still-to-send" requests when we are currently disconnected */ private _pendingCalls$ = new Map(); /** Whether or not we are currently connected. */ get connected$() { return this.connectedSubject.asObservable(); } /** @private DEBUGGING ONLY - keeps track of current requests and supports injecting messages */ readonly activeRequests = new BehaviorSubject<{ [key: string]: InspectedActiveRequest; }>({}); constructor( private websocketFactory: WebsocketService, private ngZone: NgZone, private http: HttpClient, @Inject(PORTMASTER_HTTP_API_ENDPOINT) private httpEndpoint: string, @Inject(PORTMASTER_WS_API_ENDPOINT) private wsEndpoint: string ) { // create a new websocket connection that will auto-connect // on the first subscription and will automatically reconnect // with consecutive subscribers. this.ws$ = this.createWebsocket(); // no need to keep a reference to the subscription as we're not going // to unsubscribe ... this.ws$ .pipe( retryWhen((errors) => errors.pipe( // use concatMap to keep the errors in order and make sure // they don't execute in parallel. concatMap((e, i) => of(e).pipe( // We need to forward the error to all streams here because // due to the retry feature the subscriber below won't see // any error at all. tap(() => { this._streams$.forEach((observer) => observer.error(e)); this._streams$.clear(); }), delay(1000) ) ) ) ), // Buffer all incoming messages for X ms. This creates batches (arrays of messages). bufferTime(25), // Don't process empty batches that can occur during idle periods. filter(batch => batch.length > 0), ) .subscribe( // The subscriber now receives an array of messages (a batch). (batch) => { // Re-enter the Angular Zone ONCE for the entire batch. this.ngZone.run(() => { for (const msg of batch) { const observer = this._streams$.get(msg.id); if (!observer) { // it's expected that we receive done messages from time to time here // as portmaster sends a "done" message after we "cancel" a subscription // and we already remove the observer from _streams$ if the subscription // is unsubscribed. So just hide that warning message for "done" if (msg.type !== 'done') { console.warn(`Received message for unknown request id ${msg.id} (type=${msg.type})`, msg); } continue; } // forward the message to the actual stream. observer.next(msg as ReplyMessage); } }); }, console.error, () => { // This should actually never happen but if, make sure // we handle it ... this._streams$.forEach((observer) => observer.complete()); this._streams$.clear(); } ); } /** Triggers a restart of the portmaster service */ restartPortmaster(): Observable { return this.http.post(`${this.httpEndpoint}/v1/core/restart`, undefined, { observe: 'response', responseType: 'arraybuffer', }); } /** Triggers a shutdown of the portmaster service */ shutdownPortmaster(): Observable { return this.http.post(`${this.httpEndpoint}/v1/core/shutdown`, undefined, { observe: 'response', responseType: 'arraybuffer', }); } /** Triggers a pause of the portmaster or SPN service * @param duration The duration of the pause in seconds * @param onlySPN Whether or not only the SPN should be paused */ pause(duration: number, onlySPN: boolean): Observable { return this.http.post(`${this.httpEndpoint}/v1/control/pause`, { duration, onlySPN }, { observe: 'response', responseType: 'arraybuffer', }); } /** Triggers a resume of the portmaster (and SPN) service */ resume(): Observable { return this.http.post(`${this.httpEndpoint}/v1/control/resume`, undefined, { observe: 'response', responseType: 'arraybuffer', }); } /** Force the portmaster to check for updates */ checkForUpdates(): Observable { return this.http.post(`${this.httpEndpoint}/v1/updates/check`, undefined, { observe: 'response', responseType: 'arraybuffer', reportProgress: false, }); } /** Force a reload of the UI assets */ reloadUI(): Observable { return this.http.post(`${this.httpEndpoint}/v1/ui/reload`, undefined, { observe: 'response', responseType: 'arraybuffer', }); } /** Clear DNS cache */ clearDNSCache(): Observable { return this.http.post(`${this.httpEndpoint}/v1/dns/clear`, undefined, { observe: 'response', responseType: 'arraybuffer', }); } /** Reset the broadcast notifications state */ resetBroadcastState(): Observable { return this.http.post( `${this.httpEndpoint}/v1/broadcasts/reset-state`, undefined, { observe: 'response', responseType: 'arraybuffer' } ); } /** Re-initialize the SPN */ reinitSPN(): Observable { return this.http.post(`${this.httpEndpoint}/v1/spn/reinit`, undefined, { observe: 'response', responseType: 'arraybuffer', }); } /** Cleans up the history database by applying history retention settings */ cleanupHistory(): Observable { return this.http.post( `${this.httpEndpoint}/v1/netquery/history/cleanup`, undefined, { observe: 'response', responseType: 'arraybuffer' } ); } /** Requests a resource from the portmaster as application/json and automatically parses the response body*/ getResource(resource: string): Observable; /** Requests a resource from the portmaster as text */ getResource(resource: string, type: string): Observable>; getResource( resource: string, type?: string ): Observable | any> { if (type !== undefined) { return this.http.get(`${this.httpEndpoint}/v1/updates/get/${resource}`, { headers: new HttpHeaders({ Accept: type }), observe: 'response', responseType: 'text', }); } return this.http.get( `${this.httpEndpoint}/v1/updates/get/${resource}`, { headers: new HttpHeaders({ Accept: 'application/json' }), responseType: 'json', } ); } /** Export one or more settings, either from global settings or a specific profile */ exportSettings( keys: string[], from: 'global' | string = 'global' ): Observable { return this.http.post( `${this.httpEndpoint}/v1/sync/settings/export`, { from, keys, }, { headers: new HttpHeaders({ Accept: 'text/yaml' }), responseType: 'text', observe: 'body', } ); } /** Validate a settings import for a given target */ validateSettingsImport( blob: string | Blob, target: string | 'global' = 'global', mimeType: string = 'text/yaml' ): Observable { return this.http.post( `${this.httpEndpoint}/v1/sync/settings/import`, { target, rawExport: blob.toString(), rawMime: mimeType, validateOnly: true, } ); } /** Import settings into a given target */ importSettings( blob: string | Blob, target: string | 'global' = 'global', mimeType: string = 'text/yaml', reset = false, allowUnknown = false ): Observable { return this.http.post( `${this.httpEndpoint}/v1/sync/settings/import`, { target, rawExport: blob.toString(), rawMime: mimeType, validateOnly: false, reset, allowUnknown, } ); } /** Import a profile */ importProfile( blob: string | Blob, mimeType: string = 'text/yaml', reset = false, allowUnknown = false, allowReplaceProfiles = false ): Observable { return this.http.post( `${this.httpEndpoint}/v1/sync/profile/import`, { rawExport: blob.toString(), rawMime: mimeType, validateOnly: false, reset, allowUnknown, allowReplaceProfiles, } ); } /** Import a profile */ validateProfileImport( blob: string | Blob, mimeType: string = 'text/yaml' ): Observable { return this.http.post( `${this.httpEndpoint}/v1/sync/profile/import`, { rawExport: blob.toString(), rawMime: mimeType, validateOnly: true, } ); } /** Export one or more settings, either from global settings or a specific profile */ exportProfile(id: string): Observable { return this.http.post( `${this.httpEndpoint}/v1/sync/profile/export`, { id, }, { headers: new HttpHeaders({ Accept: 'text/yaml' }), responseType: 'text', observe: 'body', } ); } /** Merge multiple profiles into one primary profile. */ mergeProfiles( name: string, primary: string, secondaries: string[] ): Observable { return this.http .post<{ new: string }>(`${this.httpEndpoint}/v1/profile/merge`, { name: name, to: primary, from: secondaries, }) .pipe(map((response) => response.new)); } /** * Injects an event into a module to trigger certain backend * behavior. * * @deprecated - Use the HTTP API instead. * * @param module The name of the module to inject * @param kind The event kind to inject */ bridgeAPI(call: string, method: string): Observable { return this.create(`api:${call}`, { Method: method, }).pipe(map(() => { })); } /** * Flushes all pending method calls that have been collected * while we were not connected to the portmaster API. */ private _flushPendingMethods() { const count = this._pendingCalls$.size; try { this._pendingCalls$.forEach((req, key) => { // It's fine if we throw an error here! this.ws$!.next(req.request); this._streams$.set(req.request.id, req.observer); this._pendingCalls$.delete(key); }); } catch (err) { // we failed to send the pending calls because the // websocket connection just broke. console.error( `Failed to flush pending calls, ${this._pendingCalls$.size} left: `, err ); } console.log(`Successfully flushed all (${count}) pending calles`); } /** * Allows to inspect currently active requests. */ inspectActiveRequests(): { [key: string]: InspectedActiveRequest } { return this.activeRequests.getValue(); } /** * Loads a database entry. The returned observable completes * after the entry has been loaded. * * @param key The database key of the entry to load. */ get(key: string): Observable { return this.request('get', { key }).pipe(map((res) => res.data)); } /** * Searches for multiple database entries at once. Each entry * is streams via the returned observable. The observable is * closed after the last entry has been published. * * @param query The query used to search the database. */ query(query: string): Observable> { return this.request('query', { query }); } /** * Subscribes for updates on entries of the selected query. * * @param query The query use to subscribe. */ sub( query: string, opts: RetryableOpts = {} ): Observable> { return this.request('sub', { query }).pipe(retryPipeline(opts)); } /** * Subscribes for updates on entries of the selected query and * ensures entries are stream once upon subscription. * * @param query The query use to subscribe. * @todo(ppacher): check what a ok/done message mean here. */ qsub( query: string, opts?: RetryableOpts ): Observable>; qsub( query: string, opts: RetryableOpts, _: { forwardDone: true } ): Observable | DoneReply>; qsub( query: string, opts: RetryableOpts = {}, { forwardDone }: { forwardDone?: true } = {} ): Observable> { return this.request('qsub', { query }, { forwardDone }).pipe( retryPipeline(opts) ); } /** * Creates a new database entry. * * @warn create operations do not validate the type of data * to be overwritten (for keys that does already exist). * Use {@function insert} for more validation. * * @param key The database key for the entry. * @param data The actual data for the entry. */ create(key: string, data: any): Observable { data = this.stripMeta(data); return this.request('create', { key, data }).pipe(map(() => { })); } /** * Updates an existing entry. * * @param key The database key for the entry * @param data The actual, updated entry data. */ update(key: string, data: any): Observable { data = this.stripMeta(data); return this.request('update', { key, data }).pipe(map(() => { })); } /** * Creates a new database entry. * * @param key The database key for the entry. * @param data The actual data for the entry. * @todo(ppacher): check what's different to create(). */ insert(key: string, data: any): Observable { data = this.stripMeta(data); return this.request('insert', { key, data }).pipe(map(() => { })); } /** * Deletes an existing database entry. * * @param key The key of the database entry to delete. */ delete(key: string): Observable { return this.request('delete', { key }).pipe(map(() => { })); } /** * Watch a database key for modifications. If the * websocket connection is lost or an error is returned * watch will automatically retry after retryDelay * milliseconds. It stops retrying to watch key once * maxRetries is exceeded. The returned observable completes * when the watched key is deleted. * * @param key The database key to watch * @param opts.retryDelay Number of milliseconds to wait * between retrying the request. Defaults to 1000 * @param opts.maxRetries Maximum number of tries before * giving up. Defaults to Infinity * @param opts.ingoreNew Whether or not `new` notifications * will be ignored. Defaults to false * @param opts.ignoreDelete Whether or not "delete" notification * will be ignored (and replaced by null) * @param forwardDone: Whether or not the "done" message should be forwarded */ watch(key: string, opts?: WatchOpts): Observable; watch( key: string, opts?: WatchOpts & { ignoreDelete: true } ): Observable; watch( key: string, opts: WatchOpts, _: { forwardDone: true } ): Observable; watch( key: string, opts: WatchOpts & { ignoreDelete: true }, _: { forwardDone: true } ): Observable; watch( key: string, opts: WatchOpts = {}, { forwardDone }: { forwardDone?: boolean } = {} ): Observable { return this.qsub(key, opts, { forwardDone } as any).pipe( filter((reply) => reply.type !== 'done' || forwardDone === true), filter((reply) => reply.type === 'done' || reply.key === key), takeWhile((reply) => opts.ignoreDelete || reply.type !== 'del'), filter((reply) => { return !opts.ingoreNew || reply.type !== 'new'; }), map((reply) => { if (reply.type === 'del') { return null; } if (reply.type === 'done') { return reply; } return reply.data; }) ); } watchAll( query: string, opts?: RetryableOpts ): Observable { return new Observable((observer) => { let values: T[] = []; let keys: string[] = []; let doneReceived = false; const sub = this.request( 'qsub', { query }, { forwardDone: true } ).subscribe({ next: (value) => { if ((value as any).type === 'done') { doneReceived = true; observer.next(values); return; } if (!doneReceived) { values.push(value.data); keys.push(value.key); return; } const idx = keys.findIndex((k) => k === value.key); switch (value.type) { case 'new': if (idx < 0) { values.push(value.data); keys.push(value.key); } else { /* const existing = values[idx]._meta!; const existingTs = existing.Modified || existing.Created; const newTs = (value.data as Record)?._meta?.Modified || (value.data as Record)?._meta?.Created || 0; console.log(`Comparing ${newTs} against ${existingTs}`); if (newTs > existingTs) { console.log(`New record is ${newTs - existingTs} seconds newer`); values[idx] = value.data; } else { return; } */ values[idx] = value.data; } break; case 'del': if (idx >= 0) { keys.splice(idx, 1); values.splice(idx, 1); } break; case 'upd': if (idx >= 0) { values[idx] = value.data; } break; } observer.next(values); }, error: (err) => { observer.error(err); }, complete: () => { observer.complete(); }, }); return () => { sub.unsubscribe(); }; }).pipe(retryPipeline(opts)); } /** * Close the current websocket connection. A new subscription * will _NOT_ trigger a reconnect. */ close() { if (!this.ws$) { return; } this.ws$.complete(); this.ws$ = null; } request( method: M, attrs: Partial>, { forwardDone }: { forwardDone?: boolean } = {} ): Observable> { return new Observable((observer) => { const id = `${++uniqueRequestId}`; if (!this.ws$) { observer.error('No websocket connection'); return; } let shouldCancel = isCancellable(method); let unsub: () => RequestMessage | null = () => { if (shouldCancel) { return { id: id, type: 'cancel', }; } return null; }; const request: any = { ...attrs, id: id, type: method, }; let inspected: InspectedActiveRequest = { type: method, messagesReceived: 0, observer: observer, payload: request, lastData: null, lastKey: '', }; if (isDevMode()) { this.activeRequests.next({ ...this.inspectActiveRequests(), [id]: inspected, }); } let stream$: Observable> = this.multiplex( request, unsub ); if (isDevMode()) { // in development mode we log all replys for the different // methods. This also includes updates to subscriptions. stream$ = stream$.pipe( tap( (msg) => { }, //msg => console.log(`[portapi] reply for ${method} ${id}: `, msg), (err) => console.error(`[portapi] error in ${method} ${id}: `, err) ) ); } const subscription = stream$?.subscribe({ next: (data) => { inspected.messagesReceived++; // in all cases, an `error` message type // terminates the data flow. if (data.type === 'error') { console.error(data.message, inspected); shouldCancel = false; observer.error(data.message); return; } if ( method === 'create' || method === 'update' || method === 'insert' || method === 'delete' ) { // for data-manipulating methods success // ends the stream. if (data.type === 'success') { observer.next(null as any); observer.complete(); return; } } if (method === 'query' || method === 'sub' || method === 'qsub') { if (data.type === 'warning') { console.warn(data.message); return; } // query based methods send `done` once all // results are sent at least once. if (data.type === 'done') { if (method === 'query') { // done ends the query but does not end sub or qsub shouldCancel = false; observer.complete(); return; } if (!!forwardDone) { // A done message in qsub does not actually represent // a DataReply but we still want to forward that. observer.next(data as any); } return; } } if (!isDataReply(data)) { console.error( `Received unexpected message type ${data.type} in a ${method} operation` ); return; } inspected.lastData = data.data; inspected.lastKey = data.key; observer.next(data); // for a `get` method the first `ok` message // also marks the end of the stream. if (method === 'get' && data.type === 'ok') { shouldCancel = false; observer.complete(); } }, error: (err) => { console.error("[portapi] request error:", err.message || err, attrs); observer.error(err); }, complete: () => { observer.complete(); }, }); if (isDevMode()) { // make sure we remove the "active" request when the subscription // goes down subscription.add(() => { const active = this.inspectActiveRequests(); delete active[request.id]; this.activeRequests.next(active); }); } return () => { subscription.unsubscribe(); }; }); } private multiplex( req: RequestMessage, cancel: (() => RequestMessage | null) | null ): Observable { return new Observable((observer) => { if (this.connectedSubject.getValue()) { // Try to directly send the request to the backend this._streams$.set(req.id, observer); this.ws$!.next(req); } else { // in case of an error we just add the request as // "pending" and wait for the connection to be // established. console.warn( `Failed to send request ${req.id}:${req.type}, marking as pending ...` ); this._pendingCalls$.set(req.id, { request: req, observer: observer, }); } return () => { // Try to cancel the request but ingore // any errors here. try { if (cancel !== null) { const cancelMsg = cancel(); if (!!cancelMsg) { this.ws$!.next(cancelMsg); } } } catch (err) { } this._pendingCalls$.delete(req.id); this._streams$.delete(req.id); }; }); } /** * Inject a message into a PortAPI stream. * * @param id The request ID to inject msg into. * @param msg The message to inject. */ _injectMessage(id: string, msg: DataReply) { // we are using runTask here so change-detection is // triggered as needed this.ngZone.runTask(() => { const req = this.activeRequests.getValue()[id]; if (!req) { return; } req.observer.next(msg as DataReply); }); } /** * Injects a 'ok' type message * * @param id The ID of the request to inject into * @param data The data blob to inject * @param key [optional] The key of the entry to inject */ _injectData(id: string, data: any, key: string = '') { this._injectMessage(id, { type: 'ok', data: data, key, id: id }); } /** * Patches the last message received on id by deeply merging * data and re-injects that message. * * @param id The ID of the request * @param data The patch to apply and reinject */ _patchLast(id: string, data: any) { const req = this.activeRequests.getValue()[id]; if (!req || !req.lastData) { return; } const newPayload = mergeDeep({}, req.lastData, data); this._injectData(id, newPayload, req.lastKey); } private stripMeta(obj: T): T { let copy = { ...obj, _meta: undefined, }; return copy; } /** * Creates a new websocket subject and configures appropriate serializer * and deserializer functions for PortAPI. * * @private */ private createWebsocket(): WebSocketSubject { return this.websocketFactory.createConnection< ReplyMessage | RequestMessage >({ url: this.wsEndpoint, serializer: (msg) => { try { return serializeMessage(msg); } catch (err) { console.error('serialize message', err); return { type: 'error', }; } }, // deserializeMessage also supports RequestMessage so cast as any deserializer: ((msg: any) => { try { const res = deserializeMessage(msg); return res; } catch (err) { console.error('deserialize message', err); return { type: 'error', }; } }), binaryType: 'arraybuffer', openObserver: { next: () => { console.log('[portapi] connection to portmaster established'); this.connectedSubject.next(true); this._flushPendingMethods(); }, }, closeObserver: { next: () => { console.log('[portapi] connection to portmaster closed'); this.connectedSubject.next(false); }, }, closingObserver: { next: () => { console.log('[portapi] connection to portmaster closing'); }, }, }); } } // Counts the number of "truthy" datafields in obj. function countTruthyDataFields(obj: { [key: string]: any }): number { let count = 0; Object.keys(obj).forEach((key) => { let value = obj[key]; if (!!value) { count++; } }); return count; } function isObject(item: any): item is Object { return item && typeof item === 'object' && !Array.isArray(item); } export function mergeDeep(target: any, ...sources: any): any { if (!sources.length) return target; const source = sources.shift(); if (isObject(target) && isObject(source)) { for (const key in source) { if (isObject(source[key])) { if (!target[key]) Object.assign(target, { [key]: {} }); mergeDeep(target[key], source[key]); } else { Object.assign(target, { [key]: source[key] }); } } } return mergeDeep(target, ...sources); } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/portapi.types.ts ================================================ import { iif, MonoTypeOperatorFunction, of, Subscriber, throwError } from 'rxjs'; import { concatMap, delay, retryWhen } from 'rxjs/operators'; /** * ReplyType contains all possible message types of a reply. */ export type ReplyType = 'ok' | 'upd' | 'new' | 'del' | 'success' | 'error' | 'warning' | 'done'; /** * RequestType contains all possible message types of a request. */ export type RequestType = 'get' | 'query' | 'sub' | 'qsub' | 'create' | 'update' | 'insert' | 'delete' | 'cancel'; // RecordMeta describes the meta-data object that is part of // every API resource. export interface RecordMeta { // Created hold a unix-epoch timestamp when the record has been // created. Created: number; // Deleted hold a unix-epoch timestamp when the record has been // deleted. Deleted: number; // Expires hold a unix-epoch timestamp when the record has been // expires. Expires: number; // Modified hold a unix-epoch timestamp when the record has been // modified last. Modified: number; // Key holds the database record key. Key: string; } export interface Process extends Record { Name: string; UserID: number; UserName: string; UserHome: string; Pid: number; Pgid: number; CreatedAt: number; ParentPid: number; ParentCreatedAt: number; Path: string; ExecName: string; Cwd: string; CmdLine: string; FirstArg: string; Env: { [key: string]: string } | null; Tags: { Key: string; Value: string; }[] | null; MatchingPath: string; PrimaryProfileID: string; FirstSeen: number; LastSeen: number; Error: string; ExecHashes: { [key: string]: string } | null; } // Record describes the base record structure of all API resources. export interface Record { _meta?: RecordMeta; } /** * All possible MessageType that are available in PortAPI. */ export type MessageType = RequestType | ReplyType; /** * BaseMessage describes the base message type that is exchanged * via PortAPI. */ export interface BaseMessage { // ID of the request. Used to correlated (multiplex) requests and // responses across a single websocket connection. id: string; // Type is the request/response message type. type: M; } /** * DoneReply marks the end of a PortAPI stream. */ export interface DoneReply extends BaseMessage<'done'> { } /** * DataReply is either sent once as a result on a `get` request or * is sent multiple times in the course of a PortAPI stream. */ export interface DataReply extends BaseMessage<'ok' | 'upd' | 'new' | 'del'> { // Key is the database key including the database prefix. key: string; // Data is the actual data of the entry. data: T; } /** * Returns true if d is a DataReply message type. * * @param d The reply message to check */ export function isDataReply(d: ReplyMessage): d is DataReply { return d.type === 'ok' || d.type === 'upd' || d.type === 'new' || d.type === 'del'; //|| d.type === 'done'; // done is actually not correct } /** * SuccessReply is used to mark an operation as successfully. It does not carry any * data. Think of it as a "201 No Content" in HTTP. */ export interface SuccessReply extends BaseMessage<'success'> { } /** * ErrorReply describes an error that happened while processing a * request. Note that an `error` type message may be sent for single * and response-stream requests. In case of a stream the `error` type * message marks the end of the stream. See WarningReply for a simple * warning message that can be transmitted via PortAPI. */ export interface ErrorReply extends BaseMessage<'error'> { // Message is the error message from the backend. message: string; } /** * WarningReply contains a warning message that describes an error * condition encountered when processing a single entitiy of a * response stream. In contrast to `error` type messages, a `warning` * can only occure during data streams and does not end the stream. */ export interface WarningReply extends BaseMessage<'warning'> { // Message describes the warning/error condition the backend // encountered. message: string; } /** * QueryRequest defines the payload for `query`, `sub` and `qsub` message * types. The result of a query request is always a stream of responses. * See ErrorReply, WarningReply and DoneReply for more information. */ export interface QueryRequest extends BaseMessage<'query' | 'sub' | 'qsub'> { // Query is the query for the database. query: string; } /** * KeyRequests defines the payload for a `get` or `delete` request. Those * message type only carry the key of the database entry to delete. Note that * `delete` can only return a `success` or `error` type message while `get` will * receive a `ok` or `error` type message. */ export interface KeyRequest extends BaseMessage<'delete' | 'get'> { // Key is the database entry key. key: string; } /** * DataRequest is used during create, insert or update operations. * TODO(ppacher): check what's the difference between create and insert, * both seem to error when trying to create a new entry. */ export interface DataRequest extends BaseMessage<'update' | 'create' | 'insert'> { // Key is the database entry key. key: string; // Data is the data to store. data: T; } /** * CancelRequest can be sent on stream operations to early-abort the request. */ export interface CancelRequest extends BaseMessage<'cancel'> { } /** * ReplyMessage is a union of all reply message types. */ export type ReplyMessage = DataReply | DoneReply | SuccessReply | WarningReply | ErrorReply; /** * RequestMessage is a union of all request message types. */ export type RequestMessage = QueryRequest | KeyRequest | DataRequest | CancelRequest; /** * Requestable can be used to accept only properties that match * the request message type M. */ export type Requestable = RequestMessage & { type: M }; /** * Returns true if m is a cancellable message type. * * @param m The message type to check. */ export function isCancellable(m: MessageType): boolean { switch (m) { case 'qsub': case 'sub': return true; default: return false; } } /** * Reflects a currently in-flight PortAPI request. Used to * intercept and mangle with responses. */ export interface InspectedActiveRequest { // The type of request. type: RequestType; // The actual request payload. // @todo(ppacher): typings payload: any; // The request observer. Use to inject data // or complete/error the subscriber. Use with // care! observer: Subscriber>; // Counter for the number of messages received // for this request. messagesReceived: number; // The last data received on the request lastData: any; // The last key received on the request lastKey: string; } export interface RetryableOpts { // A delay in milliseconds before retrying an operation. retryDelay?: number; // The maximum number of retries. maxRetries?: number; } export interface ProfileImportResult extends ImportResult { replacesProfiles: string[]; } export interface ImportResult { restartRequired: boolean; replacesExisting: boolean; containsUnknown: boolean; } /** * Returns a RxJS operator function that implements a retry pipeline * with a configurable retry delay and an optional maximum retry count. * If maxRetries is reached the last error captured is thrown. * * @param opts Configuration options for the retryPipeline. * see {@type RetryableOpts} for more information. */ export function retryPipeline({ retryDelay, maxRetries }: RetryableOpts = {}): MonoTypeOperatorFunction { return retryWhen(errors => errors.pipe( // use concatMap to keep the errors in order and make sure // they don't execute in parallel. concatMap((e, i) => iif( // conditional observable seletion, throwError if i > maxRetries // or a retryDelay otherwise () => i > (maxRetries || Infinity), throwError(() => e), of(e).pipe(delay(retryDelay || 1000)) ) ) )) } export interface WatchOpts extends RetryableOpts { // Whether or not `new` updates should be filtered // or let through. See {@method PortAPI.watch} for // more information. ingoreNew?: boolean; ignoreDelete?: boolean; } /** * Serializes a request or reply message into it's wire format. * * @param msg The request or reply messsage to serialize */ export function serializeMessage(msg: RequestMessage | ReplyMessage): any { if (msg === undefined) { return undefined; } let blob = `${msg.id}|${msg.type}`; switch (msg.type) { case 'done': // reply case 'success': // reply case 'cancel': // request break; case 'error': // reply case 'warning': // reply blob += `|${msg.message}` break; case 'ok': // reply case 'upd': // reply case 'new': // reply case 'insert': // request case 'update': // request case 'create': // request blob += `|${msg.key}|J${JSON.stringify(msg.data)}` break; case 'del': // reply case 'get': // request case 'delete': // request blob += `|${msg.key}` break; case 'query': // request case 'sub': // request case 'qsub': // request blob += `|query ${msg.query}` break; default: // We need (msg as any) here because typescript knows that we covered // all possible values above and that .type can never be something else. // Still, we want to guard against unexpected portmaster message // types. console.error(`Unknown message type ${(msg as any).type}`); } return blob; } /** * Deserializes (loads) a PortAPI message from a WebSocket message event. * * @param event The WebSocket MessageEvent to parse. */ export function deserializeMessage(event: MessageEvent): RequestMessage | ReplyMessage { let data: string; if (typeof event.data !== 'string') { data = new TextDecoder("utf-8").decode(event.data) } else { data = event.data; } const parts = data.split("|"); if (parts.length < 2) { throw new Error(`invalid number of message parts, expected 3-4 but got ${parts.length}`); } const id = parts[0]; const type = parts[1] as MessageType; var msg: Partial = { id, type, } if (parts.length > 4) { parts[3] = parts.slice(3).join('|') } switch (msg.type) { case 'done': // reply case 'success': // reply case 'cancel': // request break; case 'error': // reply case 'warning': // reply msg.message = parts[2]; break; case 'ok': // reply case 'upd': // reply case 'new': // reply case 'insert': // request case 'update': // request case 'create': // request msg.key = parts[2]; try { if (parts[3][0] === 'J') { msg.data = JSON.parse(parts[3].slice(1)); } else { msg.data = parts[3]; } } catch (e) { console.log(e, data) } break; case 'del': // reply case 'get': // request case 'delete': // request msg.key = parts[2]; break; case 'query': // request case 'sub': // request case 'qsub': // request msg.query = parts[2]; if (msg.query.startsWith("query ")) { msg.query = msg.query.slice(6); } break; default: // We need (msg as any) here because typescript knows that we covered // all possible values above and that .type can never be something else. // Still, we want to guard against unexpected portmaster message // types. console.error(`Unknown message type ${(msg as any).type}`); } return msg as (ReplyMessage | RequestMessage); // it's not partitial anymore } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/spn.service.ts ================================================ import { HttpClient, HttpParams, HttpResponse } from "@angular/common/http"; import { Inject, Injectable } from "@angular/core"; import { BehaviorSubject, Observable, of } from "rxjs"; import { filter, map, share, switchMap } from "rxjs/operators"; import { FeatureID } from "./features"; import { PORTMASTER_HTTP_API_ENDPOINT, PortapiService } from './portapi.service'; import { Feature, Pin, SPNStatus, UserProfile } from "./spn.types"; @Injectable() export class SPNService { /** Emits the SPN status whenever it changes */ status$: Observable; profile$ = this.watchProfile() .pipe( share({ connector: () => new BehaviorSubject(undefined) }), filter(val => val !== undefined) ) as Observable; private pins$: Observable; constructor( private portapi: PortapiService, private http: HttpClient, @Inject(PORTMASTER_HTTP_API_ENDPOINT) private httpAPI: string, ) { this.status$ = this.portapi.watch('runtime:spn/status', { ignoreDelete: true }) .pipe( share({ connector: () => new BehaviorSubject(null) }), filter(val => val !== null), ) this.pins$ = this.status$ .pipe( switchMap(status => { if (status.Status !== "disabled") { return this.portapi.watchAll("map:main/", { retryDelay: 50000 }) } return of([] as Pin[]); }), share({ connector: () => new BehaviorSubject(undefined) }), filter(val => val !== undefined) ) as Observable; } /** * Watches all pins of the "main" SPN map. */ watchPins(): Observable { return this.pins$; } /** * Encodes a unicode string to base64. * See https://developer.mozilla.org/en-US/docs/Web/API/btoa * and https://stackoverflow.com/questions/30106476/using-javascripts-atob-to-decode-base64-doesnt-properly-decode-utf-8-strings */ b64EncodeUnicode(str: string): string { return window.btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) { return String.fromCharCode(parseInt(p1, 16)) })) } /** * Logs into the SPN user account */ login({ username, password }: { username: string, password: string }): Observable> { return this.http.post(`${this.httpAPI}/v1/spn/account/login`, undefined, { headers: { Authorization: `Basic ${this.b64EncodeUnicode(username + ':' + password)}` }, responseType: 'text', observe: 'response' }); } /** * Log out of the SPN user account * * @param purge Whether or not the portmaster should keep user/device information for the next login */ logout(purge = false): Observable> { let params = new HttpParams(); if (!!purge) { params = params.set("purge", "true") } return this.http.delete(`${this.httpAPI}/v1/spn/account/logout`, { params, responseType: 'text', observe: 'response' }) } watchEnabledFeatures(): Observable<(Feature & { enabled: boolean })[]> { return this.profile$ .pipe( switchMap(profile => { return this.loadFeaturePackages() .pipe( map(features => { return features.map(feature => { // console.log(feature, profile?.current_plan?.feature_ids) return { ...feature, enabled: feature.RequiredFeatureID === FeatureID.None || profile?.current_plan?.feature_ids?.includes(feature.RequiredFeatureID) || false, } }) }) ) }) ); } /** Returns a list of all feature packages */ loadFeaturePackages(): Observable { return this.http.get<{ Features: Feature[] }>(`${this.httpAPI}/v1/account/features`) .pipe( map(response => response.Features.map(feature => { return { ...feature, IconURL: `${this.httpAPI}/v1/account/features/${feature.ID}/icon`, } })) ); } /** * Returns the current SPN user profile. * * @param refresh Whether or not the user profile should be refreshed from the ticket agent * @returns */ userProfile(refresh = false): Observable { let params = new HttpParams(); if (!!refresh) { params = params.set("refresh", true) } return this.http.get(`${this.httpAPI}/v1/spn/account/user/profile`, { params }); } /** * Watches the user profile. It will emit null if there is no profile available yet. */ watchProfile(): Observable { let hasSent = false; return this.portapi.watch('core:spn/account/user', { ignoreDelete: true }, { forwardDone: true }) .pipe( filter(result => { if ('type' in result && result.type === 'done') { if (hasSent) { return false; } } return true }), map(result => { hasSent = true; if ('type' in result) { return null; } return result; }) ); } } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/spn.types.ts ================================================ import { FeatureID } from './features'; import { CountryInfo, GeoCoordinates, IntelEntity } from './network.types'; import { Record } from './portapi.types'; export interface SPNStatus extends Record { Status: 'failed' | 'disabled' | 'connecting' | 'connected'; HomeHubID: string; HomeHubName: string; ConnectedIP: string; ConnectedTransport: string; ConnectedCountry: CountryInfo | null; ConnectedSince: string | null; } export interface Pin extends Record { ID: string; Name: string; FirstSeen: string; EntityV4?: IntelEntity | null; EntityV6?: IntelEntity | null; States: string[]; SessionActive: boolean; HopDistance: number; ConnectedTo: { [key: string]: Lane, }; Route: string[] | null; VerifiedOwner: string; } export interface Lane { HubID: string; Capacity: number; Latency: number; } export function getPinCoords(p: Pin): GeoCoordinates | null { if (p.EntityV4 && p.EntityV4.Coordinates) { return p.EntityV4.Coordinates; } return p.EntityV6?.Coordinates || null; } export interface Device { name: string; id: string; } export interface Subscription { ends_at: string; state: 'manual' | 'active' | 'cancelled'; next_billing_date: string; payment_provider: string; } export interface Plan { name: string; amount: number; months: number; renewable: boolean; feature_ids: FeatureID[]; } export interface View { Message: string; ShowAccountData: boolean; ShowAccountButton: boolean; ShowLoginButton: boolean; ShowRefreshButton: boolean; ShowLogoutButton: boolean; } export interface UserProfile extends Record { username: string; state: string; balance: number; device: Device | null; subscription: Subscription | null; current_plan: Plan | null; next_plan: Plan | null; view: View | null; LastNotifiedOfEnd?: string; LoggedInAt?: string; } export interface Package { Name: string; HexColor: string; } export interface Feature { ID: string; Name: string; ConfigKey: string; ConfigScope: string; RequiredFeatureID: FeatureID; InPackage: Package | null; Comment: string; Beta?: boolean; ComingSoon?: boolean; // does not come from the PM API but is set by SPNService IconURL: string; } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/utils.ts ================================================ export function deepClone(o?: T | null): T { if (o === null || o === undefined) { return null as any as T; } let _out: T = (Array.isArray(o) ? [] : {}) as T; for (let _key in (o as T)) { let v = o[_key]; _out[_key] = (typeof v === "object") ? deepClone(v) : v; } return _out as T; } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/lib/websocket.service.ts ================================================ import { Injectable } from '@angular/core'; import { webSocket, WebSocketSubject, WebSocketSubjectConfig } from 'rxjs/webSocket'; import { createTauriWsConnection } from './platform-specific/tauri/tauri-websocket-subject'; import { IsTauriEnvironment } from './platform-specific/utils'; @Injectable() export class WebsocketService { constructor() { } /** * createConnection creates a new websocket connection using opts. * * @param opts Options for the websocket connection. */ createConnection(opts: WebSocketSubjectConfig): WebSocketSubject { if (IsTauriEnvironment()) { console.log('[portmaster-api] Running under Tauri - Using Tauri WebSocket'); return createTauriWsConnection(opts); } console.log('[portmaster-api] Running in browser - Using RxJS WebSocket'); return webSocket(opts); } } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/public-api.ts ================================================ /* * Public API Surface of portmaster-api */ export * from './lib/app-profile.service'; export * from './lib/app-profile.types'; export * from './lib/config.service'; export * from './lib/config.types'; export * from './lib/core.types'; export * from './lib/debug-api.service'; export * from './lib/features'; export * from './lib/meta-api.service'; export * from './lib/module'; export * from './lib/netquery.service'; export * from './lib/network.types'; export * from './lib/portapi.service'; export * from './lib/portapi.types'; export * from './lib/spn.service'; export * from './lib/spn.types'; export * from './lib/utils'; export * from './lib/websocket.service'; ================================================ FILE: desktop/angular/projects/safing/portmaster-api/src/test.ts ================================================ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'zone.js'; import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, platformBrowserDynamicTesting(), ); ================================================ FILE: desktop/angular/projects/safing/portmaster-api/tsconfig.lib.json ================================================ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", "declaration": true, "declarationMap": true, "inlineSources": true, "types": [] }, "exclude": [ "src/test.ts", "testing/**/*", "**/*.spec.ts" ] } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/tsconfig.lib.prod.json ================================================ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false }, } ================================================ FILE: desktop/angular/projects/safing/portmaster-api/tsconfig.spec.json ================================================ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/spec", "types": [ "jasmine" ] }, "files": [ "testing/**/*.ts" ], "include": [ "testing/**/*.ts", "**/*.spec.ts", "**/*.d.ts" ] } ================================================ FILE: desktop/angular/projects/safing/ui/.eslintrc.json ================================================ { "extends": "../../../.eslintrc.json", "ignorePatterns": [ "!**/*" ], "overrides": [ { "files": [ "*.ts" ], "parserOptions": { "project": [ "projects/safing/ui/tsconfig.lib.json", "projects/safing/ui/tsconfig.spec.json" ], "createDefaultProgram": true }, "rules": { "@angular-eslint/directive-selector": [ "error", { "type": "attribute", "prefix": "sfng", "style": "camelCase" } ], "@angular-eslint/component-selector": [ "error", { "type": "element", "prefix": "sfng", "style": "kebab-case" } ] } }, { "files": [ "*.html" ], "rules": {} } ] } ================================================ FILE: desktop/angular/projects/safing/ui/README.md ================================================ # Ui This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0. ## Code scaffolding Run `ng generate component component-name --project ui` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ui`. > Note: Don't forget to add `--project ui` or else it will be added to the default project in your `angular.json` file. ## Build Run `ng build ui` to build the project. The build artifacts will be stored in the `dist/` directory. ## Publishing After building your library with `ng build ui`, go to the dist folder `cd dist/ui` and run `npm publish`. ## Running unit tests Run `ng test ui` to execute the unit tests via [Karma](https://karma-runner.github.io). ## Further help To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. ================================================ FILE: desktop/angular/projects/safing/ui/karma.conf.js ================================================ // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html module.exports = function (config) { config.set({ basePath: '', frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('karma-coverage'), require('@angular-devkit/build-angular/plugins/karma') ], client: { jasmine: { // you can add configuration options for Jasmine here // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html // for example, you can disable the random execution with `random: false` // or set a specific seed with `seed: 4321` }, clearContext: false // leave Jasmine Spec Runner output visible in browser }, jasmineHtmlReporter: { suppressAll: true // removes the duplicated traces }, coverageReporter: { dir: require('path').join(__dirname, '../../../coverage/safing/ui'), subdir: '.', reporters: [ { type: 'html' }, { type: 'text-summary' } ] }, reporters: ['progress', 'kjhtml'], port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, restartOnFileChange: true }); }; ================================================ FILE: desktop/angular/projects/safing/ui/ng-package.json ================================================ { "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", "dest": "../../../dist-lib/safing/ui", "lib": { "entryFile": "src/public-api.ts" }, "assets": [ "theming.scss", "**/_*.scss" ] } ================================================ FILE: desktop/angular/projects/safing/ui/package.json ================================================ { "name": "@safing/ui", "version": "0.0.1", "peerDependencies": { "@angular/common": "~12.2.0", "@angular/core": "~12.2.0", "@angular/cdk": "~12.2.0" }, "dependencies": { "tslib": "^2.3.0" }, "exports": { "./theming": { "sass": "./theming.scss" } } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/accordion/accordion-group.html ================================================ ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/accordion/accordion-group.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, Component, Input, OnDestroy, TemplateRef } from '@angular/core'; import { Subscription } from 'rxjs'; import { SfngAccordionComponent } from './accordion'; @Component({ selector: 'sfng-accordion-group', templateUrl: './accordion-group.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class SfngAccordionGroupComponent implements OnDestroy { /** @private Currently registered accordion components */ accordions: SfngAccordionComponent[] = []; /** * A template-ref to render as the header for each accordion-component. * Receives the accordion data as an $implicit context. */ @Input() set headerTemplate(v: TemplateRef | null) { this._headerTemplate = v; if (!!this.accordions.length) { this.accordions.forEach(a => { a.headerTemplate = v; a.cdr.markForCheck(); }) } } get headerTemplate() { return this._headerTemplate } private _headerTemplate: TemplateRef | null = null; /** Whether or not one or more components can be expanded. */ @Input() set singleMode(v: any) { this._singleMode = coerceBooleanProperty(v); } get singleMode() { return this._singleMode } private _singleMode = false; /** Whether or not the accordion is disabled and does not allow expanding */ @Input() set disabled(v: any) { this._disabled = coerceBooleanProperty(v); if (this._disabled) { this.accordions.forEach(a => a.active = false); } } get disabled(): boolean { return this._disabled; } private _disabled = false; /** A list of subscriptions to the activeChange output of the registered accordion-components */ private subscriptions: Subscription[] = []; /** * Registeres an accordion component to be handled together with this * accordion group. * * @param a The accordion component to register */ register(a: SfngAccordionComponent) { this.accordions.push(a); // Tell the accordion-component about the default header-template. if (!a.headerTemplate) { a.headerTemplate = this.headerTemplate; } // Subscribe to the activeChange output of the registered // accordion and call toggle() for each event emitted. this.subscriptions.push(a.activeChange.subscribe(() => { if (this.disabled) { return; } this.toggle(a); })) } /** * Unregisters a accordion component * * @param a The accordion component to unregister */ unregister(a: SfngAccordionComponent) { const index = this.accordions.indexOf(a); if (index === -1) return; const subscription = this.subscriptions[index]; subscription.unsubscribe(); this.accordions = this.accordions.splice(index, 1); this.subscriptions = this.subscriptions.splice(index, 1); } ngOnDestroy() { this.subscriptions.forEach(s => s.unsubscribe()); this.subscriptions = []; this.accordions = []; } /** * Expand an accordion component and collaps all others if * single-mode is selected. * * @param a The accordion component to toggle. */ private toggle(a: SfngAccordionComponent) { if (!a.active && this._singleMode) { this.accordions?.forEach(a => a.active = false); } a.active = !a.active; } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/accordion/accordion.html ================================================
================================================ FILE: desktop/angular/projects/safing/ui/src/lib/accordion/accordion.module.ts ================================================ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { SfngAccordionComponent } from "./accordion"; import { SfngAccordionGroupComponent } from "./accordion-group"; @NgModule({ imports: [ CommonModule, ], declarations: [ SfngAccordionGroupComponent, SfngAccordionComponent, ], exports: [ SfngAccordionGroupComponent, SfngAccordionComponent, ] }) export class SfngAccordionModule { } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/accordion/accordion.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, HostBinding, Input, OnDestroy, OnInit, Optional, Output, TemplateRef, TrackByFunction } from '@angular/core'; import { fadeInAnimation, fadeOutAnimation } from '../animations'; import { SfngAccordionGroupComponent } from './accordion-group'; @Component({ selector: 'sfng-accordion', templateUrl: './accordion.html', exportAs: 'sfngAccordion', changeDetection: ChangeDetectionStrategy.OnPush, animations: [ fadeInAnimation, fadeOutAnimation ] }) export class SfngAccordionComponent implements OnInit, OnDestroy { /** @deprecated in favor of [data] */ @Input() title: string = ''; /** A reference to the component provided via the template context */ component = this; /** * The data the accordion component is used for. This is passed as an $implicit context * to the header template. */ @Input() data: T | undefined = undefined; @Input() trackBy: TrackByFunction = (_, c) => c /** Whether or not the accordion component starts active. */ @Input() set active(v: any) { this._active = coerceBooleanProperty(v); } get active() { return this._active; } private _active: boolean = false; /** Emits whenever the active value changes. Supports two-way bindings. */ @Output() activeChange = new EventEmitter(); /** * The header-template to render for this component. If null, the default template from * the parent accordion-group will be used. */ @Input() headerTemplate: TemplateRef | null = null; @HostBinding('class.active') /** @private Whether or not the accordion should have the 'active' class */ get activeClass(): string { return this.active; } ngOnInit(): void { // register at our parent group-component (if any). this.group?.register(this); } ngOnDestroy(): void { this.group?.unregister(this); } /** * Toggle the active-state of the accordion-component. * * @param event The mouse event. */ toggle(event?: Event) { if (!!this.group && this.group.disabled) { return; } event?.preventDefault(); this.activeChange.emit(!this.active); } constructor( public cdr: ChangeDetectorRef, @Optional() public group: SfngAccordionGroupComponent, ) { } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/accordion/index.ts ================================================ export { SfngAccordionComponent } from './accordion'; export { SfngAccordionGroupComponent } from './accordion-group'; export { SfngAccordionModule } from './accordion.module'; ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/animations/index.ts ================================================ import { animate, query, stagger, style, transition, trigger } from '@angular/animations'; export const fadeInAnimation = trigger( 'fadeIn', [ transition( ':enter', [ style({ opacity: 0, transform: 'translateY(-5px)' }), animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 1, transform: 'translateY(0px)' })) ] ), ] ); export const fadeOutAnimation = trigger( 'fadeOut', [ transition( ':leave', [ style({ opacity: 1, transform: 'translateY(0px)' }), animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 0, transform: 'translateY(-5px)' })) ] ), ] ); export const fadeInListAnimation = trigger( 'fadeInList', [ transition(':enter, * => 0, * => -1', []), transition(':increment', [ query(':enter', [ style({ opacity: 0 }), stagger(5, [ animate('300ms ease-out', style({ opacity: 1 })), ]), ], { optional: true }) ]), ] ) export const moveInOutAnimation = trigger( 'moveInOut', [ transition( ':enter', [ style({ opacity: 0, transform: 'translateX(100%)' }), animate('.2s ease-in', style({ opacity: 1, transform: 'translateX(0%)' })) ] ), transition( ':leave', [ style({ opacity: 1 }), animate('.2s ease-out', style({ opacity: 0, transform: 'translateX(100%)' })) ] ) ] ) export const moveInOutListAnimation = trigger( 'moveInOutList', [ transition(':enter, * => 0, * => -1', []), transition(':increment', [ query(':enter', [ style({ opacity: 0, transform: 'translateX(100%)' }), stagger(50, [ animate('200ms ease-out', style({ opacity: 1, transform: 'translateX(0%)' })), ]), ], { optional: true }) ]), transition(':decrement', [ query(':leave', [ stagger(-50, [ animate('200ms ease-out', style({ opacity: 0, transform: 'translateX(100%)' })), ]), ], { optional: true }) ]), ] ) ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dialog/_confirm.dialog.scss ================================================ .sfng-confirm-dialog { display: flex; flex-direction: column; align-items: flex-start; caption { @apply text-sm; opacity: .6; font-size: .6rem; } h1 { font-size: 0.85rem; font-weight: 500; margin-bottom: 1rem; } .message, h1 { flex-shrink: 0; text-overflow: ellipsis; word-break: normal; } .message { font-size: 0.75rem; flex-grow: 1; opacity: .6; max-width: 300px; } .message~input { margin-top: 0.5rem; font-size: 95%; } .close-icon { position: absolute; top: 1rem; right: 1rem; opacity: .7; cursor: pointer; &:hover { opacity: 1; } } input[type="text"] { @apply text-primary; @apply bg-gray-500 border-gray-400 bg-opacity-75 border-opacity-75; &::placeholder { @apply text-tertiary; } } .actions { margin-top: 1rem; width: 100%; display: flex; justify-content: flex-end; align-items: center; button.action-button { &:not(:last-child) { margin-right: 0.5rem; } &:not(.outline) { @apply bg-blue; } &.danger { @apply bg-red-300; } &.outline { @apply outline-none; @apply border; @apply border-gray-400; } } &>span { display: flex; align-items: center; label { margin-left: .5rem; user-select: none; } } } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dialog/_dialog.scss ================================================ sfng-dialog-container { .container { display: block; box-shadow: 0px 0px 5px 2px rgba(0, 0, 0, 0.75); @apply p-6; @apply bg-gray-300; @apply rounded; min-width: 20rem; width: fit-content; position: relative; } #drag-handle { display: block; height: 6px; background-color: white; opacity: .4; border-radius: 3px; position: absolute; bottom: calc(0.5rem - 2px); width: 30%; left: calc(50% - 15%); &:hover { opacity: .8; } } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dialog/confirm.dialog.html ================================================
{{config.caption}}

{{config.header}}

{{ config.message }}
================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dialog/confirm.dialog.ts ================================================ import { ChangeDetectionStrategy, Component, Inject, InjectionToken } from '@angular/core'; import { SfngDialogRef, SFNG_DIALOG_REF } from './dialog.ref'; export interface ConfirmDialogButton { text: string; id: string; class?: 'danger' | 'outline'; } export interface ConfirmDialogConfig { buttons?: ConfirmDialogButton[]; canCancel?: boolean; header?: string; message?: string; caption?: string; inputType?: 'text' | 'password'; inputModel?: string; inputPlaceholder?: string; } export const CONFIRM_DIALOG_CONFIG = new InjectionToken('ConfirmDialogConfig'); @Component({ templateUrl: './confirm.dialog.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class SfngConfirmDialogComponent { constructor( @Inject(SFNG_DIALOG_REF) private dialogRef: SfngDialogRef, @Inject(CONFIRM_DIALOG_CONFIG) public config: ConfirmDialogConfig, ) { if (config.inputType !== undefined && config.inputModel === undefined) { config.inputModel = ''; } } select(action?: string) { this.dialogRef.close(action || null); } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dialog/dialog.animations.ts ================================================ import { animate, state, style, transition, trigger } from "@angular/animations"; export const dialogAnimation = trigger( 'dialogContainer', [ state('void, exit', style({ opacity: 0, transform: 'scale(0.7)' })), state('enter', style({ transform: 'none', opacity: 1 })), transition( '* => enter', animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 1, transform: 'translateY(0px)' })) ), transition( '* => void, * => exit', animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 0, transform: 'scale(0.7)' })) ), ] ); ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dialog/dialog.container.ts ================================================ import { AnimationEvent } from '@angular/animations'; import { CdkDrag } from '@angular/cdk/drag-drop'; import { CdkPortalOutlet, ComponentPortal, Portal, TemplatePortal } from '@angular/cdk/portal'; import { ChangeDetectorRef, Component, ComponentRef, EmbeddedViewRef, HostBinding, HostListener, InjectionToken, Input, ViewChild } from '@angular/core'; import { Subject } from 'rxjs'; import { dialogAnimation } from './dialog.animations'; export const SFNG_DIALOG_PORTAL = new InjectionToken>('SfngDialogPortal'); export type SfngDialogState = 'opening' | 'open' | 'closing' | 'closed'; @Component({ selector: 'sfng-dialog-container', template: `
`, animations: [dialogAnimation] }) export class SfngDialogContainerComponent { onStateChange = new Subject(); ref: ComponentRef | EmbeddedViewRef | null = null; constructor( private cdr: ChangeDetectorRef, ) { } @HostBinding('@dialogContainer') state = 'enter'; @ViewChild(CdkPortalOutlet, { static: true }) _portalOutlet: CdkPortalOutlet | null = null; @ViewChild(CdkDrag, { static: true }) drag!: CdkDrag; attachComponentPortal(portal: ComponentPortal): ComponentRef { this.ref = this._portalOutlet!.attachComponentPortal(portal) return this.ref; } attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { this.ref = this._portalOutlet!.attachTemplatePortal(portal); return this.ref; } @Input() dragable: boolean = false; @HostListener('@dialogContainer.start', ['$event']) onAnimationStart({ toState }: AnimationEvent) { if (toState === 'enter') { this.onStateChange.next('opening'); } else if (toState === 'exit') { this.onStateChange.next('closing'); } } @HostListener('@dialogContainer.done', ['$event']) onAnimationEnd({ toState }: AnimationEvent) { if (toState === 'enter') { this.onStateChange.next('open'); } else if (toState === 'exit') { this.onStateChange.next('closed'); } } /** Starts the exit animation */ _startExit() { this.state = 'exit'; this.cdr.markForCheck(); } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dialog/dialog.module.ts ================================================ import { DragDropModule } from "@angular/cdk/drag-drop"; import { OverlayModule } from "@angular/cdk/overlay"; import { PortalModule } from "@angular/cdk/portal"; import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { SfngConfirmDialogComponent } from "./confirm.dialog"; import { SfngDialogContainerComponent } from "./dialog.container"; @NgModule({ imports: [ CommonModule, OverlayModule, PortalModule, DragDropModule, FormsModule, ], declarations: [ SfngDialogContainerComponent, SfngConfirmDialogComponent, ] }) export class SfngDialogModule { } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dialog/dialog.ref.ts ================================================ import { OverlayRef } from "@angular/cdk/overlay"; import { InjectionToken } from "@angular/core"; import { Observable, PartialObserver, Subject } from "rxjs"; import { filter, take } from "rxjs/operators"; import { SfngDialogContainerComponent, SfngDialogState } from "./dialog.container"; export const SFNG_DIALOG_REF = new InjectionToken>('SfngDialogRef'); export class SfngDialogRef { constructor( private _overlayRef: OverlayRef, private container: SfngDialogContainerComponent, public readonly data: D, ) { this.container.onStateChange .pipe( filter(state => state === 'closed'), take(1) ) .subscribe(() => { this._overlayRef.detach(); this._overlayRef.dispose(); this.onClose.next(this.value); this.onClose.complete(); }); } get onStateChange(): Observable { return this.container.onStateChange; } /** * @returns The overlayref that holds the dialog container. */ overlay() { return this._overlayRef } /** * @returns the instance attached to the dialog container */ contentRef() { return this.container.ref! } /** Value holds the value passed on close() */ private value: R | null = null; /** * Emits the result of the dialog and closes the overlay. */ onClose = new Subject() /** onAction only emits if close() is called with action. */ onAction(action: T, observer: PartialObserver | ((value: T) => void)): this { (this.onClose.pipe(filter(val => val === action)) as Observable) .subscribe(observer as any); // typescript does not select the correct type overload here. return this; } close(result?: R) { this.value = result || null; this.container._startExit(); } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dialog/dialog.service.ts ================================================ import { Overlay, OverlayConfig, OverlayPositionBuilder, PositionStrategy } from '@angular/cdk/overlay'; import { ComponentPortal, ComponentType, TemplatePortal } from '@angular/cdk/portal'; import { EmbeddedViewRef, Injectable, Injector } from '@angular/core'; import { filter, take, takeUntil } from 'rxjs/operators'; import { ConfirmDialogConfig, CONFIRM_DIALOG_CONFIG, SfngConfirmDialogComponent } from './confirm.dialog'; import { SfngDialogContainerComponent } from './dialog.container'; import { SfngDialogModule } from './dialog.module'; import { SfngDialogRef, SFNG_DIALOG_REF } from './dialog.ref'; export interface BaseDialogConfig { /** whether or not the dialog should close on outside-clicks and ESC */ autoclose?: boolean; /** whether or not a backdrop should be visible */ backdrop?: boolean | 'light'; /** whether or not the dialog should be dragable */ dragable?: boolean; /** * optional position strategy for the overlay. if omitted, the * overlay will be centered on the screen */ positionStrategy?: PositionStrategy; /** * Optional data for the dialog that is available either via the * SfngDialogRef for ComponentPortals as an $implicit context value * for TemplatePortals. * * Note, for template portals, data is only set as an $implicit context * value if it is not yet set in the portal! */ data?: any; } export interface ComponentPortalConfig { injector?: Injector; } @Injectable({ providedIn: SfngDialogModule }) export class SfngDialogService { constructor( private injector: Injector, private overlay: Overlay, ) { } position(): OverlayPositionBuilder { return this.overlay.position(); } create(template: TemplatePortal, opts?: BaseDialogConfig): SfngDialogRef>; create(target: ComponentType, opts?: BaseDialogConfig & ComponentPortalConfig): SfngDialogRef; create(target: ComponentType | TemplatePortal, opts: BaseDialogConfig & ComponentPortalConfig = {}): SfngDialogRef { let position: PositionStrategy = opts?.positionStrategy || this.overlay .position() .global() .centerVertically() .centerHorizontally(); let hasBackdrop = true; let backdropClass = 'dialog-screen-backdrop'; if (opts.backdrop !== undefined) { if (opts.backdrop === false) { hasBackdrop = false; } else if (opts.backdrop === 'light') { backdropClass = 'dialog-screen-backdrop-light'; } } const cfg = new OverlayConfig({ scrollStrategy: this.overlay.scrollStrategies.noop(), positionStrategy: position, hasBackdrop: hasBackdrop, backdropClass: backdropClass, }); const overlayref = this.overlay.create(cfg); // create our dialog container and attach it to the // overlay. const containerPortal = new ComponentPortal>( SfngDialogContainerComponent, undefined, this.injector, ) const containerRef = containerPortal.attach(overlayref); if (!!opts.dragable) { containerRef.instance.dragable = true; } // create the dialog ref const dialogRef = new SfngDialogRef(overlayref, containerRef.instance, opts.data); // prepare the content portal and attach it to the container let result: any; if (target instanceof TemplatePortal) { let r = containerRef.instance.attachTemplatePortal(target) if (!!r.context && typeof r.context === 'object' && !('$implicit' in r.context)) { r.context = { $implicit: opts.data, ...r.context, } } result = r } else { const contentPortal = new ComponentPortal(target, null, Injector.create({ providers: [ { provide: SFNG_DIALOG_REF, useValue: dialogRef, } ], parent: opts?.injector || this.injector, })); result = containerRef.instance.attachComponentPortal(contentPortal); } // update the container position now that we have some content. overlayref.updatePosition(); if (!!opts?.autoclose) { overlayref.outsidePointerEvents() .pipe(take(1)) .subscribe(() => dialogRef.close()); overlayref.keydownEvents() .pipe( takeUntil(overlayref.detachments()), filter(event => event.key === 'Escape') ) .subscribe(() => { dialogRef.close(); }) } return dialogRef; } confirm(opts: ConfirmDialogConfig): SfngDialogRef { return this.create(SfngConfirmDialogComponent, { autoclose: opts.canCancel, injector: Injector.create({ providers: [ { provide: CONFIRM_DIALOG_CONFIG, useValue: opts, }, ], parent: this.injector, }) }) } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dialog/index.ts ================================================ export { ConfirmDialogConfig } from './confirm.dialog'; export * from './dialog.module'; export * from './dialog.ref'; export * from './dialog.service'; ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dropdown/dropdown.html ================================================
{{ label }}
================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dropdown/dropdown.module.ts ================================================ import { OverlayModule } from "@angular/cdk/overlay"; import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { SfngDropdownComponent } from "./dropdown"; @NgModule({ imports: [ CommonModule, OverlayModule, ], declarations: [ SfngDropdownComponent, ], exports: [ SfngDropdownComponent, ] }) export class SfngDropDownModule { } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dropdown/dropdown.ts ================================================ import { coerceBooleanProperty, coerceCssPixelValue, coerceNumberProperty } from "@angular/cdk/coercion"; import { CdkOverlayOrigin, ConnectedPosition, ScrollStrategy, ScrollStrategyOptions } from "@angular/cdk/overlay"; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnInit, Output, Renderer2, TemplateRef, ViewChild } from "@angular/core"; import { fadeInAnimation, fadeOutAnimation } from '../animations'; @Component({ selector: 'sfng-dropdown', exportAs: 'sfngDropdown', templateUrl: './dropdown.html', styles: [ ` :host { display: block; } ` ], changeDetection: ChangeDetectionStrategy.OnPush, animations: [fadeInAnimation, fadeOutAnimation], }) export class SfngDropdownComponent implements OnInit { /** The trigger origin used to open the drop-down */ @ViewChild('trigger', { read: CdkOverlayOrigin }) trigger: CdkOverlayOrigin | null = null; /** * The button/drop-down label. Only when not using * {@Link SfngDropdown.externalTrigger} */ @Input() label: string = ''; /** The trigger template to use when {@Link SfngDropdown.externalTrigger} */ @Input() triggerTemplate: TemplateRef | null = null; /** Set to true to provide an external dropdown trigger template using {@Link SfngDropdown.triggerTemplate} */ @Input() set externalTrigger(v: any) { this._externalTrigger = coerceBooleanProperty(v) } get externalTrigger() { return this._externalTrigger; } private _externalTrigger = false; /** A list of classes to apply to the overlay element */ @Input() overlayClass: string = ''; /** Whether or not the drop-down is disabled. */ @Input() set disabled(v: any) { this._disabled = coerceBooleanProperty(v) } get disabled() { return this._disabled; } private _disabled = false; /** The Y-offset of the drop-down overlay */ @Input() set offsetY(v: any) { this._offsetY = coerceNumberProperty(v); } get offsetY() { return this._offsetY } private _offsetY = 4; /** The X-offset of the drop-down overlay */ @Input() set offsetX(v: any) { this._offsetX = coerceNumberProperty(v); } get offsetX() { return this._offsetX } private _offsetX = 0; /** The scrollStrategy of the drop-down */ @Input() scrollStrategy!: ScrollStrategy; /** Whether or not the pop-over is currently shown. Do not modify this directly */ isOpen = false; /** The minimum width of the drop-down */ @Input() set minWidth(val: any) { this._minWidth = coerceCssPixelValue(val) } get minWidth() { return this._minWidth } private _minWidth: string | number = 0; /** The maximum width of the drop-down */ @Input() set maxWidth(val: any) { this._maxWidth = coerceCssPixelValue(val) } get maxWidth() { return this._maxWidth } private _maxWidth: string | number | null = null; /** The minimum height of the drop-down */ @Input() set minHeight(val: any) { this._minHeight = coerceCssPixelValue(val) } get minHeight() { return this._minHeight } private _minHeight: string | number | null = null; /** The maximum width of the drop-down */ @Input() set maxHeight(val: any) { this._maxHeight = coerceCssPixelValue(val) } get maxHeight() { return this._maxHeight } private _maxHeight: string | number | null = null; /** Emits whenever the drop-down is opened */ @Output() opened = new EventEmitter(); /** Emits whenever the drop-down is closed. */ @Output() closed = new EventEmitter(); @Input() positions: ConnectedPosition[] = [ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', }, { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', }, { originX: 'end', originY: 'bottom', overlayX: 'start', overlayY: 'bottom', }, ] constructor( public readonly elementRef: ElementRef, private changeDetectorRef: ChangeDetectorRef, private renderer: Renderer2, private scrollOptions: ScrollStrategyOptions, ) { } ngOnInit() { this.scrollStrategy = this.scrollStrategy || this.scrollOptions.close(); } onOutsideClick(event: MouseEvent) { if (!!this.trigger) { const triggerEl = this.trigger.elementRef.nativeElement; let node = event.target; while (!!node) { if (node === triggerEl) { return; } node = this.renderer.parentNode(node); } } this.close(); } onOverlayClosed() { this.closed.next(); } close() { if (!this.isOpen) { return; } this.isOpen = false; this.changeDetectorRef.markForCheck(); } toggle(t: CdkOverlayOrigin | null = this.trigger) { if (this.isOpen) { this.close(); return; } this.show(t); } show(t: CdkOverlayOrigin | null = this.trigger) { if (t === null) { return; } if (this.isOpen || this._disabled) { return; } if (!!t) { this.trigger = t; const rect = (this.trigger.elementRef.nativeElement as HTMLElement).getBoundingClientRect() this.minWidth = rect ? rect.width : this.trigger.elementRef.nativeElement.offsetWidth; } this.isOpen = true; this.opened.next(); this.changeDetectorRef.markForCheck(); } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/dropdown/index.ts ================================================ export * from './dropdown'; export * from './dropdown.module'; ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/overlay-stepper/index.ts ================================================ export * from './overlay-stepper'; export * from './overlay-stepper.module'; export * from './refs'; export * from './step'; ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/overlay-stepper/overlay-stepper-container.html ================================================
================================================ FILE: desktop/angular/projects/safing/ui/src/lib/overlay-stepper/overlay-stepper-container.ts ================================================ import { animate, style, transition, trigger } from "@angular/animations"; import { CdkPortalOutlet, ComponentPortal, ComponentType } from "@angular/cdk/portal"; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentRef, Inject, InjectionToken, Injector, isDevMode, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { Subject } from "rxjs"; import { SfngDialogRef, SFNG_DIALOG_REF } from "../dialog"; import { StepperControl, StepRef, STEP_REF } from "./refs"; import { Step, StepperConfig } from "./step"; import { StepOutletComponent, STEP_ANIMATION_DIRECTION, STEP_PORTAL } from "./step-outlet"; /** * STEP_CONFIG is used to inject the StepperConfig into the OverlayStepperContainer. */ export const STEP_CONFIG = new InjectionToken('StepperConfig'); @Component({ templateUrl: './overlay-stepper-container.html', changeDetection: ChangeDetectionStrategy.OnPush, styles: [ ` :host { position: relative; display: flex; flex-direction: column; width: 600px; } ` ], animations: [ trigger( 'moveInOut', [ transition( ':enter', [ style({ opacity: 0, transform: 'translateX({{ in }})' }), animate('.2s cubic-bezier(0.4, 0, 0.2, 1)', style({ opacity: 1, transform: 'translateX(0%)' })) ], { params: { in: '100%' } } // default parameters ), transition( ':leave', [ style({ opacity: 1 }), animate('.2s cubic-bezier(0.4, 0, 0.2, 1)', style({ opacity: 0, transform: 'translateX({{ out }})' })) ], { params: { out: '-100%' } } // default parameters ) ] )] }) export class OverlayStepperContainerComponent implements OnInit, OnDestroy, StepperControl { /** Used to keep cache the stepRef instances. See documentation for {@class StepRef} */ private stepRefCache = new Map(); /** Used to emit when the stepper finished. This is always folled by emitting on onClose$ */ private onFinish$ = new Subject(); /** Emits when the stepper finished - also see {@link OverlayStepperContainerComponent.onClose}*/ get onFinish() { return this.onFinish$.asObservable(); } /** * Emits when the stepper is closed. * If the stepper if finished then onFinish will emit first */ get onClose() { return this.dialogRef.onClose; } /** The index of the currently displayed step */ currentStepIndex = -1; /** The component instance of the current step */ currentStep: Step | null = null; /** A reference to the portalOutlet used to render our steps */ @ViewChild(CdkPortalOutlet, { static: true }) portalOutlet!: CdkPortalOutlet; /** Whether or not the user can go back */ canGoBack = false; /** Whether or not the user can abort and close the stepper */ canAbort = false; /** Whether the current step is the last step */ get isLast() { return this.currentStepIndex + 1 >= this.config.steps.length; } constructor( @Inject(STEP_CONFIG) public readonly config: StepperConfig, @Inject(SFNG_DIALOG_REF) public readonly dialogRef: SfngDialogRef, private injector: Injector, private cdr: ChangeDetectorRef ) { } /** * Moves forward to the next step or closes the stepper * when moving beyond the last one. */ next(): Promise { if (this.isLast) { this.onFinish$.next(); this.close(); return Promise.resolve(); } return this.attachStep(this.currentStepIndex + 1, true) } /** * Moves back to the previous step. This does not take canGoBack * into account. */ goBack(): Promise { return this.attachStep(this.currentStepIndex - 1, false) } /** Closes the stepper - this does not run the onFinish hooks of the steps */ async close(): Promise { this.dialogRef.close(); } ngOnInit(): void { this.next(); } ngOnDestroy(): void { this.onFinish$.complete(); } /** * Attaches a new step component in the current outlet. It detaches any previous * step and calls onBeforeBack and onBeforeNext respectively. * * @param index The index of the new step to attach. * @param forward Whether or not the new step is attached by going "forward" or "backward" * @returns */ private async attachStep(index: number, forward = true) { if (index >= this.config.steps.length) { if (isDevMode()) { throw new Error(`Cannot attach step at ${index}: index out of range`) } return; } // call onBeforeNext or onBeforeBack of the current step if (this.currentStep) { if (forward) { if (!!this.currentStep.onBeforeNext) { try { await this.currentStep.onBeforeNext(); } catch (err) { console.error(`Failed to move to next step`, err) // TODO(ppacher): display error return; } } } else { if (!!this.currentStep.onBeforeBack) { try { await this.currentStep.onBeforeBack() } catch (err) { console.error(`Step onBeforeBack callback failed`, err) } } } // detach the current step component. this.portalOutlet.detach(); } const stepType = this.config.steps[index]; const contentPortal = this.createStepContentPortal(stepType, index) const outletPortal = this.createStepOutletPortal(contentPortal, forward ? 'right' : 'left') // attach the new step (which is wrapped in a StepOutletComponent). const ref = this.portalOutlet.attachComponentPortal(outletPortal); // We need to wait for the step to be actually attached in the outlet // to get access to the actual step component instance. ref.instance.portalOutlet!.attached .subscribe((stepRef: ComponentRef) => { this.currentStep = stepRef.instance; this.currentStepIndex = index; if (typeof this.config.canAbort === 'function') { this.canAbort = this.config.canAbort(this.currentStepIndex, this.currentStep); } // make sure we trigger a change-detection cycle now // markForCheck() is not enough here as we need a CD to run // immediately for the Step.buttonTemplate to be accounted for correctly. this.cdr.detectChanges(); }) } /** * Creates a new component portal for a step and provides access to the {@class StepRef} * using dependency injection. * * @param stepType The component type of the step for which a new portal should be created. * @param index The index of the current step. Used to create/cache the {@class StepRef} */ private createStepContentPortal(stepType: ComponentType, index: number): ComponentPortal { let stepRef = this.stepRefCache.get(index); if (stepRef === undefined) { stepRef = new StepRef(index, this) this.stepRefCache.set(index, stepRef); } const injector = Injector.create({ providers: [ { provide: STEP_REF, useValue: stepRef, } ], parent: this.config.injector || this.injector, }) return new ComponentPortal(stepType, undefined, injector); } /** * Creates a new component portal for a step outlet component that will attach another content * portal and wrap the attachment in a "move in" animation for a given direction. * * @param contentPortal The portal of the actual content that should be attached in the outlet * @param dir The direction for the animation of the step outlet. */ private createStepOutletPortal(contentPortal: ComponentPortal, dir: 'left' | 'right'): ComponentPortal { const injector = Injector.create({ providers: [ { provide: STEP_PORTAL, useValue: contentPortal, }, { provide: STEP_ANIMATION_DIRECTION, useValue: dir, }, ], parent: this.injector, }) return new ComponentPortal( StepOutletComponent, undefined, injector, ) } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/overlay-stepper/overlay-stepper.module.ts ================================================ import { OverlayModule } from "@angular/cdk/overlay"; import { PortalModule } from "@angular/cdk/portal"; import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { SfngDialogModule } from "../dialog"; import { OverlayStepperContainerComponent } from "./overlay-stepper-container"; import { StepOutletComponent } from "./step-outlet"; @NgModule({ imports: [ CommonModule, PortalModule, OverlayModule, SfngDialogModule, ], declarations: [ OverlayStepperContainerComponent, StepOutletComponent, ] }) export class OverlayStepperModule { } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/overlay-stepper/overlay-stepper.ts ================================================ import { ComponentRef, Injectable, Injector } from "@angular/core"; import { SfngDialogService } from "../dialog"; import { OverlayStepperContainerComponent, STEP_CONFIG } from "./overlay-stepper-container"; import { OverlayStepperModule } from "./overlay-stepper.module"; import { StepperRef } from "./refs"; import { StepperConfig } from "./step"; @Injectable({ providedIn: OverlayStepperModule }) export class OverlayStepper { constructor( private injector: Injector, private dialog: SfngDialogService, ) { } /** * Creates a new overlay stepper given it's configuration and returns * a reference to the stepper that can be used to wait for or control * the stepper from outside. * * @param config The configuration for the overlay stepper. */ create(config: StepperConfig): StepperRef { // create a new injector for our OverlayStepperContainer // that holds a reference to the StepperConfig. const injector = this.createInjector(config); const dialogRef = this.dialog.create(OverlayStepperContainerComponent, { injector: injector, autoclose: false, backdrop: 'light', dragable: false, }) const containerComponentRef = dialogRef.contentRef() as ComponentRef; return new StepperRef(containerComponentRef.instance); } /** * Creates a new dependency injector that provides access to the * stepper configuration using the STEP_CONFIG injection token. * * @param config The stepper configuration to provide using DI * @returns */ private createInjector(config: StepperConfig): Injector { return Injector.create({ providers: [ { provide: STEP_CONFIG, useValue: config, }, ], parent: this.injector, }) } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/overlay-stepper/refs.ts ================================================ import { InjectionToken } from "@angular/core"; import { Observable } from "rxjs"; import { take } from "rxjs/operators"; import { OverlayStepperContainerComponent } from "./overlay-stepper-container"; /** * STEP_REF is the injection token that is used to provide a reference to the * Stepper to each step. */ export const STEP_REF = new InjectionToken>('StepRef') export interface StepperControl { /** * Next should move the stepper forward to the next * step or close the stepper if no more steps are * available. * If the stepper is closed this way all onFinish hooks * registered at {@link StepRef} are executed. */ next(): Promise; /** * goBack should move the stepper back to the previous * step. This is a no-op if there's no previous step to * display. */ goBack(): Promise; /** * close closes the stepper but does not run any onFinish hooks * of {@link StepRef}. */ close(): Promise; } /** * StepRef is a reference to the overlay stepper and can be used to control, abort * or otherwise interact with the stepper. * * It is made available to individual steps using the STEP_REF injection token. * Each step in the OverlayStepper receives it's own StepRef instance and will receive * a reference to the same instance in case the user goes back and re-opens a step * again. * * Steps should therefore store any configuration data that is needed to restore * the previous view in the StepRef using it's save() and load() methods. */ export class StepRef implements StepperControl { private onFinishHooks: (() => PromiseLike | void)[] = []; private data: T | null = null; constructor( private currentStepIndex: number, private stepContainerRef: OverlayStepperContainerComponent, ) { this.stepContainerRef.onFinish .pipe(take(1)) .subscribe(() => this.runOnFinishHooks) } next(): Promise { return this.stepContainerRef.next(); } goBack(): Promise { return this.stepContainerRef.goBack(); } close(): Promise { return this.stepContainerRef.close(); } /** * Save saves data of the current step in the stepper session. * This data is saved in case the user decides to "go back" to * to a previous step so the old view can be restored. * * @param data The data to save in the stepper session. */ save(data: T): void { this.data = data; } /** * Load returns the data previously stored using save(). The * StepperRef automatically makes sure the correct data is returned * for the current step. */ load(): T | null { return this.data; } /** * registerOnFinish registers fn to be called when the last step * completes and the stepper is going to finish. */ registerOnFinish(fn: () => PromiseLike | void) { this.onFinishHooks.push(fn); } /** * Executes all onFinishHooks in the order they have been defined * and waits for each hook to complete. */ private async runOnFinishHooks() { for (let i = 0; i < this.onFinishHooks.length; i++) { let res = this.onFinishHooks[i](); if (typeof res === 'object' && 'then' in res) { // res is a PromiseLike so wait for it try { await res; } catch (err) { console.error(`Failed to execute on-finish hook of step ${this.currentStepIndex}: `, err) } } } } } export class StepperRef implements StepperControl { constructor(private stepContainerRef: OverlayStepperContainerComponent) { } next(): Promise { return this.stepContainerRef.next(); } goBack(): Promise { return this.stepContainerRef.goBack(); } close(): Promise { return this.stepContainerRef.close(); } get onFinish(): Observable { return this.stepContainerRef.onFinish; } get onClose(): Observable { return this.stepContainerRef.onClose; } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/overlay-stepper/step-outlet.ts ================================================ import { animate, style, transition, trigger } from "@angular/animations"; import { CdkPortalOutlet, ComponentPortal } from "@angular/cdk/portal"; import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentRef, Inject, InjectionToken, ViewChild } from "@angular/core"; import { Step } from "./step"; export const STEP_PORTAL = new InjectionToken>('STEP_PORTAL') export const STEP_ANIMATION_DIRECTION = new InjectionToken<'left' | 'right'>('STEP_ANIMATION_DIRECTION'); /** * A simple wrapper component around CdkPortalOutlet to add nice * move animations. */ @Component({ template: `
`, styles: [ ` :host{ display: flex; flex-direction: column; overflow: hidden; } ` ], changeDetection: ChangeDetectionStrategy.OnPush, animations: [ trigger( 'moveInOut', [ transition( ':enter', [ style({ opacity: 0, transform: 'translateX({{ in }})' }), animate('.2s ease-in', style({ opacity: 1, transform: 'translateX(0%)' })) ], { params: { in: '100%' } } // default parameters ), transition( ':leave', [ style({ opacity: 1 }), animate('.2s ease-out', style({ opacity: 0, transform: 'translateX({{ out }})' })) ], { params: { out: '-100%' } } // default parameters ) ] )] }) export class StepOutletComponent implements AfterViewInit { /** @private - Whether or not the animation should run. */ _appAnimate = false; /** The actual step instance that has been attached. */ stepInstance: ComponentRef | null = null; /** @private - used in animation interpolation for translateX */ get in() { return this._animateDirection == 'left' ? '-100%' : '100%' } /** @private - used in animation interpolation for traslateX */ get out() { return this._animateDirection == 'left' ? '100%' : '-100%' } /** The portal outlet in our view used to attach the step */ @ViewChild(CdkPortalOutlet, { static: true }) portalOutlet!: CdkPortalOutlet; constructor( @Inject(STEP_PORTAL) public portal: ComponentPortal, @Inject(STEP_ANIMATION_DIRECTION) public _animateDirection: 'left' | 'right', private cdr: ChangeDetectorRef ) { } ngAfterViewInit(): void { this.portalOutlet?.attached .subscribe(ref => { this.stepInstance = ref as ComponentRef; this._appAnimate = true; this.cdr.detectChanges(); }) } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/overlay-stepper/step.ts ================================================ import { Injector, TemplateRef, Type } from "@angular/core"; import { Observable } from "rxjs"; export interface Step { /** * validChange should emit true or false when the current step * is valid and the "next" button should be visible. */ validChange: Observable; /** * onBeforeBack, if it exists, is called when the user * clicks the "Go Back" button but before the current step * is unloaded. * * The OverlayStepper will wait for the callback to resolve or * reject but will not abort going back! */ onBeforeBack?: () => Promise; /** * onBeforeNext, if it exists, is called when the user * clicks the "Next" button but before the current step * is unloaded. * * The OverlayStepper willw ait for the callback to resolve * or reject. If it rejects the current step will not be unloaded * and the rejected error will be displayed to the user. */ onBeforeNext?: () => Promise; /** * nextButtonLabel can overwrite the label for the "Next" button. */ nextButtonLabel?: string; /** * buttonTemplate may hold a tempalte ref that is rendered instead * of the default button row with a "Go Back" and a "Next" button. * Note that if set, the step component must make sure to handle * navigation itself. See {@class StepRef} for more information on how * to control the stepper. */ buttonTemplate?: TemplateRef; } export interface StepperConfig { /** * canAbort can be set to a function that is called * for each step to determine if the stepper is abortable. */ canAbort?: (idx: number, step: Step) => boolean; /** steps holds the list of steps to execute */ steps: Array> /** * injector, if set, defines the parent injector used to * create dedicated instances of the step types. */ injector?: Injector; } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/pagination/_pagination.scss ================================================ sfng-pagination { .pagination { @apply my-2 w-full flex justify-between; button { @apply text-xxs px-2 flex items-center justify-start; &.page { @apply bg-cards-secondary; @apply opacity-50; &:hover { @apply opacity-100; } } &.active-page { @apply text-blue font-medium opacity-100; } } } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/pagination/dynamic-items-paginator.ts ================================================ import { BehaviorSubject, Observable, Subscription } from "rxjs"; import { Pagination, clipPage } from "./pagination"; export interface Datasource { // view should emit all items in the given page using the specified page number. view(page: number, pageSize: number): Observable; } export class DynamicItemsPaginator implements Pagination { private _total = 0; private _pageNumber$ = new BehaviorSubject(1); private _pageItems$ = new BehaviorSubject([]); private _pageLoading$ = new BehaviorSubject(false); private _pageSubscription = Subscription.EMPTY; /** Returns the number of total pages. */ get total() { return this._total; } /** Emits the current page number */ get pageNumber$() { return this._pageNumber$.asObservable() } /** Emits all items of the current page */ get pageItems$() { return this._pageItems$.asObservable() } /** Emits whether or not we're loading the next page */ get pageLoading$() { return this._pageLoading$.asObservable() } constructor( private source: Datasource, public readonly pageSize = 25, ) { } reset(newTotal: number) { this._total = Math.ceil(newTotal / this.pageSize); this.openPage(1); } /** Clear resets the current total and emits an empty item set. */ clear() { this._total = 0; this._pageItems$.next([]); this._pageNumber$.next(1); this._pageSubscription.unsubscribe(); } openPage(pageNumber: number): void { pageNumber = clipPage(pageNumber, this.total); this._pageLoading$.next(true); this._pageSubscription.unsubscribe() this._pageSubscription = this.source.view(pageNumber, this.pageSize) .subscribe({ next: results => { this._pageLoading$.next(false); this._pageItems$.next(results); this._pageNumber$.next(pageNumber); } }); } nextPage(): void { this.openPage(this._pageNumber$.getValue() + 1) } prevPage(): void { this.openPage(this._pageNumber$.getValue() - 1) } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/pagination/index.ts ================================================ export * from './dynamic-items-paginator'; export * from './pagination'; export * from './pagination.module'; export * from './snapshot-paginator'; ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/pagination/pagination.html ================================================ ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/pagination/pagination.module.ts ================================================ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { SfngPaginationContentDirective } from "."; import { SfngPaginationWrapperComponent } from "./pagination"; @NgModule({ imports: [ CommonModule, ], declarations: [ SfngPaginationContentDirective, SfngPaginationWrapperComponent, ], exports: [ SfngPaginationContentDirective, SfngPaginationWrapperComponent, ], }) export class SfngPaginationModule { } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/pagination/pagination.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, Directive, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChanges, TemplateRef } from "@angular/core"; import { Observable, Subscription } from "rxjs"; export interface Pagination { /** * Total should return the total number of pages */ total: number; /** * pageNumber$ should emit the currently displayed page */ pageNumber$: Observable; /** * pageItems$ should emit all items of the current page */ pageItems$: Observable; /** * nextPage should progress to the next page. If there are no more * pages than nextPage() should be a no-op. */ nextPage(): void; /** * prevPage should move back the the previous page. If there is no * previous page, prevPage should be a no-op. */ prevPage(): void; /** * openPage opens the page @pageNumber. If pageNumber is greater than * the total amount of pages it is clipped to the lastPage. If it is * less than 1, it is clipped to 1. */ openPage(pageNumber: number): void } @Directive({ selector: '[sfngPageContent]' }) export class SfngPaginationContentDirective { constructor(public readonly templateRef: TemplateRef) { } } export interface PageChangeEvent { totalPages: number; currentPage: number; } @Component({ selector: 'sfng-pagination', templateUrl: './pagination.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class SfngPaginationWrapperComponent implements OnChanges, OnDestroy { private _sub: Subscription = Subscription.EMPTY; @Input() source: Pagination | null = null; @Output() pageChange = new EventEmitter(); @ContentChild(SfngPaginationContentDirective) content: SfngPaginationContentDirective | null = null; currentPageIdx: number = 0; pageNumbers: number[] = []; ngOnChanges(changes: SimpleChanges) { if ('source' in changes) { this.subscribeToSource(changes.source.currentValue); } } ngOnDestroy() { this._sub.unsubscribe(); } private subscribeToSource(source: Pagination) { // Unsubscribe from the previous pagination, if any this._sub.unsubscribe(); this._sub = new Subscription(); this._sub.add( source.pageNumber$ .subscribe(current => { this.currentPageIdx = current; this.pageNumbers = generatePageNumbers(current - 1, source.total); this.cdr.markForCheck(); this.pageChange.next({ totalPages: source.total, currentPage: current, }) }) ) } constructor(private cdr: ChangeDetectorRef) { } } /** * Generates an array of page numbers that should be displayed in paginations. * * @param current The current page number * @param countPages The total number of pages * @returns An array of page numbers to display */ export function generatePageNumbers(current: number, countPages: number): number[] { let delta = 2; let leftRange = current - delta; let rightRange = current + delta + 1; return Array.from({ length: countPages }, (v, k) => k + 1) .filter(i => i === 1 || i === countPages || (i >= leftRange && i < rightRange)); } export function clipPage(pageNumber: number, total: number): number { if (pageNumber < 1) { return 1; } if (pageNumber > total) { return total; } return pageNumber; } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/pagination/snapshot-paginator.ts ================================================ import { BehaviorSubject, Observable } from "rxjs"; import { debounceTime, map } from "rxjs/operators"; import { clipPage, Pagination } from "./pagination"; export class SnapshotPaginator implements Pagination { private _itemSnapshot: T[] = []; private _activePageItems = new BehaviorSubject([]); private _totalPages = 1; private _updatePending = false; constructor( public items$: Observable, public readonly pageSize: number, ) { items$ .pipe(debounceTime(100)) .subscribe(data => { this._itemSnapshot = data; this.openPage(this._currentPage.getValue()); }); this._currentPage .subscribe(page => { this._updatePending = false; const start = this.pageSize * (page - 1); const end = this.pageSize * page; this._totalPages = Math.ceil(this._itemSnapshot.length / this.pageSize) || 1; this._activePageItems.next(this._itemSnapshot.slice(start, end)); }) } private _currentPage = new BehaviorSubject(0); get updatePending() { return this._updatePending; } get pageNumber$(): Observable { return this._activePageItems.pipe(map(() => this._currentPage.getValue())); } get pageNumber(): number { return this._currentPage.getValue(); } get total(): number { return this._totalPages } get pageItems$(): Observable { return this._activePageItems.asObservable(); } get pageItems(): T[] { return this._activePageItems.getValue(); } get snapshot(): T[] { return this._itemSnapshot }; reload(): void { this.openPage(this._currentPage.getValue()) } nextPage(): void { this.openPage(this._currentPage.getValue() + 1) } prevPage(): void { this.openPage(this._currentPage.getValue() - 1) } openPage(pageNumber: number): void { pageNumber = clipPage(pageNumber, this.total); this._currentPage.next(pageNumber); } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/select/_select.scss ================================================ .sfng-select { @apply cursor-pointer relative p-0 flex whitespace-nowrap w-full items-center outline-none self-center overflow-hidden; @apply hover:bg-gray-400; @apply bg-gray-300 border border-gray-300 transition ease-in-out duration-200; &.disabled { @apply cursor-not-allowed opacity-75 hover:bg-gray-400; } min-width: 6rem; max-width: 12rem; &.active { @apply bg-gray-400; div.arrow svg { @apply transform -rotate-90; } } & > span { @apply flex-grow text-ellipsis inline-block overflow-hidden; @apply px-2; } div.arrow { @apply flex flex-row items-center justify-center bg-gray-200 rounded-r-sm; @apply w-5 h-7; svg { @apply w-4 m-0 p-0 rotate-90 transform transition ease-in-out duration-100; g { @apply text-white; stroke: currentColor; } } } } .sfng-select-dropdown { ul { max-height: 12rem; @apply relative py-1 overflow-auto; li { @apply py-2; @apply flex flex-row items-center justify-start gap-1 transition duration-200 ease-in-out cursor-pointer hover:bg-gray-300; } li:not(.disabled) { @apply hover:bg-gray-300; } li.disabled { @apply cursor-not-allowed; } } } .sfng-select-dropdown.sfng-select-inline { ul { max-height: unset; } } sfng-select-item { @apply text-xxs w-full font-medium gap-3 text-primary flex flex-row items-center justify-start; &.disabled { @apply opacity-75 cursor-not-allowed; } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/select/index.ts ================================================ export * from './item'; export * from './select'; export * from './select.module'; ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/select/item.ts ================================================ import { ListKeyManagerOption } from '@angular/cdk/a11y'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Component, Directive, HostBinding, Input, Optional, TemplateRef } from '@angular/core'; export interface SelectOption extends ListKeyManagerOption { value: any; selected: boolean; data?: T; label?: string; description?: string; templateRef?: TemplateRef; disabled?: boolean; } @Component({ selector: 'sfng-select-item', template: ``, }) export class SfngSelectItemComponent implements ListKeyManagerOption { @HostBinding('class.disabled') get disabled() { return this.sfngSelectValue?.disabled || false; } getLabel() { return this.sfngSelectValue?.label || ''; } constructor(@Optional() private sfngSelectValue: SfngSelectValueDirective) { } } @Directive({ selector: '[sfngSelectValue]', }) export class SfngSelectValueDirective implements SelectOption { @Input('sfngSelectValue') value: any; @Input('sfngSelectValueLabel') label?: string; @Input('sfngSelectValueData') data?: T; @Input('sfngSelectValueDescription') description = ''; @Input('sfngSelectValueDisabled') set disabled(v: any) { this._disabled = coerceBooleanProperty(v) } get disabled() { return this._disabled } private _disabled = false; getLabel() { return this.label || ('' + this.value); } /** Whether or not the item is currently selected */ selected = false; constructor(public templateRef: TemplateRef) { } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/select/select.html ================================================
  • Add {{ searchText }}
{{ data.label || data.value }} ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/select/select.module.ts ================================================ import { CdkScrollableModule } from "@angular/cdk/scrolling"; import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { SfngDropDownModule } from "../dropdown"; import { SfngTooltipModule } from "../tooltip"; import { SfngSelectItemComponent, SfngSelectValueDirective } from "./item"; import { SfngSelectComponent, SfngSelectRenderedItemDirective } from "./select"; @NgModule({ imports: [ CommonModule, FormsModule, ReactiveFormsModule, SfngDropDownModule, SfngTooltipModule, CdkScrollableModule ], declarations: [ SfngSelectComponent, SfngSelectValueDirective, SfngSelectItemComponent, SfngSelectRenderedItemDirective ], exports: [ SfngSelectComponent, SfngSelectValueDirective, SfngSelectItemComponent, ] }) export class SfngSelectModule { } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/select/select.ts ================================================ import { ListKeyManager, ListKeyManagerOption } from '@angular/cdk/a11y'; import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion'; import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChildren, DestroyRef, Directive, ElementRef, EventEmitter, HostBinding, HostListener, Input, OnDestroy, Output, QueryList, TemplateRef, ViewChild, ViewChildren, forwardRef, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { BehaviorSubject, combineLatest } from 'rxjs'; import { startWith } from 'rxjs/operators'; import { SfngDropdownComponent } from '../dropdown'; import { SelectOption, SfngSelectValueDirective } from './item'; export type SelectModes = 'single' | 'multi'; type ModeInput = { mode: SelectModes; } type SelectValue = S['mode'] extends 'single' ? T : T[]; export type SortByFunc = (a: SelectOption, b: SelectOption) => number; export type SelectDisplayMode = 'dropdown' | 'inline'; @Directive({ selector: '[sfngSelectRenderedListItem]' }) export class SfngSelectRenderedItemDirective implements ListKeyManagerOption { @Input('sfngSelectRenderedListItem') option: SelectOption | null = null; getLabel() { return this.option?.label || ''; } get disabled() { return this.option?.disabled || false; } @HostBinding('class.bg-gray-300') set focused(v: boolean) { this._focused = v; } get focused() { return this._focused } private _focused = false; constructor(public readonly elementRef: ElementRef) { } } @Component({ selector: 'sfng-select', templateUrl: './select.html', changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SfngSelectComponent), multi: true, }, ] }) export class SfngSelectComponent implements AfterViewInit, ControlValueAccessor, OnDestroy { /** emits the search text entered by the user */ private search$ = new BehaviorSubject(''); /** emits and completes when the component is destroyed. */ private destroyRef = inject(DestroyRef); /** the key manager used for keyboard support */ private keyManager!: ListKeyManager; @ViewChild(SfngDropdownComponent, { static: false }) dropdown: SfngDropdownComponent | null = null; /** A reference to the cdk-scrollable directive that's placed on the item list */ @ViewChild('scrollable', { read: ElementRef }) scrollableList?: ElementRef; @ContentChildren(SfngSelectValueDirective) userProvidedItems!: QueryList; @ViewChildren('renderedItem', { read: SfngSelectRenderedItemDirective }) renderedItems!: QueryList; /** A list of all items available in the select box including dynamic ones. */ allItems: SelectOption[] = [] /** The acutally rendered list of items after applying search and item threshold */ items: SelectOption[] = []; @Input() @HostBinding('attr.tabindex') readonly tabindex = 0; @HostBinding('attr.role') readonly role = 'listbox'; value?: SelectValue; /** A list of currently selected items */ currentItems: SelectOption[] = []; /** The current search text. Used by ngModel */ searchText = ''; /** Whether or not the select operates in "single" or "multi" mode */ @Input() mode: SelectModes = 'single'; @Input() displayMode: SelectDisplayMode = 'dropdown'; /** The placehodler to show when nothing is selected */ @Input() placeholder = 'Select' /** The type of item to show in multi mode when more than one value is selected */ @Input() itemName = ''; /** The maximum number of items to render. */ @Input() set itemLimit(v: any) { this._maxItemLimit = coerceNumberProperty(v) } get itemLimit(): number { return this._maxItemLimit } private _maxItemLimit = Infinity; /** The placeholder text for the search bar */ @Input() searchPlaceholder = ''; /** Whether or not the search bar is visible */ @Input() set allowSearch(v: any) { this._allowSearch = coerceBooleanProperty(v); } get allowSearch(): boolean { return this._allowSearch; } private _allowSearch = false; /** The minimum number of items required for the search bar to be visible */ @Input() set searchItemThreshold(v: any) { this._searchItemThreshold = coerceNumberProperty(v); } get searchItemThreshold(): number { return this._searchItemThreshold; } private _searchItemThreshold = 0; /** * Whether or not the select should be disabled when not options * are available. */ @Input() set disableWhenEmpty(v: any) { this._disableWhenEmpty = coerceBooleanProperty(v); } get disableWhenEmpty() { return this._disableWhenEmpty; } private _disableWhenEmpty = false; /** Whether or not the select component will add options for dynamic values as well. */ @Input() set dynamicValues(v: any) { this._dynamicValues = coerceBooleanProperty(v); } get dynamicValues() { return this._dynamicValues } private _dynamicValues = false; /** An optional template to use for dynamic values. */ @Input() dynamicValueTemplate?: TemplateRef; /** The minimum-width of the drop-down. See {@link SfngDropdownComponent.minWidth} */ @Input() minWidth: any; /** The minimum-width of the drop-down. See {@link SfngDropdownComponent.minHeight} */ @Input() minHeight: any; /** Whether or not selected items should be sorted to the top */ @Input() set sortValues(v: any) { this._sortValues = coerceBooleanProperty(v); } get sortValues() { if (this._sortValues === null) { return this.mode === 'multi'; } return this._sortValues; } private _sortValues: boolean | null = null; /** The sort function to use. Defaults to sort by label/value */ @Input() sortBy: SortByFunc = (a: SelectOption, b: SelectOption) => { if ((a.label || a.value) < (b.label || b.value)) { return 1; } if ((a.label || a.value) > (b.label || b.value)) { return -1; } return 0; } @Input() set disabled(v: any) { const disabled = coerceBooleanProperty(v); this.setDisabledState(disabled); } get disabled() { return this._disabled; } private _disabled: boolean = false; @HostListener('keydown.enter', ['$event']) @HostListener('keydown.space', ['$event']) onEnter(event: Event) { if (!this.dropdown?.isOpen) { this.dropdown?.toggle() event.preventDefault(); event.stopPropagation(); return; } if (this.keyManager.activeItem !== null && !!this.keyManager.activeItem?.option) { this.selectItem(this.keyManager.activeItem.option) event.preventDefault(); event.stopPropagation(); return; } } @HostListener('keydown', ['$event']) onKeyDown(event: KeyboardEvent) { this.keyManager.onKeydown(event); } @Output() closed = new EventEmitter(); @Output() opened = new EventEmitter(); trackItem(_: number, item: SelectOption) { return item.value; } setDisabledState(disabled: boolean) { this._disabled = disabled; this.cdr.markForCheck(); } constructor(private cdr: ChangeDetectorRef) { } ngAfterViewInit(): void { this.keyManager = new ListKeyManager(this.renderedItems) .withVerticalOrientation() .withHomeAndEnd() .withWrap() .withTypeAhead(); this.keyManager.change .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(itemIdx => { this.renderedItems.forEach(item => { item.focused = false; }) this.keyManager.activeItem!.focused = true; // the item might be out-of-view so make sure // we scroll enough to have it inside the view const scrollable = this.scrollableList?.nativeElement; if (!!scrollable) { const active = this.keyManager.activeItem!.elementRef.nativeElement; const activeHeight = active.getBoundingClientRect().height; const bottom = scrollable.scrollTop + scrollable.getBoundingClientRect().height; const top = scrollable.scrollTop; let scrollTo = -1; if (active.offsetTop >= bottom) { scrollTo = top + active.offsetTop - bottom + activeHeight; } else if (active.offsetTop < top) { scrollTo = active.offsetTop; } if (scrollTo > -1) { scrollable.scrollTo({ behavior: 'smooth', top: scrollTo, }) } } this.cdr.markForCheck(); }) combineLatest([ this.userProvidedItems!.changes .pipe(startWith(undefined)), this.search$ ]) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe( ([_, search]) => { this.updateItems(); search = (search || '').toLocaleLowerCase() let items: SelectOption[] = []; if (search === '') { items = this.allItems!; } else { items = this.allItems!.filter(item => { // we always count selected items as a "match" in search mode. // this is to ensure the user always see all selected items. if (item.selected) { return true; } if (!!item.value && typeof item.value === 'string') { if (item.value.toLocaleLowerCase().includes(search)) { return true; } } if (!!item.label) { if (item.label.toLocaleLowerCase().includes(search)) { return true } } return false; }) } this.items = items.slice(0, this._maxItemLimit); this.keyManager.setActiveItem(0); this.cdr.detectChanges(); } ); } ngOnDestroy(): void { this.search$.complete(); } @HostListener('blur') onBlur(): void { this.onTouch(); } /** @private - called when the internal dropdown opens */ onDropdownOpen() { // emit the open event on this component as well this.opened.next(); // reset the search. We do that when opened instead of closed // to avoid flickering when the component height increases // during the "close" animation this.onSearch(''); } /** @private - called when the internal dropdown closes */ onDropdownClose() { this.closed.next(); } onSearch(text: string) { this.searchText = text; this.search$.next(text); } selectItem(item: SelectOption) { if (item.disabled) { return; } const isSelected = this.currentItems.findIndex(selected => item.value === selected.value); if (isSelected === -1) { item.selected = true; if (this.mode === 'single') { this.currentItems.forEach(i => i.selected = false); this.currentItems = [item]; this.value = item.value; } else { this.currentItems.push(item); // TODO(ppacher): somehow typescript does not correctly pick up // the type of this.value here although it can be infered from the // mode === 'single' check above. this.value = [ ...(this.value || []) as any, item.value, ] as any } } else if (this.mode !== 'single') { // "unselecting" a value is not allowed in single mode this.currentItems.splice(isSelected, 1) item.selected = false; // same note about typescript as above. this.value = (this.value as T[]).filter(val => val !== item.value) as any; } // only close the drop down in single mode. In multi-mode // we keep it open as the user might want to select an additional // item as well. if (this.mode === 'single') { this.dropdown?.close(); } this.onChange(this.value!); } private updateItems() { let values: T[] = []; if (this.mode === 'single') { values = [this.value as T]; } else { values = (this.value as T[]) || []; } this.currentItems = []; this.allItems = []; // mark all user-selected items as "deselected" first this.userProvidedItems?.forEach(item => { item.selected = false; this.allItems.push(item); }); for (let i = 0; i < values.length; i++) { const val = values[i]; let option: SelectOption | undefined = this.userProvidedItems?.find(item => item.value === val); if (!option) { if (!this._dynamicValues) { continue } option = { selected: true, value: val, label: `${val}`, } this.allItems.push(option); } else { option.selected = true } this.currentItems.push(option); } if (this.sortValues) { this.allItems.sort((a, b) => { if (b.selected && !a.selected) { return 1; } if (a.selected && !b.selected) { return -1; } return this.sortBy(a, b) }) } } writeValue(value: SelectValue): void { this.value = value; this.updateItems(); this.cdr.markForCheck(); } onChange = (value: SelectValue): void => { } registerOnChange(fn: (value: SelectValue) => void): void { this.onChange = fn; } onTouch = (): void => { } registerOnTouched(fn: () => void): void { this.onTouch = fn; } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tabs/_tab-group.scss ================================================ sfng-tab-group { @apply flex flex-col overflow-hidden; } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tabs/index.ts ================================================ export { SfngTabComponent, SfngTabContentDirective } from './tab'; export { SfngTabContentScrollEvent, SfngTabGroupComponent } from './tab-group'; export { SfngTabModule as TabModule } from './tabs.module'; ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tabs/tab-group.html ================================================
{{ tab.title }}
================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tabs/tab-group.ts ================================================ import { ListKeyManager } from "@angular/cdk/a11y"; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { CdkPortalOutlet, ComponentPortal } from "@angular/cdk/portal"; import { AfterContentInit, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentRef, ContentChildren, DestroyRef, ElementRef, EventEmitter, Injector, Input, OnInit, Output, QueryList, ViewChild, ViewChildren, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, Router } from "@angular/router"; import { Observable, Subject } from "rxjs"; import { distinctUntilChanged, map, startWith } from "rxjs/operators"; import { SfngTabComponent, TAB_ANIMATION_DIRECTION, TAB_PORTAL, TAB_SCROLL_HANDLER, TabOutletComponent } from "./tab"; export interface SfngTabContentScrollEvent { event?: Event; scrollTop: number; previousScrollTop: number; } /** * Tab group component for rendering a tab-style navigation with support for * keyboard navigation and type-ahead. Tab content are lazy loaded using a * structural directive. * The tab group component also supports adding the current active tab index * to the active route so it is possible to navigate through tabs using back/forward * keys (browser history) as well. * * Example: * * * *
* Some content *
*
* * *
* Some different content *
*
* *
*/ @Component({ selector: 'sfng-tab-group', templateUrl: './tab-group.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class SfngTabGroupComponent implements AfterContentInit, AfterViewInit, OnInit { @ContentChildren(SfngTabComponent) tabs: QueryList | null = null; /** References to all tab header elements */ @ViewChildren('tabHeader', { read: ElementRef }) tabHeaders: QueryList> | null = null; /** Reference to the active tab bar element */ @ViewChild('activeTabBar', { read: ElementRef, static: false }) activeTabBar: ElementRef | null = null; /** Reference to the portal outlet that we will use to render a TabOutletComponent. */ @ViewChild(CdkPortalOutlet, { static: true }) portalOutlet: CdkPortalOutlet | null = null; @Output() tabContentScroll = new EventEmitter(); /** The name of the tab group. Used to update the currently active tab in the route */ @Input() name = 'tab' @Input() outletClass = ''; private scrollTop: number = 0; /** Whether or not the current tab should be syncronized with the angular router using a query parameter */ @Input() set linkRouter(v: any) { this._linkRouter = coerceBooleanProperty(v) } get linkRouter() { return this._linkRouter } private _linkRouter = true; /** Whether or not the default tab header should be rendered */ @Input() set customHeader(v: any) { this._customHeader = coerceBooleanProperty(v) } get customHeader() { return this._customHeader } private _customHeader = false; private tabActivate$ = new Subject(); private destroyRef = inject(DestroyRef); /** Emits the tab QueryList every time there are changes to the content-children */ get tabs$() { return this.tabs?.changes .pipe( map(() => this.tabs), startWith(this.tabs) ) } /** onActivate fires when a tab has been activated. */ get onActivate(): Observable { return this.tabActivate$.asObservable() } /** the index of the currently active tab. */ activeTabIndex = -1; /** The key manager used to support keyboard navigation and type-ahead in the tab group */ private keymanager: ListKeyManager | null = null; /** Used to force the animation direction when calling activateTab. */ private forceAnimationDirection: 'left' | 'right' | null = null; /** * pendingTabIdx holds the id or the index of a tab that should be activated after the component * has been bootstrapped. We need to cache this value here because the ActivatedRoute might emit * before we are AfterViewInit. */ private pendingTabIdx: string | null = null; constructor( private injector: Injector, private route: ActivatedRoute, private router: Router, private cdr: ChangeDetectorRef ) { } /** * @private * Used to forward keyboard events to the keymanager. */ onKeydown(v: KeyboardEvent) { this.keymanager?.onKeydown(v); } ngOnInit(): void { this.route.queryParamMap .pipe( takeUntilDestroyed(this.destroyRef), map(params => params.get(this.name)), distinctUntilChanged(), ) .subscribe(newIdx => { if (!this._linkRouter) { return; } if (!!this.keymanager && !!this.tabs) { const actualIndex = this.getIndex(newIdx); if (actualIndex !== null) { this.keymanager.setActiveItem(actualIndex); this.cdr.markForCheck(); } } else { this.pendingTabIdx = newIdx; } }) } ngAfterContentInit(): void { this.keymanager = new ListKeyManager(this.tabs!) .withHomeAndEnd() .withHorizontalOrientation("ltr") .withTypeAhead() .withWrap() this.tabs!.changes .subscribe(() => { if (this.portalOutlet?.hasAttached()) { if (this.tabs!.length === 0) { this.portalOutlet.detach(); } } else { if (this.tabs!.length > 0) { this.activateTab(0) } } }) this.keymanager.change .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(change => { const activeTab = this.tabs!.get(change); if (!!activeTab && !!activeTab.tabContent) { const prevIdx = this.activeTabIndex; let animationDirection: 'left' | 'right' = prevIdx < change ? 'left' : 'right'; if (this.forceAnimationDirection !== null) { animationDirection = this.forceAnimationDirection; this.forceAnimationDirection = null; } if (this.portalOutlet?.attachedRef) { // we know for sure that attachedRef is a ComponentRef of TabOutletComponent const ref = (this.portalOutlet.attachedRef as ComponentRef) ref.instance._animateDirection = animationDirection; ref.instance.outletClass = this.outletClass; ref.changeDetectorRef.detectChanges(); } this.portalOutlet?.detach(); const newOutletPortal = this.createTabOutlet(activeTab, animationDirection); this.activeTabIndex = change; this.tabContentScroll.next({ scrollTop: 0, previousScrollTop: this.scrollTop, }) this.scrollTop = 0; this.tabActivate$.next(activeTab.id); this.portalOutlet?.attach(newOutletPortal); this.repositionTabBar(); if (this._linkRouter) { this.router.navigate([], { queryParams: { ...this.route.snapshot.queryParams, [this.name]: this.activeTabIndex, } }) } this.cdr.markForCheck(); } }); if (this.pendingTabIdx === null) { // active the first tab that is NOT disabled const firstActivatable = this.tabs?.toArray().findIndex(tap => !tap.disabled); if (firstActivatable !== undefined) { this.keymanager.setActiveItem(firstActivatable); } } else { const idx = this.getIndex(this.pendingTabIdx); if (idx !== null) { this.keymanager.setActiveItem(idx); this.pendingTabIdx = null; } } } ngAfterViewInit(): void { this.repositionTabBar(); this.tabHeaders?.changes.subscribe(() => this.repositionTabBar()) setTimeout(() => this.repositionTabBar(), 250) } /** * @private * Activates a new tab * * @param idx The index of the new tab. */ activateTab(idx: number, forceDirection?: 'left' | 'right') { if (forceDirection !== undefined) { this.forceAnimationDirection = forceDirection; } this.keymanager?.setActiveItem(idx); } private getIndex(newIdx: string | null): number | null { let actualIndex: number = -1; if (!this.tabs) { return null; } if (newIdx === undefined || newIdx === null) { // not present in the URL return null; } if (isNaN(+newIdx)) { // likley the ID of a tab actualIndex = this.tabs?.toArray().findIndex(tab => tab.id === newIdx) || -1; } else { // it's a number as a string actualIndex = +newIdx; } if (actualIndex < 0) { return null; } return actualIndex; } private repositionTabBar() { if (!this.tabHeaders) { return; } requestAnimationFrame(() => { const tabHeader = this.tabHeaders!.get(this.activeTabIndex); if (!tabHeader || !this.activeTabBar) { return; } const rect = tabHeader.nativeElement.getBoundingClientRect(); const transform = `translate(${tabHeader.nativeElement.offsetLeft}px, ${tabHeader.nativeElement.offsetTop + rect.height}px)` this.activeTabBar.nativeElement.style.width = `${rect.width}px` this.activeTabBar.nativeElement.style.transform = transform; this.activeTabBar.nativeElement.style.opacity = '1'; // initialize animations on the active-tab-bar required if (!this.activeTabBar.nativeElement.classList.contains("transition-all")) { // only initialize the transitions if this is the very first "reposition" // this is to prevent the bar from animating to the "bottom" line of the tab // header the first time. requestAnimationFrame(() => { this.activeTabBar?.nativeElement.classList.add("transition-all", "duration-200"); }) } }) } private createTabOutlet(tab: SfngTabComponent, animationDir: 'left' | 'right'): ComponentPortal { const injector = Injector.create({ providers: [ { provide: TAB_PORTAL, useValue: tab.tabContent!.portal, }, { provide: TAB_ANIMATION_DIRECTION, useValue: animationDir, }, { provide: TAB_SCROLL_HANDLER, useValue: (e: Event) => { const newScrollTop = (e.target as HTMLElement).scrollTop; tab.tabContentScroll.next(e); this.tabContentScroll.next({ event: e, scrollTop: newScrollTop, previousScrollTop: this.scrollTop, }); this.scrollTop = newScrollTop; } }, ], parent: this.injector, name: 'TabOutletInjectot', }) return new ComponentPortal( TabOutletComponent, undefined, injector ) } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tabs/tab.ts ================================================ import { animate, style, transition, trigger } from "@angular/animations"; import { ListKeyManagerOption } from "@angular/cdk/a11y"; import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { CdkPortalOutlet, TemplatePortal } from "@angular/cdk/portal"; import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, Directive, EventEmitter, Inject, InjectionToken, Input, Output, TemplateRef, ViewChild, ViewContainerRef } from "@angular/core"; /** TAB_PORTAL is the injection token used to inject the TabContentDirective portal into TabOutletComponent */ export const TAB_PORTAL = new InjectionToken('TAB_PORTAL'); /** TAB_ANIMATION_DIRECTION is the injection token used to control the :enter animation origin of TabOutletComponent */ export const TAB_ANIMATION_DIRECTION = new InjectionToken<'left' | 'right'>('TAB_ANIMATION_DIRECTION'); /** TAB_SCROLL_HANDLER is called by the SfngTabOutletComponent when a scroll event occurs. */ export const TAB_SCROLL_HANDLER = new InjectionToken<(_: Event) => void>('TAB_SCROLL_HANDLER') /** * Structural directive (*sfngTabContent) to defined lazy-loaded tab content. */ @Directive({ selector: '[sfngTabContent]', }) export class SfngTabContentDirective { portal: TemplatePortal; constructor( public readonly templateRef: TemplateRef, public readonly viewRef: ViewContainerRef, ) { this.portal = new TemplatePortal(this.templateRef, this.viewRef); } } /** * The tab component that is used to define a new tab as a part of a tab group. * The content of the tab is lazy-loaded by using the TabContentDirective. */ @Component({ selector: 'sfng-tab', template: '', changeDetection: ChangeDetectionStrategy.OnPush, }) export class SfngTabComponent implements ListKeyManagerOption { @ContentChild(SfngTabContentDirective, { static: false }) tabContent: SfngTabContentDirective | null = null; /** The ID of the tab used to programatically activate the tab. */ @Input() id = ''; /** The title for the tab as displayed in the tab group header. */ @Input() title = ''; /** The key for the tip up in the tab group header. */ @Input() tipUpKey = ''; @Input() set warning(v) { this._warning = coerceBooleanProperty(v) } get warning() { return this._warning } private _warning = false; /** Emits when the tab content is scrolled */ @Output() tabContentScroll = new EventEmitter(); /** Whether or not the tab is currently disabled. */ @Input() set disabled(v: any) { this._disabled = coerceBooleanProperty(v); } get disabled() { return this._disabled; } private _disabled: boolean = false; /** getLabel is used by the list key manager to support type-ahead */ getLabel() { return this.title } } /** * A simple wrapper component around CdkPortalOutlet to add nice * move animations. */ @Component({ selector: 'sfng-tab-outlet', template: `
`, styles: [ ` :host{ display: flex; flex-direction: column; overflow: hidden; } ` ], changeDetection: ChangeDetectionStrategy.OnPush, animations: [ trigger( 'moveInOut', [ transition( ':enter', [ style({ opacity: 0, transform: 'translateX({{ in }})' }), animate('.2s ease-in', style({ opacity: 1, transform: 'translateX(0%)' })) ], { params: { in: '100%' } } // default parameters ), transition( ':leave', [ style({ opacity: 1 }), animate('.2s ease-out', style({ opacity: 0, transform: 'translateX({{ out }})' })) ], { params: { out: '-100%' } } // default parameters ) ] )] }) export class TabOutletComponent implements AfterViewInit { _appAnimate = false; @Input() outletClass = '' get in() { return this._animateDirection == 'left' ? '100%' : '-100%' } get out() { return this._animateDirection == 'left' ? '-100%' : '100%' } onTabContentScroll(event: Event) { if (!!this.scrollHandler) { this.scrollHandler(event) } } @ViewChild(CdkPortalOutlet, { static: true }) portalOutlet!: CdkPortalOutlet; constructor( @Inject(TAB_PORTAL) public portal: TemplatePortal, @Inject(TAB_ANIMATION_DIRECTION) public _animateDirection: 'left' | 'right', @Inject(TAB_SCROLL_HANDLER) public scrollHandler: (_: Event) => void, private cdr: ChangeDetectorRef ) { } ngAfterViewInit(): void { this.portalOutlet?.attached .subscribe(() => { this._appAnimate = true; this.cdr.detectChanges(); }) } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tabs/tabs.module.ts ================================================ import { PortalModule } from "@angular/cdk/portal"; import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { SfngTipUpModule } from "../tipup"; import { SfngTabComponent, SfngTabContentDirective, TabOutletComponent } from "./tab"; import { SfngTabGroupComponent } from "./tab-group"; @NgModule({ imports: [ CommonModule, PortalModule, SfngTipUpModule, BrowserAnimationsModule ], declarations: [ SfngTabContentDirective, SfngTabComponent, SfngTabGroupComponent, TabOutletComponent, ], exports: [ SfngTabContentDirective, SfngTabComponent, SfngTabGroupComponent ] }) export class SfngTabModule { } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tipup/_tipup.scss ================================================ sfng-tipup-container { display: block; caption { @apply text-sm; opacity: .6; font-size: .6rem; } h1 { font-size: 0.85rem; font-weight: 500; margin-bottom: 1rem; } .message, h1 { flex-shrink: 0; text-overflow: ellipsis; word-break: normal; } .message { font-size: 0.75rem; flex-grow: 1; opacity: .8; max-width: 300px; padding: 0; } .close-icon { position: absolute; top: 1rem; right: 1rem; opacity: .7; cursor: pointer; &:hover { opacity: 1; } } .buttons { width: 100%; display: flex; justify-content: space-between; } a { text-decoration: underline; } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tipup/anchor.ts ================================================ import { Directive, ElementRef, HostBinding, Input, isDevMode } from "@angular/core"; import { SfngTipUpPlacement } from "./utils"; @Directive({ selector: '[sfngTipUpAnchor]', }) export class SfngTipUpAnchorDirective implements SfngTipUpPlacement { constructor( public readonly elementRef: ElementRef, ) { } origin: 'left' | 'right' = 'right'; offset: number = 10; @HostBinding('class.active-tipup-anchor') isActiveAnchor = false; @Input() set sfngTipUpAnchor(posSpec: string | undefined) { const parts = (posSpec || '').split(';') if (parts.length > 2) { if (isDevMode()) { throw new Error(`Invalid value "${posSpec}" for [sfngTipUpAnchor]`); } return; } if (parts[0] === 'left') { this.origin = 'left'; } else { this.origin = 'right'; } if (parts.length === 2) { this.offset = +parts[1]; if (isNaN(this.offset)) { this.offset = 10; } } else { this.offset = 10; } } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tipup/clone-node.ts ================================================ /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** Creates a deep clone of an element. */ export function deepCloneNode(node: HTMLElement): HTMLElement { const clone = node.cloneNode(true) as HTMLElement; const descendantsWithId = clone.querySelectorAll('[id]'); const nodeName = node.nodeName.toLowerCase(); // Remove the `id` to avoid having multiple elements with the same id on the page. clone.removeAttribute('id'); for (let i = 0; i < descendantsWithId.length; i++) { descendantsWithId[i].removeAttribute('id'); } if (nodeName === 'canvas') { transferCanvasData(node as HTMLCanvasElement, clone as HTMLCanvasElement); } else if (nodeName === 'input' || nodeName === 'select' || nodeName === 'textarea') { transferInputData(node as HTMLInputElement, clone as HTMLInputElement); } transferData('canvas', node, clone, transferCanvasData); transferData('input, textarea, select', node, clone, transferInputData); return clone; } /** Matches elements between an element and its clone and allows for their data to be cloned. */ function transferData(selector: string, node: HTMLElement, clone: HTMLElement, callback: (source: T, clone: T) => void) { const descendantElements = node.querySelectorAll(selector); if (descendantElements.length) { const cloneElements = clone.querySelectorAll(selector); for (let i = 0; i < descendantElements.length; i++) { callback(descendantElements[i], cloneElements[i]); } } } // Counter for unique cloned radio button names. let cloneUniqueId = 0; /** Transfers the data of one input element to another. */ function transferInputData(source: Element & { value: string }, clone: Element & { value: string; name: string; type: string }) { // Browsers throw an error when assigning the value of a file input programmatically. if (clone.type !== 'file') { clone.value = source.value; } // Radio button `name` attributes must be unique for radio button groups // otherwise original radio buttons can lose their checked state // once the clone is inserted in the DOM. if (clone.type === 'radio' && clone.name) { clone.name = `sfng-clone-${clone.name}-${cloneUniqueId++}`; } } /** Transfers the data of one canvas element to another. */ function transferCanvasData(source: HTMLCanvasElement, clone: HTMLCanvasElement) { const context = clone.getContext('2d'); if (context) { // In some cases `drawImage` can throw (e.g. if the canvas size is 0x0). // We can't do much about it so just ignore the error. try { context.drawImage(source, 0, 0); } catch { } } } /** * Gets a 3d `transform` that can be applied to an element. * @param x Desired position of the element along the X axis. * @param y Desired position of the element along the Y axis. */ export function getTransform(x: number, y: number): string { // Round the transforms since some browsers will // blur the elements for sub-pixel transforms. return `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`; } /** * Matches the target element's size to the source's size. * @param target Element that needs to be resized. * @param sourceRect Dimensions of the source element. */ export function matchElementSize(target: HTMLElement, sourceRect: ClientRect): void { target.style.width = `${sourceRect.width}px`; target.style.height = `${sourceRect.height}px`; target.style.transform = getTransform(sourceRect.left, sourceRect.top); } /** * Shallow-extends a stylesheet object with another stylesheet-like object. * Note that the keys in `source` have to be dash-cased. */ export function extendStyles(dest: CSSStyleDeclaration, source: Record, importantProperties?: Set) { for (let key in source) { if (source.hasOwnProperty(key)) { const value = source[key]; if (value) { dest.setProperty(key, value, importantProperties?.has(key) ? 'important' : ''); } else { dest.removeProperty(key); } } } return dest; } export function removeNode(node: Node | null) { if (node && node.parentNode) { node.parentNode.removeChild(node); } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tipup/css-utils.ts ================================================ export function synchronizeCssStyles(src: HTMLElement, destination: HTMLElement, skipStyles: Set) { // Get a list of all the source and destination elements const srcElements = >src.getElementsByTagName('*'); const dstElements = >destination.getElementsByTagName('*'); cloneStyle(src, destination, skipStyles); // For each element for (let i = srcElements.length; i--;) { const srcElement = srcElements[i]; const dstElement = dstElements[i]; cloneStyle(srcElement, dstElement, skipStyles); } } function cloneStyle(srcElement: HTMLElement, dstElement: HTMLElement, skipStyles: Set) { const sourceElementStyles = document.defaultView!.getComputedStyle(srcElement, ''); const styleAttributeKeyNumbers = Object.keys(sourceElementStyles); // Copy the attribute for (let j = 0; j < styleAttributeKeyNumbers.length; j++) { const attributeKeyNumber = styleAttributeKeyNumbers[j]; const attributeKey: string = sourceElementStyles[attributeKeyNumber as any]; if (!isNaN(+attributeKey)) { continue } if (attributeKey === 'cssText') { continue } if (skipStyles.has(attributeKey)) { continue } try { dstElement.style[attributeKey as any] = sourceElementStyles[attributeKey as any]; } catch (e) { console.error(attributeKey, e); } } } /** * Returns a CSS selector for el from rootNode. * * @param el The source element to get the CSS path to * @param rootNode The root node at which the CSS path should be applyable * @returns A CSS selector to access el from rootNode. */ export function getCssSelector(el: HTMLElement, rootNode: HTMLElement | null): string { if (!el) { return ''; } let stack = []; let isShadow = false; while (el !== rootNode && el.parentNode !== null) { // console.log(el.nodeName); let sibCount = 0; let sibIndex = 0; // get sibling indexes for (let i = 0; i < (el.parentNode as HTMLElement).childNodes.length; i++) { let sib = (el.parentNode as HTMLElement).childNodes[i]; if (sib.nodeName == el.nodeName) { if (sib === el) { sibIndex = sibCount; } sibCount++; } } let nodeName = el.nodeName.toLowerCase(); if (isShadow) { throw new Error(`cannot traverse into shadow dom.`) } if (sibCount > 1) { stack.unshift(nodeName + ':nth-of-type(' + (sibIndex + 1) + ')'); } else { stack.unshift(nodeName); } el = el.parentNode as HTMLElement; if (el.nodeType === 11) { // for shadow dom, we isShadow = true; el = (el as any).host; } } return stack.join(' > '); } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tipup/index.ts ================================================ export * from './anchor'; export * from './tipup'; export * from './tipup-component'; export * from './tipup.module'; export * from './translations'; ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tipup/safe.pipe.ts ================================================ import { Pipe, PipeTransform } from '@angular/core'; import { DomSanitizer, SafeHtml, SafeStyle, SafeScript, SafeUrl, SafeResourceUrl } from '@angular/platform-browser'; @Pipe({ name: 'safe' }) export class SafePipe implements PipeTransform { constructor(protected sanitizer: DomSanitizer) { } public transform(value: any, type: string): SafeHtml | SafeStyle | SafeScript | SafeUrl | SafeResourceUrl { switch (type) { case 'html': return this.sanitizer.bypassSecurityTrustHtml(value); case 'style': return this.sanitizer.bypassSecurityTrustStyle(value); case 'script': return this.sanitizer.bypassSecurityTrustScript(value); case 'url': return this.sanitizer.bypassSecurityTrustUrl(value); case 'resourceUrl': return this.sanitizer.bypassSecurityTrustResourceUrl(value); default: throw new Error(`Invalid safe type specified: ${type}`); } } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tipup/tipup-component.ts ================================================ import { ChangeDetectionStrategy, Component, Inject, OnInit } from "@angular/core"; import { SfngDialogRef, SFNG_DIALOG_REF } from "../dialog"; import { SfngTipUpService } from "./tipup"; import { ActionRunner, Button, SFNG_TIP_UP_ACTION_RUNNER, TipUp } from './translations'; import { TIPUP_TOKEN } from "./utils"; @Component({ selector: 'sfng-tipup-container', templateUrl: './tipup.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class SfngTipUpComponent implements OnInit, TipUp { title: string = 'N/A'; content: string = 'N/A'; nextKey?: string; buttons?: Button[]; url?: string; urlText: string = 'Read More'; constructor( @Inject(TIPUP_TOKEN) public readonly token: string, @Inject(SFNG_DIALOG_REF) private readonly dialogRef: SfngDialogRef, @Inject(SFNG_TIP_UP_ACTION_RUNNER) private runner: ActionRunner, private tipupService: SfngTipUpService, ) { } ngOnInit() { const doc = this.tipupService.getTipUp(this.token); if (!!doc) { Object.assign(this, doc); this.urlText = doc.urlText || 'Read More'; } } async next() { if (!this.nextKey) { return; } this.tipupService.open(this.nextKey); this.dialogRef.close(); } async runAction(btn: Button) { await this.runner.performAction(btn.action); // if we have a nextKey for the button but do not do in-app // routing we should be able to open the next tipup as soon // as the action finished if (!!btn.nextKey) { this.tipupService.waitFor(btn.nextKey!) .subscribe({ next: () => { this.dialogRef.close(); this.tipupService.open(btn.nextKey!); }, error: console.error }) } else { this.close(); } } close() { this.dialogRef.close(); } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tipup/tipup.html ================================================
Tip

{{ urlText }}
================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tipup/tipup.module.ts ================================================ import { CommonModule } from "@angular/common"; import { ModuleWithProviders, NgModule, Type } from "@angular/core"; import { MarkdownModule } from "ngx-markdown"; import { SfngDialogModule } from "../dialog"; import { SfngTipUpAnchorDirective } from './anchor'; import { SfngsfngTipUpTriggerDirective, SfngTipUpIconComponent } from './tipup'; import { SfngTipUpComponent } from './tipup-component'; import { ActionRunner, HelpTexts, SFNG_TIP_UP_ACTION_RUNNER, SFNG_TIP_UP_CONTENTS } from "./translations"; import { SafePipe } from "./safe.pipe"; @NgModule({ imports: [ CommonModule, MarkdownModule.forChild(), SfngDialogModule, ], declarations: [ SfngTipUpIconComponent, SfngsfngTipUpTriggerDirective, SfngTipUpComponent, SfngTipUpAnchorDirective, SafePipe ], exports: [ SfngTipUpIconComponent, SfngsfngTipUpTriggerDirective, SfngTipUpComponent, SfngTipUpAnchorDirective ], }) export class SfngTipUpModule { static forRoot(text: HelpTexts, runner: Type>): ModuleWithProviders { return { ngModule: SfngTipUpModule, providers: [ { provide: SFNG_TIP_UP_CONTENTS, useValue: text, }, { provide: SFNG_TIP_UP_ACTION_RUNNER, useExisting: runner, } ] } } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tipup/tipup.ts ================================================ /* eslint-disable @angular-eslint/no-input-rename */ import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion'; import { ConnectedPosition } from '@angular/cdk/overlay'; import { _getShadowRoot } from '@angular/cdk/platform'; import { DOCUMENT } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Directive, ElementRef, HostBinding, HostListener, Inject, Injectable, Injector, Input, NgZone, OnDestroy, Optional, Renderer2, RendererFactory2 } from '@angular/core'; import { Observable, of, Subject } from 'rxjs'; import { debounce, debounceTime, filter, map, skip, take, timeout } from 'rxjs/operators'; import { SfngDialogRef, SfngDialogService } from '../dialog'; import { SfngTipUpAnchorDirective } from './anchor'; import { deepCloneNode, extendStyles, matchElementSize, removeNode } from './clone-node'; import { getCssSelector, synchronizeCssStyles } from './css-utils'; import { SfngTipUpComponent } from './tipup-component'; import { Button, HelpTexts, SFNG_TIP_UP_CONTENTS, TipUp } from './translations'; import { SfngTipUpPlacement, TIPUP_TOKEN } from './utils'; @Directive({ selector: '[sfngTipUpTrigger]', }) export class SfngsfngTipUpTriggerDirective implements OnDestroy { constructor( public readonly elementRef: ElementRef, public dialog: SfngDialogService, @Optional() @Inject(SfngTipUpAnchorDirective) public anchor: SfngTipUpAnchorDirective | ElementRef | HTMLElement, @Inject(SFNG_TIP_UP_CONTENTS) private tipUpContents: HelpTexts, private tipupService: SfngTipUpService, private cdr: ChangeDetectorRef, ) { } private dialogRef: SfngDialogRef | null = null; /** * The helptext token used to search for the tip up defintion. */ @Input('sfngTipUpTrigger') set textKey(s: string) { if (!!this._textKey) { this.tipupService.deregister(this._textKey, this); } this._textKey = s; this.tipupService.register(this._textKey, this); } get textKey() { return this._textKey; } private _textKey: string = ''; /** * The text to display inside the tip up. If unset, the tipup definition * will be loaded form helptexts.yaml. * This input property is mainly designed for programatic/dynamic tip-up generation */ @Input('sfngTipUpText') text: string | undefined; @Input('sfngTipUpTitle') title: string | undefined; @Input('sfngTipUpButtons') buttons: Button[] | undefined; /** * asTipUp returns a tip-up definition built from the input * properties sfngTipUpText and sfngTipUpTitle. If none are set * then null is returned. */ asTipUp(): TipUp | null { // TODO(ppacher): we could also merge the defintions from MyYamlFile // and the properties set on this directive.... if (!this.text) { return this.tipUpContents[this.textKey]; } return { title: this.title || '', content: this.text, buttons: this.buttons, } } /** * The default anchor for the tipup if non is provided via Dependency-Injection * or using sfngTipUpAnchorRef */ @Input('sfngTipUpDefaultAnchor') defaultAnchor: ElementRef | HTMLElement | null = null; /** Optionally overwrite the anchor element received via Dependency Injection */ @Input('sfngTipUpAnchorRef') set anchorRef(ref: ElementRef | HTMLElement | null) { this.anchor = ref ?? this.anchor; } /** Used to ensure all tip-up triggers have a pointer cursor */ @HostBinding('style.cursor') cursor = 'pointer'; /** De-register ourself upon destroy */ ngOnDestroy() { this.tipupService.deregister(this.textKey, this); } /** Whether or not we're passive-only and thus do not handle click-events form the user */ @Input('sfngTipUpPassive') set passive(v: any) { this._passive = coerceBooleanProperty(v ?? true); } get passive() { return this._passive; } private _passive = false; @Input('sfngTipUpOffset') set offset(v: any) { this._defaultOffset = coerceNumberProperty(v) } get offset() { return this._defaultOffset } private _defaultOffset = 20; @Input('sfngTipUpPlacement') placement: SfngTipUpPlacement | null = null; @HostListener('click', ['$event']) onClick(event?: MouseEvent): Promise { if (!!event) { // if there's a click event the user actually clicked the element. // we only handle this if we're not marked as passive. if (this._passive) { return Promise.resolve(); } event.preventDefault(); event.stopPropagation(); } if (!!this.dialogRef) { this.dialogRef.close(); return Promise.resolve(); } let anchorElement: ElementRef | HTMLElement | null = this.defaultAnchor || this.elementRef; let placement: SfngTipUpPlacement | null = this.placement; if (!!this.anchor) { if (this.anchor instanceof SfngTipUpAnchorDirective) { anchorElement = this.anchor.elementRef; placement = this.anchor; } else { anchorElement = this.anchor; } } this.dialogRef = this.tipupService.createTipup( anchorElement, this.textKey, this, placement, ) this.dialogRef.onClose .pipe(take(1)) .subscribe(() => { this.dialogRef = null; this.cdr.markForCheck(); }); this.cdr.detectChanges(); return this.dialogRef.onStateChange .pipe( filter(state => state === 'opening'), take(1), ) .toPromise() } } @Component({ selector: 'sfng-tipup', template: ` `, styles: [ ` :host { display: inline-block; width : 1rem; position: relative; opacity: 0.55; cursor : pointer; align-self: center; } :host:hover { opacity: 1; } ` ], changeDetection: ChangeDetectionStrategy.OnPush, }) export class SfngTipUpIconComponent implements SfngTipUpPlacement { @Input() key: string = ''; // see sfngTipUpTrigger sfngTipUpText and sfngTipUpTitle @Input() text: string | undefined = undefined; @Input() title: string | undefined = undefined; @Input() buttons: Button[] | undefined = undefined; @Input() anchor: ElementRef | HTMLElement | null = null; @Input('placement') origin: 'left' | 'right' = 'right'; @Input() set offset(v: any) { this._offset = coerceNumberProperty(v); } get offset() { return this._offset; } private _offset: number = 10; constructor(private elementRef: ElementRef) { } get placement(): SfngTipUpPlacement { return this } get parent(): HTMLElement | null { return (this.elementRef?.nativeElement as HTMLElement)?.parentElement; } } @Injectable({ providedIn: 'root' }) export class SfngTipUpService { tipups = new Map(); private _onRegister = new Subject(); private _onUnregister = new Subject(); get onRegister(): Observable { return this._onRegister.asObservable(); } get onUnregister(): Observable { return this._onUnregister.asObservable(); } waitFor(key: string): Observable { if (this.tipups.has(key)) { return of(undefined); } return this.onRegister .pipe( filter(val => val === key), debounce(() => this.ngZone.onStable.pipe(skip(2))), debounceTime(1000), take(1), map(() => { }), timeout(5000), ); } private renderer: Renderer2; constructor( @Inject(DOCUMENT) private _document: Document, private dialog: SfngDialogService, private ngZone: NgZone, private injector: Injector, rendererFactory: RendererFactory2 ) { this.renderer = rendererFactory.createRenderer(null, null) } register(key: string, trigger: SfngsfngTipUpTriggerDirective) { if (this.tipups.has(key)) { return; } this.tipups.set(key, trigger); this._onRegister.next(key); } deregister(key: string, trigger: SfngsfngTipUpTriggerDirective) { if (this.tipups.get(key) === trigger) { this.tipups.delete(key); this._onUnregister.next(key); } } getTipUp(key: string): TipUp | null { return this.tipups.get(key)?.asTipUp() || null; } private _latestTipUp: SfngDialogRef | null = null; createTipup( anchor: HTMLElement | ElementRef, key: string, origin?: SfngsfngTipUpTriggerDirective, opts: SfngTipUpPlacement | null = {}, injector?: Injector): SfngDialogRef { const lastTipUp = this._latestTipUp let closePrevious = () => { if (!!lastTipUp) { lastTipUp.close(); } } // make sure we have an ElementRef to work with if (!(anchor instanceof ElementRef)) { anchor = new ElementRef(anchor) } // the the origin placement of the tipup const positions: ConnectedPosition[] = []; if (opts?.origin === 'left') { positions.push({ originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', }) } else { positions.push({ originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', }) } // determine the offset to the tipup origin let offset = opts?.offset ?? 10; if (opts?.origin === 'left') { offset *= -1; } let postitionStrategy = this.dialog.position() .flexibleConnectedTo(anchor) .withPositions(positions) .withDefaultOffsetX(offset); const inj = Injector.create({ providers: [ { useValue: key, provide: TIPUP_TOKEN, } ], parent: injector || this.injector, }); const newTipUp = this.dialog.create(SfngTipUpComponent, { dragable: false, autoclose: true, backdrop: 'light', injector: inj, positionStrategy: postitionStrategy }); this._latestTipUp = newTipUp; const _preview = this._createPreview(anchor.nativeElement, _getShadowRoot(anchor.nativeElement)); // construct a CSS selector that targets the clicked origin (sfngTipUpTriggerDirective) from within // the anchor. We use that path to highlight the copy of the trigger-directive in the preview. if (!!origin) { const originSelector = getCssSelector(origin.elementRef.nativeElement, anchor.nativeElement); let target: HTMLElement | null = null; if (!!originSelector) { target = _preview.querySelector(originSelector); } else { target = _preview; } this.renderer.addClass(target, 'active-tipup-trigger') } newTipUp.onStateChange .pipe( filter(state => state === 'open'), take(1) ) .subscribe(() => { closePrevious(); _preview.attach() }) newTipUp.onStateChange .pipe( filter(state => state === 'closing'), take(1) ) .subscribe(() => { if (this._latestTipUp === newTipUp) { this._latestTipUp = null; } _preview.classList.remove('visible'); setTimeout(() => { removeNode(_preview); }, 300) }); return newTipUp; } private _createPreview(element: HTMLElement, shadowRoot: ShadowRoot | null): HTMLElement & { attach: () => void } { const preview = deepCloneNode(element); // clone all CSS styles by applying them directly to the copied // nodes. Though, we skip the opacity property because we use that // a lot and it makes the preview strange .... synchronizeCssStyles(element, preview, new Set([ 'opacity' ])); // make sure the preview element is at the exact same position // as the original one. matchElementSize(preview, element.getBoundingClientRect()); extendStyles(preview.style, { // We have to reset the margin, because it can throw off positioning relative to the viewport. 'margin': '0', 'position': 'fixed', 'top': '0', 'left': '0', 'z-index': '1000', 'opacity': 'unset', }, new Set(['position'])); // We add a dedicated class to the preview element so // it can handle special higlighting itself. preview.classList.add('tipup-preview') // since the user might want to click on the preview element we must // intercept the click-event, determine the path to the target element inside // the preview and eventually dispatch a click-event on the actual // - real - target inside the cloned element. preview.onclick = function (event: MouseEvent) { let path = getCssSelector(event.target as HTMLElement, preview); if (!!path) { // find the target by it's CSS path let actualTarget: HTMLElement | null = element.querySelector(path); // some (SVG) elements don't have a direct click() listener so we need to search // the parents upwards to find one that implements click(). // we're basically searching up until we reach the tag. // // TODO(ppacher): stop searching at the respective root node. if (!!actualTarget) { let iter: HTMLElement = actualTarget; while (iter != null) { if ('click' in iter && typeof iter['click'] === 'function') { iter.click(); break; } iter = iter.parentNode as HTMLElement; } } } else { // the user clicked the preview element directly try { element.click() } catch (e) { console.error(e); } } } let attach = () => { const parent = this._getPreviewInserationPoint(shadowRoot) const cdkOverlayContainer = parent.getElementsByClassName('cdk-overlay-container')[0] // if we find a cdkOverlayContainer in our inseration point (which we expect to be there) // we insert the preview element right after the overlay-backdrop. This way the tip-up // dialog will still be on top of the preview. if (!!cdkOverlayContainer) { const reference = cdkOverlayContainer.getElementsByClassName("cdk-overlay-backdrop")[0].nextSibling; cdkOverlayContainer.insertBefore(preview, reference) } else { parent.appendChild(preview); } setTimeout(() => { preview.classList.add('visible'); }) } Object.defineProperty(preview, 'attach', { value: attach, }) return preview as any; } private _getPreviewInserationPoint(shadowRoot: ShadowRoot | null): HTMLElement { const documentRef = this._document; return shadowRoot || documentRef.fullscreenElement || (documentRef as any).webkitFullscreenElement || (documentRef as any).mozFullScreenElement || (documentRef as any).msFullscreenElement || documentRef.body; } async open(key: string) { const comp = this.tipups.get(key); if (!comp) { console.error('Tried to open unknown tip-up with key ' + key); return; } comp.onClick() } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tipup/translations.ts ================================================ import { InjectionToken } from '@angular/core'; export const SFNG_TIP_UP_CONTENTS = new InjectionToken>('SfngTipUpContents'); export const SFNG_TIP_UP_ACTION_RUNNER = new InjectionToken>('SfngTipUpActionRunner') export interface Button { name: string; action: T; nextKey?: string; } export interface TipUp { title: string; content: string; url?: string; urlText?: string; buttons?: Button[]; nextKey?: string; } export interface HelpTexts { [key: string]: TipUp; } export interface ActionRunner { performAction(action: T): Promise; } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tipup/utils.ts ================================================ import { InjectionToken } from "@angular/core"; export const TIPUP_TOKEN = new InjectionToken('TipUPJSONToken'); export interface SfngTipUpPlacement { origin?: 'left' | 'right'; offset?: number; } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/toggle-switch/_toggle-switch.scss ================================================ sfng-toggle { @apply flex items-center; label { @apply inline-block w-10 h-5 relative bg-gray-500 rounded-full; } .slider { @apply absolute cursor-pointer top-0 left-0 right-0 bottom-0 bg-gray-600 transition-all duration-100 rounded-full shadow-inner-xs; } .dot { @apply absolute transition-all duration-200 rounded-full bg-white; height: 18px; width: 18px; bottom: 1px; left: 1px; } input:checked:not(:disabled)+.slider { @apply bg-green-300 bg-opacity-50 text-green; } input:disabled+.slider { @apply opacity-75 cursor-not-allowed; } .dot.checked { transform: translateX(calc(2.5rem - 18px - 2px)); } .dot.disabled { transform: translateX(calc((2.5rem - 18px - 2px)/2)); } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/toggle-switch/index.ts ================================================ export * from './toggle-switch'; export * from './toggle.module'; ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/toggle-switch/toggle-switch.html ================================================ ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/toggle-switch/toggle-switch.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, HostListener } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ selector: 'sfng-toggle', templateUrl: './toggle-switch.html', changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SfngToggleSwitchComponent), multi: true, } ] }) export class SfngToggleSwitchComponent implements ControlValueAccessor { @HostListener('blur') onBlur() { this.onTouch(); } set disabled(v: any) { this.setDisabledState(coerceBooleanProperty(v)) } get disabled() { return this._disabled; } private _disabled = false; value: boolean = false; constructor(private _changeDetector: ChangeDetectorRef) { } setDisabledState(isDisabled: boolean) { this._disabled = isDisabled; this._changeDetector.markForCheck(); } onValueChange(value: boolean) { this.value = value; this.onChange(this.value); } writeValue(value: boolean) { this.value = value; this._changeDetector.markForCheck(); } onChange = (_: any): void => { }; registerOnChange(fn: (value: any) => void) { this.onChange = fn; } onTouch = (): void => { }; registerOnTouched(fn: () => void) { this.onTouch = fn; } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/toggle-switch/toggle.module.ts ================================================ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { SfngToggleSwitchComponent } from "./toggle-switch"; @NgModule({ imports: [ CommonModule, FormsModule, ], declarations: [ SfngToggleSwitchComponent, ], exports: [ SfngToggleSwitchComponent, ] }) export class SfngToggleSwitchModule { } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tooltip/_tooltip-component.scss ================================================ sfng-tooltip-container { @apply relative block; max-width: 16rem; } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tooltip/index.ts ================================================ export * from './tooltip'; export * from './tooltip.module'; ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tooltip/tooltip-component.html ================================================
{{ message }}
================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tooltip/tooltip-component.ts ================================================ import { animate, AnimationEvent, style, transition, trigger } from "@angular/animations"; import { OverlayRef } from "@angular/cdk/overlay"; import { TemplatePortal } from "@angular/cdk/portal"; import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostBinding, HostListener, Inject, InjectionToken, OnDestroy, TemplateRef, ViewContainerRef } from "@angular/core"; import { SfngTooltipDirective } from "./tooltip"; export const SFNG_TOOLTIP_CONTENT = new InjectionToken>('SFNG_TOOLTIP_CONTENT'); export const SFNG_TOOLTIP_OVERLAY = new InjectionToken('SFNG_TOOLTIP_OVERLAY'); @Component({ selector: 'sfng-tooltip-container', templateUrl: './tooltip-component.html', changeDetection: ChangeDetectionStrategy.OnPush, animations: [ trigger( 'moveInOut', [ transition( ':enter', [ style({ opacity: 0, transform: 'translate{{ what }}({{ value }}) scale(0.75)' }), animate('.1s ease-in', style({ opacity: 1, transform: 'translate{{ what }}(0%) scale(1)' })) ], { params: { what: 'Y', value: '-8px' } } // default parameters ), transition( ':leave', [ style({ opacity: 1 }), animate('.1s ease-out', style({ opacity: 0, transform: 'translate{{ what }}({{ value }}) scale(0.75)' })) ], { params: { what: 'Y', value: '8px' } } // default parameters ) ] )] }) export class SfngTooltipComponent implements AfterViewInit, OnDestroy { /** * Adds snfg-tooltip-instance class to the host element. * This is used as a selector in the FlexibleConnectedPosition stragegy * to set a transform-origin. That origin is then used for the "arrow" anchor * placement. */ @HostBinding('class.sfng-tooltip-instance') _hostClass = true; /** * Used to clear the "hide" timeout when the cursor moves from the the origin * into the tooltip content. * This is required if the tooltip contains rich and likely clickable content. */ @HostListener('mouseenter') onMouseEnter() { this.directive.show() } /** * If the tooltip is visible because the user moved inside the tooltip-component * (see comment above) then we need to handle a mouse-leave event as well. */ @HostListener('mouseleave') onMouseLeave() { this.directive.hide() } what = 'Y'; value = '8px' transformOrigin = ''; _appAnimate = false; private observer: MutationObserver | null = null; /** Message is the tooltip message to display in case tooltipContent is a string */ message = ''; /** Portal is the tooltip content to display in case tooltipContent is a template reference */ portal: TemplatePortal | null = null; constructor( @Inject(SFNG_TOOLTIP_CONTENT) tooltipContent: string | TemplateRef, @Inject(SFNG_TOOLTIP_OVERLAY) public overlayRef: OverlayRef, private directive: SfngTooltipDirective, private elementRef: ElementRef, private cdr: ChangeDetectorRef, private viewContainer: ViewContainerRef ) { if (tooltipContent instanceof TemplateRef) { this.portal = new TemplatePortal(tooltipContent, this.viewContainer) } else { this.message = tooltipContent; } } dispose() { this._appAnimate = false; this.cdr.markForCheck(); } animationDone(event: AnimationEvent) { if (event.toState === 'void') { this.overlayRef.dispose(); } } ngOnDestroy(): void { this.observer?.disconnect(); } ngAfterViewInit(): void { this.observer = new MutationObserver(mutations => { this.transformOrigin = this.elementRef.nativeElement.style.transformOrigin; if (!this.transformOrigin) { return; } const [x, y] = this.transformOrigin.split(" "); if (x === 'center') { this.what = 'Y' if (y === 'top') { this.value = '-8px' } else { this.value = '8px' } } else { this.what = 'X' if (x === 'left') { this.value = '-8px' } else { this.value = '8px' } } this._appAnimate = true; this.cdr.detectChanges(); }); this.observer.observe(this.elementRef.nativeElement, { attributes: true, attributeFilter: ['style'] }) } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tooltip/tooltip.module.ts ================================================ import { OverlayModule } from "@angular/cdk/overlay"; import { PortalModule } from "@angular/cdk/portal"; import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { SfngTooltipDirective } from "./tooltip"; import { SfngTooltipComponent } from "./tooltip-component"; @NgModule({ imports: [ PortalModule, OverlayModule, CommonModule, ], declarations: [ SfngTooltipDirective, SfngTooltipComponent ], exports: [ SfngTooltipDirective ] }) export class SfngTooltipModule { } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/tooltip/tooltip.ts ================================================ /* eslint-disable @angular-eslint/no-input-rename */ import { coerceNumberProperty } from "@angular/cdk/coercion"; import { ConnectedPosition, Overlay, OverlayRef, PositionStrategy } from "@angular/cdk/overlay"; import { ComponentPortal } from "@angular/cdk/portal"; import { ComponentRef, Directive, ElementRef, HostListener, Injector, Input, isDevMode, OnChanges, OnDestroy, OnInit, TemplateRef } from "@angular/core"; import { Subject } from "rxjs"; import { SfngTooltipComponent, SFNG_TOOLTIP_CONTENT, SFNG_TOOLTIP_OVERLAY } from "./tooltip-component"; /** The allowed tooltip positions. */ export type SfngTooltipPosition = 'left' | 'right' | 'bottom' | 'top'; @Directive({ selector: '[sfng-tooltip],[snfgTooltip]', }) export class SfngTooltipDirective implements OnInit, OnDestroy, OnChanges { /** Used to control the visibility of the tooltip */ private attach$ = new Subject(); /** Holds a reference to the tooltip overlay */ private tooltipRef: ComponentRef | null = null; /** * A reference to a timeout created by setTimeout used to debounce * displaying the tooltip */ private debouncer: any | null = null; constructor( private overlay: Overlay, private injector: Injector, private originRef: ElementRef, ) { } @HostListener('mouseenter') show(delay = this.delay) { if (this.debouncer !== null) { clearTimeout(this.debouncer); } this.debouncer = setTimeout(() => { this.debouncer = null; this.attach$.next(true); }, delay); } @HostListener('mouseleave') hide(delay = this.delay / 2) { // if we're currently debouncing a "show" than // we should clear that out to avoid re-attaching // the tooltip right after we disposed it. if (this.debouncer !== null) { clearTimeout(this.debouncer); this.debouncer = null; } this.debouncer = setTimeout(() => { this.attach$.next(false); this.debouncer = null; }, delay); } /** Debounce delay before showing the tooltip */ @Input('sfngTooltipDelay') set delay(v: any) { this._delay = coerceNumberProperty(v); } get delay() { return this._delay } private _delay = 500; /** An additional offset between the tooltip overlay and the origin centers */ @Input('sfngTooltipOffset') set offset(v: any) { this._offset = coerceNumberProperty(v); } private _offset: number | null = 8; /** The actual content that should be displayed in the tooltip overlay. */ @Input('sfngTooltip') @Input('sfng-tooltip') tooltipContent: string | TemplateRef | null = null; @Input('snfgTooltipPosition') position: ConnectedPosition | SfngTooltipPosition | (SfngTooltipPosition | ConnectedPosition)[] | 'any' = 'any'; ngOnInit() { this.attach$ .subscribe(attach => { if (attach) { this.createTooltip(); return; } if (!!this.tooltipRef) { this.tooltipRef.instance.dispose(); this.tooltipRef = null; } }) } ngOnDestroy(): void { this.attach$.next(false); this.attach$.complete(); } ngOnChanges(): void { // if the tooltip content has be set to null and we're still // showing the tooltip we treat that as an attempt to hide. if (this.tooltipContent === null && !!this.tooltipRef) { this.hide(); } } /** Creates the actual tooltip overlay */ private createTooltip() { // there's nothing to do if the tooltip is still active. if (!!this.tooltipRef) { return; } // support disabling the tooltip by passing "null" for // the content. if (this.tooltipContent === null) { return; } const position = this.buildPositionStrategy(); const overlayRef = this.overlay.create({ positionStrategy: position, scrollStrategy: this.overlay.scrollStrategies.close(), disposeOnNavigation: true, }); // make sure we close the tooltip if the user clicks on our // originRef. overlayRef.outsidePointerEvents() .subscribe(() => this.hide()); overlayRef.attachments() .subscribe(() => { if (!overlayRef) { return } overlayRef.updateSize({}); overlayRef.updatePosition(); }) // create a component portal for the tooltip component // and attach it to our newly created overlay. const portal = this.getOverlayPortal(overlayRef); this.tooltipRef = overlayRef.attach(portal); } private getOverlayPortal(ref: OverlayRef): ComponentPortal { const inj = Injector.create({ providers: [ { provide: SFNG_TOOLTIP_CONTENT, useValue: this.tooltipContent }, { provide: SFNG_TOOLTIP_OVERLAY, useValue: ref }, ], parent: this.injector, name: 'SfngTooltipDirective' }) const portal = new ComponentPortal( SfngTooltipComponent, undefined, inj ) return portal; } /** Builds a FlexibleConnectedPositionStrategy for the tooltip overlay */ private buildPositionStrategy(): PositionStrategy { let pos = this.position; if (pos === 'any') { pos = ['top', 'bottom', 'right', 'left'] } else if (!Array.isArray(pos)) { pos = [pos]; } let allowedPositions: ConnectedPosition[] = pos.map(p => { if (typeof p === 'string') { return this.getAllowedConnectedPosition(p); } // this is already a ConnectedPosition return p }); let position = this.overlay.position() .flexibleConnectedTo(this.originRef) .withFlexibleDimensions(true) .withPush(true) .withPositions(allowedPositions) .withGrowAfterOpen(true) .withTransformOriginOn('.sfng-tooltip-instance') return position; } private getAllowedConnectedPosition(type: SfngTooltipPosition): ConnectedPosition { switch (type) { case 'left': return { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: - (this._offset || 0), } case 'right': return { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: (this._offset || 0), } case 'top': return { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: - (this._offset || 0), } case 'bottom': return { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: (this._offset || 0), } default: if (isDevMode()) { throw new Error(`invalid value for SfngTooltipPosition: ${type}`) } // fallback to "right" return this.getAllowedConnectedPosition('right') } } } ================================================ FILE: desktop/angular/projects/safing/ui/src/lib/ui.module.ts ================================================ import { NgModule } from '@angular/core'; import { SfngAccordionModule } from './accordion'; @NgModule({ exports: [ SfngAccordionModule ] }) export class UiModule { } ================================================ FILE: desktop/angular/projects/safing/ui/src/public-api.ts ================================================ /* * Public API Surface of ui */ export * from './lib/accordion'; export * from './lib/dialog'; export * from './lib/dropdown'; export * from './lib/overlay-stepper'; export * from './lib/pagination'; export * from './lib/select'; export * from './lib/tabs'; export * from './lib/tipup'; export * from './lib/toggle-switch'; export * from './lib/tooltip'; export * from './lib/ui.module'; ================================================ FILE: desktop/angular/projects/safing/ui/src/test.ts ================================================ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; import 'zone.js'; import 'zone.js/testing'; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }, ); ================================================ FILE: desktop/angular/projects/safing/ui/theming.scss ================================================ @import "./src/lib/select/select"; @import "./src/lib/dialog/dialog"; @import "./src/lib/pagination/pagination"; @import "./src/lib/tabs/tab-group"; @import "./src/lib/tipup/tipup"; @import "./src/lib/tooltip/tooltip-component"; @import "./src/lib/toggle-switch/toggle-switch"; @import "./src/lib/dialog/confirm.dialog"; ================================================ FILE: desktop/angular/projects/safing/ui/tsconfig.lib.json ================================================ { "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", "declaration": true, "declarationMap": true, "inlineSources": true, "types": [], "lib": [ "dom", "es2018" ] }, "exclude": [ "src/test.ts", "**/*.spec.ts" ] } ================================================ FILE: desktop/angular/projects/safing/ui/tsconfig.lib.prod.json ================================================ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false }, } ================================================ FILE: desktop/angular/projects/safing/ui/tsconfig.spec.json ================================================ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/spec", "types": [ "jasmine" ] }, "files": [ "src/test.ts" ], "include": [ "**/*.spec.ts", "**/*.d.ts" ] } ================================================ FILE: desktop/angular/projects/tauri-builtin/src/app/app.component.html ================================================

Safing

Portmaster

Connecting to System Service ... Connecting to System Service ... Portmaster System Service is not running: Failed to find Portmaster System Service.
Please reinstall the application.
Your System Service Manager is not supported. Please make sure Portmaster is running. Your System Service Manager is not supported. Please make sure Portmaster is running. Unknown error: {{ status }}
================================================ FILE: desktop/angular/projects/tauri-builtin/src/app/app.component.ts ================================================ import { OnInit, Component, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ServiceManagerStatus, TauriIntegrationService } from 'src/app/integration/taur-app'; @Component({ selector: 'app-root', standalone: true, imports: [CommonModule], templateUrl: './app.component.html', styles: [ ` :host { @apply block w-screen h-screen bg-background; } #logo svg { @apply absolute w-20; } `, ], }) export class AppComponent implements OnInit { private tauri = inject(TauriIntegrationService); status: ServiceManagerStatus | string | null = null; getHelp() { this.tauri.openExternal("https://wiki.safing.io/en/Portmaster/App") } startService() { this.tauri.startService() .then(() => this.getStatus()) .catch(err => { this.status = err.error; }); } getStatus() { this.tauri.getServiceManagerStatus() .then(result => { this.status = result; }) .catch(err => { this.status = err.error; }) } ngOnInit() { this.getStatus(); } } ================================================ FILE: desktop/angular/projects/tauri-builtin/src/app/app.config.ts ================================================ import { ApplicationConfig } from '@angular/core'; import { TauriIntegrationService } from 'src/app/integration/taur-app'; export const appConfig: ApplicationConfig = { providers: [ { provide: TauriIntegrationService, useClass: TauriIntegrationService, deps: [] }, ], }; ================================================ FILE: desktop/angular/projects/tauri-builtin/src/index.html ================================================ TauriBuiltin ================================================ FILE: desktop/angular/projects/tauri-builtin/src/main.ts ================================================ import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; import { AppComponent } from './app/app.component'; bootstrapApplication(AppComponent, appConfig) .catch((err) => console.error(err)); ================================================ FILE: desktop/angular/projects/tauri-builtin/src/styles.scss ================================================ @tailwind base; @tailwind components; @tailwind utilities; @import "safing/ui/theming"; /** foboar **/ ================================================ FILE: desktop/angular/projects/tauri-builtin/tsconfig.app.json ================================================ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/app", "types": [] }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts", "../../src/electron-app.d.ts"] } ================================================ FILE: desktop/angular/proxy.json ================================================ { "/api": { "target": "http://localhost:817/", "secure": false } } ================================================ FILE: desktop/angular/src/app/app-routing.module.ts ================================================ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AppViewComponent } from './pages/app-view'; import { DashboardPageComponent } from './pages/dashboard/dashboard.component'; import { MonitorPageComponent } from './pages/monitor'; import { SettingsComponent } from './pages/settings/settings'; import { SpnPageComponent } from './pages/spn'; import { SupportPageComponent } from './pages/support'; import { SupportFormComponent } from './pages/support/form'; const routes: Routes = [ { path: '', pathMatch: 'full', redirectTo: 'dashboard', }, { path: 'settings', component: SettingsComponent, }, { path: 'app', pathMatch: 'full', redirectTo: 'app/overview', }, { path: 'app/overview', component: AppViewComponent, }, { path: 'app/:source/:id', component: AppViewComponent, }, { path: 'monitor', component: MonitorPageComponent, }, { path: 'monitor/profile/:source/:profile', redirectTo: 'monitor', }, { path: 'support', component: SupportPageComponent, }, { path: 'support/:id', component: SupportFormComponent, }, { path: 'spn', component: SpnPageComponent, }, { path: '**', redirectTo: 'dashboard' }, { path: 'dashboard', component: DashboardPageComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes, { anchorScrolling: 'enabled' })], exports: [RouterModule] }) export class AppRoutingModule { } ================================================ FILE: desktop/angular/src/app/app.component.html ================================================

{{overlayText}}

...

================================================ FILE: desktop/angular/src/app/app.component.scss ================================================ :host { display: flex; @apply bg-background; @apply h-screen overflow-hidden; &>* { flex-shrink: 0; } } app-navigation, app-side-dash { @apply border-r; @apply border-cards-tertiary; @apply bg-background; } app-navigation { @apply w-16; } div.main { flex-grow: 1; flex-shrink: 1; display: flex; flex-direction: column; align-items: center; @apply bg-background; height: 100vh; overflow: hidden; } app-debug { @apply border-l; @apply border-cards-tertiary; @apply bg-background; width: 30vw; height: 100vh; min-width: 350px; top: 0px; position: sticky; } .loading { z-index: 100; position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; justify-content: center; align-items: center; flex-direction: column; backdrop-filter: blur(10px); background-color: rgba(#222222, 0.35); .message { display: flex; justify-content: center; align-items: center; width: 100%; flex-direction: column; } svg { width: 100%; position: absolute; top: 0; left: 0; } div.logo { opacity: 0.8; position: relative; width: 10vh; height: 10vh; @apply mt-4; } .spin { animation-name: spin; animation-duration: 3500ms; animation-iteration-count: infinite; animation-timing-function: linear; } .reverse { animation-name: spin-reverse; } } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes spin-reverse { 0% { transform: rotate(360deg); } 100% { transform: rotate(0deg); } } ================================================ FILE: desktop/angular/src/app/app.component.spec.ts ================================================ import { TestBed, waitForAsync } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { AppComponent } from './app.component'; describe('AppComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ RouterTestingModule ], declarations: [ AppComponent ], }).compileComponents(); })); it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app).toBeTruthy(); }); it(`should have as title 'portmaster'`, () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app.title).toEqual('portmaster'); }); }); ================================================ FILE: desktop/angular/src/app/app.component.ts ================================================ import { Overlay } from '@angular/cdk/overlay'; import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, HostListener, Inject, NgZone, OnInit, Renderer2, ViewChild } from '@angular/core'; import { Params, Router } from '@angular/router'; import { PortapiService } from '@safing/portmaster-api'; import { OverlayStepper, SfngDialogService, StepperRef } from '@safing/ui'; import { BehaviorSubject, merge, Subject } from 'rxjs'; import { debounceTime, filter, mergeMap, skip, startWith, take } from 'rxjs/operators'; import { IntroModule } from './intro'; import { NotificationsService, UIStateService } from './services'; import { ActionIndicatorService } from './shared/action-indicator'; import { fadeInAnimation, fadeOutAnimation } from './shared/animations'; import { ExitService } from './shared/exit-screen'; import { SfngNetquerySearchOverlayComponent } from './shared/netquery/search-overlay'; import { INTEGRATION_SERVICE, IntegrationService } from './integration'; import { TauriIntegrationService } from './integration/taur-app'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], animations: [ fadeInAnimation, fadeOutAnimation, ] }) export class AppComponent implements OnInit, AfterViewInit { readonly connected = this.portapi.connected$.pipe( debounceTime(250), startWith(false) ); title = 'portmaster'; /** The current status of the side dash as emitted by the navigation component */ sideDashStatus: 'collapsed' | 'expanded' = 'expanded'; /** Whether or not the side-dash is in overlay mode */ sideDashOverlay = false; /** The MQL to watch for screen size changes. */ private mql!: MediaQueryList; /** Emits when the side-dash is opened or closed in non-overlay mode */ private sideDashOpen = new BehaviorSubject(false); /** Used to emit when the window size changed */ windowResizeChange = new Subject(); get sideDashOpen$() { return this.sideDashOpen.asObservable() } get showOverlay$() { return this.exitService.showOverlay$ } get onContentSizeChange$() { return merge( this.windowResizeChange, this.sideDashOpen$ ) .pipe( startWith(undefined), debounceTime(100), ) } @ViewChild('mainContent', { read: ElementRef, static: true }) mainContent!: ElementRef; @HostListener('window:resize') onWindowResize() { this.windowResizeChange.next(); } @HostListener('document:keydown', ['$event']) onKeyDown(event: KeyboardEvent) { if (event.key === ' ' && event.ctrlKey) { this.dialog.create( SfngNetquerySearchOverlayComponent, { positionStrategy: this.overlay .position() .global() .centerHorizontally() .top('1rem'), backdrop: 'light', autoclose: true, } ) return; } } constructor( public ngZone: NgZone, public portapi: PortapiService, public changeDetectorRef: ChangeDetectorRef, private router: Router, private exitService: ExitService, private overlayStepper: OverlayStepper, private dialog: SfngDialogService, private overlay: Overlay, private stateService: UIStateService, private renderer2: Renderer2, @Inject(INTEGRATION_SERVICE) private integration: IntegrationService, ) { (window as any).portapi = portapi; } onSideDashChange(state: 'expanded' | 'collapsed' | 'force-overlay') { if (state === 'force-overlay') { state = 'expanded'; if (!this.sideDashOverlay) { this.sideDashOverlay = true; } } else { this.sideDashOverlay = this.mql.matches; } this.sideDashStatus = state; if (!this.sideDashOverlay) { this.sideDashOpen.next(this.sideDashStatus === 'expanded') } } ngOnInit() { // default breakpoints used by tailwindcss const minContentWithBp = [ 640, // sfng-sm: 768, // sfng-md: 1024, // sfng-lg: 1280, // sfng-xl: 1536 // sfng-2xl: ] // prepare our breakpoint listeners and add the classes to our main element merge( this.windowResizeChange, this.sideDashOpen$ ) .pipe( startWith(undefined), debounceTime(100), ) .subscribe(() => { const rect = (this.mainContent.nativeElement as HTMLElement).getBoundingClientRect(); minContentWithBp.forEach((bp, idx) => { if (rect.width >= bp) { this.renderer2.addClass(this.mainContent.nativeElement, `min-width-${bp}px`) } else { this.renderer2.removeClass(this.mainContent.nativeElement, `min-width-${bp}px`) } }) this.changeDetectorRef.markForCheck(); }) // force a reload of the current route if we reconnected to // portmaster. This ensures we'll refresh any data that's currently // displayed. this.connected .pipe( filter(connected => !!connected), skip(1), ) .subscribe(async () => { const location = new URL(window.location.toString()); const params: Params = {} location.searchParams.forEach((value, key) => { params[key] = [ ...(params[key] || []), value, ] }) await this.router.navigateByUrl('/', { skipLocationChange: true }) this.router.navigate([location.pathname], { queryParams: params, }); }) this.stateService.uiState() .pipe(take(1)) .subscribe(state => { if (!state.introScreenFinished) { this.showIntro(); } }) this.mql = window.matchMedia('(max-width: 1200px)'); this.sideDashOverlay = this.mql.matches; this.mql.addEventListener('change', () => { this.sideDashOverlay = this.mql.matches; if (!this.sideDashOverlay) { this.sideDashOpen.next(this.sideDashStatus === 'expanded') } }) } ngAfterViewInit(): void { this.sideDashOpen.next(this.sideDashStatus !== 'collapsed') if (this.integration instanceof TauriIntegrationService) { let tauri = this.integration; tauri.shouldShow() .then(show => { console.log("should open window: ", show) if (show) { tauri.openApp(); } }); } } showIntro(): StepperRef { const stepperRef = this.overlayStepper.create(IntroModule.Stepper) stepperRef.onFinish.subscribe(() => { this.stateService.uiState() .pipe( take(1), mergeMap(state => this.stateService.saveState({ ...state, introScreenFinished: true })) ) .subscribe(); }) return stepperRef; } } ================================================ FILE: desktop/angular/src/app/app.module.ts ================================================ import { DragDropModule } from '@angular/cdk/drag-drop'; import { OverlayModule } from '@angular/cdk/overlay'; import { PortalModule } from '@angular/cdk/portal'; import { ScrollingModule } from '@angular/cdk/scrolling'; import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule, registerLocaleData } from '@angular/common'; import { APP_INITIALIZER, LOCALE_ID, NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { FaIconLibrary, FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { faGithub } from '@fortawesome/free-brands-svg-icons'; import { far } from '@fortawesome/free-regular-svg-icons'; import { fas } from '@fortawesome/free-solid-svg-icons'; import { ConfigService, PortmasterAPIModule, StringSetting, getActualValue } from '@safing/portmaster-api'; import { OverlayStepperModule, SfngAccordionModule, SfngDialogModule, SfngDropDownModule, SfngPaginationModule, SfngSelectModule, SfngTipUpModule, SfngToggleSwitchModule, SfngTooltipModule, TabModule, UiModule } from '@safing/ui'; import MyYamlFile from 'js-yaml-loader!../i18n/helptexts.yaml'; import * as i18n from 'ng-zorro-antd/i18n'; import { MarkdownModule } from 'ngx-markdown'; import { firstValueFrom } from 'rxjs'; import { environment } from 'src/environments/environment'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { IntroModule } from './intro'; import { NavigationComponent } from './layout/navigation/navigation'; import { SideDashComponent } from './layout/side-dash/side-dash'; import { AppOverviewComponent, AppViewComponent, QuickSettingInternetButtonComponent } from './pages/app-view'; import { QsHistoryComponent } from './pages/app-view/qs-history/qs-history.component'; import { QuickSettingSelectExitButtonComponent } from './pages/app-view/qs-select-exit/qs-select-exit'; import { QuickSettingUseSPNButtonComponent } from './pages/app-view/qs-use-spn/qs-use-spn'; import { DashboardPageComponent } from './pages/dashboard/dashboard.component'; import { FeatureCardComponent } from './pages/dashboard/feature-card/feature-card.component'; import { MonitorPageComponent } from './pages/monitor'; import { SettingsComponent } from './pages/settings/settings'; import { SPNModule } from './pages/spn/spn.module'; import { SupportPageComponent } from './pages/support'; import { SupportFormComponent } from './pages/support/form'; import { NotificationsService } from './services'; import { ActionIndicatorModule } from './shared/action-indicator'; import { SfngAppIconModule } from './shared/app-icon'; import { ConfigModule } from './shared/config'; import { CountIndicatorModule } from './shared/count-indicator'; import { CountryFlagModule } from './shared/country-flag'; import { EditProfileDialog } from './shared/edit-profile-dialog'; import { ExitScreenComponent } from './shared/exit-screen/exit-screen'; import { ExpertiseModule } from './shared/expertise/expertise.module'; import { ExternalLinkDirective } from './shared/external-link.directive'; import { FeatureScoutComponent } from './shared/feature-scout'; import { SfngFocusModule } from './shared/focus'; import { FuzzySearchPipe } from './shared/fuzzySearch'; import { LoadingComponent } from './shared/loading'; import { SfngMenuModule } from './shared/menu'; import { SfngMultiSwitchModule } from './shared/multi-switch'; import { NetqueryModule } from './shared/netquery'; import { NetworkScoutComponent } from './shared/network-scout'; import { NotificationListComponent } from './shared/notification-list/notification-list.component'; import { NotificationComponent } from './shared/notification/notification'; import { CommonPipesModule } from './shared/pipes'; import { ProcessDetailsDialogComponent } from './shared/process-details-dialog'; import { PromptListComponent } from './shared/prompt-list/prompt-list.component'; import { SecurityLockComponent } from './shared/security-lock'; import { SPNAccountDetailsComponent } from './shared/spn-account-details'; import { SPNLoginComponent } from './shared/spn-login'; import { SPNStatusComponent } from './shared/spn-status'; import { PlaceholderComponent } from './shared/text-placeholder'; import { DashboardWidgetComponent } from './pages/dashboard/dashboard-widget/dashboard-widget.component'; import { MergeProfileDialogComponent } from './pages/app-view/merge-profile-dialog/merge-profile-dialog.component'; import { AppInsightsComponent } from './pages/app-view/app-insights/app-insights.component'; import { INTEGRATION_SERVICE, integrationServiceFactory } from './integration'; import { SupportProgressDialogComponent } from './pages/support/progress-dialog'; function loadAndSetLocaleInitializer(configService: ConfigService) { return async function () { let angularLocaleID = 'en-GB'; let nzLocaleID: string = 'en_GB'; try { const setting = await firstValueFrom(configService.get("core/locale")) const currentValue = getActualValue(setting as StringSetting); switch (currentValue) { case 'en-US': angularLocaleID = 'en-US' nzLocaleID = 'en_US' break; case 'en-GB': angularLocaleID = 'en-GB' nzLocaleID = 'en_GB' break; default: console.error(`Unsupported locale value: ${currentValue}, defaulting to en-GB`) } } catch (err) { console.error(`failed to get locale setting, using default en-GB:`, err) } try { // Get name of module. let localeModuleID = angularLocaleID; if (localeModuleID == "en-US") { localeModuleID = "en"; } /* webpackInclude: /(en|en-GB)\.mjs$/ */ /* webpackChunkName: "./l10n-base/[request]"*/ await import(`../../node_modules/@angular/common/locales/${localeModuleID}.mjs`) .then(locale => { registerLocaleData(locale.default) localeConfig.localeId = angularLocaleID; localeConfig.nzLocale = (i18n as any)[nzLocaleID]; }) } catch (err) { console.error(`failed to load locale module for ${angularLocaleID}:`, err) } } } const localeConfig = { nzLocale: i18n.en_GB, localeId: 'en-GB' } @NgModule({ declarations: [ AppComponent, NotificationComponent, SettingsComponent, MonitorPageComponent, SideDashComponent, NavigationComponent, NotificationListComponent, PromptListComponent, FuzzySearchPipe, AppViewComponent, QuickSettingInternetButtonComponent, QuickSettingUseSPNButtonComponent, QuickSettingSelectExitButtonComponent, AppOverviewComponent, PlaceholderComponent, LoadingComponent, ExternalLinkDirective, ExitScreenComponent, SupportPageComponent, SupportFormComponent, SecurityLockComponent, SPNStatusComponent, FeatureScoutComponent, SPNLoginComponent, SPNAccountDetailsComponent, NetworkScoutComponent, EditProfileDialog, ProcessDetailsDialogComponent, QsHistoryComponent, DashboardPageComponent, DashboardWidgetComponent, FeatureCardComponent, MergeProfileDialogComponent, AppInsightsComponent, SupportProgressDialogComponent ], imports: [ BrowserModule, CommonModule, BrowserAnimationsModule, FormsModule, ReactiveFormsModule, AppRoutingModule, FontAwesomeModule, OverlayModule, PortalModule, CdkTableModule, DragDropModule, MarkdownModule.forRoot(), ScrollingModule, SfngAccordionModule, TabModule, SfngTipUpModule.forRoot(MyYamlFile, NotificationsService), SfngTooltipModule, ActionIndicatorModule, SfngDialogModule, OverlayStepperModule, IntroModule, SfngDropDownModule, SfngSelectModule, SfngMultiSwitchModule, SfngMenuModule, SfngFocusModule, SfngToggleSwitchModule, SfngPaginationModule, SfngAppIconModule, ExpertiseModule, ConfigModule, CountryFlagModule, CountIndicatorModule, NetqueryModule, CommonPipesModule, UiModule, SPNModule, PortmasterAPIModule.forRoot({ httpAPI: environment.httpAPI, websocketAPI: environment.portAPI, }), ], bootstrap: [AppComponent], providers: [ { provide: APP_INITIALIZER, useFactory: loadAndSetLocaleInitializer, deps: [ConfigService], multi: true }, { provide: i18n.NZ_I18N, useFactory: () => { console.log("nz-locale is set to", localeConfig.nzLocale) return localeConfig.nzLocale } }, { provide: LOCALE_ID, useFactory: () => { console.log("locale-id is set to", localeConfig.localeId) return localeConfig.localeId } }, { provide: INTEGRATION_SERVICE, useFactory: integrationServiceFactory } ] }) export class AppModule { constructor(library: FaIconLibrary) { library.addIconPacks(fas, far); library.addIcons(faGithub) } } ================================================ FILE: desktop/angular/src/app/integration/browser.ts ================================================ import { AppInfo, IntegrationService, ProcessInfo } from "./integration"; export class BrowserIntegrationService implements IntegrationService { writeToClipboard(text: string): Promise { if (!!navigator.clipboard) { return navigator.clipboard.writeText(text); } return Promise.reject(new Error(`Clipboard API not supported`)) } openExternal(pathOrUrl: string): Promise { window.open(pathOrUrl, '_blank') return Promise.resolve(); } getInstallDir(): Promise { return Promise.reject('Not supported in browser') } getAppIcon(_: ProcessInfo): Promise { return Promise.reject('Not supported in browser') } getAppInfo(_: ProcessInfo): Promise { return Promise.reject('Not supported in browser') } exitApp(): Promise { window.close(); return Promise.resolve(); } onExitRequest(cb: () => void): () => void { // nothing to do, there return () => { } } } ================================================ FILE: desktop/angular/src/app/integration/electron.ts ================================================ import { BrowserIntegrationService } from "./browser"; import { AppInfo, ProcessInfo } from "./integration"; export class ElectronIntegrationService extends BrowserIntegrationService { openExternal(pathOrUrl: string): Promise { if (!!window.app) { return window.app.openExternal(pathOrUrl); } return Promise.reject('No electron API available') } getInstallDir(): Promise { if (!!window.app) { return window.app.getInstallDir() } return Promise.reject('No electron API available') } getAppIcon(info: ProcessInfo): Promise { if (!!window.app) { return window.app.getFileIcon(info.execPath) } return Promise.reject('No electron API available') } getAppInfo(_: ProcessInfo): Promise { return Promise.reject('Not supported in electron') } exitApp(): Promise { if (!!window.app) { window.app.exitApp(); } return Promise.resolve(); } onExitRequest(cb: () => void): () => void { let listener = (event: MessageEvent) => { if (event.data === 'on-app-close') { cb(); } } window.addEventListener('message', listener); return () => { window.removeEventListener('message', listener) } } } ================================================ FILE: desktop/angular/src/app/integration/factory.ts ================================================ import { InjectionToken } from "@angular/core"; import { BrowserIntegrationService } from "./browser"; import { ElectronIntegrationService } from "./electron"; import { IntegrationService } from "./integration"; import { TauriIntegrationService } from "./taur-app"; export function integrationServiceFactory(): IntegrationService { if ('__TAURI__' in window) { console.log("[app] running under tauri") return new TauriIntegrationService(); } if ('app' in window) { console.log("[app] running under electron") return new ElectronIntegrationService(); } console.log("[app] running in browser") return new BrowserIntegrationService(); } export const INTEGRATION_SERVICE = new InjectionToken('INTEGRATION_SERVICE'); ================================================ FILE: desktop/angular/src/app/integration/index.ts ================================================ export * from './integration'; export * from './factory'; ================================================ FILE: desktop/angular/src/app/integration/integration.ts ================================================ export interface AppInfo { app_name: string; comment: string; icon_dataurl: string; icon_path: string; } export interface ProcessInfo { execPath: string; cmdline: string; pid: number; matchingPath: string; } export interface IntegrationService { /** writeToClipboard copies text to the system clipboard */ writeToClipboard(text: string): Promise; /** openExternal opens a file or URL in an external window */ openExternal(pathOrUrl: string): Promise; /** Gets the path to the portmaster installation directory */ getInstallDir(): Promise; /** Load application information (currently linux only) */ getAppInfo(info: ProcessInfo): Promise; /** Loads the application icon as a dataurl */ getAppIcon(info: ProcessInfo): Promise; /** Closes the application, does not return */ exitApp(): Promise; /** Registers a listener for on-close requests. */ onExitRequest(cb: () => void): () => void; } ================================================ FILE: desktop/angular/src/app/integration/taur-app.ts ================================================ import { AppInfo, IntegrationService, ProcessInfo } from "./integration"; import { writeText } from '@tauri-apps/plugin-clipboard-manager'; import { open } from '@tauri-apps/plugin-shell'; import { listen, once } from '@tauri-apps/api/event'; import { invoke } from '@tauri-apps/api/core' import { getCurrentWindow, Window } from '@tauri-apps/api/window'; // Returns a new uuidv4. If crypto.randomUUID is not available it fals back to // using Math.random(). While this is not as random as it should be it's still // enough for our use-case here (which is just to generate a random response-id). function uuid(): string { if (typeof crypto.randomUUID === 'function') { return crypto.randomUUID(); } // This one is not really random and not RFC compliant but serves enough for fallback // purposes if the UI is opened in a browser that does not yet support randomUUID console.warn('Using browser with lacking support for crypto.randomUUID()'); return Date.now().toString(36) + Math.random().toString(36).substring(2); } function asyncInvoke(method: string, args: object): Promise { return new Promise((resolve, reject) => { const eventId = uuid(); const listenerPromise = once(eventId, (event) => { if (typeof event.payload === 'object' && 'error' in event.payload) { reject(event.payload); return } resolve(event.payload); }) // Only make the invoke call after the listener is registered listenerPromise.then(() => { invoke(method, { ...args, responseId: eventId, }).catch((err: any) => { console.error("tauri:invoke rejected: ", method, args, err); reject(err) }); }) }); } export type ServiceManagerStatus = 'Running' | 'Stopped' | 'NotFound' | 'unsupported service manager' | 'unsupported operating system'; export class TauriIntegrationService implements IntegrationService { private withPrompts = false; constructor() { this.shouldHandlePrompts() .then(result => { this.withPrompts = result; }); // listen for the portmaster:show event that is emitted // when tauri want's to tell us that we should make our // window visible. listen("portmaster:show", () => { this.openApp(); }) } writeToClipboard(text: string): Promise { return writeText(text); } openExternal(pathOrUrl: string): Promise { return open(pathOrUrl); } getInstallDir(): Promise { return Promise.reject("not yet supported in tauri") } getAppInfo(info: ProcessInfo): Promise { return asyncInvoke("get_app_info", { ...info, }) } getAppIcon(info: ProcessInfo): Promise { return this.getAppInfo(info) .then(info => info.icon_dataurl) } exitApp(): Promise { // we have two options here: // - close(): close the native tauri window and release all resources of it. // this has the disadvantage that if the user re-opens the window, // it will take slightly longer because angular need to re-bootstrap // the application. // // IMPORTANT: the angular application will automatically launch prompt // windows via the tauri window interface. If we would call close(), // those prompts wouldn't work anymore because the angular app would not // be running in the background. // // - hide(): just set the window visibility to false. The advantage is that angular // is still running and interacting with portmaster but it also means that // we waste some system resources due to tauri window objects and the angular // application. getCurrentWindow().hide() return Promise.resolve(); } // Tauri specific functions that are not defined in the IntegrationService interface. // to use those methods you must check if integration instanceof TauriIntegrationService. async shouldShow(): Promise { try { const response = await invoke("should_show"); return response === "show"; } catch (err) { console.error(err); return true; } } async shouldHandlePrompts(): Promise { try { const response = await invoke("should_handle_prompts") return response === "true" } catch (err) { console.error(err); return false; } } get_state(_: string): Promise { return invoke("get_state"); } set_state(key: string, value: string): Promise { return invoke("set_state", { key, value }) } getServiceManagerStatus(): Promise { return asyncInvoke("get_service_manager_status", {}) } startService(): Promise { return asyncInvoke("start_service", {}); } onExitRequest(cb: () => void): () => void { let unlisten: () => void = () => undefined; listen('exit-requested', () => { cb(); }).then(cleanup => { unlisten = cleanup; }) return () => { unlisten(); } } openApp() { Window.getByLabel("splash").then(splash => { splash?.close();}); const current = Window.getCurrent() current.isVisible() .then(visible => { if (!visible) { current.show(); current.setFocus(); } }); } closePrompt() { Window.getByLabel("prompt").then(window => { window?.close();}); } openPrompt() { if (!this.withPrompts) { return; } Window.getByLabel("prompt").then(prompt => { if (prompt) { return; } const promptWindow = new Window("prompt", { alwaysOnTop: true, decorations: false, minimizable: false, maximizable: false, resizable: false, title: 'Portmaster Prompt', visible: false, // the prompt marks it self as visible. skipTaskbar: true, closable: false, center: true, width: 600, height: 300, // in src/main.ts we check the current location path // and if it matches /prompt, we bootstrap the PromptEntryPointComponent // instead of the AppComponent. url: `http://${window.location.host}/prompt`, } as any) promptWindow.once("tauri://error", (err) => { console.error(err); }); }); } } ================================================ FILE: desktop/angular/src/app/intro/index.ts ================================================ export * from './intro.module'; ================================================ FILE: desktop/angular/src/app/intro/intro.module.ts ================================================ import { OverlayModule } from "@angular/cdk/overlay"; import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { SfngDropDownModule, SfngTipUpModule, StepperConfig } from "@safing/ui"; import { ConfigModule } from "../shared/config"; import { Step1WelcomeComponent } from "./step-1-welcome"; import { Step2TrackersComponent } from "./step-2-trackers"; import { Step3DNSComponent } from "./step-3-dns"; import { Step4TipupsComponent } from "./step-4-tipups"; const steps = [ Step1WelcomeComponent, Step2TrackersComponent, Step3DNSComponent, Step4TipupsComponent, ] @NgModule({ imports: [ CommonModule, OverlayModule, FormsModule, SfngDropDownModule, ConfigModule, SfngTipUpModule, ], declarations: steps }) export class IntroModule { static Stepper: StepperConfig = { steps: steps, canAbort: (idx) => idx === 0, } } ================================================ FILE: desktop/angular/src/app/intro/step-1-welcome/index.ts ================================================ export * from './step-1-welcome'; ================================================ FILE: desktop/angular/src/app/intro/step-1-welcome/step-1-welcome.html ================================================

Portmaster Protects Your Privacy

Portmaster enhances your privacy with powerful defaults - no configuration needed! Of course you can customize everything to your specific needs.

================================================ FILE: desktop/angular/src/app/intro/step-1-welcome/step-1-welcome.ts ================================================ import { ChangeDetectionStrategy, Component, Inject, TemplateRef, ViewChild } from "@angular/core"; import { Step, StepRef, STEP_REF } from "@safing/ui"; import { of } from "rxjs"; @Component({ templateUrl: './step-1-welcome.html', styleUrls: ['../step.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class Step1WelcomeComponent implements Step { validChange = of(true) readonly nextButtonLabel = 'Quick Setup'; @ViewChild('buttonTemplate', { static: true }) buttonTemplate!: TemplateRef; constructor( @Inject(STEP_REF) public stepRef: StepRef, ) { } } ================================================ FILE: desktop/angular/src/app/intro/step-2-trackers/index.ts ================================================ export * from './step-2-trackers' ================================================ FILE: desktop/angular/src/app/intro/step-2-trackers/step-2-trackers.html ================================================

Trackers Are Blocked System-Wide

Portmaster automatically blocks ads, trackers and malware hosts on your whole device. Portmaster knows what to block through trusted domain lists, which are also used by Ad-Blockers in browsers, etc. You can always customize this in the settings.

================================================ FILE: desktop/angular/src/app/intro/step-2-trackers/step-2-trackers.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, ElementRef, OnInit, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ConfigService, Setting } from "@safing/portmaster-api"; import { Step } from "@safing/ui"; import { of } from "rxjs"; import { mergeMap } from "rxjs/operators"; import { SaveSettingEvent } from "src/app/shared/config/generic-setting"; @Component({ templateUrl: './step-2-trackers.html', styleUrls: ['../step.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class Step2TrackersComponent implements Step, OnInit { private destroyRef = inject(DestroyRef); validChange = of(true) setting: Setting | null = null; constructor( public configService: ConfigService, public readonly elementRef: ElementRef, private cdr: ChangeDetectorRef, ) { } ngOnInit(): void { this.configService.get('filter/lists') .pipe( mergeMap(setting => { this.setting = setting; return this.configService.watch(setting.Key) }), takeUntilDestroyed(this.destroyRef), ) .subscribe(value => { this.setting!.Value = value; this.cdr.markForCheck(); }); } saveSetting(event: SaveSettingEvent) { this.configService.save(event.key, event.value) .subscribe() } } ================================================ FILE: desktop/angular/src/app/intro/step-3-dns/index.ts ================================================ export * from './step-3-dns' ================================================ FILE: desktop/angular/src/app/intro/step-3-dns/step-3-dns.html ================================================

Secure DNS for All Connections

Portmaster automatically encrypts all your DNS queries to safeguard them from prying eyes. Portmaster sets a default provider, but you can always switch to a custom DNS-over-TLS provider in the global settings.

================================================ FILE: desktop/angular/src/app/intro/step-3-dns/step-3-dns.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, ElementRef, OnInit, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ConfigService, QuickSetting, Setting, applyQuickSetting } from "@safing/portmaster-api"; import { Step } from "@safing/ui"; import { of } from "rxjs"; import { mergeMap } from "rxjs/operators"; import { SaveSettingEvent } from "src/app/shared/config/generic-setting"; interface QuickSettingModel extends QuickSetting { active: boolean; } @Component({ templateUrl: './step-3-dns.html', styleUrls: ['../step.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class Step3DNSComponent implements Step, OnInit { private destroyRef = inject(DestroyRef); validChange = of(true) setting: Setting | null = null; quickSettings: QuickSettingModel[] = []; isCustomValue = false; constructor( public configService: ConfigService, public readonly elementRef: ElementRef, private cdr: ChangeDetectorRef, ) { } private getQuickSettings(): QuickSettingModel[] { if (!this.setting) { return []; } let val = this.setting.Annotations["safing/portbase:ui:quick-setting"]; if (val === undefined) { return []; } if (!Array.isArray(val)) { return [{ ...val, active: false, }] } return val.map(v => ({ ...v, active: false, })) } ngOnInit(): void { this.configService.get('dns/nameservers') .pipe( mergeMap(setting => { this.setting = setting; this.quickSettings = this.getQuickSettings(); return this.configService.watch(setting.Key) }), takeUntilDestroyed(this.destroyRef), ) .subscribe(value => { this.setting!.Value = value; let hasActive = false; this.isCustomValue = false; this.quickSettings.forEach(setting => { if (this.setting?.Value !== undefined && JSON.stringify(this.setting.Value) === JSON.stringify(setting.Value)) { setting.active = true; hasActive = true; } else { setting.active = false; } }); if (!hasActive) { if (this.setting?.Value !== undefined && JSON.stringify(this.setting!.Value) !== JSON.stringify(this.setting!.DefaultValue)) { this.isCustomValue = true; } else if (this.quickSettings.length > 0) { this.quickSettings[0].active = true; } } this.cdr.markForCheck(); }); } saveSetting(event: SaveSettingEvent) { this.configService.save(event.key, event.value) .subscribe() } applyQuickSetting(action: QuickSetting) { const newValue = applyQuickSetting( this.setting!.Value || this.setting!.DefaultValue, action, ) this.configService.save(this.setting!.Key, newValue) .subscribe(); } } ================================================ FILE: desktop/angular/src/app/intro/step-4-tipups/index.ts ================================================ export * from './step-4-tipups' ================================================ FILE: desktop/angular/src/app/intro/step-4-tipups/step-4-tipups.html ================================================

Learn More as You Explore

Portmaster has a lot more to offer. When you decide to dive deeper you can always click on an information icon to learn more about a certain feature. Look out for those!

Click Me!
================================================ FILE: desktop/angular/src/app/intro/step-4-tipups/step-4-tipups.ts ================================================ import { ChangeDetectionStrategy, Component } from "@angular/core"; import { Step } from "@safing/ui"; import { of } from "rxjs"; @Component({ templateUrl: './step-4-tipups.html', styleUrls: ['../step.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class Step4TipupsComponent implements Step { validChange = of(true) } ================================================ FILE: desktop/angular/src/app/intro/step.scss ================================================ :host { @apply flex flex-col items-center justify-center; } h1 { @apply text-primary text-2xl font-medium capitalize text-center py-5; } p { @apply text-tertiary text-sm font-medium text-center; } ================================================ FILE: desktop/angular/src/app/layout/navigation/navigation.html ================================================
================================================ FILE: desktop/angular/src/app/layout/navigation/navigation.scss ================================================ :host { height: 100vh; top: 0px; position: sticky; display: flex; flex-direction: column; justify-content: space-between; align-items: center; user-select: none; .logo-image { @apply w-6 -top-3 -left-3 absolute; position: absolute; } svg { &:not(.connected) { animation-timing-function: cubic-bezier(0.445, 0.05, 0.55, 0.95); path.inner { fill: theme('colors.info.red'); } } } div.nav-list { display: flex; flex-direction: column; justify-content: flex-start; align-items: center; } div.nav-lower-list { display: flex; flex-direction: column; justify-content: flex-start; align-items: center; padding-bottom: 1.5rem; } div.link { @apply my-2; width: 2rem; height: 2rem; border-radius: 10px; display: flex; justify-content: space-around; align-items: center; cursor: pointer; & { outline: none; svg, fa-icon { opacity: .5; } } &:target, &.active { background-color: #2c2c2c; svg, fa-icon { opacity: 1; transform: scale(1.08); } } &:hover { svg, fa-icon { opacity: 1; } } svg, fa-icon { &.dash, &.spn, &.monitor, &.app, &.help, &.settings { @apply text-white; width: 1.1rem; position: relative; stroke: currentColor; } } } } ================================================ FILE: desktop/angular/src/app/layout/navigation/navigation.ts ================================================ import { INTEGRATION_SERVICE, IntegrationService } from 'src/app/integration'; import { ConnectedPosition } from '@angular/cdk/overlay'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Inject, OnInit, Output, inject } from '@angular/core'; import { ConfigService, DebugAPI, PortapiService, SPNService, StringSetting, BoolSetting } from '@safing/portmaster-api'; import { tap } from 'rxjs/operators'; import { AppComponent } from 'src/app/app.component'; import { NotificationType, NotificationsService, StatusService, VersionStatus, GetModuleState, ControlPauseStateData } from 'src/app/services'; import { ActionIndicatorService } from 'src/app/shared/action-indicator'; import { fadeInAnimation, fadeOutAnimation } from 'src/app/shared/animations'; import { ExitService } from 'src/app/shared/exit-screen'; import { TauriIntegrationService } from 'src/app/integration/taur-app'; @Component({ selector: 'app-navigation', templateUrl: './navigation.html', styleUrls: ['./navigation.scss'], exportAs: 'navigation', changeDetection: ChangeDetectionStrategy.OnPush, animations: [ fadeInAnimation, fadeOutAnimation, ] }) export class NavigationComponent implements OnInit { private readonly integration = inject(INTEGRATION_SERVICE); /** Emits the current portapi connection state on changes. */ readonly connected$ = this.portapi.connected$; /** @private The available and selected resource versions. */ versions: VersionStatus | null = null; /** Whether or not we have new, unseen notifications */ hasNewNotifications = false; /** The color to use for the notifcation-available hint (dot) */ notificationColor: string = 'text-green-300'; pauseState: ControlPauseStateData | null = null; get isPaused(): boolean { return this.pauseState?.Interception===true || this.pauseState?.SPN===true; } get isPausedInterception(): boolean { return this.pauseState?.Interception===true; } get isPausedSPN(): boolean { return this.pauseState?.SPN===true; } get pauseInfo(): string { if (this.pauseState?.Interception===true && this.pauseState?.SPN===true) return 'Portmaster and SPN are paused'; else if (this.pauseState?.Interception===true) return 'Portmaster is paused'; else if (this.pauseState?.SPN===true) return 'SPN is paused'; return ''; } get pauseInfoTillTime(): string { if (this.isPaused && this.pauseState?.TillTime) { const date = new Date(this.pauseState.TillTime); if (isNaN(date.getTime()) || date.getTime() < Date.now()) return ''; return `Auto-resume at ${date.toLocaleTimeString(undefined, { hour12: false })}`; } return ''; } /** Whether or not we have new, unseen prompts */ hasNewPrompts = false; /** Whether or not prompting is globally enabled. */ globalPromptingEnabled = false; /** Whether or not the SPN is currently enabled */ spnEnabled = false; @Output() sideDashChange = new EventEmitter<'collapsed' | 'expanded' | 'force-overlay'>(); /** Whether or not the side dash should be expanded or collapsed */ sideDashStatus: 'collapsed' | 'expanded' = 'expanded'; constructor( private portapi: PortapiService, private exitService: ExitService, private statusService: StatusService, private configService: ConfigService, private appComponent: AppComponent, private debugAPI: DebugAPI, private actionIndicator: ActionIndicatorService, private notificationService: NotificationsService, private spnService: SPNService, private cdr: ChangeDetectorRef ) { } dropDownPositions: ConnectedPosition[] = [ { originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top' } ] ngOnInit() { const mql = window.matchMedia('(max-width: 1200px)'); if (mql.matches) { this.sideDashStatus = 'collapsed'; this.sideDashChange.next(this.sideDashStatus); } mql.addEventListener('change', () => { if (mql.matches) { this.sideDashStatus = 'collapsed'; } else { this.sideDashStatus = 'expanded'; } this.sideDashChange.next(this.sideDashStatus); }) this.statusService.getVersions() .subscribe(versions => { this.versions = versions; this.cdr.markForCheck(); }); this.statusService.status$.subscribe(status => { this.pauseState = GetModuleState(status, 'Control', 'control:paused')?.Data || null; }); this.configService.watch('filter/defaultAction') .subscribe(defaultAction => { this.globalPromptingEnabled = defaultAction === 'ask'; this.cdr.markForCheck(); }) this.configService.watch("spn/enable") .subscribe(value => { this.spnEnabled = value; this.cdr.markForCheck(); }); this.notificationService.new$ .subscribe(notif => { if (notif.some(n => n.Type === NotificationType.Prompt && n.EventID.startsWith("filter:prompt"))) { this.hasNewPrompts = true; if (this.integration instanceof TauriIntegrationService) { this.integration.openPrompt(); } } else { this.hasNewPrompts = false; if (this.integration instanceof TauriIntegrationService) { this.integration.closePrompt(); } } if (notif.some(n => !n.EventID.startsWith("filter:prompt"))) { this.hasNewNotifications = true; } else { this.hasNewNotifications = false; } if (notif.some(n => n.Type === NotificationType.Error)) { this.notificationColor = 'text-red-300'; } else if (notif.some(n => n.Type === NotificationType.Warning)) { this.notificationColor = 'text-yellow-300'; } else { this.notificationColor = 'text-green-300'; } this.cdr.markForCheck(); }) } toggleSideDash(event: MouseEvent) { let notify: 'expanded' | 'collapsed' | 'force-overlay' = this.sideDashStatus; if (this.sideDashStatus === 'collapsed') { this.sideDashStatus = 'expanded'; notify = 'expanded'; if (event.shiftKey) { notify = 'force-overlay' } } else { this.sideDashStatus = 'collapsed'; notify = 'collapsed' } this.sideDashChange.next(notify); } /** * @private * Injects a ui/reload event and performs a complete * reload of the window once the portmaster re-opened the * UI bundle. */ reloadUI(_: Event) { this.portapi.reloadUI() .pipe( tap(() => { setTimeout(() => window.location.reload(), 1000) }) ) .subscribe(this.actionIndicator.httpObserver( 'Reloading UI ...', 'Failed to Reload UI', )) } /** Re-initialize the SPN */ reinitSPN(_: Event) { this.portapi.reinitSPN() .subscribe(this.actionIndicator.httpObserver( 'Re-initialized SPN', 'Failed to re-initialize the SPN' )) } /** Logs the user out of the SPN completely by purgin the user profile from the local storage */ logoutCompletely(_: Event) { this.spnService.logout(true) .subscribe(this.actionIndicator.httpObserver( 'Logout', 'You have been logged out of the SPN completely.' )) } /** * @private * Clear the DNS name cache. */ clearDNSCache(_: Event) { this.portapi.clearDNSCache() .subscribe(this.actionIndicator.httpObserver( 'DNS Cache Cleared', 'Failed to Clear DNS Cache.', )) } cleanupHistory(_: Event) { this.portapi.cleanupHistory() .subscribe(this.actionIndicator.httpObserver( 'Network History Cleaned Up', 'Failed to Cleanup Network History.' )) } /** * @private * Trigger downloading of updates * * @param event - The mouse event */ downloadUpdates(event: Event) { this.portapi.checkForUpdates() .subscribe(this.actionIndicator.httpObserver( 'Downloading Updates ...', 'Failed to Check for Updates', )) } /** * @private * Trigger a shutdown of the portmaster-core service */ shutdown(_: Event) { this.exitService.shutdownPortmaster(); } /** * @private * Trigger a restart of the portmaster-core service. Requires * that portmaster has been started with a service-wrapper. * * @param event The mouse event */ restart(event: Event) { // prevent default and stop-propagation to avoid // expanding the accordion body. event.preventDefault(); event.stopPropagation(); this.portapi.restartPortmaster() .subscribe(this.actionIndicator.httpObserver( 'Restarting ...', 'Failed to Restart', )) } pause(event: Event, duration: number) { // prevent default and stop-propagation to avoid // expanding the accordion body. event.preventDefault(); event.stopPropagation(); this.portapi.pause(duration, false) .subscribe(this.actionIndicator.httpObserver( 'Pausing ...', 'Failed to Pause', )) } pauseSPN(event: Event, duration: number) { // prevent default and stop-propagation to avoid // expanding the accordion body. event.preventDefault(); event.stopPropagation(); this.portapi.pause(duration, true) .subscribe(this.actionIndicator.httpObserver( 'Pausing SPN...', 'Failed to Pause SPN', )) } resume(event: Event) { // prevent default and stop-propagation to avoid // expanding the accordion body. event.preventDefault(); event.stopPropagation(); let msg = 'Resuming ...'; if (this.pauseState?.Interception===true && this.pauseState?.SPN===true) msg = 'Resuming Portmaster and SPN ...'; else if (this.pauseState?.Interception===true) msg = 'Resuming Portmaster ...'; else if (this.pauseState?.SPN===true) msg = 'Resuming SPN ...'; this.portapi.resume() .subscribe(this.actionIndicator.httpObserver( msg, 'Failed to Resume', )) } /** * @private * Opens the data-directory of the portmaster installation. * Requires the application to run inside electron. */ async openDataDir(event: Event) { const dir = await this.integration.getInstallDir() await this.integration.openExternal(dir); } openChangeLog() { const url = "https://github.com/safing/portmaster/releases"; this.integration.openExternal(url); } showIntro() { this.appComponent.showIntro() } resetBroadcastState() { this.portapi.resetBroadcastState() .subscribe(this.actionIndicator.httpObserver( 'Notifications State Cleared', 'Failed to Reset Notifications State.', )) } copyDebugInfo(event: Event) { // prevent default and stop-propagation to avoid // expanding the accordion body. event.preventDefault(); event.stopPropagation(); this.debugAPI.getCoreDebugInfo() .subscribe( async info => { await this.integration.writeToClipboard(info); }, err => { console.error(err); this.actionIndicator.error('Failed loading debug data', err); } ) } } ================================================ FILE: desktop/angular/src/app/layout/side-dash/side-dash.html ================================================
================================================ FILE: desktop/angular/src/app/layout/side-dash/side-dash.scss ================================================ :host { display: flex; flex-direction: column; align-items: center; justify-content: flex-start; overflow: hidden; overflow-y: hidden; width: 419px; @apply pt-4; } ================================================ FILE: desktop/angular/src/app/layout/side-dash/side-dash.ts ================================================ import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ selector: 'app-side-dash', templateUrl: './side-dash.html', styleUrls: ['./side-dash.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class SideDashComponent { /** Whether or not a SPN account login is required */ spnLoginRequired = false; } ================================================ FILE: desktop/angular/src/app/package.json ================================================ { "name": "portmaster", "private": true, "description_1": "This is a special package.json file that is not used by package managers.", "description_2": "It is used to tell the tools and bundlers whether the code under this directory is free of code with non-local side-effect. Any code that does have non-local side-effects can't be well optimized (tree-shaken) and will result in unnecessary increased payload size.", "description_3": "It should be safe to set this option to 'false' for new applications, but existing code bases could be broken when built with the production config if the application code does contain non-local side-effects that the application depends on.", "description_4": "To learn more about this file see: https://angular.io/config/app-package-json.", "sideEffects": false, "devDependencies": { "@types/node": "^17.0.31" } } ================================================ FILE: desktop/angular/src/app/pages/app-view/app-insights/app-insights.component.html ================================================
================================================ FILE: desktop/angular/src/app/pages/app-view/app-insights/app-insights.component.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, Input, OnInit, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { AppProfile, BandwidthChartResult, ChartResult, Netquery } from '@safing/portmaster-api'; import { repeat } from 'rxjs'; import { CircularBarChartConfig, splitQueryResult } from 'src/app/shared/netquery/circular-bar-chart/circular-bar-chart.component'; import { DefaultBandwidthChartConfig } from 'src/app/shared/netquery/line-chart/line-chart'; interface CountryBarData { series: 'country'; value: number; country: string; } @Component({ selector: 'app-app-insights', templateUrl: './app-insights.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) export class AppInsightsComponent implements OnInit { private readonly netquery = inject(Netquery); private readonly destroyRef = inject(DestroyRef); private readonly cdr = inject(ChangeDetectorRef); @Input() profile!: AppProfile; connectionChart: ChartResult[] = []; bandwidthChart: BandwidthChartResult[] = []; bwChartConfig = DefaultBandwidthChartConfig; countryData: CountryBarData[] = []; readonly countryBarConfig: CircularBarChartConfig = { stack: 'country', seriesKey: 'series', value: 'value', ticks: 3, colorAsClass: true, series: { 'count': { color: 'text-green-300 text-opacity-50', }, }, } ngOnInit() { const key = `${this.profile.Source}/${this.profile.ID}` this.netquery.batch({ countryData: { select: [ 'country', { $count: { field: '*', as: 'count' } }, ], query: { internal: { $eq: false }, country: { $ne: '' } }, groupBy: ['country'] } }) .pipe( repeat({ delay: 10000 }), takeUntilDestroyed(this.destroyRef) ) .subscribe(result => { this.countryData = splitQueryResult(result.countryData, ['count']) as CountryBarData[]; console.log(this.countryData) this.cdr.markForCheck(); }) this.netquery.activeConnectionChart({ profile: key }) .pipe( repeat({ delay: 10000 }), takeUntilDestroyed(this.destroyRef) ) .subscribe(data => { this.connectionChart = data; this.cdr.markForCheck(); }) this.netquery.bandwidthChart({ profile: key }, undefined, 60) .pipe( repeat({ delay: 10000 }), takeUntilDestroyed(this.destroyRef) ) .subscribe(data => { this.bandwidthChart = data; this.cdr.markForCheck(); }) } } ================================================ FILE: desktop/angular/src/app/pages/app-view/app-view.html ================================================
Apps
{{ appProfile.Name }}

{{appProfile!.Name}}

Path: {{ applicationDirectory }}
Binary: {{ binaryName }}
Active Connections: {{stats?.countAliveConnections || 0}}
Network History: As of {{ historyAvailableSince | date }} Remove all {{ connectionsInHistory }} Connections None
Edit App Profile Export App Profile Delete App Profile

{{ stats!.size | prettyCount }}

Connections

{{ (100 / stats!.size) * (stats!.size - stats!.countAllowed) | number:'1.0-1' }}%

Blocked

{{ stats.bytes_received | bytes }}

Available in Plus Received

{{ stats.bytes_sent | bytes }}

Sent
App Specific Settings

{{ appProfile!.Name }} is fully using the global settings.

Start creating exceptions for it now.

{{appProfile!.Name}}

{{appProfile!.PresentationPath}}

{{appProfile!.Created * 1000 | date:'medium'}}

{{appProfile!.LastEdited * 1000 | date:'medium'}} N/A

{{!!appProfile!.Internal ? 'yes' : 'no'}}

{{appProfile!.Source}}

{{appProfile!.ID}}

{{layeredProfile?.RevisionCounter}}

  1. {{layer}}

Description

Warning

updated {{ appProfile.WarningLastUpdated | timeAgo }}

Fingerprints

This profile will be applied to processes that match one of the following fingerprints:
{{ fp.Type }} where {{ fp.Type === 'tag' ? (tagNames[fp.Key] || fp.Key) : fp.Key }} {{ fp.Operation }} {{ fp.Value }}

Delete Profile

You can completely delete this profile to get rid of any settings. The profile will be automatically re-created with default settings as soon as the application starts to use the network.

Debugging

When reporting issues with this app please make sure to include the following debug information:
================================================ FILE: desktop/angular/src/app/pages/app-view/app-view.scss ================================================ :host { @apply flex flex-col h-screen max-h-screen; } ================================================ FILE: desktop/angular/src/app/pages/app-view/app-view.ts ================================================ import { ChangeDetectorRef, Component, DestroyRef, OnDestroy, OnInit, ViewChild, inject, } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router } from '@angular/router'; import { AppProfile, AppProfileService, Condition, ConfigService, Database, DebugAPI, ExpertiseLevel, FeatureID, FlatConfigObject, IProfileStats, LayeredProfile, Netquery, PortapiService, SPNService, Setting, flattenProfileConfig, setAppSetting } from '@safing/portmaster-api'; import { SfngDialogService } from '@safing/ui'; import { BehaviorSubject, Observable, Subscription, combineLatest, interval, of, throwError, } from 'rxjs'; import { catchError, distinctUntilChanged, map, mergeMap, startWith, switchMap, } from 'rxjs/operators'; import { INTEGRATION_SERVICE } from 'src/app/integration'; import { SessionDataService } from 'src/app/services'; import { ActionIndicatorService } from 'src/app/shared/action-indicator'; import { fadeInAnimation, fadeOutAnimation } from 'src/app/shared/animations'; import { ExportConfig, ExportDialogComponent, } from 'src/app/shared/config/export-dialog/export-dialog.component'; import { SaveSettingEvent } from 'src/app/shared/config/generic-setting/generic-setting'; import { ExpertiseService } from 'src/app/shared/expertise'; import { SfngNetqueryViewer } from 'src/app/shared/netquery'; import { EditProfileDialog } from './../../shared/edit-profile-dialog/edit-profile-dialog'; @Component({ templateUrl: './app-view.html', styleUrls: ['../page.scss', './app-view.scss'], animations: [fadeOutAnimation, fadeInAnimation], }) export class AppViewComponent implements OnInit, OnDestroy { private readonly integration = inject(INTEGRATION_SERVICE); @ViewChild(SfngNetqueryViewer) netqueryViewer?: SfngNetqueryViewer; destroyRef = inject(DestroyRef); spn = inject(SPNService); canUseHistory = false; canViewBW = false; canUseSPN = false; /** subscription to our update-process observable */ private subscription = Subscription.EMPTY; /** * @private * historyAvailableSince holds the date of the oldes connection * in the history database for this app. */ historyAvailableSince: Date | null = null; /** * @private * connectionsInHistory holds the total amount of connections * in the history database for this app */ connectionsInHistory = 0; /** * @private * The current AppProfile we are showing. */ appProfile: AppProfile | null = null; /** * @private * Whether or not the overview componet should be rendered. */ get showOverview() { return this.appProfile == null && !this._loading; } /** * @private * The currently displayed list of settings */ settings: Setting[] = []; profileSettings: Setting[] = []; /** * @private * All available settings. */ allSettings: Setting[] = []; /** * @private * The current search term displayed in the search-input. */ searchTerm = ''; /** * @private * The key of the setting to highligh, if any ... */ highlightSettingKey: string | null = null; /** * @private * Emits whenever the currently used settings "view" changes. */ viewSettingChange = new BehaviorSubject<'all' | 'active'>('all'); /** * @private * The path of the application binary */ applicationDirectory = ''; /** * @private * The name of the binary */ binaryName = ''; /** * @private * Whether or not the profile warning message should be displayed */ displayWarning = false; /** * @private * The current profile statistics */ stats: IProfileStats | null = null; /** * @private * The internal, layered profile if the app is active */ layeredProfile: LayeredProfile | null = null; /** Used to track whether we are already initialized */ private _loading = true; /** * @private * * Defines what "view" we are currently in */ get viewSetting(): 'all' | 'active' { return this.viewSettingChange.getValue(); } /** A lookup map from tag ID to tag Name */ tagNames: { [tagID: string]: string; } = {}; collapseHeader = false; constructor( public sessionDataService: SessionDataService, private profileService: AppProfileService, private route: ActivatedRoute, private netquery: Netquery, private cdr: ChangeDetectorRef, private configService: ConfigService, private router: Router, private actionIndicator: ActionIndicatorService, private dialog: SfngDialogService, private debugAPI: DebugAPI, private expertiseService: ExpertiseService, private portapi: PortapiService ) { } /** * @private * Used to save a change in the app settings. Emitted by the config-view * component * * @param event The emitted save-settings-event. */ saveSetting(event: SaveSettingEvent) { // Guard against invalid usage and abort if there's not appProfile // to save. if (!this.appProfile) { return; } if (!this.appProfile!.Config) { this.appProfile.Config = {} } // If the value has been "reset to global value" we need to // set the value to "undefined". if (event.isDefault) { setAppSetting(this.appProfile!.Config, event.key, undefined); } else { setAppSetting(this.appProfile!.Config, event.key, event.value); } // Actually safe the profile this.profileService.saveProfile(this.appProfile!).subscribe({ next: () => { if (!!event.accepted) { event.accepted(); } }, error: (err) => { // if there's a callback function for errors call it. if (!!event.rejected) { event.rejected(err); } console.error(err); this.actionIndicator.error('Failed to save setting', err); }, }); } exportProfile() { if (!this.appProfile) { return; } this.portapi .exportProfile(`${this.appProfile.Source}/${this.appProfile.ID}`) .subscribe((exportBlob) => { const exportConfig: ExportConfig = { type: 'profile', content: exportBlob, }; this.dialog.create(ExportDialogComponent, { data: exportConfig, autoclose: false, backdrop: true, }); }); } editProfile() { if (!this.appProfile) { return; } this.dialog .create(EditProfileDialog, { backdrop: true, autoclose: false, data: `${this.appProfile.Source}/${this.appProfile.ID}`, }) .onAction('deleted', () => { // navigate to the app overview if it has been deleted. this.router.navigate(['/app/']); }); } cleanProfileHistory() { if (!this.appProfile) { return; } const observer = this.actionIndicator.httpObserver( 'History successfully removed', 'Failed to remove history' ); this.netquery .cleanProfileHistory(this.appProfile.Source + '/' + this.appProfile.ID) .subscribe({ next: (res) => { observer.next!(res); this.historyAvailableSince = null; this.connectionsInHistory = 0; this.cdr.markForCheck(); }, error: (err) => { observer.error!(err); }, }); } ngOnInit() { this.profileService.tagDescriptions().subscribe((tags) => { tags.forEach((t) => { this.tagNames[t.ID] = t.Name; this.cdr.markForCheck(); }); }); // watch the route parameters and start watching the referenced // application profile, it's layer profile and polling the stats. const profileStream: Observable< [AppProfile, LayeredProfile | null, IProfileStats | null] | null > = this.route.paramMap.pipe( switchMap((params) => { // Get the profile source and id. If one is unset (null) // than return a"null" emit-once stream. const source = params.get('source'); const id = params.get('id'); if (source === null || id === null) { this._loading = false; return of(null); } this._loading = true; this.historyAvailableSince = null; this.connectionsInHistory = 0; this.appProfile = null; this.stats = null; // Start watching the application profile. // switchMap will unsubscribe automatically if // we start watching a different profile. return this.profileService.getAppProfile(source, id).pipe( catchError((err) => { if (typeof err === 'string') { err = new Error(err); } this.router.navigate(['/app/overview'], { onSameUrlNavigation: 'reload', }); this.actionIndicator.error( 'Failed To Get Profile', this.actionIndicator.getErrorMessgae(err) ); return throwError(() => err); }), mergeMap(() => { return combineLatest([ this.profileService.watchAppProfile(source, id), this.profileService .watchLayeredProfile(source, id) .pipe(startWith(null)), interval(10000).pipe( startWith(-1), mergeMap(() => this.netquery .getProfileStats({ profile: `${source}/${id}`, }) .pipe(map((result) => result?.[0])) ), startWith(null) ), ]); }) ); }) ); // used to track changes to the object identity of the global configuration let prevousGlobal: FlatConfigObject = {}; this.subscription = combineLatest([ profileStream, // emits the current app profile everytime it changes this.route.queryParamMap, // for changes to the settings= query parameter this.profileService.globalConfig(), // for changes to ghe global profile this.configService.query(''), // get ALL settings (once, only the defintion is of intereset) this.viewSettingChange.pipe( // watch the current "settings-view" setting, but only if it changes distinctUntilChanged() ), ]).subscribe( async ([profile, queryMap, global, allSettings, viewSetting]) => { const previousProfile = this.appProfile; if (!!profile) { const key = profile![0].Source + '/' + profile![0].ID; const query: Condition = { profile: key, }; // ignore internal connections if the user is not in developer mode. if (this.expertiseService.currentLevel !== ExpertiseLevel.Developer) { query.internal = { $eq: false, }; } this.netquery .query( { select: [ { $min: { field: 'started', as: 'first_connection', }, }, { $count: { field: '*', as: 'totalCount', }, }, ], groupBy: ['profile'], query: { profile: `${profile[0].Source}/${profile[0].ID}`, }, databases: [Database.History], }, 'app-view-get-first-connection' ) .subscribe((result) => { if (result.length > 0) { this.historyAvailableSince = new Date( result[0].first_connection! ); this.connectionsInHistory = result[0].totalCount; } else { this.historyAvailableSince = null; this.connectionsInHistory = 0; } this.cdr.markForCheck(); }); this.appProfile = profile[0] || null; this.layeredProfile = profile[1] || null; this.stats = profile[2] || null; } else { this.appProfile = null; this.layeredProfile = null; this.stats = null; } this.displayWarning = false; if (this.appProfile?.WarningLastUpdated) { const now = new Date().getTime(); const diff = now - new Date(this.appProfile.WarningLastUpdated).getTime(); this.displayWarning = diff < 1000 * 60 * 60 * 24 * 7; } if (!!this.netqueryViewer && this._loading) { this.netqueryViewer.performSearch(); } this._loading = false; if (!!this.appProfile?.PresentationPath) { let parts: string[] = []; let sep = '/'; if (this.appProfile.PresentationPath[0] === '/') { // linux, darwin, bsd ... sep = '/'; } else { // windows ... sep = '\\'; } parts = this.appProfile.PresentationPath.split(sep); this.binaryName = parts.pop()!; this.applicationDirectory = parts.join(sep); } else { this.applicationDirectory = ''; this.binaryName = ''; } this.highlightSettingKey = queryMap.get('setting'); let profileConfig: FlatConfigObject = {}; // if we have a profile flatten it's configuration map to something // more useful. if (!!this.appProfile) { profileConfig = flattenProfileConfig(this.appProfile.Config); } // if we should highlight a setting make sure to switch the // viewSetting to all if it's the "global" default (that is, no // value is set). Otherwise the setting won't render and we cannot // highlight it. // We need to keep this even though we default to "all" now since // the following might happen: // - user already navigated to an app-page and selected "View Active". // - a notification comes in that has a "show setting" action // - the user clicks the action button and the setting should be displayed // - since the requested setting has not been changed it is not available // in "View Active" so we need to switch back to "View All". Otherwise // the action button would fail and the user would not notice something // changing. // if (!!this.highlightSettingKey) { if (profileConfig[this.highlightSettingKey] === undefined) { this.viewSettingChange.next('all'); } } // check if we got new values for the profile or the settings. In both cases, we need to update the // profile settings displayed as there might be new values to show. const profileChanged = previousProfile !== this.appProfile; const settingsChanged = allSettings !== this.allSettings; const globalChanged = global !== prevousGlobal; const settingsNeedUpdate = profileChanged || settingsChanged || globalChanged; // save the current global config object so we can compare for identity changes // the next time we're executed prevousGlobal = global; if (!!this.appProfile && settingsNeedUpdate) { // filter the settings and remove all settings that are not // profile specific (i.e. not part of the global config). Also // update the current settings value (from the app profile) and // the default value (from the global profile). this.profileSettings = allSettings.map((setting) => { setting.Value = profileConfig[setting.Key]; setting.GlobalDefault = global[setting.Key]; return setting; }); this.settings = this.profileSettings.filter((setting) => { if (!(setting.Key in global)) { return false; } const isModified = setting.Value !== undefined; if (this.viewSetting === 'all') { return true; } return isModified; }); this.allSettings = allSettings; } this.cdr.markForCheck(); } ); this.spn.profile$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({ next: (profile) => { this.canUseHistory = profile?.current_plan?.feature_ids?.includes(FeatureID.History) || false; this.canViewBW = profile?.current_plan?.feature_ids?.includes(FeatureID.Bandwidth) || false; this.canUseSPN = profile?.current_plan?.feature_ids?.includes(FeatureID.SPN) || false; }, }); } /** * @private * Retrieves debug information from the current * profile and copies it to the clipboard */ copyDebugInfo() { if (!this.appProfile) { return; } this.debugAPI .getProfileDebugInfo(this.appProfile.Source, this.appProfile.ID) .subscribe(async (data) => { console.log(data); // Copy to clip-board if supported await this.integration.writeToClipboard(data); this.actionIndicator.success('Copied to Clipboard'); }); } ngOnDestroy() { this.subscription.unsubscribe(); } /** * @private * Delete the current profile. Requires a two-step confirmation. */ deleteProfile() { if (!this.appProfile) { return; } this.dialog .confirm({ canCancel: true, caption: 'Caution', header: 'Deleting Profile ' + this.appProfile.Name, message: 'Do you really want to delete this profile? All settings will be lost.', buttons: [ { id: '', text: 'Cancel', class: 'outline' }, { id: 'delete', class: 'danger', text: 'Yes, delete it' }, ], }) .onAction('delete', () => { this.profileService.deleteProfile(this.appProfile!).subscribe(() => { this.router.navigate(['/app/overview']); this.actionIndicator.success( 'Profile Deleted', 'Successfully deleted profile ' + this.appProfile?.Name ); }); }); } } ================================================ FILE: desktop/angular/src/app/pages/app-view/index.ts ================================================ export { AppViewComponent } from './app-view'; export { AppOverviewComponent } from './overview'; export { QuickSettingInternetButtonComponent } from './qs-internet'; ================================================ FILE: desktop/angular/src/app/pages/app-view/merge-profile-dialog/merge-profile-dialog.component.html ================================================

Merge Profiles

Please select the primary profile. All other selected profiles will be merged into the primary profile by copying metadata, fingerprints and icons into a new profile. Only the settings of the primary profile will be kept.
{{ p.Name }}
================================================ FILE: desktop/angular/src/app/pages/app-view/merge-profile-dialog/merge-profile-dialog.component.ts ================================================ import { AppProfile } from './../../../../../dist-lib/safing/portmaster-api/lib/app-profile.types.d'; import { ChangeDetectionStrategy, Component, OnInit, TrackByFunction, inject } from "@angular/core"; import { Router } from '@angular/router'; import { PortapiService } from '@safing/portmaster-api'; import { SFNG_DIALOG_REF, SfngDialogRef } from "@safing/ui"; import { ActionIndicatorService } from 'src/app/shared/action-indicator'; @Component({ templateUrl: './merge-profile-dialog.component.html', changeDetection: ChangeDetectionStrategy.OnPush, styles: [ ` :host { @apply flex flex-col gap-2 justify-start h-96 w-96; } ` ] }) export class MergeProfileDialogComponent implements OnInit { readonly dialogRef: SfngDialogRef = inject(SFNG_DIALOG_REF); private readonly portapi = inject(PortapiService); private readonly router = inject(Router); private readonly uai = inject(ActionIndicatorService); get profiles(): AppProfile[] { return this.dialogRef.data; } primary: AppProfile | null = null; newName = ''; trackProfile: TrackByFunction = (_, p) => `${p.Source}/${p.ID}` ngOnInit(): void { (() => { }); } mergeProfiles() { if (!this.primary) { return } this.portapi.mergeProfiles( this.newName, `${this.primary.Source}/${this.primary.ID}`, this.profiles .filter(p => p !== this.primary) .map(p => `${p.Source}/${p.ID}`) ) .subscribe({ next: newID => { this.router.navigate(['/app/' + newID]) this.uai.success('Profiles Merged Successfully', 'All selected profiles have been merged') this.dialogRef.close() }, error: err => { this.uai.error('Failed To Merge Profiles', this.uai.getErrorMessgae(err)) } }) } } ================================================ FILE: desktop/angular/src/app/pages/app-view/overview.html ================================================

All Apps

Create profile Import Profile Merge or Delete profiles
Manage
Merge Profiles Delete Profiles Cancel
{{ selectedProfileCount}} selected

Active

Recently Edited

All

No applications match your search term.
================================================ FILE: desktop/angular/src/app/pages/app-view/overview.scss ================================================ :host { justify-content: flex-start; } .header-title { display: flex; width: 100%; margin-bottom: 0.5rem; align-items: center; height: 3rem; flex-shrink: 0; h1 { flex-grow: unset; } fa-icon[icon*="question-circle"] { margin-left: 0.35rem; } } .scrollable { width: auto; flex-grow: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; } .scrollable-header { @apply bg-background; @apply pt-4; @apply pb-1; width: 100%; position: sticky; top: 0px; display: flex; grid-column: 1 / -1; fa-icon[icon*="question-circle"] { margin-left: 0.35rem; } } .card-header { // Card headers have top-margin by default. // Since we're using a grid-gap anyway we need // to clear the margin. @apply mt-0; } ================================================ FILE: desktop/angular/src/app/pages/app-view/overview.ts ================================================ import { ChangeDetectorRef, Component, OnDestroy, OnInit, TrackByFunction, } from '@angular/core'; import { AppProfile, AppProfileService, Netquery, trackById, } from '@safing/portmaster-api'; import { SfngDialogService } from '@safing/ui'; import { BehaviorSubject, Subscription, combineLatest, forkJoin } from 'rxjs'; import { debounceTime, filter, startWith } from 'rxjs/operators'; import { fadeInAnimation, fadeInListAnimation, moveInOutListAnimation, } from 'src/app/shared/animations'; import { FuzzySearchService } from 'src/app/shared/fuzzySearch'; import { EditProfileDialog } from './../../shared/edit-profile-dialog/edit-profile-dialog'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { MergeProfileDialogComponent } from './merge-profile-dialog/merge-profile-dialog.component'; import { ActionIndicatorService } from 'src/app/shared/action-indicator'; import { Router } from '@angular/router'; import { ImportConfig, ImportDialogComponent, } from 'src/app/shared/config/import-dialog/import-dialog.component'; interface LocalAppProfile extends AppProfile { hasConfigChanges: boolean; selected: boolean; } @Component({ selector: 'app-settings-overview', templateUrl: './overview.html', styleUrls: ['../page.scss', './overview.scss'], animations: [fadeInAnimation, fadeInListAnimation, moveInOutListAnimation], }) export class AppOverviewComponent implements OnInit, OnDestroy { private subscription = Subscription.EMPTY; /** Whether or not we are currently loading */ loading = true; /** All application profiles that are actually running */ runningProfiles: LocalAppProfile[] = []; /** All application profiles that have been edited recently */ recentlyEdited: LocalAppProfile[] = []; /** All application profiles */ profiles: LocalAppProfile[] = []; /** The current search term */ searchTerm: string = ''; /** total number of profiles */ total: number = 0; /** Whether or not we are in profile-selection mode */ set selectMode(v: any) { this._selectMode = coerceBooleanProperty(v); // reset all previous profile selections if (!this._selectMode) { this.profiles.forEach((profile) => (profile.selected = false)); } } get selectMode() { return this._selectMode; } private _selectMode = false; get selectedProfileCount() { return this.profiles.reduce( (sum, profile) => (profile.selected ? sum + 1 : sum), 0 ); } /** Observable emitting the search term */ private onSearch = new BehaviorSubject(''); /** TrackBy function for the profiles. */ trackProfile: TrackByFunction = trackById; constructor( private profileService: AppProfileService, private changeDetector: ChangeDetectorRef, private searchService: FuzzySearchService, private netquery: Netquery, private dialog: SfngDialogService, private actionIndicator: ActionIndicatorService, private router: Router ) { } handleProfileClick(profile: LocalAppProfile, event: MouseEvent) { if (event.shiftKey) { // stay on the same page as clicking the app actually triggers // a navigation before this handler is executed. this.router.navigate(['/app/overview']); this.selectMode = true; event.preventDefault(); event.stopImmediatePropagation(); event.stopPropagation(); } if (this.selectMode) { profile.selected = !profile.selected; } if (event.shiftKey && this.selectedProfileCount === 0) { this.selectMode = false; } } importProfile() { const importConfig: ImportConfig = { type: 'profile', key: '', }; this.dialog.create(ImportDialogComponent, { data: importConfig, autoclose: false, backdrop: 'light', }); } stripHtmlTags(text: string): string { if (!text) return ''; // Only strip if we have proper HTML tags (opening and closing with same tag name) return text.replace(/<([a-zA-Z][a-zA-Z0-9]*)[^>]*>([^<]*)<\/\1>/g, '$2'); } openMergeDialog() { this.dialog.create(MergeProfileDialogComponent, { autoclose: true, backdrop: 'light', data: this.profiles.filter((p) => p.selected) .map((p) => ({ ...p, // Strip HTML tags from Name if it exists (e.g., highlighted search results) ...(p.Name ? { Name: this.stripHtmlTags(p.Name) } : {}) })), }); this.selectMode = false; } deleteSelectedProfiles() { this.dialog .confirm({ header: 'Confirm Profile Deletion', message: `Are you sure you want to delete all ${this.selectedProfileCount} selected profiles?`, caption: 'Attention', buttons: [ { id: 'no', text: 'Cancel', class: 'outline', }, { id: 'yes', text: 'Delete', class: 'danger', }, ], }) .onAction('yes', () => { forkJoin( this.profiles .filter((profile) => profile.selected) .map((p) => this.profileService.deleteProfile(p)) ).subscribe({ next: () => { this.actionIndicator.success( 'Selected Profiles Delete', 'All selected profiles have been deleted' ); }, error: (err) => { this.actionIndicator.error( 'Failed To Delete Profiles', `An error occured while deleting some profiles: ${this.actionIndicator.getErrorMessgae( err )}` ); }, }); }) .onClose.subscribe(() => (this.selectMode = false)); } ngOnInit() { // watch all profiles and re-emit (debounced) when the user // enters or chanages the search-text. this.subscription = combineLatest([ this.profileService.watchProfiles(), this.onSearch.pipe(debounceTime(100), startWith('')), this.netquery.getActiveProfileIDs().pipe(startWith([] as string[])), ]).subscribe(([profiles, searchTerm, activeProfiles]) => { this.loading = false; // find all profiles that match the search term. For searchTerm="" thsi // will return all profiles. const filtered = this.searchService.searchList(profiles, searchTerm, { ignoreLocation: true, ignoreFieldNorm: true, threshold: 0.1, minMatchCharLength: 3, keys: ['Name', 'PresentationPath'], }); // create a lookup map of all profiles we already loaded so we don't loose // selection state when a profile has been updated. const oldProfiles = new Map( this.profiles.map((profile) => [ `${profile.Source}/${profile.ID}`, profile, ]) ); // Prepare new, empty lists for our groups this.profiles = []; this.runningProfiles = []; this.recentlyEdited = []; // calcualte the threshold for "recently-used" (1 week). const recentlyUsedThreshold = new Date().valueOf() / 1000 - 60 * 60 * 24 * 7; // flatten the filtered profiles, sort them by name and group them into // our "app-groups" (active, recentlyUsed, others) this.total = filtered.length; filtered .map((item) => item.item) .sort((a, b) => { const aName = a.Name.toLocaleLowerCase(); const bName = b.Name.toLocaleLowerCase(); if (aName > bName) { return 1; } if (aName < bName) { return -1; } return 0; }) .forEach((profile) => { const local: LocalAppProfile = { ...profile, hasConfigChanges: profile.LastEdited > 0 && Object.keys(profile.Config || {}).length > 0, selected: oldProfiles.get(`${profile.Source}/${profile.ID}`)?.selected || false, }; if (activeProfiles.includes(profile.Source + '/' + profile.ID)) { this.runningProfiles.push(local); } else if (profile.LastEdited >= recentlyUsedThreshold) { this.recentlyEdited.push(local); } // we always add the profile to "All Apps" this.profiles.push(local); }); this.changeDetector.markForCheck(); }); } /** * @private * * Used as an ngModelChange callback on the search-input. * * @param term The search term entered by the user */ searchApps(term: string) { this.searchTerm = term; this.onSearch.next(term); } /** * @private * * Opens the create profile dialog */ createProfile() { const ref = this.dialog.create(EditProfileDialog, { backdrop: true, autoclose: false, }); ref.onClose.pipe(filter((action) => action === 'saved')).subscribe(() => { // reset the search and reload to make sure the new // profile shows up this.searchApps(''); }); } ngOnDestroy() { this.subscription.unsubscribe(); } } ================================================ FILE: desktop/angular/src/app/pages/app-view/qs-history/qs-history.component.html ================================================
Keep History Get Plus
================================================ FILE: desktop/angular/src/app/pages/app-view/qs-history/qs-history.component.scss ================================================ ================================================ FILE: desktop/angular/src/app/pages/app-view/qs-history/qs-history.component.ts ================================================ import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, inject, } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { BoolSetting, FeatureID, SPNService, Setting, getActualValue, } from '@safing/portmaster-api'; import { BehaviorSubject, Observable, map } from 'rxjs'; import { share } from 'rxjs/operators'; import { SaveSettingEvent } from 'src/app/shared/config'; @Component({ selector: 'app-qs-history', templateUrl: './qs-history.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class QsHistoryComponent implements OnChanges { currentValue = false; historyFeatureAllowed: Observable = inject(SPNService).profile$.pipe( takeUntilDestroyed(), map((profile) => { return ( profile?.current_plan?.feature_ids?.includes(FeatureID.History) || false ); }), share({ connector: () => new BehaviorSubject(false) }) ); @Input() canUse: boolean = true; @Input() settings: Setting[] = []; @Output() save = new EventEmitter>(); ngOnChanges(changes: SimpleChanges): void { if ('settings' in changes) { const historySetting = this.settings.find( (s) => s.Key === 'history/enable' ) as BoolSetting | undefined; if (historySetting) { this.currentValue = getActualValue(historySetting); } } } updateHistoryEnabled(enabled: boolean) { this.save.next({ isDefault: false, key: 'history/enable', value: enabled, }); } } ================================================ FILE: desktop/angular/src/app/pages/app-view/qs-internet/index.ts ================================================ export * from './qs-internet'; ================================================ FILE: desktop/angular/src/app/pages/app-view/qs-internet/qs-internet.html ================================================
Block Connections Prompting
The following enabled settings may interfere:
  • {{ setting.Name }}
================================================ FILE: desktop/angular/src/app/pages/app-view/qs-internet/qs-internet.ts ================================================ import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from "@angular/core"; import { Setting, StringSetting, getActualValue } from "@safing/portmaster-api"; import { SaveSettingEvent } from "src/app/shared/config/generic-setting/generic-setting"; const interferingSettings = { 'permit': [ 'filter/blockInternet', 'filter/blockLAN', 'filter/blockLocal', 'filter/blockP2P', 'filter/blockInbound', 'filter/endpoints', ], 'block': [ 'filter/endpoints', ], } @Component({ selector: 'app-qs-internet', templateUrl: './qs-internet.html', changeDetection: ChangeDetectionStrategy.OnPush }) export class QuickSettingInternetButtonComponent implements OnChanges { @Input() settings: Setting[] = []; @Output() save = new EventEmitter(); currentValue = '' interferingSettings: Setting[] = []; ngOnChanges(changes: SimpleChanges): void { if ('settings' in changes) { this.currentValue = ''; const defaultActionSetting = this.settings.find(s => s.Key == 'filter/defaultAction') as (StringSetting | undefined); if (!!defaultActionSetting) { this.currentValue = getActualValue(defaultActionSetting); this.updateInterfering(); } } } updateUseInternet(blocked: boolean) { const newValue = blocked ? 'block' : 'permit'; this.save.next({ isDefault: false, key: 'filter/defaultAction', value: newValue, }) } private updateInterfering() { this.interferingSettings = []; if (this.currentValue !== 'permit' && this.currentValue !== 'block') { return; } // create a lookup map for setting key to setting const lm = new Map(); this.settings.forEach(s => lm.set(s.Key, s)) this.interferingSettings = interferingSettings[this.currentValue] .map(key => lm.get(key)) .filter(setting => { if (!setting) { return false; } const value = getActualValue(setting); if (Array.isArray(value)) { return value.length > 0; } return !!value; }) as Setting[]; } } ================================================ FILE: desktop/angular/src/app/pages/app-view/qs-select-exit/index.ts ================================================ export * from './qs-select-exit'; ================================================ FILE: desktop/angular/src/app/pages/app-view/qs-select-exit/qs-select-exit.html ================================================
SPN Exit Get Pro Automatic {{ option.Name }} Disabled
================================================ FILE: desktop/angular/src/app/pages/app-view/qs-select-exit/qs-select-exit.scss ================================================ ================================================ FILE: desktop/angular/src/app/pages/app-view/qs-select-exit/qs-select-exit.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, inject, } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { BoolSetting, StringArraySetting, CountrySelectionQuickSetting, ConfigService, Setting, getActualValue, } from '@safing/portmaster-api'; import { SaveSettingEvent } from 'src/app/shared/config/generic-setting/generic-setting'; @Component({ selector: 'app-qs-select-exit', templateUrl: './qs-select-exit.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class QuickSettingSelectExitButtonComponent implements OnInit, OnChanges { private destroyRef = inject(DestroyRef); @Input() canUse: boolean = true; @Input() settings: Setting[] = []; @Output() save = new EventEmitter(); spnEnabled: boolean | null = null; exitRuleSetting: StringArraySetting | null = null; selectedExitRules: string | undefined = undefined; availableExitRules: CountrySelectionQuickSetting[] | null = null; constructor( private configService: ConfigService, private cdr: ChangeDetectorRef ) {} updateExitRules(newExitRules: string) { this.selectedExitRules = newExitRules; let newConfigValue: string[] = []; if (!!newExitRules) { newConfigValue = newExitRules.split(','); } this.save.next({ isDefault: false, key: 'spn/exitHubPolicy', value: newConfigValue, }); } ngOnChanges(changes: SimpleChanges): void { if ('settings' in changes) { this.exitRuleSetting = null; this.selectedExitRules = undefined; const exitRuleSetting = this.settings.find( (s) => s.Key == 'spn/exitHubPolicy' ) as StringArraySetting | undefined; if (exitRuleSetting) { this.exitRuleSetting = exitRuleSetting; this.updateOptions(); } } } ngOnInit() { this.configService .watch('spn/enable') .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((value) => { this.spnEnabled = value; this.updateOptions(); }); } private updateOptions() { if (!this.exitRuleSetting) { this.selectedExitRules = undefined; this.availableExitRules = null; return; } if (!!this.exitRuleSetting.Value && this.exitRuleSetting.Value.length > 0) { this.selectedExitRules = this.exitRuleSetting.Value.join(','); } this.availableExitRules = this.getQuickSettings(); this.cdr.markForCheck(); } private getQuickSettings(): CountrySelectionQuickSetting[] { if (!this.exitRuleSetting) { return []; } let val = this.exitRuleSetting.Annotations[ 'safing/portbase:ui:quick-setting' ] as CountrySelectionQuickSetting[]; if (val === undefined) { return []; } if (!Array.isArray(val)) { return []; } return val; } } ================================================ FILE: desktop/angular/src/app/pages/app-view/qs-use-spn/index.ts ================================================ export * from './qs-use-spn'; ================================================ FILE: desktop/angular/src/app/pages/app-view/qs-use-spn/qs-use-spn.html ================================================
Use SPN Get Pro Disabled
The following enabled settings may interfere:
  • {{ setting.Name }}
================================================ FILE: desktop/angular/src/app/pages/app-view/qs-use-spn/qs-use-spn.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { BoolSetting, ConfigService, Setting, getActualValue } from "@safing/portmaster-api"; import { SaveSettingEvent } from "src/app/shared/config/generic-setting/generic-setting"; const interferingSettingsWhenOn = [ 'spn/usagePolicy' ] @Component({ selector: 'app-qs-use-spn', templateUrl: './qs-use-spn.html', changeDetection: ChangeDetectionStrategy.OnPush }) export class QuickSettingUseSPNButtonComponent implements OnInit, OnChanges { private destroyRef = inject(DestroyRef); @Input() canUse: boolean = true; @Input() settings: Setting[] = []; @Output() save = new EventEmitter(); currentValue = false interferingSettings: Setting[] = []; /* Whether or not the SPN is currently disabled. null means we don't know yet ... */ spnDisabled: boolean | null = null; constructor( private configService: ConfigService, private cdr: ChangeDetectorRef ) { } ngOnChanges(changes: SimpleChanges): void { if ('settings' in changes) { this.currentValue = false; const useSpnSetting = this.settings.find(s => s.Key === 'spn/use') as (BoolSetting | undefined); if (!!useSpnSetting) { this.currentValue = getActualValue(useSpnSetting); this.updateInterfering(); } } } updateUseSpn(allowed: boolean) { this.save.next({ isDefault: false, key: 'spn/use', value: allowed, }) } ngOnInit() { this.configService.watch('spn/enable') .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(value => { this.spnDisabled = !value; this.cdr.markForCheck(); this.updateInterfering(); }) } private updateInterfering() { this.interferingSettings = []; // only "enabled" state has interfering settings // only show if we already know if the SPN module is enabled if (!this.currentValue || this.spnDisabled !== false) { return } // create a lookup map for setting key to setting const lm = new Map(); this.settings.forEach(s => lm.set(s.Key, s)) this.interferingSettings = interferingSettingsWhenOn .map(key => lm.get(key)) .filter(setting => { if (!setting) { return false; } const value = getActualValue(setting); if (Array.isArray(value)) { return value.length > 0; } return !!value; }) as Setting[]; } } ================================================ FILE: desktop/angular/src/app/pages/dashboard/dashboard-widget/dashboard-widget.component.html ================================================ ================================================ FILE: desktop/angular/src/app/pages/dashboard/dashboard-widget/dashboard-widget.component.ts ================================================ import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { ChangeDetectionStrategy, Component, Input } from "@angular/core"; @Component({ selector: 'app-dashboard-widget', templateUrl: './dashboard-widget.component.html', styles: [ ` :host { @apply bg-gray-200 p-4 self-stretch rounded-md flex flex-col gap-2; } label { @apply text-xs uppercase text-secondary font-light flex flex-row items-center gap-2 pb-2; } ` ], changeDetection: ChangeDetectionStrategy.OnPush }) export class DashboardWidgetComponent { @Input() set beta(v: any) { this._beta = coerceBooleanProperty(v) } get beta() { return this._beta } private _beta = false; @Input() label: string = ''; } ================================================ FILE: desktop/angular/src/app/pages/dashboard/dashboard.component.html ================================================
{{ blockedConnections }}
{{ activeConnections }}
{{ activeProfiles }}
{{ dataIncoming | bytes }} Available in
Portmaster Plus
{{ dataOutgoing | bytes }} Available in
Portmaster Plus
{{ activeIdentities }} Available in
Portmaster Pro
  • {{ countryNames[country.key] || country.key || 'N/A' }}
    {{ country.value }}
No applications have been blocked in the last 10 minutes.
  • {{ profile.Name }}
    {{ entry.count }}
Available in Portmaster Plus Available in Portmaster Plus
News is only available if intel data updates are enabled
Just a second, we're loading the latest news...

{{ card.title }}

{{ progress.percent }}%
================================================ FILE: desktop/angular/src/app/pages/dashboard/dashboard.component.scss ================================================ :host { @apply flex flex-row w-full gap-3 p-4 overflow-auto; } .dashboard-grid { @apply grid gap-4; align-items: stretch; justify-items: stretch; grid-template-columns: 1fr 1fr 1fr 1fr; grid-template-areas: "header header header header" "feature feature feature feature" "feature feature feature feature" "stats stats news news" "stats stats news news" "charts charts charts charts" "charts charts charts charts" "blocked blocked countries countries" "map map map map" "bwvis-bar bwvis-bar bwvis-line bwvis-line"; } :host-context(.min-width-1024px) { .dashboard-grid { grid-template-areas: "header header header header" "feature feature feature news" "feature feature feature news" "stats stats stats news" "stats stats stats news" "charts charts charts charts" "countries countries map map" "blocked blocked map map" "bwvis-bar bwvis-bar bwvis-line bwvis-line"; } } #header { grid-area: header; } #features { grid-area: feature; } #stats { grid-area: stats; } #charts { grid-area: charts; } #countries { grid-area: countries; } #blocked { grid-area: blocked; } #connmap { grid-area: map; } #bwvis-bar { grid-area: bwvis-bar; } #bwvis-line { grid-area: bwvis-line; } #news { grid-area: news; } .auto-grid-3 { @apply grid gap-4; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } .auto-grid-4 { @apply grid gap-4; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } app-dashboard-widget { label { @apply text-xs uppercase text-secondary font-light flex flex-row items-center gap-2 pb-2; } .feature-card-list { @apply grid gap-3 w-full; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); } .mini-stats-list { @apply grid gap-3 w-full; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } &#news { h1 { @apply text-base; @apply font-light; } ::ng-deep markdown { @apply font-light; a { @apply underline text-blue; } strong { @apply font-medium; } } } } ::ng-deep #dashboard-map { #world-group { --map-bg: #111112; --map-country-active: #424141; --map-country-inactive: #2a2a2a; --map-country-border-width: 1px; --map-country-border-color: #1e1e1e; --map-country-border-color-selected: #858585; --map-country-blocked-primary: #858585; --map-country-blocked-secondary: #402323; path { fill: var(--map-country-active); stroke: var(--map-bg); stroke-width: var(--map-country-border-width); stroke-linejoin: round; } path.active { color: #1d3c24; fill: currentColor; } path.hover { color: #4fae4f; fill: currentColor; } } } .mini-stat { @apply flex flex-col items-center justify-center py-3 px-2 bg-gray-300 rounded shadow; label { @apply font-light uppercase text-xxs text-secondary -mb-2; } span { @apply text-xl text-blue; } } ================================================ FILE: desktop/angular/src/app/pages/dashboard/dashboard.component.ts ================================================ import { KeyValue } from "@angular/common"; import { AfterContentInit, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, ElementRef, OnInit, QueryList, TrackByFunction, ViewChild, ViewChildren, forwardRef, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { AppProfileService, BandwidthChartResult, ChartResult, Database, FeatureID, Netquery, PortapiService, SPNService, UserProfile, Verdict } from "@safing/portmaster-api"; import { SfngDialogService, SfngTabGroupComponent } from "@safing/ui"; import { Observable, catchError, filter, interval, map, repeat, retry, startWith, throwError } from "rxjs"; import { ActionIndicatorService } from 'src/app/shared/action-indicator'; import { DefaultBandwidthChartConfig, SfngNetqueryLineChartComponent } from "src/app/shared/netquery/line-chart/line-chart"; import { SPNAccountDetailsComponent } from "src/app/shared/spn-account-details"; import { MAP_HANDLER, MapRef } from "../spn/map-renderer"; import { CircularBarChartConfig, splitQueryResult } from "src/app/shared/netquery/circular-bar-chart/circular-bar-chart.component"; import { BytesPipe } from "src/app/shared/pipes/bytes.pipe"; import { HttpErrorResponse } from "@angular/common/http"; interface BlockedProfile { profileID: string; count: number; } interface BandwidthBarData { profile: string; profile_name: string; series: 'sent' | 'received'; value: number; sent: number; received: number; } interface NewsCard { title: string; body: string; url?: string; footer?: string; progress?: { percent: number; style: string; } } interface News { cards: NewsCard[]; } const newsResourceIdentifier = "intel/news.yaml" @Component({ selector: 'app-dashboard', changeDetection: ChangeDetectionStrategy.OnPush, styleUrls: ['./dashboard.component.scss'], templateUrl: './dashboard.component.html', providers: [ { provide: MAP_HANDLER, useExisting: forwardRef(() => DashboardPageComponent), multi: true }, ] }) export class DashboardPageComponent implements OnInit, AfterViewInit { @ViewChildren(SfngNetqueryLineChartComponent) lineCharts!: QueryList; @ViewChild(SfngTabGroupComponent) carouselTabGroup?: SfngTabGroupComponent; private readonly destroyRef = inject(DestroyRef); private readonly netquery = inject(Netquery); private readonly spn = inject(SPNService); private readonly actionIndicator = inject(ActionIndicatorService); private readonly cdr = inject(ChangeDetectorRef); private readonly dialog = inject(SfngDialogService); private readonly portapi = inject(PortapiService) resizeObserver!: ResizeObserver; blockedProfiles: BlockedProfile[] = [] connectionsPerCountry: { [country: string]: number } = {}; get countryNames(): { [country: string]: string } { return this.mapRef?.countryNames || {}; } bandwidthLineChart: BandwidthChartResult[] = []; bandwidthBarData: BandwidthBarData[] = []; readonly bandwidthBarConfig: CircularBarChartConfig = { stack: 'profile_name', seriesKey: 'series', seriesLabel: d => { if (d === 'sent') { return 'Bytes Sent' } return 'Bytes Received' }, value: 'value', ticks: 3, colorAsClass: true, series: { 'sent': { color: 'text-deepPurple-500 text-opacity-50', }, 'received': { color: 'text-cyan-800 text-opacity-50', } }, formatTick: (tick: number) => { return new BytesPipe().transform(tick, '1.0-0') }, formatValue: (stack, series, value, data) => { const bytes = new BytesPipe().transform return `${stack}\nSent: ${bytes(data?.sent)}\nReceived: ${bytes(data?.received)}` }, formatStack: (sel, data) => { const bytes = new BytesPipe().transform return sel .call(sel => { sel.append("text") .attr("dy", "0") .attr("y", "0") .text(d => d) }) .call(sel => { sel.append("text") .attr("y", 0) .attr("dy", "0.8rem") .style("font-size", "0.6rem") .text(d => { const first = data.find(result => result.profile_name === d); return `${bytes(first?.sent)} / ${bytes(first?.received)}` }) }) } } bwChartConfig = DefaultBandwidthChartConfig; activeConnections: number = 0; blockedConnections: number = 0; activeProfiles: number = 0; activeIdentities = 0; dataIncoming = 0; dataOutgoing = 0; connectionChart: ChartResult[] = []; tunneldConnectionChart: ChartResult[] = []; countriesPerProfile: { [profile: string]: string[] } = {} profile: UserProfile | null = null; featureBw = false; featureSPN = false; hoveredCard: NewsCard | null = null; features$ = this.spn.watchEnabledFeatures() .pipe(takeUntilDestroyed()); trackCountry: TrackByFunction> = (_, ctr) => ctr.key; trackApp: TrackByFunction = (_, bp) => bp.profileID; data: any; news?: News | 'pending' = 'pending'; private mapRef: MapRef | null = null; registerMap(ref: MapRef): void { this.mapRef = ref; this.mapRef.onMapReady(() => { this.updateMapCountries(); }) } private updateMapCountries() { // this check is basically to make typescript happy ... if (!this.mapRef) { return; } this.mapRef.worldGroup .selectAll('path') .classed('active', (d: any) => { return !!this.connectionsPerCountry[d.properties.iso_a2]; }); } unregisterMap(ref: MapRef): void { this.mapRef = null; } onCarouselTabHover(card: NewsCard | null) { this.hoveredCard = card; } openAccountDetails() { this.dialog.create(SPNAccountDetailsComponent, { autoclose: true, backdrop: 'light' }) } onCountryHover(code: string | null) { if (!this.mapRef) { return } this.mapRef.worldGroup .selectAll('path') .classed('hover', (d: any) => { return (d.properties.iso_a2 === code); }); } onProfileHover(profile: string | null) { if (!this.mapRef) { return } this.mapRef.worldGroup .selectAll('path') .classed('hover', (d: any) => { if (!profile) { return false; } return this.countriesPerProfile[profile]?.includes(d.properties.iso_a2); }); } ngAfterViewInit(): void { interval(15000) .pipe( takeUntilDestroyed(this.destroyRef), startWith(-1), filter(() => this.hoveredCard === null) ) .subscribe(() => { if (!this.carouselTabGroup) { return } let next = this.carouselTabGroup.activeTabIndex + 1 if (next >= this.carouselTabGroup.tabs!.length) { next = 0 } this.carouselTabGroup.activateTab(next, "left") }) } async ngOnInit() { this.portapi.getResource(newsResourceIdentifier) .pipe( repeat({ delay: 60000 }), takeUntilDestroyed(this.destroyRef) ) .subscribe({ next: response => { this.news = response; this.cdr.markForCheck(); }, error: () => { this.news = undefined; this.cdr.markForCheck(); } }); this.netquery .batch({ bwBarChart: { query: { internal: { $eq: false }, }, select: [ 'profile', 'profile_name', { $sum: { field: 'bytes_sent', as: 'sent' } }, { $sum: { field: 'bytes_received', as: 'received' } }, ], groupBy: ['profile', 'profile_name'], }, profileCount: { select: [ 'profile', { $count: { field: '*', as: 'totalCount' } } ], query: { verdict: { $in: [Verdict.Block, Verdict.Drop] } }, groupBy: ['profile'], databases: [Database.Live] }, countryStats: { select: [ 'country', { $count: { field: '*', as: 'totalCount' } }, { $sum: { field: 'bytes_sent', as: 'bwout' } }, { $sum: { field: 'bytes_received', as: 'bwin' } }, ], query: { allowed: { $eq: true }, }, groupBy: ['country'], databases: [Database.Live] }, perCountryConns: { select: ['profile', 'country', 'active', { $count: { field: '*', as: 'totalCount' } }], query: { allowed: { $eq: true }, }, groupBy: ['profile', 'country', 'active'], databases: [Database.Live], }, exitNodes: { query: { tunneled: { $eq: true }, exit_node: { $ne: "" } }, groupBy: ['exit_node'], select: [ 'exit_node', { $count: { field: '*', as: 'totalCount' } } ], databases: [Database.Live], } }) .pipe( repeat({ delay: 10000 }), takeUntilDestroyed(this.destroyRef), ) .subscribe(response => { // bandwidth bar chart if (response?.bwBarChart){ const barChartData = response.bwBarChart .filter(value => (value.sent + value.received) > 0) .sort((a, b) => (b.sent + b.received) - (a.sent + a.received)) .slice(0, 10); this.bandwidthBarData = splitQueryResult(barChartData, ['sent', 'received']) as BandwidthBarData[] } // profileCount this.blockedConnections = 0; this.blockedProfiles = []; response.profileCount?.forEach(row => { this.blockedConnections += row.totalCount; this.blockedProfiles.push({ profileID: row.profile!, count: row.totalCount }) }); // countryStats this.connectionsPerCountry = {}; this.dataIncoming = 0; this.dataOutgoing = 0; response.countryStats?.forEach(row => { this.dataIncoming += row.bwin; this.dataOutgoing += row.bwout; if (row.country === '') { return } this.connectionsPerCountry[row.country!] = row.totalCount || 0; }) this.updateMapCountries() // perCountryConns let profiles = new Set(); this.activeConnections = 0; this.countriesPerProfile = {}; response.perCountryConns?.forEach(row => { profiles.add(row.profile!); if (row.active) { this.activeConnections += row.totalCount; } const arr = (this.countriesPerProfile[row.profile!] || []); arr.push(row.country!) this.countriesPerProfile[row.profile!] = arr; }); this.activeProfiles = profiles.size; // exitNodes this.activeIdentities = response.exitNodes?.length || 0; this.cdr.markForCheck(); }) // Charts this.netquery .activeConnectionChart({}) .pipe( repeat({ delay: 10000 }), takeUntilDestroyed(this.destroyRef), ) .subscribe(result => { this.connectionChart = result; this.cdr.markForCheck(); }) this.netquery .bandwidthChart({}, undefined, 60) .pipe( repeat({ delay: 10000 }), takeUntilDestroyed(this.destroyRef), ) .subscribe(bw => { this.bandwidthLineChart = bw; this.cdr.markForCheck(); }) this.netquery .activeConnectionChart({ tunneled: { $eq: true } }) .pipe( repeat({ delay: 10000 }), takeUntilDestroyed(this.destroyRef), ) .subscribe(result => { this.tunneldConnectionChart = result; this.cdr.markForCheck(); }) // SPN profile and enabled/allowed features this.spn .profile$ .pipe( takeUntilDestroyed(this.destroyRef) ) .subscribe({ next: (profile) => { this.profile = profile || null; this.featureBw = profile?.current_plan?.feature_ids?.includes(FeatureID.Bandwidth) || false; this.featureSPN = profile?.current_plan?.feature_ids?.includes(FeatureID.SPN) || false; // force a full change-detection cylce now! this.cdr.detectChanges() // force re-draw of the charts after change-detection because the // width may change now. this.lineCharts?.forEach(chart => chart.redraw()) this.cdr.markForCheck(); }, }) } /** Logs the user out of the SPN completely by purgin the user profile from the local storage */ logoutCompletely(_: Event) { this.spn.logout(true) .subscribe(this.actionIndicator.httpObserver( 'Logout', 'You have been logged out of the SPN completely.' )) } } ================================================ FILE: desktop/angular/src/app/pages/dashboard/feature-card/feature-card.component.html ================================================
{{ feature?.Name }}
BETA
{{ (disabled ? 'Available in ' : '') + 'Portmaster ' + feature?.InPackage?.Name}} {{ comingSoon ? ' - coming soon' : '' }} {{ feature?.Comment }}
Active
{{ feature?.InPackage?.Name }}
================================================ FILE: desktop/angular/src/app/pages/dashboard/feature-card/feature-card.component.scss ================================================ .feature-card { @apply flex flex-col p-4 bg-gray-300 rounded shadow w-full relative gap-2 overflow-hidden; .disabled-bg { @apply absolute top-0 left-0 text-gray-500 opacity-50; } &.disabled { @apply opacity-80 shadow-inner; } &.clickable { @apply cursor-pointer; &:hover { @apply bg-gray-400; } } header { @apply flex flex-row items-center justify-start gap-2 w-full; img { @apply w-5 h-5; filter: invert(1); } &>span { @apply text-base font-light; } } } .ribbon { width: 90px; height: 100%; overflow: hidden; position: absolute; top: 0px; right: 0px; z-index: 100; } .ribbon__content { left: -7px; top: 25px; -webkit-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); position: absolute; display: block; width: 125px; padding: 2px 0; text-shadow: 0 1px 0px rgba(0, 0, 0, .2); text-transform: uppercase; text-align: center; border: 2px dotted #fff; outline-color: #fff; outline-width: 1px; outline-style: solid; } ================================================ FILE: desktop/angular/src/app/pages/dashboard/feature-card/feature-card.component.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, SimpleChanges, inject } from '@angular/core'; import { Router } from '@angular/router'; import { BoolSetting, ConfigService, Feature } from '@safing/portmaster-api'; import { Subscription } from 'rxjs'; import { INTEGRATION_SERVICE } from 'src/app/integration'; @Component({ selector: 'app-feature-card', templateUrl: './feature-card.component.html', styleUrls: ['./feature-card.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class FeatureCardComponent implements OnChanges, OnDestroy { private readonly cdr = inject(ChangeDetectorRef); private readonly configService = inject(ConfigService); private readonly router = inject(Router); private readonly integration = inject(INTEGRATION_SERVICE); private configValueSubscription = Subscription.EMPTY; @Input() set disabled(v: any) { this._disabled = coerceBooleanProperty(v) } get disabled() { return this._disabled } _disabled = false; get comingSoon() { return this.feature?.ComingSoon || false } @Input() feature?: Feature; planColor: string | null = null; configValue: boolean | undefined = undefined; ngOnChanges(changes: SimpleChanges): void { if ('feature' in changes) { this.configValueSubscription.unsubscribe(); this.configValueSubscription = Subscription.EMPTY; if (!!this.feature?.ConfigKey) { this.configValueSubscription = this.configService.watch(this.feature!.ConfigKey) .subscribe(value => { this.configValue = value; this.cdr.markForCheck(); }); } if (this.feature?.InPackage?.HexColor) { this.planColor = getContrastFontColor(this.feature.InPackage.HexColor); // console.log(this.feature.InPackage.HexColor, this.planColor) this.cdr.markForCheck(); } } } ngOnDestroy() { this.configValueSubscription.unsubscribe(); } updateSettingsValue(newValue: boolean) { this.configService.save(this.feature!.ConfigKey, newValue) .subscribe() } navigateToConfigScope() { if (this.disabled) { this.integration.openExternal("https://safing.io/pricing?source=Portmaster") return; } let key: string | undefined; if (this.feature?.ConfigScope) { key = 'config:' + this.feature?.ConfigScope; } else { key = this.feature?.ConfigKey; } if (!key) { return } this.router.navigate(['/settings'], { queryParams: { setting: key, } }) } } function parseColor(input: string): number[] { if (input.substr(0, 1) === '#') { const collen = (input.length - 1) / 3; const fact = [17, 1, 0.062272][collen - 1]; return [ Math.round(parseInt(input.substr(1, collen), 16) * fact), Math.round(parseInt(input.substr(1 + collen, collen), 16) * fact), Math.round(parseInt(input.substr(1 + 2 * collen, collen), 16) * fact), ]; } return input .split('(')[1] .split(')')[0] .split(',') .map((x) => +x); } function getContrastFontColor(bgColor: string): string { // if (red*0.299 + green*0.587 + blue*0.114) > 186 use #000000 else use #ffffff // based on https://stackoverflow.com/a/3943023 let col = bgColor; if (bgColor.startsWith('#') && bgColor.length > 7) { col = bgColor.slice(0, 7); } const [r, g, b] = parseColor(col); if (r * 0.299 + g * 0.587 + b * 0.114 > 186) { return '#000000'; } return '#ffffff'; } ================================================ FILE: desktop/angular/src/app/pages/monitor/index.ts ================================================ export { MonitorPageComponent } from './monitor'; ================================================ FILE: desktop/angular/src/app/pages/monitor/monitor.html ================================================

Network Activity

Network history data available as of {{ data.first | date }}. ({{ data.count }} connections) Clear No network history data available. Enable Available in Portmaster Plus Use the search bar and drop downs to search and filter the last 10 minutes of network traffic. Optionally, search all network history data if enabled.
================================================ FILE: desktop/angular/src/app/pages/monitor/monitor.scss ================================================ :host { overflow: hidden; flex-direction: row; flex-grow: 1; width: 100%; overflow: hidden; display: flex; flex-direction: column; padding-left: 1.7rem; padding-right: 0.8rem; .header, .content { padding: 0; margin: 0; } .header { padding-top: 0.9rem; .breadcrumbs { font-size: 0.715rem; font-weight: 500; color: #cacaca; user-select: none; display: flex; span:first-child { opacity: .55; font-weight: 400; margin-right: 4px; &:hover { opacity: 1; } } svg.arrow { width: 1rem; padding: 0; margin: 0; .inner { stroke: white; } } } } } ================================================ FILE: desktop/angular/src/app/pages/monitor/monitor.ts ================================================ import { Component, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { BoolSetting, ConfigService, Database, FeatureID, Netquery, SPNService } from '@safing/portmaster-api'; import { Subject, interval, map, merge, repeat } from 'rxjs'; import { SessionDataService } from 'src/app/services'; import { ActionIndicatorService } from 'src/app/shared/action-indicator'; import { fadeInAnimation, moveInOutListAnimation } from 'src/app/shared/animations'; @Component({ templateUrl: './monitor.html', styleUrls: ['../page.scss', './monitor.scss'], providers: [], animations: [fadeInAnimation, moveInOutListAnimation], }) export class MonitorPageComponent { session = inject(SessionDataService); netquery = inject(Netquery); reload = new Subject(); configService = inject(ConfigService); uai = inject(ActionIndicatorService); historyEnabled = inject(ConfigService) .watch('history/enable'); canUseHistory = inject(SPNService).profile$ .pipe( map(profile => { return profile?.current_plan?.feature_ids?.includes(FeatureID.History) || false; }) ); history = inject(Netquery) .query({ select: [ { $min: { field: "started", as: "first_connection", }, }, { $count: { field: "*", as: "totalCount" } } ], databases: [Database.History] }, 'monitor-get-first-history-connection') .pipe( repeat({ delay: () => merge(interval(10000), this.reload) }), map(result => { if (!result.length || result[0].totalCount === 0) { return null } return { first: new Date(result[0].first_connection), count: result[0].totalCount, } }), takeUntilDestroyed() ); enableHistory() { this.configService.save('history/enable', true) .subscribe(); } clearHistoryData() { this.netquery.cleanProfileHistory([]) .subscribe(() => { this.reload.next(); }) } } ================================================ FILE: desktop/angular/src/app/pages/page.scss ================================================ :host { display : flex; flex-direction: column; width : 100%; height : 100%; } ================================================ FILE: desktop/angular/src/app/pages/settings/settings.html ================================================

Global Settings

================================================ FILE: desktop/angular/src/app/pages/settings/settings.scss ================================================ .header-title { display: flex; width: 100%; padding-left: 3rem; padding-right: 1.25rem; margin-bottom: 0.5rem; align-items: center; height: 3rem; flex-shrink: 0; h1{ flex-grow: unset; } fa-icon[icon*="question-circle"]{ margin-left: 0.35rem; } } .card-title.meta { div { display: inline-block; @apply mr-2; } } .columns { width : 100%; display : flex; flex-direction: row; } .meta { span:first-of-type { @apply text-secondary; @apply mr-1; } } .col { flex-grow: 1; } .unstable { @apply text-xs; @apply uppercase; color: theme('colors.info.yellow'); } sfng-accordion-group { @apply pl-12; @apply pr-4; // align with the scroll bar on the right side @apply my-4; } div.tableFixHead { @apply mt-4; @apply rounded-t; &:not(.empty) { @apply rounded; } max-height: 16rem; } .cdk-row.unused { opacity: 0.4; } .card-actions { display : flex; align-items: center; * { @apply ml-2; } app-menu-trigger { display: inline-block; } } ================================================ FILE: desktop/angular/src/app/pages/settings/settings.ts ================================================ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { ConfigService, Setting } from '@safing/portmaster-api'; import { Subscription } from 'rxjs'; import { StatusService, VersionStatus } from 'src/app/services'; import { ActionIndicatorService } from 'src/app/shared/action-indicator'; import { fadeInAnimation } from 'src/app/shared/animations'; import { SaveSettingEvent } from 'src/app/shared/config/generic-setting/generic-setting'; @Component({ templateUrl: './settings.html', styleUrls: [ '../page.scss', './settings.scss' ], animations: [fadeInAnimation] }) export class SettingsComponent implements OnInit, OnDestroy { /** @private The current search term for the settings. */ searchTerm: string = ''; /** @private All settings currently displayed. */ settings: Setting[] = []; /** @private The available and selected resource versions. */ versions: VersionStatus | null = null; /** * @private * The key of the setting to highligh, if any ... */ highlightSettingKey: string | null = null; /** Subscription to watch all available settings. */ private subscription = Subscription.EMPTY; constructor( public configService: ConfigService, public statusService: StatusService, private actionIndicator: ActionIndicatorService, private route: ActivatedRoute, ) { } ngOnInit(): void { this.subscription = new Subscription(); this.loadSettings(); // Request the current resource versions once. We add // it to the subscription to prevent a memory leak in // case the user leaves the page before the versions // have been loaded. const versionSub = this.statusService.getVersions() .subscribe(version => this.versions = version); this.subscription.add(versionSub); const querySub = this.route.queryParamMap .subscribe( params => { this.highlightSettingKey = params.get('setting'); } ) this.subscription.add(querySub); } ngOnDestroy() { this.subscription.unsubscribe(); } /** * Loads all settings from the portmaster. */ private loadSettings() { const configSub = this.configService.query('') .subscribe(settings => this.settings = settings); this.subscription.add(configSub); } /** * @private * SaveSettingEvent is emitted by the settings-view * component when a value has been changed and should be saved. * * @param event The save-settings event */ saveSetting(event: SaveSettingEvent) { let idx = this.settings.findIndex(setting => setting.Key === event.key); if (idx < 0) { return; } const setting = { ...this.settings[idx], } if (event.isDefault) { delete (setting['Value']); } else { setting.Value = event.value; } this.configService.save(setting) .subscribe({ next: () => { if (!!event.accepted) { event.accepted(); } this.settings[idx] = setting; // copy the settings into a new array so we trigger // an input update due to changed array identity. this.settings = [...this.settings]; // for the release level setting we need to // to a page-reload since portmaster will now // return more settings. if (setting.Key === 'core/releaseLevel') { this.loadSettings(); } }, error: err => { if (!!event.rejected) { event.rejected(err); } this.actionIndicator.error('Failed to save setting', err); console.error(err); } }) } } ================================================ FILE: desktop/angular/src/app/pages/spn/country-details/country-details.html ================================================

{{ countryName }}

Total Nodes {{ totalAliveCount }}
by Safing {{ safingNodeCount }}
by Community {{ communityNodeCount }}
Exit Nodes {{ exitNodeCount }}
by Safing {{ safingExitNodeCount }}
by Community {{ communityExitNodeCount }}
Nodes In Use {{ activeNodeCount }}
by Safing {{ activeSafingNodeCount }}
by Community {{ activeCommunityNodeCount }}
The following Apps have connections that are routed through the SPN and use an exit node in {{ countryName }} ({{ countryCode }}):
{{ app.profile.Name }} {{ app.count }} connections
================================================ FILE: desktop/angular/src/app/pages/spn/country-details/country-details.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Inject, Input, OnChanges, OnDestroy, OnInit, Optional, Output, SimpleChanges, TrackByFunction } from "@angular/core"; import { AppProfile, AppProfileService, Netquery } from '@safing/portmaster-api'; import { SFNG_DIALOG_REF, SfngDialogRef, SfngDialogService } from "@safing/ui"; import { Subscription, forkJoin, of, switchMap } from 'rxjs'; import { repeat } from 'rxjs/operators'; import { MapPin, MapService } from './../map.service'; import { PinDetailsComponent } from './../pin-details/pin-details'; @Component({ templateUrl: './country-details.html', changeDetection: ChangeDetectionStrategy.OnPush, styles: [ `:host{ display: block; min-width: 630px; height: 400px; overflow: hidden; }` ] }) export class CountryDetailsComponent implements OnInit, OnChanges, OnDestroy { /** Subscription to poll map pins and profiles. */ private subscription = Subscription.EMPTY; /** The two letter ISO country code */ @Input() countryCode: string = ''; /** The name of the country */ @Input() countryName: string = ''; /** Emits the ID of the pin that is hovered in the list. null if no pin is hovered */ @Output() pinHover = new EventEmitter(); /** @private - The list of pins available in this country */ pins: MapPin[] = []; /** @private - A list of app profiles that use this country as an exit node */ profiles: { profile: AppProfile, count: number }[] = []; /** @private - A {@link TrackByFunction} for all profiles that use this country for exit */ trackProfile: TrackByFunction = (_: number, profile: this['profiles'][0]) => `${profile.profile.Source}/${profile.profile.ID}`; /** The number of alive nodes in this country */ totalAliveCount = 0; /** The number of exit nodes in this country */ exitNodeCount = 0; /** The number of active (used) nodes in this country */ activeNodeCount = 0; /** The number of active (used) nodes operated by safing */ activeSafingNodeCount = 0; /** The number of active (used) nodes operated by the community */ activeCommunityNodeCount = 0; /** The number of nodes operated by safing */ safingNodeCount = 0; /** The number of exit nodes operated by safing */ safingExitNodeCount = 0; /** The number of nodes operated by a community member */ communityNodeCount = 0; /** The number of exit ndoes operated by the community */ communityExitNodeCount = 0; /** holds the text format of a netquery search to show all connections that exit in this country */ filterConnectionsByCountryNodes = ''; constructor( private mapService: MapService, private netquery: Netquery, private appService: AppProfileService, private cdr: ChangeDetectorRef, private dialog: SfngDialogService, @Inject(SFNG_DIALOG_REF) @Optional() public dialogRef?: SfngDialogRef, ) { } openPinDetails(id: string) { this.dialog.create(PinDetailsComponent, { data: id, backdrop: false, autoclose: true, dragable: true, }) } ngOnInit() { // if we got opened as a dialog we get the code and name of the country // from the dialogRef.data field. if (!!this.dialogRef) { this.countryCode = this.dialogRef.data.code; this.countryName = this.dialogRef.data.name; } this.subscription.unsubscribe(); this.subscription = this.mapService .pins$ .pipe( switchMap(pins => { // get a list of pins in that country const countryPins = pins.filter(pin => pin.entity.Country === this.countryCode); // prepare a netquery query that loads the IDs of all profiles that use one of the countries // pins as an exit node. Then, map those IDs to the actual app profile object const profiles = this.netquery .query({ select: [ 'profile', { $count: { field: '*', as: 'totalCount' } } ], groupBy: ['profile'], query: { 'exit_node': { $in: countryPins.map(pin => pin.pin.ID), } } }, 'get-connections-per-profile-in-country') .pipe( switchMap(queryResult => { if (queryResult.length === 0) { return of([]); } return forkJoin( queryResult.map(row => forkJoin({ profile: this.appService.getAppProfile(row.profile!), count: of(row.totalCount), }) ) ) }), ); return forkJoin({ pins: of(countryPins), profiles: profiles, }) } ), repeat({ delay: 5000 }), ) .subscribe(result => { this.pins = result.pins; this.profiles = result.profiles this.activeNodeCount = 0; this.activeCommunityNodeCount = 0; this.activeSafingNodeCount = 0; this.exitNodeCount = 0; this.safingNodeCount = 0; this.communityNodeCount = 0; this.safingExitNodeCount = 0; this.communityExitNodeCount = 0; this.pins.forEach(pin => { if (pin.isOffline) { return } this.totalAliveCount++; if (pin.pin.VerifiedOwner === 'Safing') { this.safingNodeCount++; if (pin.isExit) { this.exitNodeCount++; this.safingExitNodeCount++; } if (pin.isActive) { this.activeSafingNodeCount++; this.activeNodeCount++; } } else { this.communityNodeCount++; if (pin.isExit) { this.exitNodeCount++; this.communityExitNodeCount++; } if (pin.isActive) { this.activeCommunityNodeCount++; this.activeNodeCount++; } } }) // create a netquery text-query in the format of "exit_node: exit_node: ..." this.filterConnectionsByCountryNodes = this.pins.map(pin => `exit_node:${pin.pin.ID}`).join(" ") this.cdr.markForCheck(); }) } ngOnChanges(changes: SimpleChanges): void { // if we are rendered as a regular component (not as a dialog) we need to // handle updates to our @Inputs(). // just let ngOnInit() do it's thing if the countryCode changed. if (!!changes['countryCode']) { this.ngOnInit(); } } ngOnDestroy() { this.subscription.unsubscribe(); } } ================================================ FILE: desktop/angular/src/app/pages/spn/country-details/index.ts ================================================ export * from './country-details'; ================================================ FILE: desktop/angular/src/app/pages/spn/country-overlay/country-overlay.html ================================================ {{ countryName }} Safing Nodes: {{ safingNodes.length }} Community Nodes: {{ communityNodes.length }} Click country for details ================================================ FILE: desktop/angular/src/app/pages/spn/country-overlay/country-overlay.scss ================================================ :host { @apply flex flex-row items-center justify-center; } .country-content-wrapper { @apply flex flex-col gap-2 items-center justify-center bg-gray-200 border bg-opacity-50 border-gray-600 border-opacity-25; } .country-name { @apply text-sm flex flex-row gap-1 items-center justify-center bg-gray-100 bg-opacity-50 py-2 w-full; } .country-stats { @apply flex flex-col gap-2 items-start py-2 px-4; &>span { @apply flex flex-row gap-1 items-center; @apply text-xs font-light; } .count { @apply text-sm font-normal; } } .country-stats--safing { svg polygon { fill: #0376bb; stroke: #0376bb; transform: scale(1.15) } } .country-stats--community { svg circle { fill: #239215; stroke: #239215; transform: scale(1.15) } } ================================================ FILE: desktop/angular/src/app/pages/spn/country-overlay/country-overlay.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; import { BehaviorSubject, combineLatest, map } from 'rxjs'; import { takeWhile } from 'rxjs/operators'; import { MapPin, MapService } from './../map.service'; @Component({ // eslint-disable-next-line @angular-eslint/component-selector selector: 'spn-map-country-overlay', templateUrl: './country-overlay.html', changeDetection: ChangeDetectionStrategy.OnPush, styleUrls: [ './country-overlay.scss' ] }) export class CountryOverlayComponent implements OnInit, OnChanges, OnDestroy { /** The two-letter ISO code of the country */ @Input() countryCode!: string; /** The (english) name of the country */ @Input() countryName!: string; /** all nodes in this country operated by Safing */ safingNodes: MapPin[] = []; /** all nodes in this country operated by a community member */ communityNodes: MapPin[] = []; /** used to trigger a reload onChanges */ private reload$ = new BehaviorSubject(undefined); constructor( private mapService: MapService, private cdr: ChangeDetectorRef, ) { } ngOnChanges(changes: SimpleChanges): void { this.reload$.next(); } ngOnInit(): void { combineLatest([ this.mapService.pins$, this.reload$ ]) .pipe( takeWhile(() => !this.reload$.closed), map(([pins]) => pins.filter(pin => pin.entity.Country === this.countryCode)), ) .subscribe(pinsInCountry => { this.safingNodes = []; this.communityNodes = []; pinsInCountry.forEach(pin => { if (pin.isOffline && !pin.isActive) { return } if (pin.pin.VerifiedOwner === 'Safing') { this.safingNodes.push(pin) } else { this.communityNodes.push(pin) } }) this.cdr.markForCheck(); }) } ngOnDestroy(): void { this.reload$.complete(); } } ================================================ FILE: desktop/angular/src/app/pages/spn/country-overlay/index.ts ================================================ export * from './country-overlay'; ================================================ FILE: desktop/angular/src/app/pages/spn/index.ts ================================================ export * from './spn-page'; ================================================ FILE: desktop/angular/src/app/pages/spn/map-legend/index.ts ================================================ export * from './map-legend'; ================================================ FILE: desktop/angular/src/app/pages/spn/map-legend/map-legend.html ================================================
Safing Nodes {{ safingNodeCount }}
used as Transit {{ safingActiveCount }}
used as Exit {{ safingExitCount }}
Community Nodes {{ communityNodeCount }}
used as Transit {{ communityActiveCount }}
used as Exit {{ communityExitCount }}
================================================ FILE: desktop/angular/src/app/pages/spn/map-legend/map-legend.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core"; import { Subscription } from 'rxjs'; import { MapService } from './../map.service'; @Component({ // eslint-disable-next-line @angular-eslint/component-selector selector: 'spn-map-legend', templateUrl: './map-legend.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class SpnMapLegendComponent implements OnInit, OnDestroy { private subscription = Subscription.EMPTY; safingNodeCount = 0; safingExitCount = 0; safingActiveCount = 0; communityNodeCount = 0; communityExitCount = 0; communityActiveCount = 0; constructor( private mapService: MapService, private cdr: ChangeDetectorRef, ) { } ngOnInit() { this.subscription = this.mapService .pins$ .subscribe(pins => { this.safingActiveCount = 0; this.safingExitCount = 0; this.safingNodeCount = 0; this.communityActiveCount = 0; this.communityExitCount = 0; this.communityNodeCount = 0; pins.forEach(pin => { if (pin.pin.VerifiedOwner === 'Safing') { if (pin.isActive) { this.safingActiveCount++; } if (pin.isExit) { this.safingExitCount++ } this.safingNodeCount++ } else { if (pin.isActive) { this.communityActiveCount++; } if (pin.isExit) { this.communityExitCount++; } this.communityNodeCount++; } }) this.cdr.markForCheck(); }) } ngOnDestroy() { this.subscription.unsubscribe(); } } ================================================ FILE: desktop/angular/src/app/pages/spn/map-renderer/index.ts ================================================ export * from './map-renderer'; ================================================ FILE: desktop/angular/src/app/pages/spn/map-renderer/map-renderer.ts ================================================ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, ElementRef, Inject, InjectionToken, Input, OnDestroy, OnInit, Optional, inject } from '@angular/core'; import { GeoPath, GeoPermissibleObjects, GeoProjection, Selection, ZoomTransform, geoMercator, geoPath, json, pointer, select, zoom, zoomIdentity } from 'd3'; import { feature } from 'topojson-client'; export type MapRoot = Selection; export type WorldGroup = Selection export interface CountryEvent { event?: MouseEvent; countryCode: string; countryName: string; } export interface MapRef { onMapReady(cb: () => any): void; onZoomPan(cb: () => any): void; onCountryHover(cb: (_: CountryEvent | null) => void): void; onCountryClick(cb: (_: CountryEvent) => void): void; select(selection: string): Selection | null; countryNames: { [key: string]: string }; root: MapRoot; projection: GeoProjection; zoomScale: number; worldGroup: WorldGroup; } export interface MapHandler { registerMap(ref: MapRef): void; unregisterMap(ref: MapRef): void; } export const MAP_HANDLER = new InjectionToken('MAP_HANDLER'); @Component({ // eslint-disable-next-line @angular-eslint/component-selector selector: 'spn-map-renderer', changeDetection: ChangeDetectionStrategy.OnPush, template: '', styleUrls: [ './map-style.scss' ], }) export class MapRendererComponent implements OnInit, AfterViewInit, OnDestroy { static readonly Rotate = 0; // so [-0, 0] is the initial center of the projection static readonly Maxlat = 83; // clip northern and southern pols (infinite in mercator) static readonly MarkerSize = 4; static readonly LineAnimationDuration = 200; private readonly destroyRef = inject(DestroyRef); private destroyed = false; countryNames: { [countryCode: string]: string } = {} // SVG group elements private svg: MapRoot | null = null; worldGroup!: WorldGroup; // Projection and line rendering functions projection!: GeoProjection; zoomScale: number = 1 private pathFunc!: GeoPath; get root() { return this.svg! } @Input() mapId: string = 'map' constructor( private mapRoot: ElementRef, private cdr: ChangeDetectorRef, @Inject(MAP_HANDLER) @Optional() private overlays: MapHandler[], ) { } ngOnInit(): void { this.overlays?.forEach(ov => { ov.registerMap(this) }) this.cdr.detach() } select(selector: string) { if (!this.svg) { return null } return this.svg.select(selector); } private _readyCb: (() => void)[] = []; onMapReady(cb: () => void) { this._readyCb.push(cb); } private _zoomCb: (() => void)[] = []; onZoomPan(cb: () => void) { this._zoomCb.push(cb) } private _countryHoverCb: ((e: CountryEvent | null) => void)[] = []; onCountryHover(cb: (e: CountryEvent | null) => void) { this._countryHoverCb.push(cb); } private _countryClickCb: ((e: CountryEvent) => void)[] = []; onCountryClick(cb: (e: CountryEvent) => void) { this._countryClickCb.push(cb) } async ngAfterViewInit() { await this.renderMap() const observer = new ResizeObserver(() => { this.renderMap() }) this.destroyRef.onDestroy(() => { observer.unobserve(this.mapRoot.nativeElement) observer.disconnect() }) observer.observe(this.mapRoot.nativeElement); } async renderMap() { if (this.destroyed) { return; } if (!!this.svg) { this.svg.remove() } const map = select(this.mapRoot.nativeElement); // setup the basic SVG elements this.svg = map .append('svg') .attr('id', this.mapId) .attr("xmlns", "http://www.w3.org/2000/svg") .attr('width', '100%') .attr('preserveAspectRation', 'none') .attr('height', '100%') this.worldGroup = this.svg.append('g').attr('id', 'world-group') // load the world-map data and start rendering const world = await json('/assets/world-50m.json'); // actually render the countries const countries = (feature(world, world.objects.countries) as any); this.setupProjection(); await this.setupZoom(countries); // we need to await the initial world render here because otherwise // the initial renderPins() will not be able to update the country attributes // and cause a delay before the state of the country (has-nodes, is-blocked, ...) // is visible. this.renderWorld(countries); this._readyCb.forEach(cb => cb()); } ngOnDestroy() { this.destroyed = true; this.overlays?.forEach(ov => ov.unregisterMap(this)); this._countryClickCb = []; this._countryHoverCb = []; this._readyCb = []; this._zoomCb = []; if (!this.svg) { return; } this.svg.remove(); this.svg = null; } private renderWorld(countries: any) { // actually render the countries const data = countries.features; const self = this; data.forEach((country: any) => { this.countryNames[country.properties.iso_a2] = country.properties.name }) // Add special country values. this.countryNames["__"] = "Anycast" this.worldGroup.selectAll() .data(data) .enter() .append('path') .attr('countryCode', (d: any) => d.properties.iso_a2) .attr('name', (d: any) => d.properties.name) .attr('d', this.pathFunc) .on('mouseenter', function (event: MouseEvent) { const country = select(this).datum() as any; const countryEvent: CountryEvent = { event: event, countryCode: country.properties.iso_a2, countryName: country.properties.name, } self._countryHoverCb.forEach(cb => cb(countryEvent)) }) .on('mouseout', function (event: MouseEvent) { self._countryHoverCb.forEach(cb => cb(null)) }) .on('click', function (event: MouseEvent) { const country = select(this).datum() as any; const countryEvent: CountryEvent = { event: event, countryCode: country.properties.iso_a2, countryName: country.properties.name, } const loc = self.projection.invert!([event.clientX, event.clientY]) console.log(loc) self._countryClickCb.forEach(cb => cb(countryEvent)) }) } private setupProjection() { const size = this.mapRoot.nativeElement.getBoundingClientRect(); this.projection = geoMercator() .rotate([MapRendererComponent.Rotate, 0]) .scale(1) .translate([size.width / 2, size.height / 2]); // path is used to update the SVG path to match our mercator projection this.pathFunc = geoPath().projection(this.projection); } private async setupZoom(countries: any) { if (!this.svg) { return } // create a copy of countries countries = { ...countries, features: [...countries.features] } // remove Antarctica from the feature set so projection.fitSize ignores it // and better aligns the rest of the world :) const aqIdx = countries.features.findIndex((p: GeoJSON.Feature) => p.properties?.iso_a2 === "AQ"); if (aqIdx >= 0) { countries.features.splice(aqIdx, 1) } const size = this.mapRoot.nativeElement.getBoundingClientRect(); this.projection.fitSize([size.width, size.height], countries) //this.projection.fitWidth(size.width, countries) //this.projection.fitHeight(size.height, countries) // returns the top-left and the bottom-right of the current projection const mercatorBounds = () => { const yaw = this.projection.rotate()[0]; const xymax = this.projection([-yaw + 180 - 1e-6, -MapRendererComponent.Maxlat])!; const xymin = this.projection([-yaw - 180 + 1e-6, MapRendererComponent.Maxlat])!; return [xymin, xymax]; } const s = this.projection.scale() const scaleExtent = [s, s * 10] const transform = zoomIdentity .scale(this.projection.scale()) .translate(this.projection.translate()[0], this.projection.translate()[1]); // whenever the users zooms we need to update our groups // individually to apply the zoom effect. let tlast = { x: 0, y: 0, k: 0, } const self = this; let z = zoom() .scaleExtent(scaleExtent as [number, number]) .on('zoom', (e) => { const t: ZoomTransform = e.transform; if (t.k != tlast.k) { let p = pointer(e) let scrollToMouse = () => { }; if (!!p && !!p[0]) { const tp = this.projection.translate(); const coords = this.projection!.invert!(p) scrollToMouse = () => { const newPos = this.projection(coords!)!; const yaw = this.projection.rotate()[0]; this.projection.translate([tp[0], tp[1] + (p[1] - newPos[1])]) this.projection.rotate([yaw + 360.0 * (p[0] - newPos[0]) / size.width * scaleExtent[0] / t.k, 0, 0]) } } this.projection.scale(t.k); scrollToMouse(); } else { let dy = t.y - tlast.y; const dx = t.x - tlast.x; const yaw = this.projection.rotate()[0] const tp = this.projection.translate(); // use x translation to rotate based on current scale this.projection.rotate([yaw + 360.0 * dx / size.width * scaleExtent[0] / t.k, 0, 0]) // use y translation to translate projection clamped to bounds let bounds = mercatorBounds(); if (bounds[0][1] + dy > 0) { dy = -bounds[0][1]; } else if (bounds[1][1] + dy < size.height) { dy = size.height - bounds[1][1]; } this.projection.translate([tp[0], tp[1] + dy]); } tlast = { x: t.x, y: t.y, k: t.k, } // finally, re-render the SVG shapes according to the new projection this.worldGroup.selectAll('path') .attr('d', this.pathFunc) this._zoomCb.forEach(cb => cb()); }); this.svg.call(z) this.svg.call(z.transform, transform); } public getCoords(lat: number, lng: number) { const loc = this.projection([lng, lat]); if (!loc) { return null; } const rootElem = this.mapRoot.nativeElement.getBoundingClientRect(); const x = rootElem.x + loc[0]; const y = rootElem.y + loc[1]; return [x, y]; } public coordsInView(lat: number, lng: number) { const loc = this.projection([lng, lat]); if (!loc) { return false } const rootElem = this.mapRoot.nativeElement.getBoundingClientRect(); const x = rootElem.x + loc[0]; const y = rootElem.y + loc[1]; return x >= rootElem.left && x <= rootElem.right && y >= rootElem.top && y <= rootElem.bottom; } } ================================================ FILE: desktop/angular/src/app/pages/spn/map-renderer/map-style.scss ================================================ ::ng-deep { .pin { opacity: 0; &.in-view { opacity: 1; } } } ::ng-deep #spn-map { --map-bg: #111112; --map-country-active: #424141; --map-country-inactive: #2a2a2a; --map-country-border-width: 2px; --map-country-border-color: #1e1e1e; --map-country-border-color-selected: #858585; --map-country-blocked-primary: #858585; --map-country-blocked-secondary: #402323; .overlay { fill: none; pointer-events: all; } g { circle, polygon { fill: #626262; stroke: #626262; stroke-width: 1; stroke-linejoin: round; transition: all 200ms linear 0s; } circle:hover, polygon:hover { fill: theme('colors.yellow.200'); stroke: theme('colors.yellow.300'); stroke-width: 2; } } g[in-use=true] { circle { fill: #239215; stroke: #239215; transform: scale(1.15) } polygon { fill: #0376bb; stroke: #0376bb; transform: scale(1.15) } } g[is-exit=true] { circle, polygon { transform: scale(1.3); stroke-width: 2; } polygon { stroke: #039af4; fill: #0376bb; } circle { stroke: #30ae20; fill: #239215; } } g[is-home=true] circle { stroke: white; stroke-width: 4.5; fill: black; transform: scale(1); } g[raise=true] { circle, polygon { fill: theme('colors.yellow.200'); stroke: theme('colors.yellow.300'); stroke-width: 2; transform: scale(1.8); } } .marker { cursor: pointer; fill: #252525; stroke: rgba(151, 151, 151, 0.8); transition: all 250ms 0s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .marker-label { fill: white; } path.lane { stroke: rgba(151, 151, 151, 0.2); fill: transparent; &[in-use=true] { stroke-width: 2; stroke: #0376bb; } &[is-live=true] { stroke-width: 1; stroke: theme('colors.red.300'); &[is-encrypted=true] { stroke: theme('colors.green.200'); } &:hover { stroke-width: 3; } } } #world-group { path { fill: var(--map-country-border-color); stroke: var(--map-country-border-color); stroke-width: var(--map-country-border-width); stroke-linejoin: round; } path[has-nodes=true] { fill: var(--map-country-inactive); } path[in-use=true] { fill: var(--map-country-active); } path:hover { cursor: pointer; fill: var(--map-country-active); } path.selected { stroke: var(--map-country-border-color-selected); } } } :host-context(.disabled) { @apply bg-white; #world-group { path { fill: #000000; stroke: #111111; stroke-width: .5px; } } } ================================================ FILE: desktop/angular/src/app/pages/spn/map.service.ts ================================================ import { Injectable } from '@angular/core'; import { AppProfile, GeoCoordinates, IntelEntity, Netquery, Pin, SPNService, UnknownLocation, getPinCoords } from '@safing/portmaster-api'; import { BehaviorSubject, Observable, combineLatest, debounceTime, interval, of, startWith, switchMap } from 'rxjs'; import { distinctUntilChanged, filter, map, share } from 'rxjs/operators'; import { SPNStatus } from './../../../../projects/safing/portmaster-api/src/lib/spn.types'; export interface MapPin { pin: Pin; // location is set to the geo-coordinates that should be used // for that pin. location: GeoCoordinates; // entity is set to the intel entity that should be used for // this pin. entity: IntelEntity; // whether the pin is regarded as offline / not available. isOffline: boolean; // whether or not the pin is currently used as an exit node isExit: boolean; // whether or not the pin is used as a transit node isTransit: boolean; // whether or not the pin is currently active. isActive: boolean; // whether or not the pin is used as the entry-node. isHome: boolean; // whether the pin has any known issues hasIssues: boolean; } @Injectable({ providedIn: 'root' }) export class MapService { /** * activeSince$ emits the pre-formatted duration since the SPN is active * it formats the duration as "HH:MM:SS" or null if the SPN is not enabled. */ activeSince$: Observable; /** Emits the current status of the SPN */ status$: Observable; /** Emits all map pins */ _pins$ = new BehaviorSubject([]); get pins$(): Observable { return this._pins$.asObservable(); } pinsMap$ = this.pins$ .pipe( filter(allPins => !!allPins.length), map(allPins => { const lm = new Map(); allPins.forEach(pin => lm.set(pin.pin.ID, pin)); return lm }), share(), ) constructor( private spnService: SPNService, private netquery: Netquery, ) { this.status$ = this.spnService .status$ .pipe( map(status => !!status ? status.Status : 'disabled'), distinctUntilChanged() ); // setup the activeSince$ observable that emits every second how long the // SPN has been active. this.activeSince$ = combineLatest([ this.spnService.status$, interval(1000).pipe(startWith(-1)) ]).pipe( map(([status]) => !!status.ConnectedSince ? this.formatActiveSinceDate(status.ConnectedSince) : null), share(), ); let pinMap = new Map(); let pinResult: MapPin[] = []; // create a stream of pin updates from the SPN service if it is enabled. this.status$ .pipe( switchMap(status => { if (status !== 'disabled') { return combineLatest([ this.spnService.watchPins(), interval(5000) .pipe( startWith(-1), switchMap(() => this.getPinIDsUsedAsExit()) ) ]) } return of([[], []]); }), map(([pins, exitPinIDs]) => { const exitPins = new Set(exitPinIDs); const activePins = new Set(); const transitPins = new Set(); const seenPinIDs = new Set(); let hasChanges = false; pins.forEach(pin => pin.Route?.forEach((hop, index) => { if (index < pin.Route!.length - 1) { transitPins.add(hop) } activePins.add(hop); })); pins.forEach(pin => { // Save Pin ID as seen. seenPinIDs.add(pin.ID); const oldPinModel = pinMap.get(pin.ID); // Get states of new model. const isOffline = pin.States.includes('Offline') || !pin.States.includes('Reachable'); const isHome = pin.HopDistance === 1; const isTransit = transitPins.has(pin.ID); const isExit = exitPins.has(pin.ID); const isActive = activePins.has(pin.ID); const hasIssues = pin.States.includes('ConnectivityIssues'); const pinHasChanged = !oldPinModel || oldPinModel.pin !== pin || oldPinModel.isOffline !== isOffline || oldPinModel.isHome !== isHome || oldPinModel.isTransit !== isTransit || oldPinModel.isExit !== isExit || oldPinModel.isActive !== isActive || oldPinModel.hasIssues !== hasIssues; if (pinHasChanged) { const newPinModel: MapPin = { pin: pin, location: getPinCoords(pin) || UnknownLocation, entity: (pin.EntityV4 || pin.EntityV6)!, isExit, isTransit, isActive, isOffline, isHome, hasIssues, } pinMap.set(pin.ID, newPinModel); hasChanges = true; } }) for (let key of pinMap.keys()) { if (!seenPinIDs.has(key)) { // this pin has been removed pinMap.delete(key) hasChanges = true; } } if (hasChanges) { pinResult = Array.from(pinMap.values()); } return pinResult; }), debounceTime(10), distinctUntilChanged(), ) .subscribe(pins => this._pins$.next(pins)) } getExitPinIDsForProfile(profile: AppProfile) { return this.netquery .query({ select: ['exit_node'], groupBy: ['exit_node'], query: { profile: { $eq: `${profile.Source}/${profile.ID}` }, } }, 'map-service-get-exit-pin-ids-for-profile') .pipe(map(result => result.map(row => row.exit_node!))) } getPinIDsWithActiveSession() { return this.pins$ .pipe( map(result => result.filter(pin => pin.pin.SessionActive).map(pin => pin.pin.ID)) ) } getPinIDsUsedAsExit() { return this.netquery .query({ select: ['exit_node'], groupBy: ['exit_node'] }, 'map-service-get-pins-used-as-exit') .pipe( map(result => result.map(row => row.exit_node!)) ) } getPinIDsWithActiveConnections() { return this.netquery.query({ select: ['exit_node'], groupBy: ['exit_node'], query: { active: { $eq: true } } }, 'map-service-get-pins-with-connections') .pipe( map(activeExitNodes => { const pins = this._pins$.getValue(); const pinIDs = new Set(); const pinLookupMap = new Map(); pins.forEach(p => pinLookupMap.set(p.pin.ID, p)) activeExitNodes.map(row => { const pin = pinLookupMap.get(row.exit_node!); if (!!pin) { pin.pin.Route?.forEach(hop => { pinIDs.add(hop) }) } }) return Array.from(pinIDs); }) ) } private formatActiveSinceDate(date: string): string { const d = new Date(date); const diff = Math.floor((new Date().getTime() - d.getTime()) / 1000); const hours = Math.floor(diff / 3600); const minutes = Math.floor((diff - (hours * 3600)) / 60); const secs = diff - (hours * 3600) - (minutes * 60); const pad = (d: number) => d < 10 ? `0${d}` : '' + d; return `${pad(hours)}:${pad(minutes)}:${pad(secs)}`; } } ================================================ FILE: desktop/angular/src/app/pages/spn/node-icon/index.ts ================================================ export * from './node-icon'; ================================================ FILE: desktop/angular/src/app/pages/spn/node-icon/node-icon.html ================================================ ================================================ FILE: desktop/angular/src/app/pages/spn/node-icon/node-icon.scss ================================================ svg { circle, polygon { fill: #626262; stroke: #626262; stroke-width: 1; stroke-linejoin: round; transition: all 200ms linear 0s; } polygon.active, polygon.exit { fill: #0376bb; stroke: #0376bb; transform: scale(1.15) } circle.active, circle.exit { fill: #239215; stroke: #239215; transform: scale(1.15) } circle.exit, polygon.exit { stroke-width: 2; } circle.exit { stroke: #30ae20; } polygon.exit { stroke: #039af4; } } ================================================ FILE: desktop/angular/src/app/pages/spn/node-icon/node-icon.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, Component, Input } from "@angular/core"; @Component({ // eslint-disable-next-line @angular-eslint/component-selector selector: 'spn-node-icon', changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: './node-icon.html', styleUrls: ['./node-icon.scss'], }) export class SpnNodeIconComponent { @Input() set bySafing(v: any) { this._bySafing = coerceBooleanProperty(v); } get bySafing() { return this._bySafing } private _bySafing = false; @Input() set isActive(v: any) { this._isActive = coerceBooleanProperty(v); } get isActive() { return this._isActive } private _isActive = false; @Input() set isExit(v: any) { this._isExit = coerceBooleanProperty(v); } get isExit() { return this._isExit; } private _isExit = false; get nodeClass() { if (this._isExit) { return 'exit'; } if (this.isActive) { return 'active' } return ''; } } ================================================ FILE: desktop/angular/src/app/pages/spn/pin-details/index.ts ================================================ export * from './pin-details'; ================================================ FILE: desktop/angular/src/app/pages/spn/pin-details/pin-details.html ================================================

{{ pin?.pin?.Name || 'N/A' }}

This SPN Node is run by {{ pin.pin.VerifiedOwner || 'Community' }}
Node is Offline
Node has Issues
ID {{ pin.pin.ID }}
Verified Owner
{{ pin.pin.VerifiedOwner }}
First Seen {{ pin.pin.FirstSeen | date:'medium' }}
IPv4
{{ entity.ASOrg }} ({{ entity.ASN }}) {{ entity.IP || 'N/A' }}
IPv6
{{ entity.ASOrg }} ({{ entity.ASN }}) {{ entity.IP || 'N/A' }}
States
{{ pin.pin.States.join(", ") }}
SessionActive
{{ pin.pin.SessionActive }}
HopDistance
{{ pin.pin.HopDistance }}
Exit Connections
{{ exitConnectionCount }}
================================================ FILE: desktop/angular/src/app/pages/spn/pin-details/pin-details.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, OnChanges, OnDestroy, OnInit, Optional, SimpleChanges } from '@angular/core'; import { Netquery } from '@safing/portmaster-api'; import { SFNG_DIALOG_REF, SfngDialogRef } from '@safing/ui'; import { Subscription, forkJoin, map, of, switchMap } from 'rxjs'; import { LaneModel } from '../pin-list/pin-list'; import { MapPin, MapService } from './../map.service'; @Component({ templateUrl: './pin-details.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class PinDetailsComponent implements OnInit, OnChanges, OnDestroy { private subscription = Subscription.EMPTY; @Input() mapPinID!: string; pin: MapPin | null = null; /** Holds all pins this pin has a active connection to */ connectedPins: LaneModel[] = []; /** The number of connections that exit at this pin */ exitConnectionCount: number = 0; constructor( private mapService: MapService, private netquery: Netquery, private cdr: ChangeDetectorRef, @Optional() @Inject(SFNG_DIALOG_REF) public dialogRef?: SfngDialogRef, ) { } ngOnInit(): void { // if we got opened via a dialog we get the map pin ID from the dialog data. if (!!this.dialogRef) { this.mapPinID = this.dialogRef.data; } this.subscription.unsubscribe(); this.subscription = this.mapService .pins$ .pipe( map(pins => { return [pins.find(p => p.pin.ID === this.mapPinID), pins] as [MapPin, MapPin[]]; }), switchMap(([pin, allPins]) => forkJoin({ pin: of(pin), allPins: of(allPins), exitConnections: this.netquery.query({ select: [ { $count: { field: '*', as: 'totalCount', } }, ], query: { exit_node: pin.pin.ID, }, groupBy: ['exit_node'] }, 'pin-details-get-connections-per-exit-node') })) ) .subscribe((result) => { this.pin = result.pin || null; const lm = new Map(); result.allPins.forEach(pin => lm.set(pin.pin.ID, pin)) const connectedTo = this.pin?.pin.ConnectedTo || {}; this.connectedPins = Object.keys(connectedTo) .map(pinID => { const pin = lm.get(pinID)!; return { ...connectedTo[pinID], mapPin: pin, } }); if (result.exitConnections.length) { // we expect only one row to be returned for the above query. this.exitConnectionCount = result.exitConnections[0].totalCount; } else { this.exitConnectionCount = 0; } this.cdr.markForCheck(); }) } ngOnChanges(changes: SimpleChanges) { // if we got rendered directly (without a dialog) we need to // handle updates to the mapPinID input field by re-loading the // pin details. We do that by simply re-running ngOnInit if (!!changes['mapPinID']) { this.ngOnInit() } } ngOnDestroy(): void { this.subscription.unsubscribe(); } } ================================================ FILE: desktop/angular/src/app/pages/spn/pin-list/index.ts ================================================ ================================================ FILE: desktop/angular/src/app/pages/spn/pin-list/pin-list.html ================================================
Name Operator Used As Latency Capacity IPv4 IPv6
{{ pin.pin.Name }}
{{ pin.pin.VerifiedOwner || 'Community' }}
{{ val.Latency / 1000 / 1000 | number:'1.0-2' }} ms {{ val.Capacity / 1000 / 1000 | number:'1.0-2' }} Mbit/s {{ pin.pin.EntityV4?.IP || 'N/A' }} {{ pin.pin.EntityV6?.IP || 'N/A' }}
================================================ FILE: desktop/angular/src/app/pages/spn/pin-list/pin-list.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, TrackByFunction } from '@angular/core'; import { Lane } from '@safing/portmaster-api'; import { take } from 'rxjs/operators'; import { MapPin } from '../map.service'; import { MapService } from './../map.service'; export interface LaneModel extends Lane { mapPin: MapPin; } @Component({ // eslint-disable-next-line @angular-eslint/component-selector selector: 'spn-pin-list', templateUrl: './pin-list.html', changeDetection: ChangeDetectionStrategy.OnPush }) export class SpnPinListComponent { @Input() set allowHover(v: any) { this._allowHover = coerceBooleanProperty(v); } get allowHover() { return this._allowHover } private _allowHover = true; @Input() set allowClick(v: any) { this._allowClick = coerceBooleanProperty(v); } get allowClick() { return this._allowClick } private _allowClick = true; @Input() set pins(pins: (string | MapPin | LaneModel)[]) { this.mapService .pinsMap$ .pipe(take(1)) .subscribe(allPins => { this.lanes = null; this._pins = (pins || []).map(idOrPin => { if (typeof idOrPin === 'string') { return allPins.get(idOrPin)!; } if ('mapPin' in idOrPin) { // LaneModel if (this.lanes === null) { this.lanes = new Map(); } this.lanes.set(idOrPin.HubID, { Capacity: idOrPin.Capacity, Latency: idOrPin.Latency, }) return idOrPin.mapPin; } return idOrPin; // MapPin }) this.cdr.markForCheck(); }) } get pins(): MapPin[] { return this._pins; } private _pins: MapPin[] = []; /** If we got LaneModel in @Input() pins than this will contain a map with the capacity/latency */ lanes: Map> | null = null; /** Emits the ID of the pin that got hovered, null if the mouse left a pin */ @Output() pinHover = new EventEmitter(); @Output() pinClick = new EventEmitter(); /** @private - A {@link TrackByFunction} for all pins available in this country */ trackPin: TrackByFunction = (_: number, pin: MapPin) => pin.pin.ID; constructor( private mapService: MapService, private cdr: ChangeDetectorRef ) { } } ================================================ FILE: desktop/angular/src/app/pages/spn/pin-overlay/index.ts ================================================ export * from './pin-overlay'; ================================================ FILE: desktop/angular/src/app/pages/spn/pin-overlay/pin-overlay.html ================================================
Show Details Show exit connections Copy Node ID {{ mapPin.pin.Name }}
IPv4 {{ mapPin.pin.EntityV4?.IP || 'N/A' }}
IPv6 {{ mapPin.pin.EntityV6?.IP || 'N/A' }}
Run By {{ mapPin.pin.VerifiedOwner || 'Community' }}
Used As
Home Node Exit Node Transit Node
================================================ FILE: desktop/angular/src/app/pages/spn/pin-overlay/pin-overlay.scss ================================================ :host { min-width: 220px; display: block; } ================================================ FILE: desktop/angular/src/app/pages/spn/pin-overlay/pin-overlay.ts ================================================ import { AnimationEvent, animate, keyframes, style, transition, trigger } from '@angular/animations'; import { CdkDrag, CdkDragHandle, CdkDragRelease } from '@angular/cdk/drag-drop'; import { Overlay, OverlayRef, PositionStrategy } from '@angular/cdk/overlay'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, HostListener, Inject, Input, OnInit, Output, ViewChild, inject } from '@angular/core'; import { Router } from '@angular/router'; import { SfngDialogService } from '@safing/ui'; import { PinDetailsComponent } from '../pin-details'; import { MapOverlay, Path } from '../spn-page'; import { ActionIndicatorService } from './../../../shared/action-indicator/action-indicator.service'; import { MapPin } from './../map.service'; import { OVERLAY_REF } from './../utils'; import { INTEGRATION_SERVICE } from 'src/app/integration'; export interface PinOverlayHoverEvent { type: 'enter' | 'leave'; pinID: string; } @Component({ templateUrl: './pin-overlay.html', styleUrls: [ './pin-overlay.scss' ], changeDetection: ChangeDetectionStrategy.OnPush, animations: [ trigger('moveIn', [ transition(':enter', [ style({ transform: 'scale(0)', transformOrigin: 'top left' }), animate('200ms {{ delay }}ms cubic-bezier(0, 0, 0.2, 1)', keyframes([ style({ transform: 'scaleX(1) scaleY(0.1)', transformOrigin: 'top left', offset: 0.3 }), style({ transform: 'scaleX(1) scaleY(1)', transformOrigin: 'top left', offset: 0.8 }), ]) ) ], { params: { delay: "0" } }), transition(':leave', [ style({ transform: 'scale(1)', opacity: 1, transformOrigin: 'top left' }), animate('500ms cubic-bezier(0, 0, 0.2, 1)', keyframes([ style({ transform: 'scaleX(1) scaleY(0.1)', opacity: 0.5, transformOrigin: 'top left', offset: 0.3 }), style({ transform: 'scaleX(0) scaleY(0)', opacity: 0, transformOrigin: 'top left', offset: 0.8 }), ]) ) ]) ]) ] }) export class PinOverlayComponent implements OnInit { private readonly integration = inject(INTEGRATION_SERVICE); @Input() mapPin!: MapPin; @Input() routeHome?: Path; @Input() additionalPaths?: Path[] = []; @Input() delay: number = 0; @Output() afterDispose = new EventEmitter(); @Output() overlayHover = new EventEmitter(); @ViewChild(CdkDrag) dragContainer!: CdkDrag; @ViewChild(CdkDragHandle) dragHandle!: CdkDragHandle; showContent = false; /** Indicates whether or not the pin overlay has been moved by the user */ hasBeenMoved = false; private oldPositionStrategy?: PositionStrategy; @HostListener('mouseenter') onHostElementMouseEnter(event: MouseEvent) { this.overlayHover.next({ type: 'enter', pinID: this.mapPin.pin.ID }) this.containerClass = ''; } @HostListener('mouseleave') onHostElementMouseLeave(event: MouseEvent) { this.overlayHover.next({ type: 'leave', pinID: this.mapPin.pin.ID }) this.containerClass = 'bg-opacity-90' } /** on double-click, restore the old pin overlay position (before being initialy dragged by the user) */ onDragDblClick() { if (!!this.oldPositionStrategy) { this.overlayRef.updatePositionStrategy(this.oldPositionStrategy); this.overlayRef.updatePosition(); this.hasBeenMoved = false; } } onDragStart() { this.containerClass = 'outline' } openPinDetails() { this.dialog.create(PinDetailsComponent, { data: this.mapPin.pin.ID, autoclose: true, backdrop: false, dragable: true, }) } onDragRelease(event: CdkDragRelease) { if (!this.dragContainer || !this.overlayRef.hostElement || !this.overlayRef.hostElement.parentElement) { return; } const bbox = this.dragContainer.element.nativeElement.getBoundingClientRect(); const parent = this.overlayRef.hostElement.parentElement!.getBoundingClientRect(); if (!this.oldPositionStrategy) { this.oldPositionStrategy = this.overlayRef.getConfig().positionStrategy; } this.containerClass = ''; this.dragContainer.reset() this.overlayRef.updatePositionStrategy( this.overlay.position() .global() .top((bbox.top - parent.top) + 'px') .left((bbox.left - parent.left) + 'px') ); this.hasBeenMoved = true; } onAnimationComplete(event: AnimationEvent) { if (event.toState === 'void') { this.afterDispose.next(this.mapPin.pin.ID) this.overlayRef.dispose(); } } containerClass = ''; constructor( @Inject(OVERLAY_REF) public readonly overlayRef: OverlayRef, @Inject(MapOverlay) public overlay: Overlay, private dialog: SfngDialogService, private actionIndicator: ActionIndicatorService, private router: Router, private cdr: ChangeDetectorRef, ) { } ngOnInit(): void { this.showContent = true; this.cdr.markForCheck(); } disposeOverlay() { this.showContent = false; this.cdr.markForCheck(); } showExitConnections() { this.router.navigate(['/monitor'], { queryParams: { q: 'exit_node:' + this.mapPin.pin.ID } }) } async copyNodeID() { await this.integration.writeToClipboard(this.mapPin?.pin.ID) this.actionIndicator.success("Copied to Clipboard") } } ================================================ FILE: desktop/angular/src/app/pages/spn/pin-route/index.ts ================================================ export * from './pin-route'; ================================================ FILE: desktop/angular/src/app/pages/spn/pin-route/pin-route.html ================================================
  • Your Device
  • {{ node.entity.Country || 'No Location' }} {{ node.entity.IP || '' }} Home Exit
    {{ node.pin.Name }} by {{ node.pin.VerifiedOwner || 'Community' }}

    AS{{ node.entity.ASN }} - {{ node.entity.ASOrg || 'AS Organization not in DB' }}

    {{ node.pin.ID }}
  • Destination
================================================ FILE: desktop/angular/src/app/pages/spn/pin-route/pin-route.scss ================================================ .tunnel-path { position: relative; .line { position: absolute; top: 10px; bottom: 10px; left: 8px; width: 1px; background-color: rgba(255, 255, 255, 0.1); } .node-tag { border-radius: 1px solid rgba(255, 255, 255, 0.2); background-color: rgba(255, 255, 255, 0.1); padding: 2px; font-size: 85%; border-radius: 2px; transform: scale(0.85); display: inline-block; } ul { position: relative; padding-left: 20px; li:not(:last-of-type) { padding-bottom: 0.35rem; } .ip { margin-left: 0.35rem; } .hop-icon { display: inline-block; margin-left: -17px; margin-right: 4px; font-weight: 400; &.country { margin-left: -20px; } } .hop-title { margin-right: 2px; } .country { display: inline-block; margin-left: -20px; margin-right: 4px; &.unknown { height: 14px; width: 16px; position: relative; top: 3px; border: 1px solid rgba(0, 0, 0, 0.25); opacity: 0.5; border-radius: 3px; @apply bg-buttons-icon; } } } } ================================================ FILE: desktop/angular/src/app/pages/spn/pin-route/pin-route.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input } from "@angular/core"; import { TunnelNode } from "@safing/portmaster-api"; import { take } from 'rxjs'; import { MapPin, MapService } from './../map.service'; @Component({ // eslint-disable-next-line @angular-eslint/component-selector selector: 'sfng-spn-pin-route', templateUrl: './pin-route.html', styleUrls: ['./pin-route.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class SpnPinRouteComponent { @Input() set route(path: (string | MapPin | TunnelNode)[] | null) { this.mapService .pinsMap$ .pipe( take(1), ) .subscribe(lm => { this._route = (path || []).map(idOrPin => { if (typeof idOrPin === 'string') { return lm.get(idOrPin)!; } if ('ID' in idOrPin) { // TunnelNode return lm.get(idOrPin.ID)! } return idOrPin; }); this.cdr.markForCheck(); }) } get route(): MapPin[] { return this._route } private _route: MapPin[] = []; constructor( private mapService: MapService, private cdr: ChangeDetectorRef, ) { } } ================================================ FILE: desktop/angular/src/app/pages/spn/spn-feature-carousel/index.ts ================================================ export * from './spn-feature-carousel'; ================================================ FILE: desktop/angular/src/app/pages/spn/spn-feature-carousel/spn-feature-carousel.html ================================================

Get Multiple Identities for Each App

Automatically get a vast amount of identities (IP addresses). The SPN calculates an individual path for every connection through the privacy network. Spread your connections across the globe, without any effort.

Easily Adjust Your Privacy

SPN just works and does the heavy lifting for you. But of course you can easily configure the settings, so it fits your needs: Exclude certain apps and domains from the SPN. Or never exit in specific countries. And so much more...

Built from Scratch, for Your Privacy

SPN is built from the ground up. Privacy is cooked right into it. Inspired by Tor, it comes with onion routing and state of the art encryption. Fully open source so all our claims can be validated.

Bye Bye, VPNs

VPN technology was NOT built for user privacy, but for company security. Because of that, you can only trust a VPN provider's policy - and many have been caught abusing user data. Honestly, the best way forward: just stop paying for outdated technology.
Most VPNs
Read Comparison Blog
SPN Tor
Multiple Identities (simultaneous)
Individual Apps Settings
Easy Setup Browser Only
Availabilty
Open Source
Built for Privacy
================================================ FILE: desktop/angular/src/app/pages/spn/spn-feature-carousel/spn-feature-carousel.scss ================================================ :host { @apply flex flex-col gap-2 justify-center items-center relative; } section { @apply flex flex-row items-start gap-4 justify-evenly text-background; &.reverse { @apply flex-row-reverse } &>div { @apply flex flex-col w-1/3 gap-6; span { @apply text-base break-normal text-background text-opacity-80; } h1, h1>span { @apply text-2xl font-semibold break-normal md:text-3xl lg:text-4xl xl:text-5xl text-background; } h1>span { &.text-blue { color: theme('colors.blue.DEFAULT') !important; } } } img { position: relative; max-width: 50%; } table { @apply mb-12; th { @apply text-base; } td { @apply text-center p-2 leading-6; } tr>td:first-of-type { @apply text-left p-2 font-semibold text-base whitespace-nowrap; } } } ::ng-deep { spn-feature-carousel { sfng-tab-outlet { &>div { overflow: visible !important; } } } } ================================================ FILE: desktop/angular/src/app/pages/spn/spn-feature-carousel/spn-feature-carousel.ts ================================================ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, OnDestroy, QueryList, ViewChild, ViewChildren } from "@angular/core"; import { SfngTabComponent, SfngTabGroupComponent } from '@safing/ui'; import { filter, interval, startWith, Subscription } from 'rxjs'; @Component({ // eslint-disable-next-line @angular-eslint/component-selector selector: 'spn-feature-carousel', changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: './spn-feature-carousel.html', styleUrls: [ './spn-feature-carousel.scss' ] }) export class SPNFeatureCarouselComponent implements AfterViewInit, OnDestroy { private sub: Subscription = Subscription.EMPTY; pause = false; currentIndex = -1; @HostListener('mouseenter') onMouseEnter() { this.pause = true } @HostListener('mouseleave') onMouseLeave() { this.pause = false; } /** A list of all carousel templates */ @ViewChildren(SfngTabComponent) carousel!: QueryList; @ViewChild(SfngTabGroupComponent) tabGroup!: SfngTabGroupComponent; constructor( private cdr: ChangeDetectorRef ) { } ngAfterViewInit(): void { this.sub = interval(5000) .pipe( startWith(-1), filter(() => !this.pause), ) .subscribe(() => { this.openTab(this.currentIndex + 1, 'left') }) } ngOnDestroy(): void { this.sub.unsubscribe() } openTab(idx: number, direction?: 'left' | 'right') { // force animation to circle if we go before the first // or after the last one. if (idx < 0) { idx = this.carousel.length - 1; direction = 'right' } if (idx >= this.carousel.length) { direction = 'left' } this.currentIndex = idx % this.carousel.length; this.tabGroup.activateTab(this.currentIndex, direction)!; this.cdr.markForCheck(); } showNext() { this.sub.unsubscribe() this.openTab(this.currentIndex + 1) } showPrev() { this.sub.unsubscribe() this.openTab(this.currentIndex - 1) } } ================================================ FILE: desktop/angular/src/app/pages/spn/spn-page.html ================================================
Loading data, please wait ...
Pricing
Pro Tip:
Hold
CTRL
key and click a node on the map to immediately open the node details dialog.
Hold
SHIFT
key to open more than one node overlay when clicking the node icon.
To keep node overlays open move them using . Double click to revert the overlay position on the map. Click on a country to get more information about all nodes in that country and a list of Apps that use nodes in the country as an identity.
================================================ FILE: desktop/angular/src/app/pages/spn/spn-page.scss ================================================ :host { @apply flex flex-row w-full h-full justify-items-stretch items-stretch relative; } .text-info-red { color: theme("colors.info.red"); } .network-status-dialog { width: 50vw; height: 50vh; min-height: 300px; min-width: 400px; padding: 12px; overflow: auto; display: flex; flex-direction: column; .issue { flex-grow: 1; } .issue-list { width: 100% !important; flex-grow: 1; ul { overflow: auto; } } .issue.expanded { background-color: var(--button-light) !important; } .body { background-color: var(--cards-primary) !important; } } .connect-button { &.spn-connected { @apply bg-info-blue; } &.spn-connecting { @apply bg-info-blue; } &.spn-failed { @apply bg-info-red; } &:hover { @apply bg-info-blue opacity-75; } } .table { @apply w-full font-normal; &>div { @apply text-xs border-buttons-dark flex flex-row justify-between py-1; &:not(:last-child) { @apply border-b; } span:first-child { @apply text-tertiary; } span:last-child { @apply text-primary; } } } table tr:nth-child(odd) { background: none; } .tunnel-path { position: relative; .line { position: absolute; top: 10px; bottom: 10px; left: 8px; width: 1px; background-color: rgba(255, 255, 255, 0.1); } ul { position: relative; padding-left: 20px; li:not(:last-of-type) { padding-bottom: 0.35rem; } .ip { margin-left: 0.35rem; } .hop-icon { display: inline-block; margin-left: -17px; margin-right: 4px; font-weight: 400; &.country { margin-left: -20px; } } .hop-title { margin-right: 2px; } .country { display: inline-block; margin-left: -20px; margin-right: 4px; &.unknown { height: 14px; width: 16px; position: relative; top: 3px; border: 1px solid rgba(0, 0, 0, 0.25); opacity: 0.5; border-radius: 3px; @apply bg-buttons-icon; } } } } ================================================ FILE: desktop/angular/src/app/pages/spn/spn-page.ts ================================================ import { coerceElement } from "@angular/cdk/coercion"; import { Overlay, OverlayContainer } from "@angular/cdk/overlay"; import { ComponentPortal } from '@angular/cdk/portal'; import { HttpClient } from '@angular/common/http'; import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentRef, DestroyRef, ElementRef, Inject, Injectable, InjectionToken, Injector, OnDestroy, OnInit, QueryList, TemplateRef, ViewChild, ViewChildren, forwardRef, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, ParamMap, Router } from "@angular/router"; import { AppProfile, ConfigService, Connection, ExpertiseLevel, FeatureID, Netquery, PORTMASTER_HTTP_API_ENDPOINT, PortapiService, SPNService, SPNStatus, UserProfile } from "@safing/portmaster-api"; import { SfngDialogService } from "@safing/ui"; import { Line as D3Line, Selection, interpolateString, line, select } from 'd3'; import { BehaviorSubject, Observable, Subscription, combineLatest, interval, of } from "rxjs"; import { catchError, debounceTime, map, mergeMap, share, startWith, switchMap, take, takeUntil, withLatestFrom } from "rxjs/operators"; import { fadeInAnimation, fadeInListAnimation, fadeOutAnimation } from "src/app/shared/animations"; import { ExpertiseService } from "src/app/shared/expertise/expertise.service"; import { SPNAccountDetailsComponent } from "src/app/shared/spn-account-details"; import { CountryDetailsComponent } from "./country-details"; import { CountryEvent, MAP_HANDLER, MapRef, MapRendererComponent } from "./map-renderer/map-renderer"; import { MapPin, MapService } from "./map.service"; import { PinDetailsComponent } from "./pin-details"; import { PinOverlayComponent } from "./pin-overlay"; import { OVERLAY_REF } from './utils'; export const MapOverlay = new InjectionToken('MAP_OVERLAY') export type PinGroup = Selection; export type LaneGroup = Selection; export interface Path { id: string; points: (MapPin | [number, number])[]; attributes?: { [key: string]: string; } } export interface PinEvent { event?: MouseEvent; mapPin: MapPin; } /** * A custom class that implements the OverlayContainer interface of CDK. This * is used so we can configure a custom container element that will hold all overlays created * by the map component. This way the overlays will be bound to the map container and not overflow * the sidebar or other overlays that are created by the "root" app. */ @Injectable() class MapOverlayContainer { private _overlayContainer?: HTMLElement; setOverlayContainer(element: ElementRef | HTMLElement) { this._overlayContainer = coerceElement(element); } getContainerElement(): HTMLElement { if (!this._overlayContainer) { throw new Error("Overlay container element not initialized. Call setOverlayContainer first.") } return this._overlayContainer; } } @Component({ templateUrl: './spn-page.html', styleUrls: ['./spn-page.scss'], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ MapOverlayContainer, { provide: MapOverlay, useClass: Overlay }, { provide: OverlayContainer, useExisting: MapOverlayContainer }, { provide: MAP_HANDLER, useExisting: forwardRef(() => SpnPageComponent), multi: true } ], animations: [ fadeInListAnimation, fadeInAnimation, fadeOutAnimation ] }) export class SpnPageComponent implements OnInit, OnDestroy, AfterViewInit { private destroyRef = inject(DestroyRef); private countryDebounceTimer: any | null = null; /** a list of opened country details. required to close them on destry */ private openedCountryDetails: CountryDetailsComponent[] = []; readonly featureID = FeatureID.SPN; paths: Path[] = []; @ViewChild('overlayContainer', { static: true, read: ElementRef }) overlayContainer!: ElementRef; @ViewChild(MapRendererComponent, { static: true }) mapRenderer!: MapRendererComponent; @ViewChild('accountDetails', { read: TemplateRef, static: true }) accountDetails: TemplateRef | null = null; /** A list of pro-tip templates in our view */ @ViewChildren('proTip', { read: TemplateRef }) proTipTemplates!: QueryList>; /** The selected pro-tip template */ proTipTemplate: TemplateRef | null = null; /** currentUser holds the current SPN user profile if any */ currentUser: UserProfile | null = null; /** An observable that emits all active processes. */ activeProfiles$: Observable; /** Whether or not we are still waiting for all data in order to satisfy a "show process/pin" request by query-params */ loading = true; /** a list of currently selected pins */ selectedPins: PinOverlayComponent[] = []; /** the currently hovered country, if any */ hoveredCountry: { countryName: string; countryCode: string; } | null = null; liveMode = false; liveModePaths: Path[] = []; private liveModeSubscription = Subscription.EMPTY; /** * spnStatusTranslation translates the spn status to the text that is displayed * at the view */ readonly spnStatusTranslation: Readonly> = { connected: 'Connected', connecting: 'Connecting', disabled: 'Disabled', failed: 'Failure' } private mapRef: MapRef | null = null; private lineFunc: D3Line<(MapPin | [number, number])> | null = null; private highlightedPins = new Set(); registerMap(ref: MapRef) { this.mapRef = ref; ref.onMapReady(() => { // we want to have straight lines between our hubs so we use a custom // path function that updates x and y coordinates based on the mercator projection // without, points will no be at the correct geo-coordinates. this.lineFunc = line() .x(d => { if (Array.isArray(d)) { return this.mapRef!.projection([d[0], d[1]])![0]; } return this.mapRef!.projection([d.location.Longitude, d.location.Latitude])![0]; }) .y(d => { if (Array.isArray(d)) { return this.mapRef!.projection([d[0], d[1]])![1]; } return this.mapRef!.projection([d.location.Longitude, d.location.Latitude])![1]; }) this.mapRef!.root.append('g').attr('id', 'line-group') this.mapRef!.root.append('g').attr('id', 'pin-group') if (this.mapService._pins$.getValue().length > 0) { this.renderPins(this.mapService._pins$.getValue()) } }) ref.onCountryClick(event => this.onCountryClick(event)) ref.onCountryHover(event => this.onCountryHover(event)) ref.onZoomPan(() => this.onZoomAndPan()) } unregisterMap(ref: MapRef) { this.mapRef = null; this.lineFunc = null; } constructor( private configService: ConfigService, private spnService: SPNService, private netquery: Netquery, private expertiseService: ExpertiseService, private router: Router, private route: ActivatedRoute, private portapi: PortapiService, @Inject(PORTMASTER_HTTP_API_ENDPOINT) private httpAPI: string, private http: HttpClient, public mapService: MapService, @Inject(MapOverlay) private mapOverlay: Overlay, private dialog: SfngDialogService, private overlayContainerService: MapOverlayContainer, private cdr: ChangeDetectorRef, private injector: Injector, ) { this.activeProfiles$ = interval(5000) .pipe( startWith(-1), switchMap(() => this.netquery.getActiveProfiles()), share({ connector: () => new BehaviorSubject([]) }) ) } ngAfterViewInit() { // configure our custom overlay container this.overlayContainerService.setOverlayContainer(this.overlayContainer); // Select a random "Pro-Tip" template and run change detection this.proTipTemplate = this.proTipTemplates.get(Math.floor(Math.random() * this.proTipTemplates.length)) || null; this.cdr.detectChanges(); } openAccountDetails() { this.dialog.create(SPNAccountDetailsComponent, { autoclose: true, backdrop: 'light' }) } ngOnInit() { this.spnService .profile$ .pipe( takeUntilDestroyed(this.destroyRef), catchError(() => of(null)) ) .subscribe((user: UserProfile | null) => { if (user?.state !== '') { this.currentUser = user || null; } else { this.currentUser = null; } this.cdr.markForCheck(); }) let previousQueryMap: ParamMap | null = null; combineLatest([ this.route.queryParamMap, this.mapService.pins$, this.activeProfiles$, ]) .pipe( takeUntilDestroyed(this.destroyRef), ).subscribe(([params, pins, profiles]) => { if (params !== previousQueryMap) { const app = params.get("app") if (!!app) { const profile = profiles.find(p => `${p.Source}/${p.ID}` === app); if (!!profile) { const pinID = params.get("pin") const pin = pins.find(p => p.pin.ID === pinID); this.selectGroup(profile, pin) } } previousQueryMap = params; } this.renderPins(pins); // we're done with everything now. this.loading = false; }) } toggleLiveMode(enabled: boolean) { this.liveMode = enabled; if (!enabled) { this.liveModeSubscription.unsubscribe(); this.liveModePaths = []; this.updatePaths([]); this.cdr.markForCheck(); return; } this.liveModeSubscription = this.portapi.watchAll("network:tree") .pipe( withLatestFrom(this.mapService.pinsMap$), takeUntilDestroyed(this.destroyRef), debounceTime(100), ) .subscribe(([connections, mapPins]) => { connections = connections.filter(conn => conn.Ended === 0 && !!conn.TunnelContext); this.liveModePaths = connections.map(conn => { const points: (MapPin | [number, number])[] = conn.TunnelContext!.Path.map(hop => mapPins.get(hop.ID)!) if (!!conn.Entity.Coordinates) { points.push([conn.Entity.Coordinates.Longitude, conn.Entity.Coordinates.Latitude]) } return { id: conn.Entity.Domain || conn.ID, points: points, attributes: { 'is-live': 'true', 'is-encrypted': `${conn.Encrypted}` } } }) this.updatePaths([]) this.cdr.markForCheck(); }) } /** * Toggle the spn/enable setting. This does NOT update the view as that * will happen as soon as we get an update from the db qsub. * * @private - template only */ toggleSPN() { this.configService.get('spn/enable') .pipe( map(setting => setting.Value ?? setting.DefaultValue), mergeMap(active => this.configService.save('spn/enable', !active)) ) .subscribe() } /** * Select one or more pins by ID. If shift key is hold then all currently * selected pin overlays will be cleared before selecting the new ones. */ private selectPins(event: MouseEvent | undefined, pinIDs: Observable) { combineLatest([ this.mapService.pins$, pinIDs, ]) .pipe(take(1)) .subscribe(([allPins, pinIDs]) => { if (event?.shiftKey !== true) { this.selectedPins .filter(overlay => !overlay.hasBeenMoved) .forEach(selected => selected.disposeOverlay()) } pinIDs .filter(id => !this.selectedPins.find(selectedPin => selectedPin.mapPin.pin.ID === id)) .map(id => allPins.find(pin => pin.pin.ID === id)) .filter(mapPin => !!mapPin) .forEach(mapPin => this.onPinClick({ mapPin: mapPin!, })); }) } /** * Select all pins that are used for transit. * * @private - template only */ selectTransitNodes(event: MouseEvent) { this.selectPins(event, this.mapService.getPinIDsWithActiveSession()) } /** * Select all pins that are used as an exit hub. * * @private - template only */ selectExitNodes(event: MouseEvent) { this.selectPins(event, this.mapService.getPinIDsUsedAsExit()) } /** * Select all pins that currently host alive connections. * * @private - template only */ selectNodesWithAliveConnections(event: MouseEvent) { this.selectPins(event, this.mapService.getPinIDsWithActiveConnections()) } navigateToMonitor(process: AppProfile) { this.router.navigate(['/app', process.Source, process.ID]) } ngOnDestroy() { this.openedCountryDetails.forEach(cmp => cmp.dialogRef!.close()); } onZoomAndPan() { this.updateOverlayPositions(); if (this.mapRef) { this.mapRef.root .select('#lines-group') .selectAll('path') .attr('d', d => this.lineFunc!(d.points)) this.mapRef.root .select("#pin-group") .selectAll('g') .attr('transform', d => `translate(${this.mapRef!.projection([d.location.Longitude, d.location.Latitude])})`) } this.cdr.markForCheck(); } private createPinOverlay(pinEvent: PinEvent, lm: Map): PinOverlayComponent { const paths = this.getRouteHome(pinEvent.mapPin, lm, false) const overlayBoundingRect = this.overlayContainer.nativeElement.getBoundingClientRect(); const target = pinEvent.event?.target || this.getPinElem(pinEvent.mapPin.pin.ID)?.children[0]; let delay = 0; if (paths.length > 0) { delay = paths[0].points.length * MapRendererComponent.LineAnimationDuration; } const overlayRef = this.mapOverlay.create({ positionStrategy: this.mapOverlay.position() .flexibleConnectedTo(new ElementRef(target)) .withDefaultOffsetY(-overlayBoundingRect.y - 10) .withDefaultOffsetX(-overlayBoundingRect.x + 20) .withPositions([ { overlayX: 'start', overlayY: 'top', originX: 'start', originY: 'top' } ]), scrollStrategy: this.mapOverlay.scrollStrategies.reposition(), }) const injector = Injector.create({ providers: [ { provide: OVERLAY_REF, useValue: overlayRef, } ], parent: this.injector }) const pinOverlay = overlayRef.attach( new ComponentPortal(PinOverlayComponent, undefined, injector) ).instance; pinOverlay.delay = delay; pinOverlay.mapPin = pinEvent.mapPin; if (paths.length > 0) { pinOverlay.routeHome = { ...(paths[0]), } pinOverlay.additionalPaths = paths.slice(1); } return pinOverlay; } private openPinDetails(id: string) { this.dialog.create(PinDetailsComponent, { data: id, backdrop: false, autoclose: true, dragable: true, }) } private openCountryDetails(event: CountryEvent) { // abort if we already have the country details open. if (this.openedCountryDetails.find(cmp => cmp.countryCode === event.countryCode)) { return; } const ref = this.dialog.create(CountryDetailsComponent, { data: { name: event.countryName, code: event.countryCode, }, autoclose: false, dragable: true, backdrop: false, }) const component = (ref.contentRef() as ComponentRef).instance; // used to track whether we highlighted a map pin let hasPinHighlightActive = false; combineLatest([ component.pinHover, this.mapService.pins$, ]) .pipe( takeUntil(ref.onClose), ) .subscribe(([hovered, pins]) => { hasPinHighlightActive = hovered !== null; if (hovered !== null) { this.onPinHover({ mapPin: pins.find(p => p.pin.ID === hovered)!, }) this.highlightPin(hovered, true) } else { this.onPinHover(null); this.clearPinHighlights(); } this.cdr.markForCheck(); }) ref.onClose .subscribe(() => { if (hasPinHighlightActive) { this.clearPinHighlights(); } const index = this.openedCountryDetails.findIndex(cmp => cmp === component); if (index >= 0) { this.openedCountryDetails.splice(index, 1); } }) this.openedCountryDetails.push(component); } private updateOverlayPositions() { this.mapService.pinsMap$ .pipe(take(1)) .subscribe(allPins => { this.selectedPins.forEach(pin => { const pinObj = allPins.get(pin.mapPin.pin.ID); if (!pinObj) { return; } pin.overlayRef.updatePosition(); }) }) } onCountryClick(countryEvent: CountryEvent) { this.openCountryDetails(countryEvent); } onCountryHover(countryEvent: CountryEvent | null) { if (this.countryDebounceTimer !== null) { clearTimeout(this.countryDebounceTimer); } if (!!countryEvent) { this.hoveredCountry = { countryCode: countryEvent.countryCode, countryName: countryEvent.countryName, } this.cdr.markForCheck(); return; } this.countryDebounceTimer = setTimeout(() => { this.hoveredCountry = null; this.countryDebounceTimer = null; this.cdr.markForCheck(); }, 200) } onPinClick(pinEvent: PinEvent) { // if the control key hold when clicking a map pin, we immediately open the // pin details instead of the overlay. if (pinEvent.event?.ctrlKey) { this.openPinDetails(pinEvent.mapPin.pin.ID); } const overlay = this.selectedPins.find(por => por.mapPin.pin.ID === pinEvent.mapPin.pin.ID); if (!!overlay) { overlay.disposeOverlay() return; } // if shiftKey was not pressed during the pinClick we dispose all active overlays that have not been // moved by the user if (!pinEvent.event?.shiftKey) { this.selectedPins .filter(overlay => !overlay.hasBeenMoved) .forEach(selected => selected.disposeOverlay()) } this.mapService.pinsMap$ .pipe(take(1)) .subscribe(async lm => { const overlayComp = this.createPinOverlay(pinEvent, lm); // when the user wants to dispose a pin overlay (by clicking the X) we // - make sure the pin is not highlighted anymore // - remove the pin from the selectedPins list // - remove lines showing the route to the home hub overlayComp.afterDispose .subscribe(pinID => { this.highlightPin(pinID, false); const overlayIdx = this.selectedPins.findIndex(por => por.mapPin.pin.ID === pinEvent.mapPin.pin.ID); this.selectedPins.splice(overlayIdx, 1) this.updatePaths() this.cdr.markForCheck(); }) // when the user hovers/leaves a pin overlay, we: // - move the pin-overlay to the top when the user hovers it so stacking order is correct // - (un)hightlight the pin element on the map overlayComp.overlayHover .subscribe(evt => { this.highlightPin(evt.pinID, evt.type === 'enter') // over the overlay component to the top if (evt.type === 'enter') { this.selectedPins.forEach(ref => { if (ref !== overlayComp && ref.overlayRef.hostElement) { ref.overlayRef.hostElement.style.zIndex = '0'; } }) overlayComp.overlayRef.hostElement.style.zIndex = ''; } }) this.selectedPins.push(overlayComp) this.updatePaths([]); this.cdr.markForCheck(); }) } private updatePaths(additional: Path[] = []) { const paths = [ ...(this.selectedPins .reduce((list, pin) => { if (pin.routeHome) { list.push(pin.routeHome) } return [ ...list, ...(pin.additionalPaths || []) ] }, [] as Path[])), ...this.liveModePaths, ...additional ] this.paths = paths.map(p => { return { ...p, attributes: { class: 'lane', ...(p.attributes || {}) } } }); this.renderPaths(this.paths) } onPinHover(pinEvent: PinEvent | null) { if (!pinEvent) { this.updatePaths([]); this.onCountryHover(null); return; } // we also emit a country hover event here to keep the country // overlay open. const countryName = this.mapRenderer.countryNames[pinEvent.mapPin.entity.Country] this.onCountryHover({ event: pinEvent.event, countryCode: pinEvent.mapPin.entity.Country, countryName: countryName!, }) // in developer mode, we show all connected lanes of the hovered pin. if (this.expertiseService.currentLevel === ExpertiseLevel.Developer) { this.mapService.pinsMap$ .pipe(take(1)) .subscribe(lm => { const lanes = this.getConnectedLanes(pinEvent?.mapPin, lm) this.updatePaths(lanes); this.cdr.markForCheck(); }) } } /** * Marks a process group as selected and either selects one or all exit pins * of that group. If shiftKey is pressed during click, the ID(s) will be added * to the list of selected pins instead of replacing it. If shiftKey is pressed * the process group itself will NOT be displayed as selected. * * @private - template only */ selectGroup(grp: AppProfile, pin?: MapPin | null, event?: MouseEvent) { if (!!pin) { this.selectPins(event, of([pin.pin.ID])) return; } this.selectPins(event, this.mapService.getExitPinIDsForProfile(grp)) } /** Returns a list of lines that represent the route from pin to home. */ private getRouteHome(pin: MapPin, lm: Map, includeAllRoutes = false): Path[] { let pinsToEval: MapPin[] = [pin]; // decide whether to draw all connection routes that travel through pin. if (includeAllRoutes) { pinsToEval = [ ...pinsToEval, ...Array.from(lm.values()) .filter(p => p.pin.Route?.includes(pin.pin.ID)) ] } return pinsToEval.map(pin => ({ id: `route-home-from-${pin.pin.ID}`, points: (pin.pin.Route || []).map(hop => lm.get(hop)!), attributes: { 'in-use': 'true' } })); } /** Returns a list of lines the represent all lanes to connected pins of pin */ private getConnectedLanes(pin: MapPin, lm: Map): Path[] { let result: Path[] = []; // add all lanes for connected hubs Object.keys(pin.pin.ConnectedTo).forEach(target => { const p = lm.get(target); if (!!p) { result.push({ id: lineID([pin, p]), points: [ pin, p ] }) } }); return result; } private async renderPaths(paths: Path[]) { if (!this.mapRef) { return; } const ref = this.mapRef! const linesGroup: LaneGroup = this.mapRef.select("#line-group")! const self = this; const renderedPaths = linesGroup.selectAll('path') .data(paths, p => p.id); renderedPaths .enter() .append('path') .attr('d', path => { return self.lineFunc!(path.points) }) .attr("stroke-width", d => { if (d.attributes) { if (d.attributes['in-use']) { return 2 / ref.zoomScale } } return 1 / ref.zoomScale; }) .call(sel => { if (sel.empty()) { return; } const data = sel.datum()?.attributes || {}; Object.keys(data) .forEach(key => { sel.attr(key, data[key]) }) }) .transition("enter-lane") .duration(d => d.points.length * MapRendererComponent.LineAnimationDuration) .attrTween('stroke-dasharray', tweenDashEnter) renderedPaths.exit() .interrupt("enter-lane") .transition("leave-lane") .duration(200) .attrTween('stroke-dasharray', tweenDashExit) .remove(); } private async renderPins(pins: MapPin[]) { pins = pins.filter(pin => !pin.isOffline || pin.isActive); if (!this.mapRef) { return } const ref = this.mapRef!; const countriesWithNodes = new Set(); pins.forEach(pin => { countriesWithNodes.add(pin.entity.Country) }) const pinsGroup = ref.select('#pin-group')! const pinElements = pinsGroup .selectAll('g') .data(pins, pin => pin.pin.ID) const self = this; // add new pins pinElements .enter() .append('g') .append(d => { const val = MapRendererComponent.MarkerSize / ref.zoomScale; if (d.isHome) { const homeIcon = document.createElementNS('http://www.w3.org/2000/svg', 'circle') homeIcon.setAttribute('r', `${val * 1.25}`) return homeIcon; } if (d.pin.VerifiedOwner === 'Safing') { const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon') polygon.setAttribute('points', `0,-${val} -${val},${val} ${val},${val}`) return polygon; } const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle') circle.setAttribute('r', `${val}`) return circle; }) .attr("stroke-width", d => { if (d.isExit || self.highlightedPins.has(d.pin.ID)) { return 2 / ref.zoomScale } if (d.isHome) { return 4.5 / ref.zoomScale } return 1 / ref.zoomScale }) .call(selection => { selection .style('opacity', 0) .attr('transform', d => 'scale(0)') .transition('enter-marker') /**/.duration(1000) /**/.attr('transform', d => `scale(1)`) /**/.style('opacity', 1) }) .on('click', function (e: MouseEvent) { const pin = select(this).datum() as MapPin; self.onPinClick({ event: e, mapPin: pin }); }) .on('mouseenter', function (e: MouseEvent) { const pin = select(this).datum() as MapPin; self.onPinHover({ event: e, mapPin: pin, }) }) .on('mouseout', function (e: MouseEvent) { self.onPinHover(null); }) // remove pins from the map that disappeared pinElements .exit() .remove() // update all pins to their correct position and update their attributes pinsGroup.selectAll('g') .attr('hub-id', d => d.pin.ID) .attr('is-home', d => d.isHome) .attr('transform', d => `translate(${ref.projection([d.location.Longitude, d.location.Latitude])})`) .attr('in-use', d => d.isTransit) .attr('is-exit', d => d.isExit) .attr('raise', d => this.highlightedPins.has(d.pin.ID)) // update the attributes of the country shapes ref.worldGroup.selectAll('path') .attr('has-nodes', d => countriesWithNodes.has(d.properties.iso_a2)) // get all in-use pins and raise them to the top pinsGroup.selectAll('g[in-use=true]') .raise() // finally, re-raise all pins that are highlighted pinsGroup.selectAll('g[raise=true]') .raise() const activeCountrySet = new Set(); pins.forEach(pin => { if (pin.isTransit) { activeCountrySet.add(pin.pin.ID) } }) // update the in-use attributes of the country shapes ref.worldGroup.selectAll('path') .attr('in-use', d => activeCountrySet.has(d.properties.iso_a2)) this.cdr.detectChanges(); } public getPinElem(pinID: string) { if (!this.mapRef) { return } return this.mapRef.root .select("#pin-group") .select(`g[hub-id=${pinID}]`) .node() } public clearPinHighlights() { if (!this.mapRef) { return } this.mapRef.root .select('#pin-group') .select(`g[raise=true]`) .attr('raise', false) this.highlightedPins.clear(); } public highlightPin(pinID: string, highlight: boolean) { if (highlight) { this.highlightedPins.add(pinID) } else { this.highlightedPins.delete(pinID); } if (!this.mapRef) { return } const pinElemn = this.mapRef!.root .select("#pin-group") .select(`g[hub-id=${pinID}]`) .attr('raise', highlight) if (highlight) { pinElemn .raise() } } } function lineID(l: [MapPin, MapPin]): string { return [l[0].pin.ID, l[1].pin.ID].sort().join("-") } const tweenDashEnter = function (this: SVGPathElement) { const len = this.getTotalLength(); const interpolate = interpolateString(`0, ${len}`, `${len}, ${len}`); return (t: number) => { if (t === 1) { return '0'; } return interpolate(t); } } const tweenDashExit = function (this: SVGPathElement) { const len = this.getTotalLength(); const interpolate = interpolateString(`${len}, ${len}`, `0, ${len}`); return (t: number) => { if (t === 1) { return `${len}`; } return interpolate(t); } } ================================================ FILE: desktop/angular/src/app/pages/spn/spn.module.ts ================================================ import { A11yModule } from '@angular/cdk/a11y'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { OverlayModule } from '@angular/cdk/overlay'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { SfngToggleSwitchModule, SfngTooltipModule, TabModule } from '@safing/ui'; import { SfngAppIconModule } from 'src/app/shared/app-icon'; import { CountIndicatorModule } from 'src/app/shared/count-indicator'; import { CountryFlagModule } from 'src/app/shared/country-flag'; import { ExpertiseModule } from 'src/app/shared/expertise/expertise.module'; import { SfngFocusModule } from 'src/app/shared/focus'; import { SfngMenuModule } from 'src/app/shared/menu'; import { CommonPipesModule } from 'src/app/shared/pipes'; import { SpnPageComponent } from './'; import { CountryDetailsComponent } from './country-details'; import { CountryOverlayComponent } from './country-overlay'; import { SpnMapLegendComponent } from './map-legend'; import { MapRendererComponent } from './map-renderer'; import { SpnNodeIconComponent } from './node-icon'; import { PinDetailsComponent } from './pin-details'; import { SpnPinListComponent } from './pin-list/pin-list'; import { PinOverlayComponent } from './pin-overlay'; import { SpnPinRouteComponent } from './pin-route'; import { SPNFeatureCarouselComponent } from './spn-feature-carousel'; @NgModule({ imports: [ CommonModule, FormsModule, CountryFlagModule, SfngTooltipModule, SfngMenuModule, SfngFocusModule, SfngAppIconModule, SfngToggleSwitchModule, TabModule, A11yModule, ExpertiseModule, OverlayModule, CountIndicatorModule, FontAwesomeModule, CommonPipesModule, DragDropModule, RouterModule, ], declarations: [ MapRendererComponent, PinOverlayComponent, CountryOverlayComponent, CountryDetailsComponent, SpnNodeIconComponent, SpnMapLegendComponent, PinDetailsComponent, SpnPinRouteComponent, SPNFeatureCarouselComponent, SpnPageComponent, SpnPinListComponent, ], exports: [ SpnPageComponent, SpnPinRouteComponent, SpnNodeIconComponent, MapRendererComponent, ] }) export class SPNModule { } ================================================ FILE: desktop/angular/src/app/pages/spn/utils.ts ================================================ import { OverlayRef } from '@angular/cdk/overlay'; import { InjectionToken } from '@angular/core'; export const OVERLAY_REF = new InjectionToken('OVERLAY_REF'); ================================================ FILE: desktop/angular/src/app/pages/support/form/index.ts ================================================ export * from './support-form'; ================================================ FILE: desktop/angular/src/app/pages/support/form/support-form.html ================================================

{{ page?.title }}

{{ page?.prologue || page?.shortHelp }}

{{ page?.repoHelp }}

Title

Copy

{{section.title}}

Copy

Included Debug Info

The following debug information will be sent together with your report. Please check it and remove potentially sensitive information. The debug information sent with your reports will be saved on Safing's self-hosted pastebin server and is viewable via its created url. The data is automatically destroyed after one month.

Portmaster Version: {{version}} built on {{buildDate}}
Copy

Related Issues

Public issues related to your title:

No related issues were found.

  • {{ issue.title }} {{ issue.closed ? 'closed' : 'opened'}} in {{ repos[issue.repository] || issue.repository }} by {{ issue.user }} {{ issue.createdAt | timeAgo }}
================================================ FILE: desktop/angular/src/app/pages/support/form/support-form.scss ================================================ :host { width: 100%; display: flex; flex-grow: 1; flex-direction: column; height: 100%; } .scroll-container { overflow: auto; margin-right: 1rem; display: flex; flex-direction: row; justify-content: center; flex-grow: 1; @apply p-8; h3 { opacity: .9; font-size: 0.95rem; } } .form-wrapper { flex-grow: 2; @media (min-width: 1250px) { max-width: 800px; } } .issue-list { width: 400px; margin-left: 2rem; &, ul { overflow-y: hidden; } .issue { @apply px-4; @apply pr-8; @apply py-4; @apply rounded; @apply bg-cards-secondary; span { word-break: keep-all; } display : flex; flex-direction: column; position : relative; cursor : pointer; &:not(:last-child) { margin-bottom: 0.5rem; } .meta { @apply text-tertiary; @apply font-normal; opacity: .7; font-size: 95%; } &:hover { @apply bg-cards-tertiary; } fa-icon { position: absolute; right: calc((2rem - 12px) / 2); top: calc(50% - 8px); // actually the half height is 6px but that looks off for the icon we're using opacity: .3; } } } p.prologue { @apply mb-8; } .page-title { margin-top: 20px; margin-bottom: 40px; position: relative; border-bottom: 1px solid rgba(255, 255, 255, .2); h1 { position: absolute; top: -1rem; background-color: var(--background); @apply pr-8; } } .repo-list { @apply mb-8; } button { @apply p-2; @apply bg-buttons-dark; @apply border; @apply border-buttons-dark; opacity: .4; &:not(:last-child) { @apply mr-1; } &:hover { @apply bg-buttons-light; @apply border-buttons-light; } &.selected { @apply bg-buttons-dark; @apply border-buttons-light; opacity: 1; } } .actions { @apply mt-8; @apply pb-16; button { opacity: 1; @apply bg-transparent; &.primary { @apply bg-buttons-dark; opacity: 1; } &:hover { @apply bg-buttons-light; } } } .debug-header { height: 32px; display: flex; flex-direction: row; justify-content: space-between; align-items: center; position: relative; @apply bg-cards-primary; @apply rounded-t; top: 2px; } textarea { @apply px-4; @apply py-2; min-height: 40px; } textarea, input[type="text"].title { @apply font-medium; @apply border; @apply border-cards-secondary; @apply bg-cards-secondary; padding-right: 4.5rem; // copy button width &:hover, &:active, &:focus { @apply border-cards-primary; } } input[type="text"].title { padding-left: 1rem; } section { @apply py-8; &:not(:first-of-type) { @apply pt-0; } } .input-wrapper { position: relative; display: flex; } .copy-button { user-select: none; position: absolute; top: 1px; right: 0px; width: 4rem; height: 31px; display: flex; flex-direction: column; justify-content: center; align-items: center; cursor: pointer; @apply bg-buttons-dark; @apply rounded-sm; opacity: .5; &:hover { opacity: .9; } } .section-help { @apply bg-cards-primary; @apply border-t; @apply border-dashed; @apply border-buttons-light; @apply p-2; @apply px-4; @apply rounded-sm; color: rgba(255, 255, 255, .6); font-size: 0.7rem; position: relative; width: 100%; display: flex; flex-direction: column; } .gh-author { @apply mt-8; display: flex; flex-direction: row; justify-content: flex-start; align-items: flex-start; .input-wrapper { padding-top: 2px; @apply pr-2; } } input[type="text"].missing, textarea.missing { @apply border-info-red; } ================================================ FILE: desktop/angular/src/app/pages/support/form/support-form.ts ================================================ import { CdkScrollable } from '@angular/cdk/scrolling'; import { Component, DestroyRef, OnInit, TrackByFunction, ViewChild, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router } from '@angular/router'; import { DebugAPI } from '@safing/portmaster-api'; import { ConfirmDialogConfig, SfngDialogService } from '@safing/ui'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { debounceTime, mergeMap } from 'rxjs/operators'; import { SessionDataService, StatusService } from 'src/app/services'; import { Issue, SupportHubService } from 'src/app/services/supporthub.service'; import { ActionIndicatorService } from 'src/app/shared/action-indicator'; import { fadeInAnimation, fadeInListAnimation, moveInOutAnimation } from 'src/app/shared/animations'; import { FuzzySearchService } from 'src/app/shared/fuzzySearch'; import { SupportPage, supportTypes } from '../pages'; import { INTEGRATION_SERVICE } from 'src/app/integration'; import { SupportProgressDialogComponent, TicketData, TicketInfo } from '../progress-dialog'; @Component({ templateUrl: './support-form.html', styleUrls: ['./support-form.scss'], animations: [fadeInAnimation, moveInOutAnimation, fadeInListAnimation] }) export class SupportFormComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); private readonly search$ = new BehaviorSubject(''); private readonly integration = inject(INTEGRATION_SERVICE); page: SupportPage | null = null; debugData: string = ''; title: string = ''; form: { [key: string]: string } = {} selectedRepo: string = ''; haveGhAccount = false; version: string = ''; buildDate: string = ''; titleMissing = false; relatedIssues: Issue[] = []; allIssues: Issue[] = []; repos: { [repo: string]: string } = {}; @ViewChild(CdkScrollable) scrollContainer: CdkScrollable | null = null; trackIssue: TrackByFunction = (_: number, issue: Issue) => issue.url; constructor( private route: ActivatedRoute, private router: Router, private uai: ActionIndicatorService, private debugapi: DebugAPI, private statusService: StatusService, private dialog: SfngDialogService, private supporthub: SupportHubService, private searchService: FuzzySearchService, private sessionService: SessionDataService, ) { } ngOnInit() { this.supporthub.loadIssues().subscribe(issues => { issues = issues.reverse(); this.allIssues = issues; this.relatedIssues = issues; }) this.search$.pipe( takeUntilDestroyed(this.destroyRef), debounceTime(200), ) .subscribe((text) => { this.relatedIssues = this.searchService.searchList(this.allIssues, text, { disableHighlight: true, shouldSort: true, isCaseSensitive: false, minMatchCharLength: 4, keys: [ 'title', 'body', ], }).map(res => res.item) }) this.statusService.getVersions() .subscribe(status => { this.version = status.Core.Version; this.buildDate = status.Core.BuildTime; }) this.route.paramMap .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(params => { const id = params.get("id") for (let pIdx = 0; pIdx < supportTypes.length; pIdx++) { const pageSection = supportTypes[pIdx]; const page = pageSection.choices.find(choice => choice.type !== 'link' && choice.id === id); if (!!page) { this.page = page as SupportPage; break; } } if (!this.page) { this.router.navigate(['..']); return; } this.title = ''; this.form = {}; this.selectedRepo = 'portmaster'; this.debugData = ''; this.repos = {}; this.page.sections.forEach(section => this.form[section.title] = ''); this.page.repositories?.forEach(repo => this.repos[repo.repo] = repo.name) // try to restore from session service this.sessionService.restore(this.page.id, this); if (this.page.includeDebugData) { this.debugapi.getCoreDebugInfo('github') .subscribe({ next: data => this.debugData = data, error: err => this.uai.error('Failed to get Debug Data', this.uai.getErrorMessgae(err)) }) } }) } onModelChange() { if (!this.page) { return; } this.sessionService.save(this.page.id, this, ['title', 'form', 'selectedRepo', 'haveGhAccount']); } selectRepo(repo: string) { this.selectedRepo = repo; this.onModelChange(); } searchIssues(text: string) { this.onModelChange(); this.search$.next(text); } copyToClipboard(what: string) { this.integration.writeToClipboard(what) .then(() => this.uai.success("Copied to Clipboard")) .catch(() => this.uai.error('Failed to Copy to Clipboard')); } validate(): boolean { this.titleMissing = this.title === ''; const valid = !this.titleMissing; if (!valid) { this.scrollContainer?.scrollTo({ top: 0, behavior: 'smooth' }) } return valid; } createIssue(type: 'github' | 'private', genUrl?: boolean, email?: string) { const ticketData: TicketData = { repo: this.selectedRepo || '', title: this.title, debugInfo: this.debugData, sections: this.page?.sections.map(section => ({ title: section.title, body: this.form[section.title], })) || [], } let issue: TicketInfo; switch (type) { case 'github': issue = { type: 'github', generateUrl: genUrl || false, preset: this.page!.ghIssuePreset || '', ...ticketData }; break; case 'private': issue = { type: 'private', email: email, ...ticketData } break; } SupportProgressDialogComponent.open(this.dialog, issue) .subscribe(() => { this.sessionService.delete(this.page?.id || ''); }); } createOnGithub(genUrl?: boolean) { if (!this.validate()) { return; } if (genUrl === undefined && this.haveGhAccount) { genUrl = true; } if (genUrl === undefined) { this.dialog.confirm({ canCancel: true, caption: 'Caution', header: 'Create Issue on GitHub', message: 'You can easily create the issue with your own GitHub account. Or create the GitHub issue privately, but then we will have no way to communicate with you for further information.', buttons: [ { id: 'createWithout', text: 'Create Without Account', class: 'outline' }, { id: 'openGithub', text: 'Use My Account' }, ] }) .onAction('openGithub', () => { this.createIssue('github', true) }) .onAction('createWithout', () => { this.createIssue('github', false) }) return; } } openIssue(issue: Issue) { this.integration.openExternal(issue.url); } createPrivateTicket() { if (!this.validate()) { return; } const opts: ConfirmDialogConfig = { caption: 'Info', canCancel: true, header: 'How should we stay in touch?', message: 'Please enter your email address so we can write back and forth until the issue is concluded.', inputModel: '', inputPlaceholder: 'Optional Email', inputType: 'text', buttons: [ { id: '', class: 'outline', text: 'Cancel' }, { id: 'create', text: 'Create Ticket' }, ], } this.dialog.confirm(opts) .onAction('create', () => { this.createIssue('private', undefined, opts.inputModel); }); } } ================================================ FILE: desktop/angular/src/app/pages/support/index.ts ================================================ export * from './support'; ================================================ FILE: desktop/angular/src/app/pages/support/pages.ts ================================================ export interface PageSections { title?: string; choices: SupportType[]; style?: 'small'; } export interface QuestionSection { title: string; help?: string; } export interface SupportPage { type?: undefined; id: string; title: string; shortHelp: string; repoHelp?: string; prologue?: string; epilogue?: string; sections: QuestionSection[]; privateTicket?: boolean; ghIssuePreset?: string; includeDebugData?: boolean; repositories?: { repo: string, name: string }[]; } export interface ExternalLink { type: 'link', url: string; title: string; shortHelp: string; } export type SupportType = SupportPage | ExternalLink; export const supportTypes: PageSections[] = [ { title: "Resources", choices: [ { type: 'link', title: '📘 Portmaster Wiki & FAQ', url: 'https://wiki.safing.io/?source=Portmaster', shortHelp: 'Search the Portmaster knowledge base and FAQ.', }, { type: 'link', title: '🔖 Settings Handbook', url: 'https://docs.safing.io/portmaster/settings?source=Portmaster', shortHelp: 'A reference document of all Portmaster settings.' }, { type: 'link', title: '📑 Safing Blog', url: 'https://safing.io/blog?source=Portmaster', shortHelp: 'Read our blog posts and announcements.', } ] }, { title: "Communities & Support", style: 'small', choices: [ { type: 'link', title: 'Join us on Discord', url: 'https://discord.gg/safing', shortHelp: 'Get help from the community and our AI bot on Discord.' }, { type: 'link', title: 'Follow us on Mastodon', url: 'https://fosstodon.org/@safing', shortHelp: 'Get updates and privacy jokes on Mastodon.' }, { type: 'link', title: 'Follow us on Twitter', url: 'https://twitter.com/SafingIO', shortHelp: 'Get updates and privacy jokes on Twitter.' }, { type: 'link', title: 'Safing Support via Email', url: 'mailto:support@safing.io', shortHelp: 'As a subscriber, reach out to the Safing team directly.' } ] }, { title: "Make a Report", style: 'small', choices: [ { id: "report-bug", title: "🐞 Report a Bug", shortHelp: "Found a bug? Report your discovery and make the Portmaster better for everyone.", repoHelp: "Where did the bug take place?", sections: [ { title: "What happened?", help: "Describe what happened in detail" }, { title: "What did you expect to happen?", help: "Describe what you expected to happen instead" }, { title: "How did you reproduce it?", help: "Describe how to reproduce the issue" }, { title: "Additional information", help: "Provide extra details if needed" }, ], includeDebugData: true, privateTicket: true, ghIssuePreset: "report-bug.md", repositories: [] }, { id: "give-feedback", title: "💡 Suggest an Improvement", shortHelp: "Suggest an enhancement or a new feature for Portmaster.", repoHelp: "What would you would like to improve?", sections: [ { title: "What would you like to add or change?", }, { title: "Why do you and others need this?" } ], includeDebugData: false, privateTicket: true, ghIssuePreset: "suggest-feature.md", repositories: [] }, { id: "compatibility-report", title: "📝 Make a Compatibility Report", shortHelp: "Report Portmaster in/compatibility with Linux Distros, VPN Clients or general Software.", sections: [ { title: "What worked?", help: "Describe what worked" }, { title: "What did not work?", help: "Describe what did not work in detail" }, { title: "Additional information", help: "Provide extra details if needed" }, ], includeDebugData: true, privateTicket: true, ghIssuePreset: "report-compatibility.md", repositories: [] // not needed with the default being "portmaster" }, ], } ] ================================================ FILE: desktop/angular/src/app/pages/support/progress-dialog/index.ts ================================================ export * from './progress-dialog'; ================================================ FILE: desktop/angular/src/app/pages/support/progress-dialog/progress-dialog.html ================================================
Status
Uploading debug data .... Creating GitHub issue ... Creating private support ticket ...
Ticket prepared successfully Ticket created successfully!
Use the following button to open the pre-filled GitHub issue form:
We successfully create the issue on GitHub for you.
Use the following link to check for updates:
{{ url }}
We will contact you as soon as possbile.
Failed to create Support Ticket An error occured while creating your support ticket: {{ error || 'Unknown Error' }}
================================================ FILE: desktop/angular/src/app/pages/support/progress-dialog/progress-dialog.ts ================================================ import { ComponentPortal } from "@angular/cdk/portal"; import { HttpErrorResponse } from "@angular/common/http"; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentRef, EventEmitter, OnInit, inject } from "@angular/core"; import { SFNG_DIALOG_REF, SfngDialogRef, SfngDialogService } from "@safing/ui"; import { Observable, map, mergeMap, of } from "rxjs"; import { INTEGRATION_SERVICE } from "src/app/integration"; import { SupportHubService, SupportSection } from "src/app/services"; import { ActionIndicatorService } from "src/app/shared/action-indicator"; export interface TicketData { debugInfo: string; repo: string; title: string; sections: SupportSection[]; } export interface GithubIssue extends TicketData { type: 'github', generateUrl?: boolean; preset?: string; } export interface PrivateTicket extends TicketData { type: 'private', email?: string, } export type TicketInfo = GithubIssue | PrivateTicket; @Component({ templateUrl: './progress-dialog.html', changeDetection: ChangeDetectionStrategy.OnPush, styles: [ ` :host { @apply block flex flex-col gap-8 relative; } `, ] }) export class SupportProgressDialogComponent implements OnInit { /** Static method to open the support-progress dialog. */ static open(dialog: SfngDialogService, data: TicketInfo): Observable { const ref = dialog.create(SupportProgressDialogComponent, { data, dragable: true, backdrop: false, autoclose: false, }); return (ref.contentRef() as ComponentRef) .instance .done; } private readonly cdr = inject(ChangeDetectorRef); private readonly supporthub = inject(SupportHubService); private readonly uai = inject(ActionIndicatorService); readonly integration = inject(INTEGRATION_SERVICE); readonly dialogRef: SfngDialogRef = inject(SFNG_DIALOG_REF); /** Holds the current state of the issue-creation */ state: '' | 'debug-info' | 'create-issue' | 'create-ticket' | 'done' | 'error' = ''; /** The URL to the github issue once it was created. */ url: string = ''; /** The error message if one occured */ error: string = ''; /** Emits once the issue has been created successfully */ done = new EventEmitter; ngOnInit(): void { this.createSupportRequest(); } setState(state: typeof this['state']) { this.state = state; this.cdr.detectChanges(); } createSupportRequest(): void { const data = this.dialogRef.data; let stream = of('') // Upload debug info if (data.debugInfo) { stream = new Observable((observer) => { this.state = 'debug-info'; this.cdr.detectChanges(); this.supporthub.uploadText('debug-info', data.debugInfo) .subscribe(observer); }) } // either create on github or create a private ticket through support-hub if (data.type === 'github') { stream = stream.pipe( mergeMap((url) => { this.state = 'create-issue'; this.cdr.detectChanges(); return this.supporthub.createIssue( data.repo, data.preset || '', data.title, data.sections, url, { generateUrl: data.generateUrl || false }, ); }) ) } else { stream = stream.pipe( mergeMap((url) => { this.state = 'create-ticket'; this.cdr.markForCheck(); return this.supporthub.createTicket( data.repo, data.title, data.email || '', data.sections, url ) }), map(() => '') ) } stream.subscribe({ next: (url) => { this.state = 'done'; this.url = url; this.cdr.markForCheck(); this.done.next(); }, error: (err) => { console.error("error", err); this.state = 'error'; if (err instanceof HttpErrorResponse && err.error instanceof ProgressEvent) { this.error = err.statusText; } else { this.error = this.uai.getErrorMessage(err); } this.cdr.markForCheck(); } }); } copyUrl() { if (!this.url) { return } this.integration.writeToClipboard(this.url) .then(() => this.uai.success('URL Copied To Clipboard')) .catch(err => this.uai.error('Failed to Copy To Clipboard', this.uai.getErrorMessage(err))) } } ================================================ FILE: desktop/angular/src/app/pages/support/support.html ================================================

{{section.title}}

{{item.title}}

================================================ FILE: desktop/angular/src/app/pages/support/support.scss ================================================ :host { width: 100%; display: flex; flex-direction: column; height: 100%; overflow: auto; } .list-container { display: flex; flex-direction: column; align-items: center; justify-content: flex-start; .section-title { margin-top: 20px; margin-bottom: 40px; position: relative; border-bottom: 1px solid rgba(255, 255, 255, .2); h4 { position: absolute; top: -0.5rem; background-color: var(--background); @apply pr-8; } } .page-section { width: 100%; display: flex; justify-content: stretch; align-items: stretch; flex-direction: column; @apply px-4; @media (min-width: 1250px) { max-width: 800px; } } .option-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); grid-gap: 20px; grid-auto-rows: 1fr; width: 100%; margin-bottom: 20px; section { @apply bg-cards-secondary; @apply p-8; @apply rounded; transition: all 250ms ease-in-out; position: relative; cursor: pointer; &:hover { @apply bg-cards-tertiary; } fa-icon { position: absolute; top: 1rem; right: 1rem; opacity: .4; } } } .small .option-list section { @apply p-4; } } ================================================ FILE: desktop/angular/src/app/pages/support/support.ts ================================================ import { Component, DestroyRef, OnInit, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { Router } from '@angular/router'; import { BehaviorSubject, combineLatest, debounceTime } from 'rxjs'; import { Issue, SupportHubService } from 'src/app/services'; import { fadeInAnimation, fadeInListAnimation } from 'src/app/shared/animations'; import { FuzzySearchService } from 'src/app/shared/fuzzySearch'; import { SupportType, supportTypes } from './pages'; import { INTEGRATION_SERVICE } from 'src/app/integration'; @Component({ templateUrl: './support.html', styleUrls: ['./support.scss'], animations: [ fadeInListAnimation, fadeInAnimation, ] }) export class SupportPageComponent implements OnInit { // make supportTypes available in the page template. readonly supportTypes = supportTypes; private readonly destroyRef = inject(DestroyRef); private readonly integration = inject(INTEGRATION_SERVICE); /** @private The current search term for the FAQ entries. */ searchFaqs = new BehaviorSubject(''); searchTerm: string = ''; /** A list of all faq entries loaded from the Support Hub */ allFaqEntries: Issue[] = []; /** A list of faq entries to show */ faqEntries: Issue[] = []; constructor( private router: Router, private searchService: FuzzySearchService, private supportHub: SupportHubService, ) { } ngOnInit(): void { combineLatest([ this.searchFaqs, this.supportHub.loadIssues() ]) .pipe( takeUntilDestroyed(this.destroyRef), debounceTime(200), ) .subscribe(([searchTerm, allFaqEntries]) => { this.allFaqEntries = allFaqEntries .filter(issue => issue.labels?.includes("faq")) .map(issue => { return { ...issue, title: issue.title.replace("FAQ: ", "") } }) if (searchTerm === '') { this.faqEntries = [ ...this.allFaqEntries ] return; } this.faqEntries = this.searchService.searchList(this.allFaqEntries, searchTerm, { disableHighlight: true, shouldSort: true, isCaseSensitive: false, minMatchCharLength: 3, keys: [ 'title', 'body', ], }).map(res => res.item) }) } openIssue(issue: Issue) { this.integration.openExternal(issue.url); } openPage(item: SupportType) { if (item.type === 'link') { this.integration.openExternal(item.url); return; } this.router.navigate(['/support', item.id]); } } ================================================ FILE: desktop/angular/src/app/prompt-entrypoint/prompt-entrypoint.ts ================================================ import { CommonModule } from "@angular/common"; import { Component, OnInit, TrackByFunction, inject } from "@angular/core"; import { AppProfile, AppProfileService, PortapiService } from "@safing/portmaster-api"; import { combineLatest, forkJoin, map, of, switchMap } from "rxjs"; import { ConnectionPrompt, NotificationType, NotificationsService } from "../services"; import { SfngAppIconModule } from "../shared/app-icon"; import { getCurrentWindow } from '@tauri-apps/api/window'; import { CountryFlagModule } from "../shared/country-flag"; interface Prompt { prompts: ConnectionPrompt[]; profile: AppProfile; } @Component({ standalone: true, selector: 'app-root', templateUrl: './prompt.html', imports: [ CommonModule, SfngAppIconModule, CountryFlagModule ] }) export class PromptEntryPointComponent implements OnInit { private readonly notificationService = inject(NotificationsService); private readonly portapi = inject(PortapiService); private readonly profileService = inject(AppProfileService); prompts: Prompt[] = []; trackPrompt: TrackByFunction = (_, p) => p.EventID; trackProfile: TrackByFunction = (_, p) => p.profile._meta!.Key; ngOnInit(): void { this.notificationService .new$ .pipe( map(notifs => { return notifs.filter(n => n.Type === NotificationType.Prompt && n.EventID.startsWith("filter:prompt")) }), switchMap(notifications => { const distictProfiles = new Map(); notifications.forEach(n => { const key = `${n.EventData!.Profile.Source}/${n.EventData!.Profile.ID}` const arr = distictProfiles.get(key) || []; arr.push(n); distictProfiles.set(key, arr); }); if (distictProfiles.size === 0) { return of([]); } return combineLatest(Array.from(distictProfiles.entries()).map(([key, prompts]) => forkJoin({ profile: this.profileService.getAppProfile(key), prompts: of(Array.from(prompts)) }))); }) ) .subscribe(result => { this.prompts = result; // show the prompt now since we're ready if (this.prompts.length) { getCurrentWindow()!.show(); } }) } selectAction(prompt: ConnectionPrompt, action: string) { prompt.SelectedActionID = action; this.portapi.update(prompt._meta!.Key, prompt) .subscribe(); } } ================================================ FILE: desktop/angular/src/app/prompt-entrypoint/prompt.html ================================================

Portmaster

{{ prompt.profile.Name }} {{ prompt.profile.LinkedPath }}
Domain: {{ prompt.EventData?.Entity?.Domain || 'N/A' }}
IP: {{ prompt.EventData?.Entity?.IP || 'N/A' }}
================================================ FILE: desktop/angular/src/app/services/index.ts ================================================ export { NotificationsService } from './notifications.service'; export * from './notifications.types'; export * from './session-data.service'; export { StatusService } from './status.service'; export * from './status.types'; export * from './supporthub.service'; export * from './ui-state.service'; ================================================ FILE: desktop/angular/src/app/services/notifications.service.spec.ts ================================================ import { TestBed } from '@angular/core/testing'; import { WebsocketService } from '@safing/portmaster-api'; import { MockWebSocketSubject } from '@safing/portmaster-api/testing'; import { PartialObserver } from 'rxjs'; import { NotificationsService } from './notifications.service'; import { Notification, NotificationType } from './notifications.types'; describe('NotificationsService', () => { let service: NotificationsService; let mock: MockWebSocketSubject; beforeEach(() => { TestBed.configureTestingModule({ providers: [ { provide: WebsocketService, useValue: MockWebSocketSubject, } ] }); service = TestBed.inject(NotificationsService); mock = MockWebSocketSubject.lastMock!; }); afterEach(() => { mock.close(); }) it('should be created', () => { expect(service).toBeTruthy(); }); it('should allow to query for notifications', () => { const observer = createSpyObserver(); service.query("updates:").subscribe(observer); mock.expectLastMessage() mock.expectLastMessage('type').toBe('query') mock.expectLastMessage('query').toBe('notifications:all/updates:') mock.lastMultiplex!.next({ id: mock.lastRequestId!, type: 'ok', data: { ID: 'updates:core-update-available', Message: 'Update available', }, key: 'notifications:all/updates:core-update-available' }) mock.lastMultiplex!.next({ id: mock.lastRequestId!, type: 'ok', data: { ID: 'updates:ui-reload-required', Message: 'UI reload required', }, key: 'notifications:all/updates:ui-reload-required' }) // query collects all notifications using toArray // so nothing should be nexted yet. expect(observer.next).not.toHaveBeenCalled() expect(observer.error).not.toHaveBeenCalled() expect(observer.complete).not.toHaveBeenCalled() // finish the strea mock.lastMultiplex!.next({ id: mock.lastRequestId!, type: 'done' }) expect(observer.next).toHaveBeenCalledWith([ { ID: 'updates:core-update-available', Message: 'Update available', }, { ID: 'updates:ui-reload-required', Message: 'UI reload required', } ]) expect(observer.error).not.toHaveBeenCalled() expect(observer.complete).toHaveBeenCalled() }); describe('execute notification actions', () => { it('should work using a notif object', () => { let observer = createSpyObserver(); let notif: any = { ID: 'updates:core-update-available', Message: 'An update is available', Type: NotificationType.Info, AvailableActions: [{ ID: "restart", Text: "Restart" }], } service.execute(notif, "restart").subscribe(observer); expect(observer.error).not.toHaveBeenCalled() mock.expectLastMessage('type').toBe('update'); mock.expectLastMessage('key').toBe('notifications:all/updates:core-update-available'); mock.expectLastMessage('data').toEqual({ ID: 'updates:core-update-available', SelectedActionID: 'restart', }); mock.lastMultiplex!.next({ id: mock.lastRequestId!, type: 'success' }) expect(observer.next).toHaveBeenCalledWith(undefined); expect(observer.error).not.toHaveBeenCalled(); expect(observer.complete).toHaveBeenCalled(); }); it('should throw when executing an unknown action using a notif object', () => { let observer = createSpyObserver(); let notif: any = { ID: 'updates:core-update-available', Message: 'An update is available', Type: NotificationType.Info, AvailableActions: [{ ID: "restart", Text: "Restart" }], } service.execute(notif, "restart-with-typo").subscribe(observer); expect(observer.error).toHaveBeenCalled() expect(mock.lastMessageSent).toBeUndefined(); }); it('should work using a key', () => { let observer = createSpyObserver(); service.execute("updates:core-update-available", "restart").subscribe(observer); expect(observer.error).not.toHaveBeenCalled() mock.expectLastMessage('type').toBe('update'); mock.expectLastMessage('key').toBe('notifications:all/updates:core-update-available'); mock.expectLastMessage('data').toEqual({ ID: 'updates:core-update-available', SelectedActionID: 'restart', }); mock.lastMultiplex!.next({ id: mock.lastRequestId!, type: 'success' }) expect(observer.next).toHaveBeenCalledWith(undefined); expect(observer.error).not.toHaveBeenCalled(); expect(observer.complete).toHaveBeenCalled(); }); }) describe('resolving pending actions', () => { it('should work using a notif object', () => { let observer = createSpyObserver(); let notif: any = { ID: 'updates:core-update-available', Message: 'An update is available', Type: NotificationType.Info, Responded: Math.round(Date.now() / 1000), SelectedActionID: "restart", } service.resolvePending(notif, 100).subscribe(observer) expect(observer.error).not.toHaveBeenCalled() mock.expectLastMessage('type').toBe('update'); mock.expectLastMessage('key').toBe('notifications:all/updates:core-update-available'); mock.expectLastMessage('data').toEqual({ ID: 'updates:core-update-available', Executed: 100, }); mock.lastMultiplex!.next({ id: mock.lastRequestId!, type: 'success' }) expect(observer.next).toHaveBeenCalledWith(undefined); expect(observer.error).not.toHaveBeenCalled(); expect(observer.complete).toHaveBeenCalled(); }); it('should throw on an executed notification using a notif object', () => { let observer = createSpyObserver(); let notif: any = { ID: 'updates:core-update-available', Message: 'An update is available', Type: NotificationType.Info, SelectedActionID: 'restart', Responded: Math.round(Date.now() / 1000), Executed: Math.round(Date.now() / 1000), } service.resolvePending(notif).subscribe(observer); expect(observer.error).toHaveBeenCalled() expect(mock.lastMessageSent).toBeUndefined(); }); it('should work using a key', () => { let observer = createSpyObserver(); service.resolvePending("updates:core-update-available", 100).subscribe(observer); expect(observer.error).not.toHaveBeenCalled() mock.expectLastMessage('type').toBe('update'); mock.expectLastMessage('key').toBe('notifications:all/updates:core-update-available'); mock.expectLastMessage('data').toEqual({ ID: 'updates:core-update-available', Executed: 100, }); mock.lastMultiplex!.next({ id: mock.lastRequestId!, type: 'success' }) expect(observer.next).toHaveBeenCalledWith(undefined); expect(observer.error).not.toHaveBeenCalled(); expect(observer.complete).toHaveBeenCalled(); }); }); describe('watching notifications', () => { it('should be possible to watch for new and action-required notifs only', () => { const observer = createSpyObserver(); service.new$.subscribe(observer); let send = (msg: any) => { mock.lastMultiplex!.next({ id: mock.lastRequestId!, data: msg, type: 'ok', key: "notifications:all/" + msg.ID, }) } let n1 = { ID: "new-notif-1", Message: "a new notification", Responded: 0, Executed: 0, Expires: Math.round(Date.now() / 1000) + 60 * 60, } let n2 = { ID: "new-notif-2", Message: "a new notification", Responded: 0, Executed: 0, Expires: 0, AvailableActions: [{ ID: "action-id", Text: "some action" }], } let expired = { ID: "new-notif-3", Message: "a new notification", Responded: 0, Executed: 0, Expires: 100, } let pending = { ID: "new-notif-4", Message: "a new notification", Responded: Math.round(Date.now() / 1000), Executed: 0, SelectedActionID: "test", } send(n1) send(expired) send(n2) send(pending) expect(observer.complete).not.toHaveBeenCalled() expect(observer.error).not.toHaveBeenCalled() expect(observer.next).toHaveBeenCalledTimes(2) expect(observer.next).toHaveBeenCalledWith(n1) expect(observer.next).toHaveBeenCalledWith(n2) }) }) describe('creating notifications', () => { it('should be possible using an object', () => { let notification: Partial> = { ID: 'my-awesome-notification', AvailableActions: [ { ID: 'action-no', Text: 'No' }, { ID: 'force-no', Text: 'Hell No' } ], Message: 'Update complete, do you want to reboot?', Persistent: true, Type: NotificationType.Warning, } let observer = createSpyObserver(); service.create(notification).subscribe(observer); expect(observer.error).not.toHaveBeenCalled(); mock.expectLastMessage('type').toBe('create') mock.expectLastMessage('key').toBe('notifications:all/my-awesome-notification') mock.expectLastMessage('data').toEqual(notification); expect(notification.Created).toBeTruthy(); mock.lastMultiplex!.next({ type: 'success', id: mock.lastRequestId!, }) expect(observer.complete).toHaveBeenCalled() expect(observer.error).not.toHaveBeenCalled() expect(observer.next).toHaveBeenCalledWith(undefined) }) it('should be possible using parameters', () => { let observer = createSpyObserver(); service.create('my-param-notification', 'message', NotificationType.Prompt, { Persistent: true, Created: 100, }).subscribe(observer); expect(observer.error).not.toHaveBeenCalled(); mock.expectLastMessage('type').toBe('create') mock.expectLastMessage('key').toBe('notifications:all/my-param-notification') mock.expectLastMessage('data').toEqual({ Type: NotificationType.Prompt, ID: 'my-param-notification', Message: 'message', Created: 100, Persistent: true, }); mock.lastMultiplex!.next({ type: 'success', id: mock.lastRequestId!, }) expect(observer.complete).toHaveBeenCalled() expect(observer.error).not.toHaveBeenCalled() expect(observer.next).toHaveBeenCalledWith(undefined) }) }) }); function createSpyObserver(): PartialObserver { return jasmine.createSpyObj("observer", ["next", "error", "complete"]) } ================================================ FILE: desktop/angular/src/app/services/notifications.service.ts ================================================ import { HttpClient } from '@angular/common/http'; import { Injectable, TrackByFunction, inject } from '@angular/core'; import { Params, Router } from '@angular/router'; import { PortapiService, RetryableOpts } from '@safing/portmaster-api'; import { BehaviorSubject, Observable, combineLatest, defer, throwError } from 'rxjs'; import { map, share, toArray } from 'rxjs/operators'; import { environment } from 'src/environments/environment'; import { ActionIndicatorService } from '../shared/action-indicator'; import { Action, ActionHandler, NetqueryAction, Notification, NotificationState, NotificationType, OpenPageAction, OpenProfileAction, OpenSettingAction, OpenURLAction, PageIDs, WebhookAction } from './notifications.types'; import { VirtualNotification } from './virtual-notification'; import { INTEGRATION_SERVICE } from '../integration'; @Injectable({ providedIn: 'root' }) export class NotificationsService { private readonly integration = inject(INTEGRATION_SERVICE); /** * A {@link TrackByFunction} from tracking notifications. */ static trackBy: TrackByFunction> = function (_: number, n: Notification) { return n.EventID; }; /** * This object contains handler methods for all * notification action types we currently support. */ private actionHandler: { [key in Action['Type']]: (a: any) => Promise; } = { '': async () => { }, 'open-url': async (a: OpenURLAction) => { await this.integration.openExternal(a.Payload); }, 'open-profile': (a: OpenProfileAction) => this.router.navigate([ '/app', ...a.Payload.split('/') ]), 'open-setting': (a: OpenSettingAction) => { if (a.Payload.Profile) { return this.router.navigate(['/app', ...a.Payload.Profile.split('/')], { queryParams: { setting: a.Payload.Key, tab: 'settings' } }) } return this.router.navigate(['/settings'], { queryParams: { setting: a.Payload.Key } }) }, "open-page": (a: OpenPageAction) => { let pageID: keyof typeof PageIDs | null = null; let queryParams: Params | null = null; if (typeof a.Payload === 'string') { pageID = a.Payload; queryParams = {}; } else { pageID = a.Payload.id; queryParams = a.Payload.query; } const url = PageIDs[pageID]; if (!!url) { return this.router.navigate([url], { queryParams, }) } return Promise.reject('not yet supported'); }, "ui": (a: ActionHandler) => { return a.Run(a); }, "netquery": (a: NetqueryAction) => { return this.router.navigate(['/monitor'], { queryParams: { q: a.Payload, } }) }, "call-webhook": (a: WebhookAction) => { let method = a.Payload.Method; if (method === '') { if (a.Payload.Payload !== undefined && a.Payload.Payload !== null) { method = 'PUT' } else { method = 'POST' } } let req = this.http.request( method, `${environment.httpAPI}/v1/${a.Payload.URL}`, { body: a.Payload.Payload, observe: 'response', responseType: 'arraybuffer', } ) return new Promise((resolve, reject) => { const observer = this.actionIndicator.httpObserver(); req.subscribe({ next: res => { if (a.Payload.ResultAction === 'display') { if (!!observer?.next) { observer.next(res) } } resolve(res); }, error: err => { if (!!observer?.error) { observer.error(err); } reject(err); }, }) }) } }; // For testing purposes only VirtualNotification = VirtualNotification; /** A map of virtual notifications */ private _virtualNotifications = new Map>(); /* Emits all virtual notifications whenever they change */ private _virtualNotificationChange = new BehaviorSubject[]>([]); /* A copy of the static trackBy function. */ trackBy = NotificationsService.trackBy; /** The prefix that all notifications have */ readonly notificationPrefix = "notifications:all/"; /** new$ emits new (active) notifications as they arrive */ readonly new$: Observable[]>; constructor( private portapi: PortapiService, private router: Router, private http: HttpClient, private actionIndicator: ActionIndicatorService, ) { this.new$ = this.watchAll().pipe( src => this.injectVirtual(src), map(msgs => { return msgs.filter(msg => msg.State === NotificationState.Active || !msg.State) }), share({ connector: () => new BehaviorSubject[]>([]) }) ); } /** * Inject a new virtual notification. If not configured otherwise, * the notification is automatically removed when executed. */ inject(notif: VirtualNotification, { autoRemove } = { autoRemove: true }) { this._virtualNotifications.set(notif.EventID, notif); this._virtualNotificationChange.next( Array.from(this._virtualNotifications.values()) ) if (autoRemove) { notif.executed.subscribe({ complete: () => this.deject(notif) }); } } /** Deject (remove) a virtual notification. */ deject(notif: VirtualNotification) { this._virtualNotifications.delete(notif.EventID); this._virtualNotificationChange.next( Array.from(this._virtualNotifications.values()) ) } /** A {@link MonoOperatorFunction} that injects all virtual observables into the source. */ private injectVirtual(obs: Observable[]>): Observable { return combineLatest([ obs, this._virtualNotificationChange, ]).pipe( map(([real, virtual]) => { return [ ...real, ...virtual, ] }) ) } /** * Watch all notifications that match a query. * * * @param query The query to watch. Defaulta to all notifcations * @param opts Optional retry configuration options. */ watchAll(query: string = '', opts?: RetryableOpts): Observable[]> { return this.portapi.watchAll>(this.notificationPrefix + query, opts); } /** * Query the backend for a list of notifications. In contrast * to {@class PortAPI} query collects all results into an array * first which makes it convenient to be used in *ngFor and * friends. See {@function trackNotification} for a suitable track-by * function. * * @param query The search query. */ query(query: string): Observable[]> { return this.portapi.query>(this.notificationPrefix + query) .pipe( map(value => value.data), toArray() ) } /** * Returns the notification by ID. * * @param id The ID of the notification */ get(id: string): Observable> { return this.portapi.get(this.notificationPrefix + id) } /** * Execute an action attached to a notification. * * @param n The notification object. * @param actionId The ID of the action to execute. */ execute(n: Notification, action: Action): Observable; /** * Execute an action attached to a notification. * * @param notificationId The ID of the notification. * @param actionId The ID of the action to execute. */ execute(notificationId: string, action: Action): Observable; // overloaded implementation of execute execute(notifOrId: Notification | string, action: Action): Observable { const payload: Partial> = {}; if (typeof notifOrId === 'string') { payload.EventID = notifOrId; } else { payload.EventID = notifOrId.EventID; } // if it's a virtual notification we should let it handle the action // on it's own. if (!!this._virtualNotifications.get(payload.EventID)) { return defer(async () => { const notif = this._virtualNotifications.get(payload.EventID!); if (!!notif) { notif.selectAction(action.ID); } }) } return defer(async () => { try { await this.performAction(action); // finally, if there's an action ID, mark the notification as resolved. if (!!action.ID) { payload.SelectedActionID = action.ID; const key = this.notificationPrefix + payload.EventID; await this.portapi.update(key, payload).toPromise(); } } catch (err: any) { const msg = this.actionIndicator.getErrorMessgae(err); this.actionIndicator.error('Internal Error', 'Failed to perform action: ' + msg) } }) } async performAction(action: Action) { // if there's an action type defined execute the handler. if (!!action.Type) { const handler = this.actionHandler[action.Type] as (a: Action) => Promise; if (!!handler) { console.log(action); await handler(action); } else { this.actionIndicator.error('Internal Error', 'Cannot handle action type ' + action.Type) } } } /** * Resolve a pending notification execution. * * @param n The notification object to resolve the pending execution. * @param time optional The time at which the pending execution took place */ resolvePending(n: Notification, time?: number): Observable; /** * Resolve a pending notification execution. * * @param n The notification ID to resolve the pending execution. * @param time optional The time at which the pending execution took place */ resolvePending(n: string, time?: number): Observable; // overloaded implementation of resolvePending. resolvePending(notifOrID: Notification | string, time: number = (Math.round(Date.now() / 1000))): Observable { const payload: Partial> = {}; if (typeof notifOrID === 'string') { payload.EventID = notifOrID; } else { payload.EventID = notifOrID.EventID; if (notifOrID.State === NotificationState.Executed) { return throwError(`Notification ${notifOrID.EventID} already executed`); } } payload.State = NotificationState.Responded; const key = this.notificationPrefix + payload.EventID return this.portapi.update(key, payload); } /** * Delete a notification. * * @param n The notification to delete. */ delete(n: Notification): Observable; /** * Delete a notification. * * @param n The notification to delete. */ delete(id: string): Observable; // overloaded implementation of delete. delete(notifOrId: Notification | string): Observable { return this.portapi.delete(typeof notifOrId === 'string' ? notifOrId : notifOrId.EventID); } /** * Create a new notification. * * @param n The notification to create. */ create(n: Partial>): Observable; /** * Create a new notification. * * @param id The ID of the notificaiton. * @param message The default message of the notificaiton. * @param type The notification type * @param args Additional arguments for the notification. */ create(id: string, message: string, type: NotificationType, args?: Partial>): Observable; // overloaded implementation of create. create(notifOrId: Partial> | string, message?: string, type?: NotificationType, args?: Partial>): Observable { if (typeof notifOrId === 'string') { notifOrId = { ...args, EventID: notifOrId, State: NotificationState.Active, Message: message, Type: type, } as Notification; // it's actual Partial but that's fine. } if (!notifOrId.EventID) { return throwError(`Notification ID is required`); } if (!notifOrId.Message) { return throwError(`Notification message is required`); } if (typeof notifOrId.Type !== 'number') { return throwError(`Notification type is required`); } return this.portapi.create(this.notificationPrefix + notifOrId.EventID, notifOrId); } } ================================================ FILE: desktop/angular/src/app/services/notifications.types.ts ================================================ import { getEnumKey, IntelEntity, Record } from '@safing/portmaster-api'; /** * BaseAction defines a user selectable action and can * be attached to a notification. Once selected, * the action's ID is set as the SelectedActionID * of the notification. */ export interface BaseAction { // ID uniquely identifies the action. It's safe to // use ID to select a localizable template to use // instead of the Text property. If Type is set // to None the ID may be empty, signifying that this // action is merely to dismiss the notification. ID: string; // Text is the (default) text for the action label. Text: string; // Visibility specifies where the action should be visible. Default is always visible. // Possible values are: // - "": Action is always visible. // - "detailed": Action is only visible in the detailed view of a notification. // - "in-app-only": Visible only in the UI app, never on the system level (if ShowOnSystem is true). Visibility: string; } export interface GenericAction extends BaseAction { Type: ''; } export interface OpenURLAction extends BaseAction { Type: 'open-url'; Payload: string; } export interface OpenPageAction extends BaseAction { Type: 'open-page'; Payload: keyof typeof PageIDs | { id: keyof typeof PageIDs, query: { [key: string]: string, } }; } export interface NetqueryAction extends BaseAction { Type: 'netquery'; Payload: string; } /** * PageIDs holds a list of pages that can be opened using * the OpenPageAction. */ export const PageIDs = { 'monitor': '/monitor', 'support': '/support', 'settings': '/settings', 'apps': '/app/overview', 'spn': '/spn', } export interface OpenSettingAction extends BaseAction { Type: 'open-setting'; Payload: { Key: string; Profile?: string; } } export interface OpenProfileAction extends BaseAction { Type: 'open-profile'; Payload: string; } export interface WebhookAction extends BaseAction { Type: 'call-webhook'; Payload: { Method: string; URL: string; Payload: any; ResultAction: 'ignore' | 'display'; } } export interface ActionHandler extends BaseAction { Type: 'ui' Run: (vn: T) => Promise; Payload: T; } export type Action = GenericAction | OpenURLAction | OpenPageAction | OpenSettingAction | OpenProfileAction | WebhookAction | NetqueryAction | ActionHandler; /** All action types that perform in-application routing. */ export const routingActions = new Set([ 'open-page', 'open-profile', 'open-setting' ]) /** * Available types of notifications. Notification * types are mainly for filtering and style related * decisions. */ export enum NotificationType { // Info is an informational message only. Info = 0, // Warning is a warning message. Warning = 1, // Prompt asks the user for a decision. Prompt = 2, // Error is for error notifications and module // failure status. Error = 3, } export interface ConnectionPromptData { Profile: { ID: string; LinkedPath: string; Source: 'local'; }; Entity: IntelEntity; } /** * Returns a string representation of the notifcation type. * * @param val The notifcation type */ export function getNotificationTypeString(val: NotificationType): string { return getEnumKey(NotificationType, val) } /** * Each notification can be in one of six different states * that inform the client on how to handle the notification. */ export enum NotificationState { // Active describes a notification that is active, no expired and, // if actions are available, still waits for the user to select an // action. Active = "active", // Responded describes a notification where the user has already // selected which action to take but that action is still to be // performed. Responded = "responded", // Responded describes a notification where the user has already // selected which action to take but that action is still to be // performed. Executed = "executed", // Invalid is a UI-only state that is used when the state of a // notification is unknown. Invalid = "invalid", } export interface Notification extends Record { // EventID is used to identify a specific notification. It consists of // the module name and a per-module unique event id. // The following format is recommended: // : EventID: string; // GUID is a unique identifier for each notification instance. That is // two notifications with the same EventID must still have unique GUIDs. // The GUID is mainly used for system (Windows) integration and is // automatically populated by the notification package. Average users // don't need to care about this field. GUID: string; // Type is the notification type. It can be one of Info, Warning or Prompt. Type: NotificationType; // Message is the default message shown to the user if no localized version // of the notification is available. Note that the message should already // have any paramerized values replaced. Message may be formatted using // markdown. Message: string; // Title holds a short notification title that quickly informs the user // about the type of notification. Title: string; // Category holds an informative category for the notification and is mainly // used for presentation purposes. Category: string; // EventData contains an additional payload for the notification. This payload // may contain contextual data and may be used by a localization framework // to populate the notification message template. // If EventData implements sync.Locker it will be locked and unlocked together with the // notification. Otherwise, EventData is expected to be immutable once the // notification has been saved and handed over to the notification or database package. EventData: T | null; // Expires holds the unix epoch timestamp at which the notification expires // and can be cleaned up. // Users can safely ignore expired notifications and should handle expiry the // same as deletion. Expires: number; // State describes the current state of a notification. See State for // a list of available values and their meaning. State: NotificationState; // AvailableActions defines a list of actions that a user can choose from. AvailableActions: Action[]; // SelectedActionID is updated to match the ID of one of the AvailableActions // based on the user selection. SelectedActionID: string; } export type ConnectionPrompt = Notification; ================================================ FILE: desktop/angular/src/app/services/package.json ================================================ { "sideEffects": false } ================================================ FILE: desktop/angular/src/app/services/session-data.service.ts ================================================ import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { distinctUntilChanged, map } from 'rxjs/operators'; /** * SessionDataService is used to store transient data * that are only important as long as the application is * being used. Those data are not presisted and are * removed once the application is restarted. */ @Injectable({ providedIn: 'root' }) export class SessionDataService { private data = new Map(); private stream = new BehaviorSubject(undefined); /** Set sets a value in the session data service */ set(key: string, value: T): void { this.data.set(key, value); } get(key: string): T | null; get(key: string, def: T): T; /** Get retrieves a value from the session data service */ get(key: string, def?: any): any { const value = this.data.get(key); if (value !== undefined) { return value; } if (def !== undefined) { return def; } return null; } watch(key: string): Observable; watch(key: string, def: T): Observable; /** Watch a key for changes to it's identity. */ watch(key: string, def?: any): Observable { return this.stream .pipe( map(() => this.get(key, def)), distinctUntilChanged() ); } delete(key: string): T | null { let value = this.get(key); if (value !== null) { this.data.delete(key); } return value; } save(id: string, model: M, keys: K[]) { let copy: Partial = {}; keys.forEach(key => copy[key] = model[key]); this.set(id, copy); } restore(id: string, model: M) { let copy: Partial | null = this.get(id); if (copy === null) { return; } Object.assign(model, copy); } } ================================================ FILE: desktop/angular/src/app/services/status.service.spec.ts ================================================ import { TestBed } from '@angular/core/testing'; import { StatusService } from './status.service'; describe('StatusService', () => { let service: StatusService; beforeEach(() => { TestBed.configureTestingModule({}); service = TestBed.inject(StatusService); }); it('should be created', () => { expect(service).toBeTruthy(); }); }); ================================================ FILE: desktop/angular/src/app/services/status.service.ts ================================================ import { Injectable, TrackByFunction } from '@angular/core'; import { PortapiService, RetryableOpts, SecurityLevel, WatchOpts, trackById } from '@safing/portmaster-api'; import { BehaviorSubject, Observable } from 'rxjs'; import { filter, map, repeat, share, toArray } from 'rxjs/operators'; import { CoreStatus, Subsystem, VersionStatus } from './status.types'; @Injectable({ providedIn: 'root' }) export class StatusService { /** * A {@link TrackByFunction} from tracking subsystems. */ static trackSubsystem: TrackByFunction = trackById; readonly trackSubsystem = StatusService.trackSubsystem; /** * status$ watches the global core status. It's mutlicasted using a BehaviorSubject so new * subscribers will automatically get the latest version while only one subscription * to the backend is held. */ readonly status$: Observable = this.portapi.qsub(`runtime:system/status`) .pipe( repeat({ delay: 2000 }), map(reply => reply.data), share({ connector: () => new BehaviorSubject(null) }), filter(value => value !== null), ) as Observable; // we filtered out the null values but we cannot make that typed with RxJS. constructor(private portapi: PortapiService) { } /** Returns the currently available versions for all resources. */ getVersions(): Observable { return this.portapi.get('core:status/versions') } /** * Selectes a new security level. SecurityLevel.Off means that * the auto-pilot should take over. * * @param securityLevel The security level to select */ selectLevel(securityLevel: SecurityLevel): Observable { return this.portapi.update(`runtime:system/security-level`, { SelectedSecurityLevel: securityLevel, }); } } ================================================ FILE: desktop/angular/src/app/services/status.types.ts ================================================ import { getEnumKey, Record, ReleaseLevel, SecurityLevel } from '@safing/portmaster-api'; export interface CaptivePortal { URL: string; IP: string; Domain: string; } export enum OnlineStatus { Unknown = 0, Offline = 1, Limited = 2, // local network only, Portal = 3, SemiOnline = 4, Online = 5, } /** * Converts a online status value to a string. * * @param stat The online status value to convert */ export function getOnlineStatusString(stat: OnlineStatus): string { return getEnumKey(OnlineStatus, stat) } export interface CoreStatus extends Record { OnlineStatus: OnlineStatus; CaptivePortal: CaptivePortal; Modules: StateUpdate[]; // TODO: Do we need all modules? WorstState: { Module: string, ID: string, Name: string, Message: string, Type: ModuleStateType, // Time: time.Time, // TODO: How do we best use Go's time.Time? Data: any } } export interface StateUpdate { Module: string; States: State[]; } export interface State { ID: string; // Program-unique identifier Name: string; // State name (may serve as notification title) Message?: string; // Detailed message about the state Type?: ModuleStateType; // State type Time?: Date; // Creation time Data?: any; // Additional data for processing } export enum ModuleStateType { Undefined = "", Hint = "hint", Warning = "warning", Error = "error" } /** * Returns a string representation of a failure status value. * * @param stateType The module state type value. */ export function getModuleStateString(stateType: ModuleStateType): string { return getEnumKey(ModuleStateType, stateType) } export interface Module { Enabled: boolean; Name: string; } export interface Subsystem extends Record { ConfigKeySpace: string; Description: string; ExpertiseLevel: string; ID: string; Modules: Module[]; Name: string; ReleaseLevel: ReleaseLevel; ToggleOptionKey: string; } export interface CoreVersion { // Copied from base/info/version.go Name: string; Version: string; VersionNumber: string; License: string; Source: string; BuildTime: string; CGO: boolean; Commit: string; CommitTime: string; Dirty: boolean; } export interface ResourceVersion { Available: boolean; BetaRelease: boolean; Blacklisted: boolean; StableRelease: boolean; VersionNumber: string; } export interface Resource { ActiveVersion: ResourceVersion | null; Identifier: string; SelectedVersion: ResourceVersion; Versions: ResourceVersion[]; } export interface VersionStatus extends Record { Channel: string; Core: CoreVersion; Resources: { [key: string]: Resource } } function getModuleStates(status: CoreStatus, moduleID: string): State[] { const module = status.Modules?.find(m => m.Module === moduleID); return module?.States || []; } /** * Retrieves a specific state from a module within the CoreStatus. * @param status The CoreStatus object containing module states. * @param moduleID The identifier of the module to search within. * @param stateID The identifier of the state to retrieve. * @returns The State object if found; otherwise, null. * @example * ```typescript * const state = GetModuleState(status, 'Control', 'control:paused'); * if (state) { * console.log(`State found: ${state.Name}`); * } else { * console.log('State not found'); * } * ``` */ export function GetModuleState(status: CoreStatus, moduleID: string, stateID: string): State | null { const states = getModuleStates(status, moduleID); for (const state of states) { if (state.ID === stateID) { return state; } } return null; } /** * Data structure for the 'control:paused' state from the 'Control' module. * * This interface defines the expected structure of the Data field when Portmaster * or its components are temporarily paused by the user. * * @example * ```typescript * const pausedState = GetModuleState(status, 'Control', 'control:paused'); * if (pausedState?.Data) { * const pauseData = pausedState.Data as ControlPauseStateData; * console.log(`SPN paused: ${pauseData.SPN}`); * } * ``` */ export interface ControlPauseStateData { Interception: boolean; // Whether Portmaster interception is paused SPN: boolean; // Whether SPN is paused TillTime: string; // When the pause will end (JSON date as string, has to be converted to Date) } ================================================ FILE: desktop/angular/src/app/services/supporthub.service.ts ================================================ import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { environment } from 'src/environments/environment'; export interface SupportSection { title: string; body: string; } export interface Issue { title: string; body: string; createdAt: CreatedAt; repository: string; url: string; user: string; closed?: boolean; labels: string[]; } @Injectable({ providedIn: 'root' }) export class SupportHubService { constructor(private http: HttpClient) { } loadIssues(): Observable { interface LoadIssuesResponse { issues: Issue[]; } return this.http.get(`${environment.supportHub}/api/v1/issues`) .pipe(map(res => res.issues.map(issue => ({ ...issue, createdAt: new Date(issue.createdAt), })).reverse())); } /** Uploads content under name */ uploadText(name: string, content: string): Observable { interface UploadResponse { urls: { [key: string]: string[]; } } const blob = new Blob([content], { type: 'text/plain' }); const data = new FormData(); data.set("file", blob, name); return this.http.post(`${environment.supportHub}/api/v1/upload`, data) .pipe(map(res => res.urls['file'][0])); } /** Create github issue */ createIssue(repo: string, preset: string, title: string, sections: SupportSection[], debugInfoUrl?: string, opts?: { generateUrl: boolean, }): Observable { interface CreateIssueResponse { url: string; } const req = { title, sections, debugInfoUrl } let params = new HttpParams(); if (!!opts?.generateUrl) { params = params.set('generate-url', '') } return this.http.post(`${environment.supportHub}/api/v1/issues/${repo}/${preset}`, req, { params }).pipe(map(r => r.url)) } createTicket(repoName: string, title: string, email: string, sections: SupportSection[], debugInfoUrl?: string): Observable { const req = { title, sections, debugInfoUrl, email, repoName, } return this.http.post(`${environment.supportHub}/api/v1/ticket`, req) } } ================================================ FILE: desktop/angular/src/app/services/ui-state.service.ts ================================================ import { Injectable } from "@angular/core"; import { PortapiService, Record } from '@safing/portmaster-api'; import { Observable, of } from "rxjs"; import { catchError, map, switchMap } from "rxjs/operators"; import { SortTypes } from './../shared/network-scout/network-scout'; export interface UIState extends Record { hideExitScreen?: boolean; introScreenFinished?: boolean; netscoutSortOrder: SortTypes; } const defaultState: UIState = { hideExitScreen: false, introScreenFinished: false, netscoutSortOrder: SortTypes.static } @Injectable({ providedIn: 'root' }) export class UIStateService { constructor(private portapi: PortapiService) { } uiState(): Observable { const key = 'core:ui/v1'; return this.portapi.get(key) .pipe( catchError(err => of(defaultState)), map(state => { (Object.keys(defaultState) as (keyof UIState)[]) .forEach(key => { if (state[key] === undefined) { (state as any)[key] = defaultState[key]! } }) return state }) ) } saveState(state: UIState): Observable { const key = 'core:ui/v1'; return this.portapi.create(key, state); } set(key: K, value: V): Observable { return this.uiState() .pipe( map(state => { state[key] = value return state; }), switchMap(newState => this.saveState(newState)) ); } } ================================================ FILE: desktop/angular/src/app/services/virtual-notification.ts ================================================ import { RecordMeta } from '@safing/portmaster-api'; import { BehaviorSubject } from 'rxjs'; import { filter } from 'rxjs/operators'; import { ActionHandler, Notification, NotificationState, NotificationType } from './notifications.types'; export class VirtualNotification implements Notification { readonly AvailableActions: ActionHandler[]; readonly Category: string; readonly EventData: T | null; readonly GUID: string = ''; // TODO(ppacher): should we fake it? readonly Expires: number; readonly _meta: RecordMeta; get State() { if (this.SelectedActionID === '') { return NotificationState.Active } return NotificationState.Executed } get SelectedActionID() { return this._selectedAction.getValue(); } /** Emits as soon as the user selects one of the notification actions. */ get executed() { return this._selectedAction.pipe( filter(action => action !== '') ); } /* Used to emit the selected action */ private _selectedAction = new BehaviorSubject(''); /** * Select and execute the action by ID. * * @param aid The ID of the action to execute. */ selectAction(aid: string) { this._selectedAction.next(aid); this._meta.Modified = new Date().valueOf() / 1000; const action = this.AvailableActions.find(a => a.ID === aid); if (!!action) { action.Run(action.Payload); } } constructor( public readonly EventID: string, public readonly Type: NotificationType, public readonly Title: string, public readonly Message: string, { AvailableActions, EventData, Category, Expires, }: { AvailableActions?: ActionHandler[]; EventData?: T | null; Category?: string, Expires?: number, } = {} ) { this.AvailableActions = AvailableActions || []; this.EventData = EventData || null; this.Category = Category || ''; this.Expires = Expires || 0; this._meta = { Created: new Date().valueOf() / 1000, Deleted: 0, Expires: this.Expires, Modified: new Date().valueOf() / 1000, Key: `notifications:all/${EventID}`, } } dispose() { this._selectedAction.complete(); } } ================================================ FILE: desktop/angular/src/app/shared/action-indicator/action-indicator.module.ts ================================================ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { IndicatorComponent } from "./indicator"; @NgModule({ imports: [ CommonModule, ], declarations: [ IndicatorComponent, ] }) export class ActionIndicatorModule { } ================================================ FILE: desktop/angular/src/app/shared/action-indicator/action-indicator.service.ts ================================================ import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { Injectable, InjectionToken, Injector, isDevMode } from '@angular/core'; import { interval, PartialObserver, Subject } from 'rxjs'; import { take, takeUntil } from 'rxjs/operators'; import { IndicatorComponent } from './indicator'; export interface ActionIndicator { title: string; message?: string; status: 'info' | 'success' | 'error'; timeout?: number; } export const ACTION_REF = new InjectionToken('ActionIndicatorRef') export class ActionIndicatorRef implements ActionIndicator { title: string; message?: string; status: 'info' | 'success' | 'error'; timeout?: number; onClose = new Subject(); onCloseReplace = new Subject(); constructor(opts: ActionIndicator, private _overlayRef: OverlayRef) { this.title = opts.title; this.message = opts.message; this.status = opts.status; this.timeout = opts.timeout; } close() { this._overlayRef.detach(); this.onClose.next(); this.onClose.complete(); } } @Injectable({ providedIn: 'root' }) export class ActionIndicatorService { private _activeIndicatorRef: ActionIndicatorRef | null = null; constructor( private _injector: Injector, private overlay: Overlay, ) { } /** * Returns an observer that parses the HTTP API response * and shows a success/error action indicator. */ httpObserver(successTitle?: string, errorTitle?: string): PartialObserver> { return { next: resp => { let msg = this.getErrorMessgae(resp) if (!successTitle) { successTitle = msg; msg = ''; } this.success(successTitle || '', msg) }, error: err => { let msg = this.getErrorMessgae(err); if (!errorTitle) { errorTitle = msg; msg = ''; } this.error(errorTitle || '', msg); } } } info(title: string, message?: string, timeout?: number) { this.create({ title, message: this.ensureMessage(message), timeout, status: 'info' }) } error(title: string, message?: string | any, timeout?: number) { this.create({ title, message: this.ensureMessage(message), timeout, status: 'error' }) } success(title: string, message?: string, timeout?: number) { this.create({ title, message: this.ensureMessage(message), timeout, status: 'success' }) } /** * Creates a new user action indicator. * * @param msg The action indicator message to show */ async create(msg: ActionIndicator) { if (!!this._activeIndicatorRef) { this._activeIndicatorRef.onCloseReplace.next(); await this._activeIndicatorRef.onClose.toPromise(); } const cfg = new OverlayConfig({ scrollStrategy: this.overlay .scrollStrategies.noop(), positionStrategy: this.overlay .position() .global() .bottom('2rem') .left('5rem'), }); const overlayRef = this.overlay.create(cfg); const ref = new ActionIndicatorRef(msg, overlayRef); ref.onClose.pipe(take(1)).subscribe(() => { if (ref === this._activeIndicatorRef) { this._activeIndicatorRef = null; } }) // close after the specified time our (or 5000 seconds). const timeout = msg.timeout || 5000; interval(timeout).pipe( takeUntil(ref.onClose), take(1), ).subscribe(() => { ref.close(); }) const injector = this.createInjector(ref); const portal = new ComponentPortal( IndicatorComponent, undefined, injector ); this._activeIndicatorRef = ref; overlayRef.attach(portal); } /** * Creates a new dependency injector that provides msg as * ACTION_MESSAGE. */ private createInjector(ref: ActionIndicatorRef): Injector { return Injector.create({ providers: [ { provide: ACTION_REF, useValue: ref, } ], parent: this._injector, }) } /** * Tries to extract a meaningful error message from msg. */ private ensureMessage(msg: string | any): string | undefined { if (msg === undefined || msg === null) { return undefined; } if (msg instanceof HttpErrorResponse) { return msg.message; } if (typeof msg === 'string') { return msg; } if (typeof msg === 'object') { if ('message' in msg) { return msg.message; } if ('error' in msg) { return this.ensureMessage(msg.error); } if ('toString' in msg) { return msg.toString(); } } return JSON.stringify(msg); } /** * Coverts an untyped body received by the HTTP API to a string. */ private stringifyBody(body: any): string { if (typeof body === 'string') { return body; } if (body instanceof ArrayBuffer) { return new TextDecoder('utf-8').decode(body); } if (typeof body === 'object') { return this.ensureMessage(body) || ''; } console.error('unsupported body', body); return ''; } /** * @deprecated use the version without a typo ... */ getErrorMessgae(resp: HttpResponse | HttpErrorResponse | Error): string { return this.getErrorMessage(resp) } /** * Parses a HTTP or HTTP Error response and returns a * message that can be displayed to the user. */ getErrorMessage(resp: HttpResponse | HttpErrorResponse | Error): string { try { let body: string | null = null; if (typeof resp === 'string') { return resp } if (resp instanceof Error) { return resp.message; } if (resp instanceof HttpErrorResponse) { // A client-side or network error occured. if (resp.error instanceof Error) { body = resp.error.message; } else { body = this.stringifyBody(resp.error); } if (!!body) { body = body[0].toLocaleUpperCase() + body.slice(1) return body } } if (resp instanceof HttpResponse) { let msg = ''; const ct = resp.headers.get('content-type') || ''; body = this.stringifyBody(resp.body); if (/application\/json/.test(ct)) { if (!!body) { msg = body; } } else if (/text\/plain/.test(ct)) { msg = body; } // Make the first letter uppercase if (!!msg) { msg = msg[0].toLocaleUpperCase() + msg.slice(1) return msg; } } console.error(`Unexpected error type`, resp) return `Unknown error: ${resp}` } catch (err: any) { console.error(err) return `Unknown error: ${resp}` } } } ================================================ FILE: desktop/angular/src/app/shared/action-indicator/index.ts ================================================ export * from './action-indicator.service'; export * from './action-indicator.module'; ================================================ FILE: desktop/angular/src/app/shared/action-indicator/indicator.html ================================================

{{ ref.title }}

{{ ref.message }}
================================================ FILE: desktop/angular/src/app/shared/action-indicator/indicator.scss ================================================ :host { box-shadow: 0px 0px 5px 2px rgba(0, 0, 0, 0.75); @apply bg-gray-200; @apply p-4; @apply rounded; position: relative; width: 20rem; display: flex; cursor: pointer; border-left: 2px solid transparent; .icon { display: flex; align-items: flex-start; flex-shrink: 1; margin-right: 1rem; padding-top: 2px; } &.error { @apply border-yellow; .icon { @apply text-yellow } } .indicator-content { display: flex; flex-direction: column; align-items: flex-start; h1 { font-size: 0.85rem; font-weight: 500; margin-bottom: 0; } .message, h1 { flex-shrink: 0; text-overflow: ellipsis; } .message { font-size: 0.7rem; flex-grow: 1; opacity: .5; span { display: block; height: 100%; word-break: keep-all; } } .close-icon { position: absolute; top: 1rem; right: 1rem; opacity: .7; cursor: pointer; &:hover { opacity: 1; } } h1~.message { margin-top: .5rem; } } } ================================================ FILE: desktop/angular/src/app/shared/action-indicator/indicator.ts ================================================ import { animate, state, style, transition, trigger } from '@angular/animations'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, HostListener, Inject, OnInit } from '@angular/core'; import { takeUntil } from 'rxjs/operators'; import { ActionIndicatorRef, ACTION_REF } from './action-indicator.service'; @Component({ templateUrl: './indicator.html', styleUrls: ['./indicator.scss'], changeDetection: ChangeDetectionStrategy.OnPush, animations: [ trigger('slideIn', [ state('void', style({ opacity: 0, transform: 'translateY(32px)' })), state('showing', style({ opacity: 1, transform: 'translateY(0px)' })), state('replace', style({ transform: 'translateY(0px) rotate(-3deg)', zIndex: -100, })), transition('showing => replace', animate('10ms cubic-bezier(0, 0, 0.2, 1)')), transition('void => *', animate('220ms cubic-bezier(0, 0, 0.2, 1)')), transition('showing => void', animate('220ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 0, transform: 'translateX(-100%)' }))), transition('replace => void', animate('220ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 0, transform: 'translateY(-64px) rotate(-3deg)' }))) ]) ] }) export class IndicatorComponent implements OnInit { constructor( @Inject(ACTION_REF) public ref: ActionIndicatorRef, public cdr: ChangeDetectorRef, ) { } @HostBinding('@slideIn') state = 'showing'; @HostBinding('class.error') isError = this.ref.status === 'error'; @HostListener('click') closeIndicator() { this.ref.close(); } @HostListener('@slideIn.done', ['$event']) onAnimationDone() { if (this.state === 'replace') { this.ref.close(); } } ngOnInit() { this.ref.onCloseReplace .pipe( takeUntil(this.ref.onClose), ) .subscribe(state => { this.state = 'replace'; this.cdr.detectChanges(); }) } } ================================================ FILE: desktop/angular/src/app/shared/animations.ts ================================================ import { animate, query, stagger, style, transition, trigger } from '@angular/animations'; export const fadeInAnimation = trigger( 'fadeIn', [ transition( ':enter', [ style({ opacity: 0, transform: 'translateY(-5px)' }), animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 1, transform: 'translateY(0px)' })) ] ), ] ); export const fadeOutAnimation = trigger( 'fadeOut', [ transition( ':leave', [ style({ opacity: 1, transform: 'translateY(0px)' }), animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 0, transform: 'translateY(-5px)' })) ] ), ] ); export const fadeInListAnimation = trigger( 'fadeInList', [ transition(':enter, * => 0, * => -1', []), transition(':increment', [ query(':enter', [ style({ opacity: 0 }), stagger(5, [ animate('300ms ease-out', style({ opacity: 1 })), ]), ], { optional: true }) ]), ] ) export const moveInOutLeftAnimation = trigger( 'moveInOutLeft', [ transition( ':enter', [ style({ opacity: 0, transform: 'translateX(-100%)' }), animate('.1s ease-in', style({ opacity: 1, transform: 'translateX(0%)' })) ] ), transition( ':leave', [ style({ opacity: 1, 'z-index': -100 }), animate('.1s ease-out', style({ opacity: 0, transform: 'translateX(-100%)' })) ] ) ] ) export const moveInOutAnimation = trigger( 'moveInOut', [ transition( ':enter', [ style({ opacity: 0, transform: 'translateX(100%)' }), animate('.2s ease-in', style({ opacity: 1, transform: 'translateX(0%)' })) ] ), transition( ':leave', [ style({ opacity: 1 }), animate('.2s ease-out', style({ opacity: 0, transform: 'translateX(100%)' })) ] ) ] ) export const moveInOutListAnimation = trigger( 'moveInOutList', [ transition(':enter, * => 0, * => -1', []), transition(':increment', [ query(':enter', [ style({ opacity: 0, transform: 'translateX(100%)' }), stagger(50, [ animate('200ms ease-out', style({ opacity: 1, transform: 'translateX(0%)' })), ]), ], { optional: true }) ]), transition(':decrement', [ query(':leave', [ stagger(-50, [ animate('200ms ease-out', style({ opacity: 0, transform: 'translateX(100%)' })), ]), ], { optional: true }) ]), ] ) ================================================ FILE: desktop/angular/src/app/shared/app-icon/app-icon-resolver.ts ================================================ import { Injectable, inject, isDevMode } from "@angular/core"; import { AppProfile, AppProfileService, deepClone } from "@safing/portmaster-api"; import { firstValueFrom, map, switchMap } from "rxjs"; import { INTEGRATION_SERVICE, ProcessInfo } from "src/app/integration"; import * as parseDataURL from 'data-urls'; export abstract class AppIconResolver { abstract resolveIcon(profile: AppProfile): void; } @Injectable() export class DefaultIconResolver extends AppIconResolver { private integration = inject(INTEGRATION_SERVICE); private profileService = inject(AppProfileService); private pendingResolvers = new Map>(); resolveIcon(profile: AppProfile): void { const key = `${profile.Source}/${profile.ID}`; // if there's already a promise in flight, abort. if (this.pendingResolvers.has(key)) { if (isDevMode()) { console.log(`[icon:${profile.Name}] loading icon already in progress ...`) } return; } let promise = new Promise((resolve) => { this.profileService .getProcessesByProfile(profile) .pipe( map(processes => { // if we there are no running processes for this profile, // we try to find the icon based on the information stored in // the profile. let info: ProcessInfo[] = [{ execPath: profile.LinkedPath, cmdline: profile.PresentationPath, pid: -1, matchingPath: profile.PresentationPath, }] processes?.forEach(process => { // BUG: Portmaster sometimes runs a null entry, skip it here. if (!process) { return; } // insert at the beginning since the process data might reveal // better results than the profile one. info.splice(0, 0, { execPath: process.Path, cmdline: process.CmdLine, pid: process.Pid, matchingPath: process.MatchingPath, }) }) return info; }) ).subscribe(async (processInfos) => { for (const info of processInfos) { try { await this.loadAndSaveIcon(info, profile); // success, abort now resolve(); return; } catch (err) { // continue using the next one } } // we failed to find an icon, still resolve the promise here // because nobody actually cares .... resolve(); }) }); this.pendingResolvers.set(key, promise); promise.finally(() => this.pendingResolvers.delete(key)); } private async loadAndSaveIcon(info: ProcessInfo, profile: AppProfile): Promise { const icon = await this.integration.getAppIcon(info); const dataURL = parseDataURL(icon); if (!dataURL) { throw new Error("invalid data url"); } const blob = new Blob([dataURL.body], { type: dataURL.mimeType.essence, }) const body = await blob.arrayBuffer(); const save$ = this.profileService .setProfileIcon(body, blob.type) .pipe(switchMap(result => { // save the profile icon profile = deepClone(profile); profile.Icons = [ ...(profile.Icons || []), { Value: result.filename, Type: 'api', Source: 'ui' } ]; return this.profileService.saveProfile(profile) })); await firstValueFrom(save$); } } ================================================ FILE: desktop/angular/src/app/shared/app-icon/app-icon.html ================================================ {{letter}} ================================================ FILE: desktop/angular/src/app/shared/app-icon/app-icon.module.ts ================================================ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { AppIconComponent } from "./app-icon"; import { AppIconResolver, DefaultIconResolver } from "./app-icon-resolver"; @NgModule({ imports: [ CommonModule ], declarations: [ AppIconComponent, ], exports: [ AppIconComponent, ], providers: [ { provide: AppIconResolver, useClass: DefaultIconResolver, } ] }) export class SfngAppIconModule { } ================================================ FILE: desktop/angular/src/app/shared/app-icon/app-icon.scss ================================================ :host { border-radius: 50%; user-select: none; height: var(--app-icon-size, 25px); width: var(--app-icon-size, 25px); flex-shrink: 0; @apply mr-2; display: inline-flex; justify-content: center; align-items: center; } span, img { @apply text-primary; @apply font-medium; @apply rounded-full; text-shadow: rgba(0, 0, 0, .8) 0px 0px 1px; font-size: calc(var(--app-icon-size, 25px) / 6 * 4); } img { width: 100%; height: 100%; } ================================================ FILE: desktop/angular/src/app/shared/app-icon/app-icon.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Inject, Input, OnDestroy, OnInit, SkipSelf, inject, } from '@angular/core'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { AppProfileService, PORTMASTER_HTTP_API_ENDPOINT, PortapiService, Record, deepClone, } from '@safing/portmaster-api'; import { Subscription, map, of } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { AppIconResolver } from './app-icon-resolver'; import { AppProfile } from 'projects/safing/portmaster-api/src/public-api'; // Interface that must be satisfied for the profile-input // of app-icon. export interface IDandName { // ID of the profile. ID?: string; // Source is the source of the profile. Source?: string; // Name of the profile. Name: string; } // Some icons we don't want to show on the UI. // Note that this works on a best effort basis and might // start breaking with updates to the built-in icons... const iconBlobsToIgnore = [ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABU0lEQVRYhe2WTUrEQBCF36i4ctm4FsdTKF5AEFxL0knuILgQXAy4ELxDfgTXguAFRG/hDXKCAbtcOB3aSVenMjPRTb5NvdCE97oq3QQYGflnJlbc3T/QXxrfXF9NAGBraKPTk2Nvtey4D1l8OUiIo8ODX/Xt/cMfQCk1SAAi8upWgLquWy8rpbB7+yk2m8+mYvNWAAB4fnlt9MX5WaP397ZhCPgygCFa1IUmwJifCgB5nrMBtdbhAK6pi9QcALIs8+5c1AEOqTmwZge4EUjNiQhpmjbarcvaG4AbgcTcUhSFfwFAHMfhABxScwBIkgRA9wnwBgiOQGBORCjLkl2PoigcgB2BwNzifmi97wEOqTkRoaoqdr2zA9wIJOYWrTW785VPQR+WO2B3vdYIpBBRc9Qkp2Cw/4GVR+BjPpt23u19tUXUgU2aBzuQPz5J8oyMjGyUb9+FOUOmulVPAAAAAElFTkSuQmCC', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAACLElEQVR4nO2av07DMBDGP1DFxtaFmbeg6gtUqtQZtU3yDkgMSAxIDEi8Q/8gMVdC4m1YYO0TMNQspErdOG3Md25c7rc0st3E353v7EsLKIqiKIqiKMq/5MRueHx6NoeYSCjubm82NJ8eaiISdDtX6HauKq9tWsFmF4DPr6+d1zalBshG18RpNYfJy+tW21GFgA+lK6DdboeeBwVjyvO3qx1wGGC5XO71wCYZykc8QEqCZ/cfjNs4+X64rOz3FQ/sMMDi7R2Dfg+Lt/eN9kG/tzX24rwFA8AYYGXM+nr9aQADs9mG37FWW3HsqqBhMpnsFFRGkiTOvkoD5ELLBNtIiLcdmGXZ5jP/4Pkc2i4gIb5KRl3xrnbaQSiEeN8QGI/Hzj5aDgjh+SzLaJ7P4eWAiJZ9EVoIhBA/nU695uYdAnUI4fk0TUvbXeP3gZcDhMS7CLIL1DsHyIv3DYHRaOTs44YAZD2fpik9EfIOQohn2Rch5wBZ8bPZzOObfwiBurWAtOftoqaO511jaSEgJd4FQzwgmAQlxPuGwHA4dPbJ1QICnk+ShOb5HJlaoOHLvgi/FhAUP5/P9xpbteRtyDlA1vN2UVPH8+K7gJR45/MI4gHyK7HYxANsA7BuVvkcnniAXAtIwxYPRPTboIR4IBIDMMSL7wIhYZbF0RmgsS9EQtDY1+L5r7esCUrGvA3xHBCfeIBkgBjEi+0CMYsHHDmg7N9UiqIoiqIoiqIcFT++NKIXgDvowAAAAABJRU5ErkJggg==', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAABqUlEQVRYhe2XP2rDMBSHfymhU0dDD5BbJOQCgUDmEv+7Q6FDoUOgQ6F3cJxC50Agt+nSrD5BBr8OqVyrtfWkl8ShoG+SjJE+/95DwoDH4/nf9NTg+eWVLinym8eH+x4AXF1i8/FoiPFoaBwr+p3bAfjc7dixQhNMw7szatmTvb1XY00wCILOZYjIONcEi6JoXSgIAlw/fYhF9ouBsxzQ0IPrzRaz6QTrzbZ6NptOqvHtTR8EQklAWQIl4WdOQEkEqsaHefm9b5Zl7IfEcWwWVDJ1Ke0rHeXqmaRpeljDIrlWQQ5XufreNglGUWQW5EoslQOAJEm0uagHuRJL5YgIy+Wycc06bIIcEjmFStCUnPGYASxKLJQDYJVgGIZmQZsSS+SAv0eIKblWQQ6pHBEhz3N2fTZBrsQSOYVK0JQc24N2JXaXA2CV4Hw+NwtySOUA/QixvU1kPSiQIyKsViv2vaMTlMgpoihik2N7kEMqB6AxwXpiVlfduSAi7Qix7cGL/DS5XHWdC7rIAY4l3i8GTk1+zLsKpwS7lnMS7ErOeMzU/0c9Ho/nNHwBdUH2gB9vJRsAAAAASUVORK5CYII=', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAByElEQVRYhe1WQUoDQRCsmSh4CAreo3/w4CdE8JirLzCKGhRERPBqfISQx3j0BcaDJxHNRWS7PWRmtmdmJ9mNiSuYOmyYbOiqruoeAizw36G6p0e3WulOHeTE1NO/Qb6zu1f4qZXuqLPuMV9d38xbQyEuL86ha2EWWJKHfr+P4XAIAGg2m2i32wCA7fsXPH9kABjMgHkADP87cW6tNvCwvzG2biRAvpAYvH+54mCAmUcvmI0Yq4nM74DBG02sGwlIgqigS/ZEgdkcrSAuVbpUBEyjTiP7JSkDzKZrdo+xdSMBKas4y4K8befSiVxcLnR83UhACtYBV9TOgbBbOX4TF2YZQZY5Yi9/MYwkXQjy/3EEtjp7LgQzAeOUVSo0zCACcgOnwjUEC2LE7kxApS0AGFRgP4vZ8M5VBaQjoNGKuQ20Q2ney8Gr0H0kIAU7hK4zYiPCJxtFZYRMIyAdAQWrFgyicMSfj4oCkheRmQFyIoq2IRcy9T2QhNmCfN/FVcwMBSWu4XlsQUZe5tZmZW0HBXGU4o4FpCJorS3j6fXTEOVdUrgNApvrK9UFpPB4vlWq2DSo/S+Z6p4c9rRuHNRBTsR3dfAu8LfwDdGgu25Uax8RAAAAAElFTkSuQmCC', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAByUlEQVRYhe1WQUoDQRCs2UTwEBS8R//gwU+I4DFXX2AENRgQEcGr8RFCHuPRFxgPnkQ0F9Ht9rAzsz0zO8luTFzB1GHDZENXdVX3EGCJ/w7VO+3eJKrZrYOc+GuQ/Ab57t5+4Weiml111jvmy6vrRWsoxMV5H0ktzAJNeRgOhxiPxwCAVquFTqcDANi5e8bTewqAwQzoB8BwvxPn9loD9webE+sGAuQLidHbpy0OBpg5e8GsxRhNpH8HjF5pat1AQBREBV2yIwrM+mgEcanSpSJgyjoN7JekDDDrrtk+JtYNBMSs4jT18jadSydycbnQyXUDATEYB2xRMwfCbmX5dVyYZwRpaomd/MUwknTBy//HEZjq7LjgzQS0U0ap0DCHCMgOnPLXECyIEbozBZW2AGBQgf0sZsM5VxUQj4CyFbMbaIZSv5eDV6H7QEAMZghtZ8RahEuWRaWFzCIgHgF5q+YNonDEnY+KAqIXkZ4BsiKKtiEXMvM9EIXegnzfxVXMDAUlruFFbEFKTubGZmVsB3lxlOIOBcQiaK+v4PHlQxPlXZK/DQJbG6vVBcTw0N8uVWwW1P6XTPVOjgZJ0jisg5yIb+vgXeJv4RvrxrtwzfCUqAAAAABJRU5ErkJggg==', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADo0lEQVRYhe2Wu28cVRTGf+fcuzZeY0NCUqTgD3C8mzRU0KDQEgmqoBSGhoKWAlFEKYyQAAkrTRRqOpCQkOhAkUCio4D4IYSAzkkKB+wQbLy7c8+hmNmd2WecDQoFfNJodGfn7vc4Z84M/I9/GfLeB1cutdqH7zxSUli9fOntd4EsmrVXL1xcodVqAf6PEl37+AveWDk/dP78s08vA1eBvSgSDnd3bs49DJGKICIg+dod3J3XXn6Ogz9+49WXnu07F1gA9mOWJRqNBrNRcJ8mAQF8ZHYyuBYhI/DlV9cBAqARnBAj2agdjwARoBaETnK+/eY7NMwfaaPZPueefwaA73+4MfKeM80GAC+8+QkA19cukCQOC+ga1zDPR1//jIgjWhzBEQWNBupoNESdldNn2dm5w/FjT/SIpkEcvLAwX0PUQRwNXQGOBCvXoVpxZ31jc2ICEwWY+1y19AvzEQr3GgAtiLUUo8F690tB5DhC3sgiw800f2p/fAJ/tTtoyMOo1yOqnscdnINOIqNDO+vQbrdwMTRWEnBhfXNyAvOn9qmfOBgvwKxwC9TnAskTN3f32PnzHi1robEbv6HFUVGQJ+AOIvkQgL4U6icOqC9OSKCKu4cH/HT7Nh3P0GiEWkEcc+LBEhylB+qL+ywe+328gGrFNre3kWiE6EjsOi5EqPVS6EGEZrOJW0JVR5KMIy8TqCjQmlUcl7GLlvGrlgLcYWNzY2ICk1CUoFSgtdRPHAwtYteQeimUCuDsmebEMX7l3Pv3E1BCY+lUgqNaFZJ663ID3Fh/6ARKhFrqNVq15lVy1dRP1FjGRaZ6lQwnEKqkw+Si/QLMATwnHxhA7o65k2UJM0NwanOP30dATAPkhmjlmuYiuhCcja0fR7prNhqA4W5Fjwz3ydBTEGLZaKoV99p13y8AnGZjeeT4dfd8LrnnCYyoUQTQQsGtW7/y+tPnR7oZxPb2LywvncRd2dzaGnnP6aUlzBLJvKt1tIAsObUAF195kZ2dO0cSsLx0EgAz6yWQO3aSGeZOJ8swS5gNj+c+AeYwE4QgxlPHF6nNzkBKpGQ4EGMAnSksOGCA41nisJP/eTfuVIjAHQRCCITiPaPjBAC0kwMKMkvW7vuJTgZQffSkOBRCLqeL0cN4PKLA6trah2/FGB97wL05oSohKCEEzMBSRkpp4gf+3d3dq+SOTIAZ4Enyz+QwjYgpkIB7wF6RIxGo8eAJTgsDOpB/jP+38TcKdstukjAxWQAAAABJRU5ErkJggg==', ]; const iconIDsToIgnore = [ "a27898ddfa4e0481b62c69faa196919a738fcade", "5a3eea8bcd08b9336ce9c5083f26185164268ee9", "573393d6ad238d255b20dc1c1b303c95debe6965", "d459b2cb23c27cc31ccab5025533048d5d8301bf", "d35a0d91ebfda81df5286f68ec5ddb1d6ad6b850", "cc33187385498384f1b648e23be5ef1a2e9f5f71", ]; const profilesToIgnore = ['local/_unidentified', 'local/_unsolicited']; @Component({ selector: 'app-icon', templateUrl: './app-icon.html', styleUrls: ['./app-icon.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class AppIconComponent implements OnInit, OnDestroy { private sub = Subscription.EMPTY; private initDone = false; private resovler = inject(AppIconResolver); /** @private The data-URL for the app-icon if available */ src: SafeUrl | string = ''; /** The profile for which to show the app-icon */ @Input() set profile(p: IDandName | null | undefined | string) { if (typeof p === 'string') { const parts = p.split("/") p = { Source: parts[0], ID: parts[1], Name: '', } } if (!!this._profile && !!p && this._profile.ID === p.ID) { // skip if this is the same profile return; } this._profile = p || null; if (this.initDone) { this.updateView(); } } get profile(): IDandName | null | undefined { return this._profile; } private _profile: IDandName | null = null; /** isIgnoredProfile is set to true if the profile is part of profilesToIgnore */ isIgnoredProfile = false; /** If not icon is available, this holds the first - uppercased - letter of the app - name */ letter = ''; /** @private The background color of the component, based on icon availability and generated by ID */ @HostBinding('style.background-color') color = 'var(--text-tertiary)'; constructor( private profileService: AppProfileService, private changeDetectorRef: ChangeDetectorRef, private portapi: PortapiService, // @HostBinding() is not evaluated in our change-detection run but rather // checked by the parent component during updateRenderer. // Since we want the background color to change immediately after we set the // src path we need to tell the parent (which ever it is) to update as wel. @SkipSelf() private parentCdr: ChangeDetectorRef, private sanitzier: DomSanitizer, @Inject(PORTMASTER_HTTP_API_ENDPOINT) private httpAPI: string ) { } /** Updates the view of the app-icon and tries to find the actual application icon */ private requestedAnimationFrame: number | null = null; private updateView(skipIcon = false) { if (this.requestedAnimationFrame !== null) { cancelAnimationFrame(this.requestedAnimationFrame); } this.requestedAnimationFrame = requestAnimationFrame(() => { this.__updateView(skipIcon); }) } ngOnInit(): void { this.updateView(); this.initDone = true; } private __updateView(skipIcon = false) { this.requestedAnimationFrame = null; const p = this.profile; const sourceAndId = this.getIDAndSource(); if (!!p && sourceAndId !== null) { let idx = 0; for (let i = 0; i < (p.ID || p.Name).length; i++) { idx += (p.ID || p.Name).charCodeAt(i); } const combinedID = `${sourceAndId[0]}/${sourceAndId[1]}`; this.isIgnoredProfile = profilesToIgnore.includes(combinedID); this.updateLetter(p); if (!this.isIgnoredProfile) { this.color = AppColors[idx % AppColors.length]; } else { this.color = 'transparent'; } if (!skipIcon) { this.tryGetSystemIcon(); } } else { this.isIgnoredProfile = false; this.color = 'var(--text-tertiary)'; } this.changeDetectorRef.markForCheck(); this.parentCdr.markForCheck(); } private updateLetter(p: IDandName) { if (p.Name !== '') { if (p.Name[0] === '<') { // we might get the name with search-highlighting which // will then include tags. If the first character is a < // make sure to strip all HTML tags before getting [0]. this.letter = p.Name.replace( /( |<([^>]+)>)/gi, '' )[0].toLocaleUpperCase(); } else { this.letter = p.Name[0]; } this.letter = this.letter.toLocaleUpperCase(); } else { this.letter = '?'; } } getIDAndSource(): [string, string] | null { if (!this.profile) { return null; } const id = this.profile.ID; if (!id) { return null; } // if there's a source ID only holds the profile ID if (!!this.profile.Source) { return [this.profile.Source, id]; } // otherwise, ID likely contains the source const [source, ...rest] = id.split('/'); if (rest.length > 0) { return [source, rest.join('/')]; } // id does not contain a forward-slash so we // assume the source is local return ['local', id]; } /** * Tries to get the application icon form the system. * Requires the app to be running in the electron wrapper. */ private tryGetSystemIcon() { const sourceAndId = this.getIDAndSource(); if (sourceAndId === null) { return; } this.sub.unsubscribe(); this.sub = this.profileService .watchAppProfile(sourceAndId[0], sourceAndId[1]) .pipe( switchMap((profile: AppProfile) => { this.updateLetter(profile); if (!!profile.Icons?.length) { const firstIcon = profile.Icons[0]; console.log(`profile ${profile.Name} has icon of from source ${firstIcon.Source} stored in ${firstIcon.Type}`) switch (firstIcon.Type) { case 'database': return this.portapi .get(firstIcon.Value) .pipe( map((result) => { return result.iconData; }) ); case 'api': return of(`${this.httpAPI}/v1/profile/icon/${firstIcon.Value}`); case 'path': // TODO: Silently ignore for now. return of(''); case '': // Icon is not set. return of(''); default: console.error(`Icon type ${firstIcon.Type} not yet supported`); } } this.resovler.resolveIcon(profile); // return an empty icon here. If the resolver manages to find an icon // the profle will get updated and we'll run again here. return of(''); }) ) .subscribe({ next: (icon) => { if (iconBlobsToIgnore.some((i) => i === icon)) { icon = ''; } else if (iconIDsToIgnore.some((i) => icon.includes(i))) { // TODO: This just checks if the value (blob, URL, etc.) contains // the SHA1 sum of the icon, which is used in the URL of api icon types. // This is very unlikely to have false positivies, but this could still // be done a lot cleaner. icon = ''; } if (icon !== '') { this.src = this.sanitzier.bypassSecurityTrustUrl(icon); this.color = 'unset'; } else { this.src = ''; this.color = this.color === 'unset' ? 'var(--text-tertiary)' : this.color; } this.changeDetectorRef.detectChanges(); this.parentCdr.markForCheck(); }, error: (err) => console.error(err), }); } ngOnDestroy(): void { this.sub.unsubscribe(); } } export const AppColors: string[] = [ 'rgba(244, 67, 54, .7)', 'rgba(233, 30, 99, .7)', 'rgba(156, 39, 176, .7)', 'rgba(103, 58, 183, .7)', 'rgba(63, 81, 181, .7)', 'rgba(33, 150, 243, .7)', 'rgba(3, 169, 244, .7)', 'rgba(0, 188, 212, .7)', 'rgba(0, 150, 136, .7)', 'rgba(76, 175, 80, .7)', 'rgba(139, 195, 74, .7)', 'rgba(205, 220, 57, .7)', 'rgba(255, 235, 59, .7)', 'rgba(255, 193, 7, .7)', 'rgba(255, 152, 0, .7)', 'rgba(255, 87, 34, .7)', 'rgba(121, 85, 72, .7)', 'rgba(158, 158, 158, .7)', 'rgba(96, 125, 139, .7)', ]; ================================================ FILE: desktop/angular/src/app/shared/app-icon/index.ts ================================================ export { AppIconComponent } from './app-icon'; export { SfngAppIconModule } from './app-icon.module'; ================================================ FILE: desktop/angular/src/app/shared/config/basic-setting/basic-setting.html ================================================ {{opt.Name}} {{opt}}
{{ unit }}
{{ unit }}
================================================ FILE: desktop/angular/src/app/shared/config/basic-setting/basic-setting.scss ================================================ label { @apply text-sm; } input[type="checkbox"] { float: right; user-select: none; } .input-container { display: block; position: relative; font-size: 0.75rem; input { font-size: inherit; } .suffix { user-select: none; position: absolute; left: 0; top: calc(50% - 0.55rem); padding-left: 0.3rem; color: #aaa; font: inherit; } } ================================================ FILE: desktop/angular/src/app/shared/config/basic-setting/basic-setting.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DOCUMENT } from '@angular/common'; import { AfterViewChecked, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, forwardRef, Inject, Input, Output, ViewChild } from '@angular/core'; import { AbstractControl, ControlValueAccessor, NgModel, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms'; import { BaseSetting, ExternalOptionHint, OptionType, parseSupportedValues, SettingValueType, WellKnown } from '@safing/portmaster-api'; @Component({ selector: 'app-basic-setting', templateUrl: './basic-setting.html', styleUrls: ['./basic-setting.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, multi: true, useExisting: forwardRef(() => BasicSettingComponent), }, { provide: NG_VALIDATORS, multi: true, useExisting: forwardRef(() => BasicSettingComponent), } ], changeDetection: ChangeDetectionStrategy.OnPush }) export class BasicSettingComponent> implements ControlValueAccessor, Validator, AfterViewChecked { /** @private template-access to all external option hits */ readonly optionHints = ExternalOptionHint; /** @private template-access to parseSupportedValues */ readonly parseSupportedValues = parseSupportedValues; @ViewChild('suffixElement', { static: false, read: ElementRef }) suffixElement?: ElementRef; /** Cached canvas element used by getTextWidth */ private cachedCanvas?: HTMLCanvasElement; /** Returns the value of external-option hint annotation */ externalOptType(opt: S): ExternalOptionHint | null { return opt.Annotations?.["safing/portbase:ui:display-hint"] || null; } /** Whether or not the input should be currently disabled. */ @Input() set disabled(v: any) { const disabled = coerceBooleanProperty(v); this.setDisabledState(disabled); } get disabled() { return this._disabled; } /** The setting to display */ @Input() setting: S | null = null; /** Emits when the user activates focus on this component */ @Output() blured = new EventEmitter(); /** @private The ngModel in our view used to display the value */ @ViewChild(NgModel) model: NgModel | null = null; /** The unit of the setting */ get unit() { if (!this.setting) { return ''; } return this.setting.Annotations[WellKnown.Unit] || ''; } /** * Holds the value as it is presented to the user. * That is, a JSON encoded object or array is dumped as a * JSON string. Strings, numbers and booleans are presented * as they are. */ _value: string | number | boolean = ""; /** * Describes the type of the original settings value * as passed to writeValue(). * This may be anything that can be returned from `typeof v`. * If set to "string", "number" or "boolean" then _value is emitted * as it is. * If it's set anything else (like "object") than _value is JSON.parse`d * before being emitted. */ _type: string = ''; /* Returns true if the current _type and _value is managed as JSON */ get isJSON(): boolean { return this._type !== 'string' && this._type !== 'number' && this._type !== 'boolean' } /* * _onChange is set using registerOnChange by @angular/forms * and satisfies the ControlValueAccessor. */ private _onChange: (_: SettingValueType) => void = () => { }; /* _onTouch is set using registerOnTouched by @angular/forms * and satisfies the ControlValueAccessor. */ private _onTouch: () => void = () => { }; private _onValidatorChange: () => void = () => { }; /* Whether or not the input field is disabled. Set by setDisabledState * from @angular/forms */ _disabled: boolean = false; private _valid: boolean = true; // We are using ChangeDetectionStrategy.OnPush so angular does not // update ourself when writeValue or setDisabledState is called. // Using the changeDetectorRef we can take care of that ourself. constructor( @Inject(DOCUMENT) private document: Document, private _changeDetectorRef: ChangeDetectorRef ) { } ngAfterViewChecked() { // update the suffix position everytime angular has // checked our view for changes. this.updateUnitSuffixPosition(); } /** * Sets the user-presented value and emits a change. * Used by our view. Not meant to be used from outside! * Use writeValue instead. * @private * * @param value The value to set */ setInternalValue(value: string | number | boolean) { let toEmit: any = value; try { if (!this.isJSON) { toEmit = value; } else { toEmit = JSON.parse(value as string); } } catch (err) { this._valid = false; this._onValidatorChange(); return; } this._valid = true; this._value = value; this._onChange(toEmit); this.updateUnitSuffixPosition(); } /** * Updates the position of the value's unit suffix element */ private updateUnitSuffixPosition() { if (!!this.unit && !!this.suffixElement) { const input = this.suffixElement.nativeElement.previousSibling! as HTMLInputElement; const style = window.getComputedStyle(input); let paddingleft = parseInt(style.paddingLeft.slice(0, -2)) // we need to use `input.value` instead of `value` as we need to // get preceding zeros of the number input as well, while still // using the value as a fallback. let value = input.value || (this._value as string); const width = this.getTextWidth(value, style.font) + paddingleft; this.suffixElement.nativeElement.style.left = `${width}px`; } } /** * Validates if "value" matches the settings requirements. * It satisfies the NG_VALIDATORS interface and validates the * value for THIS component. * * @param param0 The AbstractControl to validate */ validate({ value }: AbstractControl): ValidationErrors | null { if (!this._valid) { return { jsonParseError: true } } if (this._type === 'string' || value === null) { if (!!this.setting?.DefaultValue && !value) { return { required: true, } } } if (!!this.setting?.ValidationRegex) { const re = new RegExp(this.setting.ValidationRegex); if (!this.isJSON) { if (!re.test(`${value}`)) { return { pattern: `"${value}"` } } } else { if (!Array.isArray(value)) { return { invalidType: true } } const invalidLines = value.filter(v => !re.test(v)); if (invalidLines.length) { return { pattern: invalidLines } } } } return null; } /** * Writes a new value and satisfies the ControlValueAccessor * * @param v The new value to write */ writeValue(v: SettingValueType) { // the following is a super ugly work-around for the migration // from security-settings to booleans. // // In order to not mess and hide an actual portmaster issue // we only convert v to a boolean if it's a number value and marked as a security setting. // In all other cases we don't mangle it. // // TODO(ppacher): Remove in v1.8? // BOM if (this.setting?.OptType === OptionType.Bool && this.setting?.Annotations[WellKnown.DisplayHint] === ExternalOptionHint.SecurityLevel) { if (typeof v === 'number') { (v as any) = v === 7; } } // EOM let t = typeof v; this._type = t; if (this.isJSON) { this._value = JSON.stringify(v, undefined, 2); } else { this._value = v; } this.updateUnitSuffixPosition(); this._changeDetectorRef.markForCheck(); } registerOnValidatorChange(fn: () => void) { this._onValidatorChange = fn; } /** * Registers the onChange function requred by the * ControlValueAccessor * * @param fn The fn to register */ registerOnChange(fn: (_: SettingValueType) => void) { this._onChange = fn; } /** * @private * Called when the input-component used for the setting is touched/focused. */ touched() { this._onTouch(); this.blured.next(); } /** * Registers the onTouch function requred by the * ControlValueAccessor * * @param fn The fn to register */ registerOnTouched(fn: () => void) { this._onTouch = fn; } /** * Enable or disable the component. Required for the * ControlValueAccessor. * * @param disable Whether or not the component is disabled */ setDisabledState(disable: boolean) { this._disabled = disable; this._changeDetectorRef.markForCheck(); } /** * @private * Returns the number of lines in value. If value is not * a string 1 is returned. */ lineCount(value: string | number | boolean) { if (typeof value === 'string') { return value.split('\n').length } return 1 } /** * Calculates the amount of pixel a text requires when being rendered. * It uses canvas.measureText on a dummy (no attached) element * * @param text The text that would be rendered * @param font The CSS font descriptor that would be used for the text */ private getTextWidth(text: string, font: string): number { let canvas = this.cachedCanvas || this.document.createElement('canvas'); this.cachedCanvas = canvas; let context = canvas.getContext("2d")!; context.font = font; let metrics = context.measureText(text); return metrics.width; } } ================================================ FILE: desktop/angular/src/app/shared/config/basic-setting/index.ts ================================================ export * from './basic-setting'; ================================================ FILE: desktop/angular/src/app/shared/config/config-settings.html ================================================

{{subsys.Name}}

{{cat.name}}

Other

================================================ FILE: desktop/angular/src/app/shared/config/config-settings.scss ================================================ :host { display: flex; overflow: hidden; } fa-icon[icon="spinner"] { @apply text-3xl; display: block; width: 100%; text-align: center; height: 6rem; } div.settings-nav { @apply mt-4; flex-shrink: 0; overflow: visible; white-space: nowrap; transition: height cubic-bezier(0.25, 0.46, 0.45, 0.94) .5s; @apply text-xs; ul { position: fixed; li { @apply font-medium; &.separated { margin-top: 1.25rem; } } &>li { @apply mb-1; @apply text-tertiary; span { cursor: pointer; display: block; } &:hover, &.active { @apply text-primary; } &.active { &.category:before { content: ""; width: 1px; height: 1rem; @apply bg-white block absolute; left: 0.5rem; } ul.settings { display: inline-block; } } ul.settings { position: unset; @apply mt-2; @apply ml-2; @apply pl-3; @apply text-xs; @apply border-l; @apply border-cards-tertiary; display: none; li { cursor: pointer; margin-top: 0; } } } } } .user-defined-value:before { content: ""; height: 1rem; @apply bg-blue block absolute rounded-full w-1 h-1; top: 0.45rem; left: -1rem; } .user-defined-value.category:before { left: -2rem; top: 0.35rem; } ================================================ FILE: desktop/angular/src/app/shared/config/config-settings.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ScrollDispatcher } from '@angular/cdk/overlay'; import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, QueryList, TrackByFunction, ViewChildren, } from '@angular/core'; import { ConfigService, ExpertiseLevelNumber, PortapiService, Setting, StringSetting, releaseLevelFromName, } from '@safing/portmaster-api'; import { BehaviorSubject, Subscription, combineLatest } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { StatusService } from 'src/app/services'; import { fadeInAnimation, fadeInListAnimation, fadeOutAnimation, } from 'src/app/shared/animations'; import { FuzzySearchService } from 'src/app/shared/fuzzySearch'; import { ExpertiseLevelOverwrite } from '../expertise/expertise-directive'; import { SaveSettingEvent } from './generic-setting/generic-setting'; import { ActionIndicatorService } from '../action-indicator'; import { SfngDialogService } from '@safing/ui'; import { ExportConfig, ExportDialogComponent, } from './export-dialog/export-dialog.component'; import { ImportConfig, ImportDialogComponent, } from './import-dialog/import-dialog.component'; import { subsystems, SubsystemWithExpertise } from './subsystems' interface Category { name: string; settings: Setting[]; minimumExpertise: ExpertiseLevelNumber; collapsed: boolean; hasUserDefinedValues: boolean; } @Component({ selector: 'app-settings-view', templateUrl: './config-settings.html', styleUrls: ['./config-settings.scss'], animations: [fadeInAnimation, fadeOutAnimation, fadeInListAnimation], }) export class ConfigSettingsViewComponent implements OnInit, OnDestroy, AfterViewInit { subsystems: SubsystemWithExpertise[] = subsystems; others: Setting[] | null = null; settings: Map = new Map(); /** A list of all selected settings for export */ selectedSettings: { [key: string]: boolean } = {}; /** Whether or not we are currently in "export" mode */ exportMode = false; activeSection = ''; activeCategory = ''; loading = true; @Input() resetLabelText = 'Reset to system default'; @Input() set compactView(v: any) { this._compactView = coerceBooleanProperty(v); } get compactView() { return this._compactView; } private _compactView = false; @Input() set lockDefaults(v: any) { this._lockDefaults = coerceBooleanProperty(v); } get lockDefaults() { return this._lockDefaults; } private _lockDefaults = false; @Input() set userSettingsMarker(v: any) { this._userSettingsMarker = coerceBooleanProperty(v); } get userSettingsMarker() { return this._userSettingsMarker; } private _userSettingsMarker = true; @Input() set searchTerm(v: string) { this.onSearch.next(v); } @Input() set availableSettings(v: Setting[]) { this.onSettingsChange.next(v); } @Input() set scope(scope: 'global' | string) { this._scope = scope; } get scope() { return this._scope; } private _scope: 'global' | string = 'global'; @Input() displayStackable: string | boolean = false; @Input() set highlightKey(key: string | null) { this._highlightKey = key || null; this._scrolledToHighlighted = false; // If we already loaded the settings then instruct the window // to scroll the setting into the view. if (!!key && !!this.settings && this.settings.size > 0) { this.scrollTo(key); this._scrolledToHighlighted = true; } } get highlightKey() { return this._highlightKey; } private _highlightKey: string | null = null; private _scrolledToHighlighted = false; mustShowSetting: ExpertiseLevelOverwrite = ( lvl: ExpertiseLevelNumber, s: Setting ) => { if (lvl >= s.ExpertiseLevel) { // this setting is shown anyway. return false; } if (s.Key === this.highlightKey) { return true; } // the user is searching for settings so make sure we even show advanced or developer settings if (this.onSearch.getValue() !== '') { return true; } if (s.Value === undefined) { // no value set return false; } return true; }; mustShowCategory: ExpertiseLevelOverwrite = ( lvl: ExpertiseLevelNumber, cat: Category ) => { return cat.settings.some((setting) => this.mustShowSetting(lvl, setting)); }; mustShowSubsystem: ExpertiseLevelOverwrite = ( lvl: ExpertiseLevelNumber, subsys: SubsystemWithExpertise ) => { return !!this.settings .get(subsys.ConfigKeySpace) ?.some((cat) => this.mustShowCategory(lvl, cat)); }; @Output() save = new EventEmitter(); private onSearch = new BehaviorSubject(''); private onSettingsChange = new BehaviorSubject([]); @ViewChildren('navLink', { read: ElementRef }) navLinks: QueryList | null = null; private subscription = Subscription.EMPTY; constructor( public statusService: StatusService, public configService: ConfigService, private elementRef: ElementRef, private changeDetectorRef: ChangeDetectorRef, private scrollDispatcher: ScrollDispatcher, private searchService: FuzzySearchService, private actionIndicator: ActionIndicatorService, private portapi: PortapiService, private dialog: SfngDialogService, ) { } openImportDialog() { const importConfig: ImportConfig = { type: 'setting', key: this.scope, }; this.dialog.create(ImportDialogComponent, { data: importConfig, autoclose: false, backdrop: 'light', }); } toggleExportMode() { this.exportMode = !this.exportMode; if (this.exportMode) { this.actionIndicator.info( 'Settings Export', 'Please select all settings you want to export and press "Save" to generate the export. Note that settings with system defaults cannot be exported and are hidden.' ); } } generateExport() { let selectedKeys = Object.keys(this.selectedSettings).reduce((sum, key) => { if (this.selectedSettings[key]) { sum.push(key); } return sum; }, [] as string[]); if (selectedKeys.length === 0) { selectedKeys = Array.from(this.settings.values()).reduce( (sum, current) => { current.forEach((cat) => { cat.settings.forEach((s) => { if (s.Value !== undefined) { sum.push(s.Key); } }); }); return sum; }, [] as string[] ); } this.portapi.exportSettings(selectedKeys, this.scope).subscribe({ next: (exportBlob) => { const exportConfig: ExportConfig = { type: 'setting', content: exportBlob, }; this.dialog.create(ExportDialogComponent, { data: exportConfig, backdrop: 'light', autoclose: true, }); this.exportMode = false; }, error: (err) => { const msg = this.actionIndicator.getErrorMessgae(err); this.actionIndicator.error('Failed To Generate Export', msg); }, }); } saveSetting(event: SaveSettingEvent, s: Setting) { this.save.next(event); const subsys = this.subsystems.find( (subsys) => s.Key === subsys.ToggleOptionKey ); if (!!subsys) { // trigger a reload of the page as we now might need to show more // settings. this.onSettingsChange.next(this.onSettingsChange.getValue()); } } trackSubsystem: TrackByFunction = this.statusService.trackSubsystem; trackCategory(_: number, cat: Category) { return cat.name; } ngOnInit(): void { this.subscription = combineLatest([ this.onSettingsChange, this.onSearch.pipe(debounceTime(250)), this.configService.watch('core/releaseLevel'), ]) .pipe(debounceTime(10)) .subscribe( ([settings, searchTerm, currentReleaseLevelSetting]) => { this.others = []; this.settings = new Map(); // Get the current release level as a number (fallback to 'stable' is something goes wrong) const currentReleaseLevel = releaseLevelFromName( currentReleaseLevelSetting || ('stable' as any) ); // Make sure we only display settings that are allowed by the releaselevel setting. settings = settings.filter( (setting) => setting.ReleaseLevel <= currentReleaseLevel ); // Use fuzzy-search to limit the number of settings shown. const filtered = this.searchService.searchList(settings, searchTerm, { ignoreLocation: true, ignoreFieldNorm: true, threshold: 0.1, minMatchCharLength: 3, keys: [ { name: 'Name', weight: 3 }, { name: 'Description', weight: 2 }, ], }); // The search service wraps the items in a search-result object. // Unwrap them now. settings = filtered.map((res) => res.item); // use order-annotations to sort the settings. This affects the order of // the categories as well as the settings inside the categories. settings.sort((a, b) => { const orderA = a.Annotations?.['safing/portbase:ui:order'] || 0; const orderB = b.Annotations?.['safing/portbase:ui:order'] || 0; return orderA - orderB; }); settings.forEach((setting) => { let pushed = false; this.subsystems.forEach((subsys) => { if ( setting.Key.startsWith( subsys.ConfigKeySpace.slice('config:'.length) ) ) { // get the category name annotation and fallback to 'others' let catName = 'other'; if ( !!setting.Annotations && !!setting.Annotations['safing/portbase:ui:category'] ) { catName = setting.Annotations['safing/portbase:ui:category']; } // ensure we have a category array for the subsystem. let categories = this.settings.get(subsys.ConfigKeySpace); if (!categories) { categories = []; this.settings.set(subsys.ConfigKeySpace, categories); } // find or create the appropriate category object. let cat = categories.find((c) => c.name === catName); if (!cat) { cat = { name: catName, minimumExpertise: ExpertiseLevelNumber.developer, settings: [], collapsed: false, hasUserDefinedValues: false, }; categories.push(cat); } // add the setting to the category object and update // the minimum expertise required for the category. cat.settings.push(setting); if (setting.ExpertiseLevel < cat.minimumExpertise) { cat.minimumExpertise = setting.ExpertiseLevel; } pushed = true; } }); // if we did not push the setting to some subsystem // we need to push it to "others" if (!pushed) { this.others!.push(setting); } }); if (this.others.length === 0) { this.others = null; } // Reduce the subsystem array to only contain subsystems that // actually have settings to show. // Also update the minimumExpertiseLevel for those subsystems this.subsystems = this.subsystems .filter((subsys) => { return !!this.settings.get(subsys.ConfigKeySpace); }) .map((subsys) => { let categories = this.settings.get(subsys.ConfigKeySpace)!; let hasUserDefinedValues = false; categories.forEach((c) => { c.hasUserDefinedValues = c.settings.some( (s) => s.Value !== undefined ); hasUserDefinedValues = c.hasUserDefinedValues || hasUserDefinedValues; }); subsys.hasUserDefinedValues = hasUserDefinedValues; let toggleOption: Setting | undefined = undefined; for (let c of categories) { toggleOption = c.settings.find( (s) => s.Key === subsys.ToggleOptionKey ); if (!!toggleOption) { if ( (toggleOption.Value !== undefined && !toggleOption.Value) || (toggleOption.Value === undefined && !toggleOption.DefaultValue) ) { subsys.isDisabled = true; // remove all settings for all subsystem categories // except for the ToggleOption. categories = categories .map((c) => ({ ...c, settings: c.settings.filter( (s) => s.Key === toggleOption!.Key ), })) .filter((cat) => cat.settings.length > 0); this.settings.set(subsys.ConfigKeySpace, categories); } break; } } // reduce the categories to find the smallest expertise level requirement. subsys.minimumExpertise = categories.reduce((min, current) => { if (current.minimumExpertise < min) { return current.minimumExpertise; } return min; }, ExpertiseLevelNumber.developer as ExpertiseLevelNumber); return subsys; }); // Force the core subsystem to the end. if (this.subsystems.length >= 2 && this.subsystems[0].ID === 'core') { this.subsystems.push( this.subsystems.shift() as SubsystemWithExpertise ); } // Notify the user interface that we're done loading // the settings. this.loading = false; // If there's a highlightKey set and we have not yet scrolled // to it (because it was set during component bootstrap) we // need to scroll there now. if (this._highlightKey !== null && !this._scrolledToHighlighted) { this._scrolledToHighlighted = true; // Use the next animation frame for scrolling window.requestAnimationFrame(() => { this.scrollTo(this._highlightKey || ''); }); } } ); } ngAfterViewInit() { this.subscription = new Subscription(); // Whenever our scroll-container is scrolled we might // need to update which setting is currently highlighted // in the settings-navigation. this.subscription.add( this.scrollDispatcher .scrolled(10) .subscribe(() => this.intersectionCallback()) ); // Also, entries in the settings-navigation might become // visible with expertise/release level changes so make // sure to recalculate the current one whenever a change // happens. this.subscription.add( this.navLinks?.changes.subscribe(() => { this.intersectionCallback(); this.changeDetectorRef.detectChanges(); }) ); } ngOnDestroy() { this.subscription.unsubscribe(); this.onSearch.complete(); } /** * Calculates which navigation entry should be highlighted * depending on the scroll position. */ private intersectionCallback() { // search our parents for the element that's scrollable let elem: HTMLElement = this.elementRef.nativeElement; while (!!elem) { if (elem.scrollTop > 0) { break; } elem = elem.parentElement!; } // if there's no scrolled/scrollable parent element // our content itself is scrollable so use our own // host element as the anchor for the calculation. if (!elem) { elem = this.elementRef.nativeElement; } // get the elements offset to page-top var offsetTop = 0; if (!!elem) { const viewRect = elem.getBoundingClientRect(); offsetTop = viewRect.top; } this.navLinks?.some((link) => { const subsystem = link.nativeElement.getAttribute('subsystem'); const category = link.nativeElement.getAttribute('category'); const lastChild = (link.nativeElement as HTMLElement) .lastElementChild as HTMLElement; if (!lastChild) { return false; } const rect = lastChild.getBoundingClientRect(); const styleBox = getComputedStyle(lastChild); const offset = rect.top + rect.height - parseInt(styleBox.marginBottom) - parseInt(styleBox.paddingBottom); if (offset >= offsetTop) { this.activeSection = subsystem; this.activeCategory = category; return true; } return false; }); this.changeDetectorRef.detectChanges(); } /** * @private * Performs a smooth-scroll to the given anchor element ID. * * @param id The ID of the anchor element to scroll to. */ scrollTo(id: string, cat?: Category) { if (!!cat) { cat.collapsed = false; } document.getElementById(id)?.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest', }); } } ================================================ FILE: desktop/angular/src/app/shared/config/config.module.ts ================================================ import { DragDropModule } from '@angular/cdk/drag-drop'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { SfngSelectModule, SfngTipUpModule, SfngToggleSwitchModule, SfngTooltipModule, } from '@safing/ui'; import { MarkdownModule } from 'ngx-markdown'; import { ExpertiseModule } from '../expertise/expertise.module'; import { SfngFocusModule } from '../focus'; import { SfngMenuModule } from '../menu'; import { SfngMultiSwitchModule } from '../multi-switch'; import { BasicSettingComponent } from './basic-setting/basic-setting'; import { ConfigSettingsViewComponent } from './config-settings'; import { FilterListComponent } from './filter-lists'; import { GenericSettingComponent } from './generic-setting'; import { OrderedListComponent, OrderedListItemComponent, } from './ordererd-list'; import { RuleListItemComponent } from './rule-list/list-item'; import { RuleListComponent } from './rule-list/rule-list'; import { SafePipe } from './safe.pipe'; import { ExportDialogComponent } from './export-dialog/export-dialog.component'; import { ImportDialogComponent } from './import-dialog/import-dialog.component'; import { SfngAppIconModule } from '../app-icon'; @NgModule({ imports: [ CommonModule, FormsModule, DragDropModule, SfngTooltipModule, SfngSelectModule, SfngMultiSwitchModule, SfngFocusModule, SfngMenuModule, SfngTipUpModule, FontAwesomeModule, MarkdownModule, RouterModule, ExpertiseModule, SfngToggleSwitchModule, MarkdownModule, SfngAppIconModule ], declarations: [ BasicSettingComponent, FilterListComponent, OrderedListComponent, OrderedListItemComponent, RuleListComponent, RuleListItemComponent, ConfigSettingsViewComponent, GenericSettingComponent, SafePipe, ExportDialogComponent, ImportDialogComponent, ], exports: [ BasicSettingComponent, FilterListComponent, OrderedListComponent, OrderedListItemComponent, RuleListComponent, RuleListItemComponent, ConfigSettingsViewComponent, GenericSettingComponent, SafePipe, ], }) export class ConfigModule { } ================================================ FILE: desktop/angular/src/app/shared/config/export-dialog/export-dialog.component.html ================================================

{{ dialogRef.data.type === "setting" ? "Settings" : "Profile" }} Export

================================================ FILE: desktop/angular/src/app/shared/config/export-dialog/export-dialog.component.ts ================================================ import { DOCUMENT } from '@angular/common'; import { ChangeDetectionStrategy, Component, ElementRef, OnInit, inject, } from '@angular/core'; import { SFNG_DIALOG_REF, SfngDialogRef } from '@safing/ui'; import { ActionIndicatorService } from '../../action-indicator'; import { INTEGRATION_SERVICE } from 'src/app/integration'; export interface ExportConfig { content: string; type: 'setting' | 'profile'; } @Component({ templateUrl: './export-dialog.component.html', changeDetection: ChangeDetectionStrategy.OnPush, styles: [ ` :host { @apply flex flex-col gap-2 overflow-hidden; min-height: 24rem; min-width: 24rem; max-height: 40rem; max-width: 40rem; } `, ], }) export class ExportDialogComponent implements OnInit { readonly dialogRef: SfngDialogRef< ExportDialogComponent, unknown, ExportConfig > = inject(SFNG_DIALOG_REF); private readonly elementRef: ElementRef = inject(ElementRef); private readonly document = inject(DOCUMENT); private readonly uai = inject(ActionIndicatorService); private readonly integration = inject(INTEGRATION_SERVICE); content = ''; ngOnInit(): void { this.content = '```yaml\n' + this.dialogRef.data.content + '\n```'; } download() { const blob = new Blob([this.dialogRef.data.content], { type: 'text/yaml' }); const elem = this.document.createElement('a'); elem.href = window.URL.createObjectURL(blob); elem.download = 'export.yaml'; this.elementRef.nativeElement.appendChild(elem); elem.click(); this.elementRef.nativeElement.removeChild(elem); } copyToClipboard() { this.integration.writeToClipboard(this.dialogRef.data.content) .then(() => this.uai.success('Copied to Clipboard')) .catch(() => this.uai.error('Failed to Copy to Clipboard')); } } ================================================ FILE: desktop/angular/src/app/shared/config/filter-lists/filter-list.html ================================================
{{ !!node.license ? 'License: ' + node.license : '' }}
Expand Collapse
================================================ FILE: desktop/angular/src/app/shared/config/filter-lists/filter-list.scss ================================================ :host { display: block; overflow: hidden; @apply bg-cards-secondary; @apply rounded; @apply p-2; @apply h-full; } .node { position: relative; display: flex; flex-direction: column; justify-content: flex-start; @apply py-1; .head { display: flex; flex-direction: row; align-items: baseline; input { @apply mr-2; position: relative; top: 2px; } label { display: flex; flex-direction: column; flex-grow: 1; } span.details { opacity: 0; text-transform: capitalize; font-size: 0.9em; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; max-width: 6rem; @apply text-tertiary; } &:hover { span.details { opacity: 1; } } } span.name { @apply text-primary; .id { @apply text-tertiary; font-style: italic; } } .description { position: relative; top: -2px; @apply text-tertiary; } div.expand { cursor: pointer; @apply text-secondary; display: flex; flex-direction: row; align-items: center; @apply pb-2; fa-icon { margin-right: 0.25rem; } } .children { display: flex; flex-direction: column; margin-left: 1.25rem; } .border { position: absolute; top: 1.2rem; bottom: 0.5rem; width: 0.7rem; margin-left: -0.85rem; border: 1px solid; border-right: none; border-top: none; @apply border-cards-tertiary; } } ================================================ FILE: desktop/angular/src/app/shared/config/filter-lists/filter-list.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, HostListener, OnDestroy, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { PortapiService, Record } from '@safing/portmaster-api'; import { Subscription } from 'rxjs'; import { moveInOutListAnimation } from '../../animations'; interface Category { name: string; id: string; description: string; parent?: string | null; } interface Source { name: string; id: string; description: string; category: string; // urls: Resource[]; // we don't care about the actual URLs here. website: string; contribute: string; license: string; } interface FilterListIndex extends Record { version: string; schemaVersion: string; categories: Category[]; sources: Source[]; } interface TreeNode { id: string; name: string; description: string; children: TreeNode[]; expanded: boolean; selected: boolean; parent?: TreeNode; website?: string; license?: string; hasSelectedChildren: boolean; } @Component({ selector: 'app-filter-list', templateUrl: './filter-list.html', styleUrls: ['./filter-list.scss'], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FilterListComponent), multi: true, } ], animations: [ moveInOutListAnimation, ] }) export class FilterListComponent implements OnInit, OnDestroy, ControlValueAccessor { /** The actual filter-list index as loaded from the portmaster. */ private index: FilterListIndex | null = null; /** @private a list of "tree-nodes" to render */ nodes: TreeNode[] = []; /** A lookup map for fast ID to TreeNode lookups */ private lookupMap: Map = new Map(); /** @private forward blur events to the onTouch callback. */ @HostListener('blur') onBlur() { this.onTouch(); } /** The currently selected IDs. */ private selectedIDs: string[] = []; /** Subscription to watch the filterlist index. */ private watchSubscription = Subscription.EMPTY; constructor(private portapi: PortapiService, private changeDetectorRef: ChangeDetectorRef) { } ngOnInit() { this.watchSubscription = this.portapi.watch("cache:intel/filterlists/index") .subscribe( index => this.updateIndex(index), err => { // Filter list index not yet loaded. console.error(`failed to get fitlerlist index`, err); } ); } ngOnDestroy() { this.watchSubscription.unsubscribe(); } /** The onChange callback registered by ngModel or form controls */ private _onChange: (v: string[]) => void = () => { }; /** Registers the onChange callback required by ControlValueAccessor */ registerOnChange(fn: (v: string[]) => void) { this._onChange = fn; } /** The _onTouch callback registered by ngModel and form controls */ private onTouch: () => void = () => { }; /** Registeres the onTouch callback required by ControlValueAccessor. */ registerOnTouched(fn: () => void) { this.onTouch = fn; } /** * Update the currently selected IDs. Used by ngModel * and form controls. Implements ControlValueAccessor. * * @param ids A list of selected IDs */ writeValue(ids: string[]) { this.selectedIDs = ids; if (!!this.index) { this.updateIndex(this.index); } } /** * * @param index The filter list index. */ private updateIndex(index: FilterListIndex) { this.index = index; var nodes: TreeNode[] = []; let lm = new Map(); let childCategories: Category[] = []; // Create a tree-node for each category this.index.categories.forEach(category => { let tn: TreeNode = { id: category.id, description: category.description, name: category.name, children: [], expanded: this.lookupMap.get(category.id)?.expanded || false, // keep it expanded if the user did not change anything. selected: false, hasSelectedChildren: false, }; lm.set(category.id, tn) // if the category does not have a parent // it's a root node. if (!category.parent) { nodes.push(tn); } else { // we need to handle child-categories later. childCategories.push(category); } }); // iterate over all "child" categories and add // them to the correct parent (which must be in lm already.) childCategories.forEach(category => { const tn = lm.get(category.id)!; const parent = lm.get(category.parent!); // if the parent category does not exist ignore it if (!parent) { return; } parent.children.push(tn); tn.parent = parent; }); this.index.sources.forEach(source => { let category = lm.get(source.category); if (!category) { return; } let tn: TreeNode = { id: source.id, name: source.name, description: source.description, children: [], expanded: false, selected: false, parent: category, website: source.website, license: source.license, hasSelectedChildren: false } // Add the source to the lookup-map lm.set(source.id, tn); category.children.push(tn); }); // make sure we expand all parent categories for // all selected IDs so they are actually visible. this.selectedIDs.forEach(id => { const tn = lm.get(id); if (!tn) { return; } this.updateNode(tn, true, true, true, false); let parent = tn.parent; while (!!parent) { parent.expanded = true; parent.hasSelectedChildren = true; parent = parent.parent; } }); this.nodes = nodes; this.lookupMap = lm; this.changeDetectorRef.markForCheck(); } /** Returns all actually selected IDs. */ private getIDs() { let ids: string[] = []; let collectIds = (n: TreeNode) => { if (n.selected) { // If the parent is selected we can ignore the // childs because they must be selected as well. ids.push(n.id); return; } n.children.forEach(child => collectIds(child)); } this.nodes.forEach(node => collectIds(node)) return ids; } updateNode(node: TreeNode, selected: boolean, updateChildren = true, updateParents = true, emit = true) { if (node.selected === selected) { // Nothing changed return; } // update the node an all children node.selected = selected; if (updateChildren) { node.children.forEach(child => this.updateNode(child, selected, true, false, false)); } // if we have a parent we might need to update // the parent as well. if (!!node.parent && updateParents) { if (selected) { // if we are now selected we might need to "select" the // parent if all children are selected now. const hasUnselected = node.parent.children.some(sibling => !sibling.selected); if (!hasUnselected) { // We need to update all parents but updating children // is useless. this.updateNode(node.parent, true, false, true, false); } } else if (node.parent.selected) { // if we are unselected now we might need to "unselect" the parent // but select siblings directly const selectedSiblings = node.parent.children.filter(sibling => sibling.selected && sibling !== node); this.updateNode(node.parent, false, false, true, false) } } if (emit) { const ids = this.getIDs(); this.selectedIDs = ids; this._onChange(this.selectedIDs); } } /** @private TrackByFunction for tree nodes. */ trackNode(_: number, node: TreeNode) { return node.id; } } ================================================ FILE: desktop/angular/src/app/shared/config/filter-lists/index.ts ================================================ export { FilterListComponent } from './filter-list'; ================================================ FILE: desktop/angular/src/app/shared/config/generic-setting/generic-setting.html ================================================

Saved {{ _setting?.RequiresRestart ? ' - Restart required' : (uiReloadRequired ? ' - Reload required' : '') }} Invalid Value: {{ rejected }} This feature requires a subscription.
{{setting?.Key}} Beta Experimental Advanced Developer
Quick Settings {{quick.Name}}

This setting stacks on top of the following global setting:

This setting stacks on top of the following global setting:

This setting stacks on top of the following global setting:

Inherited from Global Settings App specific configuration
{{resetLabelText}}

{{ _setting?.Name }}

================================================ FILE: desktop/angular/src/app/shared/config/generic-setting/generic-setting.scss ================================================ :host { @apply block; &.ng-invalid { @apply border border-red border-opacity-50; } &.rejected { .release-level.rejected { opacity: 1; } } &.highlighted:not(.touched) { .name { animation: fade-color 5s ease-out; } } } .stacked-values { margin-top: 0.5rem; opacity: 0.7; @apply w-full; } .unlock-button { @apply flex w-6 h-6 rounded-full; justify-content: center; align-items: center; cursor: pointer; position: absolute; right: calc(-1.5rem/2); top: calc(50% - 1.5rem/2); &:hover { @apply bg-blue; } } .description, .help-text { display: block; @apply text-secondary; } .help-text { @apply mb-2; } .notice { display: block; padding-left: 0.5rem; padding-right: 0.5rem; @apply mb-4; @apply text-secondary; fa-icon { @apply mr-2; } } .help-text { @apply p-4; @apply bg-cards-secondary; @apply rounded; .toggle { position: relative; left: -0.25rem; cursor: pointer; fa-icon { @apply pr-1; } &:hover { @apply text-primary; } } } @keyframes fade-color { 0% { @apply text-blue; } 90% { @apply text-blue; } 100% { @apply text-primary; } } ================================================ FILE: desktop/angular/src/app/shared/config/generic-setting/generic-setting.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { TemplatePortal } from '@angular/cdk/portal'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, EventEmitter, HostBinding, Input, OnInit, Output, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { NgModel } from '@angular/forms'; import { BaseSetting, ConfigService, ExpertiseLevel, ExpertiseLevelNumber, ExternalOptionHint, OptionType, PortapiService, QuickSetting, ReleaseLevel, SPNService, SettingValueType, UserProfile, WellKnown, applyQuickSetting } from '@safing/portmaster-api'; import { SfngDialogRef, SfngDialogService } from '@safing/ui'; import { Button } from 'js-yaml-loader!../../../i18n/helptexts.yaml'; import { Subject } from 'rxjs'; import { debounceTime, tap } from 'rxjs/operators'; import { ActionIndicatorService } from '../../action-indicator'; import { fadeInAnimation, fadeOutAnimation } from '../../animations'; import { ExpertiseService } from '../../expertise/expertise.service'; import { SPNAccountDetailsComponent } from '../../spn-account-details'; export interface SaveSettingEvent = any> { key: string; value: SettingValueType; isDefault: boolean; rejected?: (err: any) => void accepted?: () => void } @Component({ selector: 'app-generic-setting', templateUrl: './generic-setting.html', exportAs: 'appGenericSetting', styleUrls: ['./generic-setting.scss'], changeDetection: ChangeDetectionStrategy.OnPush, animations: [ fadeInAnimation, fadeOutAnimation ] }) export class GenericSettingComponent> implements OnInit { // // Constants used in the template. // readonly optionHint = ExternalOptionHint; readonly expertiseNames = ExpertiseLevel readonly expertise = ExpertiseLevelNumber; readonly optionType = OptionType; readonly releaseLevel = ReleaseLevel; readonly wellKnown = WellKnown; @ViewChild('helpTemplate', { read: TemplateRef, static: true }) helpTemplate: TemplateRef | null = null; private helpDialogRef: SfngDialogRef | null = null; // Whether or not the user needs to upgrade his/her account before // this setting is valid. _upgradeRequired = false; /** * Whether or not the component/setting is disabled and should * be read-only. */ @Input() @HostBinding('class.disabled') set disabled(v: any) { this._disabled = coerceBooleanProperty(v); } get disabled() { return this._disabled || this._upgradeRequired; } private _disabled: boolean = false; /** Returns the symbolMap annoation for endpoint-lists */ get symbolMap() { return this.setting?.Annotations[WellKnown.EndpointListVerdictNames] || { '+': 'Allow', '-': 'Block' }; } /** Whether or not the setting should be in select mode */ @Input() set selectMode(v: any) { this._selectMode = coerceBooleanProperty(v) if (!this.selectMode) { this.selected = false; this.selectedChange.next(false); } } get selectMode() { return this._selectMode } private _selectMode = false; /** Whether or not the setting has been selected */ @Input() set selected(v: any) { this._selected = coerceBooleanProperty(v) } get selected() { return this._selected } private _selected = false; /** Emits when the user (de-) selectes the setting. Can be used for two-way binding */ @Output() selectedChange = new EventEmitter(); /** Controls whether or not header with the setting name and success/failure markers is shown */ @Input() set showHeader(v: any) { this._showHeader = coerceBooleanProperty(v); } get showHeader() { return this._showHeader } private _showHeader = true; /** Controls whether or not the blue or red status borders are shown */ @Input() set enableActiveBorder(v: any) { this._enableActiveBorder = coerceBooleanProperty(v); } get enableActiveBorder() { return this._enableActiveBorder } private _enableActiveBorder = true; /** * Whether or not the component should be displayed as "locked" * when the default value is used (that is, no 'Value' property * in the setting) */ @Input() set lockDefaults(v: any) { this._lockDefaults = coerceBooleanProperty(v); } get lockDefaults() { return this._lockDefaults; } private _lockDefaults: boolean = false; /** The label to display in the reset-value button */ @Input() resetLabelText = 'Reset'; /** Emits an event whenever the setting should be saved. */ @Output() save = new EventEmitter>(); /** Wether or not stackable values should be displayed. */ @Input() set displayStackable(v: any) { this._displayStackable = coerceBooleanProperty(v); } get displayStackable() { return this._displayStackable; } private _displayStackable = false; /** * Whether or not the help text is currently shown */ @Input() set showHelp(v: any) { this._showHelp = coerceBooleanProperty(v); } get showHelp() { return this._showHelp; } private _showHelp = false; /** Used internally to publish save events. */ private triggerSave = new Subject(); /** Whether or not the value was reset. */ wasReset = false; /** Whether or not a save request was rejected */ @HostBinding('class.rejected') get rejected() { return this._rejected; } private _rejected = null; @HostBinding('class.saved') get changeAccepted() { return this._changeAccepted; } private _changeAccepted = false; /** * @private * Returns the external option type hint from a setting. * * @param opt The setting for with to return the external option hint */ externalOptType(opt: S | null): ExternalOptionHint | null { return opt?.Annotations?.[WellKnown.DisplayHint] || null; } /** * @private * Returns whether or not a restart is pending for this setting * to apply. */ get restartPending(): boolean { return !!this._setting?.Annotations?.[WellKnown.RestartPending]; } /** * @private * Returns whether or not a UI reload is required for this setting * to apply */ get uiReloadRequired(): boolean { return this._setting?.Annotations?.[WellKnown.RequiresUIReload] !== undefined; } /** * Returns true if the setting has been touched (modified) by the user * since the component has been rendered. */ @HostBinding('class.touched') get touched() { return this._touched; } private _touched = false; /** * Returns true if the settings is currently locked. */ @HostBinding('class.locked') get isLocked() { return (this.wasReset || !this.userConfigured) && this.lockDefaults; } /** * Returns true if the user has configured the setting on their * own or if the default value is being used. */ @HostBinding('class.changed') get userConfigured() { return this.setting?.Value !== undefined; } /** * Returns true if the setting is dirty. That is, the user * has changed the setting in the view but it has not yet * been saved. */ @HostBinding('class.dirty') get dirty() { if (typeof this._currentValue !== 'object') { return this._currentValue !== this._savedValue; } // JSON object (OptionType.StringArray) require will // not be the same reference so we need to compare their // string representations. That's a bit more costly but should // still be fast enough. // TODO(ppacher): calculate this only when required. return JSON.stringify(this._currentValue) !== JSON.stringify(this._savedValue) } /** * Returns true if the setting is pristine. That is, the * settings default value is used and the user has not yet * changed the value inside the view. */ @HostBinding('class.pristine') get pristine() { return !this.dirty && !this.userConfigured } /** A list of buttons for the tip-up */ sfngTipUpButtons: Button[] = []; /** * Unlock the setting if it is locked. Unlocking will * emit the default value to be safed for the setting. */ unlock() { if (!this.isLocked || !this.setting) { return; } this._touched = true; this.wasReset = false; let value = this.defaultValue; if (this.stackable) { // TODO(ppacher): fix this one once string[] options can be // stackable value = [] as SettingValueType; } this.updateValue(value, true); // update the settings value now so the UI // responds immediately. this.setting!.Value = value; } /** True if the current setting is stackable */ get stackable() { return !!this.setting?.Annotations[WellKnown.Stackable]; } /** Wether or not stackable values should be shown right now */ get showStackable() { return this.stackable && this.displayStackable; } /** * @private * Toggle Whether or not the help text is displayed */ toggleHelp() { this.showHelp = !this.showHelp; } /** * @private * Toggle Whether or not the setting is currently locked. */ toggleLock() { if (this.isLocked) { this.unlock(); return; } this.resetValue(); } /** * @private * Closes the help dialog. */ closeHelpDialog() { this.helpDialogRef?.close(); } @ViewChild(NgModel, { static: false }) model: NgModel | null = null; /** * The actual setting that should be managed. * The setter also updates the "currently" used * value (which is either user configured or * the default). See {@property userConfigured}. */ @Input() set setting(s: S | null) { this.sfngTipUpButtons = []; this._setting = s; if (!s) { this._currentValue = null; return; } if (this._setting?.Help) { this.sfngTipUpButtons = [ { name: 'Show More', action: { ID: '', Text: '', Type: 'ui', Visibility: '', Run: async () => { if (!this.helpTemplate) { return; } // close any existing help dialog for THIS setting. if (!!this.helpDialogRef) { this.helpDialogRef.close(); } // Create a new dialog form the helpTemplate const portal = new TemplatePortal(this.helpTemplate, this.viewRef); const ref = this.dialog.create(portal, { // we don't use a backdrop and make the dialog dragable so the user can // move it somewhere else and keep it open while configuring the setting. backdrop: false, dragable: true, }); // make sure we reset the helpDialogRef to null once it get's clsoed. this.helpDialogRef = ref; this.helpDialogRef.onClose.subscribe(() => { // but only if helpDialogRef still points to the same // dialog reference. Otherwise we got closed because the user // opened a new one and helpDialogRef already points to the new // dialog. if (this.helpDialogRef === ref) { this.helpDialogRef = null; } }); }, Payload: undefined, }, }, ] } this.updateActualValue(); } get setting(): S | null { return this._setting; } /** * The defaultValue input allows to overwrite the default * value of the setting. */ @Input() set defaultValue(val: SettingValueType) { this._defaultValue = val; this.updateActualValue(); } get defaultValue() { // Return cached value. if (this._defaultValue !== null) { return this._defaultValue; } // Stackable options are displayed differently. if (this.stackable) { if (this.setting?.GlobalDefault === undefined && this.setting?.DefaultValue !== null) { return this.setting?.DefaultValue; } return [] as SettingValueType; } // Return global, then default value. if (this.setting?.GlobalDefault !== undefined) { return this.setting.GlobalDefault } return this.setting?.DefaultValue } /* An optional default value overwrite */ _defaultValue: SettingValueType | null = null; /* Whether or not the setting has been saved */ saved = true; /* The settings value, updated by the setting() setter */ _setting: S | null = null; /* The currently configured value. Updated by the setting() setter */ _currentValue: SettingValueType | null = null; /* The currently saved value. Updated by the setting() setter */ _savedValue: SettingValueType | null = null; /* Used to cache the value of a basic-setting because we only want to save that on blur */ _basicSettingsValueCache: SettingValueType | null = null /** Whether or not the network rating system is enabled. */ networkRatingEnabled$ = this.configService.networkRatingEnabled$; get expertiseLevel() { return this.expertiseService.change; } constructor( private expertiseService: ExpertiseService, private configService: ConfigService, private portapi: PortapiService, private dialog: SfngDialogService, private changeDetectorRef: ChangeDetectorRef, private actionIndicator: ActionIndicatorService, private spn: SPNService, private viewRef: ViewContainerRef, private destryoRef: DestroyRef, ) { } ngOnInit() { this.triggerSave .pipe( debounceTime(500), takeUntilDestroyed(this.destryoRef), ) .subscribe(() => this.emitSaveRequest()) // watch the SPN user profile so we know which feature_ids // are available for the user. this.spn.profile$ .pipe(takeUntilDestroyed(this.destryoRef)) .subscribe((profile: UserProfile | null) => { let value = this.setting?.Annotations[WellKnown.RequiresFeatureID] if (value === undefined) { this._upgradeRequired = false; } else { if (!Array.isArray(value)) { value = [value]; } this._upgradeRequired = value.some(val => !(profile?.current_plan?.feature_ids || []).includes(val)) } this.changeDetectorRef.markForCheck(); }) } /** * @private * Resets the value of setting by discarding any user * configured values and reverting back to the default * value. */ resetValue() { if (!this._setting) { return; } this._touched = true; this._currentValue = this.defaultValue; this.wasReset = true; this.triggerSave.next(); } /** * @private * Aborts/reverts the current change to the value that's * already saved. */ abortChange() { this._currentValue = this._savedValue; this._touched = true; this._rejected = null; } /** * @private * Update the current value by applying a quick-setting. * * @param qs The quick-settting to apply */ applyQuickSetting(qs: QuickSetting>) { if (this.disabled) { return; } const value = applyQuickSetting(this._currentValue, qs); if (value === null) { return; } this.updateValue(value, true); } openAccountDetails() { this.dialog.create(SPNAccountDetailsComponent, { autoclose: true, backdrop: 'light' }) } restartNow() { if (this._setting?.RequiresRestart) { this.dialog.confirm({ header: 'Restart Portmaster', message: 'Do you want to restart the Portmaster now?', buttons: [ { id: 'no', text: 'Maybe Later', class: 'outline', }, { id: 'restart', text: 'Restart', class: 'danger' } ] }) .onAction('restart', () => this.portapi.restartPortmaster() .subscribe(this.actionIndicator.httpObserver( 'Restarting ...', 'Failed to Restart', )) ) .onAction('no', () => { this._changeAccepted = false; this.changeDetectorRef.markForCheck(); }); return; } if (this.uiReloadRequired) { this.portapi.reloadUI() .pipe( tap(() => { setTimeout(() => window.location.reload(), 1000) }) ) .subscribe(this.actionIndicator.httpObserver( 'Reloading UI ...', 'Failed to Reload UI', )) } } /** * Emits a save request to the parent component. */ private _saveInterval: any; private emitSaveRequest() { const isDefault = this.wasReset; let value = this._setting!['Value']; if (isDefault) { delete (this._setting!['Value']); } else { this._setting!.Value = this._currentValue; } let wasReset = this.wasReset; this.wasReset = false; this._rejected = null; this._changeAccepted = false; if (!!this._saveInterval) { clearTimeout(this._saveInterval); } this.save.next({ key: this.setting!.Key, isDefault: isDefault, value: this._setting!.Value, rejected: (err: any) => { this._setting!['Value'] = value; this._rejected = err; this.changeDetectorRef.markForCheck(); }, accepted: () => { if (!wasReset) { this._changeAccepted = true; // if no restart is required fade the "✔️ Saved" out after // a few seconds. if (!this._setting?.RequiresRestart) { this._saveInterval = setTimeout(() => { this._changeAccepted = false; this._saveInterval = null; this.changeDetectorRef.markForCheck(); }, 4000); } } this.changeDetectorRef.markForCheck(); } }) } /** * @private * Used in our view as a ngModelChange callback to * update the value. * * @param value The new value as emitted by the view */ updateValue(value: SettingValueType, save = false) { this._touched = true; this._changeAccepted = false; this._rejected = null; if (!!this._saveInterval) { clearTimeout(this._saveInterval); } if (save) { this._currentValue = value; this.triggerSave.next(); } else { this._basicSettingsValueCache = value; } } /** * @private * A list of quick-settings available for the setting. * The getter makes sure to always return an array. */ get quickSettings(): QuickSetting>[] { if (!this.setting || !this.setting.Annotations[WellKnown.QuickSetting]) { return []; } const quickSettings = this.setting.Annotations[WellKnown.QuickSetting]!; return Array.isArray(quickSettings) ? quickSettings : [quickSettings]; } /** * Determine the current, actual value of the setting * by taking the settings Value, default Value or global * default into account. */ private updateActualValue() { if (!this.setting) { return } this.wasReset = false; const s = this.setting; const value = s.Value === undefined ? this.defaultValue : s.Value; this._currentValue = value; this._savedValue = value; this._basicSettingsValueCache = value; } } ================================================ FILE: desktop/angular/src/app/shared/config/generic-setting/index.ts ================================================ export * from './generic-setting'; ================================================ FILE: desktop/angular/src/app/shared/config/import-dialog/cursor.ts ================================================ // Credit to Liam (Stack Overflow) // https://stackoverflow.com/a/41034697/3480193 export class Cursor { static getCurrentCursorPosition(parentElement: Node) { var selection = window.getSelection(), charCount = -1, node; if (selection?.focusNode) { if (Cursor._isChildOf(selection.focusNode, parentElement)) { node = selection.focusNode; charCount = selection.focusOffset; while (node) { if (node === parentElement) { break; } if (node.previousSibling) { node = node.previousSibling; charCount += node.textContent?.length || 0 } else { node = node.parentNode; if (node === null) { break; } } } } } return charCount; } static setCurrentCursorPosition(chars: number, element: Node) { if (chars >= 0) { var selection = window.getSelection(); let range = Cursor._createRange(element, { count: chars }); if (range) { range.collapse(false); selection?.removeAllRanges(); selection?.addRange(range); } } } static _createRange(node: Node, chars: { count: number }, range?: Range): Range { if (!range) { range = document.createRange() range.selectNode(node); range.setStart(node, 0); } if (chars.count === 0) { range.setEnd(node, chars.count); } else if (node && chars.count > 0) { if (node.nodeType === Node.TEXT_NODE) { if (node.textContent!.length < chars.count) { chars.count -= node.textContent!.length; } else { range.setEnd(node, chars.count); chars.count = 0; } } else { for (var lp = 0; lp < node.childNodes.length; lp++) { range = Cursor._createRange(node.childNodes[lp], chars, range); if (chars.count === 0) { break; } } } } return range; } static _isChildOf(node: Node, parentElement: Node) { while (node !== null) { if (node === parentElement) { return true; } node = node.parentNode!; } return false; } } ================================================ FILE: desktop/angular/src/app/shared/config/import-dialog/import-dialog.component.html ================================================

Import {{ dialogRef.data.type === "setting" ? "Settings" : "Profile" }}

Please paste the "Export Content" or use "Choose File" to select one from your hard disk.


Configuration
Warning
{{ errorMessage }}
  • This export contains unknown settings. To import it, you must enable "Allow unknown settings".
  • {{ dialogRef.data.type === "setting" ? "This export will overwrite settings that have been changed by you." : "This export will overwrite an existing profile." }} And deletes {{ count }} previously merged profile{{ count > 1 ? 's' : '' }}
  • This export will require a restart of the Portmaster to take effect.
================================================ FILE: desktop/angular/src/app/shared/config/import-dialog/import-dialog.component.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, ViewChild, inject, } from '@angular/core'; import { ImportResult, PortapiService, ProfileImportResult } from '@safing/portmaster-api'; import { SFNG_DIALOG_REF, SfngDialogRef } from '@safing/ui'; import { ActionIndicatorService } from '../../action-indicator'; import { getSelectionOffset, setSelectionOffset } from './selection'; import { Observable } from 'rxjs'; export interface ImportConfig { key: string; type: 'setting' | 'profile'; } @Component({ templateUrl: './import-dialog.component.html', styles: [ ` :host { @apply flex flex-col gap-2 overflow-hidden; min-height: 24rem; min-width: 24rem; max-height: 40rem; max-width: 40rem; } `, ], changeDetection: ChangeDetectionStrategy.OnPush, }) export class ImportDialogComponent { readonly dialogRef: SfngDialogRef< ImportDialogComponent, unknown, ImportConfig > = inject(SFNG_DIALOG_REF); private readonly portapi = inject(PortapiService); private readonly uai = inject(ActionIndicatorService); private readonly cdr = inject(ChangeDetectorRef); @ViewChild('codeBlock', { static: true, read: ElementRef }) codeBlockElement!: ElementRef; result: ImportResult | ProfileImportResult | null = null; reset = false; allowUnknown = false; triggerRestart = false; allowReplace = false; get replacedProfiles() { if (this.result === null) { return [] } if ('replacesProfiles' in this.result) { return this.result.replacesProfiles || []; } return []; } errorMessage: string = ''; get scope() { return this.dialogRef.data; } onBlur() { const text = this.codeBlockElement.nativeElement.innerText; this.updateAndValidate(text); } onPaste(event: ClipboardEvent) { event.stopPropagation(); event.preventDefault(); // Get pasted data via clipboard API const clipboardData = event.clipboardData || (window as any).clipboardData; const text = clipboardData.getData('Text'); this.updateAndValidate(text); } import() { const text = this.codeBlockElement.nativeElement.innerText; let saveFunc: Observable; if (this.dialogRef.data.type === 'setting') { saveFunc = this.portapi.importSettings( text, this.dialogRef.data.key, 'text/yaml', this.reset, this.allowUnknown ); } else { saveFunc = this.portapi.importProfile( text, 'text/yaml', this.reset, this.allowUnknown, this.allowReplace ); } saveFunc.subscribe({ next: (result) => { let msg = ''; if (result.restartRequired) { if (this.triggerRestart) { this.portapi.restartPortmaster().subscribe(); msg = 'Portmaster will be restarted now.'; } else { msg = 'Please restart Portmaster to apply the new settings.'; } } this.uai.success('Settings Imported Successfully', msg); this.dialogRef.close(); }, error: (err) => { this.uai.error( 'Failed To Import Settings', this.uai.getErrorMessgae(err) ); }, }); } updateAndValidate(content: string) { const [start, end] = getSelectionOffset( this.codeBlockElement.nativeElement ); const p = (window as any).Prism; const blob = p.highlight(content, p.languages.yaml, 'yaml'); this.codeBlockElement.nativeElement.innerHTML = blob; setSelectionOffset(this.codeBlockElement.nativeElement, start, end); if (content === '') { return; } window.getSelection()?.removeAllRanges(); let validateFunc: Observable; if (this.dialogRef.data.type === 'setting') { validateFunc = this.portapi.validateSettingsImport( content, this.dialogRef.data.key, 'text/yaml' ); } else { validateFunc = this.portapi.validateProfileImport(content, 'text/yaml'); } validateFunc.subscribe({ next: (result) => { this.result = result; this.errorMessage = ''; this.cdr.markForCheck(); }, error: (err) => { const msg = this.uai.getErrorMessgae(err); this.errorMessage = msg; this.result = null; this.cdr.markForCheck(); }, }); } loadFile(event: Event) { const file: File = (event.target as any).files[0]; if (!file) { this.updateAndValidate(''); return; } const reader = new FileReader(); reader.onload = (data) => { (event.target as any).value = ''; let content = (data.target as any).result; this.updateAndValidate(content); }; reader.readAsText(file); } } ================================================ FILE: desktop/angular/src/app/shared/config/import-dialog/selection.ts ================================================ /** return true if node found */ function searchNode( container: Node, startNode: Node, predicate: (node: Node) => boolean, excludeSibling?: boolean, ): boolean { if (predicate(startNode as Text)) { return true } for (let i = 0, len = startNode.childNodes.length; i < len; i++) { if (searchNode(startNode, startNode.childNodes[i], predicate, true)) { return true } } if (!excludeSibling) { let parentNode = startNode while (parentNode && parentNode !== container) { let nextSibling = parentNode.nextSibling while (nextSibling) { if (searchNode(container, nextSibling, predicate, true)) { return true } nextSibling = nextSibling.nextSibling } parentNode = parentNode.parentNode! } } return false } function createRange(container: Node, start: number, end: number): Range { let startNode: any; searchNode(container, container, node => { if (node.nodeType === Node.TEXT_NODE) { const dataLength = (node as Text).data.length if (start <= dataLength) { startNode = node return true } start -= dataLength end -= dataLength } return false }) let endNode: any; if (startNode) { searchNode(container, startNode, node => { if (node.nodeType === Node.TEXT_NODE) { const dataLength = (node as Text).data.length if (end <= dataLength) { endNode = node return true } end -= dataLength } return false }) } const range = document.createRange() if (startNode) { if (start < startNode.data.length) { range.setStart(startNode, start) } else { range.setStartAfter(startNode) } } else { if (start === 0) { range.setStart(container, 0) } else { range.setStartAfter(container) } } if (endNode) { if (end < endNode.data.length) { range.setEnd(endNode, end) } else { range.setEndAfter(endNode) } } else { if (end === 0) { range.setEnd(container, 0) } else { range.setEndAfter(container) } } return range } export function setSelectionOffset(node: Node, start: number, end: number) { const range = createRange(node, start, end) const selection = window.getSelection()! selection.removeAllRanges() selection.addRange(range) } function getAbsoluteOffset(container: Node, offset: number) { if (container.nodeType === Node.TEXT_NODE) { return offset } let absoluteOffset = 0 for (let i = 0, len = Math.min(container.childNodes.length, offset); i < len; i++) { const childNode = container.childNodes[i] searchNode(childNode, childNode, node => { if (node.nodeType === Node.TEXT_NODE) { absoluteOffset += (node as Text).data.length } return false }) } return absoluteOffset } export function getSelectionOffset(container: Node): [number, number] { let start = 0 let end = 0 const selection = window.getSelection()! for (let i = 0, len = selection.rangeCount; i < len; i++) { const range = selection.getRangeAt(i) if (range.intersectsNode(container)) { const startNode = range.startContainer searchNode(container, container, node => { if (startNode === node) { start += getAbsoluteOffset(node, range.startOffset) return true } const dataLength = node.nodeType === Node.TEXT_NODE ? (node as Text).data.length : 0 start += dataLength end += dataLength return false }) const endNode = range.endContainer searchNode(container, startNode, node => { if (endNode === node) { end += getAbsoluteOffset(node, range.endOffset) return true } const dataLength = node.nodeType === Node.TEXT_NODE ? (node as Text).data.length : 0 end += dataLength return false }) break } } return [start, end] } export function getInnerText(container: Node): string { const buffer: any = [] searchNode(container, container, node => { if (node.nodeType === Node.TEXT_NODE) { buffer.push((node as Text).data) } return false }) return buffer.join('') } ================================================ FILE: desktop/angular/src/app/shared/config/index.ts ================================================ export * from './basic-setting'; export * from './config-settings'; export * from './config.module'; export * from './filter-lists'; export * from './generic-setting'; export * from './ordererd-list'; export * from './rule-list'; export * from './safe.pipe'; ================================================ FILE: desktop/angular/src/app/shared/config/ordererd-list/index.ts ================================================ export { OrderedListComponent } from './ordered-list'; export { OrderedListItemComponent } from './item'; ================================================ FILE: desktop/angular/src/app/shared/config/ordererd-list/item.html ================================================
{{value}}
================================================ FILE: desktop/angular/src/app/shared/config/ordererd-list/item.scss ================================================ :host { @apply flex outline-none; @apply space-x-2; &>* { @apply rounded; @apply bg-gray-300; } } div.value { @apply border-gray-500 border; @apply p-1; @apply px-2; &.edit { @apply p-0; @apply bg-gray-400; input { margin: 0; width: auto; flex-grow: 1; border: none; @apply shadow-none; } input:focus+.buttons { @apply bg-gray-500 border-gray-600 bg-opacity-75 border-opacity-75; } } flex-grow : 1; display : flex; justify-content: space-between; align-items : center; .buttons { flex-shrink: 0; height: 100%; width: 4rem; @apply flex items-center justify-evenly; fa-icon { cursor: pointer; @apply text-primary; @apply p-1; opacity: 0.7; font-size: 0.6rem; &:hover { opacity: 1; } } } } ================================================ FILE: desktop/angular/src/app/shared/config/ordererd-list/item.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; @Component({ selector: 'app-ordered-list-item', templateUrl: './item.html', styleUrls: ['./item.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class OrderedListItemComponent implements OnInit { @Input() set readonly(v: any) { this._readonly = coerceBooleanProperty(v); } get readonly() { return this._readonly; } private _readonly = false; @Input() set value(v: string) { this._value = v; this._savedValue = v; } get value() { return this._value; } _value = ''; private _savedValue = ''; @Output() readonly valueChange = new EventEmitter(); @Output() readonly delete = new EventEmitter(); @Input() set edit(v: any) { this._edit = coerceBooleanProperty(v); } get edit() { return this._edit; } _edit = false; @Output() readonly editChange = new EventEmitter(); ngOnInit() { if (this._value === '' && this._savedValue === '') { this.edit = true; } } toggleEdit() { const wasEdit = this._edit; this._edit = !wasEdit; this.editChange.next(this._edit); if (!wasEdit) { return; } if (this._value !== this._savedValue) { this._value = this._value.trim() this.valueChange.next(this.value); this._savedValue = this._value; } this.changeDetectorRef.markForCheck(); } reset() { if (this._edit) { if (this._value !== '' || this._savedValue !== '') { this._value = this._savedValue; this.changeDetectorRef.markForCheck(); return; } } this.delete.next(); } constructor(private changeDetectorRef: ChangeDetectorRef) { } } ================================================ FILE: desktop/angular/src/app/shared/config/ordererd-list/ordered-list.html ================================================
================================================ FILE: desktop/angular/src/app/shared/config/ordererd-list/ordered-list.scss ================================================ :host { outline: none; } .item, .cdk-drag-preview { display: flex; align-items: center; padding: 3px; fa-icon { cursor: pointer; @apply text-tertiary; @apply text-lg; @apply mr-2; } app-ordered-list-item { flex-grow: 1; } } .cdk-drag-placeholder { left: -4px; padding: 1px; padding-left: 4px; } // TODO(ppacher9): move this transition to a mixin .list-items.cdk-drop-list-dragging .list:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); } .cdk-drag-preview { left: -4px; padding: 1px; padding-left: 4px; } .button-list { @apply mt-2; @apply ml-8; } .new-entry { position: relative; cursor: pointer; @apply w-full; @apply rounded; @apply p-1; @apply border-2; @apply border-dashed; @apply border-buttons-light; @apply bg-background; @apply text-secondary; span { @apply font-medium; } fa-icon { font-size: 1rem; } &:hover { @apply text-primary; @apply bg-cards-secondary; span { @apply text-primary; } } display : flex; align-items : center; justify-content: center; } ================================================ FILE: desktop/angular/src/app/shared/config/ordererd-list/ordered-list.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, HostBinding, HostListener, Input } from "@angular/core"; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ selector: 'app-ordered-list', templateUrl: './ordered-list.html', styleUrls: ['./ordered-list.scss'], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => OrderedListComponent), multi: true, } ] }) export class OrderedListComponent implements ControlValueAccessor { @HostBinding('tabindex') readonly tabindex = 0; @HostListener('blur') onBlur() { this.onTouch(); } @Input() set readonly(v: any) { this._readonly = coerceBooleanProperty(v); } get readonly() { return this._readonly; } _readonly = false; @Input() set fixedOrder(v: any) { this._fixedOrder = coerceBooleanProperty(v); } get fixedOrder() { return this._fixedOrder; } private _fixedOrder = false; entries: string[] = []; constructor(private changeDetector: ChangeDetectorRef) { } updateValue(index: number, newValue: string) { // we need to make a new object copy here. this.entries = [ ...this.entries, ]; this.entries[index] = newValue; this.onChange(this.entries); } deleteEntry(index: number) { this.entries = [...this.entries]; this.entries.splice(index, 1); this.onChange(this.entries); } addEntry() { // if there's already one empty entry abort if (this.entries.some(e => e.trim() === '')) { return; } this.entries = [...this.entries]; this.entries.push(''); //this.onChange(this.entries); } writeValue(value: string[]) { this.entries = value; this.changeDetector.markForCheck(); } onChange = (_: string[]): void => { }; registerOnChange(fn: (value: string[]) => void) { this.onChange = fn; } onTouch = (): void => { }; registerOnTouched(fn: () => void) { this.onTouch = fn; } drop(event: CdkDragDrop) { if (this._readonly) { return; } // create a copy of the array this.entries = [...this.entries]; moveItemInArray(this.entries, event.previousIndex, event.currentIndex); this.changeDetector.markForCheck(); this.onChange(this.entries); } trackBy(idx: number, value: string) { return `${value}`; } } ================================================ FILE: desktop/angular/src/app/shared/config/rule-list/index.ts ================================================ export * from './list-item'; export * from './rule-list'; ================================================ FILE: desktop/angular/src/app/shared/config/rule-list/list-item.html ================================================
{{ symbolMap["+"] }} {{ symbolMap["-"] }} {{ symbolMap["+"] }} {{ symbolMap["-"] }}
{{ display }}
================================================ FILE: desktop/angular/src/app/shared/config/rule-list/list-item.scss ================================================ :host { display: flex; outline: none; @apply space-x-2; &>* { @apply rounded; @apply bg-gray-300; } } div.action { @apply border-gray-500 border; flex-shrink: 0; min-width: 6rem; text-align: center; } div.value { @apply border-gray-500 border; @apply p-1.5; @apply px-2; &.edit { @apply p-0; @apply bg-gray-400; input { margin: 0; width: auto; height: 100%; flex-grow: 1; border: none; @apply shadow-none; } input:focus+.buttons { @apply bg-gray-500 border-gray-600 bg-opacity-75 border-opacity-75; } } flex-grow : 1; display : flex; justify-content: space-between; align-items : center; .buttons { flex-shrink: 0; height: 100%; width: 4rem; @apply flex items-center justify-evenly; fa-icon { cursor: pointer; @apply text-primary; @apply p-1; opacity: 0.7; font-size: 0.6rem; &:hover { opacity: 1; } } } } ================================================ FILE: desktop/angular/src/app/shared/config/rule-list/list-item.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, HostBinding, Input, OnInit, Output } from '@angular/core'; import { fadeInAnimation, fadeOutAnimation } from '../../animations'; @Component({ selector: 'app-rule-list-item', templateUrl: 'list-item.html', styleUrls: ['list-item.scss'], changeDetection: ChangeDetectionStrategy.OnPush, animations: [ fadeInAnimation, fadeOutAnimation ] }) export class RuleListItemComponent implements OnInit { /** The host element is going to fade in/out */ @HostBinding('@fadeIn') @HostBinding('@fadeOut') readonly animation = true; @Input() symbolMap: { [key: string]: string } = {} /** * The current value (rule) displayed by this component. * Supports two-way bindings. */ @Input() set value(v: string) { this.updateValue(v); this._savedValue = this._value; } private _value = ''; /** The last actually saved value of this rule. Required for resets */ private _savedValue = ''; /** * Emits whenever the rule value changes. * Supports two-way-bindings on ([value]) */ @Output() valueChange = new EventEmitter(); /** Whether or not the rule list item is selected */ @Input() set selected(v: any) { this._selected = coerceBooleanProperty(v) } get selected() { return this._selected; } private _selected = false; @Output() selectedChange = new EventEmitter(); /** * Whether or not the component is in edit mode. * Supports two-way-bindings on ([edit]) */ @Input() set edit(v: any) { this._edit = coerceBooleanProperty(v); } get edit() { return this._edit; } private _edit: boolean = false; /** * Emits whenever the component switch to or away from edit * mode. * Supports two-way-bindings on ([edit]) */ @Output() editChange = new EventEmitter(); /** * Whether or not the component should be in read-only mode. */ @Input() set readonly(v: any) { this._readonly = coerceBooleanProperty(v); } get readonly() { return this._readonly; } private _readonly: boolean = false; /** * Emits when the user presses the delete button of * this rule component. */ @Output() delete = new EventEmitter(); /** @private Whether or not this rule is a "Allow" rule - we default to allow since this is what most rules are used for */ isAllow = true; /** @private Whether or not this rule is a "Deny" rule */ isBlock = false; /** @private the actually displayed rule value (without the verdict) */ display = ''; /** @private the character representation of the current verdict */ get currentAction() { if (this.isBlock) { return '-'; } if (this.isAllow) { return '+'; } return ''; } constructor(private cdr: ChangeDetectorRef) { } ngOnInit() { // new entries always start in edit mode if (!this.isAllow && !this.isBlock) { this._edit = true; } } /** * @private * Toggle between edit and view mode. When switching from * edit to view mode, the current value is emitted to the * parent element in case it has been changed. */ toggleEdit() { if (this._edit) { // do nothing if the rule is obviously invalid (no verdict or value). if (this.display === '' || !(this.isAllow || this.isBlock)) { return; } if (this._value !== this._savedValue) { this.valueChange.next(this._value); } } this._edit = !this._edit; this.editChange.next(this._edit); } toggleSelection() { this.selected = !this.selected; this.selectedChange.next(this.selected); this.cdr.markForCheck(); } /** * @private * Sets the new rule action. Used as a callback in the drop-down. * * @param action The new action */ setAction(action: '+' | '-') { this.updateValue(`${action} ${this.display}`); } /** * @private * Update the actual value of the rule. * * @param entity The new rule value */ setEntity(entity: string) { const action = this.isAllow ? '+' : '-'; this.updateValue(`${action} ${entity}`); } /** * @private * * Reset the value to it's previously saved value if it was changed. * If the value is unchanged a reset counts as a delete and triggers * on our delete output. */ reset() { if (this._edit) { // if the user did not change anything we can immediately // delete it. if (this._savedValue !== '') { this.value = this._savedValue; this._edit = false; return; } } this.delete.next(); } /** * Updates our internal states to correctly display the rule. * * @param v The actual rule value */ private updateValue(v: string) { this._value = v.trim(); switch (this._value[0]) { case '+': this.isAllow = true; this.isBlock = false; break; case '-': this.isAllow = false; this.isBlock = true; break; default: // not yet set this.isBlock = this.isAllow = false; } this.display = this._value.slice(1).trim(); } } ================================================ FILE: desktop/angular/src/app/shared/config/rule-list/rule-list.html ================================================
No entries available
{{ selectedItems.length }} Rule{{ selectedItems.length > 1 ? 's' : ''}} selected Remove Rules Cancel
================================================ FILE: desktop/angular/src/app/shared/config/rule-list/rule-list.scss ================================================ :host { outline: none; } .item, .cdk-drag-preview { display: flex; align-items: center; padding: 3px; fa-icon { cursor: pointer; @apply text-tertiary; @apply text-lg; @apply mr-2; } app-rule-list-item { flex-grow: 1; } } .cdk-drag-placeholder { left: -4px; padding: 1px; padding-left: 4px; } // TODO(ppacher9): move this transition to a mixin .list-items.cdk-drop-list-dragging .list:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); } .cdk-drag-preview { left: -4px; padding: 1px; padding-left: 4px; } .button-list { @apply mt-2; @apply ml-8; } .dotted { @apply w-full; @apply rounded; @apply p-1; @apply border-2; @apply border-dashed; @apply border-buttons-light; @apply bg-background; @apply text-secondary; display: flex; align-items: center; justify-content: center; span { @apply font-medium; } } .new-entry { cursor: pointer; &:hover { @apply text-primary; @apply bg-gray-300; span { @apply text-primary; } } } ================================================ FILE: desktop/angular/src/app/shared/config/rule-list/rule-list.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, HostBinding, HostListener, Input, QueryList, ViewChildren } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { SfngDialogService } from '@safing/ui'; import { RuleListItemComponent } from './list-item'; @Component({ selector: 'app-rule-list', templateUrl: './rule-list.html', styleUrls: ['./rule-list.scss'], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RuleListComponent), multi: true, } ], }) export class RuleListComponent implements ControlValueAccessor { /** Add the host element into the tab-sequence */ @HostBinding('tabindex') readonly tabindex = 0; @ViewChildren(RuleListItemComponent) renderedRules!: QueryList; /** A list of selected rule indexes */ selectedItems: number[] = []; /** * @private * Mark the component as dirty by calling the onTouch callback of the control-value accessor */ @HostListener('blur') onBlur() { this.onTouch(); } @Input() symbolMap = { '+': 'Allow', '-': 'Block', } /** * Whether or not the component should be displayed as read-only. */ @Input() set readonly(v: any) { this._readonly = coerceBooleanProperty(v); } get readonly() { return this._readonly; } private _readonly = false; /** * @private * The actual rule entries. Displayed as RuleListItemComponent. */ entries: string[] = []; constructor( private changeDetector: ChangeDetectorRef, private dialog: SfngDialogService ) { } /** * @private * Update the value of a rule-list entry. Used as a callback function * for the valueChange output of the RuleListItemComponent. * * @param index The index of the rule list entry to update * @param newValue The new value of the rule */ updateValue(index: number, newValue: string) { // we need create a copy of the actual value as // the parent component might still have a reference // to the current values. this.entries = [ ...this.entries, ]; this.entries[index] = newValue; // tell the control that we have a new value this.onChange(this.entries); } /** * @private * Delete a rule list entry. * * @param index The index of the rule list entry to delete */ deleteEntry(index: number) { this.entries = [...this.entries]; this.entries.splice(index, 1); this.onChange(this.entries); } /** * @private * Add a new, empty rule list entry at the end of the * list. * * This is a no-op if there's already an empty item * available. */ addEntry() { // if there's already one empty entry abort if (this.entries.some(e => e.trim() === '')) { return; } this.entries = [...this.entries]; this.entries.push(''); } /** * Set a new value for the rule list. This is the * only way to configure the existing entries and is * used by the control-value-accessor and ngModel. * * @param value The new value set via [ngModel] */ writeValue(value: string[]) { this.entries = value; this.changeDetector.markForCheck(); } /** Toggles selection of a rule item */ selectItem(index: number, selected: boolean) { if (selected && !this.selectedItems.includes(index)) { this.selectedItems = [ ...this.selectedItems, index, ] return; } if (!selected && this.selectedItems.includes(index)) { this.selectedItems = this.selectedItems.filter(idx => idx !== index) return; } } /** Removes all selected items after displaying a confirmation dialog. */ removeSelectedItems() { this.dialog.confirm({ buttons: [ { id: 'abort', text: 'Cancel', class: 'outline' }, { id: 'delete', text: 'Delete Rules', class: 'danger' } ], canCancel: true, caption: 'Caution', header: 'Rule Deletion', message: 'Do you want to delete the selected rules' }) .onAction('delete', () => { this.entries = this.entries.filter((_, idx: number) => !this.selectedItems.includes(idx)) this.abortSelection(); this.onChange(this.entries); }) } /** Aborts the current selection */ abortSelection() { this.selectedItems.forEach(itemIdx => this.renderedRules.get(itemIdx)?.toggleSelection()) this.selectedItems = []; } /** @private onChange callback registered by ngModel and form controls */ onChange = (_: string[]): void => { }; /** Registers the onChange callback and required for the ControlValueAccessor interface */ registerOnChange(fn: (value: string[]) => void) { this.onChange = fn; } /** @private onTouch callback registered by ngModel and form controls */ onTouch = (): void => { }; /** Registers the onChange callback and required for the ControlValueAccessor interface */ registerOnTouched(fn: () => void) { this.onTouch = fn; } /** * @private * Used as a callback for the @angular/cdk drop component * and used to update the actual order of the entries. * * @param event The drop-event */ drop(event: CdkDragDrop) { if (this._readonly) { return; } // create a copy of the array this.entries = [...this.entries]; moveItemInArray(this.entries, event.previousIndex, event.currentIndex); this.changeDetector.markForCheck(); this.onChange(this.entries); } /** @private TrackByFunction for entries */ trackBy(idx: number, value: string) { return `${value}`; } } ================================================ FILE: desktop/angular/src/app/shared/config/safe.pipe.ts ================================================ import { Pipe, PipeTransform } from '@angular/core'; import { DomSanitizer, SafeHtml, SafeStyle, SafeScript, SafeUrl, SafeResourceUrl } from '@angular/platform-browser'; @Pipe({ name: 'safe' }) export class SafePipe implements PipeTransform { constructor(protected sanitizer: DomSanitizer) { } public transform(value: any, type: string): SafeHtml | SafeStyle | SafeScript | SafeUrl | SafeResourceUrl { switch (type) { case 'html': return this.sanitizer.bypassSecurityTrustHtml(value); case 'style': return this.sanitizer.bypassSecurityTrustStyle(value); case 'script': return this.sanitizer.bypassSecurityTrustScript(value); case 'url': return this.sanitizer.bypassSecurityTrustUrl(value); case 'resourceUrl': return this.sanitizer.bypassSecurityTrustResourceUrl(value); default: throw new Error(`Invalid safe type specified: ${type}`); } } } ================================================ FILE: desktop/angular/src/app/shared/config/subsystems.ts ================================================ import { ExpertiseLevelNumber } from "@safing/portmaster-api"; import { Subsystem } from "src/app/services/status.types"; export interface SubsystemWithExpertise extends Subsystem { minimumExpertise: ExpertiseLevelNumber; isDisabled: boolean; hasUserDefinedValues: boolean; } export var subsystems : SubsystemWithExpertise[] = [ { minimumExpertise: ExpertiseLevelNumber.developer, isDisabled: false, hasUserDefinedValues: false, ID: "core", Name: "Core", Description: "Base Structure and System Integration", Modules: [ { Name: "core", Enabled: true }, { Name: "subsystems", Enabled: true }, { Name: "runtime", Enabled: true }, { Name: "status", Enabled: true }, { Name: "ui", Enabled: true }, { Name: "compat", Enabled: true }, { Name: "broadcasts", Enabled: true }, { Name: "sync", Enabled: true } ], ToggleOptionKey: "", ExpertiseLevel: "user", ReleaseLevel: 0, ConfigKeySpace: "config:core/", _meta: { Created: 0, Modified: 0, Expires: 0, Deleted: 0, Key: "runtime:subsystems/core" } }, { minimumExpertise: ExpertiseLevelNumber.developer, isDisabled: false, hasUserDefinedValues: false, ID: "dns", Name: "Secure DNS", Description: "DNS resolver with scoping and DNS-over-TLS", Modules: [ { Name: "nameserver", Enabled: true }, { Name: "resolver", Enabled: true } ], ToggleOptionKey: "", ExpertiseLevel: "user", ReleaseLevel: 0, ConfigKeySpace: "config:dns/", _meta: { Created: 0, Modified: 0, Expires: 0, Deleted: 0, Key: "runtime:subsystems/dns" } }, { minimumExpertise: ExpertiseLevelNumber.developer, isDisabled: false, hasUserDefinedValues: false, ID: "filter", Name: "Privacy Filter", Description: "DNS and Network Filter", Modules: [ { Name: "filter", Enabled: true }, { Name: "interception", Enabled: true }, { Name: "base", Enabled: true }, { Name: "database", Enabled: true }, { Name: "config", Enabled: true }, { Name: "rng", Enabled: true }, { Name: "metrics", Enabled: true }, { Name: "api", Enabled: true }, { Name: "updates", Enabled: true }, { Name: "network", Enabled: true }, { Name: "netenv", Enabled: true }, { Name: "processes", Enabled: true }, { Name: "profiles", Enabled: true }, { Name: "notifications", Enabled: true }, { Name: "intel", Enabled: true }, { Name: "geoip", Enabled: true }, { Name: "filterlists", Enabled: true }, { Name: "customlists", Enabled: true } ], ToggleOptionKey: "", ExpertiseLevel: "user", ReleaseLevel: 0, ConfigKeySpace: "config:filter/", _meta: { Created: 0, Modified: 0, Expires: 0, Deleted: 0, Key: "runtime:subsystems/filter" } }, { minimumExpertise: ExpertiseLevelNumber.developer, isDisabled: false, hasUserDefinedValues: false, ID: "history", Name: "Network History", Description: "Keep Network History Data", Modules: [ { Name: "netquery", Enabled: true } ], ToggleOptionKey: "", ExpertiseLevel: "user", ReleaseLevel: 0, ConfigKeySpace: "config:history/", _meta: { Created: 0, Modified: 0, Expires: 0, Deleted: 0, Key: "runtime:subsystems/history" } }, { minimumExpertise: ExpertiseLevelNumber.developer, isDisabled: false, hasUserDefinedValues: false, ID: "spn", Name: "SPN", Description: "Safing Privacy Network", Modules: [ { Name: "captain", Enabled: false }, { Name: "terminal", Enabled: false }, { Name: "cabin", Enabled: false }, { Name: "ships", Enabled: false }, { Name: "docks", Enabled: false }, { Name: "access", Enabled: false }, { Name: "crew", Enabled: false }, { Name: "navigator", Enabled: false }, { Name: "sluice", Enabled: false }, { Name: "patrol", Enabled: false } ], ToggleOptionKey: "spn/enable", ExpertiseLevel: "user", ReleaseLevel: 0, ConfigKeySpace: "config:spn/", _meta: { Created: 0, Modified: 0, Expires: 0, Deleted: 0, Key: "runtime:subsystems/spn" } } ]; ================================================ FILE: desktop/angular/src/app/shared/count-indicator/count-indicator.html ================================================ {{ count | prettyCount }}
================================================ FILE: desktop/angular/src/app/shared/count-indicator/count-indicator.module.ts ================================================ import { NgModule } from "@angular/core"; import { CountIndicatorComponent } from "./count-indicator"; import { PrettyCountPipe } from "./count.pipe"; @NgModule({ declarations: [ CountIndicatorComponent, PrettyCountPipe, ], exports: [ CountIndicatorComponent, PrettyCountPipe, ] }) export class CountIndicatorModule { } ================================================ FILE: desktop/angular/src/app/shared/count-indicator/count-indicator.scss ================================================ @import '../../../theme/mixins/_pill.scss'; :host { @include pill-container; @apply pl-2; @apply bg-buttons-dark; @apply w-20; } ================================================ FILE: desktop/angular/src/app/shared/count-indicator/count-indicator.ts ================================================ import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core'; @Component({ selector: 'app-count-indicator', templateUrl: './count-indicator.html', styleUrls: ['./count-indicator.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class CountIndicatorComponent implements OnChanges { @Input() count = 0; @Input() countAllowed: number = 0; allowedPercentage: number = 0; ngOnChanges() { const ratio = (this.countAllowed / this.count) || 0; this.allowedPercentage = Math.round(ratio * 100); } } ================================================ FILE: desktop/angular/src/app/shared/count-indicator/count.pipe.ts ================================================ import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ name: 'prettyCount', pure: true }) export class PrettyCountPipe implements PipeTransform { transform(value: number) { if (value > 999) { const v = Math.floor(value / 1000); if (value === v * 1000) { return `${v}k`; } return `${v}k+` } return `${value}` } } ================================================ FILE: desktop/angular/src/app/shared/count-indicator/index.ts ================================================ export * from './count-indicator'; export * from './count-indicator.module'; ================================================ FILE: desktop/angular/src/app/shared/country-flag/country-flag.ts ================================================ import { AfterViewInit, Directive, ElementRef, HostBinding, Input, OnChanges, Renderer2, SimpleChanges } from '@angular/core'; @Directive({ selector: 'span[appCountryFlags]', }) export class CountryFlagDirective implements AfterViewInit, OnChanges { private readonly flagDir = "/assets/img/flags/"; private readonly OFFSET = 127397; @HostBinding('style.text-shadow') textShadow = 'rgba(255, 255, 255, .5) 0px 0px 1px'; @Input() appCountryFlags: string = ''; constructor( private el: ElementRef, private renderer: Renderer2 ) { } ngOnChanges(changes: SimpleChanges): void { if (!changes['appCountryFlags'].isFirstChange()) { this.update(); } } ngAfterViewInit() { this.update(); } private update() { const span = this.el.nativeElement as HTMLSpanElement; const flag = this.toUnicodeFlag(this.appCountryFlags); this.renderer.setAttribute(span, 'data-before', flag); span.innerHTML = ``; } private toUnicodeFlag(code: string) { const base = 127462 - 65; const cc = code.toUpperCase(); const res = String.fromCodePoint(...cc.split('').map(c => base + c.charCodeAt(0))); return res; } } ================================================ FILE: desktop/angular/src/app/shared/country-flag/country.module.ts ================================================ import { NgModule } from '@angular/core'; import { CountryFlagDirective } from './country-flag'; @NgModule({ declarations: [ CountryFlagDirective ], exports: [ CountryFlagDirective, ] }) export class CountryFlagModule { } ================================================ FILE: desktop/angular/src/app/shared/country-flag/index.ts ================================================ export * from './country-flag'; export * from './country.module'; ================================================ FILE: desktop/angular/src/app/shared/edit-profile-dialog/edit-profile-dialog.html ================================================

{{ isEditMode ? 'Edit App Profile' : 'Create New App Profile' }}

Configure basic profile information like the profile name, it's description and optionally the profile icon.
The icon must be smaller than 10kB and it's dimensions must not exceed 512x512 px. Only JPG and PNG files are supported.
{{ imageError }}
This profile will be applied to processes that match one of the following fingerprints:
No fingerprints configured. Please press "Add New" to get started.
Tag Command Line Environment Path {{ tag.Name }} Equals Prefix Regex
Select a Profile to copy settings from:
{{ p.Name }}
{{ p.Name }}
Settings will be copied from all specified profiles in order with settings from higher profiles taking precedence.
Existing settings may be overwritten.
================================================ FILE: desktop/angular/src/app/shared/edit-profile-dialog/edit-profile-dialog.scss ================================================ :host { @apply flex flex-col gap-4 max-w-2xl; min-width: 500px; width: 60vw; } .tab-content { @apply flex flex-col gap-4 overflow-x-hidden h-96 pt-2; } .input { @apply flex flex-col gap-1; label { @apply text-primary uppercase text-xxs relative left-1.5; } input[type="text"] { @apply border border-gray-500; &.ng-invalid.ng-dirty { @apply border-red-200; } } input[type="file"] { display: none; } } ================================================ FILE: desktop/angular/src/app/shared/edit-profile-dialog/edit-profile-dialog.ts ================================================ import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, TrackByFunction, } from '@angular/core'; import { AppProfile, AppProfileService, FingerpringOperation, Fingerprint, FingerprintType, PORTMASTER_HTTP_API_ENDPOINT, PortapiService, Record, TagDescription, mergeDeep, } from '@safing/portmaster-api'; import { SFNG_DIALOG_REF, SfngDialogRef, SfngDialogService } from '@safing/ui'; import { Observable, Subject, map, of, switchMap, takeUntil } from 'rxjs'; import { ActionIndicatorService } from 'src/app/shared/action-indicator'; @Component({ templateUrl: './edit-profile-dialog.html', //changeDetection: ChangeDetectionStrategy.OnPush, styleUrls: ['./edit-profile-dialog.scss'], }) // eslint-disable-next-line @angular-eslint/component-class-suffix export class EditProfileDialog implements OnInit, OnDestroy { private destory$ = new Subject(); profile: Partial = { ID: '', Source: 'local', Name: '', Description: '', Icons: [], Fingerprints: [], }; isEditMode = false; iconData: string | ArrayBuffer = ''; iconType: string = ''; iconChanged = false; iconObjectURL = ''; imageError: string | null = null; allProfiles: AppProfile[] = []; copySettingsFrom: AppProfile[] = []; selectedCopyFrom: AppProfile | null = null; fingerPrintTypes = FingerprintType; fingerPrintOperations = FingerpringOperation; processTags: TagDescription[] = []; trackFingerPrint: TrackByFunction = ( _: number, fp: Fingerprint ) => `${fp.Type}-${fp.Key}-${fp.Operation}-${fp.Value}`; constructor( @Inject(SFNG_DIALOG_REF) private dialgoRef: SfngDialogRef< EditProfileDialog, any, string | null | AppProfile >, private profileService: AppProfileService, private portapi: PortapiService, private actionIndicator: ActionIndicatorService, private dialog: SfngDialogService, private cdr: ChangeDetectorRef, @Inject(PORTMASTER_HTTP_API_ENDPOINT) private httpAPI: string ) { } ngOnInit(): void { this.profileService.tagDescriptions().subscribe((result) => { this.processTags = result; this.cdr.markForCheck(); }); this.profileService .watchProfiles() .pipe(takeUntil(this.destory$)) .subscribe((profiles) => { this.allProfiles = profiles; this.cdr.markForCheck(); }); if (!!this.dialgoRef.data && typeof this.dialgoRef.data === 'string') { this.isEditMode = true; this.profileService .getAppProfile(this.dialgoRef.data) .subscribe((profile) => { this.profile = profile; this.loadIcon(); }); } else if ( !!this.dialgoRef.data && typeof this.dialgoRef.data === 'object' ) { this.profile = this.dialgoRef.data; this.loadIcon(); } } private loadIcon() { if (!this.profile.Icons?.length) { return; } const firstIcon = this.profile.Icons[0]; // get the current icon of the profile switch (firstIcon.Type) { case 'database': this.portapi .get(firstIcon.Value) .subscribe((data) => { this.iconData = data.iconData; this.iconObjectURL = this.iconData; this.cdr.markForCheck(); }); break; case 'api': this.iconData = `${this.httpAPI}/v1/profile/icon/${firstIcon.Value}`; this.iconObjectURL = this.iconData; break; default: console.error(`Unsupported icon type ${firstIcon.Type}`); } this.cdr.markForCheck(); } ngOnDestroy() { this.destory$.next(); this.destory$.complete(); } addFingerprint() { this.profile.Fingerprints?.push({ Key: '', Operation: FingerpringOperation.Equal, Value: '', Type: FingerprintType.Path, }); } removeFingerprint(idx: number) { this.profile.Fingerprints?.splice(idx, 1); this.profile.Fingerprints = [...this.profile.Fingerprints!]; } removeCopyFrom(idx: number) { this.copySettingsFrom.splice(idx, 1); this.copySettingsFrom = [...this.copySettingsFrom]; } addCopyFrom() { this.copySettingsFrom = [...this.copySettingsFrom, this.selectedCopyFrom!]; this.selectedCopyFrom = null; } drop(event: CdkDragDrop) { // create a copy of the array this.copySettingsFrom = [...this.copySettingsFrom]; moveItemInArray( this.copySettingsFrom, event.previousIndex, event.currentIndex ); this.cdr.markForCheck(); } deleteProfile() { this.dialog .confirm({ caption: 'Caution', header: 'Confirm Profile Deletion', message: 'Do you want to delete this profile?', buttons: [ { id: 'delete', class: 'danger', text: 'Delete', }, { id: 'abort', class: 'outline', text: 'Cancel', }, ], }) .onAction('delete', () => { this.profileService .deleteProfile(this.profile as AppProfile) .subscribe({ next: () => this.dialgoRef.close('deleted'), error: (err) => { this.actionIndicator.error('Failed to delete profile', err); }, }); }); } resetIcon() { this.iconChanged = true; this.iconData = ''; this.iconType = ''; this.iconObjectURL = ''; } save() { if (!this.profile.ID) { this.profile.ID = this.uuidv4(); } if (!this.profile.Source) { this.profile.Source = 'local'; } let updateIcon: Observable = of(undefined); if (this.iconChanged) { // delete any previously set icon this.profile.Icons?.forEach((icon) => { if (icon.Type === 'database') { this.portapi.delete(icon.Value).subscribe(); } // TODO(ppacher): we cannot yet delete API based icons ... }); if (this.iconData !== '') { // save the new icon in the cache database // TODO(ppacher): we currently need to calls because the icon API in portmaster // does not update the profile but just saves the file and returns the filename. // So we still need to update the profile manually. updateIcon = this.profileService .setProfileIcon(this.iconData, this.iconType) .pipe( map(({ filename }) => { this.profile.Icons = [ { Type: 'api', Value: filename, Source: 'user', }, ]; }) ); // TODO(ppacher): reset presentationpath } else { // just clear out that there was an icon this.profile.Icons = []; } } if (this.profile.Fingerprints!.length > 1) { this.profile.PresentationPath = ''; } const oldConfig = this.profile.Config || {}; this.profile.Config = {}; mergeDeep( this.profile.Config, ...[...this.copySettingsFrom.map((p) => p.Config || {}), oldConfig] ); updateIcon .pipe( switchMap(() => { return this.profileService.saveProfile(this.profile as AppProfile); }) ) .subscribe({ next: () => { this.actionIndicator.success( this.profile.Name!, 'Profile saved successfully' ); this.dialgoRef.close('saved'); }, error: (err) => { this.actionIndicator.error('Failed to save profile', err); }, }); } abort() { this.dialgoRef.close('abort'); } fileChangeEvent(fileInput: any) { this.imageError = null; this.iconData = ''; this.iconChanged = true; if (fileInput.target.files && fileInput.target.files[0]) { const max_size = 10 * 1024; const allowed_types = [ 'image/png', 'image/jpeg', 'image/svg', 'image/gif', 'image/tiff', ]; const max_height = 512; const max_width = 512; const file: File = fileInput.target.files[0]; if (file.size > max_size) { this.imageError = 'Maximum size allowed is ' + max_size / 1000 + 'KB'; } if (!allowed_types.includes(file.type)) { this.imageError = 'Only JPG, PNG, SVG, GIF or Tiff files are allowed'; } this.iconType = file.type; const reader = new FileReader(); reader.onload = (e: ProgressEvent) => { const content: ArrayBuffer = e.target!.result! as ArrayBuffer; const blob = new Blob([content], { type: file.type }); const image = new Image(); image.src = URL.createObjectURL(blob); this.iconObjectURL = image.src; image.onload = (rs: any) => { const img_height = rs.currentTarget['height']!; const img_width = rs.currentTarget['width']; if (img_height > max_height && img_width > max_width) { this.imageError = 'Maximum dimentions allowed ' + max_height + '*' + max_width + 'px'; } else { this.iconData = content; } this.cdr.markForCheck(); }; image.onerror = (err: any) => { this.actionIndicator.error( 'Failed to get image', this.actionIndicator.getErrorMessgae(err) ); }; this.cdr.markForCheck(); }; reader.onerror = (err: any) => { this.actionIndicator.error( 'Failed to get image', this.actionIndicator.getErrorMessgae(err) ); }; reader.readAsArrayBuffer(fileInput.target.files[0]); } } private uuidv4(): string { if (typeof crypto.randomUUID === 'function') { return crypto.randomUUID(); } // This one is not really random and not RFC compliant but serves enough for fallback // purposes if the UI is opened in a browser that does not yet support randomUUID console.warn('Using browser with lacking support for crypto.randomUUID()'); return Date.now().toString(36) + Math.random().toString(36).substring(2); } } ================================================ FILE: desktop/angular/src/app/shared/edit-profile-dialog/index.ts ================================================ export * from './edit-profile-dialog'; ================================================ FILE: desktop/angular/src/app/shared/exit-screen/exit-screen.html ================================================
Tip

Close User Interface

Closing the User Interface does not shut down the Portmaster. You can shut down the Portmaster in the Settings or the Tray Notifier.
================================================ FILE: desktop/angular/src/app/shared/exit-screen/exit-screen.scss ================================================ caption { @apply text-sm; opacity : .6; font-size: .6rem; } .content-wrapper { display : flex; flex-direction: column; align-items : flex-start; h1 { font-size : 0.85rem; font-weight : 500; margin-bottom: 1rem; } .message, h1 { flex-shrink : 0; text-overflow: ellipsis; word-break : normal; } .message { font-size: 0.75rem; flex-grow: 1; opacity : .6; } .close-icon { position: absolute; top : 1rem; right : 1rem; opacity : .7; cursor : pointer; &:hover { opacity: 1; } } .actions { margin-top : 1rem; width : 100%; display : flex; justify-content: space-between; align-items : center; button { @apply bg-info-blue; &.danger { @apply bg-info-red; } } &>span { display : flex; align-items: center; label { margin-left: .5rem; user-select: none; } } } } ================================================ FILE: desktop/angular/src/app/shared/exit-screen/exit-screen.ts ================================================ import { OverlayRef } from '@angular/cdk/overlay'; import { Component, Inject, InjectionToken } from '@angular/core'; import { SfngDialogRef, SFNG_DIALOG_REF } from '@safing/ui'; import { Observable, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { UIStateService } from 'src/app/services'; import { fadeInAnimation, fadeOutAnimation } from '../animations'; export const OVERLAYREF = new InjectionToken('OverlayRef'); @Component({ templateUrl: './exit-screen.html', styleUrls: ['./exit-screen.scss'], animations: [ fadeInAnimation, fadeOutAnimation, ] }) export class ExitScreenComponent { constructor( @Inject(SFNG_DIALOG_REF) private _dialogRef: SfngDialogRef, private stateService: UIStateService, ) { } /** @private - used as ngModel form the template */ neveragain: boolean = false; closeUI() { const closeObserver = { next: () => { this._dialogRef.close('exit'); } } let close: Observable = of(null); if (this.neveragain) { close = this.stateService.uiState() .pipe( map(state => { state.hideExitScreen = true; return state; }), switchMap(state => this.stateService.saveState(state)), ) } close.subscribe(closeObserver) } cancel() { this._dialogRef.close() } } ================================================ FILE: desktop/angular/src/app/shared/exit-screen/exit.service.ts ================================================ import { IntegrationService } from './../../integration/integration'; import { Injectable, inject } from '@angular/core'; import { PortapiService } from '@safing/portmaster-api'; import { SfngDialogService } from '@safing/ui'; import { BehaviorSubject, merge, of } from 'rxjs'; import { catchError, debounceTime, distinctUntilChanged, map, skip, switchMap, tap, timeout } from 'rxjs/operators'; import { UIStateService } from 'src/app/services'; import { ActionIndicatorService } from '../action-indicator'; import { ExitScreenComponent } from './exit-screen'; import { INTEGRATION_SERVICE } from 'src/app/integration'; const MessageConnecting = 'Connecting to Portmaster'; const MessageShutdown = 'Shutting Down Portmaster'; const MessageRestart = 'Restarting Portmaster'; const MessageHidden = ''; export type OverlayMessage = typeof MessageConnecting | typeof MessageShutdown | typeof MessageRestart | typeof MessageHidden; @Injectable({ providedIn: 'root' }) export class ExitService { private integration = inject(INTEGRATION_SERVICE); private hasOverlay = false; private _showOverlay = new BehaviorSubject(MessageConnecting); /** * Emits whenever the "Connecting to ..." or "Restarting ..." overlays * should be shown. It actually emits the message that should be shown. * An empty string indicates the overlay should be closed. */ get showOverlay$() { return this._showOverlay.asObservable() } constructor( private stateService: UIStateService, private portapi: PortapiService, private dialog: SfngDialogService, private uai: ActionIndicatorService, ) { this.portapi.connected$ .pipe( distinctUntilChanged(), ) .subscribe(connected => { if (connected) { this._showOverlay.next(MessageHidden); } else if (this._showOverlay.getValue() !== MessageShutdown) { this._showOverlay.next(MessageConnecting) } }) let restartInProgress = false; merge( this.portapi.sub('runtime:modules/core/event/shutdown') .pipe(map(() => MessageShutdown)), this.portapi.sub('runtime:modules/core/event/restart') .pipe( tap(() => restartInProgress = true), map(() => MessageRestart) ), ) .pipe( tap(msg => this._showOverlay.next(msg)), switchMap(() => this.portapi.connected$), distinctUntilChanged(), skip(1), debounceTime(1000), // make sure we display the "shutdown" overlay for at least a second ) .subscribe(connected => { if (this._showOverlay.getValue() === MessageShutdown) { setTimeout(() => { this.integration.exitApp(); }, 1000) } if (connected && restartInProgress) { restartInProgress = false; this.portapi.reloadUI() .pipe( tap(() => { setTimeout(() => window.location.reload(), 1000) }) ) .subscribe(this.uai.httpObserver( 'Reloading UI ...', 'Failed to Reload UI', )) } }) window.addEventListener('beforeunload', () => { // best effort. may not work all the time depending on // the current websocket buffer state this.portapi.bridgeAPI('ui/reload', 'POST').subscribe(); }) this.integration.onExitRequest(() => { this.stateService.uiState() // make sure to not wait for the portmaster to start .pipe(timeout(1000), catchError(() => of(null))) .subscribe(state => { if (state?.hideExitScreen) { this.integration.exitApp(); return } if (this.hasOverlay) { return; } this.hasOverlay = true; this.dialog.create(ExitScreenComponent, { autoclose: true }) .onAction('exit', () => this.integration.exitApp()) .onClose.subscribe(() => this.hasOverlay = false); }) }) } shutdownPortmaster() { this.dialog.confirm({ canCancel: true, header: 'Shutting Down Portmaster', message: 'Shutting down the Portmaster will stop all Portmaster components and will leave your system unprotected!', caption: 'Caution', buttons: [ { id: 'shutdown', class: 'danger', text: 'Shut Down Portmaster' } ] }) .onAction('shutdown', () => { this.portapi.shutdownPortmaster() .subscribe(this.uai.httpObserver( 'Shutting Down ...', 'Failed to Shut Down', )) }) } } ================================================ FILE: desktop/angular/src/app/shared/exit-screen/index.ts ================================================ export * from './exit.service'; export * from './exit-screen'; ================================================ FILE: desktop/angular/src/app/shared/expertise/expertise-directive.ts ================================================ import { Directive, EmbeddedViewRef, Input, isDevMode, OnDestroy, OnInit, TemplateRef, ViewContainerRef } from '@angular/core'; import { ExpertiseLevelNumber } from '@safing/portmaster-api'; import { Subscription } from 'rxjs'; import { ExpertiseService } from './expertise.service'; // ExpertiseLevelOverwrite may be called to display a DOM node decorated // with [appExpertiseLevel] even if the current user setting does not // match the required expertise. export type ExpertiseLevelOverwrite = (lvl: ExpertiseLevelNumber, data: T) => boolean; @Directive({ selector: '[appExpertiseLevel]', }) export class ExpertiseDirective implements OnInit, OnDestroy { private allowedValue: ExpertiseLevelNumber = ExpertiseLevelNumber.user; private subscription = Subscription.EMPTY; private view: EmbeddedViewRef | null = null; @Input() set appExpertiseLevelOverwrite(fn: ExpertiseLevelOverwrite) { this._levelOverwriteFn = fn; this.update(); } private _levelOverwriteFn: ExpertiseLevelOverwrite | null = null; @Input() set appExpertiseLevelData(d: T) { this._data = d; this.update(); } private _data: T | undefined = undefined; @Input() set appExpertiseLevel(lvl: ExpertiseLevelNumber | string) { if (typeof lvl === 'string') { lvl = ExpertiseLevelNumber[lvl as any]; } if (lvl === undefined) { if (isDevMode()) { throw new Error(`[appExpertiseLevel] got undefined expertise-level value`); } return; } if (lvl !== this.allowedValue) { this.allowedValue = lvl as ExpertiseLevelNumber; this.update(); } } private update() { const current = ExpertiseLevelNumber[this.expertiseService.currentLevel]; let hide = current < this.allowedValue; // if there's an overwrite function defined make sue to check that. if (hide && !!this._levelOverwriteFn) { hide = !this._levelOverwriteFn(current, this._data!); if (!hide) { console.log("overwritten", current, this._data); } } if (hide) { if (!!this.view) { this.view.destroy(); this.viewContainer.clear(); this.view = null; } return } if (!!this.view) { this.view.markForCheck(); return; } this.view = this.viewContainer.createEmbeddedView(this.templateRef); this.view.detectChanges(); } constructor( private expertiseService: ExpertiseService, private templateRef: TemplateRef, private viewContainer: ViewContainerRef ) { } ngOnInit() { this.subscription = this.expertiseService.change.subscribe(() => this.update()) } ngOnDestroy() { this.viewContainer.clear(); this.subscription.unsubscribe(); } } ================================================ FILE: desktop/angular/src/app/shared/expertise/expertise-switch.html ================================================ Simple Interface Advanced Interface Developer Interface ================================================ FILE: desktop/angular/src/app/shared/expertise/expertise-switch.scss ================================================ :host { display: flex; @apply pl-2; user-select: none; flex-direction: row; align-items: center; justify-content: center; } sfng-tipup { margin-right: 0.5rem; } ================================================ FILE: desktop/angular/src/app/shared/expertise/expertise-switch.ts ================================================ import { Component, ElementRef } from '@angular/core'; import { ExpertiseLevel } from '@safing/portmaster-api'; import { ExpertiseService } from './expertise.service'; @Component({ selector: 'app-expertise', templateUrl: './expertise-switch.html', styleUrls: ['./expertise-switch.scss'] }) export class ExpertiseComponent { /** @private provide the expertise-level enums to the template */ readonly expertiseLevels = ExpertiseLevel; currentLevel = this.expertiseService.change; /** * @private * Getter to access the expertise level as saved in the database */ get savedLevel() { return this.expertiseService.savedLevel; } constructor( private expertiseService: ExpertiseService, public host: ElementRef, ) { } /** * @private * Configures a new expertise level * * @param lvl The new expertise level to use */ selectLevel(lvl: ExpertiseLevel) { this.expertiseService.setLevel(lvl); } } ================================================ FILE: desktop/angular/src/app/shared/expertise/expertise.module.ts ================================================ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { SfngSelectModule, SfngTipUpModule } from "@safing/ui"; import { ExpertiseDirective } from "./expertise-directive"; import { ExpertiseComponent } from "./expertise-switch"; @NgModule({ imports: [ SfngSelectModule, CommonModule, SfngTipUpModule, FormsModule, ], declarations: [ ExpertiseComponent, ExpertiseDirective, ], exports: [ ExpertiseComponent, ExpertiseDirective, ] }) export class ExpertiseModule { } ================================================ FILE: desktop/angular/src/app/shared/expertise/expertise.service.ts ================================================ import { Injectable } from '@angular/core'; import { ConfigService, ExpertiseLevel, StringSetting } from '@safing/portmaster-api'; import { BehaviorSubject, Observable } from 'rxjs'; import { distinctUntilChanged, map, repeat, share } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class ExpertiseService { /** If the user overwrites the expertise level on a per-page setting we track that here */ private _localOverwrite: ExpertiseLevel | null = null; private _currentLevel: ExpertiseLevel = ExpertiseLevel.User; /** Watches the expertise level as saved in the configuration */ private _savedLevel$ = this.configService.watch('core/expertiseLevel') .pipe( repeat({ delay: 2000 }), map(upd => { return upd as ExpertiseLevel; }), distinctUntilChanged(), share(), ); private level$ = new BehaviorSubject(ExpertiseLevel.User); get currentLevel() { return this._localOverwrite === null ? this._currentLevel : this._localOverwrite; } get savedLevel() { return this._currentLevel; } get change(): Observable { return this.level$.asObservable(); } constructor(private configService: ConfigService) { this._savedLevel$ .subscribe(lvl => { this._currentLevel = lvl; if (this._localOverwrite === null) { this.level$.next(lvl); } }); } setLevel(lvl: ExpertiseLevel | null) { if (lvl === this._currentLevel) { lvl = null; } this._localOverwrite = lvl; if (!!lvl) { this.level$.next(lvl); } else { this.level$.next(this._currentLevel!); } } } ================================================ FILE: desktop/angular/src/app/shared/expertise/index.ts ================================================ export * from './expertise-directive'; export * from './expertise-switch'; export * from './expertise.service'; ================================================ FILE: desktop/angular/src/app/shared/external-link.directive.ts ================================================ import { isPlatformBrowser } from '@angular/common'; import { Directive, HostBinding, HostListener, Inject, Input, OnChanges, PLATFORM_ID, inject } from '@angular/core'; import { INTEGRATION_SERVICE } from '../integration'; @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector selector: 'a[href]' }) export class ExternalLinkDirective implements OnChanges { private readonly integration = inject(INTEGRATION_SERVICE); @HostBinding('attr.rel') relAttr = ''; @HostBinding('attr.target') targetAttr = ''; @HostBinding('attr.href') hrefAttr = ''; @Input() href: string = ''; constructor(@Inject(PLATFORM_ID) private platformId: string) { } @HostListener('click', ['$event']) onClick(event: Event) { event.preventDefault(); event.stopPropagation(); this.integration.openExternal(this.href); } ngOnChanges() { this.hrefAttr = this.href; if (this.isLinkExternal()) { this.relAttr = 'noopener'; this.targetAttr = '_blank'; } } private isLinkExternal() { return ( isPlatformBrowser(this.platformId) && !this.href.includes(location.hostname) ); } } ================================================ FILE: desktop/angular/src/app/shared/feature-scout/feature-scout.html ================================================
SPN is connecting...
Fail-safe blocking enabled
SPN failed to connect
Fail-safe blocking enabled
SPN is connecting...
Fail-safe blocking enabled
{{ spnStatus?.HomeHubName }} in {{ spnStatus?.ConnectedCountry?.Name }}
SPN Home (Entry) Node
  • Connected to {{ spnStatus?.ConnectedIP }}
  • Uplink is always encrypted
  • Built with transport/decoy {{ spnStatus?.ConnectedTransport }}
================================================ FILE: desktop/angular/src/app/shared/feature-scout/feature-scout.scss ================================================ .feature-icon { @apply text-primary text-opacity-80; &.feature-icon-off { opacity: 0.25; } } .status-info { @apply text-primary text-opacity-80 text-xxs text-center; &:hover { cursor: default; } } ================================================ FILE: desktop/angular/src/app/shared/feature-scout/feature-scout.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, OnInit, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { BoolSetting, ConfigService, FeatureID, Netquery, SPNService, SPNStatus, UserProfile } from "@safing/portmaster-api"; import { catchError, of } from "rxjs"; import { fadeInAnimation, fadeOutAnimation } from "../animations"; import { CountryFlagModule } from 'src/app/shared/country-flag'; @Component({ selector: 'app-feature-scout', templateUrl: './feature-scout.html', styleUrls: [ './feature-scout.scss' ], changeDetection: ChangeDetectionStrategy.OnPush, animations: [ fadeInAnimation, fadeOutAnimation, ] }) export class FeatureScoutComponent implements OnInit { private destroyRef = inject(DestroyRef); /** The current SPN user profile */ profile: UserProfile | null = null; /** Whether or not the SPN is currently enabled */ spnEnabled = false; /** The current status of the SPN module */ spnStatus: SPNStatus | null = null; /** Whether or not the Network History is currently enabled */ historyEnabled = false; /** Returns whether or not the current package has the SPN feature */ get packageHasSPN() { return this.profile?.current_plan?.feature_ids?.includes(FeatureID.SPN) } /** Returns whether or not the current package has the Network History feature */ get packageHasHistory() { return this.profile?.current_plan?.feature_ids?.includes(FeatureID.History) } constructor( private configService: ConfigService, private spnService: SPNService, private cdr: ChangeDetectorRef, ) { } ngOnInit(): void { this.spnService .profile$ .pipe( takeUntilDestroyed(this.destroyRef), catchError(() => of(null)) ) .subscribe(profile => { this.profile = profile || null; this.cdr.markForCheck(); }); this.spnService.status$ .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(status => { this.spnStatus = status; this.cdr.markForCheck(); }) this.configService.watch("spn/enable") .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(value => { this.spnEnabled = value; this.cdr.markForCheck(); }); this.configService.watch("history/enable") .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(value => { this.historyEnabled = value; this.cdr.markForCheck(); }); } setSPNEnabled(v: boolean) { this.configService.save(`spn/enable`, v) .subscribe(); } setHistoryEnabled(v: boolean) { this.configService.save(`history/enable`, v) .subscribe(); } } ================================================ FILE: desktop/angular/src/app/shared/feature-scout/index.ts ================================================ export * from './feature-scout'; ================================================ FILE: desktop/angular/src/app/shared/focus/focus.directive.ts ================================================ import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { Directive, ElementRef, Input, OnInit } from "@angular/core"; @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector selector: '[autoFocus]', }) export class AutoFocusDirective implements OnInit { private _focus = true; private _afterInit = false; @Input('autoFocus') set focus(v: any) { this._focus = coerceBooleanProperty(v) !== false; if (this._afterInit && this.elementRef) { this.elementRef.nativeElement.focus() } } constructor(private elementRef: ElementRef) { } ngOnInit(): void { setTimeout(() => { if (this._focus) { this.elementRef.nativeElement.focus(); } }, 100) this._afterInit = true; } } ================================================ FILE: desktop/angular/src/app/shared/focus/focus.module.ts ================================================ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { AutoFocusDirective } from "./focus.directive"; @NgModule({ imports: [ CommonModule, ], declarations: [ AutoFocusDirective, ], exports: [ AutoFocusDirective, ] }) export class SfngFocusModule { } ================================================ FILE: desktop/angular/src/app/shared/focus/index.ts ================================================ export { AutoFocusDirective } from './focus.directive'; export * from './focus.module'; ================================================ FILE: desktop/angular/src/app/shared/fuzzySearch/fuse.service.ts ================================================ import { Injectable } from '@angular/core'; import { deepClone } from '@safing/portmaster-api'; import Fuse from 'fuse.js'; export type FuseResult = Fuse.FuseResult; export interface FuseSearchOpts extends Fuse.IFuseOptions { minSearchTermLength?: number; maximumScore?: number; } @Injectable({ providedIn: 'root' }) export class FuzzySearchService { readonly defaultOptions: FuseSearchOpts = { minMatchCharLength: 2, includeMatches: true, includeScore: true, minSearchTermLength: 3, }; searchList(list: Array, searchTerms: string, options: FuseSearchOpts & { disableHighlight?: boolean } = {}): Array> { const opts: FuseSearchOpts = { ...this.defaultOptions, ...options, } let result: FuseResult[] = []; if (searchTerms && searchTerms.length >= (opts.minSearchTermLength || 0)) { let fuse = new Fuse(list, opts); result = fuse.search(searchTerms); } else { result = list.map((item, index) => ({ item: item, refIndex: index, score: 0, })) } if (!!options.disableHighlight) { return result; } return this.handleHighlight(result, options); } private handleHighlight(result: FuseResult[], options: FuseSearchOpts): FuseResult[] { return result.map(matchObject => { matchObject.item = deepClone(matchObject.item); if (!matchObject.matches) { return matchObject; } for (let match of matchObject.matches!) { const indices = match.indices; let highlightOffset: number = 0; for (let indice of indices) { let initialValue = getFromMatch(matchObject, match); const startOffset = indice[0] + highlightOffset; const endOffset = indice[1] + highlightOffset + 1; if (endOffset - startOffset < 4) { continue } let highlightedTerm = initialValue.substring(startOffset, endOffset); let newValue = initialValue.substring(0, startOffset) + '' + highlightedTerm + '' + initialValue.substring(endOffset); highlightOffset += ''.length; setOnMatch(matchObject, match, newValue); } } return matchObject; }); } } function getFromMatch(result: Fuse.FuseResult, match: Fuse.FuseResultMatch): string { if (match.refIndex === undefined) { return (result.item as any)[match.key!]; } return (result.item as any)[match.key!][match.refIndex]; } function setOnMatch(result: Fuse.FuseResult, match: Fuse.FuseResultMatch, value: string) { if (match.refIndex === undefined) { (result.item as any)[match.key!] = value; return; } (result.item as any)[match.key!][match.refIndex] = value; } ================================================ FILE: desktop/angular/src/app/shared/fuzzySearch/index.ts ================================================ import Fuse from 'fuse.js'; export { FuseSearchOpts, FuzzySearchService } from './fuse.service'; export { FuzzySearchPipe } from './search-pipe'; ================================================ FILE: desktop/angular/src/app/shared/fuzzySearch/search-pipe.ts ================================================ import { Pipe, PipeTransform } from '@angular/core'; import { FuseResult, FuseSearchOpts, FuzzySearchService } from './fuse.service'; @Pipe({ name: 'fuzzySearch', }) export class FuzzySearchPipe implements PipeTransform { constructor( private FusejsService: FuzzySearchService ) { } transform(elements: Array, searchTerms: string, options: FuseSearchOpts = {}): Array> { return this.FusejsService.searchList(elements, searchTerms, options); } } ================================================ FILE: desktop/angular/src/app/shared/loading/index.ts ================================================ export { LoadingComponent } from './loading'; ================================================ FILE: desktop/angular/src/app/shared/loading/loading.html ================================================ ================================================ FILE: desktop/angular/src/app/shared/loading/loading.scss ================================================ :host { --internal-dot-size : var(--dot-size, 5px); --internal-animation-speed: var(--animation-speed, 1.3s); display : flex; position : relative; justify-content: space-evenly; align-items : flex-end; width : var(--animation-width, calc(var(--internal-dot-size) * 5)); height: calc(var(--internal-dot-size) * 3); &.animate { .dot { display : block; flex-shrink: 0; flex-grow : 0; width : var(--internal-dot-size); height : var(--internal-dot-size); @apply shadow-inner-xs; @apply rounded-full; @apply bg-buttons-icon; animation: wave var(--internal-animation-speed) linear infinite; &:nth-child(2) { animation-delay: -1.1s; } &:nth-child(3) { animation-delay: -0.9s; } } } } @keyframes wave { 0%, 60%, 100% { transform: initial; @apply bg-buttons-light; } 90% { transform : translateY(var(--loading-height, -9px)); background-color: white; } } ================================================ FILE: desktop/angular/src/app/shared/loading/loading.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding } from '@angular/core'; @Component({ selector: 'app-loading', templateUrl: './loading.html', styleUrls: ['./loading.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class LoadingComponent { @HostBinding('class.animate') _animate = true; constructor(private changeDetectorRef: ChangeDetectorRef) { } } ================================================ FILE: desktop/angular/src/app/shared/menu/index.ts ================================================ export { MenuComponent, MenuTriggerComponent, MenuItemComponent, MenuGroupComponent } from './menu'; export * from './menu.module'; ================================================ FILE: desktop/angular/src/app/shared/menu/menu-group.scss ================================================ :host { display: block; width: 100%; @apply p-1; @apply px-4; @apply text-secondary; display: block; text-transform: uppercase; font-size: 0.7rem; opacity: .7; } ================================================ FILE: desktop/angular/src/app/shared/menu/menu-item.scss ================================================ :host { @apply block w-full; cursor: pointer; @apply p-2; @apply px-4 text-primary text-xxs; font-weight: 500; &:hover { @apply bg-gray-300; } &.disabled { cursor: not-allowed; opacity: 0.5; } } ================================================ FILE: desktop/angular/src/app/shared/menu/menu-trigger.html ================================================
================================================ FILE: desktop/angular/src/app/shared/menu/menu-trigger.scss ================================================ :host { user-select: none; margin-right: .5rem; display: block; @apply rounded-t-sm; } div { cursor: pointer; display: flex; @apply rounded-t; flex-grow: 0; transition: all .1s ease-in-out; justify-content: center; align-items: center; @apply py-1; @apply px-3; } .dropdown { margin-left: 1px; height: auto; padding: 0; margin: 0; svg { opacity: 0.7; fill: var(--text-primary); width: 0.51rem; transition: all cubic-bezier(0.175, 0.885, 0.32, 1.275) .2s; transform: rotate(90deg); position: relative; top: 3px; } } :host.active { @apply bg-gray-400; color: white !important; } ================================================ FILE: desktop/angular/src/app/shared/menu/menu.html ================================================
================================================ FILE: desktop/angular/src/app/shared/menu/menu.module.ts ================================================ import { OverlayModule } from "@angular/cdk/overlay"; import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { SfngDropDownModule } from "@safing/ui"; import { MenuComponent, MenuGroupComponent, MenuItemComponent, MenuTriggerComponent } from "./menu"; @NgModule({ imports: [ SfngDropDownModule, CommonModule, OverlayModule, ], declarations: [ MenuComponent, MenuGroupComponent, MenuTriggerComponent, MenuItemComponent, ], exports: [ MenuComponent, MenuGroupComponent, MenuTriggerComponent, MenuItemComponent, ], }) export class SfngMenuModule { } ================================================ FILE: desktop/angular/src/app/shared/menu/menu.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { CdkOverlayOrigin } from '@angular/cdk/overlay'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChildren, EventEmitter, HostBinding, HostListener, Input, Output, QueryList, ViewChild } from '@angular/core'; import { SfngDropdownComponent } from '@safing/ui'; @Component({ selector: 'app-menu-trigger', templateUrl: './menu-trigger.html', styleUrls: ['./menu-trigger.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class MenuTriggerComponent { @ViewChild(CdkOverlayOrigin, { static: true }) origin!: CdkOverlayOrigin; @Input() menu: MenuComponent | null = null; @Input() set useContent(v: any) { this._useContent = coerceBooleanProperty(v); } get useContent() { return this._useContent; } private _useContent: boolean = false; @HostBinding('class.active') get isOpen() { if (!this.menu) { return false; } return this.menu.dropdown.isOpen; } constructor( public changeDetectorRef: ChangeDetectorRef, ) { } toggle(event: MouseEvent) { event.preventDefault(); event.stopPropagation(); this.menu?.dropdown.toggle(this.origin) } } @Component({ selector: 'app-menu-item', template: '', styleUrls: ['./menu-item.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class MenuItemComponent { @Input() @HostBinding('class.disabled') set disabled(v: any) { this._disabled = coerceBooleanProperty(v); } get disabled() { return this._disabled; } private _disabled: boolean = false; @HostListener('click', ['$event']) closeMenu(event: MouseEvent) { if (this.disabled) { return; } this.activate.next(event); this.menu.dropdown.close(); } /** * activate fires when the menu item is clicked. * Use activate rather than (click)="" if you want * [disabled] to be considered. */ @Output() activate = new EventEmitter(); constructor(private menu: MenuComponent) { } } @Component({ selector: 'app-menu-group', template: '', styleUrls: ['./menu-group.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class MenuGroupComponent { } @Component({ selector: 'app-menu', exportAs: 'appMenu', templateUrl: './menu.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class MenuComponent { @ContentChildren(MenuItemComponent) items: QueryList | null = null; @ViewChild(SfngDropdownComponent, { static: true }) dropdown!: SfngDropdownComponent; @Input() offsetY?: string | number; @Input() offsetX?: string | number; @Input() overlayClass?: string; } ================================================ FILE: desktop/angular/src/app/shared/multi-switch/index.ts ================================================ export { MultiSwitchComponent } from './multi-switch'; export { SwitchItemComponent } from './switch-item'; export * from './multi-switch.module'; ================================================ FILE: desktop/angular/src/app/shared/multi-switch/multi-switch.html ================================================
================================================ FILE: desktop/angular/src/app/shared/multi-switch/multi-switch.module.ts ================================================ import { DragDropModule } from "@angular/cdk/drag-drop"; import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { SfngTipUpModule, SfngTooltipModule } from "@safing/ui"; import { MultiSwitchComponent } from "./multi-switch"; import { SwitchItemComponent } from "./switch-item"; @NgModule({ imports: [ CommonModule, FormsModule, SfngTooltipModule, SfngTipUpModule, DragDropModule, ], declarations: [ MultiSwitchComponent, SwitchItemComponent, ], exports: [ MultiSwitchComponent, SwitchItemComponent, ], }) export class SfngMultiSwitchModule { } ================================================ FILE: desktop/angular/src/app/shared/multi-switch/multi-switch.scss ================================================ .buttons { display: flex; align-items: flex-end; position: relative; height: 3rem; flex-grow: 0; width: fit-content; fa-icon[icon*="question-circle"] { height: 100%; display: flex; align-items: center; margin-left: 1rem; } } .marker { display: block; height: 16px; width: 16px; position: absolute; bottom: -8px; cursor: grab; transition: all .5s cubic-bezier(0.175, 0.885, 0.32, 1.075); @apply rounded-full; } :host { flex-grow: 0; width: fit-content; display: block; outline: none; user-select: none; &.disabled { .marker { cursor: unset; } } &.grabbing { .marker { cursor: grabbing; } } } ================================================ FILE: desktop/angular/src/app/shared/multi-switch/multi-switch.ts ================================================ import { ListKeyManager } from '@angular/cdk/a11y'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DOCUMENT } from '@angular/common'; import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChildren, ElementRef, EventEmitter, forwardRef, HostBinding, HostListener, Inject, Input, NgZone, OnDestroy, Output, QueryList, Renderer2, ViewChild } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { animationFrameScheduler, fromEvent, Subscription } from 'rxjs'; import { map, startWith, subscribeOn, take, takeUntil } from 'rxjs/operators'; import { SwitchItemComponent } from './switch-item'; @Component({ selector: 'app-multi-switch', templateUrl: './multi-switch.html', styleUrls: ['./multi-switch.scss'], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MultiSwitchComponent), multi: true, } ] }) export class MultiSwitchComponent implements OnDestroy, AfterViewInit, ControlValueAccessor { /** Subscription to all button-select changes */ private sub = Subscription.EMPTY; /** Holds the current x-translation offset for the marker */ private markerOffset: number = 0; /** Keymanager used for keyboard navigation support */ private keyManager: ListKeyManager> | null = null; /** Subscription to the key manager */ private keyManagerSub = Subscription.EMPTY; @Input() tipUpKey: string = ''; /** All buttons projected into the multi-switch */ @ContentChildren(SwitchItemComponent) buttons: QueryList> | null = null; /** Emits whenever the selected button changes. */ @Output() changed = new EventEmitter(); /** Reference to the marker inside our view container */ @ViewChild('marker', { read: ElementRef, static: true }) marker: ElementRef | null = null; @HostListener('blur') onBlur() { this._onTouch(); } @HostBinding('attr.tabindex') readonly tabindex = 0; @HostListener('keyup', ['$event']) onKeyUp(event: KeyboardEvent) { if (this.disabled) { return; } this.keyManager!.onKeydown(event); } /** Whether or not the switch button component is disabled */ @Input() @HostBinding('class.disabled') set disabled(v: any) { this._disabled = coerceBooleanProperty(v); // Update all buttons states as well. if (!!this.buttons) { this.buttons.forEach(btn => btn.disabled = this.disabled); } } get disabled() { return this._disabled; } private _disabled = false; @HostBinding('class.grabbing') isGrabbing = false; /** External write tracks calls to writeValue so we don't end up re-emitting the values. */ private externalWrite = false; /** Which button is currently active (and holds the marker) */ activeButton: T | null = null; constructor( public host: ElementRef, private changeDetectorRef: ChangeDetectorRef, private renderer: Renderer2, private ngZone: NgZone, @Inject(DOCUMENT) private document: Document, ) { } /** Registeres the change callback. Required for ControlValueAccessor */ registerOnChange(fn: (v: T) => void) { this._onChange = fn; } private _onChange: (value: T) => void = () => { } /** Registers the touch callback. Required for ControlValueAccessor */ registerOnTouched(fn: () => void) { this._onTouch = fn; } private _onTouch: () => void = () => { }; /** Disable or enable the button. Required for ControlValueAccessor */ setDisabledState(disabled: boolean) { this.disabled = disabled; } /** Writes a new value for the multi-line switch */ writeValue(value: T) { this.activeButton = value; if (!!this.buttons) { // Set externalWrite to true while we iterate the buttons // and eventually call `setActiveItem` so we don't re-emit // the active item once the keyManager publishes the change // to use. // This workaround is required as we need to inform the // keyManager about the new active item. Otherwise it would // work with a stale internal state the next time the user // uses the keyboard. this.externalWrite = true; this.buttons.forEach(btn => { if (btn.id === value) { this.keyManager!.setActiveItem(btn); this.repositionMarker(btn); } }) this.externalWrite = false; } } ngAfterViewInit() { if (!this.buttons) { return; } this.keyManager = new ListKeyManager(this.buttons) .withHorizontalOrientation('ltr') .withTypeAhead() .withWrap(); this.keyManagerSub = this.keyManager.change .subscribe(activeIndex => { const active = Array.from(this.buttons!)[activeIndex]; this.selectButton(active, !this.externalWrite); }); // Subscribe to all (clicked) and (selectedChange) events of // all buttons projected into our content. this.buttons.changes .pipe(startWith(null)) .subscribe(() => { this.sub.unsubscribe(); this.sub = new Subscription(); this.buttons!.forEach(btn => { btn.disabled = this.disabled; this.sub.add( btn.clicked.subscribe((e: MouseEvent) => { this.keyManager!.setActiveItem(btn); }) ); }); // wait until the zone and change-detection stabilizes and // reposition the marker afterwards. Doing it right now will // likely position it wrongly since the DOM has not yet been // fully updated. this.ngZone.onStable.pipe(take(1)) .subscribe(() => this.repositionMarker()) }); this.buttons.forEach(btn => { if (this.activeButton === btn.id) { btn.selected = true; } }) this.repositionMarker(); } ngOnDestroy() { this.sub.unsubscribe(); this.keyManagerSub.unsubscribe(); } /** Selects a new button and deselects all others. */ private selectButton(btn: SwitchItemComponent, emit = true) { if (this.disabled) { return; } this.activeButton = btn.id; if (emit) { this.changed.next(btn.id!); this._onChange(btn.id!); } this.repositionMarker(btn); } /** @private View-callback for (mousedown) to start dragging the marker. */ dragStarted(event: MouseEvent) { if (this.disabled) { return; } this.isGrabbing = true; this.renderer.addClass(this.document.getElementsByTagName("body")[0], 'document-grabbing'); const mousemove$ = fromEvent(this.document, 'mousemove'); const hostRect = this.host.nativeElement.getBoundingClientRect(); const start = this.markerOffset; const markerWidth = this.marker!.nativeElement.getBoundingClientRect().width; // we don't want angular to run change detection all the time we move a pixel // so detach the change-detector for now. this.changeDetectorRef.detach(); mousemove$ .pipe( map(move => { move.preventDefault(); return move.clientX - event.clientX; }), takeUntil(fromEvent(document, 'mouseup')), subscribeOn(animationFrameScheduler) ) .subscribe({ next: diff => { // clip the new offset inside our host-view. let offset = start + diff; if (offset < 0) { offset = 0; } else if (offset > hostRect.width) { offset = hostRect.width; } // center the marker at the mouse position. offset -= Math.round(markerWidth / 2); this.markerOffset = offset; this.updatePosition(offset); let foundTarget = false; let target = this.findTargetButton(offset); if (!!target) { this.marker!.nativeElement.style.backgroundColor = target.borderColorActive; this.buttons!.forEach(btn => { if (!foundTarget && btn.group === target!.group) { this.renderer.addClass(btn.elementRef.nativeElement, 'selected'); btn.elementRef.nativeElement.style.borderColor = btn.borderColorActive; } else { this.renderer.removeClass(btn.elementRef.nativeElement, 'selected'); btn.elementRef.nativeElement.style.borderColor = btn.borderColorInactive; } if (target === btn) { foundTarget = true; } }); } }, complete: () => { this.changeDetectorRef.reattach(); this.markerDropped(); // make sure we don't keep the selected class on buttons that // are not selected anymore. this.buttons!.forEach(btn => { if (!btn.selected) { this.renderer.removeClass(btn.elementRef.nativeElement, 'selected'); btn.elementRef.nativeElement.style.borderColor = btn.borderColorInactive; } }); this.isGrabbing = false; this.renderer.removeClass(this.document.getElementsByTagName("body")[0], 'document-grabbing'); } }); } /** Update the markers position by applying a translate3d */ private updatePosition(x: number) { this.marker!.nativeElement.style.transform = `translate3d(${x}px, 0px, 0px)`; } /** Find the button item that is below x */ private findTargetButton(x: number, cb?: (item: SwitchItemComponent, target: boolean) => void): SwitchItemComponent | null { const host = this.host.nativeElement.getBoundingClientRect(); let newButton: SwitchItemComponent | null = null; this.buttons?.forEach(btn => { const btnRect = btn.elementRef.nativeElement.getBoundingClientRect(); const min = btnRect.x - host.x; const max = min + btnRect.width; if (x >= min && x <= max) { newButton = btn; if (!!cb) { cb(btn, true); } } else if (!!cb) { cb(btn, false); } }); return newButton; } /** Calculates which button should be activated based on the drop-position of the marker */ private markerDropped() { let newButton = this.findTargetButton(this.markerOffset); if (!newButton) { newButton = Array.from(this.buttons!)[0]; } if (!!newButton) { this.keyManager!.setActiveItem(newButton); } } /** * Calculates the new position required to center the * marker at the currently selected button. * If `selected` is unset the last button with selected == true is * used. * * @param selected The switch item button to select (optional). */ private repositionMarker(selected: SwitchItemComponent | null = null) { // If there's no selected button given search for the last one that // matches selected === true. if (selected === null) { this.buttons?.forEach(btn => { if (btn.selected) { selected = btn; } }); } // There's not button selected so we move the marker back to the // start. if (selected === null) { this.markerOffset = 0; this.updatePosition(0); return; } // Calculate and reposition the marker. const offsetLeft = selected!.elementRef.nativeElement.offsetLeft; const clientWidth = selected!.elementRef.nativeElement.clientWidth; this.markerOffset = Math.round(offsetLeft - 8 + clientWidth / 2); this.marker!.nativeElement.style.backgroundColor = selected.borderColorActive; this.updatePosition(this.markerOffset); this.changeDetectorRef.markForCheck(); } } ================================================ FILE: desktop/angular/src/app/shared/multi-switch/switch-item.scss ================================================ :host { display : flex; align-items : center; justify-content: center; width : 6rem; height : 2.7rem; position : relative; bottom : 0; transition : all .3s cubic-bezier(0.075, 0.82, 0.165, 1); @apply bg-buttons-dark; @apply border-b-2; &.selected { @apply bg-buttons-light; height: 3rem; } &:not(.disabled) { cursor: pointer; &:hover { @apply bg-buttons-light; } } &:first-of-type { @apply rounded-tl; } &:last-of-type { @apply rounded-tr; } } ================================================ FILE: desktop/angular/src/app/shared/multi-switch/switch-item.ts ================================================ import { Component, ChangeDetectionStrategy, Input, isDevMode, OnInit, HostBinding, Output, EventEmitter, HostListener, ElementRef, ChangeDetectorRef } from '@angular/core'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; @Component({ selector: 'app-switch-item', template: '', styleUrls: ['./switch-item.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class SwitchItemComponent implements OnInit { @Input() id: T | null = null; @Input() group = ''; @Output() clicked = new EventEmitter(); @HostListener('click', ['$event']) onClick(e: MouseEvent) { this.clicked.next(e); } @Input() borderColorActive: string = 'var(--info-green)'; @Input() borderColorInactive: string = 'var(--button-light)'; @HostBinding('style.border-color') get borderColor() { if (this.selected) { return this.borderColorActive; } return this.borderColorInactive; } @Input() @HostBinding('class.disabled') set disabled(v: any) { this._disabled = coerceBooleanProperty(v); } get disabled() { return this._disabled; } private _disabled = false; @Input() @HostBinding('class.selected') set selected(v: any) { const selected = coerceBooleanProperty(v); if (selected !== this._selected) { this._selected = selected; this.selectedChange.next(selected); } } get selected() { return this._selected; } private _selected = false; getLabel() { return this.elementRef.nativeElement.innerText; } @Output() selectedChange = new EventEmitter(); ngOnInit() { if (this.id === null && isDevMode()) { throw new Error(`SwitchItemComponent must have an ID`); } } constructor( public readonly elementRef: ElementRef, public readonly changeDetectorRef: ChangeDetectorRef, ) { } } ================================================ FILE: desktop/angular/src/app/shared/netquery/.eslintrc.json ================================================ { "extends": "../../../../.eslintrc.json", "ignorePatterns": [ "!**/*" ], "overrides": [ { "files": [ "*.ts" ], "parserOptions": { "project": [ "projects/safing/ui/tsconfig.lib.json", "projects/safing/ui/tsconfig.spec.json" ], "createDefaultProgram": true }, "rules": { "@angular-eslint/directive-selector": [ "error", { "type": "attribute", "prefix": "sfng", "style": "camelCase" } ], "@angular-eslint/component-selector": [ "error", { "type": "element", "prefix": "sfng", "style": "kebab-case" } ] } }, { "files": [ "*.html" ], "rules": {} } ] } ================================================ FILE: desktop/angular/src/app/shared/netquery/add-to-filter/add-to-filter.ts ================================================ import { ChangeDetectorRef, Directive, HostBinding, HostListener, Input, OnDestroy, OnInit, inject } from "@angular/core"; import { NetqueryConnection } from "@safing/portmaster-api"; import { Subscription, combineLatest } from "rxjs"; import { ActionIndicatorService } from "../../action-indicator"; import { NetqueryHelper } from "../connection-helper.service"; import { INTEGRATION_SERVICE } from "src/app/integration"; @Directive({ selector: '[sfngAddToFilter]' }) export class SfngNetqueryAddToFilterDirective implements OnInit, OnDestroy { private subscription = Subscription.EMPTY; private readonly integration = inject(INTEGRATION_SERVICE); @Input('sfngAddToFilter') key: keyof NetqueryConnection | null = null; @Input('sfngAddToFilterValue') set value(v: any | any[]) { if (!Array.isArray(v)) { v = [v] } this._values = v; } private _values: any[] = []; @HostListener('click', ['$event']) onClick(evt: MouseEvent) { if (!this.key) { return } let prevent = false if (evt.shiftKey) { this.helper.addToFilter(this.key, this._values); prevent = true } else if (evt.ctrlKey) { this.integration.writeToClipboard(this._values.join(', ')) .then(() => { this.uai.success("Copied to clipboard", "Successfully copied " + this._values.join(", ") + " to your clipboard") }) .catch(err => { this.uai.error("Failed to copy to clipboard", this.uai.getErrorMessgae(err)) }) prevent = true } if (prevent) { evt.preventDefault(); evt.stopPropagation(); } } @HostBinding('class.border-dashed') @HostBinding('class.border-gray-500') @HostBinding('class.hover:border-gray-700') readonly _styleHost = true; @HostBinding('class.cursor-pointer') @HostBinding('class.hover:cursor-pointer') @HostBinding('class.border-b') @HostBinding('class.select-none') get shouldHiglight() { return this.isShiftKeyPressed || this.isCtrlKeyPressed } isShiftKeyPressed = false; isCtrlKeyPressed = false; constructor( private helper: NetqueryHelper, private uai: ActionIndicatorService, private cdr: ChangeDetectorRef, ) { } ngOnInit(): void { this.subscription = combineLatest([this.helper.onShiftKey, this.helper.onCtrlKey]) .subscribe(([isShiftKeyPressed, isCtrlKeyPressed]) => { if (!this.key) { return; } this.isShiftKeyPressed = isShiftKeyPressed; this.isCtrlKeyPressed = isCtrlKeyPressed; this.cdr.markForCheck(); }) } ngOnDestroy(): void { this.subscription.unsubscribe(); } } ================================================ FILE: desktop/angular/src/app/shared/netquery/add-to-filter/index.ts ================================================ export * from './add-to-filter'; ================================================ FILE: desktop/angular/src/app/shared/netquery/circular-bar-chart/circular-bar-chart.component.ts ================================================ import { AfterViewInit, ChangeDetectionStrategy, Component, DestroyRef, ElementRef, Input, OnInit, inject } from '@angular/core'; import { QueryResult } from '@safing/portmaster-api'; import * as d3 from 'd3'; export interface CircularBarChartConfig { // stack either holds the attribute name or an accessor function // to determine which serieses belong to the same stack. stack: keyof T | ((d: T) => string); // series either holds the attribute name of the key or an accessor function. seriesKey: keyof T | ((d: T) => string); seriesLabel?: (s: string) => string; // value either holds the attribute name or an accessor function // to get the value of the series. value: keyof T | ((d: T) => number); colorAsClass?: boolean; // the actual series configuration series?: { [key: string]: { color: string; } }; // The number of ticks for the y axis ticks?: number; formatTick?: (v: number) => string; // an optional function to format the value formatValue?: (stack: string, series: string, value: number, data?: T) => string; formatStack?: (sel: d3.Selection, data: T[]) => d3.Selection; } export function splitQueryResult(results: T[], series: K[]): (QueryResult & { series: string, value: number })[] { let mapped: (QueryResult & { series: string, value: number })[] = []; results.forEach(row => { series.forEach(seriesKey => { mapped.push({ ...row, value: row[seriesKey], series: seriesKey as string, }) }) }) return mapped } @Component({ selector: 'sfng-netquery-circular-bar-chart', template: '', changeDetection: ChangeDetectionStrategy.OnPush }) export class CircularBarChartComponent implements OnInit, AfterViewInit { private readonly elementRef = inject(ElementRef) as ElementRef; private readonly destroyRef = inject(DestroyRef); // D3 related members private svg?: d3.Selection; private x?: d3.ScaleBand; private y?: d3.ScaleRadial; private height = 0; private width = 0; @Input() config: CircularBarChartConfig | null = null; @Input() innerRadius?: number; @Input() set data(d: T[] | null) { this._data = d || []; this.prepareChart() this.render(); } private _data: T[] = []; ngOnInit(): void { this.prepareChart() this.render() } ngAfterViewInit(): void { const observer = new ResizeObserver(() => { this.prepareChart() this.render() }) observer.observe(this.elementRef.nativeElement) this.destroyRef.onDestroy(() => observer.disconnect()) this.prepareChart() this.render(); } private prepareChart() { if (!!this.svg) { const parent = this.svg.node()?.parentElement parent?.remove() } const margin = 0.2 const bbox = this.elementRef.nativeElement.getBoundingClientRect(); const marginLeft = bbox.width * margin; const marginTop = bbox.height * margin; this.width = bbox.width - 2 * marginLeft; this.height = bbox.height - 2 * marginTop; this.svg = d3.select(this.elementRef.nativeElement) .append('svg') .attr('width', "100%") .attr('height', "100%") .append('g') .attr('transform', `translate(${this.width / 2 + marginLeft}, ${this.height / 2 + marginTop})`); this.x = d3.scaleBand() .range([0, 2 * Math.PI]) .align(0); this.y = d3.scaleRadial() // prepare the SVGGElement that we use for rendering this.svg.append("g") .attr("id", "chart") this.svg.append("g") .attr("id", "text") this.svg.append("g") .attr("id", "legend") this.svg.append("g") .attr("id", "ticks") } private render() { const x = this.x; const y = this.y; if (!this.svg || !x || !y) { console.log("not yet ready") return; } let stackName: (d: T) => string; if (typeof this.config?.stack === 'function') { stackName = this.config.stack; } else { stackName = (d: T) => { return d[this.config!.stack as keyof T] + '' } } let seriesKey: (d: T) => string; if (typeof this.config?.seriesKey === 'function') { seriesKey = this.config!.seriesKey } else { seriesKey = (d: T) => { return d[this.config!.seriesKey as keyof T] + '' } } let value: (d: T) => number; if (typeof this.config?.value === 'function') { value = this.config!.value } else { value = (d: T) => { return +d[this.config!.value as keyof T] } } let formatValue: Exclude["formatValue"], undefined> = (stack, series, value) => `${stack} ${series}\n${value}` if (this.config?.formatValue) { formatValue = this.config.formatValue; } // Prepare the stacked data const indexed = d3.index(this._data, stackName, seriesKey) const stackGenerator = d3.stack<[string, d3.InternMap]>() .keys(d3.union(this._data.map(seriesKey))) .value((data, key) => { const obj = data[1].get(key) if (obj === undefined) { return 0 } return value(obj); }) const series = stackGenerator(indexed) // Prepare the x domain const labels = new Set(); this._data.forEach(d => labels.add(stackName(d))); this.x!.domain(Array.from(labels)) .range([0, 2 * Math.PI]) .align(0); const innerRadius = this.innerRadius || (() => { return (series.length * 25) + 20 })() // Prepare the x domain const outerRadius = Math.min(this.width, this.height) / 2; const highest = d3.max(series, point => d3.max(point, point => point[1])!)! this.y!.domain([0, highest]) .range([innerRadius, outerRadius]); const arc = d3.arc() .innerRadius((d: any) => y(d[0])) .outerRadius((d: any) => y(d[1])) .startAngle((d: any) => x(d.data[0])!) .endAngle((d: any) => x(d.data[0])! + x.bandwidth()) .padAngle(0.01) .padRadius(innerRadius) let color: (key: string) => string; if (!this.config?.series) { const colorScale: d3.ScaleOrdinal = d3.scaleOrdinal() .domain(series.map(d => d.key)) .range(d3.schemeSpectral) .unknown("#ccc") color = key => colorScale(key); } else { color = key => this.config!.series![key].color } this.svg.select("g#chart") .selectAll() .data(series) .join("g") .call(g => { if (this.config?.colorAsClass) { g.attr("fill", "currentColor") .attr("class", d => color(d.key)) } else { g.attr("fill", d => color(d.key)) } }) .selectAll("path") .data(D => D.map(d => ((d as any).key = D.key, d))) .join("path") .attr("d", arc as any) .append("title") .text(d => { const stack = d.data[0] const series = (d as any).key const data = d.data[1].get(series); const seriesValue = data ? value(data) : 0; return formatValue(stack, series, seriesValue, data); }) const sumPerLabel = this._data.reduce((map, current) => { const stack = stackName(current) let sum = map.get(stack) || 0 sum += value(current) map.set(stack, sum) return map }, new Map()); this.svg.select("g#text") .attr("text-anchor", "middle") .selectAll() .data(x.domain()) .join("g") .attr("text-anchor", d => (x(d)! + x.bandwidth() / 2 + Math.PI) % (2 * Math.PI) < Math.PI ? "end" : "start") .attr("transform", d => "rotate(" + ((x(d)! + this.x!.bandwidth() / 2) * 180 / Math.PI - 90) + ")" + "translate(" + (y(sumPerLabel.get(d)!) + 10) + ",0)") .append("g") .attr("transform", d => (x(d)! + x.bandwidth() / 2 + Math.PI) % (2 * Math.PI) < Math.PI ? "rotate(180)" : "rotate(0)") .style("font-size", "11px") .attr("alignment-baseline", "middle") .attr("fill", "currentColor") .attr("class", "text-primary cursor-pointer") .on("mouseenter", function (data) { d3.select(this) .classed("underline", true) }) .on("mouseleave", function (data) { d3.select(this) .classed("underline", false) }) .call(g => { if (!this.config?.formatStack) { return g.append("text") .text(d => `${d}`) } return this.config.formatStack(g as any, this._data) }) // y axis const tickCount = this.config?.ticks || Math.floor((outerRadius - innerRadius) / 20) const tickFormat = this.config?.formatTick || y.tickFormat(tickCount, "s") this.svg.select("g#ticks") .attr("text-anchor", "middle") .selectAll("g") .data(y.ticks(tickCount).slice(1)) .join("g") .attr("fill", "none") .call(g => g.append("circle") .attr("stroke", "#fff") .attr("stroke-opacity", 0.25) .attr("r", y)) .call(g => g.append("text") .style("font-size", "0.6rem") .attr("y", d => -y(d)) .attr("dy", "0.35em") .attr("fill", "currentColor") .attr("class", "text-secondary") .text(tickFormat)) // color legend this.svg.select("g#legend") .selectAll() .data(series.map(s => s.key)) .join("g") .attr("transform", (d, i, nodes) => `translate(-40,${(nodes.length / 2 - i - 1) * 20})`) .call(g => g.append("circle") .attr("r", 5) .call(g => { if (this.config?.colorAsClass) { g.attr("fill", "currentColor") .attr("class", d => color(d)) } else { g.attr("fill", d => color(d)) } })) .call(g => g.append("text") .attr("x", 12) .attr("y", 4) .attr("font-size", "0.6rem") .attr("fill", "#fff") .text(d => { if (!!this.config?.seriesLabel) { return this.config.seriesLabel(d) } return d })); } } ================================================ FILE: desktop/angular/src/app/shared/netquery/combined-menu.pipe.ts ================================================ import { KeyValue } from '@angular/common'; import { Pipe, PipeTransform } from "@angular/core"; interface Model { visible: boolean | 'combinedMenu'; } @Pipe({ pure: true, name: 'combinedMenu' }) export class CombinedMenuPipe implements PipeTransform { transform(value: KeyValue[], ...args: any[]) { return value.filter(entry => entry.value?.visible === 'combinedMenu') } } ================================================ FILE: desktop/angular/src/app/shared/netquery/connection-details/conn-details.html ================================================
Started: {{ conn.started | date:'medium'}} Ended: {{ conn.ended | date:'medium'}} Duration: {{ [conn.ended, conn.started] | duration }} Profile Revision: {{ conn.profile_revision }} Connection ID: {{ conn.id }} Verdict: {{ verdict[conn.verdict] || 'N/A' }} Internal Connection: {{ conn.internal ? 'Yes' : 'No' }} Local Address: {{ conn.local_ip }} {{ ':'+conn.local_port }}
Direction: {{ conn.direction === 'inbound' ? 'Incoming' : 'Outgoing' }} Protocol: {{ Protocols[conn.ip_protocol] || 'N/A' }} Encrypted: {{ conn.encrypted ? 'yes' : 'no' }} SPN Protected: {{ conn.tunneled ? 'yes' : 'no' }} Data Received: {{ conn.bytes_received | bytes }} Data Sent: {{ conn.bytes_sent | bytes }} TLS Version: {{ tls.Version }} TLS SNI: {{ tls.SNI }} TLS Certificate: {{ firstChain[0].Subject }} by {{ firstChain[0].Issuer }} Trust-Chain
  1. {{ cert.Subject }} by {{ cert.Issuer }}
Domain: {{dns.Domain}} Query: {{dns.Question}} Response: {{dns.RCode}} Served from Cache: {{dns.ServedFromCache ? 'yes' : 'no'}} Expires: {{dns.Expires | date:'medium'}}
Domain: Scope: Internet Peer-to-Peer Internet Multicast Device-Local LAN Peer-to-Peer LAN Multicast LAN Peer-to-Peer N/A N/A N/A {{ conn.direction === 'inbound' ? ' Incoming' : ' Outgoing'}} Remote Peer: {{ conn.remote_ip || 'DNS Request'}} {{ ':'+conn.remote_port }} Country: {{ (conn.country | countryName) || 'N/A' }} ASN: {{ conn.asn || 'N/A' }} AS Org: {{ conn.as_owner || 'N/A' }}
Binary Path: {{ conn.path }} Reason: {{conn.extra_data?.reason?.Msg}} Applied Setting: {{ helper.settings[option] || '' }}   from {{ !!conn.extra_data?.reason?.Profile ? "App" : "Global" }} Settings

SPN Tunnel

This connection has not been routed through the Safing Privacy Network.
Path Costs: {{ conn.extra_data?.tunnel?.PathCost }} Routing Algorithm: {{ conn.extra_data?.tunnel?.RoutingAlg }}
The connection was routed through the Safing Privacy Network, but the tunnel information is not available. Try reloading the connections.

Data Usage

================================================ FILE: desktop/angular/src/app/shared/netquery/connection-details/conn-details.scss ================================================ :host { section { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); width: 100%; overflow: hidden; gap: 1.5rem; } } section { &>div { @apply flex flex-col gap-2 items-start justify-start text-xxs; &>span { @apply space-x-1 text-ellipsis block overflow-hidden w-full; &>span:first-child { @apply text-secondary whitespace-nowrap; } &>span:last-child { @apply whitespace-nowrap; } } } } .tunnel-path { position: relative; .line { position: absolute; top: 10px; bottom: 10px; left: 8px; width: 1px; background-color: rgba(255, 255, 255, 0.1); } .node-tag { border-radius: 1px solid rgba(255, 255, 255, 0.2); background-color: rgba(255, 255, 255, 0.1); padding: 2px; font-size: 85%; border-radius: 2px; transform: scale(0.85); } ul { position: relative; padding-left: 20px; li:not(:last-of-type) { padding-bottom: 0.35rem; } .ip { margin-left: 0.35rem; } .hop-icon { display: inline-block; margin-left: -17px; margin-right: 4px; font-weight: 400; &.country { margin-left: -20px; } } .hop-title { margin-right: 2px; } .country { display: inline-block; margin-left: -20px; margin-right: 4px; &.unknown { height: 14px; width: 16px; position: relative; top: 3px; border: 1px solid rgba(0, 0, 0, 0.25); opacity: 0.5; border-radius: 3px; @apply bg-buttons-icon; } } } } @keyframes arrow_move { 0% { top: 0%; opacity: 1; } 85% { opacity: 1; } 100% { top: 95%; opacity: 0; } } ================================================ FILE: desktop/angular/src/app/shared/netquery/connection-details/conn-details.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, inject } from "@angular/core"; import { BandwidthChartResult, ConnectionBandwidthChartResult, IPProtocol, IPScope, IsDenied, IsDNSRequest, Netquery, NetqueryConnection, PortapiService, Process, Verdict } from "@safing/portmaster-api"; import { SfngDialogService } from '@safing/ui'; import { Subscription } from "rxjs"; import { ProcessDetailsDialogComponent } from '../../process-details-dialog'; import { NetqueryHelper } from "../connection-helper.service"; import { BytesPipe } from "../../pipes/bytes.pipe"; import { formatDuration } from "../../pipes"; @Component({ selector: 'sfng-netquery-conn-details', styleUrls: ['./conn-details.scss'], templateUrl: './conn-details.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class SfngNetqueryConnectionDetailsComponent implements OnInit, OnDestroy, OnChanges { helper = inject(NetqueryHelper) private readonly portapi = inject(PortapiService) private readonly dialog = inject(SfngDialogService) private readonly cdr = inject(ChangeDetectorRef) private readonly netquery = inject(Netquery) @Input() conn: NetqueryConnection | null = null; process: Process | null = null; readonly IsDNS = IsDNSRequest; readonly verdict = Verdict; readonly Protocols = IPProtocol; readonly scopes = IPScope; private _subscription = Subscription.EMPTY; formatBytes = (n: d3.NumberValue, seriesKey?: string) => { let prefix = ''; if (seriesKey !== undefined) { prefix = seriesKey === 'incoming' ? 'Received: ' : 'Sent: ' } return prefix + new BytesPipe().transform(n.valueOf()) } formatTime = (n: Date) => { const diff = Math.floor(new Date().getTime() - n.getTime()) return formatDuration(diff, false, true) + " ago" } tooltipFormat = (n: BandwidthChartResult) => { const bytes = new BytesPipe().transform const received = `Received: ${bytes(n?.incoming || 0)}`; const sent = `Sent: ${bytes(n?.outgoing || 0)}` if ((n?.incoming || 0) > (n?.outgoing || 0)) { return `${received}\n${sent}` } return `${sent}\n${received}` } connectionNotice: string = ''; bwData: ConnectionBandwidthChartResult[] = []; ngOnChanges(changes: SimpleChanges) { if (!!changes?.conn) { this.updateConnectionNotice(); this.loadBandwidthChart(); if (this.conn?.extra_data?.pid !== undefined) { this.portapi.get(`network:tree/${this.conn.extra_data.pid}-${this.conn.extra_data.processCreatedAt}`) .subscribe({ next: p => { this.process = p; this.cdr.markForCheck(); }, error: () => { this.process = null; // the process does not exist anymore this.cdr.markForCheck(); } }) } else { this.process = null; } } } ngOnInit() { this._subscription = this.helper.refresh.subscribe(() => { this.updateConnectionNotice(); this.loadBandwidthChart(); this.cdr.markForCheck(); }) } ngOnDestroy() { this._subscription.unsubscribe(); } openProcessDetails() { this.dialog.create(ProcessDetailsDialogComponent, { data: this.process, backdrop: true, autoclose: true, }) } private loadBandwidthChart() { this.bwData = []; if (!this.conn) { this.cdr.markForCheck() return; } this.netquery.connectionBandwidthChart([this.conn!.id], 1) .subscribe(result => { if (!result[this.conn!.id]?.length) { return; } this.bwData = result[this.conn!.id]; this.cdr.markForCheck(); }); } private updateConnectionNotice() { this.connectionNotice = ''; if (!this.conn) { return; } if (this.conn!.verdict === Verdict.Failed) { this.connectionNotice = 'Failed with previous settings.' return; } if (IsDenied(this.conn!.verdict)) { this.connectionNotice = 'Blocked by previous settings.'; } else { this.connectionNotice = 'Allowed by previous settings.'; } this.connectionNotice += ' You current settings could decide differently.' } } ================================================ FILE: desktop/angular/src/app/shared/netquery/connection-details/index.ts ================================================ export * from './conn-details'; ================================================ FILE: desktop/angular/src/app/shared/netquery/connection-helper.service.ts ================================================ import { DOCUMENT } from '@angular/common'; import { Inject, Injectable, Renderer2, inject } from '@angular/core'; import { Router } from '@angular/router'; import { AppProfile, AppProfileService, ConfigService, IPScope, NetqueryConnection, Pin, PossilbeValue, QueryResult, SPNService, Verdict, deepClone, flattenProfileConfig, getAppSetting, setAppSetting } from '@safing/portmaster-api'; import { BehaviorSubject, Observable, OperatorFunction, Subject, combineLatest } from 'rxjs'; import { distinctUntilChanged, filter, map, switchMap, take, takeUntil } from 'rxjs/operators'; import { ActionIndicatorService } from '../action-indicator'; import { objKeys } from '../utils'; import { SfngSearchbarFields } from './searchbar'; import { INTEGRATION_SERVICE } from 'src/app/integration'; export const IPScopeNames: { [key in IPScope]: string } = { [IPScope.Invalid]: "Invalid", [IPScope.Undefined]: "Undefined", [IPScope.HostLocal]: "Device Local", [IPScope.LinkLocal]: "Link Local", [IPScope.SiteLocal]: "LAN", [IPScope.Global]: "Internet", [IPScope.LocalMulticast]: "LAN Multicast", [IPScope.GlobalMulitcast]: "Internet Multicast" } export interface LocalAppProfile extends AppProfile { FlatConfig: { [key: string]: any } } @Injectable() export class NetqueryHelper { readonly settings: { [key: string]: string } = {}; refresh = new Subject(); private onShiftKey$ = new BehaviorSubject(false); private onCtrlKey$ = new BehaviorSubject(false); private addToFilter$ = new Subject(); private destroy$ = new Subject(); private appProfiles$ = new BehaviorSubject([]); private spnMapPins$ = new BehaviorSubject(null); private readonly integration = inject(INTEGRATION_SERVICE); readonly onShiftKey: Observable; readonly onCtrlKey: Observable; constructor( private router: Router, private profileService: AppProfileService, private configService: ConfigService, private actionIndicator: ActionIndicatorService, private renderer: Renderer2, private spnService: SPNService, @Inject(DOCUMENT) private document: Document, ) { const cleanupKeyDown = this.renderer.listen(this.document, 'keydown', (event: KeyboardEvent) => { if (event.shiftKey) { this.onShiftKey$.next(true) } if (event.ctrlKey) { this.onCtrlKey$.next(true); } }); const cleanupKeyUp = this.renderer.listen(this.document, 'keyup', () => { this.onShiftKey$.next(false); this.onCtrlKey$.next(false); }) const windowBlur = this.renderer.listen(window, 'blur', () => { this.onShiftKey$.next(false); this.onCtrlKey$.next(false); }) this.destroy$.subscribe({ complete: () => { cleanupKeyDown(); cleanupKeyUp(); windowBlur(); } }) this.onShiftKey = this.onShiftKey$ .pipe(distinctUntilChanged()); this.onCtrlKey = this.onCtrlKey$ .pipe(distinctUntilChanged()); this.configService.query('') .subscribe(settings => { settings.forEach(setting => { this.settings[setting.Key] = setting.Name; }); this.refresh.next(); }); // watch all application profiles this.profileService.watchProfiles() .pipe(takeUntil(this.destroy$)) .subscribe(profiles => { this.appProfiles$.next((profiles || []).map(p => { return { ...p, FlatConfig: flattenProfileConfig(p.Config), } })) }); this.spnService.watchPins() .pipe(takeUntil(this.destroy$)) .subscribe(pins => { this.spnMapPins$.next(pins); }) } decodePrettyValues(field: keyof NetqueryConnection, values: any[]): any[] { if (field === 'verdict') { return values.map(val => Verdict[val]).filter(value => value !== undefined); } if (field === 'scope') { return values.map(val => { // check if it's a value of the IPScope enum const scopeValue = IPScope[val]; if (!!scopeValue) { return scopeValue; } // otherwise check if it's pretty name of the scope translation val = `${val}`.toLocaleLowerCase(); return objKeys(IPScopeNames).find(scope => IPScopeNames[scope].toLocaleLowerCase() === val) }).filter(value => value !== undefined); } if (field === 'allowed') { return values.map(val => { if (typeof val !== 'string') { return val } switch (val.toLocaleLowerCase()) { case 'yes': return true case 'no': return false case 'n/a': case 'null': return null default: return val } }) } if (field === 'exit_node') { const lm = new Map(); (this.spnMapPins$.getValue() || []) .forEach(pin => lm.set(pin.Name, pin)); return values.map(val => lm.get(val)?.ID || val) } return values; } attachProfile(): OperatorFunction { return source => combineLatest([ source, this.appProfiles$, ]).pipe( map(([items, profiles]) => { let lm = new Map(); profiles.forEach(profile => { lm.set(`${profile.Source}/${profile.ID}`, profile) }) return items.map(item => { if ('profile' in item) { item.__profile = lm.get(item.profile!) } return item; }) }) ) } attachPins(): OperatorFunction { return source => combineLatest([ source, this.spnMapPins$ .pipe( filter(result => result !== null), take(1), ), ]).pipe( map(([items, pins]) => { let lm = new Map(); pins!.forEach(pin => { lm.set(pin.ID, pin) }) return items.map(item => { if ('exit_node' in item) { item.__exitNode = lm.get(item.exit_node!) } return item; }) }) ) } encodeToPossibleValues(field: string): OperatorFunction { return source => combineLatest([ source, this.appProfiles$, this.spnMapPins$, ]).pipe( map(([items, profiles, pins]) => { // convert profile IDs to profile name if (field === 'profile') { let lm = new Map(); profiles.forEach(profile => { lm.set(`${profile.Source}/${profile.ID}`, profile) }) return items.map((item: any) => { const profile = lm.get(item.profile!) return { Name: profile?.Name || `${item.profile}`, Value: item.profile!, Description: '', __profile: profile || null, ...item, } }) } // convert verdict identifiers to their pretty name. if (field === 'verdict') { return items.map(item => { if (Verdict[item.verdict!] === undefined) { return null } return { Name: Verdict[item.verdict!], Value: item.verdict, Description: '', ...item } }) } // convert the IP scope identifier to a pretty name if (field === 'scope') { return items.map(item => { if (IPScope[item.scope!] === undefined) { return null } return { Name: IPScopeNames[item.scope!], Value: item.scope, Description: '', ...item } }) } if (field === 'allowed') { return items // we remove any "null" value from allowed here as it may happen for a really short // period of time and there's no reason to actually filter for them because // from showing a "null" value to the user clicking it the connection will have been // verdicted and thus no results will show up for "null". .filter(item => typeof item.allowed === 'boolean') .map(item => { return { Name: item.allowed ? 'Yes' : 'No', Value: item.allowed, Description: '', ...item } }) } if (field === 'exit_node') { const lm = new Map(); pins!.forEach(pin => lm.set(pin.ID, pin)); return items.map(item => { const pin = lm.get(item.exit_node!); return { Name: pin?.Name || item.exit_node, Value: item.exit_node, Description: 'Operated by ' + (pin?.VerifiedOwner || 'N/A'), ...item } }) } // the rest is just converted into the {@link PossibleValue} form // by using the value as the "Name". return items.map(item => ({ Name: `${item[field]}`, Value: item[field], Description: '', ...item, })) }), // finally, remove any values that have been mapped to null in the above stage. // this may happen for values that are not valid for the given model field (i.e. using "Foobar" for "verdict") map(results => { return results.filter(val => !!val) }) ) } dispose() { this.onShiftKey$.complete(); this.destroy$.next(); this.destroy$.complete(); } /** Emits added fields whenever addToFilter is called */ onFieldsAdded(): Observable { return this.addToFilter$.asObservable(); } /** Adds a new filter to the current query */ addToFilter(key: string, value: any[]) { this.addToFilter$.next({ [key]: value, }) } /** * @private * Returns the class used to color the connection's * verdict. * * @param conn The connection object */ getVerdictClass(conn: NetqueryConnection): string { return Verdict[conn.verdict]?.toLocaleLowerCase() || `unknown-verdict<${conn.verdict}>`; } /** * @private * Redirect the user to a settings key in the application * profile. * * @param key The settings key to redirect to */ redirectToSetting(setting: string, conn: NetqueryConnection, globalSettings = false) { const reason = conn.extra_data?.reason; if (!reason) { return; } if (!setting) { setting = reason.OptionKey; } if (!setting) { return; } if (globalSettings) { this.router.navigate( ['/', 'settings'], { queryParams: { setting: setting, } }) return; } let profile = conn.profile if (!!reason.Profile) { profile = reason.Profile; } if (profile.startsWith("core:profiles/")) { profile = profile.replace("core:profiles/", "") } this.router.navigate( ['/', 'app', ...profile.split("/")], { queryParams: { tab: 'settings', setting: setting, } }) } /** * @private * Redirect the user to "outgoing rules" setting in the * application profile/settings. */ redirectToRules(conn: NetqueryConnection) { if (conn.direction === 'inbound') { this.redirectToSetting('filter/serviceEndpoints', conn); } else { this.redirectToSetting('filter/endpoints', conn); } } /** * @private * Dump a connection to the console * * @param conn The connection to dump */ async dumpConnection(conn: NetqueryConnection) { // Copy to clip-board if supported try { await this.integration.writeToClipboard(JSON.stringify(conn, undefined, " ")) this.actionIndicator.info("Copied to Clipboard") } catch (err: any) { this.actionIndicator.error("Copy to Clipboard Failed", err?.message || JSON.stringify(err)) } } /** * @private * Creates a new "block domain" outgoing rules */ blockAll(domain: string, conn: NetqueryConnection) { /* Deactivate until exact behavior is specified. if (this.isDomainBlocked(domain)) { this.actionIndicator.info(domain + ' already blocked') return; } */ domain = domain.replace(/\.+$/, ''); const newRule = `- ${domain}`; this.updateRules(newRule, true, conn) } /** * @private * Removes a "block domain" rule from the outgoing rules */ unblockAll(domain: string, conn: NetqueryConnection) { /* Deactivate until exact behavior is specified. if (!this.isDomainBlocked(domain)) { this.actionIndicator.info(domain + ' already allowed') return; } */ domain = domain.replace(/\.+$/, ''); const newRule = `+ ${domain}`; this.updateRules(newRule, true, conn); } /** * Updates the outgoing rule set and either creates or deletes * a rule. If a rule should be created but already exists * it is moved to the top. * * @param newRule The new rule to create or delete. * @param add Whether or not to create or delete the rule. */ private updateRules(newRule: string, add: boolean, conn: NetqueryConnection) { if (!conn.profile) { return } let key = 'filter/endpoints'; if (conn.direction === 'inbound') { key = 'filter/serviceEndpoints' } this.profileService.getAppProfile(conn.profile) .pipe( switchMap(profile => { let rules = getAppSetting(profile.Config, key) || []; rules = rules.filter(rule => rule !== newRule); if (add) { rules.splice(0, 0, newRule) } const newProfile = deepClone(profile); if (newProfile.Config === null || newProfile.Config === undefined) { newProfile.Config = {} } setAppSetting(newProfile.Config, key, rules); return this.profileService.saveProfile(newProfile) }) ) .subscribe({ next: () => { if (add) { this.actionIndicator.success('Rules Updated', 'Successfully created a new rule.') } else { this.actionIndicator.success('Rules Updated', 'Successfully removed matching rule.') } }, error: err => { this.actionIndicator.error('Failed to update rules', JSON.stringify(err)) } }); } } ================================================ FILE: desktop/angular/src/app/shared/netquery/connection-row/conn-row.html ================================================
Internet Peer-to-Peer Internet Multicast Device-Local LAN Peer-to-Peer LAN Multicast LAN Peer-to-Peer N/A N/A N/A {{ conn.direction === 'inbound' ? ' Incoming' : ' Outgoing'}}
{{ conn.country | countryName }}
{{ conn.__profile.Name }}
{{ conn.remote_ip }} :{{ conn.remote_port }} DNS Request
App Setting Global Setting Allow {{ conn.domain ? 'Domain' : 'IP'}} Block {{ conn.domain ? 'Domain' : 'IP '}} Copy JSON
================================================ FILE: desktop/angular/src/app/shared/netquery/connection-row/conn-row.scss ================================================ :host { @apply w-full flex-grow gap-4 grid justify-start items-center overflow-hidden; grid-template-columns: 1fr 1fr 1fr 2rem; grid-auto-rows: 1.5rem; grid-template-rows: none; &>* { @apply overflow-hidden whitespace-nowrap; &>*:last-child { @apply overflow-hidden text-ellipsis; } } --app-icon-size: 20px; } :host-context(.min-width-768px) { :host { grid-template-columns: 1fr 4rem 1fr 1fr 5rem 2rem; ; } } :host-context(.min-width-1024px) { :host { grid-template-columns: 1fr 4rem 1fr 1fr 5rem 0.5fr 2rem; ; } } :host-context(.min-width-1280px) { :host { grid-template-columns: 1fr 4rem 1fr 1fr 8rem 1fr 2rem; ; } } ================================================ FILE: desktop/angular/src/app/shared/netquery/connection-row/conn-row.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from "@angular/core"; import { AppProfile, IPScope, NetqueryConnection, Verdict } from "@safing/portmaster-api"; import { interval, Subscription } from "rxjs"; import { share, startWith } from "rxjs/operators"; import { NetqueryHelper } from "../connection-helper.service"; interface ProfileAttachedConnection extends NetqueryConnection { __profile?: AppProfile; } @Component({ selector: 'sfng-netquery-connection-row', templateUrl: './conn-row.html', styleUrls: [ './conn-row.scss' ], changeDetection: ChangeDetectionStrategy.OnPush, }) export class SfngNetqueryConnectionRowComponent implements OnInit, OnDestroy { readonly scopes = IPScope; readonly verdicts = Verdict; @Input() set conn(c: ProfileAttachedConnection) { this._conn = c; } get conn() { return this._conn; } _conn!: ProfileAttachedConnection; @Input() activeRevision: number | undefined = 0; /* timeAgoTicker ticks every 10000 seconds to force a refresh of the timeAgo pipes */ timeAgoTicker: number = 0; private _subscription = Subscription.EMPTY; constructor( public helper: NetqueryHelper, private changeDetectorRef: ChangeDetectorRef, ) { } ngOnInit() { this._subscription = new Subscription(); const tickerSub = interval(10000).pipe( startWith(-1), share() ).subscribe(i => this.timeAgoTicker = i); const helperSub = this.helper.refresh.subscribe(() => { this.changeDetectorRef.markForCheck(); }) this._subscription.add(helperSub); this._subscription.add(tickerSub); } ngOnDestroy() { this._subscription.unsubscribe(); } } ================================================ FILE: desktop/angular/src/app/shared/netquery/connection-row/index.ts ================================================ export * from './conn-row'; ================================================ FILE: desktop/angular/src/app/shared/netquery/index.ts ================================================ export * from './netquery.component'; export * from './netquery.module'; ================================================ FILE: desktop/angular/src/app/shared/netquery/line-chart/index.ts ================================================ ================================================ FILE: desktop/angular/src/app/shared/netquery/line-chart/line-chart.ts ================================================ import { coerceBooleanProperty, coerceNumberProperty, coerceStringArray } from '@angular/cdk/coercion'; import { AfterViewInit, ChangeDetectionStrategy, Component, DestroyRef, ElementRef, Input, OnChanges, OnInit, SimpleChanges, inject } from '@angular/core'; import { BandwidthChartResult, ChartResult } from '@safing/portmaster-api'; import * as d3 from 'd3'; import { Selection } from 'd3'; import { AppComponent } from 'src/app/app.component'; import { formatDuration, timeAgo } from '../../pipes'; import { objKeys } from '../../utils'; import { BytesPipe } from '../../pipes/bytes.pipe'; export interface SeriesConfig { lineColor: string; areaColor?: string; } export interface Marker { text: string; time: Date | number | string; } export interface ChartConfig { series: { [key in Exclude]?: SeriesConfig; }, time?: { from: number | string | Date; to?: number | string | Date; }, fromMargin?: number; toMargin?: number; valueFormat?: (n: d3.NumberValue, seriesKey?: string) => string, tooltipFormat?: (data: T) => string; timeFormat?: (n: Date) => string, showDataPoints?: boolean; fillEmptyTicks?: { interval: number; }, verticalMarkers?: Marker[]; } function coerceDate(d: Date | number | string): Date { if (typeof d === 'string') { return new Date(d) } if (d instanceof Date) { return d } if (d < 0) { return new Date((new Date()).getTime() + d * 1000) } return new Date(d * 1000); } export const DefaultChartConfig: ChartConfig = { series: { value: { lineColor: 'text-green-200', areaColor: 'text-green-100 text-opacity-25' }, countBlocked: { lineColor: 'text-red-200', areaColor: 'text-red-100 text-opacity-25' } }, } export const DefaultBandwidthChartConfig: ChartConfig> = { series: { outgoing: { lineColor: 'text-deepPurple-500', areaColor: 'text-deepPurple-700 text-opacity-5', }, incoming: { lineColor: 'text-cyan-800', areaColor: 'text-cyan-700 text-opacity-5', }, }, time: { from: -10 * 60, }, valueFormat: (n: d3.NumberValue, seriesKey?: string) => { let prefix = ''; if (seriesKey !== undefined) { prefix = seriesKey === 'incoming' ? 'Received: ' : 'Sent: ' } return prefix + new BytesPipe().transform(n.valueOf()) }, timeFormat: (n: Date) => { const diff = Math.floor(new Date().getTime() - n.getTime()) return formatDuration(diff, false, true) + " ago" }, tooltipFormat: (n: BandwidthChartResult) => { const bytes = new BytesPipe().transform const received = `Received: ${bytes(n?.incoming || 0)}`; const sent = `Sent: ${bytes(n?.outgoing || 0)}` if ((n?.incoming || 0) > (n?.outgoing || 0)) { return `${received}\n${sent}` } return `${sent}\n${received}` }, showDataPoints: true, fillEmptyTicks: { interval: 60 }, } export interface SeriesData { timestamp: number; } @Component({ selector: 'sfng-netquery-line-chart', styles: [ ` :host { @apply block h-full w-full; } ` ], template: '', changeDetection: ChangeDetectionStrategy.OnPush, }) export class SfngNetqueryLineChartComponent implements OnChanges, OnInit, AfterViewInit { private destroyRef = inject(DestroyRef); @Input() data: D[] = []; private preparedData: D[] = []; private width = 700; private height = 250; @Input() set margin(v: any) { this._margin = coerceNumberProperty(v); } get margin() { return this._margin; } private _margin = 0; @Input() config!: ChartConfig; svg!: Selection; svgInner!: Selection; yScale!: d3.ScaleLinear; xScale!: d3.ScaleTime; xAxis!: Selection; yAxis!: Selection; @Input() set showAxis(v: any) { this._showAxis = coerceBooleanProperty(v); } get showAxis() { return this._showAxis; } private _showAxis = true; constructor( public chartElem: ElementRef, private app: AppComponent ) { } ngOnInit() { if (!this.config) { this.config = DefaultChartConfig as any; } const observer = new ResizeObserver(() => { this.redraw(); }) observer.observe(this.chartElem.nativeElement) this.destroyRef.onDestroy(() => observer.disconnect()) } ngAfterViewInit(): void { requestAnimationFrame(() => { this.redraw() }) } ngOnChanges(changes: SimpleChanges): void { if (Object.prototype.hasOwnProperty.call(changes, 'config') && this.config) { this.redraw() return } if (Object.prototype.hasOwnProperty.call(changes, 'data') && this.data) { this.drawChart(); } } get yMargin() { if (this.showAxis) { return 16; } return 0; } redraw(event?: Event) { if (!!this.svg) { this.svg.remove(); } this.initializeChart(); this.drawChart(); } private initializeChart(): void { this.width = this.chartElem.nativeElement.getBoundingClientRect().width; this.height = this.chartElem.nativeElement.getBoundingClientRect().height; this.svg = d3 .select(this.chartElem.nativeElement) .append('svg') this.svg.attr('width', this.width); this.svg.attr('height', this.height); this.svgInner = this.svg .append('g') .attr('height', '100%'); this.yScale = d3 .scaleLinear() this.xScale = d3.scaleTime(); // setup event handlers to higlight the closest data points let lastClosestIndex = -1; if (this.config.showDataPoints) { const self = this; this.svg .on("mousemove", function (event: MouseEvent) { let x = d3.pointer(event)[0]; let closest = self.data.reduce((best, value, idx) => { let absx = Math.abs(self.xScale(new Date(value.timestamp * 1000)) - x); if (absx < best.value) { return { index: idx, value: absx, timestamp: self.data[idx].timestamp } } return best }, { index: 0, value: Number.MAX_SAFE_INTEGER, timestamp: 0 }) if (lastClosestIndex === closest.index) { return; } lastClosestIndex = closest.index; if (self.config.tooltipFormat) { // append a title to the parent SVG, this is a quick-fix for showing some // information on the highlighted points // TODO(ppacher): actually render a nice tooltip there. let tooltip = self.svg .select('title.tooltip') if (tooltip.empty()) { tooltip = self.svg.append("title") .attr("class", "tooltip") } tooltip .text(self.config.tooltipFormat!(self.data[closest.index])) } self.svgInner .select(".vertical-marker") .selectAll(".mouse-position") .remove() self.svgInner .select(".vertical-marker") .append("line") .classed("mouse-position", true) .attr("x1", d => self.xScale(closest.timestamp * 1000)) .attr("y1", -10) .attr("x2", d => self.xScale(closest.timestamp * 1000)) .attr("y2", self.height - self.yMargin) .classed("text-secondary text-opacity-50", true) .attr("stroke", "currentColor") .attr("stroke-width", 1) .attr("stroke-dasharray", 2) self.svgInner .select(".points") .selectAll("circle") .classed("opacity-100", d => self.xScale.invert(d[0]).getTime() === closest.timestamp * 1000) }) .on("mouseleave", function () { lastClosestIndex = -1; self.svg.select("title.tooltip") .remove() self.svg.select("line.mouse-position") .remove() self.svgInner .select(".points") .selectAll("circle") .attr("r", 4) .classed("opacity-100", false) }) } objKeys(this.config.series).forEach(seriesKey => { const seriesConfig = this.config.series[seriesKey]!; if (seriesConfig.areaColor) { this.svgInner .append('path') .attr("fill", "currentColor") .attr("class", `area-${String(seriesKey)} ${(seriesConfig.areaColor || '')}`) } this.svgInner .append('g') .append('path') .style('fill', 'none') .style('stroke', 'currentColor') .style('stroke-width', '1') .attr('class', `line-${String(seriesKey)} ${seriesConfig.lineColor}`) }) this.svgInner.append("g") .attr("class", "vertical-marker") this.svgInner.append("g") .attr("class", "points") if (this.showAxis) { this.yAxis = this.svgInner .append('g') .attr('id', 'y-axis') .attr('class', 'text-secondary text-opacity-75 ') .style('transform', 'translate(' + (this.width - this.yMargin) + 'px, 0)'); this.xAxis = this.svgInner .append('g') .attr('id', 'x-axis') .attr('class', 'text-secondary text-opacity-50 ') .style('transform', 'translate(0, ' + (this.height - this.yMargin) + 'px)'); } } private getTimeRange(): { from: Date, to: Date } { const time = { from: this.data[0]?.timestamp || 0, to: this.data[this.data.length - 1]?.timestamp || 0, }; if (!!this.config.time) { time.from = coerceDate(this.config.time.from).getTime() / 1000 if (this.config.fromMargin) { time.from = time.from - this.config.fromMargin } if (this.config.time.to) { time.to = coerceDate(this.config.time.to).getTime() / 1000 if (this.config.toMargin) { time.to = time.to + this.config.toMargin } } } return { from: new Date(time.from * 1000), to: new Date(time.to * 1000) }; } private prepareDataSet(data: D[], time: { from: Date, to: Date }) { const toTimestamp = Math.round(time.to.getTime() / 1000) const fromTimestamp = Math.round(time.from.getTime() / 1000) // first, filter out all elements that are before or after the to date data = data.filter(d => { return d.timestamp >= fromTimestamp && d.timestamp <= toTimestamp }) // check if we need to fill empty ticks if (!this.config.fillEmptyTicks) { return data; } const interval = this.config.fillEmptyTicks.interval; const filledData: D[] = []; const addEmpty = (ts: number) => { const empty: any = { timestamp: ts, } Object.keys(this.config.series) .forEach(s => empty[s] = 0) filledData.push(empty) } if (!data.length) { return []; } let firstElement = data[0].timestamp; if (this.config.time?.from) { firstElement = Math.round(coerceDate(this.config.time.from).getTime() / 1000) } // add empty values for the start-time until the first element / or the start tme let lastTimeStamp = fromTimestamp - interval; for (let ts = lastTimeStamp; ts <= firstElement; ts += interval) { addEmpty(ts) } // add emepty vaues for each missing tick during the dataset lastTimeStamp = firstElement; for (let idx = 0; idx < data.length; idx++) { const elem = data[idx] const elemTs = elem.timestamp; for (let ts = lastTimeStamp + interval; ts < elemTs; ts += interval) { addEmpty(ts) } filledData.push(elem) lastTimeStamp = elemTs } // if there's a specified end-time, add empty ticks from the last datapoint // to the end-time if (this.config.time?.to) { for (let ts = lastTimeStamp + interval; ts <= toTimestamp; ts += interval) { addEmpty(ts) } } return filledData } private drawChart(): void { if (!this.svg) { return; } if (!this.data?.length) { return; } this.data.sort((a, b) => a.timestamp - b.timestamp) // determine the time range that should be displayed. const time = this.getTimeRange(); // fill empty ticks depending on the configuration. this.preparedData = this.prepareDataSet(this.data, time) this.xScale .range([0, this.width - this.yMargin]) .domain([time.from, time.to]); this.yScale .range([0, this.height - this.yMargin]) .domain([ d3.max(this.preparedData.map(d => { return d3.max( objKeys(this.config.series) .map(series => { return d[series] as number }) )! }))! * 1.3, // 30% margin to top 0 ]) if (this.showAxis) { const xAxis = d3 .axisBottom(this.xScale) .ticks(5) .tickFormat((val, idx) => { if (!!this.config.timeFormat) { return this.config.timeFormat(val as any) } return timeAgo(val as any); }) this.xAxis.call(xAxis); const yAxis = d3 .axisLeft(this.yScale) .ticks(2) .tickFormat(d => ((this.config.valueFormat || this.yScale.tickFormat(2)) as any)(d, undefined)) this.yAxis.call(yAxis); } const line = d3 .line() .x(d => d[0]) .y(d => d[1]) .curve(d3.curveMonotoneX); // define the area const area = d3.area() .x(d => d[0]) .y0(this.height - this.yMargin) .y1(d => d[1]) .curve(d3.curveMonotoneX) // render vertical markers const markers = (this.config.verticalMarkers || []) .filter(marker => !!marker.time) .map(marker => ({ text: marker.text, time: coerceDate(marker.time) })); this.svgInner.select('.vertical-marker') .selectAll("line.marker") .data(markers) .join("line") .classed("marker", true) .attr("x1", d => this.xScale(d.time)) .attr("y1", -10) .attr("x2", d => this.xScale(d.time)) .attr("y2", this.height - this.yMargin) .classed("text-secondary text-opacity-50", true) .attr("stroke", "currentColor") .attr("stroke-width", 3) .attr("stroke-dasharray", 4) .append("title") .text(d => d.text) // TODO(ppacher): somehow d3 does not recognize which data points must be removed // or re-placed. For now, just remove them all this.svgInner .select('.points') .selectAll("circle") .remove() objKeys(this.config.series) .forEach(seriesKey => { const config = this.config.series[seriesKey]!; let points: [number, number][] = this.preparedData .map(d => [ this.xScale(new Date(d.timestamp * 1000)), this.yScale((d as any)[seriesKey] || 0), ]) let data: [number, number][] = this.preparedData .map(d => [ this.xScale(new Date(d.timestamp * 1000)), this.yScale((d as any)[seriesKey] || 0), ]) if (config.areaColor) { this.svgInner.selectAll(`.area-${String(seriesKey)}`) .data([data]) .attr('d', area(data)) } this.svgInner.select(`.line-${String(seriesKey)}`) .attr('d', line(data)) if (this.config?.showDataPoints) { this.svgInner .select('.points') .selectAll(`circle.point-${String(seriesKey)}`) .data(points) .enter() .append("circle") .classed(`points-${String(seriesKey)}`, true) .attr("r", "4") .attr("fill", "currentColor") .attr("class", `opacity-0 ${config.lineColor}`) .attr("cx", d => d[0]) .attr("cy", d => d[1]) .append("title") .text(d => ((this.config.valueFormat || this.yScale.tickFormat(2)) as any)(this.yScale.invert(d[1]), String(seriesKey))) } }) } } ================================================ FILE: desktop/angular/src/app/shared/netquery/netquery.component.html ================================================
Loading ... {{ value.Name || 'N/A' }} #{{ value.count }} connections {{ item.value || 'N/A' }}

Filter by {{ model.value!.menuTitle || model.key }}

  • {{ value.Name }}
Search History: Quick Settings
  • {{ qds.name }}
{{ keyTranslation[value] || value }} {{ keyTranslation[value] || value }}

Connections

Data Usage

Loading Chart
{{ keyTranslation[key] || key }} {{ data[key] || 'N/A' }} {{ data.__profile.Name }} ( ) {{ data.__exitNode.Name }} {{ data[key] || 'N/A' }}
Use as filter App Settings
{{ totalResultCount }} Results of {{totalConnCount}} total connections
{{autoReloadIntervalName}} (in {{interval}} sec) Last Reload: {{ lastReload | timeAgo:(lastReloadTicker|async) }}
{{value}}
All connections ended more than 10 minutes ago and have been removed.
Loading connections ...
Pro Tip:
Press
CTRL + Space
on any page to bring up the quick search box.
Use your keyboard arrows to navigate through the search suggestions. Press
ENTER
to search for the suggestion or use
Shift + Enter
to add it to the search text.
Inside the search box, use
Ctrl + Space
to force loading suggestions.
Use
Shift + Click
to add connection attributes to the current filter.
Hold
Shift
to highlight attributes that can be used in the filter.
Hold
CTRL
and click attributes to copy them to the clipboard.
================================================ FILE: desktop/angular/src/app/shared/netquery/netquery.component.ts ================================================ import { coerceArray } from "@angular/cdk/coercion"; import { FormatWidth, formatDate, getLocaleDateFormat, getLocaleId } from "@angular/common"; import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, EventEmitter, Input, LOCALE_ID, OnDestroy, OnInit, Output, QueryList, TemplateRef, TrackByFunction, ViewChildren, inject, isDevMode } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, Router } from "@angular/router"; import { BandwidthChartResult, ChartResult, Condition, Database, FeatureID, GreaterOrEqual, IPScope, LessOrEqual, Netquery, NetqueryConnection, OrderBy, Pin, PossilbeValue, Query, QueryResult, SPNService, Select, Verdict } from "@safing/portmaster-api"; import { Datasource, DynamicItemsPaginator, SelectOption } from "@safing/ui"; import { BehaviorSubject, Observable, Subject, combineLatest, forkJoin, interval, merge, of, timer } from "rxjs"; import { catchError, filter, map, share, skip, startWith, switchMap, take, takeUntil } from "rxjs/operators"; import { ActionIndicatorService } from "../action-indicator"; import { ExpertiseService } from "../expertise"; import { objKeys } from "../utils"; import { fadeInAnimation } from './../animations'; import { IPScopeNames, LocalAppProfile, NetqueryHelper } from "./connection-helper.service"; import { SfngSearchbarFields } from "./searchbar"; import { SfngTagbarValue } from "./tag-bar"; import { Parser } from "./textql"; import { connectionFieldTranslation, mergeConditions } from "./utils"; import { DefaultBandwidthChartConfig } from "./line-chart/line-chart"; import { INTEGRATION_SERVICE } from "src/app/integration"; interface Suggestion extends PossilbeValue { count: number; selected?: boolean; } interface Model { suggestions: Suggestion[]; searchValues: any[]; visible: boolean | 'combinedMenu'; menuTitle?: string; loading: boolean; tipupKey?: string; virtual?: boolean; } const freeTextSearchFields: (keyof Partial)[] = [ 'domain', 'as_owner', 'path', 'profile_name', 'remote_ip' ] const groupByKeys: (keyof Partial)[] = [ 'domain', 'as_owner', 'country', 'direction', 'path', 'profile' ] const orderByKeys: (keyof Partial)[] = [ 'domain', 'as_owner', 'country', 'direction', 'path', 'started', 'ended', 'profile', ] export const reloadIntervalValues: { [key: string]: number } = { "⏸\u00A0\u00A0Don't auto-reload": 0, "↻\u00A0\u00A0Reload every 10 seconds": 10, "↻\u00A0\u00A0Reload every 1 minute": 60, "↻\u00A0\u00A0Reload every 5 minutes": 300, } interface LocalQueryResult extends QueryResult { _chart: Observable | null; _group: Observable> | null; __profile?: LocalAppProfile; __exitNode?: Pin; } interface QuickDateSetting { name: string; apply: () => [Date, Date]; } /** * Netquery Viewer * * This component is the actual viewer component for the netquery subsystem of the Portmaster. * It allows the user to specify connection filters in multiple different ways and allows * to do a deep-dive into all connections seen by the Portmaster (that are still stored in * the in-memory SQLite database of the netquery subsystem). * * The user is able to modify the filter query by either: * - using the available drop-downs * - using the searchbar which * - supports typed searches for connection fields (i.e. country:AT domain:google.at) * - free-text search across the list of supported "full-text" search fields (see freeTextSearchFields) * - by shift-clicking any value that has a SfngAddToFilter directive * - by removing values from the tag bar. */ @Component({ // eslint-disable-next-line @angular-eslint/component-selector selector: 'sfng-netquery-viewer', templateUrl: './netquery.component.html', providers: [ NetqueryHelper, ], styles: [ ` :host { @apply flex flex-col gap-3 pr-3 min-h-full; } .protip pre { @apply inline-block text-xxs uppercase rounded-sm bg-gray-500 bg-opacity-25 font-mono border-gray-500 border px-0.5; } ` ], animations: [ fadeInAnimation ], changeDetection: ChangeDetectionStrategy.OnPush }) // eslint-disable-next-line @angular-eslint/component-class-suffix export class SfngNetqueryViewer implements OnInit, OnDestroy, AfterViewInit { /** @private Used to trigger a reload of the current filter */ private search$ = new Subject(); /** @private The DestroyRef of the component, required for takeUntilDestroyed */ private destroyRef = inject(DestroyRef); /** @private Used to trigger an update of all displayed values in the tag-bar. */ private updateTagBar$ = new BehaviorSubject(undefined); /** @private Whether or not the next update on ActivatedRoute should be ignored */ private skipNextRouteUpdate = false; /** @private Whether or not we should update the URL when performSearch() finishes */ private skipUrlUpdate = false; /** @private The LOCALE_ID to format dates. */ private localeId = inject(LOCALE_ID); private integration = inject(INTEGRATION_SERVICE); /** @private the date format for the nz-range-picker */ dateFormat = getLocaleDateFormat(getLocaleId(this.localeId), FormatWidth.Medium) /** @private A list of quick-date settings for the nz-range-picker */ quickDateSettings: QuickDateSetting[] = [ { name: 'Today', apply: () => { const now = new Date(); return [ new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0), new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, -1), ] } }, { name: 'Last 24 Hours', apply: () => { const now = new Date(); return [ new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours() - 24, now.getMinutes(), now.getSeconds()), now ] } }, { name: 'Last 7 Days', apply: () => { const now = new Date(); return [ new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7, now.getHours(), now.getMinutes(), now.getSeconds()), now, ] } }, { name: 'Last Month', apply: () => { const now = new Date(); return [ new Date(now.getFullYear(), now.getMonth() - 1, now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds()), now, ] } }, ] applyQuickDateSetting(qds: QuickDateSetting) { const [from, to] = qds.apply() const fromStr = formatDate(from, 'medium', this.localeId) const toStr = formatDate(to, 'medium', this.localeId) this.onFieldsParsed({ from: [fromStr], to: [toStr] }, true) } /** @private - The paginator used for the result set */ paginator!: DynamicItemsPaginator; /** @private - The total amount of connections without the filter applied */ totalConnCount: number = 0; /** @private - The total amount of connections with the filter applied */ totalResultCount: number = 0; /** The value of the free-text search */ textSearch: string = ''; /** The date filter */ dateFilter: Date[] = [] /** a list of allowed group-by keys */ readonly allowedGroupBy = groupByKeys; /** a list of allowed order-by keys */ readonly allowedOrderBy = orderByKeys; /** @private Whether or not we are currently loading data */ loading = false; /** @private The connection chart data */ connectionChartData: ChartResult[] = []; /** @private The bandwidth chart data */ bwChartData: BandwidthChartResult[] = []; /** @private The configuration for the bandwidth chart */ readonly bwChartConfig = DefaultBandwidthChartConfig; /** @private The list of "pro-tips" that are defined in the template. Only one pro-tip will be rendered depending on proTipIdx */ @ViewChildren('proTip', { read: TemplateRef }) proTips!: QueryList> /** @private The index of the pro-tip that is currently rendered. */ proTipIdx = 0; /** @private The last time the connections were loaded */ lastReload: Date = new Date(); /** @private Used to refresh the "Last reload xxx ago" message */ private lastReloadTickerForceUpdate$ = new Subject(); lastReloadTicker = merge( interval(2000), this.lastReloadTickerForceUpdate$.pipe(takeUntilDestroyed(this.destroyRef)) ) .pipe( takeUntilDestroyed(this.destroyRef), map(() => Math.floor((new Date()).getTime() - this.lastReload.getTime()) / 1000), share() ) /** Auto-reload: The list of all intervals */ readonly reloadIntervals = Object.keys(reloadIntervalValues); /** Auto-reload: The name of the currently selected auto-reload interval */ autoReloadIntervalName: string = ''; /** Auto-reload: The timestamp of auto-reload being enabled */ autoReloadEnabledTimestamp: Date | null = null; /** Auto-reload: Enable/disable auto-reload and set the interval */ onAutoRefreshChange(intervalName: string) { const delaySec = reloadIntervalValues[intervalName] || 0; if (delaySec <= 0) { this.autoReloadIntervalName = ''; return; } this.autoReloadEnabledTimestamp = new Date(); this.autoReloadIntervalName = intervalName; } /** Auto-reload: An observable that emits the remaining seconds until the next reload, and triggers reloads*/ autoReloadInterval$ = interval(900) // use less than 1 second to prevent skipping a second .pipe( startWith(0), // Emit immediately when subscribed takeUntilDestroyed(this.destroyRef), filter(() => !!this.autoReloadIntervalName), // Only emit when auto-reload is enabled map(() => { if (this.loading) return 0; if (this.dateFilter?.length >= 2 && this.dateFilter[1] && this.dateFilter[1].getTime() <= this.lastReload.getTime()) { // Skip reload when dateFilter[1] (end date) <= lastReload (no new results expected) return 0; } const intervalSeconds = reloadIntervalValues[this.autoReloadIntervalName] || 0; if (intervalSeconds <= 0) return 0; const startTime = (this.autoReloadEnabledTimestamp && this.autoReloadEnabledTimestamp > this.lastReload) ? this.autoReloadEnabledTimestamp : this.lastReload; const elapsedSeconds = Math.floor((new Date().getTime() - startTime.getTime()) / 1000); const remainingSeconds = intervalSeconds - elapsedSeconds; if (remainingSeconds <= 0) { this.performSearch(); // Trigger reload when time is up return 0; } return remainingSeconds; }), share() ); // whether or not the history database should be queried as well. get useHistory() { return this.dateFilter?.length; } private get databases(): Database[] { if (!this.useHistory) { return [Database.Live]; } return [Database.Live, Database.History]; } // whether or not the current use has the history feature available. canUseHistory$ = inject(SPNService).profile$ .pipe( map(profile => { if (!profile) { return false; } return profile.current_plan?.feature_ids?.includes(FeatureID.History) || false; }) ); featureBw$ = inject(SPNService).profile$ .pipe( map(profile => { if (!profile) { return false; } return profile.current_plan?.feature_ids?.includes(FeatureID.Bandwidth) || false; }) ); trackPageItem: TrackByFunction = (_, r) => { if (this.groupByKeys?.length) { return this.groupByKeys.map(key => r[key]).join('-') } return r.id! } trackConnection: TrackByFunction = (_, c) => c.id constructor( private netquery: Netquery, private helper: NetqueryHelper, private expertise: ExpertiseService, private cdr: ChangeDetectorRef, private actionIndicator: ActionIndicatorService, private route: ActivatedRoute, public router: Router, ) { } @Input() set filters(v: any | keyof this['models'] | (keyof this['models'])[]) { v = coerceArray(v); objKeys(this.models).forEach(key => { // ignore any models that are marked as being shown in the combined-menu. if (this.models[key]?.visible !== 'combinedMenu') { this.models[key]!.visible = false; } }) v.forEach((val: any) => { if (typeof val !== 'string') { throw new Error("invalid value for @Input() filters") } if (!this.isValidFilter(val)) { throw new Error('invalid filter key ' + val) } this.models[val]!.visible = true; }) } /** * mergeFilter input can be used to apply an additional filter condition that cannot be modified by * the user (like forcing a "profile" filter for the App View) */ @Input() mergeFilter: Condition | null = null; /** The filter preset that will be used if no filter is configured otherwise */ @Input() filterPreset: string | null = null; @Output() filterChange: EventEmitter = new EventEmitter(); /** @private Holds the value displayed in the tag-bar */ tagbarValues: SfngTagbarValue[] = []; private updateDateRangeState() { const values = [ this.models.from.searchValues[0], this.models.to.searchValues[0], ] let fromValueTs = Date.parse(values[0]) let toValueTs = Date.parse(values[1]) // if we failed to parse the date from a string, the user might // just entered the timestamp in seconds if (isNaN(fromValueTs)) { fromValueTs = Number(values[0]) * 1000 } if (isNaN(toValueTs)) { toValueTs = Number(values[1]) * 1000 } const fromValid = !isNaN(fromValueTs) const toValid = !isNaN(toValueTs) let fromValue = new Date(fromValueTs) let toValue = new Date(toValueTs); if (fromValid && toValid && fromValue.getTime() === toValue.getTime()) { fromValue = new Date(fromValue.getFullYear(), fromValue.getMonth(), fromValue.getDate(), 0, 0, 0) toValue = new Date(toValue.getFullYear(), toValue.getMonth(), toValue.getDate() + 1, 0, 0, -1) } this.dateFilter = []; if (fromValid) { this.dateFilter.push(fromValue) this.models.from.searchValues = [ formatDate(fromValue, 'medium', this.localeId) ] } if (toValid) { if (!fromValid) { this.dateFilter.push(new Date(2000, 0, 1)) } this.dateFilter.push(toValue) this.models.to.searchValues = [ formatDate(toValue, 'medium', this.localeId) ] } this.cdr.markForCheck(); } private getDateRangeCondition(): Condition | null { this.updateDateRangeState() if (!this.dateFilter.length) { return null } const cond: GreaterOrEqual & Partial = { $ge: Math.floor(this.dateFilter[0].getTime() / 1000), } if (this.dateFilter.length >= 2) { cond['$le'] = Math.floor(this.dateFilter[1].getTime() / 1000) } return { started: cond } } models: { [key: string]: Model } = initializeModels({ domain: { visible: true, }, as_owner: { visible: true, }, country: { visible: true, }, profile: { visible: true }, allowed: { visible: true, }, path: {}, internal: {}, type: {}, encrypted: {}, scope: { visible: 'combinedMenu', menuTitle: 'Network Scope', suggestions: objKeys(IPScopeNames) .sort() .filter(key => key !== IPScope.Undefined) .map(scope => { return { Name: IPScopeNames[scope], Value: scope, count: 0, Description: '' } }) }, verdict: {}, started: {}, ended: {}, profile_revision: {}, remote_ip: {}, remote_port: {}, local_ip: {}, local_port: {}, ip_protocol: {}, direction: { visible: 'combinedMenu', menuTitle: 'Direction', suggestions: [ { Name: 'Inbound', Value: 'inbound', Description: '', count: 0, }, { Name: 'Outbound', Value: 'outbound', Description: '', count: 0, } ] }, exit_node: {}, asn: {}, active: { visible: 'combinedMenu', menuTitle: 'Active', suggestions: booleanSuggestionValues(), }, tunneled: { visible: 'combinedMenu', menuTitle: 'SPN', suggestions: booleanSuggestionValues(), tipupKey: 'spn' }, from: { virtual: true }, to: { virtual: true, }, }) /** Translations for the connection field names */ keyTranslation = connectionFieldTranslation; /** A list of keys for group-by */ groupByKeys: string[] = []; /** A list of keys for sorting */ orderByKeys: string[] = []; ngOnInit(): void { // Prepare the datasource that is used to initialize the DynamicItemPaginator. // It basically has a "view" function that executes the current page query // but with page-number and page-size applied. // This is used by the paginator to support lazy-loading the different // result pages. const dataSource: Datasource = { view: (page: number, pageSize: number) => { const query = this.getQuery(); query.page = page - 1; // UI starts at page 1 while the backend is 0-based query.pageSize = pageSize; return this.netquery.query(query, 'netquery-viewer') .pipe( this.helper.attachProfile(), this.helper.attachPins(), map(results => { return (results || []).map(r => { const grpFilter: Condition = { ...query.query, }; this.groupByKeys.forEach(key => { grpFilter[key] = r[key]; }) let page = { ...r, _chart: !!this.groupByKeys.length ? this.getGroupChart(grpFilter) : null, _group: !!this.groupByKeys.length ? this.lazyLoadGroup(grpFilter) : null, } return page; }); }) ); } } // create a new paginator that will use the datasource from above. this.paginator = new DynamicItemsPaginator(dataSource) // subscribe to the search observable that emits a value each time we want to perform // a new query. // The actual searching is debounced by second so we don't flood the Portmaster service // with queries while the user is still configuring their filters. this.search$ .pipe( this.adaptiveDebounce(1000, () => this.lastReload.getTime()), switchMap(() => { this.loading = true; this.connectionChartData = []; this.bwChartData = []; this.cdr.detectChanges(); const query = this.getQuery(); // we only load the overall connection chart, the total connection count for the filter result // as well the the total connection count without any filters here. The actual results are // loaded by the DynamicItemsPaginator using the "view" function defined above. return forkJoin({ query: of(query), response: this.netquery.batch({ totalCount: { ...query, select: { $count: { field: '*', as: 'totalCount' } }, }, totalConnCount: { ...query, select: { $count: { field: '*', as: 'totalConnCount' } }, } }) .pipe( map(response => { // the the correct resulsts here which depend on whether or not // we're applying a group by. let totalCount = 0; if (response?.totalCount) { if (this.groupByKeys.length === 0 ) { totalCount = response.totalCount[0].totalCount; } else { totalCount = response.totalCount.length; } } return { totalCount, totalConnCount: response?.totalConnCount || [], } }) ), }) }), ) .subscribe(result => { this.paginator.pageLoading$ .pipe( skip(1), takeUntil(this.search$), // skip loading the chart if the user trigger a subsequent search filter(loading => !loading), take(1), switchMap(() => forkJoin({ connectionChart: this.netquery.activeConnectionChart(result.query.query!) .pipe( catchError(err => { this.actionIndicator.error( 'Internal Error', 'Failed to load chart: ' + this.actionIndicator.getErrorMessgae(err) ); return of([] as ChartResult[]); }), ), bwChart: this.netquery.bandwidthChart(result.query.query!, [], 60) })), ) .subscribe(chart => { this.connectionChartData = chart.connectionChart; this.bwChartData = chart.bwChart; this.cdr.markForCheck(); }) // reset the paginator with the new total result count and // open the first page. this.paginator.reset(result.response.totalCount); this.totalConnCount = result.response?.totalConnCount[0]?.totalConnCount || 0; this.totalResultCount = result.response.totalCount; // update the current URL to include the new search // query and make sure we skip the parameter-update emitted by // router. if (!this.skipUrlUpdate) { this.skipNextRouteUpdate = true; const queryText = this.getQueryString(); this.filterChange.next(queryText); // note that since we only update the query parameters and stay on // the current route this component will not get re-created but will // rather receive an update on the queryParamMap (see below). this.router.navigate([], { relativeTo: this.route, queryParams: { ...this.route.snapshot.queryParams, q: queryText, }, }) } this.skipUrlUpdate = false; this.lastReload = new Date(); this.lastReloadTickerForceUpdate$.next(); this.loading = false; this.cdr.markForCheck(); }) // subscribe to router updates so we apply the filter that is part of // the current query parameter "q". // We might ignore updates here depending on the value of "skipNextRouterUpdate". // This is required as we keep the route parameters in sync with the current filter. this.route.queryParamMap .pipe( takeUntilDestroyed(this.destroyRef), ) .subscribe(params => { if (this.skipNextRouteUpdate) { this.skipNextRouteUpdate = false; return; } const query = params.get("q") if (query !== null) { objKeys(this.models).forEach(key => { this.models[key]!.searchValues = []; }) const result = Parser.parse(query!) this.onFieldsParsed({ ...result.conditions, groupBy: result.groupBy, orderBy: result.orderBy, }); this.textSearch = result.textQuery; } this.skipUrlUpdate = true; this.performSearch(); }) // we might get new search values from our helper service // in case the user "SHIFT-Clicks" a SfngAddToFilter directive. this.helper.onFieldsAdded() .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(fields => this.onFieldsParsed(fields)) // updateTagBar$ always emits a value when we need to update the current tag-bar values. // This must always be done if the current search filter has been modified in either of // the supported ways. this.updateTagBar$ .pipe( takeUntilDestroyed(this.destroyRef), switchMap(() => { const obs: Observable<{ [key: string]: (PossilbeValue & QueryResult)[] }>[] = []; // for the tag bar we try to show some pretty names for values that are meant to be // internal (like the number-constants for the verdicts or using the profile name instead // of the profile ID). Since we might need to load data from the Portmaster for this (like // for profile names) we construct a list of observables using helper.encodeToPossibleValues // and use the result for the tagbar. Object.keys(this.models) .sort() // make sure we always output values in a constant order .forEach(modelKey => { const values = this.models[modelKey]!.searchValues; if (values.length > 0) { obs.push( of(values.map(val => ({ [modelKey]: val, }))) .pipe( this.helper.encodeToPossibleValues(modelKey), map(result => ({ [modelKey]: result, })) ) ) } }) if (obs.length === 0) { return of([]); } return combineLatest(obs); }) ) .subscribe(tagBarValues => { this.tagbarValues = []; // reset the "selected" field of each model that is shown in the "combinedMenu". // we'll set the correct ones as "selected" again in the next step. objKeys(this.models).forEach(key => { if (this.models[key]?.visible === 'combinedMenu') { this.models[key]?.suggestions.forEach(val => val.selected = false); } }) // finally construct a new list of tag-bar values and update the "selected" field of // suggested-values for the "combinedMenu" items based on the actual search values. tagBarValues.forEach(obj => { objKeys(obj).forEach(key => { if (obj[key].length > 0) { this.tagbarValues.push({ key: key as string, values: obj[key], }) // update the `selected` field of suggested-values for each model that is displayed in the combined-menu const modelsKey = key as keyof NetqueryConnection; if (this.models[modelsKey]?.visible === 'combinedMenu') this.models[modelsKey]?.suggestions.forEach(suggestedValue => { suggestedValue.selected = obj[key].some(val => val.Value === suggestedValue.Value); }) } }) }) this.cdr.markForCheck(); }) // handle any filter preset // if (!!this.filterPreset) { try { const result = Parser.parse(this.filterPreset); this.onFieldsParsed({ ...result.conditions, groupBy: result.groupBy, orderBy: result.orderBy, }); } catch (err) { // only log the error in dev mode as this is most likely // just bad user input if (isDevMode()) { console.error(err); } } } } ngAfterViewInit(): void { // once we are initialized decide which pro-tip we want to show this time... this.proTipIdx = Math.floor(Math.random() * this.proTips.length); } ngOnDestroy() { this.paginator.clear(); this.search$.complete(); this.helper.dispose(); } /** * Delays emissions only when last operation was recent. * * @param minDelayMs - Minimum milliseconds between operations * @param getLastOperationTime - Function returning last operation timestamp * * @example * // Delay search only if last search was < 1 second ago * this.search$.pipe(adaptiveDebounce(1000, () => this.lastReload.getTime())) */ adaptiveDebounce( minDelayMs: number, getLastOperationTime: () => number) { return (source: Observable) => source.pipe( switchMap((value) => { const timeSinceLastOperation = Date.now() - getLastOperationTime(); if (timeSinceLastOperation >= minDelayMs) { return of(value); // Execute immediately } else { const remainingDelay = minDelayMs - timeSinceLastOperation; return timer(remainingDelay).pipe(map(() => value)); } }) ); } // lazyLoadGroup returns an observable that will emit a DynamicItemsPaginator once subscribed. // This is used in "group-by" views to lazy-load the content of the group once the user // expands it. lazyLoadGroup(groupFilter: Condition): Observable> { return new Observable(observer => { this.netquery.query({ query: groupFilter, select: [ { $count: { field: "*", as: "totalCount" } } ], orderBy: [ { field: 'started', desc: true }, { field: 'ended', desc: true } ], databases: this.databases, }, 'netquery-viewer-load-group') .subscribe(result => { const paginator = new DynamicItemsPaginator({ view: (pageNumber: number, pageSize: number) => { return this.netquery.query({ query: groupFilter, orderBy: [ { field: 'started', desc: true }, { field: 'ended', desc: true } ], page: pageNumber - 1, pageSize: pageSize, databases: this.databases, }, 'netquery-viewer-group-paginator') as Observable; } }, 25) paginator.reset(result[0]?.totalCount || 0) observer.next(paginator) }) }) } // Returns an observable that loads the current active connection chart using the // current page query but only for the condition of the displayed group. getGroupChart(groupFilter: Condition): Observable { return this.netquery.activeConnectionChart(groupFilter) } // loadSuggestion loads possible values for a given connection field // and updates the "suggestions" field of the correct models entry. // It also uses helper.encodeToPossibleValues to make sure we show // pretty names for otherwise "internal" values like verdict constants // or profile IDs. loadSuggestion(field: string): void; loadSuggestion(field: T) { const search = this.getQuery([field]); this.models[field]!.loading = !this.models[field]!.suggestions?.length; this.netquery.query({ select: [ field, { $count: { field: "*", as: "count" }, } ], query: search.query, groupBy: [ field, ], orderBy: [{ field: "count", desc: true }, { field, desc: true }], databases: this.databases, }, 'netquery-viewer-load-suggestions') .pipe(this.helper.encodeToPossibleValues(field)) .subscribe(result => { this.models[field]!.loading = false; // create a set that we can use to lookup if a value // is currently selected. // This is needed to ensure selected values are sorted to the top. let currentlySelected = new Set(); this.models[field]!.searchValues.forEach( val => currentlySelected.add(val) ); this.models[field]!.suggestions = result .sort((a, b) => { const hasA = currentlySelected.has(a.Value); const hasB = currentlySelected.has(b.Value); if (hasA && !hasB) { return -1; } if (hasB && !hasA) { return 1; } return b.count - a.count; }) as any; this.cdr.markForCheck(); }) } sortByCount(a: SelectOption, b: SelectOption) { return b.data - a.data } /** @private Callback for keyboard events on the search-input */ onFieldsParsed(fields: SfngSearchbarFields, replace = false) { const allowedKeys = new Set(Object.keys(this.models)) objKeys(fields).forEach(key => { if (key === 'groupBy') { this.groupByKeys = (fields.groupBy || this.groupByKeys) .filter(val => { // an empty value is just filtered out without an error as this is the only // way to specify "I don't want grouping" via the filter if (val === '') { return false; } if (!allowedKeys.has(val as any)) { this.actionIndicator.error("Invalid search query", "Column " + val + " is not allowed for groupby") return false; } return true; }) return; } if (key === 'orderBy') { this.orderByKeys = (fields.orderBy || this.orderByKeys) .filter(val => { if (!allowedKeys.has(val as any)) { this.actionIndicator.error("Invalid search query", "Column " + val + " is not allowed for orderby") return false; } return true; }) return; } if (!allowedKeys.has(key)) { this.actionIndicator.error("Invalid search query", "Column " + key + " is not allowed for filtering"); return; } if (fields[key]?.length === 0 && replace) { this.models[key].searchValues = []; } else { fields[key]!.forEach(val => { // quick fix to make sure domains always end in a period. if (key === 'domain' && typeof val === 'string' && val.length > 0 && !val.endsWith('.')) { val = `${val}.` } if (typeof val === 'object' && '$ne' in val) { this.actionIndicator.error("NOT conditions are not yet supported") return; } // avoid duplicates if (this.models[key]!.searchValues.includes(val)) { return; } if (!replace) { this.models[key]!.searchValues = [ ...this.models[key]!.searchValues, val, ] } else { this.models[key]!.searchValues = [val] } }) } this.updateDateRangeState() }) this.cdr.markForCheck(); this.performSearch(); } /** @private Query the portmaster service for connections matching the current settings */ performSearch() { this.loading = true; this.paginator.clear() this.search$.next(); this.updateTagbarValues(); } /** @private Returns the current query in it's string representation */ getQueryString(): string { let result = ''; objKeys(this.models).forEach(key => { this.models[key]?.searchValues.forEach(val => { // we use JSON.stringify here to make sure the value is // correclty quoted. result += `${key}:${JSON.stringify(val)} `; }) }) if (result.length > 0 && this.textSearch.length > 0) { result += ' ' } this.groupByKeys.forEach(key => { result += `groupby:"${key}" ` }) this.orderByKeys.forEach(key => { result += `orderby:"${key}" ` }) if (result.length > 0 && this.textSearch.length > 0) { result += ' ' } result += `${this.textSearch}` return result; } /** @private Copies the current query into the user clipboard */ copyQuery() { this.integration.writeToClipboard(this.getQueryString()) .then(() => { this.actionIndicator.success("Query copied to clipboard", 'Go ahead and share your query!') }) .catch((err) => { this.actionIndicator.error('Failed to copy to clipboard', this.actionIndicator.getErrorMessgae(err)) }) } /** @private Clears the current query */ clearQuery() { objKeys(this.models).forEach(key => { this.models[key]!.searchValues = []; }) this.textSearch = ''; this.updateTagbarValues(); this.performSearch(); } /** @private Constructs a query from the current page settings. Supports excluding certain fields from the query. */ getQuery(excludeFields: string[] = []): Query { let query: Condition = {} let textSearch: Query['textSearch']; const dateQuery = this.getDateRangeCondition() if (dateQuery !== null) { query = mergeConditions(query, dateQuery) } // create the query conditions for all keys on this.models Object.keys(this.models).forEach((key: string) => { if (excludeFields.includes(key)) { return; } if (this.models[key]!.searchValues.length > 0) { // check if model is virtual, and if, skip adding it to the query if (this.models[key].virtual) { return } query[key] = { $in: this.models[key]!.searchValues, } } }) if (this.expertise.currentLevel !== 'developer') { query["internal"] = { $eq: false, } } if (this.textSearch !== '') { textSearch = { fields: freeTextSearchFields, value: this.textSearch } } let select: Query['select'] | undefined = undefined; if (!!this.groupByKeys.length) { // we always want to show the total and the number of allowed connections // per group so we need to add those to the select part of the query select = [ { $count: { field: "*", as: "totalCount", }, }, { $sum: { condition: { verdict: { $in: [ Verdict.Accept, Verdict.RerouteToNs, Verdict.RerouteToTunnel ], } }, as: "countAllowed" } }, ...this.groupByKeys, ] } let normalizedQuery = mergeConditions(query, this.mergeFilter || {}) let orderBy: string[] | OrderBy[] = this.orderByKeys; if (!orderBy || orderBy.length === 0) { orderBy = [ { field: 'started', desc: true, }, { field: 'ended', desc: true, } ] } return { select: select, query: normalizedQuery, groupBy: this.groupByKeys, orderBy: orderBy, textSearch, databases: this.databases, } } /** @private Updates the current model form all values emited by the tag-bar. */ onTagbarChange(tagKinds: SfngTagbarValue[]) { objKeys(this.models).forEach(key => { this.models[key]!.searchValues = []; }); tagKinds.forEach(kind => { const key = kind.key as keyof NetqueryConnection; this.models[key]!.searchValues = kind.values.map(possibleValue => possibleValue.Value); if (this.models[key]?.visible === 'combinedMenu') this.models[key]?.suggestions.forEach(val => { val.selected = this.models[key]!.searchValues.find(searchValue => searchValue === val.Value) }) }) this.updateDateRangeState(); this.performSearch(); } onDateRangeChange(event: Date[]) { if (event.length >= 1) { event[0] = new Date(event[0].getFullYear(), event[0].getMonth(), event[0].getDate(), 0, 0, 0) this.onFieldsParsed({ from: [formatDate(event[0], 'medium', this.localeId)] }, true) } else { this.onFieldsParsed({ from: [] }, true) } if (event.length >= 2) { event[1] = new Date(event[1].getFullYear(), event[1].getMonth(), event[1].getDate() + 1, 0, 0, -1) this.onFieldsParsed({ to: [formatDate(event[1], 'medium', this.localeId)] }, true) } else { this.onFieldsParsed({ to: [] }, true) } } /** Updates the {@link tagbarValues} from {@link models}*/ private updateTagbarValues() { this.updateTagBar$.next(); } private isValidFilter(key: string): key is keyof NetqueryConnection { return Object.keys(this.models).includes(key); } useAsFilter(rec: QueryResult) { const keys = new Set(objKeys(this.models)) // reset the search values keys.forEach(key => { this.models[key]!.searchValues = []; }) objKeys(rec).forEach(key => { if (keys.has(key as keyof NetqueryConnection)) { this.models[key as keyof NetqueryConnection]!.searchValues = [rec[key]]; } }) // reset the group-by-keys since they don't make any sense anymore. this.groupByKeys = []; this.performSearch(); } /** @private - used by the combined filter menu */ toggleCombinedMenuFilter(key: string, value: Suggestion) { const k = key as keyof NetqueryConnection; if (value.selected) { this.models[k]!.searchValues = this.models[k]?.searchValues.filter(val => val !== value.Value) || []; } else { this.models[k]!.searchValues.push(value.Value) } this.updateTagbarValues(); this.performSearch(); } trackSuggestion: TrackByFunction = (_: number, s: Suggestion) => s.Name + '::' + s.Value; } function initializeModels(models: { [key: string]: Partial> }): { [key: string]: Model } { objKeys(models).forEach(key => { models[key] = { suggestions: [], searchValues: [], visible: false, loading: false, ...models[key], } }) return models as any; } function booleanSuggestionValues(): Suggestion[] { return [ { Name: 'Yes', Value: true, Description: '', count: 0, }, { Name: 'No', Value: false, Description: '', count: 0, }, ] } ================================================ FILE: desktop/angular/src/app/shared/netquery/netquery.module.ts ================================================ import { A11yModule } from "@angular/cdk/a11y"; import { OverlayModule } from "@angular/cdk/overlay"; import { CommonModule } from "@angular/common"; import { inject, NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { FontAwesomeModule } from "@fortawesome/angular-fontawesome"; import { SfngAccordionModule, SfngDropDownModule, SfngPaginationModule, SfngSelectModule, SfngTipUpModule, SfngToggleSwitchModule, SfngTooltipModule } from "@safing/ui"; import { NzDatePickerModule } from 'ng-zorro-antd/date-picker'; import { SfngAppIconModule } from "../app-icon"; import { CountIndicatorModule } from "../count-indicator"; import { CountryFlagModule } from "../country-flag"; import { ExpertiseModule } from "../expertise/expertise.module"; import { SfngFocusModule } from "../focus"; import { SfngMenuModule } from "../menu"; import { CommonPipesModule } from "../pipes"; import { SPNModule } from './../../pages/spn/spn.module'; import { SfngNetqueryAddToFilterDirective } from "./add-to-filter"; import { CombinedMenuPipe } from "./combined-menu.pipe"; import { SfngNetqueryConnectionDetailsComponent } from "./connection-details"; import { SfngNetqueryConnectionRowComponent } from "./connection-row"; import { SfngNetqueryLineChartComponent } from "./line-chart/line-chart"; import { SfngNetqueryViewer } from "./netquery.component"; import { CanShowConnection, CanUseRulesPipe, ConnectionLocationPipe, CountryNamePipe, CountryNameService, IsBlockedConnectionPipe } from "./pipes"; import { SfngNetqueryScopeLabelComponent } from "./scope-label"; import { SfngNetquerySearchOverlayComponent } from "./search-overlay"; import { SfngNetquerySearchbarComponent, SfngNetquerySuggestionDirective } from "./searchbar"; import { SfngNetqueryTagbarComponent } from "./tag-bar"; import { CircularBarChartComponent } from './circular-bar-chart/circular-bar-chart.component'; @NgModule({ imports: [ CommonModule, FormsModule, CountryFlagModule, SfngDropDownModule, SfngSelectModule, SfngTooltipModule, SfngAccordionModule, SfngMenuModule, SfngPaginationModule, SfngFocusModule, SfngAppIconModule, SfngTipUpModule, SfngToggleSwitchModule, A11yModule, ExpertiseModule, OverlayModule, CountIndicatorModule, FontAwesomeModule, CommonPipesModule, SPNModule, NzDatePickerModule, ], exports: [ SfngNetqueryViewer, SfngNetqueryLineChartComponent, SfngNetquerySearchOverlayComponent, SfngNetqueryScopeLabelComponent, CircularBarChartComponent, ], declarations: [ SfngNetqueryViewer, SfngNetqueryConnectionRowComponent, SfngNetqueryLineChartComponent, SfngNetqueryTagbarComponent, SfngNetquerySearchbarComponent, SfngNetquerySearchOverlayComponent, SfngNetquerySuggestionDirective, SfngNetqueryScopeLabelComponent, SfngNetqueryConnectionDetailsComponent, SfngNetqueryAddToFilterDirective, ConnectionLocationPipe, IsBlockedConnectionPipe, CanUseRulesPipe, CanShowConnection, CombinedMenuPipe, CircularBarChartComponent, CountryNamePipe, ], providers: [ CountryNameService ] }) export class NetqueryModule { private _unusedBootstrap = [ inject(CountryNameService), // make sure country names are loaded on bootstrap ] } ================================================ FILE: desktop/angular/src/app/shared/netquery/pipes/can-show.pipe.ts ================================================ import { Pipe, PipeTransform } from "@angular/core"; import { ExpertiseLevel, NetqueryConnection } from "@safing/portmaster-api"; @Pipe({ name: "canShowConnection", pure: true, }) export class CanShowConnection implements PipeTransform { transform(conn: NetqueryConnection, level: ExpertiseLevel) { if (!conn) { return false; } if (level === ExpertiseLevel.Developer) { // we show all connections for developers return true; } // if we are in advanced or simple mode we should // hide internal connections. return !conn.internal; } } ================================================ FILE: desktop/angular/src/app/shared/netquery/pipes/can-use-rules.pipe.ts ================================================ // the following settings are stronger than rules // and cannot be "fixed" by creating a new allow/deny import { Pipe, PipeTransform } from "@angular/core"; import { IsDenied, NetqueryConnection } from "@safing/portmaster-api"; // rule. let optionKeys = new Set([ "filter/blockInternet", "filter/blockLAN", "filter/blockLocal", "filter/blockP2P", "filter/blockInbound" ]) @Pipe({ name: "canUseRules", pure: true, }) export class CanUseRulesPipe implements PipeTransform { transform(conn: NetqueryConnection): boolean { if (!conn) { return false; } if (!!conn.extra_data?.reason?.OptionKey && IsDenied(conn.verdict)) { return !optionKeys.has(conn.extra_data.reason.OptionKey); } return true; } } ================================================ FILE: desktop/angular/src/app/shared/netquery/pipes/country-name.pipe.ts ================================================ import { HttpClient } from '@angular/common/http'; import { Pipe, PipeTransform, Injectable, inject } from '@angular/core'; import { GeoCoordinates, SPNService } from '@safing/portmaster-api'; import { environment } from 'src/environments/environment'; import { ActionIndicatorService } from '../../action-indicator'; import { objKeys } from '../../utils'; export interface CountryListResponse { [countryKey: string]: { Code: string; Name: string; Center: GeoCoordinates; Continent: { Code: string; Region: string; Name: string; } } } @Injectable() export class CountryNameService { private readonly spn = inject(SPNService); private readonly http = inject(HttpClient); private readonly uai = inject(ActionIndicatorService); private map: Map = new Map(); constructor() { this.http.get(`${environment.httpAPI}/v1/intel/geoip/countries`) .subscribe({ next: response => { objKeys(response) .forEach(key => { this.map.set(key as string, response[key].Name); }); }, error: err => { this.uai.error('Failed to fetch country data', this.uai.getErrorMessage(err)); } }) } resolveName(code: string): string { return this.map.get(code) || ''; } } @Pipe({ name: 'countryName', pure: true, }) export class CountryNamePipe implements PipeTransform { private countryService = inject(CountryNameService); transform(countryCode: string) { return this.countryService.resolveName(countryCode); } } ================================================ FILE: desktop/angular/src/app/shared/netquery/pipes/index.ts ================================================ export * from './location.pipe'; export * from './can-show.pipe'; export * from './can-use-rules.pipe'; export * from './is-blocked.pipe'; export * from './country-name.pipe'; ================================================ FILE: desktop/angular/src/app/shared/netquery/pipes/is-blocked.pipe.ts ================================================ import { Pipe, PipeTransform } from '@angular/core'; import { IsDenied, NetqueryConnection } from '@safing/portmaster-api'; @Pipe({ name: "isBlocked", pure: true }) export class IsBlockedConnectionPipe implements PipeTransform { transform(conn: NetqueryConnection): boolean { return IsDenied(conn?.verdict); } } ================================================ FILE: desktop/angular/src/app/shared/netquery/pipes/location.pipe.ts ================================================ import { Pipe, PipeTransform } from '@angular/core'; import { IsGlobalScope, IsLANScope, IsLocalhost, NetqueryConnection } from '@safing/portmaster-api'; @Pipe({ name: 'connectionLocation', pure: true, }) export class ConnectionLocationPipe implements PipeTransform { transform(conn: NetqueryConnection): string { if (conn.type === 'dns') { return ''; } if (!!conn.country) { if (conn.country === "__") { return "Anycast" } return conn.country; } const scope = conn.scope; if (IsGlobalScope(scope)) { return 'Internet' } if (IsLANScope(scope)) { return 'LAN'; } if (IsLocalhost(scope)) { return 'Device' } return ''; } } ================================================ FILE: desktop/angular/src/app/shared/netquery/scope-label/index.ts ================================================ export * from './scope-label'; ================================================ FILE: desktop/angular/src/app/shared/netquery/scope-label/scope-label.html ================================================ {{subdomain}}. {{domain}} {{ scopeTranslation[scope || ''] || 'N/A' }} ================================================ FILE: desktop/angular/src/app/shared/netquery/scope-label/scope-label.ts ================================================ import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { ScopeTranslation } from '@safing/portmaster-api'; import { parseDomain } from '../../utils'; @Component({ selector: 'sfng-netquery-scope-label', templateUrl: 'scope-label.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class SfngNetqueryScopeLabelComponent implements OnChanges { readonly scopeTranslation = ScopeTranslation; @Input() scope?: string = '' @Input() set leftRightFix(v: any) { console.warn("deprecated @Input usage") } get leftRightFix() { return false } domain: string = ''; subdomain: string = ''; ngOnChanges(change: SimpleChanges) { if (!!change['scope']) { //this.label = change.label.currentValue; const result = parseDomain(change.scope.currentValue || '') this.domain = result?.domain || ''; this.subdomain = result?.subdomain || ''; } } } ================================================ FILE: desktop/angular/src/app/shared/netquery/search-overlay/index.ts ================================================ export * from './search-overlay'; ================================================ FILE: desktop/angular/src/app/shared/netquery/search-overlay/search-overlay.html ================================================ ================================================ FILE: desktop/angular/src/app/shared/netquery/search-overlay/search-overlay.ts ================================================ import { ChangeDetectionStrategy, Component, Inject } from "@angular/core"; import { Router } from "@angular/router"; import { SfngDialogRef, SFNG_DIALOG_REF } from "@safing/ui"; import { objKeys } from "../../utils"; import { NetqueryHelper } from "../connection-helper.service"; import { SfngSearchbarFields } from "../searchbar"; import { connectionFieldTranslation } from "../utils"; @Component({ selector: 'sfng-netquery-search-overlay', templateUrl: './search-overlay.html', changeDetection: ChangeDetectionStrategy.OnPush, providers: [ NetqueryHelper, ], styles: [ ` :host { @apply block; width: 700px; } ::ng-deep sfng-netquery-search-overlay sfng-netquery-searchbar input { border: 1px solid theme("colors.gray.200") !important; } ` ] }) export class SfngNetquerySearchOverlayComponent { keyTranslation = connectionFieldTranslation; textSearch = ''; fields: SfngSearchbarFields = {}; constructor( @Inject(SFNG_DIALOG_REF) private dialogRef: SfngDialogRef, private router: Router, ) { } performSearch() { let query = ""; const fields = objKeys(this.fields) // if there's only one profile key directly navigate the user to the app view if (fields.length === 1 && fields[0] === 'profile' && this.fields.profile!.length === 1) { let profileName: string = this.fields.profile![0] || ''; if (!profileName.includes("/")) { profileName = "local/" + profileName } this.router.navigate(['/app/' + profileName || '']) this.dialogRef.close(); return; } fields.forEach(field => { this.fields[field]?.forEach(value => { query += `${field}:${JSON.stringify(value)} ` }) }) if (query !== '' && this.textSearch !== '') { query += " " } query += this.textSearch; this.router.navigate(['/monitor'], { queryParams: { q: query, } }) this.dialogRef.close(); } onFieldsParsed(fields: SfngSearchbarFields) { objKeys(fields).forEach(field => { this.fields[field] = [...(this.fields[field] || []), ...(fields[field] || [])]; }) } } ================================================ FILE: desktop/angular/src/app/shared/netquery/searchbar/index.ts ================================================ export * from './searchbar'; ================================================ FILE: desktop/angular/src/app/shared/netquery/searchbar/searchbar.html ================================================
  • Full-Text Search: {{ textSearch }}

Filter by {{ labels[sug.field] || sug.field }}

  • {{ val.display || (val.value === '' ? 'N/A' : val.value) }} #{{ val.count }} connections
Loading suggestions ...
There are no suggestions for your query. Press
Enter
to perform a full text search.
================================================ FILE: desktop/angular/src/app/shared/netquery/searchbar/searchbar.ts ================================================ import { ListKeyManager } from "@angular/cdk/a11y"; import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { CdkOverlayOrigin } from "@angular/cdk/overlay"; import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, Directive, ElementRef, EventEmitter, forwardRef, HostBinding, HostListener, inject, Input, OnDestroy, OnInit, Output, QueryList, TrackByFunction, ViewChild, ViewChildren } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; import { Condition, ExpertiseLevel, Netquery, NetqueryConnection } from "@safing/portmaster-api"; import { SfngDropdownComponent } from "@safing/ui"; import { combineLatest, Observable, of, Subject } from "rxjs"; import { catchError, debounceTime, map, switchMap } from "rxjs/operators"; import { fadeInAnimation, fadeInListAnimation } from "../../animations"; import { ExpertiseService } from "../../expertise"; import { objKeys } from "../../utils"; import { NetqueryHelper } from "../connection-helper.service"; import { Parser, ParseResult } from "../textql"; export type SfngSearchbarFields = { [key in keyof Partial]: any[]; } & { groupBy?: string[]; orderBy?: string[]; from?: string[]; to?: string[]; } export type SfngSearchbarSuggestionValue = { value: NetqueryConnection[K]; display: string; count: number; } export type SfngSearchbarSuggestion = { start?: number; field: K | '_textsearch'; values: SfngSearchbarSuggestionValue[]; } @Directive({ selector: '[sfngNetquerySuggestion]', exportAs: 'sfngNetquerySuggestion' }) export class SfngNetquerySuggestionDirective { constructor() { } @Input() sfngSuggestion?: SfngSearchbarSuggestion; @Input() sfngNetquerySuggestion?: SfngSearchbarSuggestionValue | string; set active(v: any) { this._active = coerceBooleanProperty(v); } get active() { return this._active; } private _active: boolean = false; getLabel(): string { if (typeof this.sfngNetquerySuggestion === 'string') { return this.sfngNetquerySuggestion; } return '' + this.sfngNetquerySuggestion?.value; } } @Component({ selector: 'sfng-netquery-searchbar', templateUrl: './searchbar.html', changeDetection: ChangeDetectionStrategy.OnPush, animations: [ fadeInAnimation, fadeInListAnimation ], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SfngNetquerySearchbarComponent), multi: true, } ] }) export class SfngNetquerySearchbarComponent implements ControlValueAccessor, OnInit, OnDestroy, AfterViewInit { private loadSuggestions$ = new Subject(); private triggerDropdownClose$ = new Subject(); private keyManager!: ListKeyManager>; private destroyRef = inject(DestroyRef); /** Whether or not we are currently loading suggestions */ loading = false; @ViewChild(CdkOverlayOrigin, { static: true }) searchBoxOverlayOrigin!: CdkOverlayOrigin; @ViewChild(SfngDropdownComponent) suggestionDropDown?: SfngDropdownComponent; @ViewChild('searchBar', { static: true, read: ElementRef }) searchBar!: ElementRef; @ViewChildren(SfngNetquerySuggestionDirective) suggestionValues!: QueryList>; @Output() fieldsParsed = new EventEmitter(); @Input() labels: { [key: string]: string } = {} /** Controls whether or not suggestions are shown as a drop-down or inline */ @Input() mode: 'inline' | 'default' = 'default'; suggestions: SfngSearchbarSuggestion[] = []; textSearch = ''; @HostListener('focus') onFocus() { // move focus forward to the input element this.searchBar.nativeElement.focus(); } @Input() @HostBinding('tabindex') tabindex = 0; writeValue(val: string): void { if (typeof val === 'string') { const result = Parser.parse(val); this.textSearch = result.textQuery; } else { this.textSearch = ''; } this.cdr.markForCheck(); } _onChange: (val: string) => void = () => { } registerOnChange(fn: any): void { this._onChange = fn; } _onTouched: () => void = () => { } registerOnTouched(fn: any): void { this._onTouched = fn } ngAfterViewInit(): void { this.keyManager = new ListKeyManager(this.suggestionValues) .withVerticalOrientation() .withTypeAhead() .withHomeAndEnd() .withWrap(); this.keyManager.change .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(idx => { if (!this.suggestionValues.length) { return } this.suggestionValues.forEach(val => val.active = false); this.suggestionValues.get(idx)!.active = true; this.cdr.markForCheck(); }); } ngOnInit(): void { this.loadSuggestions$ .pipe( debounceTime(500), switchMap(() => { let fields: (keyof NetqueryConnection)[] = [ 'profile', 'domain', 'as_owner', 'remote_ip', ]; let limit = 3; const parser = new Parser(this.textSearch); const parseResult = parser.process(); const queries: Observable>[] = []; const queryKeys: (keyof Partial)[] = []; // TODO(ppacher): confirm .type is an actually allowed field if (!!parser.lastUnterminatedCondition) { fields = [parser.lastUnterminatedCondition.type as keyof NetqueryConnection]; limit = 0; } fields.forEach(field => { let queryField = field; // if we are searching the profiles we use the profile name // rather than the profile_id for searching. if (field === 'profile') { queryField = 'profile_name'; } const query: Condition = { [queryField]: { $like: `%${!!parser.lastUnterminatedCondition ? parser.lastUnterminatedCondition.value : parseResult.textQuery}%` }, } // hide internal connections if the user is not a developer if (this.expertiseService.currentLevel !== ExpertiseLevel.Developer) { query.internal = { $eq: false } } const obs = this.netquery.query({ select: [ field, { $count: { field: "*", as: "count" }, } ], query: query, groupBy: [ field, ], page: 0, pageSize: limit, orderBy: [{ field: "count", desc: true }] }, 'netquery-searchbar-get-counts') .pipe( this.helper.encodeToPossibleValues(field), map(results => { let val: SfngSearchbarSuggestion = { field: field, values: [], start: parser.lastUnterminatedCondition ? parser.lastUnterminatedCondition.start : undefined, } results.forEach(res => { val.values.push({ value: res.Value, display: res.Name, count: res.count, }) }) return val; }), catchError(err => { console.error(err); return of({ field: field, values: [], }) }) ) queries.push(obs) queryKeys.push(field) }) return combineLatest(queries) }), ) .subscribe(result => { this.loading = false; this.suggestions = result .filter((sug: SfngSearchbarSuggestion) => sug.values?.length > 0) this.keyManager.setActiveItem(0); this.cdr.markForCheck(); }) this.triggerDropdownClose$ .pipe(debounceTime(100)) .subscribe(shouldClose => { if (shouldClose) { this.suggestionDropDown?.close(); } }) if (this.mode === 'inline') { this.loadSuggestions(); } } ngOnDestroy(): void { this.loadSuggestions$.complete(); this.triggerDropdownClose$.complete(); } cancelDropdownClose() { this.triggerDropdownClose$.next(false); } onSearchModelChange(value: string) { if (value.length >= 3 || this.mode === 'inline') { this.loadSuggestions(); } else if (this.suggestionDropDown?.isOpen) { // close the suggestion dropdown if the search input contains less than // 3 characters and we're currently showing the dropdown this.suggestionDropDown?.close(); } } /** @private Callback for keyboard events on the search-input */ onSearchKeyDown(event: KeyboardEvent) { if (event.key === ' ' && event.ctrlKey) { this.loadSuggestions(); event.preventDefault(); event.stopPropagation() return; } if (event.key === 'Enter') { const selectedSuggestion = this.suggestionValues.toArray().findIndex(val => val.active); if (selectedSuggestion > 0) { // we must skip 0 here as well as that's the dummy element const sug = this.suggestionValues.get(selectedSuggestion); this.applySuggestion(sug?.sfngSuggestion?.field, sug?.sfngNetquerySuggestion, event, sug?.sfngSuggestion?.start) return; } this.suggestionDropDown?.close(); this.parseAndEmit(); this.cdr.markForCheck(); return; } this.keyManager.onKeydown(event); } onFocusLost(event: FocusEvent) { this._onTouched(); } private parseAndEmit() { const result = Parser.parse(this.textSearch); this.textSearch = result.textQuery; const keys = objKeys(result.conditions) const meta = { groupBy: result.groupBy || undefined, orderBy: result.orderBy || undefined, } if (keys.length > 0 || meta.groupBy?.length || meta.orderBy?.length) { let updatedConditions: ParseResult['conditions'] = {}; keys.forEach(key => { updatedConditions[key] = this.helper.decodePrettyValues(key as keyof NetqueryConnection, result.conditions[key]) }) this.fieldsParsed.next({ ...updatedConditions, ...meta }); } this._onChange(this.textSearch); } applySuggestion(field: keyof NetqueryConnection | '_textsearch', val: any, event: { shiftKey: boolean }, start?: number) { // this is a full-text search so just emit the value, close the dropdown and we're done if (field === '_textsearch') { this._onChange(this.textSearch); this.suggestionDropDown?.close(); return } if (start !== undefined) { this.textSearch = this.textSearch.slice(0, start) } else if (!event.shiftKey) { this.textSearch = ''; } else { // the user pressed shift-key and used free-text search so we remove // the remaining part const parseRes = Parser.parse(this.textSearch); let query = ""; objKeys(parseRes.conditions).forEach(field => { parseRes.conditions[field]?.forEach(value => { query += `${field}:${JSON.stringify(value)} ` }) }) this.textSearch = query; } if (event.shiftKey) { const textqlVal = `${field}:${JSON.stringify(val)}` if (!this.textSearch.includes(textqlVal)) { if (this.textSearch !== '') { this.textSearch += " " } this.textSearch += textqlVal + " " this.triggerDropdownClose$.next(false) // load new suggestions based on the new input this.loadSuggestions(); } return; } // directly emit the new value and reset the text search this.fieldsParsed.next({ [field]: [val] }) // parse and emit the current search field but without the suggestion value this.parseAndEmit(); this.suggestionDropDown?.close(); this.cdr.markForCheck(); } resetKeyboardSelection() { this.keyManager.setActiveItem(0); } loadSuggestions() { this.loading = true; this.loadSuggestions$.next(); this.suggestionDropDown?.show(this.searchBoxOverlayOrigin) } trackSuggestion: TrackByFunction> = (_: number, val: SfngSearchbarSuggestion) => val.field; constructor( private cdr: ChangeDetectorRef, private expertiseService: ExpertiseService, private netquery: Netquery, private helper: NetqueryHelper, ) { } } ================================================ FILE: desktop/angular/src/app/shared/netquery/tag-bar/index.ts ================================================ export * from './tag-bar'; ================================================ FILE: desktop/angular/src/app/shared/netquery/tag-bar/tag-bar.html ================================================
{{labels[cat.key] || cat.key}}: {{ val.Name || (val.Value === '' ? 'N/A' : val) }}
================================================ FILE: desktop/angular/src/app/shared/netquery/tag-bar/tag-bar.ts ================================================ import { coerceBooleanProperty, coerceCssPixelValue } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, HostBinding, Input } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { PossilbeValue } from '@safing/portmaster-api'; import { fadeInListAnimation } from '../../animations'; import { NetqueryHelper } from '../connection-helper.service'; export interface SfngTagbarValue { key: string; values: PossilbeValue[]; } @Component({ selector: 'sfng-netquery-tagbar', templateUrl: 'tag-bar.html', changeDetection: ChangeDetectionStrategy.OnPush, styles: [ ` :host { @apply flex flex-row gap-3 w-auto items-center text-xxs flex-wrap; } ` ], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SfngNetqueryTagbarComponent), multi: true } ], animations: [ fadeInListAnimation ] }) export class SfngNetqueryTagbarComponent implements ControlValueAccessor { @HostBinding('@fadeInList') get itemsLength() { return this.values?.length || 0; } /** @private the current tag bar values */ values: SfngTagbarValue[] = []; /** Whether or not the user can interact with the component */ @Input() set disabled(v: any) { this.setDisabledState(v) } get disabled() { return this._disabled; } private _disabled = false; /** Translations for the value keys */ @Input() labels: { [key: string]: string } = {} /** The maximum width of the tag text before being truncated using left-side ellipsis */ @Input() set maxTagWidth(width: any) { this._maxTagWidth = coerceCssPixelValue(width) } get maxTagWidth() { return this._maxTagWidth } private _maxTagWidth: string = '8rem' /** @private A {@link TrackByFunction} for {@link SfngTagbarValue} */ trackValue(_: number, vl: SfngTagbarValue) { return vl.key; } /** Implements the {@link ControlValueAccessor} */ writeValue(obj: SfngTagbarValue[]): void { this.values = obj; this.cdr.markForCheck(); } /** Implements the {@link ControlValueAccessor} */ registerOnChange(fn: any): void { this._onChange = fn; } /** @private - callback registered via registerOnChange */ _onChange: (val: SfngTagbarValue[]) => void = () => { } /** Implements the {@link ControlValueAccessor} */ registerOnTouched(fn: any): void { this._onTouched = fn } /** @private - callback registered via registerOnTouched */ _onTouched: () => void = () => { } /** Implements the {@link ControlValueAccessor} */ setDisabledState(v: any) { this._disabled = coerceBooleanProperty(v) this.cdr.markForCheck(); } /** * remove removes the value at index from the {@link SfngTagbarValue} * that matches key. */ remove(key: string, index: number) { if (this.disabled) { return; } console.log(this.values); let cpy: SfngTagbarValue[] = []; this.values.forEach(val => { if (val.key === key) { val.values = [...val.values]; val.values.splice(index, 1) } cpy.push({ ...val, }) }); this.values = cpy; console.log(this.values); this._onChange(this.values); this.cdr.markForCheck(); } constructor( private cdr: ChangeDetectorRef, private helper: NetqueryHelper, ) { } } ================================================ FILE: desktop/angular/src/app/shared/netquery/textql/helper.ts ================================================ import { Token, TokenType } from "./token"; export function isValueToken(tok: Token): tok is Token { return [TokenType.STRING, TokenType.BOOL, TokenType.NUMBER].includes(tok.type) } export function isDigit(x: string): boolean { return /[0-9]+/.test(x); } export function isWhitespace(ch: string): boolean { return /\s/.test(ch) } export function isLetter(ch: string): boolean { return new RegExp('[\/a-zA-Z0-9\._-]').test(ch) } export function isIdentChar(ch: string): boolean { return /[a-zA-Z_]/.test(ch); } ================================================ FILE: desktop/angular/src/app/shared/netquery/textql/index.ts ================================================ export * from './parser'; ================================================ FILE: desktop/angular/src/app/shared/netquery/textql/input.ts ================================================ /** Input stream returns one character at a time */ export class InputStream { private _pos: number = 0; private _line: number = 0; constructor(private _input: string) { } /** Returns the next character and removes it from the stream */ next(): string | null { const ch = this._input.charAt(this._pos++); return ch; } get pos() { return this._pos; } /** Revert moves the current stream position back by `num` characters */ revert(num: number) { this._pos -= num; } /** Returns the next character in the stream but does not remove it */ peek(): string { return this._input.charAt(this._pos); } /** Returns true if we reached the end of the stream */ eof(): boolean { return this.peek() == ''; } get left(): string { return this._input.slice(this._pos) } /** Throws an error with the current line and column */ croak(msg: string): never { throw new Error(`${msg} at ${this._line}:${this.pos}`); } } ================================================ FILE: desktop/angular/src/app/shared/netquery/textql/lexer.ts ================================================ import { isDigit, isIdentChar, isLetter, isWhitespace } from "./helper"; import { InputStream } from "./input"; import { Token, TokenType } from "./token"; export class Lexer { private _current: Token | null = null; private _input: InputStream; constructor(input: string) { this._input = new InputStream(input); } /** peek returns the token at the current position in input. */ public peek(): Token | null { return this._current || (this._current = this.readNextToken()); } /** next returns either the current token in input or reads the next one */ public next(): Token | null { let tok = this._current; this._current = null; return tok || this.readNextToken(); } /** eof returns true if the lexer reached the end of the input stream */ public eof(): boolean { return this.peek() === null; } /** croak throws and error message at the current position in the input stream */ public croak(msg: string): never { return this._input.croak(`${msg}. Current token is "${!!this.peek() ? this.peek()!.literal : null}"`); } /** consumes the input stream as long as predicate returns true */ private readWhile(predicate: (ch: string) => boolean): string { let str = ''; while (!this._input.eof() && predicate(this._input.peek())) { str += this._input.next(); } return str; } /** reads a number token */ private readNumber(): Token | null { const start = this._input.pos; let has_dot = false; let number = this.readWhile((ch: string) => { if (ch === '.') { if (has_dot) { return false; } has_dot = true; return true; } return isDigit(ch); }); if (!this._input.eof() && !isWhitespace(this._input.peek())) { this._input.revert(number.length); return null; } return { type: TokenType.NUMBER, literal: number, value: has_dot ? parseFloat(number) : parseInt(number), start } } private readIdent(): Token { const start = this._input.pos; const id = this.readWhile(ch => isIdentChar(ch)); if (id === 'true' || id === 'yes') { return { type: TokenType.BOOL, literal: id, value: true, start } } if (id === 'false' || id === 'no') { return { type: TokenType.BOOL, literal: id, value: false, start } } if (id === 'groupby') { return { type: TokenType.GROUPBY, literal: id, value: id, start } } if (id === 'orderby') { return { type: TokenType.ORDERBY, literal: id, value: id, start } } return { type: TokenType.IDENT, literal: id, value: id, start }; } private readEscaped(end: string | RegExp, skipStart: boolean): string { let escaped = false; let str = ''; if (skipStart) { this._input.next(); } while (!this._input.eof()) { let ch = this._input.next()!; if (escaped) { str += ch; escaped = false; } else if (ch === '\\') { escaped = true; } else if ((typeof end === 'string' && ch === end) || (end instanceof RegExp && end.test(ch))) { break; } else { str += ch; } } return str; } private readString(quote: string | RegExp, skipStart: boolean): Token { const start = this._input.pos; const value = this.readEscaped(quote, skipStart) return { type: TokenType.STRING, literal: value, value: value, start } } private readWhitespace(): Token { const start = this._input.pos; const value = this.readWhile(ch => isWhitespace(ch)); return { type: TokenType.WHITESPACE, literal: value, value: value, start, } } private readNextToken(): Token | null { const start = this._input.pos; const ch = this._input.peek(); if (ch === '') { return null; } if (isWhitespace(ch)) { return this.readWhitespace() } if (ch === '"') { return this.readString('"', true); } if (ch === '\'') { return this.readString('\'', true); } if (isDigit(ch)) { const number = this.readNumber(); if (number !== null) { return number; } } if (ch === ':') { this._input.next(); return { type: TokenType.COLON, value: ':', literal: ':', start } } if (ch === '!') { this._input.next(); return { type: TokenType.NOT, value: '!', literal: '!', start } } if (isIdentChar(ch)) { const ident = this.readIdent(); const next = this._input.peek(); if (!this._input.eof() && (!isWhitespace(next) && next !== ':')) { // identifiers should always end in a colon or with a whitespace. // if neither is the case we are in the middle of a token and are // likely parsing a string without quotes. this._input.revert(ident.literal.length); // read the string and revert by one as we terminate the string // at the next WHITESPACE token const tok = this.readString(new RegExp('\\s'), false) this.revertWhitespace(); return tok; } return ident; } if (isLetter(ch)) { const tok = this.readString(new RegExp('\\s'), false) // read the string and revert by one as we terminate the string // at the next WHITESPACE token this.revertWhitespace(); return tok } // Failed to handle the input character return this._input.croak(`Can't handle character: ${ch}`); } private revertWhitespace() { this._input.revert(1) if (!isWhitespace(this._input.peek())) { this._input.next(); } } } ================================================ FILE: desktop/angular/src/app/shared/netquery/textql/parser.ts ================================================ import { isDevMode } from '@angular/core'; import { isValueToken, isWhitespace } from './helper'; import { Lexer } from './lexer'; import { Token, TokenType } from './token'; export interface ParseResult { conditions: { [key: string]: (any | { $ne: any })[]; }; textQuery: string; groupBy?: string[]; orderBy?: string[]; } export class Parser { /** The underlying lexer used to tokenize the input */ private lexer: Lexer; /** Holds the parsed conditions */ private conditions: { [key: string]: any[]; } = {}; /** The last condition that has not yet been terminated. Used for scope-based suggestions */ private _lastUnterminatedCondition: { start: number; type: string; value: any; } | null = null; /** A list of remaining strings/identifiers that are not part of a condition */ private remaining: string[] = []; /** Returns the last condition that has not yet been terminated. */ get lastUnterminatedCondition() { return this._lastUnterminatedCondition; } constructor(input: string) { this.lexer = new Lexer(input); } static aliases: { [key: string]: string } = { 'provider': 'as_owner', 'app': 'profile', 'ip': 'remote_ip', 'port': 'remote_port' } /** parse is a shortcut for new Parser(input).process() */ static parse(input: string): ParseResult { return new Parser(input).process(); } /** Process the whole input stream and return the parsed result */ process(): ParseResult { let lastIdent: Token | null = null; let hasColon = false; let not = false; let groupBy: string[] = []; let orderBy: string[] = []; while (true) { const tok = this.lexer.next() if (tok === null) { break; } if (isDevMode()) { console.log(tok) } // if we find a whitespace token we count it as a termination character // for the last unterminated condition. if (tok.type === TokenType.WHITESPACE) { this._lastUnterminatedCondition = null; } // Since we allow the user to enter values without quotes the // lexer might wrongly declare a "string value" as an IDENT. // If we have the pattern we re-classify // the last IDENT as a STRING value if (!!lastIdent && hasColon && tok.type === TokenType.IDENT) { tok.type = TokenType.STRING; } if (tok.type === TokenType.IDENT || tok.type === TokenType.GROUPBY || tok.type === TokenType.ORDERBY) { // if we had an IDENT token before and got a new one now the // previous one is pushed to the remaining list if (!!lastIdent) { this._lastUnterminatedCondition = null; this.remaining.push(lastIdent.value) } lastIdent = tok; this._lastUnterminatedCondition = { start: tok.start, type: Parser.aliases[lastIdent.value] || lastIdent.value, value: '', } continue } // if we don't have an preceding IDENT token // this must be part of remaingin if (!lastIdent) { this.remaining.push(tok.literal); this._lastUnterminatedCondition = null; continue } // we would expect a colon now if (!hasColon) { if (tok.type !== TokenType.COLON) { // we expected a colon but got something else. // this means the last IDENT is part of remaining this.remaining.push(lastIdent.value); lastIdent = null; this._lastUnterminatedCondition = null; continue } // we have a colon now so proceed to the next token hasColon = true; not = false; continue } if (lastIdent.type === TokenType.GROUPBY) { groupBy.push(Parser.aliases[tok.literal] || tok.literal) lastIdent = null hasColon = false continue } if (lastIdent.type == TokenType.ORDERBY) { orderBy.push(Parser.aliases[tok.literal] || tok.literal) lastIdent = null hasColon = false continue } if (tok.type === TokenType.NOT && not === false) { not = true continue } if (isValueToken(tok)) { let identValue = Parser.aliases[lastIdent.value] || lastIdent.value; if (!this.conditions[identValue]) { this.conditions[identValue] = []; } if (!not) { this.conditions[identValue].push(tok.value) } else { this.conditions[identValue].push({ $ne: tok.value }) } this._lastUnterminatedCondition!.value = tok.value; lastIdent = null hasColon = false not = false continue } this.remaining.push(lastIdent.value); lastIdent = null; hasColon = false; not = false; this._lastUnterminatedCondition = null; } if (!!lastIdent) { this.remaining.push(lastIdent.value); if (hasColon) { this._lastUnterminatedCondition = { start: lastIdent.start, type: Parser.aliases[lastIdent.value] || lastIdent.value, value: '' }; } else { this._lastUnterminatedCondition = null; } } return { groupBy: groupBy.length > 0 ? groupBy : undefined, orderBy: orderBy.length > 0 ? orderBy : undefined, conditions: this.conditions, textQuery: this.remaining.filter(tok => !isWhitespace(tok)).join(" "), } } } ================================================ FILE: desktop/angular/src/app/shared/netquery/textql/token.ts ================================================ /** * Language Definition: * * input: * * [EXPR] [EXPR]... * * with: * * EXPR = [IDENT][COLON][NOT?][VALUE] * NOT = "!" * VALUE = [STRING][BOOL][NUMBER] * STRING = [a-zA-Z\.0-9] * BOOL = true | false * NUMBER = [0-9]+ * COLON = ":" * */ export enum TokenType { WHITESPACE = 'WHITESPACE', IDENT = 'IDENT', COLON = 'COLON', STRING = 'STRING', NUMBER = 'NUMBER', BOOL = 'BOOL', NOT = 'NOT', GROUPBY = 'GROUPBY', ORDERBY = 'ORDERBY' } export type TokenValue = T extends TokenType.NUMBER ? number : T extends TokenType.STRING ? string : T extends TokenType.BOOL ? boolean : T extends TokenType.NOT ? '!' : T extends TokenType.GROUPBY ? 'string' : string; export interface Token { type: T; literal: string; value: TokenValue; start: number; } ================================================ FILE: desktop/angular/src/app/shared/netquery/utils.ts ================================================ import { Condition, Matcher } from "@safing/portmaster-api"; import { objKeys } from "../utils"; export const connectionFieldTranslation: { [key: string]: string } = { domain: "Domain", profile: "App", path: 'Binary Path', scope: 'Scope', as_owner: "Provider", country: "Country", direction: 'Direction', started: 'Started', ended: 'Ended', remote_ip: 'Remote IP', verdict: 'Verdict', encrypted: 'Encrypted', internal: 'Internal', asn: 'ASN', tunneled: 'SPN Active', active: 'Active', allowed: 'Allowed', from: 'From', to: 'To', remote_port: 'Port', bytes_sent: 'Bytes Sent', bytes_received: 'Bytes Received' } export function isMatcher(v: any | Matcher): v is Matcher { return typeof v === 'object' && ('$eq' in v || '$ne' in v || '$like' in v || '$in' in v || '$notin' in v); } export function mergeConditions(cond1: Condition, cond2: Condition): Condition { const result: Condition = {}; objKeys(cond1).forEach(key => { let val = cond1[key]; if (Array.isArray(val)) { result[key] = val; } else { result[key] = [val]; } }) objKeys(cond2).forEach(key => { let val = cond2[key]; if (!Array.isArray(val)) { val = [val] } if (!(key in result)) { result[key] = val; } else { result[key] = [ ...(result[key] as any), // this must be an array here ...val, ] } }) return result; } ================================================ FILE: desktop/angular/src/app/shared/network-scout/index.ts ================================================ export * from './network-scout'; ================================================ FILE: desktop/angular/src/app/shared/network-scout/network-scout.html ================================================
{{allProfiles.length}} Apps

Sort By

{{ sortMethod }}
{{ profile.exitPins.length }} IDENTITIES
Connections from {{ profile.Name }} have not been routed through the SPN.
  • {{ entity.IP }}
    {{ identity.count }} Connections HOPS: {{ identity.HopDistance }}
{{ profile.showMore ? 'Show Less Identities' : 'Show More Identities'}}
{{ data.Name }}
{{ data.bytes_sent | bytes:"1.0-0" }} {{ data.bytes_received | bytes:"1.0-0" }}
================================================ FILE: desktop/angular/src/app/shared/network-scout/network-scout.scss ================================================ :host { @apply w-full p-2 flex flex-col gap-2; } ================================================ FILE: desktop/angular/src/app/shared/network-scout/network-scout.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, OnInit, TrackByFunction, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { BoolSetting, Condition, ConfigService, ExpertiseLevel, IProfileStats, Netquery, Pin, SPNService } from "@safing/portmaster-api"; import { Subject, combineLatest, debounceTime, filter, finalize, interval, retry, startWith, switchMap, take, takeUntil } from "rxjs"; import { UIStateService } from "src/app/services"; import { fadeInListAnimation } from "../animations"; import { ExpertiseService } from './../expertise/expertise.service'; interface _Pin extends Pin { count: number; } interface _Profile extends IProfileStats { exitPins: _Pin[]; showMore: boolean; expanded: boolean; } export enum SortTypes { static = 'Static', aToZ = "A-Z", zToA = "Z-A", totalConnections = "Total Connections", connectionsDenied = "Denied Connections", connectionsAllowed = "Allowed Connections", spnIdentities = "SPN Identities", bytesSent = "Bytes Sent", bytesReceived = "Bytes Received", totalBytes = "Total Bytes" } const bandwidthSorts: SortTypes[] = [ SortTypes.bytesReceived, SortTypes.bytesSent, SortTypes.totalBytes ] @Component({ selector: 'app-network-scout', templateUrl: './network-scout.html', styleUrls: ['./network-scout.scss'], changeDetection: ChangeDetectionStrategy.OnPush, animations: [ fadeInListAnimation, ] }) export class NetworkScoutComponent implements OnInit { private destroyRef = inject(DestroyRef); sortTypes = [ SortTypes.static, SortTypes.aToZ, SortTypes.zToA, SortTypes.totalConnections, SortTypes.connectionsDenied, SortTypes.connectionsAllowed, SortTypes.spnIdentities ] readonly sortMethods = new Map([ // there's not entry for "Static" here on purpose because we'll use the sort order // returned by netquery. [SortTypes.aToZ, (a: _Profile, b: _Profile) => a.Name.localeCompare(b.Name)], [SortTypes.zToA, (a: _Profile, b: _Profile) => b.Name.localeCompare(a.Name)], [SortTypes.totalConnections, (a: _Profile, b: _Profile) => (b.countAllowed + b.countUnpermitted) - (a.countAllowed + a.countUnpermitted)], [SortTypes.connectionsAllowed, (a: _Profile, b: _Profile) => b.countAllowed - a.countAllowed], [SortTypes.connectionsDenied, (a: _Profile, b: _Profile) => b.countUnpermitted - a.countUnpermitted], [SortTypes.spnIdentities, (a: _Profile, b: _Profile) => a.identities.length - b.identities.length], [SortTypes.bytesReceived, (a: _Profile, b: _Profile) => b.bytes_received - a.bytes_received], [SortTypes.bytesSent, (a: _Profile, b: _Profile) => b.bytes_sent - a.bytes_sent], [SortTypes.totalBytes, (a: _Profile, b: _Profile) => (b.bytes_received + b.bytes_sent) - (a.bytes_received + a.bytes_sent)] ]); /** The current sort order */ sortOrder: SortTypes = SortTypes.static; get isByteSortOrder() { return bandwidthSorts.includes(this.sortOrder); } /** Used to trigger a debounced search from the template */ triggerSearch = new Subject(); /** The current search term as entered in the input[type="text"] */ searchTerm: string = ''; /** A list of all active profiles without any search applied */ allProfiles: _Profile[] = []; /** Defines if new elements should be expanded or collapsed */ expandCollapseState: 'expand' | 'collapse' = 'expand'; /** Whether or not the SPN is enabled */ spnEnabled = false; /** * Emits when the user clicks the "expand all" or "collapse all" buttons. * Once the user did that we stop updating the default state depending on whether the * SPN is enabled or not. */ private userChangedState = new Subject(); /** * A list of profiles that are currently displayed. This is basically allProfiles but with * text search applied. */ profiles: _Profile[] = []; /** TrackByFunction for the profiles. */ trackProfile: TrackByFunction<_Profile> = (_, profile) => profile.ID; /** TrackByFunction for the exit pins */ trackPin: TrackByFunction<_Pin> = (_, pin) => pin.ID; constructor( private netquery: Netquery, private spn: SPNService, private configService: ConfigService, private stateService: UIStateService, private expertise: ExpertiseService, private cdr: ChangeDetectorRef, ) { } searchProfiles(term: string) { term = term.trim(); if (term === '') { this.profiles = [ ...this.allProfiles ]; this.sortProfiles(this.profiles); return; } const lowerCaseTerm = term.toLocaleLowerCase() this.profiles = this.allProfiles.filter(p => { if (p.ID.toLocaleLowerCase().includes(lowerCaseTerm)) { return true; } if (p.Name.toLocaleLowerCase().includes(lowerCaseTerm)) { return true; } if (p.exitPins.some(pin => pin.Name.toLocaleLowerCase().includes(lowerCaseTerm))) { return true; } return false; }) this.sortProfiles(this.profiles); } sortProfiles(profiles: _Profile[]) { const method = this.sortMethods.get(this.sortOrder); if (!method) { return; } profiles.sort(method) this.cdr.markForCheck(); } updateSortOrder(newOrder: SortTypes) { this.sortOrder = newOrder; this.searchProfiles(this.searchTerm); this.stateService.set('netscoutSortOrder', newOrder) .subscribe({ error: err => { console.error(err); } }) } expandAll() { this.expandCollapseState = 'expand'; this.allProfiles.forEach(profile => profile.expanded = profile.identities.length > 0) this.searchProfiles(this.searchTerm) this.userChangedState.next(); this.cdr.markForCheck() } collapseAll() { this.expandCollapseState = 'collapse'; this.allProfiles.forEach(profile => profile.expanded = false) this.searchProfiles(this.searchTerm) this.userChangedState.next(); this.cdr.markForCheck() } ngOnInit(): void { this.stateService.uiState() .pipe(take(1)) .subscribe(state => { this.sortOrder = state.netscoutSortOrder; this.searchProfiles(this.searchTerm); }) this.configService.watch('spn/enable') .pipe( takeUntilDestroyed(this.destroyRef), takeUntil(this.userChangedState), ) .subscribe(enabled => { // if the SPN is enabled and the user did not yet change the // collapse/expand state we switch to "expand" for the default. // Otherwise, there will be no identities so there's no reason // to expand them at all so we switch to collapse if (enabled) { this.expandCollapseState = 'expand' } else { this.expandCollapseState = 'collapse' } this.spnEnabled = enabled; }); let updateInProgress = false; combineLatest([ combineLatest([ interval(5000) .pipe( filter(() => !updateInProgress) ), this.expertise.change, ]) .pipe( startWith(-1), switchMap(() => { let query: Condition = {}; if (this.expertise.currentLevel !== ExpertiseLevel.Developer) { query["internal"] = { $eq: false } } updateInProgress = true return this.netquery.getProfileStats(query) .pipe( finalize(() => updateInProgress = false) ) }), retry({ delay: 5000 }) ), this.spn.watchPins() .pipe( debounceTime(100), startWith([]), ), this.triggerSearch .pipe( debounceTime(100), startWith(''), ), ]) .pipe( takeUntilDestroyed(this.destroyRef), ) .subscribe(([res, pins, searchTerm]) => { // create a lookup map for the the SPN map pins const pinLookupMap = new Map(); pins.forEach(p => pinLookupMap.set(p.ID, p)) // create a lookup map from already known profiles so we can // inherit states like "showMore". const profileLookupMap = new Map(); this.allProfiles.forEach(p => profileLookupMap.set(p.ID, p)) // map the list of profile statistics to include the exit Pin information // as well. this.allProfiles = res.map(s => { const existing = profileLookupMap.get(s.ID); return { ...s, exitPins: s.identities .map(ident => { const pin = pinLookupMap.get(ident.exit_node); if (!pin) { return null; } return { count: ident.count, ...pin } }) .filter(pin => !!pin), showMore: existing?.showMore ?? false, expanded: existing?.expanded ?? (this.expandCollapseState === 'expand' && s.identities.length > 1 /* there's always the "direct" identity */), } as _Profile }); this.searchProfiles(searchTerm); // check if we have profiles with bandwidth data and // make sure our sort methods are updated. if (this.profiles.some(p => p.bytes_received > 0 || p.bytes_sent > 0)) { if (!this.sortTypes.includes(SortTypes.bytesReceived)) { this.sortTypes.push.apply(this.sortTypes, bandwidthSorts) } this.sortTypes = [...this.sortTypes]; } else { this.sortTypes = this.sortTypes.filter(type => { return !bandwidthSorts.includes(type) }) } this.cdr.markForCheck(); }) } } ================================================ FILE: desktop/angular/src/app/shared/notification/notification.html ================================================
Notification Broadcast Notification

{{notification.Title}}

================================================ FILE: desktop/angular/src/app/shared/notification/notification.scss ================================================ :host { @apply block; max-width: 24rem; } caption { @apply text-xxs; opacity: .6; } h1 { @apply text-base font-normal my-4; } .message, h1 { flex-shrink: 0; text-overflow: ellipsis; word-break: normal; } .message { flex-grow: 1; padding: 0; } .close-icon { position: absolute; top: 1rem; right: 1rem; opacity: .7; cursor: pointer; &:hover { opacity: 1; } } .buttons { width: 100%; display: flex; @apply flex flex-row justify-end gap-2; } a { text-decoration: underline; } ================================================ FILE: desktop/angular/src/app/shared/notification/notification.ts ================================================ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, OnInit, Output, inject } from '@angular/core'; import { SFNG_DIALOG_REF } from '@safing/ui'; import { Action, NotificationState, NotificationsService, getNotificationTypeString } from '../../services'; import { _Notification } from '../notification-list/notification-list.component'; @Component({ selector: 'app-notification', templateUrl: './notification.html', styleUrls: ['./notification.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class NotificationComponent implements OnInit { readonly ref = inject(SFNG_DIALOG_REF); readonly notification: _Notification = inject(SFNG_DIALOG_REF).data; /** * The host tag of the notification component has the notification type * and the notification state as a class name set. * Examples: * * notif-action-required notif-prompt */ @HostBinding('class') get hostClass(): string { let cls = `notif-${this.state}`; if (!!this.notification) { cls = `${cls} notif-${getNotificationTypeString(this.notification.Type)}` } return cls } state: NotificationState = NotificationState.Invalid; ngOnInit() { if (!!this.notification) { this.state = this.notification.State || NotificationState.Invalid; } else { this.state = NotificationState.Invalid; } } @Input() set allowMarkdown(v: any) { this._markdown = coerceBooleanProperty(v); } get allowMarkdown() { return this._markdown; } private _markdown: boolean = true; @Output() actionExecuted: EventEmitter = new EventEmitter(); constructor(private notifService: NotificationsService) { } execute(n: _Notification, action: Action) { this.notifService.execute(n, action) .subscribe( () => { this.actionExecuted.next(action) this.ref.close(); }, err => console.error(err), ) } } ================================================ FILE: desktop/angular/src/app/shared/notification-list/index.ts ================================================ export { NotificationListComponent as NotificationWidgetComponent, NotificationWidgetConfig } from './notification-list.component'; ================================================ FILE: desktop/angular/src/app/shared/notification-list/notification-list.component.html ================================================ Notifications
{{notif.Title || notif.Message}}
================================================ FILE: desktop/angular/src/app/shared/notification-list/notification-list.component.scss ================================================ :host { @apply flex flex-col justify-start items-center gap-2; @apply w-full px-2; @apply border-b border-gray-400 pb-2; &>* { /* do not allow to shrink */ flex-shrink: 0; } } .row, div.placeholder { display: flex; flex-direction: column; width: 100%; margin: 0; border: none; } .row { @apply overflow-hidden w-full flex flex-row rounded; @apply h-8; .type { display: flex; justify-content: center; align-items: center; width: .5rem; flex-shrink: 0; flex-grow: 0; background-color: #202020; &.info { background-color: #727272; } &.warning { background-color: theme("colors.info.yellow"); } &.error { background-color: theme("colors.info.red"); } &.broadcast { width: 2rem; color: #00000080; } } .preview { background-color: #292929; cursor: pointer; overflow: hidden; flex-grow: 1; display: flex; justify-content: space-between; align-items: center; padding-left: 1rem; border-top-left-radius: 2px; border-bottom-left-radius: 2px; position: relative; span { flex-grow: 1; text-overflow: ellipsis; overflow: hidden; word-wrap: none; white-space: nowrap; font-size: 0.7rem; font-weight: 500; color: #cacaca; .category { padding-left: 8px; font-size: 0.65rem; font-weight: 700; text-transform: capitalize; color: #999999c9; } } &:hover { background-color: #303030; .buttons { opacity: 1; transition: all .05s ease-in-out; transform: translateX(-100%); } } .buttons { opacity: 0; transition: all .05s ease-in-out; height: 100%; position: absolute; left: 100%; display: flex; white-space: nowrap; background-color: #303030; button { outline: none; @apply bg-transparent; font-size: 0.6rem; background-color: #3a3a3a; padding-left: 1.25rem; padding-right: 1.25rem; text-transform: capitalize; border-radius: 0; font-weight: 500; outline: none; color: hsla(0, 0%, 100%, 0.548); height: 100%; &:hover { background-color: #363636; color: #ffffff; } &:first-of-type { margin-left: .5rem; } &:last-of-type { background: transparent; color: hsla(0, 0%, 100%, 0.562); @apply ml-1; transition: all cubic-bezier(0.175, 0.885, 0.32, 1.275) .2s; &:hover { color: #ffffff; } } } } } } /* .notification-body { @apply bg-cards-tertiary; flex-grow: 1; @apply rounded-b; position: absolute; top: var(--slot-size); bottom: 0; .broadcast-info { background-color: #00000040; width: 100%; padding: 0.5rem; color: white !important; font-weight: 400; bottom: 0; position: absolute; flex-grow: 1; @apply flex items-center justify-center gap-1; } } */ div.placeholder { @apply font-medium; @apply text-tertiary; @apply flex-grow; position: relative; display: flex; flex-direction: column; justify-content: center; align-items: center; user-select: none; } app-loading { opacity: .5; margin-left: auto; margin-right: auto; position: relative; top: 5px; } ================================================ FILE: desktop/angular/src/app/shared/notification-list/notification-list.component.ts ================================================ import { animate, style, transition, trigger } from '@angular/animations'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostBinding, OnDestroy, OnInit, TrackByFunction, inject } from '@angular/core'; import { SfngDialogService } from '@safing/ui'; import { Subscription } from 'rxjs'; import { map } from 'rxjs/operators'; import { Action, Notification, NotificationType, NotificationsService } from 'src/app/services'; import { moveInOutAnimation, moveInOutListAnimation } from 'src/app/shared/animations'; import { NotificationComponent } from '../notification/notification'; export interface NotificationWidgetConfig { markdown: boolean; } export interface _Notification extends Notification { isBroadcast: boolean } @Component({ selector: 'app-notification-list', templateUrl: './notification-list.component.html', changeDetection: ChangeDetectionStrategy.OnPush, styleUrls: [ './notification-list.component.scss' ], animations: [ trigger( 'fadeIn', [ transition( ':enter', [ style({ opacity: 0 }), animate('.2s .2s ease-in', style({ opacity: 1 })) ] ), ] ), moveInOutAnimation, moveInOutListAnimation ] }) export class NotificationListComponent implements OnInit, OnDestroy { readonly types = NotificationType; readonly dialog = inject(SfngDialogService); readonly cdr = inject(ChangeDetectorRef); /** Used to set a fixed height when a notification is expanded. */ @HostBinding('style.height') height: null | string = null; /** Sets the overflow to hidden when a notification is expanded. */ @HostBinding('style.overflow') get overflow() { if (this.height === null) { return null; } return 'hidden'; } @HostBinding('class.empty') get isEmpty() { return this.notifications.length === 0; } @HostBinding('@moveInOutList') get length() { return this.notifications.length } /** Subscription to notification updates. */ private notifSub = Subscription.EMPTY; /** All active notifications. */ notifications: _Notification[] = []; trackBy: TrackByFunction<_Notification> = this.notifsService.trackBy; constructor( public elementRef: ElementRef, public notifsService: NotificationsService, ) { } ngOnInit(): void { this.notifSub = this.notifsService .new$ .pipe( // filter out any prompts as they are handled by a different widget. map(notifs => { return notifs.filter(notif => !notif.SelectedActionID && !(notif.Type === NotificationType.Prompt && notif.EventID.startsWith("filter:prompt"))) }) ) .subscribe(list => { this.notifications = list.map(notification => { return { ...notification, isBroadcast: notification.EventID.startsWith("broadcasts:"), } }); this.cdr.markForCheck(); }); } ngOnDestroy() { this.notifSub.unsubscribe(); } /** * @private * * Executes a notification action and updates the "expanded-notification" * view if required. * * @param n The notification object. * @param actionId The ID of the action to execute. * @param event The mouse click event. */ execute(n: _Notification, action: Action, event: MouseEvent) { event.preventDefault(); event.stopPropagation(); this.notifsService.execute(n, action) .subscribe() } /** * @private * Toggles between list mode and notification-view mode. * * @param notif The notification that has been clicked. */ toggelView(notif: _Notification) { const ref = this.dialog.create(NotificationComponent, { backdrop: 'light', autoclose: true, data: notif, }); } } ================================================ FILE: desktop/angular/src/app/shared/pipes/bytes.pipe.ts ================================================ import { DecimalPipe } from "@angular/common"; import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ pure: true, name: 'bytes', }) export class BytesPipe implements PipeTransform { transform(value: any, decimal: string = '1.0-2', ...args: any[]) { value = +value; // convert to number const ceilings = [ 'B', 'kB', 'MB', 'GB', 'TB' ] let idx = 0; while (value > 1024 && idx < ceilings.length - 1) { value = value / 1024; idx++ } return (new DecimalPipe('en-US')).transform(value, decimal) + ' ' + ceilings[idx]; } } ================================================ FILE: desktop/angular/src/app/shared/pipes/common-pipes.module.ts ================================================ import { NgModule } from "@angular/core"; import { BytesPipe } from "./bytes.pipe"; import { TimeAgoPipe } from "./time-ago.pipe"; import { ToAppProfilePipe } from "./to-profile.pipe"; import { DurationPipe } from "./duration.pipe"; import { RoundPipe } from "./round.pipe"; import { ToSecondsPipe } from "./to-seconds.pipe"; @NgModule({ declarations: [ TimeAgoPipe, BytesPipe, ToAppProfilePipe, DurationPipe, RoundPipe, ToSecondsPipe ], exports: [ TimeAgoPipe, BytesPipe, ToAppProfilePipe, DurationPipe, RoundPipe, ToSecondsPipe ] }) export class CommonPipesModule { } ================================================ FILE: desktop/angular/src/app/shared/pipes/duration.pipe.ts ================================================ import { Pipe, PipeTransform } from "@angular/core"; const millisecond = 1; const second = 1000 * millisecond; const minute = 60 * second; const hour = 60 * minute; const day = 24 * hour; export function formatDuration(millis: number, skipDays = false, skipMillis = false): string { const sign = millis < 0 ? '-' : ''; let val = Math.abs(millis); let str = ''; if (millis === 0) { return '0'; } if (!skipDays) { const days = Math.floor(val / day) if (days > 0) { str += days.toString() + 'd '; val -= days * day; } } const hours = Math.floor(val / hour); if (hours > 0) { str += hours.toString() + 'h '; val -= hours * hour; } const minutes = Math.floor(val / minute); if (minutes > 0) { str += minutes.toString() + 'm '; val -= minutes * minute; } const seconds = Math.floor(val / second); if (seconds > 0) { str += seconds.toString() + 's '; val -= seconds * second; } if (!skipMillis) { const ms = Math.floor(val / millisecond) if (ms > 0) { str += ms.toString() + 'ms ' val -= ms * millisecond } } if (str.endsWith("")) { str = str.substring(0, str.length - 1) } return sign + str; } @Pipe({ name: 'duration', pure: true }) export class DurationPipe implements PipeTransform { transform(value: number | [string, string] | [Date, Date] | [number, number], ...args: any[]) { if (Array.isArray(value)) { let firstNum: number; let secondNum: number; let [first, second] = value; if (first instanceof Date || typeof first === 'string') { first = new Date(first) firstNum = first.getTime() } else { firstNum = first } if (second instanceof Date || typeof second === 'string') { second = new Date(second); secondNum = second.getTime() } else { secondNum = second } if (secondNum < firstNum) { const t = firstNum; firstNum = secondNum secondNum = t } value = secondNum - firstNum } if (value < second) { } const result = formatDuration(value); if (result === '0') { return '< 1s' } return result } } ================================================ FILE: desktop/angular/src/app/shared/pipes/index.ts ================================================ export * from './common-pipes.module'; export * from './time-ago.pipe'; export * from './to-profile.pipe'; export * from './duration.pipe'; export * from './to-seconds.pipe'; export * from './round.pipe'; ================================================ FILE: desktop/angular/src/app/shared/pipes/round.pipe.ts ================================================ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'round', pure: true, }) export class RoundPipe implements PipeTransform { transform(value: number, roundBy: number) { if (isNaN(value)) { return NaN } return Math.floor(value / roundBy) * roundBy } } ================================================ FILE: desktop/angular/src/app/shared/pipes/time-ago.pipe.ts ================================================ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'timeAgo', pure: true }) export class TimeAgoPipe implements PipeTransform { transform(value: number | Date | string, ticker?: any): string { return timeAgo(value); } } export const timeCeilings = [ { ceiling: 1, text: "" }, { ceiling: 60, text: "sec" }, { ceiling: 3600, text: "min" }, { ceiling: 86400, text: "hour" }, { ceiling: 2629744, text: "day" }, { ceiling: 31556926, text: "month" }, { ceiling: Infinity, text: "year" } ] export function timeAgo(value: number | Date | string) { if (typeof value === 'string') { value = new Date(value) } if (value instanceof Date) { value = value.valueOf() / 1000; } let suffix = 'ago' let diffInSeconds = Math.floor(((new Date()).valueOf() - (value * 1000)) / 1000); if (diffInSeconds < 0) { diffInSeconds = diffInSeconds * -1; suffix = '' } for (let i = timeCeilings.length - 1; i >= 0; i--) { const f = timeCeilings[i]; let n = Math.floor(diffInSeconds / f.ceiling); if (n > 0) { if (i < 1) { return `< 1 min ` + suffix; } let text = timeCeilings[i + 1].text; if (n > 1) { text += 's'; } return `${n} ${text} ` + suffix } } return "< 1 min " + suffix // actually just now (diffInSeconds == 0) } ================================================ FILE: desktop/angular/src/app/shared/pipes/to-profile.pipe.ts ================================================ import { ChangeDetectorRef, OnDestroy, Pipe, PipeTransform, inject } from "@angular/core"; import { AppProfile, AppProfileService } from "@safing/portmaster-api"; import { Subscription } from "rxjs"; @Pipe({ name: 'toAppProfile', pure: false }) export class ToAppProfilePipe implements PipeTransform, OnDestroy { profileService = inject(AppProfileService); cdr = inject(ChangeDetectorRef); private _lastProfile: AppProfile | null = null; private _lastKey: string | null = null; private _subscription = Subscription.EMPTY; transform(key: string): AppProfile | null { if (key !== this._lastKey) { this._lastKey = key; this._subscription.unsubscribe(); this._subscription = this.profileService.watchAppProfile(key) .subscribe(value => { this._lastProfile = value; this.cdr.markForCheck(); }) } return this._lastProfile || null; } ngOnDestroy(): void { this._subscription.unsubscribe(); } } ================================================ FILE: desktop/angular/src/app/shared/pipes/to-seconds.pipe.ts ================================================ import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ name: 'toSeconds', pure: true, }) export class ToSecondsPipe implements PipeTransform { transform(value: Date | string, ...args: any[]) { if (value === null || value === undefined) { return NaN } if (typeof value === 'string') { value = new Date(value); } return Math.floor(value.getTime() / 1000) } } ================================================ FILE: desktop/angular/src/app/shared/process-details-dialog/index.ts ================================================ export * from './process-details-dialog'; ================================================ FILE: desktop/angular/src/app/shared/process-details-dialog/process-details-dialog.html ================================================

Process Details

Name
{{ process.Name }}
User {{ process.UserName }} ({{ process.UserID }})
Process ID {{ process.Pid }}
Process Group ID {{ process.Pgid }}
Parent Process ID {{ process.ParentPid }}
Path {{ process.Path }} ({{ process.MatchingPath }})
Executable Name {{ process.ExecName }}
Command Line {{ process.CmdLine }}
Tags
This process does not have any tags.
  • {{ tag.Key }} {{ tag.Value }}
This process does not have any environment variables.
{{ env.key }} {{ env.value }}
================================================ FILE: desktop/angular/src/app/shared/process-details-dialog/process-details-dialog.scss ================================================ :host { @apply flex flex-col gap-4 max-w-2xl; min-width: 500px; width: 60vw; min-height: 500px; height: 60vh; max-height: 80vh; overflow: hidden; } table.custom { @apply w-full overflow-hidden; th, td { @apply px-2 align-top py-2; } th { text-align: left; @apply w-32 text-secondary; } td { @apply whitespace-normal break-all; } td:last-of-type { @apply p-0; } } ================================================ FILE: desktop/angular/src/app/shared/process-details-dialog/process-details-dialog.ts ================================================ import { KeyValue } from '@angular/common'; import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; import { AppProfile, AppProfileService, FingerpringOperation, Fingerprint, FingerprintType, PortapiService, Process } from '@safing/portmaster-api'; import { SfngDialogRef, SfngDialogService, SFNG_DIALOG_REF } from '@safing/ui'; import { EditProfileDialog } from '../edit-profile-dialog'; @Component({ selector: 'app-process-details', templateUrl: './process-details-dialog.html', styleUrls: ['./process-details-dialog.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class ProcessDetailsDialogComponent { process: (Process & { ID: string }); constructor( @Inject(SFNG_DIALOG_REF) private dialogRef: SfngDialogRef, private dialog: SfngDialogService, private portapi: PortapiService, private profileService: AppProfileService ) { this.process = { ...this.dialogRef.data, ID: this.dialogRef.data.PrimaryProfileID, } } close() { this.dialogRef.close(); } createProfileForPath() { this.createProfileWithFingerprint({ Type: FingerprintType.Path, Key: '', Value: this.process.MatchingPath || this.process.Path, Operation: FingerpringOperation.Equal, }) } createProfileForCmdline() { this.createProfileWithFingerprint({ Type: FingerprintType.Cmdline, Key: '', Value: this.process.CmdLine, Operation: FingerpringOperation.Equal, }) } createProfileForEnv(env: KeyValue) { const fp: Fingerprint = { Type: FingerprintType.Env, Key: env.key, Value: env.value, Operation: FingerpringOperation.Equal, } this.createProfileWithFingerprint(fp) } openParent() { if (!!this.process.ParentPid) { this.portapi.get(`network:tree/${this.process.ParentPid}-${this.process.ParentCreatedAt}`) .subscribe(process => { this.process = { ...process, ID: process.PrimaryProfileID, }; }) } } openGroup() { this.profileService.getProcessByPid(this.process.Pid) .subscribe(result => { if (!result) { return; } this.process = { ...result, ID: result.PrimaryProfileID }; }) } private createProfileWithFingerprint(fp: Fingerprint) { let profilePreset: Partial = { Fingerprints: [ fp ] }; this.dialog.create(EditProfileDialog, { data: profilePreset, backdrop: true, autoclose: false, }) this.dialogRef.close(); } } ================================================ FILE: desktop/angular/src/app/shared/prompt-list/index.ts ================================================ export { PromptListComponent as PromptWidgetComponent } from './prompt-list.component'; ================================================ FILE: desktop/angular/src/app/shared/prompt-list/prompt-list.component.html ================================================
{{ profile.Name }} {{ profile.prompts.length }} Per Connection Allow All Block All Default Action Allow App Block App Change Default App Settings
{{ prompt.EventData?.Entity?.IP || 'N/A' }} {{prompt.subdomain}}.{{prompt.domain}}
{{ profile.prompts.length - profile.promptsLimited.length }} more
Show less
No Prompts
================================================ FILE: desktop/angular/src/app/shared/prompt-list/prompt-list.component.scss ================================================ :host { overflow: hidden; max-height: 50vh; display: flex; flex-direction: column; min-height: 10rem; @apply w-80; @apply bg-gray-300; padding-top: 1px; padding-bottom: 3px; } app-icon { --app-icon-size: 13px; } .scrollable { @apply p-0; } .group { @apply mb-3; .group-header { @apply px-2; display: flex; align-items: center; margin-left: 4px; height: 2rem; .app-name { flex-grow: 1; font-size: 0.7rem; font-weight: 500; color: #cacaca; } span.prompt-count { @apply mr-1; font-size: 0.6rem; font-weight: 600; color: #cacaca; transform: scale(0.95); user-select: none; } } } app-menu-item.item-seperator { @apply border-t; @apply border-buttons-dark } .no-prompts { @apply text-tertiary flex-grow; width: 100%; display: flex; justify-content: center; align-items: center; flex-direction: column; user-select: none; } .prompts { display: flex; .border { margin-left: calc(0.5rem + 9px); width: 0.5rem; border-left-width: 2px; border-bottom-width: 2px; border-color: #292929; } .prompt-container, .prompt, .actions { display: flex; } .prompt-container { flex-grow: 1; flex-direction: column; padding-left: 0.6rem; padding-right: 0.5rem; padding-top: 0.4rem; padding-bottom: 1rem; .prompt { padding-left: 0.75rem; margin-bottom: 4px; background-color: #292929; height: auto; border-radius: 2px; align-items: center; overflow: hidden; position: relative; &:hover { background-color: #303030; .actions { animation: .07s slidein-left ease-in-out; opacity: 1; transition: all .05s ease-in-out; } } .entity { flex-grow: 1; word-break: break-all; white-space: normal; font-size: 0.7rem; font-weight: 500; padding-top: 0.6rem; padding-bottom: 0.6rem; padding-left: 2px; padding-right: 9px; color: #cacaca; .subdomain { font-size: 0.7rem; font-weight: 500; color: #999999; } } .actions { min-width: 5rem; flex-wrap: wrap; height: 100%; opacity: 0; transition: all .05s ease-in-out; position: absolute; right: 0; background-color: #292929; button { outline: none; @apply bg-transparent; font-size: 0.6rem; background-color: #3a3a3a; padding-left: 1.25rem; padding-right: 1.25rem; text-transform: capitalize; border-radius: 0; font-weight: 500; outline: none; color: hsla(0, 0%, 100%, 0.548); padding-left: 1.25rem; padding-right: 1.25rem; text-transform: capitalize; border-radius: 0; font-weight: 500; outline: none; color: hsla(0, 0%, 100%, 0.548); &:hover { background-color: #363636; color: #ffffff; } &:last-of-type { background: transparent; color: hsla(0, 0%, 100%, 0.562); @apply ml-1; transition: all cubic-bezier(0.175, 0.885, 0.32, 1.275) .2s; &:hover { color: #ffffff; } } } } } } .more-available { position: relative; top: 1.4rem; margin-top: -1rem; cursor: pointer; font-size: 0.7rem; font-weight: 500; color: #999999; user-select: none; &:hover { color: #cacaca; } } } @keyframes slidein-left { 0% { transform: translateX(100%); } 100% { transform: translateX(0); } } ================================================ FILE: desktop/angular/src/app/shared/prompt-list/prompt-list.component.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, OnDestroy, OnInit, TrackByFunction } from '@angular/core'; import { AppProfile, AppProfileService, deepClone, setAppSetting } from '@safing/portmaster-api'; import { combineLatest, forkJoin, Observable, Subscription } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { Action, ConnectionPrompt, NotificationsService, NotificationType } from 'src/app/services'; import { moveInOutAnimation, moveInOutListAnimation } from 'src/app/shared/animations'; import { ParsedDomain, parseDomain } from 'src/app/shared/utils'; import { ActionIndicatorService } from '../action-indicator'; // ExtendedConnectionPrompt extends the normal connection prompt // with parsed domain information. interface ExtendedConnectionPrompt extends ConnectionPrompt, ParsedDomain { } // ProfilePrompts extends an application profile with prompt // information mainly used for paginagtion. interface ProfilePrompts extends AppProfile { promptsLimited: ExtendedConnectionPrompt[]; prompts: ExtendedConnectionPrompt[]; showAll: boolean; } // Number of prompts to display per application profile // before we start to paginate the list of prompts. const PromptLimit = 3; @Component({ selector: 'app-prompt-list', templateUrl: './prompt-list.component.html', styleUrls: [ './prompt-list.component.scss' ], changeDetection: ChangeDetectionStrategy.OnPush, animations: [ moveInOutAnimation, moveInOutListAnimation ] }) export class PromptListComponent implements OnInit, OnDestroy { profiles: ProfilePrompts[] = []; /** * @private * Sets "empty" class on the host element if no prompts are displayed */ @HostBinding('class.empty') get isEmpty() { return this.profiles.length === 0; } // Subscription to new prompts and profile updates. private subscription = Subscription.EMPTY; constructor( private changeDetectorRef: ChangeDetectorRef, private profileService: AppProfileService, public notifService: NotificationsService, public uai: ActionIndicatorService ) { } trackPrompts: TrackByFunction = this.notifService.trackBy; ngOnInit() { // filter the stream of all notifications to only emit // prompts that are used by the privacy filter (filter:prompt prefix). const prompts$: Observable = this.notifService .new$ .pipe( map(notifs => notifs.filter(notif => { return notif.Type === NotificationType.Prompt && notif.EventID.startsWith("filter:prompt"); })), ); // each time the notification list is emitted make sure we have an // up-to-date copy of the linked application profile as well. const profiles$ = prompts$ .pipe( switchMap(notifs => { // collect all profile keys in a distict set so we don't load // them more that once. var profileKeys = new Set(); notifs.forEach(n => profileKeys.add( this.profileService.getKey(n.EventData!.Profile.Source, n.EventData!.Profile.ID) )); // load all of them in parallel return forkJoin( Array.from(profileKeys).map(key => this.profileService.getAppProfileFromKey(key)) ) }) ); // subscribe to updates on the prompt list and the related profiles. this.subscription = combineLatest([ prompts$, profiles$, ]).subscribe(([prompts, profiles]) => { let promptsByProfile = new Map(); // for each prompt, make an "extended" connection prompt by parsing the // domain and index them by profile key prompts.forEach(prompt => { // prompts must have the connection data attached. If not, ignore it // here. if (!prompt.EventData) { return; } // get the list of prompts indexed by the profile ID. if this is // the first prompt for that profile create a new array and place // it at the index. let entries = promptsByProfile.get(prompt.EventData.Profile.ID); if (!entries) { entries = []; promptsByProfile.set(prompt.EventData.Profile.ID, entries); } // Create an "extended" version of the prompt by parsing // and assigning the domain and subdomain values. let copy: ExtendedConnectionPrompt = { ...prompt, domain: null, subdomain: null, } Object.assign(copy, parseDomain(prompt.EventData.Entity.Domain)) entries.push(copy) }); // Convert the list of application profiles into a set of ProfilePrompts // objects that we can use to actually display the prompts with pagination // applied. this.profiles = profiles .filter(profile => !!promptsByProfile.get(profile.ID)) .map(profile => { const prompts = promptsByProfile.get(profile.ID)!; return { ...profile, showAll: prompts.length < PromptLimit, promptsLimited: prompts.slice(0, PromptLimit), prompts: prompts, }; }) .sort((a, b) => { if (a.ID > b.ID) { return 1; } if (a.ID < b.ID) { return -1; } return 0; }); this.changeDetectorRef.markForCheck(); }) } allow(prompt: ConnectionPrompt) { let allowActions = [ 'allow-domain-all', 'allow-serving-ip', 'allow-ip', ]; for (let i = 0; i < allowActions.length; i++) { const action = prompt.AvailableActions.find(a => a.ID === allowActions[i]) if (!!action) { this.execute(prompt, action); return; } } } block(prompt: ConnectionPrompt) { let permitActions = [ 'block-domain-all', 'block-serving-ip', 'block-ip', ]; for (let i = 0; i < permitActions.length; i++) { const action = prompt.AvailableActions.find(a => a.ID === permitActions[i]) if (!!action) { this.execute(prompt, action); return; } } } changeDefault(profile: ProfilePrompts, newDefault: 'permit' | 'block') { this.profileService .getAppProfile(profile.Source, profile.ID) .pipe( map(rawProfile => { const copy = deepClone(rawProfile); setAppSetting(copy.Config || {}, 'filter/defaultAction', newDefault) return copy }), switchMap(updatedProfile => this.profileService.saveProfile(updatedProfile)), ) .subscribe({ error: (err) => { this.uai.error('Failed to change App Settings', this.uai.getErrorMessage(err)); } }) setAppSetting(profile.Config || {}, 'filter/defaultAction', newDefault) } allowAll(profile: ProfilePrompts) { profile.prompts.forEach(prompt => this.allow(prompt)); } denyAll(profile: ProfilePrompts) { profile.prompts.forEach(prompt => this.block(prompt)); } execute(prompt: ConnectionPrompt, action: Action) { this.notifService.execute(prompt, action) .subscribe({ error: console.error, }); } ngOnDestroy() { this.subscription.unsubscribe(); } /** @private - {@link TrackByFunction} for profile prompts */ trackProfile(_: number, p: ProfilePrompts) { return p.ID; } } ================================================ FILE: desktop/angular/src/app/shared/security-lock/index.ts ================================================ export * from './security-lock'; ================================================ FILE: desktop/angular/src/app/shared/security-lock/security-lock.html ================================================

{{lockLevel?.displayText}}

See Notifications
================================================ FILE: desktop/angular/src/app/shared/security-lock/security-lock.scss ================================================ svg.shield { width: 100%; max-width: 7.25rem; transform: scale(0.95); path { top: 0px; left: 0px; transform-origin: center center; } .shield-one { transform: scale(.62); } .shield-two { animation-delay: -1.2s; opacity: .6; transform: scale(.8); } .shield-three { animation-delay: -2.5s; opacity: .4; transform: scale(1); } &.text-green-300 { filter: saturate(1.4); .shield-one { fill: var(--protection-ok-primary); } .shield-two { fill: var(--protection-ok-secondary); } .shield-three { fill: var(--protection-ok-tertiary); } .shield-warn, .shield-fail { display: none; } .shield-ok { stroke: var(--background); fill: none; transform: scale(.5); } } &.text-yellow-300 { filter: saturate(1.3); .shield-one { fill: var(--protection-warn-primary); } .shield-three, .shield-two { //animation: shield-pulse 3s linear; } .shield-two { fill: var(--protection-warn-secondary); } .shield-three { fill: var(--protection-warn-tertiary); } .shield-ok, .shield-fail { display: none; } .shield-warn { stroke: var(--background); fill: none; transform: scale(.5); } } &.text-red-300 { filter: saturate(1.3); .shield-one { fill: var(--protection-fail-primary); } .shield-three, .shield-two { //animation: shield-pulse 3s linear reverse; } .shield-two { fill: var(--protection-fail-secondary); } .shield-three { fill: var(--protection-fail-tertiary); } .shield-warn, .shield-ok { display: none; } .shield-fail { stroke: var(--background); fill: none; transform: scale(.45); } } } ================================================ FILE: desktop/angular/src/app/shared/security-lock/security-lock.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, Input, OnInit, inject } from "@angular/core"; import { SecurityLevel } from "@safing/portmaster-api"; import { combineLatest } from "rxjs"; import { StatusService, ModuleStateType, GetModuleState, ControlPauseStateData } from "src/app/services"; import { fadeInAnimation, fadeOutAnimation } from "../animations"; interface SecurityOption { level: SecurityLevel; displayText: string; class: string; subText?: string; } @Component({ selector: 'app-security-lock', templateUrl: './security-lock.html', changeDetection: ChangeDetectionStrategy.OnPush, styleUrls: ['./security-lock.scss'], animations: [ fadeInAnimation, fadeOutAnimation ] }) export class SecurityLockComponent implements OnInit { private destroyRef = inject(DestroyRef); lockLevel: SecurityOption | null = null; /** The display mode for the security lock */ @Input() mode: 'small' | 'full' = 'full' constructor( private statusService: StatusService, private cdr: ChangeDetectorRef, ) { } ngOnInit(): void { this.statusService.status$.subscribe(status => { // By default the lock is green and we are "Secure" this.lockLevel = { level: SecurityLevel.Normal, class: 'text-green-300', displayText: 'Secure', } // update the shield depending on the worst state. switch (status.WorstState.Type) { case ModuleStateType.Warning: this.lockLevel = { level: SecurityLevel.High, class: 'text-yellow-300', displayText: 'Warning' } break; case ModuleStateType.Error: this.lockLevel = { level: SecurityLevel.Extreme, class: 'text-red-300', displayText: 'Insecure' } break; } // Checking for Control:Paused state const pausedState = GetModuleState(status, 'Control', 'control:paused'); if (pausedState?.Data) { const pauseData = pausedState.Data as ControlPauseStateData; if (pauseData.Interception === true) { this.lockLevel.displayText = 'Insecure: PAUSED'; } else if (pauseData.SPN === true) { this.lockLevel.displayText = 'Secure (SPN Paused)'; } } this.cdr.markForCheck(); }); } } ================================================ FILE: desktop/angular/src/app/shared/spn-account-details/index.ts ================================================ export * from './spn-account-details'; ================================================ FILE: desktop/angular/src/app/shared/spn-account-details/spn-account-details.html ================================================

Account Details

Your Package {{ currentUser.current_plan?.name }}
Access Until {{ currentUser.subscription.ends_at | date:'medium' }}
Your Subscription {{ currentUser.current_plan?.name }}
Status {{ currentUser.subscription.state }}
Next Payment Date {{ currentUser.subscription.next_billing_date | date:'medium' }} via {{ currentUser.subscription.payment_provider }}
Access Paid Until {{ currentUser.subscription.ends_at | date:'medium' }}
Username {{ currentUser.username }}
Device Name {{ currentUser.device?.name }}
Account State {{ currentUser.state }}
Features {{ currentUser.current_plan?.feature_ids?.join(", ") }}
Device ID {{currentUser.device?.id}}
Logged in Since {{ currentUser.LoggedInAt | date:'medium' }}
Open Account Page
================================================ FILE: desktop/angular/src/app/shared/spn-account-details/spn-account-details.scss ================================================ table tr { background-color: transparent !important; } table .table-section-start { border-top: 1.5rem solid transparent; } ================================================ FILE: desktop/angular/src/app/shared/spn-account-details/spn-account-details.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, Inject, OnInit, Optional, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { SPNService, UserProfile } from "@safing/portmaster-api"; import { SFNG_DIALOG_REF, SfngDialogRef } from "@safing/ui"; import { catchError, delay, of, tap } from "rxjs"; import { ActionIndicatorService } from "../action-indicator"; @Component({ templateUrl: './spn-account-details.html', styleUrls: ['./spn-account-details.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class SPNAccountDetailsComponent implements OnInit { private destroyRef = inject(DestroyRef); /** Whether or not we're currently refreshing the user profile from the customer agent */ refreshing = false; /** Whether or not we're still waiting for the user profile to be fetched from the backend */ loadingProfile = true; currentUser: UserProfile | null = null; constructor( private spnService: SPNService, private cdr: ChangeDetectorRef, private uai: ActionIndicatorService, @Inject(SFNG_DIALOG_REF) @Optional() public dialogRef: SfngDialogRef, ) { } /** * Force a refresh of the local user account * * @private - template only */ refreshAccount() { this.refreshing = true; this.spnService.userProfile(true) .pipe( delay(1000), tap(() => { this.refreshing = false; this.cdr.markForCheck(); }), ) .subscribe() } /** * Logout of your safing account * * @private - template only */ logout() { this.spnService.logout() .pipe(tap(() => this.dialogRef?.close())) .subscribe(this.uai.httpObserver('SPN Logout', 'SPN Logout')) } ngOnInit(): void { this.loadingProfile = false; this.spnService.profile$ .pipe( takeUntilDestroyed(this.destroyRef), catchError(err => of(null)), ) .subscribe({ next: (profile) => { this.loadingProfile = false; this.currentUser = profile || null; this.cdr.markForCheck(); }, complete: () => { // Database entry deletion will complete the observer. this.loadingProfile = false; this.currentUser = null; this.cdr.markForCheck(); }, }) } } ================================================ FILE: desktop/angular/src/app/shared/spn-login/index.ts ================================================ export * from './spn-login'; ================================================ FILE: desktop/angular/src/app/shared/spn-login/spn-login.html ================================================

Safing Account Login Unlock powerful features.

You have been logged out by the account server.
Please check your account.
================================================ FILE: desktop/angular/src/app/shared/spn-login/spn-login.scss ================================================ :host { display: block; width: 100%; } .custom-form-input { background: none; @apply border-0 border-b border-buttons-light text-secondary font-medium px-0; &:active, &:focus { background: none; } } .logo-image { @apply w-16 absolute; } svg.logo-image { animation-timing-function: cubic-bezier(0.445, 0.05, 0.55, 0.95); } .spin { animation-name: spin; animation-duration: 3500ms; animation-iteration-count: infinite; animation-timing-function: linear; } .reverse { animation-name: spin-reverse; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes spin-reverse { 0% { transform: rotate(360deg); } 100% { transform: rotate(0deg); } } ================================================ FILE: desktop/angular/src/app/shared/spn-login/spn-login.ts ================================================ import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, Input, OnInit, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { SPNService, UserProfile } from "@safing/portmaster-api"; import { catchError, finalize, of } from "rxjs"; import { ActionIndicatorService } from "../action-indicator"; @Component({ selector: 'app-spn-login', templateUrl: './spn-login.html', styleUrls: ['./spn-login.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class SPNLoginComponent implements OnInit { private destroyRef = inject(DestroyRef); /** The current user profile if the user is already logged in */ profile: UserProfile | null = null; /** The value of the username text box */ username: string = ''; /** The value of the password text box */ password: string = ''; @Input() set forcedLogout(v: any) { this._forcedLogout = coerceBooleanProperty(v); } get forcedLogout() { return this._forcedLogout } private _forcedLogout = false; constructor( private spnService: SPNService, private uai: ActionIndicatorService, private cdr: ChangeDetectorRef ) { } login(): void { if (!this.username || !this.password) { return; } this.spnService.login({ username: this.username, password: this.password }) .pipe(finalize(() => { this.password = ''; })) .subscribe(this.uai.httpObserver('SPN Login', 'SPN Login')) } ngOnInit(): void { this.spnService.profile$ .pipe( takeUntilDestroyed(this.destroyRef), catchError(() => of(null)) ) .subscribe(profile => { this.profile = profile || null; if (!!this.profile) { this.username = this.profile.username; } this.cdr.markForCheck(); }); } } ================================================ FILE: desktop/angular/src/app/shared/spn-network-status/index.ts ================================================ export * from './spn-network-status'; ================================================ FILE: desktop/angular/src/app/shared/spn-network-status/spn-network-status.html ================================================

Network Status

Loading Network Status ...
  • {{ issue.title }} {{ issue.closed ? 'closed' : 'opened'}} by {{ issue.user }} {{ issue.createdAt | timeAgo }}
================================================ FILE: desktop/angular/src/app/shared/spn-network-status/spn-network-status.scss ================================================ :host { @apply block; min-width: 500px; width: 50vw; } .issue-list { width: 100%; &, ul { overflow-y: auto; } .issue { position: relative; display: flex; flex-direction: column; cursor: pointer; @apply mx-2; .header { @apply p-4; display: flex; flex-direction: column; align-items: flex-start; justify-content: center; cursor: pointer; } @apply rounded; @apply bg-cards-primary; .title { @apply mr-4; } span { word-break: keep-all; } &:not(:last-child) { margin-bottom: 0.5rem; } .body { @apply bg-cards-secondary; @apply rounded-b; @apply p-4; } .meta { @apply text-tertiary; @apply font-normal; opacity: .7; font-size: 95%; } &:hover { @apply bg-cards-tertiary; } fa-icon { position: absolute; right: 1rem; top: 1rem; opacity: .8; cursor: pointer; } } } ================================================ FILE: desktop/angular/src/app/shared/spn-network-status/spn-network-status.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, TrackByFunction, inject } from "@angular/core"; import { map } from "rxjs"; import { INTEGRATION_SERVICE } from "src/app/integration"; import { Issue, SupportHubService } from "src/app/services"; /** The name of the SPN repository used to filter SPN support hub issues. */ const SPNRepository = "spn"; /** A set of issue labels that are eligible to be displayed */ const SPNTagSet = new Set(["network status"]) interface _Issue extends Issue { expanded: boolean; } @Component({ selector: 'app-spn-network-status', templateUrl: './spn-network-status.html', styleUrls: ['./spn-network-status.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class SPNNetworkStatusComponent implements OnInit { private readonly integration = inject(INTEGRATION_SERVICE); private readonly supportHub = inject(SupportHubService); private readonly cdr = inject(ChangeDetectorRef); /** trackIssue is used as a track-by function when rendering SPN issues. */ trackIssue: TrackByFunction<_Issue> = (_: number, issue: _Issue) => issue.url; spnIssues: _Issue[] = []; ngOnInit(): void { this.supportHub.loadIssues() .pipe( map(issues => { return issues .filter(issue => issue.repository === SPNRepository && issue.labels?.some(l => { return SPNTagSet.has(l); })) .reverse() }) ) .subscribe(issues => { let spnIssues: _Issue[] = issues .map(i => { const existing = this.spnIssues.find(existing => existing.url === i.url); return { ...i, expanded: existing !== undefined ? existing.expanded : false } }) this.spnIssues = spnIssues; this.cdr.markForCheck(); }) } /** * Open a github issue in a new tab/window * * @private - template only */ openIssue(issue: Issue) { this.integration.openExternal(issue.url); } } ================================================ FILE: desktop/angular/src/app/shared/spn-status/index.ts ================================================ export * from './spn-status'; ================================================ FILE: desktop/angular/src/app/shared/spn-status/spn-status.html ================================================

SPN

Increase privacy protection Failed to connect Connecting to the SPN ... You're protected Home: {{ spnStatus?.ConnectedIP }} via {{ spnStatus?.ConnectedTransport}}
Identities {{ identities }}
================================================ FILE: desktop/angular/src/app/shared/spn-status/spn-status.ts ================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, OnInit, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, Router } from '@angular/router'; import { BoolSetting, ChartResult, ConfigService, FeatureID, Netquery, SPNService, SPNStatus, UserProfile } from "@safing/portmaster-api"; import { SfngDialogService } from '@safing/ui'; import { catchError, forkJoin, interval, of, startWith, switchMap } from "rxjs"; import { fadeInAnimation, fadeOutAnimation } from "../animations"; import { SPNAccountDetailsComponent } from '../spn-account-details'; @Component({ selector: 'app-spn-status', templateUrl: './spn-status.html', changeDetection: ChangeDetectionStrategy.OnPush, animations: [ fadeInAnimation, fadeOutAnimation, ] }) export class SPNStatusComponent implements OnInit { private destroyRef = inject(DestroyRef); /** Whether or not the SPN is currently enabled */ spnEnabled = false; /** The chart data for the SPN connection chart */ spnConnChart: ChartResult[] = []; /** The current amount of SPN identities used */ identities: number = 0; /** The current SPN user profile */ profile: UserProfile | null = null; /** The current status of the SPN module */ spnStatus: SPNStatus | null = null; /** Returns whether or not the current package has the SPN feature */ get packageHasSPN() { return this.profile?.current_plan?.feature_ids?.includes(FeatureID.SPN) } constructor( private configService: ConfigService, private spnService: SPNService, private netquery: Netquery, private cdr: ChangeDetectorRef, private router: Router, private activeRoute: ActivatedRoute, private dialog: SfngDialogService ) { } ngOnInit(): void { this.spnService .profile$ .pipe( takeUntilDestroyed(this.destroyRef), catchError(() => of(null)) ) .subscribe(profile => { this.profile = profile || null; this.cdr.markForCheck(); }); this.spnService.status$ .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(status => { this.spnStatus = status; this.cdr.markForCheck(); }) this.configService.watch("spn/enable") .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(value => { this.spnEnabled = value; // If the user disabled the SPN clear the connection chart // as well. if (!this.spnEnabled) { this.spnConnChart = []; } this.cdr.markForCheck(); }); interval(5000) .pipe( startWith(-1), takeUntilDestroyed(this.destroyRef), switchMap(() => forkJoin({ chart: this.netquery.activeConnectionChart({ tunneled: { $eq: true } }), identities: this.netquery.query({ query: { tunneled: { $eq: true }, exit_node: { $ne: "" } }, groupBy: ['exit_node'], select: [ 'exit_node', { $count: { field: '*', as: 'totalCount' } } ] }, 'spn-status-get-connections-count-per-exit-node') })) ) .subscribe(data => { this.spnConnChart = data.chart; this.identities = data.identities.length; this.cdr.markForCheck(); }) } openOrLogin() { if (this.activeRoute.snapshot.firstChild?.url[0]?.path === "spn") { this.dialog.create(SPNAccountDetailsComponent, { autoclose: true, backdrop: 'light' }) return } this.router.navigate(['/spn']) } setSPNEnabled(v: boolean) { this.configService.save(`spn/enable`, v) .subscribe(); } } ================================================ FILE: desktop/angular/src/app/shared/text-placeholder/index.ts ================================================ export { PlaceholderComponent } from './placeholder'; ================================================ FILE: desktop/angular/src/app/shared/text-placeholder/placeholder.scss ================================================ .text-placeholder { display : inline-block; height : 0.75rem; position: relative; .background { @apply rounded; opacity : 0.8; animation-duration : 6s; animation-fill-mode : forwards; animation-iteration-count: infinite; animation-name : placeHolderShimmer; animation-timing-function: linear; background : linear-gradient(to right, #4b4b4b 8%, #5a5a5a 18%, #4b4b4b 33%); position : absolute; backface-visibility : hidden; left : 0; right : 0; top : 2px; bottom : 0; } } @keyframes placeHolderShimmer { 0% { background-position: 0px 0; } 100% { background-position: 100em 0; } } ================================================ FILE: desktop/angular/src/app/shared/text-placeholder/placeholder.ts ================================================ import { AfterContentChecked, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input } from '@angular/core'; @Component({ selector: 'app-text-placeholder', template: `
`, styleUrls: ['./placeholder.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class PlaceholderComponent implements AfterContentChecked { @Input() set width(v: string | number) { if (typeof v === 'number') { this._width = `${v}px`; return } switch (v) { case 'small': this._width = '5rem'; break; case 'medium': this._width = '10rem'; break; case 'large': this._width = '15rem'; break default: this._width = v; } } get width() { return this._width; } private _width: string = '10rem'; @Input() mode: 'auto' | 'input' = 'auto'; @Input() loading = true; constructor( private elementRef: ElementRef, private changeDetector: ChangeDetectorRef, ) { } ngAfterContentChecked() { if (this.mode === 'input') { return; } const show = this.elementRef.nativeElement.innerText === ''; if (this.loading != show) { this.loading = show; this.changeDetector.detectChanges(); } } } ================================================ FILE: desktop/angular/src/app/shared/utils.ts ================================================ import { parse } from 'psl'; export interface ParsedDomain { domain: string | null; subdomain: string | null; } export function parseDomain(scope: string): ParsedDomain { // Due to https://github.com/lupomontero/psl/issues/185 // parse will throw an error for service-discovery lookups // so make sure we split them apart. const domainParts = scope.split(".") const lastUnderscorePart = domainParts.length - [...domainParts].reverse().findIndex(dom => dom.startsWith("_")) let result: ParsedDomain = { domain: null, subdomain: null, } let cleanedDomain = scope; let removedPrefix = ''; if (lastUnderscorePart <= domainParts.length) { removedPrefix = domainParts.slice(0, lastUnderscorePart).join('.') cleanedDomain = domainParts.slice(lastUnderscorePart).join('.') } const parsed = parse(cleanedDomain); if ('listed' in parsed) { result.domain = parsed.domain || scope; result.subdomain = removedPrefix; if (!!parsed.subdomain) { if (removedPrefix != '') { result.subdomain += '.'; } result.subdomain += parsed.subdomain; } } return result } export function binarySearch(array: T[], what: T, sortFunc: (a: T, b: T) => number): number { let l = 0; let h = array.length - 1; let currentIndex: number = 0; while (l <= h) { currentIndex = (l + h) >>> 1; const result = sortFunc(what, array[currentIndex]); if (result < 0) { l = currentIndex + 1; } else if (result > 0) { h = currentIndex - 1; } else { return currentIndex; } } return ~currentIndex; } export function binaryInsert(array: T[], what: T, sortFunc: (a: T, b: T) => number, duplicate = false): number { let idx = binarySearch(array, what, sortFunc); if (idx >= 0) { if (!duplicate) { return idx; } } else { // if `what` is not part of `array` than index is the bitwise complement // of the expected index in array. idx = ~idx; } array.splice(idx, 0, what) return idx; } export function objKeys(obj: T): (keyof T)[] { return Object.keys(obj) as any; } ================================================ FILE: desktop/angular/src/electron-app.d.ts ================================================ declare global { interface Window { app: AppAPI; } } export class AppAPI { /** Returns the current platform */ getPlatform(): Promise; /** The installation directory of portmaster. */ getInstallDir(): Promise; /** * Open an URL or path using an external application. * * @param pathOrUrl The path or URL to open. */ openExternal(pathOrUrl: string): Promise; /** * Creates a new URL with the file:// scheme. Works * on any platform. * * @param path The path for the file URL. */ createFileURL(path: string): Promise; /** * Returns a dataURL for the icon that is used to represent * the path on this platform. * This method only works on windows for now. On all other * platforms an empty string is returned. * * @param path The path the the binary */ getFileIcon(path: string): Promise; /** Exit the electron appliction. */ exitApp(): Promise; } ================================================ FILE: desktop/angular/src/environments/environment.prod.ts ================================================ export const environment = { production: true, portAPI: `ws://${window.location.host}/api/database/v1`, httpAPI: `http://${window.location.host}/api`, supportHub: "https://support.safing.io" }; ================================================ FILE: desktop/angular/src/environments/environment.ts ================================================ // This file can be replaced during build by using the `fileReplacements` array. // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. // The list of file replacements can be found in `angular.json`. export const environment = { production: false, portAPI: "ws://127.0.0.1:817/api/database/v1", httpAPI: "http://127.0.0.1:817/api", supportHub: "https://support.safing.io" }; /* * For easier debugging in development mode, you can import the following file * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. * * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ import 'zone.js/dist/zone-error'; // Included with Angular CLI. ================================================ FILE: desktop/angular/src/i18n/helptexts.yaml ================================================ ########### ### Example myKey: title: Tipup Example content: | This is the Markdown formatted content. This is a super cool, new feature that you will love! It even supports markdown features like: - order lists - with multiple items And :rocket: emojis ### :tada: :facepalm: url: https://docs.safing.io/?source=Portmaster urlText: Show me! nextKey: navMonitor ############## ### Navigation introTipup: title: Hey there! content: | Thanks for installing the Portmaster. intro: title: Portmaster Tips content: | Open tips to learn how the Portmaster work. Tips like this one are found throughout the Portmaster. With some tips you can tour an element or a feature, like this: nextKey: navShield navShield: title: Status Shield & Dashboard content: | The shield gives you a high level overview of Portmaster's status. If turns any other color than green, look for a notification that tells you what is going on. __Click the shield in order to open the dashboard.__ nextKey: navMonitor navMonitor: title: Network Monitor content: | Oversee and investigate everything happening on your device. nextKey: navApps buttons: - name: Take the tour action: Type: open-page Payload: monitor nextKey: networkMonitor navApps: title: Per-App Settings content: | Configure per-app settings which override the global default. nextKey: navMap buttons: - name: Take the tour action: Type: open-page Payload: apps nextKey: appsTitle navMap: title: SPN Map content: | View the SPN map and see how your connections are routed. nextKey: navSettings navSettings: title: Global Settings content: | Configure global Portmaster settings. nextKey: navSupport buttons: - name: Take the tour action: Type: open-page Payload: settings nextKey: globalSettings navSupport: title: Get Help content: | Report a bug, contact support or view the extended Portmaster docs. nextKey: navTools buttons: - name: Open Page action: Type: open-page Payload: support navTools: title: Version and Tools content: | View the Portmaster's version and use special actions and tools. nextKey: navPause navPause: title: Pause and Resume content: | Temporarily disable Portmaster's protection and network monitoring. Choose to pause SPN only or disable all protection completely. nextKey: navPower navPower: title: Shutdown and Restart content: | Shutdown or Restart Portmaster. nextKey: uiMode uiMode: title: UI Mode content: | Quickly change the amount of settings and information shown. Hidden settings are still in effect. After closing the User Interface it changes back to the default. buttons: - name: Change Default UI Mode action: Type: "open-setting" Payload: Key: "core/expertiseLevel" ############ ### Sidedash pilot-widget: title: Portmaster Status content: | This shield shows you the current state of the Portmaster: - 🟢 all is well - 🟡 something is off, please investigate - 🔴 dangerous condition, respond immediately This color code is also displayed as part of the icon in the system tray. pilot-widget-NetworkRating: title: Network Rating content: | Control your privacy even when connecting to new networks. In the Portmaster you configure settings to be active in one environment but not in the other, like allowing sensitive connections at home but not at the public library. The only thing you have to do is to change the network rating whenever you connect to a different network. nextKey: pilot-widget-NetworkRating-Trusted pilot-widget-NetworkRating-Trusted: title: "Network Rating: Trusted" content: | You trust the current network to be secure and protect you. Examples: - your home network - network of a trusted friends nextKey: pilot-widget-NetworkRating-Untrusted pilot-widget-NetworkRating-Untrusted: title: "Network Rating: Untrusted" content: | You do not trust the current network and question if it will keep you secure and private. Examples: - public WiFi of a coffeeshop, a library, a train, a hotel, ... - network of a non-tech-savvy relative nextKey: pilot-widget-NetworkRating-Danger pilot-widget-NetworkRating-Danger: title: "Network Rating: Danger" content: | You think that the current network is hacked or otherwise hostile towards you. Examples: - something suspicious is going on in your home network _Note: In the "Danger" rating the Portmaster will become very protective. This might break functionality of apps or render them useless._ broadcast-info: title: Broadcast Notifications content: | Broadcast Notifications are public messages downloaded by the Portmaster when checking for updates. The Portmaster then locally decides which messages should be displayed. url: https://github.com/safing/portmaster/issues/703 urlText: Learn More # TODO # prompt-widget: # title: Prompts # content: | # This is where you can more easily control the # connections for the specific app for the time being. # How to use? In App settings, search for "Default Action" # and set it to "Prompt". # Note: Don't set the "Prompt" setting in your browser, # you will be spammed. You have been warned. # nextKey: notification-widget # TODO # notification-widget: # title: Notifications # content: | # This informs you with what's going on with portmaster. # Ie, Updates, Errors, Warring etc ############# ### Dashboard dashboardIntro: title: Dashboard content: | The Dashboard gives you a first overview of Portmaster's active features and what is happening on your device. Unless noted otherwise, all graphs and statistics shown are based on what Portmaster has seen in the last 10 minutes and are refreshed every 10 seconds. ######################## ### Network Monitor Page networkMonitor: title: Network Activity content: | Oversee everything happening on your device. Look at all network connections of all applications and processes that were active in the last 10 minutes. Click on any app or process to investigate further. # TODO: Wait for overview to be more useful. # networkMonitor-Overview: # title: Monitor Overview # content: | # This is just a placeholder for the meantime, but this is # just the Network Monitor with 3 stats on it. # TODO: Wait for revamp of status indication. # networkMonitor-App: # title: App Activity # content: | # There are 3 colours. Ie, Green, Red, Gray. # Allowed(Green) # The colour green shows that all the connections are allowed in # the app. # Blocked(Red) # The colour red shows that all the connections are blocked in # the app. # Allowed/Blocked(Gray) # The colour gray shows that some connections are # allowed and blocked in the app. networkMonitor-App-Focus-connection-history: title: Network Activity content: | Monitor connections as they happen. Click on any connection to view details and to take action.

2k+ Status Summary

Grouped connections have a colored bar showing the total amount of connections, as well as the percentage between allowed (green) and blocked/failed connections (grey).

An individual connection has three states:
Allowed
Blocked
Failed
If the circle is full, your _current_ settings allowed or blocked the connection.
If the circle is empty, _previous_ settings allowed or blocked the connection. Your current settings could decide differently. ######################## ### Global Settings Page globalSettings: title: Global Settings content: | Here you can set system-wide preferences and configure default rules for all your apps and connections. It is easy to create a stricter global ruleset and then create exceptions in the app settings, which override the global default. ######################### ### Per-App Settings Page appsTitle: title: Application Overview content: | All applications or processes that the Portmaster saw being active on the network are listed and can be configured here. Apps are categorized and only appear once: - **Active:** apps that are currently active and visible in the Network Monitor - **Recently Used:** apps that were active some time within the last week - **Recently Edited:** apps whose settings were edited within the last week - **Other:** all other apps appSettings: title: App Settings content: | Here you can configure app-specific settings which override the global settings. It is easy to create a stricter global ruleset and then create exceptions in the app settings, which override the global default. nextKey: appSettings-Filter appSettings-Filter: title: Display Mode content: | Quickly change what settings are displayed: **View Active:**
Only show app-specific settings which override the global default. **View All:**
Show all settings. App-specific settings which override the global default are highlighted. appSettings-QuickSettings: title: Quickly Change the Most Important Settings content: | __Block Connections__ Set the default action for when nothing else allows or blocks an outgoing connection. When other settings might overwrite this, a yellow dot next to the toggle will inform you of possible exceptions. __SPN__ Quickly enable or disable SPN for this app. When other settings might overwrite this, a yellow dot next to the toggle will inform you of possible exceptions. __Keep History__ Save connections in a database (on disk) in order to view and search them later. Changes might take a couple minutes to apply to all connections. ######################### ### Support Page support-page-related-issues: title: Local Issue Search content: | Public issues are only searched for locally so no data leaves your device until you decide so. The public GitHub issues are downloaded via our support system to prevent exposure to GitHub. ######################### ### Configuration Options spn: title: Safing Privacy Network content: | The Safing Privacy Network (SPN) is a Portmaster Add-On that protects your identity and Internet traffic from prying eyes. It spreads your connections over multiple server, letting you access the Internet from many places at once in order to effectively hide your tracks. url: https://safing.io/spn/?source=Portmaster urlText: Learn More ########################### # Process Matching and Fingerprints process-tags: title: Process Tags content: Tags holds special metadata of processes and are gathered by Portmaster. You can use these tags in fingerprints to better match processes, which would otherwise be a lot more difficult or impossible to match correctly. ================================================ FILE: desktop/angular/src/i18n/helptexts.yaml.d.ts ================================================ declare module 'js-yaml-loader!*' { import { Action } from "src/app/services/notifications.types"; export interface Button { name: string; action: Action; nextKey?: string; } export interface TipUp { title: string; content: string; url?: string; urlText?: string; buttons?: Button[]; nextKey?: string; } export interface HelpTexts { [key: string]: TipUp; } const content: HelpTexts; export default content; } ================================================ FILE: desktop/angular/src/index.html ================================================ Portmaster ================================================ FILE: desktop/angular/src/main.ts ================================================ import { enableProdMode, importProvidersFrom } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; import { INTEGRATION_SERVICE, integrationServiceFactory } from './app/integration'; import { bootstrapApplication } from '@angular/platform-browser'; import { PromptWidgetComponent } from './app/shared/prompt-list'; import { PromptEntryPointComponent } from './app/prompt-entrypoint/prompt-entrypoint'; import { provideHttpClient } from '@angular/common/http'; import { provideRouter } from '@angular/router'; import { PortmasterAPIModule } from '@safing/portmaster-api'; import { NotificationsService } from './app/services'; import { TauriIntegrationService } from './app/integration/taur-app'; if (environment.production) { enableProdMode(); } if (typeof (CSS as any)['registerProperty'] === 'function') { (CSS as any).registerProperty({ name: '--lock-color', syntax: '*', inherits: true, initialValue: '10, 10, 10' }) } function handleExternalResources(e: Event) { // TODO: // This code executes "openExternal()" when any "" element in the app is clicked. // This could potentially be a security issue. // We should consider restricting this to only external links that belong to a certain domain (e.g., https://safing.io). // get click target let target: HTMLElement | null = e.target as HTMLElement; // traverse until we reach element "" while (!!target && target.tagName !== "A") { target = target.parentElement; } if (!!target) { let href = target.getAttribute("href"); if (href?.startsWith("blob")) { return } if (!!href && !href.includes(location.hostname)) { e.preventDefault(); integrationServiceFactory().openExternal(href); } } } if (document.addEventListener) { document.addEventListener("click", handleExternalResources); } // load the font file but make sure to use the slimfix version // windows. { // we cannot use document.writeXX here as it's not allowed to // write to Document from an async loaded script. let linkTag = document.createElement("link"); linkTag.rel = "stylesheet"; linkTag.href = "/assets/fonts/roboto.css"; if (navigator.platform.startsWith("Win")) { linkTag.href = "/assets/fonts/roboto-slimfix.css" } document.head.appendChild(linkTag); } if (location.pathname !== "/prompt") { // bootstrap our normal application platformBrowserDynamic().bootstrapModule(AppModule) .catch(err => console.error(err)); } else { // bootstrap the prompt interface console.log("[INFO] Bootstrapping prompt entry point."); bootstrapApplication(PromptEntryPointComponent, { providers: [ provideHttpClient(), importProvidersFrom(PortmasterAPIModule.forRoot({ websocketAPI: "ws://localhost:817/api/database/v1", httpAPI: "http://localhost:817/api" })), NotificationsService, { provide: INTEGRATION_SERVICE, useClass: TauriIntegrationService } ], }) } ================================================ FILE: desktop/angular/src/polyfills.ts ================================================ /*************************************************************************************************** * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. */ import '@angular/localize/init'; /** * This file includes polyfills needed by Angular and is loaded before the app. * You can add your own extra polyfills to this file. * * This file is divided into 2 sections: * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. * 2. Application imports. Files imported after ZoneJS that should be loaded before your main * file. * * The current setup is for so-called "evergreen" browsers; the last versions of browsers that * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. * * Learn more in https://angular.io/guide/browser-support */ /*************************************************************************************************** * BROWSER POLYFILLS */ /** * By default, zone.js will patch all possible macroTask and DomEvents * user can disable parts of macroTask/DomEvents patch by setting following flags * because those flags need to be set before `zone.js` being loaded, and webpack * will put import in the top of bundle, so user need to create a separate file * in this directory (for example: zone-flags.ts), and put the following flags * into that file, and then add the following code before importing zone.js. * import './zone-flags'; * * The flags allowed in zone-flags.ts are listed here. * * The following flags will work for all browsers. * * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames * * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js * with the following flag, it will bypass `zone.js` patch for IE/Edge * * (window as any).__Zone_enable_cross_context_check = true; * */ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ import 'zone.js/dist/zone'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS */ ================================================ FILE: desktop/angular/src/styles.scss ================================================ // // Import our complete theme, order is important! // @import 'theme/_colors.scss'; @import 'theme/_tailwind.scss'; @import '@angular/cdk/overlay-prebuilt'; @import 'theme/_button.scss'; @import 'theme/_drag-n-drop.scss'; @import 'theme/_inputs.scss'; @import 'theme/_scroll.scss'; @import 'theme/_search.scss'; @import 'theme/_trust-level.scss'; @import 'theme/_verdict.scss'; @import 'theme/_typography.scss'; @import 'theme/_markdown.scss'; @import 'theme/_card.scss'; @import 'theme/_breadcrumbs.scss'; @import 'theme/_dialog.scss'; @import 'theme/_table.scss'; @import 'theme/_pill.scss'; @import 'safing/ui/theming'; *[routerlink] { cursor: pointer; } .form-field { display: flex; justify-content: flext-start; align-items: center; *:not(:last-child) { @apply mr-1; } } .sidebar { @apply bg-background; height: 100vh; flex-shrink: 0; flex-grow: 0; @apply px-2; display: flex; flex-direction: column; &.no-scroll { @apply px-0; } } .main { .content { flex-grow: 1; @apply pl-12; @apply pr-16; @apply mr-4; overflow: auto; } .header { display: flex; width: 100%; @apply pl-12; @apply pr-5; @apply mb-2; align-items: center; height: 3rem; flex-shrink: 0; &:first-of-type { @apply mt-2; } >* { flex-grow: 1; margin: 0; } >app-expertise { flex-grow: 0; flex-shrink: 0; } } } .tableFixHead { overflow-y: auto; } .tableFixHead thead th { position: sticky; top: 0; } fa-icon.tipup, fa-icon[icon="question-circle"], fa-icon[icon="question"] { max-width: 10px; max-height: 10px; opacity: 0.8; display: inline-block; font-size: 0.75rem; color: rgb(250 250 250 / 55%); margin-left: 3px; &:hover { opacity: unset; } } .tipup-preview { transition: all .25s ease-in-out !important; opacity: 0 !important; &.visible { opacity: 1 !important; } } ================================================ FILE: desktop/angular/src/test.ts ================================================ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'zone.js/dist/zone-testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, platformBrowserDynamicTesting() ); ================================================ FILE: desktop/angular/src/theme/_breadcrumbs.scss ================================================ h4.breadcrumbs { * { margin-left : 0.125rem; margin-right: 0.125rem; } span { outline: none; @apply text-secondary; &:hover { @apply text-primary; text-decoration: underline; } &:last-of-type { @apply text-primary; } } } ================================================ FILE: desktop/angular/src/theme/_button.scss ================================================ @layer components { button { @apply text-xs; @apply bg-buttons-dark; @apply p-1; @apply px-4; @apply capitalize; @apply rounded-sm; @apply font-medium; @apply focus:underline focus:underline-offset-4; user-select: none; outline: none; cursor: pointer; font-size: 0.7rem; &.btn-outline { background: transparent; opacity: 0.6; } &:hover { &:not(.outline):not(.bg-blue) { @apply bg-buttons-light; } opacity: 1; } &:disabled { @apply cursor-default; opacity: 0.3; &:not(.outline):hover { @apply bg-buttons-dark; } } &:active { @apply bg-buttons-dark; } &:hover, &:focus, &:active { outline: none; } } .info-circle { width: 18px; height: 18px; display: flex; justify-content: center; align-items: center; font-size: 0.8em; @apply rounded-full; @apply bg-buttons-dark; } } ================================================ FILE: desktop/angular/src/theme/_card.scss ================================================ .card-header { display : flex; align-items : center; cursor : pointer; outline : none; justify-content: space-between; @apply text-xs; @apply font-medium; margin-top : 5px; padding-top : 0.65rem; padding-bottom : 0.65rem; padding-left : 0.65rem; padding-right : 0.65rem; border-top-left-radius : 4px; border-top-right-radius: 4px; background-color : #202020e0; &:not(.open) { border-radius: 4px; } &>*:not(:last-child) { @apply mr-1; } &>app-icon:not(:last-child) { @apply mr-2; } &:hover { background-color: #292929b0; } &.active { background-color: #303030; app-count-indicator { background-color: #474747; div.state { background-color: #5c5c5c; } } } &>app-icon { --app-icon-size: 22px; } .card-title { flex-grow : 1; overflow : hidden; white-space : nowrap; text-overflow: ellipsis; font-size : 0.7rem; font-weight : 600; color : #cacaca; margin-left : 3px; .card-sub-title { display : block; font-size : 0.8em; margin-top: -3px; @apply text-tertiary; text-overflow: ellipsis; overflow : hidden; } } .card-actions { @apply mr-2; span { display : inline-block; text-align: center; min-width : 5rem; @apply px-2; @apply rounded; @apply text-xs; padding-top : 0.1rem; padding-bottom: 0.1rem; // TODO(ppacher): this is actually a "toggle-switch" / radio-button // component. make it one. &.selected { @apply bg-buttons-dark; } &:hover { @apply bg-buttons-light; } } } } .card-content { @apply bg-cards-secondary; @apply rounded-b; @apply py-2; @apply px-4; @apply mb-2; display : flex; flex-direction : column; flex : 1 0; justify-content: space-between; } ================================================ FILE: desktop/angular/src/theme/_colors.scss ================================================ /** * For debugging purposes, we define all our colors as * CSS3 variables and make tailwind put a reference to those * variables. This way we will see the variable name in the * developer-tools instead of the hex/rgba values. * * You're welcome 🚀 */ :root { --background: #121213; --text-primary : #ffffff; --text-secondary: #ababab; --text-tertiary : #888888; --cards-primary : #222222; --cards-secondary : #1b1b1b; --cards-secondary-rgb: 27, 27, 27; --cards-tertiary : #2c2c2c; --button-icon : #ababab; --button-dark : #343434; --button-light: #474747; --info-green : #3df57f; --info-red : #d12e2e; --info-gray : #ababab; --info-blue : #4e97fa; --info-yellow : #e9d31d; --info-yellow-rgb: 233, 211, 29; --protection-ok-primary : rgb(29, 233, 102); --protection-ok-secondary: rgb(24, 130, 61); --protection-ok-tertiary : rgb(20, 61, 36); --protection-warn-primary : rgb(233, 216, 29); --protection-warn-secondary: rgb(130, 121, 24); --protection-warn-tertiary : rgb(61, 58, 20); --protection-fail-primary : rgb(224, 29, 29); --protection-fail-secondary: rgb(129, 24, 24); --protection-fail-tertiary : rgb(61, 20, 20); --portmaster-plus: #2fcfae; --portmaster-pro: #029ad0; } ================================================ FILE: desktop/angular/src/theme/_dialog.scss ================================================ .dialog-screen-backdrop { backdrop-filter : blur(10px); background-color: rgba(#000000, 0.7); } .dialog-screen-backdrop-light { backdrop-filter : blur(3px); background-color: rgba(#000000, 0.4); } ================================================ FILE: desktop/angular/src/theme/_drag-n-drop.scss ================================================ .cdk-drag { .widget { user-select: none; fa-icon { opacity: 1; } } } .cdk-drag-placeholder { user-select: none; position: relative; opacity: 0.5; box-sizing: border-box; cursor: grabbing !important; @apply border-2; @apply rounded; @apply border-dashed; border-color: #292929; } .cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); user-select: none; } .cdk-drag-preview { user-select: none; box-sizing: border-box; cursor: grabbing !important; @apply rounded; @apply border-2; @apply border-dashed; border-color: #292929; @apply text-primary; } .cdk-drag-handle { cursor: grab !important; } .document-grabbing { cursor: grabbing !important; } ================================================ FILE: desktop/angular/src/theme/_inputs.scss ================================================ input:not([type="checkbox"]), textarea, select { @apply outline-none w-full block; @apply bg-gray-300 rounded; @apply text-xs text-primary; @apply border border-gray-300; @apply rounded-sm font-medium; @apply p-1.5; transition: border cubic-bezier(0.175, 0.885, 0.32, 1.275) .3s; &::placeholder { @apply text-secondary text-xxs; } &:active, &:focus { @apply text-primary; @apply bg-gray-500 border-gray-400 bg-opacity-75 border-opacity-75; &::placeholder { @apply text-tertiary; } } } input, textarea, select { .ng-invalid { @apply border-red; } } ================================================ FILE: desktop/angular/src/theme/_markdown.scss ================================================ // Mostly taken from https://github.com/sindresorhus/github-markdown-css/blob/gh-pages/license markdown { width: 100%; @apply p-2; color: white !important; @apply font-normal; details { display: block; } summary { display: list-item; } a { background-color: initial; } a:active, a:hover { outline-width: 0; } strong { font-weight: inherit; font-weight: bolder; } h1 { font-size: 2rem; margin: .67rem 0; } img { border-style: none; } code, kbd, pre { font-family: monospace, monospace; font-size: 1rem; } hr { box-sizing: initial; height: 0; overflow: visible; } input { font: inherit; margin: 0; } input { overflow: visible; } [type=checkbox] { box-sizing: border-box; padding: 0; } * { box-sizing: border-box; } input { font-family: inherit; font-size: inherit; line-height: inherit; } a { text-decoration: none; } a:hover { text-decoration: underline; } strong { font-weight: 600; } hr { height: 0; margin: 15px 0; overflow: hidden; background: transparent; border: 0; border-bottom: 1px solid #dfe2e5; } hr:after, hr:before { display: table; content: ""; } hr:after { clear: both; } table { border-spacing: 0; border-collapse: collapse; } td, th { padding: 0; } details summary { cursor: pointer; } kbd { display: inline-block; padding: 3px 5px; font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace; line-height: 10px; vertical-align: middle; background-color: #fafbfc; border: 1px solid #d1d5da; border-radius: 3px; box-shadow: inset 0 -1px 0 #d1d5da; } h1, h2, h3, h4, h5, h6 { margin-top: 0; margin-bottom: 0; } h1 { font-size: 32px; } h1, h2 { font-weight: 600; } h2 { font-size: 24px; } h3 { font-size: 20px; } h3, h4 { font-weight: 600; } h4 { font-size: 16px; } h5 { font-size: 14px; } h5, h6 { font-weight: 600; } h6 { font-size: 12px; } p { margin-top: 0; margin-bottom: 10px; } blockquote { margin: 0; } ol, ul { padding-left: 0; margin-top: 0; margin-bottom: 0; } ol { list-style-type: decimal; } ul { list-style-type: circle; } ol ol, ul ol { list-style-type: lower-roman; } ol ol ol, ol ul ol, ul ol ol, ul ul ol { list-style-type: lower-alpha; } dd { margin-left: 0; } code, pre { font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace; font-size: 12px; } pre { margin-top: 0; margin-bottom: 0; } input::-webkit-inner-spin-button, input::-webkit-outer-spin-button { margin: 0; -webkit-appearance: none; appearance: none; } a:not([href]) { color: inherit; text-decoration: none; } blockquote, details, dl, ol, p, pre, table, ul { margin-top: 0; // be carefully when ever changing this! margin-bottom: 16px; } hr { height: .25rem; padding: 0; margin: 24px 0; background-color: #e1e4e8; border: 0; } blockquote { padding: 0 1rem; border-left: .25rem solid #dfe2e5; } blockquote>:first-child { margin-top: 0; } blockquote>:last-child { margin-bottom: 0; } h1, h2, h3, h4, h5, h6 { margin-top: 24px; margin-bottom: 16px; font-weight: 600; line-height: 1.25; } h1 { font-size: 2rem; } h1, h2 { padding-bottom: .3rem; border-bottom: 1px solid #eaecef; } h2 { font-size: 1.5rem; } h3 { font-size: 1.25rem; } h4 { font-size: 1rem; } h5 { font-size: .875rem; } h6 { font-size: .85rem; } ol, ul { padding-left: 2rem; } ol ol, ol ul, ul ol, ul ul { margin-top: 0; margin-bottom: 0; } li { word-wrap: break-all; } li>p { margin-top: 16px; } li+li { margin-top: .25rem; } dl { padding: 0; } dl dt { padding: 0; margin-top: 16px; font-size: 1rem; font-style: italic; font-weight: 600; } dl dd { padding: 0 16px; margin-bottom: 16px; } table { display: block; width: 100%; overflow: auto; } table th { font-weight: 600; } table td, table th { padding: 6px 13px; border: 1px solid #dfe2e5; } table tr { background-color: #fff; border-top: 1px solid #c6cbd1; } table tr:nth-child(2n) { background-color: #f6f8fa; } img { max-width: 100%; box-sizing: initial; background-color: #fff; } img[align=right] { padding-left: 20px; } img[align=left] { padding-right: 20px; } code { padding: .2rem .4rem; margin: 0; font-size: 95%; background-color: rgba(27, 31, 35, .05); border-radius: 3px; } pre { word-wrap: normal; } pre>code { padding: 0; margin: 0; font-size: 100%; word-break: normal; white-space: pre; background: transparent; border: 0; } .highlight { margin-bottom: 16px; } .highlight pre { margin-bottom: 0; word-break: normal; } .highlight pre, pre { padding: 16px; overflow: auto; font-size: 90%; line-height: 1.45; background-color: #f6f8fa; border-radius: 3px; } pre code { display: inline; max-width: auto; padding: 0; margin: 0; overflow: visible; line-height: inherit; word-wrap: normal; background-color: initial; border: 0; } } ================================================ FILE: desktop/angular/src/theme/_pill.scss ================================================ @import 'mixins/_pill.scss'; .pill-container { @include pill-container; @apply pl-2; @apply bg-buttons-dark; } ================================================ FILE: desktop/angular/src/theme/_scroll.scss ================================================ html, body { scroll-behavior: smooth; } ::-webkit-scrollbar { @apply bg-buttons-dark; width: 4px; } ::-webkit-scrollbar-thumb { @apply bg-buttons-light; @apply rounded; cursor: pointer; } .no-scroll { overflow: hidden; } .scrollable { width : 100%; max-height: 100%; overflow : auto; overflow-x: hidden; flex-grow : 1; @apply px-3; } ================================================ FILE: desktop/angular/src/theme/_search.scss ================================================ em.search-result { @apply text-background; @apply bg-yellow; @apply border; @apply border-yellow; @apply rounded-sm; text-decoration: none; font-style: inherit; } ================================================ FILE: desktop/angular/src/theme/_table.scss ================================================ table:not(.custom) { width: 100%; th, tr, td { @apply text-xs; } th { text-align: left; @apply text-secondary; z-index: 1; } td, th { @apply p-2; @apply font-medium; } tr:nth-child(even) { @apply bg-cards-secondary; --bg-opacity: 0.5; } tr:nth-child(odd) { @apply bg-cards-tertiary; --bg-opacity: 0.6; } tr.cdk-header-row th { @apply bg-cards-tertiary; --bg-opacity: 1; // we cannot use borders directly due to // the sticky header. Use a box-shadow to // simulate a border. box-shadow: 0 2px rgba(0, 0, 0, 0.3); } } ================================================ FILE: desktop/angular/src/theme/_tailwind.scss ================================================ /** The tailwind post-processor will inject all tailwind styles here **/ @import 'tailwindcss/base'; @import 'tailwindcss/components'; @import 'tailwindcss/utilities'; ================================================ FILE: desktop/angular/src/theme/_trust-level.scss ================================================ span.trust-level { display : inline-block; position : relative; width : 6px; user-select: none; overflow : visible; &~* { @apply ml-2; } &:before { content : ""; display : block; position : relative; height : 6px; width : 6px; top : -1px; left : 0px; border-radius: 50%; } &.centered:before { top: 0px; } &:before { background-color: var(--bg-color); @apply shadow-inner-xs; } &.pulse:before { animation : pulsate-trust 1s ease-out infinite; box-shadow: 0 0 10px var(--glow-color); } &.off { --bg-color : theme('colors.info.gray'); --glow-color: theme('colors.info.gray'); } &.auto { --bg-color : theme('colors.info.blue'); --glow-color: theme('colors.info.blue'); } &.low { --bg-color : theme('colors.info.green'); --glow-color: theme('colors.info.green'); } &.medium { --bg-color : theme('colors.info.yellow'); --glow-color: theme('colors.info.yellow'); } &.high { --bg-color : theme('colors.info.red'); --glow-color: theme('colors.info.red'); } } @keyframes pulsate-trust { 100% { opacity: 0.8; } 0% { background: var(--glow-color); box-shadow: 0 0 0 var(--glow-color); opacity : 1; } } ================================================ FILE: desktop/angular/src/theme/_typography.scss ================================================ html, body { font-family: 'Roboto', sans-serif; @apply text-primary; @apply font-medium; } body, .primary-text, .secondary-text { @apply text-xs; @apply font-medium; } label, .secondary-text { @apply text-secondary; } .primary-text { @apply text-primary; } .tertiary-text { @apply text-tertiary; } h1, h2, h3 { @apply text-primary; } h1 { display: block; @apply mb-1; @apply text-xl; @apply font-normal; @apply mb-2; } h2 { @apply p-2; @apply ml-2; @apply text-lg; @apply font-medium; letter-spacing: -0.01rem; } h3 { @apply mb-1; @apply text-base; @apply font-medium; } h4 { @apply text-xs; @apply font-medium; @apply text-tertiary; } ================================================ FILE: desktop/angular/src/theme/_verdict.scss ================================================ span.verdict { display : inline-block; position : relative; width : 12px; height : 9px; align-self : center; justify-self: center; user-select : none; overflow : visible; &:before { content : ""; display : block; position : absolute; height : 8px; width : 8px; top : 0px; left : 0px; border-radius : 50%; background-color: var(--bg-color); border : 1px solid var(--bg-color); @apply shadow-inner-xs; } &.failed { --bg-color: theme('colors.info.yellow'); } &.accept, &.reroutetons, &.reroutetotunnel { --bg-color: theme('colors.info.green'); } &.block, &.drop { --bg-color: theme('colors.info.red'); } &.outdated { &:before { background-color: transparent; border-color : var(--bg-color); opacity : .85; } } } ================================================ FILE: desktop/angular/src/theme/mixins/_pill.scss ================================================ @mixin pill-container { display : flex; width : auto; height : 18px; align-items : center; justify-content: flex-end; font-size : 0.6rem; line-height : 18px; border-radius: 0.5rem; transform : scale(0.95); .counter { flex-grow : 1; display : inline-block; text-align : right; padding-right: 4px; padding-left : 2px; color : #999999ee; font-size : 0.65rem; font-weight : 800; width : max-content; } .pill { display : inline-block; width : 29px; height : 5px; background-color: #686868; border-radius : 1rem; overflow : hidden; margin-left : 0.2rem; margin-right : 0.6rem; .percentage { display : block; height : 100%; width : 75%; background-color: #21ad58; } } } ================================================ FILE: desktop/angular/src/theme.less ================================================ // Custom Theming for NG-ZORRO // For more information: https://ng.ant.design/docs/customize-theme/en @import "../node_modules/ng-zorro-antd/ng-zorro-antd.dark.less"; ================================================ FILE: desktop/angular/tailwind.config.js ================================================ const plugin = require("tailwindcss/plugin"); module.exports = { content: [ "./src/**/*.{html,scss,css,ts}", "./projects/**/*.{html,scss,css,ts}", ], theme: { colors: { transparent: "transparent", current: "currentColor", white: "#ffffff", background: "#121213", gray: { 100: "#131111", 200: "#1b1b1b", 300: "#222222", 400: "#2c2c2c", 500: "#474747", 600: "#888888", 700: "#ababab", DEFAULT: "#ababab", }, green: { 100: "#143d24", 200: "#18823d", 300: "#1de966", DEFAULT: "#18823d", }, red: { 100: "#3d1414", 200: "#811818", 300: "#e01d1d", DEFAULT: "#d12e2e", }, yellow: { 100: "#3d3a14", 200: "#827918", 300: "#e9d81d", DEFAULT: "#e9d81d", }, cyan: { 100: "#b2ebf2", 200: "#80deea", 300: "#4dd0e1", 400: "#26c6da", 500: "#00bcd4", 600: "#00acc1", 700: "#0097a7", 800: "#00838f", 900: "#006064", }, deepPurple: { 50: "#ede7f6", 100: "#d1c4e9", 200: "#b39ddb", 300: "#9575cd", 400: "#7e57c2", 500: "#673ab7", 600: "#5e35b1", 700: "#512da8", 800: "#4527a0", 900: "#311b92", }, blue: { DEFAULT: "#4e97fa", }, // Legacy color definitions // The overall application background color // Text shades cards: { primary: "var(--cards-primary)", secondary: "var(--cards-secondary)", tertiary: "var(--cards-tertiary)", }, buttons: { icon: "var(--button-icon)", dark: "var(--button-dark)", light: "var(--button-light)", }, info: { green: "var(--info-green)", red: "var(--info-red)", gray: "var(--info-gray)", blue: "var(--info-blue)", yellow: "var(--info-yellow)", }, }, textColor: (theme) => { return { primary: theme("colors.white"), secondary: theme("colors.gray.700"), tertiary: theme("colors.gray.600"), ...theme("colors"), }; }, extend: { boxShadow: { xs: "0 0 0 1px rgba(0, 0, 0, 0.05)", "inner-xs": "inset 0 2px 4px 0 rgba(0, 0, 0, 0.16)", }, fontSize: { xxs: "0.7rem", }, }, }, plugins: [ plugin(function ({ addVariant, theme }) { Object.keys(theme("screens")).forEach((key) => { addVariant("sfng-" + key, ".min-width-" + theme("screens")[key] + " &"); }); }), ], }; ================================================ FILE: desktop/angular/tsconfig.app.json ================================================ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/app", "types": [ ] }, "files": [ "src/main.ts", "src/polyfills.ts" ], "include": [ "src/**/*.d.ts" ] } ================================================ FILE: desktop/angular/tsconfig.json ================================================ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "compileOnSave": false, "compilerOptions": { "paths": { "@safing/portmaster-api": [ "dist-lib/safing/portmaster-api" ], "@safing/ui": [ "dist-lib/safing/ui" ] }, "baseUrl": "./", "outDir": "./dist/out-tsc", "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "sourceMap": true, "declaration": false, "downlevelIteration": true, "experimentalDecorators": true, "moduleResolution": "node", "importHelpers": true, "target": "ES2022", "module": "es2020", "lib": [ "es2018", "dom" ], "types": [ "./src/electron-app.d.ts", "chrome" ], "useDefineForClassFields": false }, "angularCompilerOptions": { "strictInjectionParameters": true, "strictTemplates": true } } ================================================ FILE: desktop/angular/tsconfig.spec.json ================================================ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", "types": [ "jasmine" ] }, "files": [ "src/test.ts", "src/polyfills.ts" ], "include": [ "src/**/*.spec.ts", "src/**/*.d.ts", "src/app/widgets/status-widget-factory/settings.ts" ] } ================================================ FILE: desktop/tauri/.cargo/config.toml ================================================ [target.aarch64-unknown-linux-gnu] linker = "aarch64-linux-gnu-gcc" rustflags = ["-C", "link-args=-L/usr/lib/aarch64-linux-gnu/"] [target.armv7-unknown-linux-gnueabihf] linker = "arm-linux-gnueabihf-gcc" rustflags = ["-C", "link-args=-L/usr/lib/arm-linux-gnueabihf/"] ================================================ FILE: desktop/tauri/.vscode/launch.json ================================================ { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // How to debug Tauri project: // https://v2.tauri.app/develop/debug/ // "version": "0.2.0", "configurations": [ { "type": "lldb", // `vscode-lldb` extension has to be installed (https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) "request": "launch", "name": "Debug Dev", "cargo": {"args": ["build", "--manifest-path=./src-tauri/Cargo.toml", "--no-default-features"]}, "env": { "TAURI_PM_URL": "http://127.0.0.1:817" } }, { "type": "lldb", "request": "launch", "name": "Debug Prod", "cargo": {"args": ["build", "--manifest-path=./src-tauri/Cargo.toml", "--release"]}, }, { "name": "Debug Dev (VS Win Debugger)", "type": "cppvsdbg", "request": "launch", "program": "${workspaceRoot}/src-tauri/target/debug/portmaster.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "build:debug", "env": { "TAURI_PM_URL": "http://127.0.0.1:817" } } ] } ================================================ FILE: desktop/tauri/.vscode/tasks.json ================================================ { // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "label": "build:debug", "type": "cargo", "command": "build", "args": ["--manifest-path=./src-tauri/Cargo.toml", "--no-default-features"] } ] } ================================================ FILE: desktop/tauri/src-tauri/.gitignore ================================================ # Generated by Cargo # will have compiled files and executables /target/ ================================================ FILE: desktop/tauri/src-tauri/Cargo.toml ================================================ [package] name = "portmaster" version = "2.1.8" description = "Portmaster UI" authors = ["Safing"] license = "" repository = "" default-run = "portmaster" edition = "2021" rust-version = "1.64" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] tauri-build = { version = "2.0.5", features = [] } [dependencies] # Tauri tauri = { version = "2.2.5", features = ["tray-icon", "image-png", "config-json5", "devtools"] } tauri-plugin-shell = "2.2.1" tauri-plugin-dialog = "2.2.0" tauri-plugin-clipboard-manager = "2.1.11" tauri-plugin-os = "2.2.0" tauri-plugin-single-instance = "2.2.1" tauri-plugin-notification = "2.2.1" tauri-plugin-log = "2.2.1" tauri-plugin-window-state = "2.2.1" tauri-plugin-websocket = "2" clap_lex = "0.7.2" # General serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } futures-util = { version = "0.3", features = ["sink"] } dirs = "1.0" rust-ini = "0.20.0" dataurl = "0.1.2" uuid = "1.6.1" lazy_static = "1.4.0" tokio = { version = "1.44.2", features = ["macros"] } cached = "0.46.1" notify-rust = "4.10.0" assert_matches = "1.5.0" bytes = "1.5" tokio-websockets = { version = "0.5.0", features = ["client", "ring", "rand"] } sha = "1.0.3" http = "1.0.0" url = "2.5.0" thiserror = "1.0" log = "0.4.21" reqwest = { version = "0.12", features = ["cookies", "json"] } rfd = { version = "*", default-features = false, features = [ "tokio", "gtk3", "common-controls-v6" ] } open = "5.1.3" chrono = { version = "0.4", features = ["serde"] } dark-light = "2.0.0" # Linux only [target.'cfg(target_os = "linux")'.dependencies] glib = "0.18.4" gtk-sys = "0.18.0" glib-sys = "0.18.1" gdk-pixbuf = "0.18.3" gdk-pixbuf-sys = "0.18.0" gio-sys = "0.18.1" # Windows only [target.'cfg(target_os = "windows")'.dependencies] windows-service = "0.6.0" windows = { version = "0.54.0", features = ["Win32_Foundation", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] } tauri-winrt-notification = "0.3.0" [dev-dependencies] which = "6.0.0" gtk = "0.18" ctor = "0.2.6" [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. # If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes. # DO NOT REMOVE!! custom-protocol = [ "tauri/custom-protocol" ] [package.metadata.clippy] allow = ["clippy::collapsible_else_if"] ================================================ FILE: desktop/tauri/src-tauri/Cross.toml ================================================ [target.aarch64-unknown-linux-gnu] image = "ghcr.io/cross-rs/aarch64-unknown-linux-gnu:main" pre-build = [ "dpkg --add-architecture $CROSS_DEB_ARCH", "apt-get update && apt-get --assume-yes install libssl-dev:$CROSS_DEB_ARCH libjavascriptcoregtk-4.0-dev:$CROSS_DEB_ARCH librsvg2-dev libayatana-appindicator3-dev libwebkit2gtk-4.0-dev libsoup2.4-dev libgtk-3-dev" ] ================================================ FILE: desktop/tauri/src-tauri/README.md ================================================ # Update Tauri guide Check latest versions of tauri packages and update them accordingly (https://crates.io/) Cargo.toml: ```toml [build-dependencies] tauri-build = { version = "2.0.0-beta.19", features = [] } # Update to latest [dependencies] # Tauri tauri = { version = "2.0.0-beta.24", features = ["tray-icon", "image-png", "config-json5", "devtools"] } # Update to latest tauri-plugin-shell = "2.0.0-beta" tauri-plugin-dialog = "2.0.0-beta" tauri-plugin-clipboard-manager = "2.0.0-beta" tauri-plugin-os = "2.0.0-beta" tauri-plugin-single-instance = "2.0.0-beta" tauri-plugin-cli = "2.0.0-beta" tauri-plugin-notification = "2.0.0-beta" tauri-plugin-log = "2.0.0-beta" tauri-plugin-window-state = "2.0.0-beta" tauri-cli = "2.0.0-beta.21" # Update to latest ``` Run: ```sh cargo update ``` > Make sure to update the npm tauri plugin dependencies to have the same version as the rust plugins. (desktop/angular) ## Update WIX installer template > If the migration functionality is not needed anymore remove the template, this will cause tauri to use its default template and not call the migration script. 1. Get the latest [main.wxs](https://github.com/tauri-apps/tauri/blob/dev/tooling/bundler/src/bundle/windows/templates/main.wxs) template from the repository. 2. Replace the contents of `templates/wix/main_original.wxs` with the repository version. (The file is kept only for reference) 3. Replace the contents of `templates/wix/main.wsx` and add the fallowing lines at the end of the file, inside the `Product` tag. ```xml ``` ================================================ FILE: desktop/tauri/src-tauri/build.rs ================================================ fn main() { tauri_build::build() } ================================================ FILE: desktop/tauri/src-tauri/capabilities/default.json ================================================ { "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "Capability for the main window", "windows": [ "main", "splash" ], "remote": { "urls": [ "http://127.0.0.1:817" ] }, "permissions": [ "core:path:default", "core:event:allow-listen", "core:event:allow-unlisten", "core:event:allow-emit", "core:event:allow-emit-to", "core:window:allow-hide", "core:window:allow-show", "core:window:allow-is-visible", "core:window:allow-set-focus", "core:window:allow-close", "core:window:allow-get-all-windows", "core:app:default", "core:image:default", "core:resources:default", "core:menu:default", "core:tray:default", "core:webview:allow-set-webview-zoom", "shell:allow-open", "notification:default", "window-state:allow-save-window-state", "window-state:allow-restore-state", "clipboard-manager:allow-read-text", "clipboard-manager:allow-write-text", "websocket:default" ] } ================================================ FILE: desktop/tauri/src-tauri/src/cli.rs ================================================ use log::LevelFilter; // #[cfg(not(debug_assertions))] // const DEFAULT_LOG_LEVEL: log::LevelFilter = log::LevelFilter::Warn; // #[cfg(debug_assertions)] const DEFAULT_LOG_LEVEL: log::LevelFilter = log::LevelFilter::Debug; #[derive(Debug)] pub struct CliArguments { // Path to the installation directory pub data: Option, // Log level to use: off, error, warn, info, debug, trace pub log_level: log::LevelFilter, // Start in the background without opening a window pub background: bool, // Enable experimental notifications via Tauri. Replaces the notifier app. pub with_prompts: bool, // Enable experimental prompt support via Tauri. Replaces the notifier app. pub with_notifications: bool, } impl CliArguments { fn parse_log(&mut self, level: String) { self.log_level = match level.as_ref() { "off" => LevelFilter::Off, "error" => LevelFilter::Error, "warn" => LevelFilter::Warn, "info" => LevelFilter::Info, "debug" => LevelFilter::Debug, "trace" => LevelFilter::Trace, _ => DEFAULT_LOG_LEVEL, } } } pub fn parse(raw: impl IntoIterator>) -> CliArguments { let mut cli = CliArguments { data: None, log_level: DEFAULT_LOG_LEVEL, background: false, with_prompts: true, with_notifications: true, }; let raw = clap_lex::RawArgs::new(raw); let mut cursor = raw.cursor(); raw.next(&mut cursor); // Skip the bin while let Some(arg) = raw.next(&mut cursor) { if let Some((long, value)) = arg.to_long() { match long { Ok("data") => { if let Some(value) = value { cli.data = Some(value.to_string_lossy().into_owned()); } } Ok("log") => { if let Some(value) = value { cli.parse_log(value.to_string_lossy().into_owned()); } } Ok("background") => { cli.background = true; } Ok("no-prompts") => { cli.with_prompts = false; } Ok("no-notifications") => { cli.with_notifications = false; } _ => { // Ignore unexpected flags } } } else if let Some(mut shorts) = arg.to_short() { while let Some(short) = shorts.next() { match short { Ok('l') => { if let Some(value) = shorts.next_value_os() { let mut str = value.to_string_lossy().into_owned(); _ = str.remove(0); // remove first "=" from value (in -l=warn value will be "=warn") cli.parse_log(str); } } Ok('d') => { if let Some(value) = shorts.next_value_os() { let mut str = value.to_string_lossy().into_owned(); _ = str.remove(0); // remove first "=" from value (in -d=/data value will be "=/data") cli.data = Some(str); } } Ok('b') => cli.background = true, _ => { // Ignore unexpected flags } } } } } cli } ================================================ FILE: desktop/tauri/src-tauri/src/commands/mod.rs ================================================ pub mod tauri_http; ================================================ FILE: desktop/tauri/src-tauri/src/commands/tauri_http.rs ================================================ use tauri::State; use reqwest::{Client, Method}; use serde::{Deserialize, Serialize}; /// Creates and configures a shared HTTP client for application-wide use. /// /// Returns a reqwest Client configured with: /// - Connection pooling /// - Persistent cookie store /// /// Client can be accessed from UI through the exposed Tauri command `send_tauri_http_request(...)` /// Such requests execute directly from the Tauri app binary, not from the WebView process pub fn create_http_client() -> Client { Client::builder() // Maximum idle connections per host .pool_max_idle_per_host(10) // Enable cookie support .cookie_store(true) .user_agent("Portmaster UI") .build() .expect("failed to build HTTP client") } #[derive(Deserialize)] pub struct HttpRequestOptions { method: String, headers: Vec<(String, String)>, body: Option>, } #[derive(Serialize)] pub struct HttpResponse { status: u16, status_text: String, headers: Vec<(String, String)>, body: Vec, } #[tauri::command] pub async fn send_tauri_http_request( client: State<'_, Client>, url: String, opts: HttpRequestOptions ) -> Result { //println!("URL: {}", url); // Build the request let mut req = client .request(Method::from_bytes(opts.method.as_bytes()).map_err(|e| e.to_string())?, &url); // Apply headers for (k, v) in opts.headers { req = req.header(&k, &v); } // Attach body if present if let Some(body) = opts.body { req = req.body(body); } // Send and await the response let resp = req.send().await.map_err(|e| e.to_string())?; // Read status, headers, and body let status = resp.status().as_u16(); let status_text = resp.status().canonical_reason().unwrap_or("").to_string(); let headers = resp .headers() .iter() .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())) .collect(); let body = resp.bytes().await.map_err(|e| e.to_string())?.to_vec(); Ok(HttpResponse { status, status_text, headers, body }) } ================================================ FILE: desktop/tauri/src-tauri/src/config.rs ================================================ use std::fs; use log::{debug, error}; use serde::{Deserialize, Serialize}; use tauri::{AppHandle, Manager}; #[derive(Serialize, Deserialize)] pub enum Theme { Light, Dark, System, } #[derive(Serialize, Deserialize)] pub struct Config { pub theme: Theme, } const CONFIG_FILE_NAME: &str = "config.json"; pub fn save(app: &AppHandle, config: Config) -> tauri::Result<()> { let config_dir = app.path().app_config_dir()?; let config_path = config_dir.join(CONFIG_FILE_NAME); debug!("saving config file: {:?}", config_path); let json = serde_json::to_string_pretty(&config)?; fs::write(config_path, json)?; Ok(()) } pub fn load(app: &AppHandle) -> tauri::Result { let config_dir = app.path().app_config_dir()?; let config_path = config_dir.join(CONFIG_FILE_NAME); if let Ok(json) = fs::read_to_string(config_path) { if let Ok(config) = serde_json::from_str(&json) { return Ok(config); } } error!("failed to load config file returning default config"); Ok(Config { theme: Theme::System, }) } ================================================ FILE: desktop/tauri/src-tauri/src/main.rs ================================================ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use std::{env, time::Duration}; use tauri::{AppHandle, Emitter, Listener, Manager, RunEvent, WindowEvent}; // Library crates mod portapi; mod service; #[cfg(target_os = "linux")] mod xdg; // App modules mod cli; mod config; mod portmaster; mod traymenu; mod window; mod commands; use log::{debug, error, info}; use portmaster::PortmasterExt; use tauri_plugin_log::RotationStrategy; use traymenu::setup_tray_menu; use window::{close_splash_window, create_main_window, hide_splash_window, create_debounced_window_state_saver}; use tauri_plugin_window_state::{AppHandleExt, StateFlags}; #[macro_use] extern crate lazy_static; const FALLBACK_TO_OLD_UI_EXIT_CODE: i32 = 77; // Window state configuration: save all properties except visibility to avoid // interference with the "--background" command line argument const WINDOW_STATE_FLAGS_TO_SAVE: StateFlags = StateFlags::from_bits_truncate( StateFlags::all().bits() & !StateFlags::VISIBLE.bits() ); // Default timeout for debounced window state saving const WINDOW_STATE_SAVE_TIMEOUT: Duration = Duration::from_secs(3); #[derive(Clone, serde::Serialize)] struct Payload { args: Vec, cwd: String, } struct WsHandler { handle: AppHandle, background: bool, is_first_connect: bool, } impl portmaster::Handler for WsHandler { fn name(&self) -> String { "main-handler".to_string() } fn on_connect(&mut self, cli: portapi::client::PortAPI) { info!("connection established, creating main window"); // we successfully connected to Portmaster. Set is_first_connect to false // so we don't show the splash-screen when we loose connection. self.is_first_connect = false; // The order is important. If all current windows are destroyed tauri will exit. // First create the main ui window then destroy the splash screen. // Hide splash screen. Will be closed after main window is created. if let Err(err) = hide_splash_window(&self.handle) { error!("failed to close splash window: {}", err.to_string()); } // create the main window now. It's not automatically visible by default. // Rather, the angular application will show the window itself when it finished // bootstrapping. if let Err(err) = create_main_window(&self.handle) { error!("failed to create main window: {}", err.to_string()); } else { debug!("created main window") } // Now it is safe to destroy the splash window. if let Err(err) = close_splash_window(&self.handle) { error!("failed to close splash window: {}", err.to_string()); } // Cancel the previous tray handler task if it exists let portmaster = self.handle.portmaster(); if let Ok(mut task_guard) = portmaster.tray_handler_task.lock() { if let Some(old_task) = task_guard.take() { debug!("Aborting previous tray handler task"); old_task.abort(); } // Start new tray handler and store the task handle let handle = self.handle.clone(); let task = tauri::async_runtime::spawn(async move { traymenu::tray_handler(cli, handle).await; }); *task_guard = Some(task); } } fn on_disconnect(&mut self) { // if we're not running in background and this was the first connection attempt // then display the splash-screen. // // Once we had a successful connection the splash-screen will not be shown anymore // since there's already a main window with the angular application. if !self.background && self.is_first_connect { let _ = window::create_splash_window(&self.handle.clone()); self.is_first_connect = false } } } fn show_webview_not_installed_dialog() -> i32 { use rfd::MessageDialog; let result = MessageDialog::new() .set_title("Portmaster") .set_description("Webkit is not installed. Please install it and run portmaster again") .set_buttons(rfd::MessageButtons::OkCancelCustom( "Go to install page".to_owned(), "Use old UI".to_owned(), )) .show(); println!("{:?}", result); if let rfd::MessageDialogResult::Custom(result) = result { if result.eq("Go to install page") { _ = open::that("https://wiki.safing.io/en/Portmaster/Install/Webview"); std::thread::sleep(Duration::from_secs(2)); return 0; } } FALLBACK_TO_OLD_UI_EXIT_CODE } fn main() { if tauri::webview_version().is_err() { std::process::exit(show_webview_not_installed_dialog()); } let cli_args = cli::parse(std::env::args()); // TODO(vladimir): Support for other log targets? #[cfg(target_os = "linux")] let log_target = tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout); // let log_target = if let Some(data_dir) = cli_args.data { // tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Folder { // path: Path::new(&format!("{}/logs/app2", data_dir)).into(), // file_name: None, // }) // } else { // }; // TODO(vladimir): Permission for logs/app2 folder are not guaranteed. Use the default location for now. #[cfg(target_os = "windows")] let log_target = if let Some(_) = cli_args.data { tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::LogDir { file_name: None }) } else { tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout) }; // Create a single HTTP client that: // - Pools and reuses connections for better performance // - Is exposed to UI through 'send_tauri_http_request()' command // - Such requests execute directly from the Tauri app binary, not from the WebView process let http_client = commands::tauri_http::create_http_client(); let app = tauri::Builder::default() // make HTTP client accessible in commands ('send_tauri_http_request()') .manage(http_client) .plugin(tauri_plugin_websocket::init()) // Shell plugin for open_external support .plugin(tauri_plugin_shell::init()) // Initialize Logging plugin. .plugin( tauri_plugin_log::Builder::default() .level(cli_args.log_level) .rotation_strategy(RotationStrategy::KeepAll) .clear_targets() .target(log_target) .build(), ) // Clipboard support .plugin(tauri_plugin_clipboard_manager::init()) // Dialog (Save/Open) support .plugin(tauri_plugin_dialog::init()) // OS Version and Architecture support .plugin(tauri_plugin_os::init()) // Initialize save windows state plugin. .plugin(tauri_plugin_window_state::Builder::default() // Don't save visibility state, so it will not interfere with "--background" command line argument .with_state_flags(WINDOW_STATE_FLAGS_TO_SAVE) // Don't save splash window state .with_denylist(&["splash",]) .build()) // Single instance guard .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| { // Send info to already dunning instance. let _ = app.emit("single-instance", Payload { args: argv, cwd }); })) // Notification support .plugin(tauri_plugin_notification::init()) .invoke_handler(tauri::generate_handler![ portmaster::commands::get_app_info, portmaster::commands::get_service_manager_status, portmaster::commands::start_service, portmaster::commands::get_state, portmaster::commands::set_state, portmaster::commands::should_show, portmaster::commands::should_handle_prompts, commands::tauri_http::send_tauri_http_request, ]) // Setup the app an any listeners .setup(move |app| { setup_tray_menu(app)?; portmaster::setup(app.handle().clone()); // Setup the single-instance event listener that will create/focus the main window // or the splash-screen. let handle = app.handle().clone(); app.listen_any("single-instance", move |_event| { let _ = window::open_window(&handle); }); // Handle cli flags: app.portmaster() .set_show_after_bootstrap(!cli_args.background); app.portmaster() .with_notification_support(cli_args.with_notifications); app.portmaster() .with_connection_prompts(cli_args.with_prompts); // prepare a custom portmaster plugin handler that will show the splash-screen // (if not in --background) and launch the tray-icon handler. let handler = WsHandler { handle: app.handle().clone(), background: cli_args.background, is_first_connect: true, }; // register the custom handler app.portmaster().register_handler(handler); // Setup window state saving on move/resize events with debouncing let save_trigger = create_debounced_window_state_saver(app, WINDOW_STATE_FLAGS_TO_SAVE, WINDOW_STATE_SAVE_TIMEOUT); let tx_move = save_trigger.clone(); let tx_resize = save_trigger; app.listen_any("tauri://move", move |_event| { let _ = tx_move.try_send(()); }); app.listen_any("tauri://resize", move |_event| { let _ = tx_resize.try_send(()); }); Ok(()) }) .any_thread() .build(tauri::generate_context!()) .expect("error while running tauri application"); app.run(|handle, e| { match e { RunEvent::WindowEvent { label, event, .. } => { if label != "main" { // We only have one window at most so any other label is unexpected return; } // Do not let the user close the window, instead send an event to the main // window so we can show the "will not stop portmaster" dialog and let the window // close itself using // // window.__TAURI__.window.getCurrent().close() // // Note: the above javascript does NOT trigger the CloseRequested event so // there's no need to handle that case here. if let WindowEvent::CloseRequested { api, .. } = event { debug!( "window (label={}) close request received, forwarding to user-interface.", label ); // Manually save the window state on close attempt. // This ensures the state is saved since we prevent the close event. let _ = handle.save_window_state(WINDOW_STATE_FLAGS_TO_SAVE); api.prevent_close(); if let Some(window) = handle.get_webview_window(label.as_str()) { let result = window.emit("exit-requested", ""); if let Err(err) = result { error!("failed to emit event: {}", err.to_string()); } } else { error!("window was None"); } } } RunEvent::ExitRequested { .. } => { debug!("Application exit requested, shutting down websocket"); portmaster::websocket::shutdown_websocket(); } _ => {} } }); } ================================================ FILE: desktop/tauri/src-tauri/src/portapi/client.rs ================================================ use futures_util::{SinkExt, StreamExt}; use http::Uri; use log::{debug, error, warn}; use std::collections::HashMap; use std::sync::atomic::{AtomicUsize, Ordering}; use tokio::sync::mpsc::{channel, Receiver, Sender}; use tokio::sync::RwLock; use tokio::time::{interval, Duration, Instant}; use tokio_websockets::{ClientBuilder, Error}; use bytes::Bytes; use super::message::*; use super::types::*; /// An internal representation of a Command that /// contains the PortAPI message as well as a response /// channel that will receive all responses sent from the /// server. /// /// Users should normally not need to use the Command struct /// directly since `PortAPI` already abstracts the creation of /// mpsc channels. struct Command { msg: Message, response: Sender, } /// The client implementation for PortAPI. #[derive(Clone)] pub struct PortAPI { dispatch: Sender, } /// The map type used to store message subscribers. type SubscriberMap = RwLock>>; /// Connect to PortAPI at the specified URI. /// /// This method will launch a new async thread on the `tauri::async_runtime` /// that will handle message to transmit and also multiplex server responses /// to the appropriate subscriber. pub async fn connect(uri: &str) -> Result { let parsed = match uri.parse::() { Ok(u) => u, Err(_e) => { return Err(Error::NoUriConfigured); // TODO(ppacher): fix the return error type. } }; let (mut client, _) = ClientBuilder::from_uri(parsed).connect().await?; let (tx, mut dispatch) = channel::(64); tauri::async_runtime::spawn(async move { let subscribers: SubscriberMap = RwLock::new(HashMap::new()); let next_id = AtomicUsize::new(0); // Ping/pong keep-alive mechanism let mut ping_interval = interval(Duration::from_secs(10)); // Send ping every 10 seconds let mut timeout_check = interval(Duration::from_secs(1)); // Check for timeout every 1 second let mut last_ping = Instant::now(); let mut last_pong = Instant::now(); const PONG_TIMEOUT: Duration = Duration::from_secs(5); // Declare connection dead if no pong within 5 seconds after ping loop { tokio::select! { _ = ping_interval.tick() => { // Send ping frame if let Err(err) = client.send(tokio_websockets::Message::ping(Bytes::new())).await { error!("failed to send ping: {}", err); dispatch.close(); return; } last_ping = Instant::now(); // debug!("sent websocket ping"); }, _ = timeout_check.tick() => { // Check if pong timeout expired after last ping if last_ping > last_pong && last_ping.elapsed() > PONG_TIMEOUT { warn!("no pong received for {:?} after ping, connection appears dead", PONG_TIMEOUT); dispatch.close(); return; } }, msg = client.next() => { let msg = match msg { Some(msg) => msg, None => { warn!("websocket connection lost"); dispatch.close(); return; } }; match msg { Err(err) => { error!("failed to receive frame from websocket: {}", err); dispatch.close(); return; }, Ok(msg) => { // Handle pong frames if msg.is_pong() { last_pong = Instant::now(); // debug!("received websocket pong"); continue; } if msg.is_ping() { // debug!("received websocket ping"); continue; } let text = unsafe { std::str::from_utf8_unchecked(msg.as_payload()) }; match text.parse::() { Ok(msg) => { let id = msg.id; let map = subscribers .read() .await; if let Some(sub) = map.get(&id) { let res: Result = msg.try_into(); match res { Ok(response) => { if let Err(err) = sub.send(response).await { // The receiver side has been closed already, // drop the read lock and remove the subscriber // from our hashmap drop(map); subscribers .write() .await .remove(&id); debug!("subscriber for command {} closed read side: {}", id, err); } }, Err(err) => { error!("invalid command: {}", err); } } } }, Err(err) => { error!("failed to deserialize message: {}", err) } } } } }, Some(mut cmd) = dispatch.recv() => { let id = next_id.fetch_add(1, Ordering::Relaxed); cmd.msg.id = id; let blob: String = cmd.msg.into(); debug!("Sending websocket frame: {}", blob); match client.send(tokio_websockets::Message::text(blob)).await { Ok(_) => { subscribers .write() .await .insert(id, cmd.response); }, Err(err) => { error!("failed to dispatch command: {}", err); // TODO(ppacher): we should send some error to cmd.response here. // Otherwise, the sender of cmd might get stuck waiting for responses // if they don't check for PortAPI.is_closed(). return } } } } } }); Ok(PortAPI { dispatch: tx }) } impl PortAPI { /// `request` sends a PortAPI `portapi::types::Request` to the server and returns a mpsc receiver channel /// where all server responses are forwarded. /// /// If the caller does not intend to read any responses the returned receiver may be closed or /// dropped. As soon as the async-thread launched in `connect` detects a closed receiver it is remove /// from the subscription map. /// /// The default buffer size for the channel is 64. Use `request_with_buffer_size` to specify a dedicated buffer size. pub async fn request( &self, r: Request, ) -> std::result::Result, MessageError> { self.request_with_buffer_size(r, 64).await } // Like `request` but supports explicitly specifying a channel buffer size. pub async fn request_with_buffer_size( &self, r: Request, buffer: usize, ) -> std::result::Result, MessageError> { let (tx, rx) = channel(buffer); let msg: Message = r.try_into()?; let _ = self.dispatch.send(Command { response: tx, msg }).await; Ok(rx) } /// Reports whether or not the websocket connection to the Portmaster Database API has been closed /// due to errors. /// /// Users are expected to check this field on a regular interval to detect any issues and perform /// a clean re-connect by calling `connect` again. pub fn is_closed(&self) -> bool { self.dispatch.is_closed() } } ================================================ FILE: desktop/tauri/src-tauri/src/portapi/message.rs ================================================ use thiserror::Error; /// MessageError describes any error that is encountered when parsing /// PortAPI messages or when converting between the Request/Response types. #[derive(Debug, Error)] pub enum MessageError { #[error("missing command id")] MissingID, #[error("invalid command id")] InvalidID, #[error("missing command")] MissingCommand, #[error("missing key")] MissingKey, #[error("missing payload")] MissingPayload, #[error("unknown or unsupported command: {0}")] UnknownCommand(String), #[error(transparent)] InvalidPayload(#[from] serde_json::Error), } /// Payload defines the payload type and content of a PortAPI message. /// /// For the time being, only JSON payloads (indicated by a prefixed 'J' of the payload content) /// is directly supported in `Payload::parse()`. /// /// For other payload types (like CBOR, BSON, ...) it's the user responsibility to figure out /// appropriate decoding from the `Payload::UNKNOWN` variant. #[derive(PartialEq, Debug, Clone)] pub enum Payload { Json(String), Unknown(String), } /// ParseError is returned from `Payload::parse()`. #[derive(Debug, Error)] pub enum ParseError { #[error(transparent)] Json(#[from] serde_json::Error), #[error("unknown error while parsing")] Unknown, } impl Payload { /// Parse the payload into T. /// /// Only JSON parsing is supported for now. See [Payload] for more information. pub fn parse<'a, T>(&'a self) -> std::result::Result where T: serde::de::Deserialize<'a>, { match self { Payload::Json(blob) => Ok(serde_json::from_str::(blob.as_str())?), Payload::Unknown(_) => Err(ParseError::Unknown), } } } /// Supports creating a Payload instance from a String. /// /// See [Payload] for more information. impl std::convert::From for Payload { fn from(value: String) -> Payload { let mut chars = value.chars(); let first = chars.next(); let rest = chars.as_str().to_string(); match first { Some(c) => match c { 'J' => Payload::Json(rest), _ => Payload::Unknown(value), }, None => Payload::Unknown("".to_string()), } } } /// Display implementation for Payload that just displays the raw payload. impl std::fmt::Display for Payload { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Payload::Json(payload) => { write!(f, "J{}", payload) } Payload::Unknown(payload) => { write!(f, "{}", payload) } } } } /// Message is an internal representation of a PortAPI message. /// Users should more likely use `portapi::types::Request` and `portapi::types::Response` /// instead of directly using `Message`. /// /// The struct is still public since it might be useful for debugging or to implement new /// commands not yet supported by the `portapi::types` crate. #[derive(PartialEq, Debug, Clone)] pub struct Message { pub id: usize, pub cmd: String, pub key: Option, pub payload: Option, } /// Implementation to marshal a PortAPI message into it's wire-format representation /// (which is a string). /// /// Note that this conversion does not check for invalid messages! impl std::convert::From for String { fn from(value: Message) -> Self { let mut result = String::new(); result.push_str(value.id.to_string().as_str()); result.push('|'); result.push_str(&value.cmd); if let Some(key) = value.key { result.push('|'); result.push_str(key.as_str()); } if let Some(payload) = value.payload { result.push('|'); result.push_str(payload.to_string().as_str()) } result } } /// An implementation for `String::parse()` to convert a wire-format representation /// of a PortAPI message to a Message instance. /// /// Any errors returned from `String::parse()` will be of type `MessageError` impl std::str::FromStr for Message { type Err = MessageError; fn from_str(line: &str) -> Result { let parts = line.split('|').collect::>(); let id = match parts.first() { Some(s) => match (*s).parse::() { Ok(id) => Ok(id), Err(_) => Err(MessageError::InvalidID), }, None => Err(MessageError::MissingID), }?; let cmd = match parts.get(1) { Some(s) => Ok(*s), None => Err(MessageError::MissingCommand), }? .to_string(); let key = parts.get(2).map(|key| key.to_string()); let payload: Option = parts.get(3).map(|p| p.to_string().into()); Ok(Message { id, cmd, key, payload, }) } } #[cfg(test)] mod tests { use super::*; use serde::Deserialize; #[derive(Debug, PartialEq, Deserialize)] struct Test { a: i64, s: String, } #[test] fn payload_to_string() { let p = Payload::Json("{}".to_string()); assert_eq!(p.to_string(), "J{}"); let p = Payload::Unknown("some unknown content".to_string()); assert_eq!(p.to_string(), "some unknown content"); } #[test] fn payload_from_string() { let p: Payload = "J{}".to_string().into(); assert_eq!(p, Payload::Json("{}".to_string())); let p: Payload = "some unknown content".to_string().into(); assert_eq!(p, Payload::Unknown("some unknown content".to_string())); } #[test] fn payload_parse() { let p: Payload = "J{\"a\": 100, \"s\": \"string\"}".to_string().into(); let t: Test = p.parse().expect("Expected payload parsing to work"); assert_eq!( t, Test { a: 100, s: "string".to_string(), } ); } #[test] fn parse_message() { let m = "10|insert|some:key|J{}" .parse::() .expect("Expected message to parse"); assert_eq!( m, Message { id: 10, cmd: "insert".to_string(), key: Some("some:key".to_string()), payload: Some(Payload::Json("{}".to_string())), } ); let m = "1|done" .parse::() .expect("Expected message to parse"); assert_eq!( m, Message { id: 1, cmd: "done".to_string(), key: None, payload: None } ); let m = "".parse::().expect_err("Expected parsing to fail"); if let MessageError::InvalidID = m { } else { panic!("unexpected error value: {}", m) } let m = "1" .parse::() .expect_err("Expected parsing to fail"); if let MessageError::MissingCommand = m { } else { panic!("unexpected error value: {}", m) } } } ================================================ FILE: desktop/tauri/src-tauri/src/portapi/mod.rs ================================================ pub mod client; pub mod message; pub mod types; pub mod models; ================================================ FILE: desktop/tauri/src-tauri/src/portapi/models/config.rs ================================================ use super::super::message::Payload; use serde::*; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct BooleanValue { #[serde(rename = "Value")] pub value: Option, } impl TryInto for BooleanValue { type Error = serde_json::Error; fn try_into(self) -> Result { let str = serde_json::to_string(&self)?; Ok(Payload::Json(str)) } } ================================================ FILE: desktop/tauri/src-tauri/src/portapi/models/mod.rs ================================================ pub mod config; pub mod spn; pub mod notification; pub mod system_status_types; ================================================ FILE: desktop/tauri/src-tauri/src/portapi/models/notification.rs ================================================ use serde::*; #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct Notification { #[serde(rename = "EventID")] pub event_id: String, #[serde(rename = "GUID")] pub guid: String, #[serde(rename = "Type")] pub notification_type: NotificationType, #[serde(rename = "Message")] pub message: String, #[serde(rename = "Title")] pub title: String, #[serde(rename = "Category")] pub category: String, #[serde(rename = "EventData")] pub data: serde_json::Value, #[serde(rename = "Expires")] pub expires: u64, #[serde(rename = "State")] pub state: String, #[serde(rename = "AvailableActions")] pub actions: Vec, #[serde(rename = "SelectedActionID")] pub selected_action_id: String, #[serde(rename = "ShowOnSystem")] pub show_on_system: bool, } #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct Action { #[serde(rename = "ID")] pub id: String, #[serde(rename = "Text")] pub text: String, #[serde(rename = "Type")] pub action_type: String, #[serde(rename = "Payload")] pub payload: serde_json::Value, #[serde(rename = "Visibility")] pub visibility: String, // "" - default, "detailed" - only show if the notification is expanded } #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct NotificationType(i32); #[allow(dead_code)] pub const INFO: NotificationType = NotificationType(0); #[allow(dead_code)] pub const WARN: NotificationType = NotificationType(1); #[allow(dead_code)] pub const PROMPT: NotificationType = NotificationType(2); #[allow(dead_code)] pub const ERROR: NotificationType = NotificationType(3); ================================================ FILE: desktop/tauri/src-tauri/src/portapi/models/spn.rs ================================================ use serde::*; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct SPNStatus { #[serde(rename = "Status")] pub status: String, } ================================================ FILE: desktop/tauri/src-tauri/src/portapi/models/system_status_types.rs ================================================ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum StateType { #[serde(rename = "")] Undefined, #[serde(rename = "hint")] Hint, #[serde(rename = "warning")] Warning, #[serde(rename = "error")] Error, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct State { #[serde(rename = "ID")] pub id: String, #[serde(rename = "Name")] pub name: String, #[serde(rename = "Message")] pub message: Option, #[serde(rename = "Type")] pub state_type: Option, #[serde(rename = "Time")] pub time: Option, // time.Time serialized by GoLang #[serde(rename = "Data")] pub data: Option, // any type } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StateUpdate { #[serde(rename = "Module")] pub module: String, #[serde(rename = "States")] pub states: Option>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WorstState { #[serde(rename = "Module")] pub module: String, #[serde(flatten)] pub state: State, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct SystemStatus { #[serde(rename = "Modules")] pub modules: Vec, #[serde(rename = "WorstState")] pub worst_state: Option, // add more fields when needed // ... } // PauseInfo represents pause status data from "control:paused" state in Control module #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct PauseInfo { #[serde(rename = "Interception")] pub interception: bool, #[serde(rename = "SPN")] pub spn: bool, #[serde(rename = "TillTime")] pub till_time: String, // time.Time serialized as string by GoLang } impl SystemStatus { pub fn get_module_state(&self, module_name: &str, state_id: &str) -> Option<&State> { if let Some(module) = self.modules.iter().find(|m| m.module == module_name) { if let Some(states) = &module.states { return states.iter().find(|s| s.id == state_id); } } None } } ================================================ FILE: desktop/tauri/src-tauri/src/portapi/types.rs ================================================ use super::message::*; /// Request is a strongly typed request message /// that can be converted to a `portapi::message::Message` /// object for further use by the client (`portapi::client::PortAPI`). #[derive(PartialEq, Debug)] pub enum Request { Get(String), Query(String), Subscribe(String), QuerySubscribe(String), Create(String, Payload), Update(String, Payload), Insert(String, Payload), Delete(String), Cancel, } /// Implementation to convert a internal `portapi::message::Message` to a valid /// `Request` variant. /// /// Any error returned will be of type `portapi::message::MessageError`. impl std::convert::TryFrom for Request { type Error = MessageError; fn try_from(value: Message) -> Result { match value.cmd.as_str() { "get" => { let key = value.key.ok_or(MessageError::MissingKey)?; Ok(Request::Get(key)) }, "query" => { let key = value.key.ok_or(MessageError::MissingKey)?; Ok(Request::Query(key)) }, "sub" => { let key = value.key.ok_or(MessageError::MissingKey)?; Ok(Request::Subscribe(key)) }, "qsub" => { let key = value.key.ok_or(MessageError::MissingKey)?; Ok(Request::QuerySubscribe(key)) }, "create" => { let key = value.key.ok_or(MessageError::MissingKey)?; let payload = value.payload.ok_or(MessageError::MissingPayload)?; Ok(Request::Create(key, payload)) }, "update" => { let key = value.key.ok_or(MessageError::MissingKey)?; let payload = value.payload.ok_or(MessageError::MissingPayload)?; Ok(Request::Update(key, payload)) }, "insert" => { let key = value.key.ok_or(MessageError::MissingKey)?; let payload = value.payload.ok_or(MessageError::MissingPayload)?; Ok(Request::Insert(key, payload)) }, "delete" => { let key = value.key.ok_or(MessageError::MissingKey)?; Ok(Request::Delete(key)) }, "cancel" => { Ok(Request::Cancel) }, cmd => { Err(MessageError::UnknownCommand(cmd.to_string())) } } } } /// An implementation to try to convert a `Request` variant into a valid /// `portapi::message::Message` struct. /// /// While this implementation does not yet return any errors, it's expected that /// additional validation will be added in the future so users should already expect /// to receive `portapi::message::MessageError`s. impl std::convert::TryFrom for Message { type Error = MessageError; fn try_from(value: Request) -> Result { match value { Request::Get(key) => Ok(Message { id: 0, cmd: "get".to_string(), key: Some(key), payload: None }), Request::Query(key) => Ok(Message { id: 0, cmd: "query".to_string(), key: Some(key), payload: None }), Request::Subscribe(key) => Ok(Message { id: 0, cmd: "sub".to_string(), key: Some(key), payload: None }), Request::QuerySubscribe(key) => Ok(Message { id: 0, cmd: "qsub".to_string(), key: Some(key), payload: None }), Request::Create(key, value) => Ok(Message{ id: 0, cmd: "create".to_string(), key: Some(key), payload: Some(value)}), Request::Update(key, value) => Ok(Message{ id: 0, cmd: "update".to_string(), key: Some(key), payload: Some(value)}), Request::Insert(key, value) => Ok(Message{ id: 0, cmd: "insert".to_string(), key: Some(key), payload: Some(value)}), Request::Delete(key) => Ok(Message { id: 0, cmd: "delete".to_string(), key: Some(key), payload: None }), Request::Cancel => Ok(Message { id: 0, cmd: "cancel".to_string(), key: None, payload: None }), } } } /// Response is strongly types PortAPI response message. /// that can be converted to a `portapi::message::Message` /// object for further use by the client (`portapi::client::PortAPI`). #[derive(PartialEq, Debug)] pub enum Response { Ok(String, Payload), Update(String, Payload), New(String, Payload), Delete(String), Success, Error(String), Warning(String), Done } /// Implementation to convert a internal `portapi::message::Message` to a valid /// `Response` variant. /// /// Any error returned will be of type `portapi::message::MessageError`. impl std::convert::TryFrom for Response { type Error = MessageError; fn try_from(value: Message) -> Result { match value.cmd.as_str() { "ok" => { let key = value.key.ok_or(MessageError::MissingKey)?; let payload = value.payload.ok_or(MessageError::MissingPayload)?; Ok(Response::Ok(key, payload)) }, "upd" => { let key = value.key.ok_or(MessageError::MissingKey)?; let payload = value.payload.ok_or(MessageError::MissingPayload)?; Ok(Response::Update(key, payload)) }, "new" => { let key = value.key.ok_or(MessageError::MissingKey)?; let payload = value.payload.ok_or(MessageError::MissingPayload)?; Ok(Response::New(key, payload)) }, "del" => { let key = value.key.ok_or(MessageError::MissingKey)?; Ok(Response::Delete(key)) }, "success" => { Ok(Response::Success) }, "error" => { let key = value.key.ok_or(MessageError::MissingKey)?; Ok(Response::Error(key)) }, "warning" => { let key = value.key.ok_or(MessageError::MissingKey)?; Ok(Response::Warning(key)) }, "done" => { Ok(Response::Done) }, cmd => Err(MessageError::UnknownCommand(cmd.to_string())) } } } /// An implementation to try to convert a `Response` variant into a valid /// `portapi::message::Message` struct. /// /// While this implementation does not yet return any errors, it's expected that /// additional validation will be added in the future so users should already expect /// to receive `portapi::message::MessageError`s. impl std::convert::TryFrom for Message { type Error = MessageError; fn try_from(value: Response) -> Result { match value { Response::Ok(key, payload) => Ok(Message{id: 0, cmd: "ok".to_string(), key: Some(key), payload: Some(payload)}), Response::Update(key, payload) => Ok(Message{id: 0, cmd: "upd".to_string(), key: Some(key), payload: Some(payload)}), Response::New(key, payload) => Ok(Message{id: 0, cmd: "new".to_string(), key: Some(key), payload: Some(payload)}), Response::Delete(key ) => Ok(Message{id: 0, cmd: "del".to_string(), key: Some(key), payload: None}), Response::Success => Ok(Message{id: 0, cmd: "success".to_string(), key: None, payload: None}), Response::Warning(key) => Ok(Message{id: 0, cmd: "warning".to_string(), key: Some(key), payload: None}), Response::Error(key) => Ok(Message{id: 0, cmd: "error".to_string(), key: Some(key), payload: None}), Response::Done => Ok(Message{id: 0, cmd: "done".to_string(), key: None, payload: None}), } } } ================================================ FILE: desktop/tauri/src-tauri/src/portmaster/commands.rs ================================================ use super::PortmasterInterface; use crate::service::get_service_manager; use crate::service::ServiceManager; use log::debug; use std::sync::atomic::Ordering; use tauri::{Emitter, Runtime, State, Window}; pub type Result = std::result::Result; #[derive(Clone, serde::Serialize, serde::Deserialize)] pub struct Error { pub error: String, } #[tauri::command] pub fn should_show( _window: Window, portmaster: State<'_, PortmasterInterface>, ) -> Result { if portmaster.get_show_after_bootstrap() { debug!("[tauri:rpc:should_show] application should show after bootstrap"); Ok("show".to_string()) } else { debug!("[tauri:rpc:should_show] application should hide after bootstrap"); Ok("hide".to_string()) } } #[tauri::command] pub fn should_handle_prompts( _window: Window, portmaster: State<'_, PortmasterInterface>, ) -> Result { if portmaster.handle_prompts.load(Ordering::Relaxed) { Ok("true".to_string()) } else { Ok("false".to_string()) } } #[tauri::command] pub fn get_state( _window: Window, portmaster: State<'_, PortmasterInterface>, key: String, ) -> Result { let value = portmaster.get_state(key); if let Some(value) = value { Ok(value) } else { Ok("".to_string()) } } #[tauri::command] pub fn set_state( _window: Window, portmaster: State<'_, PortmasterInterface>, key: String, value: String, ) -> Result { portmaster.set_state(key, value); Ok("".to_string()) } #[cfg(target_os = "linux")] #[tauri::command] pub fn get_app_info( window: Window, response_id: String, matching_path: String, exec_path: String, pid: i64, cmdline: String, ) -> Result { let mut id = response_id; let info = crate::xdg::ProcessInfo { cmdline, exec_path, pid, matching_path, }; if id.is_empty() { id = uuid::Uuid::new_v4().to_string() } let cloned = id.clone(); // GTK calls are not thread-safe and must run on the main thread. // Schedule the work on the GTK/GLib main thread to avoid random segfaults. glib::idle_add_local(move || { let _ = match crate::xdg::get_app_info(info.clone()) { Ok(info) => window.emit(&id, info), Err(err) => window.emit( &id, Error { error: err.to_string(), }, ), }; glib::ControlFlow::Break }); Ok(cloned) } #[cfg(target_os = "windows")] #[tauri::command] pub fn get_app_info( window: Window, response_id: String, _matching_path: String, _exec_path: String, _pid: i64, _cmdline: String, ) -> Result { let mut id = response_id; if id == "" { id = uuid::Uuid::new_v4().to_string() } let cloned = id.clone(); std::thread::spawn(move || { let _ = window.emit( &id, Error { error: "Unsupported OS".to_string(), }, ); }); Ok(cloned) } #[tauri::command] pub fn get_service_manager_status(window: Window, response_id: String) -> Result { let mut id = response_id; if id.is_empty() { id = uuid::Uuid::new_v4().to_string(); } let cloned = id.clone(); std::thread::spawn(move || { let result = match get_service_manager() { Ok(sm) => sm.status().map_err(|err| err.to_string()), Err(err) => Err(err.to_string()), }; match result { Ok(result) => window.emit(&id, &result), Err(err) => window.emit(&id, Error { error: err }), } }); Ok(cloned) } #[tauri::command] pub fn start_service(window: Window, response_id: String) -> Result { let mut id = response_id; if id.is_empty() { id = uuid::Uuid::new_v4().to_string(); } let cloned = id.clone(); std::thread::spawn(move || { let result = match get_service_manager() { Ok(sm) => sm.start().map_err(|err| err.to_string()), Err(err) => Err(err.to_string()), }; match result { Ok(result) => window.emit(&id, &result), Err(err) => window.emit(&id, Error { error: err }), } }); Ok(cloned) } ================================================ FILE: desktop/tauri/src-tauri/src/portmaster/mod.rs ================================================ /// This module contains a custom tauri plugin that handles all communication /// with the angular app loaded from the portmaster api. /// /// Using a custom-plugin for this has the advantage that all code that has /// access to a tauri::Window or a tauri::AppHandle can get access to the /// portmaster plugin using the Runtime/Manager extension by just calling /// window.portmaster() or app_handle.portmaster(). /// /// Any portmaster related features (like changing a portmaster setting) should /// live in this module. /// /// Code that handles windows should NOT live here but should rather be placed /// in the crate root. // The commands module contains tauri commands that are available to Javascript // using the invoke() and our custom invokeAsync() command. pub mod commands; // The websocket module spawns an async function on tauri's runtime that manages // a persistent connection to the Portmaster websocket API and updates the tauri Portmaster // Plugin instance. pub mod websocket; // The notification module manages system notifications from portmaster. mod notifications; use crate::portapi::{ client::PortAPI, message::Payload, models::config::BooleanValue, types::Request, }; use std::{ collections::HashMap, sync::atomic::{AtomicBool, Ordering}, }; use log::{debug, error}; use std::sync::Mutex; use tauri::{AppHandle, Emitter, Manager, Runtime}; const PORTMASTER_BASE_URL: &str = "http://127.0.0.1:817/api/v1/"; pub trait Handler { fn on_connect(&mut self, cli: PortAPI); fn on_disconnect(&mut self); fn name(&self) -> String; } pub struct PortmasterInterface { #[allow(dead_code)] app: AppHandle, // state allows the angular application to store arbitrary values in the // tauri application memory using the get_state and set_state // tauri::commands. state: Mutex>, // an atomic boolean that indicates if we're currently connected to // portmaster or not. is_reachable: AtomicBool, // holds the portapi client if any. api: Mutex>, // a vector of handlers that should be invoked on connect and disconnect of // the portmaster API. handlers: Mutex>>, // whether or not we should handle notifications here. handle_notifications: AtomicBool, // whether or not we should handle prompts. handle_prompts: AtomicBool, // whether or not the angular application should call window.show after it // finished bootstrapping. should_show_after_bootstrap: AtomicBool, // handle to the tray handler task so we can abort it when reconnecting pub tray_handler_task: Mutex>>, } impl PortmasterInterface { /// Returns a state stored in the portmaster plugin. pub fn get_state(&self, key: String) -> Option { let map = self.state.lock(); if let Ok(map) = map { map.get(&key).cloned() } else { None } } /// Adds a new state to the portmaster plugin. pub fn set_state(&self, key: String, value: String) { let map = self.state.lock(); if let Ok(mut map) = map { map.insert(key, value); } } /// Reports wheter or not we're currently connected to the Portmaster API. pub fn is_reachable(&self) -> bool { self.is_reachable.load(Ordering::Relaxed) } /// Registers a new connection handler that is called on connect /// and disconnect of the Portmaster websocket API. pub fn register_handler(&self, mut handler: impl Handler + Send + 'static) { if let Ok(mut handlers) = self.handlers.lock() { // register_handler can only be invoked after the plugin setup // completed. in this case, the websocket thread is already spawned and // we might already be connected or know that the connection failed. // Call the respective handler method immediately now. if let Some(api) = self.get_api() { debug!("already connected to Portmaster API, calling on_connect()"); handler.on_connect(api); } else { debug!("not yet connected to Portmaster API, calling on_disconnect()"); handler.on_disconnect(); } handlers.push(Box::new(handler)); debug!("number of registered handlers: {}", handlers.len()); } } /// Returns the current portapi client. pub fn get_api(&self) -> Option { if let Ok(api) = self.api.lock() { (*api).clone() } else { None } } /// Feature functions (enable/disable certain features). /// Configures whether or not our tauri app should show system /// notifications. This excludes connection prompts. Use /// with_connection_prompts to enable handling of connection prompts. pub fn with_notification_support(&self, enable: bool) { self.handle_notifications.store(enable, Ordering::Relaxed); // kick of the notification handler if we are connected. if enable { self.start_notification_handler(); } } /// Configures whether or not our angular application should show connection /// prompts via tauri. pub fn with_connection_prompts(&self, enable: bool) { self.handle_prompts.store(enable, Ordering::Relaxed); } /// Whether or not the angular application should call window.show after it /// finished bootstrapping. pub fn set_show_after_bootstrap(&self, show: bool) { self.should_show_after_bootstrap .store(show, Ordering::Relaxed); } /// Returns whether or not the angular application should call window.show /// after it finished bootstrapping. pub fn get_show_after_bootstrap(&self) -> bool { self.should_show_after_bootstrap.load(Ordering::Relaxed) } /// Tells the angular application to show the window by emitting an event. /// It calls set_show_after_bootstrap(true) automatically so the application /// also shows after bootstrapping. pub fn show_window(&self) { debug!("[tauri] showing main window"); // set show_after_bootstrap to true so the app will even show if it // misses the event below because it's still bootstrapping. self.set_show_after_bootstrap(true); if let Err(err) = self.app.emit("portmaster:show", "") { error!("failed to emit show event: {}", err.to_string()); } } /// Enables or disables the SPN. pub fn set_spn_enabled(&self, enabled: bool) { if let Some(api) = self.get_api() { let body: Result = BooleanValue { value: Some(enabled), } .try_into(); if let Ok(payload) = body { tauri::async_runtime::spawn(async move { _ = api .request(Request::Update("config:spn/enable".to_string(), payload)) .await; }); } } } /// Send Shutdown request to portmaster pub fn trigger_shutdown(&self) { tauri::async_runtime::spawn(async move { let client = reqwest::Client::new(); match client .post(format!("{}core/shutdown", PORTMASTER_BASE_URL)) .send() .await { Ok(v) => { debug!("shutdown request sent {:?}", v); } Err(err) => { error!("failed to send shutdown request {}", err); } } }); } pub fn set_resume(&self) { tauri::async_runtime::spawn(async move { let client = reqwest::Client::new(); match client .post(format!("{}control/resume", PORTMASTER_BASE_URL)) .send() .await { Ok(v) => { debug!("resume request sent {:?}", v); } Err(err) => { error!("failed to send resume request {}", err); } } }); } pub fn set_pause(&self, duration_seconds: u64, spn_only: bool) { tauri::async_runtime::spawn(async move { let client = reqwest::Client::new(); match client .post(format!("{}control/pause", PORTMASTER_BASE_URL)) .json(&serde_json::json!({ "duration": duration_seconds, "onlySPN": spn_only })) .send() .await { Ok(v) => { debug!("pause request sent {:?}", v); } Err(err) => { error!("failed to send pause request {}", err); } } }); } //// Internal functions fn start_notification_handler(&self) { if let Some(api) = self.get_api() { tauri::async_runtime::spawn(async move { notifications::notification_handler(api).await; }); } } /// Internal method to call all on_connect handlers fn on_connect(&self, api: PortAPI) { debug!("connection to portmaster established, calling handlers"); self.is_reachable.store(true, Ordering::Relaxed); // store the new api client. { let mut guard = self.api.lock().unwrap(); *guard = Some(api.clone()); } // fire-off the notification handler. if self.handle_notifications.load(Ordering::Relaxed) { self.start_notification_handler(); } if let Ok(mut handlers) = self.handlers.lock() { debug!("executing handler.on_connect()"); for handler in handlers.iter_mut() { debug!("calling registered handler: {}", handler.name()); handler.on_connect(api.clone()); } } else { error!("failed to lock handlers") } } /// Internal method to call all on_disconnect handlers fn on_disconnect(&self) { self.is_reachable.store(false, Ordering::Relaxed); // Abort the tray handler task if it's running if let Ok(mut task_guard) = self.tray_handler_task.lock() { if let Some(task) = task_guard.take() { debug!("Aborting tray handler task"); task.abort(); } } // clear the current api client reference. { let mut guard = self.api.lock().unwrap(); *guard = None; } if let Ok(mut handlers) = self.handlers.lock() { for handler in handlers.iter_mut() { handler.on_disconnect(); } } } } pub trait PortmasterExt { fn portmaster(&self) -> &PortmasterInterface; } impl> PortmasterExt for T { fn portmaster(&self) -> &PortmasterInterface { self.state::>().inner() } } pub fn setup(app: AppHandle) { let interface = PortmasterInterface { app: app.clone(), state: Mutex::new(HashMap::new()), is_reachable: AtomicBool::new(false), handlers: Mutex::new(Vec::new()), api: Mutex::new(None), handle_notifications: AtomicBool::new(false), handle_prompts: AtomicBool::new(false), should_show_after_bootstrap: AtomicBool::new(true), tray_handler_task: Mutex::new(None), }; app.manage(interface); // fire of the websocket handler websocket::start_websocket_thread(app.clone()); } ================================================ FILE: desktop/tauri/src-tauri/src/portmaster/notifications.rs ================================================ use crate::portapi::client::*; use crate::portapi::message::*; use crate::portapi::models::notification::*; use crate::portapi::types::*; use log::error; use serde_json::json; use tauri::async_runtime; pub async fn notification_handler(cli: PortAPI) { let res = cli .request(Request::QuerySubscribe("query notifications:".to_string())) .await; if let Ok(mut rx) = res { while let Some(msg) = rx.recv().await { let res = match msg { Response::Ok(key, payload) => Some((key, payload)), Response::New(key, payload) => Some((key, payload)), Response::Update(key, payload) => Some((key, payload)), _ => None, }; if let Some((key, payload)) = res { match payload.parse::() { Ok(n) => { // Skip if this one should not be shown using the system notifications if !n.show_on_system { continue; } // Skip if this action has already been acted on if !n.selected_action_id.is_empty() { continue; } show_notification(&cli, key, n).await; } Err(err) => match err { ParseError::Json(err) => { error!("failed to parse notification: {}", err); } _ => { error!("unknown error when parsing notifications payload"); } }, } } } } } #[cfg(target_os = "linux")] pub async fn show_notification(cli: &PortAPI, key: String, n: Notification) { let mut notif = notify_rust::Notification::new(); notif.body(&n.message); notif.timeout(notify_rust::Timeout::Never); // TODO(ppacher): use n.expires to calculate the timeout. notif.summary(&n.title); notif.icon("portmaster"); for action in n.actions { if !action.visibility.is_empty() { continue } notif.action(&action.id, &action.text); } { let cli_clone = cli.clone(); async_runtime::spawn(async move { let res = notif.show(); // TODO(ppacher): keep a reference of open notifications and close them // if the user reacted inside the UI: match res { Ok(handle) => { handle.wait_for_action(|action| { match action { "__closed" => { // timeout } value => { let value = value.to_string().clone(); async_runtime::spawn(async move { let _ = cli_clone .request(Request::Update( key, Payload::Json( json!({ "SelectedActionID": value }) .to_string(), ), )) .await; }); } } }) } Err(err) => { error!("failed to display notification: {}", err); } } }); } } #[cfg(target_os = "windows")] pub async fn show_notification(cli: &PortAPI, key: String, n: Notification) { use tauri_winrt_notification::{Duration, Sound, Toast}; let mut toast = Toast::new("io.safing.portmaster") .title(&n.title) .text1(&n.message) .sound(Some(Sound::Default)) .duration(Duration::Long); for action in n.actions { if !action.visibility.is_empty() { continue } toast = toast.add_button(&action.text, &action.id); } { let cli = cli.clone(); toast = toast.on_activated(move |action| -> windows::core::Result<()> { if let Some(value) = action { let cli = cli.clone(); let key = key.clone(); async_runtime::spawn(async move { let _ = cli .request(Request::Update( key, Payload::Json( json!({ "SelectedActionID": value }) .to_string(), ), )) .await; }); } else { error!("Notification clicked but no action associated."); // TODO(vladimir): If Action is None, the user clicked on the notification. Focus on the UI. } Ok(()) }); } toast.show().expect("unable to send notification"); // TODO(vladimir): keep a reference of open notifications and close them // if the user reacted inside the UI: } ================================================ FILE: desktop/tauri/src-tauri/src/portmaster/websocket.rs ================================================ use super::PortmasterExt; use crate::portapi::client::connect; use log::{debug, error, info, warn}; use std::sync::atomic::{AtomicBool, Ordering}; use tauri::{AppHandle, Runtime}; use tokio::time::{sleep, Duration}; static WEBSOCKET_SHUTDOWN: AtomicBool = AtomicBool::new(false); /// Signals the websocket thread to stop reconnecting and shut down gracefully. pub fn shutdown_websocket() { WEBSOCKET_SHUTDOWN.store(true, Ordering::Release); } /// Starts a backround thread (via tauri::async_runtime) that connects to the Portmaster /// Websocket database API. pub fn start_websocket_thread(app: AppHandle) { let app = app.clone(); tauri::async_runtime::spawn(async move { loop { // Check if we should shutdown before attempting to connect if WEBSOCKET_SHUTDOWN.load(Ordering::Acquire) { debug!("WebSocket thread shutting down gracefully"); break; } debug!("Trying to connect to websocket endpoint"); let api = connect("ws://127.0.0.1:817/api/database/v1").await; match api { Ok(cli) => { let portmaster = app.portmaster(); info!("Successfully connected to portmaster"); portmaster.on_connect(cli.clone()); // Monitor connection status loop { if WEBSOCKET_SHUTDOWN.load(Ordering::Acquire) { debug!("Shutdown signal received, closing connection"); break; } if cli.is_closed() { warn!("Connection to portmaster lost"); break; } sleep(Duration::from_secs(1)).await; } portmaster.on_disconnect(); // If shutdown was requested, exit the loop if WEBSOCKET_SHUTDOWN.load(Ordering::Acquire) { debug!("Exiting websocket thread after disconnect"); break; } warn!("lost connection to portmaster, retrying ....") } Err(err) => { error!("failed to create portapi client: {}", err); app.portmaster().on_disconnect(); // Check shutdown flag before sleeping if WEBSOCKET_SHUTDOWN.load(Ordering::Acquire) { debug!("Shutdown requested, not retrying connection"); break; } // Sleep and retry with constant 2 second delay sleep(Duration::from_secs(2)).await; } } } info!("WebSocket thread terminated"); }); } ================================================ FILE: desktop/tauri/src-tauri/src/service/manager.rs ================================================ use std::process::{Command, ExitStatus, Stdio}; use std::{fs, io}; use thiserror::Error; #[cfg(target_os = "linux")] use std::os::unix::fs::PermissionsExt; use super::status::StatusResult; static SYSTEMCTL: &str = "systemctl"; // TODO(ppacher): add support for kdesudo and gksudo enum SudoCommand { Pkexec, Gksu, } ================================================ FILE: desktop/tauri/src-tauri/src/service/mod.rs ================================================ // pub mod manager; pub mod status; #[cfg(target_os = "linux")] mod systemd; #[cfg(target_os = "windows")] mod windows_service; use std::process::ExitStatus; #[cfg(target_os = "linux")] use crate::service::systemd::SystemdServiceManager; use thiserror::Error; use self::status::StatusResult; #[allow(dead_code)] #[derive(Error, Debug)] pub enum ServiceManagerError { #[error("unsupported service manager")] UnsupportedServiceManager, #[error("unsupported operating system")] UnsupportedOperatingSystem, #[error(transparent)] FromUtf8Error(#[from] std::string::FromUtf8Error), #[error(transparent)] IoError(#[from] std::io::Error), #[error("{0} output={1}")] Other(ExitStatus, String), #[error("{0}")] WindowsError(String), } pub type Result = std::result::Result; /// A common interface to the system manager service (might be systemd, openrc, sc.exe, ...) pub trait ServiceManager { fn status(&self) -> Result; fn start(&self) -> Result; } #[allow(dead_code)] struct EmptyServiceManager(); impl ServiceManager for EmptyServiceManager { fn status(&self) -> Result { Err(ServiceManagerError::UnsupportedServiceManager) } fn start(&self) -> Result { Err(ServiceManagerError::UnsupportedServiceManager) } } pub fn get_service_manager() -> Result { #[cfg(target_os = "linux")] { if SystemdServiceManager::is_installed() { log::info!("system service manager: systemd"); Ok(SystemdServiceManager {}) } else { Err(ServiceManagerError::UnsupportedServiceManager) } } #[cfg(target_os = "windows")] return Ok(windows_service::SERVICE_MANGER.clone()); } ================================================ FILE: desktop/tauri/src-tauri/src/service/status.rs ================================================ use serde::{Serialize, Deserialize}; /// SystemResult defines the "success" codes when querying or starting /// a system service. #[derive(Serialize, Deserialize, Debug, PartialEq)] pub enum StatusResult { // The requested system service is installed and currently running. Running, // The requested system service is installed but currently stopped. Stopped, // NotFound is returned when the system service (systemd unit for linux) // has not been found and the system and likely means the Portmaster installtion // is broken all together. NotFound, } impl std::fmt::Display for StatusResult { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { StatusResult::Running => write!(f, "running"), StatusResult::Stopped => write!(f, "stopped"), StatusResult::NotFound => write!(f, "not installed") } } } ================================================ FILE: desktop/tauri/src-tauri/src/service/systemd.rs ================================================ use log::{debug, error}; use super::status::StatusResult; use super::{Result, ServiceManager, ServiceManagerError}; use std::os::unix::fs::PermissionsExt; use std::{ fs, io, process::{Command, ExitStatus, Stdio}, }; static SYSTEMCTL: &str = "systemctl"; // TODO(ppacher): add support for kdesudo and gksudo enum SudoCommand { Pkexec, Gksu, } impl From for ServiceManagerError { fn from(output: std::process::Output) -> Self { let msg = String::from_utf8(output.stderr) .ok() .filter(|s| !s.trim().is_empty()) .or_else(|| { String::from_utf8(output.stdout) .ok() .filter(|s| !s.trim().is_empty()) }) .unwrap_or_else(|| "Failed to run `systemctl`".to_string()); ServiceManagerError::Other(output.status, msg) } } /// System Service manager implementation for Linux based distros. pub struct SystemdServiceManager {} impl SystemdServiceManager { /// Checks if systemctl is available in /sbin/ /bin, /usr/bin or /usr/sbin. /// /// Note that we explicitly check those paths to avoid returning true in case /// there's a systemctl binary in the cwd and PATH includes . since this may /// pose a security risk of running an untrusted binary with root privileges. pub fn is_installed() -> bool { let paths = vec![ "/sbin/systemctl", "/bin/systemctl", "/usr/sbin/systemctl", "/usr/bin/systemctl", ]; for path in paths { debug!("checking for systemctl at path {}", path); match fs::metadata(path) { Ok(md) => { debug!("found systemctl at path {} ", path); if md.is_file() && md.permissions().mode() & 0o111 != 0 { return true; } error!( "systemctl binary found but invalid permissions: {}", md.permissions().mode().to_string() ); } Err(err) => { error!( "failed to check systemctl binary at {}: {}", path, err.to_string() ); continue; } }; } error!("failed to find systemctl binary"); false } } impl ServiceManager for SystemdServiceManager { fn status(&self) -> super::Result { let name = "portmaster.service"; let result = systemctl("is-active", name, false); match result { // If `systemctl is-active` returns without an error code and stdout matches "active" (just to guard againt // unhandled cases), the service can be considered running. Ok(stdout) => { let mut copy = stdout.to_owned(); trim_newline(&mut copy); if copy != "active" { // make sure the output is as we expected Err(ServiceManagerError::Other(ExitStatus::default(), stdout)) } else { Ok(StatusResult::Running) } } Err(e) => { if let ServiceManagerError::Other(_err, ref output) = e { let mut copy = output.to_owned(); trim_newline(&mut copy); if copy == "inactive" { return Ok(StatusResult::Stopped); } } else { error!("failed to run 'systemctl is-active': {}", e.to_string()); } // Failed to check if the unit is running match systemctl("cat", name, false) { // "systemctl cat" seems to no have stable exit codes so we need // to check the output if it looks like "No files found for yyyy.service" // At least, the exit code are not documented for systemd v255 (newest at the time of writing) Err(ServiceManagerError::Other(status, msg)) => { if msg.contains("No files found for") { Ok(StatusResult::NotFound) } else { Err(ServiceManagerError::Other(status, msg)) } } // Any other error type means something went completely wrong while running systemctl altogether. Err(e) => Err(e), // Fine, systemctl cat worked so if the output is "inactive" we know the service is installed // but stopped. Ok(_) => { // Unit seems to be installed so check the output of result let mut stderr = e.to_string(); trim_newline(&mut stderr); if stderr == "inactive" { Ok(StatusResult::Stopped) } else { Err(e) } } } } } } fn start(&self) -> Result { let name = "portmaster.service"; // This time we need to run as root through pkexec or similar binaries like kdesudo/gksudo. systemctl("start", name, true)?; // Check the status again to be sure it's started now self.status() } } fn systemctl( cmd: &str, unit: &str, run_as_root: bool, ) -> std::result::Result { let output = run(run_as_root, SYSTEMCTL, vec![cmd, unit])?; // The command have been able to run (i.e. has been spawned and executed by the kernel). // We now need to check the exit code and "stdout/stderr" output in case of an error. if output.status.success() { Ok(String::from_utf8(output.stdout)?) } else { Err(output.into()) } } fn run<'a>(root: bool, cmd: &'a str, args: Vec<&'a str>) -> std::io::Result { // clone the args vector so we can insert the actual command in case we're running // through pkexec or friends. This is just callled a couple of times on start-up // so cloning the vector does not add any mentionable performance impact here and it's better // than expecting a mutalble vector in the first place. let mut args = args.to_vec(); let mut command = match root { true => { // if we run through pkexec and friends we need to append cmd as the second argument. args.insert(0, cmd); match get_sudo_cmd() { Ok(cmd) => { match cmd { SudoCommand::Pkexec => { // disable the internal text-based prompt agent from pkexec because it won't work anyway. args.insert(0, "--disable-internal-agent"); Command::new("/usr/bin/pkexec") } SudoCommand::Gksu => { args.insert(0, "--message=Please enter your password:"); args.insert(1, "--sudo-mode"); Command::new("/usr/bin/gksudo") } } } Err(err) => return Err(err), } } false => Command::new(cmd), }; command.env("LC_ALL", "C"); command .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::piped()); command.args(args).output() } fn trim_newline(s: &mut String) { if s.ends_with('\n') { s.pop(); if s.ends_with('\r') { s.pop(); } } } fn get_sudo_cmd() -> std::result::Result { if fs::metadata("/usr/bin/pkexec").is_ok() { return Ok(SudoCommand::Pkexec); } if fs::metadata("/usr/bin/gksudo").is_ok() { return Ok(SudoCommand::Gksu); } Err(std::io::Error::new( io::ErrorKind::NotFound, "failed to detect sudo command", )) } ================================================ FILE: desktop/tauri/src-tauri/src/service/windows_service.rs ================================================ use std::{ sync::{Arc, Mutex}, time::Duration, }; use windows::{ core::{HSTRING, PCWSTR}, Win32::{Foundation::HWND, UI::WindowsAndMessaging::SHOW_WINDOW_CMD}, }; use windows_service::{ service::{Service, ServiceAccess}, service_manager::{ServiceManager, ServiceManagerAccess}, }; const SERVICE_NAME: &str = "PortmasterCore"; pub struct WindowsServiceManager { manager: Option, service: Option, } lazy_static! { pub static ref SERVICE_MANGER: Arc> = Arc::new(Mutex::new(WindowsServiceManager::new())); } impl WindowsServiceManager { pub fn new() -> Self { Self { manager: None, service: None, } } fn init_manager(&mut self) -> super::Result<()> { // Initialize service manager. This connects to the active service database and can query status. let manager = match ServiceManager::local_computer( None::<&str>, ServiceManagerAccess::ENUMERATE_SERVICE, // Only query status is allowed form non privileged application. ) { Ok(manager) => manager, Err(err) => { return Err(windows_to_manager_err(err)); } }; self.manager = Some(manager); Ok(()) } fn open_service(&mut self) -> super::Result { if let None = self.manager { self.init_manager()?; } if let Some(manager) = &self.manager { let service = match manager.open_service(SERVICE_NAME, ServiceAccess::QUERY_STATUS) { Ok(service) => service, Err(_) => { return Ok(false); // Service is not installed. } }; // Service is installed and the state can be queried. self.service = Some(service); return Ok(true); } return Err(super::ServiceManagerError::WindowsError( "failed to initialize manager".to_string(), )); } } impl super::ServiceManager for Arc> { fn status(&self) -> super::Result { if let Ok(mut manager) = self.lock() { if let None = manager.service { // Try to open service if !manager.open_service()? { // Service is not installed. return Ok(super::status::StatusResult::NotFound); } } if let Some(service) = &manager.service { match service.query_status() { Ok(status) => match status.current_state { windows_service::service::ServiceState::Stopped | windows_service::service::ServiceState::StopPending | windows_service::service::ServiceState::PausePending | windows_service::service::ServiceState::StartPending | windows_service::service::ServiceState::ContinuePending | windows_service::service::ServiceState::Paused => { // Stopped or in a transition state. return Ok(super::status::StatusResult::Stopped); } windows_service::service::ServiceState::Running => { // Everything expect Running state is considered stopped. return Ok(super::status::StatusResult::Running); } }, Err(err) => { return Err(super::ServiceManagerError::WindowsError(err.to_string())); } } } } // This should be unreachable. Ok(super::status::StatusResult::NotFound) } fn start(&self) -> super::Result { if let Ok(mut service_manager) = self.lock() { // Check if service is installed. if let None = &service_manager.service { if let Err(_) = service_manager.open_service() { return Ok(super::status::StatusResult::NotFound); } } // Run service manager with elevated privileges. This will show access popup. unsafe { windows::Win32::UI::Shell::ShellExecuteW( HWND::default(), &HSTRING::from("runas"), &HSTRING::from("C:\\Windows\\System32\\sc.exe"), &HSTRING::from(format!("start {}", SERVICE_NAME)), PCWSTR::null(), SHOW_WINDOW_CMD(0), ); } // Wait for service to start. Timeout 10s (100 * 100ms). if let Some(service) = &service_manager.service { for _ in 0..100 { match service.query_status() { Ok(status) => { if let windows_service::service::ServiceState::Running = status.current_state { return Ok(super::status::StatusResult::Running); } else { std::thread::sleep(Duration::from_millis(100)); } } Err(err) => return Err(windows_to_manager_err(err)), } } } // Timeout starting the service. return Ok(super::status::StatusResult::Stopped); } return Err(super::ServiceManagerError::WindowsError( "failed to start service".to_string(), )); } } fn windows_to_manager_err(err: windows_service::Error) -> super::ServiceManagerError { if let windows_service::Error::Winapi(_) = err { // Winapi does not contain the full error. Get the actual error from windows. return super::ServiceManagerError::WindowsError( windows::core::Error::from_win32().to_string(), // Internally will call `GetLastError()` and parse the result. ); } else { return super::ServiceManagerError::WindowsError(err.to_string()); } } ================================================ FILE: desktop/tauri/src-tauri/src/traymenu.rs ================================================ use std::ops::Deref; use std::sync::atomic::AtomicBool; use std::sync::RwLock; use std::{sync::atomic::Ordering}; use chrono::{DateTime, Local}; use log::{debug, error}; use tauri::{ image::Image, menu::{Menu, MenuBuilder, MenuItemBuilder, PredefinedMenuItem, SubmenuBuilder}, tray::{MouseButton, MouseButtonState, TrayIcon, TrayIconBuilder}, Manager, Wry, }; use tauri_plugin_window_state::{AppHandleExt, StateFlags}; use crate::config; use crate::{ portapi::{ client::PortAPI, message::{ParseError}, models::{ config::BooleanValue, spn::SPNStatus, system_status_types::{self, SystemStatus}, }, types::{Request, Response}, }, portmaster::PortmasterExt, window::{create_main_window, may_navigate_to_ui, open_window}, }; use tauri_plugin_dialog::{DialogExt, MessageDialogButtons}; pub type AppIcon = TrayIcon; pub type ContextMenu = Menu; static SPN_STATE: AtomicBool = AtomicBool::new(false); #[derive(Copy, Clone)] enum IconColor { Red, Green, Blue, Yellow, } static CURRENT_ICON_COLOR: RwLock = RwLock::new(IconColor::Red); pub static USER_THEME: RwLock = RwLock::new(dark_light::Mode::Unspecified); const OPEN_KEY: &str = "open"; const EXIT_UI_KEY: &str = "exit_ui"; const SPN_STATUS_KEY: &str = "spn_status"; const SPN_BUTTON_KEY: &str = "spn_toggle"; const GLOBAL_STATUS_KEY: &str = "global_status"; const SHUTDOWN_KEY: &str = "shutdown"; const SYSTEM_THEME_KEY: &str = "system_theme"; const LIGHT_THEME_KEY: &str = "light_theme"; const DARK_THEME_KEY: &str = "dark_theme"; const RELOAD_KEY: &str = "reload"; const FORCE_SHOW_KEY: &str = "force-show"; const PM_TRAY_ICON_ID: &str = "pm_icon"; const PM_TRAY_MENU_ID: &str = "pm_tray_menu"; const PAUSE_SPN_5_KEY: &str = "pause_spn_5"; const PAUSE_SPN_15_KEY: &str = "pause_spn_15"; const PAUSE_SPN_60_KEY: &str = "pause_spn_60"; const PAUSE_PM_5_KEY: &str = "pause_pm_5"; const PAUSE_PM_15_KEY: &str = "pause_pm_15"; const PAUSE_PM_60_KEY: &str = "pause_pm_60"; const RESUME_KEY: &str = "resume_all"; const PAUSE_INFO_KEY: &str = "pause_info"; const PAUSE_INFO_TIME_KEY: &str = "pause_info_time"; // Icons fn get_theme_mode() -> dark_light::Mode { if let Ok(value) = USER_THEME.read() { return *value.deref(); } dark_light::detect().unwrap_or(dark_light::Mode::Unspecified) } fn get_green_icon() -> &'static [u8] { const LIGHT_GREEN_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_light_green_64.png"); const DARK_GREEN_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_dark_green_64.png"); match get_theme_mode() { dark_light::Mode::Light => DARK_GREEN_ICON, _ => LIGHT_GREEN_ICON, } } fn get_blue_icon() -> &'static [u8] { const LIGHT_BLUE_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_light_blue_64.png"); const DARK_BLUE_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_dark_blue_64.png"); match get_theme_mode() { dark_light::Mode::Light => DARK_BLUE_ICON, _ => LIGHT_BLUE_ICON, } } fn get_red_icon() -> &'static [u8] { const LIGHT_RED_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_light_red_64.png"); const DARK_RED_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_dark_red_64.png"); match get_theme_mode() { dark_light::Mode::Light => DARK_RED_ICON, _ => LIGHT_RED_ICON, } } fn get_yellow_icon() -> &'static [u8] { const LIGHT_YELLOW_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_light_yellow_64.png"); const DARK_YELLOW_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_dark_yellow_64.png"); match get_theme_mode() { dark_light::Mode::Light => DARK_YELLOW_ICON, _ => LIGHT_YELLOW_ICON, } } fn get_icon(icon: IconColor) -> &'static [u8] { match icon { IconColor::Red => get_red_icon(), IconColor::Green => get_green_icon(), IconColor::Blue => get_blue_icon(), IconColor::Yellow => get_yellow_icon(), } } fn build_tray_menu( app: &tauri::AppHandle, status: &str, spn_status_text: &str, pause_info: &system_status_types::PauseInfo, ) -> core::result::Result> { load_theme(app); let open_btn = MenuItemBuilder::with_id(OPEN_KEY, "Open App").build(app)?; let exit_ui_btn = MenuItemBuilder::with_id(EXIT_UI_KEY, "Exit UI").build(app)?; let shutdown_btn = MenuItemBuilder::with_id(SHUTDOWN_KEY, "Shut Down Portmaster").build(app)?; // Global status let global_status_text = if pause_info.interception { format!("Status: {} (PAUSED)", status) } else { format!("Status: {}", status) }; let global_status = MenuItemBuilder::with_id(GLOBAL_STATUS_KEY, global_status_text) .enabled(false) .build(app) .unwrap(); // Pause items let (pause_status_item, pause_status_time_item, resume_item) = if pause_info.interception || pause_info.spn { let status_text = match (pause_info.interception, pause_info.spn) { (true, true) => "Portmaster and SPN are paused", (true, false) => "Portmaster is paused", (false, true) => "SPN is paused", _ => unreachable!(), // We already checked at least one is true }; let status_item = MenuItemBuilder::with_id(PAUSE_INFO_KEY, status_text).enabled(false).build(app).ok(); let time_item = if let Ok(resume_time) = DateTime::parse_from_rfc3339(&pause_info.till_time) { let resume_time_local = resume_time.with_timezone(&Local); if resume_time_local > Local::now() { let formatted_time = resume_time_local.format("%H:%M:%S").to_string(); MenuItemBuilder::with_id(PAUSE_INFO_TIME_KEY, format!("Auto-resume at {}", formatted_time)).enabled(false).build(app).ok() } else { None } } else { None }; let resume_item = MenuItemBuilder::with_id(RESUME_KEY, "Resume now").build(app).ok(); (status_item, time_item, resume_item) } else { (None, None, None) }; // SPN button let (spn_enabled, spn_button_text ) = match spn_status_text { "disabled" => { (false, "Enable SPN") } _ => { (true, "Disable SPN") }, }; let spn_status = MenuItemBuilder::with_id(SPN_STATUS_KEY, format!("SPN: {}", spn_status_text)) .enabled(false) .build(app) .unwrap(); let spn_button = MenuItemBuilder::with_id(SPN_BUTTON_KEY, spn_button_text) .build(app) .unwrap(); // Setup Icon theme submenu let system_theme = MenuItemBuilder::with_id(SYSTEM_THEME_KEY, "System") .build(app) .unwrap(); let light_theme = MenuItemBuilder::with_id(LIGHT_THEME_KEY, "Light") .build(app) .unwrap(); let dark_theme = MenuItemBuilder::with_id(DARK_THEME_KEY, "Dark") .build(app) .unwrap(); let theme_menu = SubmenuBuilder::new(app, "Icon Theme") .items(&[&system_theme, &light_theme, &dark_theme]) .build()?; // Setup Pause/Resume menu items let disabled_spn_pause = (!spn_enabled && !pause_info.spn) || pause_info.interception; let pause_spn_5min_item = MenuItemBuilder::with_id(PAUSE_SPN_5_KEY, "Pause SPN for 5 minutes").enabled(!disabled_spn_pause).build(app)?; let pause_spn_15min_item = MenuItemBuilder::with_id(PAUSE_SPN_15_KEY, "Pause SPN for 15 minutes").enabled(!disabled_spn_pause).build(app)?; let pause_spn_1hour_item = MenuItemBuilder::with_id(PAUSE_SPN_60_KEY, "Pause SPN for 1 hour").enabled(!disabled_spn_pause).build(app)?; let pause_pm_5min_item = MenuItemBuilder::with_id(PAUSE_PM_5_KEY, "Pause for 5 minutes").build(app)?; let pause_pm_15min_item = MenuItemBuilder::with_id(PAUSE_PM_15_KEY, "Pause for 15 minutes").build(app)?; let pause_pm_1hour_item = MenuItemBuilder::with_id(PAUSE_PM_60_KEY, "Pause for 1 hour").build(app)?; let pause_menu = if !spn_enabled && !pause_info.spn { SubmenuBuilder::new(app, "Pause") .items(&[ &pause_pm_5min_item, &pause_pm_15min_item, &pause_pm_1hour_item, ]) .build()? } else { SubmenuBuilder::new(app, "Pause") .items(&[ &pause_spn_5min_item, &pause_spn_15min_item, &pause_spn_1hour_item, &PredefinedMenuItem::separator(app)?, &pause_pm_5min_item, &pause_pm_15min_item, &pause_pm_1hour_item, ]) .build()? }; /* DEV MENU let force_show_window = MenuItemBuilder::with_id(FORCE_SHOW_KEY, "Force Show UI").build(app)?; let reload_btn = MenuItemBuilder::with_id(RELOAD_KEY, "Reload User Interface").build(app)?; let developer_menu = SubmenuBuilder::new(app, "Developer") .items(&[&reload_btn, &force_show_window]) .build()?; */ // Assemble menu items let s = PredefinedMenuItem::separator(app)?; let mut items: Vec<&dyn tauri::menu::IsMenuItem> = Vec::new(); items.push(&global_status); items.push(&s); if let Some(ref pause_status_item) = pause_status_item { items.push(pause_status_item); } if let Some(ref pause_status_time_item) = pause_status_time_item { items.push(pause_status_time_item); } if let Some(ref resume_item) = resume_item { items.push(resume_item); } items.push(&pause_menu); items.push(&s); items.push(&spn_status); items.push(&spn_button); items.push(&s); items.push(&theme_menu); items.push(&s); items.push(&open_btn); items.push(&s); items.push(&exit_ui_btn); items.push(&shutdown_btn); //items.push(&developer_menu); let menu = MenuBuilder::with_id(app, PM_TRAY_MENU_ID) .items(&items) .build()?; return Ok(menu); } pub fn setup_tray_menu( app: &mut tauri::App, ) -> core::result::Result> { let menu = build_tray_menu(app.handle(), "unknown", "disabled", &system_status_types::PauseInfo::default())?; let icon = TrayIconBuilder::with_id(PM_TRAY_ICON_ID) .icon(Image::from_bytes(get_red_icon()).unwrap()) .menu(&menu) .on_menu_event(move |app, event| match event.id().as_ref() { EXIT_UI_KEY => { let handle = app.clone(); app.dialog() .message("This does not stop the Portmaster system service") .title("Do you really want to quit the user interface?") .buttons(MessageDialogButtons::OkCancelCustom( "Yes, exit".to_owned(), "No".to_owned(), )) .show(move |answer| { if answer { // let _ = handle.emit("exit-requested", ""); handle.exit(0); } }); } OPEN_KEY => { let _ = open_window(app); } RELOAD_KEY => { if let Ok(mut win) = open_window(app) { may_navigate_to_ui(&mut win, true); } } FORCE_SHOW_KEY => { match create_main_window(app) { Ok(mut win) => { may_navigate_to_ui(&mut win, true); if let Err(err) = win.show() { error!("[tauri] failed to show window: {}", err.to_string()); }; } Err(err) => { error!("[tauri] failed to create main window: {}", err.to_string()); } }; } SPN_BUTTON_KEY => { if SPN_STATE.load(Ordering::Acquire) { app.portmaster().set_spn_enabled(false); } else { app.portmaster().set_spn_enabled(true); } } SHUTDOWN_KEY => { app.portmaster().trigger_shutdown(); } SYSTEM_THEME_KEY => update_icon_theme(app, dark_light::Mode::Unspecified), DARK_THEME_KEY => update_icon_theme(app, dark_light::Mode::Dark), LIGHT_THEME_KEY => update_icon_theme(app, dark_light::Mode::Light), PAUSE_SPN_5_KEY => app.portmaster().set_pause(60*5, true), PAUSE_SPN_15_KEY => app.portmaster().set_pause(60*15, true), PAUSE_SPN_60_KEY => app.portmaster().set_pause(60*60, true), PAUSE_PM_5_KEY => app.portmaster().set_pause(60*5, false), PAUSE_PM_15_KEY => app.portmaster().set_pause(60*15, false), PAUSE_PM_60_KEY => app.portmaster().set_pause(60*60, false), RESUME_KEY => app.portmaster().set_resume(), other => { error!("unknown menu event id: {}", other); } }) .on_tray_icon_event(|tray, event| { // not supported on linux if let tauri::tray::TrayIconEvent::Click { id: _, position: _, rect: _, button, button_state, } = event { if let (MouseButton::Left, MouseButtonState::Down) = (button, button_state) { let _ = open_window(tray.app_handle()); } } }) .build(app)?; Ok(icon) } pub fn update_icon(icon: AppIcon, system_status: SystemStatus, spn_status: String) { // Extract the worst state type let worst_state_type = system_status.worst_state .as_ref() .and_then(|ws| ws.state.state_type.clone()) .unwrap_or(system_status_types::StateType::Undefined); // Determine status and icon color in a single match expression let (status, icon_color) = match worst_state_type { system_status_types::StateType::Error => ("Insecure", IconColor::Red), system_status_types::StateType::Warning => ("Insecure", IconColor::Yellow), _ => { let color = match spn_status.as_str() { "connected" | "connecting" => IconColor::Blue, _ => IconColor::Green, }; ("Secured", color) } }; // Extract pause info from system status let pause_info = system_status .get_module_state("Control", "control:paused") .and_then(|state| state.data.as_ref()) .and_then(|data| serde_json::from_value::(data.clone()).ok()) .unwrap_or_default(); // Rebuild and set the tray menu if let Ok(menu) = build_tray_menu(icon.app_handle(), status, spn_status.as_str(), &pause_info) { if let Err(err) = icon.set_menu(Some(menu)) { error!("failed to set menu on tray icon: {}", err.to_string()); } } update_icon_color(&icon, icon_color); } pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) { let icon = match app.tray_by_id(PM_TRAY_ICON_ID) { Some(icon) => icon, None => { error!("cancel try_handler: missing try icon"); return; } }; let mut system_status_subscription = match cli .request(Request::QuerySubscribe( "query runtime:system/status".to_string(), )) .await { Ok(rx) => rx, Err(err) => { error!( "cancel try_handler: failed to subscribe to 'runtime:system/status': {}", err ); return; } }; let mut spn_status_subscription = match cli .request(Request::QuerySubscribe( "query runtime:spn/status".to_string(), )) .await { Ok(rx) => rx, Err(err) => { error!( "cancel try_handler: failed to subscribe to 'runtime:spn/status': {}", err ); return; } }; let mut spn_config_subscription = match cli .request(Request::QuerySubscribe( "query config:spn/enable".to_string(), )) .await { Ok(rx) => rx, Err(err) => { error!( "cancel try_handler: failed to subscribe to 'runtime:spn/enable': {}", err ); return; } }; let mut portmaster_shutdown_event_subscription = match cli .request(Request::Subscribe( "query runtime:modules/core/event/shutdown".to_string(), )) .await { Ok(rx) => rx, Err(err) => { error!( "cancel try_handler: failed to subscribe to 'runtime:modules/core/event/shutdown': {}", err ); return; } }; update_icon_color(&icon, IconColor::Blue); let mut system_status = SystemStatus::default(); let mut spn_status: String = "".to_string(); loop { tokio::select! { msg = system_status_subscription.recv() => { let msg = match msg { Some(m) => m, None => { break } }; let res = match msg { Response::Ok(key, payload) => Some((key, payload)), Response::New(key, payload) => Some((key, payload)), Response::Update(key, payload) => Some((key, payload)), _ => None, }; if let Some((_, payload)) = res { match payload.parse::() { Ok(system_status_update) => { system_status.clone_from(&system_status_update); update_icon(icon.clone(), system_status.clone(), spn_status.clone()); }, Err(err) => match err { ParseError::Json(err) => { error!("failed to parse SystemStatus: {}", err); } _ => { error!("unknown error when parsing SystemStatus payload"); } }, } } }, msg = spn_status_subscription.recv() => { let msg = match msg { Some(m) => m, None => { break } }; let res = match msg { Response::Ok(key, payload) => Some((key, payload)), Response::New(key, payload) => Some((key, payload)), Response::Update(key, payload) => Some((key, payload)), _ => None, }; if let Some((_, payload)) = res { match payload.parse::() { Ok(value) => { debug!("SPN status update: {}", value.status); spn_status.clone_from(&value.status); update_icon(icon.clone(), system_status.clone(), spn_status.clone()); }, Err(err) => match err { ParseError::Json(err) => { error!("failed to parse spn status value: {}", err) }, _ => { error!("unknown error when parsing spn status value") } } } } }, msg = spn_config_subscription.recv() => { let msg = match msg { Some(m) => m, None => { break } }; let res = match msg { Response::Ok(key, payload) => Some((key, payload)), Response::New(key, payload) => Some((key, payload)), Response::Update(key, payload) => Some((key, payload)), _ => None, }; if let Some((_, payload)) = res { match payload.parse::() { Ok(value) => { SPN_STATE.store(value.value.unwrap_or(false), Ordering::Release); }, Err(err) => match err { ParseError::Json(err) => { error!("failed to parse config value: {}", err) }, _ => { error!("unknown error when parsing config value") } } } } }, msg = portmaster_shutdown_event_subscription.recv() => { let msg = match msg { Some(m) => m, None => { break } }; debug!("Shutdown request received: {:?}", msg); match msg { Response::Ok(msg, _) | Response::New(msg, _) | Response::Update(msg, _) => { if let Err(err) = app.save_window_state(StateFlags::SIZE | StateFlags::POSITION) { error!("failed to save window state: {}", err); } debug!("shutting down: {}", msg); app.exit(0) }, _ => {}, } } } } update_icon_nostate(icon.clone()); } pub fn update_icon_nostate(icon: AppIcon) { update_icon_color(&icon, IconColor::Red); if let Ok(menu) = build_tray_menu(icon.app_handle(), "unknown", "unknown", &system_status_types::PauseInfo::default()) { if let Err(err) = icon.set_menu(Some(menu)) { error!("failed to set menu on tray icon: {}", err.to_string()); } } } fn update_icon_color(icon: &AppIcon, new_color: IconColor) { if let Ok(mut value) = CURRENT_ICON_COLOR.write() { *value = new_color; } _ = icon.set_icon(Some(Image::from_bytes(get_icon(new_color)).unwrap())); } fn update_icon_theme(app: &tauri::AppHandle, theme: dark_light::Mode) { if let Ok(mut value) = USER_THEME.write() { *value = theme; } let icon = match app.tray_by_id(PM_TRAY_ICON_ID) { Some(icon) => icon, None => { error!("cancel theme update: missing try icon"); return; } }; if let Ok(value) = CURRENT_ICON_COLOR.read() { _ = icon.set_icon(Some(Image::from_bytes(get_icon(*value)).unwrap())); } for (_, v) in app.webview_windows() { super::window::set_window_icon(&v); } save_theme(app, theme); } fn load_theme(app: &tauri::AppHandle) { match config::load(app) { Ok(config) => { let theme = match config.theme { config::Theme::Light => dark_light::Mode::Light, config::Theme::Dark => dark_light::Mode::Dark, config::Theme::System => dark_light::Mode::Unspecified, }; if let Ok(mut value) = USER_THEME.write() { *value = theme; } } Err(err) => error!("failed to load config file: {}", err), } } fn save_theme(app: &tauri::AppHandle, mode: dark_light::Mode) { match config::load(app) { Ok(mut config) => { let theme = match mode { dark_light::Mode::Dark => config::Theme::Dark, dark_light::Mode::Light => config::Theme::Light, dark_light::Mode::Unspecified => config::Theme::System, }; config.theme = theme; if let Err(err) = config::save(app, config) { error!("failed to save config file: {}", err) } else { debug!("config updated"); } } Err(err) => error!("failed to load config file: {}", err), } } ================================================ FILE: desktop/tauri/src-tauri/src/window.rs ================================================ use log::{debug, error}; use tauri::{ image::Image, AppHandle, Listener, Manager, Result, Theme, UserAttentionType, WebviewUrl, WebviewWindow, WebviewWindowBuilder, }; use std::sync::{atomic::{AtomicBool, Ordering}}; use tauri_plugin_window_state::{AppHandleExt, StateFlags}; use crate::{portmaster::PortmasterExt, traymenu}; const LIGHT_PM_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_light_512.png"); const DARK_PM_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_dark_512.png"); const CUSTOM_ENVVAR_FOR_WEBVIEW_PROCESS: &str = "PORTMASTER_UI_WEBVIEW_PROCESS"; static UI_PROCESS_ENV_VAR_DEFINED_FLAG: AtomicBool = AtomicBool::new(false); /// Either returns the existing "main" window or creates a new one. /// /// The window is not automatically shown (i.e it starts hidden). /// If a new main window is created (i.e. the tauri app was minimized to system-tray) /// then the window will be automatically navigated to the Portmaster UI endpoint /// if ::websocket::is_portapi_reachable returns true. /// /// Either the existing or the newly created window is returned. pub fn create_main_window(app: &AppHandle) -> Result { let mut window = if let Some(window) = app.get_webview_window("main") { debug!("[tauri] main window already created"); window } else { debug!("[tauri] creating main window"); do_before_any_window_create(); // required operations before window creation let res = WebviewWindowBuilder::new(app, "main", WebviewUrl::App("index.html".into())) .title("Portmaster") .visible(false) .inner_size(1200.0, 700.0) .min_inner_size(800.0, 600.0) .zoom_hotkeys_enabled(true) .theme(Some(Theme::Dark)) .on_page_load(|_window, _event| { debug!("[tauri] main window page loaded: {}", _event.url()); do_after_main_window_created(); // required operations after Main window creation }) .on_navigation(|url| { debug!("[tauri] main window navigation event: {}", url); if url.as_str() == "about:blank" { debug!("[tauri] blocking navigation to about:blank"); return false; } return true; }) .build(); match res { Ok(win) => { win.once("tauri://error", |event| { error!("failed to open tauri window: {}", event.payload()); }); #[cfg(target_os = "linux")] { // Workaround for KDE/Wayland environments on Linux: // On KDE with Wayland, after hiding and showing the window, // the title-bar buttons (close, minimize, maximize) may stop working. // Toggling the resizable property appears to resolve this issue. // Issue: https://github.com/safing/portmaster/issues/1909 // Additional info: https://github.com/tauri-apps/tauri/issues/6162#issuecomment-1423304398 let win_clone = win.clone(); win.listen("tauri://focus", move |_event| { let _ = win_clone.set_resizable(false); let _ = win_clone.set_resizable(true); }); } win } Err(err) => { error!("[tauri] failed to create main window: {}", err.to_string()); return Err(err); } } }; // If the window is not yet navigated to the Portmaster UI, do it now. may_navigate_to_ui(&mut window, false); set_window_icon(&window); #[cfg(debug_assertions)] if std::env::var("TAURI_SHOW_IMMEDIATELY").is_ok() { debug!("[tauri] TAURI_SHOW_IMMEDIATELY is set, opening window"); if let Err(err) = window.show() { error!("[tauri] failed to show window: {}", err.to_string()); } } Ok(window) } pub fn create_splash_window(app: &AppHandle) -> Result { if let Some(window) = app.get_webview_window("splash") { let _ = window.show(); Ok(window) } else { do_before_any_window_create(); // required operations before window creation let window = WebviewWindowBuilder::new(app, "splash", WebviewUrl::App("index.html".into())) .center() .closable(false) .focused(true) .resizable(false) .visible(true) .title("Portmaster") .inner_size(600.0, 250.0) .zoom_hotkeys_enabled(true) .build()?; set_window_icon(&window); let _ = window.request_user_attention(Some(UserAttentionType::Informational)); Ok(window) } } pub fn close_splash_window(app: &AppHandle) -> Result<()> { if let Some(window) = app.get_webview_window("splash") { let _ = window.hide(); return window.destroy(); } Err(tauri::Error::WindowNotFound) } pub fn hide_splash_window(app: &AppHandle) -> Result<()> { if let Some(window) = app.get_webview_window("splash") { return window.hide(); } Err(tauri::Error::WindowNotFound) } pub fn set_window_icon(window: &WebviewWindow) { let mut mode = if let Ok(value) = traymenu::USER_THEME.read() { *value } else { dark_light::Mode::Unspecified }; if mode == dark_light::Mode::Unspecified { mode = dark_light::detect().unwrap_or(dark_light::Mode::Dark); } let _ = match mode { dark_light::Mode::Light => window.set_icon(Image::from_bytes(DARK_PM_ICON).unwrap()), _ => window.set_icon(Image::from_bytes(LIGHT_PM_ICON).unwrap()), }; } /// This function must be called before any window is created. /// /// Temporarily sets the environment variable `PORTMASTER_WEBVIEW_UI_PROCESS` to "true". /// This ensures that any child process (i.e., the WebView process) spawned during window creation /// will inherit this environment variable. This allows portmaster-core to detect that the process /// is a child WebView of the main process. /// /// IMPORTANT: After the 'Main' window is created, you must call `do_after_main_window_created()` to remove /// the environment variable from the main process environment. /// This ensures that any subsequent child processes (such as those created by "open external" functionality) /// will not inherit this environment variable, correctly indicating that they are not part of the /// Portmaster UI WebView process. pub fn do_before_any_window_create() { UI_PROCESS_ENV_VAR_DEFINED_FLAG.store(true, Ordering::SeqCst); std::env::set_var(CUSTOM_ENVVAR_FOR_WEBVIEW_PROCESS, "true"); } /// This function must be called after the Main window is created. /// /// Removes the `PORTMASTER_WEBVIEW_UI_PROCESS` environment variable from the main process. /// This ensures that only the child WebView process has the variable set, and the main process /// does not retain it. pub fn do_after_main_window_created() { let flag_was_set = UI_PROCESS_ENV_VAR_DEFINED_FLAG.compare_exchange( true, false, Ordering::SeqCst, Ordering::SeqCst ).is_ok(); if flag_was_set { std::env::remove_var(CUSTOM_ENVVAR_FOR_WEBVIEW_PROCESS); } } /// Opens a window for the tauri application. /// /// If the main window has already been created, it is instructed to /// show even if we're currently not connected to Portmaster. /// This is safe since the main-window will only be created if Portmaster API /// was reachable so the angular application must have finished bootstrapping. /// /// If there's not main window and the Portmaster API is reachable we create a new /// main window. /// /// If the Portmaster API is unreachable and there's no main window yet, we show the /// splash-screen window. pub fn open_window(app: &AppHandle) -> Result { if app.portmaster().is_reachable() { match app.get_webview_window("main") { Some(win) => { if let Ok(true) = win.is_minimized() { let _ = win.unminimize(); } app.portmaster().show_window(); let _ = win.show(); let _ = win.set_focus(); set_window_icon(&win); Ok(win) } None => { app.portmaster().show_window(); create_main_window(app) } } } else { debug!("Show splash screen"); create_splash_window(app) } } /// If the Portmaster Websocket database API is reachable the window will be navigated /// to the HTTP endpoint of Portmaster to load the UI from there. /// /// Note that only happens if the window URL does not already point to the PM API. /// /// In #[cfg(debug_assertions)] the TAURI_PM_URL environment variable will be used /// if set. /// Otherwise or in release builds, it will be navigated to http://127.0.0.1:817. pub fn may_navigate_to_ui(win: &mut WebviewWindow, force: bool) { if !win.app_handle().portmaster().is_reachable() && !force { error!("[tauri] portmaster API is not reachable, not navigating"); return; } if force || win.label().eq("main") { #[cfg(debug_assertions)] if let Ok(target_url) = std::env::var("TAURI_PM_URL") { debug!("[tauri] navigating to {}", target_url); _ = win.navigate(target_url.parse().unwrap()); return; } #[cfg(debug_assertions)] { // Only for dev build // Allow connection to http://localhost:4200 let capabilities = include_str!("../capabilities/default.json") .replace("http://127.0.0.1:817", "http://127.0.0.1:4200"); let _ = win.add_capability(capabilities); debug!("[tauri] navigating to http://127.0.0.1:4200"); _ = win.navigate("http://127.0.0.1:4200".parse().unwrap()); } #[cfg(not(debug_assertions))] { _ = win.navigate("http://127.0.0.1:817".parse().unwrap()); } } else { error!( "not navigating to user interface: current url: {}", win.url().unwrap().as_str() ); } } /// Creates a debounced window state saver that waits for a quiet period before saving. /// /// Returns a sender that can be used to trigger save events. Multiple rapid events /// will be debounced - only saving after the specified timeout with no new events. /// /// # Example /// ```rust /// let save_trigger = create_debounced_window_state_saver(app, state_flags, Duration::from_secs(5)); /// let _ = save_trigger.try_send(()); // Trigger a save (will be debounced) /// ``` pub fn create_debounced_window_state_saver( app: &tauri::App, state_flags: StateFlags, debounce_timeout: std::time::Duration, ) -> tokio::sync::mpsc::Sender<()> { let app_handle = app.handle().clone(); let (tx, mut rx) = tokio::sync::mpsc::channel::<()>(10); // Spawn debouncer task - saves state after the specified timeout following the last event tauri::async_runtime::spawn(async move { loop { if rx.recv().await.is_none() { break; // Channel closed } loop { match tokio::time::timeout(debounce_timeout, rx.recv()).await { Ok(Some(_)) => { continue; // Received another event within timeout period, restart the timer } Ok(None) => { return; // Channel closed } Err(_) => { // Timeout: specified duration passed without new events, save state if let Err(e) = app_handle.save_window_state(state_flags) { debug!("Failed to save window state: {}", e); } break; // Exit inner loop and wait for next event } } } } }); tx } ================================================ FILE: desktop/tauri/src-tauri/src/xdg/mod.rs ================================================ use cached::proc_macro::once; use dataurl::DataUrl; use gdk_pixbuf::{Pixbuf, PixbufError}; use gtk_sys::{ gtk_icon_info_free, gtk_icon_info_get_filename, gtk_icon_theme_get_default, gtk_icon_theme_lookup_icon, }; use log::{debug, error}; use std::collections::HashMap; use std::ffi::{c_char, c_int}; use std::ffi::{CStr, CString}; use std::io; use std::path::{Path, PathBuf}; use std::sync::{Arc, RwLock}; use std::{ env, fs, io::{Error, ErrorKind}, }; use thiserror::Error; use ini::{Ini, ParseOption}; lazy_static! { static ref APP_INFO_CACHE: Arc>>> = Arc::new(RwLock::new(HashMap::new())); } #[derive(Debug, Error)] pub enum LookupError { #[error(transparent)] IoError(#[from] std::io::Error), } pub type Result = std::result::Result; #[derive(Clone, serde::Serialize)] pub struct AppInfo { pub icon_name: String, pub app_name: String, pub icon_dataurl: String, pub comment: String, } impl Default for AppInfo { fn default() -> Self { AppInfo { icon_dataurl: "".to_string(), icon_name: "".to_string(), app_name: "".to_string(), comment: "".to_string(), } } } #[derive(Clone, serde::Serialize, Debug)] pub struct ProcessInfo { pub exec_path: String, pub cmdline: String, pub pid: i64, pub matching_path: String, } impl std::fmt::Display for ProcessInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{} (cmdline={}) (pid={}) (matching_path={})", self.exec_path, self.cmdline, self.pid, self.matching_path ) } } pub fn get_app_info(process_info: ProcessInfo) -> Result { { let cache = APP_INFO_CACHE.read().unwrap(); if let Some(value) = cache.get(process_info.exec_path.as_str()) { match value { Some(app_info) => return Ok(app_info.clone()), None => { return Err(LookupError::IoError(io::Error::new( io::ErrorKind::NotFound, "not found", ))) } } } } let mut needles = Vec::new(); if !process_info.exec_path.is_empty() { needles.push(process_info.exec_path.as_str()) } if !process_info.cmdline.is_empty() { needles.push(process_info.cmdline.as_str()) } if !process_info.matching_path.is_empty() { needles.push(process_info.matching_path.as_str()) } // sort and deduplicate needles.sort(); needles.dedup(); debug!("Searching app info for {:?}", process_info); let mut desktop_files = Vec::new(); for dir in get_application_directories()? { let mut files = find_desktop_files(dir.as_path())?; desktop_files.append(&mut files); } let mut matches = Vec::new(); for needle in needles.clone() { debug!("Trying needle {} on exec path", needle); match try_get_app_info(needle, CheckType::Exec, &desktop_files) { Ok(mut result) => { matches.append(&mut result); } Err(LookupError::IoError(ioerr)) => { if ioerr.kind() != ErrorKind::NotFound { return Err(ioerr.into()); } } }; match try_get_app_info(needle, CheckType::Name, &desktop_files) { Ok(mut result) => { matches.append(&mut result); } Err(LookupError::IoError(ioerr)) => { if ioerr.kind() != ErrorKind::NotFound { return Err(ioerr.into()); } } }; } if matches.is_empty() { APP_INFO_CACHE .write() .unwrap() .insert(process_info.exec_path, None); Err(Error::new(ErrorKind::NotFound, "failed to find app info".to_string()).into()) } else { // sort matches by length matches.sort_by(|a, b| a.1.cmp(&b.1)); for mut info in matches { match get_icon_as_png_dataurl(&info.0.icon_name, 32) { Ok(du) => { debug!( "[xdg] best match for {:?} is {:?} with len {}", process_info, info.0.icon_name, info.1 ); info.0.icon_dataurl = du.1; APP_INFO_CACHE .write() .unwrap() .insert(process_info.exec_path, Some(info.0.clone())); return Ok(info.0); } Err(err) => { dbg!( "{}: failed to get icon: {}", info.0.icon_name, err.to_string() ); } }; } Err(Error::new(ErrorKind::NotFound, "failed to find app info".to_string()).into()) } } /// Returns a vector of application directories that are expected /// to contain all .desktop files the current user has access to. /// The result of this function is cached for 5 minutes as it's not expected /// that application directories actually change. #[once(time = 300, sync_writes = true, result = true)] fn get_application_directories() -> Result> { let xdg_home = match env::var_os("XDG_DATA_HOME") { Some(path) => PathBuf::from(path), None => { let home = dirs::home_dir() .ok_or(Error::new(ErrorKind::Other, "Failed to get home directory"))?; home.join(".local/share") } }; let extra_application_dirs = match env::var_os("XDG_DATA_DIRS") { Some(paths) => env::split_paths(&paths).map(PathBuf::from).collect(), None => { // Fallback if XDG_DATA_DIRS is not set. If it's set, it normally already contains /usr/share and // /usr/local/share vec![ PathBuf::from("/usr/share"), PathBuf::from("/usr/local/share"), ] } }; let mut app_dirs = Vec::new(); for extra_dir in extra_application_dirs { app_dirs.push(extra_dir.join("applications")); } app_dirs.push(xdg_home.join("applications")); Ok(app_dirs) } // TODO(ppacher): cache the result of find_desktop_files as well. // Though, seems like we cannot use the #[cached::proc_macro::cached] or #[cached::proc_macro::once] macros here // because [`Result>>`] does not implement [`Clone`] fn find_desktop_files(path: &Path) -> Result> { match path.read_dir() { Ok(files) => { let desktop_files = files .filter_map(|entry| entry.ok()) .filter(|entry| match entry.file_type() { Ok(ft) => ft.is_file() || ft.is_symlink(), _ => false, }) .filter(|entry| entry.file_name().to_string_lossy().ends_with(".desktop")) .collect::>(); Ok(desktop_files) } Err(err) => { // We ignore NotFound errors here because not all application // directories need to exist. if err.kind() == ErrorKind::NotFound { Ok(Vec::new()) } else { Err(err.into()) } } } } enum CheckType { Name, Exec, } fn try_get_app_info( needle: &str, check: CheckType, desktop_files: &Vec, ) -> Result> { let path = PathBuf::from(needle); let file_name = path.as_path().file_name().unwrap_or_default().to_str(); let mut result = Vec::new(); for file in desktop_files { let content = Ini::load_from_file_opt( file.path(), ParseOption { enabled_escape: false, enabled_quote: true, }, ) .map_err(|err| Error::new(ErrorKind::Other, err.to_string()))?; let desktop_section = match content.section(Some("Desktop Entry")) { Some(section) => section, None => { continue; } }; let matches = match check { CheckType::Name => { let name = match desktop_section.get("Name") { Some(name) => name, None => { continue; } }; if let Some(file_name) = file_name { if name.to_lowercase().contains(file_name) { file_name.len() } else { 0 } } else { 0 } } CheckType::Exec => { let exec = match desktop_section.get("Exec") { Some(exec) => exec, None => { continue; } }; if exec.to_lowercase().contains(needle) { needle.len() } else if let Some(file_name) = file_name { if exec.to_lowercase().starts_with(file_name) { file_name.len() } else { 0 } } else { 0 } } }; if matches > 0 { debug!( "[xdg] found matching desktop for needle {} file at {}", needle, file.path().to_string_lossy() ); let info = parse_app_info(desktop_section); result.push((info, matches)); } } if !result.is_empty() { Ok(result) } else { Err(Error::new(ErrorKind::NotFound, "no matching .desktop files found").into()) } } fn parse_app_info(props: &ini::Properties) -> AppInfo { AppInfo { icon_dataurl: "".to_string(), app_name: props.get("Name").unwrap_or_default().to_string(), comment: props.get("Comment").unwrap_or_default().to_string(), icon_name: props.get("Icon").unwrap_or_default().to_string(), } } fn get_icon_as_png_dataurl(name: &str, size: i8) -> Result<(String, String)> { // gtk_icon_theme_get_default() is lightweight - it returns a borrowed reference to GTK's singleton icon theme let theme = unsafe { gtk_icon_theme_get_default() }; if theme.is_null() { return Err(Error::new(ErrorKind::Other, "GTK not initialized").into()); } let mut icons = Vec::new(); // push the name icons.push(name); // if we don't find the icon by it's name and it includes an extension, // drop the extension and try without. let name_without_ext; if let Some(ext) = PathBuf::from(name).extension() { let ext = ext.to_str().unwrap(); let mut ext_dot = String::from(".").to_owned(); ext_dot.push_str(ext); name_without_ext = name.replace(ext_dot.as_str(), ""); icons.push(name_without_ext.as_str()); } else { name_without_ext = String::from(name); } // The xdg-desktop icon specification allows a fallback for icons that contains dashes. // i.e. the following lookup order is used: // - network-wired-secure // - network-wired // - network // name_without_ext .split('-') .for_each(|part| icons.push(part)); for name in icons { debug!("trying to load icon {}", name); unsafe { let c_str = CString::new(name).unwrap(); let icon_info = gtk_icon_theme_lookup_icon( theme, c_str.as_ptr() as *const c_char, size as c_int, 0, ); if icon_info.is_null() { dbg!("failed to lookup icon {}", name); continue; } let filename = gtk_icon_info_get_filename(icon_info); let filename = CStr::from_ptr(filename).to_str().unwrap().to_string(); gtk_icon_info_free(icon_info); match read_and_convert_pixbuf(filename.clone()) { Ok(pb) => return Ok((filename, pb)), Err(err) => { dbg!("failed to load icon from {}: {}", filename, err.to_string()); continue; } } } } Err(Error::new(ErrorKind::NotFound, "failed to find icon").into()) } /* fn get_icon_as_file_2(ext: &str, size: i32) -> io::Result<(String, Vec)> { let result: String; let buf: Vec; unsafe { let filename = CString::new(ext).unwrap(); let null: u8 = 0; let p_null = &null as *const u8; let nullsize: usize = 0; let mut res = 0; let p_res = &mut res as *mut i32; let p_res = gio_sys::g_content_type_guess(filename.as_ptr(), p_null, nullsize, p_res); let icon = gio_sys::g_content_type_get_icon(p_res); g_free(p_res as *mut c_void); if DEFAULT_THEME.is_none() { let theme = gtk_icon_theme_get_default(); if theme.is_null() { println!("You have to initialize GTK!"); return Err(io::Error::new(io::ErrorKind::Other, "You have to initialize GTK!")) } let theme = gtk_icon_theme_get_default(); DEFAULT_THEME = Some(theme); } let icon_names = gio_sys::g_themed_icon_get_names(icon as *mut GThemedIcon) as *mut *const i8; let icon_info = gtk_icon_theme_choose_icon(DEFAULT_THEME.unwrap(), icon_names, size, GTK_ICON_LOOKUP_NO_SVG); let filename = gtk_icon_info_get_filename(icon_info); gtk_icon_info_free(icon_info); result = CStr::from_ptr(filename).to_str().unwrap().to_string(); buf = match read_and_convert_pixbuf(result.clone()) { Ok(pb) => pb, Err(_) => Vec::new(), }; g_object_unref(icon as *mut GObject); } Ok((result, buf)) } */ fn read_and_convert_pixbuf(result: String) -> std::result::Result { let pixbuf = match Pixbuf::from_file(result.clone()) { Ok(data) => Ok(data), Err(err) => { error!("failed to load icon pixbuf: {}", err.to_string()); Pixbuf::from_resource(result.clone().as_str()) } }; match pixbuf { Ok(data) => match data.save_to_bufferv("png", &[]) { Ok(data) => { let mut du = DataUrl::new(); du.set_media_type(Some("image/png".to_string())); du.set_data(&data); Ok(du.to_string()) } Err(err) => { return Err(glib::Error::new( PixbufError::Failed, err.to_string().as_str(), )); } }, Err(err) => Err(err), } } #[cfg(test)] mod tests { use super::*; use ctor::ctor; use log::warn; use which::which; // Use the ctor create to setup a global initializer before our tests are executed. #[ctor] fn init() { // we need to initialize GTK before running our tests. // This is only required when unit tests are executed as // GTK will otherwise be initialize by Tauri. gtk::init().expect("failed to initialize GTK for tests") } #[test] fn test_find_info_success() { // we expect at least one of the following binaries to be installed // on a linux system let test_binaries = vec![ "vim", // vim is mostly bundled with a .desktop file "blueman-manager", // blueman-manager is the default bluetooth manager on most DEs "nautilus", // nautlis: file-manager on GNOME DE "thunar", // thunar: file-manager on XFCE "dolphin", // dolphin: file-manager on KDE ]; let mut bin_found = false; for cmd in test_binaries { match which(cmd) { Ok(bin) => { bin_found = true; let bin = bin.to_string_lossy().to_string(); let result = get_app_info(ProcessInfo { cmdline: cmd.to_string(), exec_path: bin.clone(), matching_path: bin.clone(), pid: 0, }) .unwrap_or_else(|_| panic!("expected to find app info for {} ({})", bin, cmd)); let empty_string = String::from(""); // just make sure all fields are populated assert_ne!(result.app_name, empty_string); assert_ne!(result.comment, empty_string); assert_ne!(result.icon_name, empty_string); assert_ne!(result.icon_dataurl, empty_string); } Err(_) => { // binary not found continue; } } } if !bin_found { warn!("test_find_info_success: no test binary found, test was skipped") } } } ================================================ FILE: desktop/tauri/src-tauri/tauri.conf.json5 ================================================ { "build": { "beforeDevCommand": { "script": "npm run tauri-dev", "cwd": "../../angular", "wait": false }, "frontendDist": "../../angular/dist/tauri-builtin", "devUrl": "http://localhost:4100" }, "plugins": { "cli": { "args": [ { "short": "d", "name": "data", "description": "Path to the installation directory", "takesValue": true }, { "short": "b", "name": "background", "description": "Start in the background without opening a window" }, { "name": "log", "description": "Log level to use: off, error, warn, info, debug, trace", "takesValue": true }, { "name": "no-notifications", "description": "Disable notifications via Tauri." }, { "name": "no-prompts", "description": "Disable prompt support via Tauri." }, ] } }, "productName": "Portmaster", "identifier": "io.safing.portmaster", // this is added as a property to the shortcut on windows (ApplicationUserModelID). Used for notifications. "app": { "withGlobalTauri": true, "enableGTKAppId": false, "security": { "csp": null } }, "bundle": { "active": true, "category": "Utility", "copyright": "Safing Limited Inc", "linux": { "deb": { "depends": [ "libayatana-appindicator3-1" ], "desktopTemplate": "../../../packaging/linux/portmaster.desktop", "files": { // Service file "/usr/lib/systemd/system/portmaster.service": "../../../packaging/linux/portmaster.service", // Binary files "/usr/lib/portmaster/portmaster-core": "binary/portmaster-core", "/usr/lib/portmaster/portmaster.zip": "binary/portmaster.zip", "/usr/lib/portmaster/assets.zip": "binary/assets.zip", // Intel files "/var/lib/portmaster/intel/index.json": "intel/index.json", "/var/lib/portmaster/intel/base.dsdl": "intel/base.dsdl", "/var/lib/portmaster/intel/geoipv4.mmdb": "intel/geoipv4.mmdb", "/var/lib/portmaster/intel/geoipv6.mmdb": "intel/geoipv6.mmdb", "/var/lib/portmaster/intel/index.dsd": "intel/index.dsd", "/var/lib/portmaster/intel/intermediate.dsdl": "intel/intermediate.dsdl", "/var/lib/portmaster/intel/urgent.dsdl": "intel/urgent.dsdl", "/var/lib/portmaster/intel/main-intel.yaml" : "intel/main-intel.yaml", "/var/lib/portmaster/intel/notifications.yaml": "intel/notifications.yaml", "/var/lib/portmaster/intel/news.yaml" : "intel/news.yaml", // Shortcut "/etc/xdg/autostart/portmaster.desktop": "../../../packaging/linux/portmaster-autostart.desktop" }, "postInstallScript": "../../../packaging/linux/postinst", "postRemoveScript": "../../../packaging/linux/postrm" }, "rpm": { "depends": [ "libayatana-appindicator-gtk3" ], "desktopTemplate": "../../../packaging/linux/portmaster.desktop", "release": "1", "files": { // Service file "/usr/lib/systemd/system/portmaster.service": "../../../packaging/linux/portmaster.service", // Binary files "/usr/lib/portmaster/portmaster-core": "binary/portmaster-core", "/usr/lib/portmaster/portmaster.zip": "binary/portmaster.zip", "/usr/lib/portmaster/assets.zip": "binary/assets.zip", // Intel files "/var/lib/portmaster/intel/index.json": "intel/index.json", "/var/lib/portmaster/intel/base.dsdl": "intel/base.dsdl", "/var/lib/portmaster/intel/geoipv4.mmdb": "intel/geoipv4.mmdb", "/var/lib/portmaster/intel/geoipv6.mmdb": "intel/geoipv6.mmdb", "/var/lib/portmaster/intel/index.dsd": "intel/index.dsd", "/var/lib/portmaster/intel/intermediate.dsdl": "intel/intermediate.dsdl", "/var/lib/portmaster/intel/urgent.dsdl": "intel/urgent.dsdl", "/var/lib/portmaster/intel/main-intel.yaml" : "intel/main-intel.yaml", "/var/lib/portmaster/intel/notifications.yaml": "intel/notifications.yaml", "/var/lib/portmaster/intel/news.yaml" : "intel/news.yaml", // Shortcut "/etc/xdg/autostart/portmaster.desktop": "../../../packaging/linux/portmaster-autostart.desktop" }, "postInstallScript": "../../../packaging/linux/postinst", "postRemoveScript": "../../../packaging/linux/postrm" } }, "windows": { "nsis": { "installMode": "perMachine", "installerHooks": "templates/nsis/install_hooks.nsh", "installerIcon": "../../../assets/data/icons/pm_light_contrast.ico" }, "wix": { "fragmentPaths": [ "templates/wix/files.wxs", "templates/wix/old_service_check.wxs", "templates/wix/migration.wxs", ], "componentGroupRefs": ["BinaryAndIntelFiles"], "template": "templates/wix/main.wxs" } }, "targets": [ "deb", "rpm", "nsis" //, "msi" ], "icon": [ "../../../assets/data/icons/pm_dark_512.png", "../../../assets/data/icons/pm_light_contrast.ico", "../../../assets/data/icons/pm_dark.ico", "../../../assets/data/icons/pm_light_512.png", "../../../assets/data/icons/pm_light.ico" ] } } ================================================ FILE: desktop/tauri/src-tauri/templates/NSIS_Simple_Service_Plugin_Unicode_1.30/License.txt ================================================ SimpleSC - NSIS Service Control Plugin - License Agreement This plugin is subject to the Mozilla Public License Version 1.1 (the "License"); You may not use this plugin except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL. Alternatively, you may redistribute this library, use and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. You may obtain a copy of the LGPL at www.gnu.org/copyleft. Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyright Portions of this software are Copyright (C) 2001 - Peter Windridge, 2003 by Bernhard Mayer, Fixed and formatted by Brett Dever http://editor.nfscheats.com/ The original code is ServiceControl.pas, released April 16, 2007. The initial developer of the original code is Rainer Budde (http://www.speed-soft.de). SimpleSC - NSIS Service Control Plugin is written, published and maintaned by Rainer Budde (rainer@speed-soft.de). ================================================ FILE: desktop/tauri/src-tauri/templates/NSIS_Simple_Service_Plugin_Unicode_1.30/Readme.txt ================================================ NSIS Simple Service Plugin This plugin contains basic service functions like start, stop the service or checking the service status. It also contains advanced service functions for example setting the service description, changed the logon account, granting or removing the service logon privilege. == Short Reference == SimpleSC::InstallService [name_of_service] [display_name] [service_type] [start_type] [binary_path] [dependencies] [account] [password] SimpleSC::RemoveService [name_of_service] SimpleSC::StartService [name_of_service] [arguments] [timeout] SimpleSC::StopService [name_of_service] [wait_for_file_release] [timeout] SimpleSC::PauseService [name_of_service] [timeout] SimpleSC::ContinueService [name_of_service] [timeout] SimpleSC::RestartService [name_of_service] [arguments] [timeout] SimpleSC::ExistsService [name_of_service] SimpleSC::GetServiceDisplayName [name_of_service] SimpleSC::GetServiceName [display_name] SimpleSC::GetServiceStatus [name_of_service] SimpleSC::GetServiceDescription [name_of_service] SimpleSC::GetServiceStartType [name_of_service] SimpleSC::GetServiceBinaryPath [name_of_service] SimpleSC::GetServiceLogon [name_of_service] SimpleSC::GetServiceFailure [name_of_service] SimpleSC::GetServiceFailureFlag [name_of_service] SimpleSC::GetServiceDelayedAutoStartInfo [name_of_service] SimpleSC::SetServiceDescription [name_of_service] [service_description] SimpleSC::SetServiceStartType [name_of_service] [start_type] SimpleSC::SetServiceBinaryPath [name_of_service] [binary_path] SimpleSC::SetServiceLogon [name_of_service] [account] [password] SimpleSC::SetServiceFailure [name_of_service] [reset_period] [reboot_message] [command] [action_type_1] [action_delay_1] [action_type_2] [action_delay_2] [action_type_3] [action_delay_3] SimpleSC::SetServiceFailureFlag [name_of_service] [failure_actions_on_non_crash_failures] SimpleSC::SetServiceDelayedAutoStartInfo [name_of_service] [delayed_autostart] SimpleSC::GrantServiceLogonPrivilege [account] SimpleSC::RemoveServiceLogonPrivilege [account] SimpleSC::ServiceIsPaused [name_of_service] SimpleSC::ServiceIsRunning [name_of_service] SimpleSC::ServiceIsStopped [name_of_service] SimpleSC::GetErrorMessage [error_code] Parameters: name_of_service - The name of the service used for Start/Stop commands and all further commands display_name - The name as shown in the service control manager applet in system control service_type - One of the following codes 1 - SERVICE_KERNEL_DRIVER - Driver service. 2 - SERVICE_FILE_SYSTEM_DRIVER - File system driver service. 16 - SERVICE_WIN32_OWN_PROCESS - Service that runs in its own process. (Should be used in most cases) 32 - SERVICE_WIN32_SHARE_PROCESS - Service that shares a process with one or more other services. 256 - SERVICE_INTERACTIVE_PROCESS - The service can interact with the desktop. Note: If you specify either SERVICE_WIN32_OWN_PROCESS or SERVICE_WIN32_SHARE_PROCESS, and the service is running in the context of the LocalSystem account, you can also specify this value. Example: SERVICE_WIN32_OWN_PROCESS or SERVICE_INTERACTIVE_PROCESS - (16 or 256) = 272 Note: Services cannot directly interact with a user as of Windows Vista. Therefore, this technique should not be used in new code. See for more information: http://msdn2.microsoft.com/en-us/library/ms683502(VS.85).aspx start_type - one of the following codes 0 - SERVICE_BOOT_START - Driver boot stage start 1 - SERVICE_SYSTEM_START - Driver scm stage start 2 - SERVICE_AUTO_START - Service auto start (Should be used in most cases) 3 - SERVICE_DEMAND_START - Driver/service manual start 4 - SERVICE_DISABLED - Driver/service disabled service_status - one of the following codes 1 - SERVICE_STOPPED 2 - SERVICE_START_PENDING 3 - SERVICE_STOP_PENDING 4 - SERVICE_RUNNING 5 - SERVICE_CONTINUE_PENDING 6 - SERVICE_PAUSE_PENDING 7 - SERVICE_PAUSED binary_path - The path to the binary including all necessary parameters dependencies - Needed services, controls which services have to be started before this one; use the forward slash "/" to add more more than one service account - The username/account which should be used password - Password of the aforementioned account to be able to logon as a service Note: If you do not specify account/password, the local system account will be used to run the service arguments - Arguments passed to the service main function. Note: Driver services do not receive these arguments. reset_period - The time after which to reset the failure count to zero if there are no failures, in seconds. Specify 0 (INFINITE) to indicate that this value should never be reset reboot_message - The message to be broadcast to server users before rebooting command - The command line of the process to execute in response to the SC_ACTION_RUN_COMMAND service controller action. This process runs under the same account as the service timeout - Timeout in seconds of the function action_type_x - one of the following codes for the action to be performed 0 - SC_ACTION_NONE - No action 1 - SC_ACTION_RESTART - Restart the service 2 - SC_ACTION_REBOOT - Reboot the computer (Note: The service user must have the SE_SHUTDOWN_NAME privilege) 3 - SC_ACTION_RUN_COMMAND - Run a command action_delay_x - The time to wait before performing the specified action, in milliseconds failure_actions_on_non_crash_failures - This setting determines when failure actions are to be executed 0 - The failure actions executed only if the service terminates without reporting a status of SERVICE_STOPPED 1 - The failure actions executed if the status of a service is SERVICE_STOPPED but the exit code of the service is not 0 delayed_autostart - The delayed auto-start setting of an auto-start service 0 - The service will be started during system boot. 1 - The service will be started after other auto-start services are started plus a short delay error_code - Error code of a function service_description - The description as shown in the service control manager applet in system control wait_for_file_release - Wait for file release after the service is stopped. This is useful if the binary file will be overwritten after stopping the service. 0 - NO_WAIT - No wait for file release 1 - WAIT - Wait for file release Note: If SERVICE_WIN32_OWN_PROCESS is used this option should be set to WAIT. If SERVICE_WIN32_SHARE_PROCESS is used this option should only be set to WAIT if the last service in the process is stopped. == The Sample Script == ; Install a service - ServiceType own process - StartType automatic - NoDependencies - Logon as System Account SimpleSC::InstallService "MyService" "My Service Display Name" "16" "2" "C:\MyPath\MyService.exe" "" "" "" Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Install a service - ServiceType interact with desktop - StartType automatic - Dependencies on "Windows Time Service" (w32time) and "WWW Publishing Service" (w3svc) - Logon as System Account SimpleSC::InstallService "MyService" "My Service Display Name" "272" "2" "C:\MyPath\MyService.exe" "w32time/w3svc" "" "" Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Remove a service SimpleSC::RemoveService "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Start a service SimpleSC::StartService "MyService" "" 30 Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Start a service with two arguments "/param1=true" "/param2=1" SimpleSC::StartService "MyService" "/param1=true /param2=1" 30 Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Start a service with two arguments "-p param1" "-param2" SimpleSC::StartService "MyService" '"-p param1" -param2' 30 Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Stop a service and waits for file release SimpleSC::StopService "MyService" 1 30 Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Stops two services and waits for file release after the last service is stopped SimpleSC::StopService "MyService1" 0 30 Pop $0 ; returns an errorcode (<>0) otherwise success (0) SimpleSC::StopService "MyService2" 1 30 Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Pause a service SimpleSC::PauseService "MyService" 30 Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Continue a service SimpleSC::ContinueService "MyService" 30 Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Restart a service SimpleSC::RestartService "MyService" "" 30 Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Restart a service with two arguments "/param1=true" "/param2=1" SimpleSC::RestartService "MyService" "/param1=true /param2=1" 30 Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Start a service with two arguments "-p param1" "-param2" SimpleSC::RestartService "MyService" '"-p param1" -param2' 30 Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Check if the service exists SimpleSC::ExistsService "MyService" Pop $0 ; returns an errorcode if the service doesnt exists (<>0)/service exists (0) ; Get the displayname of a service SimpleSC::GetServiceDisplayName "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; returns the displayname of the service ; Get the servicename of a service by the displayname SimpleSC::GetServiceName "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; returns the servicename of the service ; Get the current status of a service SimpleSC::GetServiceStatus "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; return the status of the service (See "service_status" in the parameters) ; Get the description of a service SimpleSC::GetServiceDescription "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; returns the description of the service ; Get the start type of the service SimpleSC::GetServiceStartType "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; returns the start type of the service (see "start_type" in the parameters) ; Get the binary path of a service SimpleSC::GetServiceBinaryPath "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; returns the binary path of the service ; Get the logon user of the service SimpleSC::GetServiceLogon "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; returns the logon username of the service ; Get the failure configuration of a service SimpleSC::GetServiceFailure "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; returns the reset period Pop $2 ; returns the reboot message Pop $3 ; returns the command Pop $4 ; returns the first action (See "action_type_x" in the parameters) Pop $5 ; returns the first action delay Pop $6 ; returns the second action (See "action_type_x" in the parameters) Pop $7 ; returns the second action delay Pop $8 ; returns the third action (See "action_type_x" in the parameters) Pop $9 ; returns the third action delay ; Get the failure flag configuration of a service SimpleSC::GetServiceFailureFlag "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; returns the service flag ; Get the delayed auto-start configuration of a service SimpleSC::GetServiceDelayedAutoStartInfo "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; returns the delayed auto-start configuration ; Set the description of a service SimpleSC::SetServiceDescription "MyService" "Sample Description" Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Set the starttype to automatic of a service SimpleSC::SetServiceStartType "MyService" "2" Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Sets the service binary path SimpleSC::SetServiceBinaryPath "MyService" "C:\MySoftware\MyService.exe" Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Sets the service logon to a user and grant the user the "SeServiceLogonPrivilege" SimpleSC::SetServiceLogon "MyService" "MyServiceUser" "MyServiceUserPassword" Pop $0 ; returns an errorcode (<>0) otherwise success (0) IntCmp $0 0 +1 Done Done ; If successful grant the service logon privilege to "MyServiceUser" ; Note: Every serviceuser must have the ServiceLogonPrivilege to start the service SimpleSC::GrantServiceLogonPrivilege "MyServiceUser" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Done: ; Sets the service failure configuration - First action: Restart the service after one minute - Second action: Reboot the computer after five minutes SimpleSC::SetServiceFailure "MyService" "0" "" "" "1" "60000" "2" "300000" "0" "0" Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Sets the failure flag configuration of a service SimpleSC::SetServiceFailureFlag "MyService" "1" Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Sets the delayed auto-start configuration of a service SimpleSC::SetServiceDelayedAutoStartInfo "MyService" "1" Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Remove the "SeServiceLogonPrivilege" from a user SimpleSC::RemoveServiceLogonPrivilege "MyServiceUser" Pop $0 ; returns an errorcode (<>0) otherwise success (0) ; Check if the service is paused SimpleSC::ServiceIsPaused "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; returns 1 (service is paused) - returns 0 (service is not paused) ; Check if the service is running SimpleSC::ServiceIsRunning "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; returns 1 (service is running) - returns 0 (service is not running) ; Check if the service is stopped SimpleSC::ServiceIsStopped "MyService" Pop $0 ; returns an errorcode (<>0) otherwise success (0) Pop $1 ; returns 1 (service is stopped) - returns 0 (service is not stopped) ; Show the error message if a function fails SimpleSC::StopService "MyService" 1 30 Pop $0 ; returns an errorcode (<>0) otherwise success (0) IntCmp $0 0 Done +1 +1 Push $0 SimpleSC::GetErrorMessage Pop $0 MessageBox MB_OK|MB_ICONSTOP "Stopping fails - Reason: $0" Done: == Important Notes == - The function "SetServiceLogon" only works if the servicetype is "SERVICE_WIN32_OWN_PROCESS". - The functions "GetServiceDescription", "SetServiceDescription", "GetServiceFailure" and "SetServiceFailure" are only available on systems higher than Windows NT. - The function "GetServiceFailureFlag", "SetServiceFailureFlag", "GetServiceDelayedAutoStartInfo" and "SetServiceDelayedAutoStartInfo" are only available on systems higher than Windows 2003. - If you change the logon of an service to a new user you have to grant him the Service Logon Privilege. Otherwise the service cannot be started by the user you have assigned. - The functions StartService, StopService, PauseService and ContinueService uses a timeout of 30 seconds. This means the function must be executed within 30 seconds, otherwise the functions will return an error. ================================================ FILE: desktop/tauri/src-tauri/templates/NSIS_Simple_Service_Plugin_Unicode_1.30/Source/LSASecurityControl.pas ================================================ { License Agreement This content is subject to the Mozilla Public License Version 1.1 (the "License"); You may not use this plugin except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL. Alternatively, you may redistribute this library, use and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. You may obtain a copy of the LGPL at www.gnu.org/copyleft. Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The original code is LSASecurityControl.pas, released April 16, 2007. The initial developer of the original code is Rainer Dpke (Formerly: Rainer Budde) (https://www.speed-soft.de). SimpleSC - NSIS Service Control Plugin is written, published and maintained by Rainer Dpke (rainer@speed-soft.de). } unit LSASecurityControl; interface uses Winapi.Windows; function GrantPrivilege(AccountName: String; PrivilegeName: String): Integer; function RemovePrivilege(AccountName: String; PrivilegeName: String): Integer; function EnablePrivilege(PrivilegeName: String): Integer; function DisablePrivilege(PrivilegeName: String): Integer; implementation type LSA_HANDLE = Pointer; TLSAHandle = LSA_HANDLE; LSA_UNICODE_STRING = record Length: Word; MaximumLength: Word; Buffer: PWideChar; end; TLSAUnicodeString = LSA_UNICODE_STRING; PLSAUnicodeString = ^TLSAUnicodeString; LSA_OBJECT_ATTRIBUTES = record Length: ULONG; RootDirectory: THandle; ObjectName: PLSAUnicodeString; Attributes: ULONG; SecurityDescriptor: Pointer; SecurityQualityOfService: Pointer; end; TLsaObjectAttributes = LSA_OBJECT_ATTRIBUTES; PLsaObjectAttributes = ^TLsaObjectAttributes; function LsaOpenPolicy(SystemName: PLSAUnicodeString; var ObjectAttributes: TLsaObjectAttributes; DesiredAccess: ACCESS_MASK; var PolicyHandle: LSA_HANDLE): DWORD; stdcall; external 'advapi32.dll'; function LsaAddAccountRights(PolicyHandle: LSA_HANDLE; AccountSid: PSID; UserRights: PLSAUnicodeString; CountOfRights: ULONG): DWORD; stdcall; external 'advapi32.dll'; function LsaRemoveAccountRights(PolicyHandle: LSA_HANDLE; AccountSid: PSID; AllRights: Boolean; UserRights: PLSAUnicodeString; CountOfRights: ULONG): DWORD; stdcall; external 'advapi32.dll'; function LsaClose(ObjectHandle: LSA_HANDLE): DWORD; stdcall; external 'advapi32.dll'; function GetAccountSid(const AccountName: String; var Sid: PSID): Integer; var DomainSize: LongWord; SidSize: LongWord; Domain: String; Use: SID_NAME_USE; begin Result := 0; SidSize := 0; DomainSize := 0; if not LookupAccountName(nil, PChar(AccountName), nil, SidSize, nil, DomainSize, Use) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then begin SetLength(Domain, DomainSize); Sid := AllocMem(SidSize); if not LookupAccountName(nil, PChar(AccountName), Sid, SidSize, PChar(Domain), DomainSize, Use) then begin Result := GetLastError; FreeMem(Sid); Sid := nil; end; end else Result := GetLastError; end; function GrantPrivilege(AccountName: String; PrivilegeName: String): Integer; const UNICODE_NULL = WCHAR(0); POLICY_CREATE_ACCOUNT = $00000010; POLICY_LOOKUP_NAMES = $00000800; var SID: PSID; PolicyHandle: TLSAHandle; LSAPrivilegeName: TLSAUnicodeString; LSAObjectAttributes: TLsaObjectAttributes; pwszPrivilegeName: PWideChar; PrivilegeNameLength: Cardinal; Status: DWORD; begin Result := 0; GetMem(pwszPrivilegeName, Length(PrivilegeName) * SizeOf(WideChar) + 1); StringToWideChar(PrivilegeName, pwszPrivilegeName, Length(PrivilegeName) * SizeOf(WideChar) + 1); ZeroMemory(@LSAObjectAttributes, SizeOf(TLsaObjectAttributes)); PrivilegeNameLength := Length(pwszPrivilegeName); if PrivilegeNameLength > 0 then begin Result := GetAccountSid(AccountName, SID); if Result = 0 then begin LSAPrivilegeName.Length := PrivilegeNameLength * SizeOf(WideChar); LSAPrivilegeName.MaximumLength := LSAPrivilegeName.Length + SizeOf(UNICODE_NULL); LSAPrivilegeName.Buffer := pwszPrivilegeName; Status := LsaOpenPolicy(nil, LSAObjectAttributes, POLICY_LOOKUP_NAMES or POLICY_CREATE_ACCOUNT, PolicyHandle); try if Status = 0 then Result := LsaAddAccountRights(PolicyHandle, Sid, @LSAPrivilegeName, 1) else Result := Status; finally LsaClose(PolicyHandle); end; end; end; FreeMem(pwszPrivilegeName); end; function RemovePrivilege(AccountName: String; PrivilegeName: String): Integer; const UNICODE_NULL = WCHAR(0); POLICY_CREATE_ACCOUNT = $00000010; POLICY_LOOKUP_NAMES = $00000800; var SID: PSID; PolicyHandle: TLSAHandle; LSAPrivilegeName: TLSAUnicodeString; LSAObjectAttributes: TLsaObjectAttributes; pwszPrivilegeName: PWideChar; PrivilegeNameLength: Cardinal; Status: DWORD; begin Result := 0; GetMem(pwszPrivilegeName, Length(PrivilegeName) * SizeOf(WideChar) + 1); StringToWideChar(PrivilegeName, pwszPrivilegeName, Length(PrivilegeName) * SizeOf(WideChar) + 1); ZeroMemory(@LSAObjectAttributes, SizeOf(TLsaObjectAttributes)); PrivilegeNameLength := Length(pwszPrivilegeName); if PrivilegeNameLength > 0 then begin Result := GetAccountSid(AccountName, SID); if Result = 0 then begin LSAPrivilegeName.Length := PrivilegeNameLength * SizeOf(WideChar); LSAPrivilegeName.MaximumLength := LSAPrivilegeName.Length + SizeOf(UNICODE_NULL); LSAPrivilegeName.Buffer := pwszPrivilegeName; Status := LsaOpenPolicy(nil, LSAObjectAttributes, POLICY_LOOKUP_NAMES or POLICY_CREATE_ACCOUNT, PolicyHandle); try if Status = 0 then Result := LsaRemoveAccountRights(PolicyHandle, Sid, False, @LSAPrivilegeName, 1) else Result := Status; finally LsaClose(PolicyHandle); end; end; end; FreeMem(pwszPrivilegeName); end; function EnablePrivilege(PrivilegeName: String): Integer; var TokenHandle: THandle; TokenPrivileges: TOKEN_PRIVILEGES; PreviousState: TOKEN_PRIVILEGES; ReturnLength: Cardinal; begin Result := 0; if OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, TokenHandle) then begin try if LookupPrivilegeValue(nil, PWideChar(PrivilegeName), TokenPrivileges.Privileges[0].Luid) then begin TokenPrivileges.PrivilegeCount := 1; TokenPrivileges.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; if not AdjustTokenPrivileges(TokenHandle, False, TokenPrivileges, SizeOf(TokenPrivileges), PreviousState, ReturnLength) then Result := System.GetLastError; end else Result := System.GetLastError; finally CloseHandle(TokenHandle); end; end else Result := System.GetLastError; end; function DisablePrivilege(PrivilegeName: String): Integer; var TokenHandle: THandle; TokenPrivileges: TOKEN_PRIVILEGES; PreviousState: TOKEN_PRIVILEGES; ReturnLength: Cardinal; begin Result := 0; if OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, TokenHandle) then begin try if LookupPrivilegeValue(nil, PWideChar(PrivilegeName), TokenPrivileges.Privileges[0].Luid) then begin TokenPrivileges.PrivilegeCount := 1; TokenPrivileges.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; if not AdjustTokenPrivileges(TokenHandle, False, TokenPrivileges, SizeOf(TokenPrivileges), PreviousState, ReturnLength) then Result := System.GetLastError; end else Result := System.GetLastError; finally CloseHandle(TokenHandle); end; end else Result := System.GetLastError; end; end. ================================================ FILE: desktop/tauri/src-tauri/templates/NSIS_Simple_Service_Plugin_Unicode_1.30/Source/NSIS Plugins.groupproj ================================================  {0AF40426-B62C-4F43-8B49-19A70AEA0832} Default.Personality.12 ================================================ FILE: desktop/tauri/src-tauri/templates/NSIS_Simple_Service_Plugin_Unicode_1.30/Source/NSIS.pas ================================================ { Original Code from (C) 2001 - Peter Windridge Code in separate unit and some changes 2003 by Bernhard Mayer Fixed and formatted by Brett Dever http://editor.nfscheats.com/ simply include this unit in your plugin project and export functions as needed } unit nsis; interface uses Winapi.Windows, Winapi.CommCtrl, System.SysUtils; type VarConstants = ( INST_0, // $0 INST_1, // $1 INST_2, // $2 INST_3, // $3 INST_4, // $4 INST_5, // $5 INST_6, // $6 INST_7, // $7 INST_8, // $8 INST_9, // $9 INST_R0, // $R0 INST_R1, // $R1 INST_R2, // $R2 INST_R3, // $R3 INST_R4, // $R4 INST_R5, // $R5 INST_R6, // $R6 INST_R7, // $R7 INST_R8, // $R8 INST_R9, // $R9 INST_CMDLINE, // $CMDLINE INST_INSTDIR, // $INSTDIR INST_OUTDIR, // $OUTDIR INST_EXEDIR, // $EXEDIR INST_LANG, // $LANGUAGE __INST_LAST ); TVariableList = INST_0..__INST_LAST; type PluginCallbackMessages = ( NSPIM_UNLOAD, // This is the last message a plugin gets, do final cleanup NSPIM_GUIUNLOAD // Called after .onGUIEnd ); TNSPIM = NSPIM_UNLOAD..NSPIM_GUIUNLOAD; //TPluginCallback = function (const NSPIM: Integer): Pointer; cdecl; TExecuteCodeSegment = function (const funct_id: Integer; const parent: HWND): Integer; stdcall; Tvalidate_filename = procedure (const filename: PChar); stdcall; TRegisterPluginCallback = function (const DllInstance: HMODULE; const CallbackFunction: Pointer): Integer; stdcall; pexec_flags_t = ^exec_flags_t; exec_flags_t = record autoclose: Integer; all_user_var: Integer; exec_error: Integer; abort: Integer; exec_reboot: Integer; reboot_called: Integer; XXX_cur_insttype: Integer; plugin_api_version: Integer; silent: Integer; instdir_error: Integer; rtl: Integer; errlvl: Integer; alter_reg_view: Integer; status_update: Integer; end; pextrap_t = ^extrap_t; extrap_t = record exec_flags: Pointer; // exec_flags_t; exec_code_segment: TExecuteCodeSegment; // TFarProc; validate_filename: Pointer; // Tvalidate_filename; RegisterPluginCallback: Pointer; //TRegisterPluginCallback; end; pstack_t = ^stack_t; stack_t = record next: pstack_t; text: PChar; end; var g_stringsize: integer; g_stacktop: ^pstack_t; g_variables: PChar; g_hwndParent: HWND; g_hwndList: HWND; g_hwndLogList: HWND; g_extraparameters: pextrap_t; procedure Init(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer; const extraparameters: pointer = nil); function LogMessage(Msg : String): BOOL; function Call(NSIS_func : String) : Integer; function PopString(): string; procedure PushString(const str: string=''); function GetUserVariable(const varnum: TVariableList): string; procedure SetUserVariable(const varnum: TVariableList; const value: string); procedure NSISDialog(const text, caption: string; const buttons: integer); implementation procedure Init(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer; const extraparameters: pointer = nil); begin g_stringsize := string_size; g_hwndParent := hwndParent; g_stacktop := stacktop; g_variables := variables; g_hwndList := FindWindowEx(FindWindowEx(g_hwndParent, 0, '#32770', nil), 0,'SysListView32', nil); g_extraparameters := extraparameters; end; function Call(NSIS_func : String) : Integer; var codeoffset: Integer; //The ID of nsis function begin Result := 0; codeoffset := StrToIntDef(NSIS_func, 0); if (codeoffset <> 0) and (g_extraparameters <> nil) then begin codeoffset := codeoffset - 1; Result := g_extraparameters.exec_code_segment(codeoffset, g_hwndParent); end; end; function LogMessage(Msg : String): BOOL; var ItemCount : Integer; item: TLVItem; begin Result := FAlse; if g_hwndList = 0 then exit; FillChar( item, sizeof(item), 0 ); ItemCount := SendMessage(g_hwndList, LVM_GETITEMCOUNT, 0, 0); item.iItem := ItemCount; item.mask := LVIF_TEXT; item.pszText := PChar(Msg); ListView_InsertItem(g_hwndList, item); ListView_EnsureVisible(g_hwndList, ItemCount, TRUE); end; function PopString(): string; var th: pstack_t; begin if integer(g_stacktop^) <> 0 then begin th := g_stacktop^; Result := PChar(@th.text); g_stacktop^ := th.next; GlobalFree(HGLOBAL(th)); end; end; procedure PushString(const str: string=''); var th: pstack_t; begin if integer(g_stacktop) <> 0 then begin th := pstack_t(GlobalAlloc(GPTR, SizeOf(stack_t) + g_stringsize)); lstrcpyn(@th.text, PChar(str), g_stringsize); th.next := g_stacktop^; g_stacktop^ := th; end; end; function GetUserVariable(const varnum: TVariableList): string; begin if (integer(varnum) >= 0) and (integer(varnum) < integer(__INST_LAST)) then Result := g_variables + integer(varnum) * g_stringsize else Result := ''; end; procedure SetUserVariable(const varnum: TVariableList; const value: string); begin if (value <> '') and (integer(varnum) >= 0) and (integer(varnum) < integer(__INST_LAST)) then lstrcpy(g_variables + integer(varnum) * g_stringsize, PChar(value)) end; procedure NSISDialog(const text, caption: string; const buttons: integer); var hwndOwner: HWND; begin hwndOwner := g_hwndParent; if not IsWindow(g_hwndParent) then hwndOwner := 0; // g_hwndParent is not valid in NSPIM_[GUI]UNLOAD MessageBox(hwndOwner, PChar(text), PChar(caption), buttons); end; begin end. ================================================ FILE: desktop/tauri/src-tauri/templates/NSIS_Simple_Service_Plugin_Unicode_1.30/Source/ServiceControl.pas ================================================ { License Agreement This content is subject to the Mozilla Public License Version 1.1 (the "License"); You may not use this plugin except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL. Alternatively, you may redistribute this library, use and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. You may obtain a copy of the LGPL at www.gnu.org/copyleft. Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The original code is ServiceControl.pas, released April 16, 2007. The initial developer of the original code is Rainer Dpke (Formerly: Rainer Budde) (https://www.speed-soft.de). SimpleSC - NSIS Service Control Plugin is written, published and maintained by Rainer Dpke (rainer@speed-soft.de). } unit ServiceControl; interface uses Winapi.Windows, Winapi.WinSvc, System.SysUtils, System.DateUtils; function InstallService(ServiceName, DisplayName: String; ServiceType: DWORD; StartType: DWORD; BinaryPathName: String; Dependencies: String; Username: String; Password: String): Integer; function RemoveService(ServiceName: String): Integer; function GetServiceName(DisplayName: String; var Name: String): Integer; function GetServiceDisplayName(ServiceName: String; var Name: String): Integer; function GetServiceStatus(ServiceName: String; var Status: DWORD): Integer; function GetServiceBinaryPath(ServiceName: String; var BinaryPath: String): Integer; function GetServiceStartType(ServiceName: String; var StartType: DWORD): Integer; function GetServiceDescription(ServiceName: String; var Description: String): Integer; function GetServiceLogon(ServiceName: String; var Username: String): Integer; function GetServiceFailure(ServiceName: String; var ResetPeriod: DWORD; var RebootMessage: String; var Command: String; var Action1: Integer; var ActionDelay1: DWORD; var Action2: Integer; var ActionDelay2: DWORD; var Action3: Integer; var ActionDelay3: DWORD): Integer; function GetServiceFailureFlag(ServiceName: String; var FailureActionsOnNonCrashFailures: Boolean): Integer; function GetServiceDelayedAutoStartInfo(ServiceName: String; var DelayedAutostart: Boolean): Integer; function SetServiceStartType(ServiceName: String; StartType: DWORD): Integer; function SetServiceDescription(ServiceName: String; Description: String): Integer; function SetServiceLogon(ServiceName: String; Username: String; Password: String): Integer; function SetServiceBinaryPath(ServiceName: String; BinaryPath: String): Integer; function SetServiceFailure(ServiceName: String; ResetPeriod: DWORD; RebootMessage: String; Command: String; Action1: Integer; ActionDelay1: DWORD; Action2: Integer; ActionDelay2: DWORD; Action3: Integer; ActionDelay3: DWORD): Integer; function SetServiceFailureFlag(ServiceName: String; FailureActionsOnNonCrashFailures: Boolean): Integer; function SetServiceDelayedAutoStartInfo(ServiceName: String; DelayedAutostart: Boolean): Integer; function ServiceIsRunning(ServiceName: String; var IsRunning: Boolean): Integer; function ServiceIsStopped(ServiceName: String; var IsStopped: Boolean): Integer; function ServiceIsPaused(ServiceName: String; var IsPaused: Boolean): Integer; function StartService(ServiceName: String; ServiceArguments: String; Timeout: Integer): Integer; function StopService(ServiceName: String; WaitForFileRelease: Boolean; Timeout: Integer): Integer; function PauseService(ServiceName: String; Timeout: Integer): Integer; function ContinueService(ServiceName: String; Timeout: Integer): Integer; function RestartService(ServiceName: String; ServiceArguments: String; Timeout: Integer): Integer; function ExistsService(ServiceName: String): Integer; function GetErrorMessage(ErrorCode: Integer): String; function WaitForFileRelease(ServiceName: String; Timeout: Integer): Integer; function WaitForStatus(ServiceName: String; Status: DWORD; Timeout: Integer): Integer; implementation function WaitForFileRelease(ServiceName: String; Timeout: Integer): Integer; function GetFilename(ServiceFileName: String): String; var FilePath: String; FileName: String; const ParameterDelimiter = ' '; begin FilePath := ExtractFilePath(ServiceFileName); FileName := Copy(ServiceFileName, Length(FilePath) + 1, Length(ServiceFileName) - Length(FilePath)); if Pos(ParameterDelimiter, Filename) <> 0 then FileName := Copy(FileName, 0, Pos(ParameterDelimiter, Filename) - Length(ParameterDelimiter)); Result := FilePath + FileName; end; var StatusReached: Boolean; TimeOutReached: Boolean; TimeoutDate: TDateTime; ServiceResult: Integer; ServiceFileName: String; FileName: String; FileHandle: Cardinal; const WAIT_TIMEOUT = 250; begin Result := 0; StatusReached := False; TimeOutReached := False; ServiceResult := GetServiceBinaryPath(ServiceName, ServiceFileName); if ServiceResult = 0 then begin Filename := GetFilename(ServiceFileName); if FileExists(FileName) then begin TimeoutDate := IncSecond(Now, Timeout); while not StatusReached and not TimeOutReached do begin FileHandle := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if FileHandle <> INVALID_HANDLE_VALUE then begin CloseHandle(FileHandle); StatusReached := True; end; if not StatusReached and (TimeoutDate < Now) then begin TimeOutReached := True; Result := WAIT_TIMEOUT; end; end; end; end else Result := ServiceResult; end; function WaitForStatus(ServiceName: String; Status: DWORD; Timeout: Integer): Integer; var CurrentStatus: DWORD; StatusResult: Integer; StatusReached: Boolean; TimeOutReached: Boolean; ErrorOccured: Boolean; TimeoutDate: TDateTime; const WAIT_TIMEOUT = 250; begin Result := 0; StatusReached := False; TimeOutReached := False; ErrorOccured := False; TimeoutDate := IncSecond(Now, Timeout); while not StatusReached and not ErrorOccured and not TimeOutReached do begin StatusResult := GetServiceStatus(ServiceName, CurrentStatus); if StatusResult = 0 then begin if Status = CurrentStatus then StatusReached := True else Sleep(WAIT_TIMEOUT); end else begin ErrorOccured := True; Result := StatusResult; end; if not StatusReached and not ErrorOccured and (TimeoutDate < Now) then begin TimeOutReached := True; Result := ERROR_SERVICE_REQUEST_TIMEOUT; end; end; end; function ExistsService(ServiceName: String): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; begin Result := 0; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_CONNECT); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_QUERY_CONFIG); if ServiceHandle > 0 then CloseServiceHandle(ServiceHandle) else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function StartService(ServiceName: String; ServiceArguments: String; Timeout: Integer): Integer; type TArguments = Array of PChar; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; ServiceArgVectors: TArguments; NumServiceArgs: DWORD; const ArgDelimitterQuote: String = '"'; ArgDelimitterWhiteSpace: String = ' '; procedure GetServiceArguments(ServiceArguments: String; var NumServiceArgs: DWORD; var ServiceArgVectors: TArguments); var Param: String; Split: Boolean; Quoted: Boolean; CharIsDelimitter: Boolean; begin ServiceArgVectors := nil; NumServiceArgs := 0; Quoted := False; while Length(ServiceArguments) > 0 do begin Split := False; CharIsDelimitter := False; if ServiceArguments[1] = ' ' then if not Quoted then begin CharIsDelimitter := True; Split := True; end; if ServiceArguments[1] = '"' then begin Quoted := not Quoted; CharIsDelimitter := True; if not Quoted then Split := True; end; if not CharIsDelimitter then Param := Param + ServiceArguments[1]; if Split or (Length(ServiceArguments) = 1) then begin SetLength(ServiceArgVectors, Length(ServiceArgVectors) + 1); GetMem(ServiceArgVectors[Length(ServiceArgVectors) -1], Length(Param) * SizeOf(Char) + 1); StrPCopy(ServiceArgVectors[Length(ServiceArgVectors) -1], Param); Param := ''; Delete(ServiceArguments, 1, 1); ServiceArguments := Trim(ServiceArguments); end else Delete(ServiceArguments, 1, 1); end; if Length(ServiceArgVectors) > 0 then NumServiceArgs := Length(ServiceArgVectors); end; procedure FreeServiceArguments(ServiceArgVectors: TArguments); var i: Integer; begin if Length(ServiceArgVectors) > 0 then for i := 0 to Length(ServiceArgVectors) -1 do FreeMem(ServiceArgVectors[i]); end; begin ManagerHandle := OpenSCManager('', nil, SC_MANAGER_CONNECT); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_START); if ServiceHandle > 0 then begin GetServiceArguments(ServiceArguments, NumServiceArgs, ServiceArgVectors); if Winapi.WinSvc.StartService(ServiceHandle, NumServiceArgs, ServiceArgVectors[0]) then Result := WaitForStatus(ServiceName, SERVICE_RUNNING, Timeout) else Result := System.GetLastError; FreeServiceArguments(ServiceArgVectors); CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function StopService(ServiceName: String; WaitForFileRelease: Boolean; Timeout: Integer): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; ServiceStatus: TServiceStatus; Dependencies: PEnumServiceStatus; BytesNeeded: Cardinal; ServicesReturned: Cardinal; ServicesEnumerated: Boolean; EnumerationSuccess: Boolean; i: Cardinal; begin Result := 0; BytesNeeded := 0; ServicesReturned := 0; Dependencies := nil; ServicesEnumerated := False; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_CONNECT or SC_MANAGER_ENUMERATE_SERVICE); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_STOP or SERVICE_ENUMERATE_DEPENDENTS); if ServiceHandle > 0 then begin if not EnumDependentServices(ServiceHandle, SERVICE_ACTIVE, Dependencies^, 0, BytesNeeded, ServicesReturned) then begin ServicesEnumerated := True; GetMem(Dependencies, BytesNeeded); EnumerationSuccess := EnumDependentServices(ServiceHandle, SERVICE_ACTIVE, Dependencies^, BytesNeeded, BytesNeeded, ServicesReturned); if EnumerationSuccess and (ServicesReturned > 0) then begin for i := 1 to ServicesReturned do begin Result := StopService(Dependencies.lpServiceName, False, Timeout); if Result <> 0 then Break; Inc(Dependencies); end; end else Result := System.GetLastError; end; if (ServicesEnumerated and (Result = 0)) or not ServicesEnumerated then begin if ControlService(ServiceHandle, SERVICE_CONTROL_STOP, ServiceStatus) then Result := WaitForStatus(ServiceName, SERVICE_STOPPED, Timeout) else Result := System.GetLastError end; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; if (Result = 0) and WaitForFileRelease then Result := ServiceControl.WaitForFileRelease(ServiceName, Timeout); end; function PauseService(ServiceName: String; Timeout: Integer): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; ServiceStatus: TServiceStatus; begin ManagerHandle := OpenSCManager('', nil, SC_MANAGER_CONNECT); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_PAUSE_CONTINUE); if ServiceHandle > 0 then begin if ControlService(ServiceHandle, SERVICE_CONTROL_PAUSE, ServiceStatus) then Result := WaitForStatus(ServiceName, SERVICE_PAUSED, Timeout) else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function ContinueService(ServiceName: String; Timeout: Integer): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; ServiceStatus: TServiceStatus; begin ManagerHandle := OpenSCManager('', nil, SC_MANAGER_CONNECT); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_PAUSE_CONTINUE); if ServiceHandle > 0 then begin if ControlService(ServiceHandle, SERVICE_CONTROL_CONTINUE, ServiceStatus) then Result := WaitForStatus(ServiceName, SERVICE_RUNNING, Timeout) else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function GetServiceName(DisplayName: String; var Name: String): Integer; var ManagerHandle: SC_HANDLE; ServiceName: PChar; ServiceBuffer: Cardinal; begin Result := 0; ServiceBuffer := 255; ServiceName := StrAlloc(ServiceBuffer+1); ManagerHandle := OpenSCManager('', nil, SC_MANAGER_CONNECT); if ManagerHandle > 0 then begin if Winapi.WinSvc.GetServiceKeyName(ManagerHandle, PChar(DisplayName), ServiceName, ServiceBuffer) then Name := ServiceName else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function GetServiceDisplayName(ServiceName: String; var Name: String): Integer; var ManagerHandle: SC_HANDLE; DisplayName: PChar; ServiceBuffer: Cardinal; begin Result := 0; ServiceBuffer := 255; DisplayName := StrAlloc(ServiceBuffer+1); ManagerHandle := OpenSCManager('', nil, SC_MANAGER_CONNECT); if ManagerHandle > 0 then begin if Winapi.WinSvc.GetServiceDisplayName(ManagerHandle, PChar(ServiceName), DisplayName, ServiceBuffer) then Name := DisplayName else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function GetServiceStatus(ServiceName: String; var Status: DWORD): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; ServiceStatus: TServiceStatus; begin Result := 0; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_CONNECT); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_QUERY_STATUS); if ServiceHandle > 0 then begin if QueryServiceStatus(ServiceHandle, ServiceStatus) then Status := ServiceStatus.dwCurrentState else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function GetServiceBinaryPath(ServiceName: String; var BinaryPath: String): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; BytesNeeded: DWORD; ServiceConfig: LPQUERY_SERVICE_CONFIG; begin Result := 0; ServiceConfig := nil; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_CONNECT); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_QUERY_CONFIG); if ServiceHandle > 0 then begin if not QueryServiceConfig(ServiceHandle, ServiceConfig, 0, BytesNeeded) and (System.GetLastError = ERROR_INSUFFICIENT_BUFFER) then begin GetMem(ServiceConfig, BytesNeeded); if QueryServiceConfig(ServiceHandle, ServiceConfig, BytesNeeded, BytesNeeded) then BinaryPath := ServiceConfig^.lpBinaryPathName else Result := System.GetLastError; FreeMem(ServiceConfig); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function GetServiceStartType(ServiceName: String; var StartType: DWORD): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; BytesNeeded: DWORD; ServiceConfig: LPQUERY_SERVICE_CONFIG; begin Result := 0; ServiceConfig := nil; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_CONNECT); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_QUERY_CONFIG); if ServiceHandle > 0 then begin if not QueryServiceConfig(ServiceHandle, ServiceConfig, 0, BytesNeeded) and (System.GetLastError = ERROR_INSUFFICIENT_BUFFER) then begin GetMem(ServiceConfig, BytesNeeded); if QueryServiceConfig(ServiceHandle, ServiceConfig, BytesNeeded, BytesNeeded) then StartType := ServiceConfig^.dwStartType else Result := System.GetLastError; FreeMem(ServiceConfig); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function GetServiceDescription(ServiceName: String; var Description: String): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; LockHandle: SC_LOCK; BytesNeeded: DWORD; ServiceDescription: LPSERVICE_DESCRIPTION; begin Result := 0; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_LOCK); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_QUERY_CONFIG); if ServiceHandle > 0 then begin LockHandle := LockServiceDatabase(ManagerHandle); if LockHandle <> nil then begin if not QueryServiceConfig2(ServiceHandle, SERVICE_CONFIG_DESCRIPTION, nil, 0, @BytesNeeded) and (System.GetLastError = ERROR_INSUFFICIENT_BUFFER) then begin GetMem(ServiceDescription, BytesNeeded); if QueryServiceConfig2(ServiceHandle, SERVICE_CONFIG_DESCRIPTION, PByte(ServiceDescription), BytesNeeded, @BytesNeeded) then Description := ServiceDescription.lpDescription else Result := System.GetLastError; FreeMem(ServiceDescription); end else Result := System.GetLastError; UnlockServiceDatabase(LockHandle); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function GetServiceLogon(ServiceName: String; var Username: String): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; BytesNeeded: DWORD; ServiceConfig: LPQUERY_SERVICE_CONFIG; begin Result := 0; ServiceConfig := nil; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_CONNECT); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_QUERY_CONFIG); if ServiceHandle > 0 then begin if not QueryServiceConfig(ServiceHandle, ServiceConfig, 0, BytesNeeded) and (System.GetLastError = ERROR_INSUFFICIENT_BUFFER) then begin GetMem(ServiceConfig, BytesNeeded); if QueryServiceConfig(ServiceHandle, ServiceConfig, BytesNeeded, BytesNeeded) then Username := ServiceConfig^.lpServiceStartName else Result := System.GetLastError; FreeMem(ServiceConfig); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function GetServiceFailure(ServiceName: String; var ResetPeriod: DWORD; var RebootMessage: String; var Command: String; var Action1: Integer; var ActionDelay1: DWORD; var Action2: Integer; var ActionDelay2: DWORD; var Action3: Integer; var ActionDelay3: DWORD): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; LockHandle: SC_LOCK; BytesNeeded: DWORD; ServiceFailureAction: LPSERVICE_FAILURE_ACTIONS; begin Result := 0; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_LOCK); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_QUERY_CONFIG); if ServiceHandle > 0 then begin LockHandle := LockServiceDatabase(ManagerHandle); if LockHandle <> nil then begin if not QueryServiceConfig2(ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, nil, 0, @BytesNeeded) and (System.GetLastError = ERROR_INSUFFICIENT_BUFFER) then begin GetMem(ServiceFailureAction, BytesNeeded); if QueryServiceConfig2(ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, PByte(ServiceFailureAction), BytesNeeded, @BytesNeeded) then begin ResetPeriod := ServiceFailureAction.dwResetPeriod; RebootMessage := ServiceFailureAction.lpRebootMsg; Command := ServiceFailureAction.lpCommand; if ServiceFailureAction.cActions >= 1 then begin Action1 := Integer(ServiceFailureAction.lpsaActions.&Type); ActionDelay1 := ServiceFailureAction.lpsaActions.Delay; end; if ServiceFailureAction.cActions >= 2 then begin Inc(ServiceFailureAction.lpsaActions); Action2 := Integer(ServiceFailureAction.lpsaActions.&Type); ActionDelay2 := ServiceFailureAction.lpsaActions.Delay; end; if ServiceFailureAction.cActions >= 3 then begin Inc(ServiceFailureAction.lpsaActions); Action3 := Integer(ServiceFailureAction.lpsaActions.&Type); ActionDelay3 := ServiceFailureAction.lpsaActions.Delay; end; end else Result := System.GetLastError; FreeMem(ServiceFailureAction); end else Result := System.GetLastError; UnlockServiceDatabase(LockHandle); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function GetServiceFailureFlag(ServiceName: String; var FailureActionsOnNonCrashFailures: Boolean): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; LockHandle: SC_LOCK; BytesNeeded: DWORD; ServiceFailureActionsFlag: LPSERVICE_FAILURE_ACTIONS_FLAG; begin Result := 0; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_LOCK); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_QUERY_CONFIG); if ServiceHandle > 0 then begin LockHandle := LockServiceDatabase(ManagerHandle); if LockHandle <> nil then begin if not QueryServiceConfig2(ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, nil, 0, @BytesNeeded) and (System.GetLastError = ERROR_INSUFFICIENT_BUFFER) then begin GetMem(ServiceFailureActionsFlag, BytesNeeded); if QueryServiceConfig2(ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, PByte(ServiceFailureActionsFlag), BytesNeeded, @BytesNeeded) then FailureActionsOnNonCrashFailures := ServiceFailureActionsFlag.fFailureActionsOnNonCrashFailures else Result := System.GetLastError; FreeMem(ServiceFailureActionsFlag); end else Result := System.GetLastError; UnlockServiceDatabase(LockHandle); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function GetServiceDelayedAutoStartInfo(ServiceName: String; var DelayedAutostart: Boolean): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; LockHandle: SC_LOCK; BytesNeeded: DWORD; ServiceDelayedAutoStartInfo: LPSERVICE_DELAYED_AUTO_START_INFO; begin Result := 0; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_LOCK); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_QUERY_CONFIG); if ServiceHandle > 0 then begin LockHandle := LockServiceDatabase(ManagerHandle); if LockHandle <> nil then begin if not QueryServiceConfig2(ServiceHandle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, nil, 0, @BytesNeeded) and (System.GetLastError = ERROR_INSUFFICIENT_BUFFER) then begin GetMem(ServiceDelayedAutoStartInfo, BytesNeeded); if QueryServiceConfig2(ServiceHandle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, PByte(ServiceDelayedAutoStartInfo), BytesNeeded, @BytesNeeded) then DelayedAutostart := Boolean(ServiceDelayedAutoStartInfo.fDelayedAutostart) else Result := System.GetLastError; FreeMem(ServiceDelayedAutoStartInfo); end else Result := System.GetLastError; UnlockServiceDatabase(LockHandle); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function SetServiceDescription(ServiceName: String; Description: String): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; LockHandle: SC_LOCK; begin Result := 0; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_LOCK); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_CHANGE_CONFIG); if ServiceHandle > 0 then begin LockHandle := LockServiceDatabase(ManagerHandle); if LockHandle <> nil then begin if not ChangeServiceConfig2(ServiceHandle, SERVICE_CONFIG_DESCRIPTION, @Description) then Result := System.GetLastError; UnlockServiceDatabase(LockHandle); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function SetServiceStartType(ServiceName: String; StartType: DWORD): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; LockHandle: SC_LOCK; begin Result := 0; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_LOCK); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_CHANGE_CONFIG); if ServiceHandle > 0 then begin LockHandle := LockServiceDatabase(ManagerHandle); if LockHandle <> nil then begin if not ChangeServiceConfig(ServiceHandle, SERVICE_NO_CHANGE, StartType, SERVICE_NO_CHANGE, nil, nil, nil, nil, nil, nil, nil) then Result := System.GetLastError; UnlockServiceDatabase(LockHandle); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function SetServiceLogon(ServiceName: String; Username: String; Password: String): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; LockHandle: SC_LOCK; begin Result := 0; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_LOCK); if Pos('\', Username) = 0 then Username := '.\' + Username; if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_CHANGE_CONFIG); if ServiceHandle > 0 then begin LockHandle := LockServiceDatabase(ManagerHandle); if LockHandle <> nil then begin if not ChangeServiceConfig(ServiceHandle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, nil, nil, nil, nil, PChar(Username), PChar(Password), nil) then Result := System.GetLastError; UnlockServiceDatabase(LockHandle); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function SetServiceBinaryPath(ServiceName: String; BinaryPath: String): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; LockHandle: SC_LOCK; begin Result := 0; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_LOCK); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_CHANGE_CONFIG); if ServiceHandle > 0 then begin LockHandle := LockServiceDatabase(ManagerHandle); if LockHandle <> nil then begin if not ChangeServiceConfig(ServiceHandle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, PChar(BinaryPath), nil, nil, nil, nil, nil, nil) then Result := System.GetLastError; UnlockServiceDatabase(LockHandle); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function SetServiceFailure(ServiceName: String; ResetPeriod: DWORD; RebootMessage: String; Command: String; Action1: Integer; ActionDelay1: DWORD; Action2: Integer; ActionDelay2: DWORD; Action3: Integer; ActionDelay3: DWORD): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; LockHandle: SC_LOCK; ServiceFailureAction: SERVICE_FAILURE_ACTIONS; ServiceActions: array[0..2] of SC_ACTION; ServiceAccessType: Integer; begin Result := 0; if (SC_ACTION_TYPE(Action1) = SC_ACTION_RESTART) or (SC_ACTION_TYPE(Action2) = SC_ACTION_RESTART) or (SC_ACTION_TYPE(Action3) = SC_ACTION_RESTART) then ServiceAccessType := SERVICE_CHANGE_CONFIG or SERVICE_START else ServiceAccessType := SERVICE_ALL_ACCESS; ServiceActions[0].&Type := SC_ACTION_TYPE(Action1); ServiceActions[0].Delay := ActionDelay1; ServiceActions[1].&Type := SC_ACTION_TYPE(Action2); ServiceActions[1].Delay := ActionDelay2; ServiceActions[2].&Type := SC_ACTION_TYPE(Action3); ServiceActions[2].Delay := ActionDelay3; ServiceFailureAction.dwResetPeriod := ResetPeriod; ServiceFailureAction.lpRebootMsg := PChar(RebootMessage); ServiceFailureAction.lpCommand := PChar(Command); ServiceFailureAction.cActions := Length(ServiceActions); ServiceFailureAction.lpsaActions := @ServiceActions; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_LOCK); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), ServiceAccessType); if ServiceHandle > 0 then begin LockHandle := LockServiceDatabase(ManagerHandle); if LockHandle <> nil then begin if not ChangeServiceConfig2W(ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, @ServiceFailureAction) then Result := System.GetLastError; UnlockServiceDatabase(LockHandle); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function SetServiceFailureFlag(ServiceName: String; FailureActionsOnNonCrashFailures: Boolean): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; LockHandle: SC_LOCK; ServiceFailureActionsFlag: SERVICE_FAILURE_ACTIONS_FLAG; begin Result := 0; DWORD(ServiceFailureActionsFlag.fFailureActionsOnNonCrashFailures) := DWORD(FailureActionsOnNonCrashFailures); ManagerHandle := OpenSCManager('', nil, SC_MANAGER_LOCK); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_CHANGE_CONFIG); if ServiceHandle > 0 then begin LockHandle := LockServiceDatabase(ManagerHandle); if LockHandle <> nil then begin if not ChangeServiceConfig2(ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, @ServiceFailureActionsFlag) then Result := System.GetLastError; UnlockServiceDatabase(LockHandle); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function SetServiceDelayedAutoStartInfo(ServiceName: String; DelayedAutostart: Boolean): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; LockHandle: SC_LOCK; ServiceDelayedAutoStartInfo: SERVICE_DELAYED_AUTO_START_INFO; begin Result := 0; DWORD(ServiceDelayedAutoStartInfo.fDelayedAutostart) := DWORD(DelayedAutostart); ManagerHandle := OpenSCManager('', nil, SC_MANAGER_LOCK); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_CHANGE_CONFIG); if ServiceHandle > 0 then begin LockHandle := LockServiceDatabase(ManagerHandle); if LockHandle <> nil then begin if not ChangeServiceConfig2(ServiceHandle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, @ServiceDelayedAutoStartInfo) then Result := System.GetLastError; UnlockServiceDatabase(LockHandle); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function ServiceIsRunning(ServiceName: String; var IsRunning: Boolean): Integer; var Status: DWORD; begin Result := GetServiceStatus(ServiceName, Status); if Result = 0 then IsRunning := Status = SERVICE_RUNNING else IsRunning := False; end; function ServiceIsStopped(ServiceName: String; var IsStopped: Boolean): Integer; var Status: DWORD; begin Result := GetServiceStatus(ServiceName, Status); if Result = 0 then IsStopped := Status = SERVICE_STOPPED else IsStopped := False; end; function ServiceIsPaused(ServiceName: String; var IsPaused: Boolean): Integer; var Status: DWORD; begin Result := GetServiceStatus(ServiceName, Status); if Result = 0 then IsPaused := Status = SERVICE_PAUSED else IsPaused := False; end; function RestartService(ServiceName: String; ServiceArguments: String; Timeout: Integer): Integer; begin Result := StopService(ServiceName, False, Timeout); if Result = 0 then Result := StartService(ServiceName, ServiceArguments, Timeout); end; function InstallService(ServiceName, DisplayName: String; ServiceType: DWORD; StartType: DWORD; BinaryPathName: String; Dependencies: String; Username: String; Password: String): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; PDependencies: PChar; PUsername: PChar; PPassword: PChar; const ReplaceDelimitter: String = '/'; function Replace(Value: String): String; begin while Pos(ReplaceDelimitter, Value) <> 0 do begin Result := Result + Copy(Value, 1, Pos(ReplaceDelimitter, Value) -1) + Chr(0); Delete(Value, 1, Pos(ReplaceDelimitter, Value)); end; Result := Result + Value + Chr(0) + Chr(0); end; begin Result := 0; if Dependencies = '' then PDependencies := nil else PDependencies := PChar(Replace(Dependencies)); if UserName = '' then PUsername := nil else begin if Pos('\', Username) = 0 then Username := '.\' + Username; PUsername := PChar(Username); end; if Password = '' then PPassword := nil else PPassword := PChar(Password); ManagerHandle := OpenSCManager('', nil, SC_MANAGER_ALL_ACCESS); if ManagerHandle > 0 then begin ServiceHandle := CreateService(ManagerHandle, PChar(ServiceName), PChar(DisplayName), SERVICE_START or SERVICE_QUERY_STATUS or _DELETE, ServiceType, StartType, SERVICE_ERROR_NORMAL, PChar(BinaryPathName), nil, nil, PDependencies, PUsername, PPassword); if ServiceHandle <> 0 then CloseServiceHandle(ServiceHandle) else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function RemoveService(ServiceName: String): Integer; var ManagerHandle: SC_HANDLE; ServiceHandle: SC_HANDLE; LockHandle: SC_LOCK; Deleted: Boolean; begin Result := 0; ManagerHandle := OpenSCManager('', nil, SC_MANAGER_ALL_ACCESS); if ManagerHandle > 0 then begin ServiceHandle := OpenService(ManagerHandle, PChar(ServiceName), SERVICE_ALL_ACCESS); if ServiceHandle > 0 then begin LockHandle := LockServiceDatabase(ManagerHandle); if LockHandle <> nil then begin Deleted := DeleteService(ServiceHandle); if not Deleted then Result := System.GetLastError; UnlockServiceDatabase(LockHandle); end else Result := System.GetLastError; CloseServiceHandle(ServiceHandle); end else Result := System.GetLastError; CloseServiceHandle(ManagerHandle); end else Result := System.GetLastError; end; function GetErrorMessage(ErrorCode: Integer): String; begin Result := SysErrorMessage(ErrorCode); end; end. ================================================ FILE: desktop/tauri/src-tauri/templates/NSIS_Simple_Service_Plugin_Unicode_1.30/Source/SimpleSC.dpr ================================================ library SimpleSC; uses Winapi.Windows, System.SysUtils, NSIS in 'NSIS.pas', ServiceControl in 'ServiceControl.pas', LSASecurityControl in 'LSASecurityControl.pas'; function BoolToStr(Value: Boolean): String; begin if Value then Result := '1' else Result := '0'; end; function StrToBool(Value: String): Boolean; begin Result := Value = '1'; end; procedure InstallService(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; DisplayName: String; ServiceType: Cardinal; StartType: Cardinal; BinaryPath: String; Dependencies: String; Username: String; Password: String; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; DisplayName := PopString; ServiceType := StrToInt(PopString); StartType := StrToInt(PopString); BinaryPath := PopString; Dependencies := PopString; Username := PopString; Password := PopString; ServiceResult := IntToStr(ServiceControl.InstallService(ServiceName, DisplayName, ServiceType, StartType, BinaryPath, Dependencies, Username, Password)); PushString(ServiceResult); end; procedure RemoveService(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.RemoveService(ServiceName)); PushString(ServiceResult); end; procedure StartService(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; ServiceArguments: String; Timeout: Integer; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceArguments := PopString; Timeout := StrToInt(PopString); ServiceResult := IntToStr(ServiceControl.StartService(ServiceName, ServiceArguments, Timeout)); PushString(ServiceResult); end; procedure StopService(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; WaitForFileRelease: Boolean; Timeout: Integer; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; WaitForFileRelease := StrToBool(PopString); Timeout := StrToInt(PopString); ServiceResult := IntToStr(ServiceControl.StopService(ServiceName, WaitForFileRelease, Timeout)); PushString(ServiceResult); end; procedure PauseService(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; Timeout: Integer; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; Timeout := StrToInt(PopString); ServiceResult := IntToStr(ServiceControl.PauseService(ServiceName, Timeout)); PushString(ServiceResult) end; procedure ContinueService(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; Timeout: Integer; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; Timeout := StrToInt(PopString); ServiceResult := IntToStr(ServiceControl.ContinueService(ServiceName, Timeout)); PushString(ServiceResult) end; procedure GetServiceName(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; Var DisplayName: String; ServiceResult: String; ServiceName: String; begin Init(hwndParent, string_size, variables, stacktop); DisplayName := PopString; ServiceResult := IntToStr(ServiceControl.GetServiceName(DisplayName, ServiceName)); PushString(ServiceName); PushString(ServiceResult); end; procedure GetServiceDisplayName(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; Var ServiceName: String; DisplayName: String; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.GetServiceDisplayName(ServiceName, DisplayName)); PushString(DisplayName); PushString(ServiceResult); end; procedure GetServiceStatus(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; Status: DWORD; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.GetServiceStatus(ServiceName, Status)); PushString(IntToStr(Status)); PushString(ServiceResult); end; procedure GetServiceBinaryPath(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; BinaryPath: String; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.GetServiceBinaryPath(ServiceName, BinaryPath)); PushString(BinaryPath); PushString(ServiceResult); end; procedure GetServiceDescription(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; Description: String; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.GetServiceDescription(ServiceName, Description)); PushString(Description); PushString(ServiceResult); end; procedure GetServiceStartType(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; StartType: DWORD; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.GetServiceStartType(ServiceName, StartType)); PushString(IntToStr(StartType)); PushString(ServiceResult); end; procedure GetServiceLogon(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; Username: String; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.GetServiceLogon(ServiceName, Username)); PushString(Username); PushString(ServiceResult); end; procedure GetServiceFailure(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; ResetPeriod: DWORD; RebootMessage: String; Command: String; Action1: Integer; ActionDelay1: DWORD; Action2: Integer; ActionDelay2: DWORD; Action3: Integer; ActionDelay3: DWORD; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.GetServiceFailure(ServiceName, ResetPeriod, RebootMessage, Command, Action1, ActionDelay1, Action2, ActionDelay2, Action3, ActionDelay3)); PushString(IntToStr(ActionDelay3)); PushString(IntToStr(Action3)); PushString(IntToStr(ActionDelay2)); PushString(IntToStr(Action2)); PushString(IntToStr(ActionDelay1)); PushString(IntToStr(Action1)); PushString(Command); PushString(RebootMessage); PushString(IntToStr(ResetPeriod)); PushString(ServiceResult); end; procedure GetServiceFailureFlag(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; FailureActionsOnNonCrashFailures: Boolean; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.GetServiceFailureFlag(ServiceName, FailureActionsOnNonCrashFailures)); PushString(BoolToStr(FailureActionsOnNonCrashFailures)); PushString(ServiceResult); end; procedure GetServiceDelayedAutoStartInfo(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; DelayedAutostart: Boolean; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.GetServiceDelayedAutoStartInfo(ServiceName, DelayedAutostart)); PushString(BoolToStr(DelayedAutostart)); PushString(ServiceResult); end; procedure SetServiceDescription(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; Description: String; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; Description := PopString; ServiceResult := IntToStr(ServiceControl.SetServiceDescription(ServiceName, Description)); PushString(ServiceResult); end; procedure SetServiceStartType(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; ServiceStartType: DWORD; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceStartType := StrToInt(PopString); ServiceResult := IntToStr(ServiceControl.SetServiceStartType(ServiceName, ServiceStartType)); PushString(ServiceResult); end; procedure SetServiceLogon(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; Username: String; Password: String; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; Username := PopString; Password := PopString; ServiceResult := IntToStr(ServiceControl.SetServiceLogon(ServiceName, Username, Password)); PushString(ServiceResult); end; procedure SetServiceBinaryPath(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; BinaryPath: String; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; BinaryPath := PopString; ServiceResult := IntToStr(ServiceControl.SetServiceBinaryPath(ServiceName, BinaryPath)); PushString(ServiceResult); end; procedure SetServiceFailure(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; ResetPeriod: DWORD; RebootMessage: String; Command: String; Action1: Integer; ActionDelay1: DWORD; Action2: Integer; ActionDelay2: DWORD; Action3: Integer; ActionDelay3: DWORD; ServiceResult: Integer; PrivilegeResult: Integer; const SE_SHUTDOWN_PRIVILEGE = 'SeShutdownPrivilege'; SC_ACTION_REBOOT = 2; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ResetPeriod := StrToInt(PopString); RebootMessage := PopString; Command := PopString; Action1 := StrToInt(PopString); ActionDelay1 := StrToInt(PopString); Action2 := StrToInt(PopString); ActionDelay2 := StrToInt(PopString); Action3 := StrToInt(PopString); ActionDelay3 := StrToInt(PopString); if (Action1 = SC_ACTION_REBOOT) or (Action2 = SC_ACTION_REBOOT) or (Action3 = SC_ACTION_REBOOT) then begin PrivilegeResult := LSASecurityControl.EnablePrivilege(SE_SHUTDOWN_PRIVILEGE); if not PrivilegeResult = 0 then begin PushString(IntToStr(PrivilegeResult)); Exit; end; end; ServiceResult := ServiceControl.SetServiceFailure(ServiceName, ResetPeriod, RebootMessage, Command, Action1, ActionDelay1, Action2, ActionDelay2, Action3, ActionDelay3); if (Action1 = SC_ACTION_REBOOT) or (Action2 = SC_ACTION_REBOOT) or (Action3 = SC_ACTION_REBOOT) then begin PrivilegeResult := LSASecurityControl.DisablePrivilege(SE_SHUTDOWN_PRIVILEGE); if not PrivilegeResult = 0 then begin PushString(IntToStr(PrivilegeResult)); Exit; end; end; PushString(IntToStr(ServiceResult)); end; procedure SetServiceFailureFlag(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; FailureActionsOnNonCrashFailures: Boolean; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; FailureActionsOnNonCrashFailures := StrToBool(PopString); ServiceResult := IntToStr(ServiceControl.SetServiceFailureFlag(ServiceName, FailureActionsOnNonCrashFailures)); PushString(ServiceResult) end; procedure SetServiceDelayedAutoStartInfo(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; DelayedAutostart: Boolean; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; DelayedAutostart := StrToBool(PopString); ServiceResult := IntToStr(ServiceControl.SetServiceDelayedAutoStartInfo(ServiceName, DelayedAutostart)); PushString(ServiceResult) end; procedure ServiceIsRunning(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; IsRunning: Boolean; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.ServiceIsRunning(ServiceName, IsRunning)); PushString(BoolToStr(IsRunning)); PushString(ServiceResult); end; procedure ServiceIsStopped(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; IsStopped: Boolean; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.ServiceIsStopped(ServiceName, IsStopped)); PushString(BoolToStr(IsStopped)); PushString(ServiceResult); end; procedure ServiceIsPaused(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; IsPaused: Boolean; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.ServiceIsPaused(ServiceName, IsPaused)); PushString(BoolToStr(IsPaused)); PushString(ServiceResult); end; procedure RestartService(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; ServiceArguments: String; Timeout: Integer; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceArguments := PopString; Timeout := StrToInt(PopString); ServiceResult := IntToStr(ServiceControl.RestartService(ServiceName, ServiceArguments, Timeout)); PushString(ServiceResult); end; procedure ExistsService(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ServiceName: String; ServiceResult: String; begin Init(hwndParent, string_size, variables, stacktop); ServiceName := PopString; ServiceResult := IntToStr(ServiceControl.ExistsService(ServiceName)); PushString(ServiceResult); end; procedure GrantServiceLogonPrivilege(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var AccountName: String; LSAResult: String; const SE_SERVICE_LOGON_RIGHT = 'SeServiceLogonRight'; begin Init(hwndParent, string_size, variables, stacktop); AccountName := PopString; LSAResult := IntToStr(LSASecurityControl.GrantPrivilege(AccountName, SE_SERVICE_LOGON_RIGHT)); PushString(LSAResult); end; procedure RemoveServiceLogonPrivilege(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var AccountName: String; LSAResult: String; const SE_SERVICE_LOGON_RIGHT = 'SeServiceLogonRight'; begin Init(hwndParent, string_size, variables, stacktop); AccountName := PopString; LSAResult := IntToStr(LSASecurityControl.RemovePrivilege(AccountName, SE_SERVICE_LOGON_RIGHT)); PushString(LSAResult); end; procedure GetErrorMessage(const hwndParent: HWND; const string_size: integer; const variables: PChar; const stacktop: pointer); cdecl; var ErrorCode: Integer; ErrorMessage: String; begin Init(hwndParent, string_size, variables, stacktop); ErrorCode := StrToInt(PopString); ErrorMessage := ServiceControl.GetErrorMessage(ErrorCode); PushString(ErrorMessage); end; exports InstallService; exports ExistsService; exports RemoveService; exports StartService; exports StopService; exports PauseService; exports ContinueService; exports GetServiceName; exports GetServiceDisplayName; exports GetServiceStatus; exports GetServiceBinaryPath; exports GetServiceDescription; exports GetServiceStartType; exports GetServiceLogon; exports GetServiceFailure; exports GetServiceFailureFlag; exports GetServiceDelayedAutoStartInfo; exports SetServiceDescription; exports SetServiceStartType; exports SetServiceLogon; exports SetServiceBinaryPath; exports SetServiceFailure; exports SetServiceFailureFlag; exports SetServiceDelayedAutoStartInfo; exports ServiceIsRunning; exports ServiceIsStopped; exports ServiceIsPaused; exports RestartService; exports GrantServiceLogonPrivilege; exports RemoveServiceLogonPrivilege; exports GetErrorMessage; end. ================================================ FILE: desktop/tauri/src-tauri/templates/NSIS_Simple_Service_Plugin_Unicode_1.30/Source/SimpleSC.dproj ================================================  {9A1C1FE1-FB44-40C4-9E22-99CAE6325532} 18.8 None SimpleSC.dpr True Release Win32 1 Library true true Base true true Base true true Base true true Cfg_2 true true bindcompfmx;fmx;rtl;dbrtl;IndySystem;DbxClientDriver;bindcomp;inetdb;DBXInterBaseDriver;DataSnapCommon;DataSnapClient;DataSnapServer;DataSnapProviderClient;xmlrtl;DbxCommonDriver;IndyProtocols;DBXMySQLDriver;dbxcds;bindengine;soaprtl;DBXOracleDriver;dsnap;DBXInformixDriver;IndyCore;fmxase;DBXFirebirdDriver;inet;fmxobj;inetdbxpress;DBXSybaseASADriver;fmxdae;dbexpress;DataSnapIndy10ServerTransport;IPIndyImpl;$(DCC_UsePackage) System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) true .\$(Platform)\$(Config) .\$(Platform)\$(Config) false false false false false SimpleSC None C:\Developing\NSIS Simple Service Control - Unicode\ C:\Developing\NSIS Simple Service Control - Unicode\ Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) frx16;TeeDB;Rave100VCL;vclib;Tee;inetdbbde;DBXOdbcDriver;svnui;ibxpress;DBXSybaseASEDriver;vclimg;frxDB16;intrawebdb_120_160;fmi;fs16;TeeUI;vclactnband;FMXTee;vcldb;vcldsnap;bindcompvcl;vclie;vcltouch;Intraweb_120_160;DBXDb2Driver;websnap;vclribbon;frxe16;fsDB16;vcl;DataSnapConnectors;CloudService;DBXMSSQLDriver;FmxTeeUI;dsnapcon;vclx;webdsnap;svn;bdertl;CodeSiteExpressPkg;adortl;vcldbx;VclSmp;$(DCC_UsePackage) 1033 CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) DBXOdbcDriver;DBXSybaseASEDriver;vclimg;vclactnband;vcldb;vcldsnap;bindcompvcl;vclie;vcltouch;DBXDb2Driver;websnap;vcl;DBXMSSQLDriver;dsnapcon;vclx;webdsnap;VclSmp;$(DCC_UsePackage) None CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= false 1031 false 0 0 C:\Developing\NSIS Simple Service Control - Unicode\ C:\Developing\NSIS Simple Service Control - Unicode\ 1033 (Ohne) MainSource Cfg_2 Base Base Delphi.Personality.12 False False 1 0 0 0 False False False False False 1031 1252 1.0.0.0 1.0.0.0 SimpleSC.dpr Microsoft Office 2000 Beispiele für gekapselte Komponenten für Automatisierungsserver Microsoft Office XP Beispiele für gekapselte Komponenten für Automation Server true true true 1 0 classes 1 classes 1 res\xml 1 res\xml 1 library\lib\armeabi-v7a 1 library\lib\armeabi 1 library\lib\armeabi 1 library\lib\armeabi-v7a 1 library\lib\mips 1 library\lib\mips 1 library\lib\armeabi-v7a 1 library\lib\arm64-v8a 1 library\lib\armeabi-v7a 1 res\drawable 1 res\drawable 1 res\values 1 res\values 1 res\values-v21 1 res\values-v21 1 res\values 1 res\values 1 res\drawable 1 res\drawable 1 res\drawable-xxhdpi 1 res\drawable-xxhdpi 1 res\drawable-ldpi 1 res\drawable-ldpi 1 res\drawable-mdpi 1 res\drawable-mdpi 1 res\drawable-hdpi 1 res\drawable-hdpi 1 res\drawable-xhdpi 1 res\drawable-xhdpi 1 res\drawable-mdpi 1 res\drawable-mdpi 1 res\drawable-hdpi 1 res\drawable-hdpi 1 res\drawable-xhdpi 1 res\drawable-xhdpi 1 res\drawable-xxhdpi 1 res\drawable-xxhdpi 1 res\drawable-xxxhdpi 1 res\drawable-xxxhdpi 1 res\drawable-small 1 res\drawable-small 1 res\drawable-normal 1 res\drawable-normal 1 res\drawable-large 1 res\drawable-large 1 res\drawable-xlarge 1 res\drawable-xlarge 1 res\values 1 res\values 1 1 1 0 1 .framework 1 .framework 0 1 .dylib 1 .dylib 0 .dll;.bpl 1 .dylib 1 .dylib 1 .dylib 1 .dylib 1 .dylib 0 .bpl 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 1 ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 1 1 1 1 Contents\Resources 1 Contents\Resources 1 library\lib\armeabi-v7a 1 library\lib\arm64-v8a 1 1 1 1 1 1 1 0 library\lib\armeabi-v7a 1 1 1 Assets 1 Assets 1 Assets 1 Assets 1 True False False 12 ================================================ FILE: desktop/tauri/src-tauri/templates/nsis/install_hooks.nsh ================================================ !include LogicLib.nsh !addplugindir "..\..\..\..\templates\NSIS_Simple_Service_Plugin_Unicode_1.30" var oldInstallationDir var dataDir !macro NSIS_HOOK_PREINSTALL ; Try to stop the service if it's running SimpleSC::ServiceIsStopped "PortmasterCore" Pop $0 Pop $1 ${If} $0 == 0 ${If} $1 == 0 DetailPrint "PortmasterCore service is running. Stopping service ..." SimpleSC::StopService "PortmasterCore" 1 60 Pop $0 ${If} $0 != 0 DetailPrint "Failed to stop PortmasterCore service. Error: $0" MessageBox MB_OK "PortmasterCore service is running. Stop it and run the installer again." Abort ${EndIf} ; wait a little (give change for service to fully stop) Sleep 2000 ${EndIf} ${EndIf} File "..\..\..\..\binary\portmaster-core.exe" File "..\..\..\..\binary\portmaster-kext.sys" File "..\..\..\..\binary\portmaster-core.dll" File "..\..\..\..\binary\WebView2Loader.dll" File "..\..\..\..\binary\portmaster.zip" File "..\..\..\..\binary\assets.zip" SetOutPath "$COMMONPROGRAMDATA\Portmaster\intel" File "..\..\..\..\intel\index.json" File "..\..\..\..\intel\base.dsdl" File "..\..\..\..\intel\geoipv4.mmdb" File "..\..\..\..\intel\geoipv6.mmdb" File "..\..\..\..\intel\index.dsd" File "..\..\..\..\intel\intermediate.dsdl" File "..\..\..\..\intel\urgent.dsdl" File "..\..\..\..\intel\main-intel.yaml" File "..\..\..\..\intel\notifications.yaml" File "..\..\..\..\intel\news.yaml" ; restire previous state SetOutPath "$INSTDIR" !macroend ;-------------------------------------------------- ; Post-install hook: ; - Remove old service ; - Installs the service !macro NSIS_HOOK_POSTINSTALL DetailPrint "Installing service" ; Remove old service SimpleSC::RemoveService "PortmasterCore" ; Install the service: ; Parameters: ; 1. Service Name: "PortmasterCore" ; 2. Display Name: "Portmaster Core" ; 3. Service Type: "16" for SERVICE_WIN32_OWN_PROCESS ; 4. Start Type: "2" for SERVICE_AUTO_START ; 5. Binary Path: Executable with arguments. ; 6 & 7. Dependencies and account info (empty uses defaults). SimpleSC::InstallService "PortmasterCore" "Portmaster Core" 16 2 "$INSTDIR\portmaster-core.exe --log-dir=%PROGRAMDATA%\Portmaster\logs" "" "" "" Pop $0 ; returns error code (0 on success) ${If} $0 != 0 SimpleSC::GetErrorMessage $0 Pop $0 MessageBox MB_OK "Service creation failed. Error: $0" Abort ${EndIf} SimpleSC::SetServiceDescription "PortmasterCore" "Portmaster Application Firewall - Core Service" ; ; Auto start the UI ; DetailPrint "Creating registry entry for autostart" WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "Portmaster" '"$INSTDIR\portmaster.exe" --with-prompts --with-notifications --background' ; ; MIGRATION FROM PMv1 TO PMv2 ; StrCpy $oldInstallationDir "$COMMONPROGRAMDATA\Safing\Portmaster" StrCpy $dataDir "$COMMONPROGRAMDATA\Portmaster" ; Check if the folder exists IfFileExists "$oldInstallationDir\*.*" 0 Finish ; Stop if the migration flag(file) already exists. IfFileExists "$oldInstallationDir\migrated.txt" Finish 0 ; Copy files DetailPrint "Migrating config from old installation: $oldInstallationDir" CreateDirectory "$dataDir" CreateDirectory "$dataDir\databases" CopyFiles "$oldInstallationDir\config.json" "$dataDir" CopyFiles "$oldInstallationDir\databases\*.*" "$dataDir\databases" ; Create empty file to indicate that the data has already been migrated. FileOpen $0 "$oldInstallationDir\migrated.txt" w FileClose $0 ; Delete v1 shortcuts RMDir /r "$SMPROGRAMS\Portmaster" Delete "$SMSTARTUP\Portmaster Notifier.lnk" ; Delete v1 old binaries Delete "$oldInstallationDir\portmaster-uninstaller.exe" Delete "$oldInstallationDir\portmaster-start.exe" Delete "$oldInstallationDir\portmaster.ico" RMDir /r "$oldInstallationDir\exec" RMDir /r "$oldInstallationDir\updates" RMDir /r "$oldInstallationDir\databases\cache" RMDir /r "$oldInstallationDir\intel" ; Delete the link to the ProgramData folder RMDir /r "$PROGRAMFILES64\Safing" ; Delete v1 user shortcut if its there. SetShellVarContext current Delete "$AppData\Microsoft\Windows\Start Menu\Programs\Portmaster.lnk" SetShellVarContext all ; Delete v1 registry values DeleteRegKey HKLM "SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Portmaster" Finish: !macroend ;-------------------------------------------------- ; Pre-uninstall hook: ; - Stops and removes the service. !macro NSIS_HOOK_PREUNINSTALL DetailPrint "Stopping service" ; Trigger service stop. In the worst case the service should stop in ~60 seconds. SimpleSC::StopService "PortmasterCore" 1 60 Pop $0 ${If} $0 != 0 DetailPrint "Failed to stop PortmasterCore service. Error: $0" ${Else} DetailPrint "Service PortmasterCore stopped successfully." ${EndIf} DetailPrint "Removing service" SimpleSC::RemoveService "PortmasterCore" Pop $0 ${If} $0 != 0 DetailPrint "Failed to remove PortmasterCore service. Error: $0" ${Else} DetailPrint "Service PortmasterCore removed successfully." ${EndIf} !macroend ;-------------------------------------------------- ; Post-uninstall hook: ; - Delete files !macro NSIS_HOOK_POSTUNINSTALL ; Delete binaries Delete /REBOOTOK "$INSTDIR\index.json" Delete /REBOOTOK "$INSTDIR\portmaster-core.exe" Delete /REBOOTOK "$INSTDIR\portmaster-kext.sys" Delete /REBOOTOK "$INSTDIR\portmaster-core.dll" Delete /REBOOTOK "$INSTDIR\WebView2Loader.dll" Delete /REBOOTOK "$INSTDIR\portmaster.zip" Delete /REBOOTOK "$INSTDIR\assets.zip" RMDir /r /REBOOTOK "$INSTDIR" ; remove the registry entry for the autostart DetailPrint "Removing registry entry for autostart" DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" ; delete data files Delete /REBOOTOK "$COMMONPROGRAMDATA\Portmaster\databases\history.db" RMDir /r /REBOOTOK "$COMMONPROGRAMDATA\Portmaster\databases\cache" RMDir /r /REBOOTOK "$COMMONPROGRAMDATA\Portmaster\intel" RMDir /r /REBOOTOK "$COMMONPROGRAMDATA\Portmaster\download_intel" RMDir /r /REBOOTOK "$COMMONPROGRAMDATA\Portmaster\download_binaries" RMDir /r /REBOOTOK "$COMMONPROGRAMDATA\Portmaster\exec" RMDir /r /REBOOTOK "$COMMONPROGRAMDATA\Portmaster\logs" ; Remove PMv1 migration flag Delete /REBOOTOK "$COMMONPROGRAMDATA\Safing\Portmaster\migrated.txt" ${If} $DeleteAppDataCheckboxState = 1 DetailPrint "Deleting the application data..." RMDir /r /REBOOTOK "$COMMONPROGRAMDATA\Portmaster" RMDir /r /REBOOTOK "$COMMONPROGRAMDATA\Safing" ${Else} DetailPrint "Application data kept as requested by the user." ${EndIf} !macroend ================================================ FILE: desktop/tauri/src-tauri/templates/wix/CheckServiceStatus.vbs ================================================ Option Explicit Dim objShell, objExec, strOutput, arrLines, i, arrStatus ' Create an instance of the WScript.Shell object Set objShell = CreateObject("WScript.Shell") ' Run the sc.exe command to query the service Set objExec = objShell.Exec("cmd /c sc.exe query PortmasterCore") ' Initialize an empty string to store the output strOutput = "" ' Read all output from the command line Do While Not objExec.StdOut.AtEndOfStream strOutput = strOutput & objExec.StdOut.ReadLine() & vbCrLf Loop ' Split the output into lines arrLines = Split(strOutput, vbCrLf) ' Example Output ' SERVICE_NAME: PortmasterCore ' TYPE : 10 WIN32_OWN_PROCESS ' STATE : 1 STOPPED ' WIN32_EXIT_CODE : 1077 (0x435) ' SERVICE_EXIT_CODE : 0 (0x0) ' CHECKPOINT : 0x0 ' WAIT_HINT : 0x0 For i = LBound(arrLines) To UBound(arrLines) ' Example line: STATE : 1 STOPPED If InStr(arrLines(i), "STATE") > 0 Then ' Extract and display the service state ' Example string: "1 STOPPED" arrStatus = Split(Trim(Mid(arrLines(i), InStr(arrLines(i), ":") + 1)), " ") ' Anything other the STOPPED consider as running If Not arrStatus(2) = "STOPPED" Then MsgBox("Portmaster service is running. Stop it and run the installer again.") ' Notify the installer that it should fail. WScript.Quit 1 End If End If Next ================================================ FILE: desktop/tauri/src-tauri/templates/wix/Migration.vbs ================================================ Dim FSO Set FSO = CreateObject("Scripting.FileSystemObject") Dim customData, args, oldInstallationDir, migrationFlagFile, newDataDir, doMigration customData = Session.Property("CustomActionData") ' Split the string by commas args = Split(customData, ",") ' Access individual arguments Dim commonAppDataFolder, programMenuFolder, startupFolder, appDataFolder commonAppDataFolder = Trim(args(0)) programMenuFolder = Trim(args(1)) startupFolder = Trim(args(2)) appDataFolder = Trim(args(3)) ' Read variables from the session object oldInstallationDir = commonAppDataFolder & "Safing\Portmaster\" newDataDir = commonAppDataFolder & "Portmaster" migrationFlagFile = oldInstallationDir & "migrated.txt" doMigration = true ' Check for existing installtion If Not fso.FolderExists(oldInstallationDir) Then doMigration = false End If ' Check if migration was already done If fso.FileExists(migrationFlagFile) Then doMigration = false End If If doMigration Then ' Copy the config file dim configFile configFile = "config.json" If fso.FileExists(oldInstallationDir & configFile) Then fso.CopyFile oldInstallationDir & configFile, newDataDir & configFile End If ' Copy the database folder dim databaseFolder databaseFolder = "databases" If fso.FolderExists(oldInstallationDir & databaseFolder) Then fso.CopyFolder oldInstallationDir & databaseFolder, newDataDir & databaseFolder End If ' Delete shortcuts dim shortcutsFolder notifierShortcut = programMenuFolder & "Portmaster/Portmaster Notifier.lnk" If fso.FileExists(notifierShortcut) Then fso.DeleteFile notifierShortcut, True End If ' Delete startup shortcut dim srartupFile srartupFile = startupFolder & "Portmaster Notifier.lnk" If fso.FileExists(srartupFile) Then fso.DeleteFile srartupFile, True End If ' Delete shortuct in user folder dim userShortcut userShortcut = appDataFolder & "Microsoft\Windows\Start Menu\Programs\Portmaster.lnk" If fso.FileExists(userShortcut) Then fso.DeleteFile userShortcut, True End If ' Delete the old installer dim oldUninstaller oldUninstaller = oldInstallationDir & "portmaster-uninstaller.exe" If fso.FileExists(oldUninstaller) Then fso.DeleteFile oldUninstaller, True End If ' Set the migration flag file fso.CreateTextFile(migrationFlagFile).Close End If ================================================ FILE: desktop/tauri/src-tauri/templates/wix/files.wxs ================================================ ================================================ FILE: desktop/tauri/src-tauri/templates/wix/main.wxs ================================================ {{#if allow_downgrades}} {{else}} {{/if}} Installed AND NOT UPGRADINGPRODUCTCODE {{#if banner_path}} {{/if}} {{#if dialog_image_path}} {{/if}} {{#if license}} {{/if}} {{#if homepage}} {{/if}} WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed {{#unless license}} 1 1 {{/unless}} {{#each deep_link_protocols as |protocol| ~}} {{/each~}} {{#each file_associations as |association| ~}} {{#each association.ext as |ext| ~}} {{/each~}} {{/each~}} {{#each binaries as |bin| ~}} {{/each~}} {{#if enable_elevated_update_task}} {{/if}} {{resources}} {{#each merge_modules as |msm| ~}} {{/each~}} {{#each resource_file_ids as |resource_file_id| ~}} {{/each~}} {{#if enable_elevated_update_task}} {{/if}} {{#each binaries as |bin| ~}} {{/each~}} {{#each component_group_refs as |id| ~}} {{/each~}} {{#each component_refs as |id| ~}} {{/each~}} {{#each feature_group_refs as |id| ~}} {{/each~}} {{#each feature_refs as |id| ~}} {{/each~}} {{#each merge_refs as |id| ~}} {{/each~}} {{#if install_webview}} {{#if download_bootstrapper}} {{/if}} {{#if webview2_bootstrapper_path}} {{/if}} {{#if webview2_installer_path}} {{/if}} {{/if}} {{#if enable_elevated_update_task}} NOT(REMOVE) (REMOVE = "ALL") AND NOT UPGRADINGPRODUCTCODE {{/if}} AUTOLAUNCHAPP AND NOT Installed ================================================ FILE: desktop/tauri/src-tauri/templates/wix/main_original.wxs ================================================ {{#if allow_downgrades}} {{else}} {{/if}} Installed AND NOT UPGRADINGPRODUCTCODE {{#if banner_path}} {{/if}} {{#if dialog_image_path}} {{/if}} {{#if license}} {{/if}} {{#if homepage}} {{/if}} WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed {{#unless license}} 1 1 {{/unless}} {{#each deep_link_protocols as |protocol| ~}} {{/each~}} {{#each file_associations as |association| ~}} {{#each association.ext as |ext| ~}} {{/each~}} {{/each~}} {{#each binaries as |bin| ~}} {{/each~}} {{#if enable_elevated_update_task}} {{/if}} {{resources}} {{#each merge_modules as |msm| ~}} {{/each~}} {{#each resource_file_ids as |resource_file_id| ~}} {{/each~}} {{#if enable_elevated_update_task}} {{/if}} {{#each binaries as |bin| ~}} {{/each~}} {{#each component_group_refs as |id| ~}} {{/each~}} {{#each component_refs as |id| ~}} {{/each~}} {{#each feature_group_refs as |id| ~}} {{/each~}} {{#each feature_refs as |id| ~}} {{/each~}} {{#each merge_refs as |id| ~}} {{/each~}} {{#if install_webview}} {{#if download_bootstrapper}} {{/if}} {{#if webview2_bootstrapper_path}} {{/if}} {{#if webview2_installer_path}} {{/if}} {{/if}} {{#if enable_elevated_update_task}} NOT(REMOVE) (REMOVE = "ALL") AND NOT UPGRADINGPRODUCTCODE {{/if}} AUTOLAUNCHAPP AND NOT Installed ================================================ FILE: desktop/tauri/src-tauri/templates/wix/migration.wxs ================================================ NOT(REMOVE) NOT(REMOVE) ================================================ FILE: desktop/tauri/src-tauri/templates/wix/old_service_check.wxs ================================================ NOT(REMOVE) ================================================ FILE: go.mod ================================================ module github.com/safing/portmaster go 1.24.0 require ( github.com/VictoriaMetrics/metrics v1.40.2 github.com/Xuanwo/go-locale v1.1.3 github.com/aarondl/opt v0.0.0-20250607033636-982744e1bd65 github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6 github.com/agext/levenshtein v1.2.3 github.com/armon/go-radix v1.0.0 github.com/awalterschulze/gographviz v2.0.3+incompatible github.com/bluele/gcache v0.0.2 github.com/brianvoe/gofakeit v3.18.0+incompatible github.com/cilium/ebpf v0.20.0 github.com/coreos/go-iptables v0.8.0 github.com/davecgh/go-spew v1.1.1 github.com/dgraph-io/badger v1.6.2 github.com/florianl/go-conntrack v0.4.0 github.com/florianl/go-nfqueue v1.3.2 github.com/fogleman/gg v1.3.0 github.com/ghodss/yaml v1.0.0 github.com/gobwas/glob v0.2.3 github.com/godbus/dbus/v5 v5.1.0 github.com/gofrs/uuid v4.4.0+incompatible github.com/google/gopacket v1.1.19 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/gorilla/mux v1.8.1 github.com/gorilla/websocket v1.5.3 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-version v1.7.0 github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb github.com/ivpn/desktop-app/daemon/protocol/ivpnclient v0.0.0-20260310125040-62c0e3e2eca6 github.com/jackc/puddle/v2 v2.2.2 github.com/jaswdr/faker/v2 v2.9.0 github.com/lmittmann/tint v1.1.2 github.com/maruel/panicparse/v2 v2.5.0 github.com/mattn/go-colorable v0.1.14 github.com/mattn/go-isatty v0.0.20 github.com/miekg/dns v1.1.68 github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/go-server-timing v1.0.1 github.com/mr-tron/base58 v1.2.0 github.com/oschwald/maxminddb-golang v1.13.1 github.com/r3labs/diff/v3 v3.0.2 github.com/rot256/pblind v0.0.0-20250826112722-8244c3966ed3 github.com/rubenv/sql-migrate v1.8.0 github.com/safing/jess v0.3.5 github.com/safing/structures v1.2.0 github.com/seehuhn/fortuna v1.0.2 github.com/shirou/gopsutil v3.21.11+incompatible github.com/simukti/sqldb-logger v0.0.0-20230108155151-646c1a075551 github.com/spf13/cobra v1.10.1 github.com/spkg/zipfs v0.7.1 github.com/stephenafamo/bob v0.41.1 github.com/stretchr/testify v1.11.1 github.com/tannerryan/ring v1.1.2 github.com/tc-hib/winres v0.3.1 github.com/tevino/abool v1.2.0 github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26 github.com/varlink/go v0.4.0 github.com/vincent-petithory/dataurl v1.0.0 go.etcd.io/bbolt v1.4.3 golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 golang.org/x/image v0.33.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 golang.org/x/sys v0.41.0 gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.40.0 zombiezen.com/go/sqlite v1.4.2 ) require ( al.essio.dev/pkg/shellescape v1.6.0 // indirect dario.cat/mergo v1.0.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/sergeymakinen/go-bmp v1.0.0 // indirect github.com/stephenafamo/sqlparser v0.0.0-20250521201114-5cfed001272d // indirect ) require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/aead/ecdh v0.2.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/danieljoos/wincred v1.2.3 // indirect github.com/denisenkom/go-mssqldb v0.9.0 // indirect github.com/dgraph-io/ristretto v0.2.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.13.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-sql-driver/mysql v1.7.2-0.20231213112541-0004702b931d // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/godror/godror v0.40.4 // indirect github.com/godror/knownpb v0.1.1 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/knadh/koanf/maps v0.1.1 // indirect github.com/knadh/koanf/parsers/yaml v0.1.0 // indirect github.com/knadh/koanf/providers/confmap v0.1.0 // indirect github.com/knadh/koanf/providers/env v0.1.0 // indirect github.com/knadh/koanf/providers/file v0.1.0 // indirect github.com/knadh/koanf/v2 v2.1.0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/mattn/go-oci8 v0.1.1 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mattn/go-sqlite3 v1.14.19 // indirect github.com/mdlayher/netlink v1.8.0 // indirect github.com/mdlayher/socket v0.5.1 // indirect github.com/mitchellh/cli v1.1.5 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/posener/complete v1.2.3 // indirect github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/satori/go.uuid v1.2.0 // indirect github.com/seehuhn/sha256d v1.0.0 // indirect github.com/sergeymakinen/go-ico v1.0.0 github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/cast v1.7.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stephenafamo/scan v0.7.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect github.com/tklauser/numcpus v0.10.0 // indirect github.com/urfave/cli/v2 v2.23.7 // indirect github.com/valyala/fastrand v1.1.0 // indirect github.com/valyala/histogram v1.2.0 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/volatiletech/inflect v0.0.1 // indirect github.com/volatiletech/strmangle v0.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zalando/go-keyring v0.2.6 // indirect github.com/zeebo/blake3 v0.2.4 // indirect golang.org/x/crypto v0.48.0 // indirect golang.org/x/mod v0.32.0 // indirect golang.org/x/text v0.34.0 // indirect golang.org/x/tools v0.41.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect modernc.org/libc v1.66.10 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect mvdan.cc/gofumpt v0.7.0 // indirect ) tool ( github.com/rubenv/sql-migrate/sql-migrate github.com/stephenafamo/bob/gen/bobgen-sqlite ) ================================================ FILE: go.sum ================================================ al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA= al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/UNO-SOFT/zlog v0.8.1 h1:TEFkGJHtUfTRgMkLZiAjLSHALjwSBdw6/zByMC5GJt4= github.com/UNO-SOFT/zlog v0.8.1/go.mod h1:yqFOjn3OhvJ4j7ArJqQNA+9V+u6t9zSAyIZdWdMweWc= github.com/VictoriaMetrics/metrics v1.40.2 h1:OVSjKcQEx6JAwGeu8/KQm9Su5qJ72TMEW4xYn5vw3Ac= github.com/VictoriaMetrics/metrics v1.40.2/go.mod h1:XE4uudAAIRaJE614Tl5HMrtoEU6+GDZO4QTnNSsZRuA= github.com/Xuanwo/go-locale v1.1.3 h1:EWZZJJt5rqPHHbqPRH1zFCn5D7xHjjebODctA4aUO3A= github.com/Xuanwo/go-locale v1.1.3/go.mod h1:REn+F/c+AtGSWYACBSYZgl23AP+0lfQC+SEFPN+hj30= github.com/aarondl/opt v0.0.0-20250607033636-982744e1bd65 h1:lbdPe4LBNmNDzeQFwNhEc88w90841qv737MI4+aXSYU= github.com/aarondl/opt v0.0.0-20250607033636-982744e1bd65/go.mod h1:+xKBXrTAUOvrDXO5PRwIr4E1wciHY3Glgl+6OkCXknU= github.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ= github.com/aead/ecdh v0.2.0/go.mod h1:a9HHtXuSo8J1Js1MwLQx2mBhkXMT6YwUmVVEY4tTB8U= github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6 h1:5L8Mj9Co9sJVgW3TpYk2gxGJnDjsYuboNTcRmbtGKGs= github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6/go.mod h1:3HgLJ9d18kXMLQlJvIY3+FszZYMxCz8WfE2MQ7hDY0w= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= github.com/brianvoe/gofakeit v3.18.0+incompatible h1:wDOmHc9DLG4nRjUVVaxA+CEglKOW72Y5+4WNxUIkjM8= github.com/brianvoe/gofakeit v3.18.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/cilium/ebpf v0.20.0 h1:atwWj9d3NffHyPZzVlx3hmw1on5CLe9eljR8VuHTwhM= github.com/cilium/ebpf v0.20.0/go.mod h1:pzLjFymM+uZPLk/IXZUL63xdx5VXEo+enTzxkZXdycw= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc= github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE= github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.0/go.mod h1:3+D9sFq0ahK/JeJPhCBUV1xlf4/eIYrUQaxulT0VzX8= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/florianl/go-conntrack v0.4.0 h1:TlYkxytdwgVayfU0cKwkHurQA0Rd1ZSEBRckRYDUu18= github.com/florianl/go-conntrack v0.4.0/go.mod h1:iPDx4oIats2T7X7Jm3PFyRCJM1GfZhJaSHOWROYOrE8= github.com/florianl/go-nfqueue v1.3.2 h1:8DPzhKJHywpHJAE/4ktgcqveCL7qmMLsEsVD68C4x4I= github.com/florianl/go-nfqueue v1.3.2/go.mod h1:eSnAor2YCfMCVYrVNEhkLGN/r1L+J4uDjc0EUy0tfq4= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/friendsofgo/errors v0.9.2 h1:X6NYxef4efCBdwI7BgS820zFaN7Cphrmb+Pljdzjtgk= github.com/friendsofgo/errors v0.9.2/go.mod h1:yCvFW5AkDIL9qn7suHVLiI/gH228n7PC4Pn44IGoTOI= github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s= github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI= github.com/go-sql-driver/mysql v1.7.2-0.20231213112541-0004702b931d h1:QQP1nE4qh5aHTGvI1LgOFxZYVxYoGeMfbNHikogPyoA= github.com/go-sql-driver/mysql v1.7.2-0.20231213112541-0004702b931d/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.40.4 h1:X1e7hUd02GDaLWKZj40Z7L0CP0W9TrGgmPQZw6+anBg= github.com/godror/godror v0.40.4/go.mod h1:i8YtVTHUJKfFT3wTat4A9UoqScUtZXiYB9Rf3SVARgc= github.com/godror/knownpb v0.1.1 h1:A4J7jdx7jWBhJm18NntafzSC//iZDHkDi1+juwQ5pTI= github.com/godror/knownpb v0.1.1/go.mod h1:4nRFbQo1dDuwKnblRXDxrfCFYeT4hjg3GjMqef58eRE= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/gddo v0.0.0-20180823221919-9d8ff1c67be5/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f h1:16RtHeWGkJMc80Etb8RPCcKevXGldr57+LOyZt8zOlg= github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f/go.mod h1:ijRvpgDJDI262hYq/IQVYgf8hd8IHUs93Ol0kvMBAx4= github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ivpn/desktop-app/daemon/protocol/ivpnclient v0.0.0-20260310125040-62c0e3e2eca6 h1:7rX8Ci6YSrA6TkB9BVQiM0YyA2WU5Wa9q2f1YFFImPQ= github.com/ivpn/desktop-app/daemon/protocol/ivpnclient v0.0.0-20260310125040-62c0e3e2eca6/go.mod h1:dvedOGgEXvFCK/gz+BJQhd5BSTQvIygmOr/22xPdgIw= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jaswdr/faker/v2 v2.9.0 h1:Sqqpp+pxduDO+MGOhYE3UHtI9Sowt9j95f8h8nVvips= github.com/jaswdr/faker/v2 v2.9.0/go.mod h1:jZq+qzNQr8/P+5fHd9t3txe2GNPnthrTfohtnJ7B+68= github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw= github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs= github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA= github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo= github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786 h1:N527AHMa793TP5z5GNAn/VLPzlc0ewzWdeP/25gDfgQ= github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs= github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w= github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY= github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU= github.com/knadh/koanf/providers/env v0.1.0 h1:LqKteXqfOWyx5Ab9VfGHmjY9BvRXi+clwyZozgVRiKg= github.com/knadh/koanf/providers/env v0.1.0/go.mod h1:RE8K9GbACJkeEnkl8L/Qcj8p4ZyPXZIQ191HJi44ZaQ= github.com/knadh/koanf/providers/file v0.1.0 h1:fs6U7nrV58d3CFAFh8VTde8TM262ObYf3ODrc//Lp+c= github.com/knadh/koanf/providers/file v0.1.0/go.mod h1:rjJ/nHQl64iYCtAW2QQnF0eSmDEX/YZ/eNFj5yR6BvA= github.com/knadh/koanf/v2 v2.1.0 h1:eh4QmHHBuU8BybfIJ8mB8K8gsGCD/AUQTdwGq/GzId8= github.com/knadh/koanf/v2 v2.1.0/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lmittmann/tint v1.1.2 h1:2CQzrL6rslrsyjqLDwD11bZ5OpLBPU+g3G/r5LSfS8w= github.com/lmittmann/tint v1.1.2/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/maruel/panicparse/v2 v2.5.0 h1:yCtuS0FWjfd0RTYMXGpDvWcb0kINm8xJGu18/xMUh00= github.com/maruel/panicparse/v2 v2.5.0/go.mod h1:DA2fDiBk63bKfBf4CVZP9gb4fuvzdPbLDsSI873hweQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-oci8 v0.1.1 h1:aEUDxNAyDG0tv8CA3TArnDQNyc4EhnWlsfxRgDHABHM= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60/go.mod h1:aYbhishWc4Ai3I2U4Gaa2n3kHWSwzme6EsG/46HRQbE= github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys= github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q= github.com/mdlayher/netlink v1.5.0/go.mod h1:1Kr8BBFxGyUyNmztC9WLOayqYVAd2wsgOZm18nqGuzQ= github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA= github.com/mdlayher/netlink v1.8.0 h1:e7XNIYJKD7hUct3Px04RuIGJbBxy1/c4nX7D5YyvvlM= github.com/mdlayher/netlink v1.8.0/go.mod h1:UhgKXUlDQhzb09DrCl2GuRNEglHmhYoWAHid9HK3594= github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc= github.com/mdlayher/socket v0.0.0-20211007213009-516dcbdf0267/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g= github.com/mdlayher/socket v0.1.0/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs= github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng= github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-server-timing v1.0.1 h1:f00/aIe8T3MrnLhQHu3tSWvnwc5GV/p5eutuu3hF/tE= github.com/mitchellh/go-server-timing v1.0.1/go.mod h1:Mo6GKi9FSLwWFAMn3bqVPWe20y5ri5QGQuO9D9MCOxk= github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE= github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= github.com/oklog/ulid/v2 v2.0.2 h1:r4fFzBm+bv0wNKNh5eXTwU7i85y5x+uwkxCUTNVQqLc= github.com/oklog/ulid/v2 v2.0.2/go.mod h1:mtBL0Qe/0HAx6/a4Z30qxVIAL1eQDweXq5lxOEiwQ68= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 h1:wSmWgpuccqS2IOfmYrbRiUgv+g37W5suLLLxwwniTSc= github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494/go.mod h1:yipyliwI08eQ6XwDm1fEwKPdF/xdbkiHtrU+1Hg+vc4= github.com/r3labs/diff/v3 v3.0.2 h1:yVuxAY1V6MeM4+HNur92xkS39kB/N+cFi2hMkY06BbA= github.com/r3labs/diff/v3 v3.0.2/go.mod h1:Cy542hv0BAEmhDYWtGxXRQ4kqRsVIcEjG9gChUlTmkw= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rot256/pblind v0.0.0-20250826112722-8244c3966ed3 h1:ymYWkLrwKuEaatWZboGryvjZnqap+9v0HX4gcozmZOo= github.com/rot256/pblind v0.0.0-20250826112722-8244c3966ed3/go.mod h1:rHhYqxx18ekqo/Y4U7dKP6/H8ZaUDY/pIHGKhq4DKxc= github.com/rubenv/sql-migrate v1.8.0 h1:dXnYiJk9k3wetp7GfQbKJcPHjVJL6YK19tKj8t2Ns0o= github.com/rubenv/sql-migrate v1.8.0/go.mod h1:F2bGFBwCU+pnmbtNYDeKvSuvL6lBVtXDXUUv5t+u1qw= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/safing/jess v0.3.5 h1:KS5elTKfWcDUow8SUoCj5QdyyGJNoExJNySerNkbxUU= github.com/safing/jess v0.3.5/go.mod h1:+B6UJnXVxi406Wk08SDnoC5NNBL7t3N0vZGokEbkVQI= github.com/safing/structures v1.2.0 h1:S6EzKxxGYTO6P9P3Dkab9gisLOrfAyvy7JzFOUSkOUk= github.com/safing/structures v1.2.0/go.mod h1:zIun7mz3xV0dJ3vXRZuU71ATzT8D/0hGJO+u+bk5Kvs= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/seehuhn/fortuna v1.0.2 h1:eU/UhTlFVpK/IuSiTlWyZY/S39Lbye+jjZdzNADHYEc= github.com/seehuhn/fortuna v1.0.2/go.mod h1:LX8ubejCnUoT/hX+1aKUtbKls2H6DRkqzkc7TdR3iis= github.com/seehuhn/sha256d v1.0.0 h1:TXTsAuEWr02QjRm153Fnvvb6fXXDo7Bmy1FizxarGYw= github.com/seehuhn/sha256d v1.0.0/go.mod h1:PEuxg9faClSveVuFXacQmi+NtDI/PX8bpKjtNzf2+s4= github.com/sergeymakinen/go-bmp v1.0.0 h1:SdGTzp9WvCV0A1V0mBeaS7kQAwNLdVJbmHlqNWq0R+M= github.com/sergeymakinen/go-bmp v1.0.0/go.mod h1:/mxlAQZRLxSvJFNIEGGLBE/m40f3ZnUifpgVDlcUIEY= github.com/sergeymakinen/go-ico v1.0.0 h1:uL3khgvKkY6WfAetA+RqsguClBuu7HpvBB/nq/Jvr80= github.com/sergeymakinen/go-ico v1.0.0/go.mod h1:wQ47mTczswBO5F0NoDt7O0IXgnV4Xy3ojrroMQzyhUk= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc= github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/simukti/sqldb-logger v0.0.0-20230108155151-646c1a075551 h1:+EXKKt7RC4HyE/iE8zSeFL+7YBL8Z7vpBaEE3c7lCnk= github.com/simukti/sqldb-logger v0.0.0-20230108155151-646c1a075551/go.mod h1:ztTX0ctjRZ1wn9OXrzhonvNmv43yjFUXJYJR95JQAJE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spkg/zipfs v0.7.1 h1:+2X5lvNHTybnDMQZAIHgedRXZK1WXdc+94R/P5v2XWE= github.com/spkg/zipfs v0.7.1/go.mod h1:48LW+/Rh1G7aAav1ew1PdlYn52T+LM+ARmSHfDNJvg8= github.com/stephenafamo/bob v0.41.1 h1:xcRPuRMCwtZZ9tS4JIVbZ5Erdm5Dy5dIvbS5kivwPpA= github.com/stephenafamo/bob v0.41.1/go.mod h1:8l55917DM36gF518Iz1MHjLds7KGAfkitJfxISYlth8= github.com/stephenafamo/fakedb v0.0.0-20221230081958-0b86f816ed97 h1:XItoZNmhOih06TC02jK7l3wlpZ0XT/sPQYutDcGOQjg= github.com/stephenafamo/fakedb v0.0.0-20221230081958-0b86f816ed97/go.mod h1:bM3Vmw1IakoaXocHmMIGgJFYob0vuK+CFWiJHQvz0jQ= github.com/stephenafamo/scan v0.7.0 h1:lfFiD9H5+n4AdK3qNzXQjj2M3NfTOpmWBIA39NwB94c= github.com/stephenafamo/scan v0.7.0/go.mod h1:FhIUJ8pLNyex36xGFiazDJJ5Xry0UkAi+RkWRrEcRMg= github.com/stephenafamo/sqlparser v0.0.0-20250521201114-5cfed001272d h1:YmPQh4pYOjqGWllnvJ2EoMZe1a8RgAyBrw4cH2FfabY= github.com/stephenafamo/sqlparser v0.0.0-20250521201114-5cfed001272d/go.mod h1:2ATW++wFz7Mvc/N+nUtQnU+9VIGAxrn8m9JCLDSWMsQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tannerryan/ring v1.1.2 h1:iXayOjqHQOLzuy9GwSKuG3nhWfzQkldMlQivcgIr7gQ= github.com/tannerryan/ring v1.1.2/go.mod h1:DkELJEjbZhJBtFKR9Xziwj3HKZnb/knRgljNqp65vH4= github.com/tc-hib/winres v0.3.1 h1:CwRjEGrKdbi5CvZ4ID+iyVhgyfatxFoizjPhzez9Io4= github.com/tc-hib/winres v0.3.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= github.com/testcontainers/testcontainers-go v0.38.0 h1:d7uEapLcv2P8AvH8ahLqDMMxda2W9gQN1nRbHS28HBw= github.com/testcontainers/testcontainers-go v0.38.0/go.mod h1:C52c9MoHpWO+C4aqmgSU+hxlR5jlEayWtgYrb8Pzz1w= github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA= github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d h1:dOMI4+zEbDI37KGb0TI44GUAwxHF9cMsIoDTJ7UmgfU= github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d/go.mod h1:l8xTsYB90uaVdMHXMCxKKLSgw5wLYBwBKKefNIUnm9s= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26 h1:UFHFmFfixpmfRBcxuu+LA9l8MdURWVdVNUHxO5n1d2w= github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26/go.mod h1:IGhd0qMDsUa9acVjsbsT7bu3ktadtGOHI79+idTew/M= github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY= github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ= github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY= github.com/varlink/go v0.4.0 h1:+/BQoUO9eJK/+MTSHwFcJch7TMsb6N6Dqp6g0qaXXRo= github.com/varlink/go v0.4.0/go.mod h1:DKg9Y2ctoNkesREGAEak58l+jOC6JU2aqZvUYs5DynU= github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI= github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/volatiletech/inflect v0.0.1 h1:2a6FcMQyhmPZcLa+uet3VJ8gLn/9svWhJxJYwvE8KsU= github.com/volatiletech/inflect v0.0.1/go.mod h1:IBti31tG6phkHitLlr5j7shC5SOo//x0AjDzaJU1PLA= github.com/volatiletech/strmangle v0.0.6 h1:AdOYE3B2ygRDq4rXDij/MMwq6KVK/pWAYxpC7CLrkKQ= github.com/volatiletech/strmangle v0.0.6/go.mod h1:ycDvbDkjDvhC0NUU8w3fWwl5JEMTV56vTKXzR3GeR+0= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= modernc.org/cc/v4 v4.26.5 h1:xM3bX7Mve6G8K8b+T11ReenJOT+BmVqQj0FY5T4+5Y4= modernc.org/cc/v4 v4.26.5/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v4 v4.28.1 h1:wPKYn5EC/mYTqBO373jKjvX2n+3+aK7+sICCv4Fjy1A= modernc.org/ccgo/v4 v4.28.1/go.mod h1:uD+4RnfrVgE6ec9NGguUNdhqzNIeeomeXf6CL0GTE5Q= modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA= modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= modernc.org/libc v1.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A= modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= modernc.org/sqlite v1.40.0 h1:bNWEDlYhNPAUdUdBzjAvn8icAs/2gaKlj4vM+tQ6KdQ= modernc.org/sqlite v1.40.0/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU= mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo= zombiezen.com/go/sqlite v1.4.2 h1:KZXLrBuJ7tKNEm+VJcApLMeQbhmAUOKA5VWS93DfFRo= zombiezen.com/go/sqlite v1.4.2/go.mod h1:5Kd4taTAD4MkBzT25mQ9uaAlLjyR0rFhsR6iINO70jc= ================================================ FILE: packaging/README.md ================================================ # Generate Windows installer ## Prerequisites Earthly release prep step must be executed and the output `dist` folder should be present in the root directory of the repository. (Probably needs to be done on separate machine running linux or downloaded from the CI) ``` earthly +release-prep ``` ## Building the installers In the root directory of the repository, run the PowerShell script to generate the installers: ``` ./packaging\windows\generate_windows_installers.ps1 ``` This will output both .exe (NSIS) and .msi (WIX) installers inside the dist folder: ``` ...\Portmaster\dist\windows_amd64\Portmaster_0.1.0_x64-setup.exe ...\Portmaster\dist\windows_amd64\Portmaster_0.1.0_x64_en-US.msi ``` ## Manual build ### Prerequisites Ensure you have Rust and Cargo installed. Install Tauri CLI by running: ``` cargo install tauri-cli --version "^2.0.0" --locked ``` ### Folder structure Create binary and intel folder inside the tauri project folder and place all the necessary files inside. The folder structure should look like this: ``` ...\Portmaster\desktop\tauri\src-tauri\binary assets.zip index.json portmaster-core.dll portmaster-core.exe portmaster-kext.dll portmaster-kext.sys portmaster.zip WebView2Loader.dll ...\Portmaster\desktop\tauri\src-tauri\intel base.dsdl geoipv4.mmdb geoipv6.mmdb index.dsd index.json intermediate.dsdl main-intel.yaml news.yaml notifications.yaml urgent.dsdl ``` ### Building the Installer Navigate to the `src-tauri` directory: ``` cd desktop/tauri/src-tauri ``` Run the following commands to build the installers: For both NSIS and WIX installers: ``` cargo tauri bundle ``` For NSIS installer only: ``` cargo tauri bundle --bundles nsis ``` For WIX installer only: ``` cargo tauri bundle --bundles wix ``` The produced files will be in: ``` target\release\bundle\msi\ target\release\bundle\nsis\ ``` ## Debug MSI Installer To see error messages during the build of the installer, run the bundler with the verbose flag: ``` cargo tauri bundle --bundles msi --verbose ``` To examine the logs during installation, run the installer with the following command: ``` msiexec /i "target\release\bundle\msi\Portmaster_0.1.0_x64_en-US.msi" /lv install.log ``` ================================================ FILE: packaging/linux/dev_helpers/build_tauri.sh ================================================ #!/usr/bin/env bash set -euo pipefail # This script builds the Tauri application for Portmaster on Linux. # It optionally builds the required Angular tauri-builtin project first. # The script assumes that all necessary dependencies (Node.js, Angular CLI, Rust, cargo-tauri) are installed. # Output file: dist/portmaster # Resolve script directory and project root SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/../../../" && pwd)" ORIGINAL_DIR="$(pwd)" # Create output directory OUTPUT_DIR="${SCRIPT_DIR}/dist" mkdir -p "${OUTPUT_DIR}" # Helper: check for command availability have() { command -v "$1" >/dev/null 2>&1; } # Optional: build Angular tauri-builtin read -r -p "Build Angular tauri-builtin project? (Y/N, default: Y) " REPLY REPLY=${REPLY:-Y} if [[ ! ${REPLY} =~ ^[Nn]$ ]]; then # Ensure Angular CLI is available if ! have ng; then echo "Error: Angular CLI 'ng' not found in PATH." >&2 echo "Install via: npm install -g @angular/cli" >&2 exit 1 fi # Navigate to Angular project pushd "${PROJECT_ROOT}/desktop/angular" >/dev/null # Build tauri-builtin with production config ng build --configuration production --base-href / tauri-builtin || { popd >/dev/null cd "${ORIGINAL_DIR}" exit 1 } popd >/dev/null fi # Navigate to Tauri src-tauri directory pushd "${PROJECT_ROOT}/desktop/tauri/src-tauri" >/dev/null # Ensure cargo and tauri plugin are available if ! have cargo; then echo "Error: cargo not found. Install Rust toolchain (rustup)." >&2 exit 1 fi if ! cargo tauri --help >/dev/null 2>&1; then echo "Error: cargo-tauri not installed." >&2 echo "Install via: cargo install tauri-cli" >&2 popd >/dev/null exit 1 fi # Build Tauri project (no bundle) cargo tauri build --no-bundle # Copy built binary to dist TAURI_OUTPUT_DIR="$(pwd)/target/release" if [[ -f "${TAURI_OUTPUT_DIR}/portmaster" ]]; then cp -f "${TAURI_OUTPUT_DIR}/portmaster" "${OUTPUT_DIR}/" echo "Build completed successfully: ${OUTPUT_DIR}/portmaster" else echo "Error: Built binary not found at ${TAURI_OUTPUT_DIR}/portmaster" >&2 popd >/dev/null exit 1 fi # Return to original directory popd >/dev/null cd "${ORIGINAL_DIR}" ================================================ FILE: packaging/linux/portmaster-autostart.desktop ================================================ [Desktop Entry] Name=Portmaster GenericName=Application Firewall Notifier Exec=/usr/bin/portmaster --with-prompts --with-notifications --background Icon=portmaster Terminal=false Type=Application Categories=System NoDisplay=true ================================================ FILE: packaging/linux/portmaster.desktop ================================================ [Desktop Entry] Name=Portmaster GenericName=Application Firewall Exec={{exec}} --data=/opt/safing/portmaster --with-prompts --with-notifications Icon={{icon}} StartupWMClass=portmaster Terminal=false Type=Application Categories=System ================================================ FILE: packaging/linux/portmaster.service ================================================ [Unit] Description=Portmaster by Safing Documentation=https://safing.io Documentation=https://docs.safing.io Before=nss-lookup.target network.target shutdown.target After=systemd-networkd.service Conflicts=shutdown.target Conflicts=firewalld.service Wants=nss-lookup.target [Service] Type=simple Restart=on-failure RestartSec=10 RestartPreventExitStatus=24 LockPersonality=yes MemoryDenyWriteExecute=yes MemoryLow=2G NoNewPrivileges=yes PrivateTmp=yes PIDFile=/var/lib/portmaster/core-lock.pid Environment=LOGLEVEL=info Environment=PORTMASTER_ARGS= EnvironmentFile=-/etc/default/portmaster ProtectSystem=true ReadWritePaths=/usr/lib/portmaster RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 RestrictNamespaces=yes ProtectHome=read-only ProtectKernelTunables=yes ProtectKernelLogs=yes ProtectControlGroups=yes PrivateDevices=yes AmbientCapabilities=cap_chown cap_kill cap_net_admin cap_net_bind_service cap_net_broadcast cap_net_raw cap_sys_module cap_sys_ptrace cap_dac_override cap_fowner cap_fsetid cap_sys_resource cap_bpf cap_perfmon CapabilityBoundingSet=cap_chown cap_kill cap_net_admin cap_net_bind_service cap_net_broadcast cap_net_raw cap_sys_module cap_sys_ptrace cap_dac_override cap_fowner cap_fsetid cap_sys_resource cap_bpf cap_perfmon StateDirectory=portmaster # TODO(ppacher): add --disable-software-updates once it's merged and the release process changed. WorkingDirectory=/var/lib/portmaster ExecStart=/usr/lib/portmaster/portmaster-core --log-dir=/var/lib/portmaster/log -- $PORTMASTER_ARGS ExecStopPost=-/usr/lib/portmaster/portmaster-core --recover-iptables [Install] WantedBy=multi-user.target ================================================ FILE: packaging/linux/postinst ================================================ #!/bin/bash echo "[ ] Post-Install script [arg1='$1' arg2='$2']" echo "[ ] Stopping old service (if exists)" systemctl stop portmaster.service systemctl disable portmaster.service # # Migration from v1 # OLD_INSTALLATION_DIR="/opt/safing/portmaster" MIGRATED_FILE_FLAG="$OLD_INSTALLATION_DIR/migrated.txt" if [ -d "$OLD_INSTALLATION_DIR" ]; then if [ ! -e "$MIGRATED_FILE_FLAG" ]; then echo "[ ] Starting migration form v1 ..." # Because the service file need to change path, first the links to the old service needs to be removed. echo "[ ] V1 migration: Removing old service" rm /etc/systemd/system/portmaster.service # new V2 service registered at "/usr/lib/systemd/system/portmaster.service" # Migrate config echo "[ ] V1 migration: Copying V1 configuration" cp -r $OLD_INSTALLATION_DIR/databases /var/lib/portmaster cp -r $OLD_INSTALLATION_DIR/config.json /var/lib/portmaster/config.json # Remove shortcut echo "[ ] V1 migration: Removing V1 shortcuts" rm /etc/xdg/autostart/portmaster_notifier.desktop rm /usr/share/applications/portmaster_notifier.desktop # app V1 shortcut # NOTE: new V2 shortcut registered as "Portmaster.desktop" (first letter uppercase), so we can distinguish between V1 and V2 shortcuts. rm /usr/share/applications/portmaster.desktop # Remove V1 files (except configuration) # (keeping V1 configuration for a smooth downgrade, if needed) echo "[ ] V1 migration: Removing V1 files" rm -fr $OLD_INSTALLATION_DIR/exec rm -fr $OLD_INSTALLATION_DIR/logs rm -fr $OLD_INSTALLATION_DIR/updates rm -fr $OLD_INSTALLATION_DIR/databases/cache rm -fr $OLD_INSTALLATION_DIR/databases/icons rm -fr $OLD_INSTALLATION_DIR/databases/history.db for file in $OLD_INSTALLATION_DIR/*; do if [ -f "$file" ] && [ "$(basename "$file")" != "config.json" ]; then rm "$file" fi done touch $MIGRATED_FILE_FLAG echo "[ ] Migration complete" fi fi # # Fix selinux permissions for portmaster-core if we have semanage available. # if command -V semanage >/dev/null 2>&1; then echo "[ ] Fixing SELinux permissions" semanage fcontext -a -t bin_t -s system_u $(realpath /usr/lib)'/portmaster/portmaster-core' || : restorecon -R /usr/lib/portmaster/portmaster-core 2>/dev/null >&2 || : fi echo "[ ] Initializing binary files" mv /usr/bin/portmaster /usr/lib/portmaster/portmaster ln -s /usr/lib/portmaster/portmaster /usr/bin/portmaster chmod +x /usr/lib/portmaster/portmaster-core echo "[ ] Enabling service" systemctl daemon-reload systemctl enable portmaster.service echo "[ ] Done. Please reboot your system" ================================================ FILE: packaging/linux/postrm ================================================ #!/bin/bash echo "[ ] Post-Remove script [arg1='$1' arg2='$2']" # DEB argument on upgrade - 'upgrade'; RPM - '1' if [ "$1" = "upgrade" ] || [ "$1" = "1" ] ; then echo "[ ] Post-Remove script: This is an upgrade." exit 0 fi # # Remove selinux permissions for portmaster-core if we have semanage available. # if command -V semanage >/dev/null 2>&1; then echo "[ ] Removing SELinux permissions" semanage fcontext --delete $(realpath /usr/lib)'/portmaster/portmaster-core' || : restorecon -R /usr/lib/portmaster/portmaster-core 2>/dev/null >&2 || : fi echo "[ ] Stopping and disabling service" systemctl stop portmaster.service systemctl disable portmaster.service echo "[ ] Removing files" # Remove binaries folder sudo rm -fr /usr/lib/portmaster # Remove data folder sudo rm -fr /var/lib/portmaster # remove V1 migration flag (if exists) MIGRATED_FILE_FLAG="/opt/safing/portmaster/migrated.txt" if [ -e "$MIGRATED_FILE_FLAG" ]; then echo "[ ] Removing V1 migration flag" rm "$MIGRATED_FILE_FLAG" fi ================================================ FILE: packaging/linux/readme.md ================================================ # Installation scripts order Execution order of installation scripts (`preInstallScript, preRemoveScript, postInstallScript, postRemoveScript`) is different for DEB and RPM packages. **NOTE!** 'remove' scripts is using from old version! ## DEB scripts order Useful link: https://wiki.debian.org/MaintainerScripts ``` DEB (apt) Install v2.2.2: [*] Before install (2.2.2 : deb : install) [*] After install (2.2.2 : deb : configure) DEB (apt) Upgrade v1.1.1 -> v2.2.2: [*] Before remove (1.1.1 : deb : upgrade) [*] Before install (2.2.2 : deb : upgrade) [*] After remove (1.1.1 : deb : upgrade) [*] After install (2.2.2 : deb : configure) DEB (apt) Remove: [*] Before remove (1.1.1 : deb : remove) [*] After remove (1.1.1 : deb : remove) ``` ## RPM scripts order Useful link: https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/ When scriptlets are called, they will be supplied with an argument. This argument, accessed via $1 (for shell scripts) is the number of packages of this name which will be left on the system when the action completes. ``` RPM (dnf) install: [*] Before install (2.2.2 : rpm : 1) [*] After install (2.2.2 : rpm : 1) RPM (dnf) upgrade: [*] Before install (2.2.2 : rpm : 2) [*] After install (2.2.2 : rpm : 2) [*] Before remove (1.1.1 : rpm : 1) [*] After remove (1.1.1 : rpm : 1) RPM (dnf) remove: [*] Before remove (2.2.2 : rpm : 0) [*] After remove (2.2.2 : rpm : 0) ``` ================================================ FILE: packaging/windows/.gitkeep ================================================ ================================================ FILE: packaging/windows/dev_helpers/build_angular.ps1 ================================================ # This script builds the Angular project for the Portmaster application and packages it into a zip file. # The script assumes that all necessary dependencies are installed and available. # Output file: dist/portmaster.zip [CmdletBinding()] param ( [Parameter(Mandatory=$false)] [Alias("d")] [switch]$Development, [Parameter(Mandatory=$false)] [Alias("i")] [switch]$Interactive ) # Store original directory and find project root $originalDir = Get-Location $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $projectRoot = (Get-Item $scriptDir).Parent.Parent.Parent.FullName try { # Create output directory $outputDir = Join-Path $scriptDir "dist" New-Item -ItemType Directory -Path $outputDir -Force | Out-Null # Navigate to Angular project Set-Location (Join-Path $projectRoot "desktop\angular") # npm install - always run in non-interactive mode, ask in interactive mode if (!$Interactive -or (Read-Host "Run 'npm install'? (Y/N, default: Y)") -notmatch '^[Nn]$') { npm install if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } # build libs - always run in non-interactive mode, ask in interactive mode if (!$Interactive -or (Read-Host "Build shared libraries? (Y/N, default: Y)") -notmatch '^[Nn]$') { if ($Development) { Write-Host "Building shared libraries in development mode" -ForegroundColor Yellow npm run build-libs:dev } else { Write-Host "Building shared libraries in production mode" -ForegroundColor Yellow npm run build-libs } if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } # Build Angular project if ($Development) { Write-Host "Building Angular project in development mode" -ForegroundColor Yellow ng build --configuration development --base-href /ui/modules/portmaster/ portmaster } else { Write-Host "Building Angular project in production mode" -ForegroundColor Yellow ng build --configuration production --base-href /ui/modules/portmaster/ portmaster } if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } # Create zip archive Write-Host "Creating zip archive" -ForegroundColor Yellow Set-Location dist $destinationZip = Join-Path $outputDir "portmaster.zip" if ($PSVersionTable.PSVersion.Major -ge 5) { # Option 1: Use .NET Framework directly (faster than Compress-Archive) Write-Host "Using System.IO.Compression for faster archiving" -ForegroundColor Yellow if (Test-Path $destinationZip) { Remove-Item $destinationZip -Force } # Remove existing zip if it exists Add-Type -AssemblyName System.IO.Compression.FileSystem $compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal [System.IO.Compression.ZipFile]::CreateFromDirectory((Get-Location), $destinationZip, $compressionLevel, $false) } else { # Fall back to Compress-Archive Compress-Archive -Path * -DestinationPath $destinationZip -Force } Write-Host "Build completed successfully: $(Join-Path $outputDir "portmaster.zip")" -ForegroundColor Green } finally { # Return to original directory - this will execute even if Ctrl+C is pressed Set-Location $originalDir } ================================================ FILE: packaging/windows/dev_helpers/build_tauri.ps1 ================================================ # This script builds the Tauri application for Portmaster on Windows. # It optionally builds the required Angular tauri-builtin project first. # The script assumes that all necessary dependencies (Node.js, Rust, etc.) are installed. # Output file: dist/portmaster.exe # Store original directory and find project root $originalDir = Get-Location $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $projectRoot = (Get-Item $scriptDir).Parent.Parent.Parent.FullName # Create output directory $outputDir = Join-Path $scriptDir "dist" New-Item -ItemType Directory -Path $outputDir -Force | Out-Null # Ask if user wants to build the Angular tauri-builtin project if ((Read-Host "Build Angular tauri-builtin project? (Y/N, default: Y)") -notmatch '^[Nn]$') { # Navigate to Angular project Set-Location (Join-Path $projectRoot "desktop\angular") # Build tauri-builtin project ng build --configuration production --base-href / tauri-builtin if ($LASTEXITCODE -ne 0) { Set-Location $originalDir; exit $LASTEXITCODE } } # Navigate to Tauri project directory Set-Location (Join-Path $projectRoot "desktop\tauri\src-tauri") # Build Tauri project for Windows cargo tauri build --no-bundle if ($LASTEXITCODE -ne 0) { Set-Location $originalDir; exit $LASTEXITCODE } # Copy the output files to the script's dist directory $tauriOutput = Join-Path (Get-Location) "target\release" Copy-Item -Path "$tauriOutput\portmaster.exe" -Destination $outputDir -Force # Return to original directory Set-Location $originalDir Write-Host "Build completed successfully: $outputDir\portmaster.exe" -ForegroundColor Green ================================================ FILE: packaging/windows/generate_windows_installers.ps1 ================================================ #------------------------------------------------------------------------------ # Portmaster Windows Installer Generator #------------------------------------------------------------------------------ # This script creates Windows installers (MSI and NSIS) for Portmaster application # by combining pre-compiled binaries and packaging them with Tauri. # # ## Workflow for creating Portmaster Windows installers: # # 1. Compile Core Binaries (Linux environment) # ``` # earthly +release-prep # # # Or use command to do the same AND build Linux packages: # # earthly +all-artifacts # ``` # This compiles and places files into the 'dist' folder with the required structure. # Note: Latest KEXT binaries and Intel data will be downloaded from https://updates.safing.io # # 2. Compile Windows-Specific Binaries (Windows environment) # Some files cannot be compiled by Earthly and require Windows. # - Compile 'portmaster-core.dll' from the /windows_core_dll folder # - Copy the compiled DLL to /dist/downloaded/windows_amd64 # # 3. Sign All Binaries (Windows environment) # ``` # .\packaging\windows\sign_binaries_in_dist.ps1 -certSha1 d3bf5973eb1ec3dbd6ec45d77d556881a350acd2 # ``` # This signs all binary files in the dist directory # # 4. Create Installers (Windows environment) # Note! You can run it from docker container (see example bellow). # ``` # .\generate_windows_installers.ps1 # ``` # Installers will be placed in /dist/windows_amd64 # # 5. Sign Installers (Windows environment) # ``` # .\packaging\windows\sign_binaries_in_dist.ps1 -certSha1 # ``` # This signs the newly created installer files # #------------------------------------------------------------------------------ # Running inside Docker container # Tested with docker image 'abrarov/msvc-2022:latest' # sha256:f49435d194108cd56f173ad5bc6a27c70eed98b7e8cd54488f5acd85efbd51c9 # # Note! Ensure you switched Docker Desktop to use Windows containers. # Start powershell and cd to the root of the project. # Then run: # $path = Convert-Path . # Get the absolute path of the current directory # docker run -it --rm -v "${path}:C:/app" -w "C:/app" abrarov/msvc-2022 powershell -NoProfile -File C:/app/packaging/windows/generate_windows_installers.ps1 #------------------------------------------------------------------------------ # # Optional arguments: # -i: (interactive) Can prompt for user input (e.g. when a file is not found in the primary folder but found in the alternate folder) # -v: (version) Explicitly set the version to use for the installer file name # -e: (erase) Just erase work directories #------------------------------------------------------------------------------ param ( [Alias('i')] [switch]$interactive, [Alias('v')] [string]$version, [Alias('e')] [switch]$erase ) # Save the current directory $originalDirectory = Get-Location # <<<<<<<<<<<<<<<<<<<<<<< Functions <<<<<<<<<<<<<<<<<<<<<<< # Function to copy a file, with fallback to an alternative location and detailed logging # Parameters: # $SourceDir - Primary directory to search for the file # $File - Name of the file to copy # $DestinationDir - Directory where the file will be copied to # $AlternateSourceDir - Fallback directory if file is not found in $SourceDir # # Behavior: # - Checks if the file exists in the primary source directory # - If not found and an alternate directory is provided, checks there # - In interactive mode, asks for confirmation before using the alternate source # - Logs details about the copied file (path, size, timestamp, version) # - Returns error and exits if file cannot be found or copied function Find-And-Copy-File { param ( [string]$SourceDir, [string]$File, [string]$DestinationDir, [string[]]$AlternateSourceDirs # Changed from single string to array ) $destinationPath = "$DestinationDir/$File" $fullSourcePath = if ($SourceDir) { "$SourceDir/$File" } else { "" } if ($AlternateSourceDirs -and (-not $fullSourcePath -or -not (Test-Path -Path $fullSourcePath))) { # File doesn't exist, check in alternate folders $foundInAlternate = $false foreach ($altDir in $AlternateSourceDirs) { $fallbackSourcePath = "$altDir/$File" if (Test-Path -Path $fallbackSourcePath) { if ($interactive -and $fullSourcePath) { # Do not prompt if the sourceDir is empty or "interactive" mode is not set $response = Read-Host " [?] The file '$File' found in fallback '$altDir' folder.`n Do you want to use it? (y/n)" if ($response -ne 'y' -and $response -ne 'Y') { continue # Try next alternate directory } } $fullSourcePath = $fallbackSourcePath $foundInAlternate = $true break # Found a usable file, stop searching } } if (-not $foundInAlternate) { $altDirsString = $AlternateSourceDirs -join "', '" Write-Error "Required file '$File' not found in: '$SourceDir', '$altDirsString'" exit 1 } } try { # Print details about the file $fileInfo = Get-Item -Path $fullSourcePath $fileHash = (Get-FileHash -Path $fullSourcePath -Algorithm SHA256).Hash $hashShort = $fileHash.Substring(0, 4) + "..." + $fileHash.Substring($fileHash.Length - 8) $output = "{0,-22}: {1,-29} -> {2,-38} [{3}{4}]" -f $File, $(Split-Path -Path $fullSourcePath -Parent), $(Split-Path -Path $destinationPath -Parent), "SHA256: $hashShort", $(if ($fileInfo.VersionInfo.FileVersion) { "; v$($fileInfo.VersionInfo.FileVersion)" } else { "" }) Write-Output "$output" # Create destination directory if not exists if (-not (Test-Path -Path $DestinationDir)) { New-Item -ItemType Directory -Path $DestinationDir -ErrorAction Stop > $null } # Copy the file Copy-Item -Force -Path "${fullSourcePath}" -Destination "${destinationPath}" -ErrorAction Stop } catch { Write-Error "Failed to copy file from '$fullSourcePath' to '$destinationPath'.`nError: $_" exit 1 } } # Function to set and restore Cargo.toml version function Set-CargoVersion { param ([string]$Version) if (-not (Test-Path "Cargo.toml.bak")) { Copy-Item "Cargo.toml" "Cargo.toml.bak" -Force } # Update the version in Cargo.toml. # This will allow the Tauri CLI to set the correct filename for the installer. # NOTE: This works only when the version is not explicitly defined in tauri.conf.json5. (Get-Content "Cargo.toml" -Raw) -replace '(\[package\][^\[]*?)version\s*=\s*"[^"]+"', ('$1version = "' + $Version + '"') | Set-Content "Cargo.toml" } function Restore-CargoVersion { if (Test-Path "Cargo.toml.bak") { Copy-Item "Cargo.toml.bak" "Cargo.toml" -Force Remove-Item "Cargo.toml.bak" -Force } } function Get-GitTagVersion { # Check if running in Docker and configure Git accordingly if ($env:ComputerName -like "*container*" -or $env:USERNAME -eq "ContainerAdministrator") { $currentDir = (Get-Location).Path git config --global --add safe.directory $currentDir } # Try to get exact tag pointing to current commit $version = $(git tag --points-at 2>$null) # If no tag points to current commit, use most recent tag if ([string]::IsNullOrEmpty($version)) { $devVersion = $(git describe --tags --first-parent --abbrev=0 2>$null) if (-not [string]::IsNullOrEmpty($devVersion)) { $version = "${devVersion}" } } $version = $version -replace '^v', '' return $version } # >>>>>>>>>>>>>>>>>>>>>>> End Functions >>>>>>>>>>>>>>>>>>>>>>>> # Set-Location relative to the script location "../.." (root of the project). So that the script can be run from any location. Set-Location -Path (Join-Path -Path $PSScriptRoot -ChildPath "../..") try { # CONSTANTS $destinationDir = "desktop/tauri/src-tauri" $binaryDir = "$destinationDir/binary" #portmaster\desktop\tauri\src-tauri\binary $intelDir = "$destinationDir/intel" #portmaster\desktop\tauri\src-tauri\intel $targetBase= "$destinationDir/target" #portmaster\desktop\tauri\src-tauri\target $targetDir = "$targetBase/release" #portmaster\desktop\tauri\src-tauri\target\release # Erasing work directories Write-Output "[+] Erasing work directories: '$binaryDir', '$intelDir', '$targetBase'" Remove-Item -Recurse -Force -Path $binaryDir -ErrorAction SilentlyContinue Remove-Item -Recurse -Force -Path $intelDir -ErrorAction SilentlyContinue Remove-Item -Recurse -Force -Path $targetBase -ErrorAction SilentlyContinue if ($erase) { Write-Output "[ ] Done" exit 0 } # Copying BINARY FILES Write-Output "`n[+] Copying binary files:" $filesToCopy = @( @{Folder="dist/downloaded/windows_amd64"; File="portmaster-kext.sys"; Destination=$binaryDir; }, @{Folder="dist/downloaded/windows_amd64"; File="portmaster-core.dll"; Destination=$binaryDir; }, @{Folder="dist/binary/windows_amd64"; File="portmaster-core.exe"; Destination=$binaryDir; }, @{Folder="dist/binary/windows_amd64"; File="WebView2Loader.dll"; Destination=$binaryDir; }, @{Folder="dist/binary/all"; File="portmaster.zip"; Destination=$binaryDir; }, @{Folder="dist/binary/all"; File="assets.zip"; Destination=$binaryDir; }, @{Folder="dist/binary/windows_amd64"; File="portmaster.exe"; Destination=$targetDir; } ) foreach ($file in $filesToCopy) { Find-And-Copy-File -SourceDir $file.Folder -File $file.File -DestinationDir $file.Destination -AlternateSourceDirs $file.AlternateSourceDirs } # Copying INTEL FILES Write-Output "`n[+] Copying intel files" if (-not (Test-Path -Path $intelDir)) { New-Item -ItemType Directory -Path $intelDir -ErrorAction Stop > $null } Copy-Item -Force -Path "dist/intel/*" -Destination "$intelDir/" -ErrorAction Stop } catch { Set-Location $originalDirectory Write-Error "[!] Failed! Error: $_" exit 1 } $VERSION_GIT_TAG = Get-GitTagVersion # Check versions of UI and Core binaries $VERSION_UI = (Get-Item "$targetDir/portmaster.exe").VersionInfo.FileVersion $VERSION_CORE = (& "$binaryDir/portmaster-core.exe" version | Select-String -Pattern "Portmaster\s+(\d+\.\d+\.\d+)" | ForEach-Object { $_.Matches.Groups[1].Value }) $VERSION_KEXT = (Get-Item "$binaryDir/portmaster-kext.sys").VersionInfo.FileVersion Write-Output "`n[i] VERSIONS INFO:" Write-Output " VERSION_GIT_TAG : $VERSION_GIT_TAG" Write-Output " VERSION_CORE : $VERSION_CORE" Write-Output " VERSION_UI : $VERSION_UI" Write-Output " VERSION_KEXT : $VERSION_KEXT" if ($VERSION_UI -ne $VERSION_CORE -or $VERSION_CORE -ne $VERSION_GIT_TAG) { Write-Warning "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" Write-Warning "Version mismatch between UI($VERSION_UI), Core($VERSION_CORE) and GitTag($VERSION_GIT_TAG)!" Write-Warning "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" if ($interactive) { $response = Read-Host "[?] Continue anyway? (y/n)" if ($response -ne 'y' -and $response -ne 'Y') { Write-Error "Cancelled. Version mismatch between UI and Core binaries." exit 1 } } } # Determine which version to use for building if ($version) { Write-Output "`n[i] Using explicitly provided version ($version) for installer file name`n" $VERSION_TO_USE = $version } else { Write-Output "`n[i] Using Core version version ($VERSION_CORE) for installer file name`n" $VERSION_TO_USE = $VERSION_CORE } Set-Location $destinationDir try { # Ensure Rust toolchain is installed if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) { Write-Output "[+] Installing rust toolchain..." Start-BitsTransfer -Source "https://win.rustup.rs/x86_64" -Destination "rustup.exe" ./rustup.exe install --no-self-update stable $env:PATH += ";C:\Users\ContainerAdministrator\.rustup\toolchains\stable-x86_64-pc-windows-msvc\bin\" } # Ensure Tauri CLI is available $cargoTauriCommand = "cargo-tauri.exe" if (-not (Get-Command $cargoTauriCommand -ErrorAction SilentlyContinue)) { if (-not (Test-Path "./tauri-cli/cargo-tauri.exe")) { Write-Output "[+] Tauri CLI not found. Downloading tauri-cli" Start-BitsTransfer -Source "https://github.com/tauri-apps/tauri/releases/download/tauri-cli-v2.2.7/cargo-tauri-x86_64-pc-windows-msvc.zip" -Destination "tauri-cli.zip" Expand-Archive -Force tauri-cli.zip } if (-not (Test-Path "./tauri-cli/cargo-tauri.exe")) { Write-Error "Tauri CLI not found. Download failed." exit 1 } $cargoTauriCommand = "./tauri-cli/cargo-tauri.exe" } Write-Output "[i] Tools versions info:" Write-Output " Tauri CLI: $((& $cargoTauriCommand -V | Out-String).Trim().Replace("`r`n", " "))" Write-Output " Rust : $((rustc -V | Out-String).Trim().Replace("`r`n", " ")); $((cargo -V | Out-String).Trim().Replace("`r`n", " "))" Write-Output "" # Building Tauri app bundle try { Write-Output "[+] Building Tauri app bundle with version $VERSION_TO_USE" Set-CargoVersion -Version $VERSION_TO_USE & $cargoTauriCommand bundle if ($LASTEXITCODE -ne 0) { throw "Tauri bundle command failed with exit code $LASTEXITCODE" } } catch { Write-Error "[!] Bundle failed: $_" exit 1 } finally { Restore-CargoVersion } Write-Output "[+] Copying generated bundles" $installerDist = "..\..\..\dist\windows_amd64\" if (-not (Test-Path -Path $installerDist)) { New-Item -ItemType Directory -Path $installerDist -ErrorAction Stop > $null } #Copy-Item -Path ".\target\release\bundle\msi\*" -Destination $installerDist -ErrorAction Stop Copy-Item -Path ".\target\release\bundle\nsis\*" -Destination $installerDist -ErrorAction Stop Write-Output "[i] Done." Write-Output " Installer files are available in: $(Resolve-Path $installerDist)" } catch { Write-Error "[!] Failed! Error: $_" exit 1 } finally { # Restore the original directory if not already done Set-Location $originalDirectory } ================================================ FILE: packaging/windows/sign_binaries_in_dist.ps1 ================================================ param ( [Parameter(Mandatory=$false)] [string]$certSha1, [Parameter(Mandatory=$false)] [string]$timestampServer = "http://timestamp.digicert.com" ) function Show-Help { Write-Host "Usage: sign_binaries_in_dist.ps1 -certSha1 [-timestampServer ]" Write-Host "" Write-Host "This script signs all binary files located under the '\dist\' directory recursively." Write-Host "Which should be done before creating the Portmaster installer." Write-Host "" Write-Host "Arguments:" Write-Host " -certSha1 The SHA1 hash of the certificate to use for signing (code signing certificate)." Write-Host " -timestampServer The timestamp server URL to use (optional). Default is http://timestamp.digicert.com." Write-Host "" Write-Host "Example:" Write-Host " .\sign_binaries_in_dist.ps1 -certSha1 ABCDEF1234567890ABCDEF1234567890ABCDEF12" } # Show help if no certificate SHA1 provided or help flag used if (-not $certSha1 -or ($args -contains "-h") -or ($args -contains "-help") -or ($args -contains "/h")) { Show-Help exit 0 } # Find signtool.exe - simplified approach function Find-SignTool { # First try the PATH $signtool = Get-Command signtool.exe -ErrorAction SilentlyContinue if ($signtool) { return $signtool } Write-Host "[+] signtool.exe not found in PATH. Searching in common locations..." # Common locations for signtool $commonLocations = @( # Windows SDK paths "${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x64\signtool.exe", "${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x86\signtool.exe", # Visual Studio paths via vswhere (& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.Component.MSBuild -find "**/signtool.exe" -ErrorAction SilentlyContinue) ) foreach ($location in $commonLocations) { $tools = Get-ChildItem -Path $location -ErrorAction SilentlyContinue | Sort-Object -Property FullName -Descending if ($tools -and $tools.Count -gt 0) { return $tools[0] # Return the first match } } return $null } function Get-SignatureInfo { param( [string]$filePath ) # Get the raw output from signtool $rawOutput = & $signtool verify /pa /v $filePath 2>&1 # Filter output to exclude everything after the timestamp line $filteredOutput = @() foreach ($line in $rawOutput) { if ($line -match "The signature is timestamped:") { break } $filteredOutput += $line } # Extract last subject in the signing chain - it's typically the last "Issued to:" entry $lastSubject = ($filteredOutput | Select-String -Pattern "Issued to: (.*)$" | Select-Object -Last 1 | ForEach-Object { $_.Matches.Groups[1].Value }) # Create signature info object $signInfo = @{ "IsSigned" = $LASTEXITCODE -eq 0 "Subject" = ($filteredOutput | Select-String -Pattern "Issued to: (.*)$" | ForEach-Object { $_.Matches.Groups[1].Value }) -join ", " "Issuer" = ($filteredOutput | Select-String -Pattern "Issued by: (.*)$" | ForEach-Object { $_.Matches.Groups[1].Value }) -join ", " "ExpirationDate" = ($filteredOutput | Select-String -Pattern "Expires: (.*)$" | ForEach-Object { $_.Matches.Groups[1].Value }) -join ", " "SubjectLast" = $lastSubject "SignedBySameCert" = $false } # Check if signed by our certificate $null = & $signtool verify /pa /sha1 $certSha1 $filePath 2>&1 $signInfo.SignedBySameCert = $LASTEXITCODE -eq 0 return $signInfo } # Find dist directory relative to script location $distDir = Join-Path $PSScriptRoot "../../dist" if (-not (Test-Path -Path $distDir)) { Write-Host "The directory '$distDir' does not exist." -ForegroundColor Red exit 1 } $distDir = Resolve-Path (Join-Path $PSScriptRoot "../../dist") # normalize path # Find signtool.exe $signtool = Find-SignTool if (-not $signtool) { Write-Host "signtool.exe not found in any standard location." -ForegroundColor Red Write-Host "Please install one of the following:" -ForegroundColor Yellow Write-Host "- Windows SDK" -ForegroundColor Yellow Write-Host "- Visual Studio with the 'Desktop development with C++' workload" -ForegroundColor Yellow Write-Host "- Visual Studio Build Tools with the 'Desktop development with C++' workload" -ForegroundColor Yellow exit 1 } Write-Host "[i] Using signtool: $($signtool)" # Sign all binary files in the dist directory try { # Define extensions for files that should be signed $binaryExtensions = @('.exe', '.dll', '.sys', '.msi') # Get all files with binary extensions $files = Get-ChildItem -Path $distDir -Recurse -File | Where-Object { $extension = [System.IO.Path]::GetExtension($_.Name).ToLower() $binaryExtensions -contains $extension } $totalFiles = $files.Count $signedFiles = 0 $alreadySignedFiles = 0 $wrongCertFiles = 0 $filesToSign = @() Write-Host "[+] Found $totalFiles binary files to process" -ForegroundColor Green foreach ($file in $files) { $relativeFileName = $file.FullName.Replace("$distDir\", "") # Get signature information $signInfo = Get-SignatureInfo -filePath $file.FullName if ($signInfo.IsSigned) { if ($signInfo.SignedBySameCert) { Write-Host -NoNewline " [signed OK ]" -ForegroundColor Green Write-Host -NoNewline " $($relativeFileName)" -ForegroundColor Blue Write-Host "`t: signed by our certificate" $alreadySignedFiles++ } else { Write-Host -NoNewline " [different ]" -ForegroundColor Yellow Write-Host -NoNewline " $($relativeFileName)" -ForegroundColor Blue Write-Host "`t: signed by different certificate [$($signInfo.SubjectLast)]" $wrongCertFiles++ } } else { Write-Host -NoNewline " [NOT signed]" -ForegroundColor Red Write-Host -NoNewline " $($relativeFileName)" -ForegroundColor Blue Write-Host "`t: not signed" $filesToSign += $file.FullName } } # Batch sign files if ($filesToSign.Count -gt 0) { Write-Host "`n[+] Signing $($filesToSign.Count) files in batch..." -ForegroundColor Green & $signtool sign /tr $timestampServer /td sha256 /fd sha256 /sha1 $certSha1 /v $filesToSign if ($LASTEXITCODE -ne 0) { Write-Host "Failed to sign files!" -ForegroundColor Red exit 1 } $signedFiles = $filesToSign.Count } else { Write-Host "`n[+] No files need signing." -ForegroundColor Green } Write-Host "`n[+] Summary:" -ForegroundColor Green Write-Host " - Total binary files found: $totalFiles" Write-Host " - Files already signed with our certificate: $alreadySignedFiles" Write-Host " - Files signed with different certificate: $wrongCertFiles" Write-Host " - Files newly signed: $signedFiles" } catch { Write-Host "An error occurred: $_" -ForegroundColor Red exit 1 } ================================================ FILE: runtime/.gitkeep ================================================ ================================================ FILE: service/broadcasts/api.go ================================================ package broadcasts import ( "encoding/json" "errors" "fmt" "net/http" "strings" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/accessor" "github.com/safing/portmaster/service/interop/ivpn" "github.com/safing/portmaster/service/resolver" ) func registerAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Path: `broadcasts/matching-data`, Read: api.PermitAdmin, StructFunc: handleMatchingData, Name: "Get Broadcast Notifications Matching Data", Description: "Returns the data used by the broadcast notifications to match the instance.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `broadcasts/reset-state`, Write: api.PermitAdmin, WriteMethod: http.MethodPost, ActionFunc: handleResetState, Name: "Reset Notification States", Description: "Deletes the cached state of broadcast and other notifications, causing them to appear again.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `broadcasts/simulate`, Write: api.PermitAdmin, WriteMethod: http.MethodPost, ActionFunc: handleSimulate, Name: "Simulate Broadcast Notifications", Description: "Test broadcast notifications by sending a valid source file in the body.", Parameters: []api.Parameter{ { Method: http.MethodPost, Field: "state", Value: "true", Description: "Check against state when deciding to display a broadcast notification. Acknowledgements are always saved.", }, }, }); err != nil { return err } return nil } func handleMatchingData(ar *api.Request) (i interface{}, err error) { return collectData(), nil } func handleResetState(ar *api.Request) (msg string, err error) { err = db.Delete(broadcastStatesDBKey) if err != nil && !errors.Is(err, database.ErrNotFound) { return "", err } _ = db.Delete(ivpn.Notification_DB_ID_IvpnDetectSuppressed) _ = db.Delete(resolver.Notification_DB_ID_StaleCacheSuppressed) return "Reset complete. Some notifications require a restart to reappear.", nil } func handleSimulate(ar *api.Request) (msg string, err error) { // Parse broadcast notification data. broadcasts, err := parseBroadcastSource(ar.InputData) if err != nil { return "", fmt.Errorf("failed to parse broadcast notifications update: %w", err) } // Get and marshal matching data. matchingData := collectData() matchingJSON, err := json.Marshal(matchingData) if err != nil { return "", fmt.Errorf("failed to marshal broadcast notifications matching data: %w", err) } matchingDataAccessor := accessor.NewJSONBytesAccessor(&matchingJSON) var bss *BroadcastStates if ar.URL.Query().Get("state") == "true" { // Get broadcast notification states. bss, err = getBroadcastStates() if err != nil { if !errors.Is(err, database.ErrNotFound) { return "", fmt.Errorf("failed to get broadcast notifications states: %w", err) } bss = newBroadcastStates() } } // Go through all broadcast nofications and check if they match. var results []string for _, bn := range broadcasts.Notifications { err := handleBroadcast(bn, matchingDataAccessor, bss) switch { case err == nil: results = append(results, fmt.Sprintf("%30s: displayed", bn.id)) case errors.Is(err, ErrSkip): results = append(results, fmt.Sprintf("%30s: %s", bn.id, err)) default: results = append(results, fmt.Sprintf("FAILED %23s: %s", bn.id, err)) } } return strings.Join(results, "\n"), nil } ================================================ FILE: service/broadcasts/data.go ================================================ package broadcasts import ( "strconv" "time" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/core" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/spn/access" "github.com/safing/portmaster/spn/access/account" "github.com/safing/portmaster/spn/captain" ) var portmasterStarted = time.Now() func collectData() interface{} { data := make(map[string]interface{}) // Get data about versions. versions := core.GetSimpleVersions() data["Updates"] = versions data["Version"] = versions.Build.Version numericVersion, err := MakeNumericVersion(versions.Build.Version) if err != nil { data["NumericVersion"] = &DataError{ Error: err, } } else { data["NumericVersion"] = numericVersion } // Get data about install. installInfo, err := GetInstallInfo() if err != nil { data["Install"] = &DataError{ Error: err, } } else { data["Install"] = installInfo } // Get global configuration. data["Config"] = config.GetActiveConfigValues() // Get data about device location. locs, ok := netenv.GetInternetLocation() if ok && locs.Best().LocationOrNil() != nil { loc := locs.Best() data["Location"] = &Location{ Country: loc.Location.Country.Code, Coordinates: loc.Location.Coordinates, ASN: loc.Location.AutonomousSystemNumber, ASOrg: loc.Location.AutonomousSystemOrganization, Source: loc.Source, SourceAccuracy: loc.SourceAccuracy, } } // Get data about SPN status. data["SPN"] = captain.GetSPNStatus() // Get data about account. userRecord, err := access.GetUser() if err != nil { data["Account"] = &DataError{ Error: err, } } else { account := &Account{ UserRecord: userRecord, Active: userRecord.MayUse(""), UpToDate: userRecord.Meta().Modified > time.Now().Add(-7*24*time.Hour).Unix(), } // Only add feature IDs when account is active. if account.Active { account.FeatureIDs = userRecord.CurrentPlan.FeatureIDs } data["Account"] = account } // Time running. data["UptimeHours"] = int(time.Since(portmasterStarted).Hours()) // Get current time and date. now := time.Now() data["Current"] = &Current{ UnixTime: now.Unix(), UTC: makeDateTimeInfo(now.UTC()), Local: makeDateTimeInfo(now), } return data } // Location holds location matching data. type Location struct { Country string Coordinates geoip.Coordinates ASN uint ASOrg string Source netenv.DeviceLocationSource SourceAccuracy int } // Account holds SPN account matching data. type Account struct { *access.UserRecord Active bool UpToDate bool FeatureIDs []account.FeatureID } // DataError represents an error getting some matching data. type DataError struct { Error error } // Current holds current date and time data. type Current struct { UnixTime int64 UTC *DateTime Local *DateTime } // DateTime holds date and time data in different formats. type DateTime struct { NumericDateTime int64 NumericDate int64 NumericTime int64 } func makeDateTimeInfo(t time.Time) *DateTime { info := &DateTime{} info.NumericDateTime, _ = strconv.ParseInt(t.Format("20060102150405"), 10, 64) info.NumericDate, _ = strconv.ParseInt(t.Format("20060102"), 10, 64) info.NumericTime, _ = strconv.ParseInt(t.Format("150405"), 10, 64) return info } ================================================ FILE: service/broadcasts/install_info.go ================================================ package broadcasts import ( "errors" "fmt" "strconv" "strings" "sync" "time" semver "github.com/hashicorp/go-version" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/info" "github.com/safing/portmaster/base/log" ) const installInfoDBKey = "core:status/install-info" // InstallInfo holds generic info about the install. type InstallInfo struct { record.Base sync.Mutex Version string NumericVersion int64 Time time.Time NumericDate int64 DaysSinceInstall int64 UnixTimestamp int64 } // GetInstallInfo returns the install info from the database. func GetInstallInfo() (*InstallInfo, error) { r, err := db.Get(installInfoDBKey) if err != nil { return nil, err } // Unwrap. if r.IsWrapped() { // Only allocate a new struct, if we need it. newRecord := &InstallInfo{} err = record.Unwrap(r, newRecord) if err != nil { return nil, err } return newRecord, nil } // or adjust type newRecord, ok := r.(*InstallInfo) if !ok { return nil, fmt.Errorf("record not of type *InstallInfo, but %T", r) } return newRecord, nil } func ensureInstallInfo() { // Get current install info from database. installInfo, err := GetInstallInfo() if err != nil { installInfo = &InstallInfo{} if !errors.Is(err, database.ErrNotFound) { log.Warningf("updates: failed to load install info: %s", err) } } // Fill in missing data and save. installInfo.checkAll() if err := installInfo.save(); err != nil { log.Warningf("updates: failed to save install info: %s", err) } } func (ii *InstallInfo) save() error { if !ii.KeyIsSet() { ii.SetKey(installInfoDBKey) } return db.Put(ii) } func (ii *InstallInfo) checkAll() { ii.checkVersion() ii.checkInstallDate() } func (ii *InstallInfo) checkVersion() { // Check if everything is present. if ii.Version != "" && ii.NumericVersion > 0 { return } // Update version information. versionInfo := info.GetInfo() ii.Version = versionInfo.Version // Update numeric version. if versionInfo.Version != "" { numericVersion, err := MakeNumericVersion(versionInfo.Version) if err != nil { log.Warningf("updates: failed to make numeric version: %s", err) } else { ii.NumericVersion = numericVersion } } } // MakeNumericVersion makes a numeric version with the first three version // segment always using three digits. func MakeNumericVersion(version string) (numericVersion int64, err error) { // Remove any comments. version = strings.SplitN(version, " ", 2)[0] // Parse version string. ver, err := semver.NewVersion(version) if err != nil { return 0, fmt.Errorf("failed to parse core version: %w", err) } // Transform version for numeric representation. segments := ver.Segments() for i := 0; i < 3 && i < len(segments); i++ { segmentNumber := int64(segments[i]) if segmentNumber > 999 { segmentNumber = 999 } switch i { case 0: numericVersion += segmentNumber * 1000000 case 1: numericVersion += segmentNumber * 1000 case 2: numericVersion += segmentNumber } } return numericVersion, nil } func (ii *InstallInfo) checkInstallDate() { // Check if everything is present. if ii.UnixTimestamp > 0 && ii.NumericDate > 0 && ii.DaysSinceInstall > 0 && !ii.Time.IsZero() { return } // Find oldest created database entry and use it as install time. oldest := time.Now().Unix() it, err := db.Query(query.New("core")) if err != nil { log.Warningf("updates: failed to create iterator for searching DB for install time: %s", err) return } defer it.Cancel() for r := range it.Next { if oldest > r.Meta().Created { oldest = r.Meta().Created } } // Set data. ii.UnixTimestamp = oldest ii.Time = time.Unix(oldest, 0) ii.DaysSinceInstall = int64(time.Since(ii.Time).Hours()) / 24 // Transform date for numeric representation. numericDate, err := strconv.ParseInt(ii.Time.Format("20060102"), 10, 64) if err != nil { log.Warningf("updates: failed to make numeric date from %s: %s", ii.Time, err) } else { ii.NumericDate = numericDate } } ================================================ FILE: service/broadcasts/module.go ================================================ package broadcasts import ( "errors" "sync" "sync/atomic" "time" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/updates" ) type Broadcasts struct { mgr *mgr.Manager instance instance states *mgr.StateMgr } func (b *Broadcasts) Manager() *mgr.Manager { return b.mgr } func (b *Broadcasts) Start() error { return start() } func (b *Broadcasts) Stop() error { return nil } func (b *Broadcasts) States() *mgr.StateMgr { return b.states } var ( db = database.NewInterface(&database.Options{ Local: true, Internal: true, }) startOnce sync.Once ) func prep() error { // Register API endpoints. if err := registerAPIEndpoints(); err != nil { return err } return nil } func start() error { // Ensure the install info is up to date. ensureInstallInfo() // Start broadcast notifier task. startOnce.Do(func() { module.mgr.Repeat("broadcast notifier", 10*time.Minute, broadcastNotify) }) return nil } var ( module *Broadcasts shimLoaded atomic.Bool ) // New returns a new Config module. func New(instance instance) (*Broadcasts, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Broadcasts") module = &Broadcasts{ mgr: m, instance: instance, states: m.NewStateMgr(), } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface { IntelUpdates() *updates.Updater } ================================================ FILE: service/broadcasts/notify.go ================================================ package broadcasts import ( "context" "encoding/json" "errors" "fmt" "os" "strings" "sync" "time" "github.com/ghodss/yaml" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/accessor" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/mgr" ) const ( broadcastsResourceName = "notifications.yaml" broadcastNotificationIDPrefix = "broadcasts:" minRepeatDuration = 1 * time.Hour ) // Errors. var ( ErrSkip = errors.New("broadcast skipped") ErrSkipDoesNotMatch = fmt.Errorf("%w: does not match", ErrSkip) ErrSkipAlreadyActive = fmt.Errorf("%w: already active", ErrSkip) ErrSkipAlreadyShown = fmt.Errorf("%w: already shown", ErrSkip) ErrSkipRemovedByMismatch = fmt.Errorf("%w: removed due to mismatch", ErrSkip) ErrSkipRemovedBySource = fmt.Errorf("%w: removed by source", ErrSkip) ) // BroadcastNotifications holds the data structure of the broadcast // notifications update file. type BroadcastNotifications struct { Notifications map[string]*BroadcastNotification } // BroadcastNotification is a single broadcast notification. type BroadcastNotification struct { *notifications.Notification id string // Match holds a query string that needs to match the local matching data in // order for the broadcast to be displayed. Match string matchingQuery *query.Query // AttachToModule signifies if the broadcast notification should be attached to the module. AttachToModule bool // Remove signifies that the broadcast should be canceled and its state removed. Remove bool // Permanent signifies that the broadcast cannot be acknowledge by the user // and remains in the UI indefinitely. Permanent bool // Repeat specifies a duration after which the broadcast should be shown again. Repeat string repeatDuration time.Duration } func broadcastNotify(ctx *mgr.WorkerCtx) error { // Get broadcast notifications file, load it from disk and parse it. broadcastsResource, err := module.instance.IntelUpdates().GetFile(broadcastsResourceName) if err != nil { return fmt.Errorf("failed to get broadcast notifications update: %w", err) } broadcastsData, err := os.ReadFile(broadcastsResource.Path()) if err != nil { return fmt.Errorf("failed to load broadcast notifications update: %w", err) } broadcasts, err := parseBroadcastSource(broadcastsData) if err != nil { return fmt.Errorf("failed to parse broadcast notifications update: %w", err) } // Get and marshal matching data. matchingData := collectData() matchingJSON, err := json.Marshal(matchingData) if err != nil { return fmt.Errorf("failed to marshal broadcast notifications matching data: %w", err) } matchingDataAccessor := accessor.NewJSONBytesAccessor(&matchingJSON) // Get broadcast notification states. bss, err := getBroadcastStates() if err != nil { if !errors.Is(err, database.ErrNotFound) { return fmt.Errorf("failed to get broadcast notifications states: %w", err) } bss = newBroadcastStates() } // Go through all broadcast nofications and check if they match. for _, bn := range broadcasts.Notifications { err := handleBroadcast(bn, matchingDataAccessor, bss) switch { case err == nil: log.Infof("broadcasts: displaying broadcast %s", bn.id) case errors.Is(err, ErrSkip): log.Tracef("broadcasts: skipped displaying broadcast %s: %s", bn.id, err) default: log.Warningf("broadcasts: failed to handle broadcast %s: %s", bn.id, err) } } return nil } func parseBroadcastSource(yamlData []byte) (*BroadcastNotifications, error) { // Parse data. broadcasts := &BroadcastNotifications{} err := yaml.Unmarshal(yamlData, broadcasts) if err != nil { return nil, err } // Add IDs to struct for easier handling. for id, bn := range broadcasts.Notifications { bn.id = id // Parse matching query. if bn.Match != "" { q, err := query.ParseQuery("query / where " + bn.Match) if err != nil { return nil, fmt.Errorf("failed to parse query of broadcast notification %s: %w", bn.id, err) } bn.matchingQuery = q } // Parse the repeat duration. if bn.Repeat != "" { duration, err := time.ParseDuration(bn.Repeat) if err != nil { return nil, fmt.Errorf("failed to parse repeat duration of broadcast notification %s: %w", bn.id, err) } bn.repeatDuration = duration // Raise duration to minimum. if bn.repeatDuration < minRepeatDuration { bn.repeatDuration = minRepeatDuration } } } return broadcasts, nil } func handleBroadcast(bn *BroadcastNotification, matchingDataAccessor accessor.Accessor, bss *BroadcastStates) error { // Check if broadcast was already shown. if bss != nil { state, ok := bss.States[bn.id] switch { case !ok || state.Read.IsZero(): // Was never shown, continue. case bn.repeatDuration == 0: // Was already shown and is not repeated, skip. return ErrSkipAlreadyShown case time.Now().Before(state.Read.Add(bn.repeatDuration)): // Was already shown and should be repeated - but not yet, skip. return ErrSkipAlreadyShown } } // Check if broadcast should be removed. if bn.Remove { removeBroadcast(bn, bss) return ErrSkipRemovedBySource } // Skip if broadcast does not match. if bn.matchingQuery != nil && !bn.matchingQuery.MatchesAccessor(matchingDataAccessor) { removed := removeBroadcast(bn, bss) if removed { return ErrSkipRemovedByMismatch } return ErrSkipDoesNotMatch } // Check if there is already an active notification for this. eventID := broadcastNotificationIDPrefix + bn.id n := notifications.Get(eventID) if n != nil { // Already active! return ErrSkipAlreadyActive } // Prepare notification for displaying. n = bn.Notification n.EventID = eventID n.GUID = "" n.State = "" n.SelectedActionID = "" // It is okay to edit the notification, as they are loaded from the file every time. // Add dismiss button if the notification is not permanent. if !bn.Permanent { n.AvailableActions = append(n.AvailableActions, ¬ifications.Action{ ID: "ack", Text: "Got it!", }) } n.SetActionFunction(markBroadcastAsRead) // Display notification. n.Save() if bn.AttachToModule { n.SyncWithState(module.states) } return nil } func removeBroadcast(bn *BroadcastNotification, bss *BroadcastStates) (removed bool) { // Remove any active notification. n := notifications.Get(broadcastNotificationIDPrefix + bn.id) if n != nil { removed = true n.Delete() } // Remove any state. if bss != nil { delete(bss.States, bn.id) } return } var savingBroadcastStateLock sync.Mutex func markBroadcastAsRead(ctx context.Context, n *notifications.Notification) error { // Lock persisting broadcast state. savingBroadcastStateLock.Lock() defer savingBroadcastStateLock.Unlock() // Get notification data. var broadcastID, actionID string func() { n.Lock() defer n.Unlock() broadcastID = strings.TrimPrefix(n.EventID, broadcastNotificationIDPrefix) actionID = n.SelectedActionID }() // Check response. switch actionID { case "ack": case "": return fmt.Errorf("no action ID for %s", broadcastID) default: return fmt.Errorf("unexpected action ID for %s: %s", broadcastID, actionID) } // Get broadcast notification states. bss, err := getBroadcastStates() if err != nil { if !errors.Is(err, database.ErrNotFound) { return fmt.Errorf("failed to get broadcast notifications states: %w", err) } bss = newBroadcastStates() } // Get state for this notification. bs, ok := bss.States[broadcastID] if !ok { bs = &BroadcastState{} bss.States[broadcastID] = bs } // Delete to allow for timely repeats. n.Delete() // Mark as read and save to DB. log.Infof("broadcasts: user acknowledged broadcast %s", broadcastID) bs.Read = time.Now() return bss.save() } ================================================ FILE: service/broadcasts/state.go ================================================ package broadcasts import ( "fmt" "sync" "time" "github.com/safing/portmaster/base/database/record" ) const broadcastStatesDBKey = "core:broadcasts/state" // BroadcastStates holds states for broadcast notifications. type BroadcastStates struct { record.Base sync.Mutex States map[string]*BroadcastState } // BroadcastState holds state for a single broadcast notifications. type BroadcastState struct { Read time.Time } func (bss *BroadcastStates) save() error { return db.Put(bss) } // getbroadcastStates returns the broadcast states from the database. func getBroadcastStates() (*BroadcastStates, error) { r, err := db.Get(broadcastStatesDBKey) if err != nil { return nil, err } // Unwrap. if r.IsWrapped() { // Only allocate a new struct, if we need it. newRecord := &BroadcastStates{} err = record.Unwrap(r, newRecord) if err != nil { return nil, err } return newRecord, nil } // or adjust type newRecord, ok := r.(*BroadcastStates) if !ok { return nil, fmt.Errorf("record not of type *BroadcastStates, but %T", r) } return newRecord, nil } // newBroadcastStates returns a new BroadcastStates. func newBroadcastStates() *BroadcastStates { bss := &BroadcastStates{ States: make(map[string]*BroadcastState), } bss.SetKey(broadcastStatesDBKey) return bss } ================================================ FILE: service/compat/api.go ================================================ package compat import ( "github.com/safing/portmaster/base/api" ) func registerAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Path: "compat/self-check", Read: api.PermitUser, ActionFunc: selfcheckViaAPI, Name: "Run Integration Self-Check", Description: "Runs a couple integration self-checks in order to see if the system integration works.", }); err != nil { return err } return nil } func selfcheckViaAPI(ar *api.Request) (msg string, err error) { _, err = selfcheck(ar.Context()) if err != nil { return "", err } return "self-check successful", nil } ================================================ FILE: service/compat/callbacks.go ================================================ package compat import ( "net" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/process" ) // SubmitSystemIntegrationCheckPacket submit a packet for the system integrity check. func SubmitSystemIntegrationCheckPacket(p packet.Packet) { select { case systemIntegrationCheckPackets <- p: default: } } // SubmitDNSCheckDomain submits a subdomain for the dns check. func SubmitDNSCheckDomain(subdomain string) (respondWith net.IP) { // Submit queried domain. select { case dnsCheckReceivedDomain <- subdomain: default: } // Return the answer. dnsCheckAnswerLock.Lock() defer dnsCheckAnswerLock.Unlock() return dnsCheckAnswer } // ReportSecureDNSBypassIssue reports a DNS bypassing issue for the given process. func ReportSecureDNSBypassIssue(p *process.Process) { module.mgr.Go("report secure dns bypass issue", func(w *mgr.WorkerCtx) error { secureDNSBypassIssue.notify(p) return nil }) } // ReportMultiPeerUDPTunnelIssue reports a multi-peer UDP tunnel for the given process. func ReportMultiPeerUDPTunnelIssue(p *process.Process) { module.mgr.Go("report multi-peer udp tunnel issue", func(w *mgr.WorkerCtx) error { multiPeerUDPTunnelIssue.notify(p) return nil }) } ================================================ FILE: service/compat/debug_default.go ================================================ //go:build !windows && !linux package compat import "github.com/safing/portmaster/base/utils/debug" // AddToDebugInfo adds compatibility data to the given debug.Info. func AddToDebugInfo(di *debug.Info) { // Not yet implemented on this platform. } ================================================ FILE: service/compat/debug_linux.go ================================================ package compat import ( "fmt" "github.com/safing/portmaster/base/utils/debug" ) // AddToDebugInfo adds compatibility data to the given debug.Info. func AddToDebugInfo(di *debug.Info) { // Get iptables state and add error info if it fails. chains, err := GetIPTablesChains() if err != nil { di.AddSection( "Compatibility: IPTables Chains (failed)", debug.UseCodeSection, err.Error(), ) return } // Add data as section. di.AddSection( fmt.Sprintf("Compatibility: IPTables Chains (%d)", len(chains)-10), debug.UseCodeSection|debug.AddContentLineBreaks, chains..., ) } ================================================ FILE: service/compat/debug_windows.go ================================================ package compat import ( "fmt" "strings" "github.com/safing/portmaster/base/utils/debug" ) // AddToDebugInfo adds compatibility data to the given debug.Info. func AddToDebugInfo(di *debug.Info) { // Get WFP state and add error info if it fails. wfp, err := GetWFPState() if err != nil { di.AddSection( "Compatibility: WFP State (failed)", debug.UseCodeSection, err.Error(), ) return } // Add data as section. wfpTable := wfp.AsTable() di.AddSection( fmt.Sprintf("Compatibility: WFP State (%d)", strings.Count(wfpTable, "\n")), debug.UseCodeSection, wfpTable, ) } ================================================ FILE: service/compat/iptables.go ================================================ //go:build linux package compat import ( "fmt" "github.com/coreos/go-iptables/iptables" ) var ( iptProtocols = []iptables.Protocol{ iptables.ProtocolIPv4, iptables.ProtocolIPv6, } iptTables = []string{ "filter", "nat", "mangle", "raw", } ) // GetIPTablesChains returns the chain names currently in ip(6)tables. func GetIPTablesChains() ([]string, error) { chains := make([]string, 0, 100) // Iterate over protocols. for _, protocol := range iptProtocols { if protocol == iptables.ProtocolIPv4 { chains = append(chains, "v4") } else { chains = append(chains, "v6") } // Get iptables access for protocol. tbls, err := iptables.NewWithProtocol(protocol) if err != nil { return nil, err } // Iterate over tables. for _, table := range iptTables { chains = append(chains, " "+table) // Get chain names chainNames, err := tbls.ListChains(table) if err != nil { return nil, fmt.Errorf("failed to get chains of table %s: %w", table, err) } // Add chain names to list. for _, name := range chainNames { chains = append(chains, " "+name) } } } return chains, nil } ================================================ FILE: service/compat/iptables_test.go ================================================ //go:build linux package compat import ( "testing" ) func TestIPTablesChains(t *testing.T) { // Skip in CI. if testing.Short() { t.Skip() } t.Parallel() chain, err := GetIPTablesChains() if err != nil { t.Fatal(err) } if len(chain) < 35 { t.Errorf("Expected at least 35 output lines, not %d", len(chain)) } } ================================================ FILE: service/compat/module.go ================================================ package compat import ( "errors" "sync/atomic" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/resolver" ) // Compat is the compatibility check module. type Compat struct { mgr *mgr.Manager instance instance selfcheckWorkerMgr *mgr.WorkerMgr cleanNotifyThresholdWorkerMgr *mgr.WorkerMgr states *mgr.StateMgr } // Manager returns the module manager. func (u *Compat) Manager() *mgr.Manager { return u.mgr } // States returns the module state manager. func (u *Compat) States() *mgr.StateMgr { return u.states } // Start starts the module. func (u *Compat) Start() error { return start() } // Stop stops the module. func (u *Compat) Stop() error { return stop() } var ( selfcheckTaskRetryAfter = 15 * time.Second // selfCheckIsFailing holds whether or not the self-check is currently // failing. This helps other failure systems to not make noise when there is // an underlying failure. selfCheckIsFailing = abool.New() // selfcheckFails counts how often the self check failed successively. // selfcheckFails is not locked as it is only accessed by the self-check task. selfcheckFails int // selfcheckNetworkChangedFlag is used to track changed to the network for // the self-check. selfcheckNetworkChangedFlag = netenv.GetNetworkChangedFlag() ) // selfcheckFailThreshold holds the threshold of how many times the selfcheck // must fail before it is reported. const selfcheckFailThreshold = 10 func init() { // Workaround resolver integration. // See resolver/compat.go for details. resolver.CompatDNSCheckInternalDomainScope = DNSCheckInternalDomainScope resolver.CompatSelfCheckIsFailing = SelfCheckIsFailing resolver.CompatSubmitDNSCheckDomain = SubmitDNSCheckDomain } func prep() error { return registerAPIEndpoints() } func start() error { // Create fresh worker managers on Start() to enable clean restarts after Stop(). // (after module.Manager().Stop() has been called, all workers are stopped and cannot be reused) module.selfcheckWorkerMgr = module.Manager().NewWorkerMgr("compatibility self-check", selfcheckTaskFunc, nil) module.cleanNotifyThresholdWorkerMgr = module.Manager().NewWorkerMgr("clean notify thresholds", cleanNotifyThreshold, nil) startNotify() selfcheckNetworkChangedFlag.Refresh() // Schedule periodic self-checks. module.selfcheckWorkerMgr.Repeat(5 * time.Minute).Delay(selfcheckTaskRetryAfter) module.cleanNotifyThresholdWorkerMgr.Repeat(1 * time.Hour) // Add network change callback to trigger immediate self-check. module.instance.NetEnv().EventNetworkChange.AddCallback("trigger compat self-check", func(_ *mgr.WorkerCtx, _ struct{}) (bool, error) { module.selfcheckWorkerMgr.Delay(selfcheckTaskRetryAfter) return false, nil }) return nil } func stop() error { resetSelfCheckState() return nil } func selfcheckTaskFunc(wc *mgr.WorkerCtx) error { // Create tracing logger. ctx, tracer := log.AddTracer(wc.Ctx()) defer tracer.Submit() tracer.Tracef("compat: running self-check") // Run selfcheck and return if successful. issue, err := selfcheck(ctx) switch { case err == nil: // Successful. tracer.Debugf("compat: self-check successful") case errors.Is(err, errSelfcheckSkipped): // Skipped. tracer.Debugf("compat: %s", err) case issue == nil: // Internal error. tracer.Warningf("compat: %s", err) case selfcheckNetworkChangedFlag.IsSet(): // The network changed, ignore the issue. default: // The self-check failed. // Set state and increase counter. selfCheckIsFailing.Set() selfcheckFails++ // Log and notify. tracer.Errorf("compat: %s", err) if selfcheckFails >= selfcheckFailThreshold { issue.notify(err) } // Retry quicker when failed. module.selfcheckWorkerMgr.Delay(selfcheckTaskRetryAfter) return nil } // Reset self-check state. resetSelfCheckState() return nil } func resetSelfCheckState() { selfcheckNetworkChangedFlag.Refresh() selfCheckIsFailing.UnSet() selfcheckFails = 0 resetSystemIssue() } // SelfCheckIsFailing returns whether the self check is currently failing. // This returns true after the first check fails, and does not wait for the // failing threshold to be met. func SelfCheckIsFailing() bool { return selfCheckIsFailing.IsSet() } var ( module *Compat shimLoaded atomic.Bool ) // New returns a new Compat module. func New(instance instance) (*Compat, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Compat") module = &Compat{ mgr: m, instance: instance, states: mgr.NewStateMgr(m), } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface { NetEnv() *netenv.NetEnv Resolver() *resolver.ResolverModule } ================================================ FILE: service/compat/notify.go ================================================ package compat import ( "fmt" "net" "strings" "sync" "time" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/profile" ) type baseIssue struct { id string //nolint:structcheck // Inherited. title string //nolint:structcheck // Inherited. message string //nolint:structcheck // Inherited. level notifications.Type //nolint:structcheck // Inherited. actions []*notifications.Action //nolint:structcheck // Inherited. } type systemIssue baseIssue type appIssue baseIssue var ( // Copy of firewall.CfgOptionDNSQueryInterceptionKey. cfgOptionDNSQueryInterceptionKey = "filter/dnsQueryInterception" dnsQueryInterception config.BoolOption systemIssueNotification *notifications.Notification systemIssueNotificationLock sync.Mutex systemIntegrationIssue = &systemIssue{ id: "compat:system-integration-issue", title: "Detected System Integration Issue", message: "Portmaster detected a problem with its system integration. You can try to restart or reinstall the Portmaster. If that does not help, [get support here](https://safing.io/support/).", level: notifications.Error, } systemCompatibilityIssue = &systemIssue{ id: "compat:compatibility-issue", title: "Detected Compatibility Issue", message: "Portmaster detected that something is interfering with its operation. This could be a VPN, an Anti-Virus or another network protection software. Please check if you are running an incompatible [VPN client](https://docs.safing.io/portmaster/install/status/vpn-compatibility) or [software](https://docs.safing.io/portmaster/install/status/software-compatibility) and disable it. If that does not help, [get support here](https://safing.io/support/).", level: notifications.Error, } // manualDNSSetupRequired is additionally initialized in startNotify(). manualDNSSetupRequired = &systemIssue{ id: "compat:manual-dns-setup-required", title: "Manual DNS Setup Required", level: notifications.Error, actions: []*notifications.Action{ { Text: "Revert", Type: notifications.ActionTypeOpenSetting, Payload: ¬ifications.ActionTypeOpenSettingPayload{ Key: cfgOptionDNSQueryInterceptionKey, }, }, }, } manualDNSSetupRequiredMessage = "You have disabled Seamless DNS Integration. As a result, Portmaster can no longer protect you or filter connections reliably. To fix this, you have to manually configure %s as the DNS Server in your system and in any conflicting application. This message will disappear some time after correct configuration." secureDNSBypassIssue = &appIssue{ id: "compat:secure-dns-bypass-%s", title: "Blocked Bypass Attempt by %s", message: `[APPNAME] is using its own Secure DNS resolver, which would bypass Portmaster's firewall protections. If [APPNAME] experiences problems, disable Secure DNS within [APPNAME] to restore functionality. Rest assured that Portmaster handles Secure DNS for your whole device, including [APPNAME].`, // TODO: Add this when the new docs page is finished: // , or [find out about other options](link to new docs page) level: notifications.Warning, } multiPeerUDPTunnelIssue = &appIssue{ id: "compat:multi-peer-udp-tunnel-%s", title: "Detected SPN Incompatibility in %s", message: "Portmaster detected that [APPNAME] is trying to connect to multiple servers via the SPN using a single UDP connection. This is common for technologies such as torrents. Unfortunately, the SPN does not support this feature currently. You can try to change this behavior within the affected app or you could exempt it from using the SPN.", level: notifications.Warning, } ) func startNotify() { dnsQueryInterception = config.Concurrent.GetAsBool(cfgOptionDNSQueryInterceptionKey, true) systemIssueNotificationLock.Lock() defer systemIssueNotificationLock.Unlock() manualDNSSetupRequired.message = fmt.Sprintf( manualDNSSetupRequiredMessage, `"127.0.0.1"`, ) } // SetNameserverListenIP sets the IP address the nameserver is listening on. // The IP address is used in compatibility notifications. func SetNameserverListenIP(ip net.IP) { systemIssueNotificationLock.Lock() defer systemIssueNotificationLock.Unlock() manualDNSSetupRequired.message = fmt.Sprintf( manualDNSSetupRequiredMessage, `"`+ip.String()+`"`, ) } func systemCompatOrManualDNSIssue() *systemIssue { if dnsQueryInterception() { return systemCompatibilityIssue } return manualDNSSetupRequired } func (issue *systemIssue) notify(err error) { //nolint // TODO: Should we use the error? systemIssueNotificationLock.Lock() defer systemIssueNotificationLock.Unlock() if systemIssueNotification != nil { // Ignore duplicate notification. if issue.id == systemIssueNotification.EventID { return } // Remove old notification. systemIssueNotification.Delete() } // Create new notification. n := ¬ifications.Notification{ EventID: issue.id, Type: issue.level, Title: issue.title, Message: issue.message, ShowOnSystem: true, AvailableActions: issue.actions, } notifications.Notify(n) systemIssueNotification = n n.SyncWithState(module.states) } func resetSystemIssue() { systemIssueNotificationLock.Lock() defer systemIssueNotificationLock.Unlock() if systemIssueNotification != nil { systemIssueNotification.Delete() } systemIssueNotification = nil } func (issue *appIssue) notify(proc *process.Process) { // Get profile from process. p := proc.Profile().LocalProfile() if p == nil { return } // Ignore notifications for unidentified processes. if p.ID == profile.UnidentifiedProfileID { return } // Log warning. log.Warningf( "compat: detected %s issue with %s", strings.ReplaceAll( strings.TrimPrefix( strings.TrimSuffix(issue.id, "-%s"), "compat:", ), "-", " ", ), proc.Path, ) // Check if we already have this notification. eventID := fmt.Sprintf(issue.id, p.ID) n := notifications.Get(eventID) if n != nil { return } // Check if we reach the threshold to actually send a notification. if !isOverThreshold(eventID) { return } // Build message. message := strings.ReplaceAll(issue.message, "[APPNAME]", p.Name) // Create a new notification. n = ¬ifications.Notification{ EventID: eventID, Type: issue.level, Title: fmt.Sprintf(issue.title, p.Name), Message: message, ShowOnSystem: true, AvailableActions: issue.actions, } if len(n.AvailableActions) == 0 { n.AvailableActions = []*notifications.Action{ { ID: "ack", Text: "OK", }, } } notifications.Notify(n) // Set warning on profile. module.mgr.Go("set app compat warning", func(ctx *mgr.WorkerCtx) error { var changed bool func() { p.Lock() defer p.Unlock() if p.Warning != message || time.Now().Add(-1*time.Hour).After(p.WarningLastUpdated) { p.Warning = message p.WarningLastUpdated = time.Now() changed = true } }() if changed { return p.Save() } return nil }) } const ( notifyThresholdMinIncidents = 10 notifyThresholdResetAfter = 2 * time.Minute ) var ( notifyThresholds = make(map[string]*notifyThreshold) notifyThresholdsLock sync.Mutex ) type notifyThreshold struct { FirstSeen time.Time Incidents uint } func (nt *notifyThreshold) expired() bool { return time.Now().Add(-notifyThresholdResetAfter).After(nt.FirstSeen) } func isOverThreshold(id string) bool { notifyThresholdsLock.Lock() defer notifyThresholdsLock.Unlock() // Get notify threshold and check if we reach the minimum incidents. nt, ok := notifyThresholds[id] if ok && !nt.expired() { nt.Incidents++ return nt.Incidents >= notifyThresholdMinIncidents } // Add new entry. notifyThresholds[id] = ¬ifyThreshold{ FirstSeen: time.Now(), Incidents: 1, } return false } func cleanNotifyThreshold(ctx *mgr.WorkerCtx) error { notifyThresholdsLock.Lock() defer notifyThresholdsLock.Unlock() for id, nt := range notifyThresholds { if nt.expired() { delete(notifyThresholds, id) } } return nil } ================================================ FILE: service/compat/selfcheck.go ================================================ package compat import ( "context" "encoding/hex" "errors" "fmt" "net" "strings" "sync" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/resolver" ) var ( selfcheckLock sync.Mutex // SystemIntegrationCheckDstIP is the IP address to send a packet to for the // system integration test. SystemIntegrationCheckDstIP = net.IPv4(127, 65, 67, 75) // SystemIntegrationCheckProtocol is the IP protocol to use for the system // integration test. SystemIntegrationCheckProtocol = packet.AnyHostInternalProtocol61 systemIntegrationCheckDialNet = fmt.Sprintf("ip4:%d", uint8(SystemIntegrationCheckProtocol)) systemIntegrationCheckDialIP = SystemIntegrationCheckDstIP.String() systemIntegrationCheckPackets = make(chan packet.Packet, 1) systemIntegrationCheckWaitDuration = 45 * time.Second // DNSCheckInternalDomainScope is the domain scope to use for dns checks. DNSCheckInternalDomainScope = ".self-check." + resolver.InternalSpecialUseDomain dnsCheckReceivedDomain = make(chan string, 1) dnsCheckWaitDuration = 45 * time.Second dnsCheckAnswerLock sync.Mutex dnsCheckAnswer net.IP errSelfcheckSkipped = errors.New("self-check skipped") ) func selfcheck(ctx context.Context) (issue *systemIssue, err error) { selfcheckLock.Lock() defer selfcheckLock.Unlock() // Step 0: Check if self-check makes sense. if !netenv.Online() { return nil, fmt.Errorf("%w: device is offline or in limited network", errSelfcheckSkipped) } // Step 1: Check if the system integration sees a packet. // Empty recv channel. select { case <-systemIntegrationCheckPackets: case <-ctx.Done(): return nil, context.Canceled default: } // Send packet. conn, err := net.DialTimeout( systemIntegrationCheckDialNet, systemIntegrationCheckDialIP, time.Second, ) if err != nil { return nil, fmt.Errorf("failed to create system integration conn: %w", err) } _, err = conn.Write([]byte("PORTMASTER SELF CHECK")) if err != nil { return nil, fmt.Errorf("failed to send system integration packet: %w", err) } // Wait for packet. select { case <-systemIntegrationCheckPackets: // Check passed! log.Tracer(ctx).Tracef("compat: self-check #1: system integration check passed") case <-time.After(systemIntegrationCheckWaitDuration): return systemIntegrationIssue, fmt.Errorf("self-check #1: system integration check failed: did not receive test packet after %s", systemIntegrationCheckWaitDuration) case <-ctx.Done(): return nil, context.Canceled } // Step 2: Check if a DNS request arrives at the nameserver // This step necessary also includes some setup for step 3. // Generate random subdomain. randomSubdomainBytes, err := rng.Bytes(16) if err != nil { return nil, fmt.Errorf("self-check #2: failed to get random bytes for subdomain check: %w", err) } randomSubdomain := "a" + strings.ToLower(hex.EncodeToString(randomSubdomainBytes)) + "b" // Generate random answer. var B, C, D uint64 B, err = rng.Number(255) if err == nil { C, err = rng.Number(255) } if err == nil { D, err = rng.Number(255) } if err != nil { return nil, fmt.Errorf("self-check #2: failed to get random number for subdomain check response: %w", err) } randomAnswer := net.IPv4(127, byte(B), byte(C), byte(D)) func() { dnsCheckAnswerLock.Lock() defer dnsCheckAnswerLock.Unlock() dnsCheckAnswer = randomAnswer }() // Setup variables for lookup worker. var ( dnsCheckReturnedIP net.IP dnsCheckLookupError = make(chan error) ) // Empty recv channel. select { case <-dnsCheckReceivedDomain: case <-ctx.Done(): return nil, context.Canceled default: } // Start worker for the DNS lookup. module.mgr.Go("dns check lookup", func(_ *mgr.WorkerCtx) error { ips, err := net.LookupIP(randomSubdomain + DNSCheckInternalDomainScope) if err == nil && len(ips) > 0 { dnsCheckReturnedIP = ips[0] } select { case dnsCheckLookupError <- err: case <-time.After(dnsCheckWaitDuration * 2): case <-ctx.Done(): } return nil }) // Wait for the resolver to receive the query. select { case receivedTestDomain := <-dnsCheckReceivedDomain: if receivedTestDomain != randomSubdomain { return systemCompatOrManualDNSIssue(), fmt.Errorf("self-check #2: dns integration check failed: received unmatching subdomain %q", receivedTestDomain) } case <-time.After(dnsCheckWaitDuration): return systemCompatOrManualDNSIssue(), fmt.Errorf("self-check #2: dns integration check failed: did not receive test query after %s", dnsCheckWaitDuration) } log.Tracer(ctx).Tracef("compat: self-check #2: dns integration query check passed") // Step 3: Have the nameserver respond with random data in the answer section. // Check if the resolver is enabled if module.instance.Resolver().IsDisabled() { // There is no control over the response, there is nothing more that can be checked. return nil, nil } // Wait for the reply from the resolver. select { case err := <-dnsCheckLookupError: if err != nil { return systemCompatibilityIssue, fmt.Errorf("self-check #3: dns integration check failed: failed to receive test response: %w", err) } case <-time.After(dnsCheckWaitDuration): return systemCompatibilityIssue, fmt.Errorf("self-check #3: dns integration check failed: did not receive test response after %s", dnsCheckWaitDuration) case <-ctx.Done(): return nil, context.Canceled } // Check response. if !dnsCheckReturnedIP.Equal(randomAnswer) { return systemCompatibilityIssue, fmt.Errorf("self-check #3: dns integration check failed: received unmatching response %q", dnsCheckReturnedIP) } log.Tracer(ctx).Tracef("compat: self-check #3: dns integration response check passed") return nil, nil } /* * Check if the system integration sees a packet: * Send raw IP packet with random content and protocol, report finding to compat module. * use `Dial("ip4:61", "127.65.67.75")`. * Firewall reports back the data seen on `ip4:61` to IP `127.65.67.75`. * If this fails, the system integration is broken. -> Integration Issue * Check if a DNS request arrives at the nameserver: * Send A question for `[random-subdomain].self-check.portmaster.home.arpa.`. * Nameserver reports back the data seen. * If this fails, redirection to the nameserver fails. * This means there is another software interfering with DNS. -> Compatibility Issue * Have the nameserver respond with random data in the answer section. * Compat provides nameserver with random response data. * Compat module checks if the received data matches. * If this fails, redirection to the nameserver fails. * This means there is another software interfering with DNS on the return path. -> Compatibility Issue * DROPPED: If resolvers are reported failing, but we are online: * Send out plain DNS requests to one.one.one.one. and dns.quad9.net via the Go standard lookup and check if the responses are correct. * If not, something is blocking the Portmaster -> Secure DNS Issue * Discuss if this is necessary: * Does this improve from only having a failed TCP connection to the resolver? * Could another program block port 853, but fully leave requests for one.one.one.one. to port 53 alone? */ ================================================ FILE: service/compat/wfpstate.go ================================================ package compat import ( "bytes" "encoding/xml" "errors" "fmt" "os" "path/filepath" "sort" "strings" "text/tabwriter" "github.com/safing/portmaster/base/utils/osdetail" ) // GetWFPState queries the system for the WFP state and returns a simplified // and cleaned version. func GetWFPState() (*SimplifiedWFPState, error) { // Use a file to get the wfp state, as the terminal isn't able to return the // data encoded in UTF-8. tmpDir, err := os.MkdirTemp("", "portmaster-debug-data-wfpstate") if err != nil { return nil, fmt.Errorf("failed to create tmp dir for wfpstate: %w", err) } defer func() { _ = os.RemoveAll(tmpDir) }() tmpFile := filepath.Join(tmpDir, "wfpstate.xml") // Get wfp state and write it to the tmp file. _, err = osdetail.RunCmd( "netsh.exe", "wfp", "show", "state", tmpFile, ) if err != nil { return nil, fmt.Errorf("failed to write wfp state to tmp file: %w", err) } // Get tmp file contents. output, err := os.ReadFile(tmpFile) if err != nil { return nil, fmt.Errorf("failed to read wfp state to tmp file: %w", err) } if len(output) == 0 { return nil, errors.New("wfp state tmp file was empty") } // Parse wfp state. parsedState, err := parseWFPState(output) if err != nil { return nil, fmt.Errorf("failed to parse wfpstate: %w", err) } // Return simplified and cleaned state. return parsedState.simplified(), nil } /* Interesting data is found at: providers->item[] ->displayData->name ->displayData->description ->providerKey subLayers->item[] ->displayData->name ->displayData->description ->subLayerKey layers->item[]->callouts->item[] ->displayData->name ->displayData->description ->calloutKey ->providerKey ->applicableLayer layers->item[]->filters->item[] ->displayData->name ->displayData->description ->filterKey ->providerKey ->layerKey ->subLayerKey */ // SimplifiedWFPState is a simplified version of the full WFP state. type SimplifiedWFPState struct { Providers []*WFPProvider SubLayers []*WFPSubLayer Callouts []*WFPCallout Filters []*WFPFilter } // WFPProvider represents a WFP Provider. type WFPProvider struct { Name string Description string ProviderKey string } // WFPSubLayer represents a WFP SubLayer. type WFPSubLayer struct { Name string Description string SubLayerKey string } // WFPCallout represents a WFP Callout. type WFPCallout struct { Name string Description string CalloutKey string ProviderKey string ApplicableLayer string } // WFPFilter represents a WFP Filter. type WFPFilter struct { Name string Description string FilterKey string ProviderKey string LayerKey string SubLayerKey string } // Keys returns all keys found in the WFP state. func (sw *SimplifiedWFPState) Keys() map[string]struct{} { lookupMap := make(map[string]struct{}, len(sw.Providers)+len(sw.SubLayers)+len(sw.Callouts)+len(sw.Filters)) // Collect keys. for _, provider := range sw.Providers { lookupMap[provider.ProviderKey] = struct{}{} } for _, subLayer := range sw.SubLayers { lookupMap[subLayer.SubLayerKey] = struct{}{} } for _, callout := range sw.Callouts { lookupMap[callout.CalloutKey] = struct{}{} } for _, filter := range sw.Filters { lookupMap[filter.FilterKey] = struct{}{} } return lookupMap } // AsTable formats the simplified WFP state as a table. func (sw *SimplifiedWFPState) AsTable() string { rows := make([]string, 0, len(sw.Providers)+len(sw.SubLayers)+len(sw.Callouts)+len(sw.Filters)) // Collect data and put it into rows. for _, provider := range sw.Providers { rows = append(rows, strings.Join([]string{ provider.Name, "Provider", provider.Description, provider.ProviderKey, }, "\t")) } for _, subLayer := range sw.SubLayers { rows = append(rows, strings.Join([]string{ subLayer.Name, "SubLayer", subLayer.Description, subLayer.SubLayerKey, }, "\t")) } for _, callout := range sw.Callouts { rows = append(rows, strings.Join([]string{ callout.Name, "Callout", callout.Description, callout.CalloutKey, callout.ProviderKey, callout.ApplicableLayer, }, "\t")) } for _, filter := range sw.Filters { rows = append(rows, strings.Join([]string{ filter.Name, "Filter", filter.Description, filter.FilterKey, filter.ProviderKey, filter.LayerKey, filter.SubLayerKey, }, "\t")) } // Sort and build table. sort.Strings(rows) buf := bytes.NewBuffer(nil) tabWriter := tabwriter.NewWriter(buf, 8, 4, 3, ' ', 0) for _, row := range rows { fmt.Fprint(tabWriter, row) fmt.Fprint(tabWriter, "\n") } _ = tabWriter.Flush() return buf.String() } // wfpState is the WFP state as returned by `netsh.exe wfp show state -`. type wfpState struct { XMLName xml.Name `xml:"wfpstate"` Text string `xml:",chardata"` TimeStamp string `xml:"timeStamp"` Providers struct { Text string `xml:",chardata"` NumItems string `xml:"numItems,attr"` Item []struct { Text string `xml:",chardata"` ProviderKey string `xml:"providerKey"` DisplayData struct { Text string `xml:",chardata"` Name string `xml:"name"` Description string `xml:"description"` } `xml:"displayData"` Flags struct { Text string `xml:",chardata"` NumItems string `xml:"numItems,attr"` Item string `xml:"item"` } `xml:"flags"` ProviderData string `xml:"providerData"` ServiceName string `xml:"serviceName"` } `xml:"item"` } `xml:"providers"` SubLayers struct { Text string `xml:",chardata"` NumItems string `xml:"numItems,attr"` Item []struct { Text string `xml:",chardata"` SubLayerKey string `xml:"subLayerKey"` DisplayData struct { Text string `xml:",chardata"` Name string `xml:"name"` Description string `xml:"description"` } `xml:"displayData"` Flags struct { Text string `xml:",chardata"` NumItems string `xml:"numItems,attr"` Item string `xml:"item"` } `xml:"flags"` ProviderKey string `xml:"providerKey"` ProviderData string `xml:"providerData"` Weight string `xml:"weight"` } `xml:"item"` } `xml:"subLayers"` Layers struct { Text string `xml:",chardata"` NumItems string `xml:"numItems,attr"` Item []struct { Text string `xml:",chardata"` Layer struct { Text string `xml:",chardata"` LayerKey string `xml:"layerKey"` DisplayData struct { Text string `xml:",chardata"` Name string `xml:"name"` Description string `xml:"description"` } `xml:"displayData"` Flags struct { Text string `xml:",chardata"` NumItems string `xml:"numItems,attr"` Item []string `xml:"item"` } `xml:"flags"` Field struct { Text string `xml:",chardata"` NumItems string `xml:"numItems,attr"` Item []struct { Text string `xml:",chardata"` FieldKey string `xml:"fieldKey"` Type string `xml:"type"` DataType string `xml:"dataType"` } `xml:"item"` } `xml:"field"` DefaultSubLayerKey string `xml:"defaultSubLayerKey"` LayerID string `xml:"layerId"` } `xml:"layer"` Callouts struct { Text string `xml:",chardata"` NumItems string `xml:"numItems,attr"` Item []struct { Text string `xml:",chardata"` CalloutKey string `xml:"calloutKey"` DisplayData struct { Text string `xml:",chardata"` Name string `xml:"name"` Description string `xml:"description"` } `xml:"displayData"` Flags struct { Text string `xml:",chardata"` NumItems string `xml:"numItems,attr"` Item []string `xml:"item"` } `xml:"flags"` ProviderKey string `xml:"providerKey"` ProviderData string `xml:"providerData"` ApplicableLayer string `xml:"applicableLayer"` CalloutID string `xml:"calloutId"` } `xml:"item"` } `xml:"callouts"` Filters struct { Text string `xml:",chardata"` NumItems string `xml:"numItems,attr"` Item []struct { Text string `xml:",chardata"` FilterKey string `xml:"filterKey"` DisplayData struct { Text string `xml:",chardata"` Name string `xml:"name"` Description string `xml:"description"` } `xml:"displayData"` Flags struct { Text string `xml:",chardata"` NumItems string `xml:"numItems,attr"` Item []string `xml:"item"` } `xml:"flags"` ProviderKey string `xml:"providerKey"` ProviderData struct { Text string `xml:",chardata"` Data string `xml:"data"` AsString string `xml:"asString"` } `xml:"providerData"` LayerKey string `xml:"layerKey"` SubLayerKey string `xml:"subLayerKey"` Weight struct { Text string `xml:",chardata"` Type string `xml:"type"` Uint8 string `xml:"uint8"` Uint64 string `xml:"uint64"` } `xml:"weight"` FilterCondition struct { Text string `xml:",chardata"` NumItems string `xml:"numItems,attr"` Item []struct { Text string `xml:",chardata"` FieldKey string `xml:"fieldKey"` MatchType string `xml:"matchType"` ConditionValue struct { Text string `xml:",chardata"` Type string `xml:"type"` Uint32 string `xml:"uint32"` Uint16 string `xml:"uint16"` RangeValue struct { Text string `xml:",chardata"` ValueLow struct { Text string `xml:",chardata"` Type string `xml:"type"` Uint16 string `xml:"uint16"` Uint32 string `xml:"uint32"` ByteArray16 string `xml:"byteArray16"` } `xml:"valueLow"` ValueHigh struct { Text string `xml:",chardata"` Type string `xml:"type"` Uint16 string `xml:"uint16"` Uint32 string `xml:"uint32"` ByteArray16 string `xml:"byteArray16"` } `xml:"valueHigh"` } `xml:"rangeValue"` Uint8 string `xml:"uint8"` ByteBlob struct { Text string `xml:",chardata"` Data string `xml:"data"` AsString string `xml:"asString"` } `xml:"byteBlob"` Sd string `xml:"sd"` Sid string `xml:"sid"` Uint64 string `xml:"uint64"` } `xml:"conditionValue"` } `xml:"item"` } `xml:"filterCondition"` Action struct { Text string `xml:",chardata"` Type string `xml:"type"` FilterType string `xml:"filterType"` } `xml:"action"` RawContext string `xml:"rawContext"` Reserved string `xml:"reserved"` FilterID string `xml:"filterId"` EffectiveWeight struct { Text string `xml:",chardata"` Type string `xml:"type"` Uint64 string `xml:"uint64"` } `xml:"effectiveWeight"` ProviderContextKey string `xml:"providerContextKey"` } `xml:"item"` } `xml:"filters"` } `xml:"item"` } `xml:"layers"` } func parseWFPState(data []byte) (*wfpState, error) { w := &wfpState{} err := xml.Unmarshal(data, w) if err != nil { return nil, err } return w, nil } func (w *wfpState) simplified() *SimplifiedWFPState { sw := &SimplifiedWFPState{ Providers: make([]*WFPProvider, 0, len(w.Providers.Item)), SubLayers: make([]*WFPSubLayer, 0, len(w.SubLayers.Item)), Callouts: make([]*WFPCallout, 0, len(w.Layers.Item)), Filters: make([]*WFPFilter, 0, len(w.Layers.Item)), } // Collect data. for _, provider := range w.Providers.Item { if isIgnoredProvider(provider.DisplayData.Name, provider.ProviderKey) { continue } sw.Providers = append(sw.Providers, &WFPProvider{ Name: defaultTo(provider.DisplayData.Name, "[no name]"), Description: defaultTo(provider.DisplayData.Description, "[no description]"), ProviderKey: defaultTo(provider.ProviderKey, "[no provider key]"), }) } for _, subLayer := range w.SubLayers.Item { if isIgnoredProvider(subLayer.DisplayData.Name, "") { continue } sw.SubLayers = append(sw.SubLayers, &WFPSubLayer{ Name: defaultTo(subLayer.DisplayData.Name, "[no name]"), Description: defaultTo(subLayer.DisplayData.Description, "[no description]"), SubLayerKey: defaultTo(subLayer.SubLayerKey, "[no sublayer key]"), }) } for _, layer := range w.Layers.Item { for _, callout := range layer.Callouts.Item { if isIgnoredProvider(callout.DisplayData.Name, callout.ProviderKey) { continue } sw.Callouts = append(sw.Callouts, &WFPCallout{ Name: defaultTo(callout.DisplayData.Name, "[no name]"), Description: defaultTo(callout.DisplayData.Description, "[no description]"), CalloutKey: defaultTo(callout.CalloutKey, "[no callout key]"), ProviderKey: defaultTo(callout.ProviderKey, "[no provider key]"), ApplicableLayer: defaultTo(callout.ApplicableLayer, "[no applicable layer]"), }) } for _, filter := range layer.Filters.Item { if isIgnoredProvider(filter.DisplayData.Name, filter.ProviderKey) { continue } sw.Filters = append(sw.Filters, &WFPFilter{ Name: defaultTo(filter.DisplayData.Name, "[no name]"), Description: defaultTo(filter.DisplayData.Description, "[no description]"), FilterKey: defaultTo(filter.FilterKey, "[no filter key]"), ProviderKey: defaultTo(filter.ProviderKey, "[no provider key]"), LayerKey: defaultTo(filter.LayerKey, "[no layer key]"), SubLayerKey: defaultTo(filter.SubLayerKey, "[no sublayer key]"), }) } } return sw } func isIgnoredProvider(name, key string) bool { // Check provider key. if key != "" { matched := true switch key { case "{1bebc969-61a5-4732-a177-847a0817862a}": // Microsoft Windows Defender Firewall IPsec Provider. case "{4b153735-1049-4480-aab4-d1b9bdc03710}": // Microsoft Windows Defender Firewall Provider. case "{893a4f22-9bba-49b7-8c66-3d40929c8fd5}": // Microsoft Windows Teredo firewall provider. case "{8e44982a-f477-11df-85ce-78e7d1810190}": // Windows Network Data Usage (NDU) Provider. case "{9c2532b4-0314-434f-8274-0cbaebdbda56}": // Microsoft Windows edge traversal socket option authorization provider. case "{aa6a7d87-7f8f-4d2a-be53-fda555cd5fe3}": // Microsoft Windows Defender Firewall IPsec Provider. case "{c698301d-9129-450c-937c-f4b834bfb374}": // Microsoft Windows edge traversal socket option authorization provider. case "{decc16ca-3f33-4346-be1e-8fb4ae0f3d62}": // Microsoft Windows Defender Firewall Provider. case "FWPM_PROVIDER_IKEEXT": // Microsoft Windows WFP Built-in IKEEXT provider used to identify filters added by IKE/AuthIP. case "FWPM_PROVIDER_IPSEC_DOSP_CONFIG": // Microsoft Windows WFP Built-in IPsec DoS Protection configuration provider used to identify filters added by IPsec Denial of Service Protection. case "FWPM_PROVIDER_MPSSVC_APP_ISOLATION": // Microsoft Windows WFP Built-in MPSSVC App Isolation provider. case "FWPM_PROVIDER_MPSSVC_EDP": // Microsoft Windows WFP Built-in MPSSVC Enterprise Data Protection provider. case "FWPM_PROVIDER_MPSSVC_TENANT_RESTRICTIONS": // Microsoft Windows WFP Built-in MPSSVC Tenant Restrictions provider. case "FWPM_PROVIDER_MPSSVC_WF": // Microsoft Windows WFP Built-in MPSSVC Windows Firewall provider. case "FWPM_PROVIDER_MPSSVC_WSH": // Microsoft Windows WFP Built-in MPSSVC Windows Service Hardening and Quarantine provider. case "FWPM_PROVIDER_TCP_CHIMNEY_OFFLOAD": // Microsoft Windows WFP Built-in TCP Chimney Offload provider used to identify filters added by TCP Chimney Offload. case "FWPM_PROVIDER_TCP_TEMPLATES": // Microsoft Windows WFP Built-in TCP Templates provider used to identify filters added by TCP Template based configuration. default: matched = false } if matched { return true } } // Some entries don't have a provider key (set). // These are pretty generic, but the output strings are localized. if name != "" { switch { case strings.Contains(name, "Microsoft Corporation"): return true case strings.Contains(name, "windefend"): return true case strings.Contains(name, "WFP"): return true case strings.Contains(name, "RPC"): return true case strings.Contains(name, "NDU"): return true } } return false } func defaultTo(a, b string) string { if a != "" { return a } return b } ================================================ FILE: service/compat/wfpstate_test.go ================================================ package compat import ( "fmt" "strings" "testing" ) func TestWFPState(t *testing.T) { t.Parallel() w, err := parseWFPState(wfpTestData) if err != nil { t.Fatal(err) } output := w.simplified().AsTable() fmt.Println(output) outputLines := strings.Count(output, "\n") if outputLines != 10 { t.Errorf("Expected 10 output lines, not %d", outputLines) } } var wfpTestData = []byte(` FWPM_LAYER_INBOUND_IPPACKET_V4 Inbound IP Packet v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 0 {05c55149-4732-4857-8d10-f178f3a06f8c} PortmasterInboundV4Callout This callout is used by the Portmaster to intercept inbound IPv4 traffic. FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_INBOUND_IPPACKET_V4 290 {fb2f56c5-3883-475a-8c78-047d1f93b5a9} PortmasterInboundV4Filter This filter is used by the Portmaster to intercept inbound IPv4 traffic. FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT FWPM_LAYER_INBOUND_IPPACKET_V4 {a87fb472-fc68-4805-8559-c6ae774773e0} FWP_UINT8 15 FWP_ACTION_CALLOUT_TERMINATING {05c55149-4732-4857-8d10-f178f3a06f8c} 0 68136 FWP_UINT64 17293822569102704640 FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD Inbound IP Packet v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 1 FWPM_LAYER_INBOUND_IPPACKET_V6 Inbound IP Packet v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 2 {ceff1df7-2baa-44c5-a6e5-73a95849bcff} PortmasterInboundV6Callout This callout is used by the Portmaster to intercept inbound IPv6 traffic. FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_INBOUND_IPPACKET_V6 292 {778afeeb-9680-4a59-82d5-abc33b3fb183} PortmasterInboundV6Filter This filter is used by the Portmaster to intercept inbound IPv6 traffic. FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT FWPM_LAYER_INBOUND_IPPACKET_V6 {a87fb472-fc68-4805-8559-c6ae774773e0} FWP_UINT8 15 FWP_ACTION_CALLOUT_TERMINATING {ceff1df7-2baa-44c5-a6e5-73a95849bcff} 0 68138 FWP_UINT64 17293822569102704640 FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD Inbound IP Packet v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 3 FWPM_LAYER_OUTBOUND_IPPACKET_V4 Outbound IP Packet v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 4 {41162b9e-8473-4b88-a5eb-04cf1d276b06} PortmasterOutboundV4Callout This callout is used by the Portmaster to intercept outbound IPv4 traffic. FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_OUTBOUND_IPPACKET_V4 291 {609b24b3-7b9a-48e1-8994-c72a42bbd319} PortmasterOutboundV4Filter This filter is used by the Portmaster to intercept outbound IPv4 traffic. FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT FWPM_LAYER_OUTBOUND_IPPACKET_V4 {a87fb472-fc68-4805-8559-c6ae774773e0} FWP_UINT8 15 FWP_ACTION_CALLOUT_TERMINATING {41162b9e-8473-4b88-a5eb-04cf1d276b06} 0 68137 FWP_UINT64 17293822569102704640 FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD Outbound IP Packet v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 5 FWPM_LAYER_OUTBOUND_IPPACKET_V6 Outbound IP Packet v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 6 {32bad112-6af4-4109-809b-c07570ba01b4} PortmasterOutboundV6Callout This callout is used by the Portmaster to intercept outbound IPv6 traffic. FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_OUTBOUND_IPPACKET_V6 293 {23ae199a-7949-4059-b799-67e7a1a57fcb} PortmasterOutboundV6Filter This filter is used by the Portmaster to intercept outbound IPv6 traffic. FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT FWPM_LAYER_OUTBOUND_IPPACKET_V6 {a87fb472-fc68-4805-8559-c6ae774773e0} FWP_UINT8 15 FWP_ACTION_CALLOUT_TERMINATING {32bad112-6af4-4109-809b-c07570ba01b4} 0 68139 FWP_UINT64 17293822569102704640 FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD Outbound IP Packet v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 7 FWPM_LAYER_IPFORWARD_V4 IP Forward v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_SOURCE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_DESTINATION_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_IP_FORWARD_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_SOURCE_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SOURCE_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DESTINATION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_IP_PHYSICAL_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_PHYSICAL_NEXTHOP_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 8 FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 WFP Built-in IPsec Forward Inbound Tunnel v4 Layer Callout Verifies that each received packet that is supposed to arrive over a tunnel mode security association arrives securely. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000020 FWPM_LAYER_IPFORWARD_V4 9 FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4 WFP Built-in IPsec Forward Outbound Tunnel v4 Layer Callout Indicates to IPsec the outbound traffic that must be secured over a tunnel mode security association. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000020 FWPM_LAYER_IPFORWARD_V4 11 {935b7f48-0ede-44dd-9bc2-e00bb635cda3} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_IPFORWARD_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 0 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 786432 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66238 FWP_UINT64 4 FWPM_LAYER_IPFORWARD_V4_DISCARD IP Forward v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_SOURCE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_DESTINATION_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_IP_FORWARD_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_SOURCE_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SOURCE_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DESTINATION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_IP_PHYSICAL_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_PHYSICAL_NEXTHOP_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 9 FWPM_LAYER_IPFORWARD_V6 IP Forward v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_SOURCE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_IP_FORWARD_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_SOURCE_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SOURCE_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DESTINATION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_IP_PHYSICAL_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_PHYSICAL_NEXTHOP_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 10 FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6 WFP Built-in IPsec Forward Inbound Tunnel v6 Layer Callout Verifies that each received packet that is supposed to arrive over a tunnel mode security association arrives securely. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000020 FWPM_LAYER_IPFORWARD_V6 10 FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6 WFP Built-in IPsec Forward Outbound Tunnel v6 Layer Callout Indicates to IPsec the outbound traffic that must be secured over a tunnel mode security association. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000020 FWPM_LAYER_IPFORWARD_V6 12 {941dad9d-7b1a-4354-997b-00cf1aa9b35c} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_IPFORWARD_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 0 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 786432 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66239 FWP_UINT64 4 FWPM_LAYER_IPFORWARD_V6_DISCARD IP Forward v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_SOURCE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_IP_FORWARD_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_SOURCE_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SOURCE_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DESTINATION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_IP_PHYSICAL_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_PHYSICAL_NEXTHOP_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 11 FWPM_LAYER_INBOUND_TRANSPORT_V4 Inbound Transport v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 12 FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 WFP Built-in IPsec Inbound Transport v4 Layer Callout Verifies that each received packet that is supposed to arrive over a transport mode security association arrives securely. FWPM_CALLOUT_FLAG_REGISTERED 00000260 FWPM_LAYER_INBOUND_TRANSPORT_V4 1 FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4 WFP Built-in IPsec Inbound Tunnel v4 Layer Callout Verifies that each received packet that is supposed to arrive over a tunnel mode security association arrives securely. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000020 FWPM_LAYER_INBOUND_TRANSPORT_V4 5 FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD Inbound Transport v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 13 FWPM_CALLOUT_WFP_TRANSPORT_LAYER_V4_SILENT_DROP WFP Built-in Silent Drop Transport v4 Discard Layer Callout Implements stealth-mode filtering by silently dropping all incoming packets for which TCP does not have a listening endpoint. FWP_CALLOUT_FLAG_ALLOW_OFFLOAD FWPM_CALLOUT_FLAG_REGISTERED 00000240 FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD 17 {03c67493-1802-44a0-ad5f-3ae741e25c15} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68060 FWP_UINT64 18446744073709551615 {29ccd323-aecf-4990-b96a-c5384222b0c9} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68062 FWP_UINT64 18446744073709551615 {92ff43ac-9df0-49e9-b3b3-d6f8421edcca} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_WFP_TRANSPORT_LAYER_V4_SILENT_DROP {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68064 FWP_UINT64 18446744073709551615 {b2a53430-2096-4b30-ae54-260f96a82028} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 12 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 3 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_WFP_TRANSPORT_LAYER_V4_SILENT_DROP {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68066 FWP_UINT64 13835058055349272576 FWPM_LAYER_INBOUND_TRANSPORT_V6 Inbound Transport v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 14 FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6 WFP Built-in IPsec Inbound Transport v6 Layer Callout Verifies that each received packet that is supposed to arrive over a transport mode security association arrives securely. FWPM_CALLOUT_FLAG_REGISTERED 00000260 FWPM_LAYER_INBOUND_TRANSPORT_V6 2 FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6 WFP Built-in IPsec Inbound Tunnel v6 Layer Callout Verifies that each received packet that is supposed to arrive over a tunnel mode security association arrives securely. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000020 FWPM_LAYER_INBOUND_TRANSPORT_V6 6 FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD Inbound Transport v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 15 FWPM_CALLOUT_WFP_TRANSPORT_LAYER_V6_SILENT_DROP WFP Built-in Silent Drop Transport v6 Discard Layer Callout Implements stealth-mode filtering by silently dropping all incoming packets for which TCP does not have a listening endpoint. FWP_CALLOUT_FLAG_ALLOW_OFFLOAD FWPM_CALLOUT_FLAG_REGISTERED 00000240 FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD 18 {57175e0d-0212-4ddd-b229-559795b43af7} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68061 FWP_UINT64 18446744073709551615 {d979d760-083e-489d-9360-1f377b606ef1} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68063 FWP_UINT64 18446744073709551615 {22b3ef06-6428-47ac-868f-b4a968fe995c} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_WFP_TRANSPORT_LAYER_V6_SILENT_DROP {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68065 FWP_UINT64 18446744073709551615 {f3ecd3f5-9498-4dce-a76d-da32be01382f} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 12 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 3 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_WFP_TRANSPORT_LAYER_V6_SILENT_DROP {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68067 FWP_UINT64 13835058055298940928 FWPM_LAYER_OUTBOUND_TRANSPORT_V4 Outbound Transport v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 16 FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4 WFP Built-in IPsec Outbound Transport v4 Layer Callout Indicates to IPsec the outbound traffic that must be secured over transport mode security associations. FWPM_CALLOUT_FLAG_REGISTERED 00000260 FWPM_LAYER_OUTBOUND_TRANSPORT_V4 3 FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 WFP Built-in IPsec Outbound Tunnel v4 Layer Callout Indicates to IPsec the outbound traffic that must be secured over tunnel mode security associations. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000020 FWPM_LAYER_OUTBOUND_TRANSPORT_V4 7 FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD Outbound Transport v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 17 FWPM_LAYER_OUTBOUND_TRANSPORT_V6 Outbound Transport v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 18 FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6 WFP Built-in IPsec Outbound Transport v6 Layer Callout Indicates to IPsec the outbound traffic that must be secured over transport mode security associations. FWPM_CALLOUT_FLAG_REGISTERED 00000260 FWPM_LAYER_OUTBOUND_TRANSPORT_V6 4 FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 WFP Built-in IPsec Outbound Tunnel v6 Layer Callout Indicates to IPsec the outbound traffic that must be secured over tunnel mode security associations. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000020 FWPM_LAYER_OUTBOUND_TRANSPORT_V6 8 FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD Outbound Transport v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 19 FWPM_LAYER_STREAM_V4 Stream v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_LAYER_FLAG_BUFFERED FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 20 {c3dbed20-0bb6-4bf3-828d-96732e1e0414} Windows Firewall: callout Connection-oriented protocol packet inspection. FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW FWPM_CALLOUT_FLAG_REGISTERED FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_STREAM_V4 288 {d67b238d-d80c-4ba7-96df-4a0c83464fa7} windefend_stream_v4 windefend FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_STREAM_V4 294 {821997be-23bb-4240-87a2-2615caec3d1f} Deep Protocol Inspection Filter This filter implements deep inspection for FTP protocols. FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_STREAM_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e0414} 0 68050 FWP_UINT64 18446744073709551615 {42e67750-e0ae-4ec7-aa42-97c6605b1c96} windefend_stream_v4 windefend FWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED FWPM_LAYER_STREAM_V4 {3c1cd879-1b8c-4ab4-8f83-5ed129176ef3} FWP_EMPTY FWP_ACTION_CALLOUT_TERMINATING {d67b238d-d80c-4ba7-96df-4a0c83464fa7} 0 68276 FWP_UINT64 0 FWPM_LAYER_STREAM_V4_DISCARD Stream v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_LAYER_FLAG_BUFFERED FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 21 FWPM_LAYER_STREAM_V6 Stream v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_LAYER_FLAG_BUFFERED FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 22 {c3dbed20-0bb6-4bf3-828d-96732e1e0416} Windows Firewall: callout Connection-oriented protocol packet inspection. FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW FWPM_CALLOUT_FLAG_REGISTERED FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_STREAM_V6 289 {36d4cf95-fd3a-4bb8-90a9-ffdb3749237f} windefend_stream_v6 windefend FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_STREAM_V6 295 {ecdc6e7a-8981-4a13-92d7-fb12f8d0ea9f} Deep Protocol Inspection Filter This filter implements deep inspection for FTP protocols. FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_STREAM_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e0416} 0 68051 FWP_UINT64 18446744073709551615 {094b9bb6-e451-4069-ac23-5fe695ec9662} windefend_stream_v6 windefend FWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED FWPM_LAYER_STREAM_V6 {3c1cd879-1b8c-4ab4-8f83-5ed129176ef3} FWP_EMPTY FWP_ACTION_CALLOUT_TERMINATING {36d4cf95-fd3a-4bb8-90a9-ffdb3749237f} 0 68277 FWP_UINT64 0 FWPM_LAYER_STREAM_V6_DISCARD Stream v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_LAYER_FLAG_BUFFERED FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 23 FWPM_LAYER_DATAGRAM_DATA_V4 Datagram Data v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 24 {b2d11c73-b4ef-47d1-b665-58e6ba9fe200} windefend_datagram_v4 windefend FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_DATAGRAM_DATA_V4 300 {67dc47f0-7a3b-4705-a4e7-bb8756a3e69f} windefend_datagram_v4 windefend FWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED FWPM_LAYER_DATAGRAM_DATA_V4 {3c1cd879-1b8c-4ab4-8f83-5ed129176ef3} FWP_EMPTY FWP_ACTION_CALLOUT_TERMINATING {b2d11c73-b4ef-47d1-b665-58e6ba9fe200} 0 68282 FWP_UINT64 0 FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD Datagram Data v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 25 FWPM_LAYER_DATAGRAM_DATA_V6 Datagram Data v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 26 {80cece9d-0b53-4672-ac43-4524416c0353} windefend_datagram_v6 windefend FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_DATAGRAM_DATA_V6 301 {f875d7d9-b132-436d-a729-479ef4e4ef08} windefend_datagram_v6 windefend FWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED FWPM_LAYER_DATAGRAM_DATA_V6 {3c1cd879-1b8c-4ab4-8f83-5ed129176ef3} FWP_EMPTY FWP_ACTION_CALLOUT_TERMINATING {80cece9d-0b53-4672-ac43-4524416c0353} 0 68283 FWP_UINT64 0 FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD Datagram Data v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 27 FWPM_LAYER_INBOUND_ICMP_ERROR_V4 Inbound ICMP Error v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_EMBEDDED_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_EMBEDDED_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_EMBEDDED_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_EMBEDDED_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_EMBEDDED_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 28 {0c41d586-9c19-4e01-9d66-b5b98a97576e} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66212 FWP_UINT64 18446744073709551615 {4d9581d2-aef8-4993-84cd-b986ced80d42} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66216 FWP_UINT64 18446744073709551615 {074f7f68-ee10-428a-89d1-ba78f6c327ca} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 0 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66224 FWP_UINT64 0 {91ffecf0-0a9e-4572-95f1-a7111af86967} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 0 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66228 FWP_UINT64 0 {08f09e8d-2754-4cc2-b595-7260f3a8df9d} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_INBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_BLOCK {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67629 FWP_UINT64 9223372036854775812 {b8b57642-80b0-49d5-b5d6-34834967743d} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_INBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 3 FWP_ACTION_BLOCK {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67637 FWP_UINT64 3458764513820540928 FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD Inbound ICMP Error v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_EMBEDDED_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_EMBEDDED_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_EMBEDDED_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_EMBEDDED_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_EMBEDDED_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 29 {c3dbed20-0bb6-4bf3-828d-96732e1e011d} Windows Firewall: callout Performs logging. FWPM_CALLOUT_FLAG_REGISTERED 00000200 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD 276 FWPM_LAYER_INBOUND_ICMP_ERROR_V6 Inbound ICMP Error v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_EMBEDDED_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_EMBEDDED_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_EMBEDDED_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_EMBEDDED_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_EMBEDDED_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 30 {12c38916-82ac-4737-8f38-b6957ffebad6} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66213 FWP_UINT64 18446744073709551615 {be7cbdf4-b192-4aa5-94f8-1fb5c5ee07bc} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66217 FWP_UINT64 18446744073709551615 {c016105c-eb34-4519-a5fd-5f4e4ad4d18e} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 0 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66225 FWP_UINT64 0 {64e55933-15a5-495d-a928-ccca43d44875} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_INBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 0 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66229 FWP_UINT64 0 {cc44ca04-e1d0-42ef-8fd1-ffe89f6ed166} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_INBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_BLOCK {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67633 FWP_UINT64 9223372036854775812 {49ec4b6a-348d-41ae-8777-f7c59ec17f26} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_INBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 3 FWP_ACTION_BLOCK {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67641 FWP_UINT64 3458764513820540928 FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD Inbound ICMP Error v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_EMBEDDED_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_EMBEDDED_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_EMBEDDED_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_EMBEDDED_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_EMBEDDED_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 31 {c3dbed20-0bb6-4bf3-828d-96732e1e011f} Windows Firewall: callout Performs logging. FWPM_CALLOUT_FLAG_REGISTERED 00000200 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD 277 FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 Outbound ICMP Error v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 32 {07a24961-a760-4e80-b263-6d275e1b09cb} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66220 FWP_UINT64 18446744073709551615 {b308b233-cd6c-401d-9f09-3a80217f778e} Default Outbound This is the default outbound filter which blocks or permits traffic based on user configured default settings FWPM_PROVIDER_MPSSVC_WF 9500000000000000 ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67643 FWP_UINT64 9223372036854775812 {bfe4943b-6e61-4ae6-a187-d62d68351430} Default Outbound This is the default outbound filter which blocks or permits traffic based on user configured default settings FWPM_PROVIDER_MPSSVC_WF 9500000000000000 ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 3 FWP_ACTION_PERMIT 0 67647 FWP_UINT64 3458764513820540928 {c313817b-32a7-4160-95b2-4673a26e0cab} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 0 FWP_UINT16 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68068 FWP_UINT64 9223372586040164364 {3228da2c-00c2-4f05-8f8c-682315fe7530} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 5 FWP_UINT16 12 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68069 FWP_UINT64 9223372585771728908 {a2fd53b1-e627-4962-aede-c508dac25267} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 14 FWP_UINT16 255 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68070 FWP_UINT64 9223372584463106060 {2260bd8b-6481-4879-93f1-9435d24d2528} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 0 FWP_UINT16 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68073 FWP_UINT64 9223372586040164364 {7224a028-a279-4c7b-b136-40f7516b88cc} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 5 FWP_UINT16 12 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68074 FWP_UINT64 9223372585771728908 {3339fd71-a0e9-4714-87f6-e9ec698fab6b} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 14 FWP_UINT16 255 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68075 FWP_UINT64 9223372584463106060 {9eaf6f7b-ed11-48fb-b3c5-592921db1103} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 0 FWP_UINT16 3 FWP_ACTION_BLOCK {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68078 FWP_UINT64 18446744073709551615 {ef4ccd1e-f08d-4199-85e1-3ef56963f09e} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744069414584322 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 5 FWP_UINT16 12 FWP_ACTION_BLOCK {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68079 FWP_UINT64 18446744069414584322 {0365edae-cbe6-4657-8943-2d16058c98c2} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744069414584322 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 14 FWP_UINT16 255 FWP_ACTION_BLOCK {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68080 FWP_UINT64 18446744069414584322 {c2b8dc84-bfca-4c6d-ba88-5325839ea39f} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 0 FWP_UINT16 3 FWP_ACTION_BLOCK {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68083 FWP_UINT64 18446744073709551615 {d5afb3be-9eb2-40c3-8fa9-3e8ee38948c5} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744069414584322 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 5 FWP_UINT16 12 FWP_ACTION_BLOCK {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68084 FWP_UINT64 18446744069414584322 {4c7ffc63-9f38-44b9-8624-b85abd89b7a6} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744069414584322 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 14 FWP_UINT16 255 FWP_ACTION_BLOCK {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68085 FWP_UINT64 18446744069414584322 FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD Outbound ICMP Error v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 33 {c3dbed20-0bb6-4bf3-828d-96732e1e0121} Windows Firewall: callout Performs logging. FWPM_CALLOUT_FLAG_REGISTERED 00000200 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD 272 FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 Outbound ICMP Error v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 34 {5b0cb2e2-ab87-4974-9f1c-2f22a654eeb9} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66221 FWP_UINT64 18446744073709551615 {da00b2e8-656f-40cc-9a37-444f32c5e58a} Default Outbound This is the default outbound filter which blocks or permits traffic based on user configured default settings FWPM_PROVIDER_MPSSVC_WF 9500000000000000 ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67645 FWP_UINT64 9223372036854775812 {70fb0d21-2477-44bc-967b-494c061e96eb} Default Outbound This is the default outbound filter which blocks or permits traffic based on user configured default settings FWPM_PROVIDER_MPSSVC_WF 9500000000000000 ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 3 FWP_ACTION_PERMIT 0 67649 FWP_UINT64 3458764513820540928 {b9b272fb-b866-4e48-9385-c1bee91957da} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68071 FWP_UINT64 9223372070677643276 {331a54f2-38e6-43de-943b-9742b565db8c} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 4 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 0 FWP_UINT16 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68072 FWP_UINT64 9223372071155793932 {722d30ec-406b-423a-b027-d7dd8a306836} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68076 FWP_UINT64 9223372070677643276 {c6a5f146-bbad-4cad-8de4-0c3a4d349f77} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 4 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 0 FWP_UINT16 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68077 FWP_UINT64 9223372071155793932 {a6c63b22-fa8c-4afc-88ab-3f0a8faf213a} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744069414584322 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 1 FWP_ACTION_BLOCK {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68081 FWP_UINT64 18446744069414584322 {45648f6f-4589-44e3-8959-75b391aa0038} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744069414584322 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 4 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 0 FWP_UINT16 2 FWP_ACTION_BLOCK {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68082 FWP_UINT64 18446744069414584322 {dd1e0820-fc15-4492-9403-ef2c61e329bb} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744069414584322 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 1 FWP_ACTION_BLOCK {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68086 FWP_UINT64 18446744069414584322 {4d5e5002-239a-4d48-a681-d7c3c49517d1} Port Scanning Prevention Filter This filter prevents port scanning. This many times means there are no listeners. If debugging ensure your scenario has one. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744069414584322 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 4 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 0 FWP_UINT16 2 FWP_ACTION_BLOCK {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68087 FWP_UINT64 18446744069414584322 FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD Outbound ICMP Error v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 35 {c3dbed20-0bb6-4bf3-828d-96732e1e0123} Windows Firewall: callout Performs logging. FWPM_CALLOUT_FLAG_REGISTERED 00000200 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD 273 FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 ALE Resource Assignment v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_ALE_PROMISCUOUS_MODE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 36 FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_RESOURCE_ASSIGNMENT_V4 WFP Built-in Edge Traversal ALE Resource Assignment v4 Layer Callout Signals Windows when an application is attempting to bind to an interface and has opted in for edge traversing traffic. FWPM_CALLOUT_FLAG_REGISTERED 00000014 FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 27 {c3dbed20-0bb6-4bf3-828d-96732e1e0024} Windows Firewall: callout Notifies the user. FWPM_CALLOUT_FLAG_REGISTERED FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 268 {58d7275b-2fd2-4b6c-b93a-30037e577d7e} windefend_resource_assignment_v4 windefend FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 296 {fec997ae-7462-4ec1-b9af-99c4751fd489} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 9 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e0024} {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67628 FWP_UINT64 9223442268161048576 {e71db8d0-e58f-45fa-8d34-6e66c5ec8cc1} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 3 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 9 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e0024} {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67636 FWP_UINT64 3458834745126813696 {cd49bc81-9428-4bfe-9fad-d20504a480a2} Delivery Optimization (UDP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_PROVIDER_MPSSVC_WF 9c00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67663 FWP_UINT64 11529003905476198400 {d0db3fd9-fa82-4abf-aa82-87a9645636de} Delivery Optimization (UDP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_PROVIDER_MPSSVC_WF 9c00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_RESOURCE_ASSIGNMENT_V4 0 67664 FWP_UINT64 11529003905476198400 {69f35305-c344-4543-891f-28168fbd3f31} Core Networking - Dynamic Host Configuration Protocol (DHCP-In) Allows DHCP (Dynamic Host Configuration Protocol) messages for stateful auto-configuration. FWPM_PROVIDER_MPSSVC_WF 9f00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 68 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67677 FWP_UINT64 11529003905476198400 {77c9fe79-5402-402e-8226-c8b570fd6388} Core Networking - Dynamic Host Configuration Protocol for IPv6(DHCPV6-In) Allows DHCPV6 (Dynamic Host Configuration Protocol for IPv6) messages for stateful and stateless configuration. FWPM_PROVIDER_MPSSVC_WF a000000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 546 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67681 FWP_UINT64 11529003905476198400 {8235388e-e851-4838-a33f-fb98db5a3e5a} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/Description} FWPM_PROVIDER_MPSSVC_WF b300000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_RESOURCE_ASSIGNMENT_V4 0 67773 FWP_UINT64 10376363875846062080 {eeb93afc-0f8e-4b50-b378-4517aeacf07a} XING XING FWPM_PROVIDER_MPSSVC_WF b400000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-830604253-3178213212-3804702845-3552981612-1670190244-297742492-2307346640 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_RESOURCE_ASSIGNMENT_V4 0 67785 FWP_UINT64 10376363875846062080 {8bbc020b-4c16-4c42-9147-0c7f61f2a27b} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_DisplayName} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_Description} FWPM_PROVIDER_MPSSVC_WF b500000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378-1866284433 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_RESOURCE_ASSIGNMENT_V4 0 67797 FWP_UINT64 10376363875846062080 {a2b54f0b-3d87-462c-bfe7-5aff5618a7b3} Xbox Game Bar Xbox Game Bar FWPM_PROVIDER_MPSSVC_WF b600000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1714399563-1326177402-2048222277-143663168-2151391019-765408921-4098702777 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_RESOURCE_ASSIGNMENT_V4 0 67809 FWP_UINT64 10376363875846062080 {8db0f20c-e041-4c94-93c3-2acdf0380dc4} Microsoft Teams Microsoft Teams FWPM_PROVIDER_MPSSVC_WF b800000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f007700730061007000700073005c006d006900630072006f0073006f00660074007400650061006d0073005f00320032003000300036002e003600300030002e0031003100330033002e0037003400300039005f007800360034005f005f003800770065006b007900620033006400380062006200770065005c006d0073007400650061006d0073002e006500780065000000 \device\harddiskvolume3\program files\windowsapps\microsoftteams_22006.600.1133.7409_x64__8wekyb3d8bbwe\msteams.exe FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67824 FWP_UINT64 10376363841486323712 {2d6dcea9-f654-406c-bad0-c12935683e86} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} FWPM_PROVIDER_MPSSVC_WF b900000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2226957697-3030467180-2301525-4248967783-2024719031-2325529081-2915787518 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_RESOURCE_ASSIGNMENT_V4 0 67829 FWP_UINT64 10376363875846062080 {c5f25e64-ac27-4d08-ac2e-fc083d790752} Cortana Cortana FWPM_PROVIDER_MPSSVC_WF ba00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1880626798-2296700190-2192216202-2581987570-949377748-777141861-2889999867 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_RESOURCE_ASSIGNMENT_V4 0 67841 FWP_UINT64 10376363875846062080 {68ce3ad4-d6a7-4f3b-94bf-9d7ddbbfd856} Microsoft Edge (mDNS-In) Inbound rule for Microsoft Edge to allow mDNS traffic. FWPM_PROVIDER_MPSSVC_WF bb00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073002000280078003800360029005c006d006900630072006f0073006f00660074005c00650064006700650077006500620076006900650077005c006100700070006c00690063006100740069006f006e005c00390038002e0030002e0031003100300038002e00350036005c006d0073006500640067006500770065006200760069006500770032002e006500780065000000 \device\harddiskvolume3\program files (x86)\microsoft\edgewebview\application\98.0.1108.56\msedgewebview2.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67852 FWP_UINT64 11529003871116460032 {1e498170-bf78-40d0-8534-89420cdeb047} Microsoft Edge (mDNS-In) Inbound rule for Microsoft Edge to allow mDNS traffic. FWPM_PROVIDER_MPSSVC_WF bd00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073002000280078003800360029005c006d006900630072006f0073006f00660074005c0065006400670065005c006100700070006c00690063006100740069006f006e005c006d00730065006400670065002e006500780065000000 \device\harddiskvolume3\program files (x86)\microsoft\edge\application\msedge.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67868 FWP_UINT64 11529003871116460032 {965c8ce5-bb4c-4ece-9b94-fbab1f02bd61} Microsoft Edge (mDNS-In) Inbound rule for Microsoft Edge to allow mDNS traffic. FWPM_PROVIDER_MPSSVC_WF be00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073002000280078003800360029005c006d006900630072006f0073006f00660074005c00650064006700650077006500620076006900650077005c006100700070006c00690063006100740069006f006e005c00390038002e0030002e0031003100300038002e00360032005c006d0073006500640067006500770065006200760069006500770032002e006500780065000000 \device\harddiskvolume3\program files (x86)\microsoft\edgewebview\application\98.0.1108.62\msedgewebview2.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67872 FWP_UINT64 11529003871116460032 {1663d17f-13ac-4339-a87d-0b8177c6a161} Microsoft Media Foundation Network Source IN [UDP 5004-5009] InBound Rule for the Microsoft Media Foundation's Capture SVC to open UDP port to enable RTSP FWPM_PROVIDER_MPSSVC_WF 1f01000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 5000 FWP_UINT16 5020 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68214 FWP_UINT64 11212626031653421056 {2c0936a0-ca0e-45fd-8ca2-f7a00123be5c} WFD ASP Coordination Protocol (UDP-In) Inbound rule for the WLAN Service to allow coordination protocol for WFD Service sessions [UDP 7235] FWPM_PROVIDER_MPSSVC_WF 2201000000000000 "....... FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68234 FWP_UINT64 11529003905476198400 {d89bb141-b39a-4088-96b4-558cb5f9f818} mDNS (UDP-In) Inbound rule for mDNS traffic [UDP] FWPM_PROVIDER_MPSSVC_WF 2601000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-859482183-879914841-863379149-1145462774-2388618682) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68246 FWP_UINT64 11529003905476198400 {26501d40-8df2-4445-9f4b-a8a19f2afeb8} windefend_resource_assignment_v4 windefend FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 {3c1cd879-1b8c-4ab4-8f83-5ed129176ef3} FWP_EMPTY FWP_ACTION_CALLOUT_INSPECTION {58d7275b-2fd2-4b6c-b93a-30037e577d7e} 0 68278 FWP_UINT64 0 {ba25c88a-4995-4529-bbd9-095462c2ab55} Microsoft Store Microsoft Store FWPM_PROVIDER_MPSSVC_WF 2d01000000000000 -....... FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_RESOURCE_ASSIGNMENT_V4 0 68285 FWP_UINT64 10376363875846062080 FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD ALE Resource Assignment v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_ALE_PROMISCUOUS_MODE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 37 FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 ALE Resource Assignment v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_ALE_PROMISCUOUS_MODE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 38 FWPM_CALLOUT_TEREDO_ALE_RESOURCE_ASSIGNMENT_V6 WFP Built-in Edge Traversal ALE Resource Assignment v6 Layer Callout Signals Windows when an application is attempting to bind to an interface and has opted in for edge traversing traffic. FWPM_CALLOUT_FLAG_REGISTERED 00000014 FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 28 {c3dbed20-0bb6-4bf3-828d-96732e1e0026} Windows Firewall: callout Notifies the user. FWPM_CALLOUT_FLAG_REGISTERED FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 269 {ced78e5f-1dd1-485a-9d35-7e44cc9d784d} windefend_resource_assignment_v6 windefend FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 297 {233dc13e-f91d-4576-8c0a-c258350a7620} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 9 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e0026} {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67632 FWP_UINT64 9223511674839891968 {d6ac0d0d-5259-4e91-b3da-6dd38df859a0} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 3 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 9 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e0026} {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67640 FWP_UINT64 3458904151805657088 {cb5e3e43-13f9-4f37-a3d8-c0a6ee6a157b} Delivery Optimization (UDP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_PROVIDER_MPSSVC_WF 9c00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67668 FWP_UINT64 11528792558725496832 {9c5b7174-b5ba-4ebe-87f3-49d42065c090} Delivery Optimization (UDP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_PROVIDER_MPSSVC_WF 9c00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_RESOURCE_ASSIGNMENT_V6 0 67669 FWP_UINT64 11528792558725496832 {ccb39a7c-ffcc-41e1-b3d5-5508c22d7ae8} Core Networking - Dynamic Host Configuration Protocol (DHCP-In) Allows DHCP (Dynamic Host Configuration Protocol) messages for stateful auto-configuration. FWPM_PROVIDER_MPSSVC_WF 9f00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 68 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67679 FWP_UINT64 11528792558725496832 {6bfcb550-a334-493c-bfa9-0fd5778762d3} Core Networking - Dynamic Host Configuration Protocol for IPv6(DHCPV6-In) Allows DHCPV6 (Dynamic Host Configuration Protocol for IPv6) messages for stateful and stateless configuration. FWPM_PROVIDER_MPSSVC_WF a000000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 546 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67683 FWP_UINT64 11528792558725496832 {f1d77b93-024f-4ced-96fa-583e3777c094} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/Description} FWPM_PROVIDER_MPSSVC_WF b300000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_RESOURCE_ASSIGNMENT_V6 0 67778 FWP_UINT64 10376434004072071168 {4ad1f317-75d8-4c41-b699-586bdc947b61} XING XING FWPM_PROVIDER_MPSSVC_WF b400000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-830604253-3178213212-3804702845-3552981612-1670190244-297742492-2307346640 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_RESOURCE_ASSIGNMENT_V6 0 67790 FWP_UINT64 10376434004072071168 {5856855b-110b-411a-9edb-c83af7970e2a} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_DisplayName} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_Description} FWPM_PROVIDER_MPSSVC_WF b500000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378-1866284433 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_RESOURCE_ASSIGNMENT_V6 0 67802 FWP_UINT64 10376434004072071168 {449e8fbb-acaf-4cfe-a93c-01c8b5a04e2f} Xbox Game Bar Xbox Game Bar FWPM_PROVIDER_MPSSVC_WF b600000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1714399563-1326177402-2048222277-143663168-2151391019-765408921-4098702777 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_RESOURCE_ASSIGNMENT_V6 0 67814 FWP_UINT64 10376434004072071168 {b0f609a0-8de9-4641-93e8-322951f06806} Microsoft Teams Microsoft Teams FWPM_PROVIDER_MPSSVC_WF b800000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f007700730061007000700073005c006d006900630072006f0073006f00660074007400650061006d0073005f00320032003000300036002e003600300030002e0031003100330033002e0037003400300039005f007800360034005f005f003800770065006b007900620033006400380062006200770065005c006d0073007400650061006d0073002e006500780065000000 \device\harddiskvolume3\program files\windowsapps\microsoftteams_22006.600.1133.7409_x64__8wekyb3d8bbwe\msteams.exe FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67826 FWP_UINT64 10376433729194164224 {efbc5936-c530-4944-a424-354d0f15516f} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} FWPM_PROVIDER_MPSSVC_WF b900000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2226957697-3030467180-2301525-4248967783-2024719031-2325529081-2915787518 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_RESOURCE_ASSIGNMENT_V6 0 67834 FWP_UINT64 10376434004072071168 {65a89691-b6bc-453a-ba4f-60b8f2f5afce} Cortana Cortana FWPM_PROVIDER_MPSSVC_WF ba00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1880626798-2296700190-2192216202-2581987570-949377748-777141861-2889999867 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_RESOURCE_ASSIGNMENT_V6 0 67846 FWP_UINT64 10376434004072071168 {2429f54d-a7e7-428b-8b99-db319e9fbeec} Microsoft Edge (mDNS-In) Inbound rule for Microsoft Edge to allow mDNS traffic. FWPM_PROVIDER_MPSSVC_WF bb00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073002000280078003800360029005c006d006900630072006f0073006f00660074005c00650064006700650077006500620076006900650077005c006100700070006c00690063006100740069006f006e005c00390038002e0030002e0031003100300038002e00350036005c006d0073006500640067006500770065006200760069006500770032002e006500780065000000 \device\harddiskvolume3\program files (x86)\microsoft\edgewebview\application\98.0.1108.56\msedgewebview2.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67854 FWP_UINT64 11528792283847589888 {6f59015a-2f44-4527-95c2-ef891e2ee42e} Microsoft Edge (mDNS-In) Inbound rule for Microsoft Edge to allow mDNS traffic. FWPM_PROVIDER_MPSSVC_WF bd00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073002000280078003800360029005c006d006900630072006f0073006f00660074005c0065006400670065005c006100700070006c00690063006100740069006f006e005c006d00730065006400670065002e006500780065000000 \device\harddiskvolume3\program files (x86)\microsoft\edge\application\msedge.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67870 FWP_UINT64 11528792283847589888 {4373e6d8-0cba-416a-8957-5346f3a3810f} Microsoft Edge (mDNS-In) Inbound rule for Microsoft Edge to allow mDNS traffic. FWPM_PROVIDER_MPSSVC_WF be00000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073002000280078003800360029005c006d006900630072006f0073006f00660074005c00650064006700650077006500620076006900650077005c006100700070006c00690063006100740069006f006e005c00390038002e0030002e0031003100300038002e00360032005c006d0073006500640067006500770065006200760069006500770032002e006500780065000000 \device\harddiskvolume3\program files (x86)\microsoft\edgewebview\application\98.0.1108.62\msedgewebview2.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67874 FWP_UINT64 11528792283847589888 {e4be4ec1-3874-4bb7-91f4-ef1252a96e4a} Microsoft Media Foundation Network Source IN [UDP 5004-5009] InBound Rule for the Microsoft Media Foundation's Capture SVC to open UDP port to enable RTSP FWPM_PROVIDER_MPSSVC_WF 1f01000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 5000 FWP_UINT16 5020 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68217 FWP_UINT64 11212414684902719488 {95d5968b-f1ce-43f6-8edd-f24b0776f8b0} WFD ASP Coordination Protocol (UDP-In) Inbound rule for the WLAN Service to allow coordination protocol for WFD Service sessions [UDP 7235] FWPM_PROVIDER_MPSSVC_WF 2201000000000000 "....... FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68237 FWP_UINT64 11528792558725496832 {c98871d1-4f49-4853-9a6e-35cae1fa3f17} mDNS (UDP-In) Inbound rule for mDNS traffic [UDP] FWPM_PROVIDER_MPSSVC_WF 2601000000000000 ........ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-859482183-879914841-863379149-1145462774-2388618682) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68249 FWP_UINT64 11528792558725496832 {8c0045d4-35b0-4a88-bf37-3c5bbe7a37bc} windefend_resource_assignment_v6 windefend FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 {3c1cd879-1b8c-4ab4-8f83-5ed129176ef3} FWP_EMPTY FWP_ACTION_CALLOUT_INSPECTION {ced78e5f-1dd1-485a-9d35-7e44cc9d784d} 0 68279 FWP_UINT64 0 {16dfd892-4fa5-4dc8-bf0f-2e94ffe6f1ba} Microsoft Store Microsoft Store FWPM_PROVIDER_MPSSVC_WF 2d01000000000000 -....... FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_RESOURCE_ASSIGNMENT_V6 0 68290 FWP_UINT64 10376434004072071168 FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD ALE Resource Assignment v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_ALE_PROMISCUOUS_MODE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 39 FWPM_LAYER_ALE_AUTH_LISTEN_V4 ALE Listen v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 40 FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_LISTEN_V4 WFP Built-in Edge Traversal ALE Listen v4 Layer Callout Signals Windows when an application is attempting to listen on an interface and has opted in for edge traversing traffic. FWPM_CALLOUT_FLAG_REGISTERED 00000014 FWPM_LAYER_ALE_AUTH_LISTEN_V4 29 {c3dbed20-0bb6-4bf3-828d-96732e1e0028} Windows Firewall: callout Notifies the user. FWPM_CALLOUT_FLAG_REGISTERED FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_LISTEN_V4 270 {93f12f2b-665e-44f2-a229-f5af613fe690} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e0028} {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67627 FWP_UINT64 9223372036854775840 {8f1a4c58-25f3-4358-84e9-4aaec8ab1398} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 3 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e0028} {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67635 FWP_UINT64 3458764513820540960 {85bce91c-5ad3-4c13-978f-edfd66523835} Delivery Optimization (TCP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_PROVIDER_MPSSVC_WF a300000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWP_ACTION_PERMIT 0 67696 FWP_UINT64 10376293542535364544 {18f77a24-0d44-4919-98e7-f42eb1002af3} Delivery Optimization (TCP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_PROVIDER_MPSSVC_WF a300000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_LISTEN_V4 0 67697 FWP_UINT64 10376293542535364544 {1a3b3a34-55b4-4791-aa77-28e1c9f4f78a} Wireless Display Infrastructure Back Channel (TCP-In) Inbound rule for Wireless Display Infrastructure back channel [TCP] FWPM_PROVIDER_MPSSVC_WF a500000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0063006100730074007300720076002e006500780065000000 \device\harddiskvolume3\windows\system32\castsrv.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7250 FWP_ACTION_PERMIT 0 67709 FWP_UINT64 10376293542535364480 {7fb5698e-c641-4fbc-b851-13c666f5e3aa} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/Description} FWPM_PROVIDER_MPSSVC_WF b300000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_LISTEN_V4 0 67772 FWP_UINT64 10376293541461622976 {c3711cf1-9bff-4f13-abcb-3ffdc68015d3} XING XING FWPM_PROVIDER_MPSSVC_WF b400000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-830604253-3178213212-3804702845-3552981612-1670190244-297742492-2307346640 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_LISTEN_V4 0 67784 FWP_UINT64 10376293541461622976 {fa41fb18-1600-4eb8-a46e-b1e892bdc894} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_DisplayName} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_Description} FWPM_PROVIDER_MPSSVC_WF b500000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378-1866284433 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_LISTEN_V4 0 67796 FWP_UINT64 10376293541461622976 {0ee2b732-6452-42b4-9a5e-bdb97e3a925a} Xbox Game Bar Xbox Game Bar FWPM_PROVIDER_MPSSVC_WF b600000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1714399563-1326177402-2048222277-143663168-2151391019-765408921-4098702777 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_LISTEN_V4 0 67808 FWP_UINT64 10376293541461622976 {a11e4118-9942-4da1-bdbf-698fde8b5697} Microsoft Teams Microsoft Teams FWPM_PROVIDER_MPSSVC_WF b700000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f007700730061007000700073005c006d006900630072006f0073006f00660074007400650061006d0073005f00320032003000300036002e003600300030002e0031003100330033002e0037003400300039005f007800360034005f005f003800770065006b007900620033006400380062006200770065005c006d0073007400650061006d0073002e006500780065000000 \device\harddiskvolume3\program files\windowsapps\microsoftteams_22006.600.1133.7409_x64__8wekyb3d8bbwe\msteams.exe FWP_ACTION_PERMIT 0 67820 FWP_UINT64 10376293541461622912 {066a6860-5fb4-4dd2-bbc9-8bac48b6a4d4} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} FWPM_PROVIDER_MPSSVC_WF b900000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2226957697-3030467180-2301525-4248967783-2024719031-2325529081-2915787518 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_LISTEN_V4 0 67828 FWP_UINT64 10376293541461622976 {185af45b-b8b0-4110-a5a3-9732ccf1a36a} Cortana Cortana FWPM_PROVIDER_MPSSVC_WF ba00000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1880626798-2296700190-2192216202-2581987570-949377748-777141861-2889999867 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_LISTEN_V4 0 67840 FWP_UINT64 10376293541461622976 {6ed9f2bc-535f-4575-a1b3-56e98dfc52ad} Microsoft Media Foundation Network Source IN [TCP 554] InBound Rule for the Microsoft Media Foundation's Capture SVC to open TCP port to enable RTSP FWPM_PROVIDER_MPSSVC_WF 2001000000000000 ....... FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 554 FWP_ACTION_PERMIT 0 68220 FWP_UINT64 10376293542535364544 {86edf865-f5c6-4b10-9eaa-63371a7287a1} Microsoft Media Foundation Network Source IN [TCP 554] InBound Rule for the Microsoft Media Foundation's Capture SVC to open TCP port to enable RTSP FWPM_PROVIDER_MPSSVC_WF 2001000000000000 ....... FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 8554 FWP_UINT16 8558 FWP_ACTION_PERMIT 0 68223 FWP_UINT64 10376293542379542720 {a8c97be3-77f0-4ea7-b5f8-10d91d382e1e} Microsoft Store Microsoft Store FWPM_PROVIDER_MPSSVC_WF 2d01000000000000 -....... FWPM_LAYER_ALE_AUTH_LISTEN_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_EDGE_TRAVERSAL_ALE_LISTEN_V4 0 68284 FWP_UINT64 10376293541461622976 FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD ALE Listen v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 41 FWPM_LAYER_ALE_AUTH_LISTEN_V6 ALE Listen v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 42 FWPM_CALLOUT_TEREDO_ALE_LISTEN_V6 WFP Built-in Edge Traversal ALE Listen v6 Layer Callout Signals Windows when an application is attempting to listen on an interface and has opted in for edge traversing traffic. FWPM_CALLOUT_FLAG_REGISTERED 00000014 FWPM_LAYER_ALE_AUTH_LISTEN_V6 30 {c3dbed20-0bb6-4bf3-828d-96732e1e002a} Windows Firewall: callout Notifies the user. FWPM_CALLOUT_FLAG_REGISTERED FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_LISTEN_V6 271 {a8568747-7e3e-4077-adc4-4ad699d10d4b} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e002a} {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67631 FWP_UINT64 9223372036854775840 {9412dbc5-7e4a-4296-9b6b-309a61de48b4} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 3 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e002a} {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67639 FWP_UINT64 3458764513820540960 {05a88866-6122-4377-9ca8-432cbe8f64c2} Delivery Optimization (TCP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_PROVIDER_MPSSVC_WF a300000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWP_ACTION_PERMIT 0 67701 FWP_UINT64 10376293541528731584 {8b44e0b3-d691-4d4b-a7ff-4ab5c957d05e} Delivery Optimization (TCP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_PROVIDER_MPSSVC_WF a300000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_LISTEN_V6 0 67702 FWP_UINT64 10376293541528731584 {9d45f132-f86f-4c7b-8f71-32000ba2136a} Wireless Display Infrastructure Back Channel (TCP-In) Inbound rule for Wireless Display Infrastructure back channel [TCP] FWPM_PROVIDER_MPSSVC_WF a500000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0063006100730074007300720076002e006500780065000000 \device\harddiskvolume3\windows\system32\castsrv.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7250 FWP_ACTION_PERMIT 0 67711 FWP_UINT64 10376293541528731520 {89449f5a-57ae-4ae5-ac86-e4b758669e07} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/Description} FWPM_PROVIDER_MPSSVC_WF b300000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_LISTEN_V6 0 67777 FWP_UINT64 10376293541461622976 {aedc9943-eac8-497d-acc6-0e7175b5074b} XING XING FWPM_PROVIDER_MPSSVC_WF b400000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-830604253-3178213212-3804702845-3552981612-1670190244-297742492-2307346640 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_LISTEN_V6 0 67789 FWP_UINT64 10376293541461622976 {8a3fa42a-2fab-485a-afad-8bcfbf876471} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_DisplayName} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_Description} FWPM_PROVIDER_MPSSVC_WF b500000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378-1866284433 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_LISTEN_V6 0 67801 FWP_UINT64 10376293541461622976 {09bda0cc-b7d7-4eea-84a5-587667ae638a} Xbox Game Bar Xbox Game Bar FWPM_PROVIDER_MPSSVC_WF b600000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1714399563-1326177402-2048222277-143663168-2151391019-765408921-4098702777 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_LISTEN_V6 0 67813 FWP_UINT64 10376293541461622976 {72f3c3d1-c6ff-4dc5-a385-a4cb9aad45f3} Microsoft Teams Microsoft Teams FWPM_PROVIDER_MPSSVC_WF b700000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f007700730061007000700073005c006d006900630072006f0073006f00660074007400650061006d0073005f00320032003000300036002e003600300030002e0031003100330033002e0037003400300039005f007800360034005f005f003800770065006b007900620033006400380062006200770065005c006d0073007400650061006d0073002e006500780065000000 \device\harddiskvolume3\program files\windowsapps\microsoftteams_22006.600.1133.7409_x64__8wekyb3d8bbwe\msteams.exe FWP_ACTION_PERMIT 0 67822 FWP_UINT64 10376293541461622912 {f4ed1f0f-7a1a-4025-89e2-38e6bebc3db8} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} FWPM_PROVIDER_MPSSVC_WF b900000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2226957697-3030467180-2301525-4248967783-2024719031-2325529081-2915787518 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_LISTEN_V6 0 67833 FWP_UINT64 10376293541461622976 {b452f283-2ec7-44cd-92aa-7b832d56e019} Cortana Cortana FWPM_PROVIDER_MPSSVC_WF ba00000000000000 ........ FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1880626798-2296700190-2192216202-2581987570-949377748-777141861-2889999867 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_LISTEN_V6 0 67845 FWP_UINT64 10376293541461622976 {109e4154-56f4-4c4f-92d4-40cd1dc644ce} Microsoft Media Foundation Network Source IN [TCP 554] InBound Rule for the Microsoft Media Foundation's Capture SVC to open TCP port to enable RTSP FWPM_PROVIDER_MPSSVC_WF 2001000000000000 ....... FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 554 FWP_ACTION_PERMIT 0 68226 FWP_UINT64 10376293541528731584 {ea58a6ce-9d6b-46f2-9d4c-a1977a154b5b} Microsoft Media Foundation Network Source IN [TCP 554] InBound Rule for the Microsoft Media Foundation's Capture SVC to open TCP port to enable RTSP FWPM_PROVIDER_MPSSVC_WF 2001000000000000 ....... FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 8554 FWP_UINT16 8558 FWP_ACTION_PERMIT 0 68229 FWP_UINT64 10376293541518992832 {e65cf284-71bb-4869-b2ba-c9a7051d20fc} Microsoft Store Microsoft Store FWPM_PROVIDER_MPSSVC_WF 2d01000000000000 -....... FWPM_LAYER_ALE_AUTH_LISTEN_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_CALLOUT_TERMINATING FWPM_CALLOUT_TEREDO_ALE_LISTEN_V6 0 68289 FWP_UINT64 10376293541461622976 FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD ALE Listen v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 43 FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 ALE Receive/Accept v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_ALE_REMOTE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_REMOTE_MACHINE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_NAP_CONTEXT FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_NEXTHOP_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_REAUTHORIZE_REASON FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 44 FWPM_CALLOUT_IPSEC_INBOUND_INITIATE_SECURE_V4 WFP Built-in IPsec Inbound Initiate Secure v4 Layer Callout Verifies that each incoming connection that is supposed to arrive secure arrives securely. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 13 FWPM_CALLOUT_TCP_CHIMNEY_ACCEPT_LAYER_V4 WFP Built-in TCP Chimney Offload ALE Receive/Accept v4 Layer Callout Enables or disables TCP Chimney Offload for each incoming connection. FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 21 FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_ALE_ACCEPT_V4 WFP Built-in IPsec Inbound Tunnel ALE Receive/Accept v4 Layer Callout Permits IPsec tunnel mode IP-in-IP packets when they get classified at the ALE receive/accept layer. FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 25 FWPM_CALLOUT_TCP_TEMPLATES_ACCEPT_LAYER_V4 WFP Built-in TCP Templates ALE Receive/Accept v4 Layer Callout Applies TCP Template for each incoming connection. FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 35 FWPM_CALLOUT_SET_OPTIONS_AUTH_RECV_ACCEPT_LAYER_V4 WFP Built-in Set Option ALE Receive/Accept v4 Layer Callout Sets classify options on inbound flows. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 39 FWPM_CALLOUT_POLICY_SILENT_MODE_AUTH_RECV_ACCEPT_LAYER_V4 WFP Built-in Policy Silent Mode ALE Receive/Accept v4 Layer Callout Audit and log policy for incoming connection. FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 43 {c3dbed20-0bb6-4bf3-828d-96732e1e022c} Windows Firewall: callout Allows secondary connections. FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 284 {c970a45d-57f9-4e32-a5bd-886a9662641e} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 8388608 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-0-0 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66214 FWP_UINT64 18446744073709551615 {716b48eb-0a35-4a76-92ab-1d987230d288} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 8388608 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-0-0 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66218 FWP_UINT64 18446744073709551615 {a47525e2-725b-4888-8af1-ba5a60c04f4d} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 0 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66226 FWP_UINT64 0 {13bfd422-6f75-4408-8924-9400ec0cb19c} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 0 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66230 FWP_UINT64 0 {c42f1cd6-3a95-4ae2-a513-793c3ae610c7} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 0 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66235 FWP_UINT64 35165044736 {3697a558-3ed3-49be-a4c1-c1a4448653b4} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 0 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66237 FWP_UINT64 35165044736 {d870c96c-75ee-46a6-8a02-8e4401a73423} Quarantine Default Inbound Block This filter blocks any inbound non-loopback packets when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 0 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_BLOCK {30433c31-b05f-421f-8fde-018ea4c68af4} 66241 FWP_UINT64 34359738368 {4137b143-2770-43d4-91a2-55bb0a069830} Quarantine Default Inbound Tunnel Exception This filter allows for tunneling when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 1 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_PERMIT {30433c31-b05f-421f-8fde-018ea4c68af4} 66247 FWP_UINT64 1152921539503456256 {17043d46-fac2-4561-bca1-0c7a05e95f5f} Quarantine Default Inbound Next Hop Tunnel Exception This filter allows for tunneling when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 1 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT {30433c31-b05f-421f-8fde-018ea4c68af4} 66249 FWP_UINT64 1152921539503456256 {4e718c57-c397-4221-9fbb-14fd51701d6a} Quarantine Default Inbound DHCP Exception This filter allows for DHCP connections when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 68 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 67 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_PERMIT {30433c31-b05f-421f-8fde-018ea4c68af4} 66251 FWP_UINT64 1154899560384954368 {916c0dbf-7cec-40f9-9dd9-a5e68b904510} Quarantine Default Inbound Loopback Exception This filter allows loopback packets when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {30433c31-b05f-421f-8fde-018ea4c68af4} 66599 FWP_UINT64 18446744073709551615 {89a89b7c-b5ab-4ed6-bf05-d3059281a5c5} AppContainerBoottimeFilter Boottime filter for App Containers FWPM_FILTER_FLAG_BOOTTIME FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4194304 FWP_ACTION_PERMIT 0 66995 FWP_UINT64 18446744073709551615 {e72646bc-7d3f-4c5c-a679-b3716f8c6cc8} AppContainerBoottimeFilter Boottime filter for App Containers FWPM_FILTER_FLAG_PERSISTENT FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4194304 FWP_ACTION_PERMIT 0 66997 FWP_UINT64 18446744073709551615 {21c48193-9ccd-4faf-9590-99b6e4c8af2c} AppContainerLoopback This filter allows non-AppContainers to receive loopback traffic from other non-AppContainers FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 8388608 FWP_ACTION_PERMIT {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67190 FWP_UINT64 18446744073709551615 {7a5bb72c-0e75-4ad6-ae8a-9ffd591bb744} AppContainerLoopback This filter allows non-AppContainers to receive loopback traffic from by policy allowed AppContainers FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 16777216 FWP_ACTION_PERMIT {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67192 FWP_UINT64 18446744073709551615 {19a19270-e1ff-433e-a9a7-c2fd8fb46bcb} AppContainerLoopback This filter allows an AppContainer to receive loopback traffic from itself FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4194304 FWP_ACTION_PERMIT {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67194 FWP_UINT64 18446744073709551615 {1ea45d61-721b-47aa-9b19-81422ba8fdec} AppContainerLoopback This filter blocks AppContainer loopback traffic FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551614 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:LSD:(A;;CC;;;AC)(A;;CC;;;S-1-15-3-1)(A;;CC;;;S-1-15-3-2)(A;;CC;;;S-1-15-3-3)(A;;CC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833)(A;;CC;;;WD)(A;;CC;;;AN) FWP_ACTION_BLOCK {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67200 FWP_UINT64 18446744073709551614 {558d57ba-cc4e-4bde-8ea0-8120605580b4} Interface Un-quarantine filter FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 0 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWP_MATCH_EQUAL FWP_UINT64 0 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWP_MATCH_EQUAL FWP_UINT64 6755399441055744 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_PERMIT 0 67453 FWP_UINT64 4503633987108866 {4ff11138-2d2f-4fcf-a884-d0be3936bc57} Interface Un-quarantine filter FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 0 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWP_MATCH_EQUAL FWP_UINT64 0 FWPM_CONDITION_IP_NEXTHOP_INTERFACE FWP_MATCH_EQUAL FWP_UINT64 6755399441055744 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67454 FWP_UINT64 4503633987108866 {b1686938-d80f-429d-b7d8-735a34b3605b} Interface Un-quarantine filter FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 0 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWP_MATCH_EQUAL FWP_UINT64 10000001 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWP_MATCH_EQUAL FWP_UINT64 1689399632855040 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_PERMIT 0 67457 FWP_UINT64 4503633987108866 {f708e179-0005-47bc-9659-9c9af4525161} Interface Un-quarantine filter FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 0 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWP_MATCH_EQUAL FWP_UINT64 10000001 FWPM_CONDITION_IP_NEXTHOP_INTERFACE FWP_MATCH_EQUAL FWP_UINT64 1689399632855040 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67458 FWP_UINT64 4503633987108866 {3668a51e-18e1-4b90-a348-1b26820075ee} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_BLOCK {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67630 FWP_UINT64 9223372036854776704 {996c5681-e762-4131-ba41-a4b2ba434af4} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 3 FWP_ACTION_BLOCK {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67638 FWP_UINT64 3458764513820540928 {e4bf8fce-257a-409d-82a9-c1d0de1706ef} UWP Default Inbound Block Rule This is the UWP Default Inbound Block filter. This filter blocks any inbound packets to UWP apps that do not have the correct capability tokens for listening to a resource. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_APP_ISOLATION 9900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_EMPTY FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_NOT_EQUAL FWP_SID S-1-0-0 FWP_ACTION_BLOCK {b36473ef-bf42-49b9-ac24-adba245e825c} 67657 FWP_UINT64 549755813888 {f6ac8f41-9cbd-478e-97ca-93406509632b} Core Networking - IPv6 (IPv6-In) Inbound rule required to permit IPv6 traffic for ISATAP (Intra-Site Automatic Tunnel Addressing Protocol) and 6to4 tunnelling services. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9b00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 41 FWP_ACTION_PERMIT 0 67661 FWP_UINT64 10377982941077700608 {46ea0820-54e9-4671-9ee4-b5efa03e7fb2} Delivery Optimization (UDP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9c00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67665 FWP_UINT64 10378264690932318208 {9ed57c1d-5a7c-4821-85c1-bb4d5b005495} Delivery Optimization (UDP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9c00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67666 FWP_UINT64 10378264691737624576 {20aa0d80-0876-40ab-9060-81fa4852b804} Delivery Optimization (UDP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9c00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67667 FWP_UINT64 10378264726097362944 {1d948ce2-c67e-4612-9a13-a86146c93899} Core Networking - Destination Unreachable Fragmentation Needed (ICMPv4-In) Destination Unreachable Fragmentation Needed error messages are sent from any node that a packet traverses which is unable to forward the packet because fragmentation was needed and the don't fragment bit was set. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9e00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 4 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 1 FWP_ACTION_PERMIT 0 67676 FWP_UINT64 10378272112635805696 {4a6c385c-4d41-422e-80e2-a0a8ed98e329} Core Networking - Dynamic Host Configuration Protocol (DHCP-In) Allows DHCP (Dynamic Host Configuration Protocol) messages for stateful auto-configuration. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9f00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 68 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 67 FWP_ACTION_PERMIT 0 67678 FWP_UINT64 10378272387513712640 {9858d0f0-9bcd-4854-b300-54482931a983} Core Networking - Dynamic Host Configuration Protocol for IPv6(DHCPV6-In) Allows DHCPV6 (Dynamic Host Configuration Protocol for IPv6) messages for stateful and stateless configuration. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a000000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 546 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 547 FWP_ACTION_PERMIT 0 67682 FWP_UINT64 10378272387513712640 {d864789f-7916-44d6-92d2-89c3cb3339ce} Delivery Optimization (TCP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67698 FWP_UINT64 10378264690932318208 {fa6ddfcb-e0e4-4e81-851e-bcc403c08a55} Delivery Optimization (TCP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67699 FWP_UINT64 10378264691737624576 {626051d7-734b-4953-9c98-0b07643b6ca2} Delivery Optimization (TCP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67700 FWP_UINT64 10378264726097362944 {db7d8fea-b0d0-47b8-ac4c-926e2d6fa47f} Wireless Display Infrastructure Back Channel (TCP-In) Inbound rule for Wireless Display Infrastructure back channel [TCP] FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0063006100730074007300720076002e006500780065000000 \device\harddiskvolume3\windows\system32\castsrv.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7250 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67710 FWP_UINT64 10378264416054411264 {be661f2c-e574-4046-8c33-f35b7d29a7e4} Core Networking - Internet Group Management Protocol (IGMP-In) IGMP messages are sent and received by nodes to create, join and depart multicast groups. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ac00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 2 FWP_ACTION_PERMIT 0 67747 FWP_UINT64 10377982941077700608 {ead521c1-9552-4d43-8a5c-ceb57aca1dc8} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 67774 FWP_UINT64 10376294366095343616 {1c30209e-d2ce-4392-9e8f-f377efb005e5} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67775 FWP_UINT64 10376294366900649984 {22519d4f-2ea3-4e42-8963-d6f0b7733aa2} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67776 FWP_UINT64 10376294401260388352 {29c7768e-567b-4383-9274-88ce90b281c8} XING XING FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-830604253-3178213212-3804702845-3552981612-1670190244-297742492-2307346640 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 67786 FWP_UINT64 10376294366095343616 {d6953602-d25c-44c8-89be-32af54554764} XING XING FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-830604253-3178213212-3804702845-3552981612-1670190244-297742492-2307346640 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67787 FWP_UINT64 10376294366900649984 {1d52280a-5200-4f1b-b9f2-ff576ef4721e} XING XING FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-830604253-3178213212-3804702845-3552981612-1670190244-297742492-2307346640 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67788 FWP_UINT64 10376294401260388352 {22d44aac-6653-4e7b-aa73-9e3f59633d4f} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_DisplayName} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378-1866284433 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 67798 FWP_UINT64 10376294366095343616 {d6346862-1e03-4aeb-ba63-df5639ea6fec} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_DisplayName} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378-1866284433 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67799 FWP_UINT64 10376294366900649984 {6f8d75fc-f3b5-4016-81e2-4f2df77168cf} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_DisplayName} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378-1866284433 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67800 FWP_UINT64 10376294401260388352 {c041bcbc-c3b0-4002-9e12-424d925ac610} Xbox Game Bar Xbox Game Bar FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1714399563-1326177402-2048222277-143663168-2151391019-765408921-4098702777 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 67810 FWP_UINT64 10376294366095343616 {ed0b7196-2d26-4cf8-8de9-ae943554347d} Xbox Game Bar Xbox Game Bar FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1714399563-1326177402-2048222277-143663168-2151391019-765408921-4098702777 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67811 FWP_UINT64 10376294366900649984 {d45600c9-f182-43ae-b1ea-40ebdeea9b99} Xbox Game Bar Xbox Game Bar FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1714399563-1326177402-2048222277-143663168-2151391019-765408921-4098702777 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67812 FWP_UINT64 10376294401260388352 {b6cc72ea-22a1-483e-9636-090bb5302940} Microsoft Teams Microsoft Teams FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b700000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f007700730061007000700073005c006d006900630072006f0073006f00660074007400650061006d0073005f00320032003000300036002e003600300030002e0031003100330033002e0037003400300039005f007800360034005f005f003800770065006b007900620033006400380062006200770065005c006d0073007400650061006d0073002e006500780065000000 \device\harddiskvolume3\program files\windowsapps\microsoftteams_22006.600.1133.7409_x64__8wekyb3d8bbwe\msteams.exe FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67821 FWP_UINT64 10377982941077700608 {5bd7313e-64c8-4956-a5d5-a598afeeaea0} Microsoft Teams Microsoft Teams FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b800000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f007700730061007000700073005c006d006900630072006f0073006f00660074007400650061006d0073005f00320032003000300036002e003600300030002e0031003100330033002e0037003400300039005f007800360034005f005f003800770065006b007900620033006400380062006200770065005c006d0073007400650061006d0073002e006500780065000000 \device\harddiskvolume3\program files\windowsapps\microsoftteams_22006.600.1133.7409_x64__8wekyb3d8bbwe\msteams.exe FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67825 FWP_UINT64 10377982941077700608 {ec36ba3d-18ff-43c9-a323-bd61ebf5cdf9} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2226957697-3030467180-2301525-4248967783-2024719031-2325529081-2915787518 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 67830 FWP_UINT64 10376294366095343616 {50aedebf-9228-41e5-826c-880c0dfeeeb9} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2226957697-3030467180-2301525-4248967783-2024719031-2325529081-2915787518 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67831 FWP_UINT64 10376294366900649984 {fce876c5-0650-4cf0-a8e5-32763f4572db} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2226957697-3030467180-2301525-4248967783-2024719031-2325529081-2915787518 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67832 FWP_UINT64 10376294401260388352 {8075d897-3b61-4b0d-a1b7-8cf360304598} Cortana Cortana FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ba00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1880626798-2296700190-2192216202-2581987570-949377748-777141861-2889999867 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 67842 FWP_UINT64 10376294366095343616 {e2918c6c-304d-4dba-a100-4c19a688541d} Cortana Cortana FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ba00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1880626798-2296700190-2192216202-2581987570-949377748-777141861-2889999867 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67843 FWP_UINT64 10376294366900649984 {41000d2a-eba0-40b3-93b1-42d9352f0b1f} Cortana Cortana FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ba00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1880626798-2296700190-2192216202-2581987570-949377748-777141861-2889999867 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67844 FWP_UINT64 10376294401260388352 {e7abd6b3-64e1-456b-9de4-80bd51c45892} Microsoft Edge (mDNS-In) Inbound rule for Microsoft Edge to allow mDNS traffic. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF bb00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073002000280078003800360029005c006d006900630072006f0073006f00660074005c00650064006700650077006500620076006900650077005c006100700070006c00690063006100740069006f006e005c00390038002e0030002e0031003100300038002e00350036005c006d0073006500640067006500770065006200760069006500770032002e006500780065000000 \device\harddiskvolume3\program files (x86)\microsoft\edgewebview\application\98.0.1108.56\msedgewebview2.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67853 FWP_UINT64 10378264416054411264 {3474e056-188f-44cd-bf0e-f2b9395d00b1} Microsoft Edge (mDNS-In) Inbound rule for Microsoft Edge to allow mDNS traffic. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF bd00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073002000280078003800360029005c006d006900630072006f0073006f00660074005c0065006400670065005c006100700070006c00690063006100740069006f006e005c006d00730065006400670065002e006500780065000000 \device\harddiskvolume3\program files (x86)\microsoft\edge\application\msedge.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67869 FWP_UINT64 10378264416054411264 {3bba491b-aba5-4198-9599-0560f76b1c4d} Microsoft Edge (mDNS-In) Inbound rule for Microsoft Edge to allow mDNS traffic. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF be00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073002000280078003800360029005c006d006900630072006f0073006f00660074005c00650064006700650077006500620076006900650077005c006100700070006c00690063006100740069006f006e005c00390038002e0030002e0031003100300038002e00360032005c006d0073006500640067006500770065006200760069006500770032002e006500780065000000 \device\harddiskvolume3\program files (x86)\microsoft\edgewebview\application\98.0.1108.62\msedgewebview2.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67873 FWP_UINT64 10378264416054411264 {6236913c-4c2a-430b-817d-4c4fcde825ee} Allow inbound UDP traffic to CDPSvc port 5050 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c200000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5050 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67878 FWP_UINT64 1971149470695424 {66924806-dccf-403e-97a9-14fcc9d64f9a} Allow inbound TCP traffic to CDPSvc from any port to port 5160 (Wi-Fi Direct Transport) FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c700000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5160 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67888 FWP_UINT64 1971149470695424 {1cf98e43-5bb6-4020-8666-ff5dc7a76123} Allow inbound TCP traffic to CDPSvc from any port to port 5040 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5040 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67892 FWP_UINT64 1971149470695424 {215327fd-5d26-4b42-9e18-ee1e1f871285} Block inbound traffic to omadmclient.exe FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH ca00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c006f006d00610064006d0063006c00690065006e0074002e006500780065000000 \device\harddiskvolume3\windows\system32\omadmclient.exe FWP_ACTION_BLOCK {a8b55da6-3aba-41f3-b7ba-443233f64fce} 67894 FWP_UINT64 549755813888 {9bce6a19-81a8-4ff2-8fe8-e63940b2f706} Block inbound traffic to deviceenroller.exe FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH cb00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0064006500760069006300650065006e0072006f006c006c00650072002e006500780065000000 \device\harddiskvolume3\windows\system32\deviceenroller.exe FWP_ACTION_BLOCK {c4e61e5c-3e66-4efb-b22b-87f8e56cf9c2} 67896 FWP_UINT64 549755813888 {2ea83abb-f047-4edc-857d-4776be7ddb86} Block inbound traffic to dmcertinst.exe FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH d000000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0064006d00630065007200740069006e00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\dmcertinst.exe FWP_ACTION_BLOCK {616fdcf3-1169-46b5-8224-cffc7276af2e} 67906 FWP_UINT64 549755813888 {dd658fab-9acd-48c8-9faf-a0e67d88ca4c} DhcpFirewallPolicy DhcpFirewallPolicy FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 68 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 67 FWP_ACTION_PERMIT 0 67916 FWP_UINT64 1978846052089856 {38119412-b39e-48a6-8fcd-1e7dedc4be5e} DhcpFirewallPolicy DhcpFirewallPolicy FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 546 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 547 FWP_ACTION_PERMIT 0 67918 FWP_UINT64 1978846052089856 {f4e952f7-52be-4ea6-abb2-90f88debea25} Allow RPC/TCP traffic to EventLog FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-880578595-1860270145-482643319-2788375705-1540778122) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67924 FWP_UINT64 1689691673853952 {c76886bf-c4ec-4208-8833-3b243baba297} Allow inbound UDP traffic to fdphost port 3702 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH da00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67926 FWP_UINT64 1971149470695424 {01569aae-88e0-4cfd-98c5-714f5a86ee63} Allow inbound UDP traffic to fdphost port 1900 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH dc00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 1900 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67930 FWP_UINT64 1971149470695424 {4e359e41-d670-4727-a4df-48984e785c40} Allow inbound TCP traffic to AJRouter FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3532809085-2652327567-2620918877-1058261733-582902671) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 9955 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67940 FWP_UINT64 1971149470695424 {88e8faac-2f47-487a-a93f-709d1cb0bf7b} Allow inbound UDP traffic to AJRouter FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e200000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3532809085-2652327567-2620918877-1058261733-582902671) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67942 FWP_UINT64 1689674493984768 {ac4aaa1b-a5f2-41fa-b624-39f34a249fe4} Allow Grouping to receive from port 3587 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1971585524-2528565899-3324366483-1300752743-2325226580) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3587 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67948 FWP_UINT64 1971149470695424 {1f67d6c0-f1de-4524-a4a7-8a93b03238b2} Allow PNRP to send to port 3540 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e700000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-372467825-374176116-1198570892-3192490889-1232022613) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3540 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67952 FWP_UINT64 1971149470695424 {c68ac2bf-ad79-4aaf-a764-a9de591a17f3} IPsec Policy Agent service hardening - Remote Management Allow IPsec Policy Agent inbound RPC/TCP traffic for Remote Management FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH eb00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3044542841-3639452079-4096941652-1606687743-1256249853) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67960 FWP_UINT64 1689691673853952 {9b2018aa-486c-4b5e-9a4c-a1b96ecf2cbc} Block all inbound traffic to SearchFilterHost FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH ec00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c00730065006100720063006800660069006c0074006500720068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\searchfilterhost.exe FWP_ACTION_BLOCK {3a5e8a34-82df-4083-a6ed-87ea8ed3507a} 67962 FWP_UINT64 549755813888 {27db903a-7e87-41ff-8a17-31ca20c9fb16} Block all inbound traffic to SearchProtocolHost FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH ef00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c00730065006100720063006800700072006f0074006f0063006f006c0068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\searchprotocolhost.exe FWP_ACTION_BLOCK {c0cedbe2-34f4-4491-bd19-152a55b1b6e0} 67968 FWP_UINT64 549755813888 {bd84fe34-f5b3-48c8-90f7-3f647e9bebe3} Allow inbound UDP traffic to SNMPTRAP service FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f000000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073006e006d00700074007200610070002e006500780065000000 \device\harddiskvolume3\windows\system32\snmptrap.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3964583643-2633443559-2834438935-3739664028-1580655619) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67970 FWP_UINT64 1689674493984768 {c718ce9d-2f72-430c-beeb-789fa9d7c5ea} Allow all inbound TCP and RPC to SPPEXTCOMOBJ FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007000700065007800740063006f006d006f0062006a002e006500780065000000 \device\harddiskvolume3\windows\system32\sppextcomobj.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3895724531-1583119856-3186271294-3795977770-3211684703) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67972 FWP_UINT64 1689674493984768 {bdc0147f-ebdd-4b25-a20a-5c3ed10cbd3f} TermServiceLOM FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-446051430-1559341753-4161941529-1950928533-810483104) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67976 FWP_UINT64 1689674493984768 {3ddc2b0e-33e3-417b-a578-019a4df4b502} TermServiceLOM FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_UINT64 18446744073709551615 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-446051430-1559341753-4161941529-1950928533-810483104) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_INSPECTION FWPM_CALLOUT_SET_OPTIONS_AUTH_RECV_ACCEPT_LAYER_V4 {2ea412db-5c9d-4bbd-9af2-4914d2dbfe17} 67977 FWP_UINT64 18446744073709551615 {d9a39ed3-698d-434e-83e1-f8a75a5b6a31} Allow incoming RPC traffic to VDS FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c007600640073002e006500780065000000 \device\harddiskvolume3\windows\system32\vds.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2196396108-1448510645-203779624-3888580976-3789157697) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67980 FWP_UINT64 1689691673853952 {ff843ce9-c5e3-49a0-84d2-cdfa59928fc7} Allow inbound TCP ports 389 and 636 traffic for vmicheartbeat FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-534935901-3437432317-481271085-1710633381-983106267) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 389 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67982 FWP_UINT64 1971149470695424 {810b03cc-8f67-4991-bf02-fe78e19477c2} Allow inbound TCP ports 389 and 636 traffic for vmicheartbeat FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-534935901-3437432317-481271085-1710633381-983106267) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 636 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67983 FWP_UINT64 1971149470695424 {4e788a24-1c9a-4fe2-bfbc-a7c10bd5bd7e} Allow inbound UDP traffic to NTP port 123 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f700000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-4267341169-2882910712-659946508-2704364837-2204554466) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 123 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67988 FWP_UINT64 1971149470695424 {b14b6b1e-1bfe-4dfd-8a8e-393da4d4b226} Allow inbound RPC traffic to the Block Level Backup service (wbengine) over TCP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c007700620065006e00670069006e0065002e006500780065000000 \device\harddiskvolume3\windows\system32\wbengine.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1549550529-11381693-4027442525-4081535042-2424139505) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67992 FWP_UINT64 1689691673853952 {865a8428-bb5e-4b7b-8473-b06bf1b69edb} Cast to Device streaming server hardening rules for RTSP Allow incoming RTSP connections to the Cast to Device streaming server FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0201000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c006d00640065007300650072007600650072002e006500780065000000 \device\harddiskvolume3\windows\system32\mdeserver.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 23554 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68014 FWP_UINT64 1970874592788480 {d12a44b6-3150-4214-997f-0735fad61441} Cast to Device streaming server hardening rules for RTSP Allow incoming RTSP connections to the Cast to Device streaming server FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0201000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c006d00640065007300650072007600650072002e006500780065000000 \device\harddiskvolume3\windows\system32\mdeserver.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 23555 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68015 FWP_UINT64 1970874592788480 {690e53fb-c3c2-4e74-a58a-b627852bff69} Cast to Device streaming server hardening rules for RTSP Allow incoming RTSP connections to the Cast to Device streaming server FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0201000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c006d00640065007300650072007600650072002e006500780065000000 \device\harddiskvolume3\windows\system32\mdeserver.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 23556 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68016 FWP_UINT64 1970874592788480 {c3b150a5-0e63-4acf-99d8-93fa101c9f4c} Allow incoming TCP to PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0301000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68020 FWP_UINT64 1689674493984768 {cc829f3a-8ac5-461d-bf9e-b53fa92bf4a6} Windows Media Player Network Sharing Service service hardening - RTSP Allow incoming RTSP connections to the Windows Media Player Network Sharing Service FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0901000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f007700730020006d006500640069006100200070006c0061007900650072005c0077006d0070006e006500740077006b002e006500780065000000 \device\harddiskvolume3\program files\windows media player\wmpnetwk.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2375682873-768044350-3534595160-1005545032-2873800392) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 554 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68040 FWP_UINT64 1971149470695424 {81b5a3ad-8373-4dda-8234-524b6d6411f7} Windows Media Player Network Sharing Service service hardening - RTSP Allow incoming RTSP connections to the Windows Media Player Network Sharing Service FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0901000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f007700730020006d006500640069006100200070006c0061007900650072005c0077006d0070006e006500740077006b002e006500780065000000 \device\harddiskvolume3\program files\windows media player\wmpnetwk.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2375682873-768044350-3534595160-1005545032-2873800392) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 8554 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68041 FWP_UINT64 1971149470695424 {6c2e5205-ac93-4a93-945a-9d07d7f97ff7} WSH Default Inbound Block Blocks all inbound traffic for services who have been network hardened FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE D:(A;NP;CC;;;S-1-5-80-2676549577-1911656217-2625096541-4178041876-1366760775)(A;NP;CC;;;S-1-5-80-1580948945-3239616721-2529237571-3761093093-1214243633)(A;NP;CC;;;S-1-5-80-1058592404-331734164-3167594226-3910907650-1299295147)(A;NP;CC;;;S-1-5-80-1383147646-27650227-2710666058-1662982300-1023958487)(A;NP;CC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264)(A;NP;CC;;;S-1-5-80-2611951811-1959136347-1062071333-3982815153-2811717512)(A;NP;CC;;;S-1-5-80-2839768381-3691089589-2614646340-3191585287-3380622033)(A;NP;CC;;;S-1-5-80-538170410-2190149038-799223143-2506663053-4165713448)(A;NP;CC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582)(A;NP;CC;;;S-1-5-80-3914275374-678031271-1603343729-3906112567-2888048264)(A;NP;CC;;;S-1-5-80-3787436395-2174616005-3003730137-1094982900-1570567328)(A;NP;CC;;;S-1-5-80-2970612574-78537857-698502321-558674196-1451644582)(A;NP;CC;;;S-1-5-80-880578595-1860270145-482643319-2788375705-1540778122)(A;NP;CC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675)(A;NP;CC;;;S-1-5-80-2617507558-3328795327-711547822-311560295-1636921165)(A;NP;CC;;;S-1-5-80-89818136-74175777-88572358-3912780041-2421659406)(A;NP;CC;;;S-1-5-80-172094073-716411664-54255058-185476446-2329512179)(A;NP;CC;;;S-1-5-80-3532809085-2652327567-2620918877-1058261733-582902671)(A;NP;CC;;;S-1-5-80-3088073201-1464728630-1879813800-1107566885-823218052)(A;NP;CC;;;S-1-5-80-2898649604-2335086160-1904548223-3761738420-3855444835)(A;NP;CC;;;S-1-5-80-1971585524-2528565899-3324366483-1300752743-2325226580)(A;NP;CC;;;S-1-5-80-967499406-1694984581-2959056265-2481940682-939264259)(A;NP;CC;;;S-1-5-80-1948712186-1330865447-943413596-1669284603-1648638051)(A;NP;CC;;;S-1-5-80-3596911058-2952229928-1888671852-1743692427-614402820)(A;NP;CC;;;S-1-5-80-372467825-374176116-1198570892-3192490889-1232022613)(A;NP;CC;;;S-1-5-80-3141781312-1794533130-3616533224-2008760771-2116720301)(A;NP;CC;;;S-1-5-80-3044542841-3639452079-4096941652-1606687743-1256249853)(A;NP;CC;;;S-1-5-80-117416528-2204451360-1913602512-1355018040-1234992034)(A;NP;CC;;;S-1-5-80-3964583643-2633443559-2834438935-3739664028-1580655619)(A;NP;CC;;;S-1-5-80-3895724531-1583119856-3186271294-3795977770-3211684703)(A;NP;CC;;;S-1-5-80-2590341223-3996088049-3993122417-23640849-324535191)(A;NP;CC;;;S-1-5-80-949921180-3923668869-394927020-528789358-3592448931)(A;NP;CC;;;S-1-5-80-768763963-4214222998-2156221936-2953597973-713500239)(A;NP;CC;;;S-1-5-80-2014626298-1656748749-3847481816-918933055-2469338456)(A;NP;CC;;;S-1-5-80-1989757894-211065159-731672622-1783776043-3948168785)(A;NP;CC;;;S-1-5-80-2196396108-1448510645-203779624-3888580976-3789157697)(A;NP;CC;;;S-1-5-80-3074984378-4122987768-2130325677-2031866499-3405430279)(A;NP;CC;;;S-1-5-80-534935901-3437432317-481271085-1710633381-983106267)(A;NP;CC;;;S-1-5-80-1877308096-3090306141-3032871208-3115266146-1400827410)(A;NP;CC;;;S-1-5-80-3076811988-2254870394-2658297454-3934945422-2393138642)(A;NP;CC;;;S-1-5-80-3110303136-3426481729-3186938678-1087894076-2178433439)(A;NP;CC;;;S-1-5-80-3098585136-2538892366-1097114017-2832417424-2016953023)(A;NP;CC;;;S-1-5-80-235582178-102246843-358262472-4132936818-1867412993)(A;NP;CC;;;S-1-5-80-1752088424-1054500994-3489791022-3310831482-3926524968)(A;NP;CC;;;S-1-5-80-1549550529-11381693-4027442525-4081535042-2424139505)(A;NP;CC;;;S-1-5-80-4155767994-3874329934-3800885181-2130851812-726865888)(A;NP;CC;;;S-1-5-80-3524758515-3090971750-345616940-2322499744-3530715838)(A;NP;CC;;;S-1-5-80-3299868208-4286319593-1091140620-3583751967-1732444380)(A;NP;CC;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736)(A;NP;CC;;;S-1-5-80-2455429942-3131183193-3617688776-595395669-3772047725)(A;NP;CC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142)(A;NP;CC;;;S-1-5-80-3916113136-2435487254-2535488001-4050622930-2364918814)(A;NP;CC;;;S-1-5-80-3232712927-1625117661-2590453128-1738570065-3637376297)(A;NP;CC;;;S-1-5-80-3981856537-581775623-1136376035-2066872258-409572886)(A;NP;CC;;;S-1-5-80-689100834-1985168674-2379302174-2224748125-4125308070)(A;NP;CC;;;S-1-5-80-2119957892-4152124429-3625998117-4006912763-1737903618)(A;NP;CC;;;S-1-5-80-1987853863-1639573247-1110726908-1137832616-3599624523)(A;NP;CC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329)(A;NP;CC;;;S-1-5-80-113310567-2163499630-2787090463-221477905-209227094)(A;NP;CC;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736)S:NO_ACCESS_CONTROL FWP_ACTION_BLOCK {de04f4bd-a953-47f6-b1b6-eb6e24a546d6} 68047 FWP_UINT64 274877906944 {36e4f41f-09b7-4858-a3f7-1f07b2f62dcf} FTP Inspection Filter This filter enables inspection of FTP. FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 1024 FWP_UINT16 65535 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e022c} 0 68052 FWP_UINT64 18446744073709551615 {52c05a69-b7bd-471e-90f6-b4165265dee2} InternetClientServer Inbound Default Rule InternetClientServer Inbound Default Rule FWPM_PROVIDER_MPSSVC_APP_ISOLATION 1c01000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_EMPTY FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_NOT_EQUAL FWP_SID S-1-0-0 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 0.0.0.0 FWP_UINT32 255.255.255.255 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:LSD:(A;;CC;;;S-1-15-3-2)(A;;CC;;;WD)(A;;CC;;;AN) FWP_ACTION_PERMIT 0 68208 FWP_UINT64 824633721848 {de8c0919-fdae-4263-9186-3209ef7a9203} Microsoft Media Foundation Network Source IN [UDP 5004-5009] InBound Rule for the Microsoft Media Foundation's Capture SVC to open UDP port to enable RTSP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 1f01000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 5000 FWP_UINT16 5020 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68215 FWP_UINT64 10882588684629049344 {2f69ffa4-769a-4f40-a565-b54847821e5b} Microsoft Media Foundation Network Source IN [UDP 5004-5009] InBound Rule for the Microsoft Media Foundation's Capture SVC to open UDP port to enable RTSP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 1f01000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 5000 FWP_UINT16 5020 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68216 FWP_UINT64 10882588718988787712 {11dd8bd8-1210-4a7e-adb2-460cf2b6a1c1} Microsoft Media Foundation Network Source IN [TCP 554] InBound Rule for the Microsoft Media Foundation's Capture SVC to open TCP port to enable RTSP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2001000000000000 ....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 554 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68221 FWP_UINT64 10882667849466249216 {d9e43dad-f679-4f2f-973d-26827bb8c8d3} Microsoft Media Foundation Network Source IN [TCP 554] InBound Rule for the Microsoft Media Foundation's Capture SVC to open TCP port to enable RTSP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2001000000000000 ....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 554 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68222 FWP_UINT64 10882667883825987584 {9ce6ee0e-7cfb-4b36-bc70-a57d7a63f100} Microsoft Media Foundation Network Source IN [TCP 554] InBound Rule for the Microsoft Media Foundation's Capture SVC to open TCP port to enable RTSP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2001000000000000 ....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 8554 FWP_UINT16 8558 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68224 FWP_UINT64 10882623869001138176 {80750116-2e93-43ec-8465-40d3dbf5c900} Microsoft Media Foundation Network Source IN [TCP 554] InBound Rule for the Microsoft Media Foundation's Capture SVC to open TCP port to enable RTSP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2001000000000000 ....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 8554 FWP_UINT16 8558 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68225 FWP_UINT64 10882623903360876544 {e43d0b2d-6a0f-432b-91a1-504f070a7035} WFD ASP Coordination Protocol (UDP-In) Inbound rule for the WLAN Service to allow coordination protocol for WFD Service sessions [UDP 7235] FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2201000000000000 "....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWP_ACTION_PERMIT 0 68235 FWP_UINT64 10882675546047643648 {1c5d0851-defc-4241-8f7f-20f4ce925d56} WFD ASP Coordination Protocol (UDP-In) Inbound rule for the WLAN Service to allow coordination protocol for WFD Service sessions [UDP 7235] FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2201000000000000 "....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68236 FWP_UINT64 10882675580407382016 {353edb90-351b-439f-bb1a-499f3ee0d611} mDNS (UDP-In) Inbound rule for mDNS traffic [UDP] FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2601000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-859482183-879914841-863379149-1145462774-2388618682) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68247 FWP_UINT64 10882667849466249216 {acfb0f39-be2d-43a6-a260-1b6ea1aa107d} mDNS (UDP-In) Inbound rule for mDNS traffic [UDP] FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2601000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-859482183-879914841-863379149-1145462774-2388618682) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68248 FWP_UINT64 10882667883825987584 {9f4f259a-998a-4cad-b318-aeab2fccb2d1} Wi-Fi Direct ASP Coordination Protocol (UDP-In) FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2701000000000000 '....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWP_ACTION_PERMIT 0 68252 FWP_UINT64 506382004586020864 {826bf713-3f53-422d-9ab6-f66251968bdf} Wi-Fi Direct ASP Coordination Protocol (UDP-In) FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2701000000000000 '....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68253 FWP_UINT64 506382038945759232 {f7b10da2-52b3-4496-af84-a4141986b1fb} Allow incoming WSD to PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2901000000000000 )....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68260 FWP_UINT64 506374308004626432 {a2e9e943-0681-4a0e-8c58-485b3ceff496} Allow incoming WSD to PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2901000000000000 )....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68261 FWP_UINT64 506374342364364800 {0ae65187-1f62-4d22-9fde-4f60caac6393} Allow incoming WSD to PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2a01000000000000 *....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWP_ACTION_PERMIT 0 68264 FWP_UINT64 506100529609310208 {7184c665-64f2-476c-af5a-01cc74b700da} Allow incoming WSD to PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2a01000000000000 *....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68265 FWP_UINT64 506100563969048576 {d69d41e1-ee28-47d3-90a0-4e0eef6a868f} Microsoft Store Microsoft Store FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2d01000000000000 -....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 68286 FWP_UINT64 10376294366095343616 {4442f710-abac-421f-9d4f-f02939d23c63} Microsoft Store Microsoft Store FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2d01000000000000 -....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 68287 FWP_UINT64 10376294366900649984 {d68c9ba7-1425-44da-b537-7642ce5bbe9d} Microsoft Store Microsoft Store FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2d01000000000000 -....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68288 FWP_UINT64 10376294401260388352 FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD ALE Receive/Accept v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_ALE_REMOTE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_REMOTE_MACHINE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_NAP_CONTEXT FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_NEXTHOP_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_REAUTHORIZE_REASON FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 45 {c3dbed20-0bb6-4bf3-828d-96732e1e012d} Windows Firewall: callout Performs logging. FWPM_CALLOUT_FLAG_REGISTERED 00000200 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD 278 FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 ALE Receive/Accept v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_ALE_REMOTE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_REMOTE_MACHINE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_NAP_CONTEXT FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_NEXTHOP_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_REAUTHORIZE_REASON FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 46 FWPM_CALLOUT_IPSEC_INBOUND_INITIATE_SECURE_V6 WFP Built-in IPsec Inbound Initiate Secure v6 Layer Callout Verifies that each incoming connection that is supposed to arrive secure arrives securely. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 14 FWPM_CALLOUT_TCP_CHIMNEY_ACCEPT_LAYER_V6 WFP Built-in TCP Chimney Offload ALE Receive/Accept v6 Layer Callout Enables or disables TCP Chimney Offload for each incoming connection. FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 22 FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_ALE_ACCEPT_V6 WFP Built-in IPsec Inbound Tunnel ALE Receive/Accept v6 Layer Callout Permits IPsec tunnel mode IP-in-IP packets when they get classified at the ALE receive/accept layer. FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 26 FWPM_CALLOUT_TCP_TEMPLATES_ACCEPT_LAYER_V6 WFP Built-in TCP Templates ALE Receive/Accept v6 Layer Callout Applies TCP Template for each incoming connection. FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 36 FWPM_CALLOUT_SET_OPTIONS_AUTH_RECV_ACCEPT_LAYER_V6 WFP Built-in Set Option ALE Receive/Accept v6 Layer Callout Sets classify options on inbound flows. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 40 FWPM_CALLOUT_POLICY_SILENT_MODE_AUTH_RECV_ACCEPT_LAYER_V6 WFP Built-in Policy Silent Mode ALE Receive/Accept v6 Layer Callout Audit and log policy for incoming connection. FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 44 {c3dbed20-0bb6-4bf3-828d-96732e1e022e} Windows Firewall: callout Allows secondary connections. FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 285 {dc95b53e-01cf-4058-821d-350b3d0d4676} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66210 FWP_UINT64 1153167795211468800 {f444c576-6e60-4ea2-9faa-80d57ed12cd2} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66211 FWP_UINT64 1153167795211468800 {0c3be01b-fe70-4cc4-89dc-c07996b67e6d} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 8388608 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-0-0 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66215 FWP_UINT64 18446744073709551615 {1165065e-4996-4338-abaf-4b8556b4d431} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 8388608 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-0-0 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66219 FWP_UINT64 18446744073709551615 {0ccc96a3-8c5c-45e2-b80e-7e37b16cc1ad} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 0 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66227 FWP_UINT64 0 {cbfb56db-3c85-4543-9bc2-76ea28cdd74e} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 0 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66231 FWP_UINT64 0 {2dd96961-5757-434f-b617-34e732517c0e} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 0 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66232 FWP_UINT64 8992587776 {375fb39b-08c6-40f2-bdf2-08fa63f970a2} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 0 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66233 FWP_UINT64 8992587776 {2db25e6c-f07a-44f4-b6c8-50a330d2790b} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_BOOTTIME FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 0 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66234 FWP_UINT64 8992587776 {b6fdab6b-dcc6-43e3-99ce-7aeca65063a4} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 0 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_BLOCK {93132c36-6e06-4e6f-a10b-218787cd49cf} 66236 FWP_UINT64 8992587776 {b02a4013-b6b5-4859-9168-1e3299e43b24} Quarantine Default Inbound Block This filter blocks any inbound non-loopback packets when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 0 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_BLOCK {30433c31-b05f-421f-8fde-018ea4c68af4} 66240 FWP_UINT64 8589934592 {8b50e2ec-7cf0-4b71-b42e-5b0536f6cab8} Quarantine Default Inbound Tunnel Exception This filter allows for tunneling when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 1 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_PERMIT {30433c31-b05f-421f-8fde-018ea4c68af4} 66246 FWP_UINT64 1152921513465217024 {3180114b-8338-4740-9a16-444134ad62f4} Quarantine Default Inbound Next Hop Tunnel Exception This filter allows for tunneling when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 1 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT {30433c31-b05f-421f-8fde-018ea4c68af4} 66248 FWP_UINT64 1152921513465217024 {567d3836-3f5b-4067-b9c4-952f677010a2} Quarantine Default Inbound Neighbor Discovery Exception This filter allows for Neighbor Discovery when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWP_ACTION_PERMIT {30433c31-b05f-421f-8fde-018ea4c68af4} 66250 FWP_UINT64 1153167795211468800 {3a90a266-1519-4d23-911b-e84cd0f02ab8} Quarantine Default Inbound DHCP Exception This filter allows for DHCP connections when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 546 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 547 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_PERMIT {30433c31-b05f-421f-8fde-018ea4c68af4} 66252 FWP_UINT64 1153169727946752000 {0593d9b7-8e2b-44b1-9f9e-2831da1c9bd9} Quarantine Default Inbound Loopback Exception This filter allows loopback packets when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {30433c31-b05f-421f-8fde-018ea4c68af4} 66598 FWP_UINT64 18446744073709551615 {70694559-714a-4a38-a0cd-51439e06f1d8} Quarantine Default Inbound ICMP Exception This filter allows for ICMP connections when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWP_ACTION_PERMIT {30433c31-b05f-421f-8fde-018ea4c68af4} 66602 FWP_UINT64 1225225526688350208 {84750a0c-b836-48e3-ab80-104985c857db} AppContainerBoottimeFilter Boottime filter for App Containers FWPM_FILTER_FLAG_BOOTTIME FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4194304 FWP_ACTION_PERMIT 0 66996 FWP_UINT64 18446744073709551615 {b98b75dc-17c0-4e84-bd4e-2080527ca6a6} AppContainerBoottimeFilter Boottime filter for App Containers FWPM_FILTER_FLAG_PERSISTENT FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4194304 FWP_ACTION_PERMIT 0 66998 FWP_UINT64 18446744073709551615 {5e48f4ad-9f6c-4c4e-a83a-d1ac10317436} Teredo socket option opt out block filter {c698301d-9129-450c-937c-f4b834bfb374} FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 {7b6b11f6-cbb5-433c-ae06-6a4f0076e49e} FWP_UINT64 18446744073709551615 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_BLOCK 0 67005 FWP_UINT64 18446744073709551615 {6109847e-3461-46a7-8bbb-58bd48643bf0} AppContainerLoopback This filter allows non-AppContainers to receive loopback traffic from other non-AppContainers FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 8388608 FWP_ACTION_PERMIT {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67191 FWP_UINT64 18446744073709551615 {1f1a43af-2fa5-4f1a-8d8e-395b07d53b24} AppContainerLoopback This filter allows non-AppContainers to receive loopback traffic from by policy allowed AppContainers FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 16777216 FWP_ACTION_PERMIT {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67193 FWP_UINT64 18446744073709551615 {a974e5b7-ff22-43d9-a813-01c5d1283b2e} AppContainerLoopback This filter allows an AppContainer to receive loopback traffic from itself FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4194304 FWP_ACTION_PERMIT {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67195 FWP_UINT64 18446744073709551615 {e3ac824d-4754-4603-80fb-48fe7450e46b} AppContainerLoopback This filter blocks AppContainer loopback traffic FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551614 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:LSD:(A;;CC;;;AC)(A;;CC;;;S-1-15-3-1)(A;;CC;;;S-1-15-3-2)(A;;CC;;;S-1-15-3-3)(A;;CC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833)(A;;CC;;;WD)(A;;CC;;;AN) FWP_ACTION_BLOCK {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67201 FWP_UINT64 18446744073709551614 {36bb2266-0f81-4937-af6b-63086254e9df} Interface Un-quarantine filter FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 0 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWP_MATCH_EQUAL FWP_UINT64 0 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWP_MATCH_EQUAL FWP_UINT64 6755399441055744 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_PERMIT 0 67455 FWP_UINT64 562958543355906 {d3e85111-30f5-4ff0-850f-8622624aadcf} Interface Un-quarantine filter FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 0 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWP_MATCH_EQUAL FWP_UINT64 0 FWPM_CONDITION_IP_NEXTHOP_INTERFACE FWP_MATCH_EQUAL FWP_UINT64 6755399441055744 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67456 FWP_UINT64 562958543355906 {8c9a53ad-9d03-4280-9e0a-53754eb303bd} Interface Un-quarantine filter FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 0 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWP_MATCH_EQUAL FWP_UINT64 10000002 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWP_MATCH_EQUAL FWP_UINT64 1689399632855040 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_PERMIT 0 67459 FWP_UINT64 562958543355906 {8540bba7-40c4-4959-85fd-c082688a1176} Interface Un-quarantine filter FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT8 0 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWP_MATCH_EQUAL FWP_UINT64 10000002 FWPM_CONDITION_IP_NEXTHOP_INTERFACE FWP_MATCH_EQUAL FWP_UINT64 1689399632855040 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67460 FWP_UINT64 562958543355906 {6fc5fe31-8779-4b15-9576-3ba7ff0b0ce4} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_BLOCK {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67634 FWP_UINT64 9223372036854776768 {569702e8-0383-45e2-a938-743a0b269a89} Query User Prompt the User for a decision corresponding to Inbound Traffic. This filter blocks any inbound packets for which there is no explicit rule to allow the packet, unless the user has allowed through the Query User pop up. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF 9400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 3 FWP_ACTION_BLOCK {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} 67642 FWP_UINT64 3458764513820540928 {c833a355-c4ff-4504-ba03-e56b2edcf0b2} UWP Default Inbound Block Rule This is the UWP Default Inbound Block filter. This filter blocks any inbound packets to UWP apps that do not have the correct capability tokens for listening to a resource. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_APP_ISOLATION 9900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_EMPTY FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_NOT_EQUAL FWP_SID S-1-0-0 FWP_ACTION_BLOCK {b36473ef-bf42-49b9-ac24-adba245e825c} 67658 FWP_UINT64 137438953472 {1c63c63e-acc4-4ef8-b31e-a3c7156292df} Core Networking - IPv6 (IPv6-In) Inbound rule required to permit IPv6 traffic for ISATAP (Intra-Site Automatic Tunnel Addressing Protocol) and 6to4 tunnelling services. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9b00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 41 FWP_ACTION_PERMIT 0 67662 FWP_UINT64 10376504785133109248 {b9346349-afa6-45c1-875e-5e12437b1648} Delivery Optimization (UDP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9c00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67670 FWP_UINT64 10376540038224674816 {e0e890e5-0dbb-449d-926f-36ecc0d32307} Delivery Optimization (UDP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9c00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67671 FWP_UINT64 10376540038627328000 {79923371-5423-4aa7-9d60-86afaac5ce75} Delivery Optimization (UDP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9c00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67672 FWP_UINT64 10376540047217262592 {e67d1661-76e2-4fdf-bbab-5f1cd28e3ea4} Delivery Optimization (UDP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9c00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67673 FWP_UINT64 10376540038627328000 {72a57651-9ca9-4550-ba15-cb5c797b3281} Delivery Optimization (UDP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9c00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67674 FWP_UINT64 10376540047217262592 {9ee8af53-7d0c-48f1-892e-3038f67c8771} Core Networking - Router Advertisement (ICMPv6-In) Router Advertisement messages are sent by routers to other nodes for stateless auto-configuration. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9d00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWP_ACTION_PERMIT 0 67675 FWP_UINT64 10664770345656909824 {269d6a27-dcd7-4d83-8e64-34e06f1fab75} Core Networking - Dynamic Host Configuration Protocol (DHCP-In) Allows DHCP (Dynamic Host Configuration Protocol) messages for stateful auto-configuration. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 9f00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 68 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 67 FWP_ACTION_PERMIT 0 67680 FWP_UINT64 10376541962370023424 {4f4f8d89-0d09-4170-91e5-b50e05fd12ba} Core Networking - Dynamic Host Configuration Protocol for IPv6(DHCPV6-In) Allows DHCPV6 (Dynamic Host Configuration Protocol for IPv6) messages for stateful and stateless configuration. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a000000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 546 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 547 FWP_ACTION_PERMIT 0 67684 FWP_UINT64 10376541962370023424 {dc4fed41-3e88-48ec-8363-d1dabe968953} Core Networking - Neighbour Discovery Solicitation (ICMPv6-In) Neighbour Discovery Solicitations are sent by nodes to discover the link-layer address of another on-link IPv6 node. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWP_ACTION_PERMIT 0 67685 FWP_UINT64 10376539969505198080 {24a4ff08-78cb-425a-8412-de0c6eee513f} Core Networking - Neighbour Discovery Solicitation (ICMPv6-In) Neighbour Discovery Solicitations are sent by nodes to discover the link-layer address of another on-link IPv6 node. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67686 FWP_UINT64 10376539969907851264 {925f3a0e-53c9-4c55-8506-1a50be54a688} Core Networking - Neighbour Discovery Solicitation (ICMPv6-In) Neighbour Discovery Solicitations are sent by nodes to discover the link-layer address of another on-link IPv6 node. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67687 FWP_UINT64 10376539978497785856 {b0c5f46c-f20b-455a-94a5-ba616b03ba89} Core Networking - Neighbour Discovery Solicitation (ICMPv6-In) Neighbour Discovery Solicitations are sent by nodes to discover the link-layer address of another on-link IPv6 node. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67688 FWP_UINT64 10376539969907851264 {7c5bf10d-2519-47ac-9864-28272c35ecd6} Core Networking - Neighbour Discovery Solicitation (ICMPv6-In) Neighbour Discovery Solicitations are sent by nodes to discover the link-layer address of another on-link IPv6 node. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67689 FWP_UINT64 10376539978497785856 {b0a2ee9b-60e1-4c3a-a2e8-db6ffaec87e2} Delivery Optimization (TCP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67703 FWP_UINT64 10376540038224674816 {e5c30a1f-bd12-46f2-9eb5-0dfa08e13499} Delivery Optimization (TCP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67704 FWP_UINT64 10376540038627328000 {0921d09f-c82d-41d2-814f-6ac23cfb74f5} Delivery Optimization (TCP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67705 FWP_UINT64 10376540047217262592 {31d9ed8a-a509-4a04-a9fc-c09be2ae87af} Delivery Optimization (TCP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67706 FWP_UINT64 10376540038627328000 {78a6c883-2926-4fa7-82ed-335f51c3b5bd} Delivery Optimization (TCP-In) Inbound rule to allow Delivery Optimization to connect to remote endpoints FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3055155277-3816794035-3994065555-2874236192-2193176987) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7680 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67707 FWP_UINT64 10376540047217262592 {076c0bad-60b8-4a49-a4ce-83bdc9a879f0} Core Networking - Router Solicitation (ICMPv6-In) Router Solicitation messages are sent by nodes seeking routers to provide stateless auto-configuration. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWP_ACTION_PERMIT 0 67708 FWP_UINT64 10376539969505198080 {45791038-5827-4a2e-993b-7ebfdccffa6a} Wireless Display Infrastructure Back Channel (TCP-In) Inbound rule for Wireless Display Infrastructure back channel [TCP] FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0063006100730074007300720076002e006500780065000000 \device\harddiskvolume3\windows\system32\castsrv.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7250 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67712 FWP_UINT64 10376539969505198080 {0f24cc1b-3e3d-44e1-90a0-f20ac3e4462e} Core Networking - Neighbour Discovery Advertisement (ICMPv6-In) Neighbour Discovery Advertisement messages are sent by nodes to notify other nodes of link-layer address changes or in response to a Neighbour Discovery Solicitation request. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWP_ACTION_PERMIT 0 67713 FWP_UINT64 10376539969505198080 {52dc6841-c31e-4d63-bc57-4e0e2b9b9671} Core Networking - Neighbour Discovery Advertisement (ICMPv6-In) Neighbour Discovery Advertisement messages are sent by nodes to notify other nodes of link-layer address changes or in response to a Neighbour Discovery Solicitation request. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67714 FWP_UINT64 10376539969907851264 {42f19b9f-5956-45b5-a51c-5b5267d5005a} Core Networking - Neighbour Discovery Advertisement (ICMPv6-In) Neighbour Discovery Advertisement messages are sent by nodes to notify other nodes of link-layer address changes or in response to a Neighbour Discovery Solicitation request. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67715 FWP_UINT64 10376539978497785856 {2d1be467-dab7-4309-89bc-0e16efbeb1e7} Core Networking - Neighbour Discovery Advertisement (ICMPv6-In) Neighbour Discovery Advertisement messages are sent by nodes to notify other nodes of link-layer address changes or in response to a Neighbour Discovery Solicitation request. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67716 FWP_UINT64 10376539969907851264 {c094b7da-b5e2-4cac-bf03-d3f8dd685948} Core Networking - Neighbour Discovery Advertisement (ICMPv6-In) Neighbour Discovery Advertisement messages are sent by nodes to notify other nodes of link-layer address changes or in response to a Neighbour Discovery Solicitation request. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67717 FWP_UINT64 10376539978497785856 {4aee5ef2-93d5-46e6-bee0-725d8e4cdc07} Core Networking - Parameter Problem (ICMPv6-In) Parameter Problem error messages are sent by nodes as a result of incorrectly generated packets. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a800000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 4 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWP_ACTION_PERMIT 0 67730 FWP_UINT64 10376539969505198080 {4887adbb-3a7a-48f0-a6b1-9243969c1d1c} Core Networking - Parameter Problem (ICMPv6-In) Parameter Problem error messages are sent by nodes as a result of incorrectly generated packets. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a800000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 4 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67731 FWP_UINT64 10376539969907851264 {56a02554-23d2-4984-95a9-2f461cc4fa66} Core Networking - Parameter Problem (ICMPv6-In) Parameter Problem error messages are sent by nodes as a result of incorrectly generated packets. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a800000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 4 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67732 FWP_UINT64 10376539978497785856 {13668b24-1745-4eef-8097-a78f1683cf37} Core Networking - Parameter Problem (ICMPv6-In) Parameter Problem error messages are sent by nodes as a result of incorrectly generated packets. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a800000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 4 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67733 FWP_UINT64 10376539969907851264 {d9f3965a-d5ba-402c-be4b-fd46d258c1e8} Core Networking - Parameter Problem (ICMPv6-In) Parameter Problem error messages are sent by nodes as a result of incorrectly generated packets. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a800000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 4 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67734 FWP_UINT64 10376539978497785856 {630130e6-d0ee-4f95-917f-727386eef3d2} Core Networking - Packet Too Big (ICMPv6-In) Packet Too Big error messages are sent from any node that a packet traverses which is unable to forward the packet because the packet is too large for the next link. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 2 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWP_ACTION_PERMIT 0 67735 FWP_UINT64 10376539969505198080 {24a965e8-7b7e-4248-b6fa-b4f93990c49a} Core Networking - Packet Too Big (ICMPv6-In) Packet Too Big error messages are sent from any node that a packet traverses which is unable to forward the packet because the packet is too large for the next link. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 2 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67736 FWP_UINT64 10376539969907851264 {1f160125-9b36-460c-a743-a7e17c45ed4f} Core Networking - Packet Too Big (ICMPv6-In) Packet Too Big error messages are sent from any node that a packet traverses which is unable to forward the packet because the packet is too large for the next link. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 2 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67737 FWP_UINT64 10376539978497785856 {5cdc61d6-8697-400d-91c3-ff55d00ff9a9} Core Networking - Packet Too Big (ICMPv6-In) Packet Too Big error messages are sent from any node that a packet traverses which is unable to forward the packet because the packet is too large for the next link. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 2 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67738 FWP_UINT64 10376539969907851264 {412c57e1-2038-40cf-a4eb-1006cc161782} Core Networking - Packet Too Big (ICMPv6-In) Packet Too Big error messages are sent from any node that a packet traverses which is unable to forward the packet because the packet is too large for the next link. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF a900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 2 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67739 FWP_UINT64 10376539978497785856 {33ea4c7f-33e0-4ebc-934e-5c4d8c6bb21e} Core Networking - Destination Unreachable (ICMPv6-In) Destination Unreachable error messages are sent from any node that a packet traverses which is unable to forward the packet for any reason except congestion. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ab00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWP_ACTION_PERMIT 0 67742 FWP_UINT64 10376539969505198080 {5c2001c6-fed1-458f-a2e5-f05059ba33b3} Core Networking - Destination Unreachable (ICMPv6-In) Destination Unreachable error messages are sent from any node that a packet traverses which is unable to forward the packet for any reason except congestion. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ab00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67743 FWP_UINT64 10376539969907851264 {11917a18-01cd-4f40-94a2-c11700ffd140} Core Networking - Destination Unreachable (ICMPv6-In) Destination Unreachable error messages are sent from any node that a packet traverses which is unable to forward the packet for any reason except congestion. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ab00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67744 FWP_UINT64 10376539978497785856 {65a7e69a-8dda-48bf-8f7f-77f85ef66d4d} Core Networking - Destination Unreachable (ICMPv6-In) Destination Unreachable error messages are sent from any node that a packet traverses which is unable to forward the packet for any reason except congestion. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ab00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67745 FWP_UINT64 10376539969907851264 {0c139d5c-00c0-40fe-a291-119da3eb65a1} Core Networking - Destination Unreachable (ICMPv6-In) Destination Unreachable error messages are sent from any node that a packet traverses which is unable to forward the packet for any reason except congestion. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ab00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67746 FWP_UINT64 10376539978497785856 {d91c02d3-8acf-4ca8-b00e-c70abeb4e4f4} Core Networking - Internet Group Management Protocol (IGMP-In) IGMP messages are sent and received by nodes to create, join and depart multicast groups. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ac00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 2 FWP_ACTION_PERMIT 0 67748 FWP_UINT64 10376504785133109248 {8e67bf87-b3ab-4faa-966d-0374819472f1} Core Networking - Time Exceeded (ICMPv6-In) Time Exceeded error messages are generated from any node that a packet traverses if the Hop Limit value is decremented to zero at any point on the path. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWP_ACTION_PERMIT 0 67761 FWP_UINT64 10376539969505198080 {03d3765e-5cbb-482f-92e2-b1c2813d43a0} Core Networking - Time Exceeded (ICMPv6-In) Time Exceeded error messages are generated from any node that a packet traverses if the Hop Limit value is decremented to zero at any point on the path. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67762 FWP_UINT64 10376539969907851264 {7e3d00b3-7372-4d01-9d99-0ad72294a64e} Core Networking - Time Exceeded (ICMPv6-In) Time Exceeded error messages are generated from any node that a packet traverses if the Hop Limit value is decremented to zero at any point on the path. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67763 FWP_UINT64 10376539978497785856 {f4ad030a-de57-4da0-aae8-34007c10742c} Core Networking - Time Exceeded (ICMPv6-In) Time Exceeded error messages are generated from any node that a packet traverses if the Hop Limit value is decremented to zero at any point on the path. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67764 FWP_UINT64 10376539969907851264 {db8b7e5e-dfe4-4a20-9ce2-94970713653f} Core Networking - Time Exceeded (ICMPv6-In) Time Exceeded error messages are generated from any node that a packet traverses if the Hop Limit value is decremented to zero at any point on the path. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 3 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67765 FWP_UINT64 10376539978497785856 {6fe6052c-9f1b-449f-80e6-1933bd00c2bd} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 67779 FWP_UINT64 10376293747620052992 {25113d41-4bfd-4f42-b8ed-0782825d5cef} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67780 FWP_UINT64 10376293748022706176 {e33d2046-cd76-4858-82c6-4604a02f215f} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67781 FWP_UINT64 10376293756612640768 {c67dd978-8cee-43a5-b7c5-7394d13639dd} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67782 FWP_UINT64 10376293748022706176 {577f1fbd-6697-4374-831d-ab4957b5d82a} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName} @{Microsoft.Win32WebViewHost_10.0.22000.1_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67783 FWP_UINT64 10376293756612640768 {497778bf-d906-447e-97c6-3d90e01faf13} XING XING FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-830604253-3178213212-3804702845-3552981612-1670190244-297742492-2307346640 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 67791 FWP_UINT64 10376293747620052992 {fa37a31f-edf8-45f1-a12a-3979b049b980} XING XING FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-830604253-3178213212-3804702845-3552981612-1670190244-297742492-2307346640 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67792 FWP_UINT64 10376293748022706176 {7348c729-bea7-4ce3-9126-91bacbf060ab} XING XING FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-830604253-3178213212-3804702845-3552981612-1670190244-297742492-2307346640 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67793 FWP_UINT64 10376293756612640768 {245b69e4-1a3a-4fe8-9121-f716a7758498} XING XING FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-830604253-3178213212-3804702845-3552981612-1670190244-297742492-2307346640 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67794 FWP_UINT64 10376293748022706176 {06682e90-222d-41d9-a82a-c80c77bd5d24} XING XING FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-830604253-3178213212-3804702845-3552981612-1670190244-297742492-2307346640 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67795 FWP_UINT64 10376293756612640768 {f8fe4921-4cac-4e53-9a94-6f3ba592041f} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_DisplayName} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378-1866284433 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 67803 FWP_UINT64 10376293747620052992 {54e08d95-dd62-45d7-89d8-aaa4b6ef259b} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_DisplayName} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378-1866284433 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67804 FWP_UINT64 10376293748022706176 {219df051-7115-4bae-844c-ff5b93b401c5} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_DisplayName} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378-1866284433 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67805 FWP_UINT64 10376293756612640768 {bfec4abb-9fae-4382-9946-0f22c6cc800a} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_DisplayName} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378-1866284433 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67806 FWP_UINT64 10376293748022706176 {7951db55-918d-43fc-b66a-21a5f9c754e5} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_DisplayName} @{microsoft.windowscommunicationsapps_16005.14326.20544.0_x64__8wekyb3d8bbwe?ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_OutlookDesktop_Description} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378-1866284433 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67807 FWP_UINT64 10376293756612640768 {2743ed06-9818-463f-8aa4-b97a298ee1e3} Xbox Game Bar Xbox Game Bar FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1714399563-1326177402-2048222277-143663168-2151391019-765408921-4098702777 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 67815 FWP_UINT64 10376293747620052992 {6e4a1c0a-729d-431d-9f8d-0a1df4f16f2f} Xbox Game Bar Xbox Game Bar FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1714399563-1326177402-2048222277-143663168-2151391019-765408921-4098702777 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67816 FWP_UINT64 10376293748022706176 {173e4b5d-3445-413c-a0a6-f1900681f228} Xbox Game Bar Xbox Game Bar FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1714399563-1326177402-2048222277-143663168-2151391019-765408921-4098702777 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67817 FWP_UINT64 10376293756612640768 {f5f65248-7c4f-4a2a-bf44-ef3d8385796f} Xbox Game Bar Xbox Game Bar FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1714399563-1326177402-2048222277-143663168-2151391019-765408921-4098702777 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67818 FWP_UINT64 10376293748022706176 {95150dd6-5f6b-4d72-a721-bd90f0f3e1e3} Xbox Game Bar Xbox Game Bar FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1714399563-1326177402-2048222277-143663168-2151391019-765408921-4098702777 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67819 FWP_UINT64 10376293756612640768 {5e458804-9d6b-4374-aeb0-0fab8ebfa394} Microsoft Teams Microsoft Teams FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b700000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f007700730061007000700073005c006d006900630072006f0073006f00660074007400650061006d0073005f00320032003000300036002e003600300030002e0031003100330033002e0037003400300039005f007800360034005f005f003800770065006b007900620033006400380062006200770065005c006d0073007400650061006d0073002e006500780065000000 \device\harddiskvolume3\program files\windowsapps\microsoftteams_22006.600.1133.7409_x64__8wekyb3d8bbwe\msteams.exe FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67823 FWP_UINT64 10376504785133109248 {f767a292-c59e-40a5-a090-99dbd8574026} Microsoft Teams Microsoft Teams FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b800000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f007700730061007000700073005c006d006900630072006f0073006f00660074007400650061006d0073005f00320032003000300036002e003600300030002e0031003100330033002e0037003400300039005f007800360034005f005f003800770065006b007900620033006400380062006200770065005c006d0073007400650061006d0073002e006500780065000000 \device\harddiskvolume3\program files\windowsapps\microsoftteams_22006.600.1133.7409_x64__8wekyb3d8bbwe\msteams.exe FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67827 FWP_UINT64 10376504785133109248 {5c28dd7c-af06-4a4c-876c-0dc6ab29ee70} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2226957697-3030467180-2301525-4248967783-2024719031-2325529081-2915787518 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 67835 FWP_UINT64 10376293747620052992 {559be0e2-e31c-4860-b8f4-f535b5f067ba} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2226957697-3030467180-2301525-4248967783-2024719031-2325529081-2915787518 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67836 FWP_UINT64 10376293748022706176 {16aba0dc-3359-47fe-acf5-496ac83cbaa8} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2226957697-3030467180-2301525-4248967783-2024719031-2325529081-2915787518 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67837 FWP_UINT64 10376293756612640768 {54f2e6d9-ec87-40ef-91a5-e48f51fbce78} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2226957697-3030467180-2301525-4248967783-2024719031-2325529081-2915787518 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67838 FWP_UINT64 10376293748022706176 {c176404f-b6b6-470b-b3be-7c5db41a3c08} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} @{Microsoft.Windows.Photos_2021.21120.8011.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Resources/AppStoreName} FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF b900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-2226957697-3030467180-2301525-4248967783-2024719031-2325529081-2915787518 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67839 FWP_UINT64 10376293756612640768 {0bfafb37-da2b-4b57-96c4-dd5e14ef9b9b} Cortana Cortana FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ba00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1880626798-2296700190-2192216202-2581987570-949377748-777141861-2889999867 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 67847 FWP_UINT64 10376293747620052992 {2756a9a2-8940-469d-b0e4-d093e24b8737} Cortana Cortana FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ba00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1880626798-2296700190-2192216202-2581987570-949377748-777141861-2889999867 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 67848 FWP_UINT64 10376293748022706176 {0b19d824-b94d-4b8c-8eb0-fb82a74f4c5b} Cortana Cortana FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ba00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1880626798-2296700190-2192216202-2581987570-949377748-777141861-2889999867 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67849 FWP_UINT64 10376293756612640768 {ee6e661b-040e-43b2-9443-46047eaf059e} Cortana Cortana FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ba00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1880626798-2296700190-2192216202-2581987570-949377748-777141861-2889999867 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67850 FWP_UINT64 10376293748022706176 {42de4209-2a60-449b-81c9-feda0c9914be} Cortana Cortana FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF ba00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1880626798-2296700190-2192216202-2581987570-949377748-777141861-2889999867 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 67851 FWP_UINT64 10376293756612640768 {c808a57d-2fb7-4e7d-a772-5667a9bdbc70} Microsoft Edge (mDNS-In) Inbound rule for Microsoft Edge to allow mDNS traffic. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF bb00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073002000280078003800360029005c006d006900630072006f0073006f00660074005c00650064006700650077006500620076006900650077005c006100700070006c00690063006100740069006f006e005c00390038002e0030002e0031003100300038002e00350036005c006d0073006500640067006500770065006200760069006500770032002e006500780065000000 \device\harddiskvolume3\program files (x86)\microsoft\edgewebview\application\98.0.1108.56\msedgewebview2.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67855 FWP_UINT64 10376539969505198080 {639e9c00-79c8-4dfb-a3f1-dd6713b27868} Microsoft Edge (mDNS-In) Inbound rule for Microsoft Edge to allow mDNS traffic. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF bd00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073002000280078003800360029005c006d006900630072006f0073006f00660074005c0065006400670065005c006100700070006c00690063006100740069006f006e005c006d00730065006400670065002e006500780065000000 \device\harddiskvolume3\program files (x86)\microsoft\edge\application\msedge.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67871 FWP_UINT64 10376539969505198080 {1256e94d-6137-4f0d-b73a-f8f9ccb8d64a} Microsoft Edge (mDNS-In) Inbound rule for Microsoft Edge to allow mDNS traffic. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF be00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073002000280078003800360029005c006d006900630072006f0073006f00660074005c00650064006700650077006500620076006900650077005c006100700070006c00690063006100740069006f006e005c00390038002e0030002e0031003100300038002e00360032005c006d0073006500640067006500770065006200760069006500770032002e006500780065000000 \device\harddiskvolume3\program files (x86)\microsoft\edgewebview\application\98.0.1108.62\msedgewebview2.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67875 FWP_UINT64 10376539969505198080 {5eb6cad8-1d8a-4c51-b026-807f3e3d92e0} Allow inbound UDP traffic to CDPSvc port 5050 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c200000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5050 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67879 FWP_UINT64 246496763052032 {211c67b6-5dec-468d-9bf9-564f606ad8be} Allow inbound TCP traffic to CDPSvc from any port to port 5160 (Wi-Fi Direct Transport) FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c700000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5160 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67889 FWP_UINT64 246496763052032 {d9f11c8c-075e-4271-8012-655bb3f9dde1} Allow inbound TCP traffic to CDPSvc from any port to port 5040 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5040 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67893 FWP_UINT64 246496763052032 {42b88eb0-34e6-478f-908b-585607fbe41a} Block inbound traffic to omadmclient.exe FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH ca00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c006f006d00610064006d0063006c00690065006e0074002e006500780065000000 \device\harddiskvolume3\windows\system32\omadmclient.exe FWP_ACTION_BLOCK {a8b55da6-3aba-41f3-b7ba-443233f64fce} 67895 FWP_UINT64 137438953472 {475d5508-5d3a-46d5-9805-e407589473ec} Block inbound traffic to deviceenroller.exe FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH cb00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0064006500760069006300650065006e0072006f006c006c00650072002e006500780065000000 \device\harddiskvolume3\windows\system32\deviceenroller.exe FWP_ACTION_BLOCK {c4e61e5c-3e66-4efb-b22b-87f8e56cf9c2} 67897 FWP_UINT64 137438953472 {f0faf4fb-252e-47b9-af71-f9a81a7a650d} Block inbound traffic to dmcertinst.exe FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH d000000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0064006d00630065007200740069006e00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\dmcertinst.exe FWP_ACTION_BLOCK {616fdcf3-1169-46b5-8224-cffc7276af2e} 67907 FWP_UINT64 137438953472 {62af150d-b92a-423a-ab03-b55021e030ef} DhcpFirewallPolicy DhcpFirewallPolicy FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 68 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 67 FWP_ACTION_PERMIT 0 67917 FWP_UINT64 248420908400640 {e53b388f-16cd-408e-8e6b-1aa8dfdc1ccc} DhcpFirewallPolicy DhcpFirewallPolicy FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d600000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 546 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 547 FWP_ACTION_PERMIT 0 67919 FWP_UINT64 248420908400640 {388a863a-333e-4270-8ea8-3756df7eb475} Allow RPC/TCP traffic to EventLog FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-880578595-1860270145-482643319-2788375705-1540778122) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67925 FWP_UINT64 211316685930496 {ac41a204-b76a-46b4-b943-a8c24d3c5336} Allow inbound UDP traffic to fdphost port 3702 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH da00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67927 FWP_UINT64 246496763052032 {cb0601b2-92ec-40b8-8422-c92875ed2d56} Allow inbound UDP traffic to fdphost port 1900 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH dc00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 1900 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67931 FWP_UINT64 246496763052032 {8e76e740-04c4-4d35-a298-11f7dc596749} Allow inbound TCP traffic to AJRouter FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3532809085-2652327567-2620918877-1058261733-582902671) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 9955 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67941 FWP_UINT64 246496763052032 {19ed4871-1493-432b-8d8c-0ccc59f25f6d} Allow inbound UDP traffic to AJRouter FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e200000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3532809085-2652327567-2620918877-1058261733-582902671) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67943 FWP_UINT64 211312390963200 {84c3250b-945e-4d9e-821e-09efa2c38757} Allow Grouping to receive from port 3587 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1971585524-2528565899-3324366483-1300752743-2325226580) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3587 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67949 FWP_UINT64 246496763052032 {75fe5296-4bc2-4582-a285-7442b870bf8e} Allow PNRP to send to port 3540 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e700000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-372467825-374176116-1198570892-3192490889-1232022613) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3540 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67953 FWP_UINT64 246496763052032 {ad422266-d71d-4380-b44a-fb3e2e95868b} IPsec Policy Agent service hardening - Remote Management Allow IPsec Policy Agent inbound RPC/TCP traffic for Remote Management FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH eb00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3044542841-3639452079-4096941652-1606687743-1256249853) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67961 FWP_UINT64 211316685930496 {19682e8b-d911-49dc-900a-54cef7765f40} Block all inbound traffic to SearchFilterHost FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH ec00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c00730065006100720063006800660069006c0074006500720068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\searchfilterhost.exe FWP_ACTION_BLOCK {3a5e8a34-82df-4083-a6ed-87ea8ed3507a} 67963 FWP_UINT64 137438953472 {2be90617-8637-421f-acca-fb35be82a866} Block all inbound traffic to SearchProtocolHost FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH ef00000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c00730065006100720063006800700072006f0074006f0063006f006c0068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\searchprotocolhost.exe FWP_ACTION_BLOCK {c0cedbe2-34f4-4491-bd19-152a55b1b6e0} 67969 FWP_UINT64 137438953472 {8dba5a64-72a1-4fcd-bf10-7350fdfe0e60} Allow inbound UDP traffic to SNMPTRAP service FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f000000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073006e006d00700074007200610070002e006500780065000000 \device\harddiskvolume3\windows\system32\snmptrap.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3964583643-2633443559-2834438935-3739664028-1580655619) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67971 FWP_UINT64 211312390963200 {031039e8-157e-4c49-a1b7-831802af44ea} Allow all inbound TCP and RPC to SPPEXTCOMOBJ FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f100000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007000700065007800740063006f006d006f0062006a002e006500780065000000 \device\harddiskvolume3\windows\system32\sppextcomobj.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3895724531-1583119856-3186271294-3795977770-3211684703) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67973 FWP_UINT64 211312390963200 {0adc1095-75ad-4dbe-a1ff-a2cfa50c9182} TermServiceLOM FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-446051430-1559341753-4161941529-1950928533-810483104) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67978 FWP_UINT64 211312390963200 {569847e2-c2ad-471a-a0aa-7db6eda39cce} TermServiceLOM FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f300000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_UINT64 18446744073709551615 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-446051430-1559341753-4161941529-1950928533-810483104) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_INSPECTION FWPM_CALLOUT_SET_OPTIONS_AUTH_RECV_ACCEPT_LAYER_V6 {2ea412db-5c9d-4bbd-9af2-4914d2dbfe17} 67979 FWP_UINT64 18446744073709551615 {aeb06403-14b2-4f11-911c-d2e71406b781} Allow incoming RPC traffic to VDS FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f400000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c007600640073002e006500780065000000 \device\harddiskvolume3\windows\system32\vds.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2196396108-1448510645-203779624-3888580976-3789157697) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67981 FWP_UINT64 211316685930496 {06ec84e9-0aaf-4282-b849-ffe57c1b0d6a} Allow inbound TCP ports 389 and 636 traffic for vmicheartbeat FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-534935901-3437432317-481271085-1710633381-983106267) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 389 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67984 FWP_UINT64 246496763052032 {e32e6791-5d61-4ba0-a2c7-6b02831e6a45} Allow inbound TCP ports 389 and 636 traffic for vmicheartbeat FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f500000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-534935901-3437432317-481271085-1710633381-983106267) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 636 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67985 FWP_UINT64 246496763052032 {c6c6d63a-882b-4f39-8ceb-36c8f7555219} Allow inbound UDP traffic to NTP port 123 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f700000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-4267341169-2882910712-659946508-2704364837-2204554466) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 123 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67989 FWP_UINT64 246496763052032 {0c2c073b-bf6b-4c9f-9606-9ce2126c2f50} Allow inbound RPC traffic to the Block Level Backup service (wbengine) over TCP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f900000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c007700620065006e00670069006e0065002e006500780065000000 \device\harddiskvolume3\windows\system32\wbengine.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1549550529-11381693-4027442525-4081535042-2424139505) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67993 FWP_UINT64 211316685930496 {7414eaee-1713-4f1a-836e-a33cf8e7f5ee} Cast to Device streaming server hardening rules for RTSP Allow incoming RTSP connections to the Cast to Device streaming server FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0201000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c006d00640065007300650072007600650072002e006500780065000000 \device\harddiskvolume3\windows\system32\mdeserver.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 23554 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68017 FWP_UINT64 246428043575296 {74e34cf5-b726-4da8-b473-4740ac1dacfe} Cast to Device streaming server hardening rules for RTSP Allow incoming RTSP connections to the Cast to Device streaming server FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0201000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c006d00640065007300650072007600650072002e006500780065000000 \device\harddiskvolume3\windows\system32\mdeserver.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 23555 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68018 FWP_UINT64 246428043575296 {7e0a5145-d1e5-42f7-9d4e-d4dd9196b7b0} Cast to Device streaming server hardening rules for RTSP Allow incoming RTSP connections to the Cast to Device streaming server FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0201000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c006d00640065007300650072007600650072002e006500780065000000 \device\harddiskvolume3\windows\system32\mdeserver.exe FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 23556 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68019 FWP_UINT64 246428043575296 {54d6e8b9-dec2-46f8-a998-91264ad65362} Allow incoming TCP to PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0301000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68021 FWP_UINT64 211312390963200 {80d42d94-a6d0-4a5d-b8c6-20cdeda245cb} Windows Media Player Network Sharing Service service hardening - RTSP Allow incoming RTSP connections to the Windows Media Player Network Sharing Service FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0901000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f007700730020006d006500640069006100200070006c0061007900650072005c0077006d0070006e006500740077006b002e006500780065000000 \device\harddiskvolume3\program files\windows media player\wmpnetwk.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2375682873-768044350-3534595160-1005545032-2873800392) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 554 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68042 FWP_UINT64 246496763052032 {b4aeacd5-c926-446e-addd-dc86b9979b64} Windows Media Player Network Sharing Service service hardening - RTSP Allow incoming RTSP connections to the Windows Media Player Network Sharing Service FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0901000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f007700730020006d006500640069006100200070006c0061007900650072005c0077006d0070006e006500740077006b002e006500780065000000 \device\harddiskvolume3\program files\windows media player\wmpnetwk.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2375682873-768044350-3534595160-1005545032-2873800392) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 8554 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68043 FWP_UINT64 246496763052032 {ec51a54f-1281-48b4-9a56-386529896692} WSH Default Inbound Block Blocks all inbound traffic for services who have been network hardened FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE D:(A;NP;CC;;;S-1-5-80-2676549577-1911656217-2625096541-4178041876-1366760775)(A;NP;CC;;;S-1-5-80-1580948945-3239616721-2529237571-3761093093-1214243633)(A;NP;CC;;;S-1-5-80-1058592404-331734164-3167594226-3910907650-1299295147)(A;NP;CC;;;S-1-5-80-1383147646-27650227-2710666058-1662982300-1023958487)(A;NP;CC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264)(A;NP;CC;;;S-1-5-80-2611951811-1959136347-1062071333-3982815153-2811717512)(A;NP;CC;;;S-1-5-80-2839768381-3691089589-2614646340-3191585287-3380622033)(A;NP;CC;;;S-1-5-80-538170410-2190149038-799223143-2506663053-4165713448)(A;NP;CC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582)(A;NP;CC;;;S-1-5-80-3914275374-678031271-1603343729-3906112567-2888048264)(A;NP;CC;;;S-1-5-80-3787436395-2174616005-3003730137-1094982900-1570567328)(A;NP;CC;;;S-1-5-80-2970612574-78537857-698502321-558674196-1451644582)(A;NP;CC;;;S-1-5-80-880578595-1860270145-482643319-2788375705-1540778122)(A;NP;CC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675)(A;NP;CC;;;S-1-5-80-2617507558-3328795327-711547822-311560295-1636921165)(A;NP;CC;;;S-1-5-80-89818136-74175777-88572358-3912780041-2421659406)(A;NP;CC;;;S-1-5-80-172094073-716411664-54255058-185476446-2329512179)(A;NP;CC;;;S-1-5-80-3532809085-2652327567-2620918877-1058261733-582902671)(A;NP;CC;;;S-1-5-80-3088073201-1464728630-1879813800-1107566885-823218052)(A;NP;CC;;;S-1-5-80-2898649604-2335086160-1904548223-3761738420-3855444835)(A;NP;CC;;;S-1-5-80-1971585524-2528565899-3324366483-1300752743-2325226580)(A;NP;CC;;;S-1-5-80-967499406-1694984581-2959056265-2481940682-939264259)(A;NP;CC;;;S-1-5-80-1948712186-1330865447-943413596-1669284603-1648638051)(A;NP;CC;;;S-1-5-80-3596911058-2952229928-1888671852-1743692427-614402820)(A;NP;CC;;;S-1-5-80-372467825-374176116-1198570892-3192490889-1232022613)(A;NP;CC;;;S-1-5-80-3141781312-1794533130-3616533224-2008760771-2116720301)(A;NP;CC;;;S-1-5-80-3044542841-3639452079-4096941652-1606687743-1256249853)(A;NP;CC;;;S-1-5-80-117416528-2204451360-1913602512-1355018040-1234992034)(A;NP;CC;;;S-1-5-80-3964583643-2633443559-2834438935-3739664028-1580655619)(A;NP;CC;;;S-1-5-80-3895724531-1583119856-3186271294-3795977770-3211684703)(A;NP;CC;;;S-1-5-80-2590341223-3996088049-3993122417-23640849-324535191)(A;NP;CC;;;S-1-5-80-949921180-3923668869-394927020-528789358-3592448931)(A;NP;CC;;;S-1-5-80-768763963-4214222998-2156221936-2953597973-713500239)(A;NP;CC;;;S-1-5-80-2014626298-1656748749-3847481816-918933055-2469338456)(A;NP;CC;;;S-1-5-80-1989757894-211065159-731672622-1783776043-3948168785)(A;NP;CC;;;S-1-5-80-2196396108-1448510645-203779624-3888580976-3789157697)(A;NP;CC;;;S-1-5-80-3074984378-4122987768-2130325677-2031866499-3405430279)(A;NP;CC;;;S-1-5-80-534935901-3437432317-481271085-1710633381-983106267)(A;NP;CC;;;S-1-5-80-1877308096-3090306141-3032871208-3115266146-1400827410)(A;NP;CC;;;S-1-5-80-3076811988-2254870394-2658297454-3934945422-2393138642)(A;NP;CC;;;S-1-5-80-3110303136-3426481729-3186938678-1087894076-2178433439)(A;NP;CC;;;S-1-5-80-3098585136-2538892366-1097114017-2832417424-2016953023)(A;NP;CC;;;S-1-5-80-235582178-102246843-358262472-4132936818-1867412993)(A;NP;CC;;;S-1-5-80-1752088424-1054500994-3489791022-3310831482-3926524968)(A;NP;CC;;;S-1-5-80-1549550529-11381693-4027442525-4081535042-2424139505)(A;NP;CC;;;S-1-5-80-4155767994-3874329934-3800885181-2130851812-726865888)(A;NP;CC;;;S-1-5-80-3524758515-3090971750-345616940-2322499744-3530715838)(A;NP;CC;;;S-1-5-80-3299868208-4286319593-1091140620-3583751967-1732444380)(A;NP;CC;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736)(A;NP;CC;;;S-1-5-80-2455429942-3131183193-3617688776-595395669-3772047725)(A;NP;CC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142)(A;NP;CC;;;S-1-5-80-3916113136-2435487254-2535488001-4050622930-2364918814)(A;NP;CC;;;S-1-5-80-3232712927-1625117661-2590453128-1738570065-3637376297)(A;NP;CC;;;S-1-5-80-3981856537-581775623-1136376035-2066872258-409572886)(A;NP;CC;;;S-1-5-80-689100834-1985168674-2379302174-2224748125-4125308070)(A;NP;CC;;;S-1-5-80-2119957892-4152124429-3625998117-4006912763-1737903618)(A;NP;CC;;;S-1-5-80-1987853863-1639573247-1110726908-1137832616-3599624523)(A;NP;CC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329)(A;NP;CC;;;S-1-5-80-113310567-2163499630-2787090463-221477905-209227094)(A;NP;CC;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736)S:NO_ACCESS_CONTROL FWP_ACTION_BLOCK {de04f4bd-a953-47f6-b1b6-eb6e24a546d6} 68046 FWP_UINT64 68719476736 {27d0382a-b7fd-4536-a470-71a5ea27cb99} FTP Inspection Filter This filter enables inspection of FTP. FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 1024 FWP_UINT16 65535 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e022e} 0 68053 FWP_UINT64 18446744073709551615 {58fd9e51-dfb2-4b8b-9f4e-fbe76618a080} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68088 FWP_UINT64 18446744073709551615 {98b70176-3bcc-4734-b93b-4eaeeeb6a37b} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68089 FWP_UINT64 18446744073709551615 {a300f22a-ae94-4b5f-aa5f-33ff9fbbfce7} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68090 FWP_UINT64 18446744073709551615 {d90cbaa3-eaf3-4021-a70a-c130bc327519} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68091 FWP_UINT64 18446744073709551615 {c364ce80-e89b-489f-b7ab-ba5da061dc31} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68092 FWP_UINT64 18446744073709551615 {941565df-dade-42f5-b5cb-ed48e857a508} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68093 FWP_UINT64 18446744073709551615 {00ea3695-8388-45b7-9aaf-49947f97dec5} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68094 FWP_UINT64 18446744073709551615 {73508c22-0149-461b-b1f2-3cdc929feea1} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68095 FWP_UINT64 18446744073709551615 {198ff4f2-5d0a-40bf-b43e-1d4684b11b65} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68096 FWP_UINT64 18446744073709551615 {7f8a98cc-cc4b-410d-ad3c-2d0dd1aebe0e} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68097 FWP_UINT64 18446744073709551615 {d2e40d8a-cc87-4ad1-b496-bd28937f886e} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68098 FWP_UINT64 18446744073709551615 {6c68a273-fac7-4be9-a9d1-fe56ffa5981d} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 135 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68099 FWP_UINT64 18446744073709551615 {5f7e61e7-2af6-4f12-9a07-5522f61f4b86} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68100 FWP_UINT64 18446744073709551615 {19590b9d-8cbb-42c2-9a7a-ab0775ecd93b} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68101 FWP_UINT64 18446744073709551615 {29f7b740-30f9-4972-adc7-cbfa92803082} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68102 FWP_UINT64 18446744073709551615 {6dfc4acb-0027-4d5a-b527-f452291ec069} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68103 FWP_UINT64 18446744073709551615 {b1e42ad1-a690-42c2-abf2-57ce5aded578} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68104 FWP_UINT64 18446744073709551615 {eb76a71a-600b-4489-be24-d5cf94512966} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68105 FWP_UINT64 18446744073709551615 {fed10772-760a-4002-a372-699758835bb5} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68106 FWP_UINT64 18446744073709551615 {0ce6b8ef-3f7b-44c7-b923-538b5c6f85cf} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68107 FWP_UINT64 18446744073709551615 {40e896f7-d6fa-48a0-8d3d-b89e5c480196} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68108 FWP_UINT64 18446744073709551615 {7bead13c-ef8a-406c-b796-15424030645a} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68109 FWP_UINT64 18446744073709551615 {0ef1dbc6-5f1c-46fa-a4be-7b601943da68} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68110 FWP_UINT64 18446744073709551615 {56610786-c7ca-41fc-abce-e661c467a692} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 134 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68111 FWP_UINT64 18446744073709551615 {0677b532-ed94-451f-a801-b69c51ae8932} InternetClientServer Inbound Default Rule InternetClientServer Inbound Default Rule FWPM_PROVIDER_MPSSVC_APP_ISOLATION 1c01000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_EMPTY FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_NOT_EQUAL FWP_SID S-1-0-0 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE :: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:LSD:(A;;CC;;;S-1-15-3-2)(A;;CC;;;WD)(A;;CC;;;AN) FWP_ACTION_PERMIT 0 68209 FWP_UINT64 206158431224 {1582a99d-c924-4a69-8c31-38c3e811b06e} Microsoft Media Foundation Network Source IN [UDP 5004-5009] InBound Rule for the Microsoft Media Foundation's Capture SVC to open UDP port to enable RTSP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 1f01000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 5000 FWP_UINT16 5020 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68218 FWP_UINT64 10700788216413290496 {77733464-f8b8-4139-82ce-d711bf56aa0c} Microsoft Media Foundation Network Source IN [UDP 5004-5009] InBound Rule for the Microsoft Media Foundation's Capture SVC to open UDP port to enable RTSP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 1f01000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 5000 FWP_UINT16 5020 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68219 FWP_UINT64 10700788225003225088 {b12e5bff-8fa5-42bd-a2d5-9904d7ca9d03} Microsoft Media Foundation Network Source IN [TCP 554] InBound Rule for the Microsoft Media Foundation's Capture SVC to open TCP port to enable RTSP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2001000000000000 ....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 554 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68227 FWP_UINT64 10700799211529568256 {bb69d143-1985-454a-b089-308e8e8dc13e} Microsoft Media Foundation Network Source IN [TCP 554] InBound Rule for the Microsoft Media Foundation's Capture SVC to open TCP port to enable RTSP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2001000000000000 ....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 554 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68228 FWP_UINT64 10700799220119502848 {5acc61db-fd65-4cb1-bc32-8ca12e976908} Microsoft Media Foundation Network Source IN [TCP 554] InBound Rule for the Microsoft Media Foundation's Capture SVC to open TCP port to enable RTSP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2001000000000000 ....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 8554 FWP_UINT16 8558 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68230 FWP_UINT64 10700792614459801600 {aab1ec78-7ee4-49d1-98fc-8c3b631ae069} Microsoft Media Foundation Network Source IN [TCP 554] InBound Rule for the Microsoft Media Foundation's Capture SVC to open TCP port to enable RTSP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2001000000000000 ....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3915894004-2104103821-3047269622-1811662266-774708259) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 8554 FWP_UINT16 8558 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68231 FWP_UINT64 10700792623049736192 {35a7aaa5-1838-4ae3-817a-69cb1c19cd18} Core Networking - Multicast Listener Done (ICMPv6-In) Multicast Listener Done messages inform local routers that there are no longer any members remaining for a specific multicast address on the subnet. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2101000000000000 !....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 132 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWP_ACTION_PERMIT 0 68232 FWP_UINT64 10700799142810091520 {f93ea8eb-52e1-42c1-9ad4-7c36ffb021d6} Core Networking - Multicast Listener Done (ICMPv6-In) Multicast Listener Done messages inform local routers that there are no longer any members remaining for a specific multicast address on the subnet. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2101000000000000 !....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 132 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68233 FWP_UINT64 10700799151400026112 {a06c507e-0e22-4963-8f44-87618653a39c} WFD ASP Coordination Protocol (UDP-In) Inbound rule for the WLAN Service to allow coordination protocol for WFD Service sessions [UDP 7235] FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2201000000000000 "....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWP_ACTION_PERMIT 0 68238 FWP_UINT64 10700801135674916864 {33989d24-4033-4bd0-b74f-9bcf3d09dbeb} WFD ASP Coordination Protocol (UDP-In) Inbound rule for the WLAN Service to allow coordination protocol for WFD Service sessions [UDP 7235] FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2201000000000000 "....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68239 FWP_UINT64 10700801144264851456 {6a14e12f-4cc9-4ba8-8087-ce034ce64aa9} Core Networking - Multicast Listener Report v2 (ICMPv6-In) Multicast Listener Report v2 message is used by a listening node to either immediately report its interest in receiving multicast traffic at a specific multicast address or in response to a Multicast Listener Query. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2301000000000000 #....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 143 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWP_ACTION_PERMIT 0 68240 FWP_UINT64 10700799142810091520 {b39cb12b-597e-47f5-8e3f-9dd402f046f7} Core Networking - Multicast Listener Report v2 (ICMPv6-In) Multicast Listener Report v2 message is used by a listening node to either immediately report its interest in receiving multicast traffic at a specific multicast address or in response to a Multicast Listener Query. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2301000000000000 #....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 143 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68241 FWP_UINT64 10700799151400026112 {58df562c-0db6-447a-9f51-a1a9ca36a7da} Core Networking - Multicast Listener Query (ICMPv6-In) An IPv6 multicast-capable router uses the Multicast Listener Query message to query a link for multicast group membership. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2401000000000000 $....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 130 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWP_ACTION_PERMIT 0 68242 FWP_UINT64 10700799142810091520 {422c78ee-d74f-4287-92cb-f7ebd9b13d7f} Core Networking - Multicast Listener Query (ICMPv6-In) An IPv6 multicast-capable router uses the Multicast Listener Query message to query a link for multicast group membership. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2401000000000000 $....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 130 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68243 FWP_UINT64 10700799151400026112 {c86577a4-f769-4666-8ef8-da0d20234c50} Core Networking - Multicast Listener Report (ICMPv6-In) The Multicast Listener Report message is used by a listening node to either immediately report its interest in receiving multicast traffic at a specific multicast address or in response to a Multicast Listener Query. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2501000000000000 %....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 131 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWP_ACTION_PERMIT 0 68244 FWP_UINT64 10700799142810091520 {7aebe37a-9063-4a41-bfe2-c5f36fc48aa1} Core Networking - Multicast Listener Report (ICMPv6-In) The Multicast Listener Report message is used by a listening node to either immediately report its interest in receiving multicast traffic at a specific multicast address or in response to a Multicast Listener Query. FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2501000000000000 %....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 530079007300740065006d000000 System FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWP_MATCH_EQUAL FWP_UINT16 131 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68245 FWP_UINT64 10700799151400026112 {c60857b2-86f1-4479-ac73-2d5cc8d478cb} mDNS (UDP-In) Inbound rule for mDNS traffic [UDP] FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2601000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-859482183-879914841-863379149-1145462774-2388618682) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68250 FWP_UINT64 10700799211529568256 {47eea8d3-5188-4143-acdf-202833261322} mDNS (UDP-In) Inbound rule for mDNS traffic [UDP] FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2601000000000000 ........ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-859482183-879914841-863379149-1145462774-2388618682) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5353 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68251 FWP_UINT64 10700799220119502848 {efc20c2d-6c5a-4e5b-8a34-98b4483999a2} Wi-Fi Direct ASP Coordination Protocol (UDP-In) FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2701000000000000 '....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWP_ACTION_PERMIT 0 68254 FWP_UINT64 324507594213294080 {35413795-c6d4-4215-a986-4f6430c1d790} Wi-Fi Direct ASP Coordination Protocol (UDP-In) FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2701000000000000 '....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68255 FWP_UINT64 324507602803228672 {d6b96843-703f-4584-bd03-4031d5af55ea} Allow incoming WSD to PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2901000000000000 )....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68262 FWP_UINT64 324505670067945472 {2cbbf8db-968e-4981-9676-5e84aba688c6} Allow incoming WSD to PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2901000000000000 )....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68263 FWP_UINT64 324505678657880064 {ab7af7b8-34b2-47a2-bb0d-cfac2a1a824e} Allow incoming WSD to PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2a01000000000000 *....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWP_ACTION_PERMIT 0 68266 FWP_UINT64 324472409841205248 {15f2650a-c099-4d56-a815-a14736393443} Allow incoming WSD to PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2a01000000000000 *....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68267 FWP_UINT64 324472418431139840 {69bf11f7-0703-41a2-933f-5699bf1ebbb4} Microsoft Store Microsoft Store FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2d01000000000000 -....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWP_ACTION_PERMIT 0 68291 FWP_UINT64 10376293747620052992 {436b36f0-9320-423f-a04f-1a9ca00007fb} Microsoft Store Microsoft Store FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2d01000000000000 -....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWP_ACTION_PERMIT 0 68292 FWP_UINT64 10376293748022706176 {00117075-341a-482a-acc5-e8b238826759} Microsoft Store Microsoft Store FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2d01000000000000 -....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68293 FWP_UINT64 10376293756612640768 {c5399251-8bef-439b-8544-8f21ffa7ba41} Microsoft Store Microsoft Store FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2d01000000000000 -....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 68294 FWP_UINT64 10376293748022706176 {82915472-9da4-48af-b908-8c7a02f6ddaf} Microsoft Store Microsoft Store FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WF 2d01000000000000 -....... FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 FWPM_SUBLAYER_TEREDO FWP_UINT8 9 FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_EQUAL FWP_SID S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3172917561-344687468-1323853919-1001)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-3-1)(A;;CCRC;;;S-1-15-3-2)(A;;CCRC;;;S-1-15-3-3)(A;;CCRC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833) FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68295 FWP_UINT64 10376293756612640768 FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD ALE Receive/Accept v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_ALE_REMOTE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_REMOTE_MACHINE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_NAP_CONTEXT FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_NEXTHOP_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_REAUTHORIZE_REASON FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 47 {c3dbed20-0bb6-4bf3-828d-96732e1e012f} Windows Firewall: callout Performs logging. FWPM_CALLOUT_FLAG_REGISTERED 00000200 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD 279 FWPM_LAYER_ALE_AUTH_CONNECT_V4 ALE Connect v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_ALE_REMOTE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_REMOTE_MACHINE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_NEXTHOP_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_REAUTHORIZE_REASON FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_PEER_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ALE_ORIGINAL_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_EFFECTIVE_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 48 FWPM_CALLOUT_IPSEC_ALE_CONNECT_V4 WFP Built-in IPsec ALE Connect v4 Layer Callout Applies IPsec policy modifiers to client applications. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_AUTH_CONNECT_V4 15 FWPM_CALLOUT_TCP_CHIMNEY_CONNECT_LAYER_V4 WFP Built-in TCP Chimney Offload ALE Connect v4 Layer Callout Enables or disables TCP Chimney Offload for each outgoing connection. FWPM_LAYER_ALE_AUTH_CONNECT_V4 19 FWPM_CALLOUT_SET_OPTIONS_AUTH_CONNECT_LAYER_V4 WFP Built-in Set Option ALE Connect v4 Layer Callout Sets classify options on outbound flows. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_LAYER_ALE_AUTH_CONNECT_V4 23 FWPM_CALLOUT_TCP_TEMPLATES_CONNECT_LAYER_V4 WFP Built-in TCP Templates ALE Connect v4 Layer Callout Applies TCP Template for each outgoing connection. FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_AUTH_CONNECT_V4 33 FWPM_CALLOUT_RESERVED_AUTH_CONNECT_LAYER_V4 WFP Built-in Reserved callout WFP Built-in Reserved callout FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_LAYER_ALE_AUTH_CONNECT_V4 37 FWPM_CALLOUT_POLICY_SILENT_MODE_AUTH_CONNECT_LAYER_V4 WFP Built-in Policy Silent Mode ALE Connect v4 Layer Callout Audit and log policy for outgoing connection. FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_LAYER_ALE_AUTH_CONNECT_V4 41 FWPM_CALLOUT_BUILT_IN_RESERVED_3 WFP Built-in Accept Redirect Proxy Tag ALE Connect v4 Layer Callout Tags the outgoing connection from an accept redirected proxy app to prevent infinite redirection. FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_LAYER_ALE_AUTH_CONNECT_V4 47 {c3dbed20-0bb6-4bf3-828d-96732e1e0230} Windows Firewall: callout Allows secondary connections. FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_CONNECT_V4 282 {b6b2ca61-fb98-4422-adc2-e7cf56b3680c} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66222 FWP_UINT64 18446744073709551615 {082edf36-12d7-4698-858a-cf274b78d09a} Quarantine Default Inbound Loopback Exception This filter allows loopback packets when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {30433c31-b05f-421f-8fde-018ea4c68af4} 66601 FWP_UINT64 18446744073709551615 {304a339a-845d-47b1-9ad9-df3b55c31f26} AppContainerLoopback This filter allows outbound AppContainer loopback traffic FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67196 FWP_UINT64 18446744073709551615 {a167c347-5706-417d-ab3f-dc24cdd9c69b} AppContainerLoopback This filter blocks loopback traffic to port 137 FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 137 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:LSD:(A;;CC;;;AC)(A;;CC;;;S-1-15-3-1)(A;;CC;;;S-1-15-3-2)(A;;CC;;;S-1-15-3-3)(A;;CC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833)(A;;CC;;;WD)(A;;CC;;;AN) FWP_ACTION_BLOCK {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67198 FWP_UINT64 18446744073709551615 {6b9edc4a-2d53-465e-8c54-13b65024e3b6} AppContainerLoopback This filter blocks AppContainer loopback traffic FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551614 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:LSD:(A;;CC;;;AC)(A;;CC;;;S-1-15-3-1)(A;;CC;;;S-1-15-3-2)(A;;CC;;;S-1-15-3-3)(A;;CC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833)(A;;CC;;;WD)(A;;CC;;;AN) FWP_ACTION_BLOCK {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67202 FWP_UINT64 18446744073709551614 {0a023b5d-ef1a-45a7-9cde-efcb260348a6} Default Outbound This is the default outbound filter which blocks or permits traffic based on user configured default settings FWPM_PROVIDER_MPSSVC_WF 9500000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67644 FWP_UINT64 9223372036854777848 {3bcbc8e4-3be2-43d5-8c7e-765d0de1b129} Default Outbound This is the default outbound filter which blocks or permits traffic based on user configured default settings FWPM_PROVIDER_MPSSVC_WF 9500000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 3 FWP_ACTION_PERMIT 0 67648 FWP_UINT64 3458764513820540928 {f7a47614-e0ec-4d6a-9cf2-99b8f993d1b7} UWP Default Outbound Block Rule This is the UWP Default Outbound Block filter. This filter blocks any outbound packets from UWP apps that do not have the correct capability tokens for the resource they are trying to reach. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_APP_ISOLATION 9a00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_EMPTY FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_NOT_EQUAL FWP_SID S-1-0-0 FWP_ACTION_BLOCK {11ec8831-cda4-4bab-aa69-cb45e32f4ea9} 67659 FWP_UINT64 137438953472 {81fe2467-61a3-46dc-8c59-a6a3277920ff} AxInstSV TCP outbound allow Allow only outbound TCP traffic from AxInstSV FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c100000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1058592404-331734164-3167594226-3910907650-1299295147) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67876 FWP_UINT64 985368576917504 {8b7fd125-5491-4c7a-96cb-10aacbccc7d9} Allow outbound TCP traffic from CDPSvc from any port to port 5040 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c300000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 5040 FWP_ACTION_PERMIT 0 67880 FWP_UINT64 987292722266112 {1ce149fd-f072-4c78-998c-aa9be7b13469} Allow outbound UDP traffic from any port to CDPSvc port 5050 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c400000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 5050 FWP_ACTION_PERMIT 0 67882 FWP_UINT64 987292722266112 {71a8a4a8-65c6-4a5b-9d97-f15febdc067a} Allow outbound UDP traffic from CDPSvc port 5050 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c500000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5050 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67884 FWP_UINT64 1055737321095168 {2c84d584-7bc5-40db-b6c8-b28d4e40a483} Allow outbound TCP traffic from CDPSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c600000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67886 FWP_UINT64 985368576917504 {672ccb84-0882-4542-8404-7650c867a918} Allow outbound TCP traffic from CDPSvc from any port to port 5160 (Wi-Fi Direct Transport) FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c800000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 5160 FWP_ACTION_PERMIT 0 67890 FWP_UINT64 987292722266112 {185c4b7a-6d3f-46ab-9a41-146c6ec220be} Block outbound traffic from deviceenroller.exe FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH cc00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0064006500760069006300650065006e0072006f006c006c00650072002e006500780065000000 \device\harddiskvolume3\windows\system32\deviceenroller.exe FWP_ACTION_BLOCK {4dd4109f-8980-4e66-b307-887227d761a4} 67898 FWP_UINT64 137438953472 {b832287d-98a4-4509-9edf-e796cf1a363f} Allow outbound TCP traffic from deviceenroller.exe FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH cd00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0064006500760069006300650065006e0072006f006c006c00650072002e006500780065000000 \device\harddiskvolume3\windows\system32\deviceenroller.exe FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67900 FWP_UINT64 985299857440768 {7d008627-122e-4757-94f3-40fd1e6a32ab} Block outbound traffic from omadmclient.exe FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH ce00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c006f006d00610064006d0063006c00690065006e0074002e006500780065000000 \device\harddiskvolume3\windows\system32\omadmclient.exe FWP_ACTION_BLOCK {f876928c-1566-4b9a-a110-57d531115565} 67902 FWP_UINT64 137438953472 {4d0e90b1-9d99-4847-afad-63b5336d414a} Allow outbound TCP traffic from omadmclient.exe FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH cf00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c006f006d00610064006d0063006c00690065006e0074002e006500780065000000 \device\harddiskvolume3\windows\system32\omadmclient.exe FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67904 FWP_UINT64 985299857440768 {af29eee0-e0d3-4834-8d51-668e799215c9} Block outbound traffic from dmcertinst.exe FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH d100000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0064006d00630065007200740069006e00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\dmcertinst.exe FWP_ACTION_BLOCK {3742db91-517a-4f69-b378-08c5c09364b3} 67908 FWP_UINT64 137438953472 {4fdcd4c6-c326-4b60-b320-84fc25c10cec} Allow outbound TCP traffic from dmcertinst.exe FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d200000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0064006d00630065007200740069006e00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\dmcertinst.exe FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67910 FWP_UINT64 985299857440768 {a93ec3cd-e423-4bd5-a38d-3593f40c2fe6} Allow outbound TCP traffic from DMEnrollment FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d300000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-538170410-2190149038-799223143-2506663053-4165713448) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67912 FWP_UINT64 985368576917504 {c830f042-5e91-4521-9d1a-c08aeac73a48} DhcpFirewallPolicy DhcpFirewallPolicy FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d400000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 68 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 67 FWP_ACTION_PERMIT 0 67914 FWP_UINT64 1057661466443776 {bd69a0ac-3eec-43c2-8e77-57159930f77f} DhcpFirewallPolicy DhcpFirewallPolicy FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d700000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 546 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 547 FWP_ACTION_PERMIT 0 67920 FWP_UINT64 1057661466443776 {e9f93da5-9659-46c5-9525-93d80021b23f} Device Metadata Retrieval Allow dmrc communication with WMIS FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d800000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-286057374-2594772386-1471686342-3682429118-820474675) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 80 FWP_ACTION_PERMIT 0 67922 FWP_UINT64 987292722266112 {180cd47b-5d74-4505-a1ff-cd84de1d2af5} Allow outbound UDP traffic from fdphost port 3702 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH db00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWP_ACTION_PERMIT 0 67928 FWP_UINT64 987292722266112 {8e168718-b7a5-4ae5-80ee-67e629a24d19} Allow outbound UDP traffic from fdphost port 1900 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH dd00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 1900 FWP_ACTION_PERMIT 0 67932 FWP_UINT64 987292722266112 {b0589e60-9f9a-43aa-9d6b-5f8d52dafd90} Allow outbound TCP traffic from fdphost FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH de00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67934 FWP_UINT64 985368576917504 {04419b91-af39-4843-9b6a-7676f52111a6} Allow outbound UDP traffic to LMHosts port 53 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH df00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-172094073-716411664-54255058-185476446-2329512179) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 53 FWP_ACTION_PERMIT 0 67936 FWP_UINT64 987292722266112 {5ba31685-5a93-4ec1-8559-7e4dc41cd4a0} Allow outbound TCP traffic to LMHosts port 53 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e000000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-172094073-716411664-54255058-185476446-2329512179) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 53 FWP_ACTION_PERMIT 0 67938 FWP_UINT64 987292722266112 {03d2ca8b-de9c-4271-9379-2c7e01346d1d} Allow outbound TCP traffic from AJRouter FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e300000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3532809085-2652327567-2620918877-1058261733-582902671) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67944 FWP_UINT64 985368576917504 {beec7dba-4e91-4b23-a7b7-4b946d7b0a03} Allow outbound UDP traffic from AJRouter FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e400000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3532809085-2652327567-2620918877-1058261733-582902671) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67946 FWP_UINT64 985368576917504 {0f4eb248-1eb1-44e8-af26-3139fc422409} Allow Grouping to send to port 3587 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e600000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1971585524-2528565899-3324366483-1300752743-2325226580) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 3587 FWP_ACTION_PERMIT 0 67950 FWP_UINT64 987292722266112 {8b18ea82-9285-46f1-9d5c-8825be7defbb} Allow PNRP to send to port 3540 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e800000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-372467825-374176116-1198570892-3192490889-1232022613) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3540 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 1024 FWP_UINT16 65535 FWP_ACTION_PERMIT 0 67954 FWP_UINT64 1056012199002112 {48f7051b-b0ca-4b0d-b025-7331aa9463cc} IPsec Policy Agent service hardening - LDAP/TCP Allow IPsec Policy Agent outbound LDAP/TCP traffic to Active Directory FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e900000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3044542841-3639452079-4096941652-1606687743-1256249853) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 389 FWP_ACTION_PERMIT 0 67956 FWP_UINT64 987292722266112 {2b4cfcf1-3509-4705-ab10-1385dd1b35e3} IPsec Policy Agent service hardening - LDAP/UDP Allow IPsec Policy Agent outbound LDAP/UDP traffic to Active Directory FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH ea00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3044542841-3639452079-4096941652-1606687743-1256249853) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 389 FWP_ACTION_PERMIT 0 67958 FWP_UINT64 987292722266112 {58ee92c1-2cae-43e7-ab8a-b4063a731c8a} Block all outbound traffic from SearchFilterHost FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH ed00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c00730065006100720063006800660069006c0074006500720068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\searchfilterhost.exe FWP_ACTION_BLOCK {03843031-9e01-449d-b94b-047c6fbe714c} 67964 FWP_UINT64 137438953472 {59fe49fe-991b-4b1c-bba1-70fef8bbd4c6} Allow outbound LDAP traffic from SearchIndexer FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH ee00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073006500610072006300680069006e00640065007800650072002e006500780065000000 \device\harddiskvolume3\windows\system32\searchindexer.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-117416528-2204451360-1913602512-1355018040-1234992034) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 389 FWP_ACTION_PERMIT 0 67966 FWP_UINT64 987292722266112 {0391cc05-7e9f-4eef-be8f-7ba037e44a34} Allow all outbound TCP and RPC from SPPEXTCOMOBJ FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f200000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007000700065007800740063006f006d006f0062006a002e006500780065000000 \device\harddiskvolume3\windows\system32\sppextcomobj.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3895724531-1583119856-3186271294-3795977770-3211684703) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67974 FWP_UINT64 985368576917504 {2d002771-07b6-494c-abea-3ab1a1b89169} Allow all outbound TCP ports traffic for vmicheartbeat FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f600000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-534935901-3437432317-481271085-1710633381-983106267) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 1 FWP_UINT16 65535 FWP_ACTION_PERMIT 0 67986 FWP_UINT64 985643454824448 {79d5cc16-49fc-4996-b3a5-2f02a2d79e01} Allow outbound UDP traffic from local NTP port 123 to remote NTP port 123 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f800000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-4267341169-2882910712-659946508-2704364837-2204554466) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 123 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 123 FWP_ACTION_PERMIT 0 67990 FWP_UINT64 1057661466443776 {06fc5b6e-ce4e-4355-ae4f-5b3a5c4ccf25} Allow TCP traffic from Wcmsvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH fa00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-4155767994-3874329934-3800885181-2130851812-726865888) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67994 FWP_UINT64 985368576917504 {0f6315c7-96ec-4ed5-9553-6c3ba0363148} Allow NTP traffic from Wcmsvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH fb00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-4155767994-3874329934-3800885181-2130851812-726865888) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 123 FWP_ACTION_PERMIT 0 67996 FWP_UINT64 987292722266112 {13f46409-d68e-488e-ad18-7f4fb2fe2b64} Allow Out TCP traffic from WinDefend FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH fc00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f0077007300200064006500660065006e006400650072005c006d0073006d00700065006e0067002e006500780065000000 \device\harddiskvolume3\program files\windows defender\msmpeng.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67998 FWP_UINT64 985368576917504 {aef21544-e454-4d2f-a571-e803b3a9e120} Allow outbound TCP traffic from WinHttpAutoProxySvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH fd00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2455429942-3131183193-3617688776-595395669-3772047725) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68000 FWP_UINT64 985368576917504 {8915b586-51f3-4e71-9282-cff4dbeca20c} Allow TCP traffic from lpasvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0001000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3916113136-2435487254-2535488001-4050622930-2364918814) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68010 FWP_UINT64 985368576917504 {ee4f6b00-cba1-40d4-8d89-0310b8975f30} Allow HTTP traffic from cloudidsvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0101000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2119957892-4152124429-3625998117-4006912763-1737903618) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 443 FWP_ACTION_PERMIT 0 68012 FWP_UINT64 987292722266112 {ca791cd5-85a3-4b7f-8b6b-9d9cb02ac722} Allow outgoing TCP from PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0401000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68022 FWP_UINT64 985368576917504 {4c5f7c67-c7ce-4313-a45b-70f03fbad5fc} WinDefend Outbound for TCP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0a01000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d0064006100740061005c006d006900630072006f0073006f00660074005c00770069006e0064006f0077007300200064006500660065006e006400650072005c0070006c006100740066006f0072006d005c0034002e00310038002e0032003200300031002e00310030002d0030005c006d0073006d00700065006e0067002e006500780065000000 \device\harddiskvolume3\programdata\microsoft\windows defender\platform\4.18.2201.10-0\msmpeng.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68044 FWP_UINT64 985368576917504 {d07a9c84-a50f-40c9-80f5-303ab4951d68} WSH Default Outbound Block Blocks all outbound traffic for services who have been network hardened FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE D:(A;NP;CC;;;S-1-5-80-2676549577-1911656217-2625096541-4178041876-1366760775)(A;NP;CC;;;S-1-5-80-1580948945-3239616721-2529237571-3761093093-1214243633)(A;NP;CC;;;S-1-5-80-1058592404-331734164-3167594226-3910907650-1299295147)(A;NP;CC;;;S-1-5-80-1383147646-27650227-2710666058-1662982300-1023958487)(A;NP;CC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264)(A;NP;CC;;;S-1-5-80-2611951811-1959136347-1062071333-3982815153-2811717512)(A;NP;CC;;;S-1-5-80-2839768381-3691089589-2614646340-3191585287-3380622033)(A;NP;CC;;;S-1-5-80-538170410-2190149038-799223143-2506663053-4165713448)(A;NP;CC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582)(A;NP;CC;;;S-1-5-80-2627155378-1124895651-3977206752-2157268346-3811978089)(A;NP;CC;;;S-1-5-80-3787436395-2174616005-3003730137-1094982900-1570567328)(A;NP;CC;;;S-1-5-80-2970612574-78537857-698502321-558674196-1451644582)(A;NP;CC;;;S-1-5-80-880578595-1860270145-482643319-2788375705-1540778122)(A;NP;CC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675)(A;NP;CC;;;S-1-5-80-2617507558-3328795327-711547822-311560295-1636921165)(A;NP;CC;;;S-1-5-80-89818136-74175777-88572358-3912780041-2421659406)(A;NP;CC;;;S-1-5-80-172094073-716411664-54255058-185476446-2329512179)(A;NP;CC;;;S-1-5-80-3532809085-2652327567-2620918877-1058261733-582902671)(A;NP;CC;;;S-1-5-80-3088073201-1464728630-1879813800-1107566885-823218052)(A;NP;CC;;;S-1-5-80-2898649604-2335086160-1904548223-3761738420-3855444835)(A;NP;CC;;;S-1-5-80-1971585524-2528565899-3324366483-1300752743-2325226580)(A;NP;CC;;;S-1-5-80-967499406-1694984581-2959056265-2481940682-939264259)(A;NP;CC;;;S-1-5-80-1948712186-1330865447-943413596-1669284603-1648638051)(A;NP;CC;;;S-1-5-80-3596911058-2952229928-1888671852-1743692427-614402820)(A;NP;CC;;;S-1-5-80-372467825-374176116-1198570892-3192490889-1232022613)(A;NP;CC;;;S-1-5-80-3141781312-1794533130-3616533224-2008760771-2116720301)(A;NP;CC;;;S-1-5-80-3044542841-3639452079-4096941652-1606687743-1256249853)(A;NP;CC;;;S-1-5-80-117416528-2204451360-1913602512-1355018040-1234992034)(A;NP;CC;;;S-1-5-80-3964583643-2633443559-2834438935-3739664028-1580655619)(A;NP;CC;;;S-1-5-80-3895724531-1583119856-3186271294-3795977770-3211684703)(A;NP;CC;;;S-1-5-80-2590341223-3996088049-3993122417-23640849-324535191)(A;NP;CC;;;S-1-5-80-949921180-3923668869-394927020-528789358-3592448931)(A;NP;CC;;;S-1-5-80-768763963-4214222998-2156221936-2953597973-713500239)(A;NP;CC;;;S-1-5-80-2014626298-1656748749-3847481816-918933055-2469338456)(A;NP;CC;;;S-1-5-80-1989757894-211065159-731672622-1783776043-3948168785)(A;NP;CC;;;S-1-5-80-3074984378-4122987768-2130325677-2031866499-3405430279)(A;NP;CC;;;S-1-5-80-534935901-3437432317-481271085-1710633381-983106267)(A;NP;CC;;;S-1-5-80-1877308096-3090306141-3032871208-3115266146-1400827410)(A;NP;CC;;;S-1-5-80-3076811988-2254870394-2658297454-3934945422-2393138642)(A;NP;CC;;;S-1-5-80-3110303136-3426481729-3186938678-1087894076-2178433439)(A;NP;CC;;;S-1-5-80-3098585136-2538892366-1097114017-2832417424-2016953023)(A;NP;CC;;;S-1-5-80-235582178-102246843-358262472-4132936818-1867412993)(A;NP;CC;;;S-1-5-80-1752088424-1054500994-3489791022-3310831482-3926524968)(A;NP;CC;;;S-1-5-80-1549550529-11381693-4027442525-4081535042-2424139505)(A;NP;CC;;;S-1-5-80-4155767994-3874329934-3800885181-2130851812-726865888)(A;NP;CC;;;S-1-5-80-3524758515-3090971750-345616940-2322499744-3530715838)(A;NP;CC;;;S-1-5-80-3299868208-4286319593-1091140620-3583751967-1732444380)(A;NP;CC;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736)(A;NP;CC;;;S-1-5-80-2455429942-3131183193-3617688776-595395669-3772047725)(A;NP;CC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142)(A;NP;CC;;;S-1-5-80-3916113136-2435487254-2535488001-4050622930-2364918814)(A;NP;CC;;;S-1-5-80-3232712927-1625117661-2590453128-1738570065-3637376297)(A;NP;CC;;;S-1-5-80-3981856537-581775623-1136376035-2066872258-409572886)(A;NP;CC;;;S-1-5-80-689100834-1985168674-2379302174-2224748125-4125308070)(A;NP;CC;;;S-1-5-80-2119957892-4152124429-3625998117-4006912763-1737903618)(A;NP;CC;;;S-1-5-80-1987853863-1639573247-1110726908-1137832616-3599624523)(A;NP;CC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329)(A;NP;CC;;;S-1-5-80-113310567-2163499630-2787090463-221477905-209227094)(A;NP;CC;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736)S:NO_ACCESS_CONTROL FWP_ACTION_BLOCK {de04f4bd-a953-47f6-b1b6-eb6e24a546d6} 68049 FWP_UINT64 68719476736 {e4639f53-f9fd-4949-aec7-e3eefd4e43b4} FTP Inspection Filter This filter enables inspection of FTP. FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 1024 FWP_UINT16 65535 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e0230} 0 68054 FWP_UINT64 18446744073709551615 {44a498f6-139a-41f2-8d9b-a4ec3885601a} InternetClientServer Outbound Default Rule InternetClientServer Outbound Default Rule FWPM_PROVIDER_MPSSVC_APP_ISOLATION 1d01000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_EMPTY FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_NOT_EQUAL FWP_SID S-1-0-0 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 0.0.0.0 FWP_UINT32 255.255.255.255 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:LSD:(A;;CC;;;S-1-15-3-2)(A;;CC;;;WD)(A;;CC;;;AN) FWP_ACTION_PERMIT 0 68210 FWP_UINT64 206158432248 {4c0965e1-a644-4286-b045-9dd0db4c0ac6} InternetClient Default Rule InternetClient Default Rule FWPM_PROVIDER_MPSSVC_APP_ISOLATION 1e01000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_EMPTY FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_NOT_EQUAL FWP_SID S-1-0-0 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 0.0.0.0 FWP_UINT32 255.255.255.255 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:LSD:(A;;CC;;;S-1-15-3-1)(A;;CC;;;WD)(A;;CC;;;AN) FWP_ACTION_PERMIT 0 68212 FWP_UINT64 206158432248 {c70ec623-3900-4841-b0d8-57ba37b02d03} Wi-Fi Direct ASP Coordination Protocol (UDP-Out) FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2801000000000000 (....... FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWP_ACTION_PERMIT 0 68256 FWP_UINT64 253259241672933376 {825d479b-33b3-47ee-9667-cddf6646c94a} Wi-Fi Direct ASP Coordination Protocol (UDP-Out) FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2801000000000000 (....... FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68257 FWP_UINT64 253259276032671744 {7f29d8b2-c34e-4676-a939-0a56f30f4fb2} Allow outgoing WSD from PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2b01000000000000 +....... FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68268 FWP_UINT64 253257317527584768 {71d65205-3137-4c02-a833-96c6b1ee73d8} Allow outgoing WSD from PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2b01000000000000 +....... FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68269 FWP_UINT64 253257351887323136 {427a2740-809d-4a32-8a1e-03584c5ca624} Allow outgoing WSD from PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2c01000000000000 ,....... FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWP_ACTION_PERMIT 0 68272 FWP_UINT64 253188872928755712 {4d84d151-4c1c-40c9-a0e6-d2be2bdb3bf2} Allow outgoing WSD from PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2c01000000000000 ,....... FWPM_LAYER_ALE_AUTH_CONNECT_V4 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 10.0.2.0 FWP_UINT32 10.0.2.255 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT32 224.0.0.0 FWP_UINT32 239.255.255.255 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68273 FWP_UINT64 253188907288494080 FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD ALE Connect v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_ALE_REMOTE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_REMOTE_MACHINE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_NEXTHOP_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_REAUTHORIZE_REASON FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_PEER_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ALE_ORIGINAL_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_EFFECTIVE_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 49 {c3dbed20-0bb6-4bf3-828d-96732e1e0131} Windows Firewall: callout Performs logging. FWPM_CALLOUT_FLAG_REGISTERED 00000200 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD 274 FWPM_LAYER_ALE_AUTH_CONNECT_V6 ALE Connect v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_ALE_REMOTE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_REMOTE_MACHINE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_NEXTHOP_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_REAUTHORIZE_REASON FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_PEER_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ALE_ORIGINAL_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_EFFECTIVE_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 50 FWPM_CALLOUT_IPSEC_ALE_CONNECT_V6 WFP Built-in IPsec ALE Connect v6 Layer Callout Applies IPsec policy modifiers to client applications. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_AUTH_CONNECT_V6 16 FWPM_CALLOUT_TCP_CHIMNEY_CONNECT_LAYER_V6 WFP Built-in TCP Chimney Offload ALE Connect v6 Layer Callout Enables or disables TCP Chimney Offload for each outgoing connection. FWPM_LAYER_ALE_AUTH_CONNECT_V6 20 FWPM_CALLOUT_SET_OPTIONS_AUTH_CONNECT_LAYER_V6 WFP Built-in Set Option ALE Connect v6 Layer Callout Sets classify options on outbound flows. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_LAYER_ALE_AUTH_CONNECT_V6 24 FWPM_CALLOUT_TCP_TEMPLATES_CONNECT_LAYER_V6 WFP Built-in TCP Templates ALE Connect v6 Layer Callout Applies TCP Template for each outgoing connection. FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_AUTH_CONNECT_V6 34 FWPM_CALLOUT_RESERVED_AUTH_CONNECT_LAYER_V6 WFP Built-in Reserved callout WFP Built-in Reserved callout FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_LAYER_ALE_AUTH_CONNECT_V6 38 FWPM_CALLOUT_POLICY_SILENT_MODE_AUTH_CONNECT_LAYER_V6 WFP Built-in Policy Silent Mode ALE Connect v6 Layer Callout Audit and log policy for outgoing connection. FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_LAYER_ALE_AUTH_CONNECT_V6 42 FWPM_CALLOUT_BUILT_IN_RESERVED_4 WFP Built-in Accept Redirect Proxy Tag ALE Connect v6 Layer Callout Tags the outgoing connection from an accept redirected proxy app to prevent infinite redirection. FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_LAYER_ALE_AUTH_CONNECT_V6 48 {c3dbed20-0bb6-4bf3-828d-96732e1e0232} Windows Firewall: callout Allows secondary connections. FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_CONNECT_V6 283 {0aa7fff8-919f-453c-928c-28a12122ba38} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {93132c36-6e06-4e6f-a10b-218787cd49cf} 66223 FWP_UINT64 18446744073709551615 {15d327cf-89c3-4032-9ded-774ae6b0b49f} Quarantine Default Inbound Loopback Exception This filter allows loopback packets when the network was in quarantine state. The quarantine state was triggered by a network state change. FWPM_FILTER_FLAG_PERSISTENT FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_QUARANTINE FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {30433c31-b05f-421f-8fde-018ea4c68af4} 66600 FWP_UINT64 18446744073709551615 {2c73ec55-e34c-4ebd-8e2e-6268206bc153} AppContainerLoopback This filter allows outbound AppContainer loopback traffic FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWP_ACTION_PERMIT {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67197 FWP_UINT64 18446744073709551615 {d26fbfe7-0a28-45b2-83fd-95b733e1a9fd} AppContainerLoopback This filter blocks loopback traffic to port 137 FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551615 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 137 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:LSD:(A;;CC;;;AC)(A;;CC;;;S-1-15-3-1)(A;;CC;;;S-1-15-3-2)(A;;CC;;;S-1-15-3-3)(A;;CC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833)(A;;CC;;;WD)(A;;CC;;;AN) FWP_ACTION_BLOCK {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67199 FWP_UINT64 18446744073709551615 {c5fbb5ec-e4fc-4aac-810a-4219a166cf1c} AppContainerLoopback This filter blocks AppContainer loopback traffic FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_UINT64 18446744073709551614 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 1 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:LSD:(A;;CC;;;AC)(A;;CC;;;S-1-15-3-1)(A;;CC;;;S-1-15-3-2)(A;;CC;;;S-1-15-3-3)(A;;CC;;;S-1-15-3-4214768333-1334025770-122408079-3919188833)(A;;CC;;;WD)(A;;CC;;;AN) FWP_ACTION_BLOCK {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} 67203 FWP_UINT64 18446744073709551614 {8c247d5b-b402-40bb-a35b-a55ec3043f1a} Default Outbound This is the default outbound filter which blocks or permits traffic based on user configured default settings FWPM_PROVIDER_MPSSVC_WF 9500000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 8 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT 0 67646 FWP_UINT64 9223372036854776824 {0c22b647-52e8-484c-aaea-f3d7f4d9dc11} Default Outbound This is the default outbound filter which blocks or permits traffic based on user configured default settings FWPM_PROVIDER_MPSSVC_WF 9500000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT8 3 FWP_ACTION_PERMIT 0 67650 FWP_UINT64 3458764513820540928 {3af696f6-6a4c-47bb-b032-e6c4faf8cb88} UWP Default Outbound Block Rule This is the UWP Default Outbound Block filter. This filter blocks any outbound packets from UWP apps that do not have the correct capability tokens for the resource they are trying to reach. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_APP_ISOLATION 9a00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_EMPTY FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_NOT_EQUAL FWP_SID S-1-0-0 FWP_ACTION_BLOCK {11ec8831-cda4-4bab-aa69-cb45e32f4ea9} 67660 FWP_UINT64 68719476736 {091b498c-ac44-4e38-8c3d-f40842f71020} AxInstSV TCP outbound allow Allow only outbound TCP traffic from AxInstSV FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c100000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1058592404-331734164-3167594226-3910907650-1299295147) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67877 FWP_UINT64 105656195481600 {66fc1e24-94a1-4f94-8737-a817f1f93509} Allow outbound TCP traffic from CDPSvc from any port to port 5040 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c300000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 5040 FWP_ACTION_PERMIT 0 67881 FWP_UINT64 106068512342016 {cab91dde-a33b-4d6d-a373-6f1a085eeee2} Allow outbound UDP traffic from any port to CDPSvc port 5050 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c400000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 5050 FWP_ACTION_PERMIT 0 67883 FWP_UINT64 106068512342016 {12268eee-a96f-414e-ada7-93a9394ae68b} Allow outbound UDP traffic from CDPSvc port 5050 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c500000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 5050 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67885 FWP_UINT64 123248381526016 {2bea35b9-7546-4ba2-bc6d-a11e73d8837b} Allow outbound TCP traffic from CDPSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c600000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67887 FWP_UINT64 105656195481600 {2a9440fb-0bb0-4ff6-8154-d72d60d909a7} Allow outbound TCP traffic from CDPSvc from any port to port 5160 (Wi-Fi Direct Transport) FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH c800000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 5160 FWP_ACTION_PERMIT 0 67891 FWP_UINT64 106068512342016 {8f1aa30f-7e4d-48d2-8fdf-35af8b7e5b61} Block outbound traffic from deviceenroller.exe FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH cc00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0064006500760069006300650065006e0072006f006c006c00650072002e006500780065000000 \device\harddiskvolume3\windows\system32\deviceenroller.exe FWP_ACTION_BLOCK {4dd4109f-8980-4e66-b307-887227d761a4} 67899 FWP_UINT64 68719476736 {6ce0ad1a-cbde-40c6-b942-6c6e066d8c2a} Allow outbound TCP traffic from deviceenroller.exe FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH cd00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0064006500760069006300650065006e0072006f006c006c00650072002e006500780065000000 \device\harddiskvolume3\windows\system32\deviceenroller.exe FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67901 FWP_UINT64 105621835743232 {f30f70ce-47ff-49f7-ba7c-145a3a153449} Block outbound traffic from omadmclient.exe FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH ce00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c006f006d00610064006d0063006c00690065006e0074002e006500780065000000 \device\harddiskvolume3\windows\system32\omadmclient.exe FWP_ACTION_BLOCK {f876928c-1566-4b9a-a110-57d531115565} 67903 FWP_UINT64 68719476736 {d44825a1-c9fc-485f-98ce-3dd7eecc4876} Allow outbound TCP traffic from omadmclient.exe FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH cf00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c006f006d00610064006d0063006c00690065006e0074002e006500780065000000 \device\harddiskvolume3\windows\system32\omadmclient.exe FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67905 FWP_UINT64 105621835743232 {30e3a6fe-4e5f-4560-8c1b-62c80250709a} Block outbound traffic from dmcertinst.exe FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH d100000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0064006d00630065007200740069006e00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\dmcertinst.exe FWP_ACTION_BLOCK {3742db91-517a-4f69-b378-08c5c09364b3} 67909 FWP_UINT64 68719476736 {fd5d3521-ac9a-4868-9645-f41db0a96e6a} Allow outbound TCP traffic from dmcertinst.exe FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d200000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0064006d00630065007200740069006e00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\dmcertinst.exe FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67911 FWP_UINT64 105621835743232 {1e6890f9-a50c-4153-a5fc-33c0ba2a1836} Allow outbound TCP traffic from DMEnrollment FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d300000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-538170410-2190149038-799223143-2506663053-4165713448) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67913 FWP_UINT64 105656195481600 {0036b900-9ed0-4134-bb44-eea7ec2cedee} DhcpFirewallPolicy DhcpFirewallPolicy FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d400000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 68 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 67 FWP_ACTION_PERMIT 0 67915 FWP_UINT64 123660698386432 {296e7a35-573a-40e4-b4e4-b9b262f32db0} DhcpFirewallPolicy DhcpFirewallPolicy FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d700000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 546 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 547 FWP_ACTION_PERMIT 0 67921 FWP_UINT64 123660698386432 {166a1317-b522-45a1-80ea-9e19153e6b25} Device Metadata Retrieval Allow dmrc communication with WMIS FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH d800000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-286057374-2594772386-1471686342-3682429118-820474675) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 80 FWP_ACTION_PERMIT 0 67923 FWP_UINT64 106068512342016 {a777788c-4705-40a1-ab2b-5a19760caed9} Allow outbound UDP traffic from fdphost port 3702 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH db00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWP_ACTION_PERMIT 0 67929 FWP_UINT64 106068512342016 {f0c77ee0-11f9-4b1c-a42d-8f616ae354f3} Allow outbound UDP traffic from fdphost port 1900 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH dd00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 1900 FWP_ACTION_PERMIT 0 67933 FWP_UINT64 106068512342016 {69327dfd-e878-4369-929c-e3b6e676fbfd} Allow outbound TCP traffic from fdphost FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH de00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67935 FWP_UINT64 105656195481600 {626987bd-a33b-47b9-9615-37f568d01d82} Allow outbound UDP traffic to LMHosts port 53 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH df00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-172094073-716411664-54255058-185476446-2329512179) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 53 FWP_ACTION_PERMIT 0 67937 FWP_UINT64 106068512342016 {c29ccedf-39ff-434c-9a77-dcbf23e65d78} Allow outbound TCP traffic to LMHosts port 53 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e000000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-172094073-716411664-54255058-185476446-2329512179) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 53 FWP_ACTION_PERMIT 0 67939 FWP_UINT64 106068512342016 {c0781e20-e98e-4edd-8629-83265170d024} Allow outbound TCP traffic from AJRouter FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e300000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3532809085-2652327567-2620918877-1058261733-582902671) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67945 FWP_UINT64 105656195481600 {19a3f689-f230-43bc-a1c6-ea367563c31f} Allow outbound UDP traffic from AJRouter FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e400000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3532809085-2652327567-2620918877-1058261733-582902671) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 67947 FWP_UINT64 105656195481600 {eb9eb551-58b9-438d-93e3-5e9bfd3fa4a4} Allow Grouping to send to port 3587 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e600000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1971585524-2528565899-3324366483-1300752743-2325226580) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 3587 FWP_ACTION_PERMIT 0 67951 FWP_UINT64 106068512342016 {72db2410-37d0-4a0c-ab90-2f27b91f8fa6} Allow PNRP to send to port 3540 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e800000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-372467825-374176116-1198570892-3192490889-1232022613) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3540 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 1024 FWP_UINT16 65535 FWP_ACTION_PERMIT 0 67955 FWP_UINT64 123385820479488 {fc565d30-798d-41ae-9037-248e9c9ce633} IPsec Policy Agent service hardening - LDAP/TCP Allow IPsec Policy Agent outbound LDAP/TCP traffic to Active Directory FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH e900000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3044542841-3639452079-4096941652-1606687743-1256249853) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 389 FWP_ACTION_PERMIT 0 67957 FWP_UINT64 106068512342016 {051461c3-1d6e-4214-9e67-820fc2e2d10a} IPsec Policy Agent service hardening - LDAP/UDP Allow IPsec Policy Agent outbound LDAP/UDP traffic to Active Directory FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH ea00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3044542841-3639452079-4096941652-1606687743-1256249853) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 389 FWP_ACTION_PERMIT 0 67959 FWP_UINT64 106068512342016 {755391ea-bc1b-4822-907e-1102749cfb38} Block all outbound traffic from SearchFilterHost FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH ed00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c00730065006100720063006800660069006c0074006500720068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\searchfilterhost.exe FWP_ACTION_BLOCK {03843031-9e01-449d-b94b-047c6fbe714c} 67965 FWP_UINT64 68719476736 {6a06e8ac-2968-42da-8e51-0e4a0d3b92a4} Allow outbound LDAP traffic from SearchIndexer FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH ee00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073006500610072006300680069006e00640065007800650072002e006500780065000000 \device\harddiskvolume3\windows\system32\searchindexer.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-117416528-2204451360-1913602512-1355018040-1234992034) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 389 FWP_ACTION_PERMIT 0 67967 FWP_UINT64 106068512342016 {25a4c983-4191-4c5a-8895-590d672049e0} Allow all outbound TCP and RPC from SPPEXTCOMOBJ FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f200000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007000700065007800740063006f006d006f0062006a002e006500780065000000 \device\harddiskvolume3\windows\system32\sppextcomobj.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3895724531-1583119856-3186271294-3795977770-3211684703) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67975 FWP_UINT64 105656195481600 {4222b1e0-05d8-45ef-b1d6-eda36f16a14f} Allow all outbound TCP ports traffic for vmicheartbeat FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f600000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-534935901-3437432317-481271085-1710633381-983106267) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 1 FWP_UINT16 65535 FWP_ACTION_PERMIT 0 67987 FWP_UINT64 105793634435072 {1b42b924-8daa-4a22-a438-4ce931610f24} Allow outbound UDP traffic from local NTP port 123 to remote NTP port 123 FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH f800000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-4267341169-2882910712-659946508-2704364837-2204554466) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 123 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 123 FWP_ACTION_PERMIT 0 67991 FWP_UINT64 123660698386432 {f79665c6-3633-4d7b-9c44-461d448be8be} Allow TCP traffic from Wcmsvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH fa00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-4155767994-3874329934-3800885181-2130851812-726865888) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67995 FWP_UINT64 105656195481600 {6b276741-4138-4dd3-8d90-394998728306} Allow NTP traffic from Wcmsvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH fb00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-4155767994-3874329934-3800885181-2130851812-726865888) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 123 FWP_ACTION_PERMIT 0 67997 FWP_UINT64 106068512342016 {65ad8ec4-5659-493a-998e-f37f54973c02} Allow Out TCP traffic from WinDefend FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH fc00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d002000660069006c00650073005c00770069006e0064006f0077007300200064006500660065006e006400650072005c006d0073006d00700065006e0067002e006500780065000000 \device\harddiskvolume3\program files\windows defender\msmpeng.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 67999 FWP_UINT64 105656195481600 {023dae46-b7c7-40c8-90ca-45c70ba97068} Allow outbound TCP traffic from WinHttpAutoProxySvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH fd00000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2455429942-3131183193-3617688776-595395669-3772047725) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68001 FWP_UINT64 105656195481600 {734e929d-2a7b-45e8-a576-1721a9075413} Allow TCP traffic from lpasvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0001000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3916113136-2435487254-2535488001-4050622930-2364918814) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68011 FWP_UINT64 105656195481600 {0999a296-1a9e-40e5-bd28-08bf5f4fa7a6} Allow HTTP traffic from cloudidsvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0101000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-2119957892-4152124429-3625998117-4006912763-1737903618) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 443 FWP_ACTION_PERMIT 0 68013 FWP_UINT64 106068512342016 {d60de164-082e-49f2-8c7f-1fc5f5749f53} Allow outgoing TCP from PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0401000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68023 FWP_UINT64 105656195481600 {c0d7b9ed-5246-4d59-87fc-1d39339b02c4} WinDefend Outbound for TCP FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 0a01000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00700072006f006700720061006d0064006100740061005c006d006900630072006f0073006f00660074005c00770069006e0064006f0077007300200064006500660065006e006400650072005c0070006c006100740066006f0072006d005c0034002e00310038002e0032003200300031002e00310030002d0030005c006d0073006d00700065006e0067002e006500780065000000 \device\harddiskvolume3\programdata\microsoft\windows defender\platform\4.18.2201.10-0\msmpeng.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736) FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWP_ACTION_PERMIT 0 68045 FWP_UINT64 105656195481600 {05baa320-a8d8-40e3-b310-3b7d7c115dac} WSH Default Outbound Block Blocks all outbound traffic for services who have been network hardened FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WSH FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE D:(A;NP;CC;;;S-1-5-80-2676549577-1911656217-2625096541-4178041876-1366760775)(A;NP;CC;;;S-1-5-80-1580948945-3239616721-2529237571-3761093093-1214243633)(A;NP;CC;;;S-1-5-80-1058592404-331734164-3167594226-3910907650-1299295147)(A;NP;CC;;;S-1-5-80-1383147646-27650227-2710666058-1662982300-1023958487)(A;NP;CC;;;S-1-5-80-3433512109-503559027-1389316256-1766580070-2256751264)(A;NP;CC;;;S-1-5-80-2611951811-1959136347-1062071333-3982815153-2811717512)(A;NP;CC;;;S-1-5-80-2839768381-3691089589-2614646340-3191585287-3380622033)(A;NP;CC;;;S-1-5-80-538170410-2190149038-799223143-2506663053-4165713448)(A;NP;CC;;;S-1-5-80-2940520708-3855866260-481812779-327648279-1710889582)(A;NP;CC;;;S-1-5-80-2627155378-1124895651-3977206752-2157268346-3811978089)(A;NP;CC;;;S-1-5-80-3787436395-2174616005-3003730137-1094982900-1570567328)(A;NP;CC;;;S-1-5-80-2970612574-78537857-698502321-558674196-1451644582)(A;NP;CC;;;S-1-5-80-880578595-1860270145-482643319-2788375705-1540778122)(A;NP;CC;;;S-1-5-80-364023826-931424190-487969545-1024119571-74567675)(A;NP;CC;;;S-1-5-80-2617507558-3328795327-711547822-311560295-1636921165)(A;NP;CC;;;S-1-5-80-89818136-74175777-88572358-3912780041-2421659406)(A;NP;CC;;;S-1-5-80-172094073-716411664-54255058-185476446-2329512179)(A;NP;CC;;;S-1-5-80-3532809085-2652327567-2620918877-1058261733-582902671)(A;NP;CC;;;S-1-5-80-3088073201-1464728630-1879813800-1107566885-823218052)(A;NP;CC;;;S-1-5-80-2898649604-2335086160-1904548223-3761738420-3855444835)(A;NP;CC;;;S-1-5-80-1971585524-2528565899-3324366483-1300752743-2325226580)(A;NP;CC;;;S-1-5-80-967499406-1694984581-2959056265-2481940682-939264259)(A;NP;CC;;;S-1-5-80-1948712186-1330865447-943413596-1669284603-1648638051)(A;NP;CC;;;S-1-5-80-3596911058-2952229928-1888671852-1743692427-614402820)(A;NP;CC;;;S-1-5-80-372467825-374176116-1198570892-3192490889-1232022613)(A;NP;CC;;;S-1-5-80-3141781312-1794533130-3616533224-2008760771-2116720301)(A;NP;CC;;;S-1-5-80-3044542841-3639452079-4096941652-1606687743-1256249853)(A;NP;CC;;;S-1-5-80-117416528-2204451360-1913602512-1355018040-1234992034)(A;NP;CC;;;S-1-5-80-3964583643-2633443559-2834438935-3739664028-1580655619)(A;NP;CC;;;S-1-5-80-3895724531-1583119856-3186271294-3795977770-3211684703)(A;NP;CC;;;S-1-5-80-2590341223-3996088049-3993122417-23640849-324535191)(A;NP;CC;;;S-1-5-80-949921180-3923668869-394927020-528789358-3592448931)(A;NP;CC;;;S-1-5-80-768763963-4214222998-2156221936-2953597973-713500239)(A;NP;CC;;;S-1-5-80-2014626298-1656748749-3847481816-918933055-2469338456)(A;NP;CC;;;S-1-5-80-1989757894-211065159-731672622-1783776043-3948168785)(A;NP;CC;;;S-1-5-80-3074984378-4122987768-2130325677-2031866499-3405430279)(A;NP;CC;;;S-1-5-80-534935901-3437432317-481271085-1710633381-983106267)(A;NP;CC;;;S-1-5-80-1877308096-3090306141-3032871208-3115266146-1400827410)(A;NP;CC;;;S-1-5-80-3076811988-2254870394-2658297454-3934945422-2393138642)(A;NP;CC;;;S-1-5-80-3110303136-3426481729-3186938678-1087894076-2178433439)(A;NP;CC;;;S-1-5-80-3098585136-2538892366-1097114017-2832417424-2016953023)(A;NP;CC;;;S-1-5-80-235582178-102246843-358262472-4132936818-1867412993)(A;NP;CC;;;S-1-5-80-1752088424-1054500994-3489791022-3310831482-3926524968)(A;NP;CC;;;S-1-5-80-1549550529-11381693-4027442525-4081535042-2424139505)(A;NP;CC;;;S-1-5-80-4155767994-3874329934-3800885181-2130851812-726865888)(A;NP;CC;;;S-1-5-80-3524758515-3090971750-345616940-2322499744-3530715838)(A;NP;CC;;;S-1-5-80-3299868208-4286319593-1091140620-3583751967-1732444380)(A;NP;CC;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736)(A;NP;CC;;;S-1-5-80-2455429942-3131183193-3617688776-595395669-3772047725)(A;NP;CC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142)(A;NP;CC;;;S-1-5-80-3916113136-2435487254-2535488001-4050622930-2364918814)(A;NP;CC;;;S-1-5-80-3232712927-1625117661-2590453128-1738570065-3637376297)(A;NP;CC;;;S-1-5-80-3981856537-581775623-1136376035-2066872258-409572886)(A;NP;CC;;;S-1-5-80-689100834-1985168674-2379302174-2224748125-4125308070)(A;NP;CC;;;S-1-5-80-2119957892-4152124429-3625998117-4006912763-1737903618)(A;NP;CC;;;S-1-5-80-1987853863-1639573247-1110726908-1137832616-3599624523)(A;NP;CC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329)(A;NP;CC;;;S-1-5-80-113310567-2163499630-2787090463-221477905-209227094)(A;NP;CC;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736)S:NO_ACCESS_CONTROL FWP_ACTION_BLOCK {de04f4bd-a953-47f6-b1b6-eb6e24a546d6} 68048 FWP_UINT64 34359738368 {892a3cae-0c40-4a8a-8aa4-cf539bbed38a} FTP Inspection Filter This filter enables inspection of FTP. FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_UINT16 1024 FWP_UINT16 65535 FWP_ACTION_CALLOUT_UNKNOWN {c3dbed20-0bb6-4bf3-828d-96732e1e0232} 0 68055 FWP_UINT64 18446744073709551615 {8b1704e1-3f4f-4fe2-bc92-0b013dd74bdb} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68112 FWP_UINT64 18446744073709551615 {9b3fa2c3-4e42-4c0f-8afb-e9f0d58e9261} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68113 FWP_UINT64 18446744073709551615 {4bfd4120-6a07-4b97-81fa-455048c76e4a} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68114 FWP_UINT64 18446744073709551615 {fcdc301b-92d9-44bb-b257-88b581b56248} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68115 FWP_UINT64 18446744073709551615 {408fb9f7-e31d-4c1a-9056-0660f482826f} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68116 FWP_UINT64 18446744073709551615 {b0b78a7e-20d4-45d6-987c-57a5783592fb} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68117 FWP_UINT64 18446744073709551615 {2f99501c-4c59-413b-b954-3ff993fdbfae} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68118 FWP_UINT64 18446744073709551615 {f8157303-181c-4c8d-b79c-47bfde1aeaf0} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68119 FWP_UINT64 18446744073709551615 {b17cc3b4-a615-46f3-b2c6-2687d34fab02} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68120 FWP_UINT64 18446744073709551615 {9b7b4e38-e234-44d2-b571-d374e27a4e0b} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68121 FWP_UINT64 18446744073709551615 {a1619fd8-02a0-47b7-b5e0-cfa96070a783} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68122 FWP_UINT64 18446744073709551615 {c08500c7-668a-4bac-945c-34b70d89be0b} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 136 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68123 FWP_UINT64 18446744073709551615 {58083807-89f3-4df3-9a42-0030b4a28be1} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68124 FWP_UINT64 18446744073709551615 {cc4f4f68-e749-4deb-9532-8eeecc65fd2e} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68125 FWP_UINT64 18446744073709551615 {8c5974d0-a252-4be3-8805-88649820ec32} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68126 FWP_UINT64 18446744073709551615 {1a3d3203-0424-42d8-8027-248351309474} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68127 FWP_UINT64 18446744073709551615 {0b8840ae-0286-4003-a653-1918f0bb88e6} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68128 FWP_UINT64 18446744073709551615 {59c1253f-d019-4d86-9d10-a620dedbf830} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68129 FWP_UINT64 18446744073709551615 {75410a3a-fb4b-4c4e-a43b-65fa2f6b12b6} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68130 FWP_UINT64 18446744073709551615 {8a3541a0-0456-4909-91bc-a016060fc4ba} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68131 FWP_UINT64 18446744073709551615 {bad0a5c5-8e42-4b2e-907d-43d004424df9} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68132 FWP_UINT64 18446744073709551615 {85fd608d-794c-47d5-9819-86dddddd6fb6} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 3 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68133 FWP_UINT64 18446744073709551615 {f09da621-2d41-44ef-80d0-142da1df005b} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 2 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68134 FWP_UINT64 18446744073709551615 {88bbe0f8-8dfc-4699-a847-a9349a4af991} Boot Time Filter This filter is in effect before the service starts. FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT FWPM_FILTER_FLAG_INDEXED FWPM_FILTER_FLAG_HAS_FILTER_ORIGIN FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 58 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 133 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWP_MATCH_EQUAL FWP_UINT8 3 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWP_MATCH_EQUAL FWP_UINT32 131 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 14 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWP_ACTION_PERMIT {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} 68135 FWP_UINT64 18446744073709551615 {429c6993-632c-4633-b930-65684a5b7300} InternetClientServer Outbound Default Rule InternetClientServer Outbound Default Rule FWPM_PROVIDER_MPSSVC_APP_ISOLATION 1d01000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_EMPTY FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_NOT_EQUAL FWP_SID S-1-0-0 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE :: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:LSD:(A;;CC;;;S-1-15-3-2)(A;;CC;;;WD)(A;;CC;;;AN) FWP_ACTION_PERMIT 0 68211 FWP_UINT64 103079216120 {82e6ae67-453a-49bd-b6e2-7b46d586602a} InternetClient Default Rule InternetClient Default Rule FWPM_PROVIDER_MPSSVC_APP_ISOLATION 1e01000000000000 ........ FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION FWP_EMPTY FWPM_CONDITION_ALE_PACKAGE_ID FWP_MATCH_NOT_EQUAL FWP_SID S-1-0-0 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE :: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ORIGINAL_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_CURRENT_PROFILE_ID FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:LSD:(A;;CC;;;S-1-15-3-1)(A;;CC;;;WD)(A;;CC;;;AN) FWP_ACTION_PERMIT 0 68213 FWP_UINT64 103079216120 {9a8412a0-0fd5-462c-a11f-eb5ec9fc9709} Wi-Fi Direct ASP Coordination Protocol (UDP-Out) FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2801000000000000 (....... FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWP_ACTION_PERMIT 0 68258 FWP_UINT64 162253247820595200 {ad448bcc-9e58-4f9d-9d01-844b5628280b} Wi-Fi Direct ASP Coordination Protocol (UDP-Out) FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2801000000000000 (....... FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-1428027539-3309602793-2678353003-1498846795-3763184142) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 7235 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68259 FWP_UINT64 162253265000464384 {ca82888c-e15c-4d97-9deb-f172bcc2c55c} Allow outgoing WSD from PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2b01000000000000 +....... FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_PERMIT 0 68270 FWP_UINT64 162252835503734784 {413958f1-052c-41bb-9600-59345c58297d} Allow outgoing WSD from PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2b01000000000000 +....... FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68271 FWP_UINT64 162252852683603968 {b5b94431-92bb-4993-ae20-dca728cb6a3f} Allow outgoing WSD from PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2c01000000000000 ,....... FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWP_ACTION_PERMIT 0 68274 FWP_UINT64 162235655634550784 {f3611be4-ea21-41ad-bfbb-ff12ffc536f9} Allow outgoing WSD from PeerDistSvc FWPM_FILTER_FLAG_INDEXED FWPM_PROVIDER_MPSSVC_WSH 2c01000000000000 ,....... FWPM_LAYER_ALE_AUTH_CONNECT_V6 FWPM_SUBLAYER_MPSSVC_WSH FWP_EMPTY FWPM_CONDITION_ALE_APP_ID FWP_MATCH_EQUAL FWP_BYTE_BLOB_TYPE 5c006400650076006900630065005c0068006100720064006400690073006b0076006f006c0075006d00650033005c00770069006e0064006f00770073005c00730079007300740065006d00330032005c0073007600630068006f00730074002e006500780065000000 \device\harddiskvolume3\windows\system32\svchost.exe FWPM_CONDITION_ALE_USER_ID FWP_MATCH_EQUAL FWP_SECURITY_DESCRIPTOR_TYPE O:SYG:SYD:(A;;CCRC;;;S-1-5-80-3124040864-3101396827-3094488734-3028845762-1939139329) FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE fe80:: FWP_BYTE_ARRAY16_TYPE fe80::ffff:ffff:ffff:ffff FWPM_CONDITION_IP_REMOTE_ADDRESS FWP_MATCH_RANGE FWP_RANGE_TYPE FWP_BYTE_ARRAY16_TYPE ff00:: FWP_BYTE_ARRAY16_TYPE ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 3702 FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_ALL_SET FWP_UINT32 4 FWP_ACTION_PERMIT 0 68275 FWP_UINT64 162235672814419968 FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD ALE Connect v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_ALE_REMOTE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_REMOTE_MACHINE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_ARRIVAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_NEXTHOP_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_ORIGINAL_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_REAUTHORIZE_REASON FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_PEER_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ORIGINAL_ICMP_TYPE FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_ALE_ORIGINAL_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_EFFECTIVE_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 51 {c3dbed20-0bb6-4bf3-828d-96732e1e0133} Windows Firewall: callout Performs logging. FWPM_CALLOUT_FLAG_REGISTERED 00000200 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD 275 FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 ALE Flow Established v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_ALE_REMOTE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_REMOTE_MACHINE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_ORIGINAL_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 52 {8e44982b-f477-11df-85ce-78e7d1810190} NDU Flow Established V4 Callout NDU Flow Established V4 Callout FWP_CALLOUT_FLAG_ALLOW_OFFLOAD FWPM_CALLOUT_FLAG_REGISTERED 00000360 {8e44982a-f477-11df-85ce-78e7d1810190} FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 262 {c3dbed20-0bb6-4bf3-828d-96732e1e0134} Windows Firewall: callout Performs logging. FWPM_CALLOUT_FLAG_REGISTERED 00000200 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 280 {c3dbed20-0bb6-4bf3-828d-96732e1e0334} Windows Firewall: callout Initiates deep protocol inspection on the flow. FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 286 {f762e452-8bfd-47f6-9425-368d888b01dc} windefend_flow_established_v4 windefend FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 298 {8e44982c-f477-11df-85ce-78e7d1810190} NDU Flow Established V4 SubLayer Filter NDU Flow Established V4 SubLayer Filter {8e44982a-f477-11df-85ce-78e7d1810190} FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 FWPM_SUBLAYER_INSPECTION FWP_EMPTY FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_CALLOUT_INSPECTION {8e44982b-f477-11df-85ce-78e7d1810190} 0 66999 FWP_UINT64 2 {a5ce04ca-2cda-423e-8fef-52fb9e9fcf3a} FTP Inspection Filter This filter enables inspection of FTP. FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 21 FWP_ACTION_CALLOUT_INSPECTION {c3dbed20-0bb6-4bf3-828d-96732e1e0334} 0 68056 FWP_UINT64 18446744073709551615 {a70ef9b7-c790-4db0-a376-0378f9815b02} FTP Inspection Filter This filter enables inspection of FTP. FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 21 FWP_ACTION_CALLOUT_INSPECTION {c3dbed20-0bb6-4bf3-828d-96732e1e0334} 0 68058 FWP_UINT64 18446744073709551615 {a1dd6389-d3b1-4f46-9300-611216024728} windefend_flow_established_v4 windefend FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 {3c1cd879-1b8c-4ab4-8f83-5ed129176ef3} FWP_EMPTY FWPM_CONDITION_DIRECTION FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_DIRECTION FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_INSPECTION {f762e452-8bfd-47f6-9425-368d888b01dc} 0 68280 FWP_UINT64 532575961056 FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD ALE Flow Established v4 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_ALE_REMOTE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_REMOTE_MACHINE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_ORIGINAL_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 53 FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 ALE Flow Established v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_ALE_REMOTE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_REMOTE_MACHINE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_ORIGINAL_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 54 {8e44982d-f477-11df-85ce-78e7d1810190} NDU Flow Established V6 Callout NDU Flow Established V6 Callout FWP_CALLOUT_FLAG_ALLOW_OFFLOAD FWPM_CALLOUT_FLAG_REGISTERED 00000360 {8e44982a-f477-11df-85ce-78e7d1810190} FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 263 {c3dbed20-0bb6-4bf3-828d-96732e1e0136} Windows Firewall: callout Performs logging. FWPM_CALLOUT_FLAG_REGISTERED 00000200 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 281 {c3dbed20-0bb6-4bf3-828d-96732e1e0336} Windows Firewall: callout Initiates deep protocol inspection on the flow. FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_PROVIDER_MPSSVC_WF FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 287 {31114833-2891-4edd-a8ec-2ff8549aa491} windefend_flow_established_v6 windefend FWPM_CALLOUT_FLAG_REGISTERED FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 299 {8e44982e-f477-11df-85ce-78e7d1810190} NDU Flow Established V6 SubLayer Filter NDU Flow Established V6 SubLayer Filter {8e44982a-f477-11df-85ce-78e7d1810190} FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 FWPM_SUBLAYER_INSPECTION FWP_EMPTY FWPM_CONDITION_FLAGS FWP_MATCH_FLAGS_NONE_SET FWP_UINT32 1 FWP_ACTION_CALLOUT_INSPECTION {8e44982d-f477-11df-85ce-78e7d1810190} 0 67000 FWP_UINT64 2 {8076bfdc-eed9-4602-9570-29b6e402fba5} FTP Inspection Filter This filter enables inspection of FTP. FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_REMOTE_PORT FWP_MATCH_EQUAL FWP_UINT16 21 FWP_ACTION_CALLOUT_INSPECTION {c3dbed20-0bb6-4bf3-828d-96732e1e0336} 0 68057 FWP_UINT64 18446744073709551615 {b6c1c213-7518-4af8-9bcf-eeabe82c63ac} FTP Inspection Filter This filter enables inspection of FTP. FWPM_PROVIDER_MPSSVC_WF ffffffffffffffff ........ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 FWPM_SUBLAYER_MPSSVC_WF FWP_UINT64 18446744073709551615 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_LOCAL_PORT FWP_MATCH_EQUAL FWP_UINT16 21 FWP_ACTION_CALLOUT_INSPECTION {c3dbed20-0bb6-4bf3-828d-96732e1e0336} 0 68059 FWP_UINT64 18446744073709551615 {4994b7fe-47d8-4ac5-8fa8-77b203c5b640} windefend_flow_established_v6 windefend FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 {3c1cd879-1b8c-4ab4-8f83-5ed129176ef3} FWP_EMPTY FWPM_CONDITION_DIRECTION FWP_MATCH_EQUAL FWP_UINT32 0 FWPM_CONDITION_DIRECTION FWP_MATCH_EQUAL FWP_UINT32 1 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 6 FWPM_CONDITION_IP_PROTOCOL FWP_MATCH_EQUAL FWP_UINT8 17 FWP_ACTION_CALLOUT_INSPECTION {31114833-2891-4edd-a8ec-2ff8549aa491} 0 68281 FWP_UINT64 33286004704 FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD ALE Flow Established v6 Discard Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_ALE_REMOTE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_REMOTE_MACHINE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_ORIGINAL_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 55 FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET Inbound Ethernet Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_INTERFACE_MAC_ADDRESS FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY6_TYPE FWPM_CONDITION_MAC_LOCAL_ADDRESS FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY6_TYPE FWPM_CONDITION_MAC_REMOTE_ADDRESS FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY6_TYPE FWPM_CONDITION_MAC_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_MAC_REMOTE_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_ETHER_TYPE FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VLAN_ID FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NDIS_PORT FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_L2_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 56 FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET Outbound Ethernet Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_INTERFACE_MAC_ADDRESS FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY6_TYPE FWPM_CONDITION_MAC_LOCAL_ADDRESS FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY6_TYPE FWPM_CONDITION_MAC_REMOTE_ADDRESS FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY6_TYPE FWPM_CONDITION_MAC_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_MAC_REMOTE_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_ETHER_TYPE FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VLAN_ID FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NDIS_PORT FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_L2_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 57 FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE Inbound Native MAC Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_NDIS_MEDIA_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NDIS_PHYSICAL_MEDIA_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NDIS_PORT FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_L2_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 58 FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE Outbound Native MAC Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_NDIS_MEDIA_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NDIS_PHYSICAL_MEDIA_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_NDIS_PORT FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_L2_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 59 FWPM_LAYER_NAME_RESOLUTION_CACHE_V4 Name Resolution Cache v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_PEER_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 60 FWPM_LAYER_NAME_RESOLUTION_CACHE_V6 Name Resolution Cache v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_PEER_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 61 FWPM_LAYER_ALE_RESOURCE_RELEASE_V4 ALE Resource Release v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 62 FWPM_LAYER_ALE_RESOURCE_RELEASE_V6 ALE Resource Release v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 63 FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 ALE Endpoint Closure v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 64 FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6 ALE Endpoint Closure v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 65 FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 ALE Connect Redirect v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_ORIGINAL_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 66 FWPM_LAYER_ALE_CONNECT_REDIRECT_V6 ALE Connect Redirect v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_ORIGINAL_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 67 FWPM_LAYER_ALE_BIND_REDIRECT_V4 ALE Bind Redirect v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 68 {8bc14e84-4287-4c9e-81da-532335532058} Interface Binding Callout FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_PROVIDER_MPSSVC_WSH FWPM_LAYER_ALE_BIND_REDIRECT_V4 258 FWPM_LAYER_ALE_BIND_REDIRECT_V6 ALE Bind Redirect v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 69 {8bc14e84-4287-4c9e-81da-532335532059} Interface Binding Callout FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000300 FWPM_PROVIDER_MPSSVC_WSH FWPM_LAYER_ALE_BIND_REDIRECT_V6 259 FWPM_LAYER_STREAM_PACKET_V4 Stream Packet v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 70 FWPM_LAYER_STREAM_PACKET_V6 Stream Packet v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SUB_INTERFACE_INDEX FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_TUNNEL_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 71 FWPM_LAYER_INGRESS_VSWITCH_ETHERNET Ingress vSwitch Ethernet layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_MAC_SOURCE_ADDRESS FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY6_TYPE FWPM_CONDITION_MAC_SOURCE_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_MAC_DESTINATION_ADDRESS FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY6_TYPE FWPM_CONDITION_MAC_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_ETHER_TYPE FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VLAN_ID FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_VSWITCH_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_NETWORK_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_SOURCE_VM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_L2_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 72 FWPM_LAYER_EGRESS_VSWITCH_ETHERNET Egress vSwitch Ethernet layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_MAC_SOURCE_ADDRESS FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY6_TYPE FWPM_CONDITION_MAC_SOURCE_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_MAC_DESTINATION_ADDRESS FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY6_TYPE FWPM_CONDITION_MAC_DESTINATION_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_ETHER_TYPE FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VLAN_ID FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_VSWITCH_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_NETWORK_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_SOURCE_VM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_DESTINATION_VM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_L2_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 73 FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4 Ingress vSwitch Transport v4 layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_SOURCE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_DESTINATION_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_SOURCE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_DESTINATION_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VLAN_ID FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_VSWITCH_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_NETWORK_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_SOURCE_VM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_L2_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 74 FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6 Ingress vSwitch Transport v6 layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_SOURCE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_SOURCE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_DESTINATION_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VLAN_ID FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_VSWITCH_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_NETWORK_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_SOURCE_VM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_L2_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 75 FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4 Egress vSwitch Transport v4 layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_SOURCE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_DESTINATION_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_SOURCE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_DESTINATION_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VLAN_ID FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_VSWITCH_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_NETWORK_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_SOURCE_VM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_DESTINATION_VM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_L2_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 76 FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6 Egress vSwitch Transport v6 layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_SOURCE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_DESTINATION_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_SOURCE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_DESTINATION_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VLAN_ID FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_VSWITCH_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_NETWORK_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_SOURCE_VM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_VSWITCH_DESTINATION_VM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_L2_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 77 FWPM_LAYER_INBOUND_TRANSPORT_FAST Inbound Transport v4 Layer fast FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_SUBLAYER_UNIVERSAL 78 {8e44982f-f477-11df-85ce-78e7d1810190} NDU Inbound Transport Callout NDU Inbound Transport Callout FWP_CALLOUT_FLAG_ALLOW_OFFLOAD FWPM_CALLOUT_FLAG_REGISTERED 00000360 {8e44982a-f477-11df-85ce-78e7d1810190} FWPM_LAYER_INBOUND_TRANSPORT_FAST 264 {8e449830-f477-11df-85ce-78e7d1810190} NDU Inbound Transport SubLayer Filter NDU Inbound Transport SubLayer Filter {8e44982a-f477-11df-85ce-78e7d1810190} FWPM_LAYER_INBOUND_TRANSPORT_FAST FWPM_SUBLAYER_INSPECTION FWP_EMPTY FWP_ACTION_CALLOUT_INSPECTION {8e44982f-f477-11df-85ce-78e7d1810190} 0 67001 FWP_UINT64 576460752303423487 FWPM_LAYER_OUTBOUND_TRANSPORT_FAST Outbound Transport v4 Layer fast FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_SUBLAYER_UNIVERSAL 79 {8e449833-f477-11df-85ce-78e7d1810190} NDU Outbound Transport Callout NDU Outbound Transport Callout FWP_CALLOUT_FLAG_ALLOW_OFFLOAD FWPM_CALLOUT_FLAG_REGISTERED 00000360 {8e44982a-f477-11df-85ce-78e7d1810190} FWPM_LAYER_OUTBOUND_TRANSPORT_FAST 265 {8e449834-f477-11df-85ce-78e7d1810190} NDU Outbound Transport SubLayer Filter NDU Outbound Transport SubLayer Filter {8e44982a-f477-11df-85ce-78e7d1810190} FWPM_LAYER_OUTBOUND_TRANSPORT_FAST FWPM_SUBLAYER_INSPECTION FWP_EMPTY FWP_ACTION_CALLOUT_INSPECTION {8e449833-f477-11df-85ce-78e7d1810190} 0 67002 FWP_UINT64 576460752303423487 FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST Inbound Native MAC Layer fast FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_SUBLAYER_UNIVERSAL 80 {8e449837-f477-11df-85ce-78e7d1810190} NDU Inbound Mac Frame Native Callout NDU Inbound Mac Frame Native Callout FWP_CALLOUT_FLAG_ALLOW_OFFLOAD FWPM_CALLOUT_FLAG_REGISTERED 000003E0 {8e44982a-f477-11df-85ce-78e7d1810190} FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST 266 {8e449838-f477-11df-85ce-78e7d1810190} NDU Inbound Mac Frame Native SubLayer Filter NDU Inbound Mac Frame Native SubLayer Filter {8e44982a-f477-11df-85ce-78e7d1810190} FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST FWPM_SUBLAYER_INSPECTION FWP_EMPTY FWP_ACTION_CALLOUT_INSPECTION {8e449837-f477-11df-85ce-78e7d1810190} 0 67003 FWP_UINT64 576460752303423487 FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST Outbound Native MAC Layer fast FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_SUBLAYER_UNIVERSAL 81 {8e449839-f477-11df-85ce-78e7d1810190} NDU Outbound Mac Frame Native Callout NDU Outbound Mac Frame Native Callout FWP_CALLOUT_FLAG_ALLOW_OFFLOAD FWPM_CALLOUT_FLAG_REGISTERED 000003E0 {8e44982a-f477-11df-85ce-78e7d1810190} FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST 267 {8e44983a-f477-11df-85ce-78e7d1810190} NDU Outbound Mac Frame Native SubLayer Filter NDU Outbound Mac Frame Native SubLayer Filter {8e44982a-f477-11df-85ce-78e7d1810190} FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST FWPM_SUBLAYER_INSPECTION FWP_EMPTY FWP_ACTION_CALLOUT_INSPECTION {8e449839-f477-11df-85ce-78e7d1810190} 0 67004 FWP_UINT64 576460752303423487 FWPM_LAYER_INBOUND_RESERVED2 Inbound HTTP Reserved Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_RESERVED0 FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_RESERVED1 FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_RESERVED2 FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_RESERVED3 FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_RESERVED4 FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_RESERVED5 FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_RESERVED6 FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_RESERVED7 FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_RESERVED8 FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_RESERVED9 FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_RESERVED10 FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_RESERVED11 FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_RESERVED12 FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_RESERVED13 FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_RESERVED14 FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_RESERVED15 FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_SUBLAYER_UNIVERSAL 82 {29243af8-ecaf-4436-a44e-f9fb7070aa04} ALE Accept Redirect v4 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 83 FWPM_CALLOUT_BUILT_IN_RESERVED_1 WFP Built-in Proxy Connection ALE Accept Redirect v4 Layer Callout Redirects the inbound connection to the address in the provider context. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000300 {29243af8-ecaf-4436-a44e-f9fb7070aa04} 45 {c9809347-218f-4b7f-a742-b281a3f631b4} ALE Accept Redirect v6 Layer FWPM_LAYER_FLAG_KERNEL FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_ALE_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_ALE_USER_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_ACCESS_INFORMATION_TYPE FWPM_CONDITION_ALE_PACKAGE_ID FWPM_FIELD_RAW_DATA FWP_SID FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_COMPARTMENT_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 84 FWPM_CALLOUT_BUILT_IN_RESERVED_2 WFP Built-in Proxy Connection ALE Accept Redirect v6 Layer Callout Redirects the inbound connection to the address in the provider context. FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT FWPM_CALLOUT_FLAG_REGISTERED 00000300 {c9809347-218f-4b7f-a742-b281a3f631b4} 46 FWPM_LAYER_IPSEC_KM_DEMUX_V4 IPsec KM Demux v4 Layer FWPM_LAYER_FLAG_BUILTIN FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_QM_MODE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_SUBLAYER_UNIVERSAL 85 FWPM_LAYER_IPSEC_KM_DEMUX_V6 IPsec KM Demux v6 Layer FWPM_LAYER_FLAG_BUILTIN FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_QM_MODE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_SUBLAYER_UNIVERSAL 86 FWPM_LAYER_IPSEC_V4 IPsec v4 Layer FWPM_LAYER_FLAG_BUILTIN FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_SUBLAYER_UNIVERSAL 87 FWPM_LAYER_IPSEC_V6 IPsec v6 Layer FWPM_LAYER_FLAG_BUILTIN FWPM_CONDITION_IP_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_REMOTE_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_SUBLAYER_UNIVERSAL 88 FWPM_LAYER_IKEEXT_V4 IKE v4 Layer FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_SUBLAYER_UNIVERSAL 89 FWPM_LAYER_IKEEXT_V6 IKE v6 Layer FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_IP_LOCAL_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_INTERFACE FWPM_FIELD_RAW_DATA FWP_UINT64 FWPM_CONDITION_CURRENT_PROFILE_ID FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_SECURITY_REALM_ID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_SUBLAYER_UNIVERSAL 90 FWPM_LAYER_RPC_UM RPC UM Layer FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_REMOTE_USER_TOKEN FWPM_FIELD_RAW_DATA FWP_TOKEN_INFORMATION_TYPE FWPM_CONDITION_RPC_IF_UUID FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_RPC_IF_VERSION FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_RPC_IF_FLAG FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DCOM_APP_ID FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IMAGE_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_RPC_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_RPC_AUTH_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_RPC_AUTH_LEVEL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_SEC_ENCRYPT_ALGORITHM FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SEC_KEY_SIZE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_V4 FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_V6 FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_PIPE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS_V4 FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS_V6 FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_SUBLAYER_UNIVERSAL 91 FWPM_LAYER_RPC_EPMAP RPC EPMAP Layer FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_REMOTE_USER_TOKEN FWPM_FIELD_RAW_DATA FWP_TOKEN_INFORMATION_TYPE FWPM_CONDITION_RPC_IF_UUID FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_RPC_IF_VERSION FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_RPC_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_RPC_AUTH_TYPE FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_RPC_AUTH_LEVEL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_SEC_ENCRYPT_ALGORITHM FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_SEC_KEY_SIZE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_V4 FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_LOCAL_ADDRESS_V6 FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_IP_LOCAL_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_PIPE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_IP_REMOTE_ADDRESS_V4 FWPM_FIELD_IP_ADDRESS FWP_UINT32 FWPM_CONDITION_IP_REMOTE_ADDRESS_V6 FWPM_FIELD_IP_ADDRESS FWP_BYTE_ARRAY16_TYPE FWPM_SUBLAYER_UNIVERSAL 92 FWPM_LAYER_RPC_EP_ADD RPC EP ADD Layer FWPM_LAYER_FLAG_BUILTIN FWPM_CONDITION_PROCESS_WITH_RPC_IF_UUID FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_RPC_PROTOCOL FWPM_FIELD_RAW_DATA FWP_UINT8 FWPM_CONDITION_RPC_EP_VALUE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_RPC_EP_FLAGS FWPM_FIELD_FLAGS FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 93 FWPM_LAYER_RPC_PROXY_CONN RPC Proxy Connect Layer FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_CLIENT_TOKEN FWPM_FIELD_RAW_DATA FWP_TOKEN_INFORMATION_TYPE FWPM_CONDITION_RPC_SERVER_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_RPC_SERVER_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_RPC_PROXY_AUTH_TYPE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_CLIENT_CERT_KEY_LENGTH FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CLIENT_CERT_OID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_SUBLAYER_UNIVERSAL 94 FWPM_LAYER_RPC_PROXY_IF RPC Proxy Interface Layer FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_CLIENT_TOKEN FWPM_FIELD_RAW_DATA FWP_TOKEN_INFORMATION_TYPE FWPM_CONDITION_RPC_IF_UUID FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_RPC_IF_VERSION FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_RPC_SERVER_NAME FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_RPC_SERVER_PORT FWPM_FIELD_RAW_DATA FWP_UINT16 FWPM_CONDITION_RPC_PROXY_AUTH_TYPE FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_CONDITION_CLIENT_CERT_KEY_LENGTH FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_CLIENT_CERT_OID FWPM_FIELD_RAW_DATA FWP_BYTE_BLOB_TYPE FWPM_SUBLAYER_UNIVERSAL 95 FWPM_LAYER_KM_AUTHORIZATION Keying Module Authorization Layer FWPM_LAYER_FLAG_BUILTIN FWPM_LAYER_FLAG_CLASSIFY_MOSTLY FWPM_CONDITION_REMOTE_ID FWPM_FIELD_RAW_DATA FWP_TOKEN_INFORMATION_TYPE FWPM_CONDITION_AUTHENTICATION_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_KM_TYPE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_DIRECTION FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_KM_MODE FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_CONDITION_IPSEC_POLICY_KEY FWPM_FIELD_RAW_DATA FWP_BYTE_ARRAY16_TYPE FWPM_CONDITION_KM_AUTH_NAP_CONTEXT FWPM_FIELD_RAW_DATA FWP_UINT32 FWPM_SUBLAYER_UNIVERSAL 96 FWPM_PROVIDER_IKEEXT Microsoft Corporation Microsoft Windows WFP Built-in IKEEXT provider used to identify filters added by IKE/AuthIP. FWPM_PROVIDER_FLAG_DISABLED IKEEXT FWPM_PROVIDER_TCP_CHIMNEY_OFFLOAD Microsoft Corporation Microsoft Windows WFP Built-in TCP Chimney Offload provider used to identify filters added by TCP Chimney Offload. FWPM_PROVIDER_MPSSVC_APP_ISOLATION @bfe.dll,-1217 Microsoft Windows WFP Built-in MPSSVC App Isolation provider. mpssvc FWPM_PROVIDER_MPSSVC_WSH Microsoft Corporation Microsoft Windows WFP Built-in MPSSVC Windows Service Hardening and Quarantine provider. mpssvc FWPM_PROVIDER_MPSSVC_EDP Microsoft Corporation Microsoft Windows WFP Built-in MPSSVC Enterprise Data Protection provider. mpssvc {c698301d-9129-450c-937c-f4b834bfb374} Microsoft Corporation Microsoft Windows edge traversal socket option authorization provider {893a4f22-9bba-49b7-8c66-3d40929c8fd5} Microsoft Corporation Microsoft Windows Teredo firewall provider FWPM_PROVIDER_MPSSVC_WF Microsoft Corporation Microsoft Windows WFP Built-in MPSSVC Windows Firewall provider. mpssvc FWPM_PROVIDER_IPSEC_DOSP_CONFIG Microsoft Corporation Microsoft Windows WFP Built-in IPsec DoS Protection configuration provider used to identify filters added by IPsec Denial of Service Protection. FWPM_PROVIDER_TCP_TEMPLATES Microsoft Corporation Microsoft Windows WFP Built-in TCP Templates provider used to identify filters added by TCP Template based configuration. FWPM_PROVIDER_MPSSVC_TENANT_RESTRICTIONS WFP Built-in MPSSVC Tenant Restrictions Sublayer Microsoft Windows WFP Built-in MPSSVC Tenant Restrictions provider. mpssvc {1bebc969-61a5-4732-a177-847a0817862a} Microsoft Corporation Microsoft Windows Defender Firewall IPsec Provider FWPM_PROVIDER_FLAG_PERSISTENT MPSSVC {8e44982a-f477-11df-85ce-78e7d1810190} Microsoft Corporation Windows Network Data Usage (NDU) Provider NDU FWPM_PROVIDER_CONTEXT_SECURE_SOCKET_AUTHIP WFP Built-in Default Secure Socket AuthIP Policy Provider Context Authenticated Internet Protocol (AuthIP) main mode default policy for secure sockets. FWPM_IPSEC_AUTHIP_MM_CONTEXT 0 IKEEXT_ANONYMOUS IKEEXT_IMPERSONATION_NONE IKEEXT_CIPHER_AES_128 0 0 IKEEXT_INTEGRITY_SHA1 7200 IKEEXT_DH_ECP_256 0 IKEEXT_CIPHER_3DES 0 0 IKEEXT_INTEGRITY_SHA1 7200 IKEEXT_DH_GROUP_2 0 0 0 9223372036854775808 FWPM_PROVIDER_CONTEXT_SECURE_SOCKET_IPSEC WFP Built-in Default Secure Socket IPsec Policy Provider Context Internet Protocol Security (IPsec) quick mode default policy for secure sockets. FWPM_IPSEC_AUTHIP_QM_TRANSPORT_CONTEXT 3600 56320 2147483647 IPSEC_TRANSFORM_ESP_AUTH IPSEC_AUTH_SHA_1 IPSEC_AUTH_CONFIG_HMAC_SHA_1_96 IPSEC_PFS_NONE 3600 56320 2147483647 IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER IPSEC_AUTH_SHA_1 IPSEC_AUTH_CONFIG_HMAC_SHA_1_96 IPSEC_CIPHER_TYPE_AES_128 IPSEC_CIPHER_CONFIG_CBC_AES_128 IPSEC_PFS_NONE 3600 56320 2147483647 IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER IPSEC_AUTH_SHA_1 IPSEC_AUTH_CONFIG_HMAC_SHA_1_96 IPSEC_CIPHER_TYPE_3DES IPSEC_CIPHER_CONFIG_CBC_3DES IPSEC_PFS_NONE 3600 56320 2147483647 IPSEC_TRANSFORM_AH IPSEC_AUTH_SHA_1 IPSEC_AUTH_CONFIG_HMAC_SHA_1_96 IPSEC_PFS_NONE IPSEC_POLICY_FLAG_ND_BOUNDARY 10 300 60 IKEEXT_KERBEROS IKEEXT_SSL IKEEXT_CERT_CONFIG_TRUSTED_ROOT_STORE 1 1.3.6.1.5.5.7.3.2 IKEEXT_CERT_CONFIG_TRUSTED_ROOT_STORE 2 1.3.6.1.5.5.7.3.2 1.3.6.1.5.5.8.2.2 1 1.3.6.1.5.5.7.3.2 1 1.3.6.1.5.5.8.2.2 IKEEXT_IMPERSONATION_SOCKET_PRINCIPAL 9223372036854775809 {30433c31-b05f-421f-8fde-018ea4c68af4} MPSSVC Stores filter origin FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 510075006100720061006e00740069006e0065002000440065006600610075006c0074000000 Q.u.a.r.a.n.t.i.n.e. .D.e.f.a.u.l.t... 9223372036854775810 {93132c36-6e06-4e6f-a10b-218787cd49cf} MPSSVC Stores filter origin FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 42006f006f007400740069006d0065002000440065006600610075006c0074000000 B.o.o.t.t.i.m.e. .D.e.f.a.u.l.t... 9223372036854775811 {252fe349-15c1-48d5-82a6-e62a2e483dcf} State Management Provider Context Contains State Management Options FWPM_PROVIDER_MPSSVC_WF FWPM_CLASSIFY_OPTIONS_CONTEXT FWP_CLASSIFY_OPTION_LOOSE_SOURCE_MAPPING FWP_UINT32 1 9223372036854775812 {2ea412db-5c9d-4bbd-9af2-4914d2dbfe17} State Management Provider Context Contains State Management Options FWPM_PROVIDER_MPSSVC_WF FWPM_CLASSIFY_OPTIONS_CONTEXT FWP_CLASSIFY_OPTION_LOCAL_ONLY_MAPPING FWP_UINT32 1 9223372036854775813 {9889f02d-6a85-49b4-8928-884e5ab5ee59} Policy Silent Mode Provider Context The policy is inspected only but not enforced FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 50006f006c00690063007900530069006c0065006e0074004d006f0064006500500072006f00760069006400650072000000 P.o.l.i.c.y.S.i.l.e.n.t.M.o.d.e.P.r.o.v.i.d.e.r... 9223372036854775814 {de04f4bd-a953-47f6-b1b6-eb6e24a546d6} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5700530048002000440065006600610075006c0074000000 W.S.H. .D.e.f.a.u.l.t... 9223372036854775815 {feb49053-a00e-42a7-ab12-6f0b07ae1b2b} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 51007500650072007900200055007300650072002000440065006600610075006c0074000000 Q.u.e.r.y. .U.s.e.r. .D.e.f.a.u.l.t... 9223372036854775816 {7f0fdfe9-b5aa-4df8-98c2-8bb515bbc12a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 41007000700043006f006e007400610069006e006500720020004c006f006f0070006200610063006b000000 A.p.p.C.o.n.t.a.i.n.e.r. .L.o.o.p.b.a.c.k... 9223372036854775817 {999ba50e-b870-4b5c-84e6-33a0e61cc8d1} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53007400650061006c00740068000000 S.t.e.a.l.t.h... 9223372036854775818 {20f3bb11-bbf6-4e60-aeb3-e651c675d642} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53006800690065006c0064006500640020004d00610069006e002000520075006c0065000000 S.h.i.e.l.d.e.d. .M.a.i.n. .R.u.l.e... 9223372036854776151 {28127d58-a9af-43ee-bfe2-a06cb632fa4e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 440065006600610075006c007400200049006e0062006f0075006e0064000000 D.e.f.a.u.l.t. .I.n.b.o.u.n.d... 9223372036854776154 {df7bad3c-1c14-4ad9-ab82-5eb01bc9d76b} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53006800690065006c0064006500640020004d00610069006e002000520075006c0065000000 S.h.i.e.l.d.e.d. .M.a.i.n. .R.u.l.e... 9223372036854776157 {396a665b-ae9f-4c00-a33d-098abf605990} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 440065006600610075006c007400200049006e0062006f0075006e0064000000 D.e.f.a.u.l.t. .I.n.b.o.u.n.d... 9223372036854776160 {7a06a358-d5e2-4304-8970-c724ca7c9674} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53006800690065006c0064006500640020004d00610069006e002000520075006c0065000000 S.h.i.e.l.d.e.d. .M.a.i.n. .R.u.l.e... 9223372036854776163 {abf0b72d-a22e-427e-9139-ebca0193e5d9} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 440065006600610075006c007400200049006e0062006f0075006e0064000000 D.e.f.a.u.l.t. .I.n.b.o.u.n.d... 9223372036854776166 {b36473ef-bf42-49b9-ac24-adba245e825c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5500570050002000440065006600610075006c007400200049006e0062006f0075006e006400200042006c006f0063006b002000520075006c0065000000 U.W.P. .D.e.f.a.u.l.t. .I.n.b.o.u.n.d. .B.l.o.c.k. .R.u.l.e... 9223372036854776169 {11ec8831-cda4-4bab-aa69-cb45e32f4ea9} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5500570050002000440065006600610075006c00740020004f007500740062006f0075006e006400200042006c006f0063006b002000520075006c0065000000 U.W.P. .D.e.f.a.u.l.t. .O.u.t.b.o.u.n.d. .B.l.o.c.k. .R.u.l.e... 9223372036854776170 {13fd7ae8-4dad-4362-9178-9991d6ac4670} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 41007500640069006f007300720076002d0031000000 A.u.d.i.o.s.r.v.-.1... 9223372036854776171 {74eff966-472f-46ce-9391-ad9e1303f145} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 41007500640069006f007300720076002d0032000000 A.u.d.i.o.s.r.v.-.2... 9223372036854776172 {ac7ae3b6-0cf9-4ae4-9212-6c452f977570} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4100560045006e00640070006f0069006e0074004200750069006c006400650072002d0031000000 A.V.E.n.d.p.o.i.n.t.B.u.i.l.d.e.r.-.1... 9223372036854776173 {2153ec13-392d-4ffd-aedb-b4818c4dc9f7} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4100560045006e00640070006f0069006e0074004200750069006c006400650072002d0032000000 A.V.E.n.d.p.o.i.n.t.B.u.i.l.d.e.r.-.2... 9223372036854776174 {a16428ab-a823-4c1e-aee4-9bce9dd30989} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4100780049006e0073007400530056002d0031000000 A.x.I.n.s.t.S.V.-.1... 9223372036854776175 {3cec65bf-a4e1-4376-b287-0691e08a6f63} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4100780049006e0073007400530056002d0033000000 A.x.I.n.s.t.S.V.-.3... 9223372036854776176 {9066c4f6-66a6-41fc-a268-07d1b287af42} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4200460045002d0031000000 B.F.E.-.1... 9223372036854776177 {dbc25b5e-745f-46f2-800b-2178e7367202} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4200460045002d0032000000 B.F.E.-.2... 9223372036854776178 {074c3ea9-af96-4a28-a6c6-db9b0335adb0} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4300440050005300760063002d0032000000 C.D.P.S.v.c.-.2... 9223372036854776179 {e26c5b51-f344-4e59-b535-2ce78d16974c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4300440050005300760063002d0036000000 C.D.P.S.v.c.-.6... 9223372036854776180 {8966736d-4524-4694-9e2e-ee9f1387e011} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 63006c0072005f006f007000740069006d0069007a006100740069006f006e005f00760034002e0030002e00330030003300310039005f00330032002d0031000000 c.l.r._.o.p.t.i.m.i.z.a.t.i.o.n._.v.4...0...3.0.3.1.9._.3.2.-.1... 9223372036854776181 {bd49f24b-1801-46be-969c-e0cf5afd621a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 63006c0072005f006f007000740069006d0069007a006100740069006f006e005f00760034002e0030002e00330030003300310039005f00330032002d0032000000 c.l.r._.o.p.t.i.m.i.z.a.t.i.o.n._.v.4...0...3.0.3.1.9._.3.2.-.2... 9223372036854776182 {cc755386-b9e1-4342-a317-790c4f0a9534} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 63006c0072005f006f007000740069006d0069007a006100740069006f006e005f00760034002e0030002e00330030003300310039005f00360034002d0031000000 c.l.r._.o.p.t.i.m.i.z.a.t.i.o.n._.v.4...0...3.0.3.1.9._.6.4.-.1... 9223372036854776183 {bfa85b2e-3c06-4d69-b4e1-f149cff4661e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 63006c0072005f006f007000740069006d0069007a006100740069006f006e005f00760034002e0030002e00330030003300310039005f00360034002d0032000000 c.l.r._.o.p.t.i.m.i.z.a.t.i.o.n._.v.4...0...3.0.3.1.9._.6.4.-.2... 9223372036854776184 {a8b55da6-3aba-41f3-b7ba-443233f64fce} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4400650076006900630065004d0061006e006100670065006d0065006e0074002d0031000000 D.e.v.i.c.e.M.a.n.a.g.e.m.e.n.t.-.1... 9223372036854776185 {c4e61e5c-3e66-4efb-b22b-87f8e56cf9c2} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4400650076006900630065004d0061006e006100670065006d0065006e0074002d00310030000000 D.e.v.i.c.e.M.a.n.a.g.e.m.e.n.t.-.1.0... 9223372036854776186 {4dd4109f-8980-4e66-b307-887227d761a4} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4400650076006900630065004d0061006e006100670065006d0065006e0074002d00310031000000 D.e.v.i.c.e.M.a.n.a.g.e.m.e.n.t.-.1.1... 9223372036854776187 {f876928c-1566-4b9a-a110-57d531115565} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4400650076006900630065004d0061006e006100670065006d0065006e0074002d0032000000 D.e.v.i.c.e.M.a.n.a.g.e.m.e.n.t.-.2... 9223372036854776188 {616fdcf3-1169-46b5-8224-cffc7276af2e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4400650076006900630065004d0061006e006100670065006d0065006e0074002d0034000000 D.e.v.i.c.e.M.a.n.a.g.e.m.e.n.t.-.4... 9223372036854776189 {3742db91-517a-4f69-b378-08c5c09364b3} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4400650076006900630065004d0061006e006100670065006d0065006e0074002d0035000000 D.e.v.i.c.e.M.a.n.a.g.e.m.e.n.t.-.5... 9223372036854776190 {49208958-c95e-48da-832f-60636a24827a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4400650076006900630065004d0061006e006100670065006d0065006e0074002d0037000000 D.e.v.i.c.e.M.a.n.a.g.e.m.e.n.t.-.7... 9223372036854776191 {70f84089-a6de-44ab-88ba-681a2d87204e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4400650076006900630065004d0061006e006100670065006d0065006e0074002d0038000000 D.e.v.i.c.e.M.a.n.a.g.e.m.e.n.t.-.8... 9223372036854776192 {acc4c150-270a-47dc-91d0-23dcf4b073f4} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 44004800430050002d0034000000 D.H.C.P.-.4... 9223372036854776193 {fa7bff4f-61db-455f-8fec-220c1122cda0} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 44004800430050002d0035000000 D.H.C.P.-.5... 9223372036854776194 {140da811-71af-4719-aea9-3e3196bb2c73} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 44006900730070006c006100790045006e00680061006e00630065006d0065006e00740053006500720076006900630065002000440065006e007900200041006c006c00200049006e0062006f0075006e0064000000 D.i.s.p.l.a.y.E.n.h.a.n.c.e.m.e.n.t.S.e.r.v.i.c.e. .D.e.n.y. .A.l.l. .I.n.b.o.u.n.d... 9223372036854776195 {e8b4f949-15cf-4ef0-8c72-4b472a399374} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 44006900730070006c006100790045006e00680061006e00630065006d0065006e00740053006500720076006900630065002000440065006e007900200041006c006c0020004f007500740062006f0075006e0064000000 D.i.s.p.l.a.y.E.n.h.a.n.c.e.m.e.n.t.S.e.r.v.i.c.e. .D.e.n.y. .A.l.l. .O.u.t.b.o.u.n.d... 9223372036854776196 {29442789-6efc-46ed-b261-a526c4b07a9b} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 64006f00740033007300760063002d0031000000 d.o.t.3.s.v.c.-.1... 9223372036854776197 {39e96b50-7896-45bf-8a32-6df85e30668a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 64006f00740033007300760063002d0032000000 d.o.t.3.s.v.c.-.2... 9223372036854776198 {b9e16b36-f84a-4ada-8ad6-72303a132d9c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4400500053002d0031000000 D.P.S.-.1... 9223372036854776199 {d7138c16-5c23-4147-8aa2-26c31ffa3282} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4400500053002d0032000000 D.P.S.-.2... 9223372036854776200 {56ca2cdd-1a5b-49db-b1b1-0c7ecda76a96} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4500760065006e0074006c006f0067002d0032000000 E.v.e.n.t.l.o.g.-.2... 9223372036854776201 {66d12cf5-d1c3-4fcf-beec-60486740a808} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4500760065006e0074006c006f0067002d0033000000 E.v.e.n.t.l.o.g.-.3... 9223372036854776202 {766e17d2-0adf-4c0c-a05f-bd0e3a611a0d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 66006400700068006f00730074002d0031000000 f.d.p.h.o.s.t.-.1... 9223372036854776203 {82c89ca7-9d25-44bf-b763-ae9a0fc19072} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 66006400700068006f00730074002d0032000000 f.d.p.h.o.s.t.-.2... 9223372036854776204 {d8d7429a-458d-419c-903a-72b1770c2fa5} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 660068007300760063002d0031000000 f.h.s.v.c.-.1... 9223372036854776205 {e4c31fb1-c4d0-4425-80cb-13aeaf564696} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 660068007300760063002d0032000000 f.h.s.v.c.-.2... 9223372036854776206 {4d390cf4-706e-4f3a-afc5-75de5af9327e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 48006900640053006500720076002d0031000000 H.i.d.S.e.r.v.-.1... 9223372036854776207 {a03a74d6-9d42-487f-9ebf-0576fb284312} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 48006900640053006500720076002d0032000000 H.i.d.S.e.r.v.-.2... 9223372036854776208 {dc9f453d-1eaa-4912-8a95-ec50252e8abd} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4c004d0048006f007300740073002d0033000000 L.M.H.o.s.t.s.-.3... 9223372036854776209 {7c926fc6-603b-4a56-bd0e-683fc579cb4b} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4c004d0048006f007300740073002d0034000000 L.M.H.o.s.t.s.-.4... 9223372036854776210 {482ea2c8-fdf2-41e7-8c58-b77891b9e26e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4d006900630072006f0073006f00660074002d00570069006e0064006f00770073002d0041006c006c004a006f0079006e002d0052006f0075007400650072002d0042006c006f0063006b002d0049006e002d0041006c006c0045006c00730065000000 M.i.c.r.o.s.o.f.t.-.W.i.n.d.o.w.s.-.A.l.l.J.o.y.n.-.R.o.u.t.e.r.-.B.l.o.c.k.-.I.n.-.A.l.l.E.l.s.e... 9223372036854776211 {b91e477a-b12b-4ffe-8036-fb467a589bb5} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4d006900630072006f0073006f00660074002d00570069006e0064006f00770073002d0041006c006c004a006f0079006e002d0052006f0075007400650072002d0042006c006f0063006b002d004f00750074002d0041006c006c0045006c00730065000000 M.i.c.r.o.s.o.f.t.-.W.i.n.d.o.w.s.-.A.l.l.J.o.y.n.-.R.o.u.t.e.r.-.B.l.o.c.k.-.O.u.t.-.A.l.l.E.l.s.e... 9223372036854776212 {84a6277c-c2cf-4550-8617-4766635b0978} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4d00500053005300560043002d0031000000 M.P.S.S.V.C.-.1... 9223372036854776213 {9dc391c3-71d8-4649-b1d5-75ed10f360a9} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4d00500053005300560043002d0032000000 M.P.S.S.V.C.-.2... 9223372036854776214 {0565668f-d3c2-489b-86fc-63eaa40816b7} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4e00650074006d0061006e002d0031000000 N.e.t.m.a.n.-.1... 9223372036854776215 {e66edf14-6359-4ebf-a197-ba3f7537af1b} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 4e00650074006d0061006e002d0032000000 N.e.t.m.a.n.-.2... 9223372036854776216 {a8703831-1566-41ef-a65d-e1810ef57048} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5000320050002000470072006f007500700069006e006700200042006c006f0063006b00200049006e000000 P.2.P. .G.r.o.u.p.i.n.g. .B.l.o.c.k. .I.n... 9223372036854776217 {07e62e11-172d-4a30-af90-caf4e66d513b} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5000320050002000470072006f007500700069006e006700200042006c006f0063006b0020004f00750074000000 P.2.P. .G.r.o.u.p.i.n.g. .B.l.o.c.k. .O.u.t... 9223372036854776218 {1e2445fb-9e48-41aa-98ff-d540234e8a15} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 50003200500020004900640065006e007400200042006c006f0063006b00200049006e000000 P.2.P. .I.d.e.n.t. .B.l.o.c.k. .I.n... 9223372036854776219 {4fbe1969-bcf9-4b68-8e7f-12bdb7f4b5c5} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 50003200500020004900640065006e007400200042006c006f0063006b0020004f00750074000000 P.2.P. .I.d.e.n.t. .B.l.o.c.k. .O.u.t... 9223372036854776220 {2ec90dcd-0a0b-46e8-a930-97e228065a8d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5000630061005300760063002d0031000000 P.c.a.S.v.c.-.1... 9223372036854776221 {023d6954-db44-4559-875b-c888101ce60c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5000630061005300760063002d0032000000 P.c.a.S.v.c.-.2... 9223372036854776222 {0f745c88-3d06-4f3f-836a-246ed1fdb397} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 500065007200660048006f00730074002d0031000000 P.e.r.f.H.o.s.t.-.1... 9223372036854776223 {3f019c77-26df-4344-96a3-327d030483fd} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 500065007200660048006f00730074002d0032000000 P.e.r.f.H.o.s.t.-.2... 9223372036854776224 {e39d0a93-3337-4e1e-b56a-d00861b1f26a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 50004e0052005000200042006c006f0063006b00200049006e000000 P.N.R.P. .B.l.o.c.k. .I.n... 9223372036854776225 {c4c5bf1b-6190-4fdc-95ec-0d4ba3d09dbc} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 50004e0052005000200042006c006f0063006b0020004f00750074000000 P.N.R.P. .B.l.o.c.k. .O.u.t... 9223372036854776226 {d7744c01-2520-4e53-9d09-1b4c41561898} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 50006e00720070004100750074006f00200042006c006f0063006b00200049006e000000 P.n.r.p.A.u.t.o. .B.l.o.c.k. .I.n... 9223372036854776227 {f293ff68-0301-4ecb-9c6f-b0b73b0c0f99} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 50006e00720070004100750074006f00200042006c006f0063006b0020004f00750074000000 P.n.r.p.A.u.t.o. .B.l.o.c.k. .O.u.t... 9223372036854776228 {08edbc40-f085-4664-8cf6-cfb6e9ebe8f6} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 50006f006c006900630079004100670065006e0074002d0034000000 P.o.l.i.c.y.A.g.e.n.t.-.4... 9223372036854776229 {542d5137-d23a-4bb4-8d90-9c30857d84bc} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 50006f006c006900630079004100670065006e0074002d0035000000 P.o.l.i.c.y.A.g.e.n.t.-.5... 9223372036854776230 {3a5e8a34-82df-4083-a6ed-87ea8ed3507a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 530065006100720063006800460069006c0074006500720048006f00730074002d0031000000 S.e.a.r.c.h.F.i.l.t.e.r.H.o.s.t.-.1... 9223372036854776231 {03843031-9e01-449d-b94b-047c6fbe714c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 530065006100720063006800460069006c0074006500720048006f00730074002d0032000000 S.e.a.r.c.h.F.i.l.t.e.r.H.o.s.t.-.2... 9223372036854776232 {bbe3095e-afe8-4c43-8de5-b9994738a268} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53006500610072006300680049006e00640065007800650072002d0031000000 S.e.a.r.c.h.I.n.d.e.x.e.r.-.1... 9223372036854776233 {c7031a73-1778-4847-9b46-8ce7fa8798c1} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53006500610072006300680049006e00640065007800650072002d0032000000 S.e.a.r.c.h.I.n.d.e.x.e.r.-.2... 9223372036854776234 {c0cedbe2-34f4-4491-bd19-152a55b1b6e0} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 530065006100720063006800500072006f0074006f0063006f006c0048006f00730074002d0031000000 S.e.a.r.c.h.P.r.o.t.o.c.o.l.H.o.s.t.-.1... 9223372036854776235 {be0ee23e-c670-47d6-8112-7cf182d22041} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53004e004d00500054005200410050002d0032000000 S.N.M.P.T.R.A.P.-.2... 9223372036854776236 {b508cf8d-5dbe-44e9-bc10-85402404c8f4} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53004e004d00500054005200410050002d0033000000 S.N.M.P.T.R.A.P.-.3... 9223372036854776237 {7b84e8bb-fa4a-42c3-b61d-6e2b0532fdc5} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53005000500045005800540043004f004d004f0042004a002d0033000000 S.P.P.E.X.T.C.O.M.O.B.J.-.3... 9223372036854776238 {6cc74acd-ca95-45f6-8c3b-5e62000940b0} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53005000500045005800540043004f004d004f0042004a002d0034000000 S.P.P.E.X.T.C.O.M.O.B.J.-.4... 9223372036854776239 {f63fb925-3743-4a88-95ed-a842472b93fc} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5300790073006d00610069006e002d0031000000 S.y.s.m.a.i.n.-.1... 9223372036854776240 {0613da54-aa3d-4d9d-afd6-d4d64f655001} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5300790073006d00610069006e002d0032000000 S.y.s.m.a.i.n.-.2... 9223372036854776241 {d8e3c9f1-31ab-441a-896a-8c4b0d3b2019} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5400610062006c006500740049006e0070007500740053006500720076006900630065002d0031000000 T.a.b.l.e.t.I.n.p.u.t.S.e.r.v.i.c.e.-.1... 9223372036854776242 {cfcaf1e6-48d2-47d1-a1e3-d7b017af03ee} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5400610062006c006500740049006e0070007500740053006500720076006900630065002d0032000000 T.a.b.l.e.t.I.n.p.u.t.S.e.r.v.i.c.e.-.2... 9223372036854776243 {7b6dc94c-eb68-4c29-9f34-589b83c2f93a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 540072006b0077006b0073002d0031000000 T.r.k.w.k.s.-.1... 9223372036854776244 {42e3bde9-8223-45ae-a948-b331cfad1178} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 540072006b0077006b0073002d0032000000 T.r.k.w.k.s.-.2... 9223372036854776245 {f5ce7aa1-73b7-4368-8117-1b81a708a5b8} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 55006d0052006400700053006500720076006900630065002d0031000000 U.m.R.d.p.S.e.r.v.i.c.e.-.1... 9223372036854776246 {09581f72-a5fa-4f0d-83c1-e0a072bb2e5c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 55006d0052006400700053006500720076006900630065002d0032000000 U.m.R.d.p.S.e.r.v.i.c.e.-.2... 9223372036854776247 {89946940-1d3d-4828-baf2-ef863ff4d926} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5600610063005300760063002d0031000000 V.a.c.S.v.c.-.1... 9223372036854776248 {09c48c05-27b6-4ee0-99f7-a39203499a80} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5600610063005300760063002d0032000000 V.a.c.S.v.c.-.2... 9223372036854776249 {20ba781c-7644-4706-9a3d-3cadb206da34} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5600440053002d0031000000 V.D.S.-.1... 9223372036854776250 {de982dfe-87db-49d3-ab9e-fd063af6cf3e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d00690063006700750065007300740069006e0074006500720066006100630065002d0062006c006f0063006b002d0069006e000000 v.m.i.c.g.u.e.s.t.i.n.t.e.r.f.a.c.e.-.b.l.o.c.k.-.i.n... 9223372036854776251 {66d956e2-2f1d-424c-9d3c-48ae68b15ed6} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d00690063006700750065007300740069006e0074006500720066006100630065002d0062006c006f0063006b002d006f00750074000000 v.m.i.c.g.u.e.s.t.i.n.t.e.r.f.a.c.e.-.b.l.o.c.k.-.o.u.t... 9223372036854776252 {36a4e3cc-ec47-49eb-977f-e55f10f19083} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d00690063006800650061007200740062006500610074002d0062006c006f0063006b002d0069006e000000 v.m.i.c.h.e.a.r.t.b.e.a.t.-.b.l.o.c.k.-.i.n... 9223372036854776253 {8293ae37-bed3-4430-a7a4-68b06342c2e2} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d00690063006800650061007200740062006500610074002d0062006c006f0063006b002d006f00750074000000 v.m.i.c.h.e.a.r.t.b.e.a.t.-.b.l.o.c.k.-.o.u.t... 9223372036854776254 {fcc62406-2a46-4704-8605-51da6478c028} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d00690063006b0076007000650078006300680061006e00670065002d0062006c006f0063006b002d0069006e000000 v.m.i.c.k.v.p.e.x.c.h.a.n.g.e.-.b.l.o.c.k.-.i.n... 9223372036854776255 {996f6ad1-478b-49a3-9ac5-9278bbfdab90} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d00690063006b0076007000650078006300680061006e00670065002d0062006c006f0063006b002d006f00750074000000 v.m.i.c.k.v.p.e.x.c.h.a.n.g.e.-.b.l.o.c.k.-.o.u.t... 9223372036854776256 {c9a666f9-bc1e-4e39-9ee6-52940117aad8} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d00690063007200640076002d0062006c006f0063006b002d0069006e000000 v.m.i.c.r.d.v.-.b.l.o.c.k.-.i.n... 9223372036854776257 {19d55ef7-9fd1-43d0-b47c-b7752e875f3e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d00690063007200640076002d0062006c006f0063006b002d006f00750074000000 v.m.i.c.r.d.v.-.b.l.o.c.k.-.o.u.t... 9223372036854776258 {79d972a5-c160-4af7-b091-58da324a4ff0} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d0069006300730068007500740064006f0077006e002d0062006c006f0063006b002d0069006e000000 v.m.i.c.s.h.u.t.d.o.w.n.-.b.l.o.c.k.-.i.n... 9223372036854776259 {2c1956cf-17bc-4820-b44d-522f9985dc29} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d0069006300730068007500740064006f0077006e002d0062006c006f0063006b002d006f00750074000000 v.m.i.c.s.h.u.t.d.o.w.n.-.b.l.o.c.k.-.o.u.t... 9223372036854776260 {0c6406d9-1628-4c44-b2da-b5f8ca525a44} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d0069006300740069006d006500730079006e0063002d0062006c006f0063006b002d0069006e000000 v.m.i.c.t.i.m.e.s.y.n.c.-.b.l.o.c.k.-.i.n... 9223372036854776261 {f245355c-3927-4d7a-b7eb-57a3db7827c6} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d0069006300740069006d006500730079006e0063002d0062006c006f0063006b002d006f00750074000000 v.m.i.c.t.i.m.e.s.y.n.c.-.b.l.o.c.k.-.o.u.t... 9223372036854776262 {b4fc7eca-e42d-4ae1-a29e-bc721cde5535} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d006900630076006d00730065007300730069006f006e002d0062006c006f0063006b002d0069006e000000 v.m.i.c.v.m.s.e.s.s.i.o.n.-.b.l.o.c.k.-.i.n... 9223372036854776263 {3c267f8e-944d-4068-acad-75ba5821575b} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d006900630076006d00730065007300730069006f006e002d0062006c006f0063006b002d006f00750074000000 v.m.i.c.v.m.s.e.s.s.i.o.n.-.b.l.o.c.k.-.o.u.t... 9223372036854776264 {c58cd9f2-b4e4-43f5-b494-d7260d82f973} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d00690063007600730073002d0062006c006f0063006b002d0069006e000000 v.m.i.c.v.s.s.-.b.l.o.c.k.-.i.n... 9223372036854776265 {1622032e-7b49-4f11-b377-d2c73c90bcc8} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 76006d00690063007600730073002d0062006c006f0063006b002d006f00750074000000 v.m.i.c.v.s.s.-.b.l.o.c.k.-.o.u.t... 9223372036854776266 {a5086f3a-f5e4-481e-8caf-1ff0204b001c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7700620065006e00670069006e0065002d0031000000 w.b.e.n.g.i.n.e.-.1... 9223372036854776267 {c2733d72-d500-4045-94f2-09606cd6dbc6} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7700620065006e00670069006e0065002d0033000000 w.b.e.n.g.i.n.e.-.3... 9223372036854776268 {c6f5a818-b954-42a7-b596-45591ca10f6a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 570063006d007300760063002d0031000000 W.c.m.s.v.c.-.1... 9223372036854776269 {f87d269f-537e-4352-b018-87e5976976f7} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 570063006d007300760063002d0033000000 W.c.m.s.v.c.-.3... 9223372036854776270 {0c3cddb6-de83-4012-8dca-52a7682f97da} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 570064006900530079007300740065006d0048006f00730074002d0031000000 W.d.i.S.y.s.t.e.m.H.o.s.t.-.1... 9223372036854776271 {ffd72c64-50dc-4b5b-8b1e-1dfe782f4d0d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 570064006900530079007300740065006d0048006f00730074002d0032000000 W.d.i.S.y.s.t.e.m.H.o.s.t.-.2... 9223372036854776272 {9a09d870-2858-4be6-9e2f-ce10b4907fa0} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5700650072005300760063002d0031000000 W.e.r.S.v.c.-.1... 9223372036854776273 {c54b278f-9127-40bf-aa5e-86674fa65c76} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5700650072005300760063002d0032000000 W.e.r.S.v.c.-.2... 9223372036854776274 {690f4ad8-bc37-48b7-bed6-bfa7a526a895} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 570069006e0064006f007700730044006500660065006e006400650072002d0032000000 W.i.n.d.o.w.s.D.e.f.e.n.d.e.r.-.2... 9223372036854776275 {cdfd6c38-1499-44b4-a900-60aa4e8936c2} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 570069006e0064006f007700730044006500660065006e006400650072002d0033000000 W.i.n.d.o.w.s.D.e.f.e.n.d.e.r.-.3... 9223372036854776276 {22d7902a-15f9-4e7c-85fa-062e993aa6d5} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 570069006e0048007400740070004100750074006f00500072006f00780079005300760063002d0032000000 W.i.n.H.t.t.p.A.u.t.o.P.r.o.x.y.S.v.c.-.2... 9223372036854776277 {2810cbd2-88bb-47d7-a215-409c6cd87bee} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 570069006e0048007400740070004100750074006f00500072006f00780079005300760063002d0033000000 W.i.n.H.t.t.p.A.u.t.o.P.r.o.x.y.S.v.c.-.3... 9223372036854776278 {359ad55a-40a5-45e0-91ba-c921ac9cb876} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 57006c0061006e007300760063002d0031000000 W.l.a.n.s.v.c.-.1... 9223372036854776279 {c9368850-7641-4a64-9c8d-91c38550f267} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 57006c0061006e007300760063002d0032000000 W.l.a.n.s.v.c.-.2... 9223372036854776280 {e4bb7146-61ab-49c8-b9f3-57e9d27207e1} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 77006c00700061007300760063002d0031000000 w.l.p.a.s.v.c.-.1... 9223372036854776281 {478442b4-ce51-424a-991f-9207b636722c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 77006c00700061007300760063002d0033000000 w.l.p.a.s.v.c.-.3... 9223372036854776282 {10cc650c-46e4-40e3-b9d8-1e34fee2055f} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5700530043002000440065006e007900200041006c006c00200049006e0062006f0075006e0064000000 W.S.C. .D.e.n.y. .A.l.l. .I.n.b.o.u.n.d... 9223372036854776283 {fece87b4-8033-4dbe-a385-6bc285eb6d78} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5700530043002000440065006e007900200041006c006c0020004f007500740062006f0075006e0064000000 W.S.C. .D.e.n.y. .A.l.l. .O.u.t.b.o.u.n.d... 9223372036854776284 {9724c57d-1990-4c1f-b17d-8847702bfcef} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5700770061006e005300760063002d0031000000 W.w.a.n.S.v.c.-.1... 9223372036854776285 {c0ff73ea-8d25-4280-a754-96b67c932869} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 5700770061006e005300760063002d0032000000 W.w.a.n.S.v.c.-.2... 9223372036854776286 {ed046545-5b2c-4368-b04e-e93ca55def93} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 410073007300690067006e00650064004100630063006500730073004d0061006e0061006700650072005300760063002d0031000000 A.s.s.i.g.n.e.d.A.c.c.e.s.s.M.a.n.a.g.e.r.S.v.c.-.1... 9223372036854776287 {1b7af7da-20ed-4fb5-9eb4-3e63fcb7536c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 410073007300690067006e00650064004100630063006500730073004d0061006e0061006700650072005300760063002d0032000000 A.s.s.i.g.n.e.d.A.c.c.e.s.s.M.a.n.a.g.e.r.S.v.c.-.2... 9223372036854776288 {6796b00a-0007-4eeb-b176-41c6807585a4} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 63006c006f0075006400690064007300760063002d0062006c006f0063006b002d0069006e000000 c.l.o.u.d.i.d.s.v.c.-.b.l.o.c.k.-.i.n... 9223372036854776289 {774ac201-15c6-444f-82c9-1dcdff4326f2} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 63006c006f0075006400690064007300760063002d0062006c006f0063006b002d006f00750074000000 c.l.o.u.d.i.d.s.v.c.-.b.l.o.c.k.-.o.u.t... 9223372036854776290 {b47fa28f-cdca-41e8-8360-733d8216d7f9} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 43007300630053006500720076006900630065002d0031000000 C.s.c.S.e.r.v.i.c.e.-.1... 9223372036854776291 {6e9a9b45-a4cb-4723-bd55-60149243222d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 43007300630053006500720076006900630065002d0032000000 C.s.c.S.e.r.v.i.c.e.-.2... 9223372036854776292 {5845f29b-b4a1-4df2-b66e-609e5f902183} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 50006500650072004400690073007400200042006c006f0063006b00200049006e000000 P.e.e.r.D.i.s.t. .B.l.o.c.k. .I.n... 9223372036854776293 {e22f0814-cca5-4de3-8991-4f296ccec7a5} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 50006500650072004400690073007400200042006c006f0063006b0020004f00750074000000 P.e.e.r.D.i.s.t. .B.l.o.c.k. .O.u.t... 9223372036854776294 {66e8985e-169a-4c11-9701-40419c45a9cc} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 57005000440042005500530045004e0055004d002d0031000000 W.P.D.B.U.S.E.N.U.M.-.1... 9223372036854776295 {cfef2756-ac04-449a-b6c6-ed010f460651} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 57005000440042005500530045004e0055004d002d0032000000 W.P.D.B.U.S.E.N.U.M.-.2... 9223372036854776296 {2d39a837-d11e-4242-aa55-d55d0d92ae68} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 640064003400340036003600360033002d0030003000380036002d0034006100390037002d0038003000370061002d006100380033006500390035006400640038003400640038000000 d.d.4.4.6.6.6.3.-.0.0.8.6.-.4.a.9.7.-.8.0.7.a.-.a.8.3.e.9.5.d.d.8.4.d.8... 9223372036854776297 {19d6b0d9-e808-4803-a59f-92ae87bf4b00} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 300033006400320066006500650030002d0031006100640065002d0034003800610036002d0061003700640030002d003100390033006200610064003000310035003100330038000000 0.3.d.2.f.e.e.0.-.1.a.d.e.-.4.8.a.6.-.a.7.d.0.-.1.9.3.b.a.d.0.1.5.1.3.8... 9223372036854776298 {522717a3-989a-403d-8bd9-2301754f6bd4} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00340038003500440036003700310044002d0041003500330037002d0034003800410037002d0039003000440035002d003600370038003300380041003500420041004100320045007d000000 {.4.8.5.D.6.7.1.D.-.A.5.3.7.-.4.8.A.7.-.9.0.D.5.-.6.7.8.3.8.A.5.B.A.A.2.E.}... 9223372036854776299 {fcb5f99f-cabd-4580-b4f4-d8bb7629abd2} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310043003100420039003600340036002d0032004200340046002d0034003300450046002d0038004300350042002d003300460039003000300042003700310034004100410042007d000000 {.1.C.1.B.9.6.4.6.-.2.B.4.F.-.4.3.E.F.-.8.C.5.B.-.3.F.9.0.0.B.7.1.4.A.A.B.}... 9223372036854776300 {68d4ff07-c138-454b-aa7e-316622d0a73c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310044004400440045003200300038002d0032004500330032002d0034003200410045002d0038003800380046002d004200440043003200380038003000380036004500300032007d000000 {.1.D.D.D.E.2.0.8.-.2.E.3.2.-.4.2.A.E.-.8.8.8.F.-.B.D.C.2.8.8.0.8.6.E.0.2.}... 9223372036854776301 {4237d279-1e81-4828-958f-467bf3a5349b} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00360038003600420038003300350039002d0031003200330043002d0034003100370032002d0042003100330033002d004200390032003000350030003900320036003300440033007d000000 {.6.8.6.B.8.3.5.9.-.1.2.3.C.-.4.1.7.2.-.B.1.3.3.-.B.9.2.0.5.0.9.2.6.3.D.3.}... 9223372036854776302 {6a19fa7f-ef2b-42cf-b2d6-8139181d816b} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450045003400340044004100450041002d0034003100390036002d0034003000450036002d0041003900370030002d003500340038003500330043004400460045003900440041007d000000 {.E.E.4.4.D.A.E.A.-.4.1.9.6.-.4.0.E.6.-.A.9.7.0.-.5.4.8.5.3.C.D.F.E.9.D.A.}... 9223372036854776303 {52026488-b6af-4372-9685-0d69706e6683} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00330038004300380037004200360045002d0034004600410031002d0034004400440043002d0042004300460043002d003600360033004500450035003600450030004300430030007d000000 {.3.8.C.8.7.B.6.E.-.4.F.A.1.-.4.D.D.C.-.B.C.F.C.-.6.6.3.E.E.5.6.E.0.C.C.0.}... 9223372036854776304 {249d04ff-49a5-4e08-87e9-0e68e2019df7} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00460041004400390039003200330043002d0034003900320030002d0034003200310030002d0038004400300042002d003400310030004600300039003100440043004300300043007d000000 {.F.A.D.9.9.2.3.C.-.4.9.2.0.-.4.2.1.0.-.8.D.0.B.-.4.1.0.F.0.9.1.D.C.C.0.C.}... 9223372036854776305 {cbc261c4-d5fd-4e97-8de5-9c64f045fbb0} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440034004100460032003900380030002d0039004600370038002d0034004300390041002d0039003700430030002d003800390046003300360030004300390042004300380039007d000000 {.D.4.A.F.2.9.8.0.-.9.F.7.8.-.4.C.9.A.-.9.7.C.0.-.8.9.F.3.6.0.C.9.B.C.8.9.}... 9223372036854776306 {f369fb4f-039a-40d7-89c4-e9d1636f38e1} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00460033004300450030003900350037002d0042004600320039002d0034003200300035002d0038003000350046002d003800450033004400350045003000390037003300340042007d000000 {.F.3.C.E.0.9.5.7.-.B.F.2.9.-.4.2.0.5.-.8.0.5.F.-.8.E.3.D.5.E.0.9.7.3.4.B.}... 9223372036854776307 {c29535e1-3bbf-4491-bfe8-020381591955} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00370044003900370038003700380042002d0043003900310041002d0034003200460034002d0038003700360034002d003300440037004500320030003400310038003400370030007d000000 {.7.D.9.7.8.7.8.B.-.C.9.1.A.-.4.2.F.4.-.8.7.6.4.-.3.D.7.E.2.0.4.1.8.4.7.0.}... 9223372036854776308 {b1f4be4b-02f3-4fe6-9da0-489362bd0b71} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00410042003600410042004400320035002d0038003300420030002d0034003200390030002d0041003700420045002d003400340035003300410042003000370035003100460031007d000000 {.A.B.6.A.B.D.2.5.-.8.3.B.0.-.4.2.9.0.-.A.7.B.E.-.4.4.5.3.A.B.0.7.5.1.F.1.}... 9223372036854776309 {7eb2b4ea-2142-4c7e-a44f-ebb62240040d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00360038003300380041003400340035002d0030004200460033002d0034004500390046002d0042004500410044002d004400320045003900420044003700320042004500460044007d000000 {.6.8.3.8.A.4.4.5.-.0.B.F.3.-.4.E.9.F.-.B.E.A.D.-.D.2.E.9.B.D.7.2.B.E.F.D.}... 9223372036854776310 {176827d1-c164-4c39-8c31-3afecc6a8b87} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430044003500440034003000450046002d0042004500350036002d0034003500440034002d0042003400300035002d003900350031003500450038004400310034003300410037007d000000 {.C.D.5.D.4.0.E.F.-.B.E.5.6.-.4.5.D.4.-.B.4.0.5.-.9.5.1.5.E.8.D.1.4.3.A.7.}... 9223372036854776311 {5f6a0bbb-a184-4846-b780-05f78b81c312} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00370034004600340043004500410031002d0032003600430031002d0034003100440036002d0038003900450042002d004400430033003700450037004600310039003600410033007d000000 {.7.4.F.4.C.E.A.1.-.2.6.C.1.-.4.1.D.6.-.8.9.E.B.-.D.C.3.7.E.7.F.1.9.6.A.3.}... 9223372036854776312 {2b60bea0-0fb8-431e-b6bc-2ecb692ddb06} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00390035003700300036004600330030002d0045004400300035002d0034004500370043002d0041003800440041002d003300350045004500360037003700420036003000360042007d000000 {.9.5.7.0.6.F.3.0.-.E.D.0.5.-.4.E.7.C.-.A.8.D.A.-.3.5.E.E.6.7.7.B.6.0.6.B.}... 9223372036854776313 {39ba4b09-2547-445d-a928-30377b0904fc} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450032004500430033003600380038002d0046003000300035002d0034004200430036002d0041003700420046002d003100360032003900410042004600350031003000300041007d000000 {.E.2.E.C.3.6.8.8.-.F.0.0.5.-.4.B.C.6.-.A.7.B.F.-.1.6.2.9.A.B.F.5.1.0.0.A.}... 9223372036854776314 {eab91178-7ca7-4e7c-9c51-b0fb5529aeec} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310034003500420039003500350046002d0038003200430041002d0034003700360036002d0038004600430037002d004500360033003700330039003000370042004400390046007d000000 {.1.4.5.B.9.5.5.F.-.8.2.C.A.-.4.7.6.6.-.8.F.C.7.-.E.6.3.7.3.9.0.7.B.D.9.F.}... 9223372036854776315 {172590db-b33f-47df-9a24-df562ac3ec0f} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00320032003000370041004400310042002d0034003600330036002d0034003100340033002d0038004100390034002d004100310041003400330041003100310032004300330042007d000000 {.2.2.0.7.A.D.1.B.-.4.6.3.6.-.4.1.4.3.-.8.A.9.4.-.A.1.A.4.3.A.1.1.2.C.3.B.}... 9223372036854776316 {6a3f64d4-864f-42ed-9dd8-30a826e49a5a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00320046004500440031003700460037002d0033003000300039002d0034003800440043002d0041003600320036002d003200360030004400420046004500370039003400310035007d000000 {.2.F.E.D.1.7.F.7.-.3.0.0.9.-.4.8.D.C.-.A.6.2.6.-.2.6.0.D.B.F.E.7.9.4.1.5.}... 9223372036854776317 {74988442-3bfb-451c-9a63-9eabb07c4485} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300031003200340036004100430030002d0043003800340039002d0034003100370036002d0042003400330030002d003500440046003000350046004600330041003200370036007d000000 {.0.1.2.4.6.A.C.0.-.C.8.4.9.-.4.1.7.6.-.B.4.3.0.-.5.D.F.0.5.F.F.3.A.2.7.6.}... 9223372036854776318 {5f35226f-d9fa-4d44-8136-27a1c662a034} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00410033004400440044004200360043002d0041003800300043002d0034004300450033002d0039003400420036002d003400320035004500390045004400380037004200390044007d000000 {.A.3.D.D.D.B.6.C.-.A.8.0.C.-.4.C.E.3.-.9.4.B.6.-.4.2.5.E.9.E.D.8.7.B.9.D.}... 9223372036854776319 {ee701874-f029-4ad6-b10b-a96448806f5e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00360030003700390035003700380033002d0037004100340043002d0034003700380035002d0041003100300037002d003600380043003900350036004100330042004500380037007d000000 {.6.0.7.9.5.7.8.3.-.7.A.4.C.-.4.7.8.5.-.A.1.0.7.-.6.8.C.9.5.6.A.3.B.E.8.7.}... 9223372036854776320 {f51c59dd-efbf-4d67-8098-1ed8362db864} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310037004200450034004600360034002d0033004500360031002d0034004200420037002d0039003400450031002d003200330042004500320042003800320038004600330038007d000000 {.1.7.B.E.4.F.6.4.-.3.E.6.1.-.4.B.B.7.-.9.4.E.1.-.2.3.B.E.2.B.8.2.8.F.3.8.}... 9223372036854776321 {4118e8a8-7bc3-4cfa-bc76-b58807f4b331} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300037004500340031004500350039002d0032004100330036002d0034003700300036002d0039003200310041002d003500420038003200410037003900380043004600350042007d000000 {.0.7.E.4.1.E.5.9.-.2.A.3.6.-.4.7.0.6.-.9.2.1.A.-.5.B.8.2.A.7.9.8.C.F.5.B.}... 9223372036854776322 {4cb1e008-1717-41de-b54b-002f7f5ee7ed} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00410041003900420033003900370044002d0030003700360042002d0034003700390042002d0042004600310046002d003500390034003100350044004400430030004300390046007d000000 {.A.A.9.B.3.9.7.D.-.0.7.6.B.-.4.7.9.B.-.B.F.1.F.-.5.9.4.1.5.D.D.C.0.C.9.F.}... 9223372036854776323 {c78cca83-d284-4994-afa2-36f2d141092d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440046003600310037004500420035002d0042003400450044002d0034004500410038002d0039003700420039002d004100360038003300460045004400330035004500350031007d000000 {.D.F.6.1.7.E.B.5.-.B.4.E.D.-.4.E.A.8.-.9.7.B.9.-.A.6.8.3.F.E.D.3.5.E.5.1.}... 9223372036854776324 {3992e37e-b141-4b1e-97c7-91a13aad3f4d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430032003800340030004100430043002d0032003500300034002d0034003300430046002d0038003900460045002d004200380035004500440044003600360034003400320032007d000000 {.C.2.8.4.0.A.C.C.-.2.5.0.4.-.4.3.C.F.-.8.9.F.E.-.B.8.5.E.D.D.6.6.4.4.2.2.}... 9223372036854776325 {05ec7931-2f4c-41be-a2ea-6d51d1bbbece} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00460044003800390030003600430042002d0045004400430033002d0034003500320031002d0038003000370037002d004600330036004600410038004400320038003900410044007d000000 {.F.D.8.9.0.6.C.B.-.E.D.C.3.-.4.5.2.1.-.8.0.7.7.-.F.3.6.F.A.8.D.2.8.9.A.D.}... 9223372036854776326 {99d123c2-3b7b-4ed4-9ec8-db4045ecab9c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00370037003800310044003600460038002d0036003100420046002d0034004500310030002d0042003400310042002d003000350045004200420045003800340036004500390045007d000000 {.7.7.8.1.D.6.F.8.-.6.1.B.F.-.4.E.1.0.-.B.4.1.B.-.0.5.E.B.B.E.8.4.6.E.9.E.}... 9223372036854776327 {f1e8ae28-b94f-4ea4-8146-effcf1aab52d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00350037003800340038004300320043002d0044003800310034002d0034003600450036002d0041003500360030002d003000330043003700390035003700330039003400330041007d000000 {.5.7.8.4.8.C.2.C.-.D.8.1.4.-.4.6.E.6.-.A.5.6.0.-.0.3.C.7.9.5.7.3.9.4.3.A.}... 9223372036854776328 {f110ffcf-96c3-4478-bb37-1b945cb78a70} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00370030003400320031004100330042002d0045003900410039002d0034003000360043002d0039003400350036002d003400430044003000380030003300430046003000350041007d000000 {.7.0.4.2.1.A.3.B.-.E.9.A.9.-.4.0.6.C.-.9.4.5.6.-.4.C.D.0.8.0.3.C.F.0.5.A.}... 9223372036854776329 {f981bff2-8c56-445e-8cb7-96a027e9f6b1} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00420041004500300032004400420044002d0032003600420031002d0034004100340037002d0039003200390030002d003400300030003200360043003800380043003900390034007d000000 {.B.A.E.0.2.D.B.D.-.2.6.B.1.-.4.A.4.7.-.9.2.9.0.-.4.0.0.2.6.C.8.8.C.9.9.4.}... 9223372036854776330 {fefb887e-0b75-460c-9c27-d56487aac61a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00320033003600390033004600450030002d0031003800380039002d0034003500320044002d0039004600450030002d004100390045004600420030003000390033003700330042007d000000 {.2.3.6.9.3.F.E.0.-.1.8.8.9.-.4.5.2.D.-.9.F.E.0.-.A.9.E.F.B.0.0.9.3.7.3.B.}... 9223372036854776331 {160c36c9-0822-478c-95ee-e5f979416fbb} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00350033003200380036004400320043002d0043004400460046002d0034003900450035002d0039004600320039002d004400370043004500450043004100440037003900410036007d000000 {.5.3.2.8.6.D.2.C.-.C.D.F.F.-.4.9.E.5.-.9.F.2.9.-.D.7.C.E.E.C.A.D.7.9.A.6.}... 9223372036854776332 {a005febd-1b7b-4a86-90cc-92d03d4ef881} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440041003700430032004600380033002d0030004200370031002d0034004600300044002d0042004200350030002d003700460039003300390032004200380031003000380032007d000000 {.D.A.7.C.2.F.8.3.-.0.B.7.1.-.4.F.0.D.-.B.B.5.0.-.7.F.9.3.9.2.B.8.1.0.8.2.}... 9223372036854776333 {d1a7f6b9-32b7-43f3-9c54-729684ef4756} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00460037003400360033003500310031002d0039004400300030002d0034004300420045002d0041003300420031002d003900430031004100350031003100360042004100310032007d000000 {.F.7.4.6.3.5.1.1.-.9.D.0.0.-.4.C.B.E.-.A.3.B.1.-.9.C.1.A.5.1.1.6.B.A.1.2.}... 9223372036854776334 {eb407abd-ec36-44d8-8fc6-881762cd0741} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430038004300410039004500310043002d0039003800370038002d0034004100440044002d0038003000450033002d003700420045003700360035003000330041004600340045007d000000 {.C.8.C.A.9.E.1.C.-.9.8.7.8.-.4.A.D.D.-.8.0.E.3.-.7.B.E.7.6.5.0.3.A.F.4.E.}... 9223372036854776335 {f38530b7-9907-4f2e-be76-f1071e6395f0} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00340039004600330032003800430042002d0044003500320046002d0034004100340044002d0041003800430032002d003900320036004200440034003600300037003800360038007d000000 {.4.9.F.3.2.8.C.B.-.D.5.2.F.-.4.A.4.D.-.A.8.C.2.-.9.2.6.B.D.4.6.0.7.8.6.8.}... 9223372036854776336 {45f882ef-306e-4e41-bf2b-e06259932b00} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00380031003400440032003200450033002d0046003000440042002d0034003900380034002d0039003800330046002d004200430033003200360039003600420045003000460031007d000000 {.8.1.4.D.2.2.E.3.-.F.0.D.B.-.4.9.8.4.-.9.8.3.F.-.B.C.3.2.6.9.6.B.E.0.F.1.}... 9223372036854776337 {2e30c355-e9ed-49ce-a635-266a4440be9e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440045003400300036003700420035002d0044003000310044002d0034004600370046002d0039003500420039002d004200430041003500360045004100430041004600410041007d000000 {.D.E.4.0.6.7.B.5.-.D.0.1.D.-.4.F.7.F.-.9.5.B.9.-.B.C.A.5.6.E.A.C.A.F.A.A.}... 9223372036854776338 {a5a5784f-7482-4fb0-8f7d-d76ea07b5573} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300035003400460038004100420046002d0039003200330039002d0034003800460032002d0041004300450031002d003000360038003000310030003200420042004100430036007d000000 {.0.5.4.F.8.A.B.F.-.9.2.3.9.-.4.8.F.2.-.A.C.E.1.-.0.6.8.0.1.0.2.B.B.A.C.6.}... 9223372036854776339 {e5c91f00-cb28-4d70-8ef2-9351c595e63e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00420032004300330045003700330045002d0035004400390038002d0034004200330037002d0038004100440032002d004600410036003900300035003400350037003800320045007d000000 {.B.2.C.3.E.7.3.E.-.5.D.9.8.-.4.B.3.7.-.8.A.D.2.-.F.A.6.9.0.5.4.5.7.8.2.E.}... 9223372036854776340 {13e654d8-0311-4b2b-937c-1f01e46ffbb2} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310046003500430043003400460036002d0030004100440042002d0034003200450044002d0042003500430043002d003100350045004200350045004200390030003100430034007d000000 {.1.F.5.C.C.4.F.6.-.0.A.D.B.-.4.2.E.D.-.B.5.C.C.-.1.5.E.B.5.E.B.9.0.1.C.4.}... 9223372036854776341 {df298d75-3e7f-4e8d-8b55-f56b4926c82f} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450034004500330044003300310033002d0038003500450033002d0034004300450030002d0041003100330030002d003500380035004400430039003600370041004400360036007d000000 {.E.4.E.3.D.3.1.3.-.8.5.E.3.-.4.C.E.0.-.A.1.3.0.-.5.8.5.D.C.9.6.7.A.D.6.6.}... 9223372036854776342 {9521e687-22a1-418f-bce2-7eb7fe25070a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00340033003000310042004300350039002d0032004100450045002d0034003500410038002d0039003600340038002d004600360031004200330035003400320030003500320046007d000000 {.4.3.0.1.B.C.5.9.-.2.A.E.E.-.4.5.A.8.-.9.6.4.8.-.F.6.1.B.3.5.4.2.0.5.2.F.}... 9223372036854776343 {db42024e-f9e7-4f03-b4c3-94fb3db7f934} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00390044003600420033003300320041002d0046003500320032002d0034003400430036002d0042003400380044002d003900430043004300340035003200330037003900380036007d000000 {.9.D.6.B.3.3.2.A.-.F.5.2.2.-.4.4.C.6.-.B.4.8.D.-.9.C.C.C.4.5.2.3.7.9.8.6.}... 9223372036854776344 {35f3ec13-352d-4b4d-80e7-0745db50321e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00350041003300330032003700430037002d0033003500380035002d0034004600460043002d0039004200390042002d004100380044004400310037004400380031004600380033007d000000 {.5.A.3.3.2.7.C.7.-.3.5.8.5.-.4.F.F.C.-.9.B.9.B.-.A.8.D.D.1.7.D.8.1.F.8.3.}... 9223372036854776345 {0de8b3b5-a43a-4651-9397-f7a2a5c86ec6} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00360038003900310046003600460036002d0032003500320035002d0034004600420032002d0039003300330045002d003900450046003000370046003300380033003300350044007d000000 {.6.8.9.1.F.6.F.6.-.2.5.2.5.-.4.F.B.2.-.9.3.3.E.-.9.E.F.0.7.F.3.8.3.3.5.D.}... 9223372036854776346 {23729a28-9e77-4000-a175-0d3ace63d138} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00330034003800390034003600390032002d0036003500380031002d0034003500440035002d0041004200450044002d003000440036003400350039003500360032004400320041007d000000 {.3.4.8.9.4.6.9.2.-.6.5.8.1.-.4.5.D.5.-.A.B.E.D.-.0.D.6.4.5.9.5.6.2.D.2.A.}... 9223372036854776347 {1588f8a1-b166-48f6-bef3-a2918b2b4b2e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440036004200430039003000420046002d0035004400340043002d0034003200420038002d0042004200380031002d004400330030004200350045003500390033003300300043007d000000 {.D.6.B.C.9.0.B.F.-.5.D.4.C.-.4.2.B.8.-.B.B.8.1.-.D.3.0.B.5.E.5.9.3.3.0.C.}... 9223372036854776348 {eeefb14f-6d56-4ad8-9a0d-6906e0f27143} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00370036003900310046003900420034002d0031004400310031002d0034003900360030002d0038003800310045002d003400320030003100440045003100330031004100340034007d000000 {.7.6.9.1.F.9.B.4.-.1.D.1.1.-.4.9.6.0.-.8.8.1.E.-.4.2.0.1.D.E.1.3.1.A.4.4.}... 9223372036854776349 {ef018b97-7b72-4cdb-8f9e-6cda465099ad} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00320042003100440036003200350045002d0041004500350039002d0034004300430037002d0039003500300044002d004200320035004400340046004600340043004100370039007d000000 {.2.B.1.D.6.2.5.E.-.A.E.5.9.-.4.C.C.7.-.9.5.0.D.-.B.2.5.D.4.F.F.4.C.A.7.9.}... 9223372036854776350 {70e5215e-c496-4954-a024-485e29f64ed1} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00320030004400330039003200300030002d0034004600330044002d0034004300330037002d0039004500370035002d004600370037003400310032004500430045003900300036007d000000 {.2.0.D.3.9.2.0.0.-.4.F.3.D.-.4.C.3.7.-.9.E.7.5.-.F.7.7.4.1.2.E.C.E.9.0.6.}... 9223372036854776351 {57ca4bf8-eb85-4596-b0f5-93f3f457c9da} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00340031003500440041003600430045002d0035004500390041002d0034003400360032002d0039004100310046002d004400370039004600340037003700460031003900410035007d000000 {.4.1.5.D.A.6.C.E.-.5.E.9.A.-.4.4.6.2.-.9.A.1.F.-.D.7.9.F.4.7.7.F.1.9.A.5.}... 9223372036854776352 {4b3fce1f-7c65-4f21-9cb6-12610863dc49} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440037004100460035003800320034002d0042003100430044002d0034004300360037002d0042003100440033002d003800350044003500330038003800310041004100340039007d000000 {.D.7.A.F.5.8.2.4.-.B.1.C.D.-.4.C.6.7.-.B.1.D.3.-.8.5.D.5.3.8.8.1.A.A.4.9.}... 9223372036854776353 {a0789b38-a6a5-44c8-aee7-cb273954a2c0} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430035003500330035004200370043002d0031003900390044002d0034003000380032002d0041003100380034002d004400430030003300350043003300430035003800390038007d000000 {.C.5.5.3.5.B.7.C.-.1.9.9.D.-.4.0.8.2.-.A.1.8.4.-.D.C.0.3.5.C.3.C.5.8.9.8.}... 9223372036854776354 {7954653e-00d4-493b-bf75-a7df7811caca} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00330032003000320037004200410041002d0046004400420044002d0034003200330030002d0041003900450035002d004100460034004600330037003200410037003400350042007d000000 {.3.2.0.2.7.B.A.A.-.F.D.B.D.-.4.2.3.0.-.A.9.E.5.-.A.F.4.F.3.7.2.A.7.4.5.B.}... 9223372036854776355 {986ffd0c-9682-4e34-93ee-18282e6fc7e4} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00410043003200410042003000430045002d0045004400390036002d0034003500350041002d0039003100430035002d003100440039004200430046004100330043003100380037007d000000 {.A.C.2.A.B.0.C.E.-.E.D.9.6.-.4.5.5.A.-.9.1.C.5.-.1.D.9.B.C.F.A.3.C.1.8.7.}... 9223372036854776356 {c4a200b8-5ef7-4b22-990c-3de8576a89c2} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00460042003400320039004600380038002d0039003400340039002d0034004300440046002d0041003600390032002d003700450034003800440035003200450045004500460041007d000000 {.F.B.4.2.9.F.8.8.-.9.4.4.9.-.4.C.D.F.-.A.6.9.2.-.7.E.4.8.D.5.2.E.E.E.F.A.}... 9223372036854776357 {5504179a-041a-4796-902c-9d29d6461739} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00350037003300300032004100430032002d0036003000460039002d0034003700410034002d0038003400330035002d004600420041004600450030003800300036004400320041007d000000 {.5.7.3.0.2.A.C.2.-.6.0.F.9.-.4.7.A.4.-.8.4.3.5.-.F.B.A.F.E.0.8.0.6.D.2.A.}... 9223372036854776358 {f8a49d92-f357-4994-8b2a-a3cbed9d995b} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00420039003900340034004300370046002d0037003100390030002d0034004300340045002d0041004100380046002d004400370033003600460034004200370042003900340041007d000000 {.B.9.9.4.4.C.7.F.-.7.1.9.0.-.4.C.4.E.-.A.A.8.F.-.D.7.3.6.F.4.B.7.B.9.4.A.}... 9223372036854776359 {ccee2909-294e-4ad6-b0ef-fa93cdf3d2b5} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00340045003100450038003500330036002d0046004600450037002d0034003200420039002d0042003500340035002d003900380030004400330045003200300030003700300044007d000000 {.4.E.1.E.8.5.3.6.-.F.F.E.7.-.4.2.B.9.-.B.5.4.5.-.9.8.0.D.3.E.2.0.0.7.0.D.}... 9223372036854776360 {3aaefe3a-4f84-40da-a38a-319c198ac067} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00380042003100390046003900440034002d0033004200460045002d0034003900300030002d0041004200430033002d004400410039003100380037003600410044003400320043007d000000 {.8.B.1.9.F.9.D.4.-.3.B.F.E.-.4.9.0.0.-.A.B.C.3.-.D.A.9.1.8.7.6.A.D.4.2.C.}... 9223372036854776361 {2fceaed8-43de-431e-9e1c-248e4f8f43e1} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00320041004400370033004500310035002d0041004400380041002d0034004500310037002d0042004200450030002d004200420038003200450039003600360046003900360045007d000000 {.2.A.D.7.3.E.1.5.-.A.D.8.A.-.4.E.1.7.-.B.B.E.0.-.B.B.8.2.E.9.6.6.F.9.6.E.}... 9223372036854776362 {a2ddf546-d32c-4a74-b522-12c269bee88f} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00390035003500410034003800350041002d0039004600300036002d0034004100360043002d0041003900360044002d004100330042003500430046003600390046004200350042007d000000 {.9.5.5.A.4.8.5.A.-.9.F.0.6.-.4.A.6.C.-.A.9.6.D.-.A.3.B.5.C.F.6.9.F.B.5.B.}... 9223372036854776363 {8852c503-6be2-4850-b366-41a3600b3c14} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00370043003100330033003900300038002d0030003500330045002d0034003800350039002d0039004400360034002d004600300031003300440031004100410035003600300036007d000000 {.7.C.1.3.3.9.0.8.-.0.5.3.E.-.4.8.5.9.-.9.D.6.4.-.F.0.1.3.D.1.A.A.5.6.0.6.}... 9223372036854776364 {d02a7aa2-6454-4fd6-82fb-b198cef82a49} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430043003100410039004200390046002d0046004200420033002d0034003300300045002d0039004200350033002d004300380039003800420035003400380039003100430036007d000000 {.C.C.1.A.9.B.9.F.-.F.B.B.3.-.4.3.0.E.-.9.B.5.3.-.C.8.9.8.B.5.4.8.9.1.C.6.}... 9223372036854776365 {577bdf7b-b66e-4aa7-8c1e-0056a2312d4d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00360032003500340043003500440044002d0036004600430039002d0034003900460030002d0038004300350039002d003400350030004200440033004600320039003100390038007d000000 {.6.2.5.4.C.5.D.D.-.6.F.C.9.-.4.9.F.0.-.8.C.5.9.-.4.5.0.B.D.3.F.2.9.1.9.8.}... 9223372036854776366 {e972b787-cefb-4018-8a07-cb56fb335d05} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440043004200370044003300310041002d0043003600440030002d0034004500340037002d0042003300380034002d003200460039003400430030003400440030003300360034007d000000 {.D.C.B.7.D.3.1.A.-.C.6.D.0.-.4.E.4.7.-.B.3.8.4.-.2.F.9.4.C.0.4.D.0.3.6.4.}... 9223372036854776367 {a1c1ada8-14c5-436e-a4c1-9ed9a31cf907} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430037004200320036004100450043002d0038003800440044002d0034004300390030002d0039003100310043002d004100410037003500350031003500440039003600360041007d000000 {.C.7.B.2.6.A.E.C.-.8.8.D.D.-.4.C.9.0.-.9.1.1.C.-.A.A.7.5.5.1.5.D.9.6.6.A.}... 9223372036854776368 {000023c2-38de-4459-b661-d03163e40e3a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00360046004400350033003700420035002d0036004300330031002d0034003500380042002d0038003900310033002d003400440042004100440041003700430030003000340030007d000000 {.6.F.D.5.3.7.B.5.-.6.C.3.1.-.4.5.8.B.-.8.9.1.3.-.4.D.B.A.D.A.7.C.0.0.4.0.}... 9223372036854776369 {98008b68-29a7-4a99-ab26-95244c6f3f54} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00320037003400320035004500360031002d0031003600450037002d0034003400370030002d0038004600410042002d003600440041004400430041004600320038003000380035007d000000 {.2.7.4.2.5.E.6.1.-.1.6.E.7.-.4.4.7.0.-.8.F.A.B.-.6.D.A.D.C.A.F.2.8.0.8.5.}... 9223372036854776370 {43241563-341c-4081-a5c8-5936cf234f41} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450030004400420037003000330041002d0036003800410045002d0034003200410045002d0042004600410035002d003200390036003400390039004200300033004200450031007d000000 {.E.0.D.B.7.0.3.A.-.6.8.A.E.-.4.2.A.E.-.B.F.A.5.-.2.9.6.4.9.9.B.0.3.B.E.1.}... 9223372036854776371 {447f6038-491e-4529-bc1e-a587cba618a3} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450041003900330032004500410041002d0036004300460043002d0034003200390042002d0042003700320035002d003000370044004100440033004400330035003000450044007d000000 {.E.A.9.3.2.E.A.A.-.6.C.F.C.-.4.2.9.B.-.B.7.2.5.-.0.7.D.A.D.3.D.3.5.0.E.D.}... 9223372036854776372 {5c41bff6-8bf5-48af-b42e-3b913be0d81f} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00350031004200450041003600460041002d0042004400350033002d0034003200350038002d0041003600420035002d003500430044003400410044003300300031004600310033007d000000 {.5.1.B.E.A.6.F.A.-.B.D.5.3.-.4.2.5.8.-.A.6.B.5.-.5.C.D.4.A.D.3.0.1.F.1.3.}... 9223372036854776373 {978930af-c0d6-4296-ae53-cabba5589a56} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440035003500360034004100350046002d0039003800320038002d0034003600360031002d0039004600430043002d004200460045003200320039003200460043003300370033007d000000 {.D.5.5.6.4.A.5.F.-.9.8.2.8.-.4.6.6.1.-.9.F.C.C.-.B.F.E.2.2.9.2.F.C.3.7.3.}... 9223372036854776374 {848b440e-9599-4a74-b7d7-e85f5eabbee0} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00330035003700410044004300300030002d0041003400450044002d0034004200370035002d0039003500460033002d003700410030003600370035004600440036003800320045007d000000 {.3.5.7.A.D.C.0.0.-.A.4.E.D.-.4.B.7.5.-.9.5.F.3.-.7.A.0.6.7.5.F.D.6.8.2.E.}... 9223372036854776375 {074dbf65-e26f-4e48-bcf6-2e6308011093} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00390045003900320033003700390041002d0031004600370044002d0034003400430044002d0042004400380046002d004400450041003400300033003600360031003000330045007d000000 {.9.E.9.2.3.7.9.A.-.1.F.7.D.-.4.4.C.D.-.B.D.8.F.-.D.E.A.4.0.3.6.6.1.0.3.E.}... 9223372036854776376 {8ebcef68-f34d-4139-8b85-7b57334b9329} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310037003100350031003900310046002d0035003100450038002d0034003900320046002d0038003800460044002d004500440043003000330044003500370045004600370042007d000000 {.1.7.1.5.1.9.1.F.-.5.1.E.8.-.4.9.2.F.-.8.8.F.D.-.E.D.C.0.3.D.5.7.E.F.7.B.}... 9223372036854776377 {152c9c3b-b07e-486e-a750-7d561d75f843} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450043003600410039003600330039002d0035003300360035002d0034003600430041002d0042004400460042002d004500350041004100300039003300420035004200460045007d000000 {.E.C.6.A.9.6.3.9.-.5.3.6.5.-.4.6.C.A.-.B.D.F.B.-.E.5.A.A.0.9.3.B.5.B.F.E.}... 9223372036854776378 {4c37bb57-9bdf-4ab2-891f-cccdbc2b0d0c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440041003600320046003000300041002d0041003000350034002d0034004600420032002d0038004600370033002d003100370031004100370034003900360045003500360036007d000000 {.D.A.6.2.F.0.0.A.-.A.0.5.4.-.4.F.B.2.-.8.F.7.3.-.1.7.1.A.7.4.9.6.E.5.6.6.}... 9223372036854776379 {6f3f53ac-3323-445c-9236-0794fb401551} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440042004300410038003200380035002d0035003400330042002d0034003000330035002d0042003900330035002d004200390039004500320033003100350037003500360031007d000000 {.D.B.C.A.8.2.8.5.-.5.4.3.B.-.4.0.3.5.-.B.9.3.5.-.B.9.9.E.2.3.1.5.7.5.6.1.}... 9223372036854776380 {02438bea-ea89-467b-95c8-818ba1d1c792} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300039003200340033003100330033002d0033004400350041002d0034004500340034002d0041003800310041002d003700440030003100330034004300300031003800440030007d000000 {.0.9.2.4.3.1.3.3.-.3.D.5.A.-.4.E.4.4.-.A.8.1.A.-.7.D.0.1.3.4.C.0.1.8.D.0.}... 9223372036854776381 {86b5cc90-9f68-470a-b4ea-a0649192dc04} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00330033003100430046004600440038002d0046003100320041002d0034004200410044002d0041004200410043002d004100410039004100300035003500460041003800370037007d000000 {.3.3.1.C.F.F.D.8.-.F.1.2.A.-.4.B.A.D.-.A.B.A.C.-.A.A.9.A.0.5.5.F.A.8.7.7.}... 9223372036854776382 {c7e71c4d-4300-492f-9531-78b23a4270af} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00340034003100360030004200370037002d0042003100340043002d0034003100380042002d0041004600420037002d003500350034004200330032003900420034003600310041007d000000 {.4.4.1.6.0.B.7.7.-.B.1.4.C.-.4.1.8.B.-.A.F.B.7.-.5.5.4.B.3.2.9.B.4.6.1.A.}... 9223372036854776383 {ba1eb0dc-09b8-4985-9a99-40696b5eb363} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300038004600420044003100370037002d0046003100330033002d0034003500390036002d0042003800380037002d003200430042004600420042003900440044003300420032007d000000 {.0.8.F.B.D.1.7.7.-.F.1.3.3.-.4.5.9.6.-.B.8.8.7.-.2.C.B.F.B.B.9.D.D.3.B.2.}... 9223372036854776384 {bdfe501c-4d70-448d-939f-01ee41bd1d51} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440039004100370033003800330034002d0038004100310037002d0034004400370043002d0042003600460034002d003500330038004300350032003000450033004100440036007d000000 {.D.9.A.7.3.8.3.4.-.8.A.1.7.-.4.D.7.C.-.B.6.F.4.-.5.3.8.C.5.2.0.E.3.A.D.6.}... 9223372036854776385 {84f429fe-9e92-4f64-9e85-1638507af4bb} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430042004300370037003300320035002d0044003300420032002d0034003300350032002d0039004300380043002d004300350034003300330036003800460032003100330030007d000000 {.C.B.C.7.7.3.2.5.-.D.3.B.2.-.4.3.5.2.-.9.C.8.C.-.C.5.4.3.3.6.8.F.2.1.3.0.}... 9223372036854776386 {c517f12a-4314-4b91-a3e3-cffa9ba1151c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00360032004300430045004500330042002d0035003800300030002d0034004500430036002d0038003200450041002d003700330035003300330030004400320033004100370041007d000000 {.6.2.C.C.E.E.3.B.-.5.8.0.0.-.4.E.C.6.-.8.2.E.A.-.7.3.5.3.3.0.D.2.3.A.7.A.}... 9223372036854776387 {4e4daac0-5ff3-4fd0-b89b-8515c76d2b39} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00350035004100360038003800460033002d0037004300410032002d0034004300330038002d0042004600300035002d004200350035004300340042003900390041004500330030007d000000 {.5.5.A.6.8.8.F.3.-.7.C.A.2.-.4.C.3.8.-.B.F.0.5.-.B.5.5.C.4.B.9.9.A.E.3.0.}... 9223372036854776388 {2cbac533-c29f-4914-9c3d-39c8c547a017} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440038003800330041003700450039002d0037003600420031002d0034003000350041002d0042003900380034002d003800430033003600360032003200450033004600310036007d000000 {.D.8.8.3.A.7.E.9.-.7.6.B.1.-.4.0.5.A.-.B.9.8.4.-.8.C.3.6.6.2.2.E.3.F.1.6.}... 9223372036854776389 {b09b0efc-578d-48b1-ab44-ad43ae7e0180} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300042004200300036003800340038002d0031004100450035002d0034004600310041002d0042003100310041002d004500380030003600360035003900420041003900370037007d000000 {.0.B.B.0.6.8.4.8.-.1.A.E.5.-.4.F.1.A.-.B.1.1.A.-.E.8.0.6.6.5.9.B.A.9.7.7.}... 9223372036854776390 {df4b6bcf-364f-4b59-be2e-708a2cf34736} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440032003200380042003800430043002d0037003700320043002d0034003000420046002d0041004100460046002d003700370042003600360031003500450039003300310033007d000000 {.D.2.2.8.B.8.C.C.-.7.7.2.C.-.4.0.B.F.-.A.A.F.F.-.7.7.B.6.6.1.5.E.9.3.1.3.}... 9223372036854776391 {f1e8ead7-7365-4b62-98e0-07d8c7a70f69} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440032003400340033003300320041002d0038003100320044002d0034004600350045002d0039004300390045002d004400330035004500300032003100300044003900450034007d000000 {.D.2.4.4.3.3.2.A.-.8.1.2.D.-.4.F.5.E.-.9.C.9.E.-.D.3.5.E.0.2.1.0.D.9.E.4.}... 9223372036854776392 {54c215ee-3737-4395-b870-3c39a92d6e14} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00380034003900460036003100310038002d0037003000430038002d0034003100370036002d0039004300450033002d004100390037003600320042004100300034003500460034007d000000 {.8.4.9.F.6.1.1.8.-.7.0.C.8.-.4.1.7.6.-.9.C.E.3.-.A.9.7.6.2.B.A.0.4.5.F.4.}... 9223372036854776393 {dc9e7dac-335e-4ac9-86c7-4ec827dfa623} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00360039003000390044004400360038002d0030004200390034002d0034003400330038002d0042003200450034002d003000450043003000450043003500340039004400430035007d000000 {.6.9.0.9.D.D.6.8.-.0.B.9.4.-.4.4.3.8.-.B.2.E.4.-.0.E.C.0.E.C.5.4.9.D.C.5.}... 9223372036854776394 {e34c6afe-4306-4659-93f3-4d97ec7172c6} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00410046004500320030003100380033002d0035003900310031002d0034003900380042002d0038004200320030002d003500420031004100440032003400430044003200330031007d000000 {.A.F.E.2.0.1.8.3.-.5.9.1.1.-.4.9.8.B.-.8.B.2.0.-.5.B.1.A.D.2.4.C.D.2.3.1.}... 9223372036854776395 {f2910211-63cc-46f9-b8e7-cd75d195b804} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00330038004300450046003000410033002d0039004100310032002d0034004400310042002d0042003400430046002d003200460043003300330041003500350031003500380046007d000000 {.3.8.C.E.F.0.A.3.-.9.A.1.2.-.4.D.1.B.-.B.4.C.F.-.2.F.C.3.3.A.5.5.1.5.8.F.}... 9223372036854776396 {34d3c4b0-f3dd-408d-8f06-aef15eb33e99} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300039003000310043004600320035002d0031003600430030002d0034004200360034002d0039003000390032002d003000390039003400370034004300350044003000450042007d000000 {.0.9.0.1.C.F.2.5.-.1.6.C.0.-.4.B.6.4.-.9.0.9.2.-.0.9.9.4.7.4.C.5.D.0.E.B.}... 9223372036854776397 {6256af2d-05dd-4bbd-81b0-b65cf3505cc7} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00350035003600300039003000390031002d0033004600350043002d0034003500410031002d0041004600420038002d004500340032003300460046003700350043004200300035007d000000 {.5.5.6.0.9.0.9.1.-.3.F.5.C.-.4.5.A.1.-.A.F.B.8.-.E.4.2.3.F.F.7.5.C.B.0.5.}... 9223372036854776398 {b656da4a-de0d-4d0e-8f93-4273211ec8de} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300032004400390038003000390036002d0039004100380031002d0034004600370034002d0042003900310046002d004600300042004400450030004400430034003100460041007d000000 {.0.2.D.9.8.0.9.6.-.9.A.8.1.-.4.F.7.4.-.B.9.1.F.-.F.0.B.D.E.0.D.C.4.1.F.A.}... 9223372036854776399 {db9854c8-9f5a-4731-a881-ac02fa59715d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00360032003500460030003500450038002d0045003000360046002d0034003500460035002d0042003300380036002d003700330044003000430043003500320034004100440041007d000000 {.6.2.5.F.0.5.E.8.-.E.0.6.F.-.4.5.F.5.-.B.3.8.6.-.7.3.D.0.C.C.5.2.4.A.D.A.}... 9223372036854776400 {278afac0-96f3-483f-b065-daf09e88e006} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00380030003000460046003400330045002d0035004400320030002d0034004100320034002d0041003600450045002d003000450033004200320035003600320038003600310030007d000000 {.8.0.0.F.F.4.3.E.-.5.D.2.0.-.4.A.2.4.-.A.6.E.E.-.0.E.3.B.2.5.6.2.8.6.1.0.}... 9223372036854776401 {83ff194c-2394-4712-bf17-d5ec5c46f605} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300032003100390031003700360042002d0037004300340030002d0034003400320043002d0042003000370033002d003400450045003600450042004300300032004100300043007d000000 {.0.2.1.9.1.7.6.B.-.7.C.4.0.-.4.4.2.C.-.B.0.7.3.-.4.E.E.6.E.B.C.0.2.A.0.C.}... 9223372036854776402 {d44012ee-bfc1-40ec-8efe-d82a7572b0f4} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00410037004100310046003400340031002d0030003800360031002d0034003500320036002d0041004300310039002d004300300045004600410039003100330033003300430034007d000000 {.A.7.A.1.F.4.4.1.-.0.8.6.1.-.4.5.2.6.-.A.C.1.9.-.C.0.E.F.A.9.1.3.3.3.C.4.}... 9223372036854776403 {6cc13dd8-dd41-4153-9f2c-b73d772dbef3} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300030003600360030003200300039002d0039003000340039002d0034004300450033002d0038003100450030002d004200320039004300300031004400310035003900460043007d000000 {.0.0.6.6.0.2.0.9.-.9.0.4.9.-.4.C.E.3.-.8.1.E.0.-.B.2.9.C.0.1.D.1.5.9.F.C.}... 9223372036854776404 {ff24c069-f535-479a-8c11-f081dd0ac319} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00320033003600450033003900320033002d0039003200320032002d0034003000330035002d0038003500410046002d003100390031003100410035004200380042003900340035007d000000 {.2.3.6.E.3.9.2.3.-.9.2.2.2.-.4.0.3.5.-.8.5.A.F.-.1.9.1.1.A.5.B.8.B.9.4.5.}... 9223372036854776405 {4d5c2e53-a292-49ca-a085-a5576bb43cbf} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310031004600430030003200420045002d0030004100410037002d0034003300330043002d0041003700430033002d004500310046004100360037004200340046004300410034007d000000 {.1.1.F.C.0.2.B.E.-.0.A.A.7.-.4.3.3.C.-.A.7.C.3.-.E.1.F.A.6.7.B.4.F.C.A.4.}... 9223372036854776406 {10fff4e2-b1f1-4ad9-8621-d8312897f70d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00370035003500380031004100380039002d0030003600300032002d0034004300380044002d0038003000310030002d003600440044003300340036003100440035003500330045007d000000 {.7.5.5.8.1.A.8.9.-.0.6.0.2.-.4.C.8.D.-.8.0.1.0.-.6.D.D.3.4.6.1.D.5.5.3.E.}... 9223372036854776407 {368c1f37-db34-4e36-ae3e-1a894777a379} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440043004600330045003900310032002d0036003600390035002d0034004100430039002d0038004100430039002d003700390033003300340038003200430039003400330037007d000000 {.D.C.F.3.E.9.1.2.-.6.6.9.5.-.4.A.C.9.-.8.A.C.9.-.7.9.3.3.4.8.2.C.9.4.3.7.}... 9223372036854776408 {488dae1d-fa1e-404f-a365-32b5f9a97209} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430033003600420031003000370036002d0034003700450043002d0034004200450045002d0039003500300037002d004500300035004500330041003400360036004300340045007d000000 {.C.3.6.B.1.0.7.6.-.4.7.E.C.-.4.B.E.E.-.9.5.0.7.-.E.0.5.E.3.A.4.6.6.C.4.E.}... 9223372036854776411 {6f8faa8d-e741-4380-a25e-079570bee209} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430038003300330037003200310044002d0039003500300034002d0034004400310041002d0039003900340042002d004500360036003300380043004200320041003500370045007d000000 {.C.8.3.3.7.2.1.D.-.9.5.0.4.-.4.D.1.A.-.9.9.4.B.-.E.6.6.3.8.C.B.2.A.5.7.E.}... 9223372036854776412 {7252f45c-99e4-4b10-a885-6e89864ddbdf} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440030004400460045003600420034002d0037003700350042002d0034003200360037002d0041003300340035002d003700390033004300440034003300340043003900410041007d000000 {.D.0.D.F.E.6.B.4.-.7.7.5.B.-.4.2.6.7.-.A.3.4.5.-.7.9.3.C.D.4.3.4.C.9.A.A.}... 9223372036854776413 {cc263db7-a803-4ddc-915b-c28786aa1106} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430042003200430043004500370033002d0033003300380045002d0034003400410045002d0038003600350044002d003100370039003400420041004100460039003300430044007d000000 {.C.B.2.C.C.E.7.3.-.3.3.8.E.-.4.4.A.E.-.8.6.5.D.-.1.7.9.4.B.A.A.F.9.3.C.D.}... 9223372036854776414 {41d435c4-374c-4379-81c5-e77b5ac3b000} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00340034004500450037003200430034002d0032003300450035002d0034004100340030002d0042003900340033002d003900310044004300450036003000310043003600420045007d000000 {.4.4.E.E.7.2.C.4.-.2.3.E.5.-.4.A.4.0.-.B.9.4.3.-.9.1.D.C.E.6.0.1.C.6.B.E.}... 9223372036854776415 {61b04769-24ce-41d7-a0db-551604ba286c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300046003800460043003500310032002d0038003100410034002d0034003600410038002d0039003300390036002d004400440041004500420036004300350046004200370034007d000000 {.0.F.8.F.C.5.1.2.-.8.1.A.4.-.4.6.A.8.-.9.3.9.6.-.D.D.A.E.B.6.C.5.F.B.7.4.}... 9223372036854776416 {ea99112d-7c47-468c-97a2-0f8d8b8d9434} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00420038004300450030003900440031002d0044003800390033002d0034003200340042002d0039003500370045002d003900360030004600350035004400460036003400370041007d000000 {.B.8.C.E.0.9.D.1.-.D.8.9.3.-.4.2.4.B.-.9.5.7.E.-.9.6.0.F.5.5.D.F.6.4.7.A.}... 9223372036854776417 {cce03e11-69e7-4526-b635-6c4d3d4d9e60} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00320045003000320042003700450034002d0034004300320038002d0034004300460045002d0038003000360036002d003600350039003800440046003400410039003700300041007d000000 {.2.E.0.2.B.7.E.4.-.4.C.2.8.-.4.C.F.E.-.8.0.6.6.-.6.5.9.8.D.F.4.A.9.7.0.A.}... 9223372036854776418 {9e93f20a-fa57-4f9e-a74e-b92fb120b2cd} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00350035003100350042004500420044002d0031003600430043002d0034003800420037002d0042003500430044002d003000440036003200320038003900360035004300380032007d000000 {.5.5.1.5.B.E.B.D.-.1.6.C.C.-.4.8.B.7.-.B.5.C.D.-.0.D.6.2.2.8.9.6.5.C.8.2.}... 9223372036854776419 {d7494c94-88e8-4e5e-9a7a-b5ceda82fff6} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430034003600380031003400350034002d0036004200330038002d0034003700360037002d0038003500440045002d004600340037004400420039004500310046004600310039007d000000 {.C.4.6.8.1.4.5.4.-.6.B.3.8.-.4.7.6.7.-.8.5.D.E.-.F.4.7.D.B.9.E.1.F.F.1.9.}... 9223372036854776420 {39b3a2c0-9c09-4442-b2f2-43214a54b45a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450035004600460033003900460030002d0045004100370032002d0034003300440031002d0042004100410044002d003500390031003700330039004300330043003300380039007d000000 {.E.5.F.F.3.9.F.0.-.E.A.7.2.-.4.3.D.1.-.B.A.A.D.-.5.9.1.7.3.9.C.3.C.3.8.9.}... 9223372036854776421 {2baa143c-6dbe-49e8-9267-5cc0059ed12d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00350044004600460036004500300030002d0044003800310041002d0034004300460035002d0041003300410034002d003800320045003600360033003600380036004100360041007d000000 {.5.D.F.F.6.E.0.0.-.D.8.1.A.-.4.C.F.5.-.A.3.A.4.-.8.2.E.6.6.3.6.8.6.A.6.A.}... 9223372036854776422 {26af65c1-8005-4cf0-ada0-9aa571a1365e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00380035003200380031004100330031002d0034004100420033002d0034003100310032002d0039004100390037002d004300430038003700390045003900430044003500440039007d000000 {.8.5.2.8.1.A.3.1.-.4.A.B.3.-.4.1.1.2.-.9.A.9.7.-.C.C.8.7.9.E.9.C.D.5.D.9.}... 9223372036854776423 {ea23c737-f8b8-4a7d-ab55-3396e8d029f2} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440043003500430044003200380046002d0039004300350044002d0034004300300042002d0041003100370043002d003100300045003800390030004200450037004100390043007d000000 {.D.C.5.C.D.2.8.F.-.9.C.5.D.-.4.C.0.B.-.A.1.7.C.-.1.0.E.8.9.0.B.E.7.A.9.C.}... 9223372036854776424 {bff5e1d1-5465-49b3-b3c6-c812334ae432} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00370039004200450034003500330045002d0031003300340031002d0034004200320045002d0039003000460031002d004300370044003300300033003500350038003900450041007d000000 {.7.9.B.E.4.5.3.E.-.1.3.4.1.-.4.B.2.E.-.9.0.F.1.-.C.7.D.3.0.3.5.5.8.9.E.A.}... 9223372036854776425 {a7a81b7f-2287-4d4a-b34e-eafa46a56fed} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00410036004200430039004300340030002d0034003400390041002d0034003100340043002d0038003200310038002d003700390042004100330044004200410038003000390043007d000000 {.A.6.B.C.9.C.4.0.-.4.4.9.A.-.4.1.4.C.-.8.2.1.8.-.7.9.B.A.3.D.B.A.8.0.9.C.}... 9223372036854776426 {0dfc39e6-df49-4ff4-ad33-a67704cd209b} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00410041003400310039004300440038002d0033003800440030002d0034003800390036002d0039004200350030002d003100350046003600300037003700440032003600370032007d000000 {.A.A.4.1.9.C.D.8.-.3.8.D.0.-.4.8.9.6.-.9.B.5.0.-.1.5.F.6.0.7.7.D.2.6.7.2.}... 9223372036854776427 {e014bc02-d4a9-413d-8213-199172a7df9e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00330043003900350045003100460041002d0046004400370038002d0034004400320042002d0038004200430041002d003000420045004400420038004500460035003300350034007d000000 {.3.C.9.5.E.1.F.A.-.F.D.7.8.-.4.D.2.B.-.8.B.C.A.-.0.B.E.D.B.8.E.F.5.3.5.4.}... 9223372036854776428 {21f5906e-61ef-4d15-87b2-89f4786ec5d0} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300036003800450035004300340033002d0042003800390042002d0034003200350041002d0039003700430039002d003300310036003100390041003300410038004400440030007d000000 {.0.6.8.E.5.C.4.3.-.B.8.9.B.-.4.2.5.A.-.9.7.C.9.-.3.1.6.1.9.A.3.A.8.D.D.0.}... 9223372036854776429 {a6088cfc-cd47-4787-b825-f9182c0e5bfe} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00380044003000430035004600310043002d0033003400440032002d0034003700450046002d0042004200340043002d003500330043004400350046003000300031003900310045007d000000 {.8.D.0.C.5.F.1.C.-.3.4.D.2.-.4.7.E.F.-.B.B.4.C.-.5.3.C.D.5.F.0.0.1.9.1.E.}... 9223372036854776430 {816e87d6-82f6-4a64-8f51-5ce17e19ce41} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00350042003000460030004500440045002d0031003000430046002d0034003900440036002d0041003300440033002d003800360036004300320031003800420038003400300046007d000000 {.5.B.0.F.0.E.D.E.-.1.0.C.F.-.4.9.D.6.-.A.3.D.3.-.8.6.6.C.2.1.8.B.8.4.0.F.}... 9223372036854776431 {ddb485c7-523d-473e-aa7c-006050a20609} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00460039003000410039003400460044002d0038003700390045002d0034004100380035002d0041003200300036002d004400320037004600360034003000330039003200350038007d000000 {.F.9.0.A.9.4.F.D.-.8.7.9.E.-.4.A.8.5.-.A.2.0.6.-.D.2.7.F.6.4.0.3.9.2.5.8.}... 9223372036854776432 {aab2b3fd-6d4d-4a30-8f5d-4562010bd49e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00420032003800410046003000450035002d0044003000420037002d0034003800420036002d0039004500300030002d003300430031003100320030004600430043004300460034007d000000 {.B.2.8.A.F.0.E.5.-.D.0.B.7.-.4.8.B.6.-.9.E.0.0.-.3.C.1.1.2.0.F.C.C.C.F.4.}... 9223372036854776433 {5f3612b3-d154-4a81-a87f-9812b69d879f} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00320034003100380043004400430037002d0033003100410036002d0034003400310044002d0038004500390034002d003200430030003800310033003300430034003500340041007d000000 {.2.4.1.8.C.D.C.7.-.3.1.A.6.-.4.4.1.D.-.8.E.9.4.-.2.C.0.8.1.3.3.C.4.5.4.A.}... 9223372036854776434 {abb7f935-dc63-46d8-b66c-f0cc5c1e5280} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00390033004200440033004100370031002d0031003000320036002d0034003600450031002d0041004100320032002d003700360030004100460035003500430038004200320034007d000000 {.9.3.B.D.3.A.7.1.-.1.0.2.6.-.4.6.E.1.-.A.A.2.2.-.7.6.0.A.F.5.5.C.8.B.2.4.}... 9223372036854776435 {54883c72-078c-4ac9-ba34-a84418454eca} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310046003100300039004300340033002d0035003900310038002d0034004600460043002d0039003600410030002d004600310039003900450033003500420043003200410032007d000000 {.1.F.1.0.9.C.4.3.-.5.9.1.8.-.4.F.F.C.-.9.6.A.0.-.F.1.9.9.E.3.5.B.C.2.A.2.}... 9223372036854776436 {70e256b2-922a-432a-9f93-0da28403e285} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310037004200340039003100450032002d0042004600310034002d0034004100380033002d0042003300450037002d004300390039004200390044003700390039003800340036007d000000 {.1.7.B.4.9.1.E.2.-.B.F.1.4.-.4.A.8.3.-.B.3.E.7.-.C.9.9.B.9.D.7.9.9.8.4.6.}... 9223372036854776439 {768024ef-bdcf-4652-87ba-6bab3d4c5188} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450039004500420045003000310038002d0041003000300034002d0034004100320030002d0039003400450036002d003500350036004600450030003400440038003600340044007d000000 {.E.9.E.B.E.0.1.8.-.A.0.0.4.-.4.A.2.0.-.9.4.E.6.-.5.5.6.F.E.0.4.D.8.6.4.D.}... 9223372036854776440 {3b994b2a-f242-4c76-855c-03b815f2060b} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450038003900360042003900450042002d0037003900420041002d0034003700430043002d0039003000390045002d003800410038003900420039004200370044003200370037007d000000 {.E.8.9.6.B.9.E.B.-.7.9.B.A.-.4.7.C.C.-.9.0.9.E.-.8.A.8.9.B.9.B.7.D.2.7.7.}... 9223372036854776441 {6df8b345-0230-43d9-8d8e-12dc278a5da6} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300034003300330035004500380033002d0036004400330044002d0034003400380039002d0041003800420044002d003400360031004500300031003200390046004300450036007d000000 {.0.4.3.3.5.E.8.3.-.6.D.3.D.-.4.4.8.9.-.A.8.B.D.-.4.6.1.E.0.1.2.9.F.C.E.6.}... 9223372036854776442 {f24769c1-3640-4f68-afde-76aa79179219} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00340031004100320034003700320045002d0046004600440036002d0034003600450032002d0041003000330035002d004300430038004600450031003000300034004500410038007d000000 {.4.1.A.2.4.7.2.E.-.F.F.D.6.-.4.6.E.2.-.A.0.3.5.-.C.C.8.F.E.1.0.0.4.E.A.8.}... 9223372036854776443 {aefe901c-5a83-4e4c-9d5e-178ff2082943} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00380046003100310042003900390034002d0044003100390031002d0034003000340038002d0042003900430042002d003000460044003500380045003600330030003600390036007d000000 {.8.F.1.1.B.9.9.4.-.D.1.9.1.-.4.0.4.8.-.B.9.C.B.-.0.F.D.5.8.E.6.3.0.6.9.6.}... 9223372036854776444 {78518afb-c587-4564-a904-4e6be35bf900} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00390044003400440034004500370032002d0038004600350044002d0034003000340041002d0039003400380036002d003400420035003400320039004600460035003800330031007d000000 {.9.D.4.D.4.E.7.2.-.8.F.5.D.-.4.0.4.A.-.9.4.8.6.-.4.B.5.4.2.9.F.F.5.8.3.1.}... 9223372036854776445 {95555d1b-c7d8-4915-b994-00260f75e514} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310038003400440030003600440041002d0044003100370037002d0034004600450045002d0041004300460032002d003900350042003700380032004100420035003900390046007d000000 {.1.8.4.D.0.6.D.A.-.D.1.7.7.-.4.F.E.E.-.A.C.F.2.-.9.5.B.7.8.2.A.B.5.9.9.F.}... 9223372036854776446 {0b587fb8-2b5d-4186-84ca-ff54e76304fa} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00340046003200340043004200390039002d0034003900430038002d0034004300340036002d0038003200350042002d004400370031004100390037003800360043004200440034007d000000 {.4.F.2.4.C.B.9.9.-.4.9.C.8.-.4.C.4.6.-.8.2.5.B.-.D.7.1.A.9.7.8.6.C.B.D.4.}... 9223372036854776447 {805b81fa-6d7a-43a3-91c1-f13f1a1a9162} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450034004500380046003900330036002d0043004600380036002d0034003200410036002d0039003900450031002d003400370045004200390033003400380042003700420030007d000000 {.E.4.E.8.F.9.3.6.-.C.F.8.6.-.4.2.A.6.-.9.9.E.1.-.4.7.E.B.9.3.4.8.B.7.B.0.}... 9223372036854776448 {4069af0d-2fc4-448f-807f-9c6876d0a3ea} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00390041003500440033003800450041002d0046003300350043002d0034003100380043002d0041003700310033002d003300340044003000360031004500360033003500420046007d000000 {.9.A.5.D.3.8.E.A.-.F.3.5.C.-.4.1.8.C.-.A.7.1.3.-.3.4.D.0.6.1.E.6.3.5.B.F.}... 9223372036854776449 {a5bfc811-4153-4f33-8718-d8f4522e2091} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00390038003700360044003800450035002d0046003300310035002d0034004500460043002d0039004500440043002d003800420039003300410046003100360031004300330045007d000000 {.9.8.7.6.D.8.E.5.-.F.3.1.5.-.4.E.F.C.-.9.E.D.C.-.8.B.9.3.A.F.1.6.1.C.3.E.}... 9223372036854776450 {1a89d52d-bdc9-4413-bd14-8e6e834da99c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00350041003900440046004100340039002d0039004100350030002d0034003400450036002d0038004400460046002d004100460039003500450032004500430038004400390032007d000000 {.5.A.9.D.F.A.4.9.-.9.A.5.0.-.4.4.E.6.-.8.D.F.F.-.A.F.9.5.E.2.E.C.8.D.9.2.}... 9223372036854776451 {804b6c1e-62a6-440e-a4e1-45fd8949dce6} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430043004100300043003400320032002d0037003400310041002d0034004500350044002d0038004100300045002d003300360038003700350041004300440036003100420038007d000000 {.C.C.A.0.C.4.2.2.-.7.4.1.A.-.4.E.5.D.-.8.A.0.E.-.3.6.8.7.5.A.C.D.6.1.B.8.}... 9223372036854776452 {c99679ae-a6f8-497d-b6eb-94821d27a44d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440038004600430033003500310035002d0043003200360037002d0034003700420042002d0039003700430045002d003900330032003000300035003800300043003600330045007d000000 {.D.8.F.C.3.5.1.5.-.C.2.6.7.-.4.7.B.B.-.9.7.C.E.-.9.3.2.0.0.5.8.0.C.6.3.E.}... 9223372036854776453 {221b7254-8807-42ab-bfa5-0cd2dbd0f0d5} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440046003100340037003300440037002d0030003800330033002d0034003500300042002d0039003900310041002d003100420039003800380044003300300038004400360039007d000000 {.D.F.1.4.7.3.D.7.-.0.8.3.3.-.4.5.0.B.-.9.9.1.A.-.1.B.9.8.8.D.3.0.8.D.6.9.}... 9223372036854776454 {0f24b893-b964-4d88-8875-d462390251e5} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450038003900360034004100340044002d0045003800380046002d0034003400330043002d0042003600360046002d004300420037003000450039003700410036004200380038007d000000 {.E.8.9.6.4.A.4.D.-.E.8.8.F.-.4.4.3.C.-.B.6.6.F.-.C.B.7.0.E.9.7.A.6.B.8.8.}... 9223372036854776455 {c8e8b63f-05ac-4b2c-858a-d3b66b9bca94} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300038003400420039004500320036002d0046003900320046002d0034003100350039002d0042003700370043002d003000460046003900410032003600360046004400390041007d000000 {.0.8.4.B.9.E.2.6.-.F.9.2.F.-.4.1.5.9.-.B.7.7.C.-.0.F.F.9.A.2.6.6.F.D.9.A.}... 9223372036854776456 {934834f5-e0a5-441b-85c2-73fd9be9463f} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00370045004600430031003900320033002d0038003000460042002d0034003900440046002d0042003000430033002d004100350033004400430038003800450033004300430030007d000000 {.7.E.F.C.1.9.2.3.-.8.0.F.B.-.4.9.D.F.-.B.0.C.3.-.A.5.3.D.C.8.8.E.3.C.C.0.}... 9223372036854776457 {f0e85dc1-2a2a-4fc0-b501-a30cbac74a8e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450044004200330043004400310045002d0042004600370041002d0034003500440036002d0041003800390034002d003300340046003300360039003400370034003400380041007d000000 {.E.D.B.3.C.D.1.E.-.B.F.7.A.-.4.5.D.6.-.A.8.9.4.-.3.4.F.3.6.9.4.7.4.4.8.A.}... 9223372036854776458 {09a98b8a-115e-495c-836b-42b475630183} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00450030004400420042004100410031002d0043004200330035002d0034003100450037002d0039003900350043002d003300370041004200360043003500380035003400300038007d000000 {.E.0.D.B.B.A.A.1.-.C.B.3.5.-.4.1.E.7.-.9.9.5.C.-.3.7.A.B.6.C.5.8.5.4.0.8.}... 9223372036854776461 {400124d1-e3bc-4c21-84f1-9c2fdd660eaa} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00390031003000440039003100450031002d0032003900320038002d0034003600420032002d0038003600450046002d003600410045003100460032004400380045003200410043007d000000 {.9.1.0.D.9.1.E.1.-.2.9.2.8.-.4.6.B.2.-.8.6.E.F.-.6.A.E.1.F.2.D.8.E.2.A.C.}... 9223372036854776462 {f9f5ba6c-d936-4b6f-9503-aa2fc28d2bb8} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440046004400410045004600310031002d0046004100420043002d0034003400460031002d0039003400450036002d003300440043003600320035003800460038003500420036007d000000 {.D.F.D.A.E.F.1.1.-.F.A.B.C.-.4.4.F.1.-.9.4.E.6.-.3.D.C.6.2.5.8.F.8.5.B.6.}... 9223372036854776463 {fbb9b711-0218-4bd3-bc24-5f88cdd9fcec} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310034003400450038003900370032002d0030004500310030002d0034003600350033002d0038004400440032002d003600420031003400350038003000440046004600300046007d000000 {.1.4.4.E.8.9.7.2.-.0.E.1.0.-.4.6.5.3.-.8.D.D.2.-.6.B.1.4.5.8.0.D.F.F.0.F.}... 9223372036854776464 {60847319-40c2-4c6b-9b63-67b1fdef219d} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00300043003000420030003500410046002d0046003100450043002d0034003000310039002d0038004400340032002d004300350034004400350044004400340031004500420041007d000000 {.0.C.0.B.0.5.A.F.-.F.1.E.C.-.4.0.1.9.-.8.D.4.2.-.C.5.4.D.5.D.D.4.1.E.B.A.}... 9223372036854776467 {6b9eebdb-a4d1-451c-9830-de157ab09a0a} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00430045004300380041003000420044002d0042004500320041002d0034003900440034002d0038004200310044002d004400440032003900340035004300440039003300440046007d000000 {.C.E.C.8.A.0.B.D.-.B.E.2.A.-.4.9.D.4.-.8.B.1.D.-.D.D.2.9.4.5.C.D.9.3.D.F.}... 9223372036854776468 {3e440dea-e76d-48aa-b372-560e409b30f1} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 49005000730065006300200041006400640072006500730073002000490073006f006c006100740069006f006e00200066006f0072002000530065006300750072006500200043006f006e005300650063002000520075006c00650073000000 I.P.s.e.c. .A.d.d.r.e.s.s. .I.s.o.l.a.t.i.o.n. .f.o.r. .S.e.c.u.r.e. .C.o.n.S.e.c. .R.u.l.e.s... 9223372036854776481 {9d6ea685-ea31-45e1-b8da-a022482c5663} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 49005000730065006300200041006400640072006500730073002000490073006f006c006100740069006f006e00200066006f0072002000530065006300750072006500200043006f006e005300650063002000520075006c00650073000000 I.P.s.e.c. .A.d.d.r.e.s.s. .I.s.o.l.a.t.i.o.n. .f.o.r. .S.e.c.u.r.e. .C.o.n.S.e.c. .R.u.l.e.s... 9223372036854776482 {e6cbde25-ce3a-4f6c-ad20-2430fc3be2ec} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 49005000730065006300200041006400640072006500730073002000490073006f006c006100740069006f006e00200066006f0072002000530065006300750072006500200043006f006e005300650063002000520075006c00650073000000 I.P.s.e.c. .A.d.d.r.e.s.s. .I.s.o.l.a.t.i.o.n. .f.o.r. .S.e.c.u.r.e. .C.o.n.S.e.c. .R.u.l.e.s... 9223372036854776483 {d67aa0d4-e024-4a5e-b849-9b0a4411f2c7} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 49005000730065006300200041006400640072006500730073002000490073006f006c006100740069006f006e00200066006f0072002000530065006300750072006500200043006f006e005300650063002000520075006c00650073000000 I.P.s.e.c. .A.d.d.r.e.s.s. .I.s.o.l.a.t.i.o.n. .f.o.r. .S.e.c.u.r.e. .C.o.n.S.e.c. .R.u.l.e.s... 9223372036854776484 {e7d69b11-d4ea-4140-afc1-3bd5e7bc77d3} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 49005000730065006300200041006400640072006500730073002000490073006f006c006100740069006f006e00200066006f0072002000530065006300750072006500200043006f006e005300650063002000520075006c00650073000000 I.P.s.e.c. .A.d.d.r.e.s.s. .I.s.o.l.a.t.i.o.n. .f.o.r. .S.e.c.u.r.e. .C.o.n.S.e.c. .R.u.l.e.s... 9223372036854776485 {d4108881-9c33-48bd-9ba2-da4340a6d656} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 49005000730065006300200041006400640072006500730073002000490073006f006c006100740069006f006e00200066006f0072002000530065006300750072006500200043006f006e005300650063002000520075006c00650073000000 I.P.s.e.c. .A.d.d.r.e.s.s. .I.s.o.l.a.t.i.o.n. .f.o.r. .S.e.c.u.r.e. .C.o.n.S.e.c. .R.u.l.e.s... 9223372036854776486 {321945bf-7e0d-4d0b-ba60-4c35526d1059} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00370034003000390038003700450033002d0038004200460039002d0034003000330033002d0038004400330032002d004200440030003200340036003900430035003500360032007d000000 {.7.4.0.9.8.7.E.3.-.8.B.F.9.-.4.0.3.3.-.8.D.3.2.-.B.D.0.2.4.6.9.C.5.5.6.2.}... 9223372036854776489 {fd7430b7-c11f-4981-beb4-57061db06e47} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310033004400310037003200340045002d0043003100300041002d0034004200360044002d0042003000300039002d003900370033004600320032003600440043004400340046007d000000 {.1.3.D.1.7.2.4.E.-.C.1.0.A.-.4.B.6.D.-.B.0.0.9.-.9.7.3.F.2.2.6.D.C.D.4.F.}... 9223372036854776490 {3576462a-269e-4a46-9f90-72d5b7c9c10e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53006800690065006c006400650064002000490043004d00500020007600340020004f00750074000000 S.h.i.e.l.d.e.d. .I.C.M.P. .v.4. .O.u.t... 9223372036854776491 {5848fa86-7a5a-4193-9e0b-e6a3e69395f8} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53006800690065006c006400650064002000490043004d00500020007600360020004f00750074000000 S.h.i.e.l.d.e.d. .I.C.M.P. .v.6. .O.u.t... 9223372036854776492 {8ab03769-319b-4d2e-81dd-8edc91cbbb8c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53006800690065006c006400650064002000490043004d00500020007600340020004f00750074000000 S.h.i.e.l.d.e.d. .I.C.M.P. .v.4. .O.u.t... 9223372036854776493 {302e8ae9-db45-4f11-bf3d-20e0807f3804} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53006800690065006c006400650064002000490043004d00500020007600360020004f00750074000000 S.h.i.e.l.d.e.d. .I.C.M.P. .v.6. .O.u.t... 9223372036854776494 {aa4a4221-6f16-4b9c-9a07-b4b4ba6a6bf2} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53006800690065006c006400650064002000490043004d00500020007600340020004f00750074000000 S.h.i.e.l.d.e.d. .I.C.M.P. .v.4. .O.u.t... 9223372036854776495 {11a26e34-bc51-40d3-b421-822ad6efbb3e} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 53006800690065006c006400650064002000490043004d00500020007600360020004f00750074000000 S.h.i.e.l.d.e.d. .I.C.M.P. .v.6. .O.u.t... 9223372036854776496 {7b4bd8fd-5d1f-4b8c-9836-4a01a350e746} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00320033004300340039003900450031002d0043003700360042002d0034003700300035002d0041003700310031002d004400450041004400360032004300380033003800440033007d000000 {.2.3.C.4.9.9.E.1.-.C.7.6.B.-.4.7.0.5.-.A.7.1.1.-.D.E.A.D.6.2.C.8.3.8.D.3.}... 9223372036854776497 {ab88322b-f465-4441-9345-715826fcab31} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00420045003800300036004300350034002d0041003800410046002d0034003200330043002d0039004500350036002d004300350041003800300033004600450041003300310033007d000000 {.B.E.8.0.6.C.5.4.-.A.8.A.F.-.4.2.3.C.-.9.E.5.6.-.C.5.A.8.0.3.F.E.A.3.1.3.}... 9223372036854776498 {d6011340-9eba-4253-9ed0-a763c7ef7b6c} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00310042004100420031003400360035002d0034003000430045002d0034003500390039002d0039004500420032002d003200340035003600440030004600420041003300330043007d000000 {.1.B.A.B.1.4.6.5.-.4.0.C.E.-.4.5.9.9.-.9.E.B.2.-.2.4.5.6.D.0.F.B.A.3.3.C.}... 9223372036854776499 {6186f5b6-f4b7-414d-ba20-a82103204847} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00440031003000440041003000340039002d0041004500340035002d0034003200370032002d0039003900450032002d004600370046003800350035003200450046003700420030007d000000 {.D.1.0.D.A.0.4.9.-.A.E.4.5.-.4.2.7.2.-.9.9.E.2.-.F.7.F.8.5.5.2.E.F.7.B.0.}... 9223372036854776500 {c4f20cf5-f405-49a7-b6a4-97b66f9cf587} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00420041004500420032003100440043002d0039003100330043002d0034004100300037002d0039004400440042002d004400320034003400360041003400390041003200360036007d000000 {.B.A.E.B.2.1.D.C.-.9.1.3.C.-.4.A.0.7.-.9.D.D.B.-.D.2.4.4.6.A.4.9.A.2.6.6.}... 9223372036854776501 {ba22195c-728b-4815-be61-4a4f69743fe2} MPSSVC Stores filter origin FWPM_PROVIDER_MPSSVC_WF FWPM_GENERAL_CONTEXT 7b00330030003600350032004100450030002d0035003300320031002d0034003200390033002d0041004100430042002d003200440045003300300042004500360033003600340034007d000000 {.3.0.6.5.2.A.E.0.-.5.3.2.1.-.4.2.9.3.-.A.A.C.B.-.2.D.E.3.0.B.E.6.3.6.4.4.}... 9223372036854776502 FWPM_SUBLAYER_INSPECTION WFP Built-in Inspection Sublayer 0 FWPM_SUBLAYER_TEREDO WFP Built-in Edge Traversal Sublayer 1 FWPM_SUBLAYER_MPSSVC_WF WFP Built-in MPSSVC Windows Firewall Sublayer FWPM_PROVIDER_MPSSVC_WF 2 FWPM_SUBLAYER_MPSSVC_WSH WFP Built-in MPSSVC Windows Service Hardening Sublayer FWPM_PROVIDER_MPSSVC_WSH 3 FWPM_SUBLAYER_MPSSVC_QUARANTINE WFP Built-in MPSSVC Quarantine Sublayer FWPM_PROVIDER_MPSSVC_WSH 4 FWPM_SUBLAYER_MPSSVC_EDP WFP Built-in MPSSVC Enterprise Data Protection Sublayer FWPM_PROVIDER_MPSSVC_EDP 5 FWPM_SUBLAYER_MPSSVC_TENANT_RESTRICTIONS WFP Built-in MPSSVC Tenant Restrictions Sublayer FWPM_PROVIDER_MPSSVC_TENANT_RESTRICTIONS 6 FWPM_SUBLAYER_MPSSVC_APP_ISOLATION WFP Built-in MPSSVC App Isolation Sublayer FWPM_PROVIDER_MPSSVC_APP_ISOLATION 7 {7b6b11f6-cbb5-433c-ae06-6a4f0076e49e} Edge traversal Teredo Authorization Sublayer Edge traversal Teredo Authorization Sublayer 8 {3c1cd879-1b8c-4ab4-8f83-5ed129176ef3} windefend 4096 FWPM_SUBLAYER_TCP_CHIMNEY_OFFLOAD WFP Built-in TCP Chimney Offload Sublayer 8192 FWPM_SUBLAYER_TCP_TEMPLATES WFP Built-in TCP Templates Sublayer 8704 FWPM_SUBLAYER_IPSEC_SECURITY_REALM WFP Built-in IPsec Security Realm Sublayer 12288 FWPM_SUBLAYER_SECURE_SOCKET WFP Built-in Secure Socket Sublayer 16384 FWPM_SUBLAYER_LIPS WFP Built-in Legacy IPsec Sublayer 32767 FWPM_SUBLAYER_UNIVERSAL WFP Built-in Universal Sublayer 32768 FWPM_SUBLAYER_IPSEC_DOSP WFP Built-in IPsec DoS Protection Sublayer 34816 FWPM_SUBLAYER_IPSEC_FORWARD_OUTBOUND_TUNNEL WFP Built-in IPsec Forward Outbound Tunnel Sublayer 36864 FWPM_SUBLAYER_IPSEC_TUNNEL WFP Built-in IPsec Tunnel Sublayer 40960 {9ba30013-c84e-47e5-ac6e-1e1aed72fa69} Microsoft Corporation FWPM_SUBLAYER_FLAG_PERSISTENT {1bebc969-61a5-4732-a177-847a0817862a} 40961 FWPM_SUBLAYER_RPC_AUDIT RPC Built-in Audit Sublayer 49152 {a87fb472-fc68-4805-8559-c6ae774773e0} PortmasterSublayer The Portmaster sublayer holds all it's filters. 65535 `) ================================================ FILE: service/config.go ================================================ package service import ( "errors" "fmt" "os" "path/filepath" "runtime" "github.com/safing/jess" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/configure" "github.com/safing/portmaster/service/updates" ) type ServiceConfig struct { BinDir string DataDir string LogToStdout bool LogDir string LogLevel string BinariesIndexURLs []string IntelIndexURLs []string VerifyBinaryUpdates jess.TrustStore VerifyIntelUpdates jess.TrustStore } func (sc *ServiceConfig) Init() error { // Check directories. switch runtime.GOOS { case "windows": // Fall back to defaults. if sc.BinDir == "" { exeDir, err := getCurrentBinaryFolder() // Default: C:/Program Files/Portmaster if err != nil { return fmt.Errorf("derive bin dir from running exe: %w", err) } sc.BinDir = exeDir } if sc.DataDir == "" { sc.DataDir = filepath.FromSlash("$ProgramData/Portmaster") } if sc.LogDir == "" { sc.LogDir = filepath.Join(sc.DataDir, "logs") } case "linux": // Fall back to defaults. if sc.BinDir == "" { sc.BinDir = "/usr/lib/portmaster" } if sc.DataDir == "" { sc.DataDir = "/var/lib/portmaster" } if sc.LogDir == "" { sc.LogDir = "/var/log/portmaster" } default: // Fail if not configured on other platforms. if sc.BinDir == "" { return errors.New("binary directory must be configured - auto-detection not supported on this platform") } if sc.DataDir == "" { return errors.New("binary directory must be configured - auto-detection not supported on this platform") } if !sc.LogToStdout && sc.LogDir == "" { return errors.New("logging directory must be configured - auto-detection not supported on this platform") } } // Expand path variables. sc.BinDir = os.ExpandEnv(sc.BinDir) sc.DataDir = os.ExpandEnv(sc.DataDir) sc.LogDir = os.ExpandEnv(sc.LogDir) // Apply defaults for required fields. if len(sc.BinariesIndexURLs) == 0 { sc.BinariesIndexURLs = configure.DefaultStableBinaryIndexURLs } if len(sc.IntelIndexURLs) == 0 { sc.IntelIndexURLs = configure.DefaultIntelIndexURLs } // Check log level. if sc.LogLevel != "" && log.ParseLevel(sc.LogLevel) == 0 { return fmt.Errorf("invalid log level %q", sc.LogLevel) } return nil } // returns the absolute path of the currently running executable func getCurrentBinaryPath() (string, error) { // Get the path of the currently running executable exePath, err := os.Executable() if err != nil { return "", fmt.Errorf("failed to get executable path: %w", err) } // Get the absolute path absPath, err := filepath.Abs(exePath) if err != nil { return "", fmt.Errorf("failed to get absolute path: %w", err) } return absPath, nil } func getCurrentBinaryFolder() (string, error) { // Get the absolute path of the currently running executable absPath, err := getCurrentBinaryPath() if err != nil { return "", err } // Get the directory of the executable installDir := filepath.Dir(absPath) return installDir, nil } func MakeUpdateConfigs(svcCfg *ServiceConfig) (binaryUpdateConfig, intelUpdateConfig *updates.Config, err error) { switch runtime.GOOS { case "windows": binaryUpdateConfig = &updates.Config{ Name: configure.DefaultBinaryIndexName, Directory: svcCfg.BinDir, DownloadDirectory: filepath.Join(svcCfg.DataDir, "download_binaries"), PurgeDirectory: filepath.Join(svcCfg.BinDir, "upgrade_obsolete_binaries"), Ignore: []string{"uninstall.exe"}, // "databases", "intel" and "config.json" not needed here since they are not in the bin dir. IndexURLs: svcCfg.BinariesIndexURLs, // May be changed by config during instance startup. IndexFile: "index.json", Verify: svcCfg.VerifyBinaryUpdates, AutoCheck: true, // May be changed by config during instance startup. AutoDownload: false, AutoApply: false, NeedsRestart: true, Notify: true, } intelUpdateConfig = &updates.Config{ Name: configure.DefaultIntelIndexName, Directory: filepath.Join(svcCfg.DataDir, "intel"), DownloadDirectory: filepath.Join(svcCfg.DataDir, "download_intel"), PurgeDirectory: filepath.Join(svcCfg.DataDir, "upgrade_obsolete_intel"), IndexURLs: svcCfg.IntelIndexURLs, IndexFile: "index.json", Verify: svcCfg.VerifyIntelUpdates, AutoCheck: true, // May be changed by config during instance startup. AutoDownload: true, AutoApply: true, NeedsRestart: false, Notify: false, } case "linux": binaryUpdateConfig = &updates.Config{ Name: configure.DefaultBinaryIndexName, Directory: svcCfg.BinDir, DownloadDirectory: filepath.Join(svcCfg.DataDir, "download_binaries"), PurgeDirectory: filepath.Join(svcCfg.DataDir, "upgrade_obsolete_binaries"), Ignore: []string{}, // "databases", "intel" and "config.json" not needed here since they are not in the bin dir. IndexURLs: svcCfg.BinariesIndexURLs, // May be changed by config during instance startup. IndexFile: "index.json", Verify: svcCfg.VerifyBinaryUpdates, AutoCheck: true, // May be changed by config during instance startup. AutoDownload: false, AutoApply: false, NeedsRestart: true, Notify: true, } if binPath, err := getCurrentBinaryPath(); err == nil { binaryUpdateConfig.PostUpgradeCommands = []updates.UpdateCommandConfig{ // Restore SELinux context for the new core binary after upgrade // (`restorecon /usr/lib/portmaster/portmaster-core`) { Command: "restorecon", Args: []string{binPath}, TriggerArtifactFName: binPath, FailOnError: false, // Ignore error: 'restorecon' may not be available on a non-SELinux systems. }, } } else { return nil, nil, fmt.Errorf("failed to get current binary path: %w", err) } intelUpdateConfig = &updates.Config{ Name: configure.DefaultIntelIndexName, Directory: filepath.Join(svcCfg.DataDir, "intel"), DownloadDirectory: filepath.Join(svcCfg.DataDir, "download_intel"), PurgeDirectory: filepath.Join(svcCfg.DataDir, "upgrade_obsolete_intel"), IndexURLs: svcCfg.IntelIndexURLs, IndexFile: "index.json", Verify: svcCfg.VerifyIntelUpdates, AutoCheck: true, // May be changed by config during instance startup. AutoDownload: true, AutoApply: true, NeedsRestart: false, Notify: false, } } return } ================================================ FILE: service/configure/updates.go ================================================ package configure import ( "github.com/safing/jess" ) var ( DefaultBinaryIndexName = "Portmaster Binaries" DefaultIntelIndexName = "Portmaster Intel" DefaultStableBinaryIndexURLs = []string{ "https://updates.safing.io/stable.v3.json", } DefaultBetaBinaryIndexURLs = []string{ "https://updates.safing.io/beta.v3.json", } DefaultStagingBinaryIndexURLs = []string{ "https://updates.safing.io/staging.v3.json", } DefaultSupportBinaryIndexURLs = []string{ "https://updates.safing.io/support.v3.json", } DefaultIntelIndexURLs = []string{ "https://updates.safing.io/intel.v3.json", } // BinarySigningKeys holds the signing keys in text format. BinarySigningKeys = []string{ // Safing Code Signing Key #1 "recipient:public-ed25519-key:safing-code-signing-key-1:92bgBLneQUWrhYLPpBDjqHbpFPuNVCPAaivQ951A4aq72HcTiw7R1QmPJwFM1mdePAvEVDjkeb8S4fp2pmRCsRa8HrCvWQEjd88rfZ6TznJMfY4g7P8ioGFjfpyx2ZJ8WCZJG5Qt4Z9nkabhxo2Nbi3iywBTYDLSbP5CXqi7jryW7BufWWuaRVufFFzhwUC2ryWFWMdkUmsAZcvXwde4KLN9FrkWAy61fGaJ8GCwGnGCSitANnU2cQrsGBXZzxmzxwrYD", // Safing Code Signing Key #2 "recipient:public-ed25519-key:safing-code-signing-key-2:92bgBLneQUWrhYLPpBDjqHbPC2d1o5JMyZFdavWBNVtdvbPfzDewLW95ScXfYPHd3QvWHSWCtB4xpthaYWxSkK1kYiGp68DPa2HaU8yQ5dZhaAUuV4Kzv42pJcWkCeVnBYqgGBXobuz52rFqhDJy3rz7soXEmYhJEJWwLwMeioK3VzN3QmGSYXXjosHMMNC76rjufSoLNtUQUWZDSnHmqbuxbKMCCsjFXUGGhtZVyb7bnu7QLTLk6SKHBJDMB6zdL9sw3", } // BinarySigningTrustStore is an in-memory trust store with the signing keys. BinarySigningTrustStore = jess.NewMemTrustStore() ) func init() { for _, signingKey := range BinarySigningKeys { rcpt, err := jess.RecipientFromTextFormat(signingKey) if err != nil { panic(err) } err = BinarySigningTrustStore.StoreSignet(rcpt) if err != nil { panic(err) } } } // GetBinaryUpdateURLs returns the correct binary update URLs for the given release channel. // Silently falls back to stable if release channel is invalid. func GetBinaryUpdateURLs(releaseChannel string) []string { switch releaseChannel { case "stable": return DefaultStableBinaryIndexURLs case "beta": return DefaultBetaBinaryIndexURLs case "staging": return DefaultStagingBinaryIndexURLs case "support": return DefaultSupportBinaryIndexURLs default: return DefaultStableBinaryIndexURLs } } ================================================ FILE: service/control/api.go ================================================ package control import ( "encoding/json" "fmt" "net/http" "time" "github.com/safing/portmaster/base/api" ) const ( APIEndpointPause = "control/pause" APIEndpointResume = "control/resume" ) type pauseRequestParams struct { Duration int `json:"duration"` // Duration in seconds OnlySPN bool `json:"onlySPN"` // Whether to pause only the SPN service } func (c *Control) registerAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Path: APIEndpointPause, Write: api.PermitAdmin, ActionFunc: c.handlePause, Name: "Pause Portmaster", Description: "Pause the Portmaster Core Service.", Parameters: []api.Parameter{ { Method: http.MethodPost, Field: "duration", Description: "Specify the duration to pause the service in seconds.", }, { Method: http.MethodPost, Field: "onlySPN", Value: "false", Description: "Specify whether to pause only the SPN service.", }}, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: APIEndpointResume, Write: api.PermitAdmin, ActionFunc: c.handleResume, Name: "Resume Portmaster", Description: "Resume the Portmaster Core Service.", }); err != nil { return err } return nil } func (c *Control) handlePause(r *api.Request) (msg string, err error) { params := pauseRequestParams{} if r.InputData != nil { if err := json.Unmarshal(r.InputData, ¶ms); err != nil { return "Bad Request: invalid input data", err } } if params.OnlySPN { c.mgr.Info(fmt.Sprintf("Received SPN Pause(%v) action request ", params.Duration)) } else { c.mgr.Info(fmt.Sprintf("Received Pause(%v) action request ", params.Duration)) } if err := c.pause(time.Duration(params.Duration)*time.Second, params.OnlySPN); err != nil { return "Failed to pause", err } return "Pause initiated", nil } func (c *Control) handleResume(_ *api.Request) (msg string, err error) { c.mgr.Info("Received Resume action request") if err := c.resume(); err != nil { return "Failed to resume", err } return "Resume initiated", nil } ================================================ FILE: service/control/module.go ================================================ package control import ( "fmt" "sync" "sync/atomic" "time" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/mgr" ) type PauseInfo struct { Interception bool // Whether Portmaster interception is paused SPN bool // Whether SPN is paused TillTime time.Time // When the pause will end } type Control struct { mgr *mgr.Manager instance instance states *mgr.StateMgr locker sync.Mutex resumeWorker *mgr.WorkerMgr pauseNotification *notifications.Notification pauseInfo PauseInfo cfgSpnEnabled config.BoolOption } type instance interface { Config() *config.Config InterceptionGroup() *mgr.GroupModule SPNGroup() *mgr.ExtendedGroup IsShuttingDown() bool } var ( singleton atomic.Bool ) func New(instance instance) (*Control, error) { if !singleton.CompareAndSwap(false, true) { return nil, fmt.Errorf("control: New failed: instance already created") } m := mgr.New("Control") module := &Control{ mgr: m, instance: instance, states: mgr.NewStateMgr(m), cfgSpnEnabled: config.GetAsBool("spn/enable", false), } if err := module.prep(); err != nil { return nil, err } return module, nil } func (c *Control) Manager() *mgr.Manager { return c.mgr } func (u *Control) States() *mgr.StateMgr { return u.states } func (c *Control) Start() error { return nil } func (c *Control) Stop() error { c.locker.Lock() defer c.locker.Unlock() c.stopResumeWorker() return nil } func (c *Control) prep() error { return c.registerAPIEndpoints() } ================================================ FILE: service/control/pause.go ================================================ package control import ( "errors" "fmt" "time" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/mgr" ) func (c *Control) pause(duration time.Duration, onlySPN bool) (retErr error) { if c.instance.IsShuttingDown() { c.mgr.Debug("Cannot pause: system is shutting down") return nil } c.locker.Lock() defer c.locker.Unlock() defer func() { // update states after pause attempt c.updateStatesAndNotify() // log error if pause failed if retErr != nil { c.mgr.Error("Failed to pause: " + retErr.Error()) } }() if duration <= 0 { return errors.New("invalid pause duration") } if onlySPN { if c.pauseInfo.Interception { return errors.New("cannot pause SPN separately when core is paused") } // If SPN is not running and not already paused, cannot pause it or change pause duration. if !c.cfgSpnEnabled() && !c.pauseInfo.SPN { return errors.New("cannot pause SPN when it is not running") } } // Stop resume worker if running and start a new one later. c.stopResumeWorker() defer func() { if retErr == nil { // start new resume worker (with new duration) if no error c.startResumeWorker(duration) } }() // Pause SPN if not already paused. if !c.pauseInfo.SPN { if c.cfgSpnEnabled() { // "spn/access" module is responsible for starting/stopping SPN service. // Here we just change the config to notify it to stop SPN. // TODO: the 'pause' state must not make permanent config changes. // Consider possibility to not store permanent config changes. // E.g. SPN enabled -> pause SPN -> restart PC/Portmaster -> SPN should be enabled again. config.SetConfigOption("spn/enable", false) // Wait until SPN is fully stopped with timeout 30s. err := c.waitSPNStopped(time.Second * 30) if err != nil { config.SetConfigOption("spn/enable", true) // revert config change on error return err } c.mgr.Info("SPN paused") c.pauseInfo.SPN = true } } if onlySPN { return nil } if !c.pauseInfo.Interception { if err := c.instance.InterceptionGroup().Stop(); err != nil { return err } c.mgr.Info("interception paused") c.pauseInfo.Interception = true } return nil } func (c *Control) resume() (retErr error) { if c.instance.IsShuttingDown() { c.mgr.Debug("Cannot resume: system is shutting down") return nil } c.locker.Lock() defer c.locker.Unlock() defer func() { if retErr != nil { c.updateStatesAndNotifyError("Resume operation failed", retErr) c.mgr.Error("Error occurred while resuming: " + retErr.Error()) } else { c.updateStatesAndNotify() } }() c.stopResumeWorker() if c.pauseInfo.Interception { if err := c.instance.InterceptionGroup().Start(); err != nil { return err } c.mgr.Info("interception resumed") c.pauseInfo.Interception = false } if c.pauseInfo.SPN { // "spn/access" module is responsible for starting/stopping SPN service. // Here we just change the config to notify it to start SPN. if !c.cfgSpnEnabled() { config.SetConfigOption("spn/enable", true) c.mgr.Info("SPN resumed") } c.pauseInfo.SPN = false } return nil } // stopResumeWorker stops any existing resume worker. // No thread safety, caller must hold c.locker. func (c *Control) stopResumeWorker() { c.pauseInfo.TillTime = time.Time{} if c.resumeWorker != nil { c.resumeWorker.Stop() c.resumeWorker = nil } } // startResumeWorker starts a worker that will resume normal operation after the specified duration. // No thread safety, caller must hold c.locker. func (c *Control) startResumeWorker(duration time.Duration) { // Strip monotonic clock from deadline to force wall-clock comparison. // This is critical for VM suspend/resume and system sleep scenarios. deadline := time.Now().Round(0).Add(duration) c.pauseInfo.TillTime = deadline resumerWorkerFunc := func(wc *mgr.WorkerCtx) error { wc.Info(fmt.Sprintf("Scheduling resume in %v", duration)) // Subscribe to config changes to detect SPN enable. cfgChangeEvt := c.instance.Config().EventConfigChange.Subscribe("control: spn enable check", 10) defer cfgChangeEvt.Cancel() // Timer for the deadline. timer := time.NewTimer(time.Until(deadline)) defer timer.Stop() // Periodically check resume time to handle unexpected wall-clock changes. ticker := time.NewTicker(15 * time.Second) defer ticker.Stop() // Wait until duration elapses or SPN is enabled by user. needToAutoResume := false for !needToAutoResume { select { case <-wc.Ctx().Done(): return nil case <-cfgChangeEvt.Events(): if c.cfgSpnEnabled() { cfgChangeEvt.Cancel() // we do not need it anymore (no problem to cancel multiple times) wc.Info("SPN enabled by user. Auto-resume initiated.") needToAutoResume = true } case <-ticker.C: // Check wall-clock time. if time.Now().Round(0).After(deadline) { needToAutoResume = true } case <-timer.C: needToAutoResume = true } } // Time to resume wc.Info("Resuming...") err := c.resume() if err == nil { n := ¬ifications.Notification{ EventID: "control:resumed", Type: notifications.Info, Title: "Resumed", Message: "Automatically resumed from pause state", ShowOnSystem: true, Expires: time.Now().Add(15 * time.Second).Unix(), AvailableActions: []*notifications.Action{ { ID: "ack", Text: "OK", }, }, } notifications.Notify(n) } return err } c.resumeWorker = c.mgr.NewWorkerMgr("resumer", resumerWorkerFunc, nil) c.resumeWorker.Go() } // updateStatesAndNotify updates the paused states and sends notifications accordingly. // No thread safety, caller must hold c.locker. func (c *Control) updateStatesAndNotify() { if !c.pauseInfo.Interception && !c.pauseInfo.SPN { if c.pauseNotification != nil { c.pauseNotification.Delete() c.pauseNotification = nil } return } title := "" nType := notifications.Warning if c.pauseInfo.Interception && c.pauseInfo.SPN { title = "Portmaster and SPN paused" } else if c.pauseInfo.Interception { title = "Portmaster paused" } else if c.pauseInfo.SPN { title = "SPN paused" nType = notifications.Info // less severe notification for SPN-only pause } message := fmt.Sprintf("%s until %v", title, c.pauseInfo.TillTime.Format(time.TimeOnly)) c.pauseNotification = ¬ifications.Notification{ EventID: "control:paused", Type: nType, Title: title, Message: message, ShowOnSystem: false, // TODO: Before enabling, ensure that UI client (Tauri implementation) supports ActionTypeWebhook. EventData: &c.pauseInfo, AvailableActions: []*notifications.Action{ { Text: "Resume", Type: notifications.ActionTypeWebhook, Payload: ¬ifications.ActionTypeWebhookPayload{ URL: APIEndpointResume, ResultAction: "display", }, }, }, } notifications.Notify(c.pauseNotification) c.pauseNotification.SyncWithState(c.states) } // updateStatesAndNotifyError updates the paused states and sends an error notification. // No thread safety, caller must hold c.locker. func (c *Control) updateStatesAndNotifyError(errDescription string, err error) { if err == nil { return } if errDescription == "" { errDescription = "Error" } // Error notification c.pauseNotification = ¬ifications.Notification{ EventID: "control:error", Type: notifications.Error, Title: errDescription, Message: err.Error(), EventData: &c.pauseInfo, } notifications.Notify(c.pauseNotification) c.pauseNotification.SyncWithState(c.states) } func (c *Control) showNotification(title, message string) *notifications.Notification { n := ¬ifications.Notification{ EventID: "control:status_info", Type: notifications.Info, Title: title, Message: message, } notifications.Notify(n) return n } func (c *Control) waitSPNStopped(stopTimeout time.Duration) error { var notification *notifications.Notification defer func() { if notification != nil { notification.Delete() } }() startTime := time.Now() isStopped, _ := c.instance.SPNGroup().IsStopped() for !isStopped { var err error time.Sleep(200 * time.Millisecond) if c.mgr.IsDone() || c.instance.IsShuttingDown() { return errors.New("shutting down") } isStopped, err = c.instance.SPNGroup().IsStopped() if err != nil { return fmt.Errorf("failed to stop SPN: %w", err) } if time.Since(startTime) > stopTimeout { return errors.New("timeout waiting for SPN to stop") } if notification == nil && time.Since(startTime) > time.Second { notification = c.showNotification("Waiting for SPN to stop...", "") } if c.cfgSpnEnabled() { return errors.New("SPN enabled again") } } return nil } ================================================ FILE: service/core/api.go ================================================ package core import ( "bytes" "context" "encoding/hex" "errors" "fmt" "io" "net/http" "net/url" "os" "path/filepath" "strings" "time" "github.com/ghodss/yaml" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/base/utils/debug" "github.com/safing/portmaster/service/compat" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/resolver" "github.com/safing/portmaster/service/status" "github.com/safing/portmaster/spn/captain" ) var errInvalidReadPermission = errors.New("invalid read permission") func registerAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Path: "core/shutdown", Write: api.PermitSelf, // Do NOT register as belonging to the module, so that the API is available // when something fails during starting of this module or a dependency. ActionFunc: shutdown, Name: "Shut Down Portmaster", Description: "Shut down the Portmaster Core Service and all UI components.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: "core/restart", Write: api.PermitAdmin, // Do NOT register as belonging to the module, so that the API is available // when something fails during starting of this module or a dependency. ActionFunc: restart, Name: "Restart Portmaster", Description: "Restart the Portmaster Core Service.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: "debug/core", Read: api.PermitAnyone, DataFunc: debugInfo, Name: "Get Debug Information", Description: "Returns network debugging information, similar to debug/info, but with system status data.", Parameters: []api.Parameter{{ Method: http.MethodGet, Field: "style", Value: "github", Description: "Specify the formatting style. The default is simple markdown formatting.", }}, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: "app/auth", Read: api.PermitAnyone, StructFunc: authorizeApp, Name: "Request an authentication token with a given set of permissions. The user will be prompted to either authorize or deny the request. Used for external or third-party tool integrations.", Parameters: []api.Parameter{ { Method: http.MethodGet, Field: "app-name", Description: "The name of the application requesting access", }, { Method: http.MethodGet, Field: "read", Description: "The requested read permission", }, { Method: http.MethodGet, Field: "write", Description: "The requested write permission", }, { Method: http.MethodGet, Field: "ttl", Description: "The time-to-live for the new access token. Defaults to 24h", }, }, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: "app/profile", Read: api.PermitUser, StructFunc: getMyProfile, Name: "Get the ID of the calling profile", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: "updates/check", WriteMethod: "POST", Write: api.PermitUser, ActionFunc: func(ar *api.Request) (string, error) { module.instance.BinaryUpdates().TriggerUpdateCheck() module.instance.IntelUpdates().TriggerUpdateCheck() return "update check triggered", nil }, Name: "Trigger updates check event", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: "updates/apply", WriteMethod: "POST", Write: api.PermitUser, ActionFunc: func(ar *api.Request) (string, error) { module.instance.BinaryUpdates().TriggerApplyUpdates() module.instance.IntelUpdates().TriggerApplyUpdates() return "upgrade triggered", nil }, Name: "Trigger updates apply event", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: "updates/from-url", WriteMethod: "POST", Write: api.PermitAnyone, ActionFunc: func(ar *api.Request) (string, error) { err := module.instance.BinaryUpdates().UpdateFromURL(string(ar.InputData)) if err != nil { return err.Error(), err } return "upgrade triggered", nil }, Name: "Replace current version from the version supplied in the URL", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Get Resource", Description: "Returns the requested resource from the update system", Path: `updates/get/{artifact_path:[A-Za-z0-9/\.\-_]{1,255}}/{artifact_name:[A-Za-z0-9\.\-_]{1,255}}`, Read: api.PermitUser, ReadMethod: http.MethodGet, HandlerFunc: getUpdateResource, }); err != nil { return err } return nil } // shutdown shuts the Portmaster down. func shutdown(_ *api.Request) (msg string, err error) { log.Warning("core: user requested shutdown via action") module.instance.Shutdown() return "shutdown initiated", nil } // restart restarts the Portmaster. func restart(_ *api.Request) (msg string, err error) { log.Info("core: user requested restart via action") // Trigger restart module.instance.Restart() return "restart initiated", nil } func getUpdateResource(w http.ResponseWriter, r *http.Request) { // Get identifier from URL. var identifier string if ar := api.GetAPIRequest(r); ar != nil { identifier = ar.URLVars["artifact_name"] } if identifier == "" { http.Error(w, "no resource specified", http.StatusBadRequest) return } // Get resource. artifact, err := module.instance.BinaryUpdates().GetFile(identifier) if err != nil { intelArtifact, intelErr := module.instance.IntelUpdates().GetFile(identifier) if intelErr == nil { artifact = intelArtifact } else { http.Error(w, err.Error(), http.StatusNotFound) return } } // Open file for reading. file, err := os.Open(artifact.Path()) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer file.Close() //nolint:errcheck,gosec // Assign file to reader var reader io.Reader = file // Add version and hash to header. if artifact.Version != "" { w.Header().Set("Resource-Version", artifact.Version) } if artifact.SHA256 != "" { w.Header().Set("Resource-SHA256", artifact.SHA256) } // Set Content-Type. contentType, _ := utils.MimeTypeByExtension(filepath.Ext(artifact.Path())) w.Header().Set("Content-Type", contentType) // Check if the content type may be returned. accept := r.Header.Get("Accept") if accept != "" { mimeTypes := strings.Split(accept, ",") // First, clean mime types. for i, mimeType := range mimeTypes { mimeType = strings.TrimSpace(mimeType) mimeType, _, _ = strings.Cut(mimeType, ";") mimeTypes[i] = mimeType } // Second, check if we may return anything. var acceptsAny bool for _, mimeType := range mimeTypes { switch mimeType { case "*", "*/*": acceptsAny = true } } // Third, check if we can convert. if !acceptsAny { var converted bool sourceType, _, _ := strings.Cut(contentType, ";") findConvertiblePair: for _, mimeType := range mimeTypes { switch { case sourceType == "application/yaml" && mimeType == "application/json": yamlData, err := io.ReadAll(reader) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } jsonData, err := yaml.YAMLToJSON(yamlData) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } reader = bytes.NewReader(jsonData) converted = true break findConvertiblePair } } // If we could not convert to acceptable format, return an error. if !converted { http.Error(w, "conversion to requested format not supported", http.StatusNotAcceptable) return } } } // Write file. w.WriteHeader(http.StatusOK) if r.Method != http.MethodHead { _, err = io.Copy(w, reader) if err != nil { log.Errorf("updates: failed to serve resource file: %s", err) return } } } // debugInfo returns the debugging information for support requests. func debugInfo(ar *api.Request) (data []byte, err error) { // Create debug information helper. di := new(debug.Info) di.Style = ar.Request.URL.Query().Get("style") // Add debug information. // Very basic information at the start. di.AddVersionInfo() di.AddPlatformInfo(ar.Context()) // Unexpected logs. di.AddLastUnexpectedLogs() // Status Information from various modules. status.AddToDebugInfo(di) captain.AddToDebugInfo(di) resolver.AddToDebugInfo(di) config.AddToDebugInfo(di) // Detailed information. AddVersionsToDebugInfo(di) compat.AddToDebugInfo(di) module.instance.AddWorkerInfoToDebugInfo(di) di.AddGoroutineStack() // Return data. return di.Bytes(), nil } // getSavePermission returns the requested api.Permission from p. // It only allows "user" and "admin" as external processes should // never be able to request "self". func getSavePermission(p string) api.Permission { switch p { case "user": return api.PermitUser case "admin": return api.PermitAdmin default: return api.NotSupported } } func getMyProfile(ar *api.Request) (interface{}, error) { proc, err := process.GetProcessByRequestOrigin(ar) if err != nil { return nil, err } localProfile := proc.Profile().LocalProfile() return map[string]interface{}{ "profile": localProfile.ID, "source": localProfile.Source, "name": localProfile.Name, }, nil } func authorizeApp(ar *api.Request) (interface{}, error) { appName := ar.Request.URL.Query().Get("app-name") readPermStr := ar.Request.URL.Query().Get("read") writePermStr := ar.Request.URL.Query().Get("write") ttl := time.Hour * 24 if ttlStr := ar.Request.URL.Query().Get("ttl"); ttlStr != "" { var err error ttl, err = time.ParseDuration(ttlStr) if err != nil { return nil, err } } // convert the requested read and write permissions to their api.Permission // value. This ensures only "user" or "admin" permissions can be requested. if getSavePermission(readPermStr) <= api.NotSupported { return nil, errInvalidReadPermission } if getSavePermission(writePermStr) <= api.NotSupported { return nil, errInvalidReadPermission } proc, err := process.GetProcessByRequestOrigin(ar) if err != nil { return nil, fmt.Errorf("failed to identify requesting process: %w", err) } n := notifications.Notification{ Type: notifications.Prompt, EventID: "core:authorize-app-" + time.Now().String(), Title: "An app requests access to the Portmaster", Message: "Allow " + appName + " (" + proc.Profile().LocalProfile().Name + ") to query and modify the Portmaster?\n\nBinary: " + proc.Path, ShowOnSystem: true, Expires: time.Now().Add(time.Minute).Unix(), AvailableActions: []*notifications.Action{ { ID: "allow", Text: "Authorize", }, { ID: "deny", Text: "Deny", }, }, } ch := make(chan string) validUntil := time.Now().Add(ttl) n.SetActionFunction(func(ctx context.Context, n *notifications.Notification) error { n.Lock() defer n.Unlock() if n.SelectedActionID != "allow" { close(ch) return nil } keys := config.Concurrent.GetAsStringArray(api.CfgAPIKeys, []string{})() newKeyData, err := rng.Bytes(8) if err != nil { return err } newKeyHex := hex.EncodeToString(newKeyData) query := url.Values{ "read": []string{readPermStr}, "write": []string{writePermStr}, "expires": []string{validUntil.Format(time.RFC3339)}, } keys = append(keys, fmt.Sprintf("%s?%s", newKeyHex, query.Encode())) if err := config.SetConfigOption(api.CfgAPIKeys, keys); err != nil { return err } ch <- newKeyHex return nil }) n.Save() select { case key := <-ch: if len(key) == 0 { return nil, errors.New("access denied") } return map[string]interface{}{ "key": key, "validUntil": validUntil, }, nil case <-ar.Context().Done(): return nil, errors.New("timeout") } } ================================================ FILE: service/core/base/databases.go ================================================ package base import ( "path/filepath" "github.com/safing/portmaster/base/database" _ "github.com/safing/portmaster/base/database/storage/bbolt" _ "github.com/safing/portmaster/base/database/storage/sqlite" "github.com/safing/portmaster/base/utils" ) // Default Values (changeable for testing). var ( DefaultDatabaseStorageType = "sqlite" ) func registerDatabases() error { // If there is an existing bbolt core database, use it instead. coreStorageType := DefaultDatabaseStorageType if utils.PathExists(filepath.Join(module.instance.DataDir(), "databases", "core", "bbolt")) { coreStorageType = "bbolt" } // Register core database. _, err := database.Register(&database.Database{ Name: "core", Description: "Holds core data, such as settings and profiles", StorageType: coreStorageType, }) if err != nil { return err } // If there is an existing cache bbolt database, use it instead. cacheStorageType := DefaultDatabaseStorageType if utils.PathExists(filepath.Join(module.instance.DataDir(), "databases", "cache", "bbolt")) { cacheStorageType = "bbolt" } // Register cache database. _, err = database.Register(&database.Database{ Name: "cache", Description: "Cached data, such as Intelligence and DNS Records", StorageType: cacheStorageType, }) if err != nil { return err } return nil } ================================================ FILE: service/core/base/logs.go ================================================ package base import ( "errors" "os" "path/filepath" "strings" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" ) const ( logTTL = 30 * 24 * time.Hour logFileDir = "logs" logFileSuffix = ".log" ) func registerLogCleaner() { _ = module.mgr.Delay("log cleaner", 15*time.Minute, logCleaner).Repeat(24 * time.Hour) } func logCleaner(_ *mgr.WorkerCtx) error { ageThreshold := time.Now().Add(-logTTL) return filepath.Walk( filepath.Join(module.instance.DataDir(), logFileDir), func(path string, info os.FileInfo, err error) error { if err != nil { if !errors.Is(err, os.ErrNotExist) { log.Warningf("core: failed to access %s while deleting old log files: %s", path, err) } return nil } switch { case !info.Mode().IsRegular(): // Only delete regular files. case !strings.HasSuffix(path, logFileSuffix): // Only delete files that end with the correct suffix. case info.ModTime().After(ageThreshold): // Only delete files that are older that the log TTL. default: // Delete log file. err := os.Remove(path) if err != nil { log.Warningf("core: failed to delete old log file %s: %s", path, err) } else { log.Tracef("core: deleted old log file %s", path) } } return nil }) } ================================================ FILE: service/core/base/module.go ================================================ package base import ( "errors" "sync/atomic" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/service/mgr" ) // DefaultAPIListenAddress is the default listen address for the API. var DefaultAPIListenAddress = "127.0.0.1:817" // Base is the base module. type Base struct { mgr *mgr.Manager instance instance } // Manager returns the module manager. func (b *Base) Manager() *mgr.Manager { return b.mgr } // Start starts the module. func (b *Base) Start() error { startProfiling() // registerLogCleaner() return nil } // Stop stops the module. func (b *Base) Stop() error { return nil } var ( module *Base shimLoaded atomic.Bool ) // New returns a new Base module. func New(instance instance) (*Base, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Base") module = &Base{ mgr: m, instance: instance, } // Set api listen address. api.SetDefaultAPIListenAddress(DefaultAPIListenAddress) if err := registerDatabases(); err != nil { return nil, err } return module, nil } type instance interface { DataDir() string SetCmdLineOperation(f func() error) } ================================================ FILE: service/core/base/profiling.go ================================================ package base import ( "flag" "fmt" "os" "runtime/pprof" "github.com/safing/portmaster/service/mgr" ) var cpuProfile string func init() { flag.StringVar(&cpuProfile, "cpuprofile", "", "write cpu profile to `file`") } func startProfiling() { if cpuProfile != "" { module.mgr.Go("cpu profiler", cpuProfiler) } } func cpuProfiler(ctx *mgr.WorkerCtx) error { f, err := os.Create(cpuProfile) if err != nil { return fmt.Errorf("could not create CPU profile: %w", err) } if err := pprof.StartCPUProfile(f); err != nil { return fmt.Errorf("could not start CPU profile: %w", err) } // wait for shutdown <-ctx.Done() pprof.StopCPUProfile() err = f.Close() if err != nil { return fmt.Errorf("failed to close CPU profile file: %w", err) } return nil } ================================================ FILE: service/core/config.go ================================================ package core import ( "flag" locale "github.com/Xuanwo/go-locale" "golang.org/x/exp/slices" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" ) // Configuration Keys. var ( // CfgDevModeKey was previously defined here. CfgDevModeKey = config.CfgDevModeKey CfgNetworkServiceKey = "core/networkService" defaultNetworkServiceMode bool CfgLocaleKey = "core/locale" ) func init() { flag.BoolVar( &defaultNetworkServiceMode, "network-service", false, "set default network service mode; configuration is stronger", ) } func registerConfig() error { if err := config.Register(&config.Option{ Name: "Network Service", Key: CfgNetworkServiceKey, Description: "Use the Portmaster as a network service, where applicable. You will have to take care of lots of network setup yourself in order to run this properly and securely.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelExperimental, DefaultValue: defaultNetworkServiceMode, Annotations: config.Annotations{ config.DisplayOrderAnnotation: 513, config.CategoryAnnotation: "Network Service", }, }); err != nil { return err } if err := config.Register(&config.Option{ Name: "Time and Date Format", Key: CfgLocaleKey, Description: "Configures the time and date format for the user interface. Selection is an example and correct formatting in the UI is a continual work in progress.", OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelUser, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: getDefaultLocale(), PossibleValues: []config.PossibleValue{ { Name: "24h DD-MM-YYYY", Value: enGBLocale, }, { Name: "12h MM/DD/YYYY", Value: enUSLocale, }, }, Annotations: config.Annotations{ config.CategoryAnnotation: "User Interface", config.DisplayHintAnnotation: config.DisplayHintOneOf, config.RequiresUIReloadAnnotation: true, }, }); err != nil { return err } return nil } func getDefaultLocale() string { // Get locales from system. detectedLocales, err := locale.DetectAll() if err != nil { log.Warningf("core: failed to detect locale: %s", err) return enGBLocale } // log.Debugf("core: detected locales: %s", detectedLocales) // Check if there is a locale that corresponds to the en-US locale. for _, detectedLocale := range detectedLocales { if slices.Contains[[]string, string](defaultEnUSLocales, detectedLocale.String()) { return enUSLocale } } // Otherwise, return the en-GB locale as default. return enGBLocale } var ( enGBLocale = "en-GB" enUSLocale = "en-US" defaultEnUSLocales = []string{ "en-AS", // English (American Samoa) "en-GU", // English (Guam) "en-UM", // English (U.S. Minor Outlying Islands) "en-US", // English (United States) "en-VI", // English (U.S. Virgin Islands) } ) ================================================ FILE: service/core/core.go ================================================ package core import ( "errors" "fmt" "sync/atomic" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/metrics" "github.com/safing/portmaster/base/utils/debug" "github.com/safing/portmaster/service/mgr" _ "github.com/safing/portmaster/service/netenv" _ "github.com/safing/portmaster/service/netquery" _ "github.com/safing/portmaster/service/status" _ "github.com/safing/portmaster/service/sync" _ "github.com/safing/portmaster/service/ui" "github.com/safing/portmaster/service/updates" ) var db = database.NewInterface(&database.Options{ Local: true, Internal: true, }) // Core is the core service module. type Core struct { m *mgr.Manager instance instance EventShutdown *mgr.EventMgr[struct{}] EventRestart *mgr.EventMgr[struct{}] } // Manager returns the manager. func (c *Core) Manager() *mgr.Manager { return c.m } // Start starts the module. func (c *Core) Start() error { return start() } // Stop stops the module. func (c *Core) Stop() error { return nil } func prep() error { // init config if err := registerConfig(); err != nil { return err } if err := registerUpdateConfig(); err != nil { return err } if err := registerAPIEndpoints(); err != nil { return err } if err := initModulesIntegration(); err != nil { return err } return nil } func start() error { if err := startPlatformSpecific(); err != nil { return fmt.Errorf("failed to start plattform-specific components: %w", err) } // Setup update system. initUpdateConfig() initVersionExport() // Enable persistent metrics. if err := metrics.EnableMetricPersistence("core:metrics/storage"); err != nil { log.Warningf("core: failed to enable persisted metrics: %s", err) } return nil } var ( module *Core shimLoaded atomic.Bool ) // New returns a new NetEnv module. func New(instance instance) (*Core, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Core") module = &Core{ m: m, instance: instance, EventShutdown: mgr.NewEventMgr[struct{}]("shutdown", m), EventRestart: mgr.NewEventMgr[struct{}]("restart", m), } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface { Shutdown() Restart() AddWorkerInfoToDebugInfo(di *debug.Info) Config() *config.Config BinaryUpdates() *updates.Updater IntelUpdates() *updates.Updater } ================================================ FILE: service/core/events.go ================================================ package core import ( "fmt" "sync" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/runtime" "github.com/safing/portmaster/service/mgr" ) var modulesIntegrationUpdatePusher func(...record.Record) func initModulesIntegration() (err error) { modulesIntegrationUpdatePusher, err = runtime.Register("modules/", &ModulesIntegration{}) if err != nil { return err } // Push events via API. module.EventRestart.AddCallback("expose restart event", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { // Send event as runtime:modules/core/event/restart pushModuleEvent("core", "restart", false, nil) return false, nil }) module.EventShutdown.AddCallback("expose shutdown event", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { // Send event as runtime:modules/core/event/shutdown pushModuleEvent("core", "shutdown", false, nil) return false, nil }) return nil } // ModulesIntegration provides integration with the modules system. type ModulesIntegration struct{} // Set is called when the value is set from outside. // If the runtime value is considered read-only ErrReadOnly // should be returned. It is guaranteed that the key of // the record passed to Set is prefixed with the key used // to register the value provider. func (mi *ModulesIntegration) Set(record.Record) (record.Record, error) { return nil, runtime.ErrReadOnly } // Get should return one or more records that match keyOrPrefix. // keyOrPrefix is guaranteed to be at least the prefix used to // register the ValueProvider. func (mi *ModulesIntegration) Get(keyOrPrefix string) ([]record.Record, error) { return nil, database.ErrNotFound } type eventData struct { record.Base sync.Mutex Data interface{} } func pushModuleEvent(moduleName, eventName string, internal bool, data interface{}) { // Create event record and set key. eventRecord := &eventData{ Data: data, } eventRecord.SetKey(fmt.Sprintf( "runtime:modules/%s/event/%s", moduleName, eventName, )) eventRecord.UpdateMeta() if internal { eventRecord.Meta().MakeSecret() eventRecord.Meta().MakeCrownJewel() } // Push event to database subscriptions. modulesIntegrationUpdatePusher(eventRecord) } ================================================ FILE: service/core/os_default.go ================================================ //go:build !windows package core // only return on Fatal error! func startPlatformSpecific() error { return nil } ================================================ FILE: service/core/os_windows.go ================================================ package core import ( "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils/osdetail" ) // only return on Fatal error! func startPlatformSpecific() error { // We can't catch errors when calling WindowsNTVersion() in logging, so we call the function here, just to catch possible errors if _, err := osdetail.WindowsNTVersion(); err != nil { log.Errorf("failed to obtain WindowsNTVersion: %s", err) } return nil } ================================================ FILE: service/core/update_config.go ================================================ package core import ( "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/configure" "github.com/safing/portmaster/service/mgr" ) // Release Channel Configuration Keys. const ( ReleaseChannelKey = "core/releaseChannel" ReleaseChannelJSONKey = "core.releaseChannel" ) // Release Channels. const ( ReleaseChannelStable = "stable" ReleaseChannelBeta = "beta" ReleaseChannelStaging = "staging" ReleaseChannelSupport = "support" ) const ( enableSoftwareUpdatesKey = "core/automaticUpdates" enableIntelUpdatesKey = "core/automaticIntelUpdates" ) var ( releaseChannel config.StringOption enableSoftwareUpdates config.BoolOption enableIntelUpdates config.BoolOption initialReleaseChannel string ) func registerUpdateConfig() error { err := config.Register(&config.Option{ Name: "Release Channel", Key: ReleaseChannelKey, Description: `Use "Stable" for the best experience. The "Beta" channel will have the newest features and fixes, but may also break and cause interruption. Use others only temporarily and when instructed.`, OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, RequiresRestart: true, DefaultValue: ReleaseChannelStable, PossibleValues: []config.PossibleValue{ { Name: "Stable", Description: "Production releases.", Value: ReleaseChannelStable, }, { Name: "Beta", Description: "Production releases for testing new features that may break and cause interruption.", Value: ReleaseChannelBeta, }, { Name: "Support", Description: "Support releases or version changes for troubleshooting. Only use temporarily and when instructed.", Value: ReleaseChannelSupport, }, { Name: "Staging", Description: "Dangerous development releases for testing random things and experimenting. Only use temporarily and when instructed.", Value: ReleaseChannelStaging, }, }, Annotations: config.Annotations{ config.DisplayOrderAnnotation: -4, config.DisplayHintAnnotation: config.DisplayHintOneOf, config.CategoryAnnotation: "Updates", }, }) if err != nil { return err } err = config.Register(&config.Option{ Name: "Automatic Software Updates", Key: enableSoftwareUpdatesKey, Description: "Automatically check for and download software updates. This does not include intelligence data updates.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, RequiresRestart: false, DefaultValue: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: -12, config.CategoryAnnotation: "Updates", }, }) if err != nil { return err } err = config.Register(&config.Option{ Name: "Automatic Intelligence Data Updates", Key: enableIntelUpdatesKey, Description: "Automatically check for and download intelligence data updates. This includes filter lists, geo-ip data, and more. Does not include software updates.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, RequiresRestart: false, DefaultValue: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: -11, config.CategoryAnnotation: "Updates", }, }) if err != nil { return err } return nil } func initUpdateConfig() { releaseChannel = config.Concurrent.GetAsString(ReleaseChannelKey, ReleaseChannelStable) enableSoftwareUpdates = config.Concurrent.GetAsBool(enableSoftwareUpdatesKey, true) enableIntelUpdates = config.Concurrent.GetAsBool(enableIntelUpdatesKey, true) initialReleaseChannel = releaseChannel() module.instance.Config().EventConfigChange.AddCallback("configure updates", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { configureUpdates() return false, nil }) configureUpdates() } func configureUpdates() { module.instance.BinaryUpdates().Configure(enableSoftwareUpdates(), configure.GetBinaryUpdateURLs(releaseChannel())) module.instance.IntelUpdates().Configure(enableIntelUpdates(), configure.DefaultIntelIndexURLs) } ================================================ FILE: service/core/update_versions.go ================================================ package core import ( "bytes" "fmt" "sync" "text/tabwriter" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/info" "github.com/safing/portmaster/base/utils/debug" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/updates" ) const ( // versionsDBKey is the database key for update version information. versionsDBKey = "core:status/versions" // versionsDBKey is the database key for simple update version information. simpleVersionsDBKey = "core:status/simple-versions" ) // Versions holds update versions and status information. type Versions struct { record.Base sync.Mutex Core *info.Info Resources map[string]*updates.Artifact Channel string Beta bool Staging bool } // SimpleVersions holds simplified update versions and status information. type SimpleVersions struct { record.Base sync.Mutex Build *info.Info Resources map[string]*SimplifiedResourceVersion Channel string } // SimplifiedResourceVersion holds version information about one resource. type SimplifiedResourceVersion struct { Version string } // GetVersions returns the update versions and status information. // Resources must be locked when accessed. func GetVersions() *Versions { // Get all artifacts. resources := make(map[string]*updates.Artifact) if artifacts, err := module.instance.BinaryUpdates().GetFiles(); err == nil { for _, artifact := range artifacts { resources[artifact.Filename] = artifact } } if artifacts, err := module.instance.IntelUpdates().GetFiles(); err == nil { for _, artifact := range artifacts { resources[artifact.Filename] = artifact } } return &Versions{ Core: info.GetInfo(), Resources: resources, Channel: initialReleaseChannel, Beta: initialReleaseChannel == ReleaseChannelBeta, Staging: initialReleaseChannel == ReleaseChannelStaging, } } // GetSimpleVersions returns the simplified update versions and status information. func GetSimpleVersions() *SimpleVersions { // Get all artifacts, simply map. resources := make(map[string]*SimplifiedResourceVersion) if artifacts, err := module.instance.BinaryUpdates().GetFiles(); err == nil { for _, artifact := range artifacts { resources[artifact.Filename] = &SimplifiedResourceVersion{ Version: artifact.Version, } } } if artifacts, err := module.instance.IntelUpdates().GetFiles(); err == nil { for _, artifact := range artifacts { resources[artifact.Filename] = &SimplifiedResourceVersion{ Version: artifact.Version, } } } // Fill base info. return &SimpleVersions{ Build: info.GetInfo(), Resources: resources, Channel: initialReleaseChannel, } } func initVersionExport() { module.instance.BinaryUpdates().EventResourcesUpdated.AddCallback("export version status", export) module.instance.IntelUpdates().EventResourcesUpdated.AddCallback("export version status", export) _, _ = export(nil, struct{}{}) } func (v *Versions) save() error { if !v.KeyIsSet() { v.SetKey(versionsDBKey) } return db.Put(v) } func (v *SimpleVersions) save() error { if !v.KeyIsSet() { v.SetKey(simpleVersionsDBKey) } return db.Put(v) } // export is an event hook. func export(_ *mgr.WorkerCtx, _ struct{}) (cancel bool, err error) { // Export versions. if err := GetVersions().save(); err != nil { return false, err } if err := GetSimpleVersions().save(); err != nil { return false, err } return false, nil } // AddVersionsToDebugInfo adds the update system status to the given debug.Info. func AddVersionsToDebugInfo(di *debug.Info) { overviewBuf := bytes.NewBuffer(nil) tableBuf := bytes.NewBuffer(nil) tabWriter := tabwriter.NewWriter(tableBuf, 8, 4, 3, ' ', 0) fmt.Fprint(tabWriter, "\nFile\tVersion\tIndex\tSHA256\n") // Collect data for debug info. var cnt int if index, err := module.instance.BinaryUpdates().GetIndex(); err == nil { fmt.Fprintf(overviewBuf, "Binaries Index: v%s from %s\n", index.Version, index.Published) for _, artifact := range index.Artifacts { fmt.Fprintf(tabWriter, "\n%s\t%s\t%s\t%s", artifact.Filename, vStr(artifact.Version), "binaries", artifact.SHA256) cnt++ } } if index, err := module.instance.IntelUpdates().GetIndex(); err == nil { fmt.Fprintf(overviewBuf, "Intel Index: v%s from %s\n", index.Version, index.Published) for _, artifact := range index.Artifacts { fmt.Fprintf(tabWriter, "\n%s\t%s\t%s\t%s", artifact.Filename, vStr(artifact.Version), "intel", artifact.SHA256) cnt++ } } _ = tabWriter.Flush() // Add section. di.AddSection( fmt.Sprintf("Updates: %s (%d)", initialReleaseChannel, cnt), debug.UseCodeSection, overviewBuf.String(), tableBuf.String(), ) } func vStr(v string) string { if v != "" { return v } return "unknown" } ================================================ FILE: service/debug.go ================================================ package service import ( "bytes" "errors" "fmt" "io" "runtime" "github.com/maruel/panicparse/v2/stack" "github.com/safing/portmaster/base/utils/debug" "github.com/safing/portmaster/service/mgr" ) // GetWorkerInfo returns the worker info of all running workers. func (i *Instance) GetWorkerInfo() (*mgr.WorkerInfo, error) { snapshot, _, err := stack.ScanSnapshot(bytes.NewReader(fullStack()), io.Discard, stack.DefaultOpts()) if err != nil && !errors.Is(err, io.EOF) { return nil, fmt.Errorf("get stack: %w", err) } infos := make([]*mgr.WorkerInfo, 0, 32) for _, m := range i.serviceGroup.Modules() { wi, _ := m.Manager().WorkerInfo(snapshot) // Does not fail when we provide a snapshot. infos = append(infos, wi) // Check if module is a nested modules group if gm, ok := m.(*mgr.GroupModule); ok { for _, sm := range gm.Modules() { wi, _ := sm.Manager().WorkerInfo(snapshot) // Does not fail when we provide a snapshot. infos = append(infos, wi) } } } for _, m := range i.SpnGroup.Modules() { wi, _ := m.Manager().WorkerInfo(snapshot) // Does not fail when we provide a snapshot. infos = append(infos, wi) // Check if module is a nested modules group if gm, ok := m.(*mgr.GroupModule); ok { for _, sm := range gm.Modules() { wi, _ := sm.Manager().WorkerInfo(snapshot) // Does not fail when we provide a snapshot. infos = append(infos, wi) } } } return mgr.MergeWorkerInfo(infos...), nil } // AddWorkerInfoToDebugInfo adds the worker info of all running workers to the debug info. func (i *Instance) AddWorkerInfoToDebugInfo(di *debug.Info) { info, err := i.GetWorkerInfo() if err != nil { di.AddSection( "Worker Status Failed", debug.UseCodeSection, err.Error(), ) return } di.AddSection( fmt.Sprintf("Worker Status: %d/%d (%d?)", info.Running, len(info.Workers), info.Missing+info.Other), debug.UseCodeSection, info.Format(), ) } func fullStack() []byte { buf := make([]byte, 8096) for { n := runtime.Stack(buf, true) if n < len(buf) { return buf[:n] } buf = make([]byte, 2*len(buf)) } } ================================================ FILE: service/debug_test.go ================================================ package service import ( "testing" "time" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/mgr" ) func TestDebug(t *testing.T) { t.Parallel() // Create test instance with at least one worker. i := &Instance{} n, err := notifications.New(i) if err != nil { t.Fatal(err) } i.serviceGroup = mgr.NewGroup(n) i.SpnGroup = mgr.NewExtendedGroup() err = i.Start() if err != nil { t.Fatal(err) } time.Sleep(100 * time.Millisecond) info, err := i.GetWorkerInfo() if err != nil { t.Fatal(err) } t.Log(info) } ================================================ FILE: service/detection/dga/lms.go ================================================ package dga import ( "strings" ) // LmsScoreOfDomain calculates the mean longest meaningful substring of a domain. // It follows some special rules to increase accuracy. It returns a value between // 0 and 100, representing the length-based percentage of the meaningful substring. func LmsScoreOfDomain(domain string) float64 { var totalScore float64 domain = strings.ToLower(domain) subjects := strings.Split(domain, ".") var totalLength int for _, subject := range subjects { totalLength += len(subject) } for _, subject := range subjects { // calculate score, weigh it and add it if len(subject) > 0 { totalScore += LmsScore(subject) * (float64(len(subject)) / float64(totalLength)) } } return totalScore } // LmsScore calculates the longest meaningful substring of a domain. It returns a // value between 0 and 100, representing the length-based percentage of the // meaningful substring. func LmsScore(subject string) float64 { lmsStart := -1 lmsStop := -1 longestLms := 0 for i, c := range subject { if int(c) >= int('a') && int(c) <= int('z') { if lmsStart == -1 { lmsStart = i } } else { if lmsStart > -1 { lmsStop = i if lmsStop-lmsStart > longestLms { longestLms = lmsStop - lmsStart } lmsStart = -1 } } } if lmsStop == -1 { longestLms = len(subject) } // fmt.Printf("algs: lms score of %s is %.2f\n", subject, (float64(longest_lms) * 100.0 / float64(len(subject)))) return (float64(longestLms) * 100.0 / float64(len(subject))) } ================================================ FILE: service/detection/dga/lms_test.go ================================================ package dga import "testing" func TestLmsScoreOfDomain(t *testing.T) { t.Parallel() testDomain(t, "g.symcd.com.", 100, 100) testDomain(t, "www.google.com.", 100, 100) testDomain(t, "55ttt5.12abc3.test.com.", 68, 69) testDomain(t, "mbtq6opnuodp34gcrma65fxacgxv5ukr7lq6xuhr4mhoibe7.yvqptrozfbnqyemchpovw3q5xwjibuxfsgb72mix3znhpfhc.i2n7jh2gadqaadck3zs3vg3hbv5pkmwzeay4gc75etyettbb.isi5mhmowtfriu33uxzmgvjur5g2p3tloynwohfrggee6fkn.meop7kqyd5gwxxa3.er.spotify.com.", 0, 31) } func testDomain(t *testing.T, domain string, min, max float64) { t.Helper() score := LmsScoreOfDomain(domain) if score < min || score > max { t.Errorf("domain %s has scored %.2f, but should be between %.0f and %.0f", domain, score, min, max) } } ================================================ FILE: service/firewall/api.go ================================================ package firewall import ( "context" "fmt" "net" "net/http" "path/filepath" "slices" "strings" "time" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/process" ) const ( deniedMsgUnidentified = `%wFailed to identify the requesting process. Reload to try again.` deniedMsgSystem = `%wSystem access to the Portmaster API is not permitted. You can enable the Development Mode to disable API authentication for development purposes.` deniedMsgUnauthorized = `%wThe requesting process is not authorized to access the Portmaster API. Checked process paths: %s The authorized root path is %s. You can enable the Development Mode to disable API authentication for development purposes. For production use please create an API key in the settings.` deniedMsgMisconfigured = `%wThe authentication system is misconfigured.` ) var ( apiPortSet bool apiIP net.IP apiPort uint16 ) func prepAPIAuth() error { return api.SetAuthenticator(apiAuthenticator) } func startAPIAuth() { var err error apiIP, apiPort, err = netutils.ParseIPPort(apiListenAddress()) if err != nil { log.Warningf("filter: failed to parse API address for improved api auth mechanism: %s", err) return } apiPortSet = true log.Tracef("filter: api port set to %d", apiPort) } func apiAuthenticator(r *http.Request, s *http.Server) (token *api.AuthToken, err error) { if configReady.IsSet() && devMode() { return &api.AuthToken{ Read: api.PermitSelf, Write: api.PermitSelf, }, nil } // get local IP/Port localIP, localPort, err := netutils.ParseIPPort(s.Addr) if err != nil { return nil, fmt.Errorf("failed to get local IP/Port: %w", err) } // Correct 0.0.0.0 to 127.0.0.1 to fix local process-based authentication, // if 0.0.0.0 is used as the API listen address. if localIP.Equal(net.IPv4zero) { localIP = net.IPv4(127, 0, 0, 1) } // get remote IP/Port remoteIP, remotePort, err := netutils.ParseIPPort(r.RemoteAddr) if err != nil { return nil, fmt.Errorf("failed to get remote IP/Port: %w", err) } // Check if the request is even local. myIP, err := netenv.IsMyIP(remoteIP) if err == nil && !myIP { // Return to caller that the request was not handled. return nil, nil } log.Tracer(r.Context()).Tracef("filter: authenticating API request from %s", r.RemoteAddr) // It is important that this works, retry 5 times: every 500ms for 2.5s. var retry bool for range 5 { retry, err = authenticateAPIRequest( r.Context(), &packet.Info{ Inbound: false, // outbound as we are looking for the process of the source address Version: packet.IPv4, Protocol: packet.TCP, Src: remoteIP, // source as in the process we are looking for SrcPort: remotePort, // source as in the process we are looking for Dst: localIP, DstPort: localPort, PID: process.UndefinedProcessID, }, ) if !retry { break } // wait a little time.Sleep(500 * time.Millisecond) } if err != nil { return nil, err } return &api.AuthToken{ Read: api.PermitSelf, Write: api.PermitSelf, }, nil } func authenticateAPIRequest(ctx context.Context, pktInfo *packet.Info) (retry bool, err error) { var procsChecked []string var originalPid int // Get authenticated path. authenticatedPath := module.instance.BinaryUpdates().GetMainDir() if authenticatedPath == "" { return false, fmt.Errorf(deniedMsgMisconfigured, api.ErrAPIAccessDeniedMessage) //nolint:stylecheck // message for user } // Get real path. authenticatedPath, err = filepath.EvalSymlinks(authenticatedPath) if err != nil { return false, fmt.Errorf(deniedMsgUnidentified, api.ErrAPIAccessDeniedMessage) //nolint:stylecheck // message for user } // Add filepath separator to confine to directory. authenticatedPath += string(filepath.Separator) // Get process of request. pid, _, _ := process.GetPidOfConnection(ctx, pktInfo) if pid < 0 { return false, fmt.Errorf(deniedMsgUnidentified, api.ErrAPIAccessDeniedMessage) //nolint:stylecheck // message for user } proc, err := process.GetOrFindProcess(ctx, pid) if err != nil { log.Tracer(ctx).Debugf("filter: failed to get process of api request: %s", err) originalPid = process.UnidentifiedProcessID } else { originalPid = proc.Pid var previousPid int // Find parent for up to two levels, if we don't match the path. checkLevels := 2 checkLevelsLoop: for i := range checkLevels + 1 { // Check for eligible path. switch proc.Pid { case process.UnidentifiedProcessID, process.SystemProcessID: break checkLevelsLoop default: // normal process // Check if the requesting process is in database root / updates dir. if realPath, err := filepath.EvalSymlinks(proc.Path); err == nil { // check if the client has been allowed by flag if slices.Contains(allowedClients, realPath) { return false, nil } if strings.HasPrefix(realPath, authenticatedPath) { return false, nil } } } // Add checked path to list. procsChecked = append(procsChecked, proc.Path) // Get the parent process. if i < checkLevels { // save previous PID previousPid = proc.Pid // get parent process proc, err = process.GetOrFindProcess(ctx, proc.ParentPid) if err != nil { log.Tracer(ctx).Debugf("filter: failed to get parent process of api request: %s", err) break } // abort if we are looping if proc.Pid == previousPid { // this also catches -1 pid loops break } } } } switch originalPid { case process.UnidentifiedProcessID: log.Tracer(ctx).Warningf("filter: denying api access: failed to identify process") return true, fmt.Errorf(deniedMsgUnidentified, api.ErrAPIAccessDeniedMessage) //nolint:stylecheck // message for user case process.SystemProcessID: log.Tracer(ctx).Warningf("filter: denying api access: request by system") return false, fmt.Errorf(deniedMsgSystem, api.ErrAPIAccessDeniedMessage) //nolint:stylecheck // message for user default: // normal process log.Tracer(ctx).Warningf("filter: denying api access to %s - also checked %s (trusted root is %s)", procsChecked[0], strings.Join(procsChecked[1:], " "), module.instance.BinDir()) return false, fmt.Errorf( //nolint:stylecheck // message for user deniedMsgUnauthorized, api.ErrAPIAccessDeniedMessage, strings.Join(procsChecked, "\n"), authenticatedPath, ) } } ================================================ FILE: service/firewall/bypassing.go ================================================ package firewall import ( "context" "strings" "github.com/safing/portmaster/service/compat" "github.com/safing/portmaster/service/nameserver/nsutil" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/profile/endpoints" ) var resolverFilterLists = []string{"17-DNS"} // PreventBypassing checks if the connection should be denied or permitted // based on some bypass protection checks. func PreventBypassing(ctx context.Context, conn *network.Connection) (endpoints.EPResult, string, nsutil.Responder) { // Exclude incoming connections. if conn.Inbound { return endpoints.NoMatch, "", nil } // Exclude ICMP. switch packet.IPProtocol(conn.Entity.Protocol) { //nolint:exhaustive // Checking for specific values only. case packet.ICMP, packet.ICMPv6: return endpoints.NoMatch, "", nil } // Block firefox canary domain to disable DoH. // This MUST also affect the System Resolver, because the return value must // be correct for this to work. if strings.ToLower(conn.Entity.Domain) == "use-application-dns.net." { return endpoints.Denied, "blocked canary domain to prevent enabling of DNS-over-HTTPs", nsutil.NxDomain() } // Exclude DNS requests coming from the System Resolver. // This MUST also affect entities in the secure dns filter list, else the // System Resolver is wrongly accused of bypassing. if conn.Type == network.DNSRequest && conn.Process().IsSystemResolver() { return endpoints.NoMatch, "", nil } // If Portmaster resolver is disabled allow requests going to system dns resolver. // And allow all connections out of the System Resolver. if module.instance.Resolver().IsDisabled() { // TODO(vladimir): Is there a more specific check that can be done? if conn.Process().IsSystemResolver() { return endpoints.NoMatch, "", nil } if conn.Entity.Port == 53 && conn.Entity.IPScope.IsLocalhost() { return endpoints.NoMatch, "", nil } } // Block bypass attempts using an (encrypted) DNS server. switch { case looksLikeOutgoingDNSRequest(conn) && module.instance.Resolver().IsDisabled(): // Allow. Packet will be analyzed and blocked if its not a dns request, before sent. conn.Inspecting = true return endpoints.NoMatch, "", nil case conn.Entity.Port == 53: return endpoints.Denied, "blocked DNS query, manual dns setup required", nsutil.BlockIP() case conn.Entity.Port == 853: // Block connections to port 853 - DNS over TLS. fallthrough case conn.Entity.MatchLists(resolverFilterLists): // Block connection entities in the secure dns filter list. compat.ReportSecureDNSBypassIssue(conn.Process()) return endpoints.Denied, "blocked rogue connection to DNS resolver", nsutil.BlockIP() } return endpoints.NoMatch, "", nil } func looksLikeOutgoingDNSRequest(conn *network.Connection) bool { // Outbound on remote port 53, UDP. if conn.Inbound { return false } if conn.Entity.Port != 53 { return false } if conn.IPProtocol != packet.UDP { return false } return true } ================================================ FILE: service/firewall/config.go ================================================ package firewall import ( "github.com/tevino/abool" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/core" "github.com/safing/portmaster/spn/captain" ) // Configuration Keys. var ( CfgOptionEnableFilterKey = "filter/enable" filterEnabled config.BoolOption CfgOptionAskWithSystemNotificationsKey = "filter/askWithSystemNotifications" cfgOptionAskWithSystemNotificationsOrder = 2 askWithSystemNotifications config.BoolOption CfgOptionAskTimeoutKey = "filter/askTimeout" cfgOptionAskTimeoutOrder = 3 askTimeout config.IntOption CfgOptionPermanentVerdictsKey = "filter/permanentVerdicts" cfgOptionPermanentVerdictsOrder = 80 permanentVerdicts config.BoolOption CfgOptionDNSQueryInterceptionKey = "filter/dnsQueryInterception" cfgOptionDNSQueryInterceptionOrder = 81 dnsQueryInterception config.BoolOption ) func registerConfig() error { err := config.Register(&config.Option{ Name: "Enable Privacy Filter", Key: CfgOptionEnableFilterKey, Description: "Enable the Privacy Filter. If turned off, all privacy filter protections are fully disabled on this device. Not meant to be disabled in production - only turn off for testing.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelDeveloper, ReleaseLevel: config.ReleaseLevelExperimental, DefaultValue: true, Annotations: config.Annotations{ config.CategoryAnnotation: "General", }, }) if err != nil { return err } filterEnabled = config.Concurrent.GetAsBool(CfgOptionEnableFilterKey, true) err = config.Register(&config.Option{ Name: "Permanent Verdicts", Key: CfgOptionPermanentVerdictsKey, Description: "The Portmaster's system integration intercepts every single packet. Usually the first packet is enough for the Portmaster to set the verdict for a connection - ie. to allow or deny it. Making these verdicts permanent means that the Portmaster will tell the system integration that is does not want to see any more packets of that single connection. This brings a major performance increase.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelDeveloper, ReleaseLevel: config.ReleaseLevelExperimental, DefaultValue: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionPermanentVerdictsOrder, config.CategoryAnnotation: "Advanced", }, }) if err != nil { return err } permanentVerdicts = config.Concurrent.GetAsBool(CfgOptionPermanentVerdictsKey, true) err = config.Register(&config.Option{ Name: "Seamless DNS Integration", Key: CfgOptionDNSQueryInterceptionKey, Description: "Intercept and redirect astray DNS queries to the Portmaster's internal DNS server. This enables seamless DNS integration without having to configure the system or other software. However, this may lead to compatibility issues with other software that attempts the same.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelDeveloper, ReleaseLevel: config.ReleaseLevelExperimental, DefaultValue: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionDNSQueryInterceptionOrder, config.CategoryAnnotation: "Advanced", }, }) if err != nil { return err } dnsQueryInterception = config.Concurrent.GetAsBool(CfgOptionDNSQueryInterceptionKey, true) err = config.Register(&config.Option{ Name: "Prompt Desktop Notifications", Key: CfgOptionAskWithSystemNotificationsKey, Description: `In addition to showing prompt notifications in the Portmaster App, also send them to the Desktop. This requires the Portmaster Notifier to be running. Requires Desktop Notifications to be enabled.`, OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelUser, DefaultValue: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionAskWithSystemNotificationsOrder, config.CategoryAnnotation: "General", config.RequiresAnnotation: config.ValueRequirement{ Key: notifications.CfgUseSystemNotificationsKey, Value: true, }, }, }) if err != nil { return err } askWithSystemNotifications = config.Concurrent.GetAsBool(CfgOptionAskWithSystemNotificationsKey, true) err = config.Register(&config.Option{ Name: "Prompt Timeout", Key: CfgOptionAskTimeoutKey, Description: "How long the Portmaster will wait for a reply to a prompt notification. Please note that Desktop Notifications might not respect this or have their own limits.", OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelUser, DefaultValue: 60, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionAskTimeoutOrder, config.UnitAnnotation: "seconds", config.CategoryAnnotation: "General", }, ValidationRegex: `^[1-9][0-9]{1,5}$`, }) if err != nil { return err } askTimeout = config.Concurrent.GetAsInt(CfgOptionAskTimeoutKey, 60) return nil } // Config variables for interception and filter module. // Everything is registered by the interception module, as the filter module // can be disabled. var ( devMode config.BoolOption apiListenAddress config.StringOption tunnelEnabled config.BoolOption useCommunityNodes config.BoolOption configReady = abool.New() ) func getConfig() { devMode = config.Concurrent.GetAsBool(core.CfgDevModeKey, false) apiListenAddress = config.GetAsString(api.CfgDefaultListenAddressKey, "") tunnelEnabled = config.Concurrent.GetAsBool(captain.CfgOptionEnableSPNKey, false) useCommunityNodes = config.Concurrent.GetAsBool(captain.CfgOptionUseCommunityNodesKey, true) configReady.Set() } ================================================ FILE: service/firewall/dns.go ================================================ package firewall import ( "context" "errors" "net" "strings" "github.com/miekg/dns" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/endpoints" "github.com/safing/portmaster/service/resolver" ) func filterDNSSection( ctx context.Context, entries []dns.RR, p *profile.LayeredProfile, resolverScope netutils.IPScope, sysResolver bool, ) ([]dns.RR, []string, int, string) { // Will be filled 1:1 most of the time. goodEntries := make([]dns.RR, 0, len(entries)) // Will stay empty most of the time. var filteredRecords []string // keeps track of the number of valid and allowed // A and AAAA records. var allowedAddressRecords int var interveningOptionKey string for _, rr := range entries { // get IP and classification var ip net.IP switch v := rr.(type) { case *dns.A: ip = v.A case *dns.AAAA: ip = v.AAAA default: // add non A/AAAA entries // TODO: Add support for dns.SVCB and dns.HTTPS goodEntries = append(goodEntries, rr) continue } ipScope := netutils.GetIPScope(ip) if p.RemoveOutOfScopeDNS() { switch { case ipScope.IsLocalhost(): // No DNS should return localhost addresses filteredRecords = append(filteredRecords, formatRR(rr)) interveningOptionKey = profile.CfgOptionRemoveOutOfScopeDNSKey log.Tracer(ctx).Tracef("filter: RR violates resolver scope: %s", formatRR(rr)) continue case resolverScope.IsGlobal() && ipScope.IsLAN() && !sysResolver: // No global DNS should return LAN addresses filteredRecords = append(filteredRecords, formatRR(rr)) interveningOptionKey = profile.CfgOptionRemoveOutOfScopeDNSKey log.Tracer(ctx).Tracef("filter: RR violates resolver scope: %s", formatRR(rr)) continue } } if p.RemoveBlockedDNS() && !sysResolver { // filter by flags switch { case p.BlockScopeInternet() && ipScope.IsGlobal(): filteredRecords = append(filteredRecords, formatRR(rr)) interveningOptionKey = profile.CfgOptionBlockScopeInternetKey log.Tracer(ctx).Tracef("filter: RR is in blocked scope Internet: %s", formatRR(rr)) continue case p.BlockScopeLAN() && ipScope.IsLAN(): filteredRecords = append(filteredRecords, formatRR(rr)) interveningOptionKey = profile.CfgOptionBlockScopeLANKey log.Tracer(ctx).Tracef("filter: RR is in blocked scope LAN: %s", formatRR(rr)) continue case p.BlockScopeLocal() && ipScope.IsLocalhost(): filteredRecords = append(filteredRecords, formatRR(rr)) interveningOptionKey = profile.CfgOptionBlockScopeLocalKey log.Tracer(ctx).Tracef("filter: RR is in blocked scope Localhost: %s", formatRR(rr)) continue } // TODO: filter by endpoint list (IP only) } // if survived, add to good entries allowedAddressRecords++ goodEntries = append(goodEntries, rr) } return goodEntries, filteredRecords, allowedAddressRecords, interveningOptionKey } func filterDNSResponse( ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, rrCache *resolver.RRCache, sysResolver bool, ) *resolver.RRCache { // do not modify own queries if conn.Process().Pid == ownPID { return rrCache } // check if DNS response filtering is completely turned off if !p.RemoveOutOfScopeDNS() && !p.RemoveBlockedDNS() { return rrCache } var filteredRecords []string var validIPs int var interveningOptionKey string rrCache.Answer, filteredRecords, validIPs, interveningOptionKey = filterDNSSection(ctx, rrCache.Answer, p, rrCache.Resolver.IPScope, sysResolver) if len(filteredRecords) > 0 { rrCache.FilteredEntries = append(rrCache.FilteredEntries, filteredRecords...) } // Don't count the valid IPs in the extra section. rrCache.Extra, filteredRecords, _, _ = filterDNSSection(ctx, rrCache.Extra, p, rrCache.Resolver.IPScope, sysResolver) if len(filteredRecords) > 0 { rrCache.FilteredEntries = append(rrCache.FilteredEntries, filteredRecords...) } if len(rrCache.FilteredEntries) > 0 { rrCache.Filtered = true if validIPs == 0 { switch interveningOptionKey { case profile.CfgOptionBlockScopeInternetKey: conn.Block("Internet access blocked", interveningOptionKey) case profile.CfgOptionBlockScopeLANKey: conn.Block("LAN access blocked", interveningOptionKey) case profile.CfgOptionBlockScopeLocalKey: conn.Block("Localhost access blocked", interveningOptionKey) case profile.CfgOptionRemoveOutOfScopeDNSKey: conn.Block("DNS global/private split-view violation", interveningOptionKey) default: conn.Block("DNS response only contained to-be-blocked IPs", interveningOptionKey) } return rrCache } } return rrCache } // FilterResolvedDNS filters a dns response according to the application // profile and settings. func FilterResolvedDNS( ctx context.Context, conn *network.Connection, q *resolver.Query, rrCache *resolver.RRCache, ) *resolver.RRCache { // Check if we have a process and profile. layeredProfile := conn.Process().Profile() if layeredProfile == nil { log.Tracer(ctx).Warning("unknown process or profile") return nil } // Don't filter env responses. if rrCache.Resolver.Type == resolver.ServerTypeEnv { return rrCache } // special grant for connectivity domains if checkConnectivityDomain(ctx, conn, layeredProfile, nil) { // returns true if check triggered return rrCache } // Only filter critical things if request comes from the system resolver. sysResolver := conn.Process().IsSystemResolver() // Filter dns records and return if the query is blocked. rrCache = filterDNSResponse(ctx, conn, layeredProfile, rrCache, sysResolver) if conn.Verdict == network.VerdictBlock { return rrCache } // Block by CNAMEs. if !sysResolver { mayBlockCNAMEs(ctx, conn, layeredProfile) } return rrCache } func mayBlockCNAMEs(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile) bool { // if we have CNAMEs and the profile is configured to filter them // we need to re-check the lists and endpoints here if p.FilterCNAMEs() { conn.Entity.ResetLists() conn.Entity.EnableCNAMECheck(ctx, true) result, reason := p.MatchEndpoint(ctx, conn.Entity) if result == endpoints.Denied { conn.BlockWithContext(reason.String(), profile.CfgOptionFilterCNAMEKey, reason.Context()) return true } if result == endpoints.NoMatch { result, reason = p.MatchFilterLists(ctx, conn.Entity) if result == endpoints.Denied { conn.BlockWithContext(reason.String(), profile.CfgOptionFilterCNAMEKey, reason.Context()) return true } } } return false } // UpdateIPsAndCNAMEs saves all the IP->Name mappings to the cache database and // updates the CNAMEs in the Connection's Entity. func UpdateIPsAndCNAMEs(q *resolver.Query, rrCache *resolver.RRCache, conn *network.Connection) { // Sanity check input, as this is called from defer. if q == nil || rrCache == nil { return } // Get profileID for scoping IPInfo. var profileID string localProfile := conn.Process().Profile().LocalProfile() switch localProfile.ID { case profile.UnidentifiedProfileID, profile.SystemResolverProfileID: profileID = resolver.IPInfoProfileScopeGlobal default: profileID = localProfile.ID } // Collect IPs and CNAMEs. cnames := make(map[string]string) ips := make([]net.IP, 0, len(rrCache.Answer)) for _, rr := range append(rrCache.Answer, rrCache.Extra...) { switch v := rr.(type) { case *dns.CNAME: cnames[v.Hdr.Name] = v.Target case *dns.A: ips = append(ips, v.A) case *dns.AAAA: ips = append(ips, v.AAAA) case *dns.SVCB: if len(v.Target) >= 2 { // Ignore "" and ".". cnames[v.Hdr.Name] = v.Target } for _, pair := range v.Value { switch svcbParam := pair.(type) { case *dns.SVCBIPv4Hint: ips = append(ips, svcbParam.Hint...) case *dns.SVCBIPv6Hint: ips = append(ips, svcbParam.Hint...) } } case *dns.HTTPS: if len(v.Target) >= 2 { // Ignore "" and ".". cnames[v.Hdr.Name] = v.Target } for _, pair := range v.Value { switch svcbParam := pair.(type) { case *dns.SVCBIPv4Hint: ips = append(ips, svcbParam.Hint...) case *dns.SVCBIPv6Hint: ips = append(ips, svcbParam.Hint...) } } } } // Create new record for this IP. record := resolver.ResolvedDomain{ Domain: q.FQDN, Resolver: rrCache.Resolver, DNSRequestContext: rrCache.ToDNSRequestContext(), Expires: rrCache.Expires, } // Process CNAMEs record.AddCNAMEs(cnames) // Link connection with cnames. if conn.Type == network.DNSRequest { conn.Entity.CNAME = record.CNAMEs } SaveIPsInCache(ips, profileID, record) } // formatRR is a friendlier alternative to miekg/dns.RR.String(). func formatRR(rr dns.RR) string { return strings.ReplaceAll(rr.String(), "\t", " ") } // SaveIPsInCache saves the provided ips in the dns cashe assoseted with the record Domain and CNAMEs. func SaveIPsInCache(ips []net.IP, profileID string, record resolver.ResolvedDomain) { // Package IPs and CNAMEs into IPInfo structs. for _, ip := range ips { // Never save domain attributions for localhost IPs. if netutils.GetIPScope(ip) == netutils.HostLocal { continue } ipString := ip.String() info, err := resolver.GetIPInfo(profileID, ipString) if err != nil { if !errors.Is(err, database.ErrNotFound) { log.Errorf("nameserver: failed to search for IP info record: %s", err) } info = &resolver.IPInfo{ IP: ipString, ProfileID: profileID, } } // Add the new record to the resolved domains for this IP and scope. info.AddDomain(record) // Save if the record is new or has been updated. if err := info.Save(); err != nil { log.Errorf("nameserver: failed to save IP info record: %s", err) } } } ================================================ FILE: service/firewall/inspection/inspection.go ================================================ package inspection import ( "sync" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" ) //nolint:golint,stylecheck const ( DO_NOTHING uint8 = iota BLOCK_PACKET DROP_PACKET BLOCK_CONN DROP_CONN STOP_INSPECTING ) type inspectorFn func(*network.Connection, packet.Packet) uint8 var ( inspectors []inspectorFn inspectorNames []string inspectVerdicts []network.Verdict inspectorsLock sync.Mutex ) // RegisterInspector registers a traffic inspector. func RegisterInspector(name string, inspector inspectorFn, inspectVerdict network.Verdict) (index int) { inspectorsLock.Lock() defer inspectorsLock.Unlock() index = len(inspectors) inspectors = append(inspectors, inspector) inspectorNames = append(inspectorNames, name) inspectVerdicts = append(inspectVerdicts, inspectVerdict) return } // RunInspectors runs all the applicable inspectors on the given packet. func RunInspectors(conn *network.Connection, pkt packet.Packet) (network.Verdict, bool) { // inspectorsLock.Lock() // defer inspectorsLock.Unlock() activeInspectors := conn.GetActiveInspectors() if activeInspectors == nil { activeInspectors = make([]bool, len(inspectors)) conn.SetActiveInspectors(activeInspectors) } inspectorData := conn.GetInspectorData() if inspectorData == nil { inspectorData = make(map[uint8]interface{}) conn.SetInspectorData(inspectorData) } continueInspection := false verdict := network.VerdictUndecided for key, skip := range activeInspectors { if skip { continue } // check if the active verdict is already past the inspection criteria. if conn.Verdict > inspectVerdicts[key] { activeInspectors[key] = true continue } action := inspectors[key](conn, pkt) // Actually run inspector switch action { case DO_NOTHING: if verdict < network.VerdictAccept { verdict = network.VerdictAccept } continueInspection = true case BLOCK_PACKET: if verdict < network.VerdictBlock { verdict = network.VerdictBlock } continueInspection = true case DROP_PACKET: verdict = network.VerdictDrop continueInspection = true case BLOCK_CONN: conn.SetVerdict(network.VerdictBlock, "", "", nil) verdict = conn.Verdict activeInspectors[key] = true case DROP_CONN: conn.SetVerdict(network.VerdictDrop, "", "", nil) verdict = conn.Verdict activeInspectors[key] = true case STOP_INSPECTING: activeInspectors[key] = true } } return verdict, continueInspection } ================================================ FILE: service/firewall/interception/dnsmonitor/etwlink_windows.go ================================================ //go:build windows // +build windows package dnsmonitor import ( "fmt" "runtime" "sync" "sync/atomic" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/integration" "golang.org/x/sys/windows" ) type ETWSession struct { i *integration.ETWFunctions shutdownGuard atomic.Bool shutdownMutex sync.Mutex traceEnded chan struct{} state uintptr } // NewSession creates new ETW event listener and initializes it. This is a low level interface, make sure to call DestroySession when you are done using it. func NewSession(etwInterface *integration.ETWFunctions, callback func(domain string, pid uint32, result string)) (*ETWSession, error) { if etwInterface == nil { return nil, fmt.Errorf("etw interface was nil") } etwSession := &ETWSession{ i: etwInterface, traceEnded: make(chan struct{}), } // Make sure session from previous instances are not running. _ = etwSession.i.StopOldSession() // Initialize notification activated callback win32Callback := windows.NewCallback(func(domain *uint16, pid uint32, result *uint16) uintptr { callback(windows.UTF16PtrToString(domain), pid, windows.UTF16PtrToString(result)) return 0 }) // The function only allocates memory it will not fail. etwSession.state = etwSession.i.CreateState(win32Callback) // Make sure DestroySession is called even if caller forgets to call it. // TODO: (stenya) Finalizer directly calls i.DestroySession() bypassing synchronization in DestroySession(). // This could crash if StartTrace() is running. Consider using s.DestroySession() instead. runtime.SetFinalizer(etwSession, func(s *ETWSession) { _ = s.i.DestroySession(s.state) }) // Initialize session. err := etwSession.i.InitializeSession(etwSession.state) if err != nil { return nil, fmt.Errorf("failed to initialize session: %q", err) } return etwSession, nil } // StartTrace starts the tracing session of dns events. This is a blocking call. It will not return until the trace is stopped. func (l *ETWSession) StartTrace() error { defer close(l.traceEnded) return l.i.StartTrace(l.state) } // IsRunning returns true if DestroySession has NOT been called. func (l *ETWSession) IsRunning() bool { return !l.shutdownGuard.Load() } // FlushTrace flushes the trace buffer. func (l *ETWSession) FlushTrace() error { if l.i == nil { return fmt.Errorf("session not initialized") } l.shutdownMutex.Lock() defer l.shutdownMutex.Unlock() // Make sure session is still running. if l.shutdownGuard.Load() { return nil } return l.i.FlushTrace(l.state) } // StopTrace stops the trace. This will cause StartTrace to return. func (l *ETWSession) StopTrace() error { return l.i.StopTrace(l.state) } // DestroySession closes the session and frees the allocated memory. Listener cannot be used after this function is called. func (l *ETWSession) DestroySession() error { if l.i == nil { return fmt.Errorf("session not initialized") } l.shutdownMutex.Lock() defer l.shutdownMutex.Unlock() if l.shutdownGuard.Swap(true) { return nil } // Waiting for StartTrace() to return select { case <-l.traceEnded: case <-time.After(15 * time.Second): log.Warning("DNSMonitor: (ETWSession) Timeout waiting for trace to end before destroying session") } err := l.i.DestroySession(l.state) if err != nil { return err } l.state = 0 return nil } ================================================ FILE: service/firewall/interception/dnsmonitor/eventlistener.go ================================================ //go:build !linux && !windows // +build !linux,!windows package dnsmonitor type Listener struct{} func newListener(_ *DNSMonitor) (*Listener, error) { return &Listener{}, nil } func (l *Listener) flush() error { // Nothing to flush return nil } func (l *Listener) stop() error { return nil } ================================================ FILE: service/firewall/interception/dnsmonitor/eventlistener_linux.go ================================================ //go:build linux // +build linux package dnsmonitor import ( "errors" "fmt" "net" "os" "github.com/miekg/dns" "github.com/varlink/go/varlink" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/resolver" ) type Listener struct { varlinkConn *varlink.Connection } func newListener(module *DNSMonitor) (*Listener, error) { // Set source of the resolver. ResolverInfo.Source = resolver.ServerSourceSystemd // Check if the system has systemd-resolver. _, err := os.Stat("/run/systemd/resolve/io.systemd.Resolve.Monitor") if err != nil { return nil, fmt.Errorf("system does not support systemd resolver monitor") } listener := &Listener{} restartAttempts := 0 module.mgr.Go("systemd-resolver-event-listener", func(w *mgr.WorkerCtx) error { // Abort initialization if the connection failed after too many tries. if restartAttempts > 10 { return nil } restartAttempts += 1 // Initialize varlink connection varlinkConn, err := varlink.NewConnection(module.mgr.Ctx(), "unix:/run/systemd/resolve/io.systemd.Resolve.Monitor") if err != nil { return fmt.Errorf("failed to connect to systemd-resolver varlink service: %w", err) } defer func() { if varlinkConn != nil { err = varlinkConn.Close() if err != nil { log.Errorf("dnsmonitor: failed to close varlink connection: %s", err) } } }() listener.varlinkConn = varlinkConn // Subscribe to the dns query events receive, err := listener.varlinkConn.Send(w.Ctx(), "io.systemd.Resolve.Monitor.SubscribeQueryResults", nil, varlink.More) if err != nil { var varlinkErr *varlink.Error if errors.As(err, &varlinkErr) { return fmt.Errorf("failed to issue Varlink call: %+v", varlinkErr.Parameters) } else { return fmt.Errorf("failed to issue Varlink call: %w", err) } } for { queryResult := QueryResult{} // Receive the next event from the resolver. flags, err := receive(w.Ctx(), &queryResult) if err != nil { var varlinkErr *varlink.Error if errors.As(err, &varlinkErr) { return fmt.Errorf("failed to receive Varlink reply: %+v", varlinkErr.Parameters) } else { return fmt.Errorf("failed to receive Varlink reply: %w", err) } } // Check if the reply indicates the end of the stream if flags&varlink.Continues == 0 { break } // Ignore if there is no question. if queryResult.Question == nil || len(*queryResult.Question) == 0 { continue } // Protmaster self check domain := (*queryResult.Question)[0].Name if processIfSelfCheckDomain(dns.Fqdn(domain)) { // Not need to process result. continue } if queryResult.Rcode != nil { continue // Ignore DNS errors } listener.processAnswer(domain, &queryResult) } return nil }) return listener, nil } func (l *Listener) flush() error { // Nothing to flush return nil } func (l *Listener) stop() error { return nil } func (l *Listener) processAnswer(domain string, queryResult *QueryResult) { // Allocated data struct for the parsed result. cnames := make(map[string]string) ips := make([]net.IP, 0, 5) // Check if the query is valid if queryResult.Answer == nil { return } // Go trough each answer entry. for _, a := range *queryResult.Answer { if a.RR.Address != nil { ip := net.IP(*a.RR.Address) // Answer contains ip address. ips = append(ips, ip) } else if a.RR.Name != nil { // Answer is a CNAME. cnames[domain] = *a.RR.Name } } saveDomain(domain, ips, cnames, resolver.IPInfoProfileScopeGlobal) } ================================================ FILE: service/firewall/interception/dnsmonitor/eventlistener_windows.go ================================================ //go:build windows // +build windows package dnsmonitor import ( "context" "fmt" "net" "strconv" "strings" "github.com/miekg/dns" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/resolver" ) type Listener struct { etw *ETWSession } func newListener(module *DNSMonitor) (*Listener, error) { // Set source of the resolver. ResolverInfo.Source = resolver.ServerSourceETW listener := &Listener{} // Initialize new dns event session. err := initializeSessions(module, listener) if err != nil { // Listen for event if the dll has been loaded module.instance.OSIntegration().OnInitializedEvent.AddCallback("loader-listener", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { err = initializeSessions(module, listener) if err != nil { return false, err } return true, nil }) } return listener, nil } func initializeSessions(module *DNSMonitor, listener *Listener) error { var err error listener.etw, err = NewSession(module.instance.OSIntegration().GetETWInterface(), listener.processEvent) if err != nil { return err } // Start listener module.mgr.Go("etw-dns-event-listener", func(w *mgr.WorkerCtx) error { return listener.etw.StartTrace() }) return nil } func (l *Listener) flush() error { if l.etw == nil { return fmt.Errorf("etw not initialized") } return l.etw.FlushTrace() } func (l *Listener) stop() error { if l == nil { return fmt.Errorf("listener is nil") } if l.etw == nil { return fmt.Errorf("invalid etw session") } // Stop and destroy trace. Destroy should be called even if stop fails for some reason. err := l.etw.StopTrace() err2 := l.etw.DestroySession() if err != nil { return fmt.Errorf("StopTrace failed: %w", err) } if err2 != nil { return fmt.Errorf("DestroySession failed: %w", err2) } return nil } func (l *Listener) processEvent(domain string, pid uint32, result string) { if processIfSelfCheckDomain(dns.Fqdn(domain)) { // Not need to process result. return } // Ignore empty results if len(result) == 0 { return } profileScope := resolver.IPInfoProfileScopeGlobal // Get the profile ID if the process can be found if proc, err := process.GetOrFindProcess(context.Background(), int(pid)); err == nil { if profile := proc.Profile(); profile != nil { if localProfile := profile.LocalProfile(); localProfile != nil { profileScope = localProfile.ID } } } cnames := make(map[string]string) ips := []net.IP{} resultArray := strings.Split(result, ";") for _, r := range resultArray { // For results other than IP addresses, the string starts with "type:" if strings.HasPrefix(r, "type:") { dnsValueArray := strings.Split(r, " ") if len(dnsValueArray) < 3 { continue } // Ignore everything except CNAME records if value, err := strconv.ParseInt(dnsValueArray[1], 10, 16); err == nil && value == int64(dns.TypeCNAME) { cnames[domain] = dnsValueArray[2] } } else { // If the event doesn't start with "type:", it's an IP address ip := net.ParseIP(r) if ip != nil { ips = append(ips, ip) } } } saveDomain(domain, ips, cnames, profileScope) } ================================================ FILE: service/firewall/interception/dnsmonitor/module.go ================================================ package dnsmonitor import ( "errors" "net" "strings" "github.com/miekg/dns" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/compat" "github.com/safing/portmaster/service/integration" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/resolver" ) var ResolverInfo = resolver.ResolverInfo{ Name: "SystemResolver", Type: resolver.ServerTypeMonitor, } type DNSMonitor struct { instance instance mgr *mgr.Manager listener *Listener } // Manager returns the module manager. func (dl *DNSMonitor) Manager() *mgr.Manager { return dl.mgr } // Start starts the module. func (dl *DNSMonitor) Start() error { // Initialize dns event listener var err error dl.listener, err = newListener(dl) if err != nil { log.Warningf("dnsmonitor: failed to start dns listener: %s", err) } return nil } // Stop stops the module. func (dl *DNSMonitor) Stop() error { if dl.listener != nil { err := dl.listener.stop() if err != nil { log.Errorf("dnsmonitor: failed to close listener: %s", err) } } return nil } // Flush flushes the buffer forcing all events to be processed. func (dl *DNSMonitor) Flush() error { return dl.listener.flush() } func saveDomain(domain string, ips []net.IP, cnames map[string]string, profileScope string) { fqdn := dns.Fqdn(domain) // Create new record for this IP. record := resolver.ResolvedDomain{ Domain: fqdn, Resolver: &ResolverInfo, DNSRequestContext: &resolver.DNSRequestContext{}, Expires: 0, } // Process cnames record.AddCNAMEs(cnames) // Add to cache saveIPsInCache(ips, profileScope, record) } func New(instance instance) (*DNSMonitor, error) { // Initialize module m := mgr.New("DNSMonitor") module := &DNSMonitor{ mgr: m, instance: instance, } return module, nil } type instance interface { OSIntegration() *integration.OSIntegration } func processIfSelfCheckDomain(fqdn string) bool { // Check for compat check dns request. if strings.HasSuffix(fqdn, compat.DNSCheckInternalDomainScope) { subdomain := strings.TrimSuffix(fqdn, compat.DNSCheckInternalDomainScope) _ = compat.SubmitDNSCheckDomain(subdomain) log.Infof("dnsmonitor: self-check domain received") // No need to parse the answer. return true } return false } // saveIPsInCache saves the provided ips in the dns cashe assoseted with the record Domain and CNAMEs. func saveIPsInCache(ips []net.IP, profileID string, record resolver.ResolvedDomain) { // Package IPs and CNAMEs into IPInfo structs. for _, ip := range ips { // Never save domain attributions for localhost IPs. if netutils.GetIPScope(ip) == netutils.HostLocal { continue } ipString := ip.String() info, err := resolver.GetIPInfo(profileID, ipString) if err != nil { if !errors.Is(err, database.ErrNotFound) { log.Errorf("dnsmonitor: failed to search for IP info record: %s", err) } info = &resolver.IPInfo{ IP: ipString, ProfileID: profileID, } } // Add the new record to the resolved domains for this IP and scope. info.AddDomain(record) // Save if the record is new or has been updated. if err := info.Save(); err != nil { log.Errorf("dnsmonitor: failed to save IP info record: %s", err) } } } ================================================ FILE: service/firewall/interception/dnsmonitor/varlinktypes.go ================================================ //go:build linux // +build linux package dnsmonitor // List of struct that define the systemd-resolver varlink dns event protocol. // Source: `sudo varlinkctl introspect /run/systemd/resolve/io.systemd.Resolve.Monitor io.systemd.Resolve.Monitor` type ResourceKey struct { Class int `json:"class"` Type int `json:"type"` Name string `json:"name"` } type ResourceRecord struct { Key ResourceKey `json:"key"` Name *string `json:"name,omitempty"` Address *[]byte `json:"address,omitempty"` // Rest of the fields are not used. // Priority *int `json:"priority,omitempty"` // Weight *int `json:"weight,omitempty"` // Port *int `json:"port,omitempty"` // CPU *string `json:"cpu,omitempty"` // OS *string `json:"os,omitempty"` // Items *[]string `json:"items,omitempty"` // MName *string `json:"mname,omitempty"` // RName *string `json:"rname,omitempty"` // Serial *int `json:"serial,omitempty"` // Refresh *int `json:"refresh,omitempty"` // Expire *int `json:"expire,omitempty"` // Minimum *int `json:"minimum,omitempty"` // Exchange *string `json:"exchange,omitempty"` // Version *int `json:"version,omitempty"` // Size *int `json:"size,omitempty"` // HorizPre *int `json:"horiz_pre,omitempty"` // VertPre *int `json:"vert_pre,omitempty"` // Latitude *int `json:"latitude,omitempty"` // Longitude *int `json:"longitude,omitempty"` // Altitude *int `json:"altitude,omitempty"` // KeyTag *int `json:"key_tag,omitempty"` // Algorithm *int `json:"algorithm,omitempty"` // DigestType *int `json:"digest_type,omitempty"` // Digest *string `json:"digest,omitempty"` // FPType *int `json:"fptype,omitempty"` // Fingerprint *string `json:"fingerprint,omitempty"` // Flags *int `json:"flags,omitempty"` // Protocol *int `json:"protocol,omitempty"` // DNSKey *string `json:"dnskey,omitempty"` // Signer *string `json:"signer,omitempty"` // TypeCovered *int `json:"type_covered,omitempty"` // Labels *int `json:"labels,omitempty"` // OriginalTTL *int `json:"original_ttl,omitempty"` // Expiration *int `json:"expiration,omitempty"` // Inception *int `json:"inception,omitempty"` // Signature *string `json:"signature,omitempty"` // NextDomain *string `json:"next_domain,omitempty"` // Types *[]int `json:"types,omitempty"` // Iterations *int `json:"iterations,omitempty"` // Salt *string `json:"salt,omitempty"` // Hash *string `json:"hash,omitempty"` // CertUsage *int `json:"cert_usage,omitempty"` // Selector *int `json:"selector,omitempty"` // MatchingType *int `json:"matching_type,omitempty"` // Data *string `json:"data,omitempty"` // Tag *string `json:"tag,omitempty"` // Value *string `json:"value,omitempty"` } type Answer struct { RR *ResourceRecord `json:"rr,omitempty"` Raw string `json:"raw"` IfIndex *int `json:"ifindex,omitempty"` } type QueryResult struct { Ready *bool `json:"ready,omitempty"` State *string `json:"state,omitempty"` Rcode *int `json:"rcode,omitempty"` Errno *int `json:"errno,omitempty"` Question *[]ResourceKey `json:"question,omitempty"` CollectedQuestions *[]ResourceKey `json:"collectedQuestions,omitempty"` Answer *[]Answer `json:"answer,omitempty"` } ================================================ FILE: service/firewall/interception/ebpf/bandwidth/bpf_bpfeb.go ================================================ // Code generated by bpf2go; DO NOT EDIT. //go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 package ebpf import ( "bytes" _ "embed" "fmt" "io" "github.com/cilium/ebpf" ) type bpfSkInfo struct { Rx uint64 Tx uint64 Reported uint64 } type bpfSkKey struct { SrcIp [4]uint32 DstIp [4]uint32 SrcPort uint16 DstPort uint16 Protocol uint8 Ipv6 uint8 _ [2]byte } // loadBpf returns the embedded CollectionSpec for bpf. func loadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) spec, err := ebpf.LoadCollectionSpecFromReader(reader) if err != nil { return nil, fmt.Errorf("can't load bpf: %w", err) } return spec, err } // loadBpfObjects loads bpf and converts it into a struct. // // The following types are suitable as obj argument: // // *bpfObjects // *bpfPrograms // *bpfMaps // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { spec, err := loadBpf() if err != nil { return err } return spec.LoadAndAssign(obj, opts) } // bpfSpecs contains maps and programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfSpecs struct { bpfProgramSpecs bpfMapSpecs } // bpfSpecs contains programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfProgramSpecs struct { SocketOperations *ebpf.ProgramSpec `ebpf:"socket_operations"` UdpRecvmsg *ebpf.ProgramSpec `ebpf:"udp_recvmsg"` UdpSendmsg *ebpf.ProgramSpec `ebpf:"udp_sendmsg"` Udpv6Recvmsg *ebpf.ProgramSpec `ebpf:"udpv6_recvmsg"` Udpv6Sendmsg *ebpf.ProgramSpec `ebpf:"udpv6_sendmsg"` } // bpfMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { PmBandwidthMap *ebpf.MapSpec `ebpf:"pm_bandwidth_map"` } // bpfObjects contains all objects after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfObjects struct { bpfPrograms bpfMaps } func (o *bpfObjects) Close() error { return _BpfClose( &o.bpfPrograms, &o.bpfMaps, ) } // bpfMaps contains all maps after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { PmBandwidthMap *ebpf.Map `ebpf:"pm_bandwidth_map"` } func (m *bpfMaps) Close() error { return _BpfClose( m.PmBandwidthMap, ) } // bpfPrograms contains all programs after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfPrograms struct { SocketOperations *ebpf.Program `ebpf:"socket_operations"` UdpRecvmsg *ebpf.Program `ebpf:"udp_recvmsg"` UdpSendmsg *ebpf.Program `ebpf:"udp_sendmsg"` Udpv6Recvmsg *ebpf.Program `ebpf:"udpv6_recvmsg"` Udpv6Sendmsg *ebpf.Program `ebpf:"udpv6_sendmsg"` } func (p *bpfPrograms) Close() error { return _BpfClose( p.SocketOperations, p.UdpRecvmsg, p.UdpSendmsg, p.Udpv6Recvmsg, p.Udpv6Sendmsg, ) } func _BpfClose(closers ...io.Closer) error { for _, closer := range closers { if err := closer.Close(); err != nil { return err } } return nil } // Do not access this directly. // //go:embed bpf_bpfeb.o var _BpfBytes []byte ================================================ FILE: service/firewall/interception/ebpf/bandwidth/bpf_bpfel.go ================================================ // Code generated by bpf2go; DO NOT EDIT. //go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64 package ebpf import ( "bytes" _ "embed" "fmt" "io" "github.com/cilium/ebpf" ) type bpfSkInfo struct { Rx uint64 Tx uint64 Reported uint64 } type bpfSkKey struct { SrcIp [4]uint32 DstIp [4]uint32 SrcPort uint16 DstPort uint16 Protocol uint8 Ipv6 uint8 _ [2]byte } // loadBpf returns the embedded CollectionSpec for bpf. func loadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) spec, err := ebpf.LoadCollectionSpecFromReader(reader) if err != nil { return nil, fmt.Errorf("can't load bpf: %w", err) } return spec, err } // loadBpfObjects loads bpf and converts it into a struct. // // The following types are suitable as obj argument: // // *bpfObjects // *bpfPrograms // *bpfMaps // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { spec, err := loadBpf() if err != nil { return err } return spec.LoadAndAssign(obj, opts) } // bpfSpecs contains maps and programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfSpecs struct { bpfProgramSpecs bpfMapSpecs } // bpfSpecs contains programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfProgramSpecs struct { SocketOperations *ebpf.ProgramSpec `ebpf:"socket_operations"` UdpRecvmsg *ebpf.ProgramSpec `ebpf:"udp_recvmsg"` UdpSendmsg *ebpf.ProgramSpec `ebpf:"udp_sendmsg"` Udpv6Recvmsg *ebpf.ProgramSpec `ebpf:"udpv6_recvmsg"` Udpv6Sendmsg *ebpf.ProgramSpec `ebpf:"udpv6_sendmsg"` } // bpfMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { PmBandwidthMap *ebpf.MapSpec `ebpf:"pm_bandwidth_map"` } // bpfObjects contains all objects after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfObjects struct { bpfPrograms bpfMaps } func (o *bpfObjects) Close() error { return _BpfClose( &o.bpfPrograms, &o.bpfMaps, ) } // bpfMaps contains all maps after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { PmBandwidthMap *ebpf.Map `ebpf:"pm_bandwidth_map"` } func (m *bpfMaps) Close() error { return _BpfClose( m.PmBandwidthMap, ) } // bpfPrograms contains all programs after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfPrograms struct { SocketOperations *ebpf.Program `ebpf:"socket_operations"` UdpRecvmsg *ebpf.Program `ebpf:"udp_recvmsg"` UdpSendmsg *ebpf.Program `ebpf:"udp_sendmsg"` Udpv6Recvmsg *ebpf.Program `ebpf:"udpv6_recvmsg"` Udpv6Sendmsg *ebpf.Program `ebpf:"udpv6_sendmsg"` } func (p *bpfPrograms) Close() error { return _BpfClose( p.SocketOperations, p.UdpRecvmsg, p.UdpSendmsg, p.Udpv6Recvmsg, p.Udpv6Sendmsg, ) } func _BpfClose(closers ...io.Closer) error { for _, closer := range closers { if err := closer.Close(); err != nil { return err } } return nil } // Do not access this directly. // //go:embed bpf_bpfel.o var _BpfBytes []byte ================================================ FILE: service/firewall/interception/ebpf/bandwidth/interface.go ================================================ package ebpf import ( "context" "encoding/binary" "fmt" "net" "path/filepath" "sync/atomic" "syscall" "time" "github.com/cilium/ebpf" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/rlimit" "golang.org/x/sys/unix" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/packet" ) //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" bpf ../programs/bandwidth.c var ebpfLoadingFailed atomic.Uint32 // BandwidthStatsWorker monitors connection bandwidth using ebpf. func BandwidthStatsWorker(ctx context.Context, collectInterval time.Duration, bandwidthUpdates chan *packet.BandwidthUpdate) error { // Allow the current process to lock memory for eBPF resources. err := rlimit.RemoveMemlock() if err != nil { if ebpfLoadingFailed.Add(1) >= 5 { log.Warningf("ebpf: failed to remove memlock 5 times, giving up with error %s", err) return nil } return fmt.Errorf("ebpf: failed to remove memlock: %w", err) } // Load pre-compiled programs and maps into the kernel. objs := bpfObjects{} if err := loadBpfObjects(&objs, nil); err != nil { if ebpfLoadingFailed.Add(1) >= 5 { log.Warningf("ebpf: failed to load ebpf object 5 times, giving up with error %s", err) return nil } return fmt.Errorf("ebpf: failed to load ebpf object: %w", err) } defer objs.Close() //nolint:errcheck // Find the cgroup path path, err := findCgroupPath() if err != nil { return fmt.Errorf("ebpf: failed to find cgroup paths: %w", err) } // Attach socket options for monitoring connections sockOptionsLink, err := link.AttachCgroup(link.CgroupOptions{ Path: path, Program: objs.bpfPrograms.SocketOperations, Attach: ebpf.AttachCGroupSockOps, }) if err != nil { return fmt.Errorf("ebpf: failed to open module sockops: %w", err) } defer sockOptionsLink.Close() //nolint:errcheck // Attach Udp Ipv4 recive message tracing udpv4RMLink, err := link.AttachTracing(link.TracingOptions{ Program: objs.bpfPrograms.UdpRecvmsg, }) if err != nil { return fmt.Errorf("ebpf: failed to open trace Udp IPv4 recvmsg: %w", err) } defer udpv4RMLink.Close() //nolint:errcheck // Attach UDP IPv4 send message tracing udpv4SMLink, err := link.AttachTracing(link.TracingOptions{ Program: objs.bpfPrograms.UdpSendmsg, }) if err != nil { return fmt.Errorf("ebpf: failed to open trace Udp IPv4 sendmsg: %w", err) } defer udpv4SMLink.Close() //nolint:errcheck // Attach UDP IPv6 receive message tracing udpv6RMLink, err := link.AttachTracing(link.TracingOptions{ Program: objs.bpfPrograms.Udpv6Recvmsg, }) if err != nil { return fmt.Errorf("ebpf: failed to open trace Udp IPv6 recvmsg: %w", err) } defer udpv6RMLink.Close() //nolint:errcheck // Attach UDP IPv6 send message tracing udpv6SMLink, err := link.AttachTracing(link.TracingOptions{ Program: objs.bpfPrograms.Udpv6Sendmsg, }) if err != nil { return fmt.Errorf("ebpf: failed to open trace Udp IPv6 sendmsg: %w", err) } defer udpv6SMLink.Close() //nolint:errcheck // Setup ticker. ticker := time.NewTicker(collectInterval) defer ticker.Stop() // Collect bandwidth at every tick. for { select { case <-ticker.C: reportBandwidth(ctx, objs, bandwidthUpdates) case <-ctx.Done(): return nil } } } // reportBandwidth reports the bandwidth to the given updates channel. func reportBandwidth(ctx context.Context, objs bpfObjects, bandwidthUpdates chan *packet.BandwidthUpdate) { var ( skKey bpfSkKey skInfo bpfSkInfo updated int skipped int ) iter := objs.bpfMaps.PmBandwidthMap.Iterate() for iter.Next(&skKey, &skInfo) { // Check if already reported. if skInfo.Reported >= 1 { skipped++ continue } // Mark as reported and update the map. skInfo.Reported = 1 if err := objs.bpfMaps.PmBandwidthMap.Update(&skKey, &skInfo, ebpf.UpdateExist); err != nil { log.Debugf("ebpf: failed to mark bandwidth map entry as reported: %s", err) } connID := packet.CreateConnectionID( packet.IPProtocol(skKey.Protocol), convertArrayToIP(skKey.SrcIp, skKey.Ipv6 == 1), skKey.SrcPort, convertArrayToIP(skKey.DstIp, skKey.Ipv6 == 1), skKey.DstPort, false, ) update := &packet.BandwidthUpdate{ ConnID: connID, BytesReceived: skInfo.Rx, BytesSent: skInfo.Tx, Method: packet.Absolute, } select { case bandwidthUpdates <- update: updated++ case <-ctx.Done(): return default: log.Warningf("ebpf: bandwidth update queue is full (updated=%d, skipped=%d), ignoring rest of batch", updated, skipped) return } } } // findCgroupPath returns the default unified path of the cgroup. func findCgroupPath() (string, error) { cgroupPath := "/sys/fs/cgroup" var st syscall.Statfs_t err := syscall.Statfs(cgroupPath, &st) if err != nil { return "", err } isCgroupV2Enabled := st.Type == unix.CGROUP2_SUPER_MAGIC if !isCgroupV2Enabled { cgroupPath = filepath.Join(cgroupPath, "unified") } return cgroupPath, nil } // convertArrayToIP converts an array of uint32 values to a net.IP address. func convertArrayToIP(input [4]uint32, ipv6 bool) net.IP { if !ipv6 { addressBuf := make([]byte, 4) binary.LittleEndian.PutUint32(addressBuf, input[0]) return net.IP(addressBuf) } else { addressBuf := make([]byte, 16) for i := range 4 { binary.LittleEndian.PutUint32(addressBuf[i*4:i*4+4], input[i]) } return net.IP(addressBuf) } } ================================================ FILE: service/firewall/interception/ebpf/connection_listener/bpf_bpfeb.go ================================================ // Code generated by bpf2go; DO NOT EDIT. //go:build mips || mips64 || ppc64 || s390x package ebpf import ( "bytes" _ "embed" "fmt" "io" "github.com/cilium/ebpf" ) type bpfEvent struct { Saddr [4]uint32 Daddr [4]uint32 Sport uint16 Dport uint16 Pid uint32 IpVersion uint8 Protocol uint8 Direction uint8 _ [1]byte } // loadBpf returns the embedded CollectionSpec for bpf. func loadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) spec, err := ebpf.LoadCollectionSpecFromReader(reader) if err != nil { return nil, fmt.Errorf("can't load bpf: %w", err) } return spec, err } // loadBpfObjects loads bpf and converts it into a struct. // // The following types are suitable as obj argument: // // *bpfObjects // *bpfPrograms // *bpfMaps // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { spec, err := loadBpf() if err != nil { return err } return spec.LoadAndAssign(obj, opts) } // bpfSpecs contains maps and programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfSpecs struct { bpfProgramSpecs bpfMapSpecs } // bpfSpecs contains programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfProgramSpecs struct { TcpConnect *ebpf.ProgramSpec `ebpf:"tcp_connect"` UdpV4Connect *ebpf.ProgramSpec `ebpf:"udp_v4_connect"` UdpV6Connect *ebpf.ProgramSpec `ebpf:"udp_v6_connect"` } // bpfMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { PmConnectionEvents *ebpf.MapSpec `ebpf:"pm_connection_events"` } // bpfObjects contains all objects after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfObjects struct { bpfPrograms bpfMaps } func (o *bpfObjects) Close() error { return _BpfClose( &o.bpfPrograms, &o.bpfMaps, ) } // bpfMaps contains all maps after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { PmConnectionEvents *ebpf.Map `ebpf:"pm_connection_events"` } func (m *bpfMaps) Close() error { return _BpfClose( m.PmConnectionEvents, ) } // bpfPrograms contains all programs after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfPrograms struct { TcpConnect *ebpf.Program `ebpf:"tcp_connect"` UdpV4Connect *ebpf.Program `ebpf:"udp_v4_connect"` UdpV6Connect *ebpf.Program `ebpf:"udp_v6_connect"` } func (p *bpfPrograms) Close() error { return _BpfClose( p.TcpConnect, p.UdpV4Connect, p.UdpV6Connect, ) } func _BpfClose(closers ...io.Closer) error { for _, closer := range closers { if err := closer.Close(); err != nil { return err } } return nil } // Do not access this directly. // //go:embed bpf_bpfeb.o var _BpfBytes []byte ================================================ FILE: service/firewall/interception/ebpf/connection_listener/bpf_bpfel.go ================================================ // Code generated by bpf2go; DO NOT EDIT. //go:build 386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 package ebpf import ( "bytes" _ "embed" "fmt" "io" "github.com/cilium/ebpf" ) type bpfEvent struct { Saddr [4]uint32 Daddr [4]uint32 Sport uint16 Dport uint16 Pid uint32 IpVersion uint8 Protocol uint8 Direction uint8 _ [1]byte } // loadBpf returns the embedded CollectionSpec for bpf. func loadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) spec, err := ebpf.LoadCollectionSpecFromReader(reader) if err != nil { return nil, fmt.Errorf("can't load bpf: %w", err) } return spec, err } // loadBpfObjects loads bpf and converts it into a struct. // // The following types are suitable as obj argument: // // *bpfObjects // *bpfPrograms // *bpfMaps // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { spec, err := loadBpf() if err != nil { return err } return spec.LoadAndAssign(obj, opts) } // bpfSpecs contains maps and programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfSpecs struct { bpfProgramSpecs bpfMapSpecs } // bpfSpecs contains programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfProgramSpecs struct { TcpConnect *ebpf.ProgramSpec `ebpf:"tcp_connect"` UdpV4Connect *ebpf.ProgramSpec `ebpf:"udp_v4_connect"` UdpV6Connect *ebpf.ProgramSpec `ebpf:"udp_v6_connect"` } // bpfMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { PmConnectionEvents *ebpf.MapSpec `ebpf:"pm_connection_events"` } // bpfObjects contains all objects after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfObjects struct { bpfPrograms bpfMaps } func (o *bpfObjects) Close() error { return _BpfClose( &o.bpfPrograms, &o.bpfMaps, ) } // bpfMaps contains all maps after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { PmConnectionEvents *ebpf.Map `ebpf:"pm_connection_events"` } func (m *bpfMaps) Close() error { return _BpfClose( m.PmConnectionEvents, ) } // bpfPrograms contains all programs after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfPrograms struct { TcpConnect *ebpf.Program `ebpf:"tcp_connect"` UdpV4Connect *ebpf.Program `ebpf:"udp_v4_connect"` UdpV6Connect *ebpf.Program `ebpf:"udp_v6_connect"` } func (p *bpfPrograms) Close() error { return _BpfClose( p.TcpConnect, p.UdpV4Connect, p.UdpV6Connect, ) } func _BpfClose(closers ...io.Closer) error { for _, closer := range closers { if err := closer.Close(); err != nil { return err } } return nil } // Do not access this directly. // //go:embed bpf_bpfel.o var _BpfBytes []byte ================================================ FILE: service/firewall/interception/ebpf/connection_listener/worker.go ================================================ package ebpf import ( "bytes" "context" "encoding/binary" "errors" "fmt" "net" "strings" "sync/atomic" "time" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/ringbuf" "github.com/cilium/ebpf/rlimit" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/packet" ) //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" -type Event bpf ../programs/monitor.c var ebpfLoadingFailed atomic.Uint32 // ConnectionListenerWorker listens to new connections using ebpf. func ConnectionListenerWorker(ctx context.Context, packets chan packet.Packet) error { // Allow the current process to lock memory for eBPF resources. if err := rlimit.RemoveMemlock(); err != nil { if ebpfLoadingFailed.Add(1) >= 5 { log.Warningf("ebpf: failed to remove memlock 5 times, giving up with error %s", err) return nil } return fmt.Errorf("ebpf: failed to remove ebpf memlock: %w", err) } // Load pre-compiled programs and maps into the kernel. objs := bpfObjects{} if err := loadBpfObjects_Ex(&objs, nil); err != nil { if ebpfLoadingFailed.Add(1) >= 5 { log.Warningf("ebpf: failed to load ebpf object 5 times, giving up with error %s", err) return nil } return fmt.Errorf("ebpf: failed to load ebpf object: %w", err) } defer objs.Close() //nolint:errcheck // Create a link to the tcp_connect program. linkTCPConnect, err := link.AttachTracing(link.TracingOptions{ Program: objs.bpfPrograms.TcpConnect, }) if err != nil { return fmt.Errorf("ebpf: failed to attach to tcp_v4_connect: %w", err) } defer linkTCPConnect.Close() //nolint:errcheck // Create a link to the udp_v4_connect program. linkUDPV4, err := link.AttachTracing(link.TracingOptions{ Program: objs.bpfPrograms.UdpV4Connect, }) if err != nil { return fmt.Errorf("ebpf: failed to attach to udp_v4_connect: %w", err) } defer linkUDPV4.Close() //nolint:errcheck // Create a link to the udp_v6_connect program. linkUDPV6, err := link.AttachTracing(link.TracingOptions{ Program: objs.bpfPrograms.UdpV6Connect, }) if err != nil { return fmt.Errorf("ebpf: failed to attach to udp_v6_connect: %w", err) } defer linkUDPV6.Close() //nolint:errcheck // Create new reader to read events. rd, err := ringbuf.NewReader(objs.bpfMaps.PmConnectionEvents) if err != nil { return fmt.Errorf("ebpf: failed to open ring buffer: %w", err) } defer rd.Close() //nolint:errcheck // Start watcher to close the reader when the context is canceled. // TODO: Can we put this into a worker? go func() { <-ctx.Done() if err := rd.Close(); err != nil { log.Errorf("ebpf: failed closing ringbuf reader: %s", err) } }() for { // Read next event record, err := rd.Read() if err != nil { if errors.Is(err, ringbuf.ErrClosed) { // Normal return return nil } log.Errorf("ebpf: failed to read from ring buffer: %s", err) continue } var event bpfEvent // Parse the ringbuf event entry into a bpfEvent structure. if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.BigEndian, &event); err != nil { log.Errorf("ebpf: failed to parse ringbuf event: %s", err) continue } pkt := packet.NewInfoPacket(packet.Info{ Inbound: event.Direction == 1, InTunnel: false, Version: packet.IPVersion(event.IpVersion), Protocol: packet.IPProtocol(event.Protocol), SrcPort: event.Sport, DstPort: event.Dport, Src: convertArrayToIPv4(event.Saddr, packet.IPVersion(event.IpVersion)), Dst: convertArrayToIPv4(event.Daddr, packet.IPVersion(event.IpVersion)), PID: int(event.Pid), SeenAt: time.Now(), }) if isEventValid(event) { // DEBUG: // log.Debugf("ebpf: received valid connect event: PID: %d Conn: %s", pkt.Info().PID, pkt) packets <- pkt } else { log.Warningf("ebpf: received invalid connect event: PID: %d Conn: %s", pkt.Info().PID, pkt) } } } // loadBpfObjects_Ex loads eBPF objects with kernel-aware attach point selection. // (it extends the standard loadBpfObjects()) // // This enhanced loader automatically detects available kernel functions and selects // appropriate attach points for maximum compatibility across kernel versions. It handles // the transition from legacy function names (e.g., ip4_datagram_connect) to modern ones // (e.g., udp_connect) introduced in Linux 6.13+. func loadBpfObjects_Ex(objs interface{}, opts *ebpf.CollectionOptions) error { // Load pre-compiled programs spec, err := loadBpf() if err != nil { return fmt.Errorf("ebpf: failed to load ebpf spec: %w", err) } // Modify the attach points of the eBPF programs, if necessary. if err := modifyProgramsAttachPoints(spec); err != nil { return fmt.Errorf("ebpf: failed to modify program attach points: %w", err) } // Load the eBPF programs and maps into the kernel. if err := spec.LoadAndAssign(objs, opts); err != nil { return fmt.Errorf("ebpf: failed to load and assign ebpf objects: %w", err) } return nil } // modifyProgramAttachPoints modifies the attach points of the eBPF programs, if necessary. // This is needed to ensure compatibility with different kernel versions. func modifyProgramsAttachPoints(spec *ebpf.CollectionSpec) error { // Load the kernel spec kspec, err := btf.LoadKernelSpec() if err != nil { return err } // Function to update the attach point to a single BPF program updateIfNeeded := func(bpfProgramName, attachPoint, attachPointLegacy string) error { ps, ok := spec.Programs[bpfProgramName] if !ok { return fmt.Errorf("ebpf: program %q not found in spec", bpfProgramName) } var fn *btf.Func if err := kspec.TypeByName(attachPoint, &fn); err == nil { if !strings.EqualFold(ps.AttachTo, attachPoint) { ps.AttachTo = attachPoint log.Debugf("ebpf: using attach point %q for %q program", attachPoint, bpfProgramName) } } else { if !strings.EqualFold(ps.AttachTo, attachPointLegacy) { ps.AttachTo = attachPointLegacy log.Debugf("ebpf: using legacy attach point %q for %q program", attachPointLegacy, bpfProgramName) } } return nil } // 'udp_v4_connect' program is designed to attach to the `udp_connect` function in the kernel. // If the kernel does not support this function, we fall back to using the `ip4_datagram_connect` function. // // Kernel compatibility note: // - Linux kernels < 6.13: use `ip4_datagram_connect` function // https://elixir.bootlin.com/linux/v6.12.34/source/net/ipv4/udp.c#L2997 // - Linux kernels >= 6.13: function renamed to `udp_connect` // https://elixir.bootlin.com/linux/v6.13-rc1/source/net/ipv4/udp.c#L3131 const ( udpV4ConnectProgramName = "udp_v4_connect" udpV4ConnectAttachPoint = "udp_connect" udpV4ConnectAttachPointLegacy = "ip4_datagram_connect" ) if err := updateIfNeeded(udpV4ConnectProgramName, udpV4ConnectAttachPoint, udpV4ConnectAttachPointLegacy); err != nil { return err } // 'udp_v6_connect' program is designed to attach to the `udpv6_connect` function in the kernel. // If the kernel does not support this function, we fall back to using the `ip6_datagram_connect` function. // // Kernel compatibility note: // - Linux kernels < 6.13: use `ip6_datagram_connect` function // https://elixir.bootlin.com/linux/v6.12.34/source/net/ipv4/udp.c#L2997 // - Linux kernels >= 6.13: function renamed to `udpv6_connect` // https://elixir.bootlin.com/linux/v6.13-rc1/source/net/ipv4/udp.c#L3131 const ( udpV6ConnectProgramName = "udp_v6_connect" udpV6ConnectAttachPoint = "udpv6_connect" udpV6ConnectAttachPointLegacy = "ip6_datagram_connect" ) if err := updateIfNeeded(udpV6ConnectProgramName, udpV6ConnectAttachPoint, udpV6ConnectAttachPointLegacy); err != nil { return err } return nil } // isEventValid checks whether the given bpfEvent is valid or not. // It returns true if the event is valid, otherwise false. func isEventValid(event bpfEvent) bool { // Check if the destination port is 0 if event.Dport == 0 { return false } // Check if the source port is 0 if event.Sport == 0 { return false } // Check if the process ID is 0 if event.Pid == 0 { return false } // If the IP version is IPv4 if event.IpVersion == 4 { if event.Saddr[0] == 0 { return false } if event.Daddr[0] == 0 { return false } } return true } // convertArrayToIPv4 converts an array of uint32 values to an IPv4 net.IP address. func convertArrayToIPv4(input [4]uint32, ipVersion packet.IPVersion) net.IP { if ipVersion == packet.IPv4 { addressBuf := make([]byte, 4) binary.LittleEndian.PutUint32(addressBuf, input[0]) return net.IP(addressBuf) } addressBuf := make([]byte, 16) for i := range 4 { binary.LittleEndian.PutUint32(addressBuf[i*4:i*4+4], input[i]) } return net.IP(addressBuf) } ================================================ FILE: service/firewall/interception/ebpf/exec/bpf_bpfeb.go ================================================ // Code generated by bpf2go; DO NOT EDIT. //go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 package ebpf import ( "bytes" _ "embed" "fmt" "io" "github.com/cilium/ebpf" ) // loadBpf returns the embedded CollectionSpec for bpf. func loadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) spec, err := ebpf.LoadCollectionSpecFromReader(reader) if err != nil { return nil, fmt.Errorf("can't load bpf: %w", err) } return spec, err } // loadBpfObjects loads bpf and converts it into a struct. // // The following types are suitable as obj argument: // // *bpfObjects // *bpfPrograms // *bpfMaps // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { spec, err := loadBpf() if err != nil { return err } return spec.LoadAndAssign(obj, opts) } // bpfSpecs contains maps and programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfSpecs struct { bpfProgramSpecs bpfMapSpecs } // bpfSpecs contains programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfProgramSpecs struct { EnterExecve *ebpf.ProgramSpec `ebpf:"enter_execve"` } // bpfMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { PmExecMap *ebpf.MapSpec `ebpf:"pm_exec_map"` } // bpfObjects contains all objects after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfObjects struct { bpfPrograms bpfMaps } func (o *bpfObjects) Close() error { return _BpfClose( &o.bpfPrograms, &o.bpfMaps, ) } // bpfMaps contains all maps after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { PmExecMap *ebpf.Map `ebpf:"pm_exec_map"` } func (m *bpfMaps) Close() error { return _BpfClose( m.PmExecMap, ) } // bpfPrograms contains all programs after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfPrograms struct { EnterExecve *ebpf.Program `ebpf:"enter_execve"` } func (p *bpfPrograms) Close() error { return _BpfClose( p.EnterExecve, ) } func _BpfClose(closers ...io.Closer) error { for _, closer := range closers { if err := closer.Close(); err != nil { return err } } return nil } // Do not access this directly. // //go:embed bpf_bpfeb.o var _BpfBytes []byte ================================================ FILE: service/firewall/interception/ebpf/exec/bpf_bpfel.go ================================================ // Code generated by bpf2go; DO NOT EDIT. //go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64 package ebpf import ( "bytes" _ "embed" "fmt" "io" "github.com/cilium/ebpf" ) // loadBpf returns the embedded CollectionSpec for bpf. func loadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) spec, err := ebpf.LoadCollectionSpecFromReader(reader) if err != nil { return nil, fmt.Errorf("can't load bpf: %w", err) } return spec, err } // loadBpfObjects loads bpf and converts it into a struct. // // The following types are suitable as obj argument: // // *bpfObjects // *bpfPrograms // *bpfMaps // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { spec, err := loadBpf() if err != nil { return err } return spec.LoadAndAssign(obj, opts) } // bpfSpecs contains maps and programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfSpecs struct { bpfProgramSpecs bpfMapSpecs } // bpfSpecs contains programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfProgramSpecs struct { EnterExecve *ebpf.ProgramSpec `ebpf:"enter_execve"` } // bpfMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { PmExecMap *ebpf.MapSpec `ebpf:"pm_exec_map"` } // bpfObjects contains all objects after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfObjects struct { bpfPrograms bpfMaps } func (o *bpfObjects) Close() error { return _BpfClose( &o.bpfPrograms, &o.bpfMaps, ) } // bpfMaps contains all maps after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { PmExecMap *ebpf.Map `ebpf:"pm_exec_map"` } func (m *bpfMaps) Close() error { return _BpfClose( m.PmExecMap, ) } // bpfPrograms contains all programs after they have been loaded into the kernel. // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfPrograms struct { EnterExecve *ebpf.Program `ebpf:"enter_execve"` } func (p *bpfPrograms) Close() error { return _BpfClose( p.EnterExecve, ) } func _BpfClose(closers ...io.Closer) error { for _, closer := range closers { if err := closer.Close(); err != nil { return err } } return nil } // Do not access this directly. // //go:embed bpf_bpfel.o var _BpfBytes []byte ================================================ FILE: service/firewall/interception/ebpf/exec/exec.go ================================================ package ebpf import ( "bytes" "encoding/binary" "errors" "fmt" "io" "runtime" "runtime/debug" "strings" "sync" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/ringbuf" "github.com/cilium/ebpf/rlimit" "github.com/hashicorp/go-multierror" "golang.org/x/sys/unix" "github.com/safing/portmaster/base/log" ) //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" bpf ../programs/exec.c // These constants are defined in `bpf/handler.c` and must be kept in sync. const ( arglen = 32 argsize = 1024 ) var errTracerClosed = errors.New("tracer is closed") // event contains details about each exec call, sent from the eBPF program to // userspace through a perf ring buffer. This type must be kept in sync with // `event_t` in `bpf/handler.c`. type event struct { // Details about the process being launched. Filename [argsize]byte Argv [arglen][argsize]byte Argc uint32 UID uint32 GID uint32 PID uint32 // Name of the calling process. Comm [argsize]byte } // Event contains data about each exec event with many fields for easy // filtering and logging. type Event struct { Filename string `json:"filename"` // Argv contains the raw argv supplied to the process, including argv[0] // (which is equal to `filepath.Base(e.Filename)` in most circumstances). Argv []string `json:"argv"` // Truncated is true if we were unable to read all process arguments into // Argv because there were more than ARGLEN arguments. Truncated bool `json:"truncated"` // These values are of the new process. Keep in mind that the exec call may // fail and the PID will be released in such a case. PID uint32 `json:"pid"` UID uint32 `json:"uid"` GID uint32 `json:"gid"` // Comm is the "name" of the parent process, usually the filename of the // executable (but not always). Comm string `json:"comm"` } // Tracer is the exec tracer itself. // It must be closed after use. type Tracer struct { objs bpfObjects tp link.Link rb *ringbuf.Reader closeLock sync.Mutex closed chan struct{} } // New instantiates all of the BPF objects into the running kernel, starts // tracing, and returns the created Tracer. After calling this successfully, the // caller should immediately attach a for loop running `h.Read()`. // // The returned Tracer MUST be closed when not needed anymore otherwise kernel // resources may be leaked. func New() (*Tracer, error) { t := &Tracer{ tp: nil, rb: nil, closeLock: sync.Mutex{}, closed: make(chan struct{}), } if err := loadBpfObjects(&t.objs, nil); err != nil { return nil, fmt.Errorf("ebpf: failed to load ebpf object: %w", err) } if err := t.start(); err != nil { // Best effort. _ = t.Close() return nil, fmt.Errorf("start tracer: %w", err) } // It could be very bad if someone forgot to close this, so we'll try to // detect when it doesn't get closed and log a warning. stack := debug.Stack() runtime.SetFinalizer(t, func(t *Tracer) { err := t.Close() if errors.Is(err, errTracerClosed) { return } log.Infof("tracer was finalized but was not closed, created at: %s", stack) log.Infof("tracers must be closed when finished with to avoid leaked kernel resources") if err != nil { log.Errorf("closing tracer failed: %+v", err) } }) return t, nil } // start loads the eBPF programs and maps into the kernel and starts them. // You should immediately attach a for loop running `h.Read()` after calling // this successfully. func (t *Tracer) start() error { // If we don't startup successfully, we need to make sure all of the // stuff is cleaned up properly or we'll be leaking kernel resources. ok := false defer func() { if !ok { // Best effort. _ = t.Close() } }() // Allow the current process to lock memory for eBPF resources. This // does nothing on 5.11+ kernels which don't need this. err := rlimit.RemoveMemlock() if err != nil { return fmt.Errorf("remove memlock: %w", err) } // Attach the eBPF program to the `sys_enter_execve` tracepoint, which // is triggered at the beginning of each `execve()` syscall. t.tp, err = link.Tracepoint("syscalls", "sys_enter_execve", t.objs.EnterExecve, nil) if err != nil { return fmt.Errorf("open tracepoint: %w", err) } // Create the reader for the event ringbuf. t.rb, err = ringbuf.NewReader(t.objs.PmExecMap) if err != nil { return fmt.Errorf("open ringbuf reader: %w", err) } ok = true return nil } // Read reads an event from the eBPF program via the ringbuf, parses it and // returns it. If the *tracer is closed during the blocked call, and error that // wraps io.EOF will be returned. func (t *Tracer) Read() (*Event, error) { rb := t.rb if rb == nil { return nil, errors.New("ringbuf reader is not initialized, tracer may not be open or may have been closed") } record, err := rb.Read() if err != nil { if errors.Is(err, ringbuf.ErrClosed) { return nil, fmt.Errorf("tracer closed: %w", io.EOF) } return nil, fmt.Errorf("read from ringbuf: %w", err) } // Parse the ringbuf event entry into an event structure. var rawEvent event err = binary.Read(bytes.NewBuffer(record.RawSample), binary.NativeEndian, &rawEvent) if err != nil { return nil, fmt.Errorf("parse raw ringbuf entry into event struct: %w", err) } ev := &Event{ Filename: unix.ByteSliceToString(rawEvent.Filename[:]), Argv: []string{}, // populated below Truncated: rawEvent.Argc == arglen+1, PID: rawEvent.PID, UID: rawEvent.UID, GID: rawEvent.GID, Comm: unix.ByteSliceToString(rawEvent.Comm[:]), } // Copy only the args we're allowed to read from the array. If we read more // than rawEvent.Argc, we could be copying non-zeroed memory. argc := int(rawEvent.Argc) if argc > arglen { argc = arglen } for i := range argc { str := unix.ByteSliceToString(rawEvent.Argv[i][:]) if strings.TrimSpace(str) != "" { ev.Argv = append(ev.Argv, str) } } return ev, nil } // Close gracefully closes and frees all resources associated with the eBPF // tracepoints, maps and other resources. Any blocked `Read()` operations will // return an error that wraps `io.EOF`. func (t *Tracer) Close() error { t.closeLock.Lock() defer t.closeLock.Unlock() select { case <-t.closed: return errTracerClosed default: } close(t.closed) runtime.SetFinalizer(t, nil) // Close everything started in h.Start() in reverse order. var merr error if t.rb != nil { err := t.rb.Close() if err != nil { merr = multierror.Append(merr, fmt.Errorf("close ringbuf reader: %w", err)) } } if t.tp != nil { err := t.tp.Close() if err != nil { merr = multierror.Append(merr, fmt.Errorf("close tracepoint: %w", err)) } } err := t.objs.Close() if err != nil { merr = multierror.Append(merr, fmt.Errorf("close eBPF objects: %w", err)) } return merr } ================================================ FILE: service/firewall/interception/ebpf/programs/bandwidth.c ================================================ #include "vmlinux-x86.h" #include "bpf/bpf_helpers.h" #include "bpf/bpf_tracing.h" #include "bpf/bpf_core_read.h" #define AF_INET 2 #define AF_INET6 10 #define PROTOCOL_TCP 6 #define PROTOCOL_UDP 17 char __license[] SEC("license") = "GPL"; struct sk_key { u32 src_ip[4]; u32 dst_ip[4]; u16 src_port; u16 dst_port; u8 protocol; u8 ipv6; }; struct sk_info { u64 rx; u64 tx; u64 reported; }; // Max number of connections that will be kept. Increse the number if it's not enough. #define SOCKOPS_MAP_SIZE 5000 struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(max_entries, SOCKOPS_MAP_SIZE); __type(key, struct sk_key); __type(value, struct sk_info); } pm_bandwidth_map SEC(".maps"); SEC("sockops") int socket_operations(struct bpf_sock_ops *skops) { switch (skops->op) { case BPF_SOCK_OPS_TCP_CONNECT_CB: // Outgoing connections // Set flag so any modification on the socket, will trigger this function. bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_ALL_CB_FLAGS); return 0; case BPF_SOCK_OPS_TCP_LISTEN_CB: // Listening ports bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_ALL_CB_FLAGS); // No rx tx data for this socket object. return 0; case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: // Incoming connections // Set flag so any modification on the socket, will trigger this function. bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_ALL_CB_FLAGS); return 0; default: break; } struct bpf_sock *sk = skops->sk; if (sk == NULL) { return 0; } struct sk_key key = {0}; key.protocol = PROTOCOL_TCP; if(sk->family == AF_INET) { // Generate key for IPv4 key.src_ip[0] = sk->src_ip4; key.src_port = sk->src_port; key.dst_ip[0] = sk->dst_ip4; key.dst_port = __builtin_bswap16(sk->dst_port); key.ipv6 = 0; struct sk_info newInfo = {0}; newInfo.rx = skops->bytes_received; newInfo.tx = skops->bytes_acked; bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY); } else if(sk->family == AF_INET6){ // Generate key for IPv6 key.src_ip[0] = sk->src_ip6[0]; key.src_ip[1] = sk->src_ip6[1]; key.src_ip[2] = sk->src_ip6[2]; key.src_ip[3] = sk->src_ip6[3]; key.src_port = sk->src_port; key.dst_ip[0] = sk->dst_ip6[0]; key.dst_ip[1] = sk->dst_ip6[1]; key.dst_ip[2] = sk->dst_ip6[2]; key.dst_ip[3] = sk->dst_ip6[3]; key.dst_port = __builtin_bswap16(sk->dst_port); key.ipv6 = 1; struct sk_info newInfo = {0}; newInfo.rx = skops->bytes_received; newInfo.tx = skops->bytes_acked; bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY); } return 0; } // udp_sendmsg hookes to the respective kernel function and saves the bandwidth data SEC("fentry/udp_sendmsg") int BPF_PROG(udp_sendmsg, struct sock *sk, struct msghdr *msg, size_t len) { struct sock_common *skc = &sk->__sk_common; // Create a key for the map and set all the nececery information. struct sk_key key = {0}; key.protocol = PROTOCOL_UDP; key.src_ip[0] = skc->skc_rcv_saddr; key.dst_ip[0] = skc->skc_daddr; key.src_port = skc->skc_num; key.dst_port = __builtin_bswap16(skc->skc_dport); key.ipv6 = 0; // Update the map with the new information struct sk_info *info = bpf_map_lookup_elem(&pm_bandwidth_map, &key); if (info != NULL) { __sync_fetch_and_add(&info->tx, len); // TODO: Use atomic instead. __sync_fetch_and_and(&info->reported, 0); // TODO: Use atomic instead. } else { struct sk_info newInfo = {0}; newInfo.tx = len; bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY); } return 0; }; // udp_recvmsg hookes to the respective kernel function and saves the bandwidth data SEC("fentry/udp_recvmsg") int BPF_PROG(udp_recvmsg, struct sock *sk, struct msghdr *msg, size_t len, int flags, int *addr_len) { struct sock_common *skc = &sk->__sk_common; // Create a key for the map and set all the nececery information. struct sk_key key = {0}; key.protocol = PROTOCOL_UDP; key.src_ip[0] = skc->skc_rcv_saddr; key.dst_ip[0] = skc->skc_daddr; key.src_port = skc->skc_num; key.dst_port = __builtin_bswap16(skc->skc_dport); key.ipv6 = 0; // Update the map with the new information struct sk_info *info = bpf_map_lookup_elem(&pm_bandwidth_map, &key); if (info != NULL) { __sync_fetch_and_add(&info->rx, len); // TODO: Use atomic instead. __sync_fetch_and_and(&info->reported, 0); // TODO: Use atomic instead. } else { struct sk_info newInfo = {0}; newInfo.rx = len; bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY); } return 0; }; // udpv6_sendmsg hookes to the respective kernel function and saves the bandwidth data SEC("fentry/udpv6_sendmsg") int BPF_PROG(udpv6_sendmsg, struct sock *sk, struct msghdr *msg, size_t len) { struct sock_common *skc = &sk->__sk_common; // Create a key for the map and set all the nececery information. struct sk_key key = {0}; key.protocol = PROTOCOL_UDP; for (int i = 0; i < 4; i++) { key.src_ip[i] = skc->skc_v6_rcv_saddr.in6_u.u6_addr32[i]; key.dst_ip[i] = skc->skc_v6_rcv_saddr.in6_u.u6_addr32[i]; } key.src_port = skc->skc_num; key.dst_port = __builtin_bswap16(skc->skc_dport); key.ipv6 = 1; // Update the map with the new information struct sk_info *info = bpf_map_lookup_elem(&pm_bandwidth_map, &key); if (info != NULL) { __sync_fetch_and_add(&info->tx, len); // TODO: Use atomic instead. __sync_fetch_and_and(&info->reported, 0); // TODO: Use atomic instead. } else { struct sk_info newInfo = {0}; newInfo.tx = len; bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY); } return 0; } // udpv6_recvmsg hookes to the respective kernel function and saves the bandwidth data SEC("fentry/udpv6_recvmsg") int BPF_PROG(udpv6_recvmsg, struct sock *sk, struct msghdr *msg, size_t len, int flags, int *addr_len) { struct sock_common *skc = &sk->__sk_common; // Create a key for the map and set all the nececery information. struct sk_key key = {0}; key.protocol = PROTOCOL_UDP; for (int i = 0; i < 4; i++) { key.src_ip[i] = skc->skc_v6_rcv_saddr.in6_u.u6_addr32[i]; key.dst_ip[i] = skc->skc_v6_rcv_saddr.in6_u.u6_addr32[i]; } key.src_port = skc->skc_num; key.dst_port = __builtin_bswap16(skc->skc_dport); key.ipv6 = 1; // Update the map with the new information struct sk_info *info = bpf_map_lookup_elem(&pm_bandwidth_map, &key); if (info != NULL) { __sync_fetch_and_add(&info->rx, len); // TODO: Use atomic instead. __sync_fetch_and_and(&info->reported, 0); // TODO: Use atomic instead. } else { struct sk_info newInfo = {0}; newInfo.rx = len; bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY); } return 0; } ================================================ FILE: service/firewall/interception/ebpf/programs/bpf/bpf_core_read.h ================================================ /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ #ifndef __BPF_CORE_READ_H__ #define __BPF_CORE_READ_H__ /* * enum bpf_field_info_kind is passed as a second argument into * __builtin_preserve_field_info() built-in to get a specific aspect of * a field, captured as a first argument. __builtin_preserve_field_info(field, * info_kind) returns __u32 integer and produces BTF field relocation, which * is understood and processed by libbpf during BPF object loading. See * selftests/bpf for examples. */ enum bpf_field_info_kind { BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */ BPF_FIELD_BYTE_SIZE = 1, BPF_FIELD_EXISTS = 2, /* field existence in target kernel */ BPF_FIELD_SIGNED = 3, BPF_FIELD_LSHIFT_U64 = 4, BPF_FIELD_RSHIFT_U64 = 5, }; /* second argument to __builtin_btf_type_id() built-in */ enum bpf_type_id_kind { BPF_TYPE_ID_LOCAL = 0, /* BTF type ID in local program */ BPF_TYPE_ID_TARGET = 1, /* BTF type ID in target kernel */ }; /* second argument to __builtin_preserve_type_info() built-in */ enum bpf_type_info_kind { BPF_TYPE_EXISTS = 0, /* type existence in target kernel */ BPF_TYPE_SIZE = 1, /* type size in target kernel */ BPF_TYPE_MATCHES = 2, /* type match in target kernel */ }; /* second argument to __builtin_preserve_enum_value() built-in */ enum bpf_enum_value_kind { BPF_ENUMVAL_EXISTS = 0, /* enum value existence in kernel */ BPF_ENUMVAL_VALUE = 1, /* enum value value relocation */ }; #define __CORE_RELO(src, field, info) \ __builtin_preserve_field_info((src)->field, BPF_FIELD_##info) #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \ bpf_probe_read_kernel( \ (void *)dst, \ __CORE_RELO(src, fld, BYTE_SIZE), \ (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET)) #else /* semantics of LSHIFT_64 assumes loading values into low-ordered bytes, so * for big-endian we need to adjust destination pointer accordingly, based on * field byte size */ #define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \ bpf_probe_read_kernel( \ (void *)dst + (8 - __CORE_RELO(src, fld, BYTE_SIZE)), \ __CORE_RELO(src, fld, BYTE_SIZE), \ (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET)) #endif /* * Extract bitfield, identified by s->field, and return its value as u64. * All this is done in relocatable manner, so bitfield changes such as * signedness, bit size, offset changes, this will be handled automatically. * This version of macro is using bpf_probe_read_kernel() to read underlying * integer storage. Macro functions as an expression and its return type is * bpf_probe_read_kernel()'s return value: 0, on success, <0 on error. */ #define BPF_CORE_READ_BITFIELD_PROBED(s, field) ({ \ unsigned long long val = 0; \ \ __CORE_BITFIELD_PROBE_READ(&val, s, field); \ val <<= __CORE_RELO(s, field, LSHIFT_U64); \ if (__CORE_RELO(s, field, SIGNED)) \ val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \ else \ val = val >> __CORE_RELO(s, field, RSHIFT_U64); \ val; \ }) /* * Extract bitfield, identified by s->field, and return its value as u64. * This version of macro is using direct memory reads and should be used from * BPF program types that support such functionality (e.g., typed raw * tracepoints). */ #define BPF_CORE_READ_BITFIELD(s, field) ({ \ const void *p = (const void *)s + __CORE_RELO(s, field, BYTE_OFFSET); \ unsigned long long val; \ \ /* This is a so-called barrier_var() operation that makes specified \ * variable "a black box" for optimizing compiler. \ * It forces compiler to perform BYTE_OFFSET relocation on p and use \ * its calculated value in the switch below, instead of applying \ * the same relocation 4 times for each individual memory load. \ */ \ asm volatile("" : "=r"(p) : "0"(p)); \ \ switch (__CORE_RELO(s, field, BYTE_SIZE)) { \ case 1: val = *(const unsigned char *)p; break; \ case 2: val = *(const unsigned short *)p; break; \ case 4: val = *(const unsigned int *)p; break; \ case 8: val = *(const unsigned long long *)p; break; \ } \ val <<= __CORE_RELO(s, field, LSHIFT_U64); \ if (__CORE_RELO(s, field, SIGNED)) \ val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \ else \ val = val >> __CORE_RELO(s, field, RSHIFT_U64); \ val; \ }) #define ___bpf_field_ref1(field) (field) #define ___bpf_field_ref2(type, field) (((typeof(type) *)0)->field) #define ___bpf_field_ref(args...) \ ___bpf_apply(___bpf_field_ref, ___bpf_narg(args))(args) /* * Convenience macro to check that field actually exists in target kernel's. * Returns: * 1, if matching field is present in target kernel; * 0, if no matching field found. * * Supports two forms: * - field reference through variable access: * bpf_core_field_exists(p->my_field); * - field reference through type and field names: * bpf_core_field_exists(struct my_type, my_field). */ #define bpf_core_field_exists(field...) \ __builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_EXISTS) /* * Convenience macro to get the byte size of a field. Works for integers, * struct/unions, pointers, arrays, and enums. * * Supports two forms: * - field reference through variable access: * bpf_core_field_size(p->my_field); * - field reference through type and field names: * bpf_core_field_size(struct my_type, my_field). */ #define bpf_core_field_size(field...) \ __builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_BYTE_SIZE) /* * Convenience macro to get field's byte offset. * * Supports two forms: * - field reference through variable access: * bpf_core_field_offset(p->my_field); * - field reference through type and field names: * bpf_core_field_offset(struct my_type, my_field). */ #define bpf_core_field_offset(field...) \ __builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_BYTE_OFFSET) /* * Convenience macro to get BTF type ID of a specified type, using a local BTF * information. Return 32-bit unsigned integer with type ID from program's own * BTF. Always succeeds. */ #define bpf_core_type_id_local(type) \ __builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_LOCAL) /* * Convenience macro to get BTF type ID of a target kernel's type that matches * specified local type. * Returns: * - valid 32-bit unsigned type ID in kernel BTF; * - 0, if no matching type was found in a target kernel BTF. */ #define bpf_core_type_id_kernel(type) \ __builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_TARGET) /* * Convenience macro to check that provided named type * (struct/union/enum/typedef) exists in a target kernel. * Returns: * 1, if such type is present in target kernel's BTF; * 0, if no matching type is found. */ #define bpf_core_type_exists(type) \ __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_EXISTS) /* * Convenience macro to check that provided named type * (struct/union/enum/typedef) "matches" that in a target kernel. * Returns: * 1, if the type matches in the target kernel's BTF; * 0, if the type does not match any in the target kernel */ #define bpf_core_type_matches(type) \ __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_MATCHES) /* * Convenience macro to get the byte size of a provided named type * (struct/union/enum/typedef) in a target kernel. * Returns: * >= 0 size (in bytes), if type is present in target kernel's BTF; * 0, if no matching type is found. */ #define bpf_core_type_size(type) \ __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_SIZE) /* * Convenience macro to check that provided enumerator value is defined in * a target kernel. * Returns: * 1, if specified enum type and its enumerator value are present in target * kernel's BTF; * 0, if no matching enum and/or enum value within that enum is found. */ #define bpf_core_enum_value_exists(enum_type, enum_value) \ __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_EXISTS) /* * Convenience macro to get the integer value of an enumerator value in * a target kernel. * Returns: * 64-bit value, if specified enum type and its enumerator value are * present in target kernel's BTF; * 0, if no matching enum and/or enum value within that enum is found. */ #define bpf_core_enum_value(enum_type, enum_value) \ __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_VALUE) /* * bpf_core_read() abstracts away bpf_probe_read_kernel() call and captures * offset relocation for source address using __builtin_preserve_access_index() * built-in, provided by Clang. * * __builtin_preserve_access_index() takes as an argument an expression of * taking an address of a field within struct/union. It makes compiler emit * a relocation, which records BTF type ID describing root struct/union and an * accessor string which describes exact embedded field that was used to take * an address. See detailed description of this relocation format and * semantics in comments to struct bpf_field_reloc in libbpf_internal.h. * * This relocation allows libbpf to adjust BPF instruction to use correct * actual field offset, based on target kernel BTF type that matches original * (local) BTF, used to record relocation. */ #define bpf_core_read(dst, sz, src) \ bpf_probe_read_kernel(dst, sz, (const void *)__builtin_preserve_access_index(src)) /* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define bpf_core_read_user(dst, sz, src) \ bpf_probe_read_user(dst, sz, (const void *)__builtin_preserve_access_index(src)) /* * bpf_core_read_str() is a thin wrapper around bpf_probe_read_str() * additionally emitting BPF CO-RE field relocation for specified source * argument. */ #define bpf_core_read_str(dst, sz, src) \ bpf_probe_read_kernel_str(dst, sz, (const void *)__builtin_preserve_access_index(src)) /* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define bpf_core_read_user_str(dst, sz, src) \ bpf_probe_read_user_str(dst, sz, (const void *)__builtin_preserve_access_index(src)) #define ___concat(a, b) a ## b #define ___apply(fn, n) ___concat(fn, n) #define ___nth(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, __11, N, ...) N /* * return number of provided arguments; used for switch-based variadic macro * definitions (see ___last, ___arrow, etc below) */ #define ___narg(...) ___nth(_, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) /* * return 0 if no arguments are passed, N - otherwise; used for * recursively-defined macros to specify termination (0) case, and generic * (N) case (e.g., ___read_ptrs, ___core_read) */ #define ___empty(...) ___nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0) #define ___last1(x) x #define ___last2(a, x) x #define ___last3(a, b, x) x #define ___last4(a, b, c, x) x #define ___last5(a, b, c, d, x) x #define ___last6(a, b, c, d, e, x) x #define ___last7(a, b, c, d, e, f, x) x #define ___last8(a, b, c, d, e, f, g, x) x #define ___last9(a, b, c, d, e, f, g, h, x) x #define ___last10(a, b, c, d, e, f, g, h, i, x) x #define ___last(...) ___apply(___last, ___narg(__VA_ARGS__))(__VA_ARGS__) #define ___nolast2(a, _) a #define ___nolast3(a, b, _) a, b #define ___nolast4(a, b, c, _) a, b, c #define ___nolast5(a, b, c, d, _) a, b, c, d #define ___nolast6(a, b, c, d, e, _) a, b, c, d, e #define ___nolast7(a, b, c, d, e, f, _) a, b, c, d, e, f #define ___nolast8(a, b, c, d, e, f, g, _) a, b, c, d, e, f, g #define ___nolast9(a, b, c, d, e, f, g, h, _) a, b, c, d, e, f, g, h #define ___nolast10(a, b, c, d, e, f, g, h, i, _) a, b, c, d, e, f, g, h, i #define ___nolast(...) ___apply(___nolast, ___narg(__VA_ARGS__))(__VA_ARGS__) #define ___arrow1(a) a #define ___arrow2(a, b) a->b #define ___arrow3(a, b, c) a->b->c #define ___arrow4(a, b, c, d) a->b->c->d #define ___arrow5(a, b, c, d, e) a->b->c->d->e #define ___arrow6(a, b, c, d, e, f) a->b->c->d->e->f #define ___arrow7(a, b, c, d, e, f, g) a->b->c->d->e->f->g #define ___arrow8(a, b, c, d, e, f, g, h) a->b->c->d->e->f->g->h #define ___arrow9(a, b, c, d, e, f, g, h, i) a->b->c->d->e->f->g->h->i #define ___arrow10(a, b, c, d, e, f, g, h, i, j) a->b->c->d->e->f->g->h->i->j #define ___arrow(...) ___apply(___arrow, ___narg(__VA_ARGS__))(__VA_ARGS__) #define ___type(...) typeof(___arrow(__VA_ARGS__)) #define ___read(read_fn, dst, src_type, src, accessor) \ read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor) /* "recursively" read a sequence of inner pointers using local __t var */ #define ___rd_first(fn, src, a) ___read(fn, &__t, ___type(src), src, a); #define ___rd_last(fn, ...) \ ___read(fn, &__t, ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__)); #define ___rd_p1(fn, ...) const void *__t; ___rd_first(fn, __VA_ARGS__) #define ___rd_p2(fn, ...) ___rd_p1(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p3(fn, ...) ___rd_p2(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p4(fn, ...) ___rd_p3(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p5(fn, ...) ___rd_p4(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p6(fn, ...) ___rd_p5(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p7(fn, ...) ___rd_p6(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p8(fn, ...) ___rd_p7(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p9(fn, ...) ___rd_p8(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___read_ptrs(fn, src, ...) \ ___apply(___rd_p, ___narg(__VA_ARGS__))(fn, src, __VA_ARGS__) #define ___core_read0(fn, fn_ptr, dst, src, a) \ ___read(fn, dst, ___type(src), src, a); #define ___core_readN(fn, fn_ptr, dst, src, ...) \ ___read_ptrs(fn_ptr, src, ___nolast(__VA_ARGS__)) \ ___read(fn, dst, ___type(src, ___nolast(__VA_ARGS__)), __t, \ ___last(__VA_ARGS__)); #define ___core_read(fn, fn_ptr, dst, src, a, ...) \ ___apply(___core_read, ___empty(__VA_ARGS__))(fn, fn_ptr, dst, \ src, a, ##__VA_ARGS__) /* * BPF_CORE_READ_INTO() is a more performance-conscious variant of * BPF_CORE_READ(), in which final field is read into user-provided storage. * See BPF_CORE_READ() below for more details on general usage. */ #define BPF_CORE_READ_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read, bpf_core_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * Variant of BPF_CORE_READ_INTO() for reading from user-space memory. * * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define BPF_CORE_READ_USER_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read_user, bpf_core_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* Non-CO-RE variant of BPF_CORE_READ_INTO() */ #define BPF_PROBE_READ_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_kernel, bpf_probe_read_kernel, \ dst, (src), a, ##__VA_ARGS__) \ }) /* Non-CO-RE variant of BPF_CORE_READ_USER_INTO(). * * As no CO-RE relocations are emitted, source types can be arbitrary and are * not restricted to kernel types only. */ #define BPF_PROBE_READ_USER_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_user, bpf_probe_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * BPF_CORE_READ_STR_INTO() does same "pointer chasing" as * BPF_CORE_READ() for intermediate pointers, but then executes (and returns * corresponding error code) bpf_core_read_str() for final string read. */ #define BPF_CORE_READ_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read_str, bpf_core_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * Variant of BPF_CORE_READ_STR_INTO() for reading from user-space memory. * * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define BPF_CORE_READ_USER_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read_user_str, bpf_core_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* Non-CO-RE variant of BPF_CORE_READ_STR_INTO() */ #define BPF_PROBE_READ_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_kernel_str, bpf_probe_read_kernel, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * Non-CO-RE variant of BPF_CORE_READ_USER_STR_INTO(). * * As no CO-RE relocations are emitted, source types can be arbitrary and are * not restricted to kernel types only. */ #define BPF_PROBE_READ_USER_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_user_str, bpf_probe_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * BPF_CORE_READ() is used to simplify BPF CO-RE relocatable read, especially * when there are few pointer chasing steps. * E.g., what in non-BPF world (or in BPF w/ BCC) would be something like: * int x = s->a.b.c->d.e->f->g; * can be succinctly achieved using BPF_CORE_READ as: * int x = BPF_CORE_READ(s, a.b.c, d.e, f, g); * * BPF_CORE_READ will decompose above statement into 4 bpf_core_read (BPF * CO-RE relocatable bpf_probe_read_kernel() wrapper) calls, logically * equivalent to: * 1. const void *__t = s->a.b.c; * 2. __t = __t->d.e; * 3. __t = __t->f; * 4. return __t->g; * * Equivalence is logical, because there is a heavy type casting/preservation * involved, as well as all the reads are happening through * bpf_probe_read_kernel() calls using __builtin_preserve_access_index() to * emit CO-RE relocations. * * N.B. Only up to 9 "field accessors" are supported, which should be more * than enough for any practical purpose. */ #define BPF_CORE_READ(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_CORE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) /* * Variant of BPF_CORE_READ() for reading from user-space memory. * * NOTE: all the source types involved are still *kernel types* and need to * exist in kernel (or kernel module) BTF, otherwise CO-RE relocation will * fail. Custom user types are not relocatable with CO-RE. * The typical situation in which BPF_CORE_READ_USER() might be used is to * read kernel UAPI types from the user-space memory passed in as a syscall * input argument. */ #define BPF_CORE_READ_USER(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_CORE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) /* Non-CO-RE variant of BPF_CORE_READ() */ #define BPF_PROBE_READ(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_PROBE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) /* * Non-CO-RE variant of BPF_CORE_READ_USER(). * * As no CO-RE relocations are emitted, source types can be arbitrary and are * not restricted to kernel types only. */ #define BPF_PROBE_READ_USER(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_PROBE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) #endif ================================================ FILE: service/firewall/interception/ebpf/programs/bpf/bpf_helper_defs.h ================================================ /* This is auto-generated file. See bpf_doc.py for details. */ /* Forward declarations of BPF structs */ struct bpf_fib_lookup; struct bpf_sk_lookup; struct bpf_perf_event_data; struct bpf_perf_event_value; struct bpf_pidns_info; struct bpf_redir_neigh; struct bpf_sock; struct bpf_sock_addr; struct bpf_sock_ops; struct bpf_sock_tuple; struct bpf_spin_lock; struct bpf_sysctl; struct bpf_tcp_sock; struct bpf_tunnel_key; struct bpf_xfrm_state; struct linux_binprm; struct pt_regs; struct sk_reuseport_md; struct sockaddr; struct tcphdr; struct seq_file; struct tcp6_sock; struct tcp_sock; struct tcp_timewait_sock; struct tcp_request_sock; struct udp6_sock; struct unix_sock; struct task_struct; struct cgroup; struct __sk_buff; struct sk_msg_md; struct xdp_md; struct path; struct btf_ptr; struct inode; struct socket; struct file; struct bpf_timer; struct mptcp_sock; struct bpf_dynptr; struct iphdr; struct ipv6hdr; /* * bpf_map_lookup_elem * * Perform a lookup in *map* for an entry associated to *key*. * * Returns * Map value associated to *key*, or **NULL** if no entry was * found. */ static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; /* * bpf_map_update_elem * * Add or update the value of the entry associated to *key* in * *map* with *value*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * Flag value **BPF_NOEXIST** cannot be used for maps of types * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all * elements always exist), the helper would return an error. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, __u64 flags) = (void *) 2; /* * bpf_map_delete_elem * * Delete entry with *key* from *map*. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_map_delete_elem)(void *map, const void *key) = (void *) 3; /* * bpf_probe_read * * For tracing programs, safely attempt to read *size* bytes from * kernel space address *unsafe_ptr* and store the data in *dst*. * * Generally, use **bpf_probe_read_user**\ () or * **bpf_probe_read_kernel**\ () instead. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_probe_read)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 4; /* * bpf_ktime_get_ns * * Return the time elapsed since system boot, in nanoseconds. * Does not include time the system was suspended. * See: **clock_gettime**\ (**CLOCK_MONOTONIC**) * * Returns * Current *ktime*. */ static __u64 (*bpf_ktime_get_ns)(void) = (void *) 5; /* * bpf_trace_printk * * This helper is a "printk()-like" facility for debugging. It * prints a message defined by format *fmt* (of size *fmt_size*) * to file *\/sys/kernel/tracing/trace* from TraceFS, if * available. It can take up to three additional **u64** * arguments (as an eBPF helpers, the total number of arguments is * limited to five). * * Each time the helper is called, it appends a line to the trace. * Lines are discarded while *\/sys/kernel/tracing/trace* is * open, use *\/sys/kernel/tracing/trace_pipe* to avoid this. * The format of the trace is customizable, and the exact output * one will get depends on the options set in * *\/sys/kernel/tracing/trace_options* (see also the * *README* file under the same directory). However, it usually * defaults to something like: * * :: * * telnet-470 [001] .N.. 419421.045894: 0x00000001: * * In the above: * * * ``telnet`` is the name of the current task. * * ``470`` is the PID of the current task. * * ``001`` is the CPU number on which the task is * running. * * In ``.N..``, each character refers to a set of * options (whether irqs are enabled, scheduling * options, whether hard/softirqs are running, level of * preempt_disabled respectively). **N** means that * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED** * are set. * * ``419421.045894`` is a timestamp. * * ``0x00000001`` is a fake value used by BPF for the * instruction pointer register. * * ```` is the message formatted with * *fmt*. * * The conversion specifiers supported by *fmt* are similar, but * more limited than for printk(). They are **%d**, **%i**, * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size * of field, padding with zeroes, etc.) is available, and the * helper will return **-EINVAL** (but print nothing) if it * encounters an unknown specifier. * * Also, note that **bpf_trace_printk**\ () is slow, and should * only be used for debugging purposes. For this reason, a notice * block (spanning several lines) is printed to kernel logs and * states that the helper should not be used "for production use" * the first time this helper is used (or more precisely, when * **trace_printk**\ () buffers are allocated). For passing values * to user space, perf events should be preferred. * * Returns * The number of bytes written to the buffer, or a negative error * in case of failure. */ static long (*bpf_trace_printk)(const char *fmt, __u32 fmt_size, ...) = (void *) 6; /* * bpf_get_prandom_u32 * * Get a pseudo-random number. * * From a security point of view, this helper uses its own * pseudo-random internal state, and cannot be used to infer the * seed of other random functions in the kernel. However, it is * essential to note that the generator used by the helper is not * cryptographically secure. * * Returns * A random 32-bit unsigned value. */ static __u32 (*bpf_get_prandom_u32)(void) = (void *) 7; /* * bpf_get_smp_processor_id * * Get the SMP (symmetric multiprocessing) processor id. Note that * all programs run with migration disabled, which means that the * SMP processor id is stable during all the execution of the * program. * * Returns * The SMP id of the processor running the program. */ static __u32 (*bpf_get_smp_processor_id)(void) = (void *) 8; /* * bpf_skb_store_bytes * * Store *len* bytes from address *from* into the packet * associated to *skb*, at *offset*. *flags* are a combination of * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the * checksum for the packet after storing the bytes) and * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\ * **->swhash** and *skb*\ **->l4hash** to 0). * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len, __u64 flags) = (void *) 9; /* * bpf_l3_csum_replace * * Recompute the layer 3 (e.g. IP) checksum for the packet * associated to *skb*. Computation is incremental, so the helper * must know the former value of the header field that was * modified (*from*), the new value of this field (*to*), and the * number of bytes (2 or 4) for this field, stored in *size*. * Alternatively, it is possible to store the difference between * the previous and the new values of the header field in *to*, by * setting *from* and *size* to 0. For both methods, *offset* * indicates the location of the IP checksum within the packet. * * This helper works in combination with **bpf_csum_diff**\ (), * which does not update the checksum in-place, but offers more * flexibility and can handle sizes larger than 2 or 4 for the * checksum to update. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_l3_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 size) = (void *) 10; /* * bpf_l4_csum_replace * * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the * packet associated to *skb*. Computation is incremental, so the * helper must know the former value of the header field that was * modified (*from*), the new value of this field (*to*), and the * number of bytes (2 or 4) for this field, stored on the lowest * four bits of *flags*. Alternatively, it is possible to store * the difference between the previous and the new values of the * header field in *to*, by setting *from* and the four lowest * bits of *flags* to 0. For both methods, *offset* indicates the * location of the IP checksum within the packet. In addition to * the size of the field, *flags* can be added (bitwise OR) actual * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and * for updates resulting in a null checksum the value is set to * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates * the checksum is to be computed against a pseudo-header. * * This helper works in combination with **bpf_csum_diff**\ (), * which does not update the checksum in-place, but offers more * flexibility and can handle sizes larger than 2 or 4 for the * checksum to update. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_l4_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 flags) = (void *) 11; /* * bpf_tail_call * * This special helper is used to trigger a "tail call", or in * other words, to jump into another eBPF program. The same stack * frame is used (but values on stack and in registers for the * caller are not accessible to the callee). This mechanism allows * for program chaining, either for raising the maximum number of * available eBPF instructions, or to execute given programs in * conditional blocks. For security reasons, there is an upper * limit to the number of successive tail calls that can be * performed. * * Upon call of this helper, the program attempts to jump into a * program referenced at index *index* in *prog_array_map*, a * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes * *ctx*, a pointer to the context. * * If the call succeeds, the kernel immediately runs the first * instruction of the new program. This is not a function call, * and it never returns to the previous program. If the call * fails, then the helper has no effect, and the caller continues * to run its subsequent instructions. A call can fail if the * destination program for the jump does not exist (i.e. *index* * is superior to the number of entries in *prog_array_map*), or * if the maximum number of tail calls has been reached for this * chain of programs. This limit is defined in the kernel by the * macro **MAX_TAIL_CALL_CNT** (not accessible to user space), * which is currently set to 33. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_tail_call)(void *ctx, void *prog_array_map, __u32 index) = (void *) 12; /* * bpf_clone_redirect * * Clone and redirect the packet associated to *skb* to another * net device of index *ifindex*. Both ingress and egress * interfaces can be used for redirection. The **BPF_F_INGRESS** * value in *flags* is used to make the distinction (ingress path * is selected if the flag is present, egress path otherwise). * This is the only flag supported for now. * * In comparison with **bpf_redirect**\ () helper, * **bpf_clone_redirect**\ () has the associated cost of * duplicating the packet buffer, but this can be executed out of * the eBPF program. Conversely, **bpf_redirect**\ () is more * efficient, but it is handled through an action code where the * redirection happens only after the eBPF program has returned. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_clone_redirect)(struct __sk_buff *skb, __u32 ifindex, __u64 flags) = (void *) 13; /* * bpf_get_current_pid_tgid * * Get the current pid and tgid. * * Returns * A 64-bit integer containing the current tgid and pid, and * created as such: * *current_task*\ **->tgid << 32 \|** * *current_task*\ **->pid**. */ static __u64 (*bpf_get_current_pid_tgid)(void) = (void *) 14; /* * bpf_get_current_uid_gid * * Get the current uid and gid. * * Returns * A 64-bit integer containing the current GID and UID, and * created as such: *current_gid* **<< 32 \|** *current_uid*. */ static __u64 (*bpf_get_current_uid_gid)(void) = (void *) 15; /* * bpf_get_current_comm * * Copy the **comm** attribute of the current task into *buf* of * *size_of_buf*. The **comm** attribute contains the name of * the executable (excluding the path) for the current task. The * *size_of_buf* must be strictly positive. On success, the * helper makes sure that the *buf* is NUL-terminated. On failure, * it is filled with zeroes. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_get_current_comm)(void *buf, __u32 size_of_buf) = (void *) 16; /* * bpf_get_cgroup_classid * * Retrieve the classid for the current task, i.e. for the net_cls * cgroup to which *skb* belongs. * * This helper can be used on TC egress path, but not on ingress. * * The net_cls cgroup provides an interface to tag network packets * based on a user-provided identifier for all traffic coming from * the tasks belonging to the related cgroup. See also the related * kernel documentation, available from the Linux sources in file * *Documentation/admin-guide/cgroup-v1/net_cls.rst*. * * The Linux kernel has two versions for cgroups: there are * cgroups v1 and cgroups v2. Both are available to users, who can * use a mixture of them, but note that the net_cls cgroup is for * cgroup v1 only. This makes it incompatible with BPF programs * run on cgroups, which is a cgroup-v2-only feature (a socket can * only hold data for one version of cgroups at a time). * * This helper is only available is the kernel was compiled with * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to * "**y**" or to "**m**". * * Returns * The classid, or 0 for the default unconfigured classid. */ static __u32 (*bpf_get_cgroup_classid)(struct __sk_buff *skb) = (void *) 17; /* * bpf_skb_vlan_push * * Push a *vlan_tci* (VLAN tag control information) of protocol * *vlan_proto* to the packet associated to *skb*, then update * the checksum. Note that if *vlan_proto* is different from * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to * be **ETH_P_8021Q**. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_vlan_push)(struct __sk_buff *skb, __be16 vlan_proto, __u16 vlan_tci) = (void *) 18; /* * bpf_skb_vlan_pop * * Pop a VLAN header from the packet associated to *skb*. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_vlan_pop)(struct __sk_buff *skb) = (void *) 19; /* * bpf_skb_get_tunnel_key * * Get tunnel metadata. This helper takes a pointer *key* to an * empty **struct bpf_tunnel_key** of **size**, that will be * filled with tunnel metadata for the packet associated to *skb*. * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which * indicates that the tunnel is based on IPv6 protocol instead of * IPv4. * * The **struct bpf_tunnel_key** is an object that generalizes the * principal parameters used by various tunneling protocols into a * single struct. This way, it can be used to easily make a * decision based on the contents of the encapsulation header, * "summarized" in this struct. In particular, it holds the IP * address of the remote end (IPv4 or IPv6, depending on the case) * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also, * this struct exposes the *key*\ **->tunnel_id**, which is * generally mapped to a VNI (Virtual Network Identifier), making * it programmable together with the **bpf_skb_set_tunnel_key**\ * () helper. * * Let's imagine that the following code is part of a program * attached to the TC ingress interface, on one end of a GRE * tunnel, and is supposed to filter out all messages coming from * remote ends with IPv4 address other than 10.0.0.1: * * :: * * int ret; * struct bpf_tunnel_key key = {}; * * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0); * if (ret < 0) * return TC_ACT_SHOT; // drop packet * * if (key.remote_ipv4 != 0x0a000001) * return TC_ACT_SHOT; // drop packet * * return TC_ACT_OK; // accept packet * * This interface can also be used with all encapsulation devices * that can operate in "collect metadata" mode: instead of having * one network device per specific configuration, the "collect * metadata" mode only requires a single device where the * configuration can be extracted from this helper. * * This can be used together with various tunnels such as VXLan, * Geneve, GRE or IP in IP (IPIP). * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_get_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 20; /* * bpf_skb_set_tunnel_key * * Populate tunnel metadata for packet associated to *skb.* The * tunnel metadata is set to the contents of *key*, of *size*. The * *flags* can be set to a combination of the following values: * * **BPF_F_TUNINFO_IPV6** * Indicate that the tunnel is based on IPv6 protocol * instead of IPv4. * **BPF_F_ZERO_CSUM_TX** * For IPv4 packets, add a flag to tunnel metadata * indicating that checksum computation should be skipped * and checksum set to zeroes. * **BPF_F_DONT_FRAGMENT** * Add a flag to tunnel metadata indicating that the * packet should not be fragmented. * **BPF_F_SEQ_NUMBER** * Add a flag to tunnel metadata indicating that a * sequence number should be added to tunnel header before * sending the packet. This flag was added for GRE * encapsulation, but might be used with other protocols * as well in the future. * **BPF_F_NO_TUNNEL_KEY** * Add a flag to tunnel metadata indicating that no tunnel * key should be set in the resulting tunnel header. * * Here is a typical usage on the transmit path: * * :: * * struct bpf_tunnel_key key; * populate key ... * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0); * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0); * * See also the description of the **bpf_skb_get_tunnel_key**\ () * helper for additional information. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_set_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 21; /* * bpf_perf_event_read * * Read the value of a perf event counter. This helper relies on a * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of * the perf event counter is selected when *map* is updated with * perf event file descriptors. The *map* is an array whose size * is the number of available CPUs, and each cell contains a value * relative to one CPU. The value to retrieve is indicated by * *flags*, that contains the index of the CPU to look up, masked * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to * **BPF_F_CURRENT_CPU** to indicate that the value for the * current CPU should be retrieved. * * Note that before Linux 4.13, only hardware perf event can be * retrieved. * * Also, be aware that the newer helper * **bpf_perf_event_read_value**\ () is recommended over * **bpf_perf_event_read**\ () in general. The latter has some ABI * quirks where error and counter value are used as a return code * (which is wrong to do since ranges may overlap). This issue is * fixed with **bpf_perf_event_read_value**\ (), which at the same * time provides more features over the **bpf_perf_event_read**\ * () interface. Please refer to the description of * **bpf_perf_event_read_value**\ () for details. * * Returns * The value of the perf event counter read from the map, or a * negative error code in case of failure. */ static __u64 (*bpf_perf_event_read)(void *map, __u64 flags) = (void *) 22; /* * bpf_redirect * * Redirect the packet to another net device of index *ifindex*. * This helper is somewhat similar to **bpf_clone_redirect**\ * (), except that the packet is not cloned, which provides * increased performance. * * Except for XDP, both ingress and egress interfaces can be used * for redirection. The **BPF_F_INGRESS** value in *flags* is used * to make the distinction (ingress path is selected if the flag * is present, egress path otherwise). Currently, XDP only * supports redirection to the egress interface, and accepts no * flag at all. * * The same effect can also be attained with the more generic * **bpf_redirect_map**\ (), which uses a BPF map to store the * redirect target instead of providing it directly to the helper. * * Returns * For XDP, the helper returns **XDP_REDIRECT** on success or * **XDP_ABORTED** on error. For other program types, the values * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on * error. */ static long (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void *) 23; /* * bpf_get_route_realm * * Retrieve the realm or the route, that is to say the * **tclassid** field of the destination for the *skb*. The * identifier retrieved is a user-provided tag, similar to the * one used with the net_cls cgroup (see description for * **bpf_get_cgroup_classid**\ () helper), but here this tag is * held by a route (a destination entry), not by a task. * * Retrieving this identifier works with the clsact TC egress hook * (see also **tc-bpf(8)**), or alternatively on conventional * classful egress qdiscs, but not on TC ingress path. In case of * clsact TC egress hook, this has the advantage that, internally, * the destination entry has not been dropped yet in the transmit * path. Therefore, the destination entry does not need to be * artificially held via **netif_keep_dst**\ () for a classful * qdisc until the *skb* is freed. * * This helper is available only if the kernel was compiled with * **CONFIG_IP_ROUTE_CLASSID** configuration option. * * Returns * The realm of the route for the packet associated to *skb*, or 0 * if none was found. */ static __u32 (*bpf_get_route_realm)(struct __sk_buff *skb) = (void *) 24; /* * bpf_perf_event_output * * Write raw *data* blob into a special BPF perf event held by * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf * event must have the following attributes: **PERF_SAMPLE_RAW** * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. * * The *flags* are used to indicate the index in *map* for which * the value must be put, masked with **BPF_F_INDEX_MASK**. * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** * to indicate that the index of the current CPU core should be * used. * * The value to write, of *size*, is passed through eBPF stack and * pointed by *data*. * * The context of the program *ctx* needs also be passed to the * helper. * * On user space, a program willing to read the values needs to * call **perf_event_open**\ () on the perf event (either for * one or for all CPUs) and to store the file descriptor into the * *map*. This must be done before the eBPF program can send data * into it. An example is available in file * *samples/bpf/trace_output_user.c* in the Linux kernel source * tree (the eBPF program counterpart is in * *samples/bpf/trace_output_kern.c*). * * **bpf_perf_event_output**\ () achieves better performance * than **bpf_trace_printk**\ () for sharing data with user * space, and is much better suitable for streaming data from eBPF * programs. * * Note that this helper is not restricted to tracing use cases * and can be used with programs attached to TC or XDP as well, * where it allows for passing data to user space listeners. Data * can be: * * * Only custom structs, * * Only the packet payload, or * * A combination of both. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_perf_event_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 25; /* * bpf_skb_load_bytes * * This helper was provided as an easy way to load data from a * packet. It can be used to load *len* bytes from *offset* from * the packet associated to *skb*, into the buffer pointed by * *to*. * * Since Linux 4.7, usage of this helper has mostly been replaced * by "direct packet access", enabling packet data to be * manipulated with *skb*\ **->data** and *skb*\ **->data_end** * pointing respectively to the first byte of packet data and to * the byte after the last byte of packet data. However, it * remains useful if one wishes to read large quantities of data * at once from a packet into the eBPF stack. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_load_bytes)(const void *skb, __u32 offset, void *to, __u32 len) = (void *) 26; /* * bpf_get_stackid * * Walk a user or a kernel stack and return its id. To achieve * this, the helper needs *ctx*, which is a pointer to the context * on which the tracing program is executed, and a pointer to a * *map* of type **BPF_MAP_TYPE_STACK_TRACE**. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * a combination of the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * **BPF_F_FAST_STACK_CMP** * Compare stacks by hash only. * **BPF_F_REUSE_STACKID** * If two different stacks hash into the same *stackid*, * discard the old one. * * The stack id retrieved is a 32 bit long integer handle which * can be further combined with other data (including other stack * ids) and used as a key into maps. This can be useful for * generating a variety of graphs (such as flame graphs or off-cpu * graphs). * * For walking a stack, this helper is an improvement over * **bpf_probe_read**\ (), which can be used with unrolled loops * but is not efficient and consumes a lot of eBPF instructions. * Instead, **bpf_get_stackid**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack= * * Returns * The positive or null stack id on success, or a negative error * in case of failure. */ static long (*bpf_get_stackid)(void *ctx, void *map, __u64 flags) = (void *) 27; /* * bpf_csum_diff * * Compute a checksum difference, from the raw buffer pointed by * *from*, of length *from_size* (that must be a multiple of 4), * towards the raw buffer pointed by *to*, of size *to_size* * (same remark). An optional *seed* can be added to the value * (this can be cascaded, the seed may come from a previous call * to the helper). * * This is flexible enough to be used in several ways: * * * With *from_size* == 0, *to_size* > 0 and *seed* set to * checksum, it can be used when pushing new data. * * With *from_size* > 0, *to_size* == 0 and *seed* set to * checksum, it can be used when removing data from a packet. * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it * can be used to compute a diff. Note that *from_size* and * *to_size* do not need to be equal. * * This helper can be used in combination with * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to * which one can feed in the difference computed with * **bpf_csum_diff**\ (). * * Returns * The checksum result, or a negative error code in case of * failure. */ static __s64 (*bpf_csum_diff)(__be32 *from, __u32 from_size, __be32 *to, __u32 to_size, __wsum seed) = (void *) 28; /* * bpf_skb_get_tunnel_opt * * Retrieve tunnel options metadata for the packet associated to * *skb*, and store the raw tunnel option data to the buffer *opt* * of *size*. * * This helper can be used with encapsulation devices that can * operate in "collect metadata" mode (please refer to the related * note in the description of **bpf_skb_get_tunnel_key**\ () for * more details). A particular example where this can be used is * in combination with the Geneve encapsulation protocol, where it * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper) * and retrieving arbitrary TLVs (Type-Length-Value headers) from * the eBPF program. This allows for full customization of these * headers. * * Returns * The size of the option data retrieved. */ static long (*bpf_skb_get_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 29; /* * bpf_skb_set_tunnel_opt * * Set tunnel options metadata for the packet associated to *skb* * to the option data contained in the raw buffer *opt* of *size*. * * See also the description of the **bpf_skb_get_tunnel_opt**\ () * helper for additional information. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_set_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 30; /* * bpf_skb_change_proto * * Change the protocol of the *skb* to *proto*. Currently * supported are transition from IPv4 to IPv6, and from IPv6 to * IPv4. The helper takes care of the groundwork for the * transition, including resizing the socket buffer. The eBPF * program is expected to fill the new headers, if any, via * **skb_store_bytes**\ () and to recompute the checksums with * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ * (). The main case for this helper is to perform NAT64 * operations out of an eBPF program. * * Internally, the GSO type is marked as dodgy so that headers are * checked and segments are recalculated by the GSO/GRO engine. * The size for GSO target is adapted as well. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_change_proto)(struct __sk_buff *skb, __be16 proto, __u64 flags) = (void *) 31; /* * bpf_skb_change_type * * Change the packet type for the packet associated to *skb*. This * comes down to setting *skb*\ **->pkt_type** to *type*, except * the eBPF program does not have a write access to *skb*\ * **->pkt_type** beside this helper. Using a helper here allows * for graceful handling of errors. * * The major use case is to change incoming *skb*s to * **PACKET_HOST** in a programmatic way instead of having to * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for * example. * * Note that *type* only allows certain values. At this time, they * are: * * **PACKET_HOST** * Packet is for us. * **PACKET_BROADCAST** * Send packet to all. * **PACKET_MULTICAST** * Send packet to group. * **PACKET_OTHERHOST** * Send packet to someone else. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_change_type)(struct __sk_buff *skb, __u32 type) = (void *) 32; /* * bpf_skb_under_cgroup * * Check whether *skb* is a descendant of the cgroup2 held by * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. * * Returns * The return value depends on the result of the test, and can be: * * * 0, if the *skb* failed the cgroup2 descendant test. * * 1, if the *skb* succeeded the cgroup2 descendant test. * * A negative error code, if an error occurred. */ static long (*bpf_skb_under_cgroup)(struct __sk_buff *skb, void *map, __u32 index) = (void *) 33; /* * bpf_get_hash_recalc * * Retrieve the hash of the packet, *skb*\ **->hash**. If it is * not set, in particular if the hash was cleared due to mangling, * recompute this hash. Later accesses to the hash can be done * directly with *skb*\ **->hash**. * * Calling **bpf_set_hash_invalid**\ (), changing a packet * prototype with **bpf_skb_change_proto**\ (), or calling * **bpf_skb_store_bytes**\ () with the * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear * the hash and to trigger a new computation for the next call to * **bpf_get_hash_recalc**\ (). * * Returns * The 32-bit hash. */ static __u32 (*bpf_get_hash_recalc)(struct __sk_buff *skb) = (void *) 34; /* * bpf_get_current_task * * Get the current task. * * Returns * A pointer to the current task struct. */ static __u64 (*bpf_get_current_task)(void) = (void *) 35; /* * bpf_probe_write_user * * Attempt in a safe way to write *len* bytes from the buffer * *src* to *dst* in memory. It only works for threads that are in * user context, and *dst* must be a valid user space address. * * This helper should not be used to implement any kind of * security mechanism because of TOC-TOU attacks, but rather to * debug, divert, and manipulate execution of semi-cooperative * processes. * * Keep in mind that this feature is meant for experiments, and it * has a risk of crashing the system and running programs. * Therefore, when an eBPF program using this helper is attached, * a warning including PID and process name is printed to kernel * logs. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_probe_write_user)(void *dst, const void *src, __u32 len) = (void *) 36; /* * bpf_current_task_under_cgroup * * Check whether the probe is being run is the context of a given * subset of the cgroup2 hierarchy. The cgroup2 to test is held by * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. * * Returns * The return value depends on the result of the test, and can be: * * * 1, if current task belongs to the cgroup2. * * 0, if current task does not belong to the cgroup2. * * A negative error code, if an error occurred. */ static long (*bpf_current_task_under_cgroup)(void *map, __u32 index) = (void *) 37; /* * bpf_skb_change_tail * * Resize (trim or grow) the packet associated to *skb* to the * new *len*. The *flags* are reserved for future usage, and must * be left at zero. * * The basic idea is that the helper performs the needed work to * change the size of the packet, then the eBPF program rewrites * the rest via helpers like **bpf_skb_store_bytes**\ (), * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ () * and others. This helper is a slow path utility intended for * replies with control messages. And because it is targeted for * slow path, the helper itself can afford to be slow: it * implicitly linearizes, unclones and drops offloads from the * *skb*. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_change_tail)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 38; /* * bpf_skb_pull_data * * Pull in non-linear data in case the *skb* is non-linear and not * all of *len* are part of the linear section. Make *len* bytes * from *skb* readable and writable. If a zero value is passed for * *len*, then all bytes in the linear part of *skb* will be made * readable and writable. * * This helper is only needed for reading and writing with direct * packet access. * * For direct packet access, testing that offsets to access * are within packet boundaries (test on *skb*\ **->data_end**) is * susceptible to fail if offsets are invalid, or if the requested * data is in non-linear parts of the *skb*. On failure the * program can just bail out, or in the case of a non-linear * buffer, use a helper to make the data available. The * **bpf_skb_load_bytes**\ () helper is a first solution to access * the data. Another one consists in using **bpf_skb_pull_data** * to pull in once the non-linear parts, then retesting and * eventually access the data. * * At the same time, this also makes sure the *skb* is uncloned, * which is a necessary condition for direct write. As this needs * to be an invariant for the write part only, the verifier * detects writes and adds a prologue that is calling * **bpf_skb_pull_data()** to effectively unclone the *skb* from * the very beginning in case it is indeed cloned. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_pull_data)(struct __sk_buff *skb, __u32 len) = (void *) 39; /* * bpf_csum_update * * Add the checksum *csum* into *skb*\ **->csum** in case the * driver has supplied a checksum for the entire packet into that * field. Return an error otherwise. This helper is intended to be * used in combination with **bpf_csum_diff**\ (), in particular * when the checksum needs to be updated after data has been * written into the packet through direct packet access. * * Returns * The checksum on success, or a negative error code in case of * failure. */ static __s64 (*bpf_csum_update)(struct __sk_buff *skb, __wsum csum) = (void *) 40; /* * bpf_set_hash_invalid * * Invalidate the current *skb*\ **->hash**. It can be used after * mangling on headers through direct packet access, in order to * indicate that the hash is outdated and to trigger a * recalculation the next time the kernel tries to access this * hash or when the **bpf_get_hash_recalc**\ () helper is called. * * Returns * void. */ static void (*bpf_set_hash_invalid)(struct __sk_buff *skb) = (void *) 41; /* * bpf_get_numa_node_id * * Return the id of the current NUMA node. The primary use case * for this helper is the selection of sockets for the local NUMA * node, when the program is attached to sockets using the * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**), * but the helper is also available to other eBPF program types, * similarly to **bpf_get_smp_processor_id**\ (). * * Returns * The id of current NUMA node. */ static long (*bpf_get_numa_node_id)(void) = (void *) 42; /* * bpf_skb_change_head * * Grows headroom of packet associated to *skb* and adjusts the * offset of the MAC header accordingly, adding *len* bytes of * space. It automatically extends and reallocates memory as * required. * * This helper can be used on a layer 3 *skb* to push a MAC header * for redirection into a layer 2 device. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_change_head)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 43; /* * bpf_xdp_adjust_head * * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that * it is possible to use a negative value for *delta*. This helper * can be used to prepare the packet for pushing or popping * headers. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_xdp_adjust_head)(struct xdp_md *xdp_md, int delta) = (void *) 44; /* * bpf_probe_read_str * * Copy a NUL terminated string from an unsafe kernel address * *unsafe_ptr* to *dst*. See **bpf_probe_read_kernel_str**\ () for * more details. * * Generally, use **bpf_probe_read_user_str**\ () or * **bpf_probe_read_kernel_str**\ () instead. * * Returns * On success, the strictly positive length of the string, * including the trailing NUL character. On error, a negative * value. */ static long (*bpf_probe_read_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 45; /* * bpf_get_socket_cookie * * If the **struct sk_buff** pointed by *skb* has a known socket, * retrieve the cookie (generated by the kernel) of this socket. * If no cookie has been set yet, generate a new cookie. Once * generated, the socket cookie remains stable for the life of the * socket. This helper can be useful for monitoring per socket * networking traffic statistics as it provides a global socket * identifier that can be assumed unique. * * Returns * A 8-byte long unique number on success, or 0 if the socket * field is missing inside *skb*. */ static __u64 (*bpf_get_socket_cookie)(void *ctx) = (void *) 46; /* * bpf_get_socket_uid * * Get the owner UID of the socked associated to *skb*. * * Returns * The owner UID of the socket associated to *skb*. If the socket * is **NULL**, or if it is not a full socket (i.e. if it is a * time-wait or a request socket instead), **overflowuid** value * is returned (note that **overflowuid** might also be the actual * UID value for the socket). */ static __u32 (*bpf_get_socket_uid)(struct __sk_buff *skb) = (void *) 47; /* * bpf_set_hash * * Set the full hash for *skb* (set the field *skb*\ **->hash**) * to value *hash*. * * Returns * 0 */ static long (*bpf_set_hash)(struct __sk_buff *skb, __u32 hash) = (void *) 48; /* * bpf_setsockopt * * Emulate a call to **setsockopt()** on the socket associated to * *bpf_socket*, which must be a full socket. The *level* at * which the option resides and the name *optname* of the option * must be specified, see **setsockopt(2)** for more information. * The option value of length *optlen* is pointed by *optval*. * * *bpf_socket* should be one of the following: * * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** * and **BPF_CGROUP_INET6_CONNECT**. * * This helper actually implements a subset of **setsockopt()**. * It supports the following *level*\ s: * * * **SOL_SOCKET**, which supports the following *optname*\ s: * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**, * **SO_BINDTODEVICE**, **SO_KEEPALIVE**, **SO_REUSEADDR**, * **SO_REUSEPORT**, **SO_BINDTOIFINDEX**, **SO_TXREHASH**. * * **IPPROTO_TCP**, which supports the following *optname*\ s: * **TCP_CONGESTION**, **TCP_BPF_IW**, * **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**, * **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**, * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**, **TCP_NOTSENT_LOWAT**, * **TCP_NODELAY**, **TCP_MAXSEG**, **TCP_WINDOW_CLAMP**, * **TCP_THIN_LINEAR_TIMEOUTS**, **TCP_BPF_DELACK_MAX**, * **TCP_BPF_RTO_MIN**. * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. * * **IPPROTO_IPV6**, which supports the following *optname*\ s: * **IPV6_TCLASS**, **IPV6_AUTOFLOWLABEL**. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_setsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 49; /* * bpf_skb_adjust_room * * Grow or shrink the room for data in the packet associated to * *skb* by *len_diff*, and according to the selected *mode*. * * By default, the helper will reset any offloaded checksum * indicator of the skb to CHECKSUM_NONE. This can be avoided * by the following flag: * * * **BPF_F_ADJ_ROOM_NO_CSUM_RESET**: Do not reset offloaded * checksum data of the skb to CHECKSUM_NONE. * * There are two supported modes at this time: * * * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer * (room space is added or removed between the layer 2 and * layer 3 headers). * * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer * (room space is added or removed between the layer 3 and * layer 4 headers). * * The following flags are supported at this time: * * * **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size. * Adjusting mss in this way is not allowed for datagrams. * * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**, * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**: * Any new space is reserved to hold a tunnel header. * Configure skb offsets and other fields accordingly. * * * **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**, * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**: * Use with ENCAP_L3 flags to further specify the tunnel type. * * * **BPF_F_ADJ_ROOM_ENCAP_L2**\ (*len*): * Use with ENCAP_L3/L4 flags to further specify the tunnel * type; *len* is the length of the inner MAC header. * * * **BPF_F_ADJ_ROOM_ENCAP_L2_ETH**: * Use with BPF_F_ADJ_ROOM_ENCAP_L2 flag to further specify the * L2 type as Ethernet. * * * **BPF_F_ADJ_ROOM_DECAP_L3_IPV4**, * **BPF_F_ADJ_ROOM_DECAP_L3_IPV6**: * Indicate the new IP header version after decapsulating the outer * IP header. Used when the inner and outer IP versions are different. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_adjust_room)(struct __sk_buff *skb, __s32 len_diff, __u32 mode, __u64 flags) = (void *) 50; /* * bpf_redirect_map * * Redirect the packet to the endpoint referenced by *map* at * index *key*. Depending on its type, this *map* can contain * references to net devices (for forwarding packets through other * ports), or to CPUs (for redirecting XDP frames to another CPU; * but this is only implemented for native XDP (with driver * support) as of this writing). * * The lower two bits of *flags* are used as the return code if * the map lookup fails. This is so that the return value can be * one of the XDP program return codes up to **XDP_TX**, as chosen * by the caller. The higher bits of *flags* can be set to * BPF_F_BROADCAST or BPF_F_EXCLUDE_INGRESS as defined below. * * With BPF_F_BROADCAST the packet will be broadcasted to all the * interfaces in the map, with BPF_F_EXCLUDE_INGRESS the ingress * interface will be excluded when do broadcasting. * * See also **bpf_redirect**\ (), which only supports redirecting * to an ifindex, but doesn't require a map to do so. * * Returns * **XDP_REDIRECT** on success, or the value of the two lower bits * of the *flags* argument on error. */ static long (*bpf_redirect_map)(void *map, __u64 key, __u64 flags) = (void *) 51; /* * bpf_sk_redirect_map * * Redirect the packet to the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * * Returns * **SK_PASS** on success, or **SK_DROP** on error. */ static long (*bpf_sk_redirect_map)(struct __sk_buff *skb, void *map, __u32 key, __u64 flags) = (void *) 52; /* * bpf_sock_map_update * * Add an entry to, or update a *map* referencing sockets. The * *skops* is used as a new value for the entry associated to * *key*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * If the *map* has eBPF programs (parser and verdict), those will * be inherited by the socket being added. If the socket is * already attached to eBPF programs, this results in an error. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_sock_map_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 53; /* * bpf_xdp_adjust_meta * * Adjust the address pointed by *xdp_md*\ **->data_meta** by * *delta* (which can be positive or negative). Note that this * operation modifies the address stored in *xdp_md*\ **->data**, * so the latter must be loaded only after the helper has been * called. * * The use of *xdp_md*\ **->data_meta** is optional and programs * are not required to use it. The rationale is that when the * packet is processed with XDP (e.g. as DoS filter), it is * possible to push further meta data along with it before passing * to the stack, and to give the guarantee that an ingress eBPF * program attached as a TC classifier on the same device can pick * this up for further post-processing. Since TC works with socket * buffers, it remains possible to set from XDP the **mark** or * **priority** pointers, or other pointers for the socket buffer. * Having this scratch space generic and programmable allows for * more flexibility as the user is free to store whatever meta * data they need. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_xdp_adjust_meta)(struct xdp_md *xdp_md, int delta) = (void *) 54; /* * bpf_perf_event_read_value * * Read the value of a perf event counter, and store it into *buf* * of size *buf_size*. This helper relies on a *map* of type * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event * counter is selected when *map* is updated with perf event file * descriptors. The *map* is an array whose size is the number of * available CPUs, and each cell contains a value relative to one * CPU. The value to retrieve is indicated by *flags*, that * contains the index of the CPU to look up, masked with * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to * **BPF_F_CURRENT_CPU** to indicate that the value for the * current CPU should be retrieved. * * This helper behaves in a way close to * **bpf_perf_event_read**\ () helper, save that instead of * just returning the value observed, it fills the *buf* * structure. This allows for additional data to be retrieved: in * particular, the enabled and running times (in *buf*\ * **->enabled** and *buf*\ **->running**, respectively) are * copied. In general, **bpf_perf_event_read_value**\ () is * recommended over **bpf_perf_event_read**\ (), which has some * ABI issues and provides fewer functionalities. * * These values are interesting, because hardware PMU (Performance * Monitoring Unit) counters are limited resources. When there are * more PMU based perf events opened than available counters, * kernel will multiplex these events so each event gets certain * percentage (but not all) of the PMU time. In case that * multiplexing happens, the number of samples or counter value * will not reflect the case compared to when no multiplexing * occurs. This makes comparison between different runs difficult. * Typically, the counter value should be normalized before * comparing to other experiments. The usual normalization is done * as follows. * * :: * * normalized_counter = counter * t_enabled / t_running * * Where t_enabled is the time enabled for event and t_running is * the time running for event since last normalization. The * enabled and running times are accumulated since the perf event * open. To achieve scaling factor between two invocations of an * eBPF program, users can use CPU id as the key (which is * typical for perf array usage model) to remember the previous * value and do the calculation inside the eBPF program. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_perf_event_read_value)(void *map, __u64 flags, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 55; /* * bpf_perf_prog_read_value * * For an eBPF program attached to a perf event, retrieve the * value of the event counter associated to *ctx* and store it in * the structure pointed by *buf* and of size *buf_size*. Enabled * and running times are also stored in the structure (see * description of helper **bpf_perf_event_read_value**\ () for * more details). * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_perf_prog_read_value)(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 56; /* * bpf_getsockopt * * Emulate a call to **getsockopt()** on the socket associated to * *bpf_socket*, which must be a full socket. The *level* at * which the option resides and the name *optname* of the option * must be specified, see **getsockopt(2)** for more information. * The retrieved value is stored in the structure pointed by * *opval* and of length *optlen*. * * *bpf_socket* should be one of the following: * * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** * and **BPF_CGROUP_INET6_CONNECT**. * * This helper actually implements a subset of **getsockopt()**. * It supports the same set of *optname*\ s that is supported by * the **bpf_setsockopt**\ () helper. The exceptions are * **TCP_BPF_*** is **bpf_setsockopt**\ () only and * **TCP_SAVED_SYN** is **bpf_getsockopt**\ () only. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_getsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 57; /* * bpf_override_return * * Used for error injection, this helper uses kprobes to override * the return value of the probed function, and to set it to *rc*. * The first argument is the context *regs* on which the kprobe * works. * * This helper works by setting the PC (program counter) * to an override function which is run in place of the original * probed function. This means the probed function is not run at * all. The replacement function just returns with the required * value. * * This helper has security implications, and thus is subject to * restrictions. It is only available if the kernel was compiled * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration * option, and in this case it only works on functions tagged with * **ALLOW_ERROR_INJECTION** in the kernel code. * * Also, the helper is only available for the architectures having * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing, * x86 architecture is the only one to support this feature. * * Returns * 0 */ static long (*bpf_override_return)(struct pt_regs *regs, __u64 rc) = (void *) 58; /* * bpf_sock_ops_cb_flags_set * * Attempt to set the value of the **bpf_sock_ops_cb_flags** field * for the full TCP socket associated to *bpf_sock_ops* to * *argval*. * * The primary use of this field is to determine if there should * be calls to eBPF programs of type * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP * code. A program of the same type can change its value, per * connection and as necessary, when the connection is * established. This field is directly accessible for reading, but * this helper must be used for updates in order to return an * error if an eBPF program tries to set a callback that is not * supported in the current kernel. * * *argval* is a flag array which can combine these flags: * * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out) * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission) * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change) * * **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT) * * Therefore, this function can be used to clear a callback flag by * setting the appropriate bit to zero. e.g. to disable the RTO * callback: * * **bpf_sock_ops_cb_flags_set(bpf_sock,** * **bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)** * * Here are some examples of where one could call such eBPF * program: * * * When RTO fires. * * When a packet is retransmitted. * * When the connection terminates. * * When a packet is sent. * * When a packet is received. * * Returns * Code **-EINVAL** if the socket is not a full TCP socket; * otherwise, a positive number containing the bits that could not * be set is returned (which comes down to 0 if all bits were set * as required). */ static long (*bpf_sock_ops_cb_flags_set)(struct bpf_sock_ops *bpf_sock, int argval) = (void *) 59; /* * bpf_msg_redirect_map * * This helper is used in programs implementing policies at the * socket level. If the message *msg* is allowed to pass (i.e. if * the verdict eBPF program returns **SK_PASS**), redirect it to * the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * * Returns * **SK_PASS** on success, or **SK_DROP** on error. */ static long (*bpf_msg_redirect_map)(struct sk_msg_md *msg, void *map, __u32 key, __u64 flags) = (void *) 60; /* * bpf_msg_apply_bytes * * For socket policies, apply the verdict of the eBPF program to * the next *bytes* (number of bytes) of message *msg*. * * For example, this helper can be used in the following cases: * * * A single **sendmsg**\ () or **sendfile**\ () system call * contains multiple logical messages that the eBPF program is * supposed to read and for which it should apply a verdict. * * An eBPF program only cares to read the first *bytes* of a * *msg*. If the message has a large payload, then setting up * and calling the eBPF program repeatedly for all bytes, even * though the verdict is already known, would create unnecessary * overhead. * * When called from within an eBPF program, the helper sets a * counter internal to the BPF infrastructure, that is used to * apply the last verdict to the next *bytes*. If *bytes* is * smaller than the current data being processed from a * **sendmsg**\ () or **sendfile**\ () system call, the first * *bytes* will be sent and the eBPF program will be re-run with * the pointer for start of data pointing to byte number *bytes* * **+ 1**. If *bytes* is larger than the current data being * processed, then the eBPF verdict will be applied to multiple * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are * consumed. * * Note that if a socket closes with the internal counter holding * a non-zero value, this is not a problem because data is not * being buffered for *bytes* and is sent as it is received. * * Returns * 0 */ static long (*bpf_msg_apply_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 61; /* * bpf_msg_cork_bytes * * For socket policies, prevent the execution of the verdict eBPF * program for message *msg* until *bytes* (byte number) have been * accumulated. * * This can be used when one needs a specific number of bytes * before a verdict can be assigned, even if the data spans * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme * case would be a user calling **sendmsg**\ () repeatedly with * 1-byte long message segments. Obviously, this is bad for * performance, but it is still valid. If the eBPF program needs * *bytes* bytes to validate a header, this helper can be used to * prevent the eBPF program to be called again until *bytes* have * been accumulated. * * Returns * 0 */ static long (*bpf_msg_cork_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 62; /* * bpf_msg_pull_data * * For socket policies, pull in non-linear data from user space * for *msg* and set pointers *msg*\ **->data** and *msg*\ * **->data_end** to *start* and *end* bytes offsets into *msg*, * respectively. * * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a * *msg* it can only parse data that the (**data**, **data_end**) * pointers have already consumed. For **sendmsg**\ () hooks this * is likely the first scatterlist element. But for calls relying * on the **sendpage** handler (e.g. **sendfile**\ ()) this will * be the range (**0**, **0**) because the data is shared with * user space and by default the objective is to avoid allowing * user space to modify data while (or after) eBPF verdict is * being decided. This helper can be used to pull in data and to * set the start and end pointer to given values. Data will be * copied if necessary (i.e. if data was not linear and if start * and end pointers do not point to the same chunk). * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_msg_pull_data)(struct sk_msg_md *msg, __u32 start, __u32 end, __u64 flags) = (void *) 63; /* * bpf_bind * * Bind the socket associated to *ctx* to the address pointed by * *addr*, of length *addr_len*. This allows for making outgoing * connection from the desired IP address, which can be useful for * example when all processes inside a cgroup should use one * single IP address on a host that has multiple IP configured. * * This helper works for IPv4 and IPv6, TCP and UDP sockets. The * domain (*addr*\ **->sa_family**) must be **AF_INET** (or * **AF_INET6**). It's advised to pass zero port (**sin_port** * or **sin6_port**) which triggers IP_BIND_ADDRESS_NO_PORT-like * behavior and lets the kernel efficiently pick up an unused * port as long as 4-tuple is unique. Passing non-zero port might * lead to degraded performance. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_bind)(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len) = (void *) 64; /* * bpf_xdp_adjust_tail * * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is * possible to both shrink and grow the packet tail. * Shrink done via *delta* being a negative integer. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_xdp_adjust_tail)(struct xdp_md *xdp_md, int delta) = (void *) 65; /* * bpf_skb_get_xfrm_state * * Retrieve the XFRM state (IP transform framework, see also * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*. * * The retrieved value is stored in the **struct bpf_xfrm_state** * pointed by *xfrm_state* and of length *size*. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_XFRM** configuration option. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_get_xfrm_state)(struct __sk_buff *skb, __u32 index, struct bpf_xfrm_state *xfrm_state, __u32 size, __u64 flags) = (void *) 66; /* * bpf_get_stack * * Return a user or a kernel stack in bpf program provided buffer. * To achieve this, the helper needs *ctx*, which is a pointer * to the context on which the tracing program is executed. * To store the stacktrace, the bpf program provides *buf* with * a nonnegative *size*. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * **BPF_F_USER_BUILD_ID** * Collect (build_id, file_offset) instead of ips for user * stack, only valid if **BPF_F_USER_STACK** is also * specified. * * *file_offset* is an offset relative to the beginning * of the executable or shared object file backing the vma * which the *ip* falls in. It is *not* an offset relative * to that object's base address. Accordingly, it must be * adjusted by adding (sh_addr - sh_offset), where * sh_{addr,offset} correspond to the executable section * containing *file_offset* in the object, for comparisons * to symbols' st_value to be valid. * * **bpf_get_stack**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject * to sufficient large buffer size. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack= * * Returns * The non-negative copied *buf* length equal to or less than * *size* on success, or a negative error in case of failure. */ static long (*bpf_get_stack)(void *ctx, void *buf, __u32 size, __u64 flags) = (void *) 67; /* * bpf_skb_load_bytes_relative * * This helper is similar to **bpf_skb_load_bytes**\ () in that * it provides an easy way to load *len* bytes from *offset* * from the packet associated to *skb*, into the buffer pointed * by *to*. The difference to **bpf_skb_load_bytes**\ () is that * a fifth argument *start_header* exists in order to select a * base offset to start from. *start_header* can be one of: * * **BPF_HDR_START_MAC** * Base offset to load data from is *skb*'s mac header. * **BPF_HDR_START_NET** * Base offset to load data from is *skb*'s network header. * * In general, "direct packet access" is the preferred method to * access packet data, however, this helper is in particular useful * in socket filters where *skb*\ **->data** does not always point * to the start of the mac header and where "direct packet access" * is not available. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_load_bytes_relative)(const void *skb, __u32 offset, void *to, __u32 len, __u32 start_header) = (void *) 68; /* * bpf_fib_lookup * * Do FIB lookup in kernel tables using parameters in *params*. * If lookup is successful and result shows packet is to be * forwarded, the neighbor tables are searched for the nexthop. * If successful (ie., FIB lookup shows forwarding and nexthop * is resolved), the nexthop address is returned in ipv4_dst * or ipv6_dst based on family, smac is set to mac address of * egress device, dmac is set to nexthop mac address, rt_metric * is set to metric from route (IPv4/IPv6 only), and ifindex * is set to the device index of the nexthop from the FIB lookup. * * *plen* argument is the size of the passed in struct. * *flags* argument can be a combination of one or more of the * following values: * * **BPF_FIB_LOOKUP_DIRECT** * Do a direct table lookup vs full lookup using FIB * rules. * **BPF_FIB_LOOKUP_OUTPUT** * Perform lookup from an egress perspective (default is * ingress). * **BPF_FIB_LOOKUP_SKIP_NEIGH** * Skip the neighbour table lookup. *params*->dmac * and *params*->smac will not be set as output. A common * use case is to call **bpf_redirect_neigh**\ () after * doing **bpf_fib_lookup**\ (). * * *ctx* is either **struct xdp_md** for XDP programs or * **struct sk_buff** tc cls_act programs. * * Returns * * < 0 if any input argument is invalid * * 0 on success (packet is forwarded, nexthop neighbor exists) * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the * packet is not forwarded or needs assist from full stack * * If lookup fails with BPF_FIB_LKUP_RET_FRAG_NEEDED, then the MTU * was exceeded and output params->mtu_result contains the MTU. */ static long (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, int plen, __u32 flags) = (void *) 69; /* * bpf_sock_hash_update * * Add an entry to, or update a sockhash *map* referencing sockets. * The *skops* is used as a new value for the entry associated to * *key*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * If the *map* has eBPF programs (parser and verdict), those will * be inherited by the socket being added. If the socket is * already attached to eBPF programs, this results in an error. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_sock_hash_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 70; /* * bpf_msg_redirect_hash * * This helper is used in programs implementing policies at the * socket level. If the message *msg* is allowed to pass (i.e. if * the verdict eBPF program returns **SK_PASS**), redirect it to * the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * * Returns * **SK_PASS** on success, or **SK_DROP** on error. */ static long (*bpf_msg_redirect_hash)(struct sk_msg_md *msg, void *map, void *key, __u64 flags) = (void *) 71; /* * bpf_sk_redirect_hash * * This helper is used in programs implementing policies at the * skb socket level. If the sk_buff *skb* is allowed to pass (i.e. * if the verdict eBPF program returns **SK_PASS**), redirect it * to the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress otherwise). This is the only flag supported for now. * * Returns * **SK_PASS** on success, or **SK_DROP** on error. */ static long (*bpf_sk_redirect_hash)(struct __sk_buff *skb, void *map, void *key, __u64 flags) = (void *) 72; /* * bpf_lwt_push_encap * * Encapsulate the packet associated to *skb* within a Layer 3 * protocol header. This header is provided in the buffer at * address *hdr*, with *len* its size in bytes. *type* indicates * the protocol of the header and can be one of: * * **BPF_LWT_ENCAP_SEG6** * IPv6 encapsulation with Segment Routing Header * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH, * the IPv6 header is computed by the kernel. * **BPF_LWT_ENCAP_SEG6_INLINE** * Only works if *skb* contains an IPv6 packet. Insert a * Segment Routing Header (**struct ipv6_sr_hdr**) inside * the IPv6 header. * **BPF_LWT_ENCAP_IP** * IP encapsulation (GRE/GUE/IPIP/etc). The outer header * must be IPv4 or IPv6, followed by zero or more * additional headers, up to **LWT_BPF_MAX_HEADROOM** * total bytes in all prepended headers. Please note that * if **skb_is_gso**\ (*skb*) is true, no more than two * headers can be prepended, and the inner header, if * present, should be either GRE or UDP/GUE. * * **BPF_LWT_ENCAP_SEG6**\ \* types can be called by BPF programs * of type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can * be called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and * **BPF_PROG_TYPE_LWT_XMIT**. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_lwt_push_encap)(struct __sk_buff *skb, __u32 type, void *hdr, __u32 len) = (void *) 73; /* * bpf_lwt_seg6_store_bytes * * Store *len* bytes from address *from* into the packet * associated to *skb*, at *offset*. Only the flags, tag and TLVs * inside the outermost IPv6 Segment Routing Header can be * modified through this helper. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_lwt_seg6_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len) = (void *) 74; /* * bpf_lwt_seg6_adjust_srh * * Adjust the size allocated to TLVs in the outermost IPv6 * Segment Routing Header contained in the packet associated to * *skb*, at position *offset* by *delta* bytes. Only offsets * after the segments are accepted. *delta* can be as well * positive (growing) as negative (shrinking). * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_lwt_seg6_adjust_srh)(struct __sk_buff *skb, __u32 offset, __s32 delta) = (void *) 75; /* * bpf_lwt_seg6_action * * Apply an IPv6 Segment Routing action of type *action* to the * packet associated to *skb*. Each action takes a parameter * contained at address *param*, and of length *param_len* bytes. * *action* can be one of: * * **SEG6_LOCAL_ACTION_END_X** * End.X action: Endpoint with Layer-3 cross-connect. * Type of *param*: **struct in6_addr**. * **SEG6_LOCAL_ACTION_END_T** * End.T action: Endpoint with specific IPv6 table lookup. * Type of *param*: **int**. * **SEG6_LOCAL_ACTION_END_B6** * End.B6 action: Endpoint bound to an SRv6 policy. * Type of *param*: **struct ipv6_sr_hdr**. * **SEG6_LOCAL_ACTION_END_B6_ENCAP** * End.B6.Encap action: Endpoint bound to an SRv6 * encapsulation policy. * Type of *param*: **struct ipv6_sr_hdr**. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_lwt_seg6_action)(struct __sk_buff *skb, __u32 action, void *param, __u32 param_len) = (void *) 76; /* * bpf_rc_repeat * * This helper is used in programs implementing IR decoding, to * report a successfully decoded repeat key message. This delays * the generation of a key up event for previously generated * key down event. * * Some IR protocols like NEC have a special IR message for * repeating last button, for when a button is held down. * * The *ctx* should point to the lirc sample as passed into * the program. * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * * Returns * 0 */ static long (*bpf_rc_repeat)(void *ctx) = (void *) 77; /* * bpf_rc_keydown * * This helper is used in programs implementing IR decoding, to * report a successfully decoded key press with *scancode*, * *toggle* value in the given *protocol*. The scancode will be * translated to a keycode using the rc keymap, and reported as * an input key down event. After a period a key up event is * generated. This period can be extended by calling either * **bpf_rc_keydown**\ () again with the same values, or calling * **bpf_rc_repeat**\ (). * * Some protocols include a toggle bit, in case the button was * released and pressed again between consecutive scancodes. * * The *ctx* should point to the lirc sample as passed into * the program. * * The *protocol* is the decoded protocol number (see * **enum rc_proto** for some predefined values). * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * * Returns * 0 */ static long (*bpf_rc_keydown)(void *ctx, __u32 protocol, __u64 scancode, __u32 toggle) = (void *) 78; /* * bpf_skb_cgroup_id * * Return the cgroup v2 id of the socket associated with the *skb*. * This is roughly similar to the **bpf_get_cgroup_classid**\ () * helper for cgroup v1 by providing a tag resp. identifier that * can be matched on or used for map lookups e.g. to implement * policy. The cgroup v2 id of a given path in the hierarchy is * exposed in user space through the f_handle API in order to get * to the same 64-bit id. * * This helper can be used on TC egress path, but not on ingress, * and is available only if the kernel was compiled with the * **CONFIG_SOCK_CGROUP_DATA** configuration option. * * Returns * The id is returned or 0 in case the id could not be retrieved. */ static __u64 (*bpf_skb_cgroup_id)(struct __sk_buff *skb) = (void *) 79; /* * bpf_get_current_cgroup_id * * Get the current cgroup id based on the cgroup within which * the current task is running. * * Returns * A 64-bit integer containing the current cgroup id based * on the cgroup within which the current task is running. */ static __u64 (*bpf_get_current_cgroup_id)(void) = (void *) 80; /* * bpf_get_local_storage * * Get the pointer to the local storage area. * The type and the size of the local storage is defined * by the *map* argument. * The *flags* meaning is specific for each map type, * and has to be 0 for cgroup local storage. * * Depending on the BPF program type, a local storage area * can be shared between multiple instances of the BPF program, * running simultaneously. * * A user should care about the synchronization by himself. * For example, by using the **BPF_ATOMIC** instructions to alter * the shared data. * * Returns * A pointer to the local storage area. */ static void *(*bpf_get_local_storage)(void *map, __u64 flags) = (void *) 81; /* * bpf_sk_select_reuseport * * Select a **SO_REUSEPORT** socket from a * **BPF_MAP_TYPE_REUSEPORT_SOCKARRAY** *map*. * It checks the selected socket is matching the incoming * request in the socket buffer. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_sk_select_reuseport)(struct sk_reuseport_md *reuse, void *map, void *key, __u64 flags) = (void *) 82; /* * bpf_skb_ancestor_cgroup_id * * Return id of cgroup v2 that is ancestor of cgroup associated * with the *skb* at the *ancestor_level*. The root cgroup is at * *ancestor_level* zero and each step down the hierarchy * increments the level. If *ancestor_level* == level of cgroup * associated with *skb*, then return value will be same as that * of **bpf_skb_cgroup_id**\ (). * * The helper is useful to implement policies based on cgroups * that are upper in hierarchy than immediate cgroup associated * with *skb*. * * The format of returned id and helper limitations are same as in * **bpf_skb_cgroup_id**\ (). * * Returns * The id is returned or 0 in case the id could not be retrieved. */ static __u64 (*bpf_skb_ancestor_cgroup_id)(struct __sk_buff *skb, int ancestor_level) = (void *) 83; /* * bpf_sk_lookup_tcp * * Look for TCP socket matching *tuple*, optionally in a child * network namespace *netns*. The return value must be checked, * and if non-**NULL**, released via **bpf_sk_release**\ (). * * The *ctx* should point to the context of the program, such as * the skb or socket (depending on the hook in use). This is used * to determine the base network namespace for the lookup. * * *tuple_size* must be one of: * * **sizeof**\ (*tuple*\ **->ipv4**) * Look for an IPv4 socket. * **sizeof**\ (*tuple*\ **->ipv6**) * Look for an IPv6 socket. * * If the *netns* is a negative signed 32-bit integer, then the * socket lookup table in the netns associated with the *ctx* * will be used. For the TC hooks, this is the netns of the device * in the skb. For socket hooks, this is the netns of the socket. * If *netns* is any other signed 32-bit value greater than or * equal to zero then it specifies the ID of the netns relative to * the netns associated with the *ctx*. *netns* values beyond the * range of 32-bit integers are reserved for future use. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_NET** configuration option. * * Returns * Pointer to **struct bpf_sock**, or **NULL** in case of failure. * For sockets with reuseport option, the **struct bpf_sock** * result is from *reuse*\ **->socks**\ [] using the hash of the * tuple. */ static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 84; /* * bpf_sk_lookup_udp * * Look for UDP socket matching *tuple*, optionally in a child * network namespace *netns*. The return value must be checked, * and if non-**NULL**, released via **bpf_sk_release**\ (). * * The *ctx* should point to the context of the program, such as * the skb or socket (depending on the hook in use). This is used * to determine the base network namespace for the lookup. * * *tuple_size* must be one of: * * **sizeof**\ (*tuple*\ **->ipv4**) * Look for an IPv4 socket. * **sizeof**\ (*tuple*\ **->ipv6**) * Look for an IPv6 socket. * * If the *netns* is a negative signed 32-bit integer, then the * socket lookup table in the netns associated with the *ctx* * will be used. For the TC hooks, this is the netns of the device * in the skb. For socket hooks, this is the netns of the socket. * If *netns* is any other signed 32-bit value greater than or * equal to zero then it specifies the ID of the netns relative to * the netns associated with the *ctx*. *netns* values beyond the * range of 32-bit integers are reserved for future use. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_NET** configuration option. * * Returns * Pointer to **struct bpf_sock**, or **NULL** in case of failure. * For sockets with reuseport option, the **struct bpf_sock** * result is from *reuse*\ **->socks**\ [] using the hash of the * tuple. */ static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 85; /* * bpf_sk_release * * Release the reference held by *sock*. *sock* must be a * non-**NULL** pointer that was returned from * **bpf_sk_lookup_xxx**\ (). * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_sk_release)(void *sock) = (void *) 86; /* * bpf_map_push_elem * * Push an element *value* in *map*. *flags* is one of: * * **BPF_EXIST** * If the queue/stack is full, the oldest element is * removed to make room for this. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_map_push_elem)(void *map, const void *value, __u64 flags) = (void *) 87; /* * bpf_map_pop_elem * * Pop an element from *map*. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_map_pop_elem)(void *map, void *value) = (void *) 88; /* * bpf_map_peek_elem * * Get an element from *map* without removing it. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_map_peek_elem)(void *map, void *value) = (void *) 89; /* * bpf_msg_push_data * * For socket policies, insert *len* bytes into *msg* at offset * *start*. * * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a * *msg* it may want to insert metadata or options into the *msg*. * This can later be read and used by any of the lower layer BPF * hooks. * * This helper may fail if under memory pressure (a malloc * fails) in these cases BPF programs will get an appropriate * error and BPF programs will need to handle them. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_msg_push_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 90; /* * bpf_msg_pop_data * * Will remove *len* bytes from a *msg* starting at byte *start*. * This may result in **ENOMEM** errors under certain situations if * an allocation and copy are required due to a full ring buffer. * However, the helper will try to avoid doing the allocation * if possible. Other errors can occur if input parameters are * invalid either due to *start* byte not being valid part of *msg* * payload and/or *pop* value being to large. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_msg_pop_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 91; /* * bpf_rc_pointer_rel * * This helper is used in programs implementing IR decoding, to * report a successfully decoded pointer movement. * * The *ctx* should point to the lirc sample as passed into * the program. * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * * Returns * 0 */ static long (*bpf_rc_pointer_rel)(void *ctx, __s32 rel_x, __s32 rel_y) = (void *) 92; /* * bpf_spin_lock * * Acquire a spinlock represented by the pointer *lock*, which is * stored as part of a value of a map. Taking the lock allows to * safely update the rest of the fields in that value. The * spinlock can (and must) later be released with a call to * **bpf_spin_unlock**\ (\ *lock*\ ). * * Spinlocks in BPF programs come with a number of restrictions * and constraints: * * * **bpf_spin_lock** objects are only allowed inside maps of * types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this * list could be extended in the future). * * BTF description of the map is mandatory. * * The BPF program can take ONE lock at a time, since taking two * or more could cause dead locks. * * Only one **struct bpf_spin_lock** is allowed per map element. * * When the lock is taken, calls (either BPF to BPF or helpers) * are not allowed. * * The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not * allowed inside a spinlock-ed region. * * The BPF program MUST call **bpf_spin_unlock**\ () to release * the lock, on all execution paths, before it returns. * * The BPF program can access **struct bpf_spin_lock** only via * the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ () * helpers. Loading or storing data into the **struct * bpf_spin_lock** *lock*\ **;** field of a map is not allowed. * * To use the **bpf_spin_lock**\ () helper, the BTF description * of the map value must be a struct and have **struct * bpf_spin_lock** *anyname*\ **;** field at the top level. * Nested lock inside another struct is not allowed. * * The **struct bpf_spin_lock** *lock* field in a map value must * be aligned on a multiple of 4 bytes in that value. * * Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy * the **bpf_spin_lock** field to user space. * * Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from * a BPF program, do not update the **bpf_spin_lock** field. * * **bpf_spin_lock** cannot be on the stack or inside a * networking packet (it can only be inside of a map values). * * **bpf_spin_lock** is available to root only. * * Tracing programs and socket filter programs cannot use * **bpf_spin_lock**\ () due to insufficient preemption checks * (but this may change in the future). * * **bpf_spin_lock** is not allowed in inner maps of map-in-map. * * Returns * 0 */ static long (*bpf_spin_lock)(struct bpf_spin_lock *lock) = (void *) 93; /* * bpf_spin_unlock * * Release the *lock* previously locked by a call to * **bpf_spin_lock**\ (\ *lock*\ ). * * Returns * 0 */ static long (*bpf_spin_unlock)(struct bpf_spin_lock *lock) = (void *) 94; /* * bpf_sk_fullsock * * This helper gets a **struct bpf_sock** pointer such * that all the fields in this **bpf_sock** can be accessed. * * Returns * A **struct bpf_sock** pointer on success, or **NULL** in * case of failure. */ static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) = (void *) 95; /* * bpf_tcp_sock * * This helper gets a **struct bpf_tcp_sock** pointer from a * **struct bpf_sock** pointer. * * Returns * A **struct bpf_tcp_sock** pointer on success, or **NULL** in * case of failure. */ static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) = (void *) 96; /* * bpf_skb_ecn_set_ce * * Set ECN (Explicit Congestion Notification) field of IP header * to **CE** (Congestion Encountered) if current value is **ECT** * (ECN Capable Transport). Otherwise, do nothing. Works with IPv6 * and IPv4. * * Returns * 1 if the **CE** flag is set (either by the current helper call * or because it was already present), 0 if it is not set. */ static long (*bpf_skb_ecn_set_ce)(struct __sk_buff *skb) = (void *) 97; /* * bpf_get_listener_sock * * Return a **struct bpf_sock** pointer in **TCP_LISTEN** state. * **bpf_sk_release**\ () is unnecessary and not allowed. * * Returns * A **struct bpf_sock** pointer on success, or **NULL** in * case of failure. */ static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) = (void *) 98; /* * bpf_skc_lookup_tcp * * Look for TCP socket matching *tuple*, optionally in a child * network namespace *netns*. The return value must be checked, * and if non-**NULL**, released via **bpf_sk_release**\ (). * * This function is identical to **bpf_sk_lookup_tcp**\ (), except * that it also returns timewait or request sockets. Use * **bpf_sk_fullsock**\ () or **bpf_tcp_sock**\ () to access the * full structure. * * This helper is available only if the kernel was compiled with * **CONFIG_NET** configuration option. * * Returns * Pointer to **struct bpf_sock**, or **NULL** in case of failure. * For sockets with reuseport option, the **struct bpf_sock** * result is from *reuse*\ **->socks**\ [] using the hash of the * tuple. */ static struct bpf_sock *(*bpf_skc_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 99; /* * bpf_tcp_check_syncookie * * Check whether *iph* and *th* contain a valid SYN cookie ACK for * the listening socket in *sk*. * * *iph* points to the start of the IPv4 or IPv6 header, while * *iph_len* contains **sizeof**\ (**struct iphdr**) or * **sizeof**\ (**struct ipv6hdr**). * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header (at least * **sizeof**\ (**struct tcphdr**)). * * Returns * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative * error otherwise. */ static long (*bpf_tcp_check_syncookie)(void *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 100; /* * bpf_sysctl_get_name * * Get name of sysctl in /proc/sys/ and copy it into provided by * program buffer *buf* of size *buf_len*. * * The buffer is always NUL terminated, unless it's zero-sized. * * If *flags* is zero, full name (e.g. "net/ipv4/tcp_mem") is * copied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name * only (e.g. "tcp_mem"). * * Returns * Number of character copied (not including the trailing NUL). * * **-E2BIG** if the buffer wasn't big enough (*buf* will contain * truncated name in this case). */ static long (*bpf_sysctl_get_name)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len, __u64 flags) = (void *) 101; /* * bpf_sysctl_get_current_value * * Get current value of sysctl as it is presented in /proc/sys * (incl. newline, etc), and copy it as a string into provided * by program buffer *buf* of size *buf_len*. * * The whole value is copied, no matter what file position user * space issued e.g. sys_read at. * * The buffer is always NUL terminated, unless it's zero-sized. * * Returns * Number of character copied (not including the trailing NUL). * * **-E2BIG** if the buffer wasn't big enough (*buf* will contain * truncated name in this case). * * **-EINVAL** if current value was unavailable, e.g. because * sysctl is uninitialized and read returns -EIO for it. */ static long (*bpf_sysctl_get_current_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 102; /* * bpf_sysctl_get_new_value * * Get new value being written by user space to sysctl (before * the actual write happens) and copy it as a string into * provided by program buffer *buf* of size *buf_len*. * * User space may write new value at file position > 0. * * The buffer is always NUL terminated, unless it's zero-sized. * * Returns * Number of character copied (not including the trailing NUL). * * **-E2BIG** if the buffer wasn't big enough (*buf* will contain * truncated name in this case). * * **-EINVAL** if sysctl is being read. */ static long (*bpf_sysctl_get_new_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 103; /* * bpf_sysctl_set_new_value * * Override new value being written by user space to sysctl with * value provided by program in buffer *buf* of size *buf_len*. * * *buf* should contain a string in same form as provided by user * space on sysctl write. * * User space may write new value at file position > 0. To override * the whole sysctl value file position should be set to zero. * * Returns * 0 on success. * * **-E2BIG** if the *buf_len* is too big. * * **-EINVAL** if sysctl is being read. */ static long (*bpf_sysctl_set_new_value)(struct bpf_sysctl *ctx, const char *buf, unsigned long buf_len) = (void *) 104; /* * bpf_strtol * * Convert the initial part of the string from buffer *buf* of * size *buf_len* to a long integer according to the given base * and save the result in *res*. * * The string may begin with an arbitrary amount of white space * (as determined by **isspace**\ (3)) followed by a single * optional '**-**' sign. * * Five least significant bits of *flags* encode base, other bits * are currently unused. * * Base must be either 8, 10, 16 or 0 to detect it automatically * similar to user space **strtol**\ (3). * * Returns * Number of characters consumed on success. Must be positive but * no more than *buf_len*. * * **-EINVAL** if no valid digits were found or unsupported base * was provided. * * **-ERANGE** if resulting value was out of range. */ static long (*bpf_strtol)(const char *buf, unsigned long buf_len, __u64 flags, long *res) = (void *) 105; /* * bpf_strtoul * * Convert the initial part of the string from buffer *buf* of * size *buf_len* to an unsigned long integer according to the * given base and save the result in *res*. * * The string may begin with an arbitrary amount of white space * (as determined by **isspace**\ (3)). * * Five least significant bits of *flags* encode base, other bits * are currently unused. * * Base must be either 8, 10, 16 or 0 to detect it automatically * similar to user space **strtoul**\ (3). * * Returns * Number of characters consumed on success. Must be positive but * no more than *buf_len*. * * **-EINVAL** if no valid digits were found or unsupported base * was provided. * * **-ERANGE** if resulting value was out of range. */ static long (*bpf_strtoul)(const char *buf, unsigned long buf_len, __u64 flags, unsigned long *res) = (void *) 106; /* * bpf_sk_storage_get * * Get a bpf-local-storage from a *sk*. * * Logically, it could be thought of getting the value from * a *map* with *sk* as the **key**. From this * perspective, the usage is not much different from * **bpf_map_lookup_elem**\ (*map*, **&**\ *sk*) except this * helper enforces the key must be a full socket and the map must * be a **BPF_MAP_TYPE_SK_STORAGE** also. * * Underneath, the value is stored locally at *sk* instead of * the *map*. The *map* is used as the bpf-local-storage * "type". The bpf-local-storage "type" (i.e. the *map*) is * searched against all bpf-local-storages residing at *sk*. * * *sk* is a kernel **struct sock** pointer for LSM program. * *sk* is a **struct bpf_sock** pointer for other program types. * * An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be * used such that a new bpf-local-storage will be * created if one does not exist. *value* can be used * together with **BPF_SK_STORAGE_GET_F_CREATE** to specify * the initial value of a bpf-local-storage. If *value* is * **NULL**, the new bpf-local-storage will be zero initialized. * * Returns * A bpf-local-storage pointer is returned on success. * * **NULL** if not found or there was an error in adding * a new bpf-local-storage. */ static void *(*bpf_sk_storage_get)(void *map, void *sk, void *value, __u64 flags) = (void *) 107; /* * bpf_sk_storage_delete * * Delete a bpf-local-storage from a *sk*. * * Returns * 0 on success. * * **-ENOENT** if the bpf-local-storage cannot be found. * **-EINVAL** if sk is not a fullsock (e.g. a request_sock). */ static long (*bpf_sk_storage_delete)(void *map, void *sk) = (void *) 108; /* * bpf_send_signal * * Send signal *sig* to the process of the current task. * The signal may be delivered to any of this process's threads. * * Returns * 0 on success or successfully queued. * * **-EBUSY** if work queue under nmi is full. * * **-EINVAL** if *sig* is invalid. * * **-EPERM** if no permission to send the *sig*. * * **-EAGAIN** if bpf program can try again. */ static long (*bpf_send_signal)(__u32 sig) = (void *) 109; /* * bpf_tcp_gen_syncookie * * Try to issue a SYN cookie for the packet with corresponding * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*. * * *iph* points to the start of the IPv4 or IPv6 header, while * *iph_len* contains **sizeof**\ (**struct iphdr**) or * **sizeof**\ (**struct ipv6hdr**). * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header with options (at least * **sizeof**\ (**struct tcphdr**)). * * Returns * On success, lower 32 bits hold the generated SYN cookie in * followed by 16 bits which hold the MSS value for that cookie, * and the top 16 bits are unused. * * On failure, the returned value is one of the following: * * **-EINVAL** SYN cookie cannot be issued due to error * * **-ENOENT** SYN cookie should not be issued (no SYN flood) * * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies * * **-EPROTONOSUPPORT** IP packet version is not 4 or 6 */ static __s64 (*bpf_tcp_gen_syncookie)(void *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 110; /* * bpf_skb_output * * Write raw *data* blob into a special BPF perf event held by * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf * event must have the following attributes: **PERF_SAMPLE_RAW** * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. * * The *flags* are used to indicate the index in *map* for which * the value must be put, masked with **BPF_F_INDEX_MASK**. * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** * to indicate that the index of the current CPU core should be * used. * * The value to write, of *size*, is passed through eBPF stack and * pointed by *data*. * * *ctx* is a pointer to in-kernel struct sk_buff. * * This helper is similar to **bpf_perf_event_output**\ () but * restricted to raw_tracepoint bpf programs. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 111; /* * bpf_probe_read_user * * Safely attempt to read *size* bytes from user space address * *unsafe_ptr* and store the data in *dst*. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_probe_read_user)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 112; /* * bpf_probe_read_kernel * * Safely attempt to read *size* bytes from kernel space address * *unsafe_ptr* and store the data in *dst*. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_probe_read_kernel)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 113; /* * bpf_probe_read_user_str * * Copy a NUL terminated string from an unsafe user address * *unsafe_ptr* to *dst*. The *size* should include the * terminating NUL byte. In case the string length is smaller than * *size*, the target is not padded with further NUL bytes. If the * string length is larger than *size*, just *size*-1 bytes are * copied and the last byte is set to NUL. * * On success, returns the number of bytes that were written, * including the terminal NUL. This makes this helper useful in * tracing programs for reading strings, and more importantly to * get its length at runtime. See the following snippet: * * :: * * SEC("kprobe/sys_open") * void bpf_sys_open(struct pt_regs *ctx) * { * char buf[PATHLEN]; // PATHLEN is defined to 256 * int res = bpf_probe_read_user_str(buf, sizeof(buf), * ctx->di); * * // Consume buf, for example push it to * // userspace via bpf_perf_event_output(); we * // can use res (the string length) as event * // size, after checking its boundaries. * } * * In comparison, using **bpf_probe_read_user**\ () helper here * instead to read the string would require to estimate the length * at compile time, and would often result in copying more memory * than necessary. * * Another useful use case is when parsing individual process * arguments or individual environment variables navigating * *current*\ **->mm->arg_start** and *current*\ * **->mm->env_start**: using this helper and the return value, * one can quickly iterate at the right offset of the memory area. * * Returns * On success, the strictly positive length of the output string, * including the trailing NUL character. On error, a negative * value. */ static long (*bpf_probe_read_user_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 114; /* * bpf_probe_read_kernel_str * * Copy a NUL terminated string from an unsafe kernel address *unsafe_ptr* * to *dst*. Same semantics as with **bpf_probe_read_user_str**\ () apply. * * Returns * On success, the strictly positive length of the string, including * the trailing NUL character. On error, a negative value. */ static long (*bpf_probe_read_kernel_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 115; /* * bpf_tcp_send_ack * * Send out a tcp-ack. *tp* is the in-kernel struct **tcp_sock**. * *rcv_nxt* is the ack_seq to be sent out. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_tcp_send_ack)(void *tp, __u32 rcv_nxt) = (void *) 116; /* * bpf_send_signal_thread * * Send signal *sig* to the thread corresponding to the current task. * * Returns * 0 on success or successfully queued. * * **-EBUSY** if work queue under nmi is full. * * **-EINVAL** if *sig* is invalid. * * **-EPERM** if no permission to send the *sig*. * * **-EAGAIN** if bpf program can try again. */ static long (*bpf_send_signal_thread)(__u32 sig) = (void *) 117; /* * bpf_jiffies64 * * Obtain the 64bit jiffies * * Returns * The 64 bit jiffies */ static __u64 (*bpf_jiffies64)(void) = (void *) 118; /* * bpf_read_branch_records * * For an eBPF program attached to a perf event, retrieve the * branch records (**struct perf_branch_entry**) associated to *ctx* * and store it in the buffer pointed by *buf* up to size * *size* bytes. * * Returns * On success, number of bytes written to *buf*. On error, a * negative value. * * The *flags* can be set to **BPF_F_GET_BRANCH_RECORDS_SIZE** to * instead return the number of bytes required to store all the * branch entries. If this flag is set, *buf* may be NULL. * * **-EINVAL** if arguments invalid or **size** not a multiple * of **sizeof**\ (**struct perf_branch_entry**\ ). * * **-ENOENT** if architecture does not support branch records. */ static long (*bpf_read_branch_records)(struct bpf_perf_event_data *ctx, void *buf, __u32 size, __u64 flags) = (void *) 119; /* * bpf_get_ns_current_pid_tgid * * Returns 0 on success, values for *pid* and *tgid* as seen from the current * *namespace* will be returned in *nsdata*. * * Returns * 0 on success, or one of the following in case of failure: * * **-EINVAL** if dev and inum supplied don't match dev_t and inode number * with nsfs of current task, or if dev conversion to dev_t lost high bits. * * **-ENOENT** if pidns does not exists for the current task. */ static long (*bpf_get_ns_current_pid_tgid)(__u64 dev, __u64 ino, struct bpf_pidns_info *nsdata, __u32 size) = (void *) 120; /* * bpf_xdp_output * * Write raw *data* blob into a special BPF perf event held by * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf * event must have the following attributes: **PERF_SAMPLE_RAW** * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. * * The *flags* are used to indicate the index in *map* for which * the value must be put, masked with **BPF_F_INDEX_MASK**. * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** * to indicate that the index of the current CPU core should be * used. * * The value to write, of *size*, is passed through eBPF stack and * pointed by *data*. * * *ctx* is a pointer to in-kernel struct xdp_buff. * * This helper is similar to **bpf_perf_eventoutput**\ () but * restricted to raw_tracepoint bpf programs. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_xdp_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 121; /* * bpf_get_netns_cookie * * Retrieve the cookie (generated by the kernel) of the network * namespace the input *ctx* is associated with. The network * namespace cookie remains stable for its lifetime and provides * a global identifier that can be assumed unique. If *ctx* is * NULL, then the helper returns the cookie for the initial * network namespace. The cookie itself is very similar to that * of **bpf_get_socket_cookie**\ () helper, but for network * namespaces instead of sockets. * * Returns * A 8-byte long opaque number. */ static __u64 (*bpf_get_netns_cookie)(void *ctx) = (void *) 122; /* * bpf_get_current_ancestor_cgroup_id * * Return id of cgroup v2 that is ancestor of the cgroup associated * with the current task at the *ancestor_level*. The root cgroup * is at *ancestor_level* zero and each step down the hierarchy * increments the level. If *ancestor_level* == level of cgroup * associated with the current task, then return value will be the * same as that of **bpf_get_current_cgroup_id**\ (). * * The helper is useful to implement policies based on cgroups * that are upper in hierarchy than immediate cgroup associated * with the current task. * * The format of returned id and helper limitations are same as in * **bpf_get_current_cgroup_id**\ (). * * Returns * The id is returned or 0 in case the id could not be retrieved. */ static __u64 (*bpf_get_current_ancestor_cgroup_id)(int ancestor_level) = (void *) 123; /* * bpf_sk_assign * * Helper is overloaded depending on BPF program type. This * description applies to **BPF_PROG_TYPE_SCHED_CLS** and * **BPF_PROG_TYPE_SCHED_ACT** programs. * * Assign the *sk* to the *skb*. When combined with appropriate * routing configuration to receive the packet towards the socket, * will cause *skb* to be delivered to the specified socket. * Subsequent redirection of *skb* via **bpf_redirect**\ (), * **bpf_clone_redirect**\ () or other methods outside of BPF may * interfere with successful delivery to the socket. * * This operation is only valid from TC ingress path. * * The *flags* argument must be zero. * * Returns * 0 on success, or a negative error in case of failure: * * **-EINVAL** if specified *flags* are not supported. * * **-ENOENT** if the socket is unavailable for assignment. * * **-ENETUNREACH** if the socket is unreachable (wrong netns). * * **-EOPNOTSUPP** if the operation is not supported, for example * a call from outside of TC ingress. * * **-ESOCKTNOSUPPORT** if the socket type is not supported * (reuseport). */ static long (*bpf_sk_assign)(void *ctx, void *sk, __u64 flags) = (void *) 124; /* * bpf_ktime_get_boot_ns * * Return the time elapsed since system boot, in nanoseconds. * Does include the time the system was suspended. * See: **clock_gettime**\ (**CLOCK_BOOTTIME**) * * Returns * Current *ktime*. */ static __u64 (*bpf_ktime_get_boot_ns)(void) = (void *) 125; /* * bpf_seq_printf * * **bpf_seq_printf**\ () uses seq_file **seq_printf**\ () to print * out the format string. * The *m* represents the seq_file. The *fmt* and *fmt_size* are for * the format string itself. The *data* and *data_len* are format string * arguments. The *data* are a **u64** array and corresponding format string * values are stored in the array. For strings and pointers where pointees * are accessed, only the pointer values are stored in the *data* array. * The *data_len* is the size of *data* in bytes - must be a multiple of 8. * * Formats **%s**, **%p{i,I}{4,6}** requires to read kernel memory. * Reading kernel memory may fail due to either invalid address or * valid address but requiring a major memory fault. If reading kernel memory * fails, the string for **%s** will be an empty string, and the ip * address for **%p{i,I}{4,6}** will be 0. Not returning error to * bpf program is consistent with what **bpf_trace_printk**\ () does for now. * * Returns * 0 on success, or a negative error in case of failure: * * **-EBUSY** if per-CPU memory copy buffer is busy, can try again * by returning 1 from bpf program. * * **-EINVAL** if arguments are invalid, or if *fmt* is invalid/unsupported. * * **-E2BIG** if *fmt* contains too many format specifiers. * * **-EOVERFLOW** if an overflow happened: The same object will be tried again. */ static long (*bpf_seq_printf)(struct seq_file *m, const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 126; /* * bpf_seq_write * * **bpf_seq_write**\ () uses seq_file **seq_write**\ () to write the data. * The *m* represents the seq_file. The *data* and *len* represent the * data to write in bytes. * * Returns * 0 on success, or a negative error in case of failure: * * **-EOVERFLOW** if an overflow happened: The same object will be tried again. */ static long (*bpf_seq_write)(struct seq_file *m, const void *data, __u32 len) = (void *) 127; /* * bpf_sk_cgroup_id * * Return the cgroup v2 id of the socket *sk*. * * *sk* must be a non-**NULL** pointer to a socket, e.g. one * returned from **bpf_sk_lookup_xxx**\ (), * **bpf_sk_fullsock**\ (), etc. The format of returned id is * same as in **bpf_skb_cgroup_id**\ (). * * This helper is available only if the kernel was compiled with * the **CONFIG_SOCK_CGROUP_DATA** configuration option. * * Returns * The id is returned or 0 in case the id could not be retrieved. */ static __u64 (*bpf_sk_cgroup_id)(void *sk) = (void *) 128; /* * bpf_sk_ancestor_cgroup_id * * Return id of cgroup v2 that is ancestor of cgroup associated * with the *sk* at the *ancestor_level*. The root cgroup is at * *ancestor_level* zero and each step down the hierarchy * increments the level. If *ancestor_level* == level of cgroup * associated with *sk*, then return value will be same as that * of **bpf_sk_cgroup_id**\ (). * * The helper is useful to implement policies based on cgroups * that are upper in hierarchy than immediate cgroup associated * with *sk*. * * The format of returned id and helper limitations are same as in * **bpf_sk_cgroup_id**\ (). * * Returns * The id is returned or 0 in case the id could not be retrieved. */ static __u64 (*bpf_sk_ancestor_cgroup_id)(void *sk, int ancestor_level) = (void *) 129; /* * bpf_ringbuf_output * * Copy *size* bytes from *data* into a ring buffer *ringbuf*. * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification * of new data availability is sent. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. * If **0** is specified in *flags*, an adaptive notification * of new data availability is sent. * * An adaptive notification is a notification sent whenever the user-space * process has caught up and consumed all available payloads. In case the user-space * process is still processing a previous payload, then no notification is needed * as it will process the newly added payload automatically. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_ringbuf_output)(void *ringbuf, void *data, __u64 size, __u64 flags) = (void *) 130; /* * bpf_ringbuf_reserve * * Reserve *size* bytes of payload in a ring buffer *ringbuf*. * *flags* must be 0. * * Returns * Valid pointer with *size* bytes of memory available; NULL, * otherwise. */ static void *(*bpf_ringbuf_reserve)(void *ringbuf, __u64 size, __u64 flags) = (void *) 131; /* * bpf_ringbuf_submit * * Submit reserved ring buffer sample, pointed to by *data*. * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification * of new data availability is sent. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. * If **0** is specified in *flags*, an adaptive notification * of new data availability is sent. * * See 'bpf_ringbuf_output()' for the definition of adaptive notification. * * Returns * Nothing. Always succeeds. */ static void (*bpf_ringbuf_submit)(void *data, __u64 flags) = (void *) 132; /* * bpf_ringbuf_discard * * Discard reserved ring buffer sample, pointed to by *data*. * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification * of new data availability is sent. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. * If **0** is specified in *flags*, an adaptive notification * of new data availability is sent. * * See 'bpf_ringbuf_output()' for the definition of adaptive notification. * * Returns * Nothing. Always succeeds. */ static void (*bpf_ringbuf_discard)(void *data, __u64 flags) = (void *) 133; /* * bpf_ringbuf_query * * Query various characteristics of provided ring buffer. What * exactly is queries is determined by *flags*: * * * **BPF_RB_AVAIL_DATA**: Amount of data not yet consumed. * * **BPF_RB_RING_SIZE**: The size of ring buffer. * * **BPF_RB_CONS_POS**: Consumer position (can wrap around). * * **BPF_RB_PROD_POS**: Producer(s) position (can wrap around). * * Data returned is just a momentary snapshot of actual values * and could be inaccurate, so this facility should be used to * power heuristics and for reporting, not to make 100% correct * calculation. * * Returns * Requested value, or 0, if *flags* are not recognized. */ static __u64 (*bpf_ringbuf_query)(void *ringbuf, __u64 flags) = (void *) 134; /* * bpf_csum_level * * Change the skbs checksum level by one layer up or down, or * reset it entirely to none in order to have the stack perform * checksum validation. The level is applicable to the following * protocols: TCP, UDP, GRE, SCTP, FCOE. For example, a decap of * | ETH | IP | UDP | GUE | IP | TCP | into | ETH | IP | TCP | * through **bpf_skb_adjust_room**\ () helper with passing in * **BPF_F_ADJ_ROOM_NO_CSUM_RESET** flag would require one call * to **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_DEC** since * the UDP header is removed. Similarly, an encap of the latter * into the former could be accompanied by a helper call to * **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_INC** if the * skb is still intended to be processed in higher layers of the * stack instead of just egressing at tc. * * There are three supported level settings at this time: * * * **BPF_CSUM_LEVEL_INC**: Increases skb->csum_level for skbs * with CHECKSUM_UNNECESSARY. * * **BPF_CSUM_LEVEL_DEC**: Decreases skb->csum_level for skbs * with CHECKSUM_UNNECESSARY. * * **BPF_CSUM_LEVEL_RESET**: Resets skb->csum_level to 0 and * sets CHECKSUM_NONE to force checksum validation by the stack. * * **BPF_CSUM_LEVEL_QUERY**: No-op, returns the current * skb->csum_level. * * Returns * 0 on success, or a negative error in case of failure. In the * case of **BPF_CSUM_LEVEL_QUERY**, the current skb->csum_level * is returned or the error code -EACCES in case the skb is not * subject to CHECKSUM_UNNECESSARY. */ static long (*bpf_csum_level)(struct __sk_buff *skb, __u64 level) = (void *) 135; /* * bpf_skc_to_tcp6_sock * * Dynamically cast a *sk* pointer to a *tcp6_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct tcp6_sock *(*bpf_skc_to_tcp6_sock)(void *sk) = (void *) 136; /* * bpf_skc_to_tcp_sock * * Dynamically cast a *sk* pointer to a *tcp_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct tcp_sock *(*bpf_skc_to_tcp_sock)(void *sk) = (void *) 137; /* * bpf_skc_to_tcp_timewait_sock * * Dynamically cast a *sk* pointer to a *tcp_timewait_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct tcp_timewait_sock *(*bpf_skc_to_tcp_timewait_sock)(void *sk) = (void *) 138; /* * bpf_skc_to_tcp_request_sock * * Dynamically cast a *sk* pointer to a *tcp_request_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct tcp_request_sock *(*bpf_skc_to_tcp_request_sock)(void *sk) = (void *) 139; /* * bpf_skc_to_udp6_sock * * Dynamically cast a *sk* pointer to a *udp6_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct udp6_sock *(*bpf_skc_to_udp6_sock)(void *sk) = (void *) 140; /* * bpf_get_task_stack * * Return a user or a kernel stack in bpf program provided buffer. * To achieve this, the helper needs *task*, which is a valid * pointer to **struct task_struct**. To store the stacktrace, the * bpf program provides *buf* with a nonnegative *size*. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * **BPF_F_USER_BUILD_ID** * Collect buildid+offset instead of ips for user stack, * only valid if **BPF_F_USER_STACK** is also specified. * * **bpf_get_task_stack**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject * to sufficient large buffer size. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack= * * Returns * The non-negative copied *buf* length equal to or less than * *size* on success, or a negative error in case of failure. */ static long (*bpf_get_task_stack)(struct task_struct *task, void *buf, __u32 size, __u64 flags) = (void *) 141; /* * bpf_load_hdr_opt * * Load header option. Support reading a particular TCP header * option for bpf program (**BPF_PROG_TYPE_SOCK_OPS**). * * If *flags* is 0, it will search the option from the * *skops*\ **->skb_data**. The comment in **struct bpf_sock_ops** * has details on what skb_data contains under different * *skops*\ **->op**. * * The first byte of the *searchby_res* specifies the * kind that it wants to search. * * If the searching kind is an experimental kind * (i.e. 253 or 254 according to RFC6994). It also * needs to specify the "magic" which is either * 2 bytes or 4 bytes. It then also needs to * specify the size of the magic by using * the 2nd byte which is "kind-length" of a TCP * header option and the "kind-length" also * includes the first 2 bytes "kind" and "kind-length" * itself as a normal TCP header option also does. * * For example, to search experimental kind 254 with * 2 byte magic 0xeB9F, the searchby_res should be * [ 254, 4, 0xeB, 0x9F, 0, 0, .... 0 ]. * * To search for the standard window scale option (3), * the *searchby_res* should be [ 3, 0, 0, .... 0 ]. * Note, kind-length must be 0 for regular option. * * Searching for No-Op (0) and End-of-Option-List (1) are * not supported. * * *len* must be at least 2 bytes which is the minimal size * of a header option. * * Supported flags: * * * **BPF_LOAD_HDR_OPT_TCP_SYN** to search from the * saved_syn packet or the just-received syn packet. * * * Returns * > 0 when found, the header option is copied to *searchby_res*. * The return value is the total length copied. On failure, a * negative error code is returned: * * **-EINVAL** if a parameter is invalid. * * **-ENOMSG** if the option is not found. * * **-ENOENT** if no syn packet is available when * **BPF_LOAD_HDR_OPT_TCP_SYN** is used. * * **-ENOSPC** if there is not enough space. Only *len* number of * bytes are copied. * * **-EFAULT** on failure to parse the header options in the * packet. * * **-EPERM** if the helper cannot be used under the current * *skops*\ **->op**. */ static long (*bpf_load_hdr_opt)(struct bpf_sock_ops *skops, void *searchby_res, __u32 len, __u64 flags) = (void *) 142; /* * bpf_store_hdr_opt * * Store header option. The data will be copied * from buffer *from* with length *len* to the TCP header. * * The buffer *from* should have the whole option that * includes the kind, kind-length, and the actual * option data. The *len* must be at least kind-length * long. The kind-length does not have to be 4 byte * aligned. The kernel will take care of the padding * and setting the 4 bytes aligned value to th->doff. * * This helper will check for duplicated option * by searching the same option in the outgoing skb. * * This helper can only be called during * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. * * * Returns * 0 on success, or negative error in case of failure: * * **-EINVAL** If param is invalid. * * **-ENOSPC** if there is not enough space in the header. * Nothing has been written * * **-EEXIST** if the option already exists. * * **-EFAULT** on failure to parse the existing header options. * * **-EPERM** if the helper cannot be used under the current * *skops*\ **->op**. */ static long (*bpf_store_hdr_opt)(struct bpf_sock_ops *skops, const void *from, __u32 len, __u64 flags) = (void *) 143; /* * bpf_reserve_hdr_opt * * Reserve *len* bytes for the bpf header option. The * space will be used by **bpf_store_hdr_opt**\ () later in * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. * * If **bpf_reserve_hdr_opt**\ () is called multiple times, * the total number of bytes will be reserved. * * This helper can only be called during * **BPF_SOCK_OPS_HDR_OPT_LEN_CB**. * * * Returns * 0 on success, or negative error in case of failure: * * **-EINVAL** if a parameter is invalid. * * **-ENOSPC** if there is not enough space in the header. * * **-EPERM** if the helper cannot be used under the current * *skops*\ **->op**. */ static long (*bpf_reserve_hdr_opt)(struct bpf_sock_ops *skops, __u32 len, __u64 flags) = (void *) 144; /* * bpf_inode_storage_get * * Get a bpf_local_storage from an *inode*. * * Logically, it could be thought of as getting the value from * a *map* with *inode* as the **key**. From this * perspective, the usage is not much different from * **bpf_map_lookup_elem**\ (*map*, **&**\ *inode*) except this * helper enforces the key must be an inode and the map must also * be a **BPF_MAP_TYPE_INODE_STORAGE**. * * Underneath, the value is stored locally at *inode* instead of * the *map*. The *map* is used as the bpf-local-storage * "type". The bpf-local-storage "type" (i.e. the *map*) is * searched against all bpf_local_storage residing at *inode*. * * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be * used such that a new bpf_local_storage will be * created if one does not exist. *value* can be used * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify * the initial value of a bpf_local_storage. If *value* is * **NULL**, the new bpf_local_storage will be zero initialized. * * Returns * A bpf_local_storage pointer is returned on success. * * **NULL** if not found or there was an error in adding * a new bpf_local_storage. */ static void *(*bpf_inode_storage_get)(void *map, void *inode, void *value, __u64 flags) = (void *) 145; /* * bpf_inode_storage_delete * * Delete a bpf_local_storage from an *inode*. * * Returns * 0 on success. * * **-ENOENT** if the bpf_local_storage cannot be found. */ static int (*bpf_inode_storage_delete)(void *map, void *inode) = (void *) 146; /* * bpf_d_path * * Return full path for given **struct path** object, which * needs to be the kernel BTF *path* object. The path is * returned in the provided buffer *buf* of size *sz* and * is zero terminated. * * * Returns * On success, the strictly positive length of the string, * including the trailing NUL character. On error, a negative * value. */ static long (*bpf_d_path)(struct path *path, char *buf, __u32 sz) = (void *) 147; /* * bpf_copy_from_user * * Read *size* bytes from user space address *user_ptr* and store * the data in *dst*. This is a wrapper of **copy_from_user**\ (). * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_copy_from_user)(void *dst, __u32 size, const void *user_ptr) = (void *) 148; /* * bpf_snprintf_btf * * Use BTF to store a string representation of *ptr*->ptr in *str*, * using *ptr*->type_id. This value should specify the type * that *ptr*->ptr points to. LLVM __builtin_btf_type_id(type, 1) * can be used to look up vmlinux BTF type ids. Traversing the * data structure using BTF, the type information and values are * stored in the first *str_size* - 1 bytes of *str*. Safe copy of * the pointer data is carried out to avoid kernel crashes during * operation. Smaller types can use string space on the stack; * larger programs can use map data to store the string * representation. * * The string can be subsequently shared with userspace via * bpf_perf_event_output() or ring buffer interfaces. * bpf_trace_printk() is to be avoided as it places too small * a limit on string size to be useful. * * *flags* is a combination of * * **BTF_F_COMPACT** * no formatting around type information * **BTF_F_NONAME** * no struct/union member names/types * **BTF_F_PTR_RAW** * show raw (unobfuscated) pointer values; * equivalent to printk specifier %px. * **BTF_F_ZERO** * show zero-valued struct/union members; they * are not displayed by default * * * Returns * The number of bytes that were written (or would have been * written if output had to be truncated due to string size), * or a negative error in cases of failure. */ static long (*bpf_snprintf_btf)(char *str, __u32 str_size, struct btf_ptr *ptr, __u32 btf_ptr_size, __u64 flags) = (void *) 149; /* * bpf_seq_printf_btf * * Use BTF to write to seq_write a string representation of * *ptr*->ptr, using *ptr*->type_id as per bpf_snprintf_btf(). * *flags* are identical to those used for bpf_snprintf_btf. * * Returns * 0 on success or a negative error in case of failure. */ static long (*bpf_seq_printf_btf)(struct seq_file *m, struct btf_ptr *ptr, __u32 ptr_size, __u64 flags) = (void *) 150; /* * bpf_skb_cgroup_classid * * See **bpf_get_cgroup_classid**\ () for the main description. * This helper differs from **bpf_get_cgroup_classid**\ () in that * the cgroup v1 net_cls class is retrieved only from the *skb*'s * associated socket instead of the current process. * * Returns * The id is returned or 0 in case the id could not be retrieved. */ static __u64 (*bpf_skb_cgroup_classid)(struct __sk_buff *skb) = (void *) 151; /* * bpf_redirect_neigh * * Redirect the packet to another net device of index *ifindex* * and fill in L2 addresses from neighboring subsystem. This helper * is somewhat similar to **bpf_redirect**\ (), except that it * populates L2 addresses as well, meaning, internally, the helper * relies on the neighbor lookup for the L2 address of the nexthop. * * The helper will perform a FIB lookup based on the skb's * networking header to get the address of the next hop, unless * this is supplied by the caller in the *params* argument. The * *plen* argument indicates the len of *params* and should be set * to 0 if *params* is NULL. * * The *flags* argument is reserved and must be 0. The helper is * currently only supported for tc BPF program types, and enabled * for IPv4 and IPv6 protocols. * * Returns * The helper returns **TC_ACT_REDIRECT** on success or * **TC_ACT_SHOT** on error. */ static long (*bpf_redirect_neigh)(__u32 ifindex, struct bpf_redir_neigh *params, int plen, __u64 flags) = (void *) 152; /* * bpf_per_cpu_ptr * * Take a pointer to a percpu ksym, *percpu_ptr*, and return a * pointer to the percpu kernel variable on *cpu*. A ksym is an * extern variable decorated with '__ksym'. For ksym, there is a * global var (either static or global) defined of the same name * in the kernel. The ksym is percpu if the global var is percpu. * The returned pointer points to the global percpu var on *cpu*. * * bpf_per_cpu_ptr() has the same semantic as per_cpu_ptr() in the * kernel, except that bpf_per_cpu_ptr() may return NULL. This * happens if *cpu* is larger than nr_cpu_ids. The caller of * bpf_per_cpu_ptr() must check the returned value. * * Returns * A pointer pointing to the kernel percpu variable on *cpu*, or * NULL, if *cpu* is invalid. */ static void *(*bpf_per_cpu_ptr)(const void *percpu_ptr, __u32 cpu) = (void *) 153; /* * bpf_this_cpu_ptr * * Take a pointer to a percpu ksym, *percpu_ptr*, and return a * pointer to the percpu kernel variable on this cpu. See the * description of 'ksym' in **bpf_per_cpu_ptr**\ (). * * bpf_this_cpu_ptr() has the same semantic as this_cpu_ptr() in * the kernel. Different from **bpf_per_cpu_ptr**\ (), it would * never return NULL. * * Returns * A pointer pointing to the kernel percpu variable on this cpu. */ static void *(*bpf_this_cpu_ptr)(const void *percpu_ptr) = (void *) 154; /* * bpf_redirect_peer * * Redirect the packet to another net device of index *ifindex*. * This helper is somewhat similar to **bpf_redirect**\ (), except * that the redirection happens to the *ifindex*' peer device and * the netns switch takes place from ingress to ingress without * going through the CPU's backlog queue. * * The *flags* argument is reserved and must be 0. The helper is * currently only supported for tc BPF program types at the ingress * hook and for veth device types. The peer device must reside in a * different network namespace. * * Returns * The helper returns **TC_ACT_REDIRECT** on success or * **TC_ACT_SHOT** on error. */ static long (*bpf_redirect_peer)(__u32 ifindex, __u64 flags) = (void *) 155; /* * bpf_task_storage_get * * Get a bpf_local_storage from the *task*. * * Logically, it could be thought of as getting the value from * a *map* with *task* as the **key**. From this * perspective, the usage is not much different from * **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this * helper enforces the key must be a task_struct and the map must also * be a **BPF_MAP_TYPE_TASK_STORAGE**. * * Underneath, the value is stored locally at *task* instead of * the *map*. The *map* is used as the bpf-local-storage * "type". The bpf-local-storage "type" (i.e. the *map*) is * searched against all bpf_local_storage residing at *task*. * * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be * used such that a new bpf_local_storage will be * created if one does not exist. *value* can be used * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify * the initial value of a bpf_local_storage. If *value* is * **NULL**, the new bpf_local_storage will be zero initialized. * * Returns * A bpf_local_storage pointer is returned on success. * * **NULL** if not found or there was an error in adding * a new bpf_local_storage. */ static void *(*bpf_task_storage_get)(void *map, struct task_struct *task, void *value, __u64 flags) = (void *) 156; /* * bpf_task_storage_delete * * Delete a bpf_local_storage from a *task*. * * Returns * 0 on success. * * **-ENOENT** if the bpf_local_storage cannot be found. */ static long (*bpf_task_storage_delete)(void *map, struct task_struct *task) = (void *) 157; /* * bpf_get_current_task_btf * * Return a BTF pointer to the "current" task. * This pointer can also be used in helpers that accept an * *ARG_PTR_TO_BTF_ID* of type *task_struct*. * * Returns * Pointer to the current task. */ static struct task_struct *(*bpf_get_current_task_btf)(void) = (void *) 158; /* * bpf_bprm_opts_set * * Set or clear certain options on *bprm*: * * **BPF_F_BPRM_SECUREEXEC** Set the secureexec bit * which sets the **AT_SECURE** auxv for glibc. The bit * is cleared if the flag is not specified. * * Returns * **-EINVAL** if invalid *flags* are passed, zero otherwise. */ static long (*bpf_bprm_opts_set)(struct linux_binprm *bprm, __u64 flags) = (void *) 159; /* * bpf_ktime_get_coarse_ns * * Return a coarse-grained version of the time elapsed since * system boot, in nanoseconds. Does not include time the system * was suspended. * * See: **clock_gettime**\ (**CLOCK_MONOTONIC_COARSE**) * * Returns * Current *ktime*. */ static __u64 (*bpf_ktime_get_coarse_ns)(void) = (void *) 160; /* * bpf_ima_inode_hash * * Returns the stored IMA hash of the *inode* (if it's available). * If the hash is larger than *size*, then only *size* * bytes will be copied to *dst* * * Returns * The **hash_algo** is returned on success, * **-EOPNOTSUP** if IMA is disabled or **-EINVAL** if * invalid arguments are passed. */ static long (*bpf_ima_inode_hash)(struct inode *inode, void *dst, __u32 size) = (void *) 161; /* * bpf_sock_from_file * * If the given file represents a socket, returns the associated * socket. * * Returns * A pointer to a struct socket on success or NULL if the file is * not a socket. */ static struct socket *(*bpf_sock_from_file)(struct file *file) = (void *) 162; /* * bpf_check_mtu * * Check packet size against exceeding MTU of net device (based * on *ifindex*). This helper will likely be used in combination * with helpers that adjust/change the packet size. * * The argument *len_diff* can be used for querying with a planned * size change. This allows to check MTU prior to changing packet * ctx. Providing a *len_diff* adjustment that is larger than the * actual packet size (resulting in negative packet size) will in * principle not exceed the MTU, which is why it is not considered * a failure. Other BPF helpers are needed for performing the * planned size change; therefore the responsibility for catching * a negative packet size belongs in those helpers. * * Specifying *ifindex* zero means the MTU check is performed * against the current net device. This is practical if this isn't * used prior to redirect. * * On input *mtu_len* must be a valid pointer, else verifier will * reject BPF program. If the value *mtu_len* is initialized to * zero then the ctx packet size is use. When value *mtu_len* is * provided as input this specify the L3 length that the MTU check * is done against. Remember XDP and TC length operate at L2, but * this value is L3 as this correlate to MTU and IP-header tot_len * values which are L3 (similar behavior as bpf_fib_lookup). * * The Linux kernel route table can configure MTUs on a more * specific per route level, which is not provided by this helper. * For route level MTU checks use the **bpf_fib_lookup**\ () * helper. * * *ctx* is either **struct xdp_md** for XDP programs or * **struct sk_buff** for tc cls_act programs. * * The *flags* argument can be a combination of one or more of the * following values: * * **BPF_MTU_CHK_SEGS** * This flag will only works for *ctx* **struct sk_buff**. * If packet context contains extra packet segment buffers * (often knows as GSO skb), then MTU check is harder to * check at this point, because in transmit path it is * possible for the skb packet to get re-segmented * (depending on net device features). This could still be * a MTU violation, so this flag enables performing MTU * check against segments, with a different violation * return code to tell it apart. Check cannot use len_diff. * * On return *mtu_len* pointer contains the MTU value of the net * device. Remember the net device configured MTU is the L3 size, * which is returned here and XDP and TC length operate at L2. * Helper take this into account for you, but remember when using * MTU value in your BPF-code. * * * Returns * * 0 on success, and populate MTU value in *mtu_len* pointer. * * * < 0 if any input argument is invalid (*mtu_len* not updated) * * MTU violations return positive values, but also populate MTU * value in *mtu_len* pointer, as this can be needed for * implementing PMTU handing: * * * **BPF_MTU_CHK_RET_FRAG_NEEDED** * * **BPF_MTU_CHK_RET_SEGS_TOOBIG** */ static long (*bpf_check_mtu)(void *ctx, __u32 ifindex, __u32 *mtu_len, __s32 len_diff, __u64 flags) = (void *) 163; /* * bpf_for_each_map_elem * * For each element in **map**, call **callback_fn** function with * **map**, **callback_ctx** and other map-specific parameters. * The **callback_fn** should be a static function and * the **callback_ctx** should be a pointer to the stack. * The **flags** is used to control certain aspects of the helper. * Currently, the **flags** must be 0. * * The following are a list of supported map types and their * respective expected callback signatures: * * BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_PERCPU_HASH, * BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, * BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PERCPU_ARRAY * * long (\*callback_fn)(struct bpf_map \*map, const void \*key, void \*value, void \*ctx); * * For per_cpu maps, the map_value is the value on the cpu where the * bpf_prog is running. * * If **callback_fn** return 0, the helper will continue to the next * element. If return value is 1, the helper will skip the rest of * elements and return. Other return values are not used now. * * * Returns * The number of traversed map elements for success, **-EINVAL** for * invalid **flags**. */ static long (*bpf_for_each_map_elem)(void *map, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 164; /* * bpf_snprintf * * Outputs a string into the **str** buffer of size **str_size** * based on a format string stored in a read-only map pointed by * **fmt**. * * Each format specifier in **fmt** corresponds to one u64 element * in the **data** array. For strings and pointers where pointees * are accessed, only the pointer values are stored in the *data* * array. The *data_len* is the size of *data* in bytes - must be * a multiple of 8. * * Formats **%s** and **%p{i,I}{4,6}** require to read kernel * memory. Reading kernel memory may fail due to either invalid * address or valid address but requiring a major memory fault. If * reading kernel memory fails, the string for **%s** will be an * empty string, and the ip address for **%p{i,I}{4,6}** will be 0. * Not returning error to bpf program is consistent with what * **bpf_trace_printk**\ () does for now. * * * Returns * The strictly positive length of the formatted string, including * the trailing zero character. If the return value is greater than * **str_size**, **str** contains a truncated string, guaranteed to * be zero-terminated except when **str_size** is 0. * * Or **-EBUSY** if the per-CPU memory copy buffer is busy. */ static long (*bpf_snprintf)(char *str, __u32 str_size, const char *fmt, __u64 *data, __u32 data_len) = (void *) 165; /* * bpf_sys_bpf * * Execute bpf syscall with given arguments. * * Returns * A syscall result. */ static long (*bpf_sys_bpf)(__u32 cmd, void *attr, __u32 attr_size) = (void *) 166; /* * bpf_btf_find_by_name_kind * * Find BTF type with given name and kind in vmlinux BTF or in module's BTFs. * * Returns * Returns btf_id and btf_obj_fd in lower and upper 32 bits. */ static long (*bpf_btf_find_by_name_kind)(char *name, int name_sz, __u32 kind, int flags) = (void *) 167; /* * bpf_sys_close * * Execute close syscall for given FD. * * Returns * A syscall result. */ static long (*bpf_sys_close)(__u32 fd) = (void *) 168; /* * bpf_timer_init * * Initialize the timer. * First 4 bits of *flags* specify clockid. * Only CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_BOOTTIME are allowed. * All other bits of *flags* are reserved. * The verifier will reject the program if *timer* is not from * the same *map*. * * Returns * 0 on success. * **-EBUSY** if *timer* is already initialized. * **-EINVAL** if invalid *flags* are passed. * **-EPERM** if *timer* is in a map that doesn't have any user references. * The user space should either hold a file descriptor to a map with timers * or pin such map in bpffs. When map is unpinned or file descriptor is * closed all timers in the map will be cancelled and freed. */ static long (*bpf_timer_init)(struct bpf_timer *timer, void *map, __u64 flags) = (void *) 169; /* * bpf_timer_set_callback * * Configure the timer to call *callback_fn* static function. * * Returns * 0 on success. * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. * **-EPERM** if *timer* is in a map that doesn't have any user references. * The user space should either hold a file descriptor to a map with timers * or pin such map in bpffs. When map is unpinned or file descriptor is * closed all timers in the map will be cancelled and freed. */ static long (*bpf_timer_set_callback)(struct bpf_timer *timer, void *callback_fn) = (void *) 170; /* * bpf_timer_start * * Set timer expiration N nanoseconds from the current time. The * configured callback will be invoked in soft irq context on some cpu * and will not repeat unless another bpf_timer_start() is made. * In such case the next invocation can migrate to a different cpu. * Since struct bpf_timer is a field inside map element the map * owns the timer. The bpf_timer_set_callback() will increment refcnt * of BPF program to make sure that callback_fn code stays valid. * When user space reference to a map reaches zero all timers * in a map are cancelled and corresponding program's refcnts are * decremented. This is done to make sure that Ctrl-C of a user * process doesn't leave any timers running. If map is pinned in * bpffs the callback_fn can re-arm itself indefinitely. * bpf_map_update/delete_elem() helpers and user space sys_bpf commands * cancel and free the timer in the given map element. * The map can contain timers that invoke callback_fn-s from different * programs. The same callback_fn can serve different timers from * different maps if key/value layout matches across maps. * Every bpf_timer_set_callback() can have different callback_fn. * * *flags* can be one of: * * **BPF_F_TIMER_ABS** * Start the timer in absolute expire value instead of the * default relative one. * * * Returns * 0 on success. * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier * or invalid *flags* are passed. */ static long (*bpf_timer_start)(struct bpf_timer *timer, __u64 nsecs, __u64 flags) = (void *) 171; /* * bpf_timer_cancel * * Cancel the timer and wait for callback_fn to finish if it was running. * * Returns * 0 if the timer was not active. * 1 if the timer was active. * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. * **-EDEADLK** if callback_fn tried to call bpf_timer_cancel() on its * own timer which would have led to a deadlock otherwise. */ static long (*bpf_timer_cancel)(struct bpf_timer *timer) = (void *) 172; /* * bpf_get_func_ip * * Get address of the traced function (for tracing and kprobe programs). * * Returns * Address of the traced function. * 0 for kprobes placed within the function (not at the entry). */ static __u64 (*bpf_get_func_ip)(void *ctx) = (void *) 173; /* * bpf_get_attach_cookie * * Get bpf_cookie value provided (optionally) during the program * attachment. It might be different for each individual * attachment, even if BPF program itself is the same. * Expects BPF program context *ctx* as a first argument. * * Supported for the following program types: * - kprobe/uprobe; * - tracepoint; * - perf_event. * * Returns * Value specified by user at BPF link creation/attachment time * or 0, if it was not specified. */ static __u64 (*bpf_get_attach_cookie)(void *ctx) = (void *) 174; /* * bpf_task_pt_regs * * Get the struct pt_regs associated with **task**. * * Returns * A pointer to struct pt_regs. */ static long (*bpf_task_pt_regs)(struct task_struct *task) = (void *) 175; /* * bpf_get_branch_snapshot * * Get branch trace from hardware engines like Intel LBR. The * hardware engine is stopped shortly after the helper is * called. Therefore, the user need to filter branch entries * based on the actual use case. To capture branch trace * before the trigger point of the BPF program, the helper * should be called at the beginning of the BPF program. * * The data is stored as struct perf_branch_entry into output * buffer *entries*. *size* is the size of *entries* in bytes. * *flags* is reserved for now and must be zero. * * * Returns * On success, number of bytes written to *buf*. On error, a * negative value. * * **-EINVAL** if *flags* is not zero. * * **-ENOENT** if architecture does not support branch records. */ static long (*bpf_get_branch_snapshot)(void *entries, __u32 size, __u64 flags) = (void *) 176; /* * bpf_trace_vprintk * * Behaves like **bpf_trace_printk**\ () helper, but takes an array of u64 * to format and can handle more format args as a result. * * Arguments are to be used as in **bpf_seq_printf**\ () helper. * * Returns * The number of bytes written to the buffer, or a negative error * in case of failure. */ static long (*bpf_trace_vprintk)(const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 177; /* * bpf_skc_to_unix_sock * * Dynamically cast a *sk* pointer to a *unix_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct unix_sock *(*bpf_skc_to_unix_sock)(void *sk) = (void *) 178; /* * bpf_kallsyms_lookup_name * * Get the address of a kernel symbol, returned in *res*. *res* is * set to 0 if the symbol is not found. * * Returns * On success, zero. On error, a negative value. * * **-EINVAL** if *flags* is not zero. * * **-EINVAL** if string *name* is not the same size as *name_sz*. * * **-ENOENT** if symbol is not found. * * **-EPERM** if caller does not have permission to obtain kernel address. */ static long (*bpf_kallsyms_lookup_name)(const char *name, int name_sz, int flags, __u64 *res) = (void *) 179; /* * bpf_find_vma * * Find vma of *task* that contains *addr*, call *callback_fn* * function with *task*, *vma*, and *callback_ctx*. * The *callback_fn* should be a static function and * the *callback_ctx* should be a pointer to the stack. * The *flags* is used to control certain aspects of the helper. * Currently, the *flags* must be 0. * * The expected callback signature is * * long (\*callback_fn)(struct task_struct \*task, struct vm_area_struct \*vma, void \*callback_ctx); * * * Returns * 0 on success. * **-ENOENT** if *task->mm* is NULL, or no vma contains *addr*. * **-EBUSY** if failed to try lock mmap_lock. * **-EINVAL** for invalid **flags**. */ static long (*bpf_find_vma)(struct task_struct *task, __u64 addr, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 180; /* * bpf_loop * * For **nr_loops**, call **callback_fn** function * with **callback_ctx** as the context parameter. * The **callback_fn** should be a static function and * the **callback_ctx** should be a pointer to the stack. * The **flags** is used to control certain aspects of the helper. * Currently, the **flags** must be 0. Currently, nr_loops is * limited to 1 << 23 (~8 million) loops. * * long (\*callback_fn)(u32 index, void \*ctx); * * where **index** is the current index in the loop. The index * is zero-indexed. * * If **callback_fn** returns 0, the helper will continue to the next * loop. If return value is 1, the helper will skip the rest of * the loops and return. Other return values are not used now, * and will be rejected by the verifier. * * * Returns * The number of loops performed, **-EINVAL** for invalid **flags**, * **-E2BIG** if **nr_loops** exceeds the maximum number of loops. */ static long (*bpf_loop)(__u32 nr_loops, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 181; /* * bpf_strncmp * * Do strncmp() between **s1** and **s2**. **s1** doesn't need * to be null-terminated and **s1_sz** is the maximum storage * size of **s1**. **s2** must be a read-only string. * * Returns * An integer less than, equal to, or greater than zero * if the first **s1_sz** bytes of **s1** is found to be * less than, to match, or be greater than **s2**. */ static long (*bpf_strncmp)(const char *s1, __u32 s1_sz, const char *s2) = (void *) 182; /* * bpf_get_func_arg * * Get **n**-th argument register (zero based) of the traced function (for tracing programs) * returned in **value**. * * * Returns * 0 on success. * **-EINVAL** if n >= argument register count of traced function. */ static long (*bpf_get_func_arg)(void *ctx, __u32 n, __u64 *value) = (void *) 183; /* * bpf_get_func_ret * * Get return value of the traced function (for tracing programs) * in **value**. * * * Returns * 0 on success. * **-EOPNOTSUPP** for tracing programs other than BPF_TRACE_FEXIT or BPF_MODIFY_RETURN. */ static long (*bpf_get_func_ret)(void *ctx, __u64 *value) = (void *) 184; /* * bpf_get_func_arg_cnt * * Get number of registers of the traced function (for tracing programs) where * function arguments are stored in these registers. * * * Returns * The number of argument registers of the traced function. */ static long (*bpf_get_func_arg_cnt)(void *ctx) = (void *) 185; /* * bpf_get_retval * * Get the BPF program's return value that will be returned to the upper layers. * * This helper is currently supported by cgroup programs and only by the hooks * where BPF program's return value is returned to the userspace via errno. * * Returns * The BPF program's return value. */ static int (*bpf_get_retval)(void) = (void *) 186; /* * bpf_set_retval * * Set the BPF program's return value that will be returned to the upper layers. * * This helper is currently supported by cgroup programs and only by the hooks * where BPF program's return value is returned to the userspace via errno. * * Note that there is the following corner case where the program exports an error * via bpf_set_retval but signals success via 'return 1': * * bpf_set_retval(-EPERM); * return 1; * * In this case, the BPF program's return value will use helper's -EPERM. This * still holds true for cgroup/bind{4,6} which supports extra 'return 3' success case. * * * Returns * 0 on success, or a negative error in case of failure. */ static int (*bpf_set_retval)(int retval) = (void *) 187; /* * bpf_xdp_get_buff_len * * Get the total size of a given xdp buff (linear and paged area) * * Returns * The total size of a given xdp buffer. */ static __u64 (*bpf_xdp_get_buff_len)(struct xdp_md *xdp_md) = (void *) 188; /* * bpf_xdp_load_bytes * * This helper is provided as an easy way to load data from a * xdp buffer. It can be used to load *len* bytes from *offset* from * the frame associated to *xdp_md*, into the buffer pointed by * *buf*. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_xdp_load_bytes)(struct xdp_md *xdp_md, __u32 offset, void *buf, __u32 len) = (void *) 189; /* * bpf_xdp_store_bytes * * Store *len* bytes from buffer *buf* into the frame * associated to *xdp_md*, at *offset*. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_xdp_store_bytes)(struct xdp_md *xdp_md, __u32 offset, void *buf, __u32 len) = (void *) 190; /* * bpf_copy_from_user_task * * Read *size* bytes from user space address *user_ptr* in *tsk*'s * address space, and stores the data in *dst*. *flags* is not * used yet and is provided for future extensibility. This helper * can only be used by sleepable programs. * * Returns * 0 on success, or a negative error in case of failure. On error * *dst* buffer is zeroed out. */ static long (*bpf_copy_from_user_task)(void *dst, __u32 size, const void *user_ptr, struct task_struct *tsk, __u64 flags) = (void *) 191; /* * bpf_skb_set_tstamp * * Change the __sk_buff->tstamp_type to *tstamp_type* * and set *tstamp* to the __sk_buff->tstamp together. * * If there is no need to change the __sk_buff->tstamp_type, * the tstamp value can be directly written to __sk_buff->tstamp * instead. * * BPF_SKB_TSTAMP_DELIVERY_MONO is the only tstamp that * will be kept during bpf_redirect_*(). A non zero * *tstamp* must be used with the BPF_SKB_TSTAMP_DELIVERY_MONO * *tstamp_type*. * * A BPF_SKB_TSTAMP_UNSPEC *tstamp_type* can only be used * with a zero *tstamp*. * * Only IPv4 and IPv6 skb->protocol are supported. * * This function is most useful when it needs to set a * mono delivery time to __sk_buff->tstamp and then * bpf_redirect_*() to the egress of an iface. For example, * changing the (rcv) timestamp in __sk_buff->tstamp at * ingress to a mono delivery time and then bpf_redirect_*() * to sch_fq@phy-dev. * * Returns * 0 on success. * **-EINVAL** for invalid input * **-EOPNOTSUPP** for unsupported protocol */ static long (*bpf_skb_set_tstamp)(struct __sk_buff *skb, __u64 tstamp, __u32 tstamp_type) = (void *) 192; /* * bpf_ima_file_hash * * Returns a calculated IMA hash of the *file*. * If the hash is larger than *size*, then only *size* * bytes will be copied to *dst* * * Returns * The **hash_algo** is returned on success, * **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if * invalid arguments are passed. */ static long (*bpf_ima_file_hash)(struct file *file, void *dst, __u32 size) = (void *) 193; /* * bpf_kptr_xchg * * Exchange kptr at pointer *map_value* with *ptr*, and return the * old value. *ptr* can be NULL, otherwise it must be a referenced * pointer which will be released when this helper is called. * * Returns * The old value of kptr (which can be NULL). The returned pointer * if not NULL, is a reference which must be released using its * corresponding release function, or moved into a BPF map before * program exit. */ static void *(*bpf_kptr_xchg)(void *map_value, void *ptr) = (void *) 194; /* * bpf_map_lookup_percpu_elem * * Perform a lookup in *percpu map* for an entry associated to * *key* on *cpu*. * * Returns * Map value associated to *key* on *cpu*, or **NULL** if no entry * was found or *cpu* is invalid. */ static void *(*bpf_map_lookup_percpu_elem)(void *map, const void *key, __u32 cpu) = (void *) 195; /* * bpf_skc_to_mptcp_sock * * Dynamically cast a *sk* pointer to a *mptcp_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct mptcp_sock *(*bpf_skc_to_mptcp_sock)(void *sk) = (void *) 196; /* * bpf_dynptr_from_mem * * Get a dynptr to local memory *data*. * * *data* must be a ptr to a map value. * The maximum *size* supported is DYNPTR_MAX_SIZE. * *flags* is currently unused. * * Returns * 0 on success, -E2BIG if the size exceeds DYNPTR_MAX_SIZE, * -EINVAL if flags is not 0. */ static long (*bpf_dynptr_from_mem)(void *data, __u32 size, __u64 flags, struct bpf_dynptr *ptr) = (void *) 197; /* * bpf_ringbuf_reserve_dynptr * * Reserve *size* bytes of payload in a ring buffer *ringbuf* * through the dynptr interface. *flags* must be 0. * * Please note that a corresponding bpf_ringbuf_submit_dynptr or * bpf_ringbuf_discard_dynptr must be called on *ptr*, even if the * reservation fails. This is enforced by the verifier. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_ringbuf_reserve_dynptr)(void *ringbuf, __u32 size, __u64 flags, struct bpf_dynptr *ptr) = (void *) 198; /* * bpf_ringbuf_submit_dynptr * * Submit reserved ring buffer sample, pointed to by *data*, * through the dynptr interface. This is a no-op if the dynptr is * invalid/null. * * For more information on *flags*, please see * 'bpf_ringbuf_submit'. * * Returns * Nothing. Always succeeds. */ static void (*bpf_ringbuf_submit_dynptr)(struct bpf_dynptr *ptr, __u64 flags) = (void *) 199; /* * bpf_ringbuf_discard_dynptr * * Discard reserved ring buffer sample through the dynptr * interface. This is a no-op if the dynptr is invalid/null. * * For more information on *flags*, please see * 'bpf_ringbuf_discard'. * * Returns * Nothing. Always succeeds. */ static void (*bpf_ringbuf_discard_dynptr)(struct bpf_dynptr *ptr, __u64 flags) = (void *) 200; /* * bpf_dynptr_read * * Read *len* bytes from *src* into *dst*, starting from *offset* * into *src*. * *flags* is currently unused. * * Returns * 0 on success, -E2BIG if *offset* + *len* exceeds the length * of *src*'s data, -EINVAL if *src* is an invalid dynptr or if * *flags* is not 0. */ static long (*bpf_dynptr_read)(void *dst, __u32 len, const struct bpf_dynptr *src, __u32 offset, __u64 flags) = (void *) 201; /* * bpf_dynptr_write * * Write *len* bytes from *src* into *dst*, starting from *offset* * into *dst*. * * *flags* must be 0 except for skb-type dynptrs. * * For skb-type dynptrs: * * All data slices of the dynptr are automatically * invalidated after **bpf_dynptr_write**\ (). This is * because writing may pull the skb and change the * underlying packet buffer. * * * For *flags*, please see the flags accepted by * **bpf_skb_store_bytes**\ (). * * Returns * 0 on success, -E2BIG if *offset* + *len* exceeds the length * of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst* * is a read-only dynptr or if *flags* is not correct. For skb-type dynptrs, * other errors correspond to errors returned by **bpf_skb_store_bytes**\ (). */ static long (*bpf_dynptr_write)(const struct bpf_dynptr *dst, __u32 offset, void *src, __u32 len, __u64 flags) = (void *) 202; /* * bpf_dynptr_data * * Get a pointer to the underlying dynptr data. * * *len* must be a statically known value. The returned data slice * is invalidated whenever the dynptr is invalidated. * * skb and xdp type dynptrs may not use bpf_dynptr_data. They should * instead use bpf_dynptr_slice and bpf_dynptr_slice_rdwr. * * Returns * Pointer to the underlying dynptr data, NULL if the dynptr is * read-only, if the dynptr is invalid, or if the offset and length * is out of bounds. */ static void *(*bpf_dynptr_data)(const struct bpf_dynptr *ptr, __u32 offset, __u32 len) = (void *) 203; /* * bpf_tcp_raw_gen_syncookie_ipv4 * * Try to issue a SYN cookie for the packet with corresponding * IPv4/TCP headers, *iph* and *th*, without depending on a * listening socket. * * *iph* points to the IPv4 header. * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header (at least * **sizeof**\ (**struct tcphdr**)). * * Returns * On success, lower 32 bits hold the generated SYN cookie in * followed by 16 bits which hold the MSS value for that cookie, * and the top 16 bits are unused. * * On failure, the returned value is one of the following: * * **-EINVAL** if *th_len* is invalid. */ static __s64 (*bpf_tcp_raw_gen_syncookie_ipv4)(struct iphdr *iph, struct tcphdr *th, __u32 th_len) = (void *) 204; /* * bpf_tcp_raw_gen_syncookie_ipv6 * * Try to issue a SYN cookie for the packet with corresponding * IPv6/TCP headers, *iph* and *th*, without depending on a * listening socket. * * *iph* points to the IPv6 header. * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header (at least * **sizeof**\ (**struct tcphdr**)). * * Returns * On success, lower 32 bits hold the generated SYN cookie in * followed by 16 bits which hold the MSS value for that cookie, * and the top 16 bits are unused. * * On failure, the returned value is one of the following: * * **-EINVAL** if *th_len* is invalid. * * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. */ static __s64 (*bpf_tcp_raw_gen_syncookie_ipv6)(struct ipv6hdr *iph, struct tcphdr *th, __u32 th_len) = (void *) 205; /* * bpf_tcp_raw_check_syncookie_ipv4 * * Check whether *iph* and *th* contain a valid SYN cookie ACK * without depending on a listening socket. * * *iph* points to the IPv4 header. * * *th* points to the TCP header. * * Returns * 0 if *iph* and *th* are a valid SYN cookie ACK. * * On failure, the returned value is one of the following: * * **-EACCES** if the SYN cookie is not valid. */ static long (*bpf_tcp_raw_check_syncookie_ipv4)(struct iphdr *iph, struct tcphdr *th) = (void *) 206; /* * bpf_tcp_raw_check_syncookie_ipv6 * * Check whether *iph* and *th* contain a valid SYN cookie ACK * without depending on a listening socket. * * *iph* points to the IPv6 header. * * *th* points to the TCP header. * * Returns * 0 if *iph* and *th* are a valid SYN cookie ACK. * * On failure, the returned value is one of the following: * * **-EACCES** if the SYN cookie is not valid. * * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. */ static long (*bpf_tcp_raw_check_syncookie_ipv6)(struct ipv6hdr *iph, struct tcphdr *th) = (void *) 207; /* * bpf_ktime_get_tai_ns * * A nonsettable system-wide clock derived from wall-clock time but * ignoring leap seconds. This clock does not experience * discontinuities and backwards jumps caused by NTP inserting leap * seconds as CLOCK_REALTIME does. * * See: **clock_gettime**\ (**CLOCK_TAI**) * * Returns * Current *ktime*. */ static __u64 (*bpf_ktime_get_tai_ns)(void) = (void *) 208; /* * bpf_user_ringbuf_drain * * Drain samples from the specified user ring buffer, and invoke * the provided callback for each such sample: * * long (\*callback_fn)(const struct bpf_dynptr \*dynptr, void \*ctx); * * If **callback_fn** returns 0, the helper will continue to try * and drain the next sample, up to a maximum of * BPF_MAX_USER_RINGBUF_SAMPLES samples. If the return value is 1, * the helper will skip the rest of the samples and return. Other * return values are not used now, and will be rejected by the * verifier. * * Returns * The number of drained samples if no error was encountered while * draining samples, or 0 if no samples were present in the ring * buffer. If a user-space producer was epoll-waiting on this map, * and at least one sample was drained, they will receive an event * notification notifying them of available space in the ring * buffer. If the BPF_RB_NO_WAKEUP flag is passed to this * function, no wakeup notification will be sent. If the * BPF_RB_FORCE_WAKEUP flag is passed, a wakeup notification will * be sent even if no sample was drained. * * On failure, the returned value is one of the following: * * **-EBUSY** if the ring buffer is contended, and another calling * context was concurrently draining the ring buffer. * * **-EINVAL** if user-space is not properly tracking the ring * buffer due to the producer position not being aligned to 8 * bytes, a sample not being aligned to 8 bytes, or the producer * position not matching the advertised length of a sample. * * **-E2BIG** if user-space has tried to publish a sample which is * larger than the size of the ring buffer, or which cannot fit * within a struct bpf_dynptr. */ static long (*bpf_user_ringbuf_drain)(void *map, void *callback_fn, void *ctx, __u64 flags) = (void *) 209; /* * bpf_cgrp_storage_get * * Get a bpf_local_storage from the *cgroup*. * * Logically, it could be thought of as getting the value from * a *map* with *cgroup* as the **key**. From this * perspective, the usage is not much different from * **bpf_map_lookup_elem**\ (*map*, **&**\ *cgroup*) except this * helper enforces the key must be a cgroup struct and the map must also * be a **BPF_MAP_TYPE_CGRP_STORAGE**. * * In reality, the local-storage value is embedded directly inside of the * *cgroup* object itself, rather than being located in the * **BPF_MAP_TYPE_CGRP_STORAGE** map. When the local-storage value is * queried for some *map* on a *cgroup* object, the kernel will perform an * O(n) iteration over all of the live local-storage values for that * *cgroup* object until the local-storage value for the *map* is found. * * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be * used such that a new bpf_local_storage will be * created if one does not exist. *value* can be used * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify * the initial value of a bpf_local_storage. If *value* is * **NULL**, the new bpf_local_storage will be zero initialized. * * Returns * A bpf_local_storage pointer is returned on success. * * **NULL** if not found or there was an error in adding * a new bpf_local_storage. */ static void *(*bpf_cgrp_storage_get)(void *map, struct cgroup *cgroup, void *value, __u64 flags) = (void *) 210; /* * bpf_cgrp_storage_delete * * Delete a bpf_local_storage from a *cgroup*. * * Returns * 0 on success. * * **-ENOENT** if the bpf_local_storage cannot be found. */ static long (*bpf_cgrp_storage_delete)(void *map, struct cgroup *cgroup) = (void *) 211; ================================================ FILE: service/firewall/interception/ebpf/programs/bpf/bpf_helpers.h ================================================ /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ #ifndef __BPF_HELPERS__ #define __BPF_HELPERS__ /* * Note that bpf programs need to include either * vmlinux.h (auto-generated from BTF) or linux/types.h * in advance since bpf_helper_defs.h uses such types * as __u64. */ #include "bpf_helper_defs.h" #define __uint(name, val) int (*name)[val] #define __type(name, val) typeof(val) *name #define __array(name, val) typeof(val) *name[] /* * Helper macro to place programs, maps, license in * different sections in elf_bpf file. Section names * are interpreted by libbpf depending on the context (BPF programs, BPF maps, * extern variables, etc). * To allow use of SEC() with externs (e.g., for extern .maps declarations), * make sure __attribute__((unused)) doesn't trigger compilation warning. */ #if __GNUC__ && !__clang__ /* * Pragma macros are broken on GCC * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55578 * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90400 */ #define SEC(name) __attribute__((section(name), used)) #else #define SEC(name) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") \ __attribute__((section(name), used)) \ _Pragma("GCC diagnostic pop") \ #endif /* Avoid 'linux/stddef.h' definition of '__always_inline'. */ #undef __always_inline #define __always_inline inline __attribute__((always_inline)) #ifndef __noinline #define __noinline __attribute__((noinline)) #endif #ifndef __weak #define __weak __attribute__((weak)) #endif /* * Use __hidden attribute to mark a non-static BPF subprogram effectively * static for BPF verifier's verification algorithm purposes, allowing more * extensive and permissive BPF verification process, taking into account * subprogram's caller context. */ #define __hidden __attribute__((visibility("hidden"))) /* When utilizing vmlinux.h with BPF CO-RE, user BPF programs can't include * any system-level headers (such as stddef.h, linux/version.h, etc), and * commonly-used macros like NULL and KERNEL_VERSION aren't available through * vmlinux.h. This just adds unnecessary hurdles and forces users to re-define * them on their own. So as a convenience, provide such definitions here. */ #ifndef NULL #define NULL ((void *)0) #endif #ifndef KERNEL_VERSION #define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c))) #endif /* * Helper macros to manipulate data structures */ #ifndef offsetof #define offsetof(TYPE, MEMBER) ((unsigned long)&((TYPE *)0)->MEMBER) #endif #ifndef container_of #define container_of(ptr, type, member) \ ({ \ void *__mptr = (void *)(ptr); \ ((type *)(__mptr - offsetof(type, member))); \ }) #endif /* * Compiler (optimization) barrier. */ #ifndef barrier #define barrier() asm volatile("" ::: "memory") #endif /* Variable-specific compiler (optimization) barrier. It's a no-op which makes * compiler believe that there is some black box modification of a given * variable and thus prevents compiler from making extra assumption about its * value and potential simplifications and optimizations on this variable. * * E.g., compiler might often delay or even omit 32-bit to 64-bit casting of * a variable, making some code patterns unverifiable. Putting barrier_var() * in place will ensure that cast is performed before the barrier_var() * invocation, because compiler has to pessimistically assume that embedded * asm section might perform some extra operations on that variable. * * This is a variable-specific variant of more global barrier(). */ #ifndef barrier_var #define barrier_var(var) asm volatile("" : "+r"(var)) #endif /* * Helper macro to throw a compilation error if __bpf_unreachable() gets * built into the resulting code. This works given BPF back end does not * implement __builtin_trap(). This is useful to assert that certain paths * of the program code are never used and hence eliminated by the compiler. * * For example, consider a switch statement that covers known cases used by * the program. __bpf_unreachable() can then reside in the default case. If * the program gets extended such that a case is not covered in the switch * statement, then it will throw a build error due to the default case not * being compiled out. */ #ifndef __bpf_unreachable # define __bpf_unreachable() __builtin_trap() #endif /* * Helper function to perform a tail call with a constant/immediate map slot. */ #if __clang_major__ >= 8 && defined(__bpf__) static __always_inline void bpf_tail_call_static(void *ctx, const void *map, const __u32 slot) { if (!__builtin_constant_p(slot)) __bpf_unreachable(); /* * Provide a hard guarantee that LLVM won't optimize setting r2 (map * pointer) and r3 (constant map index) from _different paths_ ending * up at the _same_ call insn as otherwise we won't be able to use the * jmpq/nopl retpoline-free patching by the x86-64 JIT in the kernel * given they mismatch. See also d2e4c1e6c294 ("bpf: Constant map key * tracking for prog array pokes") for details on verifier tracking. * * Note on clobber list: we need to stay in-line with BPF calling * convention, so even if we don't end up using r0, r4, r5, we need * to mark them as clobber so that LLVM doesn't end up using them * before / after the call. */ asm volatile("r1 = %[ctx]\n\t" "r2 = %[map]\n\t" "r3 = %[slot]\n\t" "call 12" :: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot) : "r0", "r1", "r2", "r3", "r4", "r5"); } #endif enum libbpf_pin_type { LIBBPF_PIN_NONE, /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */ LIBBPF_PIN_BY_NAME, }; enum libbpf_tristate { TRI_NO = 0, TRI_YES = 1, TRI_MODULE = 2, }; #define __kconfig __attribute__((section(".kconfig"))) #define __ksym __attribute__((section(".ksyms"))) #define __kptr_untrusted __attribute__((btf_type_tag("kptr_untrusted"))) #define __kptr __attribute__((btf_type_tag("kptr"))) #define bpf_ksym_exists(sym) ({ \ _Static_assert(!__builtin_constant_p(!!sym), #sym " should be marked as __weak"); \ !!sym; \ }) #ifndef ___bpf_concat #define ___bpf_concat(a, b) a ## b #endif #ifndef ___bpf_apply #define ___bpf_apply(fn, n) ___bpf_concat(fn, n) #endif #ifndef ___bpf_nth #define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N #endif #ifndef ___bpf_narg #define ___bpf_narg(...) \ ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #endif #define ___bpf_fill0(arr, p, x) do {} while (0) #define ___bpf_fill1(arr, p, x) arr[p] = x #define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args) #define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args) #define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args) #define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args) #define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args) #define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args) #define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args) #define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args) #define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args) #define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args) #define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args) #define ___bpf_fill(arr, args...) \ ___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args) /* * BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values * in a structure. */ #define BPF_SEQ_PRINTF(seq, fmt, args...) \ ({ \ static const char ___fmt[] = fmt; \ unsigned long long ___param[___bpf_narg(args)]; \ \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ ___bpf_fill(___param, args); \ _Pragma("GCC diagnostic pop") \ \ bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \ ___param, sizeof(___param)); \ }) /* * BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of * an array of u64. */ #define BPF_SNPRINTF(out, out_size, fmt, args...) \ ({ \ static const char ___fmt[] = fmt; \ unsigned long long ___param[___bpf_narg(args)]; \ \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ ___bpf_fill(___param, args); \ _Pragma("GCC diagnostic pop") \ \ bpf_snprintf(out, out_size, ___fmt, \ ___param, sizeof(___param)); \ }) #ifdef BPF_NO_GLOBAL_DATA #define BPF_PRINTK_FMT_MOD #else #define BPF_PRINTK_FMT_MOD static const #endif #define __bpf_printk(fmt, ...) \ ({ \ BPF_PRINTK_FMT_MOD char ____fmt[] = fmt; \ bpf_trace_printk(____fmt, sizeof(____fmt), \ ##__VA_ARGS__); \ }) /* * __bpf_vprintk wraps the bpf_trace_vprintk helper with variadic arguments * instead of an array of u64. */ #define __bpf_vprintk(fmt, args...) \ ({ \ static const char ___fmt[] = fmt; \ unsigned long long ___param[___bpf_narg(args)]; \ \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ ___bpf_fill(___param, args); \ _Pragma("GCC diagnostic pop") \ \ bpf_trace_vprintk(___fmt, sizeof(___fmt), \ ___param, sizeof(___param)); \ }) /* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args * Otherwise use __bpf_vprintk */ #define ___bpf_pick_printk(...) \ ___bpf_nth(_, ##__VA_ARGS__, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \ __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \ __bpf_vprintk, __bpf_vprintk, __bpf_printk /*3*/, __bpf_printk /*2*/,\ __bpf_printk /*1*/, __bpf_printk /*0*/) /* Helper macro to print out debug messages */ #define bpf_printk(fmt, args...) ___bpf_pick_printk(args)(fmt, ##args) struct bpf_iter_num; extern int bpf_iter_num_new(struct bpf_iter_num *it, int start, int end) __weak __ksym; extern int *bpf_iter_num_next(struct bpf_iter_num *it) __weak __ksym; extern void bpf_iter_num_destroy(struct bpf_iter_num *it) __weak __ksym; #ifndef bpf_for_each /* bpf_for_each(iter_type, cur_elem, args...) provides generic construct for * using BPF open-coded iterators without having to write mundane explicit * low-level loop logic. Instead, it provides for()-like generic construct * that can be used pretty naturally. E.g., for some hypothetical cgroup * iterator, you'd write: * * struct cgroup *cg, *parent_cg = <...>; * * bpf_for_each(cgroup, cg, parent_cg, CG_ITER_CHILDREN) { * bpf_printk("Child cgroup id = %d", cg->cgroup_id); * if (cg->cgroup_id == 123) * break; * } * * I.e., it looks almost like high-level for each loop in other languages, * supports continue/break, and is verifiable by BPF verifier. * * For iterating integers, the difference betwen bpf_for_each(num, i, N, M) * and bpf_for(i, N, M) is in that bpf_for() provides additional proof to * verifier that i is in [N, M) range, and in bpf_for_each() case i is `int * *`, not just `int`. So for integers bpf_for() is more convenient. * * Note: this macro relies on C99 feature of allowing to declare variables * inside for() loop, bound to for() loop lifetime. It also utilizes GCC * extension: __attribute__((cleanup())), supported by both GCC and * Clang. */ #define bpf_for_each(type, cur, args...) for ( \ /* initialize and define destructor */ \ struct bpf_iter_##type ___it __attribute__((aligned(8), /* enforce, just in case */, \ cleanup(bpf_iter_##type##_destroy))), \ /* ___p pointer is just to call bpf_iter_##type##_new() *once* to init ___it */ \ *___p __attribute__((unused)) = ( \ bpf_iter_##type##_new(&___it, ##args), \ /* this is a workaround for Clang bug: it currently doesn't emit BTF */ \ /* for bpf_iter_##type##_destroy() when used from cleanup() attribute */ \ (void)bpf_iter_##type##_destroy, (void *)0); \ /* iteration and termination check */ \ (((cur) = bpf_iter_##type##_next(&___it))); \ ) #endif /* bpf_for_each */ #ifndef bpf_for /* bpf_for(i, start, end) implements a for()-like looping construct that sets * provided integer variable *i* to values starting from *start* through, * but not including, *end*. It also proves to BPF verifier that *i* belongs * to range [start, end), so this can be used for accessing arrays without * extra checks. * * Note: *start* and *end* are assumed to be expressions with no side effects * and whose values do not change throughout bpf_for() loop execution. They do * not have to be statically known or constant, though. * * Note: similarly to bpf_for_each(), it relies on C99 feature of declaring for() * loop bound variables and cleanup attribute, supported by GCC and Clang. */ #define bpf_for(i, start, end) for ( \ /* initialize and define destructor */ \ struct bpf_iter_num ___it __attribute__((aligned(8), /* enforce, just in case */ \ cleanup(bpf_iter_num_destroy))), \ /* ___p pointer is necessary to call bpf_iter_num_new() *once* to init ___it */ \ *___p __attribute__((unused)) = ( \ bpf_iter_num_new(&___it, (start), (end)), \ /* this is a workaround for Clang bug: it currently doesn't emit BTF */ \ /* for bpf_iter_num_destroy() when used from cleanup() attribute */ \ (void)bpf_iter_num_destroy, (void *)0); \ ({ \ /* iteration step */ \ int *___t = bpf_iter_num_next(&___it); \ /* termination and bounds check */ \ (___t && ((i) = *___t, (i) >= (start) && (i) < (end))); \ }); \ ) #endif /* bpf_for */ #ifndef bpf_repeat /* bpf_repeat(N) performs N iterations without exposing iteration number * * Note: similarly to bpf_for_each(), it relies on C99 feature of declaring for() * loop bound variables and cleanup attribute, supported by GCC and Clang. */ #define bpf_repeat(N) for ( \ /* initialize and define destructor */ \ struct bpf_iter_num ___it __attribute__((aligned(8), /* enforce, just in case */ \ cleanup(bpf_iter_num_destroy))), \ /* ___p pointer is necessary to call bpf_iter_num_new() *once* to init ___it */ \ *___p __attribute__((unused)) = ( \ bpf_iter_num_new(&___it, 0, (N)), \ /* this is a workaround for Clang bug: it currently doesn't emit BTF */ \ /* for bpf_iter_num_destroy() when used from cleanup() attribute */ \ (void)bpf_iter_num_destroy, (void *)0); \ bpf_iter_num_next(&___it); \ /* nothing here */ \ ) #endif /* bpf_repeat */ #endif ================================================ FILE: service/firewall/interception/ebpf/programs/bpf/bpf_tracing.h ================================================ /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ #ifndef __BPF_TRACING_H__ #define __BPF_TRACING_H__ // #include #include "bpf_helpers.h" /* Scan the ARCH passed in from ARCH env variable (see Makefile) */ #if defined(__TARGET_ARCH_x86) #define bpf_target_x86 #define bpf_target_defined #elif defined(__TARGET_ARCH_s390) #define bpf_target_s390 #define bpf_target_defined #elif defined(__TARGET_ARCH_arm) #define bpf_target_arm #define bpf_target_defined #elif defined(__TARGET_ARCH_arm64) #define bpf_target_arm64 #define bpf_target_defined #elif defined(__TARGET_ARCH_mips) #define bpf_target_mips #define bpf_target_defined #elif defined(__TARGET_ARCH_powerpc) #define bpf_target_powerpc #define bpf_target_defined #elif defined(__TARGET_ARCH_sparc) #define bpf_target_sparc #define bpf_target_defined #elif defined(__TARGET_ARCH_riscv) #define bpf_target_riscv #define bpf_target_defined #elif defined(__TARGET_ARCH_arc) #define bpf_target_arc #define bpf_target_defined #elif defined(__TARGET_ARCH_loongarch) #define bpf_target_loongarch #define bpf_target_defined #else /* Fall back to what the compiler says */ #if defined(__x86_64__) #define bpf_target_x86 #define bpf_target_defined #elif defined(__s390__) #define bpf_target_s390 #define bpf_target_defined #elif defined(__arm__) #define bpf_target_arm #define bpf_target_defined #elif defined(__aarch64__) #define bpf_target_arm64 #define bpf_target_defined #elif defined(__mips__) #define bpf_target_mips #define bpf_target_defined #elif defined(__powerpc__) #define bpf_target_powerpc #define bpf_target_defined #elif defined(__sparc__) #define bpf_target_sparc #define bpf_target_defined #elif defined(__riscv) && __riscv_xlen == 64 #define bpf_target_riscv #define bpf_target_defined #elif defined(__arc__) #define bpf_target_arc #define bpf_target_defined #elif defined(__loongarch__) #define bpf_target_loongarch #define bpf_target_defined #endif /* no compiler target */ #endif #ifndef __BPF_TARGET_MISSING #define __BPF_TARGET_MISSING "GCC error \"Must specify a BPF target arch via __TARGET_ARCH_xxx\"" #endif #if defined(bpf_target_x86) /* * https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI */ #if defined(__KERNEL__) || defined(__VMLINUX_H__) #define __PT_PARM1_REG di #define __PT_PARM2_REG si #define __PT_PARM3_REG dx #define __PT_PARM4_REG cx #define __PT_PARM5_REG r8 #define __PT_PARM6_REG r9 /* * Syscall uses r10 for PARM4. See arch/x86/entry/entry_64.S:entry_SYSCALL_64 * comments in Linux sources. And refer to syscall(2) manpage. */ #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG #define __PT_PARM4_SYSCALL_REG r10 #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG #define __PT_RET_REG sp #define __PT_FP_REG bp #define __PT_RC_REG ax #define __PT_SP_REG sp #define __PT_IP_REG ip #else #ifdef __i386__ /* i386 kernel is built with -mregparm=3 */ #define __PT_PARM1_REG eax #define __PT_PARM2_REG edx #define __PT_PARM3_REG ecx /* i386 syscall ABI is very different, refer to syscall(2) manpage */ #define __PT_PARM1_SYSCALL_REG ebx #define __PT_PARM2_SYSCALL_REG ecx #define __PT_PARM3_SYSCALL_REG edx #define __PT_PARM4_SYSCALL_REG esi #define __PT_PARM5_SYSCALL_REG edi #define __PT_PARM6_SYSCALL_REG ebp #define __PT_RET_REG esp #define __PT_FP_REG ebp #define __PT_RC_REG eax #define __PT_SP_REG esp #define __PT_IP_REG eip #else /* __i386__ */ #define __PT_PARM1_REG rdi #define __PT_PARM2_REG rsi #define __PT_PARM3_REG rdx #define __PT_PARM4_REG rcx #define __PT_PARM5_REG r8 #define __PT_PARM6_REG r9 #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG #define __PT_PARM4_SYSCALL_REG r10 #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG #define __PT_RET_REG rsp #define __PT_FP_REG rbp #define __PT_RC_REG rax #define __PT_SP_REG rsp #define __PT_IP_REG rip #endif /* __i386__ */ #endif /* __KERNEL__ || __VMLINUX_H__ */ #elif defined(bpf_target_s390) /* * https://github.com/IBM/s390x-abi/releases/download/v1.6/lzsabi_s390x.pdf */ struct pt_regs___s390 { unsigned long orig_gpr2; }; /* s390 provides user_pt_regs instead of struct pt_regs to userspace */ #define __PT_REGS_CAST(x) ((const user_pt_regs *)(x)) #define __PT_PARM1_REG gprs[2] #define __PT_PARM2_REG gprs[3] #define __PT_PARM3_REG gprs[4] #define __PT_PARM4_REG gprs[5] #define __PT_PARM5_REG gprs[6] #define __PT_PARM1_SYSCALL_REG orig_gpr2 #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG #define __PT_PARM6_SYSCALL_REG gprs[7] #define PT_REGS_PARM1_SYSCALL(x) PT_REGS_PARM1_CORE_SYSCALL(x) #define PT_REGS_PARM1_CORE_SYSCALL(x) \ BPF_CORE_READ((const struct pt_regs___s390 *)(x), __PT_PARM1_SYSCALL_REG) #define __PT_RET_REG gprs[14] #define __PT_FP_REG gprs[11] /* Works only with CONFIG_FRAME_POINTER */ #define __PT_RC_REG gprs[2] #define __PT_SP_REG gprs[15] #define __PT_IP_REG psw.addr #elif defined(bpf_target_arm) /* * https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#machine-registers */ #define __PT_PARM1_REG uregs[0] #define __PT_PARM2_REG uregs[1] #define __PT_PARM3_REG uregs[2] #define __PT_PARM4_REG uregs[3] #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG #define __PT_PARM5_SYSCALL_REG uregs[4] #define __PT_PARM6_SYSCALL_REG uregs[5] #define __PT_PARM7_SYSCALL_REG uregs[6] #define __PT_RET_REG uregs[14] #define __PT_FP_REG uregs[11] /* Works only with CONFIG_FRAME_POINTER */ #define __PT_RC_REG uregs[0] #define __PT_SP_REG uregs[13] #define __PT_IP_REG uregs[12] #elif defined(bpf_target_arm64) /* * https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#machine-registers */ struct pt_regs___arm64 { unsigned long orig_x0; }; /* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */ #define __PT_REGS_CAST(x) ((const struct user_pt_regs *)(x)) #define __PT_PARM1_REG regs[0] #define __PT_PARM2_REG regs[1] #define __PT_PARM3_REG regs[2] #define __PT_PARM4_REG regs[3] #define __PT_PARM5_REG regs[4] #define __PT_PARM6_REG regs[5] #define __PT_PARM7_REG regs[6] #define __PT_PARM8_REG regs[7] #define __PT_PARM1_SYSCALL_REG orig_x0 #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG #define PT_REGS_PARM1_SYSCALL(x) PT_REGS_PARM1_CORE_SYSCALL(x) #define PT_REGS_PARM1_CORE_SYSCALL(x) \ BPF_CORE_READ((const struct pt_regs___arm64 *)(x), __PT_PARM1_SYSCALL_REG) #define __PT_RET_REG regs[30] #define __PT_FP_REG regs[29] /* Works only with CONFIG_FRAME_POINTER */ #define __PT_RC_REG regs[0] #define __PT_SP_REG sp #define __PT_IP_REG pc #elif defined(bpf_target_mips) /* * N64 ABI is assumed right now. * https://en.wikipedia.org/wiki/MIPS_architecture#Calling_conventions */ #define __PT_PARM1_REG regs[4] #define __PT_PARM2_REG regs[5] #define __PT_PARM3_REG regs[6] #define __PT_PARM4_REG regs[7] #define __PT_PARM5_REG regs[8] #define __PT_PARM6_REG regs[9] #define __PT_PARM7_REG regs[10] #define __PT_PARM8_REG regs[11] #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG /* only N32/N64 */ #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG /* only N32/N64 */ #define __PT_RET_REG regs[31] #define __PT_FP_REG regs[30] /* Works only with CONFIG_FRAME_POINTER */ #define __PT_RC_REG regs[2] #define __PT_SP_REG regs[29] #define __PT_IP_REG cp0_epc #elif defined(bpf_target_powerpc) /* * http://refspecs.linux-foundation.org/elf/elfspec_ppc.pdf (page 3-14, * section "Function Calling Sequence") */ #define __PT_PARM1_REG gpr[3] #define __PT_PARM2_REG gpr[4] #define __PT_PARM3_REG gpr[5] #define __PT_PARM4_REG gpr[6] #define __PT_PARM5_REG gpr[7] #define __PT_PARM6_REG gpr[8] #define __PT_PARM7_REG gpr[9] #define __PT_PARM8_REG gpr[10] /* powerpc does not select ARCH_HAS_SYSCALL_WRAPPER. */ #define PT_REGS_SYSCALL_REGS(ctx) ctx #define __PT_PARM1_SYSCALL_REG orig_gpr3 #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG #if !defined(__arch64__) #define __PT_PARM7_SYSCALL_REG __PT_PARM7_REG /* only powerpc (not powerpc64) */ #endif #define __PT_RET_REG regs[31] #define __PT_FP_REG __unsupported__ #define __PT_RC_REG gpr[3] #define __PT_SP_REG sp #define __PT_IP_REG nip #elif defined(bpf_target_sparc) /* * https://en.wikipedia.org/wiki/Calling_convention#SPARC */ #define __PT_PARM1_REG u_regs[UREG_I0] #define __PT_PARM2_REG u_regs[UREG_I1] #define __PT_PARM3_REG u_regs[UREG_I2] #define __PT_PARM4_REG u_regs[UREG_I3] #define __PT_PARM5_REG u_regs[UREG_I4] #define __PT_PARM6_REG u_regs[UREG_I5] #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG #define __PT_RET_REG u_regs[UREG_I7] #define __PT_FP_REG __unsupported__ #define __PT_RC_REG u_regs[UREG_I0] #define __PT_SP_REG u_regs[UREG_FP] /* Should this also be a bpf_target check for the sparc case? */ #if defined(__arch64__) #define __PT_IP_REG tpc #else #define __PT_IP_REG pc #endif #elif defined(bpf_target_riscv) /* * https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#risc-v-calling-conventions */ #define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x)) #define __PT_PARM1_REG a0 #define __PT_PARM2_REG a1 #define __PT_PARM3_REG a2 #define __PT_PARM4_REG a3 #define __PT_PARM5_REG a4 #define __PT_PARM6_REG a5 #define __PT_PARM7_REG a6 #define __PT_PARM8_REG a7 /* riscv does not select ARCH_HAS_SYSCALL_WRAPPER. */ #define PT_REGS_SYSCALL_REGS(ctx) ctx #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG #define __PT_RET_REG ra #define __PT_FP_REG s0 #define __PT_RC_REG a0 #define __PT_SP_REG sp #define __PT_IP_REG pc #elif defined(bpf_target_arc) /* * Section "Function Calling Sequence" (page 24): * https://raw.githubusercontent.com/wiki/foss-for-synopsys-dwc-arc-processors/toolchain/files/ARCv2_ABI.pdf */ /* arc provides struct user_pt_regs instead of struct pt_regs to userspace */ #define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x)) #define __PT_PARM1_REG scratch.r0 #define __PT_PARM2_REG scratch.r1 #define __PT_PARM3_REG scratch.r2 #define __PT_PARM4_REG scratch.r3 #define __PT_PARM5_REG scratch.r4 #define __PT_PARM6_REG scratch.r5 #define __PT_PARM7_REG scratch.r6 #define __PT_PARM8_REG scratch.r7 /* arc does not select ARCH_HAS_SYSCALL_WRAPPER. */ #define PT_REGS_SYSCALL_REGS(ctx) ctx #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG #define __PT_RET_REG scratch.blink #define __PT_FP_REG scratch.fp #define __PT_RC_REG scratch.r0 #define __PT_SP_REG scratch.sp #define __PT_IP_REG scratch.ret #elif defined(bpf_target_loongarch) /* * https://docs.kernel.org/loongarch/introduction.html * https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html */ /* loongarch provides struct user_pt_regs instead of struct pt_regs to userspace */ #define __PT_REGS_CAST(x) ((const struct user_pt_regs *)(x)) #define __PT_PARM1_REG regs[4] #define __PT_PARM2_REG regs[5] #define __PT_PARM3_REG regs[6] #define __PT_PARM4_REG regs[7] #define __PT_PARM5_REG regs[8] #define __PT_PARM6_REG regs[9] #define __PT_PARM7_REG regs[10] #define __PT_PARM8_REG regs[11] /* loongarch does not select ARCH_HAS_SYSCALL_WRAPPER. */ #define PT_REGS_SYSCALL_REGS(ctx) ctx #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG #define __PT_RET_REG regs[1] #define __PT_FP_REG regs[22] #define __PT_RC_REG regs[4] #define __PT_SP_REG regs[3] #define __PT_IP_REG csr_era #endif #if defined(bpf_target_defined) struct pt_regs; /* allow some architectures to override `struct pt_regs` */ #ifndef __PT_REGS_CAST #define __PT_REGS_CAST(x) (x) #endif /* * Different architectures support different number of arguments passed * through registers. i386 supports just 3, some arches support up to 8. */ #ifndef __PT_PARM4_REG #define __PT_PARM4_REG __unsupported__ #endif #ifndef __PT_PARM5_REG #define __PT_PARM5_REG __unsupported__ #endif #ifndef __PT_PARM6_REG #define __PT_PARM6_REG __unsupported__ #endif #ifndef __PT_PARM7_REG #define __PT_PARM7_REG __unsupported__ #endif #ifndef __PT_PARM8_REG #define __PT_PARM8_REG __unsupported__ #endif /* * Similarly, syscall-specific conventions might differ between function call * conventions within each architecutre. All supported architectures pass * either 6 or 7 syscall arguments in registers. * * See syscall(2) manpage for succinct table with information on each arch. */ #ifndef __PT_PARM7_SYSCALL_REG #define __PT_PARM7_SYSCALL_REG __unsupported__ #endif #define PT_REGS_PARM1(x) (__PT_REGS_CAST(x)->__PT_PARM1_REG) #define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG) #define PT_REGS_PARM3(x) (__PT_REGS_CAST(x)->__PT_PARM3_REG) #define PT_REGS_PARM4(x) (__PT_REGS_CAST(x)->__PT_PARM4_REG) #define PT_REGS_PARM5(x) (__PT_REGS_CAST(x)->__PT_PARM5_REG) #define PT_REGS_PARM6(x) (__PT_REGS_CAST(x)->__PT_PARM6_REG) #define PT_REGS_PARM7(x) (__PT_REGS_CAST(x)->__PT_PARM7_REG) #define PT_REGS_PARM8(x) (__PT_REGS_CAST(x)->__PT_PARM8_REG) #define PT_REGS_RET(x) (__PT_REGS_CAST(x)->__PT_RET_REG) #define PT_REGS_FP(x) (__PT_REGS_CAST(x)->__PT_FP_REG) #define PT_REGS_RC(x) (__PT_REGS_CAST(x)->__PT_RC_REG) #define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG) #define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG) #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM1_REG) #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM2_REG) #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM3_REG) #define PT_REGS_PARM4_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM4_REG) #define PT_REGS_PARM5_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM5_REG) #define PT_REGS_PARM6_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM6_REG) #define PT_REGS_PARM7_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM7_REG) #define PT_REGS_PARM8_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM8_REG) #define PT_REGS_RET_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_RET_REG) #define PT_REGS_FP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_FP_REG) #define PT_REGS_RC_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_RC_REG) #define PT_REGS_SP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_SP_REG) #define PT_REGS_IP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_IP_REG) #if defined(bpf_target_powerpc) #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; }) #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP #elif defined(bpf_target_sparc) #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); }) #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP #else #define BPF_KPROBE_READ_RET_IP(ip, ctx) \ ({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); }) #define BPF_KRETPROBE_READ_RET_IP(ip, ctx) \ ({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)(PT_REGS_FP(ctx) + sizeof(ip))); }) #endif #ifndef PT_REGS_PARM1_SYSCALL #define PT_REGS_PARM1_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM1_SYSCALL_REG) #define PT_REGS_PARM1_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM1_SYSCALL_REG) #endif #ifndef PT_REGS_PARM2_SYSCALL #define PT_REGS_PARM2_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM2_SYSCALL_REG) #define PT_REGS_PARM2_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM2_SYSCALL_REG) #endif #ifndef PT_REGS_PARM3_SYSCALL #define PT_REGS_PARM3_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM3_SYSCALL_REG) #define PT_REGS_PARM3_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM3_SYSCALL_REG) #endif #ifndef PT_REGS_PARM4_SYSCALL #define PT_REGS_PARM4_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM4_SYSCALL_REG) #define PT_REGS_PARM4_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM4_SYSCALL_REG) #endif #ifndef PT_REGS_PARM5_SYSCALL #define PT_REGS_PARM5_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM5_SYSCALL_REG) #define PT_REGS_PARM5_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM5_SYSCALL_REG) #endif #ifndef PT_REGS_PARM6_SYSCALL #define PT_REGS_PARM6_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM6_SYSCALL_REG) #define PT_REGS_PARM6_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM6_SYSCALL_REG) #endif #ifndef PT_REGS_PARM7_SYSCALL #define PT_REGS_PARM7_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM7_SYSCALL_REG) #define PT_REGS_PARM7_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM7_SYSCALL_REG) #endif #else /* defined(bpf_target_defined) */ #define PT_REGS_PARM1(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM2(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM3(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM4(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM5(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM6(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM7(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM8(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_RET(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_FP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_RC(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_SP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_IP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM1_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM2_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM3_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM4_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM5_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM6_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM7_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM8_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_RET_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_FP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_RC_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_SP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_IP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM1_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM2_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM3_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM4_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM5_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM6_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM7_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM1_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM2_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM3_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM4_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM5_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM6_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM7_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #endif /* defined(bpf_target_defined) */ /* * When invoked from a syscall handler kprobe, returns a pointer to a * struct pt_regs containing syscall arguments and suitable for passing to * PT_REGS_PARMn_SYSCALL() and PT_REGS_PARMn_CORE_SYSCALL(). */ #ifndef PT_REGS_SYSCALL_REGS /* By default, assume that the arch selects ARCH_HAS_SYSCALL_WRAPPER. */ #define PT_REGS_SYSCALL_REGS(ctx) ((struct pt_regs *)PT_REGS_PARM1(ctx)) #endif #ifndef ___bpf_concat #define ___bpf_concat(a, b) a ## b #endif #ifndef ___bpf_apply #define ___bpf_apply(fn, n) ___bpf_concat(fn, n) #endif #ifndef ___bpf_nth #define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N #endif #ifndef ___bpf_narg #define ___bpf_narg(...) ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #endif #define ___bpf_ctx_cast0() ctx #define ___bpf_ctx_cast1(x) ___bpf_ctx_cast0(), (void *)ctx[0] #define ___bpf_ctx_cast2(x, args...) ___bpf_ctx_cast1(args), (void *)ctx[1] #define ___bpf_ctx_cast3(x, args...) ___bpf_ctx_cast2(args), (void *)ctx[2] #define ___bpf_ctx_cast4(x, args...) ___bpf_ctx_cast3(args), (void *)ctx[3] #define ___bpf_ctx_cast5(x, args...) ___bpf_ctx_cast4(args), (void *)ctx[4] #define ___bpf_ctx_cast6(x, args...) ___bpf_ctx_cast5(args), (void *)ctx[5] #define ___bpf_ctx_cast7(x, args...) ___bpf_ctx_cast6(args), (void *)ctx[6] #define ___bpf_ctx_cast8(x, args...) ___bpf_ctx_cast7(args), (void *)ctx[7] #define ___bpf_ctx_cast9(x, args...) ___bpf_ctx_cast8(args), (void *)ctx[8] #define ___bpf_ctx_cast10(x, args...) ___bpf_ctx_cast9(args), (void *)ctx[9] #define ___bpf_ctx_cast11(x, args...) ___bpf_ctx_cast10(args), (void *)ctx[10] #define ___bpf_ctx_cast12(x, args...) ___bpf_ctx_cast11(args), (void *)ctx[11] #define ___bpf_ctx_cast(args...) ___bpf_apply(___bpf_ctx_cast, ___bpf_narg(args))(args) /* * BPF_PROG is a convenience wrapper for generic tp_btf/fentry/fexit and * similar kinds of BPF programs, that accept input arguments as a single * pointer to untyped u64 array, where each u64 can actually be a typed * pointer or integer of different size. Instead of requring user to write * manual casts and work with array elements by index, BPF_PROG macro * allows user to declare a list of named and typed input arguments in the * same syntax as for normal C function. All the casting is hidden and * performed transparently, while user code can just assume working with * function arguments of specified type and name. * * Original raw context argument is preserved as well as 'ctx' argument. * This is useful when using BPF helpers that expect original context * as one of the parameters (e.g., for bpf_perf_event_output()). */ #define BPF_PROG(name, args...) \ name(unsigned long long *ctx); \ static __always_inline typeof(name(0)) \ ____##name(unsigned long long *ctx, ##args); \ typeof(name(0)) name(unsigned long long *ctx) \ { \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ return ____##name(___bpf_ctx_cast(args)); \ _Pragma("GCC diagnostic pop") \ } \ static __always_inline typeof(name(0)) \ ____##name(unsigned long long *ctx, ##args) #ifndef ___bpf_nth2 #define ___bpf_nth2(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, \ _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, N, ...) N #endif #ifndef ___bpf_narg2 #define ___bpf_narg2(...) \ ___bpf_nth2(_, ##__VA_ARGS__, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, \ 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0) #endif #define ___bpf_treg_cnt(t) \ __builtin_choose_expr(sizeof(t) == 1, 1, \ __builtin_choose_expr(sizeof(t) == 2, 1, \ __builtin_choose_expr(sizeof(t) == 4, 1, \ __builtin_choose_expr(sizeof(t) == 8, 1, \ __builtin_choose_expr(sizeof(t) == 16, 2, \ (void)0))))) #define ___bpf_reg_cnt0() (0) #define ___bpf_reg_cnt1(t, x) (___bpf_reg_cnt0() + ___bpf_treg_cnt(t)) #define ___bpf_reg_cnt2(t, x, args...) (___bpf_reg_cnt1(args) + ___bpf_treg_cnt(t)) #define ___bpf_reg_cnt3(t, x, args...) (___bpf_reg_cnt2(args) + ___bpf_treg_cnt(t)) #define ___bpf_reg_cnt4(t, x, args...) (___bpf_reg_cnt3(args) + ___bpf_treg_cnt(t)) #define ___bpf_reg_cnt5(t, x, args...) (___bpf_reg_cnt4(args) + ___bpf_treg_cnt(t)) #define ___bpf_reg_cnt6(t, x, args...) (___bpf_reg_cnt5(args) + ___bpf_treg_cnt(t)) #define ___bpf_reg_cnt7(t, x, args...) (___bpf_reg_cnt6(args) + ___bpf_treg_cnt(t)) #define ___bpf_reg_cnt8(t, x, args...) (___bpf_reg_cnt7(args) + ___bpf_treg_cnt(t)) #define ___bpf_reg_cnt9(t, x, args...) (___bpf_reg_cnt8(args) + ___bpf_treg_cnt(t)) #define ___bpf_reg_cnt10(t, x, args...) (___bpf_reg_cnt9(args) + ___bpf_treg_cnt(t)) #define ___bpf_reg_cnt11(t, x, args...) (___bpf_reg_cnt10(args) + ___bpf_treg_cnt(t)) #define ___bpf_reg_cnt12(t, x, args...) (___bpf_reg_cnt11(args) + ___bpf_treg_cnt(t)) #define ___bpf_reg_cnt(args...) ___bpf_apply(___bpf_reg_cnt, ___bpf_narg2(args))(args) #define ___bpf_union_arg(t, x, n) \ __builtin_choose_expr(sizeof(t) == 1, ({ union { __u8 z[1]; t x; } ___t = { .z = {ctx[n]}}; ___t.x; }), \ __builtin_choose_expr(sizeof(t) == 2, ({ union { __u16 z[1]; t x; } ___t = { .z = {ctx[n]} }; ___t.x; }), \ __builtin_choose_expr(sizeof(t) == 4, ({ union { __u32 z[1]; t x; } ___t = { .z = {ctx[n]} }; ___t.x; }), \ __builtin_choose_expr(sizeof(t) == 8, ({ union { __u64 z[1]; t x; } ___t = {.z = {ctx[n]} }; ___t.x; }), \ __builtin_choose_expr(sizeof(t) == 16, ({ union { __u64 z[2]; t x; } ___t = {.z = {ctx[n], ctx[n + 1]} }; ___t.x; }), \ (void)0))))) #define ___bpf_ctx_arg0(n, args...) #define ___bpf_ctx_arg1(n, t, x) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt1(t, x)) #define ___bpf_ctx_arg2(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt2(t, x, args)) ___bpf_ctx_arg1(n, args) #define ___bpf_ctx_arg3(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt3(t, x, args)) ___bpf_ctx_arg2(n, args) #define ___bpf_ctx_arg4(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt4(t, x, args)) ___bpf_ctx_arg3(n, args) #define ___bpf_ctx_arg5(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt5(t, x, args)) ___bpf_ctx_arg4(n, args) #define ___bpf_ctx_arg6(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt6(t, x, args)) ___bpf_ctx_arg5(n, args) #define ___bpf_ctx_arg7(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt7(t, x, args)) ___bpf_ctx_arg6(n, args) #define ___bpf_ctx_arg8(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt8(t, x, args)) ___bpf_ctx_arg7(n, args) #define ___bpf_ctx_arg9(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt9(t, x, args)) ___bpf_ctx_arg8(n, args) #define ___bpf_ctx_arg10(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt10(t, x, args)) ___bpf_ctx_arg9(n, args) #define ___bpf_ctx_arg11(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt11(t, x, args)) ___bpf_ctx_arg10(n, args) #define ___bpf_ctx_arg12(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt12(t, x, args)) ___bpf_ctx_arg11(n, args) #define ___bpf_ctx_arg(args...) ___bpf_apply(___bpf_ctx_arg, ___bpf_narg2(args))(___bpf_reg_cnt(args), args) #define ___bpf_ctx_decl0() #define ___bpf_ctx_decl1(t, x) , t x #define ___bpf_ctx_decl2(t, x, args...) , t x ___bpf_ctx_decl1(args) #define ___bpf_ctx_decl3(t, x, args...) , t x ___bpf_ctx_decl2(args) #define ___bpf_ctx_decl4(t, x, args...) , t x ___bpf_ctx_decl3(args) #define ___bpf_ctx_decl5(t, x, args...) , t x ___bpf_ctx_decl4(args) #define ___bpf_ctx_decl6(t, x, args...) , t x ___bpf_ctx_decl5(args) #define ___bpf_ctx_decl7(t, x, args...) , t x ___bpf_ctx_decl6(args) #define ___bpf_ctx_decl8(t, x, args...) , t x ___bpf_ctx_decl7(args) #define ___bpf_ctx_decl9(t, x, args...) , t x ___bpf_ctx_decl8(args) #define ___bpf_ctx_decl10(t, x, args...) , t x ___bpf_ctx_decl9(args) #define ___bpf_ctx_decl11(t, x, args...) , t x ___bpf_ctx_decl10(args) #define ___bpf_ctx_decl12(t, x, args...) , t x ___bpf_ctx_decl11(args) #define ___bpf_ctx_decl(args...) ___bpf_apply(___bpf_ctx_decl, ___bpf_narg2(args))(args) /* * BPF_PROG2 is an enhanced version of BPF_PROG in order to handle struct * arguments. Since each struct argument might take one or two u64 values * in the trampoline stack, argument type size is needed to place proper number * of u64 values for each argument. Therefore, BPF_PROG2 has different * syntax from BPF_PROG. For example, for the following BPF_PROG syntax: * * int BPF_PROG(test2, int a, int b) { ... } * * the corresponding BPF_PROG2 syntax is: * * int BPF_PROG2(test2, int, a, int, b) { ... } * * where type and the corresponding argument name are separated by comma. * * Use BPF_PROG2 macro if one of the arguments might be a struct/union larger * than 8 bytes: * * int BPF_PROG2(test_struct_arg, struct bpf_testmod_struct_arg_1, a, int, b, * int, c, int, d, struct bpf_testmod_struct_arg_2, e, int, ret) * { * // access a, b, c, d, e, and ret directly * ... * } */ #define BPF_PROG2(name, args...) \ name(unsigned long long *ctx); \ static __always_inline typeof(name(0)) \ ____##name(unsigned long long *ctx ___bpf_ctx_decl(args)); \ typeof(name(0)) name(unsigned long long *ctx) \ { \ return ____##name(ctx ___bpf_ctx_arg(args)); \ } \ static __always_inline typeof(name(0)) \ ____##name(unsigned long long *ctx ___bpf_ctx_decl(args)) struct pt_regs; #define ___bpf_kprobe_args0() ctx #define ___bpf_kprobe_args1(x) ___bpf_kprobe_args0(), (void *)PT_REGS_PARM1(ctx) #define ___bpf_kprobe_args2(x, args...) ___bpf_kprobe_args1(args), (void *)PT_REGS_PARM2(ctx) #define ___bpf_kprobe_args3(x, args...) ___bpf_kprobe_args2(args), (void *)PT_REGS_PARM3(ctx) #define ___bpf_kprobe_args4(x, args...) ___bpf_kprobe_args3(args), (void *)PT_REGS_PARM4(ctx) #define ___bpf_kprobe_args5(x, args...) ___bpf_kprobe_args4(args), (void *)PT_REGS_PARM5(ctx) #define ___bpf_kprobe_args6(x, args...) ___bpf_kprobe_args5(args), (void *)PT_REGS_PARM6(ctx) #define ___bpf_kprobe_args7(x, args...) ___bpf_kprobe_args6(args), (void *)PT_REGS_PARM7(ctx) #define ___bpf_kprobe_args8(x, args...) ___bpf_kprobe_args7(args), (void *)PT_REGS_PARM8(ctx) #define ___bpf_kprobe_args(args...) ___bpf_apply(___bpf_kprobe_args, ___bpf_narg(args))(args) /* * BPF_KPROBE serves the same purpose for kprobes as BPF_PROG for * tp_btf/fentry/fexit BPF programs. It hides the underlying platform-specific * low-level way of getting kprobe input arguments from struct pt_regs, and * provides a familiar typed and named function arguments syntax and * semantics of accessing kprobe input paremeters. * * Original struct pt_regs* context is preserved as 'ctx' argument. This might * be necessary when using BPF helpers like bpf_perf_event_output(). */ #define BPF_KPROBE(name, args...) \ name(struct pt_regs *ctx); \ static __always_inline typeof(name(0)) \ ____##name(struct pt_regs *ctx, ##args); \ typeof(name(0)) name(struct pt_regs *ctx) \ { \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ return ____##name(___bpf_kprobe_args(args)); \ _Pragma("GCC diagnostic pop") \ } \ static __always_inline typeof(name(0)) \ ____##name(struct pt_regs *ctx, ##args) #define ___bpf_kretprobe_args0() ctx #define ___bpf_kretprobe_args1(x) ___bpf_kretprobe_args0(), (void *)PT_REGS_RC(ctx) #define ___bpf_kretprobe_args(args...) ___bpf_apply(___bpf_kretprobe_args, ___bpf_narg(args))(args) /* * BPF_KRETPROBE is similar to BPF_KPROBE, except, it only provides optional * return value (in addition to `struct pt_regs *ctx`), but no input * arguments, because they will be clobbered by the time probed function * returns. */ #define BPF_KRETPROBE(name, args...) \ name(struct pt_regs *ctx); \ static __always_inline typeof(name(0)) \ ____##name(struct pt_regs *ctx, ##args); \ typeof(name(0)) name(struct pt_regs *ctx) \ { \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ return ____##name(___bpf_kretprobe_args(args)); \ _Pragma("GCC diagnostic pop") \ } \ static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args) /* If kernel has CONFIG_ARCH_HAS_SYSCALL_WRAPPER, read pt_regs directly */ #define ___bpf_syscall_args0() ctx #define ___bpf_syscall_args1(x) ___bpf_syscall_args0(), (void *)PT_REGS_PARM1_SYSCALL(regs) #define ___bpf_syscall_args2(x, args...) ___bpf_syscall_args1(args), (void *)PT_REGS_PARM2_SYSCALL(regs) #define ___bpf_syscall_args3(x, args...) ___bpf_syscall_args2(args), (void *)PT_REGS_PARM3_SYSCALL(regs) #define ___bpf_syscall_args4(x, args...) ___bpf_syscall_args3(args), (void *)PT_REGS_PARM4_SYSCALL(regs) #define ___bpf_syscall_args5(x, args...) ___bpf_syscall_args4(args), (void *)PT_REGS_PARM5_SYSCALL(regs) #define ___bpf_syscall_args6(x, args...) ___bpf_syscall_args5(args), (void *)PT_REGS_PARM6_SYSCALL(regs) #define ___bpf_syscall_args7(x, args...) ___bpf_syscall_args6(args), (void *)PT_REGS_PARM7_SYSCALL(regs) #define ___bpf_syscall_args(args...) ___bpf_apply(___bpf_syscall_args, ___bpf_narg(args))(args) /* If kernel doesn't have CONFIG_ARCH_HAS_SYSCALL_WRAPPER, we have to BPF_CORE_READ from pt_regs */ #define ___bpf_syswrap_args0() ctx #define ___bpf_syswrap_args1(x) ___bpf_syswrap_args0(), (void *)PT_REGS_PARM1_CORE_SYSCALL(regs) #define ___bpf_syswrap_args2(x, args...) ___bpf_syswrap_args1(args), (void *)PT_REGS_PARM2_CORE_SYSCALL(regs) #define ___bpf_syswrap_args3(x, args...) ___bpf_syswrap_args2(args), (void *)PT_REGS_PARM3_CORE_SYSCALL(regs) #define ___bpf_syswrap_args4(x, args...) ___bpf_syswrap_args3(args), (void *)PT_REGS_PARM4_CORE_SYSCALL(regs) #define ___bpf_syswrap_args5(x, args...) ___bpf_syswrap_args4(args), (void *)PT_REGS_PARM5_CORE_SYSCALL(regs) #define ___bpf_syswrap_args6(x, args...) ___bpf_syswrap_args5(args), (void *)PT_REGS_PARM6_CORE_SYSCALL(regs) #define ___bpf_syswrap_args7(x, args...) ___bpf_syswrap_args6(args), (void *)PT_REGS_PARM7_CORE_SYSCALL(regs) #define ___bpf_syswrap_args(args...) ___bpf_apply(___bpf_syswrap_args, ___bpf_narg(args))(args) /* * BPF_KSYSCALL is a variant of BPF_KPROBE, which is intended for * tracing syscall functions, like __x64_sys_close. It hides the underlying * platform-specific low-level way of getting syscall input arguments from * struct pt_regs, and provides a familiar typed and named function arguments * syntax and semantics of accessing syscall input parameters. * * Original struct pt_regs * context is preserved as 'ctx' argument. This might * be necessary when using BPF helpers like bpf_perf_event_output(). * * At the moment BPF_KSYSCALL does not transparently handle all the calling * convention quirks for the following syscalls: * * - mmap(): __ARCH_WANT_SYS_OLD_MMAP. * - clone(): CONFIG_CLONE_BACKWARDS, CONFIG_CLONE_BACKWARDS2 and * CONFIG_CLONE_BACKWARDS3. * - socket-related syscalls: __ARCH_WANT_SYS_SOCKETCALL. * - compat syscalls. * * This may or may not change in the future. User needs to take extra measures * to handle such quirks explicitly, if necessary. * * This macro relies on BPF CO-RE support and virtual __kconfig externs. */ #define BPF_KSYSCALL(name, args...) \ name(struct pt_regs *ctx); \ extern _Bool LINUX_HAS_SYSCALL_WRAPPER __kconfig; \ static __always_inline typeof(name(0)) \ ____##name(struct pt_regs *ctx, ##args); \ typeof(name(0)) name(struct pt_regs *ctx) \ { \ struct pt_regs *regs = LINUX_HAS_SYSCALL_WRAPPER \ ? (struct pt_regs *)PT_REGS_PARM1(ctx) \ : ctx; \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ if (LINUX_HAS_SYSCALL_WRAPPER) \ return ____##name(___bpf_syswrap_args(args)); \ else \ return ____##name(___bpf_syscall_args(args)); \ _Pragma("GCC diagnostic pop") \ } \ static __always_inline typeof(name(0)) \ ____##name(struct pt_regs *ctx, ##args) #define BPF_KPROBE_SYSCALL BPF_KSYSCALL /* BPF_UPROBE and BPF_URETPROBE are identical to BPF_KPROBE and BPF_KRETPROBE, * but are named way less confusingly for SEC("uprobe") and SEC("uretprobe") * use cases. */ #define BPF_UPROBE(name, args...) BPF_KPROBE(name, ##args) #define BPF_URETPROBE(name, args...) BPF_KRETPROBE(name, ##args) #endif ================================================ FILE: service/firewall/interception/ebpf/programs/exec.c ================================================ #include "vmlinux-x86.h" #include "bpf/bpf_helpers.h" #include "bpf/bpf_tracing.h" #define ARGLEN 32 // maximum amount of args in argv we'll copy #define ARGSIZE 1024 // maximum byte length of each arg in argv we'll copy char __license[] SEC("license") = "GPL"; // Ring buffer for all connection events struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 1 << 24); } pm_exec_map SEC(".maps"); // This struct is defined according to // /sys/kernel/debug/tracing/events/syscalls/sys_enter_execve/format struct exec_info { u16 common_type; // offset=0, size=2 u8 common_flags; // offset=2, size=1 u8 common_preempt_count; // offset=3, size=1 s32 common_pid; // offset=4, size=4 s32 syscall_nr; // offset=8, size=4 u32 pad; // offset=12, size=4 (pad) const u8 *filename; // offset=16, size=8 (ptr) const u8 *const *argv; // offset=24, size=8 (ptr) const u8 *const *envp; // offset=32, size=8 (ptr) }; // The event struct. This struct must be kept in sync with the Golang // counterpart. struct event_t { // Details about the process being launched. u8 filename[ARGSIZE]; u8 argv[ARGLEN][ARGSIZE]; u32 argc; // set to ARGLEN + 1 if there were more than ARGLEN arguments u32 uid; u32 gid; u32 pid; // Name of the calling process. u8 comm[ARGSIZE]; }; // Tracepoint at the top of execve() syscall. SEC("tracepoint/syscalls/sys_enter_execve") s32 enter_execve(struct exec_info *ctx) { // Reserve memory for our event on the `events` ring buffer defined above. struct event_t *event; event = bpf_ringbuf_reserve(&pm_exec_map, sizeof(struct event_t), 0); if (!event) { bpf_printk("could not reserve ringbuf memory"); return 1; } // Store process/calling process details. u64 uidgid = bpf_get_current_uid_gid(); u64 pidtgid = bpf_get_current_pid_tgid(); event->uid = uidgid; // uid is the first 32 bits event->gid = uidgid >> 32; // gid is the last 32 bits NOLINT(readability-magic-numbers) event->pid = pidtgid; // pid is the first 32 bits s32 ret = bpf_get_current_comm(&event->comm, sizeof(event->comm)); if (ret) { bpf_printk("could not get current comm: %d", ret); bpf_ringbuf_discard(event, 0); return 1; } // Write the filename in addition to argv[0] because the filename contains // the full path to the file which could be more useful in some situations. ret = bpf_probe_read_user_str(event->filename, sizeof(event->filename), ctx->filename); if (ret < 0) { bpf_printk("could not read filename into event struct: %d", ret); bpf_ringbuf_discard(event, 0); return 1; } // Copy everything from ctx->argv to event->argv, incrementing event->argc // as we go. for (s32 i = 0; i < ARGLEN; i++) { if (!(&ctx->argv[i])) { goto out; } // Copying the arg into it's own variable before copying it into // event->argv[i] prevents memory corruption. const u8 *argp = NULL; ret = bpf_probe_read_user(&argp, sizeof(argp), &ctx->argv[i]); if (ret || !argp) { goto out; } // Copy argp to event->argv[i]. ret = bpf_probe_read_user_str(event->argv[i], sizeof(event->argv[i]), argp); if (ret < 0) { bpf_printk("read argv %d: %d", i, ret); goto out; } event->argc++; } // This won't get hit if we `goto out` in the loop above. This is to signify // to userspace that we couldn't copy all of the arguments because it // exceeded ARGLEN. event->argc++; out: // Write the event to the ring buffer and notify userspace. This will cause // the `Read()` call in userspace to return if it was blocked. bpf_ringbuf_submit(event, 0); return 0; } ================================================ FILE: service/firewall/interception/ebpf/programs/monitor.c ================================================ #include "vmlinux-x86.h" #include "bpf/bpf_helpers.h" #include "bpf/bpf_tracing.h" // IP Version #define AF_INET 2 #define AF_INET6 10 // Protocols #define TCP 6 #define UDP 17 #define UDPLite 136 #define OUTBOUND 0 #define INBOUND 1 char __license[] SEC("license") = "GPL"; // Ring buffer for all connection events struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 1 << 24); } pm_connection_events SEC(".maps"); // Event struct that will be sent to Go on each new connection. (The name should be the same as the go generate command) struct Event { u32 saddr[4]; u32 daddr[4]; u16 sport; u16 dport; u32 pid; u8 ipVersion; u8 protocol; u8 direction; }; struct Event *unused __attribute__((unused)); // Fentry of tcp_connect will be executed when equivalent kernel function is called. // In the kernel all IP address and ports should be set before tcp_connect is called. [this-function] -> tcp_connect SEC("fentry/tcp_connect") int BPF_PROG(tcp_connect, struct sock *sk) { // Alloc space for the event struct Event *tcp_info; tcp_info = bpf_ringbuf_reserve(&pm_connection_events, sizeof(struct Event), 0); if (!tcp_info) { return 0; } // Read PID (Careful: This is the Thread Group ID in kernel speak!) tcp_info->pid = __builtin_bswap32((u32)(bpf_get_current_pid_tgid() >> 32)); // Set protocol tcp_info->protocol = TCP; // Set direction tcp_info->direction = OUTBOUND; // Set src and dist ports tcp_info->sport = __builtin_bswap16(sk->__sk_common.skc_num); tcp_info->dport = sk->__sk_common.skc_dport; // Set src and dist IPs if (sk->__sk_common.skc_family == AF_INET) { tcp_info->saddr[0] = __builtin_bswap32(sk->__sk_common.skc_rcv_saddr); tcp_info->daddr[0] = __builtin_bswap32(sk->__sk_common.skc_daddr); // Set IP version tcp_info->ipVersion = 4; } else if (sk->__sk_common.skc_family == AF_INET6) { for(int i = 0; i < 4; i++) { tcp_info->saddr[i] = __builtin_bswap32(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[i]); } for(int i = 0; i < 4; i++) { tcp_info->daddr[i] = __builtin_bswap32(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32[i]); } // Set IP version tcp_info->ipVersion = 6; } // Send event bpf_ringbuf_submit(tcp_info, 0); return 0; }; // Fexit(function exit) of `udp_v4_connect` will be executed after the `udp_connect` kernel function is called. // // Kernel compatibility note: // - Linux kernels < 6.13: use `ip4_datagram_connect` function // https://elixir.bootlin.com/linux/v6.12.34/source/net/ipv4/udp.c#L2997 // - Linux kernels >= 6.13: function renamed to `udp_connect` // https://elixir.bootlin.com/linux/v6.13-rc1/source/net/ipv4/udp.c#L3131 // SEC("fexit/udp_connect") // Note: This attach point name may be overwritten (see worker.go: modifyProgramsAttachPoints()) int BPF_PROG(udp_v4_connect, struct sock *sk) { // Ignore everything else then IPv4 if (sk->__sk_common.skc_family != AF_INET) { return 0; } // ip4_datagram_connect return error if (sk->__sk_common.skc_dport == 0) { return 0; } // Allocate space for the event. struct Event *udp_info; udp_info = bpf_ringbuf_reserve(&pm_connection_events, sizeof(struct Event), 0); if (!udp_info) { return 0; } // Read PID (Careful: This is the Thread Group ID in kernel speak!) udp_info->pid = __builtin_bswap32((u32)(bpf_get_current_pid_tgid() >> 32)); // Set src and dst ports udp_info->sport = __builtin_bswap16(sk->__sk_common.skc_num); udp_info->dport = sk->__sk_common.skc_dport; // Set src and dst IPs udp_info->saddr[0] = __builtin_bswap32(sk->__sk_common.skc_rcv_saddr); udp_info->daddr[0] = __builtin_bswap32(sk->__sk_common.skc_daddr); // Set IP version udp_info->ipVersion = 4; // Set protocol if(sk->sk_protocol == IPPROTO_UDPLITE) { udp_info->protocol = UDPLite; } else { udp_info->protocol = UDP; } // Send event bpf_ringbuf_submit(udp_info, 0); return 0; } // Fentry(function enter) of `udp_v6_connect` will be executed after the `udpv6_connect` kernel function is called. // // Kernel compatibility note: // - Linux kernels < 6.13: use `ip6_datagram_connect` function // https://elixir.bootlin.com/linux/v6.12.34/source/net/ipv4/udp.c#L2997 // - Linux kernels >= 6.13: function renamed to `udpv6_connect` // https://elixir.bootlin.com/linux/v6.13-rc1/source/net/ipv4/udp.c#L3131 // SEC("fexit/udpv6_connect") // Note: This attach point name may be overwritten (see worker.go: modifyProgramsAttachPoints()) int BPF_PROG(udp_v6_connect, struct sock *sk) { // Ignore everything else then IPv6 if (sk->__sk_common.skc_family != AF_INET6) { return 0; } // ip6_datagram_connect return error if (sk->__sk_common.skc_dport == 0) { return 0; } // Make sure its udp6 socket struct udp6_sock *us = bpf_skc_to_udp6_sock(sk); if (!us) { return 0; } // Allocate space for the event. struct Event *udp_info; udp_info = bpf_ringbuf_reserve(&pm_connection_events, sizeof(struct Event), 0); if (!udp_info) { return 0; } // Read PID (Careful: This is the Thread Group ID in kernel speak!) udp_info->pid = __builtin_bswap32((u32)(bpf_get_current_pid_tgid() >> 32)); // Set src and dst ports udp_info->sport = __builtin_bswap16(sk->__sk_common.skc_num); udp_info->dport = sk->__sk_common.skc_dport; // Set src and dst IPs for(int i = 0; i < 4; i++) { udp_info->saddr[i] = __builtin_bswap32(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[i]); } for(int i = 0; i < 4; i++) { udp_info->daddr[i] = __builtin_bswap32(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32[i]); } // IP version udp_info->ipVersion = 6; // Set protocol if(sk->sk_protocol == IPPROTO_UDPLITE) { udp_info->protocol = UDPLite; } else { udp_info->protocol = UDP; } // Send event bpf_ringbuf_submit(udp_info, 0); return 0; } ================================================ FILE: service/firewall/interception/ebpf/programs/update.sh ================================================ #!/usr/bin/env bash # Version of libbpf to fetch headers from LIBBPF_VERSION=1.2.0 # The headers we want prefix=libbpf-"$LIBBPF_VERSION" headers=( "$prefix"/src/bpf_core_read.h "$prefix"/src/bpf_helper_defs.h "$prefix"/src/bpf_helpers.h "$prefix"/src/bpf_tracing.h ) # Fetch libbpf release and extract the desired headers curl -sL "https://github.com/libbpf/libbpf/archive/refs/tags/v${LIBBPF_VERSION}.tar.gz" | \ tar -xz --xform='s#.*/#bpf/#' "${headers[@]}" # To generate the vmlinux header file # See "Export kernel information" at https://docs.ebpf.io/concepts/core/btf # # bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux-x86.h ================================================ FILE: service/firewall/interception/ebpf/programs/vmlinux-x86.h ================================================ #ifndef __VMLINUX_H__ #define __VMLINUX_H__ #ifndef BPF_NO_PRESERVE_ACCESS_INDEX #pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record) #endif typedef unsigned char __u8; typedef short int __s16; typedef short unsigned int __u16; typedef int __s32; typedef unsigned int __u32; typedef long long int __s64; typedef long long unsigned int __u64; typedef __u8 u8; typedef __s16 s16; typedef __u16 u16; typedef __s32 s32; typedef __u32 u32; typedef __s64 s64; typedef __u64 u64; enum { false = 0, true = 1, }; typedef long int __kernel_long_t; typedef long unsigned int __kernel_ulong_t; typedef int __kernel_pid_t; typedef unsigned int __kernel_uid32_t; typedef unsigned int __kernel_gid32_t; typedef __kernel_ulong_t __kernel_size_t; typedef __kernel_long_t __kernel_ssize_t; typedef long long int __kernel_loff_t; typedef long long int __kernel_time64_t; typedef __kernel_long_t __kernel_clock_t; typedef int __kernel_timer_t; typedef int __kernel_clockid_t; typedef unsigned int __poll_t; typedef u32 __kernel_dev_t; typedef __kernel_dev_t dev_t; typedef short unsigned int umode_t; typedef __kernel_pid_t pid_t; typedef __kernel_clockid_t clockid_t; typedef _Bool bool; typedef __kernel_uid32_t uid_t; typedef __kernel_gid32_t gid_t; typedef __kernel_loff_t loff_t; typedef __kernel_size_t size_t; typedef __kernel_ssize_t ssize_t; typedef s32 int32_t; typedef u32 uint32_t; typedef u64 sector_t; typedef u64 blkcnt_t; typedef unsigned int gfp_t; typedef unsigned int fmode_t; typedef u64 phys_addr_t; typedef struct { int counter; } atomic_t; typedef struct { s64 counter; } atomic64_t; struct list_head { struct list_head *next; struct list_head *prev; }; struct hlist_node; struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next; struct hlist_node **pprev; }; struct callback_head { struct callback_head *next; void (*func)(struct callback_head *); }; struct lock_class_key {}; struct fs_context; struct fs_parameter_spec; struct dentry; struct super_block; struct module; struct file_system_type { const char *name; int fs_flags; int (*init_fs_context)(struct fs_context *); const struct fs_parameter_spec *parameters; struct dentry * (*mount)(struct file_system_type *, int, const char *, void *); void (*kill_sb)(struct super_block *); struct module *owner; struct file_system_type *next; struct hlist_head fs_supers; struct lock_class_key s_lock_key; struct lock_class_key s_umount_key; struct lock_class_key s_vfs_rename_key; struct lock_class_key s_writers_key[3]; struct lock_class_key i_lock_key; struct lock_class_key i_mutex_key; struct lock_class_key i_mutex_dir_key; }; struct qspinlock { union { atomic_t val; struct { u8 locked; u8 pending; }; struct { u16 locked_pending; u16 tail; }; }; }; typedef struct qspinlock arch_spinlock_t; struct qrwlock { union { atomic_t cnts; struct { u8 wlocked; u8 __lstate[3]; }; }; arch_spinlock_t wait_lock; }; typedef struct qrwlock arch_rwlock_t; struct raw_spinlock { arch_spinlock_t raw_lock; }; typedef struct raw_spinlock raw_spinlock_t; struct spinlock { union { struct raw_spinlock rlock; }; }; typedef struct spinlock spinlock_t; typedef struct { arch_rwlock_t raw_lock; } rwlock_t; struct ratelimit_state { raw_spinlock_t lock; int interval; int burst; int printed; int missed; long unsigned int begin; long unsigned int flags; }; typedef void *fl_owner_t; struct file; struct kiocb; struct iov_iter; struct dir_context; struct poll_table_struct; struct vm_area_struct; struct inode; struct file_lock; struct page; struct pipe_inode_info; struct seq_file; struct file_operations { struct module *owner; loff_t (*llseek)(struct file *, loff_t, int); ssize_t (*read)(struct file *, char *, size_t, loff_t *); ssize_t (*write)(struct file *, const char *, size_t, loff_t *); ssize_t (*read_iter)(struct kiocb *, struct iov_iter *); ssize_t (*write_iter)(struct kiocb *, struct iov_iter *); int (*iopoll)(struct kiocb *, bool); int (*iterate)(struct file *, struct dir_context *); int (*iterate_shared)(struct file *, struct dir_context *); __poll_t (*poll)(struct file *, struct poll_table_struct *); long int (*unlocked_ioctl)(struct file *, unsigned int, long unsigned int); long int (*compat_ioctl)(struct file *, unsigned int, long unsigned int); int (*mmap)(struct file *, struct vm_area_struct *); long unsigned int mmap_supported_flags; int (*open)(struct inode *, struct file *); int (*flush)(struct file *, fl_owner_t); int (*release)(struct inode *, struct file *); int (*fsync)(struct file *, loff_t, loff_t, int); int (*fasync)(int, struct file *, int); int (*lock)(struct file *, int, struct file_lock *); ssize_t (*sendpage)(struct file *, struct page *, int, size_t, loff_t *, int); long unsigned int (*get_unmapped_area)(struct file *, long unsigned int, long unsigned int, long unsigned int, long unsigned int); int (*check_flags)(int); int (*flock)(struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long int, struct file_lock **, void **); long int (*fallocate)(struct file *, int, loff_t, loff_t); void (*show_fdinfo)(struct seq_file *, struct file *); ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, loff_t, size_t, unsigned int); loff_t (*remap_file_range)(struct file *, loff_t, struct file *, loff_t, loff_t, unsigned int); int (*fadvise)(struct file *, loff_t, loff_t, int); }; typedef __s64 time64_t; struct __kernel_timespec { __kernel_time64_t tv_sec; long long int tv_nsec; }; struct timespec64 { time64_t tv_sec; long int tv_nsec; }; enum timespec_type { TT_NONE = 0, TT_NATIVE = 1, TT_COMPAT = 2, }; typedef s32 old_time32_t; struct old_timespec32 { old_time32_t tv_sec; s32 tv_nsec; }; struct pollfd; struct restart_block { long unsigned int arch_data; long int (*fn)(struct restart_block *); union { struct { u32 *uaddr; u32 val; u32 flags; u32 bitset; u64 time; u32 *uaddr2; } futex; struct { clockid_t clockid; enum timespec_type type; union { struct __kernel_timespec *rmtp; struct old_timespec32 *compat_rmtp; }; u64 expires; } nanosleep; struct { struct pollfd *ufds; int nfds; int has_timeout; long unsigned int tv_sec; long unsigned int tv_nsec; } poll; }; }; struct thread_info { long unsigned int flags; long unsigned int syscall_work; u32 status; }; struct refcount_struct { atomic_t refs; }; typedef struct refcount_struct refcount_t; struct llist_node { struct llist_node *next; }; struct __call_single_node { struct llist_node llist; union { unsigned int u_flags; atomic_t a_flags; }; u16 src; u16 dst; }; struct load_weight { long unsigned int weight; u32 inv_weight; }; struct rb_node { long unsigned int __rb_parent_color; struct rb_node *rb_right; struct rb_node *rb_left; }; struct sched_statistics { u64 wait_start; u64 wait_max; u64 wait_count; u64 wait_sum; u64 iowait_count; u64 iowait_sum; u64 sleep_start; u64 sleep_max; s64 sum_sleep_runtime; u64 block_start; u64 block_max; u64 exec_max; u64 slice_max; u64 nr_migrations_cold; u64 nr_failed_migrations_affine; u64 nr_failed_migrations_running; u64 nr_failed_migrations_hot; u64 nr_forced_migrations; u64 nr_wakeups; u64 nr_wakeups_sync; u64 nr_wakeups_migrate; u64 nr_wakeups_local; u64 nr_wakeups_remote; u64 nr_wakeups_affine; u64 nr_wakeups_affine_attempts; u64 nr_wakeups_passive; u64 nr_wakeups_idle; }; struct util_est { unsigned int enqueued; unsigned int ewma; }; struct sched_avg { u64 last_update_time; u64 load_sum; u64 runnable_sum; u32 util_sum; u32 period_contrib; long unsigned int load_avg; long unsigned int runnable_avg; long unsigned int util_avg; struct util_est util_est; }; struct cfs_rq; struct sched_entity { struct load_weight load; struct rb_node run_node; struct list_head group_node; unsigned int on_rq; u64 exec_start; u64 sum_exec_runtime; u64 vruntime; u64 prev_sum_exec_runtime; u64 nr_migrations; struct sched_statistics statistics; int depth; struct sched_entity *parent; struct cfs_rq *cfs_rq; struct cfs_rq *my_q; long unsigned int runnable_weight; long: 64; long: 64; long: 64; struct sched_avg avg; }; struct sched_rt_entity { struct list_head run_list; long unsigned int timeout; long unsigned int watchdog_stamp; unsigned int time_slice; short unsigned int on_rq; short unsigned int on_list; struct sched_rt_entity *back; }; typedef s64 ktime_t; struct timerqueue_node { struct rb_node node; ktime_t expires; }; enum hrtimer_restart { HRTIMER_NORESTART = 0, HRTIMER_RESTART = 1, }; struct hrtimer_clock_base; struct hrtimer { struct timerqueue_node node; ktime_t _softexpires; enum hrtimer_restart (*function)(struct hrtimer *); struct hrtimer_clock_base *base; u8 state; u8 is_rel; u8 is_soft; u8 is_hard; }; struct sched_dl_entity { struct rb_node rb_node; u64 dl_runtime; u64 dl_deadline; u64 dl_period; u64 dl_bw; u64 dl_density; s64 runtime; u64 deadline; unsigned int flags; unsigned int dl_throttled: 1; unsigned int dl_yielded: 1; unsigned int dl_non_contending: 1; unsigned int dl_overrun: 1; struct hrtimer dl_timer; struct hrtimer inactive_timer; struct sched_dl_entity *pi_se; }; struct uclamp_se { unsigned int value: 11; unsigned int bucket_id: 3; unsigned int active: 1; unsigned int user_defined: 1; }; struct cpumask { long unsigned int bits[5]; }; typedef struct cpumask cpumask_t; union rcu_special { struct { u8 blocked; u8 need_qs; u8 exp_hint; u8 need_mb; } b; u32 s; }; struct sched_info { long unsigned int pcount; long long unsigned int run_delay; long long unsigned int last_arrival; long long unsigned int last_queued; }; struct plist_node { int prio; struct list_head prio_list; struct list_head node_list; }; struct vmacache { u64 seqnum; struct vm_area_struct *vmas[4]; }; struct task_rss_stat { int events; int count[4]; }; struct prev_cputime { u64 utime; u64 stime; raw_spinlock_t lock; }; struct rb_root { struct rb_node *rb_node; }; struct rb_root_cached { struct rb_root rb_root; struct rb_node *rb_leftmost; }; struct timerqueue_head { struct rb_root_cached rb_root; }; struct posix_cputimer_base { u64 nextevt; struct timerqueue_head tqhead; }; struct posix_cputimers { struct posix_cputimer_base bases[3]; unsigned int timers_active; unsigned int expiry_active; }; struct posix_cputimers_work { struct callback_head work; unsigned int scheduled; }; struct sem_undo_list; struct sysv_sem { struct sem_undo_list *undo_list; }; struct sysv_shm { struct list_head shm_clist; }; typedef struct { long unsigned int sig[1]; } sigset_t; struct sigpending { struct list_head list; sigset_t signal; }; typedef struct { uid_t val; } kuid_t; struct seccomp_filter; struct seccomp { int mode; atomic_t filter_count; struct seccomp_filter *filter; }; struct syscall_user_dispatch { char *selector; long unsigned int offset; long unsigned int len; bool on_dispatch; }; struct wake_q_node { struct wake_q_node *next; }; struct task_io_accounting { u64 rchar; u64 wchar; u64 syscr; u64 syscw; u64 read_bytes; u64 write_bytes; u64 cancelled_write_bytes; }; typedef struct { long unsigned int bits[1]; } nodemask_t; struct seqcount { unsigned int sequence; }; typedef struct seqcount seqcount_t; struct seqcount_spinlock { seqcount_t seqcount; }; typedef struct seqcount_spinlock seqcount_spinlock_t; typedef atomic64_t atomic_long_t; struct optimistic_spin_queue { atomic_t tail; }; struct mutex { atomic_long_t owner; spinlock_t wait_lock; struct optimistic_spin_queue osq; struct list_head wait_list; }; struct arch_tlbflush_unmap_batch { struct cpumask cpumask; }; struct tlbflush_unmap_batch { struct arch_tlbflush_unmap_batch arch; bool flush_required; bool writable; }; struct page_frag { struct page *page; __u32 offset; __u32 size; }; struct latency_record { long unsigned int backtrace[12]; unsigned int count; long unsigned int time; long unsigned int max; }; struct kmap_ctrl {}; struct llist_head { struct llist_node *first; }; struct desc_struct { u16 limit0; u16 base0; u16 base1: 8; u16 type: 4; u16 s: 1; u16 dpl: 2; u16 p: 1; u16 limit1: 4; u16 avl: 1; u16 l: 1; u16 d: 1; u16 g: 1; u16 base2: 8; }; struct fregs_state { u32 cwd; u32 swd; u32 twd; u32 fip; u32 fcs; u32 foo; u32 fos; u32 st_space[20]; u32 status; }; struct fxregs_state { u16 cwd; u16 swd; u16 twd; u16 fop; union { struct { u64 rip; u64 rdp; }; struct { u32 fip; u32 fcs; u32 foo; u32 fos; }; }; u32 mxcsr; u32 mxcsr_mask; u32 st_space[32]; u32 xmm_space[64]; u32 padding[12]; union { u32 padding1[12]; u32 sw_reserved[12]; }; }; struct math_emu_info; struct swregs_state { u32 cwd; u32 swd; u32 twd; u32 fip; u32 fcs; u32 foo; u32 fos; u32 st_space[20]; u8 ftop; u8 changed; u8 lookahead; u8 no_update; u8 rm; u8 alimit; struct math_emu_info *info; u32 entry_eip; }; struct xstate_header { u64 xfeatures; u64 xcomp_bv; u64 reserved[6]; }; struct xregs_state { struct fxregs_state i387; struct xstate_header header; u8 extended_state_area[0]; }; union fpregs_state { struct fregs_state fsave; struct fxregs_state fxsave; struct swregs_state soft; struct xregs_state xsave; u8 __padding[4096]; }; struct fpu { unsigned int last_cpu; long unsigned int avx512_timestamp; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; union fpregs_state state; }; struct perf_event; struct io_bitmap; struct thread_struct { struct desc_struct tls_array[3]; long unsigned int sp; short unsigned int es; short unsigned int ds; short unsigned int fsindex; short unsigned int gsindex; long unsigned int fsbase; long unsigned int gsbase; struct perf_event *ptrace_bps[4]; long unsigned int virtual_dr6; long unsigned int ptrace_dr7; long unsigned int cr2; long unsigned int trap_nr; long unsigned int error_code; struct io_bitmap *io_bitmap; long unsigned int iopl_emul; unsigned int sig_on_uaccess_err: 1; u32 pkru; long: 64; long: 64; long: 64; long: 64; long: 64; struct fpu fpu; }; struct sched_class; struct task_group; struct rcu_node; struct mm_struct; struct pid; struct completion; struct cred; struct key; struct nameidata; struct fs_struct; struct files_struct; struct io_uring_task; struct nsproxy; struct signal_struct; struct sighand_struct; struct audit_context; struct rt_mutex_waiter; struct bio_list; struct blk_plug; struct reclaim_state; struct backing_dev_info; struct io_context; struct capture_control; struct kernel_siginfo; typedef struct kernel_siginfo kernel_siginfo_t; struct css_set; struct robust_list_head; struct compat_robust_list_head; struct futex_pi_state; struct perf_event_context; struct mempolicy; struct numa_group; struct rseq; struct task_delay_info; struct ftrace_ret_stack; struct mem_cgroup; struct request_queue; struct uprobe_task; struct vm_struct; struct bpf_local_storage; struct task_struct { struct thread_info thread_info; unsigned int __state; void *stack; refcount_t usage; unsigned int flags; unsigned int ptrace; int on_cpu; struct __call_single_node wake_entry; unsigned int cpu; unsigned int wakee_flips; long unsigned int wakee_flip_decay_ts; struct task_struct *last_wakee; int recent_used_cpu; int wake_cpu; int on_rq; int prio; int static_prio; int normal_prio; unsigned int rt_priority; const struct sched_class *sched_class; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; struct sched_entity se; struct sched_rt_entity rt; struct sched_dl_entity dl; struct rb_node core_node; long unsigned int core_cookie; unsigned int core_occupation; struct task_group *sched_task_group; struct uclamp_se uclamp_req[2]; struct uclamp_se uclamp[2]; struct hlist_head preempt_notifiers; unsigned int btrace_seq; unsigned int policy; int nr_cpus_allowed; const cpumask_t *cpus_ptr; cpumask_t cpus_mask; void *migration_pending; short unsigned int migration_disabled; short unsigned int migration_flags; int rcu_read_lock_nesting; union rcu_special rcu_read_unlock_special; struct list_head rcu_node_entry; struct rcu_node *rcu_blocked_node; long unsigned int rcu_tasks_nvcsw; u8 rcu_tasks_holdout; u8 rcu_tasks_idx; int rcu_tasks_idle_cpu; struct list_head rcu_tasks_holdout_list; int trc_reader_nesting; int trc_ipi_to_cpu; union rcu_special trc_reader_special; bool trc_reader_checked; struct list_head trc_holdout_list; struct sched_info sched_info; struct list_head tasks; struct plist_node pushable_tasks; struct rb_node pushable_dl_tasks; struct mm_struct *mm; struct mm_struct *active_mm; struct vmacache vmacache; struct task_rss_stat rss_stat; int exit_state; int exit_code; int exit_signal; int pdeath_signal; long unsigned int jobctl; unsigned int personality; unsigned int sched_reset_on_fork: 1; unsigned int sched_contributes_to_load: 1; unsigned int sched_migrated: 1; unsigned int sched_psi_wake_requeue: 1; int: 28; unsigned int sched_remote_wakeup: 1; unsigned int in_execve: 1; unsigned int in_iowait: 1; unsigned int restore_sigmask: 1; unsigned int in_user_fault: 1; unsigned int no_cgroup_migration: 1; unsigned int frozen: 1; unsigned int use_memdelay: 1; unsigned int in_memstall: 1; long unsigned int atomic_flags; struct restart_block restart_block; pid_t pid; pid_t tgid; long unsigned int stack_canary; struct task_struct *real_parent; struct task_struct *parent; struct list_head children; struct list_head sibling; struct task_struct *group_leader; struct list_head ptraced; struct list_head ptrace_entry; struct pid *thread_pid; struct hlist_node pid_links[4]; struct list_head thread_group; struct list_head thread_node; struct completion *vfork_done; int *set_child_tid; int *clear_child_tid; void *pf_io_worker; u64 utime; u64 stime; u64 gtime; struct prev_cputime prev_cputime; long unsigned int nvcsw; long unsigned int nivcsw; u64 start_time; u64 start_boottime; long unsigned int min_flt; long unsigned int maj_flt; struct posix_cputimers posix_cputimers; struct posix_cputimers_work posix_cputimers_work; const struct cred *ptracer_cred; const struct cred *real_cred; const struct cred *cred; struct key *cached_requested_key; char comm[16]; struct nameidata *nameidata; struct sysv_sem sysvsem; struct sysv_shm sysvshm; long unsigned int last_switch_count; long unsigned int last_switch_time; struct fs_struct *fs; struct files_struct *files; struct io_uring_task *io_uring; struct nsproxy *nsproxy; struct signal_struct *signal; struct sighand_struct *sighand; sigset_t blocked; sigset_t real_blocked; sigset_t saved_sigmask; struct sigpending pending; long unsigned int sas_ss_sp; size_t sas_ss_size; unsigned int sas_ss_flags; struct callback_head *task_works; struct audit_context *audit_context; kuid_t loginuid; unsigned int sessionid; struct seccomp seccomp; struct syscall_user_dispatch syscall_dispatch; u64 parent_exec_id; u64 self_exec_id; spinlock_t alloc_lock; raw_spinlock_t pi_lock; struct wake_q_node wake_q; struct rb_root_cached pi_waiters; struct task_struct *pi_top_task; struct rt_mutex_waiter *pi_blocked_on; void *journal_info; struct bio_list *bio_list; struct blk_plug *plug; struct reclaim_state *reclaim_state; struct backing_dev_info *backing_dev_info; struct io_context *io_context; struct capture_control *capture_control; long unsigned int ptrace_message; kernel_siginfo_t *last_siginfo; struct task_io_accounting ioac; unsigned int psi_flags; u64 acct_rss_mem1; u64 acct_vm_mem1; u64 acct_timexpd; nodemask_t mems_allowed; seqcount_spinlock_t mems_allowed_seq; int cpuset_mem_spread_rotor; int cpuset_slab_spread_rotor; struct css_set *cgroups; struct list_head cg_list; u32 closid; u32 rmid; struct robust_list_head *robust_list; struct compat_robust_list_head *compat_robust_list; struct list_head pi_state_list; struct futex_pi_state *pi_state_cache; struct mutex futex_exit_mutex; unsigned int futex_state; struct perf_event_context *perf_event_ctxp[2]; struct mutex perf_event_mutex; struct list_head perf_event_list; struct mempolicy *mempolicy; short int il_prev; short int pref_node_fork; int numa_scan_seq; unsigned int numa_scan_period; unsigned int numa_scan_period_max; int numa_preferred_nid; long unsigned int numa_migrate_retry; u64 node_stamp; u64 last_task_numa_placement; u64 last_sum_exec_runtime; struct callback_head numa_work; struct numa_group *numa_group; long unsigned int *numa_faults; long unsigned int total_numa_faults; long unsigned int numa_faults_locality[3]; long unsigned int numa_pages_migrated; struct rseq *rseq; u32 rseq_sig; long unsigned int rseq_event_mask; struct tlbflush_unmap_batch tlb_ubc; union { refcount_t rcu_users; struct callback_head rcu; }; struct pipe_inode_info *splice_pipe; struct page_frag task_frag; struct task_delay_info *delays; int nr_dirtied; int nr_dirtied_pause; long unsigned int dirty_paused_when; int latency_record_count; struct latency_record latency_record[32]; u64 timer_slack_ns; u64 default_timer_slack_ns; int curr_ret_stack; int curr_ret_depth; struct ftrace_ret_stack *ret_stack; long long unsigned int ftrace_timestamp; atomic_t trace_overrun; atomic_t tracing_graph_pause; long unsigned int trace; long unsigned int trace_recursion; struct mem_cgroup *memcg_in_oom; gfp_t memcg_oom_gfp_mask; int memcg_oom_order; unsigned int memcg_nr_pages_over_high; struct mem_cgroup *active_memcg; struct request_queue *throttle_queue; struct uprobe_task *utask; unsigned int sequential_io; unsigned int sequential_io_avg; struct kmap_ctrl kmap_ctrl; int pagefault_disabled; struct task_struct *oom_reaper_list; struct vm_struct *stack_vm_area; refcount_t stack_refcount; void *security; struct bpf_local_storage *bpf_storage; void *mce_vaddr; __u64 mce_kflags; u64 mce_addr; __u64 mce_ripv: 1; __u64 mce_whole_page: 1; __u64 __mce_reserved: 62; struct callback_head mce_kill_me; struct llist_head kretprobe_instances; long: 64; long: 64; long: 64; struct thread_struct thread; }; struct screen_info { __u8 orig_x; __u8 orig_y; __u16 ext_mem_k; __u16 orig_video_page; __u8 orig_video_mode; __u8 orig_video_cols; __u8 flags; __u8 unused2; __u16 orig_video_ega_bx; __u16 unused3; __u8 orig_video_lines; __u8 orig_video_isVGA; __u16 orig_video_points; __u16 lfb_width; __u16 lfb_height; __u16 lfb_depth; __u32 lfb_base; __u32 lfb_size; __u16 cl_magic; __u16 cl_offset; __u16 lfb_linelength; __u8 red_size; __u8 red_pos; __u8 green_size; __u8 green_pos; __u8 blue_size; __u8 blue_pos; __u8 rsvd_size; __u8 rsvd_pos; __u16 vesapm_seg; __u16 vesapm_off; __u16 pages; __u16 vesa_attributes; __u32 capabilities; __u32 ext_lfb_base; __u8 _reserved[2]; } __attribute__((packed)); struct apm_bios_info { __u16 version; __u16 cseg; __u32 offset; __u16 cseg_16; __u16 dseg; __u16 flags; __u16 cseg_len; __u16 cseg_16_len; __u16 dseg_len; }; struct edd_device_params { __u16 length; __u16 info_flags; __u32 num_default_cylinders; __u32 num_default_heads; __u32 sectors_per_track; __u64 number_of_sectors; __u16 bytes_per_sector; __u32 dpte_ptr; __u16 key; __u8 device_path_info_length; __u8 reserved2; __u16 reserved3; __u8 host_bus_type[4]; __u8 interface_type[8]; union { struct { __u16 base_address; __u16 reserved1; __u32 reserved2; } isa; struct { __u8 bus; __u8 slot; __u8 function; __u8 channel; __u32 reserved; } pci; struct { __u64 reserved; } ibnd; struct { __u64 reserved; } xprs; struct { __u64 reserved; } htpt; struct { __u64 reserved; } unknown; } interface_path; union { struct { __u8 device; __u8 reserved1; __u16 reserved2; __u32 reserved3; __u64 reserved4; } ata; struct { __u8 device; __u8 lun; __u8 reserved1; __u8 reserved2; __u32 reserved3; __u64 reserved4; } atapi; struct { __u16 id; __u64 lun; __u16 reserved1; __u32 reserved2; } __attribute__((packed)) scsi; struct { __u64 serial_number; __u64 reserved; } usb; struct { __u64 eui; __u64 reserved; } i1394; struct { __u64 wwid; __u64 lun; } fibre; struct { __u64 identity_tag; __u64 reserved; } i2o; struct { __u32 array_number; __u32 reserved1; __u64 reserved2; } raid; struct { __u8 device; __u8 reserved1; __u16 reserved2; __u32 reserved3; __u64 reserved4; } sata; struct { __u64 reserved1; __u64 reserved2; } unknown; } device_path; __u8 reserved4; __u8 checksum; } __attribute__((packed)); struct edd_info { __u8 device; __u8 version; __u16 interface_support; __u16 legacy_max_cylinder; __u8 legacy_max_head; __u8 legacy_sectors_per_track; struct edd_device_params params; } __attribute__((packed)); struct ist_info { __u32 signature; __u32 command; __u32 event; __u32 perf_level; }; struct edid_info { unsigned char dummy[128]; }; struct setup_header { __u8 setup_sects; __u16 root_flags; __u32 syssize; __u16 ram_size; __u16 vid_mode; __u16 root_dev; __u16 boot_flag; __u16 jump; __u32 header; __u16 version; __u32 realmode_swtch; __u16 start_sys_seg; __u16 kernel_version; __u8 type_of_loader; __u8 loadflags; __u16 setup_move_size; __u32 code32_start; __u32 ramdisk_image; __u32 ramdisk_size; __u32 bootsect_kludge; __u16 heap_end_ptr; __u8 ext_loader_ver; __u8 ext_loader_type; __u32 cmd_line_ptr; __u32 initrd_addr_max; __u32 kernel_alignment; __u8 relocatable_kernel; __u8 min_alignment; __u16 xloadflags; __u32 cmdline_size; __u32 hardware_subarch; __u64 hardware_subarch_data; __u32 payload_offset; __u32 payload_length; __u64 setup_data; __u64 pref_address; __u32 init_size; __u32 handover_offset; __u32 kernel_info_offset; } __attribute__((packed)); struct sys_desc_table { __u16 length; __u8 table[14]; }; struct olpc_ofw_header { __u32 ofw_magic; __u32 ofw_version; __u32 cif_handler; __u32 irq_desc_table; }; struct efi_info { __u32 efi_loader_signature; __u32 efi_systab; __u32 efi_memdesc_size; __u32 efi_memdesc_version; __u32 efi_memmap; __u32 efi_memmap_size; __u32 efi_systab_hi; __u32 efi_memmap_hi; }; struct boot_e820_entry { __u64 addr; __u64 size; __u32 type; } __attribute__((packed)); struct boot_params { struct screen_info screen_info; struct apm_bios_info apm_bios_info; __u8 _pad2[4]; __u64 tboot_addr; struct ist_info ist_info; __u64 acpi_rsdp_addr; __u8 _pad3[8]; __u8 hd0_info[16]; __u8 hd1_info[16]; struct sys_desc_table sys_desc_table; struct olpc_ofw_header olpc_ofw_header; __u32 ext_ramdisk_image; __u32 ext_ramdisk_size; __u32 ext_cmd_line_ptr; __u8 _pad4[116]; struct edid_info edid_info; struct efi_info efi_info; __u32 alt_mem_k; __u32 scratch; __u8 e820_entries; __u8 eddbuf_entries; __u8 edd_mbr_sig_buf_entries; __u8 kbd_status; __u8 secure_boot; __u8 _pad5[2]; __u8 sentinel; __u8 _pad6[1]; struct setup_header hdr; __u8 _pad7[36]; __u32 edd_mbr_sig_buffer[16]; struct boot_e820_entry e820_table[128]; __u8 _pad8[48]; struct edd_info eddbuf[6]; __u8 _pad9[276]; } __attribute__((packed)); enum x86_hardware_subarch { X86_SUBARCH_PC = 0, X86_SUBARCH_LGUEST = 1, X86_SUBARCH_XEN = 2, X86_SUBARCH_INTEL_MID = 3, X86_SUBARCH_CE4100 = 4, X86_NR_SUBARCHS = 5, }; struct range { u64 start; u64 end; }; struct pt_regs { long unsigned int r15; long unsigned int r14; long unsigned int r13; long unsigned int r12; long unsigned int bp; long unsigned int bx; long unsigned int r11; long unsigned int r10; long unsigned int r9; long unsigned int r8; long unsigned int ax; long unsigned int cx; long unsigned int dx; long unsigned int si; long unsigned int di; long unsigned int orig_ax; long unsigned int ip; long unsigned int cs; long unsigned int flags; long unsigned int sp; long unsigned int ss; }; enum { GATE_INTERRUPT = 14, GATE_TRAP = 15, GATE_CALL = 12, GATE_TASK = 5, }; struct idt_bits { u16 ist: 3; u16 zero: 5; u16 type: 5; u16 dpl: 2; u16 p: 1; }; struct idt_data { unsigned int vector; unsigned int segment; struct idt_bits bits; const void *addr; }; struct gate_struct { u16 offset_low; u16 segment; struct idt_bits bits; u16 offset_middle; u32 offset_high; u32 reserved; }; typedef struct gate_struct gate_desc; struct desc_ptr { short unsigned int size; long unsigned int address; } __attribute__((packed)); typedef long unsigned int pteval_t; typedef long unsigned int pmdval_t; typedef long unsigned int pudval_t; typedef long unsigned int p4dval_t; typedef long unsigned int pgdval_t; typedef long unsigned int pgprotval_t; typedef struct { pteval_t pte; } pte_t; struct pgprot { pgprotval_t pgprot; }; typedef struct pgprot pgprot_t; typedef struct { pgdval_t pgd; } pgd_t; typedef struct { p4dval_t p4d; } p4d_t; typedef struct { pudval_t pud; } pud_t; typedef struct { pmdval_t pmd; } pmd_t; typedef struct page *pgtable_t; struct address_space; struct page_pool; struct kmem_cache; struct dev_pagemap; struct page { long unsigned int flags; union { struct { struct list_head lru; struct address_space *mapping; long unsigned int index; long unsigned int private; }; struct { long unsigned int pp_magic; struct page_pool *pp; long unsigned int _pp_mapping_pad; long unsigned int dma_addr[2]; }; struct { union { struct list_head slab_list; struct { struct page *next; int pages; int pobjects; }; }; struct kmem_cache *slab_cache; void *freelist; union { void *s_mem; long unsigned int counters; struct { unsigned int inuse: 16; unsigned int objects: 15; unsigned int frozen: 1; }; }; }; struct { long unsigned int compound_head; unsigned char compound_dtor; unsigned char compound_order; atomic_t compound_mapcount; unsigned int compound_nr; }; struct { long unsigned int _compound_pad_1; atomic_t hpage_pinned_refcount; struct list_head deferred_list; }; struct { long unsigned int _pt_pad_1; pgtable_t pmd_huge_pte; long unsigned int _pt_pad_2; union { struct mm_struct *pt_mm; atomic_t pt_frag_refcount; }; spinlock_t ptl; }; struct { struct dev_pagemap *pgmap; void *zone_device_data; }; struct callback_head callback_head; }; union { atomic_t _mapcount; unsigned int page_type; unsigned int active; int units; }; atomic_t _refcount; long unsigned int memcg_data; }; struct paravirt_callee_save { void *func; }; struct pv_lazy_ops { void (*enter)(); void (*leave)(); void (*flush)(); }; struct pv_cpu_ops { void (*io_delay)(); long unsigned int (*get_debugreg)(int); void (*set_debugreg)(int, long unsigned int); long unsigned int (*read_cr0)(); void (*write_cr0)(long unsigned int); void (*write_cr4)(long unsigned int); void (*load_tr_desc)(); void (*load_gdt)(const struct desc_ptr *); void (*load_idt)(const struct desc_ptr *); void (*set_ldt)(const void *, unsigned int); long unsigned int (*store_tr)(); void (*load_tls)(struct thread_struct *, unsigned int); void (*load_gs_index)(unsigned int); void (*write_ldt_entry)(struct desc_struct *, int, const void *); void (*write_gdt_entry)(struct desc_struct *, int, const void *, int); void (*write_idt_entry)(gate_desc *, int, const gate_desc *); void (*alloc_ldt)(struct desc_struct *, unsigned int); void (*free_ldt)(struct desc_struct *, unsigned int); void (*load_sp0)(long unsigned int); void (*invalidate_io_bitmap)(); void (*update_io_bitmap)(); void (*wbinvd)(); void (*cpuid)(unsigned int *, unsigned int *, unsigned int *, unsigned int *); u64 (*read_msr)(unsigned int); void (*write_msr)(unsigned int, unsigned int, unsigned int); u64 (*read_msr_safe)(unsigned int, int *); int (*write_msr_safe)(unsigned int, unsigned int, unsigned int); u64 (*read_pmc)(int); void (*start_context_switch)(struct task_struct *); void (*end_context_switch)(struct task_struct *); }; struct pv_irq_ops { struct paravirt_callee_save save_fl; struct paravirt_callee_save irq_disable; struct paravirt_callee_save irq_enable; void (*safe_halt)(); void (*halt)(); }; struct flush_tlb_info; struct mmu_gather; struct pv_mmu_ops { void (*flush_tlb_user)(); void (*flush_tlb_kernel)(); void (*flush_tlb_one_user)(long unsigned int); void (*flush_tlb_multi)(const struct cpumask *, const struct flush_tlb_info *); void (*tlb_remove_table)(struct mmu_gather *, void *); void (*exit_mmap)(struct mm_struct *); struct paravirt_callee_save read_cr2; void (*write_cr2)(long unsigned int); long unsigned int (*read_cr3)(); void (*write_cr3)(long unsigned int); void (*activate_mm)(struct mm_struct *, struct mm_struct *); void (*dup_mmap)(struct mm_struct *, struct mm_struct *); int (*pgd_alloc)(struct mm_struct *); void (*pgd_free)(struct mm_struct *, pgd_t *); void (*alloc_pte)(struct mm_struct *, long unsigned int); void (*alloc_pmd)(struct mm_struct *, long unsigned int); void (*alloc_pud)(struct mm_struct *, long unsigned int); void (*alloc_p4d)(struct mm_struct *, long unsigned int); void (*release_pte)(long unsigned int); void (*release_pmd)(long unsigned int); void (*release_pud)(long unsigned int); void (*release_p4d)(long unsigned int); void (*set_pte)(pte_t *, pte_t); void (*set_pmd)(pmd_t *, pmd_t); pte_t (*ptep_modify_prot_start)(struct vm_area_struct *, long unsigned int, pte_t *); void (*ptep_modify_prot_commit)(struct vm_area_struct *, long unsigned int, pte_t *, pte_t); struct paravirt_callee_save pte_val; struct paravirt_callee_save make_pte; struct paravirt_callee_save pgd_val; struct paravirt_callee_save make_pgd; void (*set_pud)(pud_t *, pud_t); struct paravirt_callee_save pmd_val; struct paravirt_callee_save make_pmd; struct paravirt_callee_save pud_val; struct paravirt_callee_save make_pud; void (*set_p4d)(p4d_t *, p4d_t); struct paravirt_callee_save p4d_val; struct paravirt_callee_save make_p4d; void (*set_pgd)(pgd_t *, pgd_t); struct pv_lazy_ops lazy_mode; void (*set_fixmap)(unsigned int, phys_addr_t, pgprot_t); }; struct flush_tlb_info { struct mm_struct *mm; long unsigned int start; long unsigned int end; u64 new_tlb_gen; unsigned int initiating_cpu; u8 stride_shift; u8 freed_tables; }; struct rw_semaphore { atomic_long_t count; atomic_long_t owner; struct optimistic_spin_queue osq; raw_spinlock_t wait_lock; struct list_head wait_list; }; struct mm_rss_stat { atomic_long_t count[4]; }; struct ldt_struct; struct vdso_image; typedef struct { u64 ctx_id; atomic64_t tlb_gen; struct rw_semaphore ldt_usr_sem; struct ldt_struct *ldt; short unsigned int flags; struct mutex lock; void *vdso; const struct vdso_image *vdso_image; atomic_t perf_rdpmc_allowed; u16 pkey_allocation_map; s16 execute_only_pkey; } mm_context_t; struct xol_area; struct uprobes_state { struct xol_area *xol_area; }; struct work_struct; typedef void (*work_func_t)(struct work_struct *); struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; }; struct linux_binfmt; struct core_state; struct kioctx_table; struct user_namespace; struct mmu_notifier_subscriptions; struct mm_struct { struct { struct vm_area_struct *mmap; struct rb_root mm_rb; u64 vmacache_seqnum; long unsigned int (*get_unmapped_area)(struct file *, long unsigned int, long unsigned int, long unsigned int, long unsigned int); long unsigned int mmap_base; long unsigned int mmap_legacy_base; long unsigned int mmap_compat_base; long unsigned int mmap_compat_legacy_base; long unsigned int task_size; long unsigned int highest_vm_end; pgd_t *pgd; atomic_t membarrier_state; atomic_t mm_users; atomic_t mm_count; atomic_long_t pgtables_bytes; int map_count; spinlock_t page_table_lock; struct rw_semaphore mmap_lock; struct list_head mmlist; long unsigned int hiwater_rss; long unsigned int hiwater_vm; long unsigned int total_vm; long unsigned int locked_vm; atomic64_t pinned_vm; long unsigned int data_vm; long unsigned int exec_vm; long unsigned int stack_vm; long unsigned int def_flags; seqcount_t write_protect_seq; spinlock_t arg_lock; long unsigned int start_code; long unsigned int end_code; long unsigned int start_data; long unsigned int end_data; long unsigned int start_brk; long unsigned int brk; long unsigned int start_stack; long unsigned int arg_start; long unsigned int arg_end; long unsigned int env_start; long unsigned int env_end; long unsigned int saved_auxv[48]; struct mm_rss_stat rss_stat; struct linux_binfmt *binfmt; mm_context_t context; long unsigned int flags; struct core_state *core_state; spinlock_t ioctx_lock; struct kioctx_table *ioctx_table; struct task_struct *owner; struct user_namespace *user_ns; struct file *exe_file; struct mmu_notifier_subscriptions *notifier_subscriptions; long unsigned int numa_next_scan; long unsigned int numa_scan_offset; int numa_scan_seq; atomic_t tlb_flush_pending; bool tlb_flush_batched; struct uprobes_state uprobes_state; atomic_long_t hugetlb_usage; struct work_struct async_put_work; u32 pasid; }; long unsigned int cpu_bitmap[0]; }; struct userfaultfd_ctx; struct vm_userfaultfd_ctx { struct userfaultfd_ctx *ctx; }; struct anon_vma; struct vm_operations_struct; struct vm_area_struct { long unsigned int vm_start; long unsigned int vm_end; struct vm_area_struct *vm_next; struct vm_area_struct *vm_prev; struct rb_node vm_rb; long unsigned int rb_subtree_gap; struct mm_struct *vm_mm; pgprot_t vm_page_prot; long unsigned int vm_flags; struct { struct rb_node rb; long unsigned int rb_subtree_last; } shared; struct list_head anon_vma_chain; struct anon_vma *anon_vma; const struct vm_operations_struct *vm_ops; long unsigned int vm_pgoff; struct file *vm_file; void *vm_private_data; atomic_long_t swap_readahead_info; struct mempolicy *vm_policy; struct vm_userfaultfd_ctx vm_userfaultfd_ctx; }; struct pv_lock_ops { void (*queued_spin_lock_slowpath)(struct qspinlock *, u32); struct paravirt_callee_save queued_spin_unlock; void (*wait)(u8 *, u8); void (*kick)(int); struct paravirt_callee_save vcpu_is_preempted; }; struct paravirt_patch_template { struct pv_cpu_ops cpu; struct pv_irq_ops irq; struct pv_mmu_ops mmu; struct pv_lock_ops lock; }; struct math_emu_info { long int ___orig_eip; struct pt_regs *regs; }; enum { UNAME26 = 131072, ADDR_NO_RANDOMIZE = 262144, FDPIC_FUNCPTRS = 524288, MMAP_PAGE_ZERO = 1048576, ADDR_COMPAT_LAYOUT = 2097152, READ_IMPLIES_EXEC = 4194304, ADDR_LIMIT_32BIT = 8388608, SHORT_INODE = 16777216, WHOLE_SECONDS = 33554432, STICKY_TIMEOUTS = 67108864, ADDR_LIMIT_3GB = 134217728, }; enum tlb_infos { ENTRIES = 0, NR_INFO = 1, }; enum pcpu_fc { PCPU_FC_AUTO = 0, PCPU_FC_EMBED = 1, PCPU_FC_PAGE = 2, PCPU_FC_NR = 3, }; struct vm_struct { struct vm_struct *next; void *addr; long unsigned int size; long unsigned int flags; struct page **pages; unsigned int nr_pages; phys_addr_t phys_addr; const void *caller; }; struct wait_queue_head { spinlock_t lock; struct list_head head; }; typedef struct wait_queue_head wait_queue_head_t; struct seqcount_raw_spinlock { seqcount_t seqcount; }; typedef struct seqcount_raw_spinlock seqcount_raw_spinlock_t; typedef struct { seqcount_spinlock_t seqcount; spinlock_t lock; } seqlock_t; enum node_states { N_POSSIBLE = 0, N_ONLINE = 1, N_NORMAL_MEMORY = 2, N_HIGH_MEMORY = 2, N_MEMORY = 3, N_CPU = 4, N_GENERIC_INITIATOR = 5, NR_NODE_STATES = 6, }; enum { MM_FILEPAGES = 0, MM_ANONPAGES = 1, MM_SWAPENTS = 2, MM_SHMEMPAGES = 3, NR_MM_COUNTERS = 4, }; struct swait_queue_head { raw_spinlock_t lock; struct list_head task_list; }; struct completion { unsigned int done; struct swait_queue_head wait; }; struct arch_uprobe_task { long unsigned int saved_scratch_register; unsigned int saved_trap_nr; unsigned int saved_tf; }; enum uprobe_task_state { UTASK_RUNNING = 0, UTASK_SSTEP = 1, UTASK_SSTEP_ACK = 2, UTASK_SSTEP_TRAPPED = 3, }; struct uprobe; struct return_instance; struct uprobe_task { enum uprobe_task_state state; union { struct { struct arch_uprobe_task autask; long unsigned int vaddr; }; struct { struct callback_head dup_xol_work; long unsigned int dup_xol_addr; }; }; struct uprobe *active_uprobe; long unsigned int xol_vaddr; struct return_instance *return_instances; unsigned int depth; }; struct return_instance { struct uprobe *uprobe; long unsigned int func; long unsigned int stack; long unsigned int orig_ret_vaddr; bool chained; struct return_instance *next; }; struct vdso_image { void *data; long unsigned int size; long unsigned int alt; long unsigned int alt_len; long unsigned int extable_base; long unsigned int extable_len; const void *extable; long int sym_vvar_start; long int sym_vvar_page; long int sym_pvclock_page; long int sym_hvclock_page; long int sym_timens_page; long int sym_VDSO32_NOTE_MASK; long int sym___kernel_sigreturn; long int sym___kernel_rt_sigreturn; long int sym___kernel_vsyscall; long int sym_int80_landing_pad; long int sym_vdso32_sigreturn_landing_pad; long int sym_vdso32_rt_sigreturn_landing_pad; }; struct xarray { spinlock_t xa_lock; gfp_t xa_flags; void *xa_head; }; typedef u32 errseq_t; struct address_space_operations; struct address_space { struct inode *host; struct xarray i_pages; gfp_t gfp_mask; atomic_t i_mmap_writable; atomic_t nr_thps; struct rb_root_cached i_mmap; struct rw_semaphore i_mmap_rwsem; long unsigned int nrpages; long unsigned int writeback_index; const struct address_space_operations *a_ops; long unsigned int flags; errseq_t wb_err; spinlock_t private_lock; struct list_head private_list; void *private_data; }; struct vmem_altmap { long unsigned int base_pfn; const long unsigned int end_pfn; const long unsigned int reserve; long unsigned int free; long unsigned int align; long unsigned int alloc; }; struct percpu_ref_data; struct percpu_ref { long unsigned int percpu_count_ptr; struct percpu_ref_data *data; }; enum memory_type { MEMORY_DEVICE_PRIVATE = 1, MEMORY_DEVICE_FS_DAX = 2, MEMORY_DEVICE_GENERIC = 3, MEMORY_DEVICE_PCI_P2PDMA = 4, }; struct dev_pagemap_ops; struct dev_pagemap { struct vmem_altmap altmap; struct percpu_ref *ref; struct percpu_ref internal_ref; struct completion done; enum memory_type type; unsigned int flags; const struct dev_pagemap_ops *ops; void *owner; int nr_range; union { struct range range; struct range ranges[0]; }; }; struct vfsmount; struct path { struct vfsmount *mnt; struct dentry *dentry; }; enum rw_hint { WRITE_LIFE_NOT_SET = 0, WRITE_LIFE_NONE = 1, WRITE_LIFE_SHORT = 2, WRITE_LIFE_MEDIUM = 3, WRITE_LIFE_LONG = 4, WRITE_LIFE_EXTREME = 5, }; enum pid_type { PIDTYPE_PID = 0, PIDTYPE_TGID = 1, PIDTYPE_PGID = 2, PIDTYPE_SID = 3, PIDTYPE_MAX = 4, }; struct fown_struct { rwlock_t lock; struct pid *pid; enum pid_type pid_type; kuid_t uid; kuid_t euid; int signum; }; struct file_ra_state { long unsigned int start; unsigned int size; unsigned int async_size; unsigned int ra_pages; unsigned int mmap_miss; loff_t prev_pos; }; struct file { union { struct llist_node fu_llist; struct callback_head fu_rcuhead; } f_u; struct path f_path; struct inode *f_inode; const struct file_operations *f_op; spinlock_t f_lock; enum rw_hint f_write_hint; atomic_long_t f_count; unsigned int f_flags; fmode_t f_mode; struct mutex f_pos_lock; loff_t f_pos; struct fown_struct f_owner; const struct cred *f_cred; struct file_ra_state f_ra; u64 f_version; void *f_security; void *private_data; struct hlist_head *f_ep; struct address_space *f_mapping; errseq_t f_wb_err; errseq_t f_sb_err; }; typedef unsigned int vm_fault_t; enum page_entry_size { PE_SIZE_PTE = 0, PE_SIZE_PMD = 1, PE_SIZE_PUD = 2, }; struct vm_fault; struct vm_operations_struct { void (*open)(struct vm_area_struct *); void (*close)(struct vm_area_struct *); int (*may_split)(struct vm_area_struct *, long unsigned int); int (*mremap)(struct vm_area_struct *); int (*mprotect)(struct vm_area_struct *, long unsigned int, long unsigned int, long unsigned int); vm_fault_t (*fault)(struct vm_fault *); vm_fault_t (*huge_fault)(struct vm_fault *, enum page_entry_size); vm_fault_t (*map_pages)(struct vm_fault *, long unsigned int, long unsigned int); long unsigned int (*pagesize)(struct vm_area_struct *); vm_fault_t (*page_mkwrite)(struct vm_fault *); vm_fault_t (*pfn_mkwrite)(struct vm_fault *); int (*access)(struct vm_area_struct *, long unsigned int, void *, int, int); const char * (*name)(struct vm_area_struct *); int (*set_policy)(struct vm_area_struct *, struct mempolicy *); struct mempolicy * (*get_policy)(struct vm_area_struct *, long unsigned int); struct page * (*find_special_page)(struct vm_area_struct *, long unsigned int); }; struct core_thread { struct task_struct *task; struct core_thread *next; }; struct core_state { atomic_t nr_threads; struct core_thread dumper; struct completion startup; }; enum fault_flag { FAULT_FLAG_WRITE = 1, FAULT_FLAG_MKWRITE = 2, FAULT_FLAG_ALLOW_RETRY = 4, FAULT_FLAG_RETRY_NOWAIT = 8, FAULT_FLAG_KILLABLE = 16, FAULT_FLAG_TRIED = 32, FAULT_FLAG_USER = 64, FAULT_FLAG_REMOTE = 128, FAULT_FLAG_INSTRUCTION = 256, FAULT_FLAG_INTERRUPTIBLE = 512, }; struct vm_fault { const struct { struct vm_area_struct *vma; gfp_t gfp_mask; long unsigned int pgoff; long unsigned int address; }; enum fault_flag flags; pmd_t *pmd; pud_t *pud; union { pte_t orig_pte; pmd_t orig_pmd; }; struct page *cow_page; struct page *page; pte_t *pte; spinlock_t *ptl; pgtable_t prealloc_pte; }; enum migratetype { MIGRATE_UNMOVABLE = 0, MIGRATE_MOVABLE = 1, MIGRATE_RECLAIMABLE = 2, MIGRATE_PCPTYPES = 3, MIGRATE_HIGHATOMIC = 3, MIGRATE_CMA = 4, MIGRATE_ISOLATE = 5, MIGRATE_TYPES = 6, }; enum numa_stat_item { NUMA_HIT = 0, NUMA_MISS = 1, NUMA_FOREIGN = 2, NUMA_INTERLEAVE_HIT = 3, NUMA_LOCAL = 4, NUMA_OTHER = 5, NR_VM_NUMA_EVENT_ITEMS = 6, }; enum zone_stat_item { NR_FREE_PAGES = 0, NR_ZONE_LRU_BASE = 1, NR_ZONE_INACTIVE_ANON = 1, NR_ZONE_ACTIVE_ANON = 2, NR_ZONE_INACTIVE_FILE = 3, NR_ZONE_ACTIVE_FILE = 4, NR_ZONE_UNEVICTABLE = 5, NR_ZONE_WRITE_PENDING = 6, NR_MLOCK = 7, NR_BOUNCE = 8, NR_ZSPAGES = 9, NR_FREE_CMA_PAGES = 10, NR_VM_ZONE_STAT_ITEMS = 11, }; enum node_stat_item { NR_LRU_BASE = 0, NR_INACTIVE_ANON = 0, NR_ACTIVE_ANON = 1, NR_INACTIVE_FILE = 2, NR_ACTIVE_FILE = 3, NR_UNEVICTABLE = 4, NR_SLAB_RECLAIMABLE_B = 5, NR_SLAB_UNRECLAIMABLE_B = 6, NR_ISOLATED_ANON = 7, NR_ISOLATED_FILE = 8, WORKINGSET_NODES = 9, WORKINGSET_REFAULT_BASE = 10, WORKINGSET_REFAULT_ANON = 10, WORKINGSET_REFAULT_FILE = 11, WORKINGSET_ACTIVATE_BASE = 12, WORKINGSET_ACTIVATE_ANON = 12, WORKINGSET_ACTIVATE_FILE = 13, WORKINGSET_RESTORE_BASE = 14, WORKINGSET_RESTORE_ANON = 14, WORKINGSET_RESTORE_FILE = 15, WORKINGSET_NODERECLAIM = 16, NR_ANON_MAPPED = 17, NR_FILE_MAPPED = 18, NR_FILE_PAGES = 19, NR_FILE_DIRTY = 20, NR_WRITEBACK = 21, NR_WRITEBACK_TEMP = 22, NR_SHMEM = 23, NR_SHMEM_THPS = 24, NR_SHMEM_PMDMAPPED = 25, NR_FILE_THPS = 26, NR_FILE_PMDMAPPED = 27, NR_ANON_THPS = 28, NR_VMSCAN_WRITE = 29, NR_VMSCAN_IMMEDIATE = 30, NR_DIRTIED = 31, NR_WRITTEN = 32, NR_KERNEL_MISC_RECLAIMABLE = 33, NR_FOLL_PIN_ACQUIRED = 34, NR_FOLL_PIN_RELEASED = 35, NR_KERNEL_STACK_KB = 36, NR_PAGETABLE = 37, NR_SWAPCACHE = 38, NR_VM_NODE_STAT_ITEMS = 39, }; enum lru_list { LRU_INACTIVE_ANON = 0, LRU_ACTIVE_ANON = 1, LRU_INACTIVE_FILE = 2, LRU_ACTIVE_FILE = 3, LRU_UNEVICTABLE = 4, NR_LRU_LISTS = 5, }; typedef unsigned int isolate_mode_t; enum zone_watermarks { WMARK_MIN = 0, WMARK_LOW = 1, WMARK_HIGH = 2, NR_WMARK = 3, }; enum { ZONELIST_FALLBACK = 0, ZONELIST_NOFALLBACK = 1, MAX_ZONELISTS = 2, }; typedef void percpu_ref_func_t(struct percpu_ref *); struct percpu_ref_data { atomic_long_t count; percpu_ref_func_t *release; percpu_ref_func_t *confirm_switch; bool force_atomic: 1; bool allow_reinit: 1; struct callback_head rcu; struct percpu_ref *ref; }; struct shrink_control { gfp_t gfp_mask; int nid; long unsigned int nr_to_scan; long unsigned int nr_scanned; struct mem_cgroup *memcg; }; struct shrinker { long unsigned int (*count_objects)(struct shrinker *, struct shrink_control *); long unsigned int (*scan_objects)(struct shrinker *, struct shrink_control *); long int batch; int seeks; unsigned int flags; struct list_head list; int id; atomic_long_t *nr_deferred; }; struct rlimit { __kernel_ulong_t rlim_cur; __kernel_ulong_t rlim_max; }; struct dev_pagemap_ops { void (*page_free)(struct page *); void (*kill)(struct dev_pagemap *); void (*cleanup)(struct dev_pagemap *); vm_fault_t (*migrate_to_ram)(struct vm_fault *); }; struct pid_namespace; struct upid { int nr; struct pid_namespace *ns; }; struct pid { refcount_t count; unsigned int level; spinlock_t lock; struct hlist_head tasks[4]; struct hlist_head inodes; wait_queue_head_t wait_pidfd; struct callback_head rcu; struct upid numbers[1]; }; typedef struct { gid_t val; } kgid_t; struct hrtimer_cpu_base; struct hrtimer_clock_base { struct hrtimer_cpu_base *cpu_base; unsigned int index; clockid_t clockid; seqcount_raw_spinlock_t seq; struct hrtimer *running; struct timerqueue_head active; ktime_t (*get_time)(); ktime_t offset; }; struct hrtimer_cpu_base { raw_spinlock_t lock; unsigned int cpu; unsigned int active_bases; unsigned int clock_was_set_seq; unsigned int hres_active: 1; unsigned int in_hrtirq: 1; unsigned int hang_detected: 1; unsigned int softirq_activated: 1; unsigned int nr_events; short unsigned int nr_retries; short unsigned int nr_hangs; unsigned int max_hang_time; ktime_t expires_next; struct hrtimer *next_timer; ktime_t softirq_expires_next; struct hrtimer *softirq_next_timer; struct hrtimer_clock_base clock_base[8]; }; enum hrtimer_base_type { HRTIMER_BASE_MONOTONIC = 0, HRTIMER_BASE_REALTIME = 1, HRTIMER_BASE_BOOTTIME = 2, HRTIMER_BASE_TAI = 3, HRTIMER_BASE_MONOTONIC_SOFT = 4, HRTIMER_BASE_REALTIME_SOFT = 5, HRTIMER_BASE_BOOTTIME_SOFT = 6, HRTIMER_BASE_TAI_SOFT = 7, HRTIMER_MAX_CLOCK_BASES = 8, }; typedef void __signalfn_t(int); typedef __signalfn_t *__sighandler_t; typedef void __restorefn_t(); typedef __restorefn_t *__sigrestore_t; union sigval { int sival_int; void *sival_ptr; }; typedef union sigval sigval_t; union __sifields { struct { __kernel_pid_t _pid; __kernel_uid32_t _uid; } _kill; struct { __kernel_timer_t _tid; int _overrun; sigval_t _sigval; int _sys_private; } _timer; struct { __kernel_pid_t _pid; __kernel_uid32_t _uid; sigval_t _sigval; } _rt; struct { __kernel_pid_t _pid; __kernel_uid32_t _uid; int _status; __kernel_clock_t _utime; __kernel_clock_t _stime; } _sigchld; struct { void *_addr; union { int _trapno; short int _addr_lsb; struct { char _dummy_bnd[8]; void *_lower; void *_upper; } _addr_bnd; struct { char _dummy_pkey[8]; __u32 _pkey; } _addr_pkey; struct { long unsigned int _data; __u32 _type; } _perf; }; } _sigfault; struct { long int _band; int _fd; } _sigpoll; struct { void *_call_addr; int _syscall; unsigned int _arch; } _sigsys; }; struct kernel_siginfo { struct { int si_signo; int si_errno; int si_code; union __sifields _sifields; }; }; struct sigaction { __sighandler_t sa_handler; long unsigned int sa_flags; __sigrestore_t sa_restorer; sigset_t sa_mask; }; struct k_sigaction { struct sigaction sa; }; struct cpu_itimer { u64 expires; u64 incr; }; struct task_cputime_atomic { atomic64_t utime; atomic64_t stime; atomic64_t sum_exec_runtime; }; struct thread_group_cputimer { struct task_cputime_atomic cputime_atomic; }; struct pacct_struct { int ac_flag; long int ac_exitcode; long unsigned int ac_mem; u64 ac_utime; u64 ac_stime; long unsigned int ac_minflt; long unsigned int ac_majflt; }; struct tty_struct; struct autogroup; struct taskstats; struct tty_audit_buf; struct signal_struct { refcount_t sigcnt; atomic_t live; int nr_threads; struct list_head thread_head; wait_queue_head_t wait_chldexit; struct task_struct *curr_target; struct sigpending shared_pending; struct hlist_head multiprocess; int group_exit_code; int notify_count; struct task_struct *group_exit_task; int group_stop_count; unsigned int flags; unsigned int is_child_subreaper: 1; unsigned int has_child_subreaper: 1; int posix_timer_id; struct list_head posix_timers; struct hrtimer real_timer; ktime_t it_real_incr; struct cpu_itimer it[2]; struct thread_group_cputimer cputimer; struct posix_cputimers posix_cputimers; struct pid *pids[4]; struct pid *tty_old_pgrp; int leader; struct tty_struct *tty; struct autogroup *autogroup; seqlock_t stats_lock; u64 utime; u64 stime; u64 cutime; u64 cstime; u64 gtime; u64 cgtime; struct prev_cputime prev_cputime; long unsigned int nvcsw; long unsigned int nivcsw; long unsigned int cnvcsw; long unsigned int cnivcsw; long unsigned int min_flt; long unsigned int maj_flt; long unsigned int cmin_flt; long unsigned int cmaj_flt; long unsigned int inblock; long unsigned int oublock; long unsigned int cinblock; long unsigned int coublock; long unsigned int maxrss; long unsigned int cmaxrss; struct task_io_accounting ioac; long long unsigned int sum_sched_runtime; struct rlimit rlim[16]; struct pacct_struct pacct; struct taskstats *stats; unsigned int audit_tty; struct tty_audit_buf *tty_audit_buf; bool oom_flag_origin; short int oom_score_adj; short int oom_score_adj_min; struct mm_struct *oom_mm; struct mutex cred_guard_mutex; struct rw_semaphore exec_update_lock; }; enum rseq_cs_flags_bit { RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT_BIT = 0, RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL_BIT = 1, RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT = 2, }; struct rseq { __u32 cpu_id_start; __u32 cpu_id; union { __u64 ptr64; __u64 ptr; } rseq_cs; __u32 flags; long: 32; long: 64; }; enum uclamp_id { UCLAMP_MIN = 0, UCLAMP_MAX = 1, UCLAMP_CNT = 2, }; enum perf_event_task_context { perf_invalid_context = 4294967295, perf_hw_context = 0, perf_sw_context = 1, perf_nr_task_contexts = 2, }; struct rq; struct rq_flags; struct sched_class { int uclamp_enabled; void (*enqueue_task)(struct rq *, struct task_struct *, int); void (*dequeue_task)(struct rq *, struct task_struct *, int); void (*yield_task)(struct rq *); bool (*yield_to_task)(struct rq *, struct task_struct *); void (*check_preempt_curr)(struct rq *, struct task_struct *, int); struct task_struct * (*pick_next_task)(struct rq *); void (*put_prev_task)(struct rq *, struct task_struct *); void (*set_next_task)(struct rq *, struct task_struct *, bool); int (*balance)(struct rq *, struct task_struct *, struct rq_flags *); int (*select_task_rq)(struct task_struct *, int, int); struct task_struct * (*pick_task)(struct rq *); void (*migrate_task_rq)(struct task_struct *, int); void (*task_woken)(struct rq *, struct task_struct *); void (*set_cpus_allowed)(struct task_struct *, const struct cpumask *, u32); void (*rq_online)(struct rq *); void (*rq_offline)(struct rq *); struct rq * (*find_lock_rq)(struct task_struct *, struct rq *); void (*task_tick)(struct rq *, struct task_struct *, int); void (*task_fork)(struct task_struct *); void (*task_dead)(struct task_struct *); void (*switched_from)(struct rq *, struct task_struct *); void (*switched_to)(struct rq *, struct task_struct *); void (*prio_changed)(struct rq *, struct task_struct *, int); unsigned int (*get_rr_interval)(struct rq *, struct task_struct *); void (*update_curr)(struct rq *); void (*task_change_group)(struct task_struct *, int); }; struct kernel_cap_struct { __u32 cap[2]; }; typedef struct kernel_cap_struct kernel_cap_t; struct user_struct; struct ucounts; struct group_info; struct cred { atomic_t usage; kuid_t uid; kgid_t gid; kuid_t suid; kgid_t sgid; kuid_t euid; kgid_t egid; kuid_t fsuid; kgid_t fsgid; unsigned int securebits; kernel_cap_t cap_inheritable; kernel_cap_t cap_permitted; kernel_cap_t cap_effective; kernel_cap_t cap_bset; kernel_cap_t cap_ambient; unsigned char jit_keyring; struct key *session_keyring; struct key *process_keyring; struct key *thread_keyring; struct key *request_key_auth; void *security; struct user_struct *user; struct user_namespace *user_ns; struct ucounts *ucounts; struct group_info *group_info; union { int non_rcu; struct callback_head rcu; }; }; typedef int32_t key_serial_t; typedef uint32_t key_perm_t; struct key_type; struct key_tag; struct keyring_index_key { long unsigned int hash; union { struct { u16 desc_len; char desc[6]; }; long unsigned int x; }; struct key_type *type; struct key_tag *domain_tag; const char *description; }; union key_payload { void *rcu_data0; void *data[4]; }; struct assoc_array_ptr; struct assoc_array { struct assoc_array_ptr *root; long unsigned int nr_leaves_on_tree; }; struct watch_list; struct key_user; struct key_restriction; struct key { refcount_t usage; key_serial_t serial; union { struct list_head graveyard_link; struct rb_node serial_node; }; struct watch_list *watchers; struct rw_semaphore sem; struct key_user *user; void *security; union { time64_t expiry; time64_t revoked_at; }; time64_t last_used_at; kuid_t uid; kgid_t gid; key_perm_t perm; short unsigned int quotalen; short unsigned int datalen; short int state; long unsigned int flags; union { struct keyring_index_key index_key; struct { long unsigned int hash; long unsigned int len_desc; struct key_type *type; struct key_tag *domain_tag; char *description; }; }; union { union key_payload payload; struct { struct list_head name_link; struct assoc_array keys; }; }; struct key_restriction *restrict_link; }; struct sighand_struct { spinlock_t siglock; refcount_t count; wait_queue_head_t signalfd_wqh; struct k_sigaction action[64]; }; struct io_cq; struct io_context { atomic_long_t refcount; atomic_t active_ref; atomic_t nr_tasks; spinlock_t lock; short unsigned int ioprio; struct xarray icq_tree; struct io_cq *icq_hint; struct hlist_head icq_list; struct work_struct release_work; }; enum rseq_event_mask_bits { RSEQ_EVENT_PREEMPT_BIT = 0, RSEQ_EVENT_SIGNAL_BIT = 1, RSEQ_EVENT_MIGRATE_BIT = 2, }; enum fixed_addresses { VSYSCALL_PAGE = 511, FIX_DBGP_BASE = 512, FIX_EARLYCON_MEM_BASE = 513, FIX_APIC_BASE = 514, FIX_IO_APIC_BASE_0 = 515, FIX_IO_APIC_BASE_END = 642, FIX_PARAVIRT_BOOTMAP = 643, FIX_APEI_GHES_IRQ = 644, FIX_APEI_GHES_NMI = 645, __end_of_permanent_fixed_addresses = 646, FIX_BTMAP_END = 1024, FIX_BTMAP_BEGIN = 1535, __end_of_fixed_addresses = 1536, }; struct hlist_bl_node; struct hlist_bl_head { struct hlist_bl_node *first; }; struct hlist_bl_node { struct hlist_bl_node *next; struct hlist_bl_node **pprev; }; struct lockref { union { __u64 lock_count; struct { spinlock_t lock; int count; }; }; }; struct qstr { union { struct { u32 hash; u32 len; }; u64 hash_len; }; const unsigned char *name; }; struct dentry_operations; struct dentry { unsigned int d_flags; seqcount_spinlock_t d_seq; struct hlist_bl_node d_hash; struct dentry *d_parent; struct qstr d_name; struct inode *d_inode; unsigned char d_iname[32]; struct lockref d_lockref; const struct dentry_operations *d_op; struct super_block *d_sb; long unsigned int d_time; void *d_fsdata; union { struct list_head d_lru; wait_queue_head_t *d_wait; }; struct list_head d_child; struct list_head d_subdirs; union { struct hlist_node d_alias; struct hlist_bl_node d_in_lookup_hash; struct callback_head d_rcu; } d_u; }; struct posix_acl; struct inode_operations; struct bdi_writeback; struct file_lock_context; struct cdev; struct fsnotify_mark_connector; struct fscrypt_info; struct fsverity_info; struct inode { umode_t i_mode; short unsigned int i_opflags; kuid_t i_uid; kgid_t i_gid; unsigned int i_flags; struct posix_acl *i_acl; struct posix_acl *i_default_acl; const struct inode_operations *i_op; struct super_block *i_sb; struct address_space *i_mapping; void *i_security; long unsigned int i_ino; union { const unsigned int i_nlink; unsigned int __i_nlink; }; dev_t i_rdev; loff_t i_size; struct timespec64 i_atime; struct timespec64 i_mtime; struct timespec64 i_ctime; spinlock_t i_lock; short unsigned int i_bytes; u8 i_blkbits; u8 i_write_hint; blkcnt_t i_blocks; long unsigned int i_state; struct rw_semaphore i_rwsem; long unsigned int dirtied_when; long unsigned int dirtied_time_when; struct hlist_node i_hash; struct list_head i_io_list; struct bdi_writeback *i_wb; int i_wb_frn_winner; u16 i_wb_frn_avg_time; u16 i_wb_frn_history; struct list_head i_lru; struct list_head i_sb_list; struct list_head i_wb_list; union { struct hlist_head i_dentry; struct callback_head i_rcu; }; atomic64_t i_version; atomic64_t i_sequence; atomic_t i_count; atomic_t i_dio_count; atomic_t i_writecount; atomic_t i_readcount; union { const struct file_operations *i_fop; void (*free_inode)(struct inode *); }; struct file_lock_context *i_flctx; struct address_space i_data; struct list_head i_devices; union { struct pipe_inode_info *i_pipe; struct cdev *i_cdev; char *i_link; unsigned int i_dir_seq; }; __u32 i_generation; __u32 i_fsnotify_mask; struct fsnotify_mark_connector *i_fsnotify_marks; struct fscrypt_info *i_crypt_info; struct fsverity_info *i_verity_info; void *i_private; }; struct dentry_operations { int (*d_revalidate)(struct dentry *, unsigned int); int (*d_weak_revalidate)(struct dentry *, unsigned int); int (*d_hash)(const struct dentry *, struct qstr *); int (*d_compare)(const struct dentry *, unsigned int, const char *, const struct qstr *); int (*d_delete)(const struct dentry *); int (*d_init)(struct dentry *); void (*d_release)(struct dentry *); void (*d_prune)(struct dentry *); void (*d_iput)(struct dentry *, struct inode *); char * (*d_dname)(struct dentry *, char *, int); struct vfsmount * (*d_automount)(struct path *); int (*d_manage)(const struct path *, bool); struct dentry * (*d_real)(struct dentry *, const struct inode *); long: 64; long: 64; long: 64; }; struct mtd_info; typedef long long int qsize_t; struct quota_format_type; struct mem_dqinfo { struct quota_format_type *dqi_format; int dqi_fmt_id; struct list_head dqi_dirty_list; long unsigned int dqi_flags; unsigned int dqi_bgrace; unsigned int dqi_igrace; qsize_t dqi_max_spc_limit; qsize_t dqi_max_ino_limit; void *dqi_priv; }; struct quota_format_ops; struct quota_info { unsigned int flags; struct rw_semaphore dqio_sem; struct inode *files[3]; struct mem_dqinfo info[3]; const struct quota_format_ops *ops[3]; }; struct rcu_sync { int gp_state; int gp_count; wait_queue_head_t gp_wait; struct callback_head cb_head; }; struct rcuwait { struct task_struct *task; }; struct percpu_rw_semaphore { struct rcu_sync rss; unsigned int *read_count; struct rcuwait writer; wait_queue_head_t waiters; atomic_t block; }; struct sb_writers { int frozen; wait_queue_head_t wait_unfrozen; struct percpu_rw_semaphore rw_sem[3]; }; typedef struct { __u8 b[16]; } uuid_t; struct list_lru_node; struct list_lru { struct list_lru_node *node; struct list_head list; int shrinker_id; bool memcg_aware; }; struct super_operations; struct dquot_operations; struct quotactl_ops; struct export_operations; struct xattr_handler; struct fscrypt_operations; struct fsverity_operations; struct unicode_map; struct block_device; struct workqueue_struct; struct super_block { struct list_head s_list; dev_t s_dev; unsigned char s_blocksize_bits; long unsigned int s_blocksize; loff_t s_maxbytes; struct file_system_type *s_type; const struct super_operations *s_op; const struct dquot_operations *dq_op; const struct quotactl_ops *s_qcop; const struct export_operations *s_export_op; long unsigned int s_flags; long unsigned int s_iflags; long unsigned int s_magic; struct dentry *s_root; struct rw_semaphore s_umount; int s_count; atomic_t s_active; void *s_security; const struct xattr_handler **s_xattr; const struct fscrypt_operations *s_cop; struct key *s_master_keys; const struct fsverity_operations *s_vop; struct unicode_map *s_encoding; __u16 s_encoding_flags; struct hlist_bl_head s_roots; struct list_head s_mounts; struct block_device *s_bdev; struct backing_dev_info *s_bdi; struct mtd_info *s_mtd; struct hlist_node s_instances; unsigned int s_quota_types; struct quota_info s_dquot; struct sb_writers s_writers; void *s_fs_info; u32 s_time_gran; time64_t s_time_min; time64_t s_time_max; __u32 s_fsnotify_mask; struct fsnotify_mark_connector *s_fsnotify_marks; char s_id[32]; uuid_t s_uuid; unsigned int s_max_links; fmode_t s_mode; struct mutex s_vfs_rename_mutex; const char *s_subtype; const struct dentry_operations *s_d_op; int cleancache_poolid; struct shrinker s_shrink; atomic_long_t s_remove_count; atomic_long_t s_fsnotify_inode_refs; int s_readonly_remount; errseq_t s_wb_err; struct workqueue_struct *s_dio_done_wq; struct hlist_head s_pins; struct user_namespace *s_user_ns; struct list_lru s_dentry_lru; struct list_lru s_inode_lru; struct callback_head rcu; struct work_struct destroy_work; struct mutex s_sync_lock; int s_stack_depth; int: 32; spinlock_t s_inode_list_lock; struct list_head s_inodes; spinlock_t s_inode_wblist_lock; struct list_head s_inodes_wb; long: 64; long: 64; }; struct vfsmount { struct dentry *mnt_root; struct super_block *mnt_sb; int mnt_flags; struct user_namespace *mnt_userns; }; struct kstat { u32 result_mask; umode_t mode; unsigned int nlink; uint32_t blksize; u64 attributes; u64 attributes_mask; u64 ino; dev_t dev; dev_t rdev; kuid_t uid; kgid_t gid; loff_t size; struct timespec64 atime; struct timespec64 mtime; struct timespec64 ctime; struct timespec64 btime; u64 blocks; u64 mnt_id; }; struct list_lru_one { struct list_head list; long int nr_items; }; struct list_lru_memcg { struct callback_head rcu; struct list_lru_one *lru[0]; }; struct list_lru_node { spinlock_t lock; struct list_lru_one lru; struct list_lru_memcg *memcg_lrus; long int nr_items; long: 64; long: 64; }; enum migrate_mode { MIGRATE_ASYNC = 0, MIGRATE_SYNC_LIGHT = 1, MIGRATE_SYNC = 2, MIGRATE_SYNC_NO_COPY = 3, }; struct key_tag { struct callback_head rcu; refcount_t usage; bool removed; }; typedef int (*request_key_actor_t)(struct key *, void *); struct key_preparsed_payload; struct key_match_data; struct kernel_pkey_params; struct kernel_pkey_query; struct key_type { const char *name; size_t def_datalen; unsigned int flags; int (*vet_description)(const char *); int (*preparse)(struct key_preparsed_payload *); void (*free_preparse)(struct key_preparsed_payload *); int (*instantiate)(struct key *, struct key_preparsed_payload *); int (*update)(struct key *, struct key_preparsed_payload *); int (*match_preparse)(struct key_match_data *); void (*match_free)(struct key_match_data *); void (*revoke)(struct key *); void (*destroy)(struct key *); void (*describe)(const struct key *, struct seq_file *); long int (*read)(const struct key *, char *, size_t); request_key_actor_t request_key; struct key_restriction * (*lookup_restriction)(const char *); int (*asym_query)(const struct kernel_pkey_params *, struct kernel_pkey_query *); int (*asym_eds_op)(struct kernel_pkey_params *, const void *, void *); int (*asym_verify_signature)(struct kernel_pkey_params *, const void *, const void *); struct list_head link; struct lock_class_key lock_class; }; typedef int (*key_restrict_link_func_t)(struct key *, const struct key_type *, const union key_payload *, struct key *); struct key_restriction { key_restrict_link_func_t check; struct key *key; struct key_type *keytype; }; struct user_struct { refcount_t __count; atomic_long_t epoll_watches; long unsigned int unix_inflight; atomic_long_t pipe_bufs; struct hlist_node uidhash_node; kuid_t uid; atomic_long_t locked_vm; atomic_t nr_watches; struct ratelimit_state ratelimit; }; struct group_info { atomic_t usage; int ngroups; kgid_t gid[0]; }; struct delayed_call { void (*fn)(void *); void *arg; }; struct io_cq { struct request_queue *q; struct io_context *ioc; union { struct list_head q_node; struct kmem_cache *__rcu_icq_cache; }; union { struct hlist_node ioc_node; struct callback_head __rcu_head; }; unsigned int flags; }; struct wait_page_queue; struct kiocb { struct file *ki_filp; loff_t ki_pos; void (*ki_complete)(struct kiocb *, long int, long int); void *private; int ki_flags; u16 ki_hint; u16 ki_ioprio; union { unsigned int ki_cookie; struct wait_page_queue *ki_waitq; }; }; struct iattr { unsigned int ia_valid; umode_t ia_mode; kuid_t ia_uid; kgid_t ia_gid; loff_t ia_size; struct timespec64 ia_atime; struct timespec64 ia_mtime; struct timespec64 ia_ctime; struct file *ia_file; }; typedef __kernel_uid32_t projid_t; typedef struct { projid_t val; } kprojid_t; enum quota_type { USRQUOTA = 0, GRPQUOTA = 1, PRJQUOTA = 2, }; struct kqid { union { kuid_t uid; kgid_t gid; kprojid_t projid; }; enum quota_type type; }; struct mem_dqblk { qsize_t dqb_bhardlimit; qsize_t dqb_bsoftlimit; qsize_t dqb_curspace; qsize_t dqb_rsvspace; qsize_t dqb_ihardlimit; qsize_t dqb_isoftlimit; qsize_t dqb_curinodes; time64_t dqb_btime; time64_t dqb_itime; }; struct dquot { struct hlist_node dq_hash; struct list_head dq_inuse; struct list_head dq_free; struct list_head dq_dirty; struct mutex dq_lock; spinlock_t dq_dqb_lock; atomic_t dq_count; struct super_block *dq_sb; struct kqid dq_id; loff_t dq_off; long unsigned int dq_flags; struct mem_dqblk dq_dqb; }; enum { DQF_ROOT_SQUASH_B = 0, DQF_SYS_FILE_B = 16, DQF_PRIVATE = 17, }; struct quota_format_type { int qf_fmt_id; const struct quota_format_ops *qf_ops; struct module *qf_owner; struct quota_format_type *qf_next; }; enum { DQST_LOOKUPS = 0, DQST_DROPS = 1, DQST_READS = 2, DQST_WRITES = 3, DQST_CACHE_HITS = 4, DQST_ALLOC_DQUOTS = 5, DQST_FREE_DQUOTS = 6, DQST_SYNCS = 7, _DQST_DQSTAT_LAST = 8, }; struct quota_format_ops { int (*check_quota_file)(struct super_block *, int); int (*read_file_info)(struct super_block *, int); int (*write_file_info)(struct super_block *, int); int (*free_file_info)(struct super_block *, int); int (*read_dqblk)(struct dquot *); int (*commit_dqblk)(struct dquot *); int (*release_dqblk)(struct dquot *); int (*get_next_id)(struct super_block *, struct kqid *); }; struct dquot_operations { int (*write_dquot)(struct dquot *); struct dquot * (*alloc_dquot)(struct super_block *, int); void (*destroy_dquot)(struct dquot *); int (*acquire_dquot)(struct dquot *); int (*release_dquot)(struct dquot *); int (*mark_dirty)(struct dquot *); int (*write_info)(struct super_block *, int); qsize_t * (*get_reserved_space)(struct inode *); int (*get_projid)(struct inode *, kprojid_t *); int (*get_inode_usage)(struct inode *, qsize_t *); int (*get_next_id)(struct super_block *, struct kqid *); }; struct qc_dqblk { int d_fieldmask; u64 d_spc_hardlimit; u64 d_spc_softlimit; u64 d_ino_hardlimit; u64 d_ino_softlimit; u64 d_space; u64 d_ino_count; s64 d_ino_timer; s64 d_spc_timer; int d_ino_warns; int d_spc_warns; u64 d_rt_spc_hardlimit; u64 d_rt_spc_softlimit; u64 d_rt_space; s64 d_rt_spc_timer; int d_rt_spc_warns; }; struct qc_type_state { unsigned int flags; unsigned int spc_timelimit; unsigned int ino_timelimit; unsigned int rt_spc_timelimit; unsigned int spc_warnlimit; unsigned int ino_warnlimit; unsigned int rt_spc_warnlimit; long long unsigned int ino; blkcnt_t blocks; blkcnt_t nextents; }; struct qc_state { unsigned int s_incoredqs; struct qc_type_state s_state[3]; }; struct qc_info { int i_fieldmask; unsigned int i_flags; unsigned int i_spc_timelimit; unsigned int i_ino_timelimit; unsigned int i_rt_spc_timelimit; unsigned int i_spc_warnlimit; unsigned int i_ino_warnlimit; unsigned int i_rt_spc_warnlimit; }; struct quotactl_ops { int (*quota_on)(struct super_block *, int, int, const struct path *); int (*quota_off)(struct super_block *, int); int (*quota_enable)(struct super_block *, unsigned int); int (*quota_disable)(struct super_block *, unsigned int); int (*quota_sync)(struct super_block *, int); int (*set_info)(struct super_block *, int, struct qc_info *); int (*get_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *); int (*get_nextdqblk)(struct super_block *, struct kqid *, struct qc_dqblk *); int (*set_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *); int (*get_state)(struct super_block *, struct qc_state *); int (*rm_xquota)(struct super_block *, unsigned int); }; struct writeback_control; struct readahead_control; struct swap_info_struct; struct address_space_operations { int (*writepage)(struct page *, struct writeback_control *); int (*readpage)(struct file *, struct page *); int (*writepages)(struct address_space *, struct writeback_control *); int (*set_page_dirty)(struct page *); int (*readpages)(struct file *, struct address_space *, struct list_head *, unsigned int); void (*readahead)(struct readahead_control *); int (*write_begin)(struct file *, struct address_space *, loff_t, unsigned int, unsigned int, struct page **, void **); int (*write_end)(struct file *, struct address_space *, loff_t, unsigned int, unsigned int, struct page *, void *); sector_t (*bmap)(struct address_space *, sector_t); void (*invalidatepage)(struct page *, unsigned int, unsigned int); int (*releasepage)(struct page *, gfp_t); void (*freepage)(struct page *); ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *); int (*migratepage)(struct address_space *, struct page *, struct page *, enum migrate_mode); bool (*isolate_page)(struct page *, isolate_mode_t); void (*putback_page)(struct page *); int (*launder_page)(struct page *); int (*is_partially_uptodate)(struct page *, long unsigned int, long unsigned int); void (*is_dirty_writeback)(struct page *, bool *, bool *); int (*error_remove_page)(struct address_space *, struct page *); int (*swap_activate)(struct swap_info_struct *, struct file *, sector_t *); void (*swap_deactivate)(struct file *); }; struct fiemap_extent_info; struct fileattr; struct inode_operations { struct dentry * (*lookup)(struct inode *, struct dentry *, unsigned int); const char * (*get_link)(struct dentry *, struct inode *, struct delayed_call *); int (*permission)(struct user_namespace *, struct inode *, int); struct posix_acl * (*get_acl)(struct inode *, int); int (*readlink)(struct dentry *, char *, int); int (*create)(struct user_namespace *, struct inode *, struct dentry *, umode_t, bool); int (*link)(struct dentry *, struct inode *, struct dentry *); int (*unlink)(struct inode *, struct dentry *); int (*symlink)(struct user_namespace *, struct inode *, struct dentry *, const char *); int (*mkdir)(struct user_namespace *, struct inode *, struct dentry *, umode_t); int (*rmdir)(struct inode *, struct dentry *); int (*mknod)(struct user_namespace *, struct inode *, struct dentry *, umode_t, dev_t); int (*rename)(struct user_namespace *, struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int); int (*setattr)(struct user_namespace *, struct dentry *, struct iattr *); int (*getattr)(struct user_namespace *, const struct path *, struct kstat *, u32, unsigned int); ssize_t (*listxattr)(struct dentry *, char *, size_t); int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64, u64); int (*update_time)(struct inode *, struct timespec64 *, int); int (*atomic_open)(struct inode *, struct dentry *, struct file *, unsigned int, umode_t); int (*tmpfile)(struct user_namespace *, struct inode *, struct dentry *, umode_t); int (*set_acl)(struct user_namespace *, struct inode *, struct posix_acl *, int); int (*fileattr_set)(struct user_namespace *, struct dentry *, struct fileattr *); int (*fileattr_get)(struct dentry *, struct fileattr *); long: 64; }; struct file_lock_context { spinlock_t flc_lock; struct list_head flc_flock; struct list_head flc_posix; struct list_head flc_lease; }; struct file_lock_operations { void (*fl_copy_lock)(struct file_lock *, struct file_lock *); void (*fl_release_private)(struct file_lock *); }; struct nlm_lockowner; struct nfs_lock_info { u32 state; struct nlm_lockowner *owner; struct list_head list; }; struct nfs4_lock_state; struct nfs4_lock_info { struct nfs4_lock_state *owner; }; struct fasync_struct; struct lock_manager_operations; struct file_lock { struct file_lock *fl_blocker; struct list_head fl_list; struct hlist_node fl_link; struct list_head fl_blocked_requests; struct list_head fl_blocked_member; fl_owner_t fl_owner; unsigned int fl_flags; unsigned char fl_type; unsigned int fl_pid; int fl_link_cpu; wait_queue_head_t fl_wait; struct file *fl_file; loff_t fl_start; loff_t fl_end; struct fasync_struct *fl_fasync; long unsigned int fl_break_time; long unsigned int fl_downgrade_time; const struct file_lock_operations *fl_ops; const struct lock_manager_operations *fl_lmops; union { struct nfs_lock_info nfs_fl; struct nfs4_lock_info nfs4_fl; struct { struct list_head link; int state; unsigned int debug_id; } afs; } fl_u; }; struct lock_manager_operations { fl_owner_t (*lm_get_owner)(fl_owner_t); void (*lm_put_owner)(fl_owner_t); void (*lm_notify)(struct file_lock *); int (*lm_grant)(struct file_lock *, int); bool (*lm_break)(struct file_lock *); int (*lm_change)(struct file_lock *, int, struct list_head *); void (*lm_setup)(struct file_lock *, void **); bool (*lm_breaker_owns_lease)(struct file_lock *); }; struct fasync_struct { rwlock_t fa_lock; int magic; int fa_fd; struct fasync_struct *fa_next; struct file *fa_file; struct callback_head fa_rcu; }; enum { SB_UNFROZEN = 0, SB_FREEZE_WRITE = 1, SB_FREEZE_PAGEFAULT = 2, SB_FREEZE_FS = 3, SB_FREEZE_COMPLETE = 4, }; struct kstatfs; struct super_operations { struct inode * (*alloc_inode)(struct super_block *); void (*destroy_inode)(struct inode *); void (*free_inode)(struct inode *); void (*dirty_inode)(struct inode *, int); int (*write_inode)(struct inode *, struct writeback_control *); int (*drop_inode)(struct inode *); void (*evict_inode)(struct inode *); void (*put_super)(struct super_block *); int (*sync_fs)(struct super_block *, int); int (*freeze_super)(struct super_block *); int (*freeze_fs)(struct super_block *); int (*thaw_super)(struct super_block *); int (*unfreeze_fs)(struct super_block *); int (*statfs)(struct dentry *, struct kstatfs *); int (*remount_fs)(struct super_block *, int *, char *); void (*umount_begin)(struct super_block *); int (*show_options)(struct seq_file *, struct dentry *); int (*show_devname)(struct seq_file *, struct dentry *); int (*show_path)(struct seq_file *, struct dentry *); int (*show_stats)(struct seq_file *, struct dentry *); ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t); ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t); struct dquot ** (*get_dquots)(struct inode *); long int (*nr_cached_objects)(struct super_block *, struct shrink_control *); long int (*free_cached_objects)(struct super_block *, struct shrink_control *); }; struct iomap; struct fid; struct export_operations { int (*encode_fh)(struct inode *, __u32 *, int *, struct inode *); struct dentry * (*fh_to_dentry)(struct super_block *, struct fid *, int, int); struct dentry * (*fh_to_parent)(struct super_block *, struct fid *, int, int); int (*get_name)(struct dentry *, char *, struct dentry *); struct dentry * (*get_parent)(struct dentry *); int (*commit_metadata)(struct inode *); int (*get_uuid)(struct super_block *, u8 *, u32 *, u64 *); int (*map_blocks)(struct inode *, loff_t, u64, struct iomap *, bool, u32 *); int (*commit_blocks)(struct inode *, struct iomap *, int, struct iattr *); u64 (*fetch_iversion)(struct inode *); long unsigned int flags; }; struct xattr_handler { const char *name; const char *prefix; int flags; bool (*list)(struct dentry *); int (*get)(const struct xattr_handler *, struct dentry *, struct inode *, const char *, void *, size_t); int (*set)(const struct xattr_handler *, struct user_namespace *, struct dentry *, struct inode *, const char *, const void *, size_t, int); }; union fscrypt_policy; struct fscrypt_operations { unsigned int flags; const char *key_prefix; int (*get_context)(struct inode *, void *, size_t); int (*set_context)(struct inode *, const void *, size_t, void *); const union fscrypt_policy * (*get_dummy_policy)(struct super_block *); bool (*empty_dir)(struct inode *); unsigned int max_namelen; bool (*has_stable_inodes)(struct super_block *); void (*get_ino_and_lblk_bits)(struct super_block *, int *, int *); int (*get_num_devices)(struct super_block *); void (*get_devices)(struct super_block *, struct request_queue **); }; struct fsverity_operations { int (*begin_enable_verity)(struct file *); int (*end_enable_verity)(struct file *, const void *, size_t, u64); int (*get_verity_descriptor)(struct inode *, void *, size_t); struct page * (*read_merkle_tree_page)(struct inode *, long unsigned int, long unsigned int); int (*write_merkle_tree_block)(struct inode *, const void *, u64, int); }; typedef int (*filldir_t)(struct dir_context *, const char *, int, loff_t, u64, unsigned int); struct dir_context { filldir_t actor; loff_t pos; }; struct p_log; struct fs_parameter; struct fs_parse_result; typedef int fs_param_type(struct p_log *, const struct fs_parameter_spec *, struct fs_parameter *, struct fs_parse_result *); struct fs_parameter_spec { const char *name; fs_param_type *type; u8 opt; short unsigned int flags; const void *data; }; enum compound_dtor_id { NULL_COMPOUND_DTOR = 0, COMPOUND_PAGE_DTOR = 1, HUGETLB_PAGE_DTOR = 2, TRANSHUGE_PAGE_DTOR = 3, NR_COMPOUND_DTORS = 4, }; enum vm_event_item { PGPGIN = 0, PGPGOUT = 1, PSWPIN = 2, PSWPOUT = 3, PGALLOC_DMA = 4, PGALLOC_DMA32 = 5, PGALLOC_NORMAL = 6, PGALLOC_MOVABLE = 7, ALLOCSTALL_DMA = 8, ALLOCSTALL_DMA32 = 9, ALLOCSTALL_NORMAL = 10, ALLOCSTALL_MOVABLE = 11, PGSCAN_SKIP_DMA = 12, PGSCAN_SKIP_DMA32 = 13, PGSCAN_SKIP_NORMAL = 14, PGSCAN_SKIP_MOVABLE = 15, PGFREE = 16, PGACTIVATE = 17, PGDEACTIVATE = 18, PGLAZYFREE = 19, PGFAULT = 20, PGMAJFAULT = 21, PGLAZYFREED = 22, PGREFILL = 23, PGREUSE = 24, PGSTEAL_KSWAPD = 25, PGSTEAL_DIRECT = 26, PGSCAN_KSWAPD = 27, PGSCAN_DIRECT = 28, PGSCAN_DIRECT_THROTTLE = 29, PGSCAN_ANON = 30, PGSCAN_FILE = 31, PGSTEAL_ANON = 32, PGSTEAL_FILE = 33, PGSCAN_ZONE_RECLAIM_FAILED = 34, PGINODESTEAL = 35, SLABS_SCANNED = 36, KSWAPD_INODESTEAL = 37, KSWAPD_LOW_WMARK_HIT_QUICKLY = 38, KSWAPD_HIGH_WMARK_HIT_QUICKLY = 39, PAGEOUTRUN = 40, PGROTATED = 41, DROP_PAGECACHE = 42, DROP_SLAB = 43, OOM_KILL = 44, NUMA_PTE_UPDATES = 45, NUMA_HUGE_PTE_UPDATES = 46, NUMA_HINT_FAULTS = 47, NUMA_HINT_FAULTS_LOCAL = 48, NUMA_PAGE_MIGRATE = 49, PGMIGRATE_SUCCESS = 50, PGMIGRATE_FAIL = 51, THP_MIGRATION_SUCCESS = 52, THP_MIGRATION_FAIL = 53, THP_MIGRATION_SPLIT = 54, COMPACTMIGRATE_SCANNED = 55, COMPACTFREE_SCANNED = 56, COMPACTISOLATED = 57, COMPACTSTALL = 58, COMPACTFAIL = 59, COMPACTSUCCESS = 60, KCOMPACTD_WAKE = 61, KCOMPACTD_MIGRATE_SCANNED = 62, KCOMPACTD_FREE_SCANNED = 63, HTLB_BUDDY_PGALLOC = 64, HTLB_BUDDY_PGALLOC_FAIL = 65, CMA_ALLOC_SUCCESS = 66, CMA_ALLOC_FAIL = 67, UNEVICTABLE_PGCULLED = 68, UNEVICTABLE_PGSCANNED = 69, UNEVICTABLE_PGRESCUED = 70, UNEVICTABLE_PGMLOCKED = 71, UNEVICTABLE_PGMUNLOCKED = 72, UNEVICTABLE_PGCLEARED = 73, UNEVICTABLE_PGSTRANDED = 74, THP_FAULT_ALLOC = 75, THP_FAULT_FALLBACK = 76, THP_FAULT_FALLBACK_CHARGE = 77, THP_COLLAPSE_ALLOC = 78, THP_COLLAPSE_ALLOC_FAILED = 79, THP_FILE_ALLOC = 80, THP_FILE_FALLBACK = 81, THP_FILE_FALLBACK_CHARGE = 82, THP_FILE_MAPPED = 83, THP_SPLIT_PAGE = 84, THP_SPLIT_PAGE_FAILED = 85, THP_DEFERRED_SPLIT_PAGE = 86, THP_SPLIT_PMD = 87, THP_SPLIT_PUD = 88, THP_ZERO_PAGE_ALLOC = 89, THP_ZERO_PAGE_ALLOC_FAILED = 90, THP_SWPOUT = 91, THP_SWPOUT_FALLBACK = 92, BALLOON_INFLATE = 93, BALLOON_DEFLATE = 94, BALLOON_MIGRATE = 95, SWAP_RA = 96, SWAP_RA_HIT = 97, DIRECT_MAP_LEVEL2_SPLIT = 98, DIRECT_MAP_LEVEL3_SPLIT = 99, NR_VM_EVENT_ITEMS = 100, }; struct tlb_context { u64 ctx_id; u64 tlb_gen; }; struct tlb_state { struct mm_struct *loaded_mm; union { struct mm_struct *last_user_mm; long unsigned int last_user_mm_ibpb; }; u16 loaded_mm_asid; u16 next_asid; bool invalidate_other; short unsigned int user_pcid_flush_mask; long unsigned int cr4; struct tlb_context ctxs[6]; }; struct boot_params_to_save { unsigned int start; unsigned int len; }; enum cpu_idle_type { CPU_IDLE = 0, CPU_NOT_IDLE = 1, CPU_NEWLY_IDLE = 2, CPU_MAX_IDLE_TYPES = 3, }; enum { __SD_BALANCE_NEWIDLE = 0, __SD_BALANCE_EXEC = 1, __SD_BALANCE_FORK = 2, __SD_BALANCE_WAKE = 3, __SD_WAKE_AFFINE = 4, __SD_ASYM_CPUCAPACITY = 5, __SD_ASYM_CPUCAPACITY_FULL = 6, __SD_SHARE_CPUCAPACITY = 7, __SD_SHARE_PKG_RESOURCES = 8, __SD_SERIALIZE = 9, __SD_ASYM_PACKING = 10, __SD_PREFER_SIBLING = 11, __SD_OVERLAP = 12, __SD_NUMA = 13, __SD_FLAG_CNT = 14, }; struct x86_legacy_devices { int pnpbios; }; enum x86_legacy_i8042_state { X86_LEGACY_I8042_PLATFORM_ABSENT = 0, X86_LEGACY_I8042_FIRMWARE_ABSENT = 1, X86_LEGACY_I8042_EXPECTED_PRESENT = 2, }; struct x86_legacy_features { enum x86_legacy_i8042_state i8042; int rtc; int warm_reset; int no_vga; int reserve_bios_regions; struct x86_legacy_devices devices; }; struct ghcb; struct x86_hyper_runtime { void (*pin_vcpu)(int); void (*sev_es_hcall_prepare)(struct ghcb *, struct pt_regs *); bool (*sev_es_hcall_finish)(struct ghcb *, struct pt_regs *); }; struct x86_platform_ops { long unsigned int (*calibrate_cpu)(); long unsigned int (*calibrate_tsc)(); void (*get_wallclock)(struct timespec64 *); int (*set_wallclock)(const struct timespec64 *); void (*iommu_shutdown)(); bool (*is_untracked_pat_range)(u64, u64); void (*nmi_init)(); unsigned char (*get_nmi_reason)(); void (*save_sched_clock_state)(); void (*restore_sched_clock_state)(); void (*apic_post_init)(); struct x86_legacy_features legacy; void (*set_legacy_features)(); struct x86_hyper_runtime hyper; }; typedef signed char __s8; typedef __s8 s8; typedef __u32 __le32; typedef long unsigned int irq_hw_number_t; struct kernel_symbol { int value_offset; int name_offset; int namespace_offset; }; typedef int (*initcall_t)(); typedef int initcall_entry_t; struct obs_kernel_param { const char *str; int (*setup_func)(char *); int early; }; struct lockdep_map {}; struct jump_entry { s32 code; s32 target; long int key; }; struct static_key_mod; struct static_key { atomic_t enabled; union { long unsigned int type; struct jump_entry *entries; struct static_key_mod *next; }; }; struct static_key_true { struct static_key key; }; struct static_key_false { struct static_key key; }; struct _ddebug { const char *modname; const char *function; const char *filename; const char *format; unsigned int lineno: 18; unsigned int flags: 8; union { struct static_key_true dd_key_true; struct static_key_false dd_key_false; } key; }; struct static_call_site { s32 addr; s32 key; }; struct static_call_mod { struct static_call_mod *next; struct module *mod; struct static_call_site *sites; }; struct static_call_key { void *func; union { long unsigned int type; struct static_call_mod *mods; struct static_call_site *sites; }; }; enum system_states { SYSTEM_BOOTING = 0, SYSTEM_SCHEDULING = 1, SYSTEM_RUNNING = 2, SYSTEM_HALT = 3, SYSTEM_POWER_OFF = 4, SYSTEM_RESTART = 5, SYSTEM_SUSPEND = 6, }; struct orc_entry { s16 sp_offset; s16 bp_offset; unsigned int sp_reg: 4; unsigned int bp_reg: 4; unsigned int type: 2; unsigned int end: 1; } __attribute__((packed)); struct bug_entry { int bug_addr_disp; int file_disp; short unsigned int line; short unsigned int flags; }; typedef struct cpumask cpumask_var_t[1]; struct tracepoint_func { void *func; void *data; int prio; }; struct tracepoint { const char *name; struct static_key key; struct static_call_key *static_call_key; void *static_call_tramp; void *iterator; int (*regfunc)(); void (*unregfunc)(); struct tracepoint_func *funcs; }; typedef const int tracepoint_ptr_t; struct bpf_raw_event_map { struct tracepoint *tp; void *bpf_func; u32 num_args; u32 writable_size; long: 64; }; struct seq_operations { void * (*start)(struct seq_file *, loff_t *); void (*stop)(struct seq_file *, void *); void * (*next)(struct seq_file *, void *, loff_t *); int (*show)(struct seq_file *, void *); }; struct fixed_percpu_data { char gs_base[40]; long unsigned int stack_canary; }; enum perf_event_state { PERF_EVENT_STATE_DEAD = 4294967292, PERF_EVENT_STATE_EXIT = 4294967293, PERF_EVENT_STATE_ERROR = 4294967294, PERF_EVENT_STATE_OFF = 4294967295, PERF_EVENT_STATE_INACTIVE = 0, PERF_EVENT_STATE_ACTIVE = 1, }; typedef struct { atomic_long_t a; } local_t; typedef struct { local_t a; } local64_t; struct perf_event_attr { __u32 type; __u32 size; __u64 config; union { __u64 sample_period; __u64 sample_freq; }; __u64 sample_type; __u64 read_format; __u64 disabled: 1; __u64 inherit: 1; __u64 pinned: 1; __u64 exclusive: 1; __u64 exclude_user: 1; __u64 exclude_kernel: 1; __u64 exclude_hv: 1; __u64 exclude_idle: 1; __u64 mmap: 1; __u64 comm: 1; __u64 freq: 1; __u64 inherit_stat: 1; __u64 enable_on_exec: 1; __u64 task: 1; __u64 watermark: 1; __u64 precise_ip: 2; __u64 mmap_data: 1; __u64 sample_id_all: 1; __u64 exclude_host: 1; __u64 exclude_guest: 1; __u64 exclude_callchain_kernel: 1; __u64 exclude_callchain_user: 1; __u64 mmap2: 1; __u64 comm_exec: 1; __u64 use_clockid: 1; __u64 context_switch: 1; __u64 write_backward: 1; __u64 namespaces: 1; __u64 ksymbol: 1; __u64 bpf_event: 1; __u64 aux_output: 1; __u64 cgroup: 1; __u64 text_poke: 1; __u64 build_id: 1; __u64 inherit_thread: 1; __u64 remove_on_exec: 1; __u64 sigtrap: 1; __u64 __reserved_1: 26; union { __u32 wakeup_events; __u32 wakeup_watermark; }; __u32 bp_type; union { __u64 bp_addr; __u64 kprobe_func; __u64 uprobe_path; __u64 config1; }; union { __u64 bp_len; __u64 kprobe_addr; __u64 probe_offset; __u64 config2; }; __u64 branch_sample_type; __u64 sample_regs_user; __u32 sample_stack_user; __s32 clockid; __u64 sample_regs_intr; __u32 aux_watermark; __u16 sample_max_stack; __u16 __reserved_2; __u32 aux_sample_size; __u32 __reserved_3; __u64 sig_data; }; struct hw_perf_event_extra { u64 config; unsigned int reg; int alloc; int idx; }; struct arch_hw_breakpoint { long unsigned int address; long unsigned int mask; u8 len; u8 type; }; struct hw_perf_event { union { struct { u64 config; u64 last_tag; long unsigned int config_base; long unsigned int event_base; int event_base_rdpmc; int idx; int last_cpu; int flags; struct hw_perf_event_extra extra_reg; struct hw_perf_event_extra branch_reg; }; struct { struct hrtimer hrtimer; }; struct { struct list_head tp_list; }; struct { u64 pwr_acc; u64 ptsc; }; struct { struct arch_hw_breakpoint info; struct list_head bp_list; }; struct { u8 iommu_bank; u8 iommu_cntr; u16 padding; u64 conf; u64 conf1; }; }; struct task_struct *target; void *addr_filters; long unsigned int addr_filters_gen; int state; local64_t prev_count; u64 sample_period; union { struct { u64 last_period; local64_t period_left; }; struct { u64 saved_metric; u64 saved_slots; }; }; u64 interrupts_seq; u64 interrupts; u64 freq_time_stamp; u64 freq_count_stamp; }; struct irq_work { struct __call_single_node node; void (*func)(struct irq_work *); }; struct perf_addr_filters_head { struct list_head list; raw_spinlock_t lock; unsigned int nr_file_filters; }; struct perf_sample_data; typedef void (*perf_overflow_handler_t)(struct perf_event *, struct perf_sample_data *, struct pt_regs *); struct ftrace_ops; struct ftrace_regs; typedef void (*ftrace_func_t)(long unsigned int, long unsigned int, struct ftrace_ops *, struct ftrace_regs *); struct ftrace_hash; struct ftrace_ops_hash { struct ftrace_hash *notrace_hash; struct ftrace_hash *filter_hash; struct mutex regex_lock; }; struct ftrace_ops { ftrace_func_t func; struct ftrace_ops *next; long unsigned int flags; void *private; ftrace_func_t saved_func; struct ftrace_ops_hash local_hash; struct ftrace_ops_hash *func_hash; struct ftrace_ops_hash old_hash; long unsigned int trampoline; long unsigned int trampoline_size; struct list_head list; }; struct pmu; struct perf_buffer; struct perf_addr_filter_range; struct bpf_prog; struct trace_event_call; struct event_filter; struct perf_cgroup; struct perf_event { struct list_head event_entry; struct list_head sibling_list; struct list_head active_list; struct rb_node group_node; u64 group_index; struct list_head migrate_entry; struct hlist_node hlist_entry; struct list_head active_entry; int nr_siblings; int event_caps; int group_caps; struct perf_event *group_leader; struct pmu *pmu; void *pmu_private; enum perf_event_state state; unsigned int attach_state; local64_t count; atomic64_t child_count; u64 total_time_enabled; u64 total_time_running; u64 tstamp; u64 shadow_ctx_time; struct perf_event_attr attr; u16 header_size; u16 id_header_size; u16 read_size; struct hw_perf_event hw; struct perf_event_context *ctx; atomic_long_t refcount; atomic64_t child_total_time_enabled; atomic64_t child_total_time_running; struct mutex child_mutex; struct list_head child_list; struct perf_event *parent; int oncpu; int cpu; struct list_head owner_entry; struct task_struct *owner; struct mutex mmap_mutex; atomic_t mmap_count; struct perf_buffer *rb; struct list_head rb_entry; long unsigned int rcu_batches; int rcu_pending; wait_queue_head_t waitq; struct fasync_struct *fasync; int pending_wakeup; int pending_kill; int pending_disable; long unsigned int pending_addr; struct irq_work pending; atomic_t event_limit; struct perf_addr_filters_head addr_filters; struct perf_addr_filter_range *addr_filter_ranges; long unsigned int addr_filters_gen; struct perf_event *aux_event; void (*destroy)(struct perf_event *); struct callback_head callback_head; struct pid_namespace *ns; u64 id; u64 (*clock)(); perf_overflow_handler_t overflow_handler; void *overflow_handler_context; perf_overflow_handler_t orig_overflow_handler; struct bpf_prog *prog; struct trace_event_call *tp_event; struct event_filter *filter; struct ftrace_ops ftrace_ops; struct perf_cgroup *cgrp; void *security; struct list_head sb_list; }; struct uid_gid_extent { u32 first; u32 lower_first; u32 count; }; struct uid_gid_map { u32 nr_extents; union { struct uid_gid_extent extent[5]; struct { struct uid_gid_extent *forward; struct uid_gid_extent *reverse; }; }; }; struct proc_ns_operations; struct ns_common { atomic_long_t stashed; const struct proc_ns_operations *ops; unsigned int inum; refcount_t count; }; struct ctl_table; struct ctl_table_root; struct ctl_table_set; struct ctl_dir; struct ctl_node; struct ctl_table_header { union { struct { struct ctl_table *ctl_table; int used; int count; int nreg; }; struct callback_head rcu; }; struct completion *unregistering; struct ctl_table *ctl_table_arg; struct ctl_table_root *root; struct ctl_table_set *set; struct ctl_dir *parent; struct ctl_node *node; struct hlist_head inodes; }; struct ctl_dir { struct ctl_table_header header; struct rb_root root; }; struct ctl_table_set { int (*is_seen)(struct ctl_table_set *); struct ctl_dir dir; }; struct user_namespace { struct uid_gid_map uid_map; struct uid_gid_map gid_map; struct uid_gid_map projid_map; struct user_namespace *parent; int level; kuid_t owner; kgid_t group; struct ns_common ns; long unsigned int flags; bool parent_could_setfcap; struct list_head keyring_name_list; struct key *user_keyring_register; struct rw_semaphore keyring_sem; struct key *persistent_keyring_register; struct work_struct work; struct ctl_table_set set; struct ctl_table_header *sysctls; struct ucounts *ucounts; long int ucount_max[16]; }; struct pollfd { int fd; short int events; short int revents; }; typedef void (*smp_call_func_t)(void *); struct __call_single_data { struct __call_single_node node; smp_call_func_t func; void *info; }; struct smp_ops { void (*smp_prepare_boot_cpu)(); void (*smp_prepare_cpus)(unsigned int); void (*smp_cpus_done)(unsigned int); void (*stop_other_cpus)(int); void (*crash_stop_other_cpus)(); void (*smp_send_reschedule)(int); int (*cpu_up)(unsigned int, struct task_struct *); int (*cpu_disable)(); void (*cpu_die)(unsigned int); void (*play_dead)(); void (*send_call_func_ipi)(const struct cpumask *); void (*send_call_func_single_ipi)(int); }; struct wait_queue_entry; typedef int (*wait_queue_func_t)(struct wait_queue_entry *, unsigned int, int, void *); struct wait_queue_entry { unsigned int flags; void *private; wait_queue_func_t func; struct list_head entry; }; typedef struct wait_queue_entry wait_queue_entry_t; struct timer_list { struct hlist_node entry; long unsigned int expires; void (*function)(struct timer_list *); u32 flags; }; struct delayed_work { struct work_struct work; struct timer_list timer; struct workqueue_struct *wq; int cpu; }; struct rcu_work { struct work_struct work; struct callback_head rcu; struct workqueue_struct *wq; }; struct rcu_segcblist { struct callback_head *head; struct callback_head **tails[4]; long unsigned int gp_seq[4]; long int len; long int seglen[4]; u8 flags; }; struct srcu_node; struct srcu_struct; struct srcu_data { long unsigned int srcu_lock_count[2]; long unsigned int srcu_unlock_count[2]; long: 64; long: 64; long: 64; long: 64; spinlock_t lock; struct rcu_segcblist srcu_cblist; long unsigned int srcu_gp_seq_needed; long unsigned int srcu_gp_seq_needed_exp; bool srcu_cblist_invoking; struct timer_list delay_work; struct work_struct work; struct callback_head srcu_barrier_head; struct srcu_node *mynode; long unsigned int grpmask; int cpu; struct srcu_struct *ssp; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct srcu_node { spinlock_t lock; long unsigned int srcu_have_cbs[4]; long unsigned int srcu_data_have_cbs[4]; long unsigned int srcu_gp_seq_needed_exp; struct srcu_node *srcu_parent; int grplo; int grphi; }; struct srcu_struct { struct srcu_node node[21]; struct srcu_node *level[3]; struct mutex srcu_cb_mutex; spinlock_t lock; struct mutex srcu_gp_mutex; unsigned int srcu_idx; long unsigned int srcu_gp_seq; long unsigned int srcu_gp_seq_needed; long unsigned int srcu_gp_seq_needed_exp; long unsigned int srcu_last_gp_end; struct srcu_data *sda; long unsigned int srcu_barrier_seq; struct mutex srcu_barrier_mutex; struct completion srcu_barrier_completion; atomic_t srcu_barrier_cpu_cnt; struct delayed_work work; struct lockdep_map dep_map; }; struct anon_vma { struct anon_vma *root; struct rw_semaphore rwsem; atomic_t refcount; unsigned int degree; struct anon_vma *parent; struct rb_root_cached rb_root; }; struct mempolicy { atomic_t refcnt; short unsigned int mode; short unsigned int flags; nodemask_t nodes; union { nodemask_t cpuset_mems_allowed; nodemask_t user_nodemask; } w; }; struct linux_binprm; struct coredump_params; struct linux_binfmt { struct list_head lh; struct module *module; int (*load_binary)(struct linux_binprm *); int (*load_shlib)(struct file *); int (*core_dump)(struct coredump_params *); long unsigned int min_coredump; }; struct free_area { struct list_head free_list[6]; long unsigned int nr_free; }; struct zone_padding { char x[0]; }; struct pglist_data; struct lruvec { struct list_head lists[5]; spinlock_t lru_lock; long unsigned int anon_cost; long unsigned int file_cost; atomic_long_t nonresident_age; long unsigned int refaults[2]; long unsigned int flags; struct pglist_data *pgdat; }; struct per_cpu_pages; struct per_cpu_zonestat; struct zone { long unsigned int _watermark[3]; long unsigned int watermark_boost; long unsigned int nr_reserved_highatomic; long int lowmem_reserve[5]; int node; struct pglist_data *zone_pgdat; struct per_cpu_pages *per_cpu_pageset; struct per_cpu_zonestat *per_cpu_zonestats; int pageset_high; int pageset_batch; long unsigned int zone_start_pfn; atomic_long_t managed_pages; long unsigned int spanned_pages; long unsigned int present_pages; long unsigned int cma_pages; const char *name; long unsigned int nr_isolate_pageblock; seqlock_t span_seqlock; int initialized; int: 32; struct zone_padding _pad1_; struct free_area free_area[11]; long unsigned int flags; spinlock_t lock; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; struct zone_padding _pad2_; long unsigned int percpu_drift_mark; long unsigned int compact_cached_free_pfn; long unsigned int compact_cached_migrate_pfn[2]; long unsigned int compact_init_migrate_pfn; long unsigned int compact_init_free_pfn; unsigned int compact_considered; unsigned int compact_defer_shift; int compact_order_failed; bool compact_blockskip_flush; bool contiguous; short: 16; struct zone_padding _pad3_; atomic_long_t vm_stat[11]; atomic_long_t vm_numa_event[6]; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct zoneref { struct zone *zone; int zone_idx; }; struct zonelist { struct zoneref _zonerefs[161]; }; enum zone_type { ZONE_DMA = 0, ZONE_DMA32 = 1, ZONE_NORMAL = 2, ZONE_MOVABLE = 3, ZONE_DEVICE = 4, __MAX_NR_ZONES = 5, }; struct deferred_split { spinlock_t split_queue_lock; struct list_head split_queue; long unsigned int split_queue_len; }; struct per_cpu_nodestat; struct pglist_data { struct zone node_zones[5]; struct zonelist node_zonelists[2]; int nr_zones; spinlock_t node_size_lock; long unsigned int node_start_pfn; long unsigned int node_present_pages; long unsigned int node_spanned_pages; int node_id; wait_queue_head_t kswapd_wait; wait_queue_head_t pfmemalloc_wait; struct task_struct *kswapd; int kswapd_order; enum zone_type kswapd_highest_zoneidx; int kswapd_failures; int kcompactd_max_order; enum zone_type kcompactd_highest_zoneidx; wait_queue_head_t kcompactd_wait; struct task_struct *kcompactd; long unsigned int totalreserve_pages; long unsigned int min_unmapped_pages; long unsigned int min_slab_pages; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; struct zone_padding _pad1_; struct deferred_split deferred_split_queue; struct lruvec __lruvec; long unsigned int flags; long: 64; struct zone_padding _pad2_; struct per_cpu_nodestat *per_cpu_nodestats; atomic_long_t vm_stat[39]; }; struct per_cpu_pages { int count; int high; int batch; short int free_factor; short int expire; struct list_head lists[15]; }; struct per_cpu_zonestat { s8 vm_stat_diff[11]; s8 stat_threshold; long unsigned int vm_numa_event[6]; }; struct per_cpu_nodestat { s8 stat_threshold; s8 vm_node_stat_diff[39]; }; typedef struct pglist_data pg_data_t; enum irq_domain_bus_token { DOMAIN_BUS_ANY = 0, DOMAIN_BUS_WIRED = 1, DOMAIN_BUS_GENERIC_MSI = 2, DOMAIN_BUS_PCI_MSI = 3, DOMAIN_BUS_PLATFORM_MSI = 4, DOMAIN_BUS_NEXUS = 5, DOMAIN_BUS_IPI = 6, DOMAIN_BUS_FSL_MC_MSI = 7, DOMAIN_BUS_TI_SCI_INTA_MSI = 8, DOMAIN_BUS_WAKEUP = 9, DOMAIN_BUS_VMD_MSI = 10, }; struct irq_domain_ops; struct fwnode_handle; struct irq_domain_chip_generic; struct irq_data; struct irq_domain { struct list_head link; const char *name; const struct irq_domain_ops *ops; void *host_data; unsigned int flags; unsigned int mapcount; struct fwnode_handle *fwnode; enum irq_domain_bus_token bus_token; struct irq_domain_chip_generic *gc; struct irq_domain *parent; irq_hw_number_t hwirq_max; unsigned int revmap_size; struct xarray revmap_tree; struct mutex revmap_mutex; struct irq_data *revmap[0]; }; typedef int proc_handler(struct ctl_table *, int, void *, size_t *, loff_t *); struct ctl_table_poll; struct ctl_table { const char *procname; void *data; int maxlen; umode_t mode; struct ctl_table *child; proc_handler *proc_handler; struct ctl_table_poll *poll; void *extra1; void *extra2; }; struct ctl_table_poll { atomic_t event; wait_queue_head_t wait; }; struct ctl_node { struct rb_node node; struct ctl_table_header *header; }; struct ctl_table_root { struct ctl_table_set default_set; struct ctl_table_set * (*lookup)(struct ctl_table_root *); void (*set_ownership)(struct ctl_table_header *, struct ctl_table *, kuid_t *, kgid_t *); int (*permissions)(struct ctl_table_header *, struct ctl_table *); }; enum umh_disable_depth { UMH_ENABLED = 0, UMH_FREEZING = 1, UMH_DISABLED = 2, }; typedef __u64 Elf64_Addr; typedef __u16 Elf64_Half; typedef __u32 Elf64_Word; typedef __u64 Elf64_Xword; struct elf64_sym { Elf64_Word st_name; unsigned char st_info; unsigned char st_other; Elf64_Half st_shndx; Elf64_Addr st_value; Elf64_Xword st_size; }; typedef struct elf64_sym Elf64_Sym; struct idr { struct xarray idr_rt; unsigned int idr_base; unsigned int idr_next; }; struct kernfs_root; struct kernfs_elem_dir { long unsigned int subdirs; struct rb_root children; struct kernfs_root *root; }; struct kernfs_node; struct kernfs_syscall_ops; struct kernfs_root { struct kernfs_node *kn; unsigned int flags; struct idr ino_idr; u32 last_id_lowbits; u32 id_highbits; struct kernfs_syscall_ops *syscall_ops; struct list_head supers; wait_queue_head_t deactivate_waitq; }; struct kernfs_elem_symlink { struct kernfs_node *target_kn; }; struct kernfs_ops; struct kernfs_open_node; struct kernfs_elem_attr { const struct kernfs_ops *ops; struct kernfs_open_node *open; loff_t size; struct kernfs_node *notify_next; }; struct kernfs_iattrs; struct kernfs_node { atomic_t count; atomic_t active; struct kernfs_node *parent; const char *name; struct rb_node rb; const void *ns; unsigned int hash; union { struct kernfs_elem_dir dir; struct kernfs_elem_symlink symlink; struct kernfs_elem_attr attr; }; void *priv; u64 id; short unsigned int flags; umode_t mode; struct kernfs_iattrs *iattr; }; struct kernfs_open_file; struct kernfs_ops { int (*open)(struct kernfs_open_file *); void (*release)(struct kernfs_open_file *); int (*seq_show)(struct seq_file *, void *); void * (*seq_start)(struct seq_file *, loff_t *); void * (*seq_next)(struct seq_file *, void *, loff_t *); void (*seq_stop)(struct seq_file *, void *); ssize_t (*read)(struct kernfs_open_file *, char *, size_t, loff_t); size_t atomic_write_len; bool prealloc; ssize_t (*write)(struct kernfs_open_file *, char *, size_t, loff_t); __poll_t (*poll)(struct kernfs_open_file *, struct poll_table_struct *); int (*mmap)(struct kernfs_open_file *, struct vm_area_struct *); }; struct kernfs_syscall_ops { int (*show_options)(struct seq_file *, struct kernfs_root *); int (*mkdir)(struct kernfs_node *, const char *, umode_t); int (*rmdir)(struct kernfs_node *); int (*rename)(struct kernfs_node *, struct kernfs_node *, const char *); int (*show_path)(struct seq_file *, struct kernfs_node *, struct kernfs_root *); }; struct seq_file { char *buf; size_t size; size_t from; size_t count; size_t pad_until; loff_t index; loff_t read_pos; struct mutex lock; const struct seq_operations *op; int poll_event; const struct file *file; void *private; }; struct kernfs_open_file { struct kernfs_node *kn; struct file *file; struct seq_file *seq_file; void *priv; struct mutex mutex; struct mutex prealloc_mutex; int event; struct list_head list; char *prealloc_buf; size_t atomic_write_len; bool mmapped: 1; bool released: 1; const struct vm_operations_struct *vm_ops; }; typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *); struct poll_table_struct { poll_queue_proc _qproc; __poll_t _key; }; enum kobj_ns_type { KOBJ_NS_TYPE_NONE = 0, KOBJ_NS_TYPE_NET = 1, KOBJ_NS_TYPES = 2, }; struct sock; struct kobj_ns_type_operations { enum kobj_ns_type type; bool (*current_may_mount)(); void * (*grab_current_ns)(); const void * (*netlink_ns)(struct sock *); const void * (*initial_ns)(); void (*drop_ns)(void *); }; struct attribute { const char *name; umode_t mode; }; struct kobject; struct bin_attribute; struct attribute_group { const char *name; umode_t (*is_visible)(struct kobject *, struct attribute *, int); umode_t (*is_bin_visible)(struct kobject *, struct bin_attribute *, int); struct attribute **attrs; struct bin_attribute **bin_attrs; }; struct kref { refcount_t refcount; }; struct kset; struct kobj_type; struct kobject { const char *name; struct list_head entry; struct kobject *parent; struct kset *kset; struct kobj_type *ktype; struct kernfs_node *sd; struct kref kref; unsigned int state_initialized: 1; unsigned int state_in_sysfs: 1; unsigned int state_add_uevent_sent: 1; unsigned int state_remove_uevent_sent: 1; unsigned int uevent_suppress: 1; }; struct bin_attribute { struct attribute attr; size_t size; void *private; struct address_space *mapping; ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t); ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t); int (*mmap)(struct file *, struct kobject *, struct bin_attribute *, struct vm_area_struct *); }; struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *, char *); ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); }; enum refcount_saturation_type { REFCOUNT_ADD_NOT_ZERO_OVF = 0, REFCOUNT_ADD_OVF = 1, REFCOUNT_ADD_UAF = 2, REFCOUNT_SUB_UAF = 3, REFCOUNT_DEC_LEAK = 4, }; struct kset_uevent_ops; struct kset { struct list_head list; spinlock_t list_lock; struct kobject kobj; const struct kset_uevent_ops *uevent_ops; }; struct kobj_type { void (*release)(struct kobject *); const struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; const struct attribute_group **default_groups; const struct kobj_ns_type_operations * (*child_ns_type)(struct kobject *); const void * (*namespace)(struct kobject *); void (*get_ownership)(struct kobject *, kuid_t *, kgid_t *); }; struct kobj_uevent_env { char *argv[3]; char *envp[64]; int envp_idx; char buf[2048]; int buflen; }; struct kset_uevent_ops { int (* const filter)(struct kset *, struct kobject *); const char * (* const name)(struct kset *, struct kobject *); int (* const uevent)(struct kset *, struct kobject *, struct kobj_uevent_env *); }; struct kernel_param; struct kernel_param_ops { unsigned int flags; int (*set)(const char *, const struct kernel_param *); int (*get)(char *, const struct kernel_param *); void (*free)(void *); }; struct kparam_string; struct kparam_array; struct kernel_param { const char *name; struct module *mod; const struct kernel_param_ops *ops; const u16 perm; s8 level; u8 flags; union { void *arg; const struct kparam_string *str; const struct kparam_array *arr; }; }; struct kparam_string { unsigned int maxlen; char *string; }; struct kparam_array { unsigned int max; unsigned int elemsize; unsigned int *num; const struct kernel_param_ops *ops; void *elem; }; enum module_state { MODULE_STATE_LIVE = 0, MODULE_STATE_COMING = 1, MODULE_STATE_GOING = 2, MODULE_STATE_UNFORMED = 3, }; struct module_param_attrs; struct module_kobject { struct kobject kobj; struct module *mod; struct kobject *drivers_dir; struct module_param_attrs *mp; struct completion *kobj_completion; }; struct latch_tree_node { struct rb_node node[2]; }; struct mod_tree_node { struct module *mod; struct latch_tree_node node; }; struct module_layout { void *base; unsigned int size; unsigned int text_size; unsigned int ro_size; unsigned int ro_after_init_size; struct mod_tree_node mtn; }; struct mod_arch_specific { unsigned int num_orcs; int *orc_unwind_ip; struct orc_entry *orc_unwind; }; struct mod_kallsyms { Elf64_Sym *symtab; unsigned int num_symtab; char *strtab; char *typetab; }; struct module_attribute; struct exception_table_entry; struct module_sect_attrs; struct module_notes_attrs; struct trace_eval_map; struct error_injection_entry; struct module { enum module_state state; struct list_head list; char name[56]; unsigned char build_id[20]; struct module_kobject mkobj; struct module_attribute *modinfo_attrs; const char *version; const char *srcversion; struct kobject *holders_dir; const struct kernel_symbol *syms; const s32 *crcs; unsigned int num_syms; struct mutex param_lock; struct kernel_param *kp; unsigned int num_kp; unsigned int num_gpl_syms; const struct kernel_symbol *gpl_syms; const s32 *gpl_crcs; bool using_gplonly_symbols; bool sig_ok; bool async_probe_requested; unsigned int num_exentries; struct exception_table_entry *extable; int (*init)(); long: 64; long: 64; long: 64; long: 64; long: 64; struct module_layout core_layout; struct module_layout init_layout; struct mod_arch_specific arch; long unsigned int taints; unsigned int num_bugs; struct list_head bug_list; struct bug_entry *bug_table; struct mod_kallsyms *kallsyms; struct mod_kallsyms core_kallsyms; struct module_sect_attrs *sect_attrs; struct module_notes_attrs *notes_attrs; char *args; void *percpu; unsigned int percpu_size; void *noinstr_text_start; unsigned int noinstr_text_size; unsigned int num_tracepoints; tracepoint_ptr_t *tracepoints_ptrs; unsigned int num_srcu_structs; struct srcu_struct **srcu_struct_ptrs; unsigned int num_bpf_raw_events; struct bpf_raw_event_map *bpf_raw_events; unsigned int btf_data_size; void *btf_data; struct jump_entry *jump_entries; unsigned int num_jump_entries; unsigned int num_trace_bprintk_fmt; const char **trace_bprintk_fmt_start; struct trace_event_call **trace_events; unsigned int num_trace_events; struct trace_eval_map **trace_evals; unsigned int num_trace_evals; unsigned int num_ftrace_callsites; long unsigned int *ftrace_callsites; void *kprobes_text_start; unsigned int kprobes_text_size; long unsigned int *kprobe_blacklist; unsigned int num_kprobe_blacklist; int num_static_call_sites; struct static_call_site *static_call_sites; struct list_head source_list; struct list_head target_list; void (*exit)(); atomic_t refcnt; struct error_injection_entry *ei_funcs; unsigned int num_ei_funcs; long: 32; long: 64; long: 64; long: 64; long: 64; }; struct error_injection_entry { long unsigned int addr; int etype; }; struct module_attribute { struct attribute attr; ssize_t (*show)(struct module_attribute *, struct module_kobject *, char *); ssize_t (*store)(struct module_attribute *, struct module_kobject *, const char *, size_t); void (*setup)(struct module *, const char *); int (*test)(struct module *); void (*free)(struct module *); }; struct exception_table_entry { int insn; int fixup; int handler; }; struct trace_event_functions; struct trace_event { struct hlist_node node; struct list_head list; int type; struct trace_event_functions *funcs; }; struct trace_event_class; struct bpf_prog_array; struct trace_event_call { struct list_head list; struct trace_event_class *class; union { char *name; struct tracepoint *tp; }; struct trace_event event; char *print_fmt; struct event_filter *filter; void *mod; void *data; int flags; int perf_refcount; struct hlist_head *perf_events; struct bpf_prog_array *prog_array; int (*perf_perm)(struct trace_event_call *, struct perf_event *); }; struct trace_eval_map { const char *system; const char *eval_string; long unsigned int eval_value; }; struct cgroup; struct cgroup_subsys; struct cgroup_subsys_state { struct cgroup *cgroup; struct cgroup_subsys *ss; struct percpu_ref refcnt; struct list_head sibling; struct list_head children; struct list_head rstat_css_node; int id; unsigned int flags; u64 serial_nr; atomic_t online_cnt; struct work_struct destroy_work; struct rcu_work destroy_rwork; struct cgroup_subsys_state *parent; }; struct mem_cgroup_id { int id; refcount_t ref; }; struct page_counter { atomic_long_t usage; long unsigned int min; long unsigned int low; long unsigned int high; long unsigned int max; long unsigned int emin; atomic_long_t min_usage; atomic_long_t children_min_usage; long unsigned int elow; atomic_long_t low_usage; atomic_long_t children_low_usage; long unsigned int watermark; long unsigned int failcnt; struct page_counter *parent; }; struct vmpressure { long unsigned int scanned; long unsigned int reclaimed; long unsigned int tree_scanned; long unsigned int tree_reclaimed; spinlock_t sr_lock; struct list_head events; struct mutex events_lock; struct work_struct work; }; struct cgroup_file { struct kernfs_node *kn; long unsigned int notified_at; struct timer_list notify_timer; }; struct mem_cgroup_threshold_ary; struct mem_cgroup_thresholds { struct mem_cgroup_threshold_ary *primary; struct mem_cgroup_threshold_ary *spare; }; struct memcg_padding { char x[0]; }; struct memcg_vmstats { long int state[42]; long unsigned int events[100]; long int state_pending[42]; long unsigned int events_pending[100]; }; enum memcg_kmem_state { KMEM_NONE = 0, KMEM_ALLOCATED = 1, KMEM_ONLINE = 2, }; struct percpu_counter { raw_spinlock_t lock; s64 count; struct list_head list; s32 *counters; }; struct fprop_global { struct percpu_counter events; unsigned int period; seqcount_t sequence; }; struct wb_domain { spinlock_t lock; struct fprop_global completions; struct timer_list period_timer; long unsigned int period_time; long unsigned int dirty_limit_tstamp; long unsigned int dirty_limit; }; struct wb_completion { atomic_t cnt; wait_queue_head_t *waitq; }; struct memcg_cgwb_frn { u64 bdi_id; int memcg_id; u64 at; struct wb_completion done; }; struct obj_cgroup; struct memcg_vmstats_percpu; struct mem_cgroup_per_node; struct mem_cgroup { struct cgroup_subsys_state css; struct mem_cgroup_id id; struct page_counter memory; union { struct page_counter swap; struct page_counter memsw; }; struct page_counter kmem; struct page_counter tcpmem; struct work_struct high_work; long unsigned int soft_limit; struct vmpressure vmpressure; bool oom_group; bool oom_lock; int under_oom; int swappiness; int oom_kill_disable; struct cgroup_file events_file; struct cgroup_file events_local_file; struct cgroup_file swap_events_file; struct mutex thresholds_lock; struct mem_cgroup_thresholds thresholds; struct mem_cgroup_thresholds memsw_thresholds; struct list_head oom_notify; long unsigned int move_charge_at_immigrate; spinlock_t move_lock; long unsigned int move_lock_flags; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; struct memcg_padding _pad1_; struct memcg_vmstats vmstats; atomic_long_t memory_events[8]; atomic_long_t memory_events_local[8]; long unsigned int socket_pressure; bool tcpmem_active; int tcpmem_pressure; int kmemcg_id; enum memcg_kmem_state kmem_state; struct obj_cgroup *objcg; struct list_head objcg_list; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; struct memcg_padding _pad2_; atomic_t moving_account; struct task_struct *move_lock_task; struct memcg_vmstats_percpu *vmstats_percpu; struct list_head cgwb_list; struct wb_domain cgwb_domain; struct memcg_cgwb_frn cgwb_frn[4]; struct list_head event_list; spinlock_t event_list_lock; struct deferred_split deferred_split_queue; struct mem_cgroup_per_node *nodeinfo[0]; long: 64; }; struct fs_pin; struct pid_namespace { struct idr idr; struct callback_head rcu; unsigned int pid_allocated; struct task_struct *child_reaper; struct kmem_cache *pid_cachep; unsigned int level; struct pid_namespace *parent; struct fs_pin *bacct; struct user_namespace *user_ns; struct ucounts *ucounts; int reboot; struct ns_common ns; }; struct ucounts { struct hlist_node node; struct user_namespace *ns; kuid_t uid; atomic_t count; atomic_long_t ucount[16]; }; struct task_cputime { u64 stime; u64 utime; long long unsigned int sum_exec_runtime; }; struct uts_namespace; struct ipc_namespace; struct mnt_namespace; struct net; struct time_namespace; struct cgroup_namespace; struct nsproxy { atomic_t count; struct uts_namespace *uts_ns; struct ipc_namespace *ipc_ns; struct mnt_namespace *mnt_ns; struct pid_namespace *pid_ns_for_children; struct net *net_ns; struct time_namespace *time_ns; struct time_namespace *time_ns_for_children; struct cgroup_namespace *cgroup_ns; }; struct bio; struct bio_list { struct bio *head; struct bio *tail; }; struct blk_plug { struct list_head mq_list; struct list_head cb_list; short unsigned int rq_count; bool multiple_queues; bool nowait; }; struct reclaim_state { long unsigned int reclaimed_slab; }; struct fprop_local_percpu { struct percpu_counter events; unsigned int period; raw_spinlock_t lock; }; enum wb_reason { WB_REASON_BACKGROUND = 0, WB_REASON_VMSCAN = 1, WB_REASON_SYNC = 2, WB_REASON_PERIODIC = 3, WB_REASON_LAPTOP_TIMER = 4, WB_REASON_FS_FREE_SPACE = 5, WB_REASON_FORKER_THREAD = 6, WB_REASON_FOREIGN_FLUSH = 7, WB_REASON_MAX = 8, }; struct bdi_writeback { struct backing_dev_info *bdi; long unsigned int state; long unsigned int last_old_flush; struct list_head b_dirty; struct list_head b_io; struct list_head b_more_io; struct list_head b_dirty_time; spinlock_t list_lock; struct percpu_counter stat[4]; long unsigned int congested; long unsigned int bw_time_stamp; long unsigned int dirtied_stamp; long unsigned int written_stamp; long unsigned int write_bandwidth; long unsigned int avg_write_bandwidth; long unsigned int dirty_ratelimit; long unsigned int balanced_dirty_ratelimit; struct fprop_local_percpu completions; int dirty_exceeded; enum wb_reason start_all_reason; spinlock_t work_lock; struct list_head work_list; struct delayed_work dwork; long unsigned int dirty_sleep; struct list_head bdi_node; struct percpu_ref refcnt; struct fprop_local_percpu memcg_completions; struct cgroup_subsys_state *memcg_css; struct cgroup_subsys_state *blkcg_css; struct list_head memcg_node; struct list_head blkcg_node; struct list_head b_attached; struct list_head offline_node; union { struct work_struct release_work; struct callback_head rcu; }; }; struct device; struct backing_dev_info { u64 id; struct rb_node rb_node; struct list_head bdi_list; long unsigned int ra_pages; long unsigned int io_pages; struct kref refcnt; unsigned int capabilities; unsigned int min_ratio; unsigned int max_ratio; unsigned int max_prop_frac; atomic_long_t tot_write_bandwidth; struct bdi_writeback wb; struct list_head wb_list; struct xarray cgwb_tree; struct mutex cgwb_release_mutex; struct rw_semaphore wb_switch_rwsem; wait_queue_head_t wb_waitq; struct device *dev; char dev_name[64]; struct device *owner; struct timer_list laptop_mode_wb_timer; struct dentry *debug_dir; }; struct css_set { struct cgroup_subsys_state *subsys[14]; refcount_t refcount; struct css_set *dom_cset; struct cgroup *dfl_cgrp; int nr_tasks; struct list_head tasks; struct list_head mg_tasks; struct list_head dying_tasks; struct list_head task_iters; struct list_head e_cset_node[14]; struct list_head threaded_csets; struct list_head threaded_csets_node; struct hlist_node hlist; struct list_head cgrp_links; struct list_head mg_preload_node; struct list_head mg_node; struct cgroup *mg_src_cgrp; struct cgroup *mg_dst_cgrp; struct css_set *mg_dst_cset; bool dead; struct callback_head callback_head; }; typedef u32 compat_uptr_t; struct compat_robust_list { compat_uptr_t next; }; typedef s32 compat_long_t; struct compat_robust_list_head { struct compat_robust_list list; compat_long_t futex_offset; compat_uptr_t list_op_pending; }; struct perf_event_groups { struct rb_root tree; u64 index; }; struct perf_event_context { struct pmu *pmu; raw_spinlock_t lock; struct mutex mutex; struct list_head active_ctx_list; struct perf_event_groups pinned_groups; struct perf_event_groups flexible_groups; struct list_head event_list; struct list_head pinned_active; struct list_head flexible_active; int nr_events; int nr_active; int is_active; int nr_stat; int nr_freq; int rotate_disable; int rotate_necessary; refcount_t refcount; struct task_struct *task; u64 time; u64 timestamp; struct perf_event_context *parent_ctx; u64 parent_gen; u64 generation; int pin_count; int nr_cgroups; void *task_ctx_data; struct callback_head callback_head; }; struct task_delay_info { raw_spinlock_t lock; unsigned int flags; u64 blkio_start; u64 blkio_delay; u64 swapin_delay; u32 blkio_count; u32 swapin_count; u64 freepages_start; u64 freepages_delay; u64 thrashing_start; u64 thrashing_delay; u32 freepages_count; u32 thrashing_count; }; struct ftrace_ret_stack { long unsigned int ret; long unsigned int func; long long unsigned int calltime; long long unsigned int subtime; long unsigned int *retp; }; struct blk_integrity_profile; struct blk_integrity { const struct blk_integrity_profile *profile; unsigned char flags; unsigned char tuple_size; unsigned char interval_exp; unsigned char tag_size; }; enum rpm_status { RPM_ACTIVE = 0, RPM_RESUMING = 1, RPM_SUSPENDED = 2, RPM_SUSPENDING = 3, }; struct blk_rq_stat { u64 mean; u64 min; u64 max; u32 nr_samples; u64 batch; }; struct sbitmap_word; struct sbitmap { unsigned int depth; unsigned int shift; unsigned int map_nr; bool round_robin; struct sbitmap_word *map; unsigned int *alloc_hint; }; struct sbq_wait_state; struct sbitmap_queue { struct sbitmap sb; unsigned int wake_batch; atomic_t wake_index; struct sbq_wait_state *ws; atomic_t ws_active; unsigned int min_shallow_depth; }; enum blk_bounce { BLK_BOUNCE_NONE = 0, BLK_BOUNCE_HIGH = 1, }; enum blk_zoned_model { BLK_ZONED_NONE = 0, BLK_ZONED_HA = 1, BLK_ZONED_HM = 2, }; struct queue_limits { enum blk_bounce bounce; long unsigned int seg_boundary_mask; long unsigned int virt_boundary_mask; unsigned int max_hw_sectors; unsigned int max_dev_sectors; unsigned int chunk_sectors; unsigned int max_sectors; unsigned int max_segment_size; unsigned int physical_block_size; unsigned int logical_block_size; unsigned int alignment_offset; unsigned int io_min; unsigned int io_opt; unsigned int max_discard_sectors; unsigned int max_hw_discard_sectors; unsigned int max_write_same_sectors; unsigned int max_write_zeroes_sectors; unsigned int max_zone_append_sectors; unsigned int discard_granularity; unsigned int discard_alignment; unsigned int zone_write_granularity; short unsigned int max_segments; short unsigned int max_integrity_segments; short unsigned int max_discard_segments; unsigned char misaligned; unsigned char discard_misaligned; unsigned char raid_partial_stripes_expensive; enum blk_zoned_model zoned; }; struct bsg_ops; struct bsg_class_device { struct device *class_dev; int minor; struct request_queue *queue; const struct bsg_ops *ops; }; typedef void *mempool_alloc_t(gfp_t, void *); typedef void mempool_free_t(void *, void *); struct mempool_s { spinlock_t lock; int min_nr; int curr_nr; void **elements; void *pool_data; mempool_alloc_t *alloc; mempool_free_t *free; wait_queue_head_t wait; }; typedef struct mempool_s mempool_t; struct bio_set { struct kmem_cache *bio_slab; unsigned int front_pad; mempool_t bio_pool; mempool_t bvec_pool; mempool_t bio_integrity_pool; mempool_t bvec_integrity_pool; unsigned int back_pad; spinlock_t rescue_lock; struct bio_list rescue_list; struct work_struct rescue_work; struct workqueue_struct *rescue_workqueue; }; struct request; struct elevator_queue; struct blk_queue_stats; struct rq_qos; struct blk_mq_ops; struct blk_mq_ctx; struct blk_mq_hw_ctx; struct blk_keyslot_manager; struct blk_stat_callback; struct blkcg_gq; struct blk_trace; struct blk_flush_queue; struct throtl_data; struct blk_mq_tag_set; struct request_queue { struct request *last_merge; struct elevator_queue *elevator; struct percpu_ref q_usage_counter; struct blk_queue_stats *stats; struct rq_qos *rq_qos; const struct blk_mq_ops *mq_ops; struct blk_mq_ctx *queue_ctx; unsigned int queue_depth; struct blk_mq_hw_ctx **queue_hw_ctx; unsigned int nr_hw_queues; struct backing_dev_info *backing_dev_info; void *queuedata; long unsigned int queue_flags; atomic_t pm_only; int id; spinlock_t queue_lock; struct kobject kobj; struct kobject *mq_kobj; struct blk_integrity integrity; struct device *dev; enum rpm_status rpm_status; long unsigned int nr_requests; unsigned int dma_pad_mask; unsigned int dma_alignment; struct blk_keyslot_manager *ksm; unsigned int rq_timeout; int poll_nsec; struct blk_stat_callback *poll_cb; struct blk_rq_stat poll_stat[16]; struct timer_list timeout; struct work_struct timeout_work; atomic_t nr_active_requests_shared_sbitmap; struct sbitmap_queue sched_bitmap_tags; struct sbitmap_queue sched_breserved_tags; struct list_head icq_list; long unsigned int blkcg_pols[1]; struct blkcg_gq *root_blkg; struct list_head blkg_list; struct queue_limits limits; unsigned int required_elevator_features; unsigned int nr_zones; long unsigned int *conv_zones_bitmap; long unsigned int *seq_zones_wlock; unsigned int max_open_zones; unsigned int max_active_zones; unsigned int sg_timeout; unsigned int sg_reserved_size; int node; struct mutex debugfs_mutex; struct blk_trace *blk_trace; struct blk_flush_queue *fq; struct list_head requeue_list; spinlock_t requeue_lock; struct delayed_work requeue_work; struct mutex sysfs_lock; struct mutex sysfs_dir_lock; struct list_head unused_hctx_list; spinlock_t unused_hctx_lock; int mq_freeze_depth; struct bsg_class_device bsg_dev; struct throtl_data *td; struct callback_head callback_head; wait_queue_head_t mq_freeze_wq; struct mutex mq_freeze_lock; struct blk_mq_tag_set *tag_set; struct list_head tag_set_list; struct bio_set bio_split; struct dentry *debugfs_dir; struct dentry *sched_debugfs_dir; struct dentry *rqos_debugfs_dir; bool mq_sysfs_init_done; size_t cmd_size; u64 write_hints[5]; }; struct cgroup_base_stat { struct task_cputime cputime; }; struct psi_group_cpu; struct psi_group { struct mutex avgs_lock; struct psi_group_cpu *pcpu; u64 avg_total[6]; u64 avg_last_update; u64 avg_next_update; struct delayed_work avgs_work; u64 total[12]; long unsigned int avg[18]; struct task_struct *poll_task; struct timer_list poll_timer; wait_queue_head_t poll_wait; atomic_t poll_wakeup; struct mutex trigger_lock; struct list_head triggers; u32 nr_triggers[6]; u32 poll_states; u64 poll_min_period; u64 polling_total[6]; u64 polling_next_update; u64 polling_until; }; struct cgroup_bpf { struct bpf_prog_array *effective[41]; struct list_head progs[41]; u32 flags[41]; struct list_head storages; struct bpf_prog_array *inactive; struct percpu_ref refcnt; struct work_struct release_work; }; struct cgroup_freezer_state { bool freeze; int e_freeze; int nr_frozen_descendants; int nr_frozen_tasks; }; struct cgroup_root; struct cgroup_rstat_cpu; struct cgroup { struct cgroup_subsys_state self; long unsigned int flags; int level; int max_depth; int nr_descendants; int nr_dying_descendants; int max_descendants; int nr_populated_csets; int nr_populated_domain_children; int nr_populated_threaded_children; int nr_threaded_children; struct kernfs_node *kn; struct cgroup_file procs_file; struct cgroup_file events_file; u16 subtree_control; u16 subtree_ss_mask; u16 old_subtree_control; u16 old_subtree_ss_mask; struct cgroup_subsys_state *subsys[14]; struct cgroup_root *root; struct list_head cset_links; struct list_head e_csets[14]; struct cgroup *dom_cgrp; struct cgroup *old_dom_cgrp; struct cgroup_rstat_cpu *rstat_cpu; struct list_head rstat_css_list; struct cgroup_base_stat last_bstat; struct cgroup_base_stat bstat; struct prev_cputime prev_cputime; struct list_head pidlists; struct mutex pidlist_mutex; wait_queue_head_t offline_waitq; struct work_struct release_agent_work; struct psi_group psi; struct cgroup_bpf bpf; atomic_t congestion_count; struct cgroup_freezer_state freezer; u64 ancestor_ids[0]; }; struct taskstats { __u16 version; __u32 ac_exitcode; __u8 ac_flag; __u8 ac_nice; __u64 cpu_count; __u64 cpu_delay_total; __u64 blkio_count; __u64 blkio_delay_total; __u64 swapin_count; __u64 swapin_delay_total; __u64 cpu_run_real_total; __u64 cpu_run_virtual_total; char ac_comm[32]; __u8 ac_sched; __u8 ac_pad[3]; int: 32; __u32 ac_uid; __u32 ac_gid; __u32 ac_pid; __u32 ac_ppid; __u32 ac_btime; __u64 ac_etime; __u64 ac_utime; __u64 ac_stime; __u64 ac_minflt; __u64 ac_majflt; __u64 coremem; __u64 virtmem; __u64 hiwater_rss; __u64 hiwater_vm; __u64 read_char; __u64 write_char; __u64 read_syscalls; __u64 write_syscalls; __u64 read_bytes; __u64 write_bytes; __u64 cancelled_write_bytes; __u64 nvcsw; __u64 nivcsw; __u64 ac_utimescaled; __u64 ac_stimescaled; __u64 cpu_scaled_run_real_total; __u64 freepages_count; __u64 freepages_delay_total; __u64 thrashing_count; __u64 thrashing_delay_total; __u64 ac_btime64; }; typedef struct { __u8 b[16]; } guid_t; struct wait_page_queue { struct page *page; int bit_nr; wait_queue_entry_t wait; }; enum writeback_sync_modes { WB_SYNC_NONE = 0, WB_SYNC_ALL = 1, }; struct writeback_control { long int nr_to_write; long int pages_skipped; loff_t range_start; loff_t range_end; enum writeback_sync_modes sync_mode; unsigned int for_kupdate: 1; unsigned int for_background: 1; unsigned int tagged_writepages: 1; unsigned int for_reclaim: 1; unsigned int range_cyclic: 1; unsigned int for_sync: 1; unsigned int no_cgroup_owner: 1; unsigned int punt_to_cgroup: 1; struct bdi_writeback *wb; struct inode *inode; int wb_id; int wb_lcand_id; int wb_tcand_id; size_t wb_bytes; size_t wb_lcand_bytes; size_t wb_tcand_bytes; }; struct readahead_control { struct file *file; struct address_space *mapping; struct file_ra_state *ra; long unsigned int _index; unsigned int _nr_pages; unsigned int _batch_count; }; struct iovec; struct kvec; struct bio_vec; struct iov_iter { u8 iter_type; bool data_source; size_t iov_offset; size_t count; union { const struct iovec *iov; const struct kvec *kvec; const struct bio_vec *bvec; struct xarray *xarray; struct pipe_inode_info *pipe; }; union { long unsigned int nr_segs; struct { unsigned int head; unsigned int start_head; }; loff_t xarray_start; }; }; struct swap_cluster_info { spinlock_t lock; unsigned int data: 24; unsigned int flags: 8; }; struct swap_cluster_list { struct swap_cluster_info head; struct swap_cluster_info tail; }; struct percpu_cluster; struct swap_info_struct { struct percpu_ref users; long unsigned int flags; short int prio; struct plist_node list; signed char type; unsigned int max; unsigned char *swap_map; struct swap_cluster_info *cluster_info; struct swap_cluster_list free_clusters; unsigned int lowest_bit; unsigned int highest_bit; unsigned int pages; unsigned int inuse_pages; unsigned int cluster_next; unsigned int cluster_nr; unsigned int *cluster_next_cpu; struct percpu_cluster *percpu_cluster; struct rb_root swap_extent_root; struct block_device *bdev; struct file *swap_file; unsigned int old_block_size; struct completion comp; long unsigned int *frontswap_map; atomic_t frontswap_pages; spinlock_t lock; spinlock_t cont_lock; struct work_struct discard_work; struct swap_cluster_list discard_clusters; struct plist_node avail_lists[0]; }; struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; }; enum dl_dev_state { DL_DEV_NO_DRIVER = 0, DL_DEV_PROBING = 1, DL_DEV_DRIVER_BOUND = 2, DL_DEV_UNBINDING = 3, }; struct dev_links_info { struct list_head suppliers; struct list_head consumers; struct list_head defer_sync; enum dl_dev_state status; }; struct pm_message { int event; }; typedef struct pm_message pm_message_t; enum rpm_request { RPM_REQ_NONE = 0, RPM_REQ_IDLE = 1, RPM_REQ_SUSPEND = 2, RPM_REQ_AUTOSUSPEND = 3, RPM_REQ_RESUME = 4, }; struct wakeup_source; struct wake_irq; struct pm_subsys_data; struct dev_pm_qos; struct dev_pm_info { pm_message_t power_state; unsigned int can_wakeup: 1; unsigned int async_suspend: 1; bool in_dpm_list: 1; bool is_prepared: 1; bool is_suspended: 1; bool is_noirq_suspended: 1; bool is_late_suspended: 1; bool no_pm: 1; bool early_init: 1; bool direct_complete: 1; u32 driver_flags; spinlock_t lock; struct list_head entry; struct completion completion; struct wakeup_source *wakeup; bool wakeup_path: 1; bool syscore: 1; bool no_pm_callbacks: 1; unsigned int must_resume: 1; unsigned int may_skip_resume: 1; struct hrtimer suspend_timer; u64 timer_expires; struct work_struct work; wait_queue_head_t wait_queue; struct wake_irq *wakeirq; atomic_t usage_count; atomic_t child_count; unsigned int disable_depth: 3; unsigned int idle_notification: 1; unsigned int request_pending: 1; unsigned int deferred_resume: 1; unsigned int needs_force_resume: 1; unsigned int runtime_auto: 1; bool ignore_children: 1; unsigned int no_callbacks: 1; unsigned int irq_safe: 1; unsigned int use_autosuspend: 1; unsigned int timer_autosuspends: 1; unsigned int memalloc_noio: 1; unsigned int links_count; enum rpm_request request; enum rpm_status runtime_status; int runtime_error; int autosuspend_delay; u64 last_busy; u64 active_time; u64 suspended_time; u64 accounting_timestamp; struct pm_subsys_data *subsys_data; void (*set_latency_tolerance)(struct device *, s32); struct dev_pm_qos *qos; }; struct dev_archdata {}; enum device_removable { DEVICE_REMOVABLE_NOT_SUPPORTED = 0, DEVICE_REMOVABLE_UNKNOWN = 1, DEVICE_FIXED = 2, DEVICE_REMOVABLE = 3, }; struct device_private; struct device_type; struct bus_type; struct device_driver; struct dev_pm_domain; struct em_perf_domain; struct dev_pin_info; struct dma_map_ops; struct bus_dma_region; struct device_dma_parameters; struct cma; struct device_node; struct class; struct iommu_group; struct dev_iommu; struct device { struct kobject kobj; struct device *parent; struct device_private *p; const char *init_name; const struct device_type *type; struct bus_type *bus; struct device_driver *driver; void *platform_data; void *driver_data; struct mutex mutex; struct dev_links_info links; struct dev_pm_info power; struct dev_pm_domain *pm_domain; struct em_perf_domain *em_pd; struct irq_domain *msi_domain; struct dev_pin_info *pins; raw_spinlock_t msi_lock; struct list_head msi_list; const struct dma_map_ops *dma_ops; u64 *dma_mask; u64 coherent_dma_mask; u64 bus_dma_limit; const struct bus_dma_region *dma_range_map; struct device_dma_parameters *dma_parms; struct list_head dma_pools; struct cma *cma_area; struct dev_archdata archdata; struct device_node *of_node; struct fwnode_handle *fwnode; int numa_node; dev_t devt; u32 id; spinlock_t devres_lock; struct list_head devres_head; struct class *class; const struct attribute_group **groups; void (*release)(struct device *); struct iommu_group *iommu_group; struct dev_iommu *iommu; enum device_removable removable; bool offline_disabled: 1; bool offline: 1; bool of_node_reused: 1; bool state_synced: 1; bool can_match: 1; }; struct disk_stats; struct gendisk; struct partition_meta_info; struct block_device { sector_t bd_start_sect; struct disk_stats *bd_stats; long unsigned int bd_stamp; bool bd_read_only; dev_t bd_dev; int bd_openers; struct inode *bd_inode; struct super_block *bd_super; void *bd_claiming; struct device bd_device; void *bd_holder; int bd_holders; bool bd_write_holder; struct list_head bd_holder_disks; struct kobject *bd_holder_dir; u8 bd_partno; spinlock_t bd_size_lock; struct gendisk *bd_disk; struct backing_dev_info *bd_bdi; int bd_fsfreeze_count; struct mutex bd_fsfreeze_mutex; struct super_block *bd_fsfreeze_sb; struct partition_meta_info *bd_meta_info; }; struct fc_log; struct p_log { const char *prefix; struct fc_log *log; }; enum fs_context_purpose { FS_CONTEXT_FOR_MOUNT = 0, FS_CONTEXT_FOR_SUBMOUNT = 1, FS_CONTEXT_FOR_RECONFIGURE = 2, }; enum fs_context_phase { FS_CONTEXT_CREATE_PARAMS = 0, FS_CONTEXT_CREATING = 1, FS_CONTEXT_AWAITING_MOUNT = 2, FS_CONTEXT_AWAITING_RECONF = 3, FS_CONTEXT_RECONF_PARAMS = 4, FS_CONTEXT_RECONFIGURING = 5, FS_CONTEXT_FAILED = 6, }; struct fs_context_operations; struct fs_context { const struct fs_context_operations *ops; struct mutex uapi_mutex; struct file_system_type *fs_type; void *fs_private; void *sget_key; struct dentry *root; struct user_namespace *user_ns; struct net *net_ns; const struct cred *cred; struct p_log log; const char *source; void *security; void *s_fs_info; unsigned int sb_flags; unsigned int sb_flags_mask; unsigned int s_iflags; unsigned int lsm_flags; enum fs_context_purpose purpose: 8; enum fs_context_phase phase: 8; bool need_free: 1; bool global: 1; bool oldapi: 1; }; struct audit_names; struct filename { const char *name; const char *uptr; int refcnt; struct audit_names *aname; const char iname[0]; }; typedef u8 blk_status_t; struct bvec_iter { sector_t bi_sector; unsigned int bi_size; unsigned int bi_idx; unsigned int bi_bvec_done; }; typedef void bio_end_io_t(struct bio *); struct bio_issue { u64 value; }; struct bio_vec { struct page *bv_page; unsigned int bv_len; unsigned int bv_offset; }; struct bio_crypt_ctx; struct bio_integrity_payload; struct bio { struct bio *bi_next; struct block_device *bi_bdev; unsigned int bi_opf; short unsigned int bi_flags; short unsigned int bi_ioprio; short unsigned int bi_write_hint; blk_status_t bi_status; atomic_t __bi_remaining; struct bvec_iter bi_iter; bio_end_io_t *bi_end_io; void *bi_private; struct blkcg_gq *bi_blkg; struct bio_issue bi_issue; u64 bi_iocost_cost; struct bio_crypt_ctx *bi_crypt_context; union { struct bio_integrity_payload *bi_integrity; }; short unsigned int bi_vcnt; short unsigned int bi_max_vecs; atomic_t __bi_cnt; struct bio_vec *bi_io_vec; struct bio_set *bi_pool; struct bio_vec bi_inline_vecs[0]; }; struct linux_binprm { struct vm_area_struct *vma; long unsigned int vma_pages; struct mm_struct *mm; long unsigned int p; long unsigned int argmin; unsigned int have_execfd: 1; unsigned int execfd_creds: 1; unsigned int secureexec: 1; unsigned int point_of_no_return: 1; struct file *executable; struct file *interpreter; struct file *file; struct cred *cred; int unsafe; unsigned int per_clear; int argc; int envc; const char *filename; const char *interp; const char *fdpath; unsigned int interp_flags; int execfd; long unsigned int loader; long unsigned int exec; struct rlimit rlim_stack; char buf[256]; }; struct coredump_params { const kernel_siginfo_t *siginfo; struct pt_regs *regs; struct file *file; long unsigned int limit; long unsigned int mm_flags; loff_t written; loff_t pos; loff_t to_skip; }; struct em_perf_state { long unsigned int frequency; long unsigned int power; long unsigned int cost; }; struct em_perf_domain { struct em_perf_state *table; int nr_perf_states; int milliwatts; long unsigned int cpus[0]; }; struct dev_pm_ops { int (*prepare)(struct device *); void (*complete)(struct device *); int (*suspend)(struct device *); int (*resume)(struct device *); int (*freeze)(struct device *); int (*thaw)(struct device *); int (*poweroff)(struct device *); int (*restore)(struct device *); int (*suspend_late)(struct device *); int (*resume_early)(struct device *); int (*freeze_late)(struct device *); int (*thaw_early)(struct device *); int (*poweroff_late)(struct device *); int (*restore_early)(struct device *); int (*suspend_noirq)(struct device *); int (*resume_noirq)(struct device *); int (*freeze_noirq)(struct device *); int (*thaw_noirq)(struct device *); int (*poweroff_noirq)(struct device *); int (*restore_noirq)(struct device *); int (*runtime_suspend)(struct device *); int (*runtime_resume)(struct device *); int (*runtime_idle)(struct device *); }; struct pm_domain_data; struct pm_subsys_data { spinlock_t lock; unsigned int refcount; unsigned int clock_op_might_sleep; struct mutex clock_mutex; struct list_head clock_list; struct pm_domain_data *domain_data; }; struct wakeup_source { const char *name; int id; struct list_head entry; spinlock_t lock; struct wake_irq *wakeirq; struct timer_list timer; long unsigned int timer_expires; ktime_t total_time; ktime_t max_time; ktime_t last_time; ktime_t start_prevent_time; ktime_t prevent_sleep_time; long unsigned int event_count; long unsigned int active_count; long unsigned int relax_count; long unsigned int expire_count; long unsigned int wakeup_count; struct device *dev; bool active: 1; bool autosleep_enabled: 1; }; struct dev_pm_domain { struct dev_pm_ops ops; int (*start)(struct device *); void (*detach)(struct device *, bool); int (*activate)(struct device *); void (*sync)(struct device *); void (*dismiss)(struct device *); }; struct iommu_ops; struct subsys_private; struct bus_type { const char *name; const char *dev_name; struct device *dev_root; const struct attribute_group **bus_groups; const struct attribute_group **dev_groups; const struct attribute_group **drv_groups; int (*match)(struct device *, struct device_driver *); int (*uevent)(struct device *, struct kobj_uevent_env *); int (*probe)(struct device *); void (*sync_state)(struct device *); int (*remove)(struct device *); void (*shutdown)(struct device *); int (*online)(struct device *); int (*offline)(struct device *); int (*suspend)(struct device *, pm_message_t); int (*resume)(struct device *); int (*num_vf)(struct device *); int (*dma_configure)(struct device *); const struct dev_pm_ops *pm; const struct iommu_ops *iommu_ops; struct subsys_private *p; struct lock_class_key lock_key; bool need_parent_lock; }; enum probe_type { PROBE_DEFAULT_STRATEGY = 0, PROBE_PREFER_ASYNCHRONOUS = 1, PROBE_FORCE_SYNCHRONOUS = 2, }; struct of_device_id; struct acpi_device_id; struct driver_private; struct device_driver { const char *name; struct bus_type *bus; struct module *owner; const char *mod_name; bool suppress_bind_attrs; enum probe_type probe_type; const struct of_device_id *of_match_table; const struct acpi_device_id *acpi_match_table; int (*probe)(struct device *); void (*sync_state)(struct device *); int (*remove)(struct device *); void (*shutdown)(struct device *); int (*suspend)(struct device *, pm_message_t); int (*resume)(struct device *); const struct attribute_group **groups; const struct attribute_group **dev_groups; const struct dev_pm_ops *pm; void (*coredump)(struct device *); struct driver_private *p; }; enum iommu_cap { IOMMU_CAP_CACHE_COHERENCY = 0, IOMMU_CAP_INTR_REMAP = 1, IOMMU_CAP_NOEXEC = 2, }; typedef u64 dma_addr_t; enum iommu_dev_features { IOMMU_DEV_FEAT_AUX = 0, IOMMU_DEV_FEAT_SVA = 1, IOMMU_DEV_FEAT_IOPF = 2, }; struct iommu_domain; struct iommu_iotlb_gather; struct iommu_device; struct iommu_resv_region; struct of_phandle_args; struct iommu_sva; struct iommu_fault_event; struct iommu_page_response; struct iommu_cache_invalidate_info; struct iommu_gpasid_bind_data; struct iommu_ops { bool (*capable)(enum iommu_cap); struct iommu_domain * (*domain_alloc)(unsigned int); void (*domain_free)(struct iommu_domain *); int (*attach_dev)(struct iommu_domain *, struct device *); void (*detach_dev)(struct iommu_domain *, struct device *); int (*map)(struct iommu_domain *, long unsigned int, phys_addr_t, size_t, int, gfp_t); size_t (*unmap)(struct iommu_domain *, long unsigned int, size_t, struct iommu_iotlb_gather *); void (*flush_iotlb_all)(struct iommu_domain *); void (*iotlb_sync_map)(struct iommu_domain *, long unsigned int, size_t); void (*iotlb_sync)(struct iommu_domain *, struct iommu_iotlb_gather *); phys_addr_t (*iova_to_phys)(struct iommu_domain *, dma_addr_t); struct iommu_device * (*probe_device)(struct device *); void (*release_device)(struct device *); void (*probe_finalize)(struct device *); struct iommu_group * (*device_group)(struct device *); int (*enable_nesting)(struct iommu_domain *); int (*set_pgtable_quirks)(struct iommu_domain *, long unsigned int); void (*get_resv_regions)(struct device *, struct list_head *); void (*put_resv_regions)(struct device *, struct list_head *); void (*apply_resv_region)(struct device *, struct iommu_domain *, struct iommu_resv_region *); int (*of_xlate)(struct device *, struct of_phandle_args *); bool (*is_attach_deferred)(struct iommu_domain *, struct device *); bool (*dev_has_feat)(struct device *, enum iommu_dev_features); bool (*dev_feat_enabled)(struct device *, enum iommu_dev_features); int (*dev_enable_feat)(struct device *, enum iommu_dev_features); int (*dev_disable_feat)(struct device *, enum iommu_dev_features); int (*aux_attach_dev)(struct iommu_domain *, struct device *); void (*aux_detach_dev)(struct iommu_domain *, struct device *); int (*aux_get_pasid)(struct iommu_domain *, struct device *); struct iommu_sva * (*sva_bind)(struct device *, struct mm_struct *, void *); void (*sva_unbind)(struct iommu_sva *); u32 (*sva_get_pasid)(struct iommu_sva *); int (*page_response)(struct device *, struct iommu_fault_event *, struct iommu_page_response *); int (*cache_invalidate)(struct iommu_domain *, struct device *, struct iommu_cache_invalidate_info *); int (*sva_bind_gpasid)(struct iommu_domain *, struct device *, struct iommu_gpasid_bind_data *); int (*sva_unbind_gpasid)(struct device *, u32); int (*def_domain_type)(struct device *); long unsigned int pgsize_bitmap; struct module *owner; }; struct device_type { const char *name; const struct attribute_group **groups; int (*uevent)(struct device *, struct kobj_uevent_env *); char * (*devnode)(struct device *, umode_t *, kuid_t *, kgid_t *); void (*release)(struct device *); const struct dev_pm_ops *pm; }; struct class { const char *name; struct module *owner; const struct attribute_group **class_groups; const struct attribute_group **dev_groups; struct kobject *dev_kobj; int (*dev_uevent)(struct device *, struct kobj_uevent_env *); char * (*devnode)(struct device *, umode_t *); void (*class_release)(struct class *); void (*dev_release)(struct device *); int (*shutdown_pre)(struct device *); const struct kobj_ns_type_operations *ns_type; const void * (*namespace)(struct device *); void (*get_ownership)(struct device *, kuid_t *, kgid_t *); const struct dev_pm_ops *pm; struct subsys_private *p; }; struct of_device_id { char name[32]; char type[32]; char compatible[128]; const void *data; }; typedef long unsigned int kernel_ulong_t; struct acpi_device_id { __u8 id[9]; kernel_ulong_t driver_data; __u32 cls; __u32 cls_msk; }; struct device_dma_parameters { unsigned int max_segment_size; unsigned int min_align_mask; long unsigned int segment_boundary_mask; }; enum dma_data_direction { DMA_BIDIRECTIONAL = 0, DMA_TO_DEVICE = 1, DMA_FROM_DEVICE = 2, DMA_NONE = 3, }; struct sg_table; struct scatterlist; struct dma_map_ops { void * (*alloc)(struct device *, size_t, dma_addr_t *, gfp_t, long unsigned int); void (*free)(struct device *, size_t, void *, dma_addr_t, long unsigned int); struct page * (*alloc_pages)(struct device *, size_t, dma_addr_t *, enum dma_data_direction, gfp_t); void (*free_pages)(struct device *, size_t, struct page *, dma_addr_t, enum dma_data_direction); struct sg_table * (*alloc_noncontiguous)(struct device *, size_t, enum dma_data_direction, gfp_t, long unsigned int); void (*free_noncontiguous)(struct device *, size_t, struct sg_table *, enum dma_data_direction); int (*mmap)(struct device *, struct vm_area_struct *, void *, dma_addr_t, size_t, long unsigned int); int (*get_sgtable)(struct device *, struct sg_table *, void *, dma_addr_t, size_t, long unsigned int); dma_addr_t (*map_page)(struct device *, struct page *, long unsigned int, size_t, enum dma_data_direction, long unsigned int); void (*unmap_page)(struct device *, dma_addr_t, size_t, enum dma_data_direction, long unsigned int); int (*map_sg)(struct device *, struct scatterlist *, int, enum dma_data_direction, long unsigned int); void (*unmap_sg)(struct device *, struct scatterlist *, int, enum dma_data_direction, long unsigned int); dma_addr_t (*map_resource)(struct device *, phys_addr_t, size_t, enum dma_data_direction, long unsigned int); void (*unmap_resource)(struct device *, dma_addr_t, size_t, enum dma_data_direction, long unsigned int); void (*sync_single_for_cpu)(struct device *, dma_addr_t, size_t, enum dma_data_direction); void (*sync_single_for_device)(struct device *, dma_addr_t, size_t, enum dma_data_direction); void (*sync_sg_for_cpu)(struct device *, struct scatterlist *, int, enum dma_data_direction); void (*sync_sg_for_device)(struct device *, struct scatterlist *, int, enum dma_data_direction); void (*cache_sync)(struct device *, void *, size_t, enum dma_data_direction); int (*dma_supported)(struct device *, u64); u64 (*get_required_mask)(struct device *); size_t (*max_mapping_size)(struct device *); long unsigned int (*get_merge_boundary)(struct device *); }; struct bus_dma_region { phys_addr_t cpu_start; dma_addr_t dma_start; u64 size; u64 offset; }; typedef u32 phandle; struct fwnode_operations; struct fwnode_handle { struct fwnode_handle *secondary; const struct fwnode_operations *ops; struct device *dev; struct list_head suppliers; struct list_head consumers; u8 flags; }; struct property; struct device_node { const char *name; phandle phandle; const char *full_name; struct fwnode_handle fwnode; struct property *properties; struct property *deadprops; struct device_node *parent; struct device_node *child; struct device_node *sibling; long unsigned int _flags; void *data; }; enum cpuhp_state { CPUHP_INVALID = 4294967295, CPUHP_OFFLINE = 0, CPUHP_CREATE_THREADS = 1, CPUHP_PERF_PREPARE = 2, CPUHP_PERF_X86_PREPARE = 3, CPUHP_PERF_X86_AMD_UNCORE_PREP = 4, CPUHP_PERF_POWER = 5, CPUHP_PERF_SUPERH = 6, CPUHP_X86_HPET_DEAD = 7, CPUHP_X86_APB_DEAD = 8, CPUHP_X86_MCE_DEAD = 9, CPUHP_VIRT_NET_DEAD = 10, CPUHP_SLUB_DEAD = 11, CPUHP_DEBUG_OBJ_DEAD = 12, CPUHP_MM_WRITEBACK_DEAD = 13, CPUHP_MM_VMSTAT_DEAD = 14, CPUHP_SOFTIRQ_DEAD = 15, CPUHP_NET_MVNETA_DEAD = 16, CPUHP_CPUIDLE_DEAD = 17, CPUHP_ARM64_FPSIMD_DEAD = 18, CPUHP_ARM_OMAP_WAKE_DEAD = 19, CPUHP_IRQ_POLL_DEAD = 20, CPUHP_BLOCK_SOFTIRQ_DEAD = 21, CPUHP_ACPI_CPUDRV_DEAD = 22, CPUHP_S390_PFAULT_DEAD = 23, CPUHP_BLK_MQ_DEAD = 24, CPUHP_FS_BUFF_DEAD = 25, CPUHP_PRINTK_DEAD = 26, CPUHP_MM_MEMCQ_DEAD = 27, CPUHP_PERCPU_CNT_DEAD = 28, CPUHP_RADIX_DEAD = 29, CPUHP_PAGE_ALLOC = 30, CPUHP_NET_DEV_DEAD = 31, CPUHP_PCI_XGENE_DEAD = 32, CPUHP_IOMMU_IOVA_DEAD = 33, CPUHP_LUSTRE_CFS_DEAD = 34, CPUHP_AP_ARM_CACHE_B15_RAC_DEAD = 35, CPUHP_PADATA_DEAD = 36, CPUHP_WORKQUEUE_PREP = 37, CPUHP_POWER_NUMA_PREPARE = 38, CPUHP_HRTIMERS_PREPARE = 39, CPUHP_PROFILE_PREPARE = 40, CPUHP_X2APIC_PREPARE = 41, CPUHP_SMPCFD_PREPARE = 42, CPUHP_RELAY_PREPARE = 43, CPUHP_SLAB_PREPARE = 44, CPUHP_MD_RAID5_PREPARE = 45, CPUHP_RCUTREE_PREP = 46, CPUHP_CPUIDLE_COUPLED_PREPARE = 47, CPUHP_POWERPC_PMAC_PREPARE = 48, CPUHP_POWERPC_MMU_CTX_PREPARE = 49, CPUHP_XEN_PREPARE = 50, CPUHP_XEN_EVTCHN_PREPARE = 51, CPUHP_ARM_SHMOBILE_SCU_PREPARE = 52, CPUHP_SH_SH3X_PREPARE = 53, CPUHP_NET_FLOW_PREPARE = 54, CPUHP_TOPOLOGY_PREPARE = 55, CPUHP_NET_IUCV_PREPARE = 56, CPUHP_ARM_BL_PREPARE = 57, CPUHP_TRACE_RB_PREPARE = 58, CPUHP_MM_ZS_PREPARE = 59, CPUHP_MM_ZSWP_MEM_PREPARE = 60, CPUHP_MM_ZSWP_POOL_PREPARE = 61, CPUHP_KVM_PPC_BOOK3S_PREPARE = 62, CPUHP_ZCOMP_PREPARE = 63, CPUHP_TIMERS_PREPARE = 64, CPUHP_MIPS_SOC_PREPARE = 65, CPUHP_BP_PREPARE_DYN = 66, CPUHP_BP_PREPARE_DYN_END = 86, CPUHP_BRINGUP_CPU = 87, CPUHP_AP_IDLE_DEAD = 88, CPUHP_AP_OFFLINE = 89, CPUHP_AP_SCHED_STARTING = 90, CPUHP_AP_RCUTREE_DYING = 91, CPUHP_AP_CPU_PM_STARTING = 92, CPUHP_AP_IRQ_GIC_STARTING = 93, CPUHP_AP_IRQ_HIP04_STARTING = 94, CPUHP_AP_IRQ_APPLE_AIC_STARTING = 95, CPUHP_AP_IRQ_ARMADA_XP_STARTING = 96, CPUHP_AP_IRQ_BCM2836_STARTING = 97, CPUHP_AP_IRQ_MIPS_GIC_STARTING = 98, CPUHP_AP_IRQ_RISCV_STARTING = 99, CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING = 100, CPUHP_AP_ARM_MVEBU_COHERENCY = 101, CPUHP_AP_MICROCODE_LOADER = 102, CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING = 103, CPUHP_AP_PERF_X86_STARTING = 104, CPUHP_AP_PERF_X86_AMD_IBS_STARTING = 105, CPUHP_AP_PERF_X86_CQM_STARTING = 106, CPUHP_AP_PERF_X86_CSTATE_STARTING = 107, CPUHP_AP_PERF_XTENSA_STARTING = 108, CPUHP_AP_MIPS_OP_LOONGSON3_STARTING = 109, CPUHP_AP_ARM_SDEI_STARTING = 110, CPUHP_AP_ARM_VFP_STARTING = 111, CPUHP_AP_ARM64_DEBUG_MONITORS_STARTING = 112, CPUHP_AP_PERF_ARM_HW_BREAKPOINT_STARTING = 113, CPUHP_AP_PERF_ARM_ACPI_STARTING = 114, CPUHP_AP_PERF_ARM_STARTING = 115, CPUHP_AP_ARM_L2X0_STARTING = 116, CPUHP_AP_EXYNOS4_MCT_TIMER_STARTING = 117, CPUHP_AP_ARM_ARCH_TIMER_STARTING = 118, CPUHP_AP_ARM_GLOBAL_TIMER_STARTING = 119, CPUHP_AP_JCORE_TIMER_STARTING = 120, CPUHP_AP_ARM_TWD_STARTING = 121, CPUHP_AP_QCOM_TIMER_STARTING = 122, CPUHP_AP_TEGRA_TIMER_STARTING = 123, CPUHP_AP_ARMADA_TIMER_STARTING = 124, CPUHP_AP_MARCO_TIMER_STARTING = 125, CPUHP_AP_MIPS_GIC_TIMER_STARTING = 126, CPUHP_AP_ARC_TIMER_STARTING = 127, CPUHP_AP_RISCV_TIMER_STARTING = 128, CPUHP_AP_CLINT_TIMER_STARTING = 129, CPUHP_AP_CSKY_TIMER_STARTING = 130, CPUHP_AP_TI_GP_TIMER_STARTING = 131, CPUHP_AP_HYPERV_TIMER_STARTING = 132, CPUHP_AP_KVM_STARTING = 133, CPUHP_AP_KVM_ARM_VGIC_INIT_STARTING = 134, CPUHP_AP_KVM_ARM_VGIC_STARTING = 135, CPUHP_AP_KVM_ARM_TIMER_STARTING = 136, CPUHP_AP_DUMMY_TIMER_STARTING = 137, CPUHP_AP_ARM_XEN_STARTING = 138, CPUHP_AP_ARM_CORESIGHT_STARTING = 139, CPUHP_AP_ARM_CORESIGHT_CTI_STARTING = 140, CPUHP_AP_ARM64_ISNDEP_STARTING = 141, CPUHP_AP_SMPCFD_DYING = 142, CPUHP_AP_X86_TBOOT_DYING = 143, CPUHP_AP_ARM_CACHE_B15_RAC_DYING = 144, CPUHP_AP_ONLINE = 145, CPUHP_TEARDOWN_CPU = 146, CPUHP_AP_ONLINE_IDLE = 147, CPUHP_AP_SCHED_WAIT_EMPTY = 148, CPUHP_AP_SMPBOOT_THREADS = 149, CPUHP_AP_X86_VDSO_VMA_ONLINE = 150, CPUHP_AP_IRQ_AFFINITY_ONLINE = 151, CPUHP_AP_BLK_MQ_ONLINE = 152, CPUHP_AP_ARM_MVEBU_SYNC_CLOCKS = 153, CPUHP_AP_X86_INTEL_EPB_ONLINE = 154, CPUHP_AP_PERF_ONLINE = 155, CPUHP_AP_PERF_X86_ONLINE = 156, CPUHP_AP_PERF_X86_UNCORE_ONLINE = 157, CPUHP_AP_PERF_X86_AMD_UNCORE_ONLINE = 158, CPUHP_AP_PERF_X86_AMD_POWER_ONLINE = 159, CPUHP_AP_PERF_X86_RAPL_ONLINE = 160, CPUHP_AP_PERF_X86_CQM_ONLINE = 161, CPUHP_AP_PERF_X86_CSTATE_ONLINE = 162, CPUHP_AP_PERF_X86_IDXD_ONLINE = 163, CPUHP_AP_PERF_S390_CF_ONLINE = 164, CPUHP_AP_PERF_S390_SF_ONLINE = 165, CPUHP_AP_PERF_ARM_CCI_ONLINE = 166, CPUHP_AP_PERF_ARM_CCN_ONLINE = 167, CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE = 168, CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE = 169, CPUHP_AP_PERF_ARM_HISI_L3_ONLINE = 170, CPUHP_AP_PERF_ARM_HISI_PA_ONLINE = 171, CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE = 172, CPUHP_AP_PERF_ARM_L2X0_ONLINE = 173, CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE = 174, CPUHP_AP_PERF_ARM_QCOM_L3_ONLINE = 175, CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE = 176, CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE = 177, CPUHP_AP_PERF_POWERPC_NEST_IMC_ONLINE = 178, CPUHP_AP_PERF_POWERPC_CORE_IMC_ONLINE = 179, CPUHP_AP_PERF_POWERPC_THREAD_IMC_ONLINE = 180, CPUHP_AP_PERF_POWERPC_TRACE_IMC_ONLINE = 181, CPUHP_AP_PERF_POWERPC_HV_24x7_ONLINE = 182, CPUHP_AP_PERF_POWERPC_HV_GPCI_ONLINE = 183, CPUHP_AP_PERF_CSKY_ONLINE = 184, CPUHP_AP_WATCHDOG_ONLINE = 185, CPUHP_AP_WORKQUEUE_ONLINE = 186, CPUHP_AP_RCUTREE_ONLINE = 187, CPUHP_AP_BASE_CACHEINFO_ONLINE = 188, CPUHP_AP_ONLINE_DYN = 189, CPUHP_AP_ONLINE_DYN_END = 219, CPUHP_AP_X86_HPET_ONLINE = 220, CPUHP_AP_X86_KVM_CLK_ONLINE = 221, CPUHP_AP_DTPM_CPU_ONLINE = 222, CPUHP_AP_ACTIVE = 223, CPUHP_ONLINE = 224, }; struct ring_buffer_event { u32 type_len: 5; u32 time_delta: 27; u32 array[0]; }; struct seq_buf { char *buffer; size_t size; size_t len; loff_t readpos; }; struct trace_seq { char buffer[4096]; struct seq_buf seq; int full; }; enum perf_sw_ids { PERF_COUNT_SW_CPU_CLOCK = 0, PERF_COUNT_SW_TASK_CLOCK = 1, PERF_COUNT_SW_PAGE_FAULTS = 2, PERF_COUNT_SW_CONTEXT_SWITCHES = 3, PERF_COUNT_SW_CPU_MIGRATIONS = 4, PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, PERF_COUNT_SW_EMULATION_FAULTS = 8, PERF_COUNT_SW_DUMMY = 9, PERF_COUNT_SW_BPF_OUTPUT = 10, PERF_COUNT_SW_CGROUP_SWITCHES = 11, PERF_COUNT_SW_MAX = 12, }; union perf_mem_data_src { __u64 val; struct { __u64 mem_op: 5; __u64 mem_lvl: 14; __u64 mem_snoop: 5; __u64 mem_lock: 2; __u64 mem_dtlb: 7; __u64 mem_lvl_num: 4; __u64 mem_remote: 1; __u64 mem_snoopx: 2; __u64 mem_blk: 3; __u64 mem_rsvd: 21; }; }; struct perf_branch_entry { __u64 from; __u64 to; __u64 mispred: 1; __u64 predicted: 1; __u64 in_tx: 1; __u64 abort: 1; __u64 cycles: 16; __u64 type: 4; __u64 reserved: 40; }; union perf_sample_weight { __u64 full; struct { __u32 var1_dw; __u16 var2_w; __u16 var3_w; }; }; struct new_utsname { char sysname[65]; char nodename[65]; char release[65]; char version[65]; char machine[65]; char domainname[65]; }; struct uts_namespace { struct new_utsname name; struct user_namespace *user_ns; struct ucounts *ucounts; struct ns_common ns; }; struct cgroup_namespace { struct ns_common ns; struct user_namespace *user_ns; struct ucounts *ucounts; struct css_set *root_cset; }; struct nsset { unsigned int flags; struct nsproxy *nsproxy; struct fs_struct *fs; const struct cred *cred; }; struct proc_ns_operations { const char *name; const char *real_ns_name; int type; struct ns_common * (*get)(struct task_struct *); void (*put)(struct ns_common *); int (*install)(struct nsset *, struct ns_common *); struct user_namespace * (*owner)(struct ns_common *); struct ns_common * (*get_parent)(struct ns_common *); }; struct perf_cpu_context; struct perf_output_handle; struct pmu { struct list_head entry; struct module *module; struct device *dev; const struct attribute_group **attr_groups; const struct attribute_group **attr_update; const char *name; int type; int capabilities; int *pmu_disable_count; struct perf_cpu_context *pmu_cpu_context; atomic_t exclusive_cnt; int task_ctx_nr; int hrtimer_interval_ms; unsigned int nr_addr_filters; void (*pmu_enable)(struct pmu *); void (*pmu_disable)(struct pmu *); int (*event_init)(struct perf_event *); void (*event_mapped)(struct perf_event *, struct mm_struct *); void (*event_unmapped)(struct perf_event *, struct mm_struct *); int (*add)(struct perf_event *, int); void (*del)(struct perf_event *, int); void (*start)(struct perf_event *, int); void (*stop)(struct perf_event *, int); void (*read)(struct perf_event *); void (*start_txn)(struct pmu *, unsigned int); int (*commit_txn)(struct pmu *); void (*cancel_txn)(struct pmu *); int (*event_idx)(struct perf_event *); void (*sched_task)(struct perf_event_context *, bool); struct kmem_cache *task_ctx_cache; void (*swap_task_ctx)(struct perf_event_context *, struct perf_event_context *); void * (*setup_aux)(struct perf_event *, void **, int, bool); void (*free_aux)(void *); long int (*snapshot_aux)(struct perf_event *, struct perf_output_handle *, long unsigned int); int (*addr_filters_validate)(struct list_head *); void (*addr_filters_sync)(struct perf_event *); int (*aux_output_match)(struct perf_event *); int (*filter_match)(struct perf_event *); int (*check_period)(struct perf_event *, u64); }; struct ftrace_regs { struct pt_regs regs; }; struct iovec { void *iov_base; __kernel_size_t iov_len; }; struct kvec { void *iov_base; size_t iov_len; }; struct perf_regs { __u64 abi; struct pt_regs *regs; }; struct u64_stats_sync {}; struct bpf_cgroup_storage_key { __u64 cgroup_inode_id; __u32 attach_type; }; enum kmalloc_cache_type { KMALLOC_NORMAL = 0, KMALLOC_CGROUP = 1, KMALLOC_RECLAIM = 2, KMALLOC_DMA = 3, NR_KMALLOC_TYPES = 4, }; struct bpf_cgroup_storage; struct bpf_prog_array_item { struct bpf_prog *prog; struct bpf_cgroup_storage *cgroup_storage[2]; }; struct bpf_storage_buffer; struct bpf_cgroup_storage_map; struct bpf_cgroup_storage { union { struct bpf_storage_buffer *buf; void *percpu_buf; }; struct bpf_cgroup_storage_map *map; struct bpf_cgroup_storage_key key; struct list_head list_map; struct list_head list_cg; struct rb_node node; struct callback_head rcu; }; struct bpf_prog_array { struct callback_head rcu; struct bpf_prog_array_item items[0]; }; struct bpf_storage_buffer { struct callback_head rcu; char data[0]; }; struct psi_group_cpu { seqcount_t seq; unsigned int tasks[4]; u32 state_mask; u32 times[7]; u64 state_start; u32 times_prev[14]; long: 64; }; struct cgroup_taskset; struct cftype; struct cgroup_subsys { struct cgroup_subsys_state * (*css_alloc)(struct cgroup_subsys_state *); int (*css_online)(struct cgroup_subsys_state *); void (*css_offline)(struct cgroup_subsys_state *); void (*css_released)(struct cgroup_subsys_state *); void (*css_free)(struct cgroup_subsys_state *); void (*css_reset)(struct cgroup_subsys_state *); void (*css_rstat_flush)(struct cgroup_subsys_state *, int); int (*css_extra_stat_show)(struct seq_file *, struct cgroup_subsys_state *); int (*can_attach)(struct cgroup_taskset *); void (*cancel_attach)(struct cgroup_taskset *); void (*attach)(struct cgroup_taskset *); void (*post_attach)(); int (*can_fork)(struct task_struct *, struct css_set *); void (*cancel_fork)(struct task_struct *, struct css_set *); void (*fork)(struct task_struct *); void (*exit)(struct task_struct *); void (*release)(struct task_struct *); void (*bind)(struct cgroup_subsys_state *); bool early_init: 1; bool implicit_on_dfl: 1; bool threaded: 1; int id; const char *name; const char *legacy_name; struct cgroup_root *root; struct idr css_idr; struct list_head cfts; struct cftype *dfl_cftypes; struct cftype *legacy_cftypes; unsigned int depends_on; }; struct cgroup_rstat_cpu { struct u64_stats_sync bsync; struct cgroup_base_stat bstat; struct cgroup_base_stat last_bstat; struct cgroup *updated_children; struct cgroup *updated_next; }; struct cgroup_root { struct kernfs_root *kf_root; unsigned int subsys_mask; int hierarchy_id; struct cgroup cgrp; u64 cgrp_ancestor_id_storage; atomic_t nr_cgrps; struct list_head root_list; unsigned int flags; char release_agent_path[4096]; char name[64]; }; struct cftype { char name[64]; long unsigned int private; size_t max_write_len; unsigned int flags; unsigned int file_offset; struct cgroup_subsys *ss; struct list_head node; struct kernfs_ops *kf_ops; int (*open)(struct kernfs_open_file *); void (*release)(struct kernfs_open_file *); u64 (*read_u64)(struct cgroup_subsys_state *, struct cftype *); s64 (*read_s64)(struct cgroup_subsys_state *, struct cftype *); int (*seq_show)(struct seq_file *, void *); void * (*seq_start)(struct seq_file *, loff_t *); void * (*seq_next)(struct seq_file *, void *, loff_t *); void (*seq_stop)(struct seq_file *, void *); int (*write_u64)(struct cgroup_subsys_state *, struct cftype *, u64); int (*write_s64)(struct cgroup_subsys_state *, struct cftype *, s64); ssize_t (*write)(struct kernfs_open_file *, char *, size_t, loff_t); __poll_t (*poll)(struct kernfs_open_file *, struct poll_table_struct *); }; struct perf_callchain_entry { __u64 nr; __u64 ip[0]; }; typedef long unsigned int (*perf_copy_f)(void *, const void *, long unsigned int, long unsigned int); struct perf_raw_frag { union { struct perf_raw_frag *next; long unsigned int pad; }; perf_copy_f copy; void *data; u32 size; } __attribute__((packed)); struct perf_raw_record { struct perf_raw_frag frag; u32 size; }; struct perf_branch_stack { __u64 nr; __u64 hw_idx; struct perf_branch_entry entries[0]; }; struct perf_cpu_context { struct perf_event_context ctx; struct perf_event_context *task_ctx; int active_oncpu; int exclusive; raw_spinlock_t hrtimer_lock; struct hrtimer hrtimer; ktime_t hrtimer_interval; unsigned int hrtimer_active; struct perf_cgroup *cgrp; struct list_head cgrp_cpuctx_entry; struct list_head sched_cb_entry; int sched_cb_usage; int online; int heap_size; struct perf_event **heap; struct perf_event *heap_default[2]; }; struct perf_output_handle { struct perf_event *event; struct perf_buffer *rb; long unsigned int wakeup; long unsigned int size; u64 aux_flags; union { void *addr; long unsigned int head; }; int page; }; struct perf_addr_filter_range { long unsigned int start; long unsigned int size; }; struct perf_sample_data { u64 addr; struct perf_raw_record *raw; struct perf_branch_stack *br_stack; u64 period; union perf_sample_weight weight; u64 txn; union perf_mem_data_src data_src; u64 type; u64 ip; struct { u32 pid; u32 tid; } tid_entry; u64 time; u64 id; u64 stream_id; struct { u32 cpu; u32 reserved; } cpu_entry; struct perf_callchain_entry *callchain; u64 aux_size; struct perf_regs regs_user; struct perf_regs regs_intr; u64 stack_user_size; u64 phys_addr; u64 cgroup; u64 data_page_size; u64 code_page_size; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct perf_cgroup_info; struct perf_cgroup { struct cgroup_subsys_state css; struct perf_cgroup_info *info; }; struct perf_cgroup_info { u64 time; u64 timestamp; }; struct trace_entry { short unsigned int type; unsigned char flags; unsigned char preempt_count; int pid; }; struct trace_array; struct tracer; struct array_buffer; struct ring_buffer_iter; struct trace_iterator { struct trace_array *tr; struct tracer *trace; struct array_buffer *array_buffer; void *private; int cpu_file; struct mutex mutex; struct ring_buffer_iter **buffer_iter; long unsigned int iter_flags; void *temp; unsigned int temp_size; char *fmt; unsigned int fmt_size; struct trace_seq tmp_seq; cpumask_var_t started; bool snapshot; struct trace_seq seq; struct trace_entry *ent; long unsigned int lost_events; int leftover; int ent_size; int cpu; u64 ts; loff_t pos; long int idx; }; enum print_line_t { TRACE_TYPE_PARTIAL_LINE = 0, TRACE_TYPE_HANDLED = 1, TRACE_TYPE_UNHANDLED = 2, TRACE_TYPE_NO_CONSUME = 3, }; typedef enum print_line_t (*trace_print_func)(struct trace_iterator *, int, struct trace_event *); struct trace_event_functions { trace_print_func trace; trace_print_func raw; trace_print_func hex; trace_print_func binary; }; enum trace_reg { TRACE_REG_REGISTER = 0, TRACE_REG_UNREGISTER = 1, TRACE_REG_PERF_REGISTER = 2, TRACE_REG_PERF_UNREGISTER = 3, TRACE_REG_PERF_OPEN = 4, TRACE_REG_PERF_CLOSE = 5, TRACE_REG_PERF_ADD = 6, TRACE_REG_PERF_DEL = 7, }; struct trace_event_fields { const char *type; union { struct { const char *name; const int size; const int align; const int is_signed; const int filter_type; }; int (*define_fields)(struct trace_event_call *); }; }; struct trace_event_class { const char *system; void *probe; void *perf_probe; int (*reg)(struct trace_event_call *, enum trace_reg, void *); struct trace_event_fields *fields_array; struct list_head * (*get_fields)(struct trace_event_call *); struct list_head fields; int (*raw_init)(struct trace_event_call *); }; struct trace_buffer; struct trace_event_file; struct trace_event_buffer { struct trace_buffer *buffer; struct ring_buffer_event *event; struct trace_event_file *trace_file; void *entry; unsigned int trace_ctx; struct pt_regs *regs; }; struct trace_subsystem_dir; struct trace_event_file { struct list_head list; struct trace_event_call *event_call; struct event_filter *filter; struct dentry *dir; struct trace_array *tr; struct trace_subsystem_dir *system; struct list_head triggers; long unsigned int flags; atomic_t sm_ref; atomic_t tm_ref; }; enum { TRACE_EVENT_FL_FILTERED_BIT = 0, TRACE_EVENT_FL_CAP_ANY_BIT = 1, TRACE_EVENT_FL_NO_SET_FILTER_BIT = 2, TRACE_EVENT_FL_IGNORE_ENABLE_BIT = 3, TRACE_EVENT_FL_TRACEPOINT_BIT = 4, TRACE_EVENT_FL_KPROBE_BIT = 5, TRACE_EVENT_FL_UPROBE_BIT = 6, }; enum { TRACE_EVENT_FL_FILTERED = 1, TRACE_EVENT_FL_CAP_ANY = 2, TRACE_EVENT_FL_NO_SET_FILTER = 4, TRACE_EVENT_FL_IGNORE_ENABLE = 8, TRACE_EVENT_FL_TRACEPOINT = 16, TRACE_EVENT_FL_KPROBE = 32, TRACE_EVENT_FL_UPROBE = 64, }; enum { EVENT_FILE_FL_ENABLED_BIT = 0, EVENT_FILE_FL_RECORDED_CMD_BIT = 1, EVENT_FILE_FL_RECORDED_TGID_BIT = 2, EVENT_FILE_FL_FILTERED_BIT = 3, EVENT_FILE_FL_NO_SET_FILTER_BIT = 4, EVENT_FILE_FL_SOFT_MODE_BIT = 5, EVENT_FILE_FL_SOFT_DISABLED_BIT = 6, EVENT_FILE_FL_TRIGGER_MODE_BIT = 7, EVENT_FILE_FL_TRIGGER_COND_BIT = 8, EVENT_FILE_FL_PID_FILTER_BIT = 9, EVENT_FILE_FL_WAS_ENABLED_BIT = 10, }; enum { EVENT_FILE_FL_ENABLED = 1, EVENT_FILE_FL_RECORDED_CMD = 2, EVENT_FILE_FL_RECORDED_TGID = 4, EVENT_FILE_FL_FILTERED = 8, EVENT_FILE_FL_NO_SET_FILTER = 16, EVENT_FILE_FL_SOFT_MODE = 32, EVENT_FILE_FL_SOFT_DISABLED = 64, EVENT_FILE_FL_TRIGGER_MODE = 128, EVENT_FILE_FL_TRIGGER_COND = 256, EVENT_FILE_FL_PID_FILTER = 512, EVENT_FILE_FL_WAS_ENABLED = 1024, }; enum event_trigger_type { ETT_NONE = 0, ETT_TRACE_ONOFF = 1, ETT_SNAPSHOT = 2, ETT_STACKTRACE = 4, ETT_EVENT_ENABLE = 8, ETT_EVENT_HIST = 16, ETT_HIST_ENABLE = 32, }; enum { FILTER_OTHER = 0, FILTER_STATIC_STRING = 1, FILTER_DYN_STRING = 2, FILTER_PTR_STRING = 3, FILTER_TRACE_FN = 4, FILTER_COMM = 5, FILTER_CPU = 6, }; struct fwnode_reference_args; struct fwnode_endpoint; struct fwnode_operations { struct fwnode_handle * (*get)(struct fwnode_handle *); void (*put)(struct fwnode_handle *); bool (*device_is_available)(const struct fwnode_handle *); const void * (*device_get_match_data)(const struct fwnode_handle *, const struct device *); bool (*property_present)(const struct fwnode_handle *, const char *); int (*property_read_int_array)(const struct fwnode_handle *, const char *, unsigned int, void *, size_t); int (*property_read_string_array)(const struct fwnode_handle *, const char *, const char **, size_t); const char * (*get_name)(const struct fwnode_handle *); const char * (*get_name_prefix)(const struct fwnode_handle *); struct fwnode_handle * (*get_parent)(const struct fwnode_handle *); struct fwnode_handle * (*get_next_child_node)(const struct fwnode_handle *, struct fwnode_handle *); struct fwnode_handle * (*get_named_child_node)(const struct fwnode_handle *, const char *); int (*get_reference_args)(const struct fwnode_handle *, const char *, const char *, unsigned int, unsigned int, struct fwnode_reference_args *); struct fwnode_handle * (*graph_get_next_endpoint)(const struct fwnode_handle *, struct fwnode_handle *); struct fwnode_handle * (*graph_get_remote_endpoint)(const struct fwnode_handle *); struct fwnode_handle * (*graph_get_port_parent)(struct fwnode_handle *); int (*graph_parse_endpoint)(const struct fwnode_handle *, struct fwnode_endpoint *); int (*add_links)(struct fwnode_handle *); }; struct fwnode_endpoint { unsigned int port; unsigned int id; const struct fwnode_handle *local_fwnode; }; struct fwnode_reference_args { struct fwnode_handle *fwnode; unsigned int nargs; u64 args[8]; }; struct property { char *name; int length; void *value; struct property *next; }; struct irq_fwspec { struct fwnode_handle *fwnode; int param_count; u32 param[16]; }; struct irq_domain_ops { int (*match)(struct irq_domain *, struct device_node *, enum irq_domain_bus_token); int (*select)(struct irq_domain *, struct irq_fwspec *, enum irq_domain_bus_token); int (*map)(struct irq_domain *, unsigned int, irq_hw_number_t); void (*unmap)(struct irq_domain *, unsigned int); int (*xlate)(struct irq_domain *, struct device_node *, const u32 *, unsigned int, long unsigned int *, unsigned int *); int (*alloc)(struct irq_domain *, unsigned int, unsigned int, void *); void (*free)(struct irq_domain *, unsigned int, unsigned int); int (*activate)(struct irq_domain *, struct irq_data *, bool); void (*deactivate)(struct irq_domain *, struct irq_data *); int (*translate)(struct irq_domain *, struct irq_fwspec *, long unsigned int *, unsigned int *); }; struct xbc_node { u16 next; u16 child; u16 parent; u16 data; }; enum wb_stat_item { WB_RECLAIMABLE = 0, WB_WRITEBACK = 1, WB_DIRTIED = 2, WB_WRITTEN = 3, NR_WB_STAT_ITEMS = 4, }; struct block_device_operations; struct timer_rand_state; struct disk_events; struct cdrom_device_info; struct badblocks; struct gendisk { int major; int first_minor; int minors; char disk_name[32]; short unsigned int events; short unsigned int event_flags; struct xarray part_tbl; struct block_device *part0; const struct block_device_operations *fops; struct request_queue *queue; void *private_data; int flags; long unsigned int state; struct mutex open_mutex; unsigned int open_partitions; struct kobject *slave_dir; struct timer_rand_state *random; atomic_t sync_io; struct disk_events *ev; struct kobject integrity_kobj; struct cdrom_device_info *cdi; int node_id; struct badblocks *bb; struct lockdep_map lockdep_map; }; struct partition_meta_info { char uuid[37]; u8 volname[64]; }; struct bio_integrity_payload { struct bio *bip_bio; struct bvec_iter bip_iter; short unsigned int bip_vcnt; short unsigned int bip_max_vcnt; short unsigned int bip_flags; struct bvec_iter bio_iter; struct work_struct bip_work; struct bio_vec *bip_vec; struct bio_vec bip_inline_vecs[0]; }; struct blkg_iostat { u64 bytes[3]; u64 ios[3]; }; struct blkg_iostat_set { struct u64_stats_sync sync; struct blkg_iostat cur; struct blkg_iostat last; }; struct blkcg; struct blkg_policy_data; struct blkcg_gq { struct request_queue *q; struct list_head q_node; struct hlist_node blkcg_node; struct blkcg *blkcg; struct blkcg_gq *parent; struct percpu_ref refcnt; bool online; struct blkg_iostat_set *iostat_cpu; struct blkg_iostat_set iostat; struct blkg_policy_data *pd[6]; spinlock_t async_bio_lock; struct bio_list async_bios; struct work_struct async_bio_work; atomic_t use_delay; atomic64_t delay_nsec; atomic64_t delay_start; u64 last_delay; int last_use; struct callback_head callback_head; }; typedef unsigned int blk_qc_t; struct blk_integrity_iter; typedef blk_status_t integrity_processing_fn(struct blk_integrity_iter *); typedef void integrity_prepare_fn(struct request *); typedef void integrity_complete_fn(struct request *, unsigned int); struct blk_integrity_profile { integrity_processing_fn *generate_fn; integrity_processing_fn *verify_fn; integrity_prepare_fn *prepare_fn; integrity_complete_fn *complete_fn; const char *name; }; struct blk_zone; typedef int (*report_zones_cb)(struct blk_zone *, unsigned int, void *); struct hd_geometry; struct pr_ops; struct block_device_operations { blk_qc_t (*submit_bio)(struct bio *); int (*open)(struct block_device *, fmode_t); void (*release)(struct gendisk *, fmode_t); int (*rw_page)(struct block_device *, sector_t, struct page *, unsigned int); int (*ioctl)(struct block_device *, fmode_t, unsigned int, long unsigned int); int (*compat_ioctl)(struct block_device *, fmode_t, unsigned int, long unsigned int); unsigned int (*check_events)(struct gendisk *, unsigned int); void (*unlock_native_capacity)(struct gendisk *); int (*getgeo)(struct block_device *, struct hd_geometry *); int (*set_read_only)(struct block_device *, bool); void (*swap_slot_free_notify)(struct block_device *, long unsigned int); int (*report_zones)(struct gendisk *, sector_t, unsigned int, report_zones_cb, void *); char * (*devnode)(struct gendisk *, umode_t *); struct module *owner; const struct pr_ops *pr_ops; }; struct sg_io_v4 { __s32 guard; __u32 protocol; __u32 subprotocol; __u32 request_len; __u64 request; __u64 request_tag; __u32 request_attr; __u32 request_priority; __u32 request_extra; __u32 max_response_len; __u64 response; __u32 dout_iovec_count; __u32 dout_xfer_len; __u32 din_iovec_count; __u32 din_xfer_len; __u64 dout_xferp; __u64 din_xferp; __u32 timeout; __u32 flags; __u64 usr_ptr; __u32 spare_in; __u32 driver_status; __u32 transport_status; __u32 device_status; __u32 retry_delay; __u32 info; __u32 duration; __u32 response_len; __s32 din_resid; __s32 dout_resid; __u64 generated_tag; __u32 spare_out; __u32 padding; }; struct bsg_ops { int (*check_proto)(struct sg_io_v4 *); int (*fill_hdr)(struct request *, struct sg_io_v4 *, fmode_t); int (*complete_rq)(struct request *, struct sg_io_v4 *); void (*free_rq)(struct request *); }; typedef __u32 req_flags_t; typedef void rq_end_io_fn(struct request *, blk_status_t); enum mq_rq_state { MQ_RQ_IDLE = 0, MQ_RQ_IN_FLIGHT = 1, MQ_RQ_COMPLETE = 2, }; struct blk_ksm_keyslot; struct request { struct request_queue *q; struct blk_mq_ctx *mq_ctx; struct blk_mq_hw_ctx *mq_hctx; unsigned int cmd_flags; req_flags_t rq_flags; int tag; int internal_tag; unsigned int __data_len; sector_t __sector; struct bio *bio; struct bio *biotail; struct list_head queuelist; union { struct hlist_node hash; struct llist_node ipi_list; }; union { struct rb_node rb_node; struct bio_vec special_vec; void *completion_data; int error_count; }; union { struct { struct io_cq *icq; void *priv[2]; } elv; struct { unsigned int seq; struct list_head list; rq_end_io_fn *saved_end_io; } flush; }; struct gendisk *rq_disk; struct block_device *part; u64 alloc_time_ns; u64 start_time_ns; u64 io_start_time_ns; short unsigned int wbt_flags; short unsigned int stats_sectors; short unsigned int nr_phys_segments; short unsigned int nr_integrity_segments; struct bio_crypt_ctx *crypt_ctx; struct blk_ksm_keyslot *crypt_keyslot; short unsigned int write_hint; short unsigned int ioprio; enum mq_rq_state state; refcount_t ref; unsigned int timeout; long unsigned int deadline; union { struct __call_single_data csd; u64 fifo_time; }; rq_end_io_fn *end_io; void *end_io_data; }; struct blk_zone { __u64 start; __u64 len; __u64 wp; __u8 type; __u8 cond; __u8 non_seq; __u8 reset; __u8 resv[4]; __u64 capacity; __u8 reserved[24]; }; struct sbitmap_word { long unsigned int depth; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long unsigned int word; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long unsigned int cleared; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct sbq_wait_state { atomic_t wait_cnt; wait_queue_head_t wait; long: 64; long: 64; long: 64; long: 64; }; enum elv_merge { ELEVATOR_NO_MERGE = 0, ELEVATOR_FRONT_MERGE = 1, ELEVATOR_BACK_MERGE = 2, ELEVATOR_DISCARD_MERGE = 3, }; struct elevator_type; struct blk_mq_alloc_data; struct elevator_mq_ops { int (*init_sched)(struct request_queue *, struct elevator_type *); void (*exit_sched)(struct elevator_queue *); int (*init_hctx)(struct blk_mq_hw_ctx *, unsigned int); void (*exit_hctx)(struct blk_mq_hw_ctx *, unsigned int); void (*depth_updated)(struct blk_mq_hw_ctx *); bool (*allow_merge)(struct request_queue *, struct request *, struct bio *); bool (*bio_merge)(struct request_queue *, struct bio *, unsigned int); int (*request_merge)(struct request_queue *, struct request **, struct bio *); void (*request_merged)(struct request_queue *, struct request *, enum elv_merge); void (*requests_merged)(struct request_queue *, struct request *, struct request *); void (*limit_depth)(unsigned int, struct blk_mq_alloc_data *); void (*prepare_request)(struct request *); void (*finish_request)(struct request *); void (*insert_requests)(struct blk_mq_hw_ctx *, struct list_head *, bool); struct request * (*dispatch_request)(struct blk_mq_hw_ctx *); bool (*has_work)(struct blk_mq_hw_ctx *); void (*completed_request)(struct request *, u64); void (*requeue_request)(struct request *); struct request * (*former_request)(struct request_queue *, struct request *); struct request * (*next_request)(struct request_queue *, struct request *); void (*init_icq)(struct io_cq *); void (*exit_icq)(struct io_cq *); }; struct elv_fs_entry; struct blk_mq_debugfs_attr; struct elevator_type { struct kmem_cache *icq_cache; struct elevator_mq_ops ops; size_t icq_size; size_t icq_align; struct elv_fs_entry *elevator_attrs; const char *elevator_name; const char *elevator_alias; const unsigned int elevator_features; struct module *elevator_owner; const struct blk_mq_debugfs_attr *queue_debugfs_attrs; const struct blk_mq_debugfs_attr *hctx_debugfs_attrs; char icq_cache_name[22]; struct list_head list; }; struct elevator_queue { struct elevator_type *type; void *elevator_data; struct kobject kobj; struct mutex sysfs_lock; unsigned int registered: 1; struct hlist_head hash[64]; }; struct elv_fs_entry { struct attribute attr; ssize_t (*show)(struct elevator_queue *, char *); ssize_t (*store)(struct elevator_queue *, const char *, size_t); }; struct blk_mq_debugfs_attr { const char *name; umode_t mode; int (*show)(void *, struct seq_file *); ssize_t (*write)(void *, const char *, size_t, loff_t *); const struct seq_operations *seq_ops; }; enum blk_eh_timer_return { BLK_EH_DONE = 0, BLK_EH_RESET_TIMER = 1, }; struct blk_mq_queue_data; struct blk_mq_ops { blk_status_t (*queue_rq)(struct blk_mq_hw_ctx *, const struct blk_mq_queue_data *); void (*commit_rqs)(struct blk_mq_hw_ctx *); int (*get_budget)(struct request_queue *); void (*put_budget)(struct request_queue *, int); void (*set_rq_budget_token)(struct request *, int); int (*get_rq_budget_token)(struct request *); enum blk_eh_timer_return (*timeout)(struct request *, bool); int (*poll)(struct blk_mq_hw_ctx *); void (*complete)(struct request *); int (*init_hctx)(struct blk_mq_hw_ctx *, void *, unsigned int); void (*exit_hctx)(struct blk_mq_hw_ctx *, unsigned int); int (*init_request)(struct blk_mq_tag_set *, struct request *, unsigned int, unsigned int); void (*exit_request)(struct blk_mq_tag_set *, struct request *, unsigned int); void (*initialize_rq_fn)(struct request *); void (*cleanup_rq)(struct request *); bool (*busy)(struct request_queue *); int (*map_queues)(struct blk_mq_tag_set *); void (*show_rq)(struct seq_file *, struct request *); }; struct blk_integrity_iter { void *prot_buf; void *data_buf; sector_t seed; unsigned int data_size; short unsigned int interval; const char *disk_name; }; enum pr_type { PR_WRITE_EXCLUSIVE = 1, PR_EXCLUSIVE_ACCESS = 2, PR_WRITE_EXCLUSIVE_REG_ONLY = 3, PR_EXCLUSIVE_ACCESS_REG_ONLY = 4, PR_WRITE_EXCLUSIVE_ALL_REGS = 5, PR_EXCLUSIVE_ACCESS_ALL_REGS = 6, }; struct pr_ops { int (*pr_register)(struct block_device *, u64, u64, u32); int (*pr_reserve)(struct block_device *, u64, enum pr_type, u32); int (*pr_release)(struct block_device *, u64, enum pr_type); int (*pr_preempt)(struct block_device *, u64, u64, enum pr_type, bool); int (*pr_clear)(struct block_device *, u64); }; enum blkg_iostat_type { BLKG_IOSTAT_READ = 0, BLKG_IOSTAT_WRITE = 1, BLKG_IOSTAT_DISCARD = 2, BLKG_IOSTAT_NR = 3, }; struct blkcg_policy_data; struct blkcg { struct cgroup_subsys_state css; spinlock_t lock; refcount_t online_pin; struct xarray blkg_tree; struct blkcg_gq *blkg_hint; struct hlist_head blkg_list; struct blkcg_policy_data *cpd[6]; struct list_head all_blkcgs_node; char fc_app_id[129]; struct list_head cgwb_list; }; struct blkcg_policy_data { struct blkcg *blkcg; int plid; }; struct blkg_policy_data { struct blkcg_gq *blkg; int plid; }; typedef long unsigned int efi_status_t; typedef u8 efi_bool_t; typedef u16 efi_char16_t; typedef guid_t efi_guid_t; typedef struct { u64 signature; u32 revision; u32 headersize; u32 crc32; u32 reserved; } efi_table_hdr_t; typedef struct { u32 type; u32 pad; u64 phys_addr; u64 virt_addr; u64 num_pages; u64 attribute; } efi_memory_desc_t; typedef struct { efi_guid_t guid; u32 headersize; u32 flags; u32 imagesize; } efi_capsule_header_t; typedef struct { u16 year; u8 month; u8 day; u8 hour; u8 minute; u8 second; u8 pad1; u32 nanosecond; s16 timezone; u8 daylight; u8 pad2; } efi_time_t; typedef struct { u32 resolution; u32 accuracy; u8 sets_to_zero; } efi_time_cap_t; typedef struct { efi_table_hdr_t hdr; u32 get_time; u32 set_time; u32 get_wakeup_time; u32 set_wakeup_time; u32 set_virtual_address_map; u32 convert_pointer; u32 get_variable; u32 get_next_variable; u32 set_variable; u32 get_next_high_mono_count; u32 reset_system; u32 update_capsule; u32 query_capsule_caps; u32 query_variable_info; } efi_runtime_services_32_t; typedef efi_status_t efi_get_time_t(efi_time_t *, efi_time_cap_t *); typedef efi_status_t efi_set_time_t(efi_time_t *); typedef efi_status_t efi_get_wakeup_time_t(efi_bool_t *, efi_bool_t *, efi_time_t *); typedef efi_status_t efi_set_wakeup_time_t(efi_bool_t, efi_time_t *); typedef efi_status_t efi_get_variable_t(efi_char16_t *, efi_guid_t *, u32 *, long unsigned int *, void *); typedef efi_status_t efi_get_next_variable_t(long unsigned int *, efi_char16_t *, efi_guid_t *); typedef efi_status_t efi_set_variable_t(efi_char16_t *, efi_guid_t *, u32, long unsigned int, void *); typedef efi_status_t efi_get_next_high_mono_count_t(u32 *); typedef void efi_reset_system_t(int, efi_status_t, long unsigned int, efi_char16_t *); typedef efi_status_t efi_query_variable_info_t(u32, u64 *, u64 *, u64 *); typedef efi_status_t efi_update_capsule_t(efi_capsule_header_t **, long unsigned int, long unsigned int); typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **, long unsigned int, u64 *, int *); typedef union { struct { efi_table_hdr_t hdr; efi_status_t (*get_time)(efi_time_t *, efi_time_cap_t *); efi_status_t (*set_time)(efi_time_t *); efi_status_t (*get_wakeup_time)(efi_bool_t *, efi_bool_t *, efi_time_t *); efi_status_t (*set_wakeup_time)(efi_bool_t, efi_time_t *); efi_status_t (*set_virtual_address_map)(long unsigned int, long unsigned int, u32, efi_memory_desc_t *); void *convert_pointer; efi_status_t (*get_variable)(efi_char16_t *, efi_guid_t *, u32 *, long unsigned int *, void *); efi_status_t (*get_next_variable)(long unsigned int *, efi_char16_t *, efi_guid_t *); efi_status_t (*set_variable)(efi_char16_t *, efi_guid_t *, u32, long unsigned int, void *); efi_status_t (*get_next_high_mono_count)(u32 *); void (*reset_system)(int, efi_status_t, long unsigned int, efi_char16_t *); efi_status_t (*update_capsule)(efi_capsule_header_t **, long unsigned int, long unsigned int); efi_status_t (*query_capsule_caps)(efi_capsule_header_t **, long unsigned int, u64 *, int *); efi_status_t (*query_variable_info)(u32, u64 *, u64 *, u64 *); }; efi_runtime_services_32_t mixed_mode; } efi_runtime_services_t; struct efi_memory_map { phys_addr_t phys_map; void *map; void *map_end; int nr_map; long unsigned int desc_version; long unsigned int desc_size; long unsigned int flags; }; struct efi { const efi_runtime_services_t *runtime; unsigned int runtime_version; unsigned int runtime_supported_mask; long unsigned int acpi; long unsigned int acpi20; long unsigned int smbios; long unsigned int smbios3; long unsigned int esrt; long unsigned int tpm_log; long unsigned int tpm_final_log; long unsigned int mokvar_table; efi_get_time_t *get_time; efi_set_time_t *set_time; efi_get_wakeup_time_t *get_wakeup_time; efi_set_wakeup_time_t *set_wakeup_time; efi_get_variable_t *get_variable; efi_get_next_variable_t *get_next_variable; efi_set_variable_t *set_variable; efi_set_variable_t *set_variable_nonblocking; efi_query_variable_info_t *query_variable_info; efi_query_variable_info_t *query_variable_info_nonblocking; efi_update_capsule_t *update_capsule; efi_query_capsule_caps_t *query_capsule_caps; efi_get_next_high_mono_count_t *get_next_high_mono_count; efi_reset_system_t *reset_system; struct efi_memory_map memmap; long unsigned int flags; }; enum memcg_stat_item { MEMCG_SWAP = 39, MEMCG_SOCK = 40, MEMCG_PERCPU_B = 41, MEMCG_NR_STAT = 42, }; enum memcg_memory_event { MEMCG_LOW = 0, MEMCG_HIGH = 1, MEMCG_MAX = 2, MEMCG_OOM = 3, MEMCG_OOM_KILL = 4, MEMCG_SWAP_HIGH = 5, MEMCG_SWAP_MAX = 6, MEMCG_SWAP_FAIL = 7, MEMCG_NR_MEMORY_EVENTS = 8, }; enum mem_cgroup_events_target { MEM_CGROUP_TARGET_THRESH = 0, MEM_CGROUP_TARGET_SOFTLIMIT = 1, MEM_CGROUP_NTARGETS = 2, }; struct memcg_vmstats_percpu { long int state[42]; long unsigned int events[100]; long int state_prev[42]; long unsigned int events_prev[100]; long unsigned int nr_page_events; long unsigned int targets[2]; }; struct mem_cgroup_reclaim_iter { struct mem_cgroup *position; unsigned int generation; }; struct lruvec_stat { long int count[39]; }; struct batched_lruvec_stat { s32 count[39]; }; struct shrinker_info { struct callback_head rcu; atomic_long_t *nr_deferred; long unsigned int *map; }; struct mem_cgroup_per_node { struct lruvec lruvec; struct lruvec_stat *lruvec_stat_local; struct batched_lruvec_stat *lruvec_stat_cpu; atomic_long_t lruvec_stat[39]; long unsigned int lru_zone_size[25]; struct mem_cgroup_reclaim_iter iter; struct shrinker_info *shrinker_info; struct rb_node tree_node; long unsigned int usage_in_excess; bool on_tree; struct mem_cgroup *memcg; }; struct eventfd_ctx; struct mem_cgroup_threshold { struct eventfd_ctx *eventfd; long unsigned int threshold; }; struct mem_cgroup_threshold_ary { int current_threshold; unsigned int size; struct mem_cgroup_threshold entries[0]; }; struct obj_cgroup { struct percpu_ref refcnt; struct mem_cgroup *memcg; atomic_t nr_charged_bytes; union { struct list_head list; struct callback_head rcu; }; }; struct percpu_cluster { struct swap_cluster_info index; unsigned int next; }; enum fs_value_type { fs_value_is_undefined = 0, fs_value_is_flag = 1, fs_value_is_string = 2, fs_value_is_blob = 3, fs_value_is_filename = 4, fs_value_is_file = 5, }; struct fs_parameter { const char *key; enum fs_value_type type: 8; union { char *string; void *blob; struct filename *name; struct file *file; }; size_t size; int dirfd; }; struct fc_log { refcount_t usage; u8 head; u8 tail; u8 need_free; struct module *owner; char *buffer[8]; }; struct fs_context_operations { void (*free)(struct fs_context *); int (*dup)(struct fs_context *, struct fs_context *); int (*parse_param)(struct fs_context *, struct fs_parameter *); int (*parse_monolithic)(struct fs_context *, void *); int (*get_tree)(struct fs_context *); int (*reconfigure)(struct fs_context *); }; struct fs_parse_result { bool negated; union { bool boolean; int int_32; unsigned int uint_32; u64 uint_64; }; }; struct trace_event_raw_initcall_level { struct trace_entry ent; u32 __data_loc_level; char __data[0]; }; struct trace_event_raw_initcall_start { struct trace_entry ent; initcall_t func; char __data[0]; }; struct trace_event_raw_initcall_finish { struct trace_entry ent; initcall_t func; int ret; char __data[0]; }; struct trace_event_data_offsets_initcall_level { u32 level; }; struct trace_event_data_offsets_initcall_start {}; struct trace_event_data_offsets_initcall_finish {}; typedef void (*btf_trace_initcall_level)(void *, const char *); typedef void (*btf_trace_initcall_start)(void *, initcall_t); typedef void (*btf_trace_initcall_finish)(void *, initcall_t, int); struct blacklist_entry { struct list_head next; char *buf; }; typedef __u32 Elf32_Word; struct elf32_note { Elf32_Word n_namesz; Elf32_Word n_descsz; Elf32_Word n_type; }; enum { PROC_ROOT_INO = 1, PROC_IPC_INIT_INO = 4026531839, PROC_UTS_INIT_INO = 4026531838, PROC_USER_INIT_INO = 4026531837, PROC_PID_INIT_INO = 4026531836, PROC_CGROUP_INIT_INO = 4026531835, PROC_TIME_INIT_INO = 4026531834, }; typedef __u16 __le16; typedef __u16 __be16; typedef __u32 __be32; typedef __u64 __be64; typedef __u32 __wsum; typedef unsigned int slab_flags_t; struct notifier_block; typedef int (*notifier_fn_t)(struct notifier_block *, long unsigned int, void *); struct notifier_block { notifier_fn_t notifier_call; struct notifier_block *next; int priority; }; struct blocking_notifier_head { struct rw_semaphore rwsem; struct notifier_block *head; }; struct raw_notifier_head { struct notifier_block *head; }; struct page_pool_params { unsigned int flags; unsigned int order; unsigned int pool_size; int nid; struct device *dev; enum dma_data_direction dma_dir; unsigned int max_len; unsigned int offset; }; struct pp_alloc_cache { u32 count; struct page *cache[128]; }; struct ptr_ring { int producer; spinlock_t producer_lock; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; int consumer_head; int consumer_tail; spinlock_t consumer_lock; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; int size; int batch; void **queue; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct page_pool { struct page_pool_params p; struct delayed_work release_dw; void (*disconnect)(void *); long unsigned int defer_start; long unsigned int defer_warn; u32 pages_state_hold_cnt; long: 32; long: 64; long: 64; long: 64; long: 64; struct pp_alloc_cache alloc; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; struct ptr_ring ring; atomic_t pages_state_release_cnt; refcount_t user_cnt; u64 destroy_cnt; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; typedef __u64 __addrpair; typedef __u32 __portpair; typedef struct { struct net *net; } possible_net_t; struct in6_addr { union { __u8 u6_addr8[16]; __be16 u6_addr16[8]; __be32 u6_addr32[4]; } in6_u; }; struct hlist_nulls_node { struct hlist_nulls_node *next; struct hlist_nulls_node **pprev; }; struct proto; struct inet_timewait_death_row; struct sock_common { union { __addrpair skc_addrpair; struct { __be32 skc_daddr; __be32 skc_rcv_saddr; }; }; union { unsigned int skc_hash; __u16 skc_u16hashes[2]; }; union { __portpair skc_portpair; struct { __be16 skc_dport; __u16 skc_num; }; }; short unsigned int skc_family; volatile unsigned char skc_state; unsigned char skc_reuse: 4; unsigned char skc_reuseport: 1; unsigned char skc_ipv6only: 1; unsigned char skc_net_refcnt: 1; int skc_bound_dev_if; union { struct hlist_node skc_bind_node; struct hlist_node skc_portaddr_node; }; struct proto *skc_prot; possible_net_t skc_net; struct in6_addr skc_v6_daddr; struct in6_addr skc_v6_rcv_saddr; atomic64_t skc_cookie; union { long unsigned int skc_flags; struct sock *skc_listener; struct inet_timewait_death_row *skc_tw_dr; }; int skc_dontcopy_begin[0]; union { struct hlist_node skc_node; struct hlist_nulls_node skc_nulls_node; }; short unsigned int skc_tx_queue_mapping; short unsigned int skc_rx_queue_mapping; union { int skc_incoming_cpu; u32 skc_rcv_wnd; u32 skc_tw_rcv_nxt; }; refcount_t skc_refcnt; int skc_dontcopy_end[0]; union { u32 skc_rxhash; u32 skc_window_clamp; u32 skc_tw_snd_nxt; }; }; typedef struct { spinlock_t slock; int owned; wait_queue_head_t wq; } socket_lock_t; struct sk_buff; struct sk_buff_head { struct sk_buff *next; struct sk_buff *prev; __u32 qlen; spinlock_t lock; }; typedef u64 netdev_features_t; struct sock_cgroup_data { union { struct { u8 is_data: 1; u8 no_refcnt: 1; u8 unused: 6; u8 padding; u16 prioidx; u32 classid; }; u64 val; }; }; struct sk_filter; struct socket_wq; struct xfrm_policy; struct dst_entry; struct socket; struct net_device; struct sock_reuseport; struct sock { struct sock_common __sk_common; socket_lock_t sk_lock; atomic_t sk_drops; int sk_rcvlowat; struct sk_buff_head sk_error_queue; struct sk_buff *sk_rx_skb_cache; struct sk_buff_head sk_receive_queue; struct { atomic_t rmem_alloc; int len; struct sk_buff *head; struct sk_buff *tail; } sk_backlog; int sk_forward_alloc; unsigned int sk_ll_usec; unsigned int sk_napi_id; int sk_rcvbuf; struct sk_filter *sk_filter; union { struct socket_wq *sk_wq; struct socket_wq *sk_wq_raw; }; struct xfrm_policy *sk_policy[2]; struct dst_entry *sk_rx_dst; struct dst_entry *sk_dst_cache; atomic_t sk_omem_alloc; int sk_sndbuf; int sk_wmem_queued; refcount_t sk_wmem_alloc; long unsigned int sk_tsq_flags; union { struct sk_buff *sk_send_head; struct rb_root tcp_rtx_queue; }; struct sk_buff *sk_tx_skb_cache; struct sk_buff_head sk_write_queue; __s32 sk_peek_off; int sk_write_pending; __u32 sk_dst_pending_confirm; u32 sk_pacing_status; long int sk_sndtimeo; struct timer_list sk_timer; __u32 sk_priority; __u32 sk_mark; long unsigned int sk_pacing_rate; long unsigned int sk_max_pacing_rate; struct page_frag sk_frag; netdev_features_t sk_route_caps; netdev_features_t sk_route_nocaps; netdev_features_t sk_route_forced_caps; int sk_gso_type; unsigned int sk_gso_max_size; gfp_t sk_allocation; __u32 sk_txhash; u8 sk_padding: 1; u8 sk_kern_sock: 1; u8 sk_no_check_tx: 1; u8 sk_no_check_rx: 1; u8 sk_userlocks: 4; u8 sk_pacing_shift; u16 sk_type; u16 sk_protocol; u16 sk_gso_max_segs; long unsigned int sk_lingertime; struct proto *sk_prot_creator; rwlock_t sk_callback_lock; int sk_err; int sk_err_soft; u32 sk_ack_backlog; u32 sk_max_ack_backlog; kuid_t sk_uid; u8 sk_prefer_busy_poll; u16 sk_busy_poll_budget; struct pid *sk_peer_pid; const struct cred *sk_peer_cred; long int sk_rcvtimeo; ktime_t sk_stamp; u16 sk_tsflags; int sk_bind_phc; u8 sk_shutdown; u32 sk_tskey; atomic_t sk_zckey; u8 sk_clockid; u8 sk_txtime_deadline_mode: 1; u8 sk_txtime_report_errors: 1; u8 sk_txtime_unused: 6; struct socket *sk_socket; void *sk_user_data; void *sk_security; struct sock_cgroup_data sk_cgrp_data; struct mem_cgroup *sk_memcg; void (*sk_state_change)(struct sock *); void (*sk_data_ready)(struct sock *); void (*sk_write_space)(struct sock *); void (*sk_error_report)(struct sock *); int (*sk_backlog_rcv)(struct sock *, struct sk_buff *); struct sk_buff * (*sk_validate_xmit_skb)(struct sock *, struct net_device *, struct sk_buff *); void (*sk_destruct)(struct sock *); struct sock_reuseport *sk_reuseport_cb; struct bpf_local_storage *sk_bpf_storage; struct callback_head sk_rcu; }; struct rhash_head { struct rhash_head *next; }; struct rhashtable; struct rhashtable_compare_arg { struct rhashtable *ht; const void *key; }; typedef u32 (*rht_hashfn_t)(const void *, u32, u32); typedef u32 (*rht_obj_hashfn_t)(const void *, u32, u32); typedef int (*rht_obj_cmpfn_t)(struct rhashtable_compare_arg *, const void *); struct rhashtable_params { u16 nelem_hint; u16 key_len; u16 key_offset; u16 head_offset; unsigned int max_size; u16 min_size; bool automatic_shrinking; rht_hashfn_t hashfn; rht_obj_hashfn_t obj_hashfn; rht_obj_cmpfn_t obj_cmpfn; }; struct bucket_table; struct rhashtable { struct bucket_table *tbl; unsigned int key_len; unsigned int max_elems; struct rhashtable_params p; bool rhlist; struct work_struct run_work; struct mutex mutex; spinlock_t lock; atomic_t nelems; }; struct fs_struct { int users; spinlock_t lock; seqcount_spinlock_t seq; int umask; int in_exec; struct path root; struct path pwd; }; struct pipe_buffer; struct watch_queue; struct pipe_inode_info { struct mutex mutex; wait_queue_head_t rd_wait; wait_queue_head_t wr_wait; unsigned int head; unsigned int tail; unsigned int max_usage; unsigned int ring_size; bool note_loss; unsigned int nr_accounted; unsigned int readers; unsigned int writers; unsigned int files; unsigned int r_counter; unsigned int w_counter; unsigned int poll_usage; struct page *tmp_page; struct fasync_struct *fasync_readers; struct fasync_struct *fasync_writers; struct pipe_buffer *bufs; struct user_struct *user; struct watch_queue *watch_queue; }; typedef short unsigned int __kernel_sa_family_t; typedef __kernel_sa_family_t sa_family_t; struct sockaddr { sa_family_t sa_family; char sa_data[14]; }; struct msghdr { void *msg_name; int msg_namelen; struct iov_iter msg_iter; union { void *msg_control; void *msg_control_user; }; bool msg_control_is_user: 1; __kernel_size_t msg_controllen; unsigned int msg_flags; struct kiocb *msg_iocb; }; typedef struct { unsigned int clock_rate; unsigned int clock_type; short unsigned int loopback; } sync_serial_settings; typedef struct { unsigned int clock_rate; unsigned int clock_type; short unsigned int loopback; unsigned int slot_map; } te1_settings; typedef struct { short unsigned int encoding; short unsigned int parity; } raw_hdlc_proto; typedef struct { unsigned int t391; unsigned int t392; unsigned int n391; unsigned int n392; unsigned int n393; short unsigned int lmi; short unsigned int dce; } fr_proto; typedef struct { unsigned int dlci; } fr_proto_pvc; typedef struct { unsigned int dlci; char master[16]; } fr_proto_pvc_info; typedef struct { unsigned int interval; unsigned int timeout; } cisco_proto; typedef struct { short unsigned int dce; unsigned int modulo; unsigned int window; unsigned int t1; unsigned int t2; unsigned int n2; } x25_hdlc_proto; struct ifmap { long unsigned int mem_start; long unsigned int mem_end; short unsigned int base_addr; unsigned char irq; unsigned char dma; unsigned char port; }; struct if_settings { unsigned int type; unsigned int size; union { raw_hdlc_proto *raw_hdlc; cisco_proto *cisco; fr_proto *fr; fr_proto_pvc *fr_pvc; fr_proto_pvc_info *fr_pvc_info; x25_hdlc_proto *x25; sync_serial_settings *sync; te1_settings *te1; } ifs_ifsu; }; struct ifreq { union { char ifrn_name[16]; } ifr_ifrn; union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; struct sockaddr ifru_netmask; struct sockaddr ifru_hwaddr; short int ifru_flags; int ifru_ivalue; int ifru_mtu; struct ifmap ifru_map; char ifru_slave[16]; char ifru_newname[16]; void *ifru_data; struct if_settings ifru_settings; } ifr_ifru; }; struct ld_semaphore { atomic_long_t count; raw_spinlock_t wait_lock; unsigned int wait_readers; struct list_head read_wait; struct list_head write_wait; }; typedef unsigned int tcflag_t; typedef unsigned char cc_t; typedef unsigned int speed_t; struct ktermios { tcflag_t c_iflag; tcflag_t c_oflag; tcflag_t c_cflag; tcflag_t c_lflag; cc_t c_line; cc_t c_cc[19]; speed_t c_ispeed; speed_t c_ospeed; }; struct winsize { short unsigned int ws_row; short unsigned int ws_col; short unsigned int ws_xpixel; short unsigned int ws_ypixel; }; struct tty_driver; struct tty_operations; struct tty_ldisc; struct tty_port; struct tty_struct { int magic; struct kref kref; struct device *dev; struct tty_driver *driver; const struct tty_operations *ops; int index; struct ld_semaphore ldisc_sem; struct tty_ldisc *ldisc; struct mutex atomic_write_lock; struct mutex legacy_mutex; struct mutex throttle_mutex; struct rw_semaphore termios_rwsem; struct mutex winsize_mutex; struct ktermios termios; struct ktermios termios_locked; char name[64]; long unsigned int flags; int count; struct winsize winsize; struct { spinlock_t lock; bool stopped; bool tco_stopped; long unsigned int unused[0]; } flow; struct { spinlock_t lock; struct pid *pgrp; struct pid *session; unsigned char pktstatus; bool packet; long unsigned int unused[0]; } ctrl; int hw_stopped; unsigned int receive_room; int flow_change; struct tty_struct *link; struct fasync_struct *fasync; wait_queue_head_t write_wait; wait_queue_head_t read_wait; struct work_struct hangup_work; void *disc_data; void *driver_data; spinlock_t files_lock; struct list_head tty_files; int closing; unsigned char *write_buf; int write_cnt; struct work_struct SAK_work; struct tty_port *port; }; typedef struct { size_t written; size_t count; union { char *buf; void *data; } arg; int error; } read_descriptor_t; struct posix_acl_entry { short int e_tag; short unsigned int e_perm; union { kuid_t e_uid; kgid_t e_gid; }; }; struct posix_acl { refcount_t a_refcount; struct callback_head a_rcu; unsigned int a_count; struct posix_acl_entry a_entries[0]; }; struct serial_icounter_struct; struct serial_struct; struct tty_operations { struct tty_struct * (*lookup)(struct tty_driver *, struct file *, int); int (*install)(struct tty_driver *, struct tty_struct *); void (*remove)(struct tty_driver *, struct tty_struct *); int (*open)(struct tty_struct *, struct file *); void (*close)(struct tty_struct *, struct file *); void (*shutdown)(struct tty_struct *); void (*cleanup)(struct tty_struct *); int (*write)(struct tty_struct *, const unsigned char *, int); int (*put_char)(struct tty_struct *, unsigned char); void (*flush_chars)(struct tty_struct *); unsigned int (*write_room)(struct tty_struct *); unsigned int (*chars_in_buffer)(struct tty_struct *); int (*ioctl)(struct tty_struct *, unsigned int, long unsigned int); long int (*compat_ioctl)(struct tty_struct *, unsigned int, long unsigned int); void (*set_termios)(struct tty_struct *, struct ktermios *); void (*throttle)(struct tty_struct *); void (*unthrottle)(struct tty_struct *); void (*stop)(struct tty_struct *); void (*start)(struct tty_struct *); void (*hangup)(struct tty_struct *); int (*break_ctl)(struct tty_struct *, int); void (*flush_buffer)(struct tty_struct *); void (*set_ldisc)(struct tty_struct *); void (*wait_until_sent)(struct tty_struct *, int); void (*send_xchar)(struct tty_struct *, char); int (*tiocmget)(struct tty_struct *); int (*tiocmset)(struct tty_struct *, unsigned int, unsigned int); int (*resize)(struct tty_struct *, struct winsize *); int (*get_icount)(struct tty_struct *, struct serial_icounter_struct *); int (*get_serial)(struct tty_struct *, struct serial_struct *); int (*set_serial)(struct tty_struct *, struct serial_struct *); void (*show_fdinfo)(struct tty_struct *, struct seq_file *); int (*proc_show)(struct seq_file *, void *); }; struct proc_dir_entry; struct tty_driver { int magic; struct kref kref; struct cdev **cdevs; struct module *owner; const char *driver_name; const char *name; int name_base; int major; int minor_start; unsigned int num; short int type; short int subtype; struct ktermios init_termios; long unsigned int flags; struct proc_dir_entry *proc_entry; struct tty_driver *other; struct tty_struct **ttys; struct tty_port **ports; struct ktermios **termios; void *driver_state; const struct tty_operations *ops; struct list_head tty_drivers; }; struct tty_buffer { union { struct tty_buffer *next; struct llist_node free; }; int used; int size; int commit; int read; int flags; long unsigned int data[0]; }; struct tty_bufhead { struct tty_buffer *head; struct work_struct work; struct mutex lock; atomic_t priority; struct tty_buffer sentinel; struct llist_head free; atomic_t mem_used; int mem_limit; struct tty_buffer *tail; }; struct tty_port_operations; struct tty_port_client_operations; struct tty_port { struct tty_bufhead buf; struct tty_struct *tty; struct tty_struct *itty; const struct tty_port_operations *ops; const struct tty_port_client_operations *client_ops; spinlock_t lock; int blocked_open; int count; wait_queue_head_t open_wait; wait_queue_head_t delta_msr_wait; long unsigned int flags; long unsigned int iflags; unsigned char console: 1; struct mutex mutex; struct mutex buf_mutex; unsigned char *xmit_buf; unsigned int close_delay; unsigned int closing_wait; int drain_delay; struct kref kref; void *client_data; }; struct tty_ldisc_ops { char *name; int num; int flags; int (*open)(struct tty_struct *); void (*close)(struct tty_struct *); void (*flush_buffer)(struct tty_struct *); ssize_t (*read)(struct tty_struct *, struct file *, unsigned char *, size_t, void **, long unsigned int); ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t); int (*ioctl)(struct tty_struct *, struct file *, unsigned int, long unsigned int); int (*compat_ioctl)(struct tty_struct *, struct file *, unsigned int, long unsigned int); void (*set_termios)(struct tty_struct *, struct ktermios *); __poll_t (*poll)(struct tty_struct *, struct file *, struct poll_table_struct *); int (*hangup)(struct tty_struct *); void (*receive_buf)(struct tty_struct *, const unsigned char *, const char *, int); void (*write_wakeup)(struct tty_struct *); void (*dcd_change)(struct tty_struct *, unsigned int); int (*receive_buf2)(struct tty_struct *, const unsigned char *, const char *, int); struct module *owner; }; struct tty_ldisc { struct tty_ldisc_ops *ops; struct tty_struct *tty; }; struct tty_port_operations { int (*carrier_raised)(struct tty_port *); void (*dtr_rts)(struct tty_port *, int); void (*shutdown)(struct tty_port *); int (*activate)(struct tty_port *, struct tty_struct *); void (*destruct)(struct tty_port *); }; struct tty_port_client_operations { int (*receive_buf)(struct tty_port *, const unsigned char *, const unsigned char *, size_t); void (*write_wakeup)(struct tty_port *); }; struct prot_inuse; struct netns_core { struct ctl_table_header *sysctl_hdr; int sysctl_somaxconn; int *sock_inuse; struct prot_inuse *prot_inuse; }; struct ipstats_mib; struct tcp_mib; struct linux_mib; struct udp_mib; struct linux_xfrm_mib; struct linux_tls_mib; struct mptcp_mib; struct icmp_mib; struct icmpmsg_mib; struct icmpv6_mib; struct icmpv6msg_mib; struct netns_mib { struct ipstats_mib *ip_statistics; struct ipstats_mib *ipv6_statistics; struct tcp_mib *tcp_statistics; struct linux_mib *net_statistics; struct udp_mib *udp_statistics; struct udp_mib *udp_stats_in6; struct linux_xfrm_mib *xfrm_statistics; struct linux_tls_mib *tls_statistics; struct mptcp_mib *mptcp_statistics; struct udp_mib *udplite_statistics; struct udp_mib *udplite_stats_in6; struct icmp_mib *icmp_statistics; struct icmpmsg_mib *icmpmsg_statistics; struct icmpv6_mib *icmpv6_statistics; struct icmpv6msg_mib *icmpv6msg_statistics; struct proc_dir_entry *proc_net_devsnmp6; }; struct netns_packet { struct mutex sklist_lock; struct hlist_head sklist; }; struct netns_unix { int sysctl_max_dgram_qlen; struct ctl_table_header *ctl; }; struct netns_nexthop { struct rb_root rb_root; struct hlist_head *devhash; unsigned int seq; u32 last_id_allocated; struct blocking_notifier_head notifier_chain; }; struct inet_hashinfo; struct inet_timewait_death_row { atomic_t tw_count; char tw_pad[60]; struct inet_hashinfo *hashinfo; int sysctl_max_tw_buckets; }; struct local_ports { seqlock_t lock; int range[2]; bool warned; }; struct ping_group_range { seqlock_t lock; kgid_t range[2]; }; typedef struct { u64 key[2]; } siphash_key_t; struct ipv4_devconf; struct ip_ra_chain; struct fib_rules_ops; struct fib_table; struct inet_peer_base; struct fqdir; struct tcp_congestion_ops; struct tcp_fastopen_context; struct fib_notifier_ops; struct netns_ipv4 { struct inet_timewait_death_row tcp_death_row; struct ctl_table_header *forw_hdr; struct ctl_table_header *frags_hdr; struct ctl_table_header *ipv4_hdr; struct ctl_table_header *route_hdr; struct ctl_table_header *xfrm4_hdr; struct ipv4_devconf *devconf_all; struct ipv4_devconf *devconf_dflt; struct ip_ra_chain *ra_chain; struct mutex ra_mutex; struct fib_rules_ops *rules_ops; struct fib_table *fib_main; struct fib_table *fib_default; unsigned int fib_rules_require_fldissect; bool fib_has_custom_rules; bool fib_has_custom_local_routes; bool fib_offload_disabled; int fib_num_tclassid_users; struct hlist_head *fib_table_hash; struct sock *fibnl; struct sock **icmp_sk; struct sock *mc_autojoin_sk; struct inet_peer_base *peers; struct sock **tcp_sk; struct fqdir *fqdir; u8 sysctl_icmp_echo_ignore_all; u8 sysctl_icmp_echo_enable_probe; u8 sysctl_icmp_echo_ignore_broadcasts; u8 sysctl_icmp_ignore_bogus_error_responses; u8 sysctl_icmp_errors_use_inbound_ifaddr; int sysctl_icmp_ratelimit; int sysctl_icmp_ratemask; struct local_ports ip_local_ports; u8 sysctl_tcp_ecn; u8 sysctl_tcp_ecn_fallback; u8 sysctl_ip_default_ttl; u8 sysctl_ip_no_pmtu_disc; u8 sysctl_ip_fwd_use_pmtu; u8 sysctl_ip_fwd_update_priority; u8 sysctl_ip_nonlocal_bind; u8 sysctl_ip_autobind_reuse; u8 sysctl_ip_dynaddr; u8 sysctl_ip_early_demux; u8 sysctl_raw_l3mdev_accept; u8 sysctl_tcp_early_demux; u8 sysctl_udp_early_demux; u8 sysctl_nexthop_compat_mode; u8 sysctl_fwmark_reflect; u8 sysctl_tcp_fwmark_accept; u8 sysctl_tcp_l3mdev_accept; u8 sysctl_tcp_mtu_probing; int sysctl_tcp_mtu_probe_floor; int sysctl_tcp_base_mss; int sysctl_tcp_min_snd_mss; int sysctl_tcp_probe_threshold; u32 sysctl_tcp_probe_interval; int sysctl_tcp_keepalive_time; int sysctl_tcp_keepalive_intvl; u8 sysctl_tcp_keepalive_probes; u8 sysctl_tcp_syn_retries; u8 sysctl_tcp_synack_retries; u8 sysctl_tcp_syncookies; u8 sysctl_tcp_migrate_req; int sysctl_tcp_reordering; u8 sysctl_tcp_retries1; u8 sysctl_tcp_retries2; u8 sysctl_tcp_orphan_retries; u8 sysctl_tcp_tw_reuse; int sysctl_tcp_fin_timeout; unsigned int sysctl_tcp_notsent_lowat; u8 sysctl_tcp_sack; u8 sysctl_tcp_window_scaling; u8 sysctl_tcp_timestamps; u8 sysctl_tcp_early_retrans; u8 sysctl_tcp_recovery; u8 sysctl_tcp_thin_linear_timeouts; u8 sysctl_tcp_slow_start_after_idle; u8 sysctl_tcp_retrans_collapse; u8 sysctl_tcp_stdurg; u8 sysctl_tcp_rfc1337; u8 sysctl_tcp_abort_on_overflow; u8 sysctl_tcp_fack; int sysctl_tcp_max_reordering; int sysctl_tcp_adv_win_scale; u8 sysctl_tcp_dsack; u8 sysctl_tcp_app_win; u8 sysctl_tcp_frto; u8 sysctl_tcp_nometrics_save; u8 sysctl_tcp_no_ssthresh_metrics_save; u8 sysctl_tcp_moderate_rcvbuf; u8 sysctl_tcp_tso_win_divisor; u8 sysctl_tcp_workaround_signed_windows; int sysctl_tcp_limit_output_bytes; int sysctl_tcp_challenge_ack_limit; int sysctl_tcp_min_rtt_wlen; u8 sysctl_tcp_min_tso_segs; u8 sysctl_tcp_autocorking; u8 sysctl_tcp_reflect_tos; u8 sysctl_tcp_comp_sack_nr; int sysctl_tcp_invalid_ratelimit; int sysctl_tcp_pacing_ss_ratio; int sysctl_tcp_pacing_ca_ratio; int sysctl_tcp_wmem[3]; int sysctl_tcp_rmem[3]; long unsigned int sysctl_tcp_comp_sack_delay_ns; long unsigned int sysctl_tcp_comp_sack_slack_ns; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; const struct tcp_congestion_ops *tcp_congestion_control; struct tcp_fastopen_context *tcp_fastopen_ctx; spinlock_t tcp_fastopen_ctx_lock; unsigned int sysctl_tcp_fastopen_blackhole_timeout; atomic_t tfo_active_disable_times; long unsigned int tfo_active_disable_stamp; int sysctl_udp_wmem_min; int sysctl_udp_rmem_min; u8 sysctl_fib_notify_on_flag_change; u8 sysctl_udp_l3mdev_accept; u8 sysctl_igmp_llm_reports; int sysctl_igmp_max_memberships; int sysctl_igmp_max_msf; int sysctl_igmp_qrv; struct ping_group_range ping_group_range; atomic_t dev_addr_genid; long unsigned int *sysctl_local_reserved_ports; int sysctl_ip_prot_sock; struct list_head mr_tables; struct fib_rules_ops *mr_rules_ops; u32 sysctl_fib_multipath_hash_fields; u8 sysctl_fib_multipath_use_neigh; u8 sysctl_fib_multipath_hash_policy; struct fib_notifier_ops *notifier_ops; unsigned int fib_seq; struct fib_notifier_ops *ipmr_notifier_ops; unsigned int ipmr_seq; atomic_t rt_genid; siphash_key_t ip_id_key; long: 64; long: 64; long: 64; long: 64; }; struct neighbour; struct dst_ops { short unsigned int family; unsigned int gc_thresh; int (*gc)(struct dst_ops *); struct dst_entry * (*check)(struct dst_entry *, __u32); unsigned int (*default_advmss)(const struct dst_entry *); unsigned int (*mtu)(const struct dst_entry *); u32 * (*cow_metrics)(struct dst_entry *, long unsigned int); void (*destroy)(struct dst_entry *); void (*ifdown)(struct dst_entry *, struct net_device *, int); struct dst_entry * (*negative_advice)(struct dst_entry *); void (*link_failure)(struct sk_buff *); void (*update_pmtu)(struct dst_entry *, struct sock *, struct sk_buff *, u32, bool); void (*redirect)(struct dst_entry *, struct sock *, struct sk_buff *); int (*local_out)(struct net *, struct sock *, struct sk_buff *); struct neighbour * (*neigh_lookup)(const struct dst_entry *, struct sk_buff *, const void *); void (*confirm_neigh)(const struct dst_entry *, const void *); struct kmem_cache *kmem_cachep; struct percpu_counter pcpuc_entries; long: 64; long: 64; long: 64; }; struct netns_sysctl_ipv6 { struct ctl_table_header *hdr; struct ctl_table_header *route_hdr; struct ctl_table_header *icmp_hdr; struct ctl_table_header *frags_hdr; struct ctl_table_header *xfrm6_hdr; int flush_delay; int ip6_rt_max_size; int ip6_rt_gc_min_interval; int ip6_rt_gc_timeout; int ip6_rt_gc_interval; int ip6_rt_gc_elasticity; int ip6_rt_mtu_expires; int ip6_rt_min_advmss; u32 multipath_hash_fields; u8 multipath_hash_policy; u8 bindv6only; u8 flowlabel_consistency; u8 auto_flowlabels; int icmpv6_time; u8 icmpv6_echo_ignore_all; u8 icmpv6_echo_ignore_multicast; u8 icmpv6_echo_ignore_anycast; long unsigned int icmpv6_ratemask[4]; long unsigned int *icmpv6_ratemask_ptr; u8 anycast_src_echo_reply; u8 ip_nonlocal_bind; u8 fwmark_reflect; u8 flowlabel_state_ranges; int idgen_retries; int idgen_delay; int flowlabel_reflect; int max_dst_opts_cnt; int max_hbh_opts_cnt; int max_dst_opts_len; int max_hbh_opts_len; int seg6_flowlabel; bool skip_notify_on_dev_down; u8 fib_notify_on_flag_change; }; struct ipv6_devconf; struct fib6_info; struct rt6_info; struct rt6_statistics; struct fib6_table; struct seg6_pernet_data; struct netns_ipv6 { struct dst_ops ip6_dst_ops; struct netns_sysctl_ipv6 sysctl; struct ipv6_devconf *devconf_all; struct ipv6_devconf *devconf_dflt; struct inet_peer_base *peers; struct fqdir *fqdir; struct fib6_info *fib6_null_entry; struct rt6_info *ip6_null_entry; struct rt6_statistics *rt6_stats; struct timer_list ip6_fib_timer; struct hlist_head *fib_table_hash; struct fib6_table *fib6_main_tbl; struct list_head fib6_walkers; rwlock_t fib6_walker_lock; spinlock_t fib6_gc_lock; unsigned int ip6_rt_gc_expire; long unsigned int ip6_rt_last_gc; unsigned int fib6_rules_require_fldissect; bool fib6_has_custom_rules; unsigned int fib6_routes_require_src; struct rt6_info *ip6_prohibit_entry; struct rt6_info *ip6_blk_hole_entry; struct fib6_table *fib6_local_tbl; struct fib_rules_ops *fib6_rules_ops; struct sock **icmp_sk; struct sock *ndisc_sk; struct sock *tcp_sk; struct sock *igmp_sk; struct sock *mc_autojoin_sk; struct list_head mr6_tables; struct fib_rules_ops *mr6_rules_ops; atomic_t dev_addr_genid; atomic_t fib6_sernum; struct seg6_pernet_data *seg6_data; struct fib_notifier_ops *notifier_ops; struct fib_notifier_ops *ip6mr_notifier_ops; unsigned int ipmr_seq; struct { struct hlist_head head; spinlock_t lock; u32 seq; } ip6addrlbl_table; long: 64; long: 64; long: 64; }; struct netns_sysctl_lowpan { struct ctl_table_header *frags_hdr; }; struct netns_ieee802154_lowpan { struct netns_sysctl_lowpan sysctl; struct fqdir *fqdir; }; struct sctp_mib; struct netns_sctp { struct sctp_mib *sctp_statistics; struct proc_dir_entry *proc_net_sctp; struct ctl_table_header *sysctl_header; struct sock *ctl_sock; struct sock *udp4_sock; struct sock *udp6_sock; int udp_port; int encap_port; struct list_head local_addr_list; struct list_head addr_waitq; struct timer_list addr_wq_timer; struct list_head auto_asconf_splist; spinlock_t addr_wq_lock; spinlock_t local_addr_lock; unsigned int rto_initial; unsigned int rto_min; unsigned int rto_max; int rto_alpha; int rto_beta; int max_burst; int cookie_preserve_enable; char *sctp_hmac_alg; unsigned int valid_cookie_life; unsigned int sack_timeout; unsigned int hb_interval; unsigned int probe_interval; int max_retrans_association; int max_retrans_path; int max_retrans_init; int pf_retrans; int ps_retrans; int pf_enable; int pf_expose; int sndbuf_policy; int rcvbuf_policy; int default_auto_asconf; int addip_enable; int addip_noauth; int prsctp_enable; int reconf_enable; int auth_enable; int intl_enable; int ecn_enable; int scope_policy; int rwnd_upd_shift; long unsigned int max_autoclose; }; struct nf_queue_handler; struct nf_logger; struct nf_hook_entries; struct netns_nf { struct proc_dir_entry *proc_netfilter; const struct nf_queue_handler *queue_handler; const struct nf_logger *nf_loggers[13]; struct ctl_table_header *nf_log_dir_header; struct nf_hook_entries *hooks_ipv4[5]; struct nf_hook_entries *hooks_ipv6[5]; struct nf_hook_entries *hooks_arp[3]; struct nf_hook_entries *hooks_bridge[5]; }; struct netns_xt { bool notrack_deprecated_warning; bool clusterip_deprecated_warning; }; struct nf_generic_net { unsigned int timeout; }; struct nf_tcp_net { unsigned int timeouts[14]; u8 tcp_loose; u8 tcp_be_liberal; u8 tcp_max_retrans; u8 tcp_ignore_invalid_rst; unsigned int offload_timeout; }; struct nf_udp_net { unsigned int timeouts[2]; unsigned int offload_timeout; }; struct nf_icmp_net { unsigned int timeout; }; struct nf_dccp_net { u8 dccp_loose; unsigned int dccp_timeout[10]; }; struct nf_sctp_net { unsigned int timeouts[10]; }; struct nf_gre_net { struct list_head keymap_list; unsigned int timeouts[2]; }; struct nf_ip_net { struct nf_generic_net generic; struct nf_tcp_net tcp; struct nf_udp_net udp; struct nf_icmp_net icmp; struct nf_icmp_net icmpv6; struct nf_dccp_net dccp; struct nf_sctp_net sctp; struct nf_gre_net gre; }; struct ct_pcpu; struct ip_conntrack_stat; struct nf_ct_event_notifier; struct nf_exp_event_notifier; struct netns_ct { bool ecache_dwork_pending; u8 sysctl_log_invalid; u8 sysctl_events; u8 sysctl_acct; u8 sysctl_auto_assign_helper; u8 sysctl_tstamp; u8 sysctl_checksum; struct ct_pcpu *pcpu_lists; struct ip_conntrack_stat *stat; struct nf_ct_event_notifier *nf_conntrack_event_cb; struct nf_exp_event_notifier *nf_expect_event_cb; struct nf_ip_net nf_ct_proto; unsigned int labels_used; }; struct netns_nftables { u8 gencursor; }; struct netns_bpf { struct bpf_prog_array *run_array[2]; struct bpf_prog *progs[2]; struct list_head links[2]; }; struct xfrm_policy_hash { struct hlist_head *table; unsigned int hmask; u8 dbits4; u8 sbits4; u8 dbits6; u8 sbits6; }; struct xfrm_policy_hthresh { struct work_struct work; seqlock_t lock; u8 lbits4; u8 rbits4; u8 lbits6; u8 rbits6; }; struct netns_xfrm { struct list_head state_all; struct hlist_head *state_bydst; struct hlist_head *state_bysrc; struct hlist_head *state_byspi; struct hlist_head *state_byseq; unsigned int state_hmask; unsigned int state_num; struct work_struct state_hash_work; struct list_head policy_all; struct hlist_head *policy_byidx; unsigned int policy_idx_hmask; struct hlist_head policy_inexact[3]; struct xfrm_policy_hash policy_bydst[3]; unsigned int policy_count[6]; struct work_struct policy_hash_work; struct xfrm_policy_hthresh policy_hthresh; struct list_head inexact_bins; struct sock *nlsk; struct sock *nlsk_stash; u32 sysctl_aevent_etime; u32 sysctl_aevent_rseqth; int sysctl_larval_drop; u32 sysctl_acq_expires; struct ctl_table_header *sysctl_hdr; long: 64; long: 64; long: 64; long: 64; struct dst_ops xfrm4_dst_ops; struct dst_ops xfrm6_dst_ops; spinlock_t xfrm_state_lock; seqcount_spinlock_t xfrm_state_hash_generation; seqcount_spinlock_t xfrm_policy_hash_generation; spinlock_t xfrm_policy_lock; struct mutex xfrm_cfg_mutex; long: 64; long: 64; }; struct netns_ipvs; struct mpls_route; struct netns_mpls { int ip_ttl_propagate; int default_ttl; size_t platform_labels; struct mpls_route **platform_label; struct ctl_table_header *ctl; }; struct can_dev_rcv_lists; struct can_pkg_stats; struct can_rcv_lists_stats; struct netns_can { struct proc_dir_entry *proc_dir; struct proc_dir_entry *pde_stats; struct proc_dir_entry *pde_reset_stats; struct proc_dir_entry *pde_rcvlist_all; struct proc_dir_entry *pde_rcvlist_fil; struct proc_dir_entry *pde_rcvlist_inv; struct proc_dir_entry *pde_rcvlist_sff; struct proc_dir_entry *pde_rcvlist_eff; struct proc_dir_entry *pde_rcvlist_err; struct proc_dir_entry *bcmproc_dir; struct can_dev_rcv_lists *rx_alldev_list; spinlock_t rcvlists_lock; struct timer_list stattimer; struct can_pkg_stats *pkg_stats; struct can_rcv_lists_stats *rcv_lists_stats; struct hlist_head cgw_list; }; struct netns_xdp { struct mutex lock; struct hlist_head list; }; struct smc_stats; struct smc_stats_rsn; struct netns_smc { struct smc_stats *smc_stats; struct mutex mutex_fback_rsn; struct smc_stats_rsn *fback_rsn; }; struct uevent_sock; struct net_generic; struct net { refcount_t passive; spinlock_t rules_mod_lock; unsigned int dev_unreg_count; unsigned int dev_base_seq; int ifindex; spinlock_t nsid_lock; atomic_t fnhe_genid; struct list_head list; struct list_head exit_list; struct llist_node cleanup_list; struct key_tag *key_domain; struct user_namespace *user_ns; struct ucounts *ucounts; struct idr netns_ids; struct ns_common ns; struct list_head dev_base_head; struct proc_dir_entry *proc_net; struct proc_dir_entry *proc_net_stat; struct ctl_table_set sysctls; struct sock *rtnl; struct sock *genl_sock; struct uevent_sock *uevent_sock; struct hlist_head *dev_name_head; struct hlist_head *dev_index_head; struct raw_notifier_head netdev_chain; u32 hash_mix; struct net_device *loopback_dev; struct list_head rules_ops; struct netns_core core; struct netns_mib mib; struct netns_packet packet; struct netns_unix unx; struct netns_nexthop nexthop; struct netns_ipv4 ipv4; struct netns_ipv6 ipv6; struct netns_ieee802154_lowpan ieee802154_lowpan; struct netns_sctp sctp; struct netns_nf nf; struct netns_xt xt; struct netns_ct ct; struct netns_nftables nft; struct sk_buff_head wext_nlevents; struct net_generic *gen; struct netns_bpf bpf; long: 64; long: 64; struct netns_xfrm xfrm; u64 net_cookie; struct netns_ipvs *ipvs; struct netns_mpls mpls; struct netns_can can; struct netns_xdp xdp; struct sock *crypto_nlsk; struct sock *diag_nlsk; struct netns_smc smc; long: 64; }; typedef struct { local64_t v; } u64_stats_t; struct bpf_insn { __u8 code; __u8 dst_reg: 4; __u8 src_reg: 4; __s16 off; __s32 imm; }; enum bpf_map_type { BPF_MAP_TYPE_UNSPEC = 0, BPF_MAP_TYPE_HASH = 1, BPF_MAP_TYPE_ARRAY = 2, BPF_MAP_TYPE_PROG_ARRAY = 3, BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4, BPF_MAP_TYPE_PERCPU_HASH = 5, BPF_MAP_TYPE_PERCPU_ARRAY = 6, BPF_MAP_TYPE_STACK_TRACE = 7, BPF_MAP_TYPE_CGROUP_ARRAY = 8, BPF_MAP_TYPE_LRU_HASH = 9, BPF_MAP_TYPE_LRU_PERCPU_HASH = 10, BPF_MAP_TYPE_LPM_TRIE = 11, BPF_MAP_TYPE_ARRAY_OF_MAPS = 12, BPF_MAP_TYPE_HASH_OF_MAPS = 13, BPF_MAP_TYPE_DEVMAP = 14, BPF_MAP_TYPE_SOCKMAP = 15, BPF_MAP_TYPE_CPUMAP = 16, BPF_MAP_TYPE_XSKMAP = 17, BPF_MAP_TYPE_SOCKHASH = 18, BPF_MAP_TYPE_CGROUP_STORAGE = 19, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY = 20, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 21, BPF_MAP_TYPE_QUEUE = 22, BPF_MAP_TYPE_STACK = 23, BPF_MAP_TYPE_SK_STORAGE = 24, BPF_MAP_TYPE_DEVMAP_HASH = 25, BPF_MAP_TYPE_STRUCT_OPS = 26, BPF_MAP_TYPE_RINGBUF = 27, BPF_MAP_TYPE_INODE_STORAGE = 28, BPF_MAP_TYPE_TASK_STORAGE = 29, }; enum bpf_prog_type { BPF_PROG_TYPE_UNSPEC = 0, BPF_PROG_TYPE_SOCKET_FILTER = 1, BPF_PROG_TYPE_KPROBE = 2, BPF_PROG_TYPE_SCHED_CLS = 3, BPF_PROG_TYPE_SCHED_ACT = 4, BPF_PROG_TYPE_TRACEPOINT = 5, BPF_PROG_TYPE_XDP = 6, BPF_PROG_TYPE_PERF_EVENT = 7, BPF_PROG_TYPE_CGROUP_SKB = 8, BPF_PROG_TYPE_CGROUP_SOCK = 9, BPF_PROG_TYPE_LWT_IN = 10, BPF_PROG_TYPE_LWT_OUT = 11, BPF_PROG_TYPE_LWT_XMIT = 12, BPF_PROG_TYPE_SOCK_OPS = 13, BPF_PROG_TYPE_SK_SKB = 14, BPF_PROG_TYPE_CGROUP_DEVICE = 15, BPF_PROG_TYPE_SK_MSG = 16, BPF_PROG_TYPE_RAW_TRACEPOINT = 17, BPF_PROG_TYPE_CGROUP_SOCK_ADDR = 18, BPF_PROG_TYPE_LWT_SEG6LOCAL = 19, BPF_PROG_TYPE_LIRC_MODE2 = 20, BPF_PROG_TYPE_SK_REUSEPORT = 21, BPF_PROG_TYPE_FLOW_DISSECTOR = 22, BPF_PROG_TYPE_CGROUP_SYSCTL = 23, BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE = 24, BPF_PROG_TYPE_CGROUP_SOCKOPT = 25, BPF_PROG_TYPE_TRACING = 26, BPF_PROG_TYPE_STRUCT_OPS = 27, BPF_PROG_TYPE_EXT = 28, BPF_PROG_TYPE_LSM = 29, BPF_PROG_TYPE_SK_LOOKUP = 30, BPF_PROG_TYPE_SYSCALL = 31, }; enum bpf_attach_type { BPF_CGROUP_INET_INGRESS = 0, BPF_CGROUP_INET_EGRESS = 1, BPF_CGROUP_INET_SOCK_CREATE = 2, BPF_CGROUP_SOCK_OPS = 3, BPF_SK_SKB_STREAM_PARSER = 4, BPF_SK_SKB_STREAM_VERDICT = 5, BPF_CGROUP_DEVICE = 6, BPF_SK_MSG_VERDICT = 7, BPF_CGROUP_INET4_BIND = 8, BPF_CGROUP_INET6_BIND = 9, BPF_CGROUP_INET4_CONNECT = 10, BPF_CGROUP_INET6_CONNECT = 11, BPF_CGROUP_INET4_POST_BIND = 12, BPF_CGROUP_INET6_POST_BIND = 13, BPF_CGROUP_UDP4_SENDMSG = 14, BPF_CGROUP_UDP6_SENDMSG = 15, BPF_LIRC_MODE2 = 16, BPF_FLOW_DISSECTOR = 17, BPF_CGROUP_SYSCTL = 18, BPF_CGROUP_UDP4_RECVMSG = 19, BPF_CGROUP_UDP6_RECVMSG = 20, BPF_CGROUP_GETSOCKOPT = 21, BPF_CGROUP_SETSOCKOPT = 22, BPF_TRACE_RAW_TP = 23, BPF_TRACE_FENTRY = 24, BPF_TRACE_FEXIT = 25, BPF_MODIFY_RETURN = 26, BPF_LSM_MAC = 27, BPF_TRACE_ITER = 28, BPF_CGROUP_INET4_GETPEERNAME = 29, BPF_CGROUP_INET6_GETPEERNAME = 30, BPF_CGROUP_INET4_GETSOCKNAME = 31, BPF_CGROUP_INET6_GETSOCKNAME = 32, BPF_XDP_DEVMAP = 33, BPF_CGROUP_INET_SOCK_RELEASE = 34, BPF_XDP_CPUMAP = 35, BPF_SK_LOOKUP = 36, BPF_XDP = 37, BPF_SK_SKB_VERDICT = 38, BPF_SK_REUSEPORT_SELECT = 39, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE = 40, __MAX_BPF_ATTACH_TYPE = 41, }; union bpf_attr { struct { __u32 map_type; __u32 key_size; __u32 value_size; __u32 max_entries; __u32 map_flags; __u32 inner_map_fd; __u32 numa_node; char map_name[16]; __u32 map_ifindex; __u32 btf_fd; __u32 btf_key_type_id; __u32 btf_value_type_id; __u32 btf_vmlinux_value_type_id; }; struct { __u32 map_fd; __u64 key; union { __u64 value; __u64 next_key; }; __u64 flags; }; struct { __u64 in_batch; __u64 out_batch; __u64 keys; __u64 values; __u32 count; __u32 map_fd; __u64 elem_flags; __u64 flags; } batch; struct { __u32 prog_type; __u32 insn_cnt; __u64 insns; __u64 license; __u32 log_level; __u32 log_size; __u64 log_buf; __u32 kern_version; __u32 prog_flags; char prog_name[16]; __u32 prog_ifindex; __u32 expected_attach_type; __u32 prog_btf_fd; __u32 func_info_rec_size; __u64 func_info; __u32 func_info_cnt; __u32 line_info_rec_size; __u64 line_info; __u32 line_info_cnt; __u32 attach_btf_id; union { __u32 attach_prog_fd; __u32 attach_btf_obj_fd; }; __u64 fd_array; }; struct { __u64 pathname; __u32 bpf_fd; __u32 file_flags; }; struct { __u32 target_fd; __u32 attach_bpf_fd; __u32 attach_type; __u32 attach_flags; __u32 replace_bpf_fd; }; struct { __u32 prog_fd; __u32 retval; __u32 data_size_in; __u32 data_size_out; __u64 data_in; __u64 data_out; __u32 repeat; __u32 duration; __u32 ctx_size_in; __u32 ctx_size_out; __u64 ctx_in; __u64 ctx_out; __u32 flags; __u32 cpu; } test; struct { union { __u32 start_id; __u32 prog_id; __u32 map_id; __u32 btf_id; __u32 link_id; }; __u32 next_id; __u32 open_flags; }; struct { __u32 bpf_fd; __u32 info_len; __u64 info; } info; struct { __u32 target_fd; __u32 attach_type; __u32 query_flags; __u32 attach_flags; __u64 prog_ids; __u32 prog_cnt; } query; struct { __u64 name; __u32 prog_fd; } raw_tracepoint; struct { __u64 btf; __u64 btf_log_buf; __u32 btf_size; __u32 btf_log_size; __u32 btf_log_level; }; struct { __u32 pid; __u32 fd; __u32 flags; __u32 buf_len; __u64 buf; __u32 prog_id; __u32 fd_type; __u64 probe_offset; __u64 probe_addr; } task_fd_query; struct { __u32 prog_fd; union { __u32 target_fd; __u32 target_ifindex; }; __u32 attach_type; __u32 flags; union { __u32 target_btf_id; struct { __u64 iter_info; __u32 iter_info_len; }; }; } link_create; struct { __u32 link_fd; __u32 new_prog_fd; __u32 flags; __u32 old_prog_fd; } link_update; struct { __u32 link_fd; } link_detach; struct { __u32 type; } enable_stats; struct { __u32 link_fd; __u32 flags; } iter_create; struct { __u32 prog_fd; __u32 map_fd; __u32 flags; } prog_bind_map; }; struct bpf_func_info { __u32 insn_off; __u32 type_id; }; struct bpf_line_info { __u32 insn_off; __u32 file_name_off; __u32 line_off; __u32 line_col; }; typedef struct { union { void *kernel; void *user; }; bool is_kernel: 1; } sockptr_t; struct bpf_iter_aux_info; typedef int (*bpf_iter_init_seq_priv_t)(void *, struct bpf_iter_aux_info *); struct bpf_map; struct bpf_iter_aux_info { struct bpf_map *map; }; typedef void (*bpf_iter_fini_seq_priv_t)(void *); struct bpf_iter_seq_info { const struct seq_operations *seq_ops; bpf_iter_init_seq_priv_t init_seq_private; bpf_iter_fini_seq_priv_t fini_seq_private; u32 seq_priv_size; }; struct btf; struct btf_type; struct bpf_prog_aux; struct bpf_local_storage_map; struct bpf_verifier_env; struct bpf_func_state; struct bpf_map_ops { int (*map_alloc_check)(union bpf_attr *); struct bpf_map * (*map_alloc)(union bpf_attr *); void (*map_release)(struct bpf_map *, struct file *); void (*map_free)(struct bpf_map *); int (*map_get_next_key)(struct bpf_map *, void *, void *); void (*map_release_uref)(struct bpf_map *); void * (*map_lookup_elem_sys_only)(struct bpf_map *, void *); int (*map_lookup_batch)(struct bpf_map *, const union bpf_attr *, union bpf_attr *); int (*map_lookup_and_delete_elem)(struct bpf_map *, void *, void *, u64); int (*map_lookup_and_delete_batch)(struct bpf_map *, const union bpf_attr *, union bpf_attr *); int (*map_update_batch)(struct bpf_map *, const union bpf_attr *, union bpf_attr *); int (*map_delete_batch)(struct bpf_map *, const union bpf_attr *, union bpf_attr *); void * (*map_lookup_elem)(struct bpf_map *, void *); int (*map_update_elem)(struct bpf_map *, void *, void *, u64); int (*map_delete_elem)(struct bpf_map *, void *); int (*map_push_elem)(struct bpf_map *, void *, u64); int (*map_pop_elem)(struct bpf_map *, void *); int (*map_peek_elem)(struct bpf_map *, void *); void * (*map_fd_get_ptr)(struct bpf_map *, struct file *, int); void (*map_fd_put_ptr)(void *); int (*map_gen_lookup)(struct bpf_map *, struct bpf_insn *); u32 (*map_fd_sys_lookup_elem)(void *); void (*map_seq_show_elem)(struct bpf_map *, void *, struct seq_file *); int (*map_check_btf)(const struct bpf_map *, const struct btf *, const struct btf_type *, const struct btf_type *); int (*map_poke_track)(struct bpf_map *, struct bpf_prog_aux *); void (*map_poke_untrack)(struct bpf_map *, struct bpf_prog_aux *); void (*map_poke_run)(struct bpf_map *, u32, struct bpf_prog *, struct bpf_prog *); int (*map_direct_value_addr)(const struct bpf_map *, u64 *, u32); int (*map_direct_value_meta)(const struct bpf_map *, u64, u32 *); int (*map_mmap)(struct bpf_map *, struct vm_area_struct *); __poll_t (*map_poll)(struct bpf_map *, struct file *, struct poll_table_struct *); int (*map_local_storage_charge)(struct bpf_local_storage_map *, void *, u32); void (*map_local_storage_uncharge)(struct bpf_local_storage_map *, void *, u32); struct bpf_local_storage ** (*map_owner_storage_ptr)(void *); int (*map_redirect)(struct bpf_map *, u32, u64); bool (*map_meta_equal)(const struct bpf_map *, const struct bpf_map *); int (*map_set_for_each_callback_args)(struct bpf_verifier_env *, struct bpf_func_state *, struct bpf_func_state *); int (*map_for_each_callback)(struct bpf_map *, void *, void *, u64); const char * const map_btf_name; int *map_btf_id; const struct bpf_iter_seq_info *iter_seq_info; }; struct bpf_map { const struct bpf_map_ops *ops; struct bpf_map *inner_map_meta; void *security; enum bpf_map_type map_type; u32 key_size; u32 value_size; u32 max_entries; u32 map_flags; int spin_lock_off; u32 id; int numa_node; u32 btf_key_type_id; u32 btf_value_type_id; struct btf *btf; struct mem_cgroup *memcg; char name[16]; u32 btf_vmlinux_value_type_id; bool bypass_spec_v1; bool frozen; long: 16; long: 64; long: 64; long: 64; atomic64_t refcnt; atomic64_t usercnt; struct work_struct work; struct mutex freeze_mutex; u64 writecnt; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct btf_header { __u16 magic; __u8 version; __u8 flags; __u32 hdr_len; __u32 type_off; __u32 type_len; __u32 str_off; __u32 str_len; }; struct btf { void *data; struct btf_type **types; u32 *resolved_ids; u32 *resolved_sizes; const char *strings; void *nohdr_data; struct btf_header hdr; u32 nr_types; u32 types_size; u32 data_size; refcount_t refcnt; u32 id; struct callback_head rcu; struct btf *base_btf; u32 start_id; u32 start_str_off; char name[56]; bool kernel_btf; }; struct btf_type { __u32 name_off; __u32 info; union { __u32 size; __u32 type; }; }; struct bpf_ksym { long unsigned int start; long unsigned int end; char name[128]; struct list_head lnode; struct latch_tree_node tnode; bool prog; }; struct bpf_ctx_arg_aux; struct bpf_trampoline; struct bpf_jit_poke_descriptor; struct bpf_kfunc_desc_tab; struct bpf_prog_ops; struct btf_mod_pair; struct bpf_prog_offload; struct bpf_func_info_aux; struct bpf_prog_aux { atomic64_t refcnt; u32 used_map_cnt; u32 used_btf_cnt; u32 max_ctx_offset; u32 max_pkt_offset; u32 max_tp_access; u32 stack_depth; u32 id; u32 func_cnt; u32 func_idx; u32 attach_btf_id; u32 ctx_arg_info_size; u32 max_rdonly_access; u32 max_rdwr_access; struct btf *attach_btf; const struct bpf_ctx_arg_aux *ctx_arg_info; struct mutex dst_mutex; struct bpf_prog *dst_prog; struct bpf_trampoline *dst_trampoline; enum bpf_prog_type saved_dst_prog_type; enum bpf_attach_type saved_dst_attach_type; bool verifier_zext; bool offload_requested; bool attach_btf_trace; bool func_proto_unreliable; bool sleepable; bool tail_call_reachable; struct hlist_node tramp_hlist; const struct btf_type *attach_func_proto; const char *attach_func_name; struct bpf_prog **func; void *jit_data; struct bpf_jit_poke_descriptor *poke_tab; struct bpf_kfunc_desc_tab *kfunc_tab; u32 size_poke_tab; struct bpf_ksym ksym; const struct bpf_prog_ops *ops; struct bpf_map **used_maps; struct mutex used_maps_mutex; struct btf_mod_pair *used_btfs; struct bpf_prog *prog; struct user_struct *user; u64 load_time; struct bpf_map *cgroup_storage[2]; char name[16]; void *security; struct bpf_prog_offload *offload; struct btf *btf; struct bpf_func_info *func_info; struct bpf_func_info_aux *func_info_aux; struct bpf_line_info *linfo; void **jited_linfo; u32 func_info_cnt; u32 nr_linfo; u32 linfo_idx; u32 num_exentries; struct exception_table_entry *extable; union { struct work_struct work; struct callback_head rcu; }; }; struct sock_filter { __u16 code; __u8 jt; __u8 jf; __u32 k; }; struct bpf_prog_stats; struct sock_fprog_kern; struct bpf_prog { u16 pages; u16 jited: 1; u16 jit_requested: 1; u16 gpl_compatible: 1; u16 cb_access: 1; u16 dst_needed: 1; u16 blinded: 1; u16 is_func: 1; u16 kprobe_override: 1; u16 has_callchain_buf: 1; u16 enforce_expected_attach_type: 1; u16 call_get_stack: 1; enum bpf_prog_type type; enum bpf_attach_type expected_attach_type; u32 len; u32 jited_len; u8 tag[8]; struct bpf_prog_stats *stats; int *active; unsigned int (*bpf_func)(const void *, const struct bpf_insn *); struct bpf_prog_aux *aux; struct sock_fprog_kern *orig_prog; struct sock_filter insns[0]; struct bpf_insn insnsi[0]; }; struct bpf_offloaded_map; struct bpf_map_dev_ops { int (*map_get_next_key)(struct bpf_offloaded_map *, void *, void *); int (*map_lookup_elem)(struct bpf_offloaded_map *, void *, void *); int (*map_update_elem)(struct bpf_offloaded_map *, void *, void *, u64); int (*map_delete_elem)(struct bpf_offloaded_map *, void *); }; struct bpf_offloaded_map { struct bpf_map map; struct net_device *netdev; const struct bpf_map_dev_ops *dev_ops; void *dev_priv; struct list_head offloads; long: 64; long: 64; long: 64; }; struct net_device_stats { long unsigned int rx_packets; long unsigned int tx_packets; long unsigned int rx_bytes; long unsigned int tx_bytes; long unsigned int rx_errors; long unsigned int tx_errors; long unsigned int rx_dropped; long unsigned int tx_dropped; long unsigned int multicast; long unsigned int collisions; long unsigned int rx_length_errors; long unsigned int rx_over_errors; long unsigned int rx_crc_errors; long unsigned int rx_frame_errors; long unsigned int rx_fifo_errors; long unsigned int rx_missed_errors; long unsigned int tx_aborted_errors; long unsigned int tx_carrier_errors; long unsigned int tx_fifo_errors; long unsigned int tx_heartbeat_errors; long unsigned int tx_window_errors; long unsigned int rx_compressed; long unsigned int tx_compressed; }; struct netdev_hw_addr_list { struct list_head list; int count; }; struct tipc_bearer; struct mpls_dev; enum rx_handler_result { RX_HANDLER_CONSUMED = 0, RX_HANDLER_ANOTHER = 1, RX_HANDLER_EXACT = 2, RX_HANDLER_PASS = 3, }; typedef enum rx_handler_result rx_handler_result_t; typedef rx_handler_result_t rx_handler_func_t(struct sk_buff **); enum netdev_ml_priv_type { ML_PRIV_NONE = 0, ML_PRIV_CAN = 1, }; struct pcpu_dstats; struct garp_port; struct mrp_port; struct netdev_tc_txq { u16 count; u16 offset; }; struct macsec_ops; struct udp_tunnel_nic; struct bpf_xdp_link; struct bpf_xdp_entity { struct bpf_prog *prog; struct bpf_xdp_link *link; }; struct netdev_name_node; struct dev_ifalias; struct net_device_ops; struct iw_handler_def; struct iw_public_data; struct ethtool_ops; struct l3mdev_ops; struct ndisc_ops; struct xfrmdev_ops; struct tlsdev_ops; struct header_ops; struct vlan_info; struct dsa_port; struct in_device; struct inet6_dev; struct wireless_dev; struct wpan_dev; struct netdev_rx_queue; struct mini_Qdisc; struct netdev_queue; struct cpu_rmap; struct Qdisc; struct xdp_dev_bulk_queue; struct xps_dev_maps; struct netpoll_info; struct pcpu_lstats; struct pcpu_sw_netstats; struct rtnl_link_ops; struct dcbnl_rtnl_ops; struct netprio_map; struct phy_device; struct sfp_bus; struct udp_tunnel_nic_info; struct net_device { char name[16]; struct netdev_name_node *name_node; struct dev_ifalias *ifalias; long unsigned int mem_end; long unsigned int mem_start; long unsigned int base_addr; long unsigned int state; struct list_head dev_list; struct list_head napi_list; struct list_head unreg_list; struct list_head close_list; struct list_head ptype_all; struct list_head ptype_specific; struct { struct list_head upper; struct list_head lower; } adj_list; unsigned int flags; unsigned int priv_flags; const struct net_device_ops *netdev_ops; int ifindex; short unsigned int gflags; short unsigned int hard_header_len; unsigned int mtu; short unsigned int needed_headroom; short unsigned int needed_tailroom; netdev_features_t features; netdev_features_t hw_features; netdev_features_t wanted_features; netdev_features_t vlan_features; netdev_features_t hw_enc_features; netdev_features_t mpls_features; netdev_features_t gso_partial_features; unsigned int min_mtu; unsigned int max_mtu; short unsigned int type; unsigned char min_header_len; unsigned char name_assign_type; int group; struct net_device_stats stats; atomic_long_t rx_dropped; atomic_long_t tx_dropped; atomic_long_t rx_nohandler; atomic_t carrier_up_count; atomic_t carrier_down_count; const struct iw_handler_def *wireless_handlers; struct iw_public_data *wireless_data; const struct ethtool_ops *ethtool_ops; const struct l3mdev_ops *l3mdev_ops; const struct ndisc_ops *ndisc_ops; const struct xfrmdev_ops *xfrmdev_ops; const struct tlsdev_ops *tlsdev_ops; const struct header_ops *header_ops; unsigned char operstate; unsigned char link_mode; unsigned char if_port; unsigned char dma; unsigned char perm_addr[32]; unsigned char addr_assign_type; unsigned char addr_len; unsigned char upper_level; unsigned char lower_level; short unsigned int neigh_priv_len; short unsigned int dev_id; short unsigned int dev_port; short unsigned int padded; spinlock_t addr_list_lock; int irq; struct netdev_hw_addr_list uc; struct netdev_hw_addr_list mc; struct netdev_hw_addr_list dev_addrs; struct kset *queues_kset; unsigned int promiscuity; unsigned int allmulti; bool uc_promisc; struct vlan_info *vlan_info; struct dsa_port *dsa_ptr; struct tipc_bearer *tipc_ptr; void *atalk_ptr; struct in_device *ip_ptr; struct inet6_dev *ip6_ptr; void *ax25_ptr; struct wireless_dev *ieee80211_ptr; struct wpan_dev *ieee802154_ptr; struct mpls_dev *mpls_ptr; unsigned char *dev_addr; struct netdev_rx_queue *_rx; unsigned int num_rx_queues; unsigned int real_num_rx_queues; struct bpf_prog *xdp_prog; long unsigned int gro_flush_timeout; int napi_defer_hard_irqs; rx_handler_func_t *rx_handler; void *rx_handler_data; struct mini_Qdisc *miniq_ingress; struct netdev_queue *ingress_queue; struct nf_hook_entries *nf_hooks_ingress; unsigned char broadcast[32]; struct cpu_rmap *rx_cpu_rmap; struct hlist_node index_hlist; long: 64; struct netdev_queue *_tx; unsigned int num_tx_queues; unsigned int real_num_tx_queues; struct Qdisc *qdisc; unsigned int tx_queue_len; spinlock_t tx_global_lock; struct xdp_dev_bulk_queue *xdp_bulkq; struct xps_dev_maps *xps_maps[2]; struct mini_Qdisc *miniq_egress; struct hlist_head qdisc_hash[16]; struct timer_list watchdog_timer; int watchdog_timeo; u32 proto_down_reason; struct list_head todo_list; int *pcpu_refcnt; struct list_head link_watch_list; enum { NETREG_UNINITIALIZED = 0, NETREG_REGISTERED = 1, NETREG_UNREGISTERING = 2, NETREG_UNREGISTERED = 3, NETREG_RELEASED = 4, NETREG_DUMMY = 5, } reg_state: 8; bool dismantle; enum { RTNL_LINK_INITIALIZED = 0, RTNL_LINK_INITIALIZING = 1, } rtnl_link_state: 16; bool needs_free_netdev; void (*priv_destructor)(struct net_device *); struct netpoll_info *npinfo; possible_net_t nd_net; void *ml_priv; enum netdev_ml_priv_type ml_priv_type; union { struct pcpu_lstats *lstats; struct pcpu_sw_netstats *tstats; struct pcpu_dstats *dstats; }; struct garp_port *garp_port; struct mrp_port *mrp_port; struct device dev; const struct attribute_group *sysfs_groups[4]; const struct attribute_group *sysfs_rx_queue_group; const struct rtnl_link_ops *rtnl_link_ops; unsigned int gso_max_size; u16 gso_max_segs; const struct dcbnl_rtnl_ops *dcbnl_ops; s16 num_tc; struct netdev_tc_txq tc_to_txq[16]; u8 prio_tc_map[16]; unsigned int fcoe_ddp_xid; struct netprio_map *priomap; struct phy_device *phydev; struct sfp_bus *sfp_bus; struct lock_class_key *qdisc_tx_busylock; struct lock_class_key *qdisc_running_key; bool proto_down; unsigned int wol_enabled: 1; unsigned int threaded: 1; struct list_head net_notifier_list; const struct macsec_ops *macsec_ops; const struct udp_tunnel_nic_info *udp_tunnel_nic_info; struct udp_tunnel_nic *udp_tunnel_nic; struct bpf_xdp_entity xdp_state[3]; long: 64; long: 64; long: 64; }; enum bpf_reg_type { NOT_INIT = 0, SCALAR_VALUE = 1, PTR_TO_CTX = 2, CONST_PTR_TO_MAP = 3, PTR_TO_MAP_VALUE = 4, PTR_TO_MAP_VALUE_OR_NULL = 5, PTR_TO_STACK = 6, PTR_TO_PACKET_META = 7, PTR_TO_PACKET = 8, PTR_TO_PACKET_END = 9, PTR_TO_FLOW_KEYS = 10, PTR_TO_SOCKET = 11, PTR_TO_SOCKET_OR_NULL = 12, PTR_TO_SOCK_COMMON = 13, PTR_TO_SOCK_COMMON_OR_NULL = 14, PTR_TO_TCP_SOCK = 15, PTR_TO_TCP_SOCK_OR_NULL = 16, PTR_TO_TP_BUFFER = 17, PTR_TO_XDP_SOCK = 18, PTR_TO_BTF_ID = 19, PTR_TO_BTF_ID_OR_NULL = 20, PTR_TO_MEM = 21, PTR_TO_MEM_OR_NULL = 22, PTR_TO_RDONLY_BUF = 23, PTR_TO_RDONLY_BUF_OR_NULL = 24, PTR_TO_RDWR_BUF = 25, PTR_TO_RDWR_BUF_OR_NULL = 26, PTR_TO_PERCPU_BTF_ID = 27, PTR_TO_FUNC = 28, PTR_TO_MAP_KEY = 29, __BPF_REG_TYPE_MAX = 30, }; struct bpf_prog_ops { int (*test_run)(struct bpf_prog *, const union bpf_attr *, union bpf_attr *); }; struct bpf_offload_dev; struct bpf_prog_offload { struct bpf_prog *prog; struct net_device *netdev; struct bpf_offload_dev *offdev; void *dev_priv; struct list_head offloads; bool dev_state; bool opt_failed; void *jited_image; u32 jited_len; }; struct btf_func_model { u8 ret_size; u8 nr_args; u8 arg_size[12]; }; struct bpf_tramp_image { void *image; struct bpf_ksym ksym; struct percpu_ref pcref; void *ip_after_call; void *ip_epilogue; union { struct callback_head rcu; struct work_struct work; }; }; struct bpf_trampoline { struct hlist_node hlist; struct mutex mutex; refcount_t refcnt; u64 key; struct { struct btf_func_model model; void *addr; bool ftrace_managed; } func; struct bpf_prog *extension_prog; struct hlist_head progs_hlist[3]; int progs_cnt[3]; struct bpf_tramp_image *cur_image; u64 selector; struct module *mod; }; struct bpf_func_info_aux { u16 linkage; bool unreliable; }; struct bpf_jit_poke_descriptor { void *tailcall_target; void *tailcall_bypass; void *bypass_addr; void *aux; union { struct { struct bpf_map *map; u32 key; } tail_call; }; bool tailcall_target_stable; u8 adj_off; u16 reason; u32 insn_idx; }; struct bpf_ctx_arg_aux { u32 offset; enum bpf_reg_type reg_type; u32 btf_id; }; struct btf_mod_pair { struct btf *btf; struct module *module; }; typedef unsigned int sk_buff_data_t; struct skb_ext; struct sk_buff { union { struct { struct sk_buff *next; struct sk_buff *prev; union { struct net_device *dev; long unsigned int dev_scratch; }; }; struct rb_node rbnode; struct list_head list; }; union { struct sock *sk; int ip_defrag_offset; }; union { ktime_t tstamp; u64 skb_mstamp_ns; }; char cb[48]; union { struct { long unsigned int _skb_refdst; void (*destructor)(struct sk_buff *); }; struct list_head tcp_tsorted_anchor; long unsigned int _sk_redir; }; long unsigned int _nfct; unsigned int len; unsigned int data_len; __u16 mac_len; __u16 hdr_len; __u16 queue_mapping; __u8 __cloned_offset[0]; __u8 cloned: 1; __u8 nohdr: 1; __u8 fclone: 2; __u8 peeked: 1; __u8 head_frag: 1; __u8 pfmemalloc: 1; __u8 pp_recycle: 1; __u8 active_extensions; __u32 headers_start[0]; __u8 __pkt_type_offset[0]; __u8 pkt_type: 3; __u8 ignore_df: 1; __u8 nf_trace: 1; __u8 ip_summed: 2; __u8 ooo_okay: 1; __u8 l4_hash: 1; __u8 sw_hash: 1; __u8 wifi_acked_valid: 1; __u8 wifi_acked: 1; __u8 no_fcs: 1; __u8 encapsulation: 1; __u8 encap_hdr_csum: 1; __u8 csum_valid: 1; __u8 __pkt_vlan_present_offset[0]; __u8 vlan_present: 1; __u8 csum_complete_sw: 1; __u8 csum_level: 2; __u8 csum_not_inet: 1; __u8 dst_pending_confirm: 1; __u8 ndisc_nodetype: 2; __u8 ipvs_property: 1; __u8 inner_protocol_type: 1; __u8 remcsum_offload: 1; __u8 offload_fwd_mark: 1; __u8 offload_l3_fwd_mark: 1; __u8 tc_skip_classify: 1; __u8 tc_at_ingress: 1; __u8 redirected: 1; __u8 from_ingress: 1; __u8 decrypted: 1; __u16 tc_index; union { __wsum csum; struct { __u16 csum_start; __u16 csum_offset; }; }; __u32 priority; int skb_iif; __u32 hash; __be16 vlan_proto; __u16 vlan_tci; union { unsigned int napi_id; unsigned int sender_cpu; }; __u32 secmark; union { __u32 mark; __u32 reserved_tailroom; }; union { __be16 inner_protocol; __u8 inner_ipproto; }; __u16 inner_transport_header; __u16 inner_network_header; __u16 inner_mac_header; __be16 protocol; __u16 transport_header; __u16 network_header; __u16 mac_header; __u32 headers_end[0]; sk_buff_data_t tail; sk_buff_data_t end; unsigned char *head; unsigned char *data; unsigned int truesize; refcount_t users; struct skb_ext *extensions; }; struct scatterlist { long unsigned int page_link; unsigned int offset; unsigned int length; dma_addr_t dma_address; unsigned int dma_length; }; enum { Root_NFS = 255, Root_CIFS = 254, Root_RAM0 = 1048576, Root_RAM1 = 1048577, Root_FD0 = 2097152, Root_HDA1 = 3145729, Root_HDA2 = 3145730, Root_SDA1 = 8388609, Root_SDA2 = 8388610, Root_HDC1 = 23068673, Root_SR0 = 11534336, }; struct flowi_tunnel { __be64 tun_id; }; struct flowi_common { int flowic_oif; int flowic_iif; __u32 flowic_mark; __u8 flowic_tos; __u8 flowic_scope; __u8 flowic_proto; __u8 flowic_flags; __u32 flowic_secid; kuid_t flowic_uid; struct flowi_tunnel flowic_tun_key; __u32 flowic_multipath_hash; }; union flowi_uli { struct { __be16 dport; __be16 sport; } ports; struct { __u8 type; __u8 code; } icmpt; struct { __le16 dport; __le16 sport; } dnports; __be32 gre_key; struct { __u8 type; } mht; }; struct flowi4 { struct flowi_common __fl_common; __be32 saddr; __be32 daddr; union flowi_uli uli; }; struct flowi6 { struct flowi_common __fl_common; struct in6_addr daddr; struct in6_addr saddr; __be32 flowlabel; union flowi_uli uli; __u32 mp_hash; }; struct flowidn { struct flowi_common __fl_common; __le16 daddr; __le16 saddr; union flowi_uli uli; }; struct flowi { union { struct flowi_common __fl_common; struct flowi4 ip4; struct flowi6 ip6; struct flowidn dn; } u; }; struct ipstats_mib { u64 mibs[37]; struct u64_stats_sync syncp; }; struct icmp_mib { long unsigned int mibs[28]; }; struct icmpmsg_mib { atomic_long_t mibs[512]; }; struct icmpv6_mib { long unsigned int mibs[6]; }; struct icmpv6_mib_device { atomic_long_t mibs[6]; }; struct icmpv6msg_mib { atomic_long_t mibs[512]; }; struct icmpv6msg_mib_device { atomic_long_t mibs[512]; }; struct tcp_mib { long unsigned int mibs[16]; }; struct udp_mib { long unsigned int mibs[10]; }; struct linux_mib { long unsigned int mibs[126]; }; struct linux_xfrm_mib { long unsigned int mibs[29]; }; struct linux_tls_mib { long unsigned int mibs[11]; }; struct inet_frags; struct fqdir { long int high_thresh; long int low_thresh; int timeout; int max_dist; struct inet_frags *f; struct net *net; bool dead; long: 56; long: 64; long: 64; struct rhashtable rhashtable; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; atomic_long_t mem; struct work_struct destroy_work; struct llist_node free_list; long: 64; long: 64; }; struct inet_frag_queue; struct inet_frags { unsigned int qsize; void (*constructor)(struct inet_frag_queue *, const void *); void (*destructor)(struct inet_frag_queue *); void (*frag_expire)(struct timer_list *); struct kmem_cache *frags_cachep; const char *frags_cache_name; struct rhashtable_params rhash_params; refcount_t refcnt; struct completion completion; }; struct frag_v4_compare_key { __be32 saddr; __be32 daddr; u32 user; u32 vif; __be16 id; u16 protocol; }; struct frag_v6_compare_key { struct in6_addr saddr; struct in6_addr daddr; u32 user; __be32 id; u32 iif; }; struct inet_frag_queue { struct rhash_head node; union { struct frag_v4_compare_key v4; struct frag_v6_compare_key v6; } key; struct timer_list timer; spinlock_t lock; refcount_t refcnt; struct rb_root rb_fragments; struct sk_buff *fragments_tail; struct sk_buff *last_run_head; ktime_t stamp; int len; int meat; __u8 flags; u16 max_size; struct fqdir *fqdir; struct callback_head rcu; }; struct fib_rule; struct fib_lookup_arg; struct fib_rule_hdr; struct nlattr; struct netlink_ext_ack; struct nla_policy; struct fib_rules_ops { int family; struct list_head list; int rule_size; int addr_size; int unresolved_rules; int nr_goto_rules; unsigned int fib_rules_seq; int (*action)(struct fib_rule *, struct flowi *, int, struct fib_lookup_arg *); bool (*suppress)(struct fib_rule *, struct fib_lookup_arg *); int (*match)(struct fib_rule *, struct flowi *, int); int (*configure)(struct fib_rule *, struct sk_buff *, struct fib_rule_hdr *, struct nlattr **, struct netlink_ext_ack *); int (*delete)(struct fib_rule *); int (*compare)(struct fib_rule *, struct fib_rule_hdr *, struct nlattr **); int (*fill)(struct fib_rule *, struct sk_buff *, struct fib_rule_hdr *); size_t (*nlmsg_payload)(struct fib_rule *); void (*flush_cache)(struct fib_rules_ops *); int nlgroup; const struct nla_policy *policy; struct list_head rules_list; struct module *owner; struct net *fro_net; struct callback_head rcu; }; enum tcp_ca_event { CA_EVENT_TX_START = 0, CA_EVENT_CWND_RESTART = 1, CA_EVENT_COMPLETE_CWR = 2, CA_EVENT_LOSS = 3, CA_EVENT_ECN_NO_CE = 4, CA_EVENT_ECN_IS_CE = 5, }; struct ack_sample; struct rate_sample; union tcp_cc_info; struct tcp_congestion_ops { u32 (*ssthresh)(struct sock *); void (*cong_avoid)(struct sock *, u32, u32); void (*set_state)(struct sock *, u8); void (*cwnd_event)(struct sock *, enum tcp_ca_event); void (*in_ack_event)(struct sock *, u32); void (*pkts_acked)(struct sock *, const struct ack_sample *); u32 (*min_tso_segs)(struct sock *); void (*cong_control)(struct sock *, const struct rate_sample *); u32 (*undo_cwnd)(struct sock *); u32 (*sndbuf_expand)(struct sock *); size_t (*get_info)(struct sock *, u32, int *, union tcp_cc_info *); char name[16]; struct module *owner; struct list_head list; u32 key; u32 flags; void (*init)(struct sock *); void (*release)(struct sock *); long: 64; long: 64; long: 64; long: 64; long: 64; }; struct fib_notifier_ops { int family; struct list_head list; unsigned int (*fib_seq_read)(struct net *); int (*fib_dump)(struct net *, struct notifier_block *, struct netlink_ext_ack *); struct module *owner; struct callback_head rcu; }; struct xfrm_state; struct lwtunnel_state; struct dst_entry { struct net_device *dev; struct dst_ops *ops; long unsigned int _metrics; long unsigned int expires; struct xfrm_state *xfrm; int (*input)(struct sk_buff *); int (*output)(struct net *, struct sock *, struct sk_buff *); short unsigned int flags; short int obsolete; short unsigned int header_len; short unsigned int trailer_len; atomic_t __refcnt; int __use; long unsigned int lastuse; struct lwtunnel_state *lwtstate; struct callback_head callback_head; short int error; short int __pad; __u32 tclassid; }; struct hh_cache { unsigned int hh_len; seqlock_t hh_lock; long unsigned int hh_data[16]; }; struct neigh_table; struct neigh_parms; struct neigh_ops; struct neighbour { struct neighbour *next; struct neigh_table *tbl; struct neigh_parms *parms; long unsigned int confirmed; long unsigned int updated; rwlock_t lock; refcount_t refcnt; unsigned int arp_queue_len_bytes; struct sk_buff_head arp_queue; struct timer_list timer; long unsigned int used; atomic_t probes; __u8 flags; __u8 nud_state; __u8 type; __u8 dead; u8 protocol; seqlock_t ha_lock; int: 32; unsigned char ha[32]; struct hh_cache hh; int (*output)(struct neighbour *, struct sk_buff *); const struct neigh_ops *ops; struct list_head gc_list; struct callback_head rcu; struct net_device *dev; u8 primary_key[0]; }; struct ipv6_stable_secret { bool initialized; struct in6_addr secret; }; struct ipv6_devconf { __s32 forwarding; __s32 hop_limit; __s32 mtu6; __s32 accept_ra; __s32 accept_redirects; __s32 autoconf; __s32 dad_transmits; __s32 rtr_solicits; __s32 rtr_solicit_interval; __s32 rtr_solicit_max_interval; __s32 rtr_solicit_delay; __s32 force_mld_version; __s32 mldv1_unsolicited_report_interval; __s32 mldv2_unsolicited_report_interval; __s32 use_tempaddr; __s32 temp_valid_lft; __s32 temp_prefered_lft; __s32 regen_max_retry; __s32 max_desync_factor; __s32 max_addresses; __s32 accept_ra_defrtr; __u32 ra_defrtr_metric; __s32 accept_ra_min_hop_limit; __s32 accept_ra_pinfo; __s32 ignore_routes_with_linkdown; __s32 accept_ra_rtr_pref; __s32 rtr_probe_interval; __s32 accept_ra_rt_info_min_plen; __s32 accept_ra_rt_info_max_plen; __s32 proxy_ndp; __s32 accept_source_route; __s32 accept_ra_from_local; __s32 optimistic_dad; __s32 use_optimistic; __s32 mc_forwarding; __s32 disable_ipv6; __s32 drop_unicast_in_l2_multicast; __s32 accept_dad; __s32 force_tllao; __s32 ndisc_notify; __s32 suppress_frag_ndisc; __s32 accept_ra_mtu; __s32 drop_unsolicited_na; struct ipv6_stable_secret stable_secret; __s32 use_oif_addrs_only; __s32 keep_addr_on_down; __s32 seg6_enabled; __s32 seg6_require_hmac; __u32 enhanced_dad; __u32 addr_gen_mode; __s32 disable_policy; __s32 ndisc_tclass; __s32 rpl_seg_enabled; struct ctl_table_header *sysctl_header; }; struct nf_queue_entry; struct nf_queue_handler { int (*outfn)(struct nf_queue_entry *, unsigned int); void (*nf_hook_drop)(struct net *); }; enum nf_log_type { NF_LOG_TYPE_LOG = 0, NF_LOG_TYPE_ULOG = 1, NF_LOG_TYPE_MAX = 2, }; typedef u8 u_int8_t; struct nf_loginfo; typedef void nf_logfn(struct net *, u_int8_t, unsigned int, const struct sk_buff *, const struct net_device *, const struct net_device *, const struct nf_loginfo *, const char *); struct nf_logger { char *name; enum nf_log_type type; nf_logfn *logfn; struct module *me; }; struct hlist_nulls_head { struct hlist_nulls_node *first; }; struct ip_conntrack_stat { unsigned int found; unsigned int invalid; unsigned int insert; unsigned int insert_failed; unsigned int clash_resolve; unsigned int drop; unsigned int early_drop; unsigned int error; unsigned int expect_new; unsigned int expect_create; unsigned int expect_delete; unsigned int search_restart; }; struct ct_pcpu { spinlock_t lock; struct hlist_nulls_head unconfirmed; struct hlist_nulls_head dying; }; typedef enum { SS_FREE = 0, SS_UNCONNECTED = 1, SS_CONNECTING = 2, SS_CONNECTED = 3, SS_DISCONNECTING = 4, } socket_state; struct socket_wq { wait_queue_head_t wait; struct fasync_struct *fasync_list; long unsigned int flags; struct callback_head rcu; long: 64; }; struct proto_ops; struct socket { socket_state state; short int type; long unsigned int flags; struct file *file; struct sock *sk; const struct proto_ops *ops; long: 64; long: 64; long: 64; struct socket_wq wq; }; typedef int (*sk_read_actor_t)(read_descriptor_t *, struct sk_buff *, unsigned int, size_t); struct proto_ops { int family; struct module *owner; int (*release)(struct socket *); int (*bind)(struct socket *, struct sockaddr *, int); int (*connect)(struct socket *, struct sockaddr *, int, int); int (*socketpair)(struct socket *, struct socket *); int (*accept)(struct socket *, struct socket *, int, bool); int (*getname)(struct socket *, struct sockaddr *, int); __poll_t (*poll)(struct file *, struct socket *, struct poll_table_struct *); int (*ioctl)(struct socket *, unsigned int, long unsigned int); int (*compat_ioctl)(struct socket *, unsigned int, long unsigned int); int (*gettstamp)(struct socket *, void *, bool, bool); int (*listen)(struct socket *, int); int (*shutdown)(struct socket *, int); int (*setsockopt)(struct socket *, int, int, sockptr_t, unsigned int); int (*getsockopt)(struct socket *, int, int, char *, int *); void (*show_fdinfo)(struct seq_file *, struct socket *); int (*sendmsg)(struct socket *, struct msghdr *, size_t); int (*recvmsg)(struct socket *, struct msghdr *, size_t, int); int (*mmap)(struct file *, struct socket *, struct vm_area_struct *); ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int); ssize_t (*splice_read)(struct socket *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*set_peek_off)(struct sock *, int); int (*peek_len)(struct socket *); int (*read_sock)(struct sock *, read_descriptor_t *, sk_read_actor_t); int (*sendpage_locked)(struct sock *, struct page *, int, size_t, int); int (*sendmsg_locked)(struct sock *, struct msghdr *, size_t); int (*set_rcvlowat)(struct sock *, int); }; struct pipe_buf_operations; struct pipe_buffer { struct page *page; unsigned int offset; unsigned int len; const struct pipe_buf_operations *ops; unsigned int flags; long unsigned int private; }; struct pipe_buf_operations { int (*confirm)(struct pipe_inode_info *, struct pipe_buffer *); void (*release)(struct pipe_inode_info *, struct pipe_buffer *); bool (*try_steal)(struct pipe_inode_info *, struct pipe_buffer *); bool (*get)(struct pipe_inode_info *, struct pipe_buffer *); }; struct skb_ext { refcount_t refcnt; u8 offset[4]; u8 chunks; long: 56; char data[0]; }; struct dql { unsigned int num_queued; unsigned int adj_limit; unsigned int last_obj_cnt; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; unsigned int limit; unsigned int num_completed; unsigned int prev_ovlimit; unsigned int prev_num_queued; unsigned int prev_last_obj_cnt; unsigned int lowest_slack; long unsigned int slack_start_time; unsigned int max_limit; unsigned int min_limit; unsigned int slack_hold_time; long: 32; long: 64; long: 64; }; struct ieee_ets { __u8 willing; __u8 ets_cap; __u8 cbs; __u8 tc_tx_bw[8]; __u8 tc_rx_bw[8]; __u8 tc_tsa[8]; __u8 prio_tc[8]; __u8 tc_reco_bw[8]; __u8 tc_reco_tsa[8]; __u8 reco_prio_tc[8]; }; struct ieee_maxrate { __u64 tc_maxrate[8]; }; struct ieee_qcn { __u8 rpg_enable[8]; __u32 rppp_max_rps[8]; __u32 rpg_time_reset[8]; __u32 rpg_byte_reset[8]; __u32 rpg_threshold[8]; __u32 rpg_max_rate[8]; __u32 rpg_ai_rate[8]; __u32 rpg_hai_rate[8]; __u32 rpg_gd[8]; __u32 rpg_min_dec_fac[8]; __u32 rpg_min_rate[8]; __u32 cndd_state_machine[8]; }; struct ieee_qcn_stats { __u64 rppp_rp_centiseconds[8]; __u32 rppp_created_rps[8]; }; struct ieee_pfc { __u8 pfc_cap; __u8 pfc_en; __u8 mbc; __u16 delay; __u64 requests[8]; __u64 indications[8]; }; struct dcbnl_buffer { __u8 prio2buffer[8]; __u32 buffer_size[8]; __u32 total_size; }; struct cee_pg { __u8 willing; __u8 error; __u8 pg_en; __u8 tcs_supported; __u8 pg_bw[8]; __u8 prio_pg[8]; }; struct cee_pfc { __u8 willing; __u8 error; __u8 pfc_en; __u8 tcs_supported; }; struct dcb_app { __u8 selector; __u8 priority; __u16 protocol; }; struct dcb_peer_app_info { __u8 willing; __u8 error; }; struct dcbnl_rtnl_ops { int (*ieee_getets)(struct net_device *, struct ieee_ets *); int (*ieee_setets)(struct net_device *, struct ieee_ets *); int (*ieee_getmaxrate)(struct net_device *, struct ieee_maxrate *); int (*ieee_setmaxrate)(struct net_device *, struct ieee_maxrate *); int (*ieee_getqcn)(struct net_device *, struct ieee_qcn *); int (*ieee_setqcn)(struct net_device *, struct ieee_qcn *); int (*ieee_getqcnstats)(struct net_device *, struct ieee_qcn_stats *); int (*ieee_getpfc)(struct net_device *, struct ieee_pfc *); int (*ieee_setpfc)(struct net_device *, struct ieee_pfc *); int (*ieee_getapp)(struct net_device *, struct dcb_app *); int (*ieee_setapp)(struct net_device *, struct dcb_app *); int (*ieee_delapp)(struct net_device *, struct dcb_app *); int (*ieee_peer_getets)(struct net_device *, struct ieee_ets *); int (*ieee_peer_getpfc)(struct net_device *, struct ieee_pfc *); u8 (*getstate)(struct net_device *); u8 (*setstate)(struct net_device *, u8); void (*getpermhwaddr)(struct net_device *, u8 *); void (*setpgtccfgtx)(struct net_device *, int, u8, u8, u8, u8); void (*setpgbwgcfgtx)(struct net_device *, int, u8); void (*setpgtccfgrx)(struct net_device *, int, u8, u8, u8, u8); void (*setpgbwgcfgrx)(struct net_device *, int, u8); void (*getpgtccfgtx)(struct net_device *, int, u8 *, u8 *, u8 *, u8 *); void (*getpgbwgcfgtx)(struct net_device *, int, u8 *); void (*getpgtccfgrx)(struct net_device *, int, u8 *, u8 *, u8 *, u8 *); void (*getpgbwgcfgrx)(struct net_device *, int, u8 *); void (*setpfccfg)(struct net_device *, int, u8); void (*getpfccfg)(struct net_device *, int, u8 *); u8 (*setall)(struct net_device *); u8 (*getcap)(struct net_device *, int, u8 *); int (*getnumtcs)(struct net_device *, int, u8 *); int (*setnumtcs)(struct net_device *, int, u8); u8 (*getpfcstate)(struct net_device *); void (*setpfcstate)(struct net_device *, u8); void (*getbcncfg)(struct net_device *, int, u32 *); void (*setbcncfg)(struct net_device *, int, u32); void (*getbcnrp)(struct net_device *, int, u8 *); void (*setbcnrp)(struct net_device *, int, u8); int (*setapp)(struct net_device *, u8, u16, u8); int (*getapp)(struct net_device *, u8, u16); u8 (*getfeatcfg)(struct net_device *, int, u8 *); u8 (*setfeatcfg)(struct net_device *, int, u8); u8 (*getdcbx)(struct net_device *); u8 (*setdcbx)(struct net_device *, u8); int (*peer_getappinfo)(struct net_device *, struct dcb_peer_app_info *, u16 *); int (*peer_getapptable)(struct net_device *, struct dcb_app *); int (*cee_peer_getpg)(struct net_device *, struct cee_pg *); int (*cee_peer_getpfc)(struct net_device *, struct cee_pfc *); int (*dcbnl_getbuffer)(struct net_device *, struct dcbnl_buffer *); int (*dcbnl_setbuffer)(struct net_device *, struct dcbnl_buffer *); }; struct netprio_map { struct callback_head rcu; u32 priomap_len; u32 priomap[0]; }; struct xdp_mem_info { u32 type; u32 id; }; struct xdp_rxq_info { struct net_device *dev; u32 queue_index; u32 reg_state; struct xdp_mem_info mem; unsigned int napi_id; long: 32; long: 64; long: 64; long: 64; long: 64; }; struct xdp_frame { void *data; u16 len; u16 headroom; u32 metasize: 8; u32 frame_sz: 24; struct xdp_mem_info mem; struct net_device *dev_rx; }; struct nlmsghdr { __u32 nlmsg_len; __u16 nlmsg_type; __u16 nlmsg_flags; __u32 nlmsg_seq; __u32 nlmsg_pid; }; struct nlattr { __u16 nla_len; __u16 nla_type; }; struct netlink_ext_ack { const char *_msg; const struct nlattr *bad_attr; const struct nla_policy *policy; u8 cookie[20]; u8 cookie_len; }; struct netlink_range_validation; struct netlink_range_validation_signed; struct nla_policy { u8 type; u8 validation_type; u16 len; union { const u32 bitfield32_valid; const u32 mask; const char *reject_message; const struct nla_policy *nested_policy; struct netlink_range_validation *range; struct netlink_range_validation_signed *range_signed; struct { s16 min; s16 max; }; int (*validate)(const struct nlattr *, struct netlink_ext_ack *); u16 strict_start_type; }; }; struct netlink_callback { struct sk_buff *skb; const struct nlmsghdr *nlh; int (*dump)(struct sk_buff *, struct netlink_callback *); int (*done)(struct netlink_callback *); void *data; struct module *module; struct netlink_ext_ack *extack; u16 family; u16 answer_flags; u32 min_dump_alloc; unsigned int prev_seq; unsigned int seq; bool strict_check; union { u8 ctx[48]; long int args[6]; }; }; struct ndmsg { __u8 ndm_family; __u8 ndm_pad1; __u16 ndm_pad2; __s32 ndm_ifindex; __u16 ndm_state; __u8 ndm_flags; __u8 ndm_type; }; struct rtnl_link_stats64 { __u64 rx_packets; __u64 tx_packets; __u64 rx_bytes; __u64 tx_bytes; __u64 rx_errors; __u64 tx_errors; __u64 rx_dropped; __u64 tx_dropped; __u64 multicast; __u64 collisions; __u64 rx_length_errors; __u64 rx_over_errors; __u64 rx_crc_errors; __u64 rx_frame_errors; __u64 rx_fifo_errors; __u64 rx_missed_errors; __u64 tx_aborted_errors; __u64 tx_carrier_errors; __u64 tx_fifo_errors; __u64 tx_heartbeat_errors; __u64 tx_window_errors; __u64 rx_compressed; __u64 tx_compressed; __u64 rx_nohandler; }; struct ifla_vf_guid { __u32 vf; __u64 guid; }; struct ifla_vf_stats { __u64 rx_packets; __u64 tx_packets; __u64 rx_bytes; __u64 tx_bytes; __u64 broadcast; __u64 multicast; __u64 rx_dropped; __u64 tx_dropped; }; struct ifla_vf_info { __u32 vf; __u8 mac[32]; __u32 vlan; __u32 qos; __u32 spoofchk; __u32 linkstate; __u32 min_tx_rate; __u32 max_tx_rate; __u32 rss_query_en; __u32 trusted; __be16 vlan_proto; }; struct tc_stats { __u64 bytes; __u32 packets; __u32 drops; __u32 overlimits; __u32 bps; __u32 pps; __u32 qlen; __u32 backlog; }; struct tc_sizespec { unsigned char cell_log; unsigned char size_log; short int cell_align; int overhead; unsigned int linklayer; unsigned int mpu; unsigned int mtu; unsigned int tsize; }; enum netdev_tx { __NETDEV_TX_MIN = 2147483648, NETDEV_TX_OK = 0, NETDEV_TX_BUSY = 16, }; typedef enum netdev_tx netdev_tx_t; struct header_ops { int (*create)(struct sk_buff *, struct net_device *, short unsigned int, const void *, const void *, unsigned int); int (*parse)(const struct sk_buff *, unsigned char *); int (*cache)(const struct neighbour *, struct hh_cache *, __be16); void (*cache_update)(struct hh_cache *, const struct net_device *, const unsigned char *); bool (*validate)(const char *, unsigned int); __be16 (*parse_protocol)(const struct sk_buff *); }; struct xsk_buff_pool; struct netdev_queue { struct net_device *dev; struct Qdisc *qdisc; struct Qdisc *qdisc_sleeping; struct kobject kobj; int numa_node; long unsigned int tx_maxrate; long unsigned int trans_timeout; struct net_device *sb_dev; struct xsk_buff_pool *pool; spinlock_t _xmit_lock; int xmit_lock_owner; long unsigned int trans_start; long unsigned int state; long: 64; long: 64; long: 64; long: 64; long: 64; struct dql dql; }; struct net_rate_estimator; struct qdisc_skb_head { struct sk_buff *head; struct sk_buff *tail; __u32 qlen; spinlock_t lock; }; struct gnet_stats_basic_packed { __u64 bytes; __u64 packets; }; struct gnet_stats_queue { __u32 qlen; __u32 backlog; __u32 drops; __u32 requeues; __u32 overlimits; }; struct Qdisc_ops; struct qdisc_size_table; struct gnet_stats_basic_cpu; struct Qdisc { int (*enqueue)(struct sk_buff *, struct Qdisc *, struct sk_buff **); struct sk_buff * (*dequeue)(struct Qdisc *); unsigned int flags; u32 limit; const struct Qdisc_ops *ops; struct qdisc_size_table *stab; struct hlist_node hash; u32 handle; u32 parent; struct netdev_queue *dev_queue; struct net_rate_estimator *rate_est; struct gnet_stats_basic_cpu *cpu_bstats; struct gnet_stats_queue *cpu_qstats; int pad; refcount_t refcnt; long: 64; long: 64; long: 64; struct sk_buff_head gso_skb; struct qdisc_skb_head q; struct gnet_stats_basic_packed bstats; seqcount_t running; struct gnet_stats_queue qstats; long unsigned int state; struct Qdisc *next_sched; struct sk_buff_head skb_bad_txq; spinlock_t busylock; spinlock_t seqlock; struct callback_head rcu; long: 64; long: 64; long: 64; long: 64; long: 64; long int privdata[0]; }; struct rps_map { unsigned int len; struct callback_head rcu; u16 cpus[0]; }; struct rps_dev_flow { u16 cpu; u16 filter; unsigned int last_qtail; }; struct rps_dev_flow_table { unsigned int mask; struct callback_head rcu; struct rps_dev_flow flows[0]; }; struct netdev_rx_queue { struct rps_map *rps_map; struct rps_dev_flow_table *rps_flow_table; struct kobject kobj; struct net_device *dev; long: 64; long: 64; long: 64; long: 64; long: 64; struct xdp_rxq_info xdp_rxq; struct xsk_buff_pool *pool; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct xps_map { unsigned int len; unsigned int alloc_len; struct callback_head rcu; u16 queues[0]; }; struct xps_dev_maps { struct callback_head rcu; unsigned int nr_ids; s16 num_tc; struct xps_map *attr_map[0]; }; struct netdev_fcoe_hbainfo { char manufacturer[64]; char serial_number[64]; char hardware_version[64]; char driver_version[64]; char optionrom_version[64]; char firmware_version[64]; char model[256]; char model_description[256]; }; struct netdev_phys_item_id { unsigned char id[32]; unsigned char id_len; }; enum net_device_path_type { DEV_PATH_ETHERNET = 0, DEV_PATH_VLAN = 1, DEV_PATH_BRIDGE = 2, DEV_PATH_PPPOE = 3, DEV_PATH_DSA = 4, }; struct net_device_path { enum net_device_path_type type; const struct net_device *dev; union { struct { u16 id; __be16 proto; u8 h_dest[6]; } encap; struct { enum { DEV_PATH_BR_VLAN_KEEP = 0, DEV_PATH_BR_VLAN_TAG = 1, DEV_PATH_BR_VLAN_UNTAG = 2, DEV_PATH_BR_VLAN_UNTAG_HW = 3, } vlan_mode; u16 vlan_id; __be16 vlan_proto; } bridge; struct { int port; u16 proto; } dsa; }; }; struct net_device_path_ctx { const struct net_device *dev; const u8 *daddr; int num_vlans; struct { u16 id; __be16 proto; } vlan[2]; }; enum tc_setup_type { TC_SETUP_QDISC_MQPRIO = 0, TC_SETUP_CLSU32 = 1, TC_SETUP_CLSFLOWER = 2, TC_SETUP_CLSMATCHALL = 3, TC_SETUP_CLSBPF = 4, TC_SETUP_BLOCK = 5, TC_SETUP_QDISC_CBS = 6, TC_SETUP_QDISC_RED = 7, TC_SETUP_QDISC_PRIO = 8, TC_SETUP_QDISC_MQ = 9, TC_SETUP_QDISC_ETF = 10, TC_SETUP_ROOT_QDISC = 11, TC_SETUP_QDISC_GRED = 12, TC_SETUP_QDISC_TAPRIO = 13, TC_SETUP_FT = 14, TC_SETUP_QDISC_ETS = 15, TC_SETUP_QDISC_TBF = 16, TC_SETUP_QDISC_FIFO = 17, TC_SETUP_QDISC_HTB = 18, }; enum bpf_netdev_command { XDP_SETUP_PROG = 0, XDP_SETUP_PROG_HW = 1, BPF_OFFLOAD_MAP_ALLOC = 2, BPF_OFFLOAD_MAP_FREE = 3, XDP_SETUP_XSK_POOL = 4, }; struct netdev_bpf { enum bpf_netdev_command command; union { struct { u32 flags; struct bpf_prog *prog; struct netlink_ext_ack *extack; }; struct { struct bpf_offloaded_map *offmap; }; struct { struct xsk_buff_pool *pool; u16 queue_id; } xsk; }; }; struct xfrmdev_ops { int (*xdo_dev_state_add)(struct xfrm_state *); void (*xdo_dev_state_delete)(struct xfrm_state *); void (*xdo_dev_state_free)(struct xfrm_state *); bool (*xdo_dev_offload_ok)(struct sk_buff *, struct xfrm_state *); void (*xdo_dev_state_advance_esn)(struct xfrm_state *); }; struct dev_ifalias { struct callback_head rcuhead; char ifalias[0]; }; struct netdev_name_node { struct hlist_node hlist; struct list_head list; struct net_device *dev; const char *name; }; struct devlink_port; struct ip_tunnel_parm; struct net_device_ops { int (*ndo_init)(struct net_device *); void (*ndo_uninit)(struct net_device *); int (*ndo_open)(struct net_device *); int (*ndo_stop)(struct net_device *); netdev_tx_t (*ndo_start_xmit)(struct sk_buff *, struct net_device *); netdev_features_t (*ndo_features_check)(struct sk_buff *, struct net_device *, netdev_features_t); u16 (*ndo_select_queue)(struct net_device *, struct sk_buff *, struct net_device *); void (*ndo_change_rx_flags)(struct net_device *, int); void (*ndo_set_rx_mode)(struct net_device *); int (*ndo_set_mac_address)(struct net_device *, void *); int (*ndo_validate_addr)(struct net_device *); int (*ndo_do_ioctl)(struct net_device *, struct ifreq *, int); int (*ndo_set_config)(struct net_device *, struct ifmap *); int (*ndo_change_mtu)(struct net_device *, int); int (*ndo_neigh_setup)(struct net_device *, struct neigh_parms *); void (*ndo_tx_timeout)(struct net_device *, unsigned int); void (*ndo_get_stats64)(struct net_device *, struct rtnl_link_stats64 *); bool (*ndo_has_offload_stats)(const struct net_device *, int); int (*ndo_get_offload_stats)(int, const struct net_device *, void *); struct net_device_stats * (*ndo_get_stats)(struct net_device *); int (*ndo_vlan_rx_add_vid)(struct net_device *, __be16, u16); int (*ndo_vlan_rx_kill_vid)(struct net_device *, __be16, u16); void (*ndo_poll_controller)(struct net_device *); int (*ndo_netpoll_setup)(struct net_device *, struct netpoll_info *); void (*ndo_netpoll_cleanup)(struct net_device *); int (*ndo_set_vf_mac)(struct net_device *, int, u8 *); int (*ndo_set_vf_vlan)(struct net_device *, int, u16, u8, __be16); int (*ndo_set_vf_rate)(struct net_device *, int, int, int); int (*ndo_set_vf_spoofchk)(struct net_device *, int, bool); int (*ndo_set_vf_trust)(struct net_device *, int, bool); int (*ndo_get_vf_config)(struct net_device *, int, struct ifla_vf_info *); int (*ndo_set_vf_link_state)(struct net_device *, int, int); int (*ndo_get_vf_stats)(struct net_device *, int, struct ifla_vf_stats *); int (*ndo_set_vf_port)(struct net_device *, int, struct nlattr **); int (*ndo_get_vf_port)(struct net_device *, int, struct sk_buff *); int (*ndo_get_vf_guid)(struct net_device *, int, struct ifla_vf_guid *, struct ifla_vf_guid *); int (*ndo_set_vf_guid)(struct net_device *, int, u64, int); int (*ndo_set_vf_rss_query_en)(struct net_device *, int, bool); int (*ndo_setup_tc)(struct net_device *, enum tc_setup_type, void *); int (*ndo_fcoe_enable)(struct net_device *); int (*ndo_fcoe_disable)(struct net_device *); int (*ndo_fcoe_ddp_setup)(struct net_device *, u16, struct scatterlist *, unsigned int); int (*ndo_fcoe_ddp_done)(struct net_device *, u16); int (*ndo_fcoe_ddp_target)(struct net_device *, u16, struct scatterlist *, unsigned int); int (*ndo_fcoe_get_hbainfo)(struct net_device *, struct netdev_fcoe_hbainfo *); int (*ndo_fcoe_get_wwn)(struct net_device *, u64 *, int); int (*ndo_rx_flow_steer)(struct net_device *, const struct sk_buff *, u16, u32); int (*ndo_add_slave)(struct net_device *, struct net_device *, struct netlink_ext_ack *); int (*ndo_del_slave)(struct net_device *, struct net_device *); struct net_device * (*ndo_get_xmit_slave)(struct net_device *, struct sk_buff *, bool); struct net_device * (*ndo_sk_get_lower_dev)(struct net_device *, struct sock *); netdev_features_t (*ndo_fix_features)(struct net_device *, netdev_features_t); int (*ndo_set_features)(struct net_device *, netdev_features_t); int (*ndo_neigh_construct)(struct net_device *, struct neighbour *); void (*ndo_neigh_destroy)(struct net_device *, struct neighbour *); int (*ndo_fdb_add)(struct ndmsg *, struct nlattr **, struct net_device *, const unsigned char *, u16, u16, struct netlink_ext_ack *); int (*ndo_fdb_del)(struct ndmsg *, struct nlattr **, struct net_device *, const unsigned char *, u16); int (*ndo_fdb_dump)(struct sk_buff *, struct netlink_callback *, struct net_device *, struct net_device *, int *); int (*ndo_fdb_get)(struct sk_buff *, struct nlattr **, struct net_device *, const unsigned char *, u16, u32, u32, struct netlink_ext_ack *); int (*ndo_bridge_setlink)(struct net_device *, struct nlmsghdr *, u16, struct netlink_ext_ack *); int (*ndo_bridge_getlink)(struct sk_buff *, u32, u32, struct net_device *, u32, int); int (*ndo_bridge_dellink)(struct net_device *, struct nlmsghdr *, u16); int (*ndo_change_carrier)(struct net_device *, bool); int (*ndo_get_phys_port_id)(struct net_device *, struct netdev_phys_item_id *); int (*ndo_get_port_parent_id)(struct net_device *, struct netdev_phys_item_id *); int (*ndo_get_phys_port_name)(struct net_device *, char *, size_t); void * (*ndo_dfwd_add_station)(struct net_device *, struct net_device *); void (*ndo_dfwd_del_station)(struct net_device *, void *); int (*ndo_set_tx_maxrate)(struct net_device *, int, u32); int (*ndo_get_iflink)(const struct net_device *); int (*ndo_change_proto_down)(struct net_device *, bool); int (*ndo_fill_metadata_dst)(struct net_device *, struct sk_buff *); void (*ndo_set_rx_headroom)(struct net_device *, int); int (*ndo_bpf)(struct net_device *, struct netdev_bpf *); int (*ndo_xdp_xmit)(struct net_device *, int, struct xdp_frame **, u32); int (*ndo_xsk_wakeup)(struct net_device *, u32, u32); struct devlink_port * (*ndo_get_devlink_port)(struct net_device *); int (*ndo_tunnel_ctl)(struct net_device *, struct ip_tunnel_parm *, int); struct net_device * (*ndo_get_peer_dev)(struct net_device *); int (*ndo_fill_forward_path)(struct net_device_path_ctx *, struct net_device_path *); }; struct neigh_parms { possible_net_t net; struct net_device *dev; struct list_head list; int (*neigh_setup)(struct neighbour *); struct neigh_table *tbl; void *sysctl_table; int dead; refcount_t refcnt; struct callback_head callback_head; int reachable_time; int data[13]; long unsigned int data_state[1]; }; struct pcpu_lstats { u64_stats_t packets; u64_stats_t bytes; struct u64_stats_sync syncp; }; struct pcpu_sw_netstats { u64 rx_packets; u64 rx_bytes; u64 tx_packets; u64 tx_bytes; struct u64_stats_sync syncp; }; struct iw_request_info; union iwreq_data; typedef int (*iw_handler)(struct net_device *, struct iw_request_info *, union iwreq_data *, char *); struct iw_priv_args; struct iw_statistics; struct iw_handler_def { const iw_handler *standard; __u16 num_standard; __u16 num_private; __u16 num_private_args; const iw_handler *private; const struct iw_priv_args *private_args; struct iw_statistics * (*get_wireless_stats)(struct net_device *); }; enum ethtool_phys_id_state { ETHTOOL_ID_INACTIVE = 0, ETHTOOL_ID_ACTIVE = 1, ETHTOOL_ID_ON = 2, ETHTOOL_ID_OFF = 3, }; struct ethtool_drvinfo; struct ethtool_regs; struct ethtool_wolinfo; struct ethtool_link_ext_state_info; struct ethtool_eeprom; struct ethtool_coalesce; struct ethtool_ringparam; struct ethtool_pause_stats; struct ethtool_pauseparam; struct ethtool_test; struct ethtool_stats; struct ethtool_rxnfc; struct ethtool_flash; struct ethtool_channels; struct ethtool_dump; struct ethtool_ts_info; struct ethtool_modinfo; struct ethtool_eee; struct ethtool_tunable; struct ethtool_link_ksettings; struct ethtool_fec_stats; struct ethtool_fecparam; struct ethtool_module_eeprom; struct ethtool_eth_phy_stats; struct ethtool_eth_mac_stats; struct ethtool_eth_ctrl_stats; struct ethtool_rmon_stats; struct ethtool_rmon_hist_range; struct ethtool_ops { u32 cap_link_lanes_supported: 1; u32 supported_coalesce_params; void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *); int (*get_regs_len)(struct net_device *); void (*get_regs)(struct net_device *, struct ethtool_regs *, void *); void (*get_wol)(struct net_device *, struct ethtool_wolinfo *); int (*set_wol)(struct net_device *, struct ethtool_wolinfo *); u32 (*get_msglevel)(struct net_device *); void (*set_msglevel)(struct net_device *, u32); int (*nway_reset)(struct net_device *); u32 (*get_link)(struct net_device *); int (*get_link_ext_state)(struct net_device *, struct ethtool_link_ext_state_info *); int (*get_eeprom_len)(struct net_device *); int (*get_eeprom)(struct net_device *, struct ethtool_eeprom *, u8 *); int (*set_eeprom)(struct net_device *, struct ethtool_eeprom *, u8 *); int (*get_coalesce)(struct net_device *, struct ethtool_coalesce *); int (*set_coalesce)(struct net_device *, struct ethtool_coalesce *); void (*get_ringparam)(struct net_device *, struct ethtool_ringparam *); int (*set_ringparam)(struct net_device *, struct ethtool_ringparam *); void (*get_pause_stats)(struct net_device *, struct ethtool_pause_stats *); void (*get_pauseparam)(struct net_device *, struct ethtool_pauseparam *); int (*set_pauseparam)(struct net_device *, struct ethtool_pauseparam *); void (*self_test)(struct net_device *, struct ethtool_test *, u64 *); void (*get_strings)(struct net_device *, u32, u8 *); int (*set_phys_id)(struct net_device *, enum ethtool_phys_id_state); void (*get_ethtool_stats)(struct net_device *, struct ethtool_stats *, u64 *); int (*begin)(struct net_device *); void (*complete)(struct net_device *); u32 (*get_priv_flags)(struct net_device *); int (*set_priv_flags)(struct net_device *, u32); int (*get_sset_count)(struct net_device *, int); int (*get_rxnfc)(struct net_device *, struct ethtool_rxnfc *, u32 *); int (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *); int (*flash_device)(struct net_device *, struct ethtool_flash *); int (*reset)(struct net_device *, u32 *); u32 (*get_rxfh_key_size)(struct net_device *); u32 (*get_rxfh_indir_size)(struct net_device *); int (*get_rxfh)(struct net_device *, u32 *, u8 *, u8 *); int (*set_rxfh)(struct net_device *, const u32 *, const u8 *, const u8); int (*get_rxfh_context)(struct net_device *, u32 *, u8 *, u8 *, u32); int (*set_rxfh_context)(struct net_device *, const u32 *, const u8 *, const u8, u32 *, bool); void (*get_channels)(struct net_device *, struct ethtool_channels *); int (*set_channels)(struct net_device *, struct ethtool_channels *); int (*get_dump_flag)(struct net_device *, struct ethtool_dump *); int (*get_dump_data)(struct net_device *, struct ethtool_dump *, void *); int (*set_dump)(struct net_device *, struct ethtool_dump *); int (*get_ts_info)(struct net_device *, struct ethtool_ts_info *); int (*get_module_info)(struct net_device *, struct ethtool_modinfo *); int (*get_module_eeprom)(struct net_device *, struct ethtool_eeprom *, u8 *); int (*get_eee)(struct net_device *, struct ethtool_eee *); int (*set_eee)(struct net_device *, struct ethtool_eee *); int (*get_tunable)(struct net_device *, const struct ethtool_tunable *, void *); int (*set_tunable)(struct net_device *, const struct ethtool_tunable *, const void *); int (*get_per_queue_coalesce)(struct net_device *, u32, struct ethtool_coalesce *); int (*set_per_queue_coalesce)(struct net_device *, u32, struct ethtool_coalesce *); int (*get_link_ksettings)(struct net_device *, struct ethtool_link_ksettings *); int (*set_link_ksettings)(struct net_device *, const struct ethtool_link_ksettings *); void (*get_fec_stats)(struct net_device *, struct ethtool_fec_stats *); int (*get_fecparam)(struct net_device *, struct ethtool_fecparam *); int (*set_fecparam)(struct net_device *, struct ethtool_fecparam *); void (*get_ethtool_phy_stats)(struct net_device *, struct ethtool_stats *, u64 *); int (*get_phy_tunable)(struct net_device *, const struct ethtool_tunable *, void *); int (*set_phy_tunable)(struct net_device *, const struct ethtool_tunable *, const void *); int (*get_module_eeprom_by_page)(struct net_device *, const struct ethtool_module_eeprom *, struct netlink_ext_ack *); void (*get_eth_phy_stats)(struct net_device *, struct ethtool_eth_phy_stats *); void (*get_eth_mac_stats)(struct net_device *, struct ethtool_eth_mac_stats *); void (*get_eth_ctrl_stats)(struct net_device *, struct ethtool_eth_ctrl_stats *); void (*get_rmon_stats)(struct net_device *, struct ethtool_rmon_stats *, const struct ethtool_rmon_hist_range **); }; struct l3mdev_ops { u32 (*l3mdev_fib_table)(const struct net_device *); struct sk_buff * (*l3mdev_l3_rcv)(struct net_device *, struct sk_buff *, u16); struct sk_buff * (*l3mdev_l3_out)(struct net_device *, struct sock *, struct sk_buff *, u16); struct dst_entry * (*l3mdev_link_scope_lookup)(const struct net_device *, struct flowi6 *); }; struct nd_opt_hdr; struct ndisc_options; struct prefix_info; struct ndisc_ops { int (*is_useropt)(u8); int (*parse_options)(const struct net_device *, struct nd_opt_hdr *, struct ndisc_options *); void (*update)(const struct net_device *, struct neighbour *, u32, u8, const struct ndisc_options *); int (*opt_addr_space)(const struct net_device *, u8, struct neighbour *, u8 *, u8 **); void (*fill_addr_option)(const struct net_device *, struct sk_buff *, u8, const u8 *); void (*prefix_rcv_add_addr)(struct net *, struct net_device *, const struct prefix_info *, struct inet6_dev *, struct in6_addr *, int, u32, bool, bool, __u32, u32, bool); }; enum tls_offload_ctx_dir { TLS_OFFLOAD_CTX_DIR_RX = 0, TLS_OFFLOAD_CTX_DIR_TX = 1, }; struct tls_crypto_info; struct tls_context; struct tlsdev_ops { int (*tls_dev_add)(struct net_device *, struct sock *, enum tls_offload_ctx_dir, struct tls_crypto_info *, u32); void (*tls_dev_del)(struct net_device *, struct tls_context *, enum tls_offload_ctx_dir); int (*tls_dev_resync)(struct net_device *, struct sock *, u32, u8 *, enum tls_offload_ctx_dir); }; struct ipv6_devstat { struct proc_dir_entry *proc_dir_entry; struct ipstats_mib *ipv6; struct icmpv6_mib_device *icmpv6dev; struct icmpv6msg_mib_device *icmpv6msgdev; }; struct ifmcaddr6; struct ifacaddr6; struct inet6_dev { struct net_device *dev; struct list_head addr_list; struct ifmcaddr6 *mc_list; struct ifmcaddr6 *mc_tomb; unsigned char mc_qrv; unsigned char mc_gq_running; unsigned char mc_ifc_count; unsigned char mc_dad_count; long unsigned int mc_v1_seen; long unsigned int mc_qi; long unsigned int mc_qri; long unsigned int mc_maxdelay; struct delayed_work mc_gq_work; struct delayed_work mc_ifc_work; struct delayed_work mc_dad_work; struct delayed_work mc_query_work; struct delayed_work mc_report_work; struct sk_buff_head mc_query_queue; struct sk_buff_head mc_report_queue; spinlock_t mc_query_lock; spinlock_t mc_report_lock; struct mutex mc_lock; struct ifacaddr6 *ac_list; rwlock_t lock; refcount_t refcnt; __u32 if_flags; int dead; u32 desync_factor; struct list_head tempaddr_list; struct in6_addr token; struct neigh_parms *nd_parms; struct ipv6_devconf cnf; struct ipv6_devstat stats; struct timer_list rs_timer; __s32 rs_interval; __u8 rs_probes; long unsigned int tstamp; struct callback_head rcu; }; struct tcf_proto; struct tcf_block; struct mini_Qdisc { struct tcf_proto *filter_list; struct tcf_block *block; struct gnet_stats_basic_cpu *cpu_bstats; struct gnet_stats_queue *cpu_qstats; struct callback_head rcu; }; struct rtnl_link_ops { struct list_head list; const char *kind; size_t priv_size; struct net_device * (*alloc)(struct nlattr **, const char *, unsigned char, unsigned int, unsigned int); void (*setup)(struct net_device *); bool netns_refund; unsigned int maxtype; const struct nla_policy *policy; int (*validate)(struct nlattr **, struct nlattr **, struct netlink_ext_ack *); int (*newlink)(struct net *, struct net_device *, struct nlattr **, struct nlattr **, struct netlink_ext_ack *); int (*changelink)(struct net_device *, struct nlattr **, struct nlattr **, struct netlink_ext_ack *); void (*dellink)(struct net_device *, struct list_head *); size_t (*get_size)(const struct net_device *); int (*fill_info)(struct sk_buff *, const struct net_device *); size_t (*get_xstats_size)(const struct net_device *); int (*fill_xstats)(struct sk_buff *, const struct net_device *); unsigned int (*get_num_tx_queues)(); unsigned int (*get_num_rx_queues)(); unsigned int slave_maxtype; const struct nla_policy *slave_policy; int (*slave_changelink)(struct net_device *, struct net_device *, struct nlattr **, struct nlattr **, struct netlink_ext_ack *); size_t (*get_slave_size)(const struct net_device *, const struct net_device *); int (*fill_slave_info)(struct sk_buff *, const struct net_device *, const struct net_device *); struct net * (*get_link_net)(const struct net_device *); size_t (*get_linkxstats_size)(const struct net_device *, int); int (*fill_linkxstats)(struct sk_buff *, const struct net_device *, int *, int); }; struct udp_tunnel_nic_table_info { unsigned int n_entries; unsigned int tunnel_types; }; struct udp_tunnel_info; struct udp_tunnel_nic_shared; struct udp_tunnel_nic_info { int (*set_port)(struct net_device *, unsigned int, unsigned int, struct udp_tunnel_info *); int (*unset_port)(struct net_device *, unsigned int, unsigned int, struct udp_tunnel_info *); int (*sync_table)(struct net_device *, unsigned int); struct udp_tunnel_nic_shared *shared; unsigned int flags; struct udp_tunnel_nic_table_info tables[4]; }; enum { RTAX_UNSPEC = 0, RTAX_LOCK = 1, RTAX_MTU = 2, RTAX_WINDOW = 3, RTAX_RTT = 4, RTAX_RTTVAR = 5, RTAX_SSTHRESH = 6, RTAX_CWND = 7, RTAX_ADVMSS = 8, RTAX_REORDERING = 9, RTAX_HOPLIMIT = 10, RTAX_INITCWND = 11, RTAX_FEATURES = 12, RTAX_RTO_MIN = 13, RTAX_INITRWND = 14, RTAX_QUICKACK = 15, RTAX_CC_ALGO = 16, RTAX_FASTOPEN_NO_COOKIE = 17, __RTAX_MAX = 18, }; struct tcmsg { unsigned char tcm_family; unsigned char tcm__pad1; short unsigned int tcm__pad2; int tcm_ifindex; __u32 tcm_handle; __u32 tcm_parent; __u32 tcm_info; }; struct gnet_stats_basic_cpu { struct gnet_stats_basic_packed bstats; struct u64_stats_sync syncp; }; struct gnet_dump { spinlock_t *lock; struct sk_buff *skb; struct nlattr *tail; int compat_tc_stats; int compat_xstats; int padattr; void *xstats; int xstats_len; struct tc_stats tc_stats; }; struct netlink_range_validation { u64 min; u64 max; }; struct netlink_range_validation_signed { s64 min; s64 max; }; enum flow_action_hw_stats_bit { FLOW_ACTION_HW_STATS_IMMEDIATE_BIT = 0, FLOW_ACTION_HW_STATS_DELAYED_BIT = 1, FLOW_ACTION_HW_STATS_DISABLED_BIT = 2, FLOW_ACTION_HW_STATS_NUM_BITS = 3, }; struct flow_block { struct list_head cb_list; }; typedef int flow_setup_cb_t(enum tc_setup_type, void *, void *); struct qdisc_size_table { struct callback_head rcu; struct list_head list; struct tc_sizespec szopts; int refcnt; u16 data[0]; }; struct Qdisc_class_ops; struct Qdisc_ops { struct Qdisc_ops *next; const struct Qdisc_class_ops *cl_ops; char id[16]; int priv_size; unsigned int static_flags; int (*enqueue)(struct sk_buff *, struct Qdisc *, struct sk_buff **); struct sk_buff * (*dequeue)(struct Qdisc *); struct sk_buff * (*peek)(struct Qdisc *); int (*init)(struct Qdisc *, struct nlattr *, struct netlink_ext_ack *); void (*reset)(struct Qdisc *); void (*destroy)(struct Qdisc *); int (*change)(struct Qdisc *, struct nlattr *, struct netlink_ext_ack *); void (*attach)(struct Qdisc *); int (*change_tx_queue_len)(struct Qdisc *, unsigned int); int (*dump)(struct Qdisc *, struct sk_buff *); int (*dump_stats)(struct Qdisc *, struct gnet_dump *); void (*ingress_block_set)(struct Qdisc *, u32); void (*egress_block_set)(struct Qdisc *, u32); u32 (*ingress_block_get)(struct Qdisc *); u32 (*egress_block_get)(struct Qdisc *); struct module *owner; }; struct qdisc_walker; struct Qdisc_class_ops { unsigned int flags; struct netdev_queue * (*select_queue)(struct Qdisc *, struct tcmsg *); int (*graft)(struct Qdisc *, long unsigned int, struct Qdisc *, struct Qdisc **, struct netlink_ext_ack *); struct Qdisc * (*leaf)(struct Qdisc *, long unsigned int); void (*qlen_notify)(struct Qdisc *, long unsigned int); long unsigned int (*find)(struct Qdisc *, u32); int (*change)(struct Qdisc *, u32, u32, struct nlattr **, long unsigned int *, struct netlink_ext_ack *); int (*delete)(struct Qdisc *, long unsigned int, struct netlink_ext_ack *); void (*walk)(struct Qdisc *, struct qdisc_walker *); struct tcf_block * (*tcf_block)(struct Qdisc *, long unsigned int, struct netlink_ext_ack *); long unsigned int (*bind_tcf)(struct Qdisc *, long unsigned int, u32); void (*unbind_tcf)(struct Qdisc *, long unsigned int); int (*dump)(struct Qdisc *, long unsigned int, struct sk_buff *, struct tcmsg *); int (*dump_stats)(struct Qdisc *, long unsigned int, struct gnet_dump *); }; struct tcf_chain; struct tcf_block { struct mutex lock; struct list_head chain_list; u32 index; u32 classid; refcount_t refcnt; struct net *net; struct Qdisc *q; struct rw_semaphore cb_lock; struct flow_block flow_block; struct list_head owner_list; bool keep_dst; atomic_t offloadcnt; unsigned int nooffloaddevcnt; unsigned int lockeddevcnt; struct { struct tcf_chain *chain; struct list_head filter_chain_list; } chain0; struct callback_head rcu; struct hlist_head proto_destroy_ht[128]; struct mutex proto_destroy_lock; }; struct tcf_result; struct tcf_proto_ops; struct tcf_proto { struct tcf_proto *next; void *root; int (*classify)(struct sk_buff *, const struct tcf_proto *, struct tcf_result *); __be16 protocol; u32 prio; void *data; const struct tcf_proto_ops *ops; struct tcf_chain *chain; spinlock_t lock; bool deleting; refcount_t refcnt; struct callback_head rcu; struct hlist_node destroy_ht_node; }; struct tcf_result { union { struct { long unsigned int class; u32 classid; }; const struct tcf_proto *goto_tp; struct { bool ingress; struct gnet_stats_queue *qstats; }; }; }; struct tcf_walker; struct tcf_proto_ops { struct list_head head; char kind[16]; int (*classify)(struct sk_buff *, const struct tcf_proto *, struct tcf_result *); int (*init)(struct tcf_proto *); void (*destroy)(struct tcf_proto *, bool, struct netlink_ext_ack *); void * (*get)(struct tcf_proto *, u32); void (*put)(struct tcf_proto *, void *); int (*change)(struct net *, struct sk_buff *, struct tcf_proto *, long unsigned int, u32, struct nlattr **, void **, bool, bool, struct netlink_ext_ack *); int (*delete)(struct tcf_proto *, void *, bool *, bool, struct netlink_ext_ack *); bool (*delete_empty)(struct tcf_proto *); void (*walk)(struct tcf_proto *, struct tcf_walker *, bool); int (*reoffload)(struct tcf_proto *, bool, flow_setup_cb_t *, void *, struct netlink_ext_ack *); void (*hw_add)(struct tcf_proto *, void *); void (*hw_del)(struct tcf_proto *, void *); void (*bind_class)(void *, u32, long unsigned int, void *, long unsigned int); void * (*tmplt_create)(struct net *, struct tcf_chain *, struct nlattr **, struct netlink_ext_ack *); void (*tmplt_destroy)(void *); int (*dump)(struct net *, struct tcf_proto *, void *, struct sk_buff *, struct tcmsg *, bool); int (*terse_dump)(struct net *, struct tcf_proto *, void *, struct sk_buff *, struct tcmsg *, bool); int (*tmplt_dump)(struct sk_buff *, struct net *, void *); struct module *owner; int flags; }; struct tcf_chain { struct mutex filter_chain_lock; struct tcf_proto *filter_chain; struct list_head list; struct tcf_block *block; u32 index; unsigned int refcnt; unsigned int action_refcnt; bool explicitly_created; bool flushing; const struct tcf_proto_ops *tmplt_ops; void *tmplt_priv; struct callback_head rcu; }; struct sock_fprog_kern { u16 len; struct sock_filter *filter; }; struct bpf_prog_stats { u64 cnt; u64 nsecs; u64 misses; struct u64_stats_sync syncp; long: 64; }; struct sk_filter { refcount_t refcnt; struct callback_head rcu; struct bpf_prog *prog; }; enum { NEIGH_VAR_MCAST_PROBES = 0, NEIGH_VAR_UCAST_PROBES = 1, NEIGH_VAR_APP_PROBES = 2, NEIGH_VAR_MCAST_REPROBES = 3, NEIGH_VAR_RETRANS_TIME = 4, NEIGH_VAR_BASE_REACHABLE_TIME = 5, NEIGH_VAR_DELAY_PROBE_TIME = 6, NEIGH_VAR_GC_STALETIME = 7, NEIGH_VAR_QUEUE_LEN_BYTES = 8, NEIGH_VAR_PROXY_QLEN = 9, NEIGH_VAR_ANYCAST_DELAY = 10, NEIGH_VAR_PROXY_DELAY = 11, NEIGH_VAR_LOCKTIME = 12, NEIGH_VAR_QUEUE_LEN = 13, NEIGH_VAR_RETRANS_TIME_MS = 14, NEIGH_VAR_BASE_REACHABLE_TIME_MS = 15, NEIGH_VAR_GC_INTERVAL = 16, NEIGH_VAR_GC_THRESH1 = 17, NEIGH_VAR_GC_THRESH2 = 18, NEIGH_VAR_GC_THRESH3 = 19, NEIGH_VAR_MAX = 20, }; struct pneigh_entry; struct neigh_statistics; struct neigh_hash_table; struct neigh_table { int family; unsigned int entry_size; unsigned int key_len; __be16 protocol; __u32 (*hash)(const void *, const struct net_device *, __u32 *); bool (*key_eq)(const struct neighbour *, const void *); int (*constructor)(struct neighbour *); int (*pconstructor)(struct pneigh_entry *); void (*pdestructor)(struct pneigh_entry *); void (*proxy_redo)(struct sk_buff *); int (*is_multicast)(const void *); bool (*allow_add)(const struct net_device *, struct netlink_ext_ack *); char *id; struct neigh_parms parms; struct list_head parms_list; int gc_interval; int gc_thresh1; int gc_thresh2; int gc_thresh3; long unsigned int last_flush; struct delayed_work gc_work; struct timer_list proxy_timer; struct sk_buff_head proxy_queue; atomic_t entries; atomic_t gc_entries; struct list_head gc_list; rwlock_t lock; long unsigned int last_rand; struct neigh_statistics *stats; struct neigh_hash_table *nht; struct pneigh_entry **phash_buckets; }; struct neigh_statistics { long unsigned int allocs; long unsigned int destroys; long unsigned int hash_grows; long unsigned int res_failed; long unsigned int lookups; long unsigned int hits; long unsigned int rcv_probes_mcast; long unsigned int rcv_probes_ucast; long unsigned int periodic_gc_runs; long unsigned int forced_gc_runs; long unsigned int unres_discards; long unsigned int table_fulls; }; struct neigh_ops { int family; void (*solicit)(struct neighbour *, struct sk_buff *); void (*error_report)(struct neighbour *, struct sk_buff *); int (*output)(struct neighbour *, struct sk_buff *); int (*connected_output)(struct neighbour *, struct sk_buff *); }; struct pneigh_entry { struct pneigh_entry *next; possible_net_t net; struct net_device *dev; u8 flags; u8 protocol; u8 key[0]; }; struct neigh_hash_table { struct neighbour **hash_buckets; unsigned int hash_shift; __u32 hash_rnd[4]; struct callback_head rcu; }; enum { TCP_ESTABLISHED = 1, TCP_SYN_SENT = 2, TCP_SYN_RECV = 3, TCP_FIN_WAIT1 = 4, TCP_FIN_WAIT2 = 5, TCP_TIME_WAIT = 6, TCP_CLOSE = 7, TCP_CLOSE_WAIT = 8, TCP_LAST_ACK = 9, TCP_LISTEN = 10, TCP_CLOSING = 11, TCP_NEW_SYN_RECV = 12, TCP_MAX_STATES = 13, }; struct fib_rule_hdr { __u8 family; __u8 dst_len; __u8 src_len; __u8 tos; __u8 table; __u8 res1; __u8 res2; __u8 action; __u32 flags; }; struct fib_rule_port_range { __u16 start; __u16 end; }; struct fib_kuid_range { kuid_t start; kuid_t end; }; struct fib_rule { struct list_head list; int iifindex; int oifindex; u32 mark; u32 mark_mask; u32 flags; u32 table; u8 action; u8 l3mdev; u8 proto; u8 ip_proto; u32 target; __be64 tun_id; struct fib_rule *ctarget; struct net *fr_net; refcount_t refcnt; u32 pref; int suppress_ifgroup; int suppress_prefixlen; char iifname[16]; char oifname[16]; struct fib_kuid_range uid_range; struct fib_rule_port_range sport_range; struct fib_rule_port_range dport_range; struct callback_head rcu; }; struct fib_lookup_arg { void *lookup_ptr; const void *lookup_data; void *result; struct fib_rule *rule; u32 table; int flags; }; struct smc_hashinfo; struct sk_psock; struct request_sock_ops; struct timewait_sock_ops; struct udp_table; struct raw_hashinfo; struct proto { void (*close)(struct sock *, long int); int (*pre_connect)(struct sock *, struct sockaddr *, int); int (*connect)(struct sock *, struct sockaddr *, int); int (*disconnect)(struct sock *, int); struct sock * (*accept)(struct sock *, int, int *, bool); int (*ioctl)(struct sock *, int, long unsigned int); int (*init)(struct sock *); void (*destroy)(struct sock *); void (*shutdown)(struct sock *, int); int (*setsockopt)(struct sock *, int, int, sockptr_t, unsigned int); int (*getsockopt)(struct sock *, int, int, char *, int *); void (*keepalive)(struct sock *, int); int (*compat_ioctl)(struct sock *, unsigned int, long unsigned int); int (*sendmsg)(struct sock *, struct msghdr *, size_t); int (*recvmsg)(struct sock *, struct msghdr *, size_t, int, int, int *); int (*sendpage)(struct sock *, struct page *, int, size_t, int); int (*bind)(struct sock *, struct sockaddr *, int); int (*bind_add)(struct sock *, struct sockaddr *, int); int (*backlog_rcv)(struct sock *, struct sk_buff *); bool (*bpf_bypass_getsockopt)(int, int); void (*release_cb)(struct sock *); int (*hash)(struct sock *); void (*unhash)(struct sock *); void (*rehash)(struct sock *); int (*get_port)(struct sock *, short unsigned int); int (*psock_update_sk_prot)(struct sock *, struct sk_psock *, bool); unsigned int inuse_idx; bool (*stream_memory_free)(const struct sock *, int); bool (*stream_memory_read)(const struct sock *); void (*enter_memory_pressure)(struct sock *); void (*leave_memory_pressure)(struct sock *); atomic_long_t *memory_allocated; struct percpu_counter *sockets_allocated; long unsigned int *memory_pressure; long int *sysctl_mem; int *sysctl_wmem; int *sysctl_rmem; u32 sysctl_wmem_offset; u32 sysctl_rmem_offset; int max_header; bool no_autobind; struct kmem_cache *slab; unsigned int obj_size; slab_flags_t slab_flags; unsigned int useroffset; unsigned int usersize; struct percpu_counter *orphan_count; struct request_sock_ops *rsk_prot; struct timewait_sock_ops *twsk_prot; union { struct inet_hashinfo *hashinfo; struct udp_table *udp_table; struct raw_hashinfo *raw_hash; struct smc_hashinfo *smc_hash; } h; struct module *owner; char name[32]; struct list_head node; int (*diag_destroy)(struct sock *, int); }; struct request_sock; struct request_sock_ops { int family; unsigned int obj_size; struct kmem_cache *slab; char *slab_name; int (*rtx_syn_ack)(const struct sock *, struct request_sock *); void (*send_ack)(const struct sock *, struct sk_buff *, struct request_sock *); void (*send_reset)(const struct sock *, struct sk_buff *); void (*destructor)(struct request_sock *); void (*syn_ack_timeout)(const struct request_sock *); }; struct timewait_sock_ops { struct kmem_cache *twsk_slab; char *twsk_slab_name; unsigned int twsk_obj_size; int (*twsk_unique)(struct sock *, struct sock *, void *); void (*twsk_destructor)(struct sock *); }; struct saved_syn; struct request_sock { struct sock_common __req_common; struct request_sock *dl_next; u16 mss; u8 num_retrans; u8 syncookie: 1; u8 num_timeout: 7; u32 ts_recent; struct timer_list rsk_timer; const struct request_sock_ops *rsk_ops; struct sock *sk; struct saved_syn *saved_syn; u32 secid; u32 peer_secid; }; struct saved_syn { u32 mac_hdrlen; u32 network_hdrlen; u32 tcp_hdrlen; u8 data[0]; }; enum tsq_enum { TSQ_THROTTLED = 0, TSQ_QUEUED = 1, TCP_TSQ_DEFERRED = 2, TCP_WRITE_TIMER_DEFERRED = 3, TCP_DELACK_TIMER_DEFERRED = 4, TCP_MTU_REDUCED_DEFERRED = 5, }; struct ip6_sf_list { struct ip6_sf_list *sf_next; struct in6_addr sf_addr; long unsigned int sf_count[2]; unsigned char sf_gsresp; unsigned char sf_oldin; unsigned char sf_crcount; struct callback_head rcu; }; struct ifmcaddr6 { struct in6_addr mca_addr; struct inet6_dev *idev; struct ifmcaddr6 *next; struct ip6_sf_list *mca_sources; struct ip6_sf_list *mca_tomb; unsigned int mca_sfmode; unsigned char mca_crcount; long unsigned int mca_sfcount[2]; struct delayed_work mca_work; unsigned int mca_flags; int mca_users; refcount_t mca_refcnt; long unsigned int mca_cstamp; long unsigned int mca_tstamp; struct callback_head rcu; }; struct ifacaddr6 { struct in6_addr aca_addr; struct fib6_info *aca_rt; struct ifacaddr6 *aca_next; struct hlist_node aca_addr_lst; int aca_users; refcount_t aca_refcnt; long unsigned int aca_cstamp; long unsigned int aca_tstamp; struct callback_head rcu; }; enum { __ND_OPT_PREFIX_INFO_END = 0, ND_OPT_SOURCE_LL_ADDR = 1, ND_OPT_TARGET_LL_ADDR = 2, ND_OPT_PREFIX_INFO = 3, ND_OPT_REDIRECT_HDR = 4, ND_OPT_MTU = 5, ND_OPT_NONCE = 14, __ND_OPT_ARRAY_MAX = 15, ND_OPT_ROUTE_INFO = 24, ND_OPT_RDNSS = 25, ND_OPT_DNSSL = 31, ND_OPT_6CO = 34, ND_OPT_CAPTIVE_PORTAL = 37, ND_OPT_PREF64 = 38, __ND_OPT_MAX = 39, }; struct nd_opt_hdr { __u8 nd_opt_type; __u8 nd_opt_len; }; struct ndisc_options { struct nd_opt_hdr *nd_opt_array[15]; struct nd_opt_hdr *nd_opts_ri; struct nd_opt_hdr *nd_opts_ri_end; struct nd_opt_hdr *nd_useropts; struct nd_opt_hdr *nd_useropts_end; struct nd_opt_hdr *nd_802154_opt_array[3]; }; struct prefix_info { __u8 type; __u8 length; __u8 prefix_len; __u8 reserved: 6; __u8 autoconf: 1; __u8 onlink: 1; __be32 valid; __be32 prefered; __be32 reserved2; struct in6_addr prefix; }; enum nfs_opnum4 { OP_ACCESS = 3, OP_CLOSE = 4, OP_COMMIT = 5, OP_CREATE = 6, OP_DELEGPURGE = 7, OP_DELEGRETURN = 8, OP_GETATTR = 9, OP_GETFH = 10, OP_LINK = 11, OP_LOCK = 12, OP_LOCKT = 13, OP_LOCKU = 14, OP_LOOKUP = 15, OP_LOOKUPP = 16, OP_NVERIFY = 17, OP_OPEN = 18, OP_OPENATTR = 19, OP_OPEN_CONFIRM = 20, OP_OPEN_DOWNGRADE = 21, OP_PUTFH = 22, OP_PUTPUBFH = 23, OP_PUTROOTFH = 24, OP_READ = 25, OP_READDIR = 26, OP_READLINK = 27, OP_REMOVE = 28, OP_RENAME = 29, OP_RENEW = 30, OP_RESTOREFH = 31, OP_SAVEFH = 32, OP_SECINFO = 33, OP_SETATTR = 34, OP_SETCLIENTID = 35, OP_SETCLIENTID_CONFIRM = 36, OP_VERIFY = 37, OP_WRITE = 38, OP_RELEASE_LOCKOWNER = 39, OP_BACKCHANNEL_CTL = 40, OP_BIND_CONN_TO_SESSION = 41, OP_EXCHANGE_ID = 42, OP_CREATE_SESSION = 43, OP_DESTROY_SESSION = 44, OP_FREE_STATEID = 45, OP_GET_DIR_DELEGATION = 46, OP_GETDEVICEINFO = 47, OP_GETDEVICELIST = 48, OP_LAYOUTCOMMIT = 49, OP_LAYOUTGET = 50, OP_LAYOUTRETURN = 51, OP_SECINFO_NO_NAME = 52, OP_SEQUENCE = 53, OP_SET_SSV = 54, OP_TEST_STATEID = 55, OP_WANT_DELEGATION = 56, OP_DESTROY_CLIENTID = 57, OP_RECLAIM_COMPLETE = 58, OP_ALLOCATE = 59, OP_COPY = 60, OP_COPY_NOTIFY = 61, OP_DEALLOCATE = 62, OP_IO_ADVISE = 63, OP_LAYOUTERROR = 64, OP_LAYOUTSTATS = 65, OP_OFFLOAD_CANCEL = 66, OP_OFFLOAD_STATUS = 67, OP_READ_PLUS = 68, OP_SEEK = 69, OP_WRITE_SAME = 70, OP_CLONE = 71, OP_GETXATTR = 72, OP_SETXATTR = 73, OP_LISTXATTRS = 74, OP_REMOVEXATTR = 75, OP_ILLEGAL = 10044, }; enum perf_branch_sample_type_shift { PERF_SAMPLE_BRANCH_USER_SHIFT = 0, PERF_SAMPLE_BRANCH_KERNEL_SHIFT = 1, PERF_SAMPLE_BRANCH_HV_SHIFT = 2, PERF_SAMPLE_BRANCH_ANY_SHIFT = 3, PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT = 4, PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT = 5, PERF_SAMPLE_BRANCH_IND_CALL_SHIFT = 6, PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT = 7, PERF_SAMPLE_BRANCH_IN_TX_SHIFT = 8, PERF_SAMPLE_BRANCH_NO_TX_SHIFT = 9, PERF_SAMPLE_BRANCH_COND_SHIFT = 10, PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT = 11, PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT = 12, PERF_SAMPLE_BRANCH_CALL_SHIFT = 13, PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT = 14, PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT = 15, PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT = 16, PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT = 17, PERF_SAMPLE_BRANCH_MAX_SHIFT = 18, }; enum exception_stack_ordering { ESTACK_DF = 0, ESTACK_NMI = 1, ESTACK_DB = 2, ESTACK_MCE = 3, ESTACK_VC = 4, ESTACK_VC2 = 5, N_EXCEPTION_STACKS = 6, }; enum { TSK_TRACE_FL_TRACE_BIT = 0, TSK_TRACE_FL_GRAPH_BIT = 1, }; struct uuidcmp { const char *uuid; int len; }; struct subprocess_info { struct work_struct work; struct completion *complete; const char *path; char **argv; char **envp; int wait; int retval; int (*init)(struct subprocess_info *, struct cred *); void (*cleanup)(struct subprocess_info *); void *data; }; typedef phys_addr_t resource_size_t; struct __va_list_tag { unsigned int gp_offset; unsigned int fp_offset; void *overflow_arg_area; void *reg_save_area; }; typedef __builtin_va_list __gnuc_va_list; typedef __gnuc_va_list va_list; struct resource { resource_size_t start; resource_size_t end; const char *name; long unsigned int flags; long unsigned int desc; struct resource *parent; struct resource *sibling; struct resource *child; }; typedef u64 async_cookie_t; typedef void (*async_func_t)(void *, async_cookie_t); struct async_domain { struct list_head pending; unsigned int registered: 1; }; struct hash { int ino; int minor; int major; umode_t mode; struct hash *next; char name[4098]; }; struct dir_entry { struct list_head list; char *name; time64_t mtime; }; enum state { Start = 0, Collect = 1, GotHeader = 2, SkipIt = 3, GotName = 4, CopyFile = 5, GotSymlink = 6, Reset = 7, }; typedef int (*decompress_fn)(unsigned char *, long int, long int (*)(void *, long unsigned int), long int (*)(void *, long unsigned int), unsigned char *, long int *, void (*)(char *)); enum { HI_SOFTIRQ = 0, TIMER_SOFTIRQ = 1, NET_TX_SOFTIRQ = 2, NET_RX_SOFTIRQ = 3, BLOCK_SOFTIRQ = 4, IRQ_POLL_SOFTIRQ = 5, TASKLET_SOFTIRQ = 6, SCHED_SOFTIRQ = 7, HRTIMER_SOFTIRQ = 8, RCU_SOFTIRQ = 9, NR_SOFTIRQS = 10, }; enum ucount_type { UCOUNT_USER_NAMESPACES = 0, UCOUNT_PID_NAMESPACES = 1, UCOUNT_UTS_NAMESPACES = 2, UCOUNT_IPC_NAMESPACES = 3, UCOUNT_NET_NAMESPACES = 4, UCOUNT_MNT_NAMESPACES = 5, UCOUNT_CGROUP_NAMESPACES = 6, UCOUNT_TIME_NAMESPACES = 7, UCOUNT_INOTIFY_INSTANCES = 8, UCOUNT_INOTIFY_WATCHES = 9, UCOUNT_FANOTIFY_GROUPS = 10, UCOUNT_FANOTIFY_MARKS = 11, UCOUNT_RLIMIT_NPROC = 12, UCOUNT_RLIMIT_MSGQUEUE = 13, UCOUNT_RLIMIT_SIGPENDING = 14, UCOUNT_RLIMIT_MEMLOCK = 15, UCOUNT_COUNTS = 16, }; enum flow_dissector_key_id { FLOW_DISSECTOR_KEY_CONTROL = 0, FLOW_DISSECTOR_KEY_BASIC = 1, FLOW_DISSECTOR_KEY_IPV4_ADDRS = 2, FLOW_DISSECTOR_KEY_IPV6_ADDRS = 3, FLOW_DISSECTOR_KEY_PORTS = 4, FLOW_DISSECTOR_KEY_PORTS_RANGE = 5, FLOW_DISSECTOR_KEY_ICMP = 6, FLOW_DISSECTOR_KEY_ETH_ADDRS = 7, FLOW_DISSECTOR_KEY_TIPC = 8, FLOW_DISSECTOR_KEY_ARP = 9, FLOW_DISSECTOR_KEY_VLAN = 10, FLOW_DISSECTOR_KEY_FLOW_LABEL = 11, FLOW_DISSECTOR_KEY_GRE_KEYID = 12, FLOW_DISSECTOR_KEY_MPLS_ENTROPY = 13, FLOW_DISSECTOR_KEY_ENC_KEYID = 14, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS = 15, FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS = 16, FLOW_DISSECTOR_KEY_ENC_CONTROL = 17, FLOW_DISSECTOR_KEY_ENC_PORTS = 18, FLOW_DISSECTOR_KEY_MPLS = 19, FLOW_DISSECTOR_KEY_TCP = 20, FLOW_DISSECTOR_KEY_IP = 21, FLOW_DISSECTOR_KEY_CVLAN = 22, FLOW_DISSECTOR_KEY_ENC_IP = 23, FLOW_DISSECTOR_KEY_ENC_OPTS = 24, FLOW_DISSECTOR_KEY_META = 25, FLOW_DISSECTOR_KEY_CT = 26, FLOW_DISSECTOR_KEY_HASH = 27, FLOW_DISSECTOR_KEY_MAX = 28, }; enum { IPSTATS_MIB_NUM = 0, IPSTATS_MIB_INPKTS = 1, IPSTATS_MIB_INOCTETS = 2, IPSTATS_MIB_INDELIVERS = 3, IPSTATS_MIB_OUTFORWDATAGRAMS = 4, IPSTATS_MIB_OUTPKTS = 5, IPSTATS_MIB_OUTOCTETS = 6, IPSTATS_MIB_INHDRERRORS = 7, IPSTATS_MIB_INTOOBIGERRORS = 8, IPSTATS_MIB_INNOROUTES = 9, IPSTATS_MIB_INADDRERRORS = 10, IPSTATS_MIB_INUNKNOWNPROTOS = 11, IPSTATS_MIB_INTRUNCATEDPKTS = 12, IPSTATS_MIB_INDISCARDS = 13, IPSTATS_MIB_OUTDISCARDS = 14, IPSTATS_MIB_OUTNOROUTES = 15, IPSTATS_MIB_REASMTIMEOUT = 16, IPSTATS_MIB_REASMREQDS = 17, IPSTATS_MIB_REASMOKS = 18, IPSTATS_MIB_REASMFAILS = 19, IPSTATS_MIB_FRAGOKS = 20, IPSTATS_MIB_FRAGFAILS = 21, IPSTATS_MIB_FRAGCREATES = 22, IPSTATS_MIB_INMCASTPKTS = 23, IPSTATS_MIB_OUTMCASTPKTS = 24, IPSTATS_MIB_INBCASTPKTS = 25, IPSTATS_MIB_OUTBCASTPKTS = 26, IPSTATS_MIB_INMCASTOCTETS = 27, IPSTATS_MIB_OUTMCASTOCTETS = 28, IPSTATS_MIB_INBCASTOCTETS = 29, IPSTATS_MIB_OUTBCASTOCTETS = 30, IPSTATS_MIB_CSUMERRORS = 31, IPSTATS_MIB_NOECTPKTS = 32, IPSTATS_MIB_ECT1PKTS = 33, IPSTATS_MIB_ECT0PKTS = 34, IPSTATS_MIB_CEPKTS = 35, IPSTATS_MIB_REASM_OVERLAPS = 36, __IPSTATS_MIB_MAX = 37, }; enum { ICMP_MIB_NUM = 0, ICMP_MIB_INMSGS = 1, ICMP_MIB_INERRORS = 2, ICMP_MIB_INDESTUNREACHS = 3, ICMP_MIB_INTIMEEXCDS = 4, ICMP_MIB_INPARMPROBS = 5, ICMP_MIB_INSRCQUENCHS = 6, ICMP_MIB_INREDIRECTS = 7, ICMP_MIB_INECHOS = 8, ICMP_MIB_INECHOREPS = 9, ICMP_MIB_INTIMESTAMPS = 10, ICMP_MIB_INTIMESTAMPREPS = 11, ICMP_MIB_INADDRMASKS = 12, ICMP_MIB_INADDRMASKREPS = 13, ICMP_MIB_OUTMSGS = 14, ICMP_MIB_OUTERRORS = 15, ICMP_MIB_OUTDESTUNREACHS = 16, ICMP_MIB_OUTTIMEEXCDS = 17, ICMP_MIB_OUTPARMPROBS = 18, ICMP_MIB_OUTSRCQUENCHS = 19, ICMP_MIB_OUTREDIRECTS = 20, ICMP_MIB_OUTECHOS = 21, ICMP_MIB_OUTECHOREPS = 22, ICMP_MIB_OUTTIMESTAMPS = 23, ICMP_MIB_OUTTIMESTAMPREPS = 24, ICMP_MIB_OUTADDRMASKS = 25, ICMP_MIB_OUTADDRMASKREPS = 26, ICMP_MIB_CSUMERRORS = 27, __ICMP_MIB_MAX = 28, }; enum { ICMP6_MIB_NUM = 0, ICMP6_MIB_INMSGS = 1, ICMP6_MIB_INERRORS = 2, ICMP6_MIB_OUTMSGS = 3, ICMP6_MIB_OUTERRORS = 4, ICMP6_MIB_CSUMERRORS = 5, __ICMP6_MIB_MAX = 6, }; enum { TCP_MIB_NUM = 0, TCP_MIB_RTOALGORITHM = 1, TCP_MIB_RTOMIN = 2, TCP_MIB_RTOMAX = 3, TCP_MIB_MAXCONN = 4, TCP_MIB_ACTIVEOPENS = 5, TCP_MIB_PASSIVEOPENS = 6, TCP_MIB_ATTEMPTFAILS = 7, TCP_MIB_ESTABRESETS = 8, TCP_MIB_CURRESTAB = 9, TCP_MIB_INSEGS = 10, TCP_MIB_OUTSEGS = 11, TCP_MIB_RETRANSSEGS = 12, TCP_MIB_INERRS = 13, TCP_MIB_OUTRSTS = 14, TCP_MIB_CSUMERRORS = 15, __TCP_MIB_MAX = 16, }; enum { UDP_MIB_NUM = 0, UDP_MIB_INDATAGRAMS = 1, UDP_MIB_NOPORTS = 2, UDP_MIB_INERRORS = 3, UDP_MIB_OUTDATAGRAMS = 4, UDP_MIB_RCVBUFERRORS = 5, UDP_MIB_SNDBUFERRORS = 6, UDP_MIB_CSUMERRORS = 7, UDP_MIB_IGNOREDMULTI = 8, UDP_MIB_MEMERRORS = 9, __UDP_MIB_MAX = 10, }; enum { LINUX_MIB_NUM = 0, LINUX_MIB_SYNCOOKIESSENT = 1, LINUX_MIB_SYNCOOKIESRECV = 2, LINUX_MIB_SYNCOOKIESFAILED = 3, LINUX_MIB_EMBRYONICRSTS = 4, LINUX_MIB_PRUNECALLED = 5, LINUX_MIB_RCVPRUNED = 6, LINUX_MIB_OFOPRUNED = 7, LINUX_MIB_OUTOFWINDOWICMPS = 8, LINUX_MIB_LOCKDROPPEDICMPS = 9, LINUX_MIB_ARPFILTER = 10, LINUX_MIB_TIMEWAITED = 11, LINUX_MIB_TIMEWAITRECYCLED = 12, LINUX_MIB_TIMEWAITKILLED = 13, LINUX_MIB_PAWSACTIVEREJECTED = 14, LINUX_MIB_PAWSESTABREJECTED = 15, LINUX_MIB_DELAYEDACKS = 16, LINUX_MIB_DELAYEDACKLOCKED = 17, LINUX_MIB_DELAYEDACKLOST = 18, LINUX_MIB_LISTENOVERFLOWS = 19, LINUX_MIB_LISTENDROPS = 20, LINUX_MIB_TCPHPHITS = 21, LINUX_MIB_TCPPUREACKS = 22, LINUX_MIB_TCPHPACKS = 23, LINUX_MIB_TCPRENORECOVERY = 24, LINUX_MIB_TCPSACKRECOVERY = 25, LINUX_MIB_TCPSACKRENEGING = 26, LINUX_MIB_TCPSACKREORDER = 27, LINUX_MIB_TCPRENOREORDER = 28, LINUX_MIB_TCPTSREORDER = 29, LINUX_MIB_TCPFULLUNDO = 30, LINUX_MIB_TCPPARTIALUNDO = 31, LINUX_MIB_TCPDSACKUNDO = 32, LINUX_MIB_TCPLOSSUNDO = 33, LINUX_MIB_TCPLOSTRETRANSMIT = 34, LINUX_MIB_TCPRENOFAILURES = 35, LINUX_MIB_TCPSACKFAILURES = 36, LINUX_MIB_TCPLOSSFAILURES = 37, LINUX_MIB_TCPFASTRETRANS = 38, LINUX_MIB_TCPSLOWSTARTRETRANS = 39, LINUX_MIB_TCPTIMEOUTS = 40, LINUX_MIB_TCPLOSSPROBES = 41, LINUX_MIB_TCPLOSSPROBERECOVERY = 42, LINUX_MIB_TCPRENORECOVERYFAIL = 43, LINUX_MIB_TCPSACKRECOVERYFAIL = 44, LINUX_MIB_TCPRCVCOLLAPSED = 45, LINUX_MIB_TCPDSACKOLDSENT = 46, LINUX_MIB_TCPDSACKOFOSENT = 47, LINUX_MIB_TCPDSACKRECV = 48, LINUX_MIB_TCPDSACKOFORECV = 49, LINUX_MIB_TCPABORTONDATA = 50, LINUX_MIB_TCPABORTONCLOSE = 51, LINUX_MIB_TCPABORTONMEMORY = 52, LINUX_MIB_TCPABORTONTIMEOUT = 53, LINUX_MIB_TCPABORTONLINGER = 54, LINUX_MIB_TCPABORTFAILED = 55, LINUX_MIB_TCPMEMORYPRESSURES = 56, LINUX_MIB_TCPMEMORYPRESSURESCHRONO = 57, LINUX_MIB_TCPSACKDISCARD = 58, LINUX_MIB_TCPDSACKIGNOREDOLD = 59, LINUX_MIB_TCPDSACKIGNOREDNOUNDO = 60, LINUX_MIB_TCPSPURIOUSRTOS = 61, LINUX_MIB_TCPMD5NOTFOUND = 62, LINUX_MIB_TCPMD5UNEXPECTED = 63, LINUX_MIB_TCPMD5FAILURE = 64, LINUX_MIB_SACKSHIFTED = 65, LINUX_MIB_SACKMERGED = 66, LINUX_MIB_SACKSHIFTFALLBACK = 67, LINUX_MIB_TCPBACKLOGDROP = 68, LINUX_MIB_PFMEMALLOCDROP = 69, LINUX_MIB_TCPMINTTLDROP = 70, LINUX_MIB_TCPDEFERACCEPTDROP = 71, LINUX_MIB_IPRPFILTER = 72, LINUX_MIB_TCPTIMEWAITOVERFLOW = 73, LINUX_MIB_TCPREQQFULLDOCOOKIES = 74, LINUX_MIB_TCPREQQFULLDROP = 75, LINUX_MIB_TCPRETRANSFAIL = 76, LINUX_MIB_TCPRCVCOALESCE = 77, LINUX_MIB_TCPBACKLOGCOALESCE = 78, LINUX_MIB_TCPOFOQUEUE = 79, LINUX_MIB_TCPOFODROP = 80, LINUX_MIB_TCPOFOMERGE = 81, LINUX_MIB_TCPCHALLENGEACK = 82, LINUX_MIB_TCPSYNCHALLENGE = 83, LINUX_MIB_TCPFASTOPENACTIVE = 84, LINUX_MIB_TCPFASTOPENACTIVEFAIL = 85, LINUX_MIB_TCPFASTOPENPASSIVE = 86, LINUX_MIB_TCPFASTOPENPASSIVEFAIL = 87, LINUX_MIB_TCPFASTOPENLISTENOVERFLOW = 88, LINUX_MIB_TCPFASTOPENCOOKIEREQD = 89, LINUX_MIB_TCPFASTOPENBLACKHOLE = 90, LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES = 91, LINUX_MIB_BUSYPOLLRXPACKETS = 92, LINUX_MIB_TCPAUTOCORKING = 93, LINUX_MIB_TCPFROMZEROWINDOWADV = 94, LINUX_MIB_TCPTOZEROWINDOWADV = 95, LINUX_MIB_TCPWANTZEROWINDOWADV = 96, LINUX_MIB_TCPSYNRETRANS = 97, LINUX_MIB_TCPORIGDATASENT = 98, LINUX_MIB_TCPHYSTARTTRAINDETECT = 99, LINUX_MIB_TCPHYSTARTTRAINCWND = 100, LINUX_MIB_TCPHYSTARTDELAYDETECT = 101, LINUX_MIB_TCPHYSTARTDELAYCWND = 102, LINUX_MIB_TCPACKSKIPPEDSYNRECV = 103, LINUX_MIB_TCPACKSKIPPEDPAWS = 104, LINUX_MIB_TCPACKSKIPPEDSEQ = 105, LINUX_MIB_TCPACKSKIPPEDFINWAIT2 = 106, LINUX_MIB_TCPACKSKIPPEDTIMEWAIT = 107, LINUX_MIB_TCPACKSKIPPEDCHALLENGE = 108, LINUX_MIB_TCPWINPROBE = 109, LINUX_MIB_TCPKEEPALIVE = 110, LINUX_MIB_TCPMTUPFAIL = 111, LINUX_MIB_TCPMTUPSUCCESS = 112, LINUX_MIB_TCPDELIVERED = 113, LINUX_MIB_TCPDELIVEREDCE = 114, LINUX_MIB_TCPACKCOMPRESSED = 115, LINUX_MIB_TCPZEROWINDOWDROP = 116, LINUX_MIB_TCPRCVQDROP = 117, LINUX_MIB_TCPWQUEUETOOBIG = 118, LINUX_MIB_TCPFASTOPENPASSIVEALTKEY = 119, LINUX_MIB_TCPTIMEOUTREHASH = 120, LINUX_MIB_TCPDUPLICATEDATAREHASH = 121, LINUX_MIB_TCPDSACKRECVSEGS = 122, LINUX_MIB_TCPDSACKIGNOREDDUBIOUS = 123, LINUX_MIB_TCPMIGRATEREQSUCCESS = 124, LINUX_MIB_TCPMIGRATEREQFAILURE = 125, __LINUX_MIB_MAX = 126, }; enum { LINUX_MIB_XFRMNUM = 0, LINUX_MIB_XFRMINERROR = 1, LINUX_MIB_XFRMINBUFFERERROR = 2, LINUX_MIB_XFRMINHDRERROR = 3, LINUX_MIB_XFRMINNOSTATES = 4, LINUX_MIB_XFRMINSTATEPROTOERROR = 5, LINUX_MIB_XFRMINSTATEMODEERROR = 6, LINUX_MIB_XFRMINSTATESEQERROR = 7, LINUX_MIB_XFRMINSTATEEXPIRED = 8, LINUX_MIB_XFRMINSTATEMISMATCH = 9, LINUX_MIB_XFRMINSTATEINVALID = 10, LINUX_MIB_XFRMINTMPLMISMATCH = 11, LINUX_MIB_XFRMINNOPOLS = 12, LINUX_MIB_XFRMINPOLBLOCK = 13, LINUX_MIB_XFRMINPOLERROR = 14, LINUX_MIB_XFRMOUTERROR = 15, LINUX_MIB_XFRMOUTBUNDLEGENERROR = 16, LINUX_MIB_XFRMOUTBUNDLECHECKERROR = 17, LINUX_MIB_XFRMOUTNOSTATES = 18, LINUX_MIB_XFRMOUTSTATEPROTOERROR = 19, LINUX_MIB_XFRMOUTSTATEMODEERROR = 20, LINUX_MIB_XFRMOUTSTATESEQERROR = 21, LINUX_MIB_XFRMOUTSTATEEXPIRED = 22, LINUX_MIB_XFRMOUTPOLBLOCK = 23, LINUX_MIB_XFRMOUTPOLDEAD = 24, LINUX_MIB_XFRMOUTPOLERROR = 25, LINUX_MIB_XFRMFWDHDRERROR = 26, LINUX_MIB_XFRMOUTSTATEINVALID = 27, LINUX_MIB_XFRMACQUIREERROR = 28, __LINUX_MIB_XFRMMAX = 29, }; enum { LINUX_MIB_TLSNUM = 0, LINUX_MIB_TLSCURRTXSW = 1, LINUX_MIB_TLSCURRRXSW = 2, LINUX_MIB_TLSCURRTXDEVICE = 3, LINUX_MIB_TLSCURRRXDEVICE = 4, LINUX_MIB_TLSTXSW = 5, LINUX_MIB_TLSRXSW = 6, LINUX_MIB_TLSTXDEVICE = 7, LINUX_MIB_TLSRXDEVICE = 8, LINUX_MIB_TLSDECRYPTERROR = 9, LINUX_MIB_TLSRXDEVICERESYNC = 10, __LINUX_MIB_TLSMAX = 11, }; enum nf_inet_hooks { NF_INET_PRE_ROUTING = 0, NF_INET_LOCAL_IN = 1, NF_INET_FORWARD = 2, NF_INET_LOCAL_OUT = 3, NF_INET_POST_ROUTING = 4, NF_INET_NUMHOOKS = 5, NF_INET_INGRESS = 5, }; enum { NFPROTO_UNSPEC = 0, NFPROTO_INET = 1, NFPROTO_IPV4 = 2, NFPROTO_ARP = 3, NFPROTO_NETDEV = 5, NFPROTO_BRIDGE = 7, NFPROTO_IPV6 = 10, NFPROTO_DECNET = 12, NFPROTO_NUMPROTO = 13, }; enum tcp_conntrack { TCP_CONNTRACK_NONE = 0, TCP_CONNTRACK_SYN_SENT = 1, TCP_CONNTRACK_SYN_RECV = 2, TCP_CONNTRACK_ESTABLISHED = 3, TCP_CONNTRACK_FIN_WAIT = 4, TCP_CONNTRACK_CLOSE_WAIT = 5, TCP_CONNTRACK_LAST_ACK = 6, TCP_CONNTRACK_TIME_WAIT = 7, TCP_CONNTRACK_CLOSE = 8, TCP_CONNTRACK_LISTEN = 9, TCP_CONNTRACK_MAX = 10, TCP_CONNTRACK_IGNORE = 11, TCP_CONNTRACK_RETRANS = 12, TCP_CONNTRACK_UNACK = 13, TCP_CONNTRACK_TIMEOUT_MAX = 14, }; enum ct_dccp_states { CT_DCCP_NONE = 0, CT_DCCP_REQUEST = 1, CT_DCCP_RESPOND = 2, CT_DCCP_PARTOPEN = 3, CT_DCCP_OPEN = 4, CT_DCCP_CLOSEREQ = 5, CT_DCCP_CLOSING = 6, CT_DCCP_TIMEWAIT = 7, CT_DCCP_IGNORE = 8, CT_DCCP_INVALID = 9, __CT_DCCP_MAX = 10, }; enum ip_conntrack_dir { IP_CT_DIR_ORIGINAL = 0, IP_CT_DIR_REPLY = 1, IP_CT_DIR_MAX = 2, }; enum sctp_conntrack { SCTP_CONNTRACK_NONE = 0, SCTP_CONNTRACK_CLOSED = 1, SCTP_CONNTRACK_COOKIE_WAIT = 2, SCTP_CONNTRACK_COOKIE_ECHOED = 3, SCTP_CONNTRACK_ESTABLISHED = 4, SCTP_CONNTRACK_SHUTDOWN_SENT = 5, SCTP_CONNTRACK_SHUTDOWN_RECD = 6, SCTP_CONNTRACK_SHUTDOWN_ACK_SENT = 7, SCTP_CONNTRACK_HEARTBEAT_SENT = 8, SCTP_CONNTRACK_HEARTBEAT_ACKED = 9, SCTP_CONNTRACK_MAX = 10, }; enum udp_conntrack { UDP_CT_UNREPLIED = 0, UDP_CT_REPLIED = 1, UDP_CT_MAX = 2, }; enum gre_conntrack { GRE_CT_UNREPLIED = 0, GRE_CT_REPLIED = 1, GRE_CT_MAX = 2, }; enum { XFRM_POLICY_IN = 0, XFRM_POLICY_OUT = 1, XFRM_POLICY_FWD = 2, XFRM_POLICY_MASK = 3, XFRM_POLICY_MAX = 3, }; enum netns_bpf_attach_type { NETNS_BPF_INVALID = 4294967295, NETNS_BPF_FLOW_DISSECTOR = 0, NETNS_BPF_SK_LOOKUP = 1, MAX_NETNS_BPF_ATTACH_TYPE = 2, }; enum skb_ext_id { SKB_EXT_BRIDGE_NF = 0, SKB_EXT_SEC_PATH = 1, TC_SKB_EXT = 2, SKB_EXT_MPTCP = 3, SKB_EXT_NUM = 4, }; enum audit_ntp_type { AUDIT_NTP_OFFSET = 0, AUDIT_NTP_FREQ = 1, AUDIT_NTP_STATUS = 2, AUDIT_NTP_TAI = 3, AUDIT_NTP_TICK = 4, AUDIT_NTP_ADJUST = 5, AUDIT_NTP_NVALS = 6, }; typedef long int (*sys_call_ptr_t)(const struct pt_regs *); struct io_bitmap { u64 sequence; refcount_t refcnt; unsigned int max; long unsigned int bitmap[1024]; }; enum irqreturn { IRQ_NONE = 0, IRQ_HANDLED = 1, IRQ_WAKE_THREAD = 2, }; typedef enum irqreturn irqreturn_t; typedef struct { u16 __softirq_pending; u8 kvm_cpu_l1tf_flush_l1d; unsigned int __nmi_count; unsigned int apic_timer_irqs; unsigned int irq_spurious_count; unsigned int icr_read_retry_count; unsigned int kvm_posted_intr_ipis; unsigned int kvm_posted_intr_wakeup_ipis; unsigned int kvm_posted_intr_nested_ipis; unsigned int x86_platform_ipis; unsigned int apic_perf_irqs; unsigned int apic_irq_work_irqs; unsigned int irq_resched_count; unsigned int irq_call_count; unsigned int irq_tlb_count; unsigned int irq_thermal_count; unsigned int irq_threshold_count; unsigned int irq_deferred_error_count; unsigned int irq_hv_callback_count; unsigned int irq_hv_reenlightenment_count; unsigned int hyperv_stimer0_count; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; } irq_cpustat_t; typedef irqreturn_t (*irq_handler_t)(int, void *); struct irqaction { irq_handler_t handler; void *dev_id; void *percpu_dev_id; struct irqaction *next; irq_handler_t thread_fn; struct task_struct *thread; struct irqaction *secondary; unsigned int irq; unsigned int flags; long unsigned int thread_flags; long unsigned int thread_mask; const char *name; struct proc_dir_entry *dir; long: 64; long: 64; long: 64; long: 64; }; struct irq_affinity_notify { unsigned int irq; struct kref kref; struct work_struct work; void (*notify)(struct irq_affinity_notify *, const cpumask_t *); void (*release)(struct kref *); }; struct irq_affinity_desc { struct cpumask mask; unsigned int is_managed: 1; }; enum irqchip_irq_state { IRQCHIP_STATE_PENDING = 0, IRQCHIP_STATE_ACTIVE = 1, IRQCHIP_STATE_MASKED = 2, IRQCHIP_STATE_LINE_LEVEL = 3, }; enum { EI_ETYPE_NONE = 0, EI_ETYPE_NULL = 1, EI_ETYPE_ERRNO = 2, EI_ETYPE_ERRNO_NULL = 3, EI_ETYPE_TRUE = 4, }; struct syscall_metadata { const char *name; int syscall_nr; int nb_args; const char **types; const char **args; struct list_head enter_fields; struct trace_event_call *enter_event; struct trace_event_call *exit_event; }; struct irqentry_state { union { bool exit_rcu; bool lockdep; }; }; typedef struct irqentry_state irqentry_state_t; struct irq_desc; typedef void (*irq_flow_handler_t)(struct irq_desc *); struct msi_desc; struct irq_common_data { unsigned int state_use_accessors; unsigned int node; void *handler_data; struct msi_desc *msi_desc; cpumask_var_t affinity; cpumask_var_t effective_affinity; }; struct irq_chip; struct irq_data { u32 mask; unsigned int irq; long unsigned int hwirq; struct irq_common_data *common; struct irq_chip *chip; struct irq_domain *domain; struct irq_data *parent_data; void *chip_data; }; struct irq_desc { struct irq_common_data irq_common_data; struct irq_data irq_data; unsigned int *kstat_irqs; irq_flow_handler_t handle_irq; struct irqaction *action; unsigned int status_use_accessors; unsigned int core_internal_state__do_not_mess_with_it; unsigned int depth; unsigned int wake_depth; unsigned int tot_count; unsigned int irq_count; long unsigned int last_unhandled; unsigned int irqs_unhandled; atomic_t threads_handled; int threads_handled_last; raw_spinlock_t lock; struct cpumask *percpu_enabled; const struct cpumask *percpu_affinity; const struct cpumask *affinity_hint; struct irq_affinity_notify *affinity_notify; cpumask_var_t pending_mask; long unsigned int threads_oneshot; atomic_t threads_active; wait_queue_head_t wait_for_threads; unsigned int nr_actions; unsigned int no_suspend_depth; unsigned int cond_suspend_depth; unsigned int force_resume_depth; struct proc_dir_entry *dir; struct callback_head rcu; struct kobject kobj; struct mutex request_mutex; int parent_irq; struct module *owner; const char *name; long: 64; }; struct x86_msi_addr_lo { union { struct { u32 reserved_0: 2; u32 dest_mode_logical: 1; u32 redirect_hint: 1; u32 reserved_1: 1; u32 virt_destid_8_14: 7; u32 destid_0_7: 8; u32 base_address: 12; }; struct { u32 dmar_reserved_0: 2; u32 dmar_index_15: 1; u32 dmar_subhandle_valid: 1; u32 dmar_format: 1; u32 dmar_index_0_14: 15; u32 dmar_base_address: 12; }; }; }; typedef struct x86_msi_addr_lo arch_msi_msg_addr_lo_t; struct x86_msi_addr_hi { u32 reserved: 8; u32 destid_8_31: 24; }; typedef struct x86_msi_addr_hi arch_msi_msg_addr_hi_t; struct x86_msi_data { u32 vector: 8; u32 delivery_mode: 3; u32 dest_mode_logical: 1; u32 reserved: 2; u32 active_low: 1; u32 is_level: 1; u32 dmar_subhandle; } __attribute__((packed)); typedef struct x86_msi_data arch_msi_msg_data_t; struct msi_msg { union { u32 address_lo; arch_msi_msg_addr_lo_t arch_addr_lo; }; union { u32 address_hi; arch_msi_msg_addr_hi_t arch_addr_hi; }; union { u32 data; arch_msi_msg_data_t arch_data; }; }; struct platform_msi_priv_data; struct platform_msi_desc { struct platform_msi_priv_data *msi_priv_data; u16 msi_index; }; struct fsl_mc_msi_desc { u16 msi_index; }; struct ti_sci_inta_msi_desc { u16 dev_index; }; struct msi_desc { struct list_head list; unsigned int irq; unsigned int nvec_used; struct device *dev; struct msi_msg msg; struct irq_affinity_desc *affinity; const void *iommu_cookie; void (*write_msi_msg)(struct msi_desc *, void *); void *write_msi_msg_data; union { struct { u32 masked; struct { u8 is_msix: 1; u8 multiple: 3; u8 multi_cap: 3; u8 maskbit: 1; u8 is_64: 1; u8 is_virtual: 1; u16 entry_nr; unsigned int default_irq; } msi_attrib; union { u8 mask_pos; void *mask_base; }; }; struct platform_msi_desc platform; struct fsl_mc_msi_desc fsl_mc; struct ti_sci_inta_msi_desc inta; }; }; struct irq_chip { struct device *parent_device; const char *name; unsigned int (*irq_startup)(struct irq_data *); void (*irq_shutdown)(struct irq_data *); void (*irq_enable)(struct irq_data *); void (*irq_disable)(struct irq_data *); void (*irq_ack)(struct irq_data *); void (*irq_mask)(struct irq_data *); void (*irq_mask_ack)(struct irq_data *); void (*irq_unmask)(struct irq_data *); void (*irq_eoi)(struct irq_data *); int (*irq_set_affinity)(struct irq_data *, const struct cpumask *, bool); int (*irq_retrigger)(struct irq_data *); int (*irq_set_type)(struct irq_data *, unsigned int); int (*irq_set_wake)(struct irq_data *, unsigned int); void (*irq_bus_lock)(struct irq_data *); void (*irq_bus_sync_unlock)(struct irq_data *); void (*irq_cpu_online)(struct irq_data *); void (*irq_cpu_offline)(struct irq_data *); void (*irq_suspend)(struct irq_data *); void (*irq_resume)(struct irq_data *); void (*irq_pm_shutdown)(struct irq_data *); void (*irq_calc_mask)(struct irq_data *); void (*irq_print_chip)(struct irq_data *, struct seq_file *); int (*irq_request_resources)(struct irq_data *); void (*irq_release_resources)(struct irq_data *); void (*irq_compose_msi_msg)(struct irq_data *, struct msi_msg *); void (*irq_write_msi_msg)(struct irq_data *, struct msi_msg *); int (*irq_get_irqchip_state)(struct irq_data *, enum irqchip_irq_state, bool *); int (*irq_set_irqchip_state)(struct irq_data *, enum irqchip_irq_state, bool); int (*irq_set_vcpu_affinity)(struct irq_data *, void *); void (*ipi_send_single)(struct irq_data *, unsigned int); void (*ipi_send_mask)(struct irq_data *, const struct cpumask *); int (*irq_nmi_setup)(struct irq_data *); void (*irq_nmi_teardown)(struct irq_data *); long unsigned int flags; }; struct irq_chip_regs { long unsigned int enable; long unsigned int disable; long unsigned int mask; long unsigned int ack; long unsigned int eoi; long unsigned int type; long unsigned int polarity; }; struct irq_chip_type { struct irq_chip chip; struct irq_chip_regs regs; irq_flow_handler_t handler; u32 type; u32 mask_cache_priv; u32 *mask_cache; }; struct irq_chip_generic { raw_spinlock_t lock; void *reg_base; u32 (*reg_readl)(void *); void (*reg_writel)(u32, void *); void (*suspend)(struct irq_chip_generic *); void (*resume)(struct irq_chip_generic *); unsigned int irq_base; unsigned int irq_cnt; u32 mask_cache; u32 type_cache; u32 polarity_cache; u32 wake_enabled; u32 wake_active; unsigned int num_ct; void *private; long unsigned int installed; long unsigned int unused; struct irq_domain *domain; struct list_head list; struct irq_chip_type chip_types[0]; }; enum irq_gc_flags { IRQ_GC_INIT_MASK_CACHE = 1, IRQ_GC_INIT_NESTED_LOCK = 2, IRQ_GC_MASK_CACHE_PER_TYPE = 4, IRQ_GC_NO_MASK = 8, IRQ_GC_BE_IO = 16, }; struct irq_domain_chip_generic { unsigned int irqs_per_chip; unsigned int num_chips; unsigned int irq_flags_to_clear; unsigned int irq_flags_to_set; enum irq_gc_flags gc_flags; struct irq_chip_generic *gc[0]; }; struct alt_instr { s32 instr_offset; s32 repl_offset; u16 cpuid; u8 instrlen; u8 replacementlen; }; struct timens_offset { s64 sec; u64 nsec; }; enum vm_fault_reason { VM_FAULT_OOM = 1, VM_FAULT_SIGBUS = 2, VM_FAULT_MAJOR = 4, VM_FAULT_WRITE = 8, VM_FAULT_HWPOISON = 16, VM_FAULT_HWPOISON_LARGE = 32, VM_FAULT_SIGSEGV = 64, VM_FAULT_NOPAGE = 256, VM_FAULT_LOCKED = 512, VM_FAULT_RETRY = 1024, VM_FAULT_FALLBACK = 2048, VM_FAULT_DONE_COW = 4096, VM_FAULT_NEEDDSYNC = 8192, VM_FAULT_HINDEX_MASK = 983040, }; struct vm_special_mapping { const char *name; struct page **pages; vm_fault_t (*fault)(const struct vm_special_mapping *, struct vm_area_struct *, struct vm_fault *); int (*mremap)(const struct vm_special_mapping *, struct vm_area_struct *); }; struct timens_offsets { struct timespec64 monotonic; struct timespec64 boottime; }; struct time_namespace { struct user_namespace *user_ns; struct ucounts *ucounts; struct ns_common ns; struct timens_offsets offsets; struct page *vvar_page; bool frozen_offsets; }; struct pvclock_vcpu_time_info { u32 version; u32 pad0; u64 tsc_timestamp; u64 system_time; u32 tsc_to_system_mul; s8 tsc_shift; u8 flags; u8 pad[2]; }; struct pvclock_vsyscall_time_info { struct pvclock_vcpu_time_info pvti; long: 64; long: 64; long: 64; long: 64; }; enum vdso_clock_mode { VDSO_CLOCKMODE_NONE = 0, VDSO_CLOCKMODE_TSC = 1, VDSO_CLOCKMODE_PVCLOCK = 2, VDSO_CLOCKMODE_HVCLOCK = 3, VDSO_CLOCKMODE_MAX = 4, VDSO_CLOCKMODE_TIMENS = 2147483647, }; struct arch_vdso_data {}; struct vdso_timestamp { u64 sec; u64 nsec; }; struct vdso_data { u32 seq; s32 clock_mode; u64 cycle_last; u64 mask; u32 mult; u32 shift; union { struct vdso_timestamp basetime[12]; struct timens_offset offset[12]; }; s32 tz_minuteswest; s32 tz_dsttime; u32 hrtimer_res; u32 __unused; struct arch_vdso_data arch_data; }; struct ms_hyperv_tsc_page { volatile u32 tsc_sequence; u32 reserved1; volatile u64 tsc_scale; volatile s64 tsc_offset; }; enum { TASKSTATS_CMD_UNSPEC = 0, TASKSTATS_CMD_GET = 1, TASKSTATS_CMD_NEW = 2, __TASKSTATS_CMD_MAX = 3, }; enum cpu_usage_stat { CPUTIME_USER = 0, CPUTIME_NICE = 1, CPUTIME_SYSTEM = 2, CPUTIME_SOFTIRQ = 3, CPUTIME_IRQ = 4, CPUTIME_IDLE = 5, CPUTIME_IOWAIT = 6, CPUTIME_STEAL = 7, CPUTIME_GUEST = 8, CPUTIME_GUEST_NICE = 9, NR_STATS = 10, }; enum bpf_cgroup_storage_type { BPF_CGROUP_STORAGE_SHARED = 0, BPF_CGROUP_STORAGE_PERCPU = 1, __BPF_CGROUP_STORAGE_MAX = 2, }; enum bpf_tramp_prog_type { BPF_TRAMP_FENTRY = 0, BPF_TRAMP_FEXIT = 1, BPF_TRAMP_MODIFY_RETURN = 2, BPF_TRAMP_MAX = 3, BPF_TRAMP_REPLACE = 4, }; enum psi_task_count { NR_IOWAIT = 0, NR_MEMSTALL = 1, NR_RUNNING = 2, NR_ONCPU = 3, NR_PSI_TASK_COUNTS = 4, }; enum psi_states { PSI_IO_SOME = 0, PSI_IO_FULL = 1, PSI_MEM_SOME = 2, PSI_MEM_FULL = 3, PSI_CPU_SOME = 4, PSI_CPU_FULL = 5, PSI_NONIDLE = 6, NR_PSI_STATES = 7, }; enum psi_aggregators { PSI_AVGS = 0, PSI_POLL = 1, NR_PSI_AGGREGATORS = 2, }; enum cgroup_subsys_id { cpuset_cgrp_id = 0, cpu_cgrp_id = 1, cpuacct_cgrp_id = 2, io_cgrp_id = 3, memory_cgrp_id = 4, devices_cgrp_id = 5, freezer_cgrp_id = 6, net_cls_cgrp_id = 7, perf_event_cgrp_id = 8, net_prio_cgrp_id = 9, hugetlb_cgrp_id = 10, pids_cgrp_id = 11, rdma_cgrp_id = 12, misc_cgrp_id = 13, CGROUP_SUBSYS_COUNT = 14, }; struct vdso_exception_table_entry { int insn; int fixup; }; struct cpuinfo_x86 { __u8 x86; __u8 x86_vendor; __u8 x86_model; __u8 x86_stepping; int x86_tlbsize; __u32 vmx_capability[3]; __u8 x86_virt_bits; __u8 x86_phys_bits; __u8 x86_coreid_bits; __u8 cu_id; __u32 extended_cpuid_level; int cpuid_level; union { __u32 x86_capability[21]; long unsigned int x86_capability_alignment; }; char x86_vendor_id[16]; char x86_model_id[64]; unsigned int x86_cache_size; int x86_cache_alignment; int x86_cache_max_rmid; int x86_cache_occ_scale; int x86_cache_mbm_width_offset; int x86_power; long unsigned int loops_per_jiffy; u16 x86_max_cores; u16 apicid; u16 initial_apicid; u16 x86_clflush_size; u16 booted_cores; u16 phys_proc_id; u16 logical_proc_id; u16 cpu_core_id; u16 cpu_die_id; u16 logical_die_id; u16 cpu_index; u32 microcode; u8 x86_cache_bits; unsigned int initialized: 1; }; enum syscall_work_bit { SYSCALL_WORK_BIT_SECCOMP = 0, SYSCALL_WORK_BIT_SYSCALL_TRACEPOINT = 1, SYSCALL_WORK_BIT_SYSCALL_TRACE = 2, SYSCALL_WORK_BIT_SYSCALL_EMU = 3, SYSCALL_WORK_BIT_SYSCALL_AUDIT = 4, SYSCALL_WORK_BIT_SYSCALL_USER_DISPATCH = 5, SYSCALL_WORK_BIT_SYSCALL_EXIT_TRAP = 6, }; struct seccomp_data { int nr; __u32 arch; __u64 instruction_pointer; __u64 args[6]; }; enum x86_pf_error_code { X86_PF_PROT = 1, X86_PF_WRITE = 2, X86_PF_USER = 4, X86_PF_RSVD = 8, X86_PF_INSTR = 16, X86_PF_PK = 32, X86_PF_SGX = 32768, }; struct trace_event_raw_emulate_vsyscall { struct trace_entry ent; int nr; char __data[0]; }; struct trace_event_data_offsets_emulate_vsyscall {}; typedef void (*btf_trace_emulate_vsyscall)(void *, int); enum { EMULATE = 0, XONLY = 1, NONE = 2, }; enum perf_type_id { PERF_TYPE_HARDWARE = 0, PERF_TYPE_SOFTWARE = 1, PERF_TYPE_TRACEPOINT = 2, PERF_TYPE_HW_CACHE = 3, PERF_TYPE_RAW = 4, PERF_TYPE_BREAKPOINT = 5, PERF_TYPE_MAX = 6, }; enum perf_hw_id { PERF_COUNT_HW_CPU_CYCLES = 0, PERF_COUNT_HW_INSTRUCTIONS = 1, PERF_COUNT_HW_CACHE_REFERENCES = 2, PERF_COUNT_HW_CACHE_MISSES = 3, PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, PERF_COUNT_HW_BRANCH_MISSES = 5, PERF_COUNT_HW_BUS_CYCLES = 6, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, PERF_COUNT_HW_REF_CPU_CYCLES = 9, PERF_COUNT_HW_MAX = 10, }; enum perf_hw_cache_id { PERF_COUNT_HW_CACHE_L1D = 0, PERF_COUNT_HW_CACHE_L1I = 1, PERF_COUNT_HW_CACHE_LL = 2, PERF_COUNT_HW_CACHE_DTLB = 3, PERF_COUNT_HW_CACHE_ITLB = 4, PERF_COUNT_HW_CACHE_BPU = 5, PERF_COUNT_HW_CACHE_NODE = 6, PERF_COUNT_HW_CACHE_MAX = 7, }; enum perf_hw_cache_op_id { PERF_COUNT_HW_CACHE_OP_READ = 0, PERF_COUNT_HW_CACHE_OP_WRITE = 1, PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, PERF_COUNT_HW_CACHE_OP_MAX = 3, }; enum perf_hw_cache_op_result_id { PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, PERF_COUNT_HW_CACHE_RESULT_MISS = 1, PERF_COUNT_HW_CACHE_RESULT_MAX = 2, }; enum perf_event_sample_format { PERF_SAMPLE_IP = 1, PERF_SAMPLE_TID = 2, PERF_SAMPLE_TIME = 4, PERF_SAMPLE_ADDR = 8, PERF_SAMPLE_READ = 16, PERF_SAMPLE_CALLCHAIN = 32, PERF_SAMPLE_ID = 64, PERF_SAMPLE_CPU = 128, PERF_SAMPLE_PERIOD = 256, PERF_SAMPLE_STREAM_ID = 512, PERF_SAMPLE_RAW = 1024, PERF_SAMPLE_BRANCH_STACK = 2048, PERF_SAMPLE_REGS_USER = 4096, PERF_SAMPLE_STACK_USER = 8192, PERF_SAMPLE_WEIGHT = 16384, PERF_SAMPLE_DATA_SRC = 32768, PERF_SAMPLE_IDENTIFIER = 65536, PERF_SAMPLE_TRANSACTION = 131072, PERF_SAMPLE_REGS_INTR = 262144, PERF_SAMPLE_PHYS_ADDR = 524288, PERF_SAMPLE_AUX = 1048576, PERF_SAMPLE_CGROUP = 2097152, PERF_SAMPLE_DATA_PAGE_SIZE = 4194304, PERF_SAMPLE_CODE_PAGE_SIZE = 8388608, PERF_SAMPLE_WEIGHT_STRUCT = 16777216, PERF_SAMPLE_MAX = 33554432, __PERF_SAMPLE_CALLCHAIN_EARLY = 0, }; enum perf_branch_sample_type { PERF_SAMPLE_BRANCH_USER = 1, PERF_SAMPLE_BRANCH_KERNEL = 2, PERF_SAMPLE_BRANCH_HV = 4, PERF_SAMPLE_BRANCH_ANY = 8, PERF_SAMPLE_BRANCH_ANY_CALL = 16, PERF_SAMPLE_BRANCH_ANY_RETURN = 32, PERF_SAMPLE_BRANCH_IND_CALL = 64, PERF_SAMPLE_BRANCH_ABORT_TX = 128, PERF_SAMPLE_BRANCH_IN_TX = 256, PERF_SAMPLE_BRANCH_NO_TX = 512, PERF_SAMPLE_BRANCH_COND = 1024, PERF_SAMPLE_BRANCH_CALL_STACK = 2048, PERF_SAMPLE_BRANCH_IND_JUMP = 4096, PERF_SAMPLE_BRANCH_CALL = 8192, PERF_SAMPLE_BRANCH_NO_FLAGS = 16384, PERF_SAMPLE_BRANCH_NO_CYCLES = 32768, PERF_SAMPLE_BRANCH_TYPE_SAVE = 65536, PERF_SAMPLE_BRANCH_HW_INDEX = 131072, PERF_SAMPLE_BRANCH_MAX = 262144, }; struct perf_event_mmap_page { __u32 version; __u32 compat_version; __u32 lock; __u32 index; __s64 offset; __u64 time_enabled; __u64 time_running; union { __u64 capabilities; struct { __u64 cap_bit0: 1; __u64 cap_bit0_is_deprecated: 1; __u64 cap_user_rdpmc: 1; __u64 cap_user_time: 1; __u64 cap_user_time_zero: 1; __u64 cap_user_time_short: 1; __u64 cap_____res: 58; }; }; __u16 pmc_width; __u16 time_shift; __u32 time_mult; __u64 time_offset; __u64 time_zero; __u32 size; __u32 __reserved_1; __u64 time_cycles; __u64 time_mask; __u8 __reserved[928]; __u64 data_head; __u64 data_tail; __u64 data_offset; __u64 data_size; __u64 aux_head; __u64 aux_tail; __u64 aux_offset; __u64 aux_size; }; struct pv_info { u16 extra_user_64bit_cs; const char *name; }; typedef bool (*smp_cond_func_t)(int, void *); struct ldt_struct { struct desc_struct *entries; unsigned int nr_entries; int slot; }; enum apic_delivery_modes { APIC_DELIVERY_MODE_FIXED = 0, APIC_DELIVERY_MODE_LOWESTPRIO = 1, APIC_DELIVERY_MODE_SMI = 2, APIC_DELIVERY_MODE_NMI = 4, APIC_DELIVERY_MODE_INIT = 5, APIC_DELIVERY_MODE_EXTINT = 7, }; struct physid_mask { long unsigned int mask[512]; }; typedef struct physid_mask physid_mask_t; struct x86_pmu_capability { int version; int num_counters_gp; int num_counters_fixed; int bit_width_gp; int bit_width_fixed; unsigned int events_mask; int events_mask_len; }; struct debug_store { u64 bts_buffer_base; u64 bts_index; u64 bts_absolute_maximum; u64 bts_interrupt_threshold; u64 pebs_buffer_base; u64 pebs_index; u64 pebs_absolute_maximum; u64 pebs_interrupt_threshold; u64 pebs_event_reset[12]; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; enum stack_type { STACK_TYPE_UNKNOWN = 0, STACK_TYPE_TASK = 1, STACK_TYPE_IRQ = 2, STACK_TYPE_SOFTIRQ = 3, STACK_TYPE_ENTRY = 4, STACK_TYPE_EXCEPTION = 5, STACK_TYPE_EXCEPTION_LAST = 10, }; struct stack_info { enum stack_type type; long unsigned int *begin; long unsigned int *end; long unsigned int *next_sp; }; struct stack_frame { struct stack_frame *next_frame; long unsigned int return_address; }; struct stack_frame_ia32 { u32 next_frame; u32 return_address; }; struct perf_guest_switch_msr { unsigned int msr; u64 host; u64 guest; }; struct perf_guest_info_callbacks { int (*is_in_guest)(); int (*is_user_mode)(); long unsigned int (*get_guest_ip)(); void (*handle_intel_pt_intr)(); }; struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *, struct device_attribute *, char *); ssize_t (*store)(struct device *, struct device_attribute *, const char *, size_t); }; enum perf_event_x86_regs { PERF_REG_X86_AX = 0, PERF_REG_X86_BX = 1, PERF_REG_X86_CX = 2, PERF_REG_X86_DX = 3, PERF_REG_X86_SI = 4, PERF_REG_X86_DI = 5, PERF_REG_X86_BP = 6, PERF_REG_X86_SP = 7, PERF_REG_X86_IP = 8, PERF_REG_X86_FLAGS = 9, PERF_REG_X86_CS = 10, PERF_REG_X86_SS = 11, PERF_REG_X86_DS = 12, PERF_REG_X86_ES = 13, PERF_REG_X86_FS = 14, PERF_REG_X86_GS = 15, PERF_REG_X86_R8 = 16, PERF_REG_X86_R9 = 17, PERF_REG_X86_R10 = 18, PERF_REG_X86_R11 = 19, PERF_REG_X86_R12 = 20, PERF_REG_X86_R13 = 21, PERF_REG_X86_R14 = 22, PERF_REG_X86_R15 = 23, PERF_REG_X86_32_MAX = 16, PERF_REG_X86_64_MAX = 24, PERF_REG_X86_XMM0 = 32, PERF_REG_X86_XMM1 = 34, PERF_REG_X86_XMM2 = 36, PERF_REG_X86_XMM3 = 38, PERF_REG_X86_XMM4 = 40, PERF_REG_X86_XMM5 = 42, PERF_REG_X86_XMM6 = 44, PERF_REG_X86_XMM7 = 46, PERF_REG_X86_XMM8 = 48, PERF_REG_X86_XMM9 = 50, PERF_REG_X86_XMM10 = 52, PERF_REG_X86_XMM11 = 54, PERF_REG_X86_XMM12 = 56, PERF_REG_X86_XMM13 = 58, PERF_REG_X86_XMM14 = 60, PERF_REG_X86_XMM15 = 62, PERF_REG_X86_XMM_MAX = 64, }; struct perf_callchain_entry_ctx { struct perf_callchain_entry *entry; u32 max_stack; u32 nr; short int contexts; bool contexts_maxed; }; struct perf_pmu_events_attr { struct device_attribute attr; u64 id; const char *event_str; }; struct perf_pmu_events_ht_attr { struct device_attribute attr; u64 id; const char *event_str_ht; const char *event_str_noht; }; struct perf_pmu_events_hybrid_attr { struct device_attribute attr; u64 id; const char *event_str; u64 pmu_type; }; struct apic { void (*eoi_write)(u32, u32); void (*native_eoi_write)(u32, u32); void (*write)(u32, u32); u32 (*read)(u32); void (*wait_icr_idle)(); u32 (*safe_wait_icr_idle)(); void (*send_IPI)(int, int); void (*send_IPI_mask)(const struct cpumask *, int); void (*send_IPI_mask_allbutself)(const struct cpumask *, int); void (*send_IPI_allbutself)(int); void (*send_IPI_all)(int); void (*send_IPI_self)(int); u32 disable_esr; enum apic_delivery_modes delivery_mode; bool dest_mode_logical; u32 (*calc_dest_apicid)(unsigned int); u64 (*icr_read)(); void (*icr_write)(u32, u32); int (*probe)(); int (*acpi_madt_oem_check)(char *, char *); int (*apic_id_valid)(u32); int (*apic_id_registered)(); bool (*check_apicid_used)(physid_mask_t *, int); void (*init_apic_ldr)(); void (*ioapic_phys_id_map)(physid_mask_t *, physid_mask_t *); void (*setup_apic_routing)(); int (*cpu_present_to_apicid)(int); void (*apicid_to_cpu_present)(int, physid_mask_t *); int (*check_phys_apicid_present)(int); int (*phys_pkg_id)(int, int); u32 (*get_apic_id)(long unsigned int); u32 (*set_apic_id)(unsigned int); int (*wakeup_secondary_cpu)(int, long unsigned int); void (*inquire_remote_apic)(int); char *name; }; enum { NMI_LOCAL = 0, NMI_UNKNOWN = 1, NMI_SERR = 2, NMI_IO_CHECK = 3, NMI_MAX = 4, }; typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); struct nmiaction { struct list_head list; nmi_handler_t handler; u64 max_duration; long unsigned int flags; const char *name; }; struct gdt_page { struct desc_struct gdt[16]; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct cyc2ns_data { u32 cyc2ns_mul; u32 cyc2ns_shift; u64 cyc2ns_offset; }; struct unwind_state { struct stack_info stack_info; long unsigned int stack_mask; struct task_struct *task; int graph_idx; bool error; bool signal; bool full_regs; long unsigned int sp; long unsigned int bp; long unsigned int ip; struct pt_regs *regs; struct pt_regs *prev_regs; }; enum extra_reg_type { EXTRA_REG_NONE = 4294967295, EXTRA_REG_RSP_0 = 0, EXTRA_REG_RSP_1 = 1, EXTRA_REG_LBR = 2, EXTRA_REG_LDLAT = 3, EXTRA_REG_FE = 4, EXTRA_REG_MAX = 5, }; struct event_constraint { union { long unsigned int idxmsk[1]; u64 idxmsk64; }; u64 code; u64 cmask; int weight; int overlap; int flags; unsigned int size; }; struct amd_nb { int nb_id; int refcnt; struct perf_event *owners[64]; struct event_constraint event_constraints[64]; }; struct er_account { raw_spinlock_t lock; u64 config; u64 reg; atomic_t ref; }; struct intel_shared_regs { struct er_account regs[5]; int refcnt; unsigned int core_id; }; enum intel_excl_state_type { INTEL_EXCL_UNUSED = 0, INTEL_EXCL_SHARED = 1, INTEL_EXCL_EXCLUSIVE = 2, }; struct intel_excl_states { enum intel_excl_state_type state[64]; bool sched_started; }; struct intel_excl_cntrs { raw_spinlock_t lock; struct intel_excl_states states[2]; union { u16 has_exclusive[2]; u32 exclusive_present; }; int refcnt; unsigned int core_id; }; enum { X86_PERF_KFREE_SHARED = 0, X86_PERF_KFREE_EXCL = 1, X86_PERF_KFREE_MAX = 2, }; struct cpu_hw_events { struct perf_event *events[64]; long unsigned int active_mask[1]; long unsigned int dirty[1]; int enabled; int n_events; int n_added; int n_txn; int n_txn_pair; int n_txn_metric; int assign[64]; u64 tags[64]; struct perf_event *event_list[64]; struct event_constraint *event_constraint[64]; int n_excl; unsigned int txn_flags; int is_fake; struct debug_store *ds; void *ds_pebs_vaddr; void *ds_bts_vaddr; u64 pebs_enabled; int n_pebs; int n_large_pebs; int n_pebs_via_pt; int pebs_output; u64 pebs_data_cfg; u64 active_pebs_data_cfg; int pebs_record_size; int lbr_users; int lbr_pebs_users; struct perf_branch_stack lbr_stack; struct perf_branch_entry lbr_entries[32]; union { struct er_account *lbr_sel; struct er_account *lbr_ctl; }; u64 br_sel; void *last_task_ctx; int last_log_id; int lbr_select; void *lbr_xsave; u64 intel_ctrl_guest_mask; u64 intel_ctrl_host_mask; struct perf_guest_switch_msr guest_switch_msrs[64]; u64 intel_cp_status; struct intel_shared_regs *shared_regs; struct event_constraint *constraint_list; struct intel_excl_cntrs *excl_cntrs; int excl_thread_id; u64 tfa_shadow; int n_metric; struct amd_nb *amd_nb; u64 perf_ctr_virt_mask; int n_pair; void *kfree_on_online[2]; struct pmu *pmu; }; struct extra_reg { unsigned int event; unsigned int msr; u64 config_mask; u64 valid_mask; int idx; bool extra_msr_access; }; union perf_capabilities { struct { u64 lbr_format: 6; u64 pebs_trap: 1; u64 pebs_arch_reg: 1; u64 pebs_format: 4; u64 smm_freeze: 1; u64 full_width_write: 1; u64 pebs_baseline: 1; u64 perf_metrics: 1; u64 pebs_output_pt_available: 1; u64 anythread_deprecated: 1; }; u64 capabilities; }; struct x86_pmu_quirk { struct x86_pmu_quirk *next; void (*func)(); }; enum { x86_lbr_exclusive_lbr = 0, x86_lbr_exclusive_bts = 1, x86_lbr_exclusive_pt = 2, x86_lbr_exclusive_max = 3, }; struct x86_hybrid_pmu { struct pmu pmu; const char *name; u8 cpu_type; cpumask_t supported_cpus; union perf_capabilities intel_cap; u64 intel_ctrl; int max_pebs_events; int num_counters; int num_counters_fixed; struct event_constraint unconstrained; u64 hw_cache_event_ids[42]; u64 hw_cache_extra_regs[42]; struct event_constraint *event_constraints; struct event_constraint *pebs_constraints; struct extra_reg *extra_regs; unsigned int late_ack: 1; unsigned int mid_ack: 1; unsigned int enabled_ack: 1; }; enum hybrid_pmu_type { hybrid_big = 64, hybrid_small = 32, hybrid_big_small = 96, }; struct x86_pmu { const char *name; int version; int (*handle_irq)(struct pt_regs *); void (*disable_all)(); void (*enable_all)(int); void (*enable)(struct perf_event *); void (*disable)(struct perf_event *); void (*add)(struct perf_event *); void (*del)(struct perf_event *); void (*read)(struct perf_event *); int (*hw_config)(struct perf_event *); int (*schedule_events)(struct cpu_hw_events *, int, int *); unsigned int eventsel; unsigned int perfctr; int (*addr_offset)(int, bool); int (*rdpmc_index)(int); u64 (*event_map)(int); int max_events; int num_counters; int num_counters_fixed; int cntval_bits; u64 cntval_mask; union { long unsigned int events_maskl; long unsigned int events_mask[1]; }; int events_mask_len; int apic; u64 max_period; struct event_constraint * (*get_event_constraints)(struct cpu_hw_events *, int, struct perf_event *); void (*put_event_constraints)(struct cpu_hw_events *, struct perf_event *); void (*start_scheduling)(struct cpu_hw_events *); void (*commit_scheduling)(struct cpu_hw_events *, int, int); void (*stop_scheduling)(struct cpu_hw_events *); struct event_constraint *event_constraints; struct x86_pmu_quirk *quirks; int perfctr_second_write; u64 (*limit_period)(struct perf_event *, u64); unsigned int late_ack: 1; unsigned int mid_ack: 1; unsigned int enabled_ack: 1; int attr_rdpmc_broken; int attr_rdpmc; struct attribute **format_attrs; ssize_t (*events_sysfs_show)(char *, u64); const struct attribute_group **attr_update; long unsigned int attr_freeze_on_smi; int (*cpu_prepare)(int); void (*cpu_starting)(int); void (*cpu_dying)(int); void (*cpu_dead)(int); void (*check_microcode)(); void (*sched_task)(struct perf_event_context *, bool); u64 intel_ctrl; union perf_capabilities intel_cap; unsigned int bts: 1; unsigned int bts_active: 1; unsigned int pebs: 1; unsigned int pebs_active: 1; unsigned int pebs_broken: 1; unsigned int pebs_prec_dist: 1; unsigned int pebs_no_tlb: 1; unsigned int pebs_no_isolation: 1; unsigned int pebs_block: 1; int pebs_record_size; int pebs_buffer_size; int max_pebs_events; void (*drain_pebs)(struct pt_regs *, struct perf_sample_data *); struct event_constraint *pebs_constraints; void (*pebs_aliases)(struct perf_event *); long unsigned int large_pebs_flags; u64 rtm_abort_event; unsigned int lbr_tos; unsigned int lbr_from; unsigned int lbr_to; unsigned int lbr_info; unsigned int lbr_nr; union { u64 lbr_sel_mask; u64 lbr_ctl_mask; }; union { const int *lbr_sel_map; int *lbr_ctl_map; }; bool lbr_double_abort; bool lbr_pt_coexist; unsigned int lbr_depth_mask: 8; unsigned int lbr_deep_c_reset: 1; unsigned int lbr_lip: 1; unsigned int lbr_cpl: 1; unsigned int lbr_filter: 1; unsigned int lbr_call_stack: 1; unsigned int lbr_mispred: 1; unsigned int lbr_timed_lbr: 1; unsigned int lbr_br_type: 1; void (*lbr_reset)(); void (*lbr_read)(struct cpu_hw_events *); void (*lbr_save)(void *); void (*lbr_restore)(void *); atomic_t lbr_exclusive[3]; int num_topdown_events; u64 (*update_topdown_event)(struct perf_event *); int (*set_topdown_event_period)(struct perf_event *); void (*swap_task_ctx)(struct perf_event_context *, struct perf_event_context *); unsigned int amd_nb_constraints: 1; u64 perf_ctr_pair_en; struct extra_reg *extra_regs; unsigned int flags; struct perf_guest_switch_msr * (*guest_get_msrs)(int *); int (*check_period)(struct perf_event *, u64); int (*aux_output_match)(struct perf_event *); int (*filter_match)(struct perf_event *); int num_hybrid_pmus; struct x86_hybrid_pmu *hybrid_pmu; u8 (*get_hybrid_cpu_type)(); }; struct sched_state { int weight; int event; int counter; int unassigned; int nr_gp; u64 used; }; struct perf_sched { int max_weight; int max_events; int max_gp; int saved_states; struct event_constraint **constraints; struct sched_state state; struct sched_state saved[2]; }; struct perf_msr { u64 msr; struct attribute_group *grp; bool (*test)(int, void *); bool no_check; u64 mask; }; struct amd_uncore { int id; int refcnt; int cpu; int num_counters; int rdpmc_base; u32 msr_base; cpumask_t *active_mask; struct pmu *pmu; struct perf_event *events[6]; struct hlist_node node; }; typedef int pci_power_t; typedef unsigned int pci_channel_state_t; typedef short unsigned int pci_dev_flags_t; struct pci_bus; struct pci_slot; struct aer_stats; struct rcec_ea; struct pci_driver; struct pcie_link_state; struct pci_vpd; struct pci_sriov; struct pci_p2pdma; struct pci_dev { struct list_head bus_list; struct pci_bus *bus; struct pci_bus *subordinate; void *sysdata; struct proc_dir_entry *procent; struct pci_slot *slot; unsigned int devfn; short unsigned int vendor; short unsigned int device; short unsigned int subsystem_vendor; short unsigned int subsystem_device; unsigned int class; u8 revision; u8 hdr_type; u16 aer_cap; struct aer_stats *aer_stats; struct rcec_ea *rcec_ea; struct pci_dev *rcec; u8 pcie_cap; u8 msi_cap; u8 msix_cap; u8 pcie_mpss: 3; u8 rom_base_reg; u8 pin; u16 pcie_flags_reg; long unsigned int *dma_alias_mask; struct pci_driver *driver; u64 dma_mask; struct device_dma_parameters dma_parms; pci_power_t current_state; unsigned int imm_ready: 1; u8 pm_cap; unsigned int pme_support: 5; unsigned int pme_poll: 1; unsigned int d1_support: 1; unsigned int d2_support: 1; unsigned int no_d1d2: 1; unsigned int no_d3cold: 1; unsigned int bridge_d3: 1; unsigned int d3cold_allowed: 1; unsigned int mmio_always_on: 1; unsigned int wakeup_prepared: 1; unsigned int runtime_d3cold: 1; unsigned int skip_bus_pm: 1; unsigned int ignore_hotplug: 1; unsigned int hotplug_user_indicators: 1; unsigned int clear_retrain_link: 1; unsigned int d3hot_delay; unsigned int d3cold_delay; struct pcie_link_state *link_state; unsigned int ltr_path: 1; u16 l1ss; unsigned int eetlp_prefix_path: 1; pci_channel_state_t error_state; struct device dev; int cfg_size; unsigned int irq; struct resource resource[17]; bool match_driver; unsigned int transparent: 1; unsigned int io_window: 1; unsigned int pref_window: 1; unsigned int pref_64_window: 1; unsigned int multifunction: 1; unsigned int is_busmaster: 1; unsigned int no_msi: 1; unsigned int no_64bit_msi: 1; unsigned int block_cfg_access: 1; unsigned int broken_parity_status: 1; unsigned int irq_reroute_variant: 2; unsigned int msi_enabled: 1; unsigned int msix_enabled: 1; unsigned int ari_enabled: 1; unsigned int ats_enabled: 1; unsigned int pasid_enabled: 1; unsigned int pri_enabled: 1; unsigned int is_managed: 1; unsigned int needs_freset: 1; unsigned int state_saved: 1; unsigned int is_physfn: 1; unsigned int is_virtfn: 1; unsigned int reset_fn: 1; unsigned int is_hotplug_bridge: 1; unsigned int shpc_managed: 1; unsigned int is_thunderbolt: 1; unsigned int untrusted: 1; unsigned int external_facing: 1; unsigned int broken_intx_masking: 1; unsigned int io_window_1k: 1; unsigned int irq_managed: 1; unsigned int non_compliant_bars: 1; unsigned int is_probed: 1; unsigned int link_active_reporting: 1; unsigned int no_vf_scan: 1; unsigned int no_command_memory: 1; pci_dev_flags_t dev_flags; atomic_t enable_cnt; u32 saved_config_space[16]; struct hlist_head saved_cap_space; int rom_attr_enabled; struct bin_attribute *res_attr[17]; struct bin_attribute *res_attr_wc[17]; unsigned int broken_cmd_compl: 1; unsigned int ptm_root: 1; unsigned int ptm_enabled: 1; u8 ptm_granularity; const struct attribute_group **msi_irq_groups; struct pci_vpd *vpd; u16 dpc_cap; unsigned int dpc_rp_extensions: 1; u8 dpc_rp_log_size; union { struct pci_sriov *sriov; struct pci_dev *physfn; }; u16 ats_cap; u8 ats_stu; u16 pri_cap; u32 pri_reqs_alloc; unsigned int pasid_required: 1; u16 pasid_cap; u16 pasid_features; struct pci_p2pdma *p2pdma; u16 acs_cap; phys_addr_t rom; size_t romlen; char *driver_override; long unsigned int priv_flags; }; struct pci_device_id { __u32 vendor; __u32 device; __u32 subvendor; __u32 subdevice; __u32 class; __u32 class_mask; kernel_ulong_t driver_data; }; struct hotplug_slot; struct pci_slot { struct pci_bus *bus; struct list_head list; struct hotplug_slot *hotplug; unsigned char number; struct kobject kobj; }; typedef short unsigned int pci_bus_flags_t; struct pci_ops; struct pci_bus { struct list_head node; struct pci_bus *parent; struct list_head children; struct list_head devices; struct pci_dev *self; struct list_head slots; struct resource *resource[4]; struct list_head resources; struct resource busn_res; struct pci_ops *ops; void *sysdata; struct proc_dir_entry *procdir; unsigned char number; unsigned char primary; unsigned char max_bus_speed; unsigned char cur_bus_speed; char name[48]; short unsigned int bridge_ctl; pci_bus_flags_t bus_flags; struct device *bridge; struct device dev; struct bin_attribute *legacy_io; struct bin_attribute *legacy_mem; unsigned int is_added: 1; }; enum { PCI_STD_RESOURCES = 0, PCI_STD_RESOURCE_END = 5, PCI_ROM_RESOURCE = 6, PCI_IOV_RESOURCES = 7, PCI_IOV_RESOURCE_END = 12, PCI_BRIDGE_RESOURCES = 13, PCI_BRIDGE_RESOURCE_END = 16, PCI_NUM_RESOURCES = 17, DEVICE_COUNT_RESOURCE = 17, }; typedef unsigned int pcie_reset_state_t; struct pci_dynids { spinlock_t lock; struct list_head list; }; struct pci_error_handlers; struct pci_driver { struct list_head node; const char *name; const struct pci_device_id *id_table; int (*probe)(struct pci_dev *, const struct pci_device_id *); void (*remove)(struct pci_dev *); int (*suspend)(struct pci_dev *, pm_message_t); int (*resume)(struct pci_dev *); void (*shutdown)(struct pci_dev *); int (*sriov_configure)(struct pci_dev *, int); int (*sriov_set_msix_vec_count)(struct pci_dev *, int); u32 (*sriov_get_vf_total_msix)(struct pci_dev *); const struct pci_error_handlers *err_handler; const struct attribute_group **groups; const struct attribute_group **dev_groups; struct device_driver driver; struct pci_dynids dynids; }; struct pci_ops { int (*add_bus)(struct pci_bus *); void (*remove_bus)(struct pci_bus *); void * (*map_bus)(struct pci_bus *, unsigned int, int); int (*read)(struct pci_bus *, unsigned int, int, int, u32 *); int (*write)(struct pci_bus *, unsigned int, int, int, u32); }; typedef unsigned int pci_ers_result_t; struct pci_error_handlers { pci_ers_result_t (*error_detected)(struct pci_dev *, pci_channel_state_t); pci_ers_result_t (*mmio_enabled)(struct pci_dev *); pci_ers_result_t (*slot_reset)(struct pci_dev *); void (*reset_prepare)(struct pci_dev *); void (*reset_done)(struct pci_dev *); void (*resume)(struct pci_dev *); }; struct syscore_ops { struct list_head node; int (*suspend)(); void (*resume)(); void (*shutdown)(); }; enum ibs_states { IBS_ENABLED = 0, IBS_STARTED = 1, IBS_STOPPING = 2, IBS_STOPPED = 3, IBS_MAX_STATES = 4, }; struct cpu_perf_ibs { struct perf_event *event; long unsigned int state[1]; }; struct perf_ibs { struct pmu pmu; unsigned int msr; u64 config_mask; u64 cnt_mask; u64 enable_mask; u64 valid_mask; u64 max_period; long unsigned int offset_mask[1]; int offset_max; unsigned int fetch_count_reset_broken: 1; unsigned int fetch_ignore_if_zero_rip: 1; struct cpu_perf_ibs *pcpu; struct attribute **format_attrs; struct attribute_group format_group; const struct attribute_group *attr_groups[2]; u64 (*get_count)(u64); }; struct perf_ibs_data { u32 size; union { u32 data[0]; u32 caps; }; u64 regs[8]; }; struct amd_iommu; struct perf_amd_iommu { struct list_head list; struct pmu pmu; struct amd_iommu *iommu; char name[16]; u8 max_banks; u8 max_counters; u64 cntr_assign_mask; raw_spinlock_t lock; }; struct amd_iommu_event_desc { struct device_attribute attr; const char *event; }; enum perf_msr_id { PERF_MSR_TSC = 0, PERF_MSR_APERF = 1, PERF_MSR_MPERF = 2, PERF_MSR_PPERF = 3, PERF_MSR_SMI = 4, PERF_MSR_PTSC = 5, PERF_MSR_IRPERF = 6, PERF_MSR_THERM = 7, PERF_MSR_EVENT_MAX = 8, }; struct x86_cpu_desc { u8 x86_family; u8 x86_vendor; u8 x86_model; u8 x86_stepping; u32 x86_microcode_rev; }; union cpuid10_eax { struct { unsigned int version_id: 8; unsigned int num_counters: 8; unsigned int bit_width: 8; unsigned int mask_length: 8; } split; unsigned int full; }; union cpuid10_ebx { struct { unsigned int no_unhalted_core_cycles: 1; unsigned int no_instructions_retired: 1; unsigned int no_unhalted_reference_cycles: 1; unsigned int no_llc_reference: 1; unsigned int no_llc_misses: 1; unsigned int no_branch_instruction_retired: 1; unsigned int no_branch_misses_retired: 1; } split; unsigned int full; }; union cpuid10_edx { struct { unsigned int num_counters_fixed: 5; unsigned int bit_width_fixed: 8; unsigned int reserved1: 2; unsigned int anythread_deprecated: 1; unsigned int reserved2: 16; } split; unsigned int full; }; struct perf_pmu_format_hybrid_attr { struct device_attribute attr; u64 pmu_type; }; enum { LBR_FORMAT_32 = 0, LBR_FORMAT_LIP = 1, LBR_FORMAT_EIP = 2, LBR_FORMAT_EIP_FLAGS = 3, LBR_FORMAT_EIP_FLAGS2 = 4, LBR_FORMAT_INFO = 5, LBR_FORMAT_TIME = 6, LBR_FORMAT_MAX_KNOWN = 6, }; union x86_pmu_config { struct { u64 event: 8; u64 umask: 8; u64 usr: 1; u64 os: 1; u64 edge: 1; u64 pc: 1; u64 interrupt: 1; u64 __reserved1: 1; u64 en: 1; u64 inv: 1; u64 cmask: 8; u64 event2: 4; u64 __reserved2: 4; u64 go: 1; u64 ho: 1; } bits; u64 value; }; enum pageflags { PG_locked = 0, PG_referenced = 1, PG_uptodate = 2, PG_dirty = 3, PG_lru = 4, PG_active = 5, PG_workingset = 6, PG_waiters = 7, PG_error = 8, PG_slab = 9, PG_owner_priv_1 = 10, PG_arch_1 = 11, PG_reserved = 12, PG_private = 13, PG_private_2 = 14, PG_writeback = 15, PG_head = 16, PG_mappedtodisk = 17, PG_reclaim = 18, PG_swapbacked = 19, PG_unevictable = 20, PG_mlocked = 21, PG_uncached = 22, PG_hwpoison = 23, PG_young = 24, PG_idle = 25, PG_arch_2 = 26, __NR_PAGEFLAGS = 27, PG_checked = 10, PG_swapcache = 10, PG_fscache = 14, PG_pinned = 10, PG_savepinned = 3, PG_foreign = 10, PG_xen_remapped = 10, PG_slob_free = 13, PG_double_map = 6, PG_isolated = 18, PG_reported = 2, }; struct bts_ctx { struct perf_output_handle handle; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; struct debug_store ds_back; int state; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; enum { BTS_STATE_STOPPED = 0, BTS_STATE_INACTIVE = 1, BTS_STATE_ACTIVE = 2, }; struct bts_phys { struct page *page; long unsigned int size; long unsigned int offset; long unsigned int displacement; }; struct bts_buffer { size_t real_size; unsigned int nr_pages; unsigned int nr_bufs; unsigned int cur_buf; bool snapshot; local_t data_size; local_t head; long unsigned int end; void **data_pages; struct bts_phys buf[0]; }; struct lbr_entry { u64 from; u64 to; u64 info; }; struct x86_hw_tss { u32 reserved1; u64 sp0; u64 sp1; u64 sp2; u64 reserved2; u64 ist[7]; u32 reserved3; u32 reserved4; u16 reserved5; u16 io_bitmap_base; } __attribute__((packed)); struct entry_stack { char stack[4096]; }; struct entry_stack_page { struct entry_stack stack; }; struct x86_io_bitmap { u64 prev_sequence; unsigned int prev_max; long unsigned int bitmap[1025]; long unsigned int mapall[1025]; }; struct tss_struct { struct x86_hw_tss x86_tss; struct x86_io_bitmap io_bitmap; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct debug_store_buffers { char bts_buffer[65536]; char pebs_buffer[65536]; }; struct cea_exception_stacks { char DF_stack_guard[4096]; char DF_stack[4096]; char NMI_stack_guard[4096]; char NMI_stack[4096]; char DB_stack_guard[4096]; char DB_stack[4096]; char MCE_stack_guard[4096]; char MCE_stack[4096]; char VC_stack_guard[4096]; char VC_stack[4096]; char VC2_stack_guard[4096]; char VC2_stack[4096]; char IST_top_guard[4096]; }; struct cpu_entry_area { char gdt[4096]; struct entry_stack_page entry_stack_page; struct tss_struct tss; struct cea_exception_stacks estacks; struct debug_store cpu_debug_store; struct debug_store_buffers cpu_debug_buffers; }; struct pebs_basic { u64 format_size; u64 ip; u64 applicable_counters; u64 tsc; }; struct pebs_meminfo { u64 address; u64 aux; u64 latency; u64 tsx_tuning; }; struct pebs_gprs { u64 flags; u64 ip; u64 ax; u64 cx; u64 dx; u64 bx; u64 sp; u64 bp; u64 si; u64 di; u64 r8; u64 r9; u64 r10; u64 r11; u64 r12; u64 r13; u64 r14; u64 r15; }; struct pebs_xmm { u64 xmm[32]; }; struct x86_perf_regs { struct pt_regs regs; u64 *xmm_regs; }; typedef unsigned int insn_attr_t; typedef unsigned char insn_byte_t; typedef int insn_value_t; struct insn_field { union { insn_value_t value; insn_byte_t bytes[4]; }; unsigned char got; unsigned char nbytes; }; struct insn { struct insn_field prefixes; struct insn_field rex_prefix; struct insn_field vex_prefix; struct insn_field opcode; struct insn_field modrm; struct insn_field sib; struct insn_field displacement; union { struct insn_field immediate; struct insn_field moffset1; struct insn_field immediate1; }; union { struct insn_field moffset2; struct insn_field immediate2; }; int emulate_prefix_size; insn_attr_t attr; unsigned char opnd_bytes; unsigned char addr_bytes; unsigned char length; unsigned char x86_64; const insn_byte_t *kaddr; const insn_byte_t *end_kaddr; const insn_byte_t *next_byte; }; enum { PERF_TXN_ELISION = 1, PERF_TXN_TRANSACTION = 2, PERF_TXN_SYNC = 4, PERF_TXN_ASYNC = 8, PERF_TXN_RETRY = 16, PERF_TXN_CONFLICT = 32, PERF_TXN_CAPACITY_WRITE = 64, PERF_TXN_CAPACITY_READ = 128, PERF_TXN_MAX = 256, PERF_TXN_ABORT_MASK = 0, PERF_TXN_ABORT_SHIFT = 32, }; struct perf_event_header { __u32 type; __u16 misc; __u16 size; }; union intel_x86_pebs_dse { u64 val; struct { unsigned int ld_dse: 4; unsigned int ld_stlb_miss: 1; unsigned int ld_locked: 1; unsigned int ld_data_blk: 1; unsigned int ld_addr_blk: 1; unsigned int ld_reserved: 24; }; struct { unsigned int st_l1d_hit: 1; unsigned int st_reserved1: 3; unsigned int st_stlb_miss: 1; unsigned int st_locked: 1; unsigned int st_reserved2: 26; }; struct { unsigned int st_lat_dse: 4; unsigned int st_lat_stlb_miss: 1; unsigned int st_lat_locked: 1; unsigned int ld_reserved3: 26; }; }; struct pebs_record_core { u64 flags; u64 ip; u64 ax; u64 bx; u64 cx; u64 dx; u64 si; u64 di; u64 bp; u64 sp; u64 r8; u64 r9; u64 r10; u64 r11; u64 r12; u64 r13; u64 r14; u64 r15; }; struct pebs_record_nhm { u64 flags; u64 ip; u64 ax; u64 bx; u64 cx; u64 dx; u64 si; u64 di; u64 bp; u64 sp; u64 r8; u64 r9; u64 r10; u64 r11; u64 r12; u64 r13; u64 r14; u64 r15; u64 status; u64 dla; u64 dse; u64 lat; }; union hsw_tsx_tuning { struct { u32 cycles_last_block: 32; u32 hle_abort: 1; u32 rtm_abort: 1; u32 instruction_abort: 1; u32 non_instruction_abort: 1; u32 retry: 1; u32 data_conflict: 1; u32 capacity_writes: 1; u32 capacity_reads: 1; }; u64 value; }; struct pebs_record_skl { u64 flags; u64 ip; u64 ax; u64 bx; u64 cx; u64 dx; u64 si; u64 di; u64 bp; u64 sp; u64 r8; u64 r9; u64 r10; u64 r11; u64 r12; u64 r13; u64 r14; u64 r15; u64 status; u64 dla; u64 dse; u64 lat; u64 real_ip; u64 tsx_tuning; u64 tsc; }; struct bts_record { u64 from; u64 to; u64 flags; }; enum { PERF_BR_UNKNOWN = 0, PERF_BR_COND = 1, PERF_BR_UNCOND = 2, PERF_BR_IND = 3, PERF_BR_CALL = 4, PERF_BR_IND_CALL = 5, PERF_BR_RET = 6, PERF_BR_SYSCALL = 7, PERF_BR_SYSRET = 8, PERF_BR_COND_CALL = 9, PERF_BR_COND_RET = 10, PERF_BR_MAX = 11, }; enum xfeature { XFEATURE_FP = 0, XFEATURE_SSE = 1, XFEATURE_YMM = 2, XFEATURE_BNDREGS = 3, XFEATURE_BNDCSR = 4, XFEATURE_OPMASK = 5, XFEATURE_ZMM_Hi256 = 6, XFEATURE_Hi16_ZMM = 7, XFEATURE_PT_UNIMPLEMENTED_SO_FAR = 8, XFEATURE_PKRU = 9, XFEATURE_PASID = 10, XFEATURE_RSRVD_COMP_11 = 11, XFEATURE_RSRVD_COMP_12 = 12, XFEATURE_RSRVD_COMP_13 = 13, XFEATURE_RSRVD_COMP_14 = 14, XFEATURE_LBR = 15, XFEATURE_MAX = 16, }; struct arch_lbr_state { u64 lbr_ctl; u64 lbr_depth; u64 ler_from; u64 ler_to; u64 ler_info; struct lbr_entry entries[0]; }; union cpuid28_eax { struct { unsigned int lbr_depth_mask: 8; unsigned int reserved: 22; unsigned int lbr_deep_c_reset: 1; unsigned int lbr_lip: 1; } split; unsigned int full; }; union cpuid28_ebx { struct { unsigned int lbr_cpl: 1; unsigned int lbr_filter: 1; unsigned int lbr_call_stack: 1; } split; unsigned int full; }; union cpuid28_ecx { struct { unsigned int lbr_mispred: 1; unsigned int lbr_timed_lbr: 1; unsigned int lbr_br_type: 1; } split; unsigned int full; }; struct x86_pmu_lbr { unsigned int nr; unsigned int from; unsigned int to; unsigned int info; }; struct x86_perf_task_context_opt { int lbr_callstack_users; int lbr_stack_state; int log_id; }; struct x86_perf_task_context { u64 lbr_sel; int tos; int valid_lbrs; struct x86_perf_task_context_opt opt; struct lbr_entry lbr[32]; }; struct x86_perf_task_context_arch_lbr { struct x86_perf_task_context_opt opt; struct lbr_entry entries[0]; }; struct x86_perf_task_context_arch_lbr_xsave { struct x86_perf_task_context_opt opt; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; union { struct xregs_state xsave; struct { struct fxregs_state i387; struct xstate_header header; struct arch_lbr_state lbr; long: 64; long: 64; long: 64; }; }; }; enum { X86_BR_NONE = 0, X86_BR_USER = 1, X86_BR_KERNEL = 2, X86_BR_CALL = 4, X86_BR_RET = 8, X86_BR_SYSCALL = 16, X86_BR_SYSRET = 32, X86_BR_INT = 64, X86_BR_IRET = 128, X86_BR_JCC = 256, X86_BR_JMP = 512, X86_BR_IRQ = 1024, X86_BR_IND_CALL = 2048, X86_BR_ABORT = 4096, X86_BR_IN_TX = 8192, X86_BR_NO_TX = 16384, X86_BR_ZERO_CALL = 32768, X86_BR_CALL_STACK = 65536, X86_BR_IND_JMP = 131072, X86_BR_TYPE_SAVE = 262144, }; enum { LBR_NONE = 0, LBR_VALID = 1, }; enum { ARCH_LBR_BR_TYPE_JCC = 0, ARCH_LBR_BR_TYPE_NEAR_IND_JMP = 1, ARCH_LBR_BR_TYPE_NEAR_REL_JMP = 2, ARCH_LBR_BR_TYPE_NEAR_IND_CALL = 3, ARCH_LBR_BR_TYPE_NEAR_REL_CALL = 4, ARCH_LBR_BR_TYPE_NEAR_RET = 5, ARCH_LBR_BR_TYPE_KNOWN_MAX = 5, ARCH_LBR_BR_TYPE_MAP_MAX = 16, }; enum P4_EVENTS { P4_EVENT_TC_DELIVER_MODE = 0, P4_EVENT_BPU_FETCH_REQUEST = 1, P4_EVENT_ITLB_REFERENCE = 2, P4_EVENT_MEMORY_CANCEL = 3, P4_EVENT_MEMORY_COMPLETE = 4, P4_EVENT_LOAD_PORT_REPLAY = 5, P4_EVENT_STORE_PORT_REPLAY = 6, P4_EVENT_MOB_LOAD_REPLAY = 7, P4_EVENT_PAGE_WALK_TYPE = 8, P4_EVENT_BSQ_CACHE_REFERENCE = 9, P4_EVENT_IOQ_ALLOCATION = 10, P4_EVENT_IOQ_ACTIVE_ENTRIES = 11, P4_EVENT_FSB_DATA_ACTIVITY = 12, P4_EVENT_BSQ_ALLOCATION = 13, P4_EVENT_BSQ_ACTIVE_ENTRIES = 14, P4_EVENT_SSE_INPUT_ASSIST = 15, P4_EVENT_PACKED_SP_UOP = 16, P4_EVENT_PACKED_DP_UOP = 17, P4_EVENT_SCALAR_SP_UOP = 18, P4_EVENT_SCALAR_DP_UOP = 19, P4_EVENT_64BIT_MMX_UOP = 20, P4_EVENT_128BIT_MMX_UOP = 21, P4_EVENT_X87_FP_UOP = 22, P4_EVENT_TC_MISC = 23, P4_EVENT_GLOBAL_POWER_EVENTS = 24, P4_EVENT_TC_MS_XFER = 25, P4_EVENT_UOP_QUEUE_WRITES = 26, P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE = 27, P4_EVENT_RETIRED_BRANCH_TYPE = 28, P4_EVENT_RESOURCE_STALL = 29, P4_EVENT_WC_BUFFER = 30, P4_EVENT_B2B_CYCLES = 31, P4_EVENT_BNR = 32, P4_EVENT_SNOOP = 33, P4_EVENT_RESPONSE = 34, P4_EVENT_FRONT_END_EVENT = 35, P4_EVENT_EXECUTION_EVENT = 36, P4_EVENT_REPLAY_EVENT = 37, P4_EVENT_INSTR_RETIRED = 38, P4_EVENT_UOPS_RETIRED = 39, P4_EVENT_UOP_TYPE = 40, P4_EVENT_BRANCH_RETIRED = 41, P4_EVENT_MISPRED_BRANCH_RETIRED = 42, P4_EVENT_X87_ASSIST = 43, P4_EVENT_MACHINE_CLEAR = 44, P4_EVENT_INSTR_COMPLETED = 45, }; enum P4_EVENT_OPCODES { P4_EVENT_TC_DELIVER_MODE_OPCODE = 257, P4_EVENT_BPU_FETCH_REQUEST_OPCODE = 768, P4_EVENT_ITLB_REFERENCE_OPCODE = 6147, P4_EVENT_MEMORY_CANCEL_OPCODE = 517, P4_EVENT_MEMORY_COMPLETE_OPCODE = 2050, P4_EVENT_LOAD_PORT_REPLAY_OPCODE = 1026, P4_EVENT_STORE_PORT_REPLAY_OPCODE = 1282, P4_EVENT_MOB_LOAD_REPLAY_OPCODE = 770, P4_EVENT_PAGE_WALK_TYPE_OPCODE = 260, P4_EVENT_BSQ_CACHE_REFERENCE_OPCODE = 3079, P4_EVENT_IOQ_ALLOCATION_OPCODE = 774, P4_EVENT_IOQ_ACTIVE_ENTRIES_OPCODE = 6662, P4_EVENT_FSB_DATA_ACTIVITY_OPCODE = 5894, P4_EVENT_BSQ_ALLOCATION_OPCODE = 1287, P4_EVENT_BSQ_ACTIVE_ENTRIES_OPCODE = 1543, P4_EVENT_SSE_INPUT_ASSIST_OPCODE = 13313, P4_EVENT_PACKED_SP_UOP_OPCODE = 2049, P4_EVENT_PACKED_DP_UOP_OPCODE = 3073, P4_EVENT_SCALAR_SP_UOP_OPCODE = 2561, P4_EVENT_SCALAR_DP_UOP_OPCODE = 3585, P4_EVENT_64BIT_MMX_UOP_OPCODE = 513, P4_EVENT_128BIT_MMX_UOP_OPCODE = 6657, P4_EVENT_X87_FP_UOP_OPCODE = 1025, P4_EVENT_TC_MISC_OPCODE = 1537, P4_EVENT_GLOBAL_POWER_EVENTS_OPCODE = 4870, P4_EVENT_TC_MS_XFER_OPCODE = 1280, P4_EVENT_UOP_QUEUE_WRITES_OPCODE = 2304, P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE_OPCODE = 1282, P4_EVENT_RETIRED_BRANCH_TYPE_OPCODE = 1026, P4_EVENT_RESOURCE_STALL_OPCODE = 257, P4_EVENT_WC_BUFFER_OPCODE = 1285, P4_EVENT_B2B_CYCLES_OPCODE = 5635, P4_EVENT_BNR_OPCODE = 2051, P4_EVENT_SNOOP_OPCODE = 1539, P4_EVENT_RESPONSE_OPCODE = 1027, P4_EVENT_FRONT_END_EVENT_OPCODE = 2053, P4_EVENT_EXECUTION_EVENT_OPCODE = 3077, P4_EVENT_REPLAY_EVENT_OPCODE = 2309, P4_EVENT_INSTR_RETIRED_OPCODE = 516, P4_EVENT_UOPS_RETIRED_OPCODE = 260, P4_EVENT_UOP_TYPE_OPCODE = 514, P4_EVENT_BRANCH_RETIRED_OPCODE = 1541, P4_EVENT_MISPRED_BRANCH_RETIRED_OPCODE = 772, P4_EVENT_X87_ASSIST_OPCODE = 773, P4_EVENT_MACHINE_CLEAR_OPCODE = 517, P4_EVENT_INSTR_COMPLETED_OPCODE = 1796, }; enum P4_ESCR_EMASKS { P4_EVENT_TC_DELIVER_MODE__DD = 512, P4_EVENT_TC_DELIVER_MODE__DB = 1024, P4_EVENT_TC_DELIVER_MODE__DI = 2048, P4_EVENT_TC_DELIVER_MODE__BD = 4096, P4_EVENT_TC_DELIVER_MODE__BB = 8192, P4_EVENT_TC_DELIVER_MODE__BI = 16384, P4_EVENT_TC_DELIVER_MODE__ID = 32768, P4_EVENT_BPU_FETCH_REQUEST__TCMISS = 512, P4_EVENT_ITLB_REFERENCE__HIT = 512, P4_EVENT_ITLB_REFERENCE__MISS = 1024, P4_EVENT_ITLB_REFERENCE__HIT_UK = 2048, P4_EVENT_MEMORY_CANCEL__ST_RB_FULL = 2048, P4_EVENT_MEMORY_CANCEL__64K_CONF = 4096, P4_EVENT_MEMORY_COMPLETE__LSC = 512, P4_EVENT_MEMORY_COMPLETE__SSC = 1024, P4_EVENT_LOAD_PORT_REPLAY__SPLIT_LD = 1024, P4_EVENT_STORE_PORT_REPLAY__SPLIT_ST = 1024, P4_EVENT_MOB_LOAD_REPLAY__NO_STA = 1024, P4_EVENT_MOB_LOAD_REPLAY__NO_STD = 4096, P4_EVENT_MOB_LOAD_REPLAY__PARTIAL_DATA = 8192, P4_EVENT_MOB_LOAD_REPLAY__UNALGN_ADDR = 16384, P4_EVENT_PAGE_WALK_TYPE__DTMISS = 512, P4_EVENT_PAGE_WALK_TYPE__ITMISS = 1024, P4_EVENT_BSQ_CACHE_REFERENCE__RD_2ndL_HITS = 512, P4_EVENT_BSQ_CACHE_REFERENCE__RD_2ndL_HITE = 1024, P4_EVENT_BSQ_CACHE_REFERENCE__RD_2ndL_HITM = 2048, P4_EVENT_BSQ_CACHE_REFERENCE__RD_3rdL_HITS = 4096, P4_EVENT_BSQ_CACHE_REFERENCE__RD_3rdL_HITE = 8192, P4_EVENT_BSQ_CACHE_REFERENCE__RD_3rdL_HITM = 16384, P4_EVENT_BSQ_CACHE_REFERENCE__RD_2ndL_MISS = 131072, P4_EVENT_BSQ_CACHE_REFERENCE__RD_3rdL_MISS = 262144, P4_EVENT_BSQ_CACHE_REFERENCE__WR_2ndL_MISS = 524288, P4_EVENT_IOQ_ALLOCATION__DEFAULT = 512, P4_EVENT_IOQ_ALLOCATION__ALL_READ = 16384, P4_EVENT_IOQ_ALLOCATION__ALL_WRITE = 32768, P4_EVENT_IOQ_ALLOCATION__MEM_UC = 65536, P4_EVENT_IOQ_ALLOCATION__MEM_WC = 131072, P4_EVENT_IOQ_ALLOCATION__MEM_WT = 262144, P4_EVENT_IOQ_ALLOCATION__MEM_WP = 524288, P4_EVENT_IOQ_ALLOCATION__MEM_WB = 1048576, P4_EVENT_IOQ_ALLOCATION__OWN = 4194304, P4_EVENT_IOQ_ALLOCATION__OTHER = 8388608, P4_EVENT_IOQ_ALLOCATION__PREFETCH = 16777216, P4_EVENT_IOQ_ACTIVE_ENTRIES__DEFAULT = 512, P4_EVENT_IOQ_ACTIVE_ENTRIES__ALL_READ = 16384, P4_EVENT_IOQ_ACTIVE_ENTRIES__ALL_WRITE = 32768, P4_EVENT_IOQ_ACTIVE_ENTRIES__MEM_UC = 65536, P4_EVENT_IOQ_ACTIVE_ENTRIES__MEM_WC = 131072, P4_EVENT_IOQ_ACTIVE_ENTRIES__MEM_WT = 262144, P4_EVENT_IOQ_ACTIVE_ENTRIES__MEM_WP = 524288, P4_EVENT_IOQ_ACTIVE_ENTRIES__MEM_WB = 1048576, P4_EVENT_IOQ_ACTIVE_ENTRIES__OWN = 4194304, P4_EVENT_IOQ_ACTIVE_ENTRIES__OTHER = 8388608, P4_EVENT_IOQ_ACTIVE_ENTRIES__PREFETCH = 16777216, P4_EVENT_FSB_DATA_ACTIVITY__DRDY_DRV = 512, P4_EVENT_FSB_DATA_ACTIVITY__DRDY_OWN = 1024, P4_EVENT_FSB_DATA_ACTIVITY__DRDY_OTHER = 2048, P4_EVENT_FSB_DATA_ACTIVITY__DBSY_DRV = 4096, P4_EVENT_FSB_DATA_ACTIVITY__DBSY_OWN = 8192, P4_EVENT_FSB_DATA_ACTIVITY__DBSY_OTHER = 16384, P4_EVENT_BSQ_ALLOCATION__REQ_TYPE0 = 512, P4_EVENT_BSQ_ALLOCATION__REQ_TYPE1 = 1024, P4_EVENT_BSQ_ALLOCATION__REQ_LEN0 = 2048, P4_EVENT_BSQ_ALLOCATION__REQ_LEN1 = 4096, P4_EVENT_BSQ_ALLOCATION__REQ_IO_TYPE = 16384, P4_EVENT_BSQ_ALLOCATION__REQ_LOCK_TYPE = 32768, P4_EVENT_BSQ_ALLOCATION__REQ_CACHE_TYPE = 65536, P4_EVENT_BSQ_ALLOCATION__REQ_SPLIT_TYPE = 131072, P4_EVENT_BSQ_ALLOCATION__REQ_DEM_TYPE = 262144, P4_EVENT_BSQ_ALLOCATION__REQ_ORD_TYPE = 524288, P4_EVENT_BSQ_ALLOCATION__MEM_TYPE0 = 1048576, P4_EVENT_BSQ_ALLOCATION__MEM_TYPE1 = 2097152, P4_EVENT_BSQ_ALLOCATION__MEM_TYPE2 = 4194304, P4_EVENT_BSQ_ACTIVE_ENTRIES__REQ_TYPE0 = 512, P4_EVENT_BSQ_ACTIVE_ENTRIES__REQ_TYPE1 = 1024, P4_EVENT_BSQ_ACTIVE_ENTRIES__REQ_LEN0 = 2048, P4_EVENT_BSQ_ACTIVE_ENTRIES__REQ_LEN1 = 4096, P4_EVENT_BSQ_ACTIVE_ENTRIES__REQ_IO_TYPE = 16384, P4_EVENT_BSQ_ACTIVE_ENTRIES__REQ_LOCK_TYPE = 32768, P4_EVENT_BSQ_ACTIVE_ENTRIES__REQ_CACHE_TYPE = 65536, P4_EVENT_BSQ_ACTIVE_ENTRIES__REQ_SPLIT_TYPE = 131072, P4_EVENT_BSQ_ACTIVE_ENTRIES__REQ_DEM_TYPE = 262144, P4_EVENT_BSQ_ACTIVE_ENTRIES__REQ_ORD_TYPE = 524288, P4_EVENT_BSQ_ACTIVE_ENTRIES__MEM_TYPE0 = 1048576, P4_EVENT_BSQ_ACTIVE_ENTRIES__MEM_TYPE1 = 2097152, P4_EVENT_BSQ_ACTIVE_ENTRIES__MEM_TYPE2 = 4194304, P4_EVENT_SSE_INPUT_ASSIST__ALL = 16777216, P4_EVENT_PACKED_SP_UOP__ALL = 16777216, P4_EVENT_PACKED_DP_UOP__ALL = 16777216, P4_EVENT_SCALAR_SP_UOP__ALL = 16777216, P4_EVENT_SCALAR_DP_UOP__ALL = 16777216, P4_EVENT_64BIT_MMX_UOP__ALL = 16777216, P4_EVENT_128BIT_MMX_UOP__ALL = 16777216, P4_EVENT_X87_FP_UOP__ALL = 16777216, P4_EVENT_TC_MISC__FLUSH = 8192, P4_EVENT_GLOBAL_POWER_EVENTS__RUNNING = 512, P4_EVENT_TC_MS_XFER__CISC = 512, P4_EVENT_UOP_QUEUE_WRITES__FROM_TC_BUILD = 512, P4_EVENT_UOP_QUEUE_WRITES__FROM_TC_DELIVER = 1024, P4_EVENT_UOP_QUEUE_WRITES__FROM_ROM = 2048, P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE__CONDITIONAL = 1024, P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE__CALL = 2048, P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE__RETURN = 4096, P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE__INDIRECT = 8192, P4_EVENT_RETIRED_BRANCH_TYPE__CONDITIONAL = 1024, P4_EVENT_RETIRED_BRANCH_TYPE__CALL = 2048, P4_EVENT_RETIRED_BRANCH_TYPE__RETURN = 4096, P4_EVENT_RETIRED_BRANCH_TYPE__INDIRECT = 8192, P4_EVENT_RESOURCE_STALL__SBFULL = 16384, P4_EVENT_WC_BUFFER__WCB_EVICTS = 512, P4_EVENT_WC_BUFFER__WCB_FULL_EVICTS = 1024, P4_EVENT_FRONT_END_EVENT__NBOGUS = 512, P4_EVENT_FRONT_END_EVENT__BOGUS = 1024, P4_EVENT_EXECUTION_EVENT__NBOGUS0 = 512, P4_EVENT_EXECUTION_EVENT__NBOGUS1 = 1024, P4_EVENT_EXECUTION_EVENT__NBOGUS2 = 2048, P4_EVENT_EXECUTION_EVENT__NBOGUS3 = 4096, P4_EVENT_EXECUTION_EVENT__BOGUS0 = 8192, P4_EVENT_EXECUTION_EVENT__BOGUS1 = 16384, P4_EVENT_EXECUTION_EVENT__BOGUS2 = 32768, P4_EVENT_EXECUTION_EVENT__BOGUS3 = 65536, P4_EVENT_REPLAY_EVENT__NBOGUS = 512, P4_EVENT_REPLAY_EVENT__BOGUS = 1024, P4_EVENT_INSTR_RETIRED__NBOGUSNTAG = 512, P4_EVENT_INSTR_RETIRED__NBOGUSTAG = 1024, P4_EVENT_INSTR_RETIRED__BOGUSNTAG = 2048, P4_EVENT_INSTR_RETIRED__BOGUSTAG = 4096, P4_EVENT_UOPS_RETIRED__NBOGUS = 512, P4_EVENT_UOPS_RETIRED__BOGUS = 1024, P4_EVENT_UOP_TYPE__TAGLOADS = 1024, P4_EVENT_UOP_TYPE__TAGSTORES = 2048, P4_EVENT_BRANCH_RETIRED__MMNP = 512, P4_EVENT_BRANCH_RETIRED__MMNM = 1024, P4_EVENT_BRANCH_RETIRED__MMTP = 2048, P4_EVENT_BRANCH_RETIRED__MMTM = 4096, P4_EVENT_MISPRED_BRANCH_RETIRED__NBOGUS = 512, P4_EVENT_X87_ASSIST__FPSU = 512, P4_EVENT_X87_ASSIST__FPSO = 1024, P4_EVENT_X87_ASSIST__POAO = 2048, P4_EVENT_X87_ASSIST__POAU = 4096, P4_EVENT_X87_ASSIST__PREA = 8192, P4_EVENT_MACHINE_CLEAR__CLEAR = 512, P4_EVENT_MACHINE_CLEAR__MOCLEAR = 1024, P4_EVENT_MACHINE_CLEAR__SMCLEAR = 2048, P4_EVENT_INSTR_COMPLETED__NBOGUS = 512, P4_EVENT_INSTR_COMPLETED__BOGUS = 1024, }; enum P4_PEBS_METRIC { P4_PEBS_METRIC__none = 0, P4_PEBS_METRIC__1stl_cache_load_miss_retired = 1, P4_PEBS_METRIC__2ndl_cache_load_miss_retired = 2, P4_PEBS_METRIC__dtlb_load_miss_retired = 3, P4_PEBS_METRIC__dtlb_store_miss_retired = 4, P4_PEBS_METRIC__dtlb_all_miss_retired = 5, P4_PEBS_METRIC__tagged_mispred_branch = 6, P4_PEBS_METRIC__mob_load_replay_retired = 7, P4_PEBS_METRIC__split_load_retired = 8, P4_PEBS_METRIC__split_store_retired = 9, P4_PEBS_METRIC__max = 10, }; struct p4_event_bind { unsigned int opcode; unsigned int escr_msr[2]; unsigned int escr_emask; unsigned int shared; char cntr[6]; }; struct p4_pebs_bind { unsigned int metric_pebs; unsigned int metric_vert; }; struct p4_event_alias { u64 original; u64 alternative; }; enum cpuid_regs_idx { CPUID_EAX = 0, CPUID_EBX = 1, CPUID_ECX = 2, CPUID_EDX = 3, }; struct dev_ext_attribute { struct device_attribute attr; void *var; }; enum pt_capabilities { PT_CAP_max_subleaf = 0, PT_CAP_cr3_filtering = 1, PT_CAP_psb_cyc = 2, PT_CAP_ip_filtering = 3, PT_CAP_mtc = 4, PT_CAP_ptwrite = 5, PT_CAP_power_event_trace = 6, PT_CAP_topa_output = 7, PT_CAP_topa_multiple_entries = 8, PT_CAP_single_range_output = 9, PT_CAP_output_subsys = 10, PT_CAP_payloads_lip = 11, PT_CAP_num_address_ranges = 12, PT_CAP_mtc_periods = 13, PT_CAP_cycle_thresholds = 14, PT_CAP_psb_periods = 15, }; enum perf_addr_filter_action_t { PERF_ADDR_FILTER_ACTION_STOP = 0, PERF_ADDR_FILTER_ACTION_START = 1, PERF_ADDR_FILTER_ACTION_FILTER = 2, }; struct perf_addr_filter { struct list_head entry; struct path path; long unsigned int offset; long unsigned int size; enum perf_addr_filter_action_t action; }; struct topa_entry { u64 end: 1; u64 rsvd0: 1; u64 intr: 1; u64 rsvd1: 1; u64 stop: 1; u64 rsvd2: 1; u64 size: 4; u64 rsvd3: 2; u64 base: 36; u64 rsvd4: 16; }; struct pt_pmu { struct pmu pmu; u32 caps[8]; bool vmx; bool branch_en_always_on; long unsigned int max_nonturbo_ratio; unsigned int tsc_art_num; unsigned int tsc_art_den; }; struct topa; struct pt_buffer { struct list_head tables; struct topa *first; struct topa *last; struct topa *cur; unsigned int cur_idx; size_t output_off; long unsigned int nr_pages; local_t data_size; local64_t head; bool snapshot; bool single; long int stop_pos; long int intr_pos; struct topa_entry *stop_te; struct topa_entry *intr_te; void **data_pages; }; struct topa { struct list_head list; u64 offset; size_t size; int last; unsigned int z_count; }; struct pt_filter { long unsigned int msr_a; long unsigned int msr_b; long unsigned int config; }; struct pt_filters { struct pt_filter filter[4]; unsigned int nr_filters; }; struct pt { struct perf_output_handle handle; struct pt_filters filters; int handle_nmi; int vmx_on; u64 output_base; u64 output_mask; }; struct pt_cap_desc { const char *name; u32 leaf; u8 reg; u32 mask; }; struct pt_address_range { long unsigned int msr_a; long unsigned int msr_b; unsigned int reg_off; }; struct topa_page { struct topa_entry table[507]; struct topa topa; }; typedef s8 int8_t; typedef u8 uint8_t; typedef u64 uint64_t; struct atomic_notifier_head { spinlock_t lock; struct notifier_block *head; }; enum xen_domain_type { XEN_NATIVE = 0, XEN_PV_DOMAIN = 1, XEN_HVM_DOMAIN = 2, }; typedef long unsigned int xen_pfn_t; typedef long unsigned int xen_ulong_t; struct arch_shared_info { long unsigned int max_pfn; xen_pfn_t pfn_to_mfn_frame_list_list; long unsigned int nmi_reason; long unsigned int p2m_cr3; long unsigned int p2m_vaddr; long unsigned int p2m_generation; }; struct arch_vcpu_info { long unsigned int cr2; long unsigned int pad; }; struct pvclock_wall_clock { u32 version; u32 sec; u32 nsec; }; struct vcpu_info { uint8_t evtchn_upcall_pending; uint8_t evtchn_upcall_mask; xen_ulong_t evtchn_pending_sel; struct arch_vcpu_info arch; struct pvclock_vcpu_time_info time; }; struct shared_info { struct vcpu_info vcpu_info[32]; xen_ulong_t evtchn_pending[64]; xen_ulong_t evtchn_mask[64]; struct pvclock_wall_clock wc; uint32_t wc_sec_hi; struct arch_shared_info arch; }; struct start_info { char magic[32]; long unsigned int nr_pages; long unsigned int shared_info; uint32_t flags; xen_pfn_t store_mfn; uint32_t store_evtchn; union { struct { xen_pfn_t mfn; uint32_t evtchn; } domU; struct { uint32_t info_off; uint32_t info_size; } dom0; } console; long unsigned int pt_base; long unsigned int nr_pt_frames; long unsigned int mfn_list; long unsigned int mod_start; long unsigned int mod_len; int8_t cmd_line[1024]; long unsigned int first_p2m_pfn; long unsigned int nr_p2m_frames; }; struct sched_shutdown { unsigned int reason; }; struct sched_pin_override { int32_t pcpu; }; struct vcpu_register_vcpu_info { uint64_t mfn; uint32_t offset; uint32_t rsvd; }; struct xmaddr { phys_addr_t maddr; }; typedef struct xmaddr xmaddr_t; struct xpaddr { phys_addr_t paddr; }; typedef struct xpaddr xpaddr_t; typedef s16 int16_t; typedef u16 uint16_t; enum clocksource_ids { CSID_GENERIC = 0, CSID_ARM_ARCH_COUNTER = 1, CSID_MAX = 2, }; struct clocksource { u64 (*read)(struct clocksource *); u64 mask; u32 mult; u32 shift; u64 max_idle_ns; u32 maxadj; u32 uncertainty_margin; u64 max_cycles; const char *name; struct list_head list; int rating; enum clocksource_ids id; enum vdso_clock_mode vdso_clock_mode; long unsigned int flags; int (*enable)(struct clocksource *); void (*disable)(struct clocksource *); void (*suspend)(struct clocksource *); void (*resume)(struct clocksource *); void (*mark_unstable)(struct clocksource *); void (*tick_stable)(struct clocksource *); struct list_head wd_list; u64 cs_last; u64 wd_last; struct module *owner; }; struct x86_init_mpparse { void (*setup_ioapic_ids)(); void (*find_smp_config)(); void (*get_smp_config)(unsigned int); }; struct x86_init_resources { void (*probe_roms)(); void (*reserve_resources)(); char * (*memory_setup)(); }; struct x86_init_irqs { void (*pre_vector_init)(); void (*intr_init)(); void (*intr_mode_select)(); void (*intr_mode_init)(); struct irq_domain * (*create_pci_msi_domain)(); }; struct x86_init_oem { void (*arch_setup)(); void (*banner)(); }; struct x86_init_paging { void (*pagetable_init)(); }; struct x86_init_timers { void (*setup_percpu_clockev)(); void (*timer_init)(); void (*wallclock_init)(); }; struct x86_init_iommu { int (*iommu_init)(); }; struct x86_init_pci { int (*arch_init)(); int (*init)(); void (*init_irq)(); void (*fixup_irqs)(); }; struct x86_hyper_init { void (*init_platform)(); void (*guest_late_init)(); bool (*x2apic_available)(); bool (*msi_ext_dest_id)(); void (*init_mem_mapping)(); void (*init_after_bootmem)(); }; struct x86_init_acpi { void (*set_root_pointer)(u64); u64 (*get_root_pointer)(); void (*reduced_hw_early_init)(); }; struct x86_init_ops { struct x86_init_resources resources; struct x86_init_mpparse mpparse; struct x86_init_irqs irqs; struct x86_init_oem oem; struct x86_init_paging paging; struct x86_init_timers timers; struct x86_init_iommu iommu; struct x86_init_pci pci; struct x86_hyper_init hyper; struct x86_init_acpi acpi; }; struct x86_cpuinit_ops { void (*setup_percpu_clockev)(); void (*early_percpu_clock_init)(); void (*fixup_cpu_id)(struct cpuinfo_x86 *, int); }; enum clock_event_state { CLOCK_EVT_STATE_DETACHED = 0, CLOCK_EVT_STATE_SHUTDOWN = 1, CLOCK_EVT_STATE_PERIODIC = 2, CLOCK_EVT_STATE_ONESHOT = 3, CLOCK_EVT_STATE_ONESHOT_STOPPED = 4, }; struct clock_event_device { void (*event_handler)(struct clock_event_device *); int (*set_next_event)(long unsigned int, struct clock_event_device *); int (*set_next_ktime)(ktime_t, struct clock_event_device *); ktime_t next_event; u64 max_delta_ns; u64 min_delta_ns; u32 mult; u32 shift; enum clock_event_state state_use_accessors; unsigned int features; long unsigned int retries; int (*set_state_periodic)(struct clock_event_device *); int (*set_state_oneshot)(struct clock_event_device *); int (*set_state_oneshot_stopped)(struct clock_event_device *); int (*set_state_shutdown)(struct clock_event_device *); int (*tick_resume)(struct clock_event_device *); void (*broadcast)(const struct cpumask *); void (*suspend)(struct clock_event_device *); void (*resume)(struct clock_event_device *); long unsigned int min_delta_ticks; long unsigned int max_delta_ticks; const char *name; int rating; int irq; int bound_on; const struct cpumask *cpumask; struct list_head list; struct module *owner; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct tk_read_base { struct clocksource *clock; u64 mask; u64 cycle_last; u32 mult; u32 shift; u64 xtime_nsec; ktime_t base; u64 base_real; }; struct timekeeper { struct tk_read_base tkr_mono; struct tk_read_base tkr_raw; u64 xtime_sec; long unsigned int ktime_sec; struct timespec64 wall_to_monotonic; ktime_t offs_real; ktime_t offs_boot; ktime_t offs_tai; s32 tai_offset; unsigned int clock_was_set_seq; u8 cs_was_changed_seq; ktime_t next_leap_ktime; u64 raw_sec; struct timespec64 monotonic_to_boot; u64 cycle_interval; u64 xtime_interval; s64 xtime_remainder; u64 raw_interval; u64 ntp_tick; s64 ntp_error; u32 ntp_error_shift; u32 ntp_err_mult; u32 skip_second_overflow; }; typedef unsigned char *__guest_handle_uchar; typedef char *__guest_handle_char; typedef void *__guest_handle_void; typedef uint64_t *__guest_handle_uint64_t; typedef uint32_t *__guest_handle_uint32_t; struct vcpu_time_info { uint32_t version; uint32_t pad0; uint64_t tsc_timestamp; uint64_t system_time; uint32_t tsc_to_system_mul; int8_t tsc_shift; int8_t pad1[3]; }; struct xenpf_settime32 { uint32_t secs; uint32_t nsecs; uint64_t system_time; }; struct xenpf_settime64 { uint64_t secs; uint32_t nsecs; uint32_t mbz; uint64_t system_time; }; struct xenpf_add_memtype { xen_pfn_t mfn; uint64_t nr_mfns; uint32_t type; uint32_t handle; uint32_t reg; }; struct xenpf_del_memtype { uint32_t handle; uint32_t reg; }; struct xenpf_read_memtype { uint32_t reg; xen_pfn_t mfn; uint64_t nr_mfns; uint32_t type; }; struct xenpf_microcode_update { __guest_handle_void data; uint32_t length; }; struct xenpf_platform_quirk { uint32_t quirk_id; }; struct xenpf_efi_time { uint16_t year; uint8_t month; uint8_t day; uint8_t hour; uint8_t min; uint8_t sec; uint32_t ns; int16_t tz; uint8_t daylight; }; struct xenpf_efi_guid { uint32_t data1; uint16_t data2; uint16_t data3; uint8_t data4[8]; }; struct xenpf_efi_runtime_call { uint32_t function; uint32_t misc; xen_ulong_t status; union { struct { struct xenpf_efi_time time; uint32_t resolution; uint32_t accuracy; } get_time; struct xenpf_efi_time set_time; struct xenpf_efi_time get_wakeup_time; struct xenpf_efi_time set_wakeup_time; struct { __guest_handle_void name; xen_ulong_t size; __guest_handle_void data; struct xenpf_efi_guid vendor_guid; } get_variable; struct { __guest_handle_void name; xen_ulong_t size; __guest_handle_void data; struct xenpf_efi_guid vendor_guid; } set_variable; struct { xen_ulong_t size; __guest_handle_void name; struct xenpf_efi_guid vendor_guid; } get_next_variable_name; struct { uint32_t attr; uint64_t max_store_size; uint64_t remain_store_size; uint64_t max_size; } query_variable_info; struct { __guest_handle_void capsule_header_array; xen_ulong_t capsule_count; uint64_t max_capsule_size; uint32_t reset_type; } query_capsule_capabilities; struct { __guest_handle_void capsule_header_array; xen_ulong_t capsule_count; uint64_t sg_list; } update_capsule; } u; }; union xenpf_efi_info { uint32_t version; struct { uint64_t addr; uint32_t nent; } cfg; struct { uint32_t revision; uint32_t bufsz; __guest_handle_void name; } vendor; struct { uint64_t addr; uint64_t size; uint64_t attr; uint32_t type; } mem; }; struct xenpf_firmware_info { uint32_t type; uint32_t index; union { struct { uint8_t device; uint8_t version; uint16_t interface_support; uint16_t legacy_max_cylinder; uint8_t legacy_max_head; uint8_t legacy_sectors_per_track; __guest_handle_void edd_params; } disk_info; struct { uint8_t device; uint32_t mbr_signature; } disk_mbr_signature; struct { uint8_t capabilities; uint8_t edid_transfer_time; __guest_handle_uchar edid; } vbeddc_info; union xenpf_efi_info efi_info; uint8_t kbd_shift_flags; } u; }; struct xenpf_enter_acpi_sleep { uint16_t val_a; uint16_t val_b; uint32_t sleep_state; uint32_t flags; }; struct xenpf_change_freq { uint32_t flags; uint32_t cpu; uint64_t freq; }; struct xenpf_getidletime { __guest_handle_uchar cpumap_bitmap; uint32_t cpumap_nr_cpus; __guest_handle_uint64_t idletime; uint64_t now; }; struct xen_power_register { uint32_t space_id; uint32_t bit_width; uint32_t bit_offset; uint32_t access_size; uint64_t address; }; struct xen_processor_csd { uint32_t domain; uint32_t coord_type; uint32_t num; }; typedef struct xen_processor_csd *__guest_handle_xen_processor_csd; struct xen_processor_cx { struct xen_power_register reg; uint8_t type; uint32_t latency; uint32_t power; uint32_t dpcnt; __guest_handle_xen_processor_csd dp; }; typedef struct xen_processor_cx *__guest_handle_xen_processor_cx; struct xen_processor_flags { uint32_t bm_control: 1; uint32_t bm_check: 1; uint32_t has_cst: 1; uint32_t power_setup_done: 1; uint32_t bm_rld_set: 1; }; struct xen_processor_power { uint32_t count; struct xen_processor_flags flags; __guest_handle_xen_processor_cx states; }; struct xen_pct_register { uint8_t descriptor; uint16_t length; uint8_t space_id; uint8_t bit_width; uint8_t bit_offset; uint8_t reserved; uint64_t address; }; struct xen_processor_px { uint64_t core_frequency; uint64_t power; uint64_t transition_latency; uint64_t bus_master_latency; uint64_t control; uint64_t status; }; typedef struct xen_processor_px *__guest_handle_xen_processor_px; struct xen_psd_package { uint64_t num_entries; uint64_t revision; uint64_t domain; uint64_t coord_type; uint64_t num_processors; }; struct xen_processor_performance { uint32_t flags; uint32_t platform_limit; struct xen_pct_register control_register; struct xen_pct_register status_register; uint32_t state_count; __guest_handle_xen_processor_px states; struct xen_psd_package domain_info; uint32_t shared_type; }; struct xenpf_set_processor_pminfo { uint32_t id; uint32_t type; union { struct xen_processor_power power; struct xen_processor_performance perf; __guest_handle_uint32_t pdc; }; }; struct xenpf_pcpuinfo { uint32_t xen_cpuid; uint32_t max_present; uint32_t flags; uint32_t apic_id; uint32_t acpi_id; }; struct xenpf_cpu_ol { uint32_t cpuid; }; struct xenpf_cpu_hotadd { uint32_t apic_id; uint32_t acpi_id; uint32_t pxm; }; struct xenpf_mem_hotadd { uint64_t spfn; uint64_t epfn; uint32_t pxm; uint32_t flags; }; struct xenpf_core_parking { uint32_t type; uint32_t idle_nums; }; struct xenpf_symdata { uint32_t namelen; uint32_t symnum; __guest_handle_char name; uint64_t address; char type; }; struct xen_platform_op { uint32_t cmd; uint32_t interface_version; union { struct xenpf_settime32 settime32; struct xenpf_settime64 settime64; struct xenpf_add_memtype add_memtype; struct xenpf_del_memtype del_memtype; struct xenpf_read_memtype read_memtype; struct xenpf_microcode_update microcode; struct xenpf_platform_quirk platform_quirk; struct xenpf_efi_runtime_call efi_runtime_call; struct xenpf_firmware_info firmware_info; struct xenpf_enter_acpi_sleep enter_acpi_sleep; struct xenpf_change_freq change_freq; struct xenpf_getidletime getidletime; struct xenpf_set_processor_pminfo set_pminfo; struct xenpf_pcpuinfo pcpu_info; struct xenpf_cpu_ol cpu_ol; struct xenpf_cpu_hotadd cpu_add; struct xenpf_mem_hotadd mem_add; struct xenpf_core_parking core_parking; struct xenpf_symdata symdata; uint8_t pad[128]; } u; }; struct vcpu_set_singleshot_timer { uint64_t timeout_abs_ns; uint32_t flags; }; typedef struct vcpu_time_info *__guest_handle_vcpu_time_info; struct vcpu_register_time_memory_area { union { __guest_handle_vcpu_time_info h; struct pvclock_vcpu_time_info *v; uint64_t p; } addr; }; struct xen_clock_event_device { struct clock_event_device evt; char name[16]; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; typedef int (*pte_fn_t)(pte_t *, long unsigned int, void *); typedef uint16_t grant_status_t; struct grant_frames { xen_pfn_t *pfn; unsigned int count; void *vaddr; }; struct gnttab_vm_area { struct vm_struct *area; pte_t **ptes; int idx; }; enum acpi_irq_model_id { ACPI_IRQ_MODEL_PIC = 0, ACPI_IRQ_MODEL_IOAPIC = 1, ACPI_IRQ_MODEL_IOSAPIC = 2, ACPI_IRQ_MODEL_PLATFORM = 3, ACPI_IRQ_MODEL_GIC = 4, ACPI_IRQ_MODEL_COUNT = 5, }; typedef uint16_t domid_t; struct xen_add_to_physmap { domid_t domid; uint16_t size; unsigned int space; xen_ulong_t idx; xen_pfn_t gpfn; }; struct machine_ops { void (*restart)(char *); void (*halt)(); void (*power_off)(); void (*shutdown)(); void (*crash_shutdown)(struct pt_regs *); void (*emergency_restart)(); }; enum x86_hypervisor_type { X86_HYPER_NATIVE = 0, X86_HYPER_VMWARE = 1, X86_HYPER_MS_HYPERV = 2, X86_HYPER_XEN_PV = 3, X86_HYPER_XEN_HVM = 4, X86_HYPER_KVM = 5, X86_HYPER_JAILHOUSE = 6, X86_HYPER_ACRN = 7, }; struct hypervisor_x86 { const char *name; uint32_t (*detect)(); enum x86_hypervisor_type type; struct x86_hyper_init init; struct x86_hyper_runtime runtime; bool ignore_nopv; }; enum e820_type { E820_TYPE_RAM = 1, E820_TYPE_RESERVED = 2, E820_TYPE_ACPI = 3, E820_TYPE_NVS = 4, E820_TYPE_UNUSABLE = 5, E820_TYPE_PMEM = 7, E820_TYPE_PRAM = 12, E820_TYPE_SOFT_RESERVED = 4026531839, E820_TYPE_RESERVED_KERN = 128, }; struct xen_hvm_pagetable_dying { domid_t domid; __u64 gpa; }; enum hvmmem_type_t { HVMMEM_ram_rw = 0, HVMMEM_ram_ro = 1, HVMMEM_mmio_dm = 2, }; struct xen_hvm_get_mem_type { domid_t domid; uint16_t mem_type; uint16_t pad[2]; uint64_t pfn; }; struct e820_entry { u64 addr; u64 size; enum e820_type type; } __attribute__((packed)); struct e820_table { __u32 nr_entries; struct e820_entry entries[224]; } __attribute__((packed)); typedef xen_pfn_t *__guest_handle_xen_pfn_t; typedef long unsigned int xen_callback_t; struct mmu_update { uint64_t ptr; uint64_t val; }; struct xen_memory_region { long unsigned int start_pfn; long unsigned int n_pfns; }; struct callback_register { uint16_t type; uint16_t flags; xen_callback_t address; }; struct xen_memory_reservation { __guest_handle_xen_pfn_t extent_start; xen_ulong_t nr_extents; unsigned int extent_order; unsigned int address_bits; domid_t domid; }; struct xen_memory_map { unsigned int nr_entries; __guest_handle_void buffer; }; struct x86_apic_ops { unsigned int (*io_apic_read)(unsigned int, unsigned int); void (*restore)(); }; struct physdev_apic { long unsigned int apic_physbase; uint32_t reg; uint32_t value; }; typedef long unsigned int uintptr_t; struct xen_pmu_amd_ctxt { uint32_t counters; uint32_t ctrls; uint64_t regs[0]; }; struct xen_pmu_cntr_pair { uint64_t counter; uint64_t control; }; struct xen_pmu_intel_ctxt { uint32_t fixed_counters; uint32_t arch_counters; uint64_t global_ctrl; uint64_t global_ovf_ctrl; uint64_t global_status; uint64_t fixed_ctrl; uint64_t ds_area; uint64_t pebs_enable; uint64_t debugctl; uint64_t regs[0]; }; struct xen_pmu_regs { uint64_t ip; uint64_t sp; uint64_t flags; uint16_t cs; uint16_t ss; uint8_t cpl; uint8_t pad[3]; }; struct xen_pmu_arch { union { struct xen_pmu_regs regs; uint8_t pad[64]; } r; uint64_t pmu_flags; union { uint32_t lapic_lvtpc; uint64_t pad; } l; union { struct xen_pmu_amd_ctxt amd; struct xen_pmu_intel_ctxt intel; uint8_t pad[128]; } c; }; struct xen_pmu_params { struct { uint32_t maj; uint32_t min; } version; uint64_t val; uint32_t vcpu; uint32_t pad; }; struct xen_pmu_data { uint32_t vcpu_id; uint32_t pcpu_id; domid_t domain_id; uint8_t pad[6]; struct xen_pmu_arch pmu; }; struct xenpmu { struct xen_pmu_data *xenpmu_data; uint8_t flags; }; enum pg_level { PG_LEVEL_NONE = 0, PG_LEVEL_4K = 1, PG_LEVEL_2M = 2, PG_LEVEL_1G = 3, PG_LEVEL_512G = 4, PG_LEVEL_NUM = 5, }; typedef uint32_t grant_ref_t; typedef uint32_t grant_handle_t; struct gnttab_map_grant_ref { uint64_t host_addr; uint32_t flags; grant_ref_t ref; domid_t dom; int16_t status; grant_handle_t handle; uint64_t dev_bus_addr; }; struct gnttab_unmap_grant_ref { uint64_t host_addr; uint64_t dev_bus_addr; grant_handle_t handle; int16_t status; }; enum { DESC_TSS = 9, DESC_LDT = 2, DESCTYPE_S = 16, }; enum paravirt_lazy_mode { PARAVIRT_LAZY_NONE = 0, PARAVIRT_LAZY_MMU = 1, PARAVIRT_LAZY_CPU = 2, }; struct plist_head { struct list_head node_list; }; enum pm_qos_type { PM_QOS_UNITIALIZED = 0, PM_QOS_MAX = 1, PM_QOS_MIN = 2, }; struct pm_qos_constraints { struct plist_head list; s32 target_value; s32 default_value; s32 no_constraint_value; enum pm_qos_type type; struct blocking_notifier_head *notifiers; }; struct freq_constraints { struct pm_qos_constraints min_freq; struct blocking_notifier_head min_freq_notifiers; struct pm_qos_constraints max_freq; struct blocking_notifier_head max_freq_notifiers; }; struct pm_qos_flags { struct list_head list; s32 effective_flags; }; struct dev_pm_qos_request; struct dev_pm_qos { struct pm_qos_constraints resume_latency; struct pm_qos_constraints latency_tolerance; struct freq_constraints freq; struct pm_qos_flags flags; struct dev_pm_qos_request *resume_latency_req; struct dev_pm_qos_request *latency_tolerance_req; struct dev_pm_qos_request *flags_req; }; typedef long int xen_long_t; struct trap_info { uint8_t vector; uint8_t flags; uint16_t cs; long unsigned int address; }; struct mmuext_op { unsigned int cmd; union { xen_pfn_t mfn; long unsigned int linear_addr; } arg1; union { unsigned int nr_ents; void *vcpumask; xen_pfn_t src_mfn; } arg2; }; struct multicall_entry { xen_ulong_t op; xen_long_t result; xen_ulong_t args[6]; }; struct dom0_vga_console_info { uint8_t video_type; union { struct { uint16_t font_height; uint16_t cursor_x; uint16_t cursor_y; uint16_t rows; uint16_t columns; } text_mode_3; struct { uint16_t width; uint16_t height; uint16_t bytes_per_line; uint16_t bits_per_pixel; uint32_t lfb_base; uint32_t lfb_size; uint8_t red_pos; uint8_t red_size; uint8_t green_pos; uint8_t green_size; uint8_t blue_pos; uint8_t blue_size; uint8_t rsvd_pos; uint8_t rsvd_size; uint32_t gbl_caps; uint16_t mode_attrs; } vesa_lfb; } u; }; struct physdev_set_iopl { uint32_t iopl; }; struct physdev_set_iobitmap { uint8_t *bitmap; uint32_t nr_ports; }; struct xen_extraversion { char extraversion[16]; }; typedef u32 acpi_status; struct pm_qos_flags_request { struct list_head node; s32 flags; }; enum freq_qos_req_type { FREQ_QOS_MIN = 1, FREQ_QOS_MAX = 2, }; struct freq_qos_request { enum freq_qos_req_type type; struct plist_node pnode; struct freq_constraints *qos; }; enum dev_pm_qos_req_type { DEV_PM_QOS_RESUME_LATENCY = 1, DEV_PM_QOS_LATENCY_TOLERANCE = 2, DEV_PM_QOS_MIN_FREQUENCY = 3, DEV_PM_QOS_MAX_FREQUENCY = 4, DEV_PM_QOS_FLAGS = 5, }; struct dev_pm_qos_request { enum dev_pm_qos_req_type type; union { struct plist_node pnode; struct pm_qos_flags_request flr; struct freq_qos_request freq; } data; struct device *dev; }; struct multicall_space { struct multicall_entry *mc; void *args; }; struct tls_descs { struct desc_struct desc[3]; }; struct trap_array_entry { void (*orig)(); void (*xen)(); bool ist_okay; }; struct mmu_gather_batch { struct mmu_gather_batch *next; unsigned int nr; unsigned int max; struct page *pages[0]; }; struct mmu_table_batch; struct mmu_gather { struct mm_struct *mm; struct mmu_table_batch *batch; long unsigned int start; long unsigned int end; unsigned int fullmm: 1; unsigned int need_flush_all: 1; unsigned int freed_tables: 1; unsigned int cleared_ptes: 1; unsigned int cleared_pmds: 1; unsigned int cleared_puds: 1; unsigned int cleared_p4ds: 1; unsigned int vma_exec: 1; unsigned int vma_huge: 1; unsigned int batch_count; struct mmu_gather_batch *active; struct mmu_gather_batch local; struct page *__pages[8]; }; struct mmu_table_batch { struct callback_head rcu; unsigned int nr; void *tables[0]; }; struct xen_memory_exchange { struct xen_memory_reservation in; struct xen_memory_reservation out; xen_ulong_t nr_exchanged; }; struct xen_machphys_mapping { xen_ulong_t v_start; xen_ulong_t v_end; xen_ulong_t max_mfn; }; enum pt_level { PT_PGD = 0, PT_P4D = 1, PT_PUD = 2, PT_PMD = 3, PT_PTE = 4, }; struct remap_data { xen_pfn_t *pfn; bool contiguous; bool no_translate; pgprot_t prot; struct mmu_update *mmu_update; }; enum xen_mc_flush_reason { XEN_MC_FL_NONE = 0, XEN_MC_FL_BATCH = 1, XEN_MC_FL_ARGS = 2, XEN_MC_FL_CALLBACK = 3, }; enum xen_mc_extend_args { XEN_MC_XE_OK = 0, XEN_MC_XE_BAD_OP = 1, XEN_MC_XE_NO_SPACE = 2, }; typedef void (*xen_mc_callback_fn_t)(void *); struct callback { void (*fn)(void *); void *data; }; struct mc_buffer { unsigned int mcidx; unsigned int argidx; unsigned int cbidx; struct multicall_entry entries[32]; unsigned char args[512]; struct callback callbacks[32]; }; struct hvm_start_info { uint32_t magic; uint32_t version; uint32_t flags; uint32_t nr_modules; uint64_t modlist_paddr; uint64_t cmdline_paddr; uint64_t rsdp_paddr; uint64_t memmap_paddr; uint32_t memmap_entries; uint32_t reserved; }; struct trace_event_raw_xen_mc__batch { struct trace_entry ent; enum paravirt_lazy_mode mode; char __data[0]; }; struct trace_event_raw_xen_mc_entry { struct trace_entry ent; unsigned int op; unsigned int nargs; long unsigned int args[6]; char __data[0]; }; struct trace_event_raw_xen_mc_entry_alloc { struct trace_entry ent; size_t args; char __data[0]; }; struct trace_event_raw_xen_mc_callback { struct trace_entry ent; xen_mc_callback_fn_t fn; void *data; char __data[0]; }; struct trace_event_raw_xen_mc_flush_reason { struct trace_entry ent; enum xen_mc_flush_reason reason; char __data[0]; }; struct trace_event_raw_xen_mc_flush { struct trace_entry ent; unsigned int mcidx; unsigned int argidx; unsigned int cbidx; char __data[0]; }; struct trace_event_raw_xen_mc_extend_args { struct trace_entry ent; unsigned int op; size_t args; enum xen_mc_extend_args res; char __data[0]; }; struct trace_event_raw_xen_mmu__set_pte { struct trace_entry ent; pte_t *ptep; pteval_t pteval; char __data[0]; }; struct trace_event_raw_xen_mmu_set_pmd { struct trace_entry ent; pmd_t *pmdp; pmdval_t pmdval; char __data[0]; }; struct trace_event_raw_xen_mmu_set_pud { struct trace_entry ent; pud_t *pudp; pudval_t pudval; char __data[0]; }; struct trace_event_raw_xen_mmu_set_p4d { struct trace_entry ent; p4d_t *p4dp; p4d_t *user_p4dp; p4dval_t p4dval; char __data[0]; }; struct trace_event_raw_xen_mmu_ptep_modify_prot { struct trace_entry ent; struct mm_struct *mm; long unsigned int addr; pte_t *ptep; pteval_t pteval; char __data[0]; }; struct trace_event_raw_xen_mmu_alloc_ptpage { struct trace_entry ent; struct mm_struct *mm; long unsigned int pfn; unsigned int level; bool pinned; char __data[0]; }; struct trace_event_raw_xen_mmu_release_ptpage { struct trace_entry ent; long unsigned int pfn; unsigned int level; bool pinned; char __data[0]; }; struct trace_event_raw_xen_mmu_pgd { struct trace_entry ent; struct mm_struct *mm; pgd_t *pgd; char __data[0]; }; struct trace_event_raw_xen_mmu_flush_tlb_one_user { struct trace_entry ent; long unsigned int addr; char __data[0]; }; struct trace_event_raw_xen_mmu_flush_tlb_multi { struct trace_entry ent; unsigned int ncpus; struct mm_struct *mm; long unsigned int addr; long unsigned int end; char __data[0]; }; struct trace_event_raw_xen_mmu_write_cr3 { struct trace_entry ent; bool kernel; long unsigned int cr3; char __data[0]; }; struct trace_event_raw_xen_cpu_write_ldt_entry { struct trace_entry ent; struct desc_struct *dt; int entrynum; u64 desc; char __data[0]; }; struct trace_event_raw_xen_cpu_write_idt_entry { struct trace_entry ent; gate_desc *dt; int entrynum; char __data[0]; }; struct trace_event_raw_xen_cpu_load_idt { struct trace_entry ent; long unsigned int addr; char __data[0]; }; struct trace_event_raw_xen_cpu_write_gdt_entry { struct trace_entry ent; u64 desc; struct desc_struct *dt; int entrynum; int type; char __data[0]; }; struct trace_event_raw_xen_cpu_set_ldt { struct trace_entry ent; const void *addr; unsigned int entries; char __data[0]; }; struct trace_event_data_offsets_xen_mc__batch {}; struct trace_event_data_offsets_xen_mc_entry {}; struct trace_event_data_offsets_xen_mc_entry_alloc {}; struct trace_event_data_offsets_xen_mc_callback {}; struct trace_event_data_offsets_xen_mc_flush_reason {}; struct trace_event_data_offsets_xen_mc_flush {}; struct trace_event_data_offsets_xen_mc_extend_args {}; struct trace_event_data_offsets_xen_mmu__set_pte {}; struct trace_event_data_offsets_xen_mmu_set_pmd {}; struct trace_event_data_offsets_xen_mmu_set_pud {}; struct trace_event_data_offsets_xen_mmu_set_p4d {}; struct trace_event_data_offsets_xen_mmu_ptep_modify_prot {}; struct trace_event_data_offsets_xen_mmu_alloc_ptpage {}; struct trace_event_data_offsets_xen_mmu_release_ptpage {}; struct trace_event_data_offsets_xen_mmu_pgd {}; struct trace_event_data_offsets_xen_mmu_flush_tlb_one_user {}; struct trace_event_data_offsets_xen_mmu_flush_tlb_multi {}; struct trace_event_data_offsets_xen_mmu_write_cr3 {}; struct trace_event_data_offsets_xen_cpu_write_ldt_entry {}; struct trace_event_data_offsets_xen_cpu_write_idt_entry {}; struct trace_event_data_offsets_xen_cpu_load_idt {}; struct trace_event_data_offsets_xen_cpu_write_gdt_entry {}; struct trace_event_data_offsets_xen_cpu_set_ldt {}; typedef void (*btf_trace_xen_mc_batch)(void *, enum paravirt_lazy_mode); typedef void (*btf_trace_xen_mc_issue)(void *, enum paravirt_lazy_mode); typedef void (*btf_trace_xen_mc_entry)(void *, struct multicall_entry *, unsigned int); typedef void (*btf_trace_xen_mc_entry_alloc)(void *, size_t); typedef void (*btf_trace_xen_mc_callback)(void *, xen_mc_callback_fn_t, void *); typedef void (*btf_trace_xen_mc_flush_reason)(void *, enum xen_mc_flush_reason); typedef void (*btf_trace_xen_mc_flush)(void *, unsigned int, unsigned int, unsigned int); typedef void (*btf_trace_xen_mc_extend_args)(void *, long unsigned int, size_t, enum xen_mc_extend_args); typedef void (*btf_trace_xen_mmu_set_pte)(void *, pte_t *, pte_t); typedef void (*btf_trace_xen_mmu_set_pmd)(void *, pmd_t *, pmd_t); typedef void (*btf_trace_xen_mmu_set_pud)(void *, pud_t *, pud_t); typedef void (*btf_trace_xen_mmu_set_p4d)(void *, p4d_t *, p4d_t *, p4d_t); typedef void (*btf_trace_xen_mmu_ptep_modify_prot_start)(void *, struct mm_struct *, long unsigned int, pte_t *, pte_t); typedef void (*btf_trace_xen_mmu_ptep_modify_prot_commit)(void *, struct mm_struct *, long unsigned int, pte_t *, pte_t); typedef void (*btf_trace_xen_mmu_alloc_ptpage)(void *, struct mm_struct *, long unsigned int, unsigned int, bool); typedef void (*btf_trace_xen_mmu_release_ptpage)(void *, long unsigned int, unsigned int, bool); typedef void (*btf_trace_xen_mmu_pgd_pin)(void *, struct mm_struct *, pgd_t *); typedef void (*btf_trace_xen_mmu_pgd_unpin)(void *, struct mm_struct *, pgd_t *); typedef void (*btf_trace_xen_mmu_flush_tlb_one_user)(void *, long unsigned int); typedef void (*btf_trace_xen_mmu_flush_tlb_multi)(void *, const struct cpumask *, struct mm_struct *, long unsigned int, long unsigned int); typedef void (*btf_trace_xen_mmu_write_cr3)(void *, bool, long unsigned int); typedef void (*btf_trace_xen_cpu_write_ldt_entry)(void *, struct desc_struct *, int, u64); typedef void (*btf_trace_xen_cpu_write_idt_entry)(void *, gate_desc *, int, const gate_desc *); typedef void (*btf_trace_xen_cpu_load_idt)(void *, const struct desc_ptr *); typedef void (*btf_trace_xen_cpu_write_gdt_entry)(void *, struct desc_struct *, int, const void *, int); typedef void (*btf_trace_xen_cpu_set_ldt)(void *, const void *, unsigned int); enum ipi_vector { XEN_RESCHEDULE_VECTOR = 0, XEN_CALL_FUNCTION_VECTOR = 1, XEN_CALL_FUNCTION_SINGLE_VECTOR = 2, XEN_SPIN_UNLOCK_VECTOR = 3, XEN_IRQ_WORK_VECTOR = 4, XEN_NMI_VECTOR = 5, XEN_NR_IPIS = 6, }; struct xen_common_irq { int irq; char *name; }; struct cpu_user_regs { uint64_t r15; uint64_t r14; uint64_t r13; uint64_t r12; union { uint64_t rbp; uint64_t ebp; uint32_t _ebp; }; union { uint64_t rbx; uint64_t ebx; uint32_t _ebx; }; uint64_t r11; uint64_t r10; uint64_t r9; uint64_t r8; union { uint64_t rax; uint64_t eax; uint32_t _eax; }; union { uint64_t rcx; uint64_t ecx; uint32_t _ecx; }; union { uint64_t rdx; uint64_t edx; uint32_t _edx; }; union { uint64_t rsi; uint64_t esi; uint32_t _esi; }; union { uint64_t rdi; uint64_t edi; uint32_t _edi; }; uint32_t error_code; uint32_t entry_vector; union { uint64_t rip; uint64_t eip; uint32_t _eip; }; uint16_t cs; uint16_t _pad0[1]; uint8_t saved_upcall_mask; uint8_t _pad1[3]; union { uint64_t rflags; uint64_t eflags; uint32_t _eflags; }; union { uint64_t rsp; uint64_t esp; uint32_t _esp; }; uint16_t ss; uint16_t _pad2[3]; uint16_t es; uint16_t _pad3[3]; uint16_t ds; uint16_t _pad4[3]; uint16_t fs; uint16_t _pad5[3]; uint16_t gs; uint16_t _pad6[3]; }; struct vcpu_guest_context { struct { char x[512]; } fpu_ctxt; long unsigned int flags; struct cpu_user_regs user_regs; struct trap_info trap_ctxt[256]; long unsigned int ldt_base; long unsigned int ldt_ents; long unsigned int gdt_frames[16]; long unsigned int gdt_ents; long unsigned int kernel_ss; long unsigned int kernel_sp; long unsigned int ctrlreg[8]; long unsigned int debugreg[8]; long unsigned int event_callback_eip; long unsigned int failsafe_callback_eip; long unsigned int syscall_callback_eip; long unsigned int vm_assist; uint64_t fs_base; uint64_t gs_base_kernel; uint64_t gs_base_user; }; struct sg_table { struct scatterlist *sgl; unsigned int nents; unsigned int orig_nents; }; enum swiotlb_force { SWIOTLB_NORMAL = 0, SWIOTLB_FORCE = 1, SWIOTLB_NO_FORCE = 2, }; struct iommu_table_entry { initcall_t detect; initcall_t depend; void (*early_init)(); void (*late_init)(); int flags; }; union efi_boot_services; typedef union efi_boot_services efi_boot_services_t; typedef struct { efi_table_hdr_t hdr; u32 fw_vendor; u32 fw_revision; u32 con_in_handle; u32 con_in; u32 con_out_handle; u32 con_out; u32 stderr_handle; u32 stderr; u32 runtime; u32 boottime; u32 nr_tables; u32 tables; } efi_system_table_32_t; union efi_simple_text_input_protocol; typedef union efi_simple_text_input_protocol efi_simple_text_input_protocol_t; union efi_simple_text_output_protocol; typedef union efi_simple_text_output_protocol efi_simple_text_output_protocol_t; typedef union { struct { efi_table_hdr_t hdr; long unsigned int fw_vendor; u32 fw_revision; long unsigned int con_in_handle; efi_simple_text_input_protocol_t *con_in; long unsigned int con_out_handle; efi_simple_text_output_protocol_t *con_out; long unsigned int stderr_handle; long unsigned int stderr; efi_runtime_services_t *runtime; efi_boot_services_t *boottime; long unsigned int nr_tables; long unsigned int tables; }; efi_system_table_32_t mixed_mode; } efi_system_table_t; enum efi_secureboot_mode { efi_secureboot_mode_unset = 0, efi_secureboot_mode_unknown = 1, efi_secureboot_mode_disabled = 2, efi_secureboot_mode_enabled = 3, }; struct hvm_modlist_entry { uint64_t paddr; uint64_t size; uint64_t cmdline_paddr; uint64_t reserved; }; struct hvm_memmap_table_entry { uint64_t addr; uint64_t size; uint32_t type; uint32_t reserved; }; enum { WORK_STRUCT_PENDING_BIT = 0, WORK_STRUCT_DELAYED_BIT = 1, WORK_STRUCT_PWQ_BIT = 2, WORK_STRUCT_LINKED_BIT = 3, WORK_STRUCT_COLOR_SHIFT = 4, WORK_STRUCT_COLOR_BITS = 4, WORK_STRUCT_PENDING = 1, WORK_STRUCT_DELAYED = 2, WORK_STRUCT_PWQ = 4, WORK_STRUCT_LINKED = 8, WORK_STRUCT_STATIC = 0, WORK_NR_COLORS = 15, WORK_NO_COLOR = 15, WORK_CPU_UNBOUND = 320, WORK_STRUCT_FLAG_BITS = 8, WORK_OFFQ_FLAG_BASE = 4, __WORK_OFFQ_CANCELING = 4, WORK_OFFQ_CANCELING = 16, WORK_OFFQ_FLAG_BITS = 1, WORK_OFFQ_POOL_SHIFT = 5, WORK_OFFQ_LEFT = 59, WORK_OFFQ_POOL_BITS = 31, WORK_OFFQ_POOL_NONE = 2147483647, WORK_STRUCT_FLAG_MASK = 255, WORK_STRUCT_WQ_DATA_MASK = 4294967040, WORK_STRUCT_NO_POOL = 4294967264, WORK_BUSY_PENDING = 1, WORK_BUSY_RUNNING = 2, WORKER_DESC_LEN = 24, }; enum { MEMREMAP_WB = 1, MEMREMAP_WT = 2, MEMREMAP_WC = 4, MEMREMAP_ENC = 8, MEMREMAP_DEC = 16, }; enum hv_isolation_type { HV_ISOLATION_TYPE_NONE = 0, HV_ISOLATION_TYPE_VBS = 1, HV_ISOLATION_TYPE_SNP = 2, }; union hv_x64_msr_hypercall_contents { u64 as_uint64; struct { u64 enable: 1; u64 reserved: 11; u64 guest_physical_address: 52; }; }; struct hv_reenlightenment_control { __u64 vector: 8; __u64 reserved1: 8; __u64 enabled: 1; __u64 reserved2: 15; __u64 target_vp: 32; }; struct hv_tsc_emulation_control { __u64 enabled: 1; __u64 reserved: 63; }; struct hv_tsc_emulation_status { __u64 inprogress: 1; __u64 reserved: 63; }; struct hv_nested_enlightenments_control { struct { __u32 directhypercall: 1; __u32 reserved: 31; } features; struct { __u32 reserved; } hypercallControls; }; struct hv_vp_assist_page { __u32 apic_assist; __u32 reserved1; __u64 vtl_control[3]; struct hv_nested_enlightenments_control nested_control; __u8 enlighten_vmentry; __u8 reserved2[7]; __u64 current_nested_vmcs; }; struct hv_get_partition_id { u64 partition_id; }; struct ms_hyperv_info { u32 features; u32 priv_high; u32 misc_features; u32 hints; u32 nested_features; u32 max_vp_index; u32 max_lp_index; u32 isolation_config_a; u32 isolation_config_b; }; enum HV_GENERIC_SET_FORMAT { HV_GENERIC_SET_SPARSE_4K = 0, HV_GENERIC_SET_ALL = 1, }; struct hv_vpset { u64 format; u64 valid_bank_mask; u64 bank_contents[0]; }; struct hv_tlb_flush { u64 address_space; u64 flags; u64 processor_mask; u64 gva_list[0]; }; struct hv_tlb_flush_ex { u64 address_space; u64 flags; struct hv_vpset hv_vp_set; u64 gva_list[0]; }; struct trace_event_raw_hyperv_mmu_flush_tlb_multi { struct trace_entry ent; unsigned int ncpus; struct mm_struct *mm; long unsigned int addr; long unsigned int end; char __data[0]; }; struct trace_event_raw_hyperv_nested_flush_guest_mapping { struct trace_entry ent; u64 as; int ret; char __data[0]; }; struct trace_event_raw_hyperv_nested_flush_guest_mapping_range { struct trace_entry ent; u64 as; int ret; char __data[0]; }; struct trace_event_raw_hyperv_send_ipi_mask { struct trace_entry ent; unsigned int ncpus; int vector; char __data[0]; }; struct trace_event_raw_hyperv_send_ipi_one { struct trace_entry ent; int cpu; int vector; char __data[0]; }; struct trace_event_data_offsets_hyperv_mmu_flush_tlb_multi {}; struct trace_event_data_offsets_hyperv_nested_flush_guest_mapping {}; struct trace_event_data_offsets_hyperv_nested_flush_guest_mapping_range {}; struct trace_event_data_offsets_hyperv_send_ipi_mask {}; struct trace_event_data_offsets_hyperv_send_ipi_one {}; typedef void (*btf_trace_hyperv_mmu_flush_tlb_multi)(void *, const struct cpumask *, const struct flush_tlb_info *); typedef void (*btf_trace_hyperv_nested_flush_guest_mapping)(void *, u64, int); typedef void (*btf_trace_hyperv_nested_flush_guest_mapping_range)(void *, u64, int); typedef void (*btf_trace_hyperv_send_ipi_mask)(void *, const struct cpumask *, int); typedef void (*btf_trace_hyperv_send_ipi_one)(void *, int, int); struct hv_guest_mapping_flush { u64 address_space; u64 flags; }; union hv_gpa_page_range { u64 address_space; struct { u64 additional_pages: 11; u64 largepage: 1; u64 basepfn: 52; } page; struct { u64 reserved: 12; u64 page_size: 1; u64 reserved1: 8; u64 base_large_pfn: 43; }; }; struct hv_guest_mapping_flush_list { u64 address_space; u64 flags; union hv_gpa_page_range gpa_list[510]; }; typedef int (*hyperv_fill_flush_list_func)(struct hv_guest_mapping_flush_list *, void *); struct acpi_device; struct pci_sysdata { int domain; int node; struct acpi_device *companion; void *iommu; void *fwnode; struct pci_dev *vmd_dev; }; enum { IRQCHIP_SET_TYPE_MASKED = 1, IRQCHIP_EOI_IF_HANDLED = 2, IRQCHIP_MASK_ON_SUSPEND = 4, IRQCHIP_ONOFFLINE_ENABLED = 8, IRQCHIP_SKIP_SET_WAKE = 16, IRQCHIP_ONESHOT_SAFE = 32, IRQCHIP_EOI_THREADED = 64, IRQCHIP_SUPPORTS_LEVEL_MSI = 128, IRQCHIP_SUPPORTS_NMI = 256, IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND = 512, IRQCHIP_AFFINITY_PRE_STARTUP = 1024, }; enum irq_alloc_type { X86_IRQ_ALLOC_TYPE_IOAPIC = 1, X86_IRQ_ALLOC_TYPE_HPET = 2, X86_IRQ_ALLOC_TYPE_PCI_MSI = 3, X86_IRQ_ALLOC_TYPE_PCI_MSIX = 4, X86_IRQ_ALLOC_TYPE_DMAR = 5, X86_IRQ_ALLOC_TYPE_AMDVI = 6, X86_IRQ_ALLOC_TYPE_UV = 7, }; struct ioapic_alloc_info { int pin; int node; u32 is_level: 1; u32 active_low: 1; u32 valid: 1; }; struct uv_alloc_info { int limit; int blade; long unsigned int offset; char *name; }; struct irq_alloc_info { enum irq_alloc_type type; u32 flags; u32 devid; irq_hw_number_t hwirq; const struct cpumask *mask; struct msi_desc *desc; void *data; union { struct ioapic_alloc_info ioapic; struct uv_alloc_info uv; }; }; struct irq_cfg { unsigned int dest_apicid; unsigned int vector; }; enum { IRQCHIP_FWNODE_REAL = 0, IRQCHIP_FWNODE_NAMED = 1, IRQCHIP_FWNODE_NAMED_ID = 2, }; typedef struct irq_alloc_info msi_alloc_info_t; struct msi_domain_info; struct msi_domain_ops { irq_hw_number_t (*get_hwirq)(struct msi_domain_info *, msi_alloc_info_t *); int (*msi_init)(struct irq_domain *, struct msi_domain_info *, unsigned int, irq_hw_number_t, msi_alloc_info_t *); void (*msi_free)(struct irq_domain *, struct msi_domain_info *, unsigned int); int (*msi_check)(struct irq_domain *, struct msi_domain_info *, struct device *); int (*msi_prepare)(struct irq_domain *, struct device *, int, msi_alloc_info_t *); void (*msi_finish)(msi_alloc_info_t *, int); void (*set_desc)(msi_alloc_info_t *, struct msi_desc *); int (*handle_error)(struct irq_domain *, struct msi_desc *, int); int (*domain_alloc_irqs)(struct irq_domain *, struct device *, int); void (*domain_free_irqs)(struct irq_domain *, struct device *); }; struct msi_domain_info { u32 flags; struct msi_domain_ops *ops; struct irq_chip *chip; void *chip_data; irq_flow_handler_t handler; void *handler_data; const char *handler_name; void *data; }; enum { MSI_FLAG_USE_DEF_DOM_OPS = 1, MSI_FLAG_USE_DEF_CHIP_OPS = 2, MSI_FLAG_MULTI_PCI_MSI = 4, MSI_FLAG_PCI_MSIX = 8, MSI_FLAG_ACTIVATE_EARLY = 16, MSI_FLAG_MUST_REACTIVATE = 32, MSI_FLAG_LEVEL_CAPABLE = 64, }; enum hv_interrupt_type { HV_X64_INTERRUPT_TYPE_FIXED = 0, HV_X64_INTERRUPT_TYPE_LOWESTPRIORITY = 1, HV_X64_INTERRUPT_TYPE_SMI = 2, HV_X64_INTERRUPT_TYPE_REMOTEREAD = 3, HV_X64_INTERRUPT_TYPE_NMI = 4, HV_X64_INTERRUPT_TYPE_INIT = 5, HV_X64_INTERRUPT_TYPE_SIPI = 6, HV_X64_INTERRUPT_TYPE_EXTINT = 7, HV_X64_INTERRUPT_TYPE_LOCALINT0 = 8, HV_X64_INTERRUPT_TYPE_LOCALINT1 = 9, HV_X64_INTERRUPT_TYPE_MAXIMUM = 10, }; union hv_msi_address_register { u32 as_uint32; struct { u32 reserved1: 2; u32 destination_mode: 1; u32 redirection_hint: 1; u32 reserved2: 8; u32 destination_id: 8; u32 msi_base: 12; }; }; union hv_msi_data_register { u32 as_uint32; struct { u32 vector: 8; u32 delivery_mode: 3; u32 reserved1: 3; u32 level_assert: 1; u32 trigger_mode: 1; u32 reserved2: 16; }; }; union hv_msi_entry { u64 as_uint64; struct { union hv_msi_address_register address; union hv_msi_data_register data; }; }; union hv_ioapic_rte { u64 as_uint64; struct { u32 vector: 8; u32 delivery_mode: 3; u32 destination_mode: 1; u32 delivery_status: 1; u32 interrupt_polarity: 1; u32 remote_irr: 1; u32 trigger_mode: 1; u32 interrupt_mask: 1; u32 reserved1: 15; u32 reserved2: 24; u32 destination_id: 8; }; struct { u32 low_uint32; u32 high_uint32; }; }; struct hv_interrupt_entry { u32 source; u32 reserved1; union { union hv_msi_entry msi_entry; union hv_ioapic_rte ioapic_rte; }; }; struct hv_device_interrupt_target { u32 vector; u32 flags; union { u64 vp_mask; struct hv_vpset vp_set; }; }; enum hv_device_type { HV_DEVICE_TYPE_LOGICAL = 0, HV_DEVICE_TYPE_PCI = 1, HV_DEVICE_TYPE_IOAPIC = 2, HV_DEVICE_TYPE_ACPI = 3, }; typedef u16 hv_pci_rid; typedef u16 hv_pci_segment; union hv_pci_bdf { u16 as_uint16; struct { u8 function: 3; u8 device: 5; u8 bus; }; }; union hv_pci_bus_range { u16 as_uint16; struct { u8 subordinate_bus; u8 secondary_bus; }; }; union hv_device_id { u64 as_uint64; struct { u64 reserved0: 62; u64 device_type: 2; }; struct { u64 id: 62; u64 device_type: 2; } logical; struct { union { hv_pci_rid rid; union hv_pci_bdf bdf; }; hv_pci_segment segment; union hv_pci_bus_range shadow_bus_range; u16 phantom_function_bits: 2; u16 source_shadow: 1; u16 rsvdz0: 11; u16 device_type: 2; } pci; struct { u8 ioapic_id; u8 rsvdz0; u16 rsvdz1; u16 rsvdz2; u16 rsvdz3: 14; u16 device_type: 2; } ioapic; struct { u32 input_mapping_base; u32 input_mapping_count: 30; u32 device_type: 2; } acpi; }; enum hv_interrupt_trigger_mode { HV_INTERRUPT_TRIGGER_MODE_EDGE = 0, HV_INTERRUPT_TRIGGER_MODE_LEVEL = 1, }; struct hv_device_interrupt_descriptor { u32 interrupt_type; u32 trigger_mode; u32 vector_count; u32 reserved; struct hv_device_interrupt_target target; }; struct hv_input_map_device_interrupt { u64 partition_id; u64 device_id; u64 flags; struct hv_interrupt_entry logical_interrupt_entry; struct hv_device_interrupt_descriptor interrupt_descriptor; }; struct hv_output_map_device_interrupt { struct hv_interrupt_entry interrupt_entry; }; struct hv_input_unmap_device_interrupt { u64 partition_id; u64 device_id; struct hv_interrupt_entry interrupt_entry; }; struct rid_data { struct pci_dev *bridge; u32 rid; }; struct hv_send_ipi { u32 vector; u32 reserved; u64 cpu_mask; }; struct hv_send_ipi_ex { u32 vector; u32 reserved; struct hv_vpset vp_set; }; struct hv_deposit_memory { u64 partition_id; u64 gpa_page_list[0]; }; struct hv_proximity_domain_flags { u32 proximity_preferred: 1; u32 reserved: 30; u32 proximity_info_valid: 1; }; union hv_proximity_domain_info { struct { u32 domain_id; struct hv_proximity_domain_flags flags; }; u64 as_uint64; }; struct hv_lp_startup_status { u64 hv_status; u64 substatus1; u64 substatus2; u64 substatus3; u64 substatus4; u64 substatus5; u64 substatus6; }; struct hv_add_logical_processor_in { u32 lp_index; u32 apic_id; union hv_proximity_domain_info proximity_domain_info; u64 flags; }; struct hv_add_logical_processor_out { struct hv_lp_startup_status startup_status; }; enum HV_SUBNODE_TYPE { HvSubnodeAny = 0, HvSubnodeSocket = 1, HvSubnodeAmdNode = 2, HvSubnodeL3 = 3, HvSubnodeCount = 4, HvSubnodeInvalid = 4294967295, }; struct hv_create_vp { u64 partition_id; u32 vp_index; u8 padding[3]; u8 subnode_type; u64 subnode_id; union hv_proximity_domain_info proximity_domain_info; u64 flags; }; struct real_mode_header { u32 text_start; u32 ro_end; u32 trampoline_start; u32 trampoline_header; u32 sev_es_trampoline_start; u32 trampoline_pgd; u32 wakeup_start; u32 wakeup_header; u32 machine_real_restart_asm; u32 machine_real_restart_seg; }; struct trampoline_header { u64 start; u64 efer; u32 cr4; u32 flags; }; enum show_regs_mode { SHOW_REGS_SHORT = 0, SHOW_REGS_USER = 1, SHOW_REGS_ALL = 2, }; struct resctrl_pqr_state { u32 cur_rmid; u32 cur_closid; u32 default_rmid; u32 default_closid; }; enum which_selector { FS = 0, GS = 1, }; struct sigcontext_64 { __u64 r8; __u64 r9; __u64 r10; __u64 r11; __u64 r12; __u64 r13; __u64 r14; __u64 r15; __u64 di; __u64 si; __u64 bp; __u64 bx; __u64 dx; __u64 ax; __u64 cx; __u64 sp; __u64 ip; __u64 flags; __u16 cs; __u16 gs; __u16 fs; __u16 ss; __u64 err; __u64 trapno; __u64 oldmask; __u64 cr2; __u64 fpstate; __u64 reserved1[8]; }; struct sigaltstack { void *ss_sp; int ss_flags; size_t ss_size; }; typedef struct sigaltstack stack_t; struct siginfo { union { struct { int si_signo; int si_errno; int si_code; union __sifields _sifields; }; int _si_pad[32]; }; }; typedef struct siginfo siginfo_t; struct ksignal { struct k_sigaction ka; kernel_siginfo_t info; int sig; }; struct __large_struct { long unsigned int buf[100]; }; typedef u32 compat_sigset_word; typedef struct { compat_sigset_word sig[2]; } compat_sigset_t; struct ucontext { long unsigned int uc_flags; struct ucontext *uc_link; stack_t uc_stack; struct sigcontext_64 uc_mcontext; sigset_t uc_sigmask; }; struct kernel_vm86_regs { struct pt_regs pt; short unsigned int es; short unsigned int __esh; short unsigned int ds; short unsigned int __dsh; short unsigned int fs; short unsigned int __fsh; short unsigned int gs; short unsigned int __gsh; }; struct rt_sigframe { char *pretcode; struct ucontext uc; struct siginfo info; }; typedef s32 compat_clock_t; typedef s32 compat_pid_t; typedef s32 compat_timer_t; typedef s32 compat_int_t; typedef u32 compat_ulong_t; typedef u32 __compat_uid32_t; union compat_sigval { compat_int_t sival_int; compat_uptr_t sival_ptr; }; typedef union compat_sigval compat_sigval_t; struct compat_siginfo { int si_signo; int si_errno; int si_code; union { int _pad[29]; struct { compat_pid_t _pid; __compat_uid32_t _uid; } _kill; struct { compat_timer_t _tid; int _overrun; compat_sigval_t _sigval; } _timer; struct { compat_pid_t _pid; __compat_uid32_t _uid; compat_sigval_t _sigval; } _rt; struct { compat_pid_t _pid; __compat_uid32_t _uid; int _status; compat_clock_t _utime; compat_clock_t _stime; } _sigchld; struct { compat_uptr_t _addr; union { int _trapno; short int _addr_lsb; struct { char _dummy_bnd[4]; compat_uptr_t _lower; compat_uptr_t _upper; } _addr_bnd; struct { char _dummy_pkey[4]; u32 _pkey; } _addr_pkey; struct { compat_ulong_t _data; u32 _type; } _perf; }; } _sigfault; struct { compat_long_t _band; int _fd; } _sigpoll; struct { compat_uptr_t _call_addr; int _syscall; unsigned int _arch; } _sigsys; } _sifields; }; typedef struct compat_siginfo compat_siginfo_t; enum bug_trap_type { BUG_TRAP_TYPE_NONE = 0, BUG_TRAP_TYPE_WARN = 1, BUG_TRAP_TYPE_BUG = 2, }; enum insn_mode { INSN_MODE_32 = 0, INSN_MODE_64 = 1, INSN_MODE_KERN = 2, INSN_NUM_MODES = 3, }; typedef u8 kprobe_opcode_t; struct kprobe; struct arch_specific_insn { kprobe_opcode_t *insn; unsigned int boostable: 1; unsigned char size; union { unsigned char opcode; struct { unsigned char type; } jcc; struct { unsigned char type; unsigned char asize; } loop; struct { unsigned char reg; } indirect; }; s32 rel32; void (*emulate_op)(struct kprobe *, struct pt_regs *); int tp_len; }; typedef int (*kprobe_pre_handler_t)(struct kprobe *, struct pt_regs *); typedef void (*kprobe_post_handler_t)(struct kprobe *, struct pt_regs *, long unsigned int); struct kprobe { struct hlist_node hlist; struct list_head list; long unsigned int nmissed; kprobe_opcode_t *addr; const char *symbol_name; unsigned int offset; kprobe_pre_handler_t pre_handler; kprobe_post_handler_t post_handler; kprobe_opcode_t opcode; struct arch_specific_insn ainsn; u32 flags; }; enum die_val { DIE_OOPS = 1, DIE_INT3 = 2, DIE_DEBUG = 3, DIE_PANIC = 4, DIE_NMI = 5, DIE_DIE = 6, DIE_KERNELDEBUG = 7, DIE_TRAP = 8, DIE_GPF = 9, DIE_CALL = 10, DIE_PAGE_FAULT = 11, DIE_NMIUNKNOWN = 12, }; enum kernel_gp_hint { GP_NO_HINT = 0, GP_NON_CANONICAL = 1, GP_CANONICAL = 2, }; struct bad_iret_stack { void *error_entry_ret; struct pt_regs regs; }; typedef struct irq_desc *vector_irq_t[256]; struct trace_event_raw_x86_irq_vector { struct trace_entry ent; int vector; char __data[0]; }; struct trace_event_raw_vector_config { struct trace_entry ent; unsigned int irq; unsigned int vector; unsigned int cpu; unsigned int apicdest; char __data[0]; }; struct trace_event_raw_vector_mod { struct trace_entry ent; unsigned int irq; unsigned int vector; unsigned int cpu; unsigned int prev_vector; unsigned int prev_cpu; char __data[0]; }; struct trace_event_raw_vector_reserve { struct trace_entry ent; unsigned int irq; int ret; char __data[0]; }; struct trace_event_raw_vector_alloc { struct trace_entry ent; unsigned int irq; unsigned int vector; bool reserved; int ret; char __data[0]; }; struct trace_event_raw_vector_alloc_managed { struct trace_entry ent; unsigned int irq; unsigned int vector; int ret; char __data[0]; }; struct trace_event_raw_vector_activate { struct trace_entry ent; unsigned int irq; bool is_managed; bool can_reserve; bool reserve; char __data[0]; }; struct trace_event_raw_vector_teardown { struct trace_entry ent; unsigned int irq; bool is_managed; bool has_reserved; char __data[0]; }; struct trace_event_raw_vector_setup { struct trace_entry ent; unsigned int irq; bool is_legacy; int ret; char __data[0]; }; struct trace_event_raw_vector_free_moved { struct trace_entry ent; unsigned int irq; unsigned int cpu; unsigned int vector; bool is_managed; char __data[0]; }; struct trace_event_data_offsets_x86_irq_vector {}; struct trace_event_data_offsets_vector_config {}; struct trace_event_data_offsets_vector_mod {}; struct trace_event_data_offsets_vector_reserve {}; struct trace_event_data_offsets_vector_alloc {}; struct trace_event_data_offsets_vector_alloc_managed {}; struct trace_event_data_offsets_vector_activate {}; struct trace_event_data_offsets_vector_teardown {}; struct trace_event_data_offsets_vector_setup {}; struct trace_event_data_offsets_vector_free_moved {}; typedef void (*btf_trace_local_timer_entry)(void *, int); typedef void (*btf_trace_local_timer_exit)(void *, int); typedef void (*btf_trace_spurious_apic_entry)(void *, int); typedef void (*btf_trace_spurious_apic_exit)(void *, int); typedef void (*btf_trace_error_apic_entry)(void *, int); typedef void (*btf_trace_error_apic_exit)(void *, int); typedef void (*btf_trace_x86_platform_ipi_entry)(void *, int); typedef void (*btf_trace_x86_platform_ipi_exit)(void *, int); typedef void (*btf_trace_irq_work_entry)(void *, int); typedef void (*btf_trace_irq_work_exit)(void *, int); typedef void (*btf_trace_reschedule_entry)(void *, int); typedef void (*btf_trace_reschedule_exit)(void *, int); typedef void (*btf_trace_call_function_entry)(void *, int); typedef void (*btf_trace_call_function_exit)(void *, int); typedef void (*btf_trace_call_function_single_entry)(void *, int); typedef void (*btf_trace_call_function_single_exit)(void *, int); typedef void (*btf_trace_threshold_apic_entry)(void *, int); typedef void (*btf_trace_threshold_apic_exit)(void *, int); typedef void (*btf_trace_deferred_error_apic_entry)(void *, int); typedef void (*btf_trace_deferred_error_apic_exit)(void *, int); typedef void (*btf_trace_thermal_apic_entry)(void *, int); typedef void (*btf_trace_thermal_apic_exit)(void *, int); typedef void (*btf_trace_vector_config)(void *, unsigned int, unsigned int, unsigned int, unsigned int); typedef void (*btf_trace_vector_update)(void *, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); typedef void (*btf_trace_vector_clear)(void *, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); typedef void (*btf_trace_vector_reserve_managed)(void *, unsigned int, int); typedef void (*btf_trace_vector_reserve)(void *, unsigned int, int); typedef void (*btf_trace_vector_alloc)(void *, unsigned int, unsigned int, bool, int); typedef void (*btf_trace_vector_alloc_managed)(void *, unsigned int, unsigned int, int); typedef void (*btf_trace_vector_activate)(void *, unsigned int, bool, bool, bool); typedef void (*btf_trace_vector_deactivate)(void *, unsigned int, bool, bool, bool); typedef void (*btf_trace_vector_teardown)(void *, unsigned int, bool, bool); typedef void (*btf_trace_vector_setup)(void *, unsigned int, bool, int); typedef void (*btf_trace_vector_free_moved)(void *, unsigned int, unsigned int, unsigned int, bool); struct irq_stack { char stack[16384]; }; struct estack_pages { u32 offs; u16 size; u16 type; }; enum lockdown_reason { LOCKDOWN_NONE = 0, LOCKDOWN_MODULE_SIGNATURE = 1, LOCKDOWN_DEV_MEM = 2, LOCKDOWN_EFI_TEST = 3, LOCKDOWN_KEXEC = 4, LOCKDOWN_HIBERNATION = 5, LOCKDOWN_PCI_ACCESS = 6, LOCKDOWN_IOPORT = 7, LOCKDOWN_MSR = 8, LOCKDOWN_ACPI_TABLES = 9, LOCKDOWN_PCMCIA_CIS = 10, LOCKDOWN_TIOCSSERIAL = 11, LOCKDOWN_MODULE_PARAMETERS = 12, LOCKDOWN_MMIOTRACE = 13, LOCKDOWN_DEBUGFS = 14, LOCKDOWN_XMON_WR = 15, LOCKDOWN_BPF_WRITE_USER = 16, LOCKDOWN_INTEGRITY_MAX = 17, LOCKDOWN_KCORE = 18, LOCKDOWN_KPROBES = 19, LOCKDOWN_BPF_READ_KERNEL = 20, LOCKDOWN_PERF = 21, LOCKDOWN_TRACEFS = 22, LOCKDOWN_XMON_RW = 23, LOCKDOWN_XFRM_SECRET = 24, LOCKDOWN_CONFIDENTIALITY_MAX = 25, }; enum lockdep_ok { LOCKDEP_STILL_OK = 0, LOCKDEP_NOW_UNRELIABLE = 1, }; struct trace_event_raw_nmi_handler { struct trace_entry ent; void *handler; s64 delta_ns; int handled; char __data[0]; }; struct trace_event_data_offsets_nmi_handler {}; typedef void (*btf_trace_nmi_handler)(void *, void *, s64, int); struct nmi_desc { raw_spinlock_t lock; struct list_head head; }; struct nmi_stats { unsigned int normal; unsigned int unknown; unsigned int external; unsigned int swallow; }; enum nmi_states { NMI_NOT_RUNNING = 0, NMI_EXECUTING = 1, NMI_LATCHED = 2, }; struct user_desc { unsigned int entry_number; unsigned int base_addr; unsigned int limit; unsigned int seg_32bit: 1; unsigned int contents: 2; unsigned int read_exec_only: 1; unsigned int limit_in_pages: 1; unsigned int seg_not_present: 1; unsigned int useable: 1; unsigned int lm: 1; }; enum con_scroll { SM_UP = 0, SM_DOWN = 1, }; enum vc_intensity { VCI_HALF_BRIGHT = 0, VCI_NORMAL = 1, VCI_BOLD = 2, VCI_MASK = 3, }; struct vc_data; struct console_font; struct consw { struct module *owner; const char * (*con_startup)(); void (*con_init)(struct vc_data *, int); void (*con_deinit)(struct vc_data *); void (*con_clear)(struct vc_data *, int, int, int, int); void (*con_putc)(struct vc_data *, int, int, int); void (*con_putcs)(struct vc_data *, const short unsigned int *, int, int, int); void (*con_cursor)(struct vc_data *, int); bool (*con_scroll)(struct vc_data *, unsigned int, unsigned int, enum con_scroll, unsigned int); int (*con_switch)(struct vc_data *); int (*con_blank)(struct vc_data *, int, int); int (*con_font_set)(struct vc_data *, struct console_font *, unsigned int); int (*con_font_get)(struct vc_data *, struct console_font *); int (*con_font_default)(struct vc_data *, struct console_font *, char *); int (*con_resize)(struct vc_data *, unsigned int, unsigned int, unsigned int); void (*con_set_palette)(struct vc_data *, const unsigned char *); void (*con_scrolldelta)(struct vc_data *, int); int (*con_set_origin)(struct vc_data *); void (*con_save_screen)(struct vc_data *); u8 (*con_build_attr)(struct vc_data *, u8, enum vc_intensity, bool, bool, bool, bool); void (*con_invert_region)(struct vc_data *, u16 *, int); u16 * (*con_screen_pos)(const struct vc_data *, int); long unsigned int (*con_getxy)(struct vc_data *, long unsigned int, int *, int *); void (*con_flush_scrollback)(struct vc_data *); int (*con_debug_enter)(struct vc_data *); int (*con_debug_leave)(struct vc_data *); }; struct vc_state { unsigned int x; unsigned int y; unsigned char color; unsigned char Gx_charset[2]; unsigned int charset: 1; enum vc_intensity intensity; bool italic; bool underline; bool blink; bool reverse; }; struct console_font { unsigned int width; unsigned int height; unsigned int charcount; unsigned char *data; }; struct vt_mode { char mode; char waitv; short int relsig; short int acqsig; short int frsig; }; struct uni_pagedir; struct uni_screen; struct vc_data { struct tty_port port; struct vc_state state; struct vc_state saved_state; short unsigned int vc_num; unsigned int vc_cols; unsigned int vc_rows; unsigned int vc_size_row; unsigned int vc_scan_lines; unsigned int vc_cell_height; long unsigned int vc_origin; long unsigned int vc_scr_end; long unsigned int vc_visible_origin; unsigned int vc_top; unsigned int vc_bottom; const struct consw *vc_sw; short unsigned int *vc_screenbuf; unsigned int vc_screenbuf_size; unsigned char vc_mode; unsigned char vc_attr; unsigned char vc_def_color; unsigned char vc_ulcolor; unsigned char vc_itcolor; unsigned char vc_halfcolor; unsigned int vc_cursor_type; short unsigned int vc_complement_mask; short unsigned int vc_s_complement_mask; long unsigned int vc_pos; short unsigned int vc_hi_font_mask; struct console_font vc_font; short unsigned int vc_video_erase_char; unsigned int vc_state; unsigned int vc_npar; unsigned int vc_par[16]; struct vt_mode vt_mode; struct pid *vt_pid; int vt_newvt; wait_queue_head_t paste_wait; unsigned int vc_disp_ctrl: 1; unsigned int vc_toggle_meta: 1; unsigned int vc_decscnm: 1; unsigned int vc_decom: 1; unsigned int vc_decawm: 1; unsigned int vc_deccm: 1; unsigned int vc_decim: 1; unsigned int vc_priv: 3; unsigned int vc_need_wrap: 1; unsigned int vc_can_do_color: 1; unsigned int vc_report_mouse: 2; unsigned char vc_utf: 1; unsigned char vc_utf_count; int vc_utf_char; long unsigned int vc_tab_stop[4]; unsigned char vc_palette[48]; short unsigned int *vc_translate; unsigned int vc_resize_user; unsigned int vc_bell_pitch; unsigned int vc_bell_duration; short unsigned int vc_cur_blink_ms; struct vc_data **vc_display_fg; struct uni_pagedir *vc_uni_pagedir; struct uni_pagedir **vc_uni_pagedir_loc; struct uni_screen *vc_uni_screen; }; struct edd { unsigned int mbr_signature[16]; struct edd_info edd_info[6]; unsigned char mbr_signature_nr; unsigned char edd_info_nr; }; struct setup_data { __u64 next; __u32 type; __u32 len; __u8 data[0]; }; struct setup_indirect { __u32 type; __u32 reserved; __u64 len; __u64 addr; }; enum memblock_flags { MEMBLOCK_NONE = 0, MEMBLOCK_HOTPLUG = 1, MEMBLOCK_MIRROR = 2, MEMBLOCK_NOMAP = 4, }; struct memblock_region { phys_addr_t base; phys_addr_t size; enum memblock_flags flags; int nid; }; struct memblock_type { long unsigned int cnt; long unsigned int max; phys_addr_t total_size; struct memblock_region *regions; char *name; }; struct memblock { bool bottom_up; phys_addr_t current_limit; struct memblock_type memory; struct memblock_type reserved; }; struct x86_msi_ops { void (*restore_msi_irqs)(struct pci_dev *); }; struct legacy_pic { int nr_legacy_irqs; struct irq_chip *chip; void (*mask)(unsigned int); void (*unmask)(unsigned int); void (*mask_all)(); void (*restore_mask)(); void (*init)(int); int (*probe)(); int (*irq_pending)(unsigned int); void (*make_irq)(unsigned int); }; enum jump_label_type { JUMP_LABEL_NOP = 0, JUMP_LABEL_JMP = 1, }; union text_poke_insn { u8 text[5]; struct { u8 opcode; s32 disp; } __attribute__((packed)); }; struct jump_label_patch { const void *code; int size; }; enum { JL_STATE_START = 0, JL_STATE_NO_UPDATE = 1, JL_STATE_UPDATE = 2, }; typedef short unsigned int __kernel_old_uid_t; typedef short unsigned int __kernel_old_gid_t; typedef struct { int val[2]; } __kernel_fsid_t; typedef __kernel_old_uid_t old_uid_t; typedef __kernel_old_gid_t old_gid_t; struct kernel_clone_args { u64 flags; int *pidfd; int *child_tid; int *parent_tid; int exit_signal; long unsigned int stack; long unsigned int stack_size; long unsigned int tls; pid_t *set_tid; size_t set_tid_size; int cgroup; int io_thread; struct cgroup *cgrp; struct css_set *cset; }; struct kstatfs { long int f_type; long int f_bsize; u64 f_blocks; u64 f_bfree; u64 f_bavail; u64 f_files; u64 f_ffree; __kernel_fsid_t f_fsid; long int f_namelen; long int f_frsize; long int f_flags; long int f_spare[4]; }; struct stat64 { long long unsigned int st_dev; unsigned char __pad0[4]; unsigned int __st_ino; unsigned int st_mode; unsigned int st_nlink; unsigned int st_uid; unsigned int st_gid; long long unsigned int st_rdev; unsigned char __pad3[4]; long long int st_size; unsigned int st_blksize; long long int st_blocks; unsigned int st_atime; unsigned int st_atime_nsec; unsigned int st_mtime; unsigned int st_mtime_nsec; unsigned int st_ctime; unsigned int st_ctime_nsec; long long unsigned int st_ino; } __attribute__((packed)); struct mmap_arg_struct32 { unsigned int addr; unsigned int len; unsigned int prot; unsigned int flags; unsigned int fd; unsigned int offset; }; struct vm_unmapped_area_info { long unsigned int flags; long unsigned int length; long unsigned int low_limit; long unsigned int high_limit; long unsigned int align_mask; long unsigned int align_offset; }; enum align_flags { ALIGN_VA_32 = 1, ALIGN_VA_64 = 2, }; struct va_alignment { int flags; long unsigned int mask; long unsigned int bits; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct kobj_attribute { struct attribute attr; ssize_t (*show)(struct kobject *, struct kobj_attribute *, char *); ssize_t (*store)(struct kobject *, struct kobj_attribute *, const char *, size_t); }; typedef void (*swap_func_t)(void *, void *, int); typedef int (*cmp_func_t)(const void *, const void *); enum { IORES_DESC_NONE = 0, IORES_DESC_CRASH_KERNEL = 1, IORES_DESC_ACPI_TABLES = 2, IORES_DESC_ACPI_NV_STORAGE = 3, IORES_DESC_PERSISTENT_MEMORY = 4, IORES_DESC_PERSISTENT_MEMORY_LEGACY = 5, IORES_DESC_DEVICE_PRIVATE_MEMORY = 6, IORES_DESC_RESERVED = 7, IORES_DESC_SOFT_RESERVED = 8, }; struct change_member { struct e820_entry *entry; long long unsigned int addr; }; struct iommu_fault_param; struct iopf_device_param; struct iommu_fwspec; struct dev_iommu { struct mutex lock; struct iommu_fault_param *fault_param; struct iopf_device_param *iopf_param; struct iommu_fwspec *fwspec; struct iommu_device *iommu_dev; void *priv; }; struct of_phandle_args { struct device_node *np; int args_count; uint32_t args[16]; }; struct iommu_fault_unrecoverable { __u32 reason; __u32 flags; __u32 pasid; __u32 perm; __u64 addr; __u64 fetch_addr; }; struct iommu_fault_page_request { __u32 flags; __u32 pasid; __u32 grpid; __u32 perm; __u64 addr; __u64 private_data[2]; }; struct iommu_fault { __u32 type; __u32 padding; union { struct iommu_fault_unrecoverable event; struct iommu_fault_page_request prm; __u8 padding2[56]; }; }; struct iommu_page_response { __u32 argsz; __u32 version; __u32 flags; __u32 pasid; __u32 grpid; __u32 code; }; struct iommu_inv_addr_info { __u32 flags; __u32 archid; __u64 pasid; __u64 addr; __u64 granule_size; __u64 nb_granules; }; struct iommu_inv_pasid_info { __u32 flags; __u32 archid; __u64 pasid; }; struct iommu_cache_invalidate_info { __u32 argsz; __u32 version; __u8 cache; __u8 granularity; __u8 padding[6]; union { struct iommu_inv_pasid_info pasid_info; struct iommu_inv_addr_info addr_info; } granu; }; struct iommu_gpasid_bind_data_vtd { __u64 flags; __u32 pat; __u32 emt; }; struct iommu_gpasid_bind_data { __u32 argsz; __u32 version; __u32 format; __u32 addr_width; __u64 flags; __u64 gpgd; __u64 hpasid; __u64 gpasid; __u8 padding[8]; union { struct iommu_gpasid_bind_data_vtd vtd; } vendor; }; typedef int (*iommu_fault_handler_t)(struct iommu_domain *, struct device *, long unsigned int, int, void *); struct iommu_domain_geometry { dma_addr_t aperture_start; dma_addr_t aperture_end; bool force_aperture; }; struct iommu_domain { unsigned int type; const struct iommu_ops *ops; long unsigned int pgsize_bitmap; iommu_fault_handler_t handler; void *handler_token; struct iommu_domain_geometry geometry; void *iova_cookie; }; typedef int (*iommu_dev_fault_handler_t)(struct iommu_fault *, void *); enum iommu_resv_type { IOMMU_RESV_DIRECT = 0, IOMMU_RESV_DIRECT_RELAXABLE = 1, IOMMU_RESV_RESERVED = 2, IOMMU_RESV_MSI = 3, IOMMU_RESV_SW_MSI = 4, }; struct iommu_resv_region { struct list_head list; phys_addr_t start; size_t length; int prot; enum iommu_resv_type type; }; struct iommu_iotlb_gather { long unsigned int start; long unsigned int end; size_t pgsize; struct page *freelist; }; struct iommu_device { struct list_head list; const struct iommu_ops *ops; struct fwnode_handle *fwnode; struct device *dev; }; struct iommu_sva { struct device *dev; }; struct iommu_fault_event { struct iommu_fault fault; struct list_head list; }; struct iommu_fault_param { iommu_dev_fault_handler_t handler; void *data; struct list_head faults; struct mutex lock; }; struct iommu_fwspec { const struct iommu_ops *ops; struct fwnode_handle *iommu_fwnode; u32 flags; unsigned int num_ids; u32 ids[0]; }; enum dmi_field { DMI_NONE = 0, DMI_BIOS_VENDOR = 1, DMI_BIOS_VERSION = 2, DMI_BIOS_DATE = 3, DMI_BIOS_RELEASE = 4, DMI_EC_FIRMWARE_RELEASE = 5, DMI_SYS_VENDOR = 6, DMI_PRODUCT_NAME = 7, DMI_PRODUCT_VERSION = 8, DMI_PRODUCT_SERIAL = 9, DMI_PRODUCT_UUID = 10, DMI_PRODUCT_SKU = 11, DMI_PRODUCT_FAMILY = 12, DMI_BOARD_VENDOR = 13, DMI_BOARD_NAME = 14, DMI_BOARD_VERSION = 15, DMI_BOARD_SERIAL = 16, DMI_BOARD_ASSET_TAG = 17, DMI_CHASSIS_VENDOR = 18, DMI_CHASSIS_TYPE = 19, DMI_CHASSIS_VERSION = 20, DMI_CHASSIS_SERIAL = 21, DMI_CHASSIS_ASSET_TAG = 22, DMI_STRING_MAX = 23, DMI_OEM_STRING = 24, }; enum { NONE_FORCE_HPET_RESUME = 0, OLD_ICH_FORCE_HPET_RESUME = 1, ICH_FORCE_HPET_RESUME = 2, VT8237_FORCE_HPET_RESUME = 3, NVIDIA_FORCE_HPET_RESUME = 4, ATI_FORCE_HPET_RESUME = 5, }; enum meminit_context { MEMINIT_EARLY = 0, MEMINIT_HOTPLUG = 1, }; struct cpu { int node_id; int hotpluggable; struct device dev; }; struct x86_cpu { struct cpu cpu; }; struct debugfs_blob_wrapper { void *data; long unsigned int size; }; struct setup_data_node { u64 paddr; u32 type; u32 len; }; struct paravirt_patch_site { u8 *instr; u8 type; u8 len; }; struct die_args { struct pt_regs *regs; const char *str; long int err; int trapnr; int signr; }; struct tlb_state_shared { bool is_lazy; }; struct smp_alt_module { struct module *mod; char *name; const s32 *locks; const s32 *locks_end; u8 *text; u8 *text_end; struct list_head next; }; typedef struct { struct mm_struct *mm; } temp_mm_state_t; struct text_poke_loc { s32 rel_addr; s32 rel32; u8 opcode; const u8 text[5]; u8 old; }; struct bp_patching_desc { struct text_poke_loc *vec; int nr_entries; atomic_t refs; }; enum { HW_BREAKPOINT_LEN_1 = 1, HW_BREAKPOINT_LEN_2 = 2, HW_BREAKPOINT_LEN_3 = 3, HW_BREAKPOINT_LEN_4 = 4, HW_BREAKPOINT_LEN_5 = 5, HW_BREAKPOINT_LEN_6 = 6, HW_BREAKPOINT_LEN_7 = 7, HW_BREAKPOINT_LEN_8 = 8, }; enum { HW_BREAKPOINT_EMPTY = 0, HW_BREAKPOINT_R = 1, HW_BREAKPOINT_W = 2, HW_BREAKPOINT_RW = 3, HW_BREAKPOINT_X = 4, HW_BREAKPOINT_INVALID = 7, }; typedef unsigned int u_int; typedef long long unsigned int cycles_t; struct system_counterval_t { u64 cycles; struct clocksource *cs; }; typedef struct { seqcount_t seqcount; } seqcount_latch_t; enum cpufreq_table_sorting { CPUFREQ_TABLE_UNSORTED = 0, CPUFREQ_TABLE_SORTED_ASCENDING = 1, CPUFREQ_TABLE_SORTED_DESCENDING = 2, }; struct cpufreq_cpuinfo { unsigned int max_freq; unsigned int min_freq; unsigned int transition_latency; }; struct clk; struct cpufreq_governor; struct cpufreq_frequency_table; struct cpufreq_stats; struct thermal_cooling_device; struct cpufreq_policy { cpumask_var_t cpus; cpumask_var_t related_cpus; cpumask_var_t real_cpus; unsigned int shared_type; unsigned int cpu; struct clk *clk; struct cpufreq_cpuinfo cpuinfo; unsigned int min; unsigned int max; unsigned int cur; unsigned int suspend_freq; unsigned int policy; unsigned int last_policy; struct cpufreq_governor *governor; void *governor_data; char last_governor[16]; struct work_struct update; struct freq_constraints constraints; struct freq_qos_request *min_freq_req; struct freq_qos_request *max_freq_req; struct cpufreq_frequency_table *freq_table; enum cpufreq_table_sorting freq_table_sorted; struct list_head policy_list; struct kobject kobj; struct completion kobj_unregister; struct rw_semaphore rwsem; bool fast_switch_possible; bool fast_switch_enabled; bool strict_target; unsigned int transition_delay_us; bool dvfs_possible_from_any_cpu; unsigned int cached_target_freq; unsigned int cached_resolved_idx; bool transition_ongoing; spinlock_t transition_lock; wait_queue_head_t transition_wait; struct task_struct *transition_task; struct cpufreq_stats *stats; void *driver_data; struct thermal_cooling_device *cdev; struct notifier_block nb_min; struct notifier_block nb_max; }; struct cpufreq_governor { char name[16]; int (*init)(struct cpufreq_policy *); void (*exit)(struct cpufreq_policy *); int (*start)(struct cpufreq_policy *); void (*stop)(struct cpufreq_policy *); void (*limits)(struct cpufreq_policy *); ssize_t (*show_setspeed)(struct cpufreq_policy *, char *); int (*store_setspeed)(struct cpufreq_policy *, unsigned int); struct list_head governor_list; struct module *owner; u8 flags; }; struct cpufreq_frequency_table { unsigned int flags; unsigned int driver_data; unsigned int frequency; }; struct cpufreq_freqs { struct cpufreq_policy *policy; unsigned int old; unsigned int new; u8 flags; }; struct cyc2ns { struct cyc2ns_data data[2]; seqcount_latch_t seq; }; struct x86_cpu_id { __u16 vendor; __u16 family; __u16 model; __u16 steppings; __u16 feature; kernel_ulong_t driver_data; }; struct muldiv { u32 multiplier; u32 divider; }; struct freq_desc { bool use_msr_plat; struct muldiv muldiv[16]; u32 freqs[16]; u32 mask; }; struct dmi_strmatch { unsigned char slot: 7; unsigned char exact_match: 1; char substr[79]; }; struct dmi_system_id { int (*callback)(const struct dmi_system_id *); const char *ident; struct dmi_strmatch matches[4]; void *driver_data; }; struct pdev_archdata {}; struct platform_device_id; struct mfd_cell; struct platform_device { const char *name; int id; bool id_auto; struct device dev; u64 platform_dma_mask; struct device_dma_parameters dma_parms; u32 num_resources; struct resource *resource; const struct platform_device_id *id_entry; char *driver_override; struct mfd_cell *mfd_cell; struct pdev_archdata archdata; }; struct platform_device_id { char name[20]; kernel_ulong_t driver_data; }; struct rtc_time { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; }; struct pnp_device_id { __u8 id[8]; kernel_ulong_t driver_data; }; struct pnp_card_device_id { __u8 id[8]; kernel_ulong_t driver_data; struct { __u8 id[8]; } devs[8]; }; struct acpi_table_header { char signature[4]; u32 length; u8 revision; u8 checksum; char oem_id[6]; char oem_table_id[8]; u32 oem_revision; char asl_compiler_id[4]; u32 asl_compiler_revision; }; struct acpi_generic_address { u8 space_id; u8 bit_width; u8 bit_offset; u8 access_width; u64 address; } __attribute__((packed)); struct acpi_table_fadt { struct acpi_table_header header; u32 facs; u32 dsdt; u8 model; u8 preferred_profile; u16 sci_interrupt; u32 smi_command; u8 acpi_enable; u8 acpi_disable; u8 s4_bios_request; u8 pstate_control; u32 pm1a_event_block; u32 pm1b_event_block; u32 pm1a_control_block; u32 pm1b_control_block; u32 pm2_control_block; u32 pm_timer_block; u32 gpe0_block; u32 gpe1_block; u8 pm1_event_length; u8 pm1_control_length; u8 pm2_control_length; u8 pm_timer_length; u8 gpe0_block_length; u8 gpe1_block_length; u8 gpe1_base; u8 cst_control; u16 c2_latency; u16 c3_latency; u16 flush_size; u16 flush_stride; u8 duty_offset; u8 duty_width; u8 day_alarm; u8 month_alarm; u8 century; u16 boot_flags; u8 reserved; u32 flags; struct acpi_generic_address reset_register; u8 reset_value; u16 arm_boot_flags; u8 minor_revision; u64 Xfacs; u64 Xdsdt; struct acpi_generic_address xpm1a_event_block; struct acpi_generic_address xpm1b_event_block; struct acpi_generic_address xpm1a_control_block; struct acpi_generic_address xpm1b_control_block; struct acpi_generic_address xpm2_control_block; struct acpi_generic_address xpm_timer_block; struct acpi_generic_address xgpe0_block; struct acpi_generic_address xgpe1_block; struct acpi_generic_address sleep_control; struct acpi_generic_address sleep_status; u64 hypervisor_id; } __attribute__((packed)); struct pnp_protocol; struct pnp_id; struct pnp_card { struct device dev; unsigned char number; struct list_head global_list; struct list_head protocol_list; struct list_head devices; struct pnp_protocol *protocol; struct pnp_id *id; char name[50]; unsigned char pnpver; unsigned char productver; unsigned int serial; unsigned char checksum; struct proc_dir_entry *procdir; }; struct pnp_dev; struct pnp_protocol { struct list_head protocol_list; char *name; int (*get)(struct pnp_dev *); int (*set)(struct pnp_dev *); int (*disable)(struct pnp_dev *); bool (*can_wakeup)(struct pnp_dev *); int (*suspend)(struct pnp_dev *, pm_message_t); int (*resume)(struct pnp_dev *); unsigned char number; struct device dev; struct list_head cards; struct list_head devices; }; struct pnp_id { char id[8]; struct pnp_id *next; }; struct pnp_card_driver; struct pnp_card_link { struct pnp_card *card; struct pnp_card_driver *driver; void *driver_data; pm_message_t pm_state; }; struct pnp_driver { const char *name; const struct pnp_device_id *id_table; unsigned int flags; int (*probe)(struct pnp_dev *, const struct pnp_device_id *); void (*remove)(struct pnp_dev *); void (*shutdown)(struct pnp_dev *); int (*suspend)(struct pnp_dev *, pm_message_t); int (*resume)(struct pnp_dev *); struct device_driver driver; }; struct pnp_card_driver { struct list_head global_list; char *name; const struct pnp_card_device_id *id_table; unsigned int flags; int (*probe)(struct pnp_card_link *, const struct pnp_card_device_id *); void (*remove)(struct pnp_card_link *); int (*suspend)(struct pnp_card_link *, pm_message_t); int (*resume)(struct pnp_card_link *); struct pnp_driver link; }; struct pnp_dev { struct device dev; u64 dma_mask; unsigned int number; int status; struct list_head global_list; struct list_head protocol_list; struct list_head card_list; struct list_head rdev_list; struct pnp_protocol *protocol; struct pnp_card *card; struct pnp_driver *driver; struct pnp_card_link *card_link; struct pnp_id *id; int active; int capabilities; unsigned int num_dependent_sets; struct list_head resources; struct list_head options; char name[50]; int flags; struct proc_dir_entry *procent; void *data; }; enum insn_type { CALL = 0, NOP = 1, JMP = 2, RET = 3, }; struct ldttss_desc { u16 limit0; u16 base0; u16 base1: 8; u16 type: 5; u16 dpl: 2; u16 p: 1; u16 limit1: 4; u16 zero0: 3; u16 g: 1; u16 base2: 8; u32 base3; u32 zero1; }; typedef struct ldttss_desc tss_desc; enum idle_boot_override { IDLE_NO_OVERRIDE = 0, IDLE_HALT = 1, IDLE_NOMWAIT = 2, IDLE_POLL = 3, }; enum tick_broadcast_mode { TICK_BROADCAST_OFF = 0, TICK_BROADCAST_ON = 1, TICK_BROADCAST_FORCE = 2, }; enum tick_broadcast_state { TICK_BROADCAST_EXIT = 0, TICK_BROADCAST_ENTER = 1, }; struct inactive_task_frame { long unsigned int r15; long unsigned int r14; long unsigned int r13; long unsigned int r12; long unsigned int bx; long unsigned int bp; long unsigned int ret_addr; }; struct fork_frame { struct inactive_task_frame frame; struct pt_regs regs; }; struct ssb_state { struct ssb_state *shared_state; raw_spinlock_t lock; unsigned int disable_state; long unsigned int local_state; }; struct trace_event_raw_x86_fpu { struct trace_entry ent; struct fpu *fpu; bool load_fpu; u64 xfeatures; u64 xcomp_bv; char __data[0]; }; struct trace_event_data_offsets_x86_fpu {}; typedef void (*btf_trace_x86_fpu_before_save)(void *, struct fpu *); typedef void (*btf_trace_x86_fpu_after_save)(void *, struct fpu *); typedef void (*btf_trace_x86_fpu_before_restore)(void *, struct fpu *); typedef void (*btf_trace_x86_fpu_after_restore)(void *, struct fpu *); typedef void (*btf_trace_x86_fpu_regs_activated)(void *, struct fpu *); typedef void (*btf_trace_x86_fpu_regs_deactivated)(void *, struct fpu *); typedef void (*btf_trace_x86_fpu_init_state)(void *, struct fpu *); typedef void (*btf_trace_x86_fpu_dropped)(void *, struct fpu *); typedef void (*btf_trace_x86_fpu_copy_src)(void *, struct fpu *); typedef void (*btf_trace_x86_fpu_copy_dst)(void *, struct fpu *); typedef void (*btf_trace_x86_fpu_xstate_check_failed)(void *, struct fpu *); struct _fpreg { __u16 significand[4]; __u16 exponent; }; struct _fpxreg { __u16 significand[4]; __u16 exponent; __u16 padding[3]; }; struct user_i387_ia32_struct { u32 cwd; u32 swd; u32 twd; u32 fip; u32 fcs; u32 foo; u32 fos; u32 st_space[20]; }; struct user32_fxsr_struct { short unsigned int cwd; short unsigned int swd; short unsigned int twd; short unsigned int fop; int fip; int fcs; int foo; int fos; int mxcsr; int reserved; int st_space[32]; int xmm_space[32]; int padding[56]; }; enum xstate_copy_mode { XSTATE_COPY_FP = 0, XSTATE_COPY_FX = 1, XSTATE_COPY_XSAVE = 2, }; struct membuf { void *p; size_t left; }; struct user_regset; typedef int user_regset_active_fn(struct task_struct *, const struct user_regset *); typedef int user_regset_get2_fn(struct task_struct *, const struct user_regset *, struct membuf); typedef int user_regset_set_fn(struct task_struct *, const struct user_regset *, unsigned int, unsigned int, const void *, const void *); typedef int user_regset_writeback_fn(struct task_struct *, const struct user_regset *, int); struct user_regset { user_regset_get2_fn *regset_get; user_regset_set_fn *set; user_regset_active_fn *active; user_regset_writeback_fn *writeback; unsigned int n; unsigned int size; unsigned int align; unsigned int bias; unsigned int core_note_type; }; struct _fpx_sw_bytes { __u32 magic1; __u32 extended_size; __u64 xfeatures; __u32 xstate_size; __u32 padding[7]; }; struct _xmmreg { __u32 element[4]; }; struct _fpstate_32 { __u32 cw; __u32 sw; __u32 tag; __u32 ipoff; __u32 cssel; __u32 dataoff; __u32 datasel; struct _fpreg _st[8]; __u16 status; __u16 magic; __u32 _fxsr_env[6]; __u32 mxcsr; __u32 reserved; struct _fpxreg _fxsr_st[8]; struct _xmmreg _xmm[8]; union { __u32 padding1[44]; __u32 padding[44]; }; union { __u32 padding2[12]; struct _fpx_sw_bytes sw_reserved; }; }; struct pkru_state { u32 pkru; u32 pad; }; struct user_regset_view { const char *name; const struct user_regset *regsets; unsigned int n; u32 e_flags; u16 e_machine; u8 ei_osabi; }; enum x86_regset { REGSET_GENERAL = 0, REGSET_FP = 1, REGSET_XFP = 2, REGSET_IOPERM64 = 2, REGSET_XSTATE = 3, REGSET_TLS = 4, REGSET_IOPERM32 = 5, }; struct pt_regs_offset { const char *name; int offset; }; typedef bool (*stack_trace_consume_fn)(void *, long unsigned int); struct stack_frame_user { const void *next_fp; long unsigned int ret_addr; }; enum cache_type { CACHE_TYPE_NOCACHE = 0, CACHE_TYPE_INST = 1, CACHE_TYPE_DATA = 2, CACHE_TYPE_SEPARATE = 3, CACHE_TYPE_UNIFIED = 4, }; struct cacheinfo { unsigned int id; enum cache_type type; unsigned int level; unsigned int coherency_line_size; unsigned int number_of_sets; unsigned int ways_of_associativity; unsigned int physical_line_partition; unsigned int size; cpumask_t shared_cpu_map; unsigned int attributes; void *fw_token; bool disable_sysfs; void *priv; }; struct cpu_cacheinfo { struct cacheinfo *info_list; unsigned int num_levels; unsigned int num_leaves; bool cpu_map_populated; }; struct amd_l3_cache { unsigned int indices; u8 subcaches[4]; }; struct threshold_block { unsigned int block; unsigned int bank; unsigned int cpu; u32 address; u16 interrupt_enable; bool interrupt_capable; u16 threshold_limit; struct kobject kobj; struct list_head miscj; }; struct threshold_bank { struct kobject *kobj; struct threshold_block *blocks; refcount_t cpus; unsigned int shared; }; struct amd_northbridge { struct pci_dev *root; struct pci_dev *misc; struct pci_dev *link; struct amd_l3_cache l3_cache; struct threshold_bank *bank4; }; struct _cache_table { unsigned char descriptor; char cache_type; short int size; }; enum _cache_type { CTYPE_NULL = 0, CTYPE_DATA = 1, CTYPE_INST = 2, CTYPE_UNIFIED = 3, }; union _cpuid4_leaf_eax { struct { enum _cache_type type: 5; unsigned int level: 3; unsigned int is_self_initializing: 1; unsigned int is_fully_associative: 1; unsigned int reserved: 4; unsigned int num_threads_sharing: 12; unsigned int num_cores_on_die: 6; } split; u32 full; }; union _cpuid4_leaf_ebx { struct { unsigned int coherency_line_size: 12; unsigned int physical_line_partition: 10; unsigned int ways_of_associativity: 10; } split; u32 full; }; union _cpuid4_leaf_ecx { struct { unsigned int number_of_sets: 32; } split; u32 full; }; struct _cpuid4_info_regs { union _cpuid4_leaf_eax eax; union _cpuid4_leaf_ebx ebx; union _cpuid4_leaf_ecx ecx; unsigned int id; long unsigned int size; struct amd_northbridge *nb; }; union l1_cache { struct { unsigned int line_size: 8; unsigned int lines_per_tag: 8; unsigned int assoc: 8; unsigned int size_in_kb: 8; }; unsigned int val; }; union l2_cache { struct { unsigned int line_size: 8; unsigned int lines_per_tag: 4; unsigned int assoc: 4; unsigned int size_in_kb: 16; }; unsigned int val; }; union l3_cache { struct { unsigned int line_size: 8; unsigned int lines_per_tag: 4; unsigned int assoc: 4; unsigned int res: 2; unsigned int size_encoded: 14; }; unsigned int val; }; struct cpuid_bit { u16 feature; u8 reg; u8 bit; u32 level; u32 sub_leaf; }; enum cpuid_leafs { CPUID_1_EDX = 0, CPUID_8000_0001_EDX = 1, CPUID_8086_0001_EDX = 2, CPUID_LNX_1 = 3, CPUID_1_ECX = 4, CPUID_C000_0001_EDX = 5, CPUID_8000_0001_ECX = 6, CPUID_LNX_2 = 7, CPUID_LNX_3 = 8, CPUID_7_0_EBX = 9, CPUID_D_1_EAX = 10, CPUID_LNX_4 = 11, CPUID_7_1_EAX = 12, CPUID_8000_0008_EBX = 13, CPUID_6_EAX = 14, CPUID_8000_000A_EDX = 15, CPUID_7_ECX = 16, CPUID_8000_0007_EBX = 17, CPUID_7_EDX = 18, CPUID_8000_001F_EAX = 19, }; struct cpu_dev { const char *c_vendor; const char *c_ident[2]; void (*c_early_init)(struct cpuinfo_x86 *); void (*c_bsp_init)(struct cpuinfo_x86 *); void (*c_init)(struct cpuinfo_x86 *); void (*c_identify)(struct cpuinfo_x86 *); void (*c_detect_tlb)(struct cpuinfo_x86 *); int c_x86_vendor; }; struct cpuid_dependent_feature { u32 feature; u32 level; }; enum spectre_v2_mitigation { SPECTRE_V2_NONE = 0, SPECTRE_V2_RETPOLINE_GENERIC = 1, SPECTRE_V2_RETPOLINE_AMD = 2, SPECTRE_V2_IBRS_ENHANCED = 3, }; enum spectre_v2_user_mitigation { SPECTRE_V2_USER_NONE = 0, SPECTRE_V2_USER_STRICT = 1, SPECTRE_V2_USER_STRICT_PREFERRED = 2, SPECTRE_V2_USER_PRCTL = 3, SPECTRE_V2_USER_SECCOMP = 4, }; enum ssb_mitigation { SPEC_STORE_BYPASS_NONE = 0, SPEC_STORE_BYPASS_DISABLE = 1, SPEC_STORE_BYPASS_PRCTL = 2, SPEC_STORE_BYPASS_SECCOMP = 3, }; enum l1tf_mitigations { L1TF_MITIGATION_OFF = 0, L1TF_MITIGATION_FLUSH_NOWARN = 1, L1TF_MITIGATION_FLUSH = 2, L1TF_MITIGATION_FLUSH_NOSMT = 3, L1TF_MITIGATION_FULL = 4, L1TF_MITIGATION_FULL_FORCE = 5, }; enum mds_mitigations { MDS_MITIGATION_OFF = 0, MDS_MITIGATION_FULL = 1, MDS_MITIGATION_VMWERV = 2, }; enum cpuhp_smt_control { CPU_SMT_ENABLED = 0, CPU_SMT_DISABLED = 1, CPU_SMT_FORCE_DISABLED = 2, CPU_SMT_NOT_SUPPORTED = 3, CPU_SMT_NOT_IMPLEMENTED = 4, }; enum vmx_l1d_flush_state { VMENTER_L1D_FLUSH_AUTO = 0, VMENTER_L1D_FLUSH_NEVER = 1, VMENTER_L1D_FLUSH_COND = 2, VMENTER_L1D_FLUSH_ALWAYS = 3, VMENTER_L1D_FLUSH_EPT_DISABLED = 4, VMENTER_L1D_FLUSH_NOT_REQUIRED = 5, }; enum taa_mitigations { TAA_MITIGATION_OFF = 0, TAA_MITIGATION_UCODE_NEEDED = 1, TAA_MITIGATION_VERW = 2, TAA_MITIGATION_TSX_DISABLED = 3, }; enum srbds_mitigations { SRBDS_MITIGATION_OFF = 0, SRBDS_MITIGATION_UCODE_NEEDED = 1, SRBDS_MITIGATION_FULL = 2, SRBDS_MITIGATION_TSX_OFF = 3, SRBDS_MITIGATION_HYPERVISOR = 4, }; enum spectre_v1_mitigation { SPECTRE_V1_MITIGATION_NONE = 0, SPECTRE_V1_MITIGATION_AUTO = 1, }; enum spectre_v2_mitigation_cmd { SPECTRE_V2_CMD_NONE = 0, SPECTRE_V2_CMD_AUTO = 1, SPECTRE_V2_CMD_FORCE = 2, SPECTRE_V2_CMD_RETPOLINE = 3, SPECTRE_V2_CMD_RETPOLINE_GENERIC = 4, SPECTRE_V2_CMD_RETPOLINE_AMD = 5, }; enum spectre_v2_user_cmd { SPECTRE_V2_USER_CMD_NONE = 0, SPECTRE_V2_USER_CMD_AUTO = 1, SPECTRE_V2_USER_CMD_FORCE = 2, SPECTRE_V2_USER_CMD_PRCTL = 3, SPECTRE_V2_USER_CMD_PRCTL_IBPB = 4, SPECTRE_V2_USER_CMD_SECCOMP = 5, SPECTRE_V2_USER_CMD_SECCOMP_IBPB = 6, }; enum ssb_mitigation_cmd { SPEC_STORE_BYPASS_CMD_NONE = 0, SPEC_STORE_BYPASS_CMD_AUTO = 1, SPEC_STORE_BYPASS_CMD_ON = 2, SPEC_STORE_BYPASS_CMD_PRCTL = 3, SPEC_STORE_BYPASS_CMD_SECCOMP = 4, }; enum hk_flags { HK_FLAG_TIMER = 1, HK_FLAG_RCU = 2, HK_FLAG_MISC = 4, HK_FLAG_SCHED = 8, HK_FLAG_TICK = 16, HK_FLAG_DOMAIN = 32, HK_FLAG_WQ = 64, HK_FLAG_MANAGED_IRQ = 128, HK_FLAG_KTHREAD = 256, }; struct aperfmperf_sample { unsigned int khz; atomic_t scfpending; ktime_t time; u64 aperf; u64 mperf; }; struct cpuid_dep { unsigned int feature; unsigned int depends; }; enum vmx_feature_leafs { MISC_FEATURES = 0, PRIMARY_CTLS = 1, SECONDARY_CTLS = 2, NR_VMX_FEATURE_WORDS = 3, }; struct _tlb_table { unsigned char descriptor; char tlb_type; unsigned int entries; char info[128]; }; enum tsx_ctrl_states { TSX_CTRL_ENABLE = 0, TSX_CTRL_DISABLE = 1, TSX_CTRL_RTM_ALWAYS_ABORT = 2, TSX_CTRL_NOT_SUPPORTED = 3, }; enum split_lock_detect_state { sld_off = 0, sld_warn = 1, sld_fatal = 2, sld_ratelimit = 3, }; struct sku_microcode { u8 model; u8 stepping; u32 microcode; }; struct cpuid_regs { u32 eax; u32 ebx; u32 ecx; u32 edx; }; enum pconfig_target { INVALID_TARGET = 0, MKTME_TARGET = 1, PCONFIG_TARGET_NR = 2, }; enum { PCONFIG_CPUID_SUBLEAF_INVALID = 0, PCONFIG_CPUID_SUBLEAF_TARGETID = 1, }; enum task_work_notify_mode { TWA_NONE = 0, TWA_RESUME = 1, TWA_SIGNAL = 2, }; enum mf_flags { MF_COUNT_INCREASED = 1, MF_ACTION_REQUIRED = 2, MF_MUST_KILL = 4, MF_SOFT_OFFLINE = 8, }; struct mce { __u64 status; __u64 misc; __u64 addr; __u64 mcgstatus; __u64 ip; __u64 tsc; __u64 time; __u8 cpuvendor; __u8 inject_flags; __u8 severity; __u8 pad; __u32 cpuid; __u8 cs; __u8 bank; __u8 cpu; __u8 finished; __u32 extcpu; __u32 socketid; __u32 apicid; __u64 mcgcap; __u64 synd; __u64 ipid; __u64 ppin; __u32 microcode; __u64 kflags; }; enum mce_notifier_prios { MCE_PRIO_LOWEST = 0, MCE_PRIO_MCELOG = 1, MCE_PRIO_EDAC = 2, MCE_PRIO_NFIT = 3, MCE_PRIO_EXTLOG = 4, MCE_PRIO_UC = 5, MCE_PRIO_EARLY = 6, MCE_PRIO_CEC = 7, MCE_PRIO_HIGHEST = 7, }; typedef long unsigned int mce_banks_t[1]; enum mcp_flags { MCP_TIMESTAMP = 1, MCP_UC = 2, MCP_DONTLOG = 4, }; enum severity_level { MCE_NO_SEVERITY = 0, MCE_DEFERRED_SEVERITY = 1, MCE_UCNA_SEVERITY = 1, MCE_KEEP_SEVERITY = 2, MCE_SOME_SEVERITY = 3, MCE_AO_SEVERITY = 4, MCE_UC_SEVERITY = 5, MCE_AR_SEVERITY = 6, MCE_PANIC_SEVERITY = 7, }; struct mce_evt_llist { struct llist_node llnode; struct mce mce; }; struct mca_config { bool dont_log_ce; bool cmci_disabled; bool ignore_ce; bool print_all; __u64 lmce_disabled: 1; __u64 disabled: 1; __u64 ser: 1; __u64 recovery: 1; __u64 bios_cmci_threshold: 1; int: 27; __u64 __reserved: 59; s8 bootlog; int tolerant; int monarch_timeout; int panic_timeout; u32 rip_msr; }; struct mce_vendor_flags { __u64 overflow_recov: 1; __u64 succor: 1; __u64 smca: 1; __u64 amd_threshold: 1; __u64 __reserved_0: 60; }; struct mca_msr_regs { u32 (*ctl)(int); u32 (*status)(int); u32 (*addr)(int); u32 (*misc)(int); }; struct trace_event_raw_mce_record { struct trace_entry ent; u64 mcgcap; u64 mcgstatus; u64 status; u64 addr; u64 misc; u64 synd; u64 ipid; u64 ip; u64 tsc; u64 walltime; u32 cpu; u32 cpuid; u32 apicid; u32 socketid; u8 cs; u8 bank; u8 cpuvendor; char __data[0]; }; struct trace_event_data_offsets_mce_record {}; typedef void (*btf_trace_mce_record)(void *, struct mce *); struct mce_bank { u64 ctl; bool init; }; struct mce_bank_dev { struct device_attribute attr; char attrname[16]; u8 bank; }; enum handler_type { EX_HANDLER_NONE = 0, EX_HANDLER_FAULT = 1, EX_HANDLER_UACCESS = 2, EX_HANDLER_OTHER = 3, }; enum context { IN_KERNEL = 1, IN_USER = 2, IN_KERNEL_RECOV = 3, }; enum ser { SER_REQUIRED = 1, NO_SER = 2, }; enum exception { EXCP_CONTEXT = 1, NO_EXCP = 2, }; struct severity { u64 mask; u64 result; unsigned char sev; unsigned char mcgmask; unsigned char mcgres; unsigned char ser; unsigned char context; unsigned char excp; unsigned char covered; unsigned char cpu_model; unsigned char cpu_minstepping; unsigned char bank_lo; unsigned char bank_hi; char *msg; }; struct gen_pool; typedef long unsigned int (*genpool_algo_t)(long unsigned int *, long unsigned int, long unsigned int, unsigned int, void *, struct gen_pool *, long unsigned int); struct gen_pool { spinlock_t lock; struct list_head chunks; int min_alloc_order; genpool_algo_t algo; void *data; const char *name; }; enum { CMCI_STORM_NONE = 0, CMCI_STORM_ACTIVE = 1, CMCI_STORM_SUBSIDED = 2, }; enum kobject_action { KOBJ_ADD = 0, KOBJ_REMOVE = 1, KOBJ_CHANGE = 2, KOBJ_MOVE = 3, KOBJ_ONLINE = 4, KOBJ_OFFLINE = 5, KOBJ_BIND = 6, KOBJ_UNBIND = 7, }; enum smca_bank_types { SMCA_LS = 0, SMCA_LS_V2 = 1, SMCA_IF = 2, SMCA_L2_CACHE = 3, SMCA_DE = 4, SMCA_RESERVED = 5, SMCA_EX = 6, SMCA_FP = 7, SMCA_L3_CACHE = 8, SMCA_CS = 9, SMCA_CS_V2 = 10, SMCA_PIE = 11, SMCA_UMC = 12, SMCA_UMC_V2 = 13, SMCA_PB = 14, SMCA_PSP = 15, SMCA_PSP_V2 = 16, SMCA_SMU = 17, SMCA_SMU_V2 = 18, SMCA_MP5 = 19, SMCA_NBIO = 20, SMCA_PCIE = 21, SMCA_PCIE_V2 = 22, SMCA_XGMI_PCS = 23, SMCA_XGMI_PHY = 24, SMCA_WAFL_PHY = 25, N_SMCA_BANK_TYPES = 26, }; struct smca_hwid { unsigned int bank_type; u32 hwid_mcatype; u8 count; }; struct smca_bank { struct smca_hwid *hwid; u32 id; u8 sysfs_id; }; struct smca_bank_name { const char *name; const char *long_name; }; struct thresh_restart { struct threshold_block *b; int reset; int set_lvt_off; int lvt_off; u16 old_limit; }; struct threshold_attr { struct attribute attr; ssize_t (*show)(struct threshold_block *, char *); ssize_t (*store)(struct threshold_block *, const char *, size_t); }; enum { CPER_SEV_RECOVERABLE = 0, CPER_SEV_FATAL = 1, CPER_SEV_CORRECTED = 2, CPER_SEV_INFORMATIONAL = 3, }; struct cper_record_header { char signature[4]; u16 revision; u32 signature_end; u16 section_count; u32 error_severity; u32 validation_bits; u32 record_length; u64 timestamp; guid_t platform_id; guid_t partition_id; guid_t creator_id; guid_t notification_type; u64 record_id; u32 flags; u64 persistence_information; u8 reserved[12]; } __attribute__((packed)); struct cper_section_descriptor { u32 section_offset; u32 section_length; u16 revision; u8 validation_bits; u8 reserved; u32 flags; guid_t section_type; guid_t fru_id; u32 section_severity; u8 fru_text[20]; }; struct cper_ia_proc_ctx { u16 reg_ctx_type; u16 reg_arr_size; u32 msr_addr; u64 mm_reg_addr; }; struct cper_sec_mem_err { u64 validation_bits; u64 error_status; u64 physical_addr; u64 physical_addr_mask; u16 node; u16 card; u16 module; u16 bank; u16 device; u16 row; u16 column; u16 bit_pos; u64 requestor_id; u64 responder_id; u64 target_id; u8 error_type; u8 extended; u16 rank; u16 mem_array_handle; u16 mem_dev_handle; }; enum { GHES_SEV_NO = 0, GHES_SEV_CORRECTED = 1, GHES_SEV_RECOVERABLE = 2, GHES_SEV_PANIC = 3, }; struct cper_mce_record { struct cper_record_header hdr; struct cper_section_descriptor sec_hdr; struct mce mce; }; typedef int (*cpu_stop_fn_t)(void *); typedef __u8 mtrr_type; struct mtrr_ops { u32 vendor; u32 use_intel_if; void (*set)(unsigned int, long unsigned int, long unsigned int, mtrr_type); void (*set_all)(); void (*get)(unsigned int, long unsigned int *, long unsigned int *, mtrr_type *); int (*get_free_region)(long unsigned int, long unsigned int, int); int (*validate_add_page)(long unsigned int, long unsigned int, unsigned int); int (*have_wrcomb)(); }; struct set_mtrr_data { long unsigned int smp_base; long unsigned int smp_size; unsigned int smp_reg; mtrr_type smp_type; }; struct mtrr_value { mtrr_type ltype; long unsigned int lbase; long unsigned int lsize; }; struct proc_ops { unsigned int proc_flags; int (*proc_open)(struct inode *, struct file *); ssize_t (*proc_read)(struct file *, char *, size_t, loff_t *); ssize_t (*proc_read_iter)(struct kiocb *, struct iov_iter *); ssize_t (*proc_write)(struct file *, const char *, size_t, loff_t *); loff_t (*proc_lseek)(struct file *, loff_t, int); int (*proc_release)(struct inode *, struct file *); __poll_t (*proc_poll)(struct file *, struct poll_table_struct *); long int (*proc_ioctl)(struct file *, unsigned int, long unsigned int); long int (*proc_compat_ioctl)(struct file *, unsigned int, long unsigned int); int (*proc_mmap)(struct file *, struct vm_area_struct *); long unsigned int (*proc_get_unmapped_area)(struct file *, long unsigned int, long unsigned int, long unsigned int, long unsigned int); }; struct mtrr_sentry { __u64 base; __u32 size; __u32 type; }; struct mtrr_gentry { __u64 base; __u32 size; __u32 regnum; __u32 type; __u32 _pad; }; typedef u32 compat_uint_t; struct mtrr_sentry32 { compat_ulong_t base; compat_uint_t size; compat_uint_t type; }; struct mtrr_gentry32 { compat_ulong_t regnum; compat_uint_t base; compat_uint_t size; compat_uint_t type; }; struct mtrr_var_range { __u32 base_lo; __u32 base_hi; __u32 mask_lo; __u32 mask_hi; }; struct mtrr_state_type { struct mtrr_var_range var_ranges[256]; mtrr_type fixed_ranges[88]; unsigned char enabled; unsigned char have_fixed; mtrr_type def_type; }; struct fixed_range_block { int base_msr; int ranges; }; struct var_mtrr_range_state { long unsigned int base_pfn; long unsigned int size_pfn; mtrr_type type; }; struct var_mtrr_state { long unsigned int range_startk; long unsigned int range_sizek; long unsigned int chunk_sizek; long unsigned int gran_sizek; unsigned int reg; }; struct mtrr_cleanup_result { long unsigned int gran_sizek; long unsigned int chunk_sizek; long unsigned int lose_cover_sizek; unsigned int num_reg; int bad; }; struct subsys_interface { const char *name; struct bus_type *subsys; struct list_head node; int (*add_dev)(struct device *, struct subsys_interface *); void (*remove_dev)(struct device *, struct subsys_interface *); }; struct property_entry; struct platform_device_info { struct device *parent; struct fwnode_handle *fwnode; bool of_node_reused; const char *name; int id; const struct resource *res; unsigned int num_res; const void *data; size_t size_data; u64 dma_mask; const struct property_entry *properties; }; enum dev_prop_type { DEV_PROP_U8 = 0, DEV_PROP_U16 = 1, DEV_PROP_U32 = 2, DEV_PROP_U64 = 3, DEV_PROP_STRING = 4, DEV_PROP_REF = 5, }; struct property_entry { const char *name; size_t length; bool is_inline; enum dev_prop_type type; union { const void *pointer; union { u8 u8_data[8]; u16 u16_data[4]; u32 u32_data[2]; u64 u64_data[1]; const char *str[1]; } value; }; }; struct builtin_fw { char *name; void *data; long unsigned int size; }; struct cpio_data { void *data; size_t size; char name[18]; }; struct cpu_signature { unsigned int sig; unsigned int pf; unsigned int rev; }; enum ucode_state { UCODE_OK = 0, UCODE_NEW = 1, UCODE_UPDATED = 2, UCODE_NFOUND = 3, UCODE_ERROR = 4, }; struct microcode_ops { enum ucode_state (*request_microcode_user)(int, const void *, size_t); enum ucode_state (*request_microcode_fw)(int, struct device *, bool); void (*microcode_fini_cpu)(int); enum ucode_state (*apply_microcode)(int); int (*collect_cpu_info)(int, struct cpu_signature *); }; struct ucode_cpu_info { struct cpu_signature cpu_sig; int valid; void *mc; }; struct cpu_info_ctx { struct cpu_signature *cpu_sig; int err; }; struct firmware { size_t size; const u8 *data; void *priv; }; struct ucode_patch { struct list_head plist; void *data; u32 patch_id; u16 equiv_cpu; }; struct microcode_header_intel { unsigned int hdrver; unsigned int rev; unsigned int date; unsigned int sig; unsigned int cksum; unsigned int ldrver; unsigned int pf; unsigned int datasize; unsigned int totalsize; unsigned int reserved[3]; }; struct microcode_intel { struct microcode_header_intel hdr; unsigned int bits[0]; }; struct extended_signature { unsigned int sig; unsigned int pf; unsigned int cksum; }; struct extended_sigtable { unsigned int count; unsigned int cksum; unsigned int reserved[3]; struct extended_signature sigs[0]; }; struct equiv_cpu_entry { u32 installed_cpu; u32 fixed_errata_mask; u32 fixed_errata_compare; u16 equiv_cpu; u16 res; }; struct microcode_header_amd { u32 data_code; u32 patch_id; u16 mc_patch_data_id; u8 mc_patch_data_len; u8 init_flag; u32 mc_patch_data_checksum; u32 nb_dev_id; u32 sb_dev_id; u16 processor_rev_id; u8 nb_rev_id; u8 sb_rev_id; u8 bios_api_rev; u8 reserved1[3]; u32 match_reg[8]; }; struct microcode_amd { struct microcode_header_amd hdr; unsigned int mpb[0]; }; struct equiv_cpu_table { unsigned int num_entries; struct equiv_cpu_entry *entry; }; struct cont_desc { struct microcode_amd *mc; u32 cpuid_1_eax; u32 psize; u8 *data; size_t size; }; typedef void (*exitcall_t)(); enum rdt_group_type { RDTCTRL_GROUP = 0, RDTMON_GROUP = 1, RDT_NUM_GROUP = 2, }; struct rdtgroup; struct mongroup { struct kernfs_node *mon_data_kn; struct rdtgroup *parent; struct list_head crdtgrp_list; u32 rmid; }; enum rdtgrp_mode { RDT_MODE_SHAREABLE = 0, RDT_MODE_EXCLUSIVE = 1, RDT_MODE_PSEUDO_LOCKSETUP = 2, RDT_MODE_PSEUDO_LOCKED = 3, RDT_NUM_MODES = 4, }; struct pseudo_lock_region; struct rdtgroup { struct kernfs_node *kn; struct list_head rdtgroup_list; u32 closid; struct cpumask cpu_mask; int flags; atomic_t waitcount; enum rdt_group_type type; struct mongroup mon; enum rdtgrp_mode mode; struct pseudo_lock_region *plr; }; struct rdt_cache { unsigned int cbm_len; unsigned int min_cbm_bits; unsigned int cbm_idx_mult; unsigned int cbm_idx_offset; unsigned int shareable_bits; bool arch_has_sparse_bitmaps; bool arch_has_empty_bitmaps; bool arch_has_per_cpu_cfg; }; enum membw_throttle_mode { THREAD_THROTTLE_UNDEFINED = 0, THREAD_THROTTLE_MAX = 1, THREAD_THROTTLE_PER_THREAD = 2, }; struct rdt_membw { u32 min_bw; u32 bw_gran; u32 delay_linear; bool arch_needs_linear; enum membw_throttle_mode throttle_mode; bool mba_sc; u32 *mb_map; }; struct rdt_domain; struct msr_param; struct rdt_parse_data; struct rdt_resource { int rid; bool alloc_enabled; bool mon_enabled; bool alloc_capable; bool mon_capable; char *name; int num_closid; int cache_level; u32 default_ctrl; unsigned int msr_base; void (*msr_update)(struct rdt_domain *, struct msr_param *, struct rdt_resource *); int data_width; struct list_head domains; struct rdt_cache cache; struct rdt_membw membw; const char *format_str; int (*parse_ctrlval)(struct rdt_parse_data *, struct rdt_resource *, struct rdt_domain *); struct list_head evt_list; int num_rmid; unsigned int mon_scale; unsigned int mbm_width; long unsigned int fflags; }; struct mbm_state; struct rdt_domain { struct list_head list; int id; struct cpumask cpu_mask; long unsigned int *rmid_busy_llc; struct mbm_state *mbm_total; struct mbm_state *mbm_local; struct delayed_work mbm_over; struct delayed_work cqm_limbo; int mbm_work_cpu; int cqm_work_cpu; u32 *ctrl_val; u32 *mbps_val; u32 new_ctrl; bool have_new_ctrl; struct pseudo_lock_region *plr; }; struct pseudo_lock_region { struct rdt_resource *r; struct rdt_domain *d; u32 cbm; wait_queue_head_t lock_thread_wq; int thread_done; int cpu; unsigned int line_size; unsigned int size; void *kmem; unsigned int minor; struct dentry *debugfs_dir; struct list_head pm_reqs; }; struct mbm_state { u64 chunks; u64 prev_msr; u64 prev_bw_msr; u32 prev_bw; u32 delta_bw; bool delta_comp; }; struct msr_param { struct rdt_resource *res; int low; int high; }; struct rdt_parse_data { struct rdtgroup *rdtgrp; char *buf; }; enum { RDT_RESOURCE_L3 = 0, RDT_RESOURCE_L3DATA = 1, RDT_RESOURCE_L3CODE = 2, RDT_RESOURCE_L2 = 3, RDT_RESOURCE_L2DATA = 4, RDT_RESOURCE_L2CODE = 5, RDT_RESOURCE_MBA = 6, RDT_NUM_RESOURCES = 7, }; union cpuid_0x10_1_eax { struct { unsigned int cbm_len: 5; } split; unsigned int full; }; union cpuid_0x10_3_eax { struct { unsigned int max_delay: 12; } split; unsigned int full; }; union cpuid_0x10_x_edx { struct { unsigned int cos_max: 16; } split; unsigned int full; }; enum { RDT_FLAG_CMT = 0, RDT_FLAG_MBM_TOTAL = 1, RDT_FLAG_MBM_LOCAL = 2, RDT_FLAG_L3_CAT = 3, RDT_FLAG_L3_CDP = 4, RDT_FLAG_L2_CAT = 5, RDT_FLAG_L2_CDP = 6, RDT_FLAG_MBA = 7, }; struct rdt_options { char *name; int flag; bool force_off; bool force_on; }; typedef unsigned int uint; enum kernfs_node_type { KERNFS_DIR = 1, KERNFS_FILE = 2, KERNFS_LINK = 4, }; enum kernfs_root_flag { KERNFS_ROOT_CREATE_DEACTIVATED = 1, KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK = 2, KERNFS_ROOT_SUPPORT_EXPORTOP = 4, KERNFS_ROOT_SUPPORT_USER_XATTR = 8, }; struct kernfs_fs_context { struct kernfs_root *root; void *ns_tag; long unsigned int magic; bool new_sb_created; }; struct rdt_fs_context { struct kernfs_fs_context kfc; bool enable_cdpl2; bool enable_cdpl3; bool enable_mba_mbps; }; struct mon_evt { u32 evtid; char *name; struct list_head list; }; union mon_data_bits { void *priv; struct { unsigned int rid: 10; unsigned int evtid: 8; unsigned int domid: 14; } u; }; struct rmid_read { struct rdtgroup *rgrp; struct rdt_resource *r; struct rdt_domain *d; int evtid; bool first; u64 val; }; struct rftype { char *name; umode_t mode; const struct kernfs_ops *kf_ops; long unsigned int flags; long unsigned int fflags; int (*seq_show)(struct kernfs_open_file *, struct seq_file *, void *); ssize_t (*write)(struct kernfs_open_file *, char *, size_t, loff_t); }; enum rdt_param { Opt_cdp = 0, Opt_cdpl2 = 1, Opt_mba_mbps = 2, nr__rdt_params = 3, }; struct rmid_entry { u32 rmid; int busy; struct list_head list; }; struct mbm_correction_factor_table { u32 rmidthreshold; u64 cf; }; struct trace_event_raw_pseudo_lock_mem_latency { struct trace_entry ent; u32 latency; char __data[0]; }; struct trace_event_raw_pseudo_lock_l2 { struct trace_entry ent; u64 l2_hits; u64 l2_miss; char __data[0]; }; struct trace_event_raw_pseudo_lock_l3 { struct trace_entry ent; u64 l3_hits; u64 l3_miss; char __data[0]; }; struct trace_event_data_offsets_pseudo_lock_mem_latency {}; struct trace_event_data_offsets_pseudo_lock_l2 {}; struct trace_event_data_offsets_pseudo_lock_l3 {}; typedef void (*btf_trace_pseudo_lock_mem_latency)(void *, u32); typedef void (*btf_trace_pseudo_lock_l2)(void *, u64, u64); typedef void (*btf_trace_pseudo_lock_l3)(void *, u64, u64); struct pseudo_lock_pm_req { struct list_head list; struct dev_pm_qos_request req; }; struct residency_counts { u64 miss_before; u64 hits_before; u64 miss_after; u64 hits_after; }; struct miscdevice { int minor; const char *name; const struct file_operations *fops; struct list_head list; struct device *parent; struct device *this_device; const struct attribute_group **groups; const char *nodename; umode_t mode; }; enum mmu_notifier_event { MMU_NOTIFY_UNMAP = 0, MMU_NOTIFY_CLEAR = 1, MMU_NOTIFY_PROTECTION_VMA = 2, MMU_NOTIFY_PROTECTION_PAGE = 3, MMU_NOTIFY_SOFT_DIRTY = 4, MMU_NOTIFY_RELEASE = 5, MMU_NOTIFY_MIGRATE = 6, MMU_NOTIFY_EXCLUSIVE = 7, }; struct mmu_notifier; struct mmu_notifier_range; struct mmu_notifier_ops { void (*release)(struct mmu_notifier *, struct mm_struct *); int (*clear_flush_young)(struct mmu_notifier *, struct mm_struct *, long unsigned int, long unsigned int); int (*clear_young)(struct mmu_notifier *, struct mm_struct *, long unsigned int, long unsigned int); int (*test_young)(struct mmu_notifier *, struct mm_struct *, long unsigned int); void (*change_pte)(struct mmu_notifier *, struct mm_struct *, long unsigned int, pte_t); int (*invalidate_range_start)(struct mmu_notifier *, const struct mmu_notifier_range *); void (*invalidate_range_end)(struct mmu_notifier *, const struct mmu_notifier_range *); void (*invalidate_range)(struct mmu_notifier *, struct mm_struct *, long unsigned int, long unsigned int); struct mmu_notifier * (*alloc_notifier)(struct mm_struct *); void (*free_notifier)(struct mmu_notifier *); }; struct mmu_notifier { struct hlist_node hlist; const struct mmu_notifier_ops *ops; struct mm_struct *mm; struct callback_head rcu; unsigned int users; }; struct mmu_notifier_range { struct vm_area_struct *vma; struct mm_struct *mm; long unsigned int start; long unsigned int end; unsigned int flags; enum mmu_notifier_event event; void *owner; }; enum sgx_page_type { SGX_PAGE_TYPE_SECS = 0, SGX_PAGE_TYPE_TCS = 1, SGX_PAGE_TYPE_REG = 2, SGX_PAGE_TYPE_VA = 3, SGX_PAGE_TYPE_TRIM = 4, }; struct sgx_encl_page; struct sgx_epc_page { unsigned int section; unsigned int flags; struct sgx_encl_page *owner; struct list_head list; }; struct sgx_encl; struct sgx_va_page; struct sgx_encl_page { long unsigned int desc; long unsigned int vm_max_prot_bits; struct sgx_epc_page *epc_page; struct sgx_encl *encl; struct sgx_va_page *va_page; }; struct sgx_encl { long unsigned int base; long unsigned int size; long unsigned int flags; unsigned int page_cnt; unsigned int secs_child_cnt; struct mutex lock; struct xarray page_array; struct sgx_encl_page secs; long unsigned int attributes; long unsigned int attributes_mask; cpumask_t cpumask; struct file *backing; struct kref refcount; struct list_head va_pages; long unsigned int mm_list_version; struct list_head mm_list; spinlock_t mm_lock; struct srcu_struct srcu; }; struct sgx_va_page { struct sgx_epc_page *epc_page; long unsigned int slots[8]; struct list_head list; }; struct sgx_encl_mm { struct sgx_encl *encl; struct mm_struct *mm; struct list_head list; struct mmu_notifier mmu_notifier; }; typedef unsigned int xa_mark_t; struct xa_node { unsigned char shift; unsigned char offset; unsigned char count; unsigned char nr_values; struct xa_node *parent; struct xarray *array; union { struct list_head private_list; struct callback_head callback_head; }; void *slots[64]; union { long unsigned int tags[3]; long unsigned int marks[3]; }; }; typedef void (*xa_update_node_t)(struct xa_node *); struct xa_state { struct xarray *xa; long unsigned int xa_index; unsigned char xa_shift; unsigned char xa_sibs; unsigned char xa_offset; unsigned char xa_pad; struct xa_node *xa_node; struct xa_node *xa_alloc; xa_update_node_t xa_update; }; enum { XA_CHECK_SCHED = 4096, }; enum sgx_encls_function { ECREATE = 0, EADD = 1, EINIT = 2, EREMOVE = 3, EDGBRD = 4, EDGBWR = 5, EEXTEND = 6, ELDU = 8, EBLOCK = 9, EPA = 10, EWB = 11, ETRACK = 12, EAUG = 13, EMODPR = 14, EMODT = 15, }; struct sgx_pageinfo { u64 addr; u64 contents; u64 metadata; u64 secs; }; struct sgx_numa_node { struct list_head free_page_list; spinlock_t lock; }; struct sgx_epc_section { long unsigned int phys_addr; void *virt_addr; struct sgx_epc_page *pages; struct sgx_numa_node *node; }; enum sgx_encl_flags { SGX_ENCL_IOCTL = 1, SGX_ENCL_DEBUG = 2, SGX_ENCL_CREATED = 4, SGX_ENCL_INITIALIZED = 8, }; struct sgx_backing { long unsigned int page_index; struct page *contents; struct page *pcmd; long unsigned int pcmd_offset; }; enum sgx_return_code { SGX_NOT_TRACKED = 11, SGX_CHILD_PRESENT = 13, SGX_INVALID_EINITTOKEN = 16, SGX_UNMASKED_EVENT = 128, }; enum sgx_attribute { SGX_ATTR_INIT = 1, SGX_ATTR_DEBUG = 2, SGX_ATTR_MODE64BIT = 4, SGX_ATTR_PROVISIONKEY = 16, SGX_ATTR_EINITTOKENKEY = 32, SGX_ATTR_KSS = 128, }; struct sgx_secs { u64 size; u64 base; u32 ssa_frame_size; u32 miscselect; u8 reserved1[24]; u64 attributes; u64 xfrm; u32 mrenclave[8]; u8 reserved2[32]; u32 mrsigner[8]; u8 reserved3[32]; u32 config_id[16]; u16 isv_prod_id; u16 isv_svn; u16 config_svn; u8 reserved4[3834]; }; enum sgx_secinfo_flags { SGX_SECINFO_R = 1, SGX_SECINFO_W = 2, SGX_SECINFO_X = 4, SGX_SECINFO_SECS = 0, SGX_SECINFO_TCS = 256, SGX_SECINFO_REG = 512, SGX_SECINFO_VA = 768, SGX_SECINFO_TRIM = 1024, }; struct sgx_secinfo { u64 flags; u8 reserved[56]; }; struct sgx_sigstruct_header { u64 header1[2]; u32 vendor; u32 date; u64 header2[2]; u32 swdefined; u8 reserved1[84]; }; struct sgx_sigstruct_body { u32 miscselect; u32 misc_mask; u8 reserved2[20]; u64 attributes; u64 xfrm; u64 attributes_mask; u64 xfrm_mask; u8 mrenclave[32]; u8 reserved3[32]; u16 isvprodid; u16 isvsvn; } __attribute__((packed)); struct sgx_sigstruct { struct sgx_sigstruct_header header; u8 modulus[384]; u32 exponent; u8 signature[384]; struct sgx_sigstruct_body body; u8 reserved4[12]; u8 q1[384]; u8 q2[384]; } __attribute__((packed)); struct crypto_alg; struct crypto_tfm { u32 crt_flags; int node; void (*exit)(struct crypto_tfm *); struct crypto_alg *__crt_alg; void *__crt_ctx[0]; }; struct cipher_alg { unsigned int cia_min_keysize; unsigned int cia_max_keysize; int (*cia_setkey)(struct crypto_tfm *, const u8 *, unsigned int); void (*cia_encrypt)(struct crypto_tfm *, u8 *, const u8 *); void (*cia_decrypt)(struct crypto_tfm *, u8 *, const u8 *); }; struct compress_alg { int (*coa_compress)(struct crypto_tfm *, const u8 *, unsigned int, u8 *, unsigned int *); int (*coa_decompress)(struct crypto_tfm *, const u8 *, unsigned int, u8 *, unsigned int *); }; struct crypto_istat_aead { atomic64_t encrypt_cnt; atomic64_t encrypt_tlen; atomic64_t decrypt_cnt; atomic64_t decrypt_tlen; atomic64_t err_cnt; }; struct crypto_istat_akcipher { atomic64_t encrypt_cnt; atomic64_t encrypt_tlen; atomic64_t decrypt_cnt; atomic64_t decrypt_tlen; atomic64_t verify_cnt; atomic64_t sign_cnt; atomic64_t err_cnt; }; struct crypto_istat_cipher { atomic64_t encrypt_cnt; atomic64_t encrypt_tlen; atomic64_t decrypt_cnt; atomic64_t decrypt_tlen; atomic64_t err_cnt; }; struct crypto_istat_compress { atomic64_t compress_cnt; atomic64_t compress_tlen; atomic64_t decompress_cnt; atomic64_t decompress_tlen; atomic64_t err_cnt; }; struct crypto_istat_hash { atomic64_t hash_cnt; atomic64_t hash_tlen; atomic64_t err_cnt; }; struct crypto_istat_kpp { atomic64_t setsecret_cnt; atomic64_t generate_public_key_cnt; atomic64_t compute_shared_secret_cnt; atomic64_t err_cnt; }; struct crypto_istat_rng { atomic64_t generate_cnt; atomic64_t generate_tlen; atomic64_t seed_cnt; atomic64_t err_cnt; }; struct crypto_type; struct crypto_alg { struct list_head cra_list; struct list_head cra_users; u32 cra_flags; unsigned int cra_blocksize; unsigned int cra_ctxsize; unsigned int cra_alignmask; int cra_priority; refcount_t cra_refcnt; char cra_name[128]; char cra_driver_name[128]; const struct crypto_type *cra_type; union { struct cipher_alg cipher; struct compress_alg compress; } cra_u; int (*cra_init)(struct crypto_tfm *); void (*cra_exit)(struct crypto_tfm *); void (*cra_destroy)(struct crypto_alg *); struct module *cra_module; union { struct crypto_istat_aead aead; struct crypto_istat_akcipher akcipher; struct crypto_istat_cipher cipher; struct crypto_istat_compress compress; struct crypto_istat_hash hash; struct crypto_istat_rng rng; struct crypto_istat_kpp kpp; } stats; }; struct crypto_instance; struct crypto_type { unsigned int (*ctxsize)(struct crypto_alg *, u32, u32); unsigned int (*extsize)(struct crypto_alg *); int (*init)(struct crypto_tfm *, u32, u32); int (*init_tfm)(struct crypto_tfm *); void (*show)(struct seq_file *, struct crypto_alg *); int (*report)(struct sk_buff *, struct crypto_alg *); void (*free)(struct crypto_instance *); unsigned int type; unsigned int maskclear; unsigned int maskset; unsigned int tfmsize; }; struct crypto_shash; struct shash_desc { struct crypto_shash *tfm; void *__ctx[0]; }; struct crypto_shash { unsigned int descsize; struct crypto_tfm base; }; enum sgx_page_flags { SGX_PAGE_MEASURE = 1, }; struct sgx_enclave_create { __u64 src; }; struct sgx_enclave_add_pages { __u64 src; __u64 offset; __u64 length; __u64 secinfo; __u64 flags; __u64 count; }; struct sgx_enclave_init { __u64 sigstruct; }; struct sgx_enclave_provision { __u64 fd; }; struct sgx_vepc { struct xarray page_array; struct mutex lock; }; struct vmcb_seg { u16 selector; u16 attrib; u32 limit; u64 base; }; struct vmcb_save_area { struct vmcb_seg es; struct vmcb_seg cs; struct vmcb_seg ss; struct vmcb_seg ds; struct vmcb_seg fs; struct vmcb_seg gs; struct vmcb_seg gdtr; struct vmcb_seg ldtr; struct vmcb_seg idtr; struct vmcb_seg tr; u8 reserved_1[43]; u8 cpl; u8 reserved_2[4]; u64 efer; u8 reserved_3[104]; u64 xss; u64 cr4; u64 cr3; u64 cr0; u64 dr7; u64 dr6; u64 rflags; u64 rip; u8 reserved_4[88]; u64 rsp; u8 reserved_5[24]; u64 rax; u64 star; u64 lstar; u64 cstar; u64 sfmask; u64 kernel_gs_base; u64 sysenter_cs; u64 sysenter_esp; u64 sysenter_eip; u64 cr2; u8 reserved_6[32]; u64 g_pat; u64 dbgctl; u64 br_from; u64 br_to; u64 last_excp_from; u64 last_excp_to; u8 reserved_7[72]; u32 spec_ctrl; u8 reserved_7b[4]; u32 pkru; u8 reserved_7a[20]; u64 reserved_8; u64 rcx; u64 rdx; u64 rbx; u64 reserved_9; u64 rbp; u64 rsi; u64 rdi; u64 r8; u64 r9; u64 r10; u64 r11; u64 r12; u64 r13; u64 r14; u64 r15; u8 reserved_10[16]; u64 sw_exit_code; u64 sw_exit_info_1; u64 sw_exit_info_2; u64 sw_scratch; u8 reserved_11[56]; u64 xcr0; u8 valid_bitmap[16]; u64 x87_state_gpa; }; struct ghcb { struct vmcb_save_area save; u8 reserved_save[1016]; u8 shared_buffer[2032]; u8 reserved_1[10]; u16 protocol_version; u32 ghcb_usage; }; enum intercept_words { INTERCEPT_CR = 0, INTERCEPT_DR = 1, INTERCEPT_EXCEPTION = 2, INTERCEPT_WORD3 = 3, INTERCEPT_WORD4 = 4, INTERCEPT_WORD5 = 5, MAX_INTERCEPT = 6, }; struct vmware_steal_time { union { uint64_t clock; struct { uint32_t clock_low; uint32_t clock_high; }; }; uint64_t reserved[7]; }; struct mpc_intsrc { unsigned char type; unsigned char irqtype; short unsigned int irqflag; unsigned char srcbus; unsigned char srcbusirq; unsigned char dstapic; unsigned char dstirq; }; enum mp_irq_source_types { mp_INT = 0, mp_NMI = 1, mp_SMI = 2, mp_ExtINT = 3, }; typedef u64 acpi_io_address; typedef u64 acpi_physical_address; typedef char *acpi_string; typedef void *acpi_handle; typedef u32 acpi_object_type; typedef u8 acpi_adr_space_type; union acpi_object { acpi_object_type type; struct { acpi_object_type type; u64 value; } integer; struct { acpi_object_type type; u32 length; char *pointer; } string; struct { acpi_object_type type; u32 length; u8 *pointer; } buffer; struct { acpi_object_type type; u32 count; union acpi_object *elements; } package; struct { acpi_object_type type; acpi_object_type actual_type; acpi_handle handle; } reference; struct { acpi_object_type type; u32 proc_id; acpi_io_address pblk_address; u32 pblk_length; } processor; struct { acpi_object_type type; u32 system_level; u32 resource_order; } power_resource; }; struct acpi_object_list { u32 count; union acpi_object *pointer; }; struct acpi_subtable_header { u8 type; u8 length; }; struct acpi_table_boot { struct acpi_table_header header; u8 cmos_index; u8 reserved[3]; }; struct acpi_hmat_structure { u16 type; u16 reserved; u32 length; }; struct acpi_table_hpet { struct acpi_table_header header; u32 id; struct acpi_generic_address address; u8 sequence; u16 minimum_tick; u8 flags; } __attribute__((packed)); struct acpi_table_madt { struct acpi_table_header header; u32 address; u32 flags; }; enum acpi_madt_type { ACPI_MADT_TYPE_LOCAL_APIC = 0, ACPI_MADT_TYPE_IO_APIC = 1, ACPI_MADT_TYPE_INTERRUPT_OVERRIDE = 2, ACPI_MADT_TYPE_NMI_SOURCE = 3, ACPI_MADT_TYPE_LOCAL_APIC_NMI = 4, ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE = 5, ACPI_MADT_TYPE_IO_SAPIC = 6, ACPI_MADT_TYPE_LOCAL_SAPIC = 7, ACPI_MADT_TYPE_INTERRUPT_SOURCE = 8, ACPI_MADT_TYPE_LOCAL_X2APIC = 9, ACPI_MADT_TYPE_LOCAL_X2APIC_NMI = 10, ACPI_MADT_TYPE_GENERIC_INTERRUPT = 11, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR = 12, ACPI_MADT_TYPE_GENERIC_MSI_FRAME = 13, ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR = 14, ACPI_MADT_TYPE_GENERIC_TRANSLATOR = 15, ACPI_MADT_TYPE_MULTIPROC_WAKEUP = 16, ACPI_MADT_TYPE_RESERVED = 17, }; struct acpi_madt_local_apic { struct acpi_subtable_header header; u8 processor_id; u8 id; u32 lapic_flags; }; struct acpi_madt_io_apic { struct acpi_subtable_header header; u8 id; u8 reserved; u32 address; u32 global_irq_base; }; struct acpi_madt_interrupt_override { struct acpi_subtable_header header; u8 bus; u8 source_irq; u32 global_irq; u16 inti_flags; } __attribute__((packed)); struct acpi_madt_nmi_source { struct acpi_subtable_header header; u16 inti_flags; u32 global_irq; }; struct acpi_madt_local_apic_nmi { struct acpi_subtable_header header; u8 processor_id; u16 inti_flags; u8 lint; } __attribute__((packed)); struct acpi_madt_local_apic_override { struct acpi_subtable_header header; u16 reserved; u64 address; } __attribute__((packed)); struct acpi_madt_local_sapic { struct acpi_subtable_header header; u8 processor_id; u8 id; u8 eid; u8 reserved[3]; u32 lapic_flags; u32 uid; char uid_string[1]; } __attribute__((packed)); struct acpi_madt_local_x2apic { struct acpi_subtable_header header; u16 reserved; u32 local_apic_id; u32 lapic_flags; u32 uid; }; struct acpi_madt_local_x2apic_nmi { struct acpi_subtable_header header; u16 inti_flags; u32 uid; u8 lint; u8 reserved[3]; }; struct acpi_prmt_module_header { u16 revision; u16 length; }; union acpi_subtable_headers { struct acpi_subtable_header common; struct acpi_hmat_structure hmat; struct acpi_prmt_module_header prmt; }; typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *); typedef int (*acpi_tbl_entry_handler)(union acpi_subtable_headers *, const long unsigned int); struct acpi_subtable_proc { int id; acpi_tbl_entry_handler handler; int count; }; typedef u32 phys_cpuid_t; struct serial_icounter_struct { int cts; int dsr; int rng; int dcd; int rx; int tx; int frame; int overrun; int parity; int brk; int buf_overrun; int reserved[9]; }; struct serial_struct { int type; int line; unsigned int port; int irq; int flags; int xmit_fifo_size; int custom_divisor; int baud_base; short unsigned int close_delay; char io_type; char reserved_char[1]; int hub6; short unsigned int closing_wait; short unsigned int closing_wait2; unsigned char *iomem_base; short unsigned int iomem_reg_shift; unsigned int port_high; long unsigned int iomap_base; }; enum ioapic_domain_type { IOAPIC_DOMAIN_INVALID = 0, IOAPIC_DOMAIN_LEGACY = 1, IOAPIC_DOMAIN_STRICT = 2, IOAPIC_DOMAIN_DYNAMIC = 3, }; struct ioapic_domain_cfg { enum ioapic_domain_type type; const struct irq_domain_ops *ops; struct device_node *dev; }; struct wakeup_header { u16 video_mode; u32 pmode_entry; u16 pmode_cs; u32 pmode_cr0; u32 pmode_cr3; u32 pmode_cr4; u32 pmode_efer_low; u32 pmode_efer_high; u64 pmode_gdt; u32 pmode_misc_en_low; u32 pmode_misc_en_high; u32 pmode_behavior; u32 realmode_flags; u32 real_magic; u32 signature; } __attribute__((packed)); struct acpi_hest_header { u16 type; u16 source_id; }; struct acpi_hest_ia_error_bank { u8 bank_number; u8 clear_status_on_init; u8 status_format; u8 reserved; u32 control_register; u64 control_data; u32 status_register; u32 address_register; u32 misc_register; } __attribute__((packed)); struct acpi_hest_notify { u8 type; u8 length; u16 config_write_enable; u32 poll_interval; u32 vector; u32 polling_threshold_value; u32 polling_threshold_window; u32 error_threshold_value; u32 error_threshold_window; }; struct acpi_hest_ia_corrected { struct acpi_hest_header header; u16 reserved1; u8 flags; u8 enabled; u32 records_to_preallocate; u32 max_sections_per_record; struct acpi_hest_notify notify; u8 num_hardware_banks; u8 reserved2[3]; }; struct cpc_reg { u8 descriptor; u16 length; u8 space_id; u8 bit_width; u8 bit_offset; u8 access_width; u64 address; } __attribute__((packed)); struct acpi_power_register { u8 descriptor; u16 length; u8 space_id; u8 bit_width; u8 bit_offset; u8 access_size; u64 address; } __attribute__((packed)); struct acpi_processor_cx { u8 valid; u8 type; u32 address; u8 entry_method; u8 index; u32 latency; u8 bm_sts_skip; char desc[32]; }; struct acpi_processor_flags { u8 power: 1; u8 performance: 1; u8 throttling: 1; u8 limit: 1; u8 bm_control: 1; u8 bm_check: 1; u8 has_cst: 1; u8 has_lpi: 1; u8 power_setup_done: 1; u8 bm_rld_set: 1; u8 need_hotplug_init: 1; }; struct cstate_entry { struct { unsigned int eax; unsigned int ecx; } states[8]; }; enum reboot_mode { REBOOT_UNDEFINED = 4294967295, REBOOT_COLD = 0, REBOOT_WARM = 1, REBOOT_HARD = 2, REBOOT_SOFT = 3, REBOOT_GPIO = 4, }; enum reboot_type { BOOT_TRIPLE = 116, BOOT_KBD = 107, BOOT_BIOS = 98, BOOT_ACPI = 97, BOOT_EFI = 101, BOOT_CF9_FORCE = 112, BOOT_CF9_SAFE = 113, }; typedef void (*nmi_shootdown_cb)(int, struct pt_regs *); enum allow_write_msrs { MSR_WRITES_ON = 0, MSR_WRITES_OFF = 1, MSR_WRITES_DEFAULT = 2, }; typedef struct __call_single_data call_single_data_t; struct cpuid_regs_done { struct cpuid_regs regs; struct completion done; }; struct intel_early_ops { resource_size_t (*stolen_size)(int, int, int); resource_size_t (*stolen_base)(int, int, int, resource_size_t); }; struct chipset { u32 vendor; u32 device; u32 class; u32 class_mask; u32 flags; void (*f)(int, int, int); }; enum { SD_BALANCE_NEWIDLE = 1, SD_BALANCE_EXEC = 2, SD_BALANCE_FORK = 4, SD_BALANCE_WAKE = 8, SD_WAKE_AFFINE = 16, SD_ASYM_CPUCAPACITY = 32, SD_ASYM_CPUCAPACITY_FULL = 64, SD_SHARE_CPUCAPACITY = 128, SD_SHARE_PKG_RESOURCES = 256, SD_SERIALIZE = 512, SD_ASYM_PACKING = 1024, SD_PREFER_SIBLING = 2048, SD_OVERLAP = 4096, SD_NUMA = 8192, }; struct sched_domain_shared { atomic_t ref; atomic_t nr_busy_cpus; int has_idle_cores; }; struct sched_group; struct sched_domain { struct sched_domain *parent; struct sched_domain *child; struct sched_group *groups; long unsigned int min_interval; long unsigned int max_interval; unsigned int busy_factor; unsigned int imbalance_pct; unsigned int cache_nice_tries; int nohz_idle; int flags; int level; long unsigned int last_balance; unsigned int balance_interval; unsigned int nr_balance_failed; u64 max_newidle_lb_cost; long unsigned int next_decay_max_lb_cost; u64 avg_scan_cost; unsigned int lb_count[3]; unsigned int lb_failed[3]; unsigned int lb_balanced[3]; unsigned int lb_imbalance[3]; unsigned int lb_gained[3]; unsigned int lb_hot_gained[3]; unsigned int lb_nobusyg[3]; unsigned int lb_nobusyq[3]; unsigned int alb_count; unsigned int alb_failed; unsigned int alb_pushed; unsigned int sbe_count; unsigned int sbe_balanced; unsigned int sbe_pushed; unsigned int sbf_count; unsigned int sbf_balanced; unsigned int sbf_pushed; unsigned int ttwu_wake_remote; unsigned int ttwu_move_affine; unsigned int ttwu_move_balance; char *name; union { void *private; struct callback_head rcu; }; struct sched_domain_shared *shared; unsigned int span_weight; long unsigned int span[0]; }; typedef const struct cpumask * (*sched_domain_mask_f)(int); typedef int (*sched_domain_flags_f)(); struct sched_group_capacity; struct sd_data { struct sched_domain **sd; struct sched_domain_shared **sds; struct sched_group **sg; struct sched_group_capacity **sgc; }; struct sched_domain_topology_level { sched_domain_mask_f mask; sched_domain_flags_f sd_flags; int flags; int numa_level; struct sd_data data; char *name; }; enum apic_intr_mode_id { APIC_PIC = 0, APIC_VIRTUAL_WIRE = 1, APIC_VIRTUAL_WIRE_NO_CONFIG = 2, APIC_SYMMETRIC_IO = 3, APIC_SYMMETRIC_IO_NO_ROUTING = 4, }; struct cppc_perf_caps { u32 guaranteed_perf; u32 highest_perf; u32 nominal_perf; u32 lowest_perf; u32 lowest_nonlinear_perf; u32 lowest_freq; u32 nominal_freq; }; struct tsc_adjust { s64 bootval; s64 adjusted; long unsigned int nextcheck; bool warned; }; typedef void * (*pcpu_fc_alloc_fn_t)(unsigned int, size_t, size_t); typedef void (*pcpu_fc_free_fn_t)(void *, size_t); typedef void (*pcpu_fc_populate_pte_fn_t)(long unsigned int); typedef int pcpu_fc_cpu_distance_fn_t(unsigned int, unsigned int); enum { DUMP_PREFIX_NONE = 0, DUMP_PREFIX_ADDRESS = 1, DUMP_PREFIX_OFFSET = 2, }; struct mpf_intel { char signature[4]; unsigned int physptr; unsigned char length; unsigned char specification; unsigned char checksum; unsigned char feature1; unsigned char feature2; unsigned char feature3; unsigned char feature4; unsigned char feature5; }; struct mpc_table { char signature[4]; short unsigned int length; char spec; char checksum; char oem[8]; char productid[12]; unsigned int oemptr; short unsigned int oemsize; short unsigned int oemcount; unsigned int lapic; unsigned int reserved; }; struct mpc_cpu { unsigned char type; unsigned char apicid; unsigned char apicver; unsigned char cpuflag; unsigned int cpufeature; unsigned int featureflag; unsigned int reserved[2]; }; struct mpc_bus { unsigned char type; unsigned char busid; unsigned char bustype[6]; }; struct mpc_ioapic { unsigned char type; unsigned char apicid; unsigned char apicver; unsigned char flags; unsigned int apicaddr; }; struct mpc_lintsrc { unsigned char type; unsigned char irqtype; short unsigned int irqflag; unsigned char srcbusid; unsigned char srcbusirq; unsigned char destapic; unsigned char destapiclint; }; enum page_cache_mode { _PAGE_CACHE_MODE_WB = 0, _PAGE_CACHE_MODE_WC = 1, _PAGE_CACHE_MODE_UC_MINUS = 2, _PAGE_CACHE_MODE_UC = 3, _PAGE_CACHE_MODE_WT = 4, _PAGE_CACHE_MODE_WP = 5, _PAGE_CACHE_MODE_NUM = 8, }; enum { IRQ_REMAP_XAPIC_MODE = 0, IRQ_REMAP_X2APIC_MODE = 1, }; union apic_ir { long unsigned int map[4]; u32 regs[8]; }; enum { X2APIC_OFF = 0, X2APIC_ON = 1, X2APIC_DISABLED = 2, }; enum { IRQ_SET_MASK_OK = 0, IRQ_SET_MASK_OK_NOCOPY = 1, IRQ_SET_MASK_OK_DONE = 2, }; enum { IRQD_TRIGGER_MASK = 15, IRQD_SETAFFINITY_PENDING = 256, IRQD_ACTIVATED = 512, IRQD_NO_BALANCING = 1024, IRQD_PER_CPU = 2048, IRQD_AFFINITY_SET = 4096, IRQD_LEVEL = 8192, IRQD_WAKEUP_STATE = 16384, IRQD_MOVE_PCNTXT = 32768, IRQD_IRQ_DISABLED = 65536, IRQD_IRQ_MASKED = 131072, IRQD_IRQ_INPROGRESS = 262144, IRQD_WAKEUP_ARMED = 524288, IRQD_FORWARDED_TO_VCPU = 1048576, IRQD_AFFINITY_MANAGED = 2097152, IRQD_IRQ_STARTED = 4194304, IRQD_MANAGED_SHUTDOWN = 8388608, IRQD_SINGLE_TARGET = 16777216, IRQD_DEFAULT_TRIGGER_SET = 33554432, IRQD_CAN_RESERVE = 67108864, IRQD_MSI_NOMASK_QUIRK = 134217728, IRQD_HANDLE_ENFORCE_IRQCTX = 268435456, IRQD_AFFINITY_ON_ACTIVATE = 536870912, IRQD_IRQ_ENABLED_ON_SUSPEND = 1073741824, }; enum { X86_IRQ_ALLOC_CONTIGUOUS_VECTORS = 1, X86_IRQ_ALLOC_LEGACY = 2, }; struct apic_chip_data { struct irq_cfg hw_irq_cfg; unsigned int vector; unsigned int prev_vector; unsigned int cpu; unsigned int prev_cpu; unsigned int irq; struct hlist_node clist; unsigned int move_in_progress: 1; unsigned int is_managed: 1; unsigned int can_reserve: 1; unsigned int has_reserved: 1; }; struct irq_matrix; enum { IRQ_TYPE_NONE = 0, IRQ_TYPE_EDGE_RISING = 1, IRQ_TYPE_EDGE_FALLING = 2, IRQ_TYPE_EDGE_BOTH = 3, IRQ_TYPE_LEVEL_HIGH = 4, IRQ_TYPE_LEVEL_LOW = 8, IRQ_TYPE_LEVEL_MASK = 12, IRQ_TYPE_SENSE_MASK = 15, IRQ_TYPE_DEFAULT = 15, IRQ_TYPE_PROBE = 16, IRQ_LEVEL = 256, IRQ_PER_CPU = 512, IRQ_NOPROBE = 1024, IRQ_NOREQUEST = 2048, IRQ_NOAUTOEN = 4096, IRQ_NO_BALANCING = 8192, IRQ_MOVE_PCNTXT = 16384, IRQ_NESTED_THREAD = 32768, IRQ_NOTHREAD = 65536, IRQ_PER_CPU_DEVID = 131072, IRQ_IS_POLLED = 262144, IRQ_DISABLE_UNLAZY = 524288, IRQ_HIDDEN = 1048576, IRQ_NO_DEBUG = 2097152, }; struct clock_event_device___2; union IO_APIC_reg_00 { u32 raw; struct { u32 __reserved_2: 14; u32 LTS: 1; u32 delivery_type: 1; u32 __reserved_1: 8; u32 ID: 8; } bits; }; union IO_APIC_reg_01 { u32 raw; struct { u32 version: 8; u32 __reserved_2: 7; u32 PRQ: 1; u32 entries: 8; u32 __reserved_1: 8; } bits; }; union IO_APIC_reg_02 { u32 raw; struct { u32 __reserved_2: 24; u32 arbitration: 4; u32 __reserved_1: 4; } bits; }; union IO_APIC_reg_03 { u32 raw; struct { u32 boot_DT: 1; u32 __reserved_1: 31; } bits; }; struct IO_APIC_route_entry { union { struct { u64 vector: 8; u64 delivery_mode: 3; u64 dest_mode_logical: 1; u64 delivery_status: 1; u64 active_low: 1; u64 irr: 1; u64 is_level: 1; u64 masked: 1; u64 reserved_0: 15; u64 reserved_1: 17; u64 virt_destid_8_14: 7; u64 destid_0_7: 8; }; struct { u64 ir_shared_0: 8; u64 ir_zero: 3; u64 ir_index_15: 1; u64 ir_shared_1: 5; u64 ir_reserved_0: 31; u64 ir_format: 1; u64 ir_index_0_14: 15; }; struct { u64 w1: 32; u64 w2: 32; }; }; }; struct irq_pin_list { struct list_head list; int apic; int pin; }; struct mp_chip_data { struct list_head irq_2_pin; struct IO_APIC_route_entry entry; bool is_level; bool active_low; bool isa_irq; u32 count; }; struct mp_ioapic_gsi { u32 gsi_base; u32 gsi_end; }; struct ioapic { int nr_registers; struct IO_APIC_route_entry *saved_registers; struct mpc_ioapic mp_config; struct mp_ioapic_gsi gsi_config; struct ioapic_domain_cfg irqdomain_cfg; struct irq_domain *irqdomain; struct resource *iomem_res; }; struct io_apic { unsigned int index; unsigned int unused[3]; unsigned int data; unsigned int unused2[11]; unsigned int eoi; }; enum { IRQ_DOMAIN_FLAG_HIERARCHY = 1, IRQ_DOMAIN_NAME_ALLOCATED = 2, IRQ_DOMAIN_FLAG_IPI_PER_CPU = 4, IRQ_DOMAIN_FLAG_IPI_SINGLE = 8, IRQ_DOMAIN_FLAG_MSI = 16, IRQ_DOMAIN_FLAG_MSI_REMAP = 32, IRQ_DOMAIN_MSI_NOMASK_QUIRK = 64, IRQ_DOMAIN_FLAG_NO_MAP = 128, IRQ_DOMAIN_FLAG_NONCORE = 65536, }; struct cluster_mask { unsigned int clusterid; int node; struct cpumask mask; }; struct dyn_arch_ftrace {}; enum { FTRACE_OPS_FL_ENABLED = 1, FTRACE_OPS_FL_DYNAMIC = 2, FTRACE_OPS_FL_SAVE_REGS = 4, FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 8, FTRACE_OPS_FL_RECURSION = 16, FTRACE_OPS_FL_STUB = 32, FTRACE_OPS_FL_INITIALIZED = 64, FTRACE_OPS_FL_DELETED = 128, FTRACE_OPS_FL_ADDING = 256, FTRACE_OPS_FL_REMOVING = 512, FTRACE_OPS_FL_MODIFYING = 1024, FTRACE_OPS_FL_ALLOC_TRAMP = 2048, FTRACE_OPS_FL_IPMODIFY = 4096, FTRACE_OPS_FL_PID = 8192, FTRACE_OPS_FL_RCU = 16384, FTRACE_OPS_FL_TRACE_ARRAY = 32768, FTRACE_OPS_FL_PERMANENT = 65536, FTRACE_OPS_FL_DIRECT = 131072, }; enum { FTRACE_FL_ENABLED = 2147483648, FTRACE_FL_REGS = 1073741824, FTRACE_FL_REGS_EN = 536870912, FTRACE_FL_TRAMP = 268435456, FTRACE_FL_TRAMP_EN = 134217728, FTRACE_FL_IPMODIFY = 67108864, FTRACE_FL_DISABLED = 33554432, FTRACE_FL_DIRECT = 16777216, FTRACE_FL_DIRECT_EN = 8388608, }; struct dyn_ftrace { long unsigned int ip; long unsigned int flags; struct dyn_arch_ftrace arch; }; enum { FTRACE_UPDATE_IGNORE = 0, FTRACE_UPDATE_MAKE_CALL = 1, FTRACE_UPDATE_MODIFY_CALL = 2, FTRACE_UPDATE_MAKE_NOP = 3, }; union ftrace_op_code_union { char code[7]; struct { char op[3]; int offset; } __attribute__((packed)); }; struct ftrace_rec_iter; typedef __u64 Elf64_Off; typedef __s64 Elf64_Sxword; struct elf64_rela { Elf64_Addr r_offset; Elf64_Xword r_info; Elf64_Sxword r_addend; }; typedef struct elf64_rela Elf64_Rela; struct elf64_hdr { unsigned char e_ident[16]; Elf64_Half e_type; Elf64_Half e_machine; Elf64_Word e_version; Elf64_Addr e_entry; Elf64_Off e_phoff; Elf64_Off e_shoff; Elf64_Word e_flags; Elf64_Half e_ehsize; Elf64_Half e_phentsize; Elf64_Half e_phnum; Elf64_Half e_shentsize; Elf64_Half e_shnum; Elf64_Half e_shstrndx; }; typedef struct elf64_hdr Elf64_Ehdr; struct elf64_shdr { Elf64_Word sh_name; Elf64_Word sh_type; Elf64_Xword sh_flags; Elf64_Addr sh_addr; Elf64_Off sh_offset; Elf64_Xword sh_size; Elf64_Word sh_link; Elf64_Word sh_info; Elf64_Xword sh_addralign; Elf64_Xword sh_entsize; }; typedef struct elf64_shdr Elf64_Shdr; struct kimage_arch { p4d_t *p4d; pud_t *pud; pmd_t *pmd; pte_t *pte; }; typedef long unsigned int kimage_entry_t; struct kexec_segment { union { void *buf; void *kbuf; }; size_t bufsz; long unsigned int mem; size_t memsz; }; struct purgatory_info { const Elf64_Ehdr *ehdr; Elf64_Shdr *sechdrs; void *purgatory_buf; }; typedef int kexec_probe_t(const char *, long unsigned int); struct kimage; typedef void *kexec_load_t(struct kimage *, char *, long unsigned int, char *, long unsigned int, char *, long unsigned int); struct kexec_file_ops; struct kimage { kimage_entry_t head; kimage_entry_t *entry; kimage_entry_t *last_entry; long unsigned int start; struct page *control_code_page; struct page *swap_page; void *vmcoreinfo_data_copy; long unsigned int nr_segments; struct kexec_segment segment[16]; struct list_head control_pages; struct list_head dest_pages; struct list_head unusable_pages; long unsigned int control_page; unsigned int type: 1; unsigned int preserve_context: 1; unsigned int file_mode: 1; struct kimage_arch arch; void *kernel_buf; long unsigned int kernel_buf_len; void *initrd_buf; long unsigned int initrd_buf_len; char *cmdline_buf; long unsigned int cmdline_buf_len; const struct kexec_file_ops *fops; void *image_loader_data; struct purgatory_info purgatory_info; void *elf_headers; long unsigned int elf_headers_sz; long unsigned int elf_load_addr; }; typedef int kexec_cleanup_t(void *); struct kexec_file_ops { kexec_probe_t *probe; kexec_load_t *load; kexec_cleanup_t *cleanup; }; struct x86_mapping_info { void * (*alloc_pgt_page)(void *); void *context; long unsigned int page_flag; long unsigned int offset; bool direct_gbpages; long unsigned int kernpg_flag; }; struct init_pgtable_data { struct x86_mapping_info *info; pgd_t *level4p; }; typedef void crash_vmclear_fn(); struct kexec_buf { struct kimage *image; void *buffer; long unsigned int bufsz; long unsigned int mem; long unsigned int memsz; long unsigned int buf_align; long unsigned int buf_min; long unsigned int buf_max; bool top_down; }; struct crash_mem_range { u64 start; u64 end; }; struct crash_mem { unsigned int max_nr_ranges; unsigned int nr_ranges; struct crash_mem_range ranges[0]; }; struct crash_memmap_data { struct boot_params *params; unsigned int type; }; struct kexec_entry64_regs { uint64_t rax; uint64_t rcx; uint64_t rdx; uint64_t rbx; uint64_t rsp; uint64_t rbp; uint64_t rsi; uint64_t rdi; uint64_t r8; uint64_t r9; uint64_t r10; uint64_t r11; uint64_t r12; uint64_t r13; uint64_t r14; uint64_t r15; uint64_t rip; }; enum key_being_used_for { VERIFYING_MODULE_SIGNATURE = 0, VERIFYING_FIRMWARE_SIGNATURE = 1, VERIFYING_KEXEC_PE_SIGNATURE = 2, VERIFYING_KEY_SIGNATURE = 3, VERIFYING_KEY_SELF_SIGNATURE = 4, VERIFYING_UNSPECIFIED_SIGNATURE = 5, NR__KEY_BEING_USED_FOR = 6, }; struct efi_setup_data { u64 fw_vendor; u64 __unused; u64 tables; u64 smbios; u64 reserved[8]; }; struct bzimage64_data { void *bootparams_buf; }; struct freelist_node { atomic_t refs; struct freelist_node *next; }; struct freelist_head { struct freelist_node *head; }; struct prev_kprobe { struct kprobe *kp; long unsigned int status; long unsigned int old_flags; long unsigned int saved_flags; }; struct kprobe_ctlblk { long unsigned int kprobe_status; long unsigned int kprobe_old_flags; long unsigned int kprobe_saved_flags; struct prev_kprobe prev_kprobe; }; struct kretprobe_instance; typedef int (*kretprobe_handler_t)(struct kretprobe_instance *, struct pt_regs *); struct kretprobe_holder; struct kretprobe_instance { union { struct freelist_node freelist; struct callback_head rcu; }; struct llist_node llist; struct kretprobe_holder *rph; kprobe_opcode_t *ret_addr; void *fp; char data[0]; }; struct kretprobe; struct kretprobe_holder { struct kretprobe *rp; refcount_t ref; }; struct kretprobe { struct kprobe kp; kretprobe_handler_t handler; kretprobe_handler_t entry_handler; int maxactive; int nmissed; size_t data_size; struct freelist_head freelist; struct kretprobe_holder *rph; }; struct kretprobe_blackpoint { const char *name; void *addr; }; struct kprobe_insn_cache { struct mutex mutex; void * (*alloc)(); void (*free)(void *); const char *sym; struct list_head pages; size_t insn_size; int nr_garbage; }; struct __arch_relative_insn { u8 op; s32 raddr; } __attribute__((packed)); struct arch_optimized_insn { kprobe_opcode_t copied_insn[4]; kprobe_opcode_t *insn; size_t size; }; struct optimized_kprobe { struct kprobe kp; struct list_head list; struct arch_optimized_insn optinsn; }; enum { TRACE_FTRACE_BIT = 0, TRACE_FTRACE_NMI_BIT = 1, TRACE_FTRACE_IRQ_BIT = 2, TRACE_FTRACE_SIRQ_BIT = 3, TRACE_INTERNAL_BIT = 4, TRACE_INTERNAL_NMI_BIT = 5, TRACE_INTERNAL_IRQ_BIT = 6, TRACE_INTERNAL_SIRQ_BIT = 7, TRACE_BRANCH_BIT = 8, TRACE_IRQ_BIT = 9, TRACE_GRAPH_BIT = 10, TRACE_GRAPH_DEPTH_START_BIT = 11, TRACE_GRAPH_DEPTH_END_BIT = 12, TRACE_GRAPH_NOTRACE_BIT = 13, TRACE_TRANSITION_BIT = 14, TRACE_RECORD_RECURSION_BIT = 15, }; enum { TRACE_CTX_NMI = 0, TRACE_CTX_IRQ = 1, TRACE_CTX_SOFTIRQ = 2, TRACE_CTX_NORMAL = 3, }; struct console { char name[16]; void (*write)(struct console *, const char *, unsigned int); int (*read)(struct console *, char *, unsigned int); struct tty_driver * (*device)(struct console *, int *); void (*unblank)(); int (*setup)(struct console *, char *); int (*exit)(struct console *); int (*match)(struct console *, char *, int, char *); short int flags; short int index; int cflag; void *data; struct console *next; }; struct hpet_data { long unsigned int hd_phys_address; void *hd_address; short unsigned int hd_nirqs; unsigned int hd_state; unsigned int hd_irq[32]; }; typedef irqreturn_t (*rtc_irq_handler)(int, void *); enum hpet_mode { HPET_MODE_UNUSED = 0, HPET_MODE_LEGACY = 1, HPET_MODE_CLOCKEVT = 2, HPET_MODE_DEVICE = 3, }; struct hpet_channel { struct clock_event_device evt; unsigned int num; unsigned int cpu; unsigned int irq; unsigned int in_use; enum hpet_mode mode; unsigned int boot_cfg; char name[10]; long: 48; long: 64; long: 64; long: 64; }; struct hpet_base { unsigned int nr_channels; unsigned int nr_clockevents; unsigned int boot_cfg; struct hpet_channel *channels; }; union hpet_lock { struct { arch_spinlock_t lock; u32 value; }; u64 lockval; }; struct amd_nb_bus_dev_range { u8 bus; u8 dev_base; u8 dev_limit; }; struct amd_northbridge_info { u16 num; u64 flags; struct amd_northbridge *nb; }; struct swait_queue { struct task_struct *task; struct list_head task_list; }; struct kvm_steal_time { __u64 steal; __u32 version; __u32 flags; __u8 preempted; __u8 u8_pad[3]; __u32 pad[11]; }; struct kvm_vcpu_pv_apf_data { __u32 flags; __u32 token; __u8 pad[56]; __u32 enabled; }; struct kvm_task_sleep_node { struct hlist_node link; struct swait_queue_head wq; u32 token; int cpu; }; struct kvm_task_sleep_head { raw_spinlock_t lock; struct hlist_head list; }; typedef struct ldttss_desc ldt_desc; struct branch { unsigned char opcode; u32 delta; } __attribute__((packed)); typedef long unsigned int ulong; struct jailhouse_setup_data { struct { __u16 version; __u16 compatible_version; } hdr; struct { __u16 pm_timer_address; __u16 num_cpus; __u64 pci_mmconfig_base; __u32 tsc_khz; __u32 apic_khz; __u8 standard_ioapic; __u8 cpu_ids[255]; } __attribute__((packed)) v1; struct { __u32 flags; } v2; } __attribute__((packed)); struct circ_buf { char *buf; int head; int tail; }; struct serial_rs485 { __u32 flags; __u32 delay_rts_before_send; __u32 delay_rts_after_send; __u32 padding[5]; }; struct serial_iso7816 { __u32 flags; __u32 tg; __u32 sc_fi; __u32 sc_di; __u32 clk; __u32 reserved[5]; }; struct uart_port; struct uart_ops { unsigned int (*tx_empty)(struct uart_port *); void (*set_mctrl)(struct uart_port *, unsigned int); unsigned int (*get_mctrl)(struct uart_port *); void (*stop_tx)(struct uart_port *); void (*start_tx)(struct uart_port *); void (*throttle)(struct uart_port *); void (*unthrottle)(struct uart_port *); void (*send_xchar)(struct uart_port *, char); void (*stop_rx)(struct uart_port *); void (*enable_ms)(struct uart_port *); void (*break_ctl)(struct uart_port *, int); int (*startup)(struct uart_port *); void (*shutdown)(struct uart_port *); void (*flush_buffer)(struct uart_port *); void (*set_termios)(struct uart_port *, struct ktermios *, struct ktermios *); void (*set_ldisc)(struct uart_port *, struct ktermios *); void (*pm)(struct uart_port *, unsigned int, unsigned int); const char * (*type)(struct uart_port *); void (*release_port)(struct uart_port *); int (*request_port)(struct uart_port *); void (*config_port)(struct uart_port *, int); int (*verify_port)(struct uart_port *, struct serial_struct *); int (*ioctl)(struct uart_port *, unsigned int, long unsigned int); }; struct uart_icount { __u32 cts; __u32 dsr; __u32 rng; __u32 dcd; __u32 rx; __u32 tx; __u32 frame; __u32 overrun; __u32 parity; __u32 brk; __u32 buf_overrun; }; typedef unsigned int upf_t; typedef unsigned int upstat_t; struct gpio_desc; struct uart_state; struct uart_port { spinlock_t lock; long unsigned int iobase; unsigned char *membase; unsigned int (*serial_in)(struct uart_port *, int); void (*serial_out)(struct uart_port *, int, int); void (*set_termios)(struct uart_port *, struct ktermios *, struct ktermios *); void (*set_ldisc)(struct uart_port *, struct ktermios *); unsigned int (*get_mctrl)(struct uart_port *); void (*set_mctrl)(struct uart_port *, unsigned int); unsigned int (*get_divisor)(struct uart_port *, unsigned int, unsigned int *); void (*set_divisor)(struct uart_port *, unsigned int, unsigned int, unsigned int); int (*startup)(struct uart_port *); void (*shutdown)(struct uart_port *); void (*throttle)(struct uart_port *); void (*unthrottle)(struct uart_port *); int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int, unsigned int); void (*handle_break)(struct uart_port *); int (*rs485_config)(struct uart_port *, struct serial_rs485 *); int (*iso7816_config)(struct uart_port *, struct serial_iso7816 *); unsigned int irq; long unsigned int irqflags; unsigned int uartclk; unsigned int fifosize; unsigned char x_char; unsigned char regshift; unsigned char iotype; unsigned char quirks; unsigned int read_status_mask; unsigned int ignore_status_mask; struct uart_state *state; struct uart_icount icount; struct console *cons; upf_t flags; upstat_t status; int hw_stopped; unsigned int mctrl; unsigned int timeout; unsigned int type; const struct uart_ops *ops; unsigned int custom_divisor; unsigned int line; unsigned int minor; resource_size_t mapbase; resource_size_t mapsize; struct device *dev; long unsigned int sysrq; unsigned int sysrq_ch; unsigned char has_sysrq; unsigned char sysrq_seq; unsigned char hub6; unsigned char suspended; unsigned char console_reinit; const char *name; struct attribute_group *attr_group; const struct attribute_group **tty_groups; struct serial_rs485 rs485; struct gpio_desc *rs485_term_gpio; struct serial_iso7816 iso7816; void *private_data; }; enum uart_pm_state { UART_PM_STATE_ON = 0, UART_PM_STATE_OFF = 3, UART_PM_STATE_UNDEFINED = 4, }; struct uart_state { struct tty_port port; enum uart_pm_state pm_state; struct circ_buf xmit; atomic_t refcount; wait_queue_head_t remove_wait; struct uart_port *uart_port; }; struct pci_mmcfg_region { struct list_head list; struct resource res; u64 address; char *virt; u16 segment; u8 start_bus; u8 end_bus; char name[30]; }; struct scan_area { u64 addr; u64 size; }; struct uprobe_xol_ops; struct arch_uprobe { union { u8 insn[16]; u8 ixol[16]; }; const struct uprobe_xol_ops *ops; union { struct { s32 offs; u8 ilen; u8 opc1; } branch; struct { u8 fixups; u8 ilen; } defparam; struct { u8 reg_offset; u8 ilen; } push; }; }; struct uprobe_xol_ops { bool (*emulate)(struct arch_uprobe *, struct pt_regs *); int (*pre_xol)(struct arch_uprobe *, struct pt_regs *); int (*post_xol)(struct arch_uprobe *, struct pt_regs *); void (*abort)(struct arch_uprobe *, struct pt_regs *); }; enum rp_check { RP_CHECK_CALL = 0, RP_CHECK_CHAIN_CALL = 1, RP_CHECK_RET = 2, }; struct simplefb_platform_data { u32 width; u32 height; u32 stride; const char *format; }; enum { M_I17 = 0, M_I20 = 1, M_I20_SR = 2, M_I24 = 3, M_I24_8_1 = 4, M_I24_10_1 = 5, M_I27_11_1 = 6, M_MINI = 7, M_MINI_3_1 = 8, M_MINI_4_1 = 9, M_MB = 10, M_MB_2 = 11, M_MB_3 = 12, M_MB_5_1 = 13, M_MB_6_1 = 14, M_MB_7_1 = 15, M_MB_SR = 16, M_MBA = 17, M_MBA_3 = 18, M_MBP = 19, M_MBP_2 = 20, M_MBP_2_2 = 21, M_MBP_SR = 22, M_MBP_4 = 23, M_MBP_5_1 = 24, M_MBP_5_2 = 25, M_MBP_5_3 = 26, M_MBP_6_1 = 27, M_MBP_6_2 = 28, M_MBP_7_1 = 29, M_MBP_8_2 = 30, M_UNKNOWN = 31, }; struct efifb_dmi_info { char *optname; long unsigned int base; int stride; int width; int height; int flags; }; enum { OVERRIDE_NONE = 0, OVERRIDE_BASE = 1, OVERRIDE_STRIDE = 2, OVERRIDE_HEIGHT = 4, OVERRIDE_WIDTH = 8, }; enum perf_sample_regs_abi { PERF_SAMPLE_REGS_ABI_NONE = 0, PERF_SAMPLE_REGS_ABI_32 = 1, PERF_SAMPLE_REGS_ABI_64 = 2, }; struct va_format { const char *fmt; va_list *va; }; enum es_result { ES_OK = 0, ES_UNSUPPORTED = 1, ES_VMM_ERROR = 2, ES_DECODE_FAILED = 3, ES_EXCEPTION = 4, ES_RETRY = 5, }; struct es_fault_info { long unsigned int vector; long unsigned int error_code; long unsigned int cr2; }; struct es_em_ctxt { struct pt_regs *regs; struct insn insn; struct es_fault_info fi; }; struct sev_es_runtime_data { struct ghcb ghcb_page; char ist_stack[4096]; char fallback_stack[4096]; struct ghcb backup_ghcb; bool ghcb_active; bool backup_ghcb_active; long unsigned int dr7; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct ghcb_state { struct ghcb *ghcb; }; struct pci_hostbridge_probe { u32 bus; u32 slot; u32 vendor; u32 device; }; struct trace_print_flags { long unsigned int mask; const char *name; }; enum tlb_flush_reason { TLB_FLUSH_ON_TASK_SWITCH = 0, TLB_REMOTE_SHOOTDOWN = 1, TLB_LOCAL_SHOOTDOWN = 2, TLB_LOCAL_MM_SHOOTDOWN = 3, TLB_REMOTE_SEND_IPI = 4, NR_TLB_FLUSH_REASONS = 5, }; enum { REGION_INTERSECTS = 0, REGION_DISJOINT = 1, REGION_MIXED = 2, }; struct trace_event_raw_tlb_flush { struct trace_entry ent; int reason; long unsigned int pages; char __data[0]; }; struct trace_event_data_offsets_tlb_flush {}; typedef void (*btf_trace_tlb_flush)(void *, int, long unsigned int); struct map_range { long unsigned int start; long unsigned int end; unsigned int page_size_mask; }; struct mhp_params { struct vmem_altmap *altmap; pgprot_t pgprot; }; struct mem_section_usage { long unsigned int subsection_map[1]; long unsigned int pageblock_flags[0]; }; struct mem_section { long unsigned int section_mem_map; struct mem_section_usage *usage; }; enum kcore_type { KCORE_TEXT = 0, KCORE_VMALLOC = 1, KCORE_RAM = 2, KCORE_VMEMMAP = 3, KCORE_USER = 4, }; struct kcore_list { struct list_head list; long unsigned int addr; size_t size; int type; }; enum { MEMORY_HOTPLUG_MIN_BOOTMEM_TYPE = 12, SECTION_INFO = 12, MIX_SECTION_INFO = 13, NODE_INFO = 14, MEMORY_HOTPLUG_MAX_BOOTMEM_TYPE = 14, }; struct hstate { struct mutex resize_lock; int next_nid_to_alloc; int next_nid_to_free; unsigned int order; long unsigned int mask; long unsigned int max_huge_pages; long unsigned int nr_huge_pages; long unsigned int free_huge_pages; long unsigned int resv_huge_pages; long unsigned int surplus_huge_pages; long unsigned int nr_overcommit_huge_pages; struct list_head hugepage_activelist; struct list_head hugepage_freelists[32]; unsigned int nr_huge_pages_node[32]; unsigned int free_huge_pages_node[32]; unsigned int surplus_huge_pages_node[32]; unsigned int nr_free_vmemmap_pages; struct cftype cgroup_files_dfl[7]; struct cftype cgroup_files_legacy[9]; char name[32]; }; struct trace_event_raw_x86_exceptions { struct trace_entry ent; long unsigned int address; long unsigned int ip; long unsigned int error_code; char __data[0]; }; struct trace_event_data_offsets_x86_exceptions {}; typedef void (*btf_trace_page_fault_user)(void *, long unsigned int, struct pt_regs *, long unsigned int); typedef void (*btf_trace_page_fault_kernel)(void *, long unsigned int, struct pt_regs *, long unsigned int); enum { IORES_MAP_SYSTEM_RAM = 1, IORES_MAP_ENCRYPTED = 2, }; struct ioremap_desc { unsigned int flags; }; typedef bool (*ex_handler_t)(const struct exception_table_entry *, struct pt_regs *, int, long unsigned int, long unsigned int); struct hugepage_subpool { spinlock_t lock; long int count; long int max_hpages; long int used_hpages; struct hstate *hstate; long int min_hpages; long int rsv_hpages; }; struct hugetlbfs_sb_info { long int max_inodes; long int free_inodes; spinlock_t stat_lock; struct hstate *hstate; struct hugepage_subpool *spool; kuid_t uid; kgid_t gid; umode_t mode; }; struct exception_stacks { char DF_stack_guard[0]; char DF_stack[4096]; char NMI_stack_guard[0]; char NMI_stack[4096]; char DB_stack_guard[0]; char DB_stack[4096]; char MCE_stack_guard[0]; char MCE_stack[4096]; char VC_stack_guard[0]; char VC_stack[0]; char VC2_stack_guard[0]; char VC2_stack[0]; char IST_top_guard[0]; }; struct vm_event_state { long unsigned int event[100]; }; struct cpa_data { long unsigned int *vaddr; pgd_t *pgd; pgprot_t mask_set; pgprot_t mask_clr; long unsigned int numpages; long unsigned int curpage; long unsigned int pfn; unsigned int flags; unsigned int force_split: 1; unsigned int force_static_prot: 1; unsigned int force_flush_all: 1; struct page **pages; }; enum cpa_warn { CPA_CONFLICT = 0, CPA_PROTECT = 1, CPA_DETECT = 2, }; typedef struct { u64 val; } pfn_t; struct memtype { u64 start; u64 end; u64 subtree_max_end; enum page_cache_mode type; struct rb_node rb; }; enum { PAT_UC = 0, PAT_WC = 1, PAT_WT = 4, PAT_WP = 5, PAT_WB = 6, PAT_UC_MINUS = 7, }; struct pagerange_state { long unsigned int cur_pfn; int ram; int not_ram; }; struct rb_augment_callbacks { void (*propagate)(struct rb_node *, struct rb_node *); void (*copy)(struct rb_node *, struct rb_node *); void (*rotate)(struct rb_node *, struct rb_node *); }; enum { MEMTYPE_EXACT_MATCH = 0, MEMTYPE_END_MATCH = 1, }; struct ptdump_range { long unsigned int start; long unsigned int end; }; struct ptdump_state { void (*note_page)(struct ptdump_state *, long unsigned int, int, u64); void (*effective_prot)(struct ptdump_state *, int, u64); const struct ptdump_range *range; }; struct addr_marker; struct pg_state { struct ptdump_state ptdump; int level; pgprotval_t current_prot; pgprotval_t effective_prot; pgprotval_t prot_levels[5]; long unsigned int start_address; const struct addr_marker *marker; long unsigned int lines; bool to_dmesg; bool check_wx; long unsigned int wx_pages; struct seq_file *seq; }; struct addr_marker { long unsigned int start_address; const char *name; long unsigned int max_lines; }; enum address_markers_idx { USER_SPACE_NR = 0, KERNEL_SPACE_NR = 1, LDT_NR = 2, LOW_KERNEL_NR = 3, VMALLOC_START_NR = 4, VMEMMAP_START_NR = 5, CPU_ENTRY_AREA_NR = 6, ESPFIX_START_NR = 7, EFI_END_NR = 8, HIGH_KERNEL_NR = 9, MODULES_VADDR_NR = 10, MODULES_END_NR = 11, FIXADDR_START_NR = 12, END_OF_SPACE_NR = 13, }; typedef void (*rcu_callback_t)(struct callback_head *); struct kmmio_probe; typedef void (*kmmio_pre_handler_t)(struct kmmio_probe *, struct pt_regs *, long unsigned int); typedef void (*kmmio_post_handler_t)(struct kmmio_probe *, long unsigned int, struct pt_regs *); struct kmmio_probe { struct list_head list; long unsigned int addr; long unsigned int len; kmmio_pre_handler_t pre_handler; kmmio_post_handler_t post_handler; void *private; }; struct kmmio_fault_page { struct list_head list; struct kmmio_fault_page *release_next; long unsigned int addr; pteval_t old_presence; bool armed; int count; bool scheduled_for_release; }; struct kmmio_delayed_release { struct callback_head rcu; struct kmmio_fault_page *release_list; }; struct kmmio_context { struct kmmio_fault_page *fpage; struct kmmio_probe *probe; long unsigned int saved_flags; long unsigned int addr; int active; }; enum reason_type { NOT_ME = 0, NOTHING = 1, REG_READ = 2, REG_WRITE = 3, IMM_WRITE = 4, OTHERS = 5, }; struct prefix_bits { unsigned int shorted: 1; unsigned int enlarged: 1; unsigned int rexr: 1; unsigned int rex: 1; }; enum { arg_AL = 0, arg_CL = 1, arg_DL = 2, arg_BL = 3, arg_AH = 4, arg_CH = 5, arg_DH = 6, arg_BH = 7, arg_AX = 0, arg_CX = 1, arg_DX = 2, arg_BX = 3, arg_SP = 4, arg_BP = 5, arg_SI = 6, arg_DI = 7, arg_R8 = 8, arg_R9 = 9, arg_R10 = 10, arg_R11 = 11, arg_R12 = 12, arg_R13 = 13, arg_R14 = 14, arg_R15 = 15, }; enum mm_io_opcode { MMIO_READ = 1, MMIO_WRITE = 2, MMIO_PROBE = 3, MMIO_UNPROBE = 4, MMIO_UNKNOWN_OP = 5, }; struct mmiotrace_rw { resource_size_t phys; long unsigned int value; long unsigned int pc; int map_id; unsigned char opcode; unsigned char width; }; struct mmiotrace_map { resource_size_t phys; long unsigned int virt; long unsigned int len; int map_id; unsigned char opcode; }; struct trap_reason { long unsigned int addr; long unsigned int ip; enum reason_type type; int active_traces; }; struct remap_trace { struct list_head list; struct kmmio_probe probe; resource_size_t phys; long unsigned int id; }; struct numa_memblk { u64 start; u64 end; int nid; }; struct numa_meminfo { int nr_blks; struct numa_memblk blk[64]; }; struct acpi_srat_cpu_affinity { struct acpi_subtable_header header; u8 proximity_domain_lo; u8 apic_id; u32 flags; u8 local_sapic_eid; u8 proximity_domain_hi[3]; u32 clock_domain; }; struct acpi_srat_x2apic_cpu_affinity { struct acpi_subtable_header header; u16 reserved; u32 proximity_domain; u32 apic_id; u32 flags; u32 clock_domain; u32 reserved2; }; enum uv_system_type { UV_NONE = 0, UV_LEGACY_APIC = 1, UV_X2APIC = 2, }; struct rnd_state { __u32 s1; __u32 s2; __u32 s3; __u32 s4; }; struct kaslr_memory_region { long unsigned int *base; long unsigned int size_tb; }; enum pti_mode { PTI_AUTO = 0, PTI_FORCE_OFF = 1, PTI_FORCE_ON = 2, }; enum pti_clone_level { PTI_CLONE_PMD = 0, PTI_CLONE_PTE = 1, }; struct sme_populate_pgd_data { void *pgtable_area; pgd_t *pgd; pmdval_t pmd_flags; pteval_t pte_flags; long unsigned int paddr; long unsigned int vaddr; long unsigned int vaddr_end; }; struct sigcontext_32 { __u16 gs; __u16 __gsh; __u16 fs; __u16 __fsh; __u16 es; __u16 __esh; __u16 ds; __u16 __dsh; __u32 di; __u32 si; __u32 bp; __u32 sp; __u32 bx; __u32 dx; __u32 cx; __u32 ax; __u32 trapno; __u32 err; __u32 ip; __u16 cs; __u16 __csh; __u32 flags; __u32 sp_at_signal; __u16 ss; __u16 __ssh; __u32 fpstate; __u32 oldmask; __u32 cr2; }; typedef u32 compat_size_t; struct compat_sigaltstack { compat_uptr_t ss_sp; int ss_flags; compat_size_t ss_size; }; typedef struct compat_sigaltstack compat_stack_t; struct ucontext_ia32 { unsigned int uc_flags; unsigned int uc_link; compat_stack_t uc_stack; struct sigcontext_32 uc_mcontext; compat_sigset_t uc_sigmask; }; struct sigframe_ia32 { u32 pretcode; int sig; struct sigcontext_32 sc; struct _fpstate_32 fpstate_unused; unsigned int extramask[1]; char retcode[8]; }; struct rt_sigframe_ia32 { u32 pretcode; int sig; u32 pinfo; u32 puc; compat_siginfo_t info; struct ucontext_ia32 uc; char retcode[8]; }; typedef struct { efi_guid_t guid; u64 table; } efi_config_table_64_t; struct efi_memory_map_data { phys_addr_t phys_map; long unsigned int size; long unsigned int desc_version; long unsigned int desc_size; long unsigned int flags; }; struct efi_mem_range { struct range range; u64 attribute; }; enum efi_rts_ids { EFI_NONE = 0, EFI_GET_TIME = 1, EFI_SET_TIME = 2, EFI_GET_WAKEUP_TIME = 3, EFI_SET_WAKEUP_TIME = 4, EFI_GET_VARIABLE = 5, EFI_GET_NEXT_VARIABLE = 6, EFI_SET_VARIABLE = 7, EFI_QUERY_VARIABLE_INFO = 8, EFI_GET_NEXT_HIGH_MONO_COUNT = 9, EFI_RESET_SYSTEM = 10, EFI_UPDATE_CAPSULE = 11, EFI_QUERY_CAPSULE_CAPS = 12, }; struct efi_runtime_work { void *arg1; void *arg2; void *arg3; void *arg4; void *arg5; efi_status_t status; struct work_struct work; enum efi_rts_ids efi_rts_id; struct completion efi_rts_comp; }; typedef struct { efi_guid_t guid; u32 table; } efi_config_table_32_t; typedef union { struct { efi_guid_t guid; void *table; }; efi_config_table_32_t mixed_mode; } efi_config_table_t; typedef struct { efi_guid_t guid; long unsigned int *ptr; const char name[16]; } efi_config_table_type_t; typedef struct { efi_table_hdr_t hdr; u64 fw_vendor; u32 fw_revision; u32 __pad1; u64 con_in_handle; u64 con_in; u64 con_out_handle; u64 con_out; u64 stderr_handle; u64 stderr; u64 runtime; u64 boottime; u32 nr_tables; u32 __pad2; u64 tables; } efi_system_table_64_t; typedef struct { u32 version; u32 length; u64 memory_protection_attribute; } efi_properties_table_t; typedef int (*efi_memattr_perm_setter)(struct mm_struct *, efi_memory_desc_t *); typedef u16 ucs2_char_t; struct pm_qos_request { struct plist_node node; struct pm_qos_constraints *qos; }; enum { BPF_REG_0 = 0, BPF_REG_1 = 1, BPF_REG_2 = 2, BPF_REG_3 = 3, BPF_REG_4 = 4, BPF_REG_5 = 5, BPF_REG_6 = 6, BPF_REG_7 = 7, BPF_REG_8 = 8, BPF_REG_9 = 9, BPF_REG_10 = 10, __MAX_BPF_REG = 11, }; struct bpf_tramp_progs { struct bpf_prog *progs[38]; int nr_progs; }; enum bpf_jit_poke_reason { BPF_POKE_REASON_TAIL_CALL = 0, }; struct bpf_array_aux { enum bpf_prog_type type; bool jited; struct list_head poke_progs; struct bpf_map *map; struct mutex poke_mutex; struct work_struct work; }; struct bpf_array { struct bpf_map map; u32 elem_size; u32 index_mask; struct bpf_array_aux *aux; union { char value[0]; void *ptrs[0]; void *pptrs[0]; }; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; enum bpf_text_poke_type { BPF_MOD_CALL = 0, BPF_MOD_JUMP = 1, }; struct bpf_binary_header { u32 pages; int: 32; u8 image[0]; }; typedef void (*bpf_jit_fill_hole_t)(void *, unsigned int); struct jit_context { int cleanup_addr; }; struct x64_jit_data { struct bpf_binary_header *header; int *addrs; u8 *image; int proglen; struct jit_context ctx; }; enum tk_offsets { TK_OFFS_REAL = 0, TK_OFFS_BOOT = 1, TK_OFFS_TAI = 2, TK_OFFS_MAX = 3, }; typedef long unsigned int vm_flags_t; struct clone_args { __u64 flags; __u64 pidfd; __u64 child_tid; __u64 parent_tid; __u64 exit_signal; __u64 stack; __u64 stack_size; __u64 tls; __u64 set_tid; __u64 set_tid_size; __u64 cgroup; }; enum hrtimer_mode { HRTIMER_MODE_ABS = 0, HRTIMER_MODE_REL = 1, HRTIMER_MODE_PINNED = 2, HRTIMER_MODE_SOFT = 4, HRTIMER_MODE_HARD = 8, HRTIMER_MODE_ABS_PINNED = 2, HRTIMER_MODE_REL_PINNED = 3, HRTIMER_MODE_ABS_SOFT = 4, HRTIMER_MODE_REL_SOFT = 5, HRTIMER_MODE_ABS_PINNED_SOFT = 6, HRTIMER_MODE_REL_PINNED_SOFT = 7, HRTIMER_MODE_ABS_HARD = 8, HRTIMER_MODE_REL_HARD = 9, HRTIMER_MODE_ABS_PINNED_HARD = 10, HRTIMER_MODE_REL_PINNED_HARD = 11, }; struct fdtable { unsigned int max_fds; struct file **fd; long unsigned int *close_on_exec; long unsigned int *open_fds; long unsigned int *full_fds_bits; struct callback_head rcu; }; struct files_struct { atomic_t count; bool resize_in_progress; wait_queue_head_t resize_wait; struct fdtable *fdt; struct fdtable fdtab; long: 64; long: 64; long: 64; long: 64; spinlock_t file_lock; unsigned int next_fd; long unsigned int close_on_exec_init[1]; long unsigned int open_fds_init[1]; long unsigned int full_fds_bits_init[1]; struct file *fd_array[64]; long: 64; long: 64; long: 64; long: 64; }; struct robust_list { struct robust_list *next; }; struct robust_list_head { struct robust_list list; long int futex_offset; struct robust_list *list_op_pending; }; struct multiprocess_signals { sigset_t signal; struct hlist_node node; }; typedef int (*proc_visitor)(struct task_struct *, void *); enum { IOPRIO_CLASS_NONE = 0, IOPRIO_CLASS_RT = 1, IOPRIO_CLASS_BE = 2, IOPRIO_CLASS_IDLE = 3, }; typedef struct poll_table_struct poll_table; enum { FUTEX_STATE_OK = 0, FUTEX_STATE_EXITING = 1, FUTEX_STATE_DEAD = 2, }; enum proc_hidepid { HIDEPID_OFF = 0, HIDEPID_NO_ACCESS = 1, HIDEPID_INVISIBLE = 2, HIDEPID_NOT_PTRACEABLE = 4, }; enum proc_pidonly { PROC_PIDONLY_OFF = 0, PROC_PIDONLY_ON = 1, }; struct proc_fs_info { struct pid_namespace *pid_ns; struct dentry *proc_self; struct dentry *proc_thread_self; kgid_t pid_gid; enum proc_hidepid hide_pid; enum proc_pidonly pidonly; }; struct trace_event_raw_task_newtask { struct trace_entry ent; pid_t pid; char comm[16]; long unsigned int clone_flags; short int oom_score_adj; char __data[0]; }; struct trace_event_raw_task_rename { struct trace_entry ent; pid_t pid; char oldcomm[16]; char newcomm[16]; short int oom_score_adj; char __data[0]; }; struct trace_event_data_offsets_task_newtask {}; struct trace_event_data_offsets_task_rename {}; typedef void (*btf_trace_task_newtask)(void *, struct task_struct *, long unsigned int); typedef void (*btf_trace_task_rename)(void *, struct task_struct *, const char *); struct taint_flag { char c_true; char c_false; bool module; }; enum ftrace_dump_mode { DUMP_NONE = 0, DUMP_ALL = 1, DUMP_ORIG = 2, }; enum kmsg_dump_reason { KMSG_DUMP_UNDEF = 0, KMSG_DUMP_PANIC = 1, KMSG_DUMP_OOPS = 2, KMSG_DUMP_EMERG = 3, KMSG_DUMP_SHUTDOWN = 4, KMSG_DUMP_MAX = 5, }; enum con_flush_mode { CONSOLE_FLUSH_PENDING = 0, CONSOLE_REPLAY_ALL = 1, }; struct warn_args { const char *fmt; va_list args; }; struct smp_hotplug_thread { struct task_struct **store; struct list_head list; int (*thread_should_run)(unsigned int); void (*thread_fn)(unsigned int); void (*create)(unsigned int); void (*setup)(unsigned int); void (*cleanup)(unsigned int, bool); void (*park)(unsigned int); void (*unpark)(unsigned int); bool selfparking; const char *thread_comm; }; struct trace_event_raw_cpuhp_enter { struct trace_entry ent; unsigned int cpu; int target; int idx; void *fun; char __data[0]; }; struct trace_event_raw_cpuhp_multi_enter { struct trace_entry ent; unsigned int cpu; int target; int idx; void *fun; char __data[0]; }; struct trace_event_raw_cpuhp_exit { struct trace_entry ent; unsigned int cpu; int state; int idx; int ret; char __data[0]; }; struct trace_event_data_offsets_cpuhp_enter {}; struct trace_event_data_offsets_cpuhp_multi_enter {}; struct trace_event_data_offsets_cpuhp_exit {}; typedef void (*btf_trace_cpuhp_enter)(void *, unsigned int, int, int, int (*)(unsigned int)); typedef void (*btf_trace_cpuhp_multi_enter)(void *, unsigned int, int, int, int (*)(unsigned int, struct hlist_node *), struct hlist_node *); typedef void (*btf_trace_cpuhp_exit)(void *, unsigned int, int, int, int); struct cpuhp_cpu_state { enum cpuhp_state state; enum cpuhp_state target; enum cpuhp_state fail; struct task_struct *thread; bool should_run; bool rollback; bool single; bool bringup; int cpu; struct hlist_node *node; struct hlist_node *last; enum cpuhp_state cb_state; int result; struct completion done_up; struct completion done_down; }; struct cpuhp_step { const char *name; union { int (*single)(unsigned int); int (*multi)(unsigned int, struct hlist_node *); } startup; union { int (*single)(unsigned int); int (*multi)(unsigned int, struct hlist_node *); } teardown; struct hlist_head list; bool cant_stop; bool multi_instance; }; enum cpu_mitigations { CPU_MITIGATIONS_OFF = 0, CPU_MITIGATIONS_AUTO = 1, CPU_MITIGATIONS_AUTO_NOSMT = 2, }; struct __kernel_old_timeval { __kernel_long_t tv_sec; __kernel_long_t tv_usec; }; struct old_timeval32 { old_time32_t tv_sec; s32 tv_usec; }; struct rusage { struct __kernel_old_timeval ru_utime; struct __kernel_old_timeval ru_stime; __kernel_long_t ru_maxrss; __kernel_long_t ru_ixrss; __kernel_long_t ru_idrss; __kernel_long_t ru_isrss; __kernel_long_t ru_minflt; __kernel_long_t ru_majflt; __kernel_long_t ru_nswap; __kernel_long_t ru_inblock; __kernel_long_t ru_oublock; __kernel_long_t ru_msgsnd; __kernel_long_t ru_msgrcv; __kernel_long_t ru_nsignals; __kernel_long_t ru_nvcsw; __kernel_long_t ru_nivcsw; }; typedef struct {} mm_segment_t; struct compat_rusage { struct old_timeval32 ru_utime; struct old_timeval32 ru_stime; compat_long_t ru_maxrss; compat_long_t ru_ixrss; compat_long_t ru_idrss; compat_long_t ru_isrss; compat_long_t ru_minflt; compat_long_t ru_majflt; compat_long_t ru_nswap; compat_long_t ru_inblock; compat_long_t ru_oublock; compat_long_t ru_msgsnd; compat_long_t ru_msgrcv; compat_long_t ru_nsignals; compat_long_t ru_nvcsw; compat_long_t ru_nivcsw; }; struct waitid_info { pid_t pid; uid_t uid; int status; int cause; }; struct wait_opts { enum pid_type wo_type; int wo_flags; struct pid *wo_pid; struct waitid_info *wo_info; int wo_stat; struct rusage *wo_rusage; wait_queue_entry_t child_wait; int notask_error; }; struct softirq_action { void (*action)(struct softirq_action *); }; struct tasklet_struct { struct tasklet_struct *next; long unsigned int state; atomic_t count; bool use_callback; union { void (*func)(long unsigned int); void (*callback)(struct tasklet_struct *); }; long unsigned int data; }; enum { TASKLET_STATE_SCHED = 0, TASKLET_STATE_RUN = 1, }; struct kernel_stat { long unsigned int irqs_sum; unsigned int softirqs[10]; }; struct wait_bit_key { void *flags; int bit_nr; long unsigned int timeout; }; struct wait_bit_queue_entry { struct wait_bit_key key; struct wait_queue_entry wq_entry; }; struct trace_event_raw_irq_handler_entry { struct trace_entry ent; int irq; u32 __data_loc_name; char __data[0]; }; struct trace_event_raw_irq_handler_exit { struct trace_entry ent; int irq; int ret; char __data[0]; }; struct trace_event_raw_softirq { struct trace_entry ent; unsigned int vec; char __data[0]; }; struct trace_event_data_offsets_irq_handler_entry { u32 name; }; struct trace_event_data_offsets_irq_handler_exit {}; struct trace_event_data_offsets_softirq {}; typedef void (*btf_trace_irq_handler_entry)(void *, int, struct irqaction *); typedef void (*btf_trace_irq_handler_exit)(void *, int, struct irqaction *, int); typedef void (*btf_trace_softirq_entry)(void *, unsigned int); typedef void (*btf_trace_softirq_exit)(void *, unsigned int); typedef void (*btf_trace_softirq_raise)(void *, unsigned int); struct tasklet_head { struct tasklet_struct *head; struct tasklet_struct **tail; }; struct pseudo_fs_context { const struct super_operations *ops; const struct xattr_handler **xattr; const struct dentry_operations *dops; long unsigned int magic; }; typedef void (*dr_release_t)(struct device *, void *); typedef int (*dr_match_t)(struct device *, void *, void *); struct resource_entry { struct list_head node; struct resource *res; resource_size_t offset; struct resource __res; }; struct resource_constraint { resource_size_t min; resource_size_t max; resource_size_t align; resource_size_t (*alignf)(void *, const struct resource *, resource_size_t, resource_size_t); void *alignf_data; }; enum { MAX_IORES_LEVEL = 5, }; struct region_devres { struct resource *parent; resource_size_t start; resource_size_t n; }; typedef __kernel_clock_t clock_t; struct dentry_stat_t { long int nr_dentry; long int nr_unused; long int age_limit; long int want_pages; long int nr_negative; long int dummy; }; struct files_stat_struct { long unsigned int nr_files; long unsigned int nr_free_files; long unsigned int max_files; }; struct inodes_stat_t { long int nr_inodes; long int nr_unused; long int dummy[5]; }; enum sysctl_writes_mode { SYSCTL_WRITES_LEGACY = 4294967295, SYSCTL_WRITES_WARN = 0, SYSCTL_WRITES_STRICT = 1, }; struct do_proc_dointvec_minmax_conv_param { int *min; int *max; }; struct do_proc_douintvec_minmax_conv_param { unsigned int *min; unsigned int *max; }; struct __user_cap_header_struct { __u32 version; int pid; }; typedef struct __user_cap_header_struct *cap_user_header_t; struct __user_cap_data_struct { __u32 effective; __u32 permitted; __u32 inheritable; }; typedef struct __user_cap_data_struct *cap_user_data_t; struct sigqueue { struct list_head list; int flags; kernel_siginfo_t info; struct ucounts *ucounts; }; typedef int wait_bit_action_f(struct wait_bit_key *, int); struct ptrace_peeksiginfo_args { __u64 off; __u32 flags; __s32 nr; }; struct ptrace_syscall_info { __u8 op; __u8 pad[3]; __u32 arch; __u64 instruction_pointer; __u64 stack_pointer; union { struct { __u64 nr; __u64 args[6]; } entry; struct { __s64 rval; __u8 is_error; } exit; struct { __u64 nr; __u64 args[6]; __u32 ret_data; } seccomp; }; }; struct ptrace_rseq_configuration { __u64 rseq_abi_pointer; __u32 rseq_abi_size; __u32 signature; __u32 flags; __u32 pad; }; struct compat_iovec { compat_uptr_t iov_base; compat_size_t iov_len; }; typedef long unsigned int old_sigset_t; enum siginfo_layout { SIL_KILL = 0, SIL_TIMER = 1, SIL_POLL = 2, SIL_FAULT = 3, SIL_FAULT_TRAPNO = 4, SIL_FAULT_MCEERR = 5, SIL_FAULT_BNDERR = 6, SIL_FAULT_PKUERR = 7, SIL_PERF_EVENT = 8, SIL_CHLD = 9, SIL_RT = 10, SIL_SYS = 11, }; struct fd { struct file *file; unsigned int flags; }; typedef u32 compat_old_sigset_t; struct compat_sigaction { compat_uptr_t sa_handler; compat_ulong_t sa_flags; compat_uptr_t sa_restorer; compat_sigset_t sa_mask; }; struct compat_old_sigaction { compat_uptr_t sa_handler; compat_old_sigset_t sa_mask; compat_ulong_t sa_flags; compat_uptr_t sa_restorer; }; enum { TRACE_SIGNAL_DELIVERED = 0, TRACE_SIGNAL_IGNORED = 1, TRACE_SIGNAL_ALREADY_PENDING = 2, TRACE_SIGNAL_OVERFLOW_FAIL = 3, TRACE_SIGNAL_LOSE_INFO = 4, }; struct trace_event_raw_signal_generate { struct trace_entry ent; int sig; int errno; int code; char comm[16]; pid_t pid; int group; int result; char __data[0]; }; struct trace_event_raw_signal_deliver { struct trace_entry ent; int sig; int errno; int code; long unsigned int sa_handler; long unsigned int sa_flags; char __data[0]; }; struct trace_event_data_offsets_signal_generate {}; struct trace_event_data_offsets_signal_deliver {}; typedef void (*btf_trace_signal_generate)(void *, int, struct kernel_siginfo *, struct task_struct *, int, int); typedef void (*btf_trace_signal_deliver)(void *, int, struct kernel_siginfo *, struct k_sigaction *); struct sysinfo { __kernel_long_t uptime; __kernel_ulong_t loads[3]; __kernel_ulong_t totalram; __kernel_ulong_t freeram; __kernel_ulong_t sharedram; __kernel_ulong_t bufferram; __kernel_ulong_t totalswap; __kernel_ulong_t freeswap; __u16 procs; __u16 pad; __kernel_ulong_t totalhigh; __kernel_ulong_t freehigh; __u32 mem_unit; char _f[0]; }; enum { PER_LINUX = 0, PER_LINUX_32BIT = 8388608, PER_LINUX_FDPIC = 524288, PER_SVR4 = 68157441, PER_SVR3 = 83886082, PER_SCOSVR3 = 117440515, PER_OSR5 = 100663299, PER_WYSEV386 = 83886084, PER_ISCR4 = 67108869, PER_BSD = 6, PER_SUNOS = 67108870, PER_XENIX = 83886087, PER_LINUX32 = 8, PER_LINUX32_3GB = 134217736, PER_IRIX32 = 67108873, PER_IRIXN32 = 67108874, PER_IRIX64 = 67108875, PER_RISCOS = 12, PER_SOLARIS = 67108877, PER_UW7 = 68157454, PER_OSF4 = 15, PER_HPUX = 16, PER_MASK = 255, }; struct rlimit64 { __u64 rlim_cur; __u64 rlim_max; }; struct oldold_utsname { char sysname[9]; char nodename[9]; char release[9]; char version[9]; char machine[9]; }; struct old_utsname { char sysname[65]; char nodename[65]; char release[65]; char version[65]; char machine[65]; }; enum uts_proc { UTS_PROC_OSTYPE = 0, UTS_PROC_OSRELEASE = 1, UTS_PROC_VERSION = 2, UTS_PROC_HOSTNAME = 3, UTS_PROC_DOMAINNAME = 4, }; struct prctl_mm_map { __u64 start_code; __u64 end_code; __u64 start_data; __u64 end_data; __u64 start_brk; __u64 brk; __u64 start_stack; __u64 arg_start; __u64 arg_end; __u64 env_start; __u64 env_end; __u64 *auxv; __u32 auxv_size; __u32 exe_fd; }; struct compat_tms { compat_clock_t tms_utime; compat_clock_t tms_stime; compat_clock_t tms_cutime; compat_clock_t tms_cstime; }; struct compat_rlimit { compat_ulong_t rlim_cur; compat_ulong_t rlim_max; }; struct tms { __kernel_clock_t tms_utime; __kernel_clock_t tms_stime; __kernel_clock_t tms_cutime; __kernel_clock_t tms_cstime; }; struct getcpu_cache { long unsigned int blob[16]; }; struct compat_sysinfo { s32 uptime; u32 loads[3]; u32 totalram; u32 freeram; u32 sharedram; u32 bufferram; u32 totalswap; u32 freeswap; u16 procs; u16 pad; u32 totalhigh; u32 freehigh; u32 mem_unit; char _f[8]; }; struct wq_flusher; struct worker; struct workqueue_attrs; struct pool_workqueue; struct wq_device; struct workqueue_struct { struct list_head pwqs; struct list_head list; struct mutex mutex; int work_color; int flush_color; atomic_t nr_pwqs_to_flush; struct wq_flusher *first_flusher; struct list_head flusher_queue; struct list_head flusher_overflow; struct list_head maydays; struct worker *rescuer; int nr_drainers; int saved_max_active; struct workqueue_attrs *unbound_attrs; struct pool_workqueue *dfl_pwq; struct wq_device *wq_dev; char name[24]; struct callback_head rcu; long: 64; long: 64; long: 64; long: 64; long: 64; unsigned int flags; struct pool_workqueue *cpu_pwqs; struct pool_workqueue *numa_pwq_tbl[0]; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct workqueue_attrs { int nice; cpumask_var_t cpumask; bool no_numa; }; struct execute_work { struct work_struct work; }; enum { WQ_UNBOUND = 2, WQ_FREEZABLE = 4, WQ_MEM_RECLAIM = 8, WQ_HIGHPRI = 16, WQ_CPU_INTENSIVE = 32, WQ_SYSFS = 64, WQ_POWER_EFFICIENT = 128, __WQ_DRAINING = 65536, __WQ_ORDERED = 131072, __WQ_LEGACY = 262144, __WQ_ORDERED_EXPLICIT = 524288, WQ_MAX_ACTIVE = 512, WQ_MAX_UNBOUND_PER_CPU = 4, WQ_DFL_ACTIVE = 256, }; enum xa_lock_type { XA_LOCK_IRQ = 1, XA_LOCK_BH = 2, }; struct ida { struct xarray xa; }; struct __una_u32 { u32 x; }; struct worker_pool; struct worker { union { struct list_head entry; struct hlist_node hentry; }; struct work_struct *current_work; work_func_t current_func; struct pool_workqueue *current_pwq; struct list_head scheduled; struct task_struct *task; struct worker_pool *pool; struct list_head node; long unsigned int last_active; unsigned int flags; int id; int sleeping; char desc[24]; struct workqueue_struct *rescue_wq; work_func_t last_func; }; struct pool_workqueue { struct worker_pool *pool; struct workqueue_struct *wq; int work_color; int flush_color; int refcnt; int nr_in_flight[15]; int nr_active; int max_active; struct list_head delayed_works; struct list_head pwqs_node; struct list_head mayday_node; struct work_struct unbound_release_work; struct callback_head rcu; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct worker_pool { raw_spinlock_t lock; int cpu; int node; int id; unsigned int flags; long unsigned int watchdog_ts; struct list_head worklist; int nr_workers; int nr_idle; struct list_head idle_list; struct timer_list idle_timer; struct timer_list mayday_timer; struct hlist_head busy_hash[64]; struct worker *manager; struct list_head workers; struct completion *detach_completion; struct ida worker_ida; struct workqueue_attrs *attrs; struct hlist_node hash_node; int refcnt; long: 32; long: 64; long: 64; long: 64; atomic_t nr_running; struct callback_head rcu; long: 64; long: 64; long: 64; long: 64; long: 64; }; enum { POOL_MANAGER_ACTIVE = 1, POOL_DISASSOCIATED = 4, WORKER_DIE = 2, WORKER_IDLE = 4, WORKER_PREP = 8, WORKER_CPU_INTENSIVE = 64, WORKER_UNBOUND = 128, WORKER_REBOUND = 256, WORKER_NOT_RUNNING = 456, NR_STD_WORKER_POOLS = 2, UNBOUND_POOL_HASH_ORDER = 6, BUSY_WORKER_HASH_ORDER = 6, MAX_IDLE_WORKERS_RATIO = 4, IDLE_WORKER_TIMEOUT = 90000, MAYDAY_INITIAL_TIMEOUT = 3, MAYDAY_INTERVAL = 30, CREATE_COOLDOWN = 300, RESCUER_NICE_LEVEL = 4294967276, HIGHPRI_NICE_LEVEL = 4294967276, WQ_NAME_LEN = 24, }; struct wq_flusher { struct list_head list; int flush_color; struct completion done; }; struct wq_device { struct workqueue_struct *wq; struct device dev; }; struct trace_event_raw_workqueue_queue_work { struct trace_entry ent; void *work; void *function; u32 __data_loc_workqueue; unsigned int req_cpu; unsigned int cpu; char __data[0]; }; struct trace_event_raw_workqueue_activate_work { struct trace_entry ent; void *work; char __data[0]; }; struct trace_event_raw_workqueue_execute_start { struct trace_entry ent; void *work; void *function; char __data[0]; }; struct trace_event_raw_workqueue_execute_end { struct trace_entry ent; void *work; void *function; char __data[0]; }; struct trace_event_data_offsets_workqueue_queue_work { u32 workqueue; }; struct trace_event_data_offsets_workqueue_activate_work {}; struct trace_event_data_offsets_workqueue_execute_start {}; struct trace_event_data_offsets_workqueue_execute_end {}; typedef void (*btf_trace_workqueue_queue_work)(void *, unsigned int, struct pool_workqueue *, struct work_struct *); typedef void (*btf_trace_workqueue_activate_work)(void *, struct work_struct *); typedef void (*btf_trace_workqueue_execute_start)(void *, struct work_struct *); typedef void (*btf_trace_workqueue_execute_end)(void *, struct work_struct *, work_func_t); struct wq_barrier { struct work_struct work; struct completion done; struct task_struct *task; }; struct cwt_wait { wait_queue_entry_t wait; struct work_struct *work; }; struct apply_wqattrs_ctx { struct workqueue_struct *wq; struct workqueue_attrs *attrs; struct list_head list; struct pool_workqueue *dfl_pwq; struct pool_workqueue *pwq_tbl[0]; }; struct work_for_cpu { struct work_struct work; long int (*fn)(void *); void *arg; long int ret; }; typedef struct {} local_lock_t; struct radix_tree_preload { local_lock_t lock; unsigned int nr; struct xa_node *nodes; }; typedef void (*task_work_func_t)(struct callback_head *); enum { KERNEL_PARAM_OPS_FL_NOARG = 1, }; enum { KERNEL_PARAM_FL_UNSAFE = 1, KERNEL_PARAM_FL_HWPARAM = 2, }; struct param_attribute { struct module_attribute mattr; const struct kernel_param *param; }; struct module_param_attrs { unsigned int num; struct attribute_group grp; struct param_attribute attrs[0]; }; struct module_version_attribute { struct module_attribute mattr; const char *module_name; const char *version; }; struct kmalloced_param { struct list_head list; char val[0]; }; struct sched_param { int sched_priority; }; enum { __PERCPU_REF_ATOMIC = 1, __PERCPU_REF_DEAD = 2, __PERCPU_REF_ATOMIC_DEAD = 3, __PERCPU_REF_FLAG_BITS = 2, }; struct kthread_work; typedef void (*kthread_work_func_t)(struct kthread_work *); struct kthread_worker; struct kthread_work { struct list_head node; kthread_work_func_t func; struct kthread_worker *worker; int canceling; }; enum { KTW_FREEZABLE = 1, }; struct kthread_worker { unsigned int flags; raw_spinlock_t lock; struct list_head work_list; struct list_head delayed_work_list; struct task_struct *task; struct kthread_work *current_work; }; struct kthread_delayed_work { struct kthread_work work; struct timer_list timer; }; enum { CSS_NO_REF = 1, CSS_ONLINE = 2, CSS_RELEASED = 4, CSS_VISIBLE = 8, CSS_DYING = 16, }; struct kthread_create_info { int (*threadfn)(void *); void *data; int node; struct task_struct *result; struct completion *done; struct list_head list; }; struct kthread { long unsigned int flags; unsigned int cpu; int (*threadfn)(void *); void *data; mm_segment_t oldfs; struct completion parked; struct completion exited; struct cgroup_subsys_state *blkcg_css; }; enum KTHREAD_BITS { KTHREAD_IS_PER_CPU = 0, KTHREAD_SHOULD_STOP = 1, KTHREAD_SHOULD_PARK = 2, }; struct kthread_flush_work { struct kthread_work work; struct completion done; }; struct pt_regs___2; struct ipc_ids { int in_use; short unsigned int seq; struct rw_semaphore rwsem; struct idr ipcs_idr; int max_idx; int last_idx; int next_id; struct rhashtable key_ht; }; struct ipc_namespace { struct ipc_ids ids[3]; int sem_ctls[4]; int used_sems; unsigned int msg_ctlmax; unsigned int msg_ctlmnb; unsigned int msg_ctlmni; atomic_t msg_bytes; atomic_t msg_hdrs; size_t shm_ctlmax; size_t shm_ctlall; long unsigned int shm_tot; int shm_ctlmni; int shm_rmid_forced; struct notifier_block ipcns_nb; struct vfsmount *mq_mnt; unsigned int mq_queues_count; unsigned int mq_queues_max; unsigned int mq_msg_max; unsigned int mq_msgsize_max; unsigned int mq_msg_default; unsigned int mq_msgsize_default; struct user_namespace *user_ns; struct ucounts *ucounts; struct llist_node mnt_llist; struct ns_common ns; }; struct srcu_notifier_head { struct mutex mutex; struct srcu_struct srcu; struct notifier_block *head; }; enum what { PROC_EVENT_NONE = 0, PROC_EVENT_FORK = 1, PROC_EVENT_EXEC = 2, PROC_EVENT_UID = 4, PROC_EVENT_GID = 64, PROC_EVENT_SID = 128, PROC_EVENT_PTRACE = 256, PROC_EVENT_COMM = 512, PROC_EVENT_COREDUMP = 1073741824, PROC_EVENT_EXIT = 2147483648, }; struct async_entry { struct list_head domain_list; struct list_head global_list; struct work_struct work; async_cookie_t cookie; async_func_t func; void *data; struct async_domain *domain; }; struct smpboot_thread_data { unsigned int cpu; unsigned int status; struct smp_hotplug_thread *ht; }; enum { HP_THREAD_NONE = 0, HP_THREAD_ACTIVE = 1, HP_THREAD_PARKED = 2, }; struct umd_info { const char *driver_name; struct file *pipe_to_umh; struct file *pipe_from_umh; struct path wd; struct pid *tgid; }; struct pin_cookie {}; struct preempt_notifier; struct preempt_ops { void (*sched_in)(struct preempt_notifier *, int); void (*sched_out)(struct preempt_notifier *, struct task_struct *); }; struct preempt_notifier { struct hlist_node link; struct preempt_ops *ops; }; enum { CSD_FLAG_LOCK = 1, IRQ_WORK_PENDING = 1, IRQ_WORK_BUSY = 2, IRQ_WORK_LAZY = 4, IRQ_WORK_HARD_IRQ = 8, IRQ_WORK_CLAIMED = 3, CSD_TYPE_ASYNC = 0, CSD_TYPE_SYNC = 16, CSD_TYPE_IRQ_WORK = 32, CSD_TYPE_TTWU = 48, CSD_FLAG_TYPE_MASK = 240, }; struct dl_bw { raw_spinlock_t lock; u64 bw; u64 total_bw; }; struct cpudl_item; struct cpudl { raw_spinlock_t lock; int size; cpumask_var_t free_cpus; struct cpudl_item *elements; }; struct cpupri_vec { atomic_t count; cpumask_var_t mask; }; struct cpupri { struct cpupri_vec pri_to_cpu[101]; int *cpu_to_pri; }; struct perf_domain; struct root_domain { atomic_t refcount; atomic_t rto_count; struct callback_head rcu; cpumask_var_t span; cpumask_var_t online; int overload; int overutilized; cpumask_var_t dlo_mask; atomic_t dlo_count; struct dl_bw dl_bw; struct cpudl cpudl; u64 visit_gen; struct irq_work rto_push_work; raw_spinlock_t rto_lock; int rto_loop; int rto_cpu; atomic_t rto_loop_next; atomic_t rto_loop_start; cpumask_var_t rto_mask; struct cpupri cpupri; long unsigned int max_cpu_capacity; struct perf_domain *pd; }; struct cfs_rq { struct load_weight load; unsigned int nr_running; unsigned int h_nr_running; unsigned int idle_h_nr_running; u64 exec_clock; u64 min_vruntime; unsigned int forceidle_seq; u64 min_vruntime_fi; struct rb_root_cached tasks_timeline; struct sched_entity *curr; struct sched_entity *next; struct sched_entity *last; struct sched_entity *skip; unsigned int nr_spread_over; long: 32; long: 64; struct sched_avg avg; struct { raw_spinlock_t lock; int nr; long unsigned int load_avg; long unsigned int util_avg; long unsigned int runnable_avg; long: 64; long: 64; long: 64; long: 64; } removed; long unsigned int tg_load_avg_contrib; long int propagate; long int prop_runnable_sum; long unsigned int h_load; u64 last_h_load_update; struct sched_entity *h_load_next; struct rq *rq; int on_list; struct list_head leaf_cfs_rq_list; struct task_group *tg; int runtime_enabled; s64 runtime_remaining; u64 throttled_clock; u64 throttled_clock_task; u64 throttled_clock_task_time; int throttled; int throttle_count; struct list_head throttled_list; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct cfs_bandwidth { raw_spinlock_t lock; ktime_t period; u64 quota; u64 runtime; u64 burst; s64 hierarchical_quota; u8 idle; u8 period_active; u8 slack_started; struct hrtimer period_timer; struct hrtimer slack_timer; struct list_head throttled_cfs_rq; int nr_periods; int nr_throttled; u64 throttled_time; }; struct task_group { struct cgroup_subsys_state css; struct sched_entity **se; struct cfs_rq **cfs_rq; long unsigned int shares; long: 64; long: 64; long: 64; long: 64; atomic_long_t load_avg; struct callback_head rcu; struct list_head list; struct task_group *parent; struct list_head siblings; struct list_head children; struct autogroup *autogroup; struct cfs_bandwidth cfs_bandwidth; unsigned int uclamp_pct[2]; struct uclamp_se uclamp_req[2]; struct uclamp_se uclamp[2]; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct sched_domain_attr { int relax_domain_level; }; struct sched_group { struct sched_group *next; atomic_t ref; unsigned int group_weight; struct sched_group_capacity *sgc; int asym_prefer_cpu; long unsigned int cpumask[0]; }; struct sched_group_capacity { atomic_t ref; long unsigned int capacity; long unsigned int min_capacity; long unsigned int max_capacity; long unsigned int next_update; int imbalance; int id; long unsigned int cpumask[0]; }; struct autogroup { struct kref kref; struct task_group *tg; struct rw_semaphore lock; long unsigned int id; int nice; }; enum ctx_state { CONTEXT_DISABLED = 4294967295, CONTEXT_KERNEL = 0, CONTEXT_USER = 1, CONTEXT_GUEST = 2, }; struct kernel_cpustat { u64 cpustat[10]; }; enum { MEMBARRIER_STATE_PRIVATE_EXPEDITED_READY = 1, MEMBARRIER_STATE_PRIVATE_EXPEDITED = 2, MEMBARRIER_STATE_GLOBAL_EXPEDITED_READY = 4, MEMBARRIER_STATE_GLOBAL_EXPEDITED = 8, MEMBARRIER_STATE_PRIVATE_EXPEDITED_SYNC_CORE_READY = 16, MEMBARRIER_STATE_PRIVATE_EXPEDITED_SYNC_CORE = 32, MEMBARRIER_STATE_PRIVATE_EXPEDITED_RSEQ_READY = 64, MEMBARRIER_STATE_PRIVATE_EXPEDITED_RSEQ = 128, }; enum { CFTYPE_ONLY_ON_ROOT = 1, CFTYPE_NOT_ON_ROOT = 2, CFTYPE_NS_DELEGATABLE = 4, CFTYPE_NO_PREFIX = 8, CFTYPE_WORLD_WRITABLE = 16, CFTYPE_DEBUG = 32, CFTYPE_PRESSURE = 64, __CFTYPE_ONLY_ON_DFL = 65536, __CFTYPE_NOT_ON_DFL = 131072, }; struct css_task_iter { struct cgroup_subsys *ss; unsigned int flags; struct list_head *cset_pos; struct list_head *cset_head; struct list_head *tcset_pos; struct list_head *tcset_head; struct list_head *task_pos; struct list_head *cur_tasks_head; struct css_set *cur_cset; struct css_set *cur_dcset; struct task_struct *cur_task; struct list_head iters_node; }; struct trace_event_raw_sched_kthread_stop { struct trace_entry ent; char comm[16]; pid_t pid; char __data[0]; }; struct trace_event_raw_sched_kthread_stop_ret { struct trace_entry ent; int ret; char __data[0]; }; struct trace_event_raw_sched_kthread_work_queue_work { struct trace_entry ent; void *work; void *function; void *worker; char __data[0]; }; struct trace_event_raw_sched_kthread_work_execute_start { struct trace_entry ent; void *work; void *function; char __data[0]; }; struct trace_event_raw_sched_kthread_work_execute_end { struct trace_entry ent; void *work; void *function; char __data[0]; }; struct trace_event_raw_sched_wakeup_template { struct trace_entry ent; char comm[16]; pid_t pid; int prio; int target_cpu; char __data[0]; }; struct trace_event_raw_sched_switch { struct trace_entry ent; char prev_comm[16]; pid_t prev_pid; int prev_prio; long int prev_state; char next_comm[16]; pid_t next_pid; int next_prio; char __data[0]; }; struct trace_event_raw_sched_migrate_task { struct trace_entry ent; char comm[16]; pid_t pid; int prio; int orig_cpu; int dest_cpu; char __data[0]; }; struct trace_event_raw_sched_process_template { struct trace_entry ent; char comm[16]; pid_t pid; int prio; char __data[0]; }; struct trace_event_raw_sched_process_wait { struct trace_entry ent; char comm[16]; pid_t pid; int prio; char __data[0]; }; struct trace_event_raw_sched_process_fork { struct trace_entry ent; char parent_comm[16]; pid_t parent_pid; char child_comm[16]; pid_t child_pid; char __data[0]; }; struct trace_event_raw_sched_process_exec { struct trace_entry ent; u32 __data_loc_filename; pid_t pid; pid_t old_pid; char __data[0]; }; struct trace_event_raw_sched_stat_template { struct trace_entry ent; char comm[16]; pid_t pid; u64 delay; char __data[0]; }; struct trace_event_raw_sched_stat_runtime { struct trace_entry ent; char comm[16]; pid_t pid; u64 runtime; u64 vruntime; char __data[0]; }; struct trace_event_raw_sched_pi_setprio { struct trace_entry ent; char comm[16]; pid_t pid; int oldprio; int newprio; char __data[0]; }; struct trace_event_raw_sched_process_hang { struct trace_entry ent; char comm[16]; pid_t pid; char __data[0]; }; struct trace_event_raw_sched_move_numa { struct trace_entry ent; pid_t pid; pid_t tgid; pid_t ngid; int src_cpu; int src_nid; int dst_cpu; int dst_nid; char __data[0]; }; struct trace_event_raw_sched_numa_pair_template { struct trace_entry ent; pid_t src_pid; pid_t src_tgid; pid_t src_ngid; int src_cpu; int src_nid; pid_t dst_pid; pid_t dst_tgid; pid_t dst_ngid; int dst_cpu; int dst_nid; char __data[0]; }; struct trace_event_raw_sched_wake_idle_without_ipi { struct trace_entry ent; int cpu; char __data[0]; }; struct trace_event_data_offsets_sched_kthread_stop {}; struct trace_event_data_offsets_sched_kthread_stop_ret {}; struct trace_event_data_offsets_sched_kthread_work_queue_work {}; struct trace_event_data_offsets_sched_kthread_work_execute_start {}; struct trace_event_data_offsets_sched_kthread_work_execute_end {}; struct trace_event_data_offsets_sched_wakeup_template {}; struct trace_event_data_offsets_sched_switch {}; struct trace_event_data_offsets_sched_migrate_task {}; struct trace_event_data_offsets_sched_process_template {}; struct trace_event_data_offsets_sched_process_wait {}; struct trace_event_data_offsets_sched_process_fork {}; struct trace_event_data_offsets_sched_process_exec { u32 filename; }; struct trace_event_data_offsets_sched_stat_template {}; struct trace_event_data_offsets_sched_stat_runtime {}; struct trace_event_data_offsets_sched_pi_setprio {}; struct trace_event_data_offsets_sched_process_hang {}; struct trace_event_data_offsets_sched_move_numa {}; struct trace_event_data_offsets_sched_numa_pair_template {}; struct trace_event_data_offsets_sched_wake_idle_without_ipi {}; typedef void (*btf_trace_sched_kthread_stop)(void *, struct task_struct *); typedef void (*btf_trace_sched_kthread_stop_ret)(void *, int); typedef void (*btf_trace_sched_kthread_work_queue_work)(void *, struct kthread_worker *, struct kthread_work *); typedef void (*btf_trace_sched_kthread_work_execute_start)(void *, struct kthread_work *); typedef void (*btf_trace_sched_kthread_work_execute_end)(void *, struct kthread_work *, kthread_work_func_t); typedef void (*btf_trace_sched_waking)(void *, struct task_struct *); typedef void (*btf_trace_sched_wakeup)(void *, struct task_struct *); typedef void (*btf_trace_sched_wakeup_new)(void *, struct task_struct *); typedef void (*btf_trace_sched_switch)(void *, bool, struct task_struct *, struct task_struct *); typedef void (*btf_trace_sched_migrate_task)(void *, struct task_struct *, int); typedef void (*btf_trace_sched_process_free)(void *, struct task_struct *); typedef void (*btf_trace_sched_process_exit)(void *, struct task_struct *); typedef void (*btf_trace_sched_wait_task)(void *, struct task_struct *); typedef void (*btf_trace_sched_process_wait)(void *, struct pid *); typedef void (*btf_trace_sched_process_fork)(void *, struct task_struct *, struct task_struct *); typedef void (*btf_trace_sched_process_exec)(void *, struct task_struct *, pid_t, struct linux_binprm *); typedef void (*btf_trace_sched_stat_wait)(void *, struct task_struct *, u64); typedef void (*btf_trace_sched_stat_sleep)(void *, struct task_struct *, u64); typedef void (*btf_trace_sched_stat_iowait)(void *, struct task_struct *, u64); typedef void (*btf_trace_sched_stat_blocked)(void *, struct task_struct *, u64); typedef void (*btf_trace_sched_stat_runtime)(void *, struct task_struct *, u64, u64); typedef void (*btf_trace_sched_pi_setprio)(void *, struct task_struct *, struct task_struct *); typedef void (*btf_trace_sched_process_hang)(void *, struct task_struct *); typedef void (*btf_trace_sched_move_numa)(void *, struct task_struct *, int, int); typedef void (*btf_trace_sched_stick_numa)(void *, struct task_struct *, int, struct task_struct *, int); typedef void (*btf_trace_sched_swap_numa)(void *, struct task_struct *, int, struct task_struct *, int); typedef void (*btf_trace_sched_wake_idle_without_ipi)(void *, int); typedef void (*btf_trace_pelt_cfs_tp)(void *, struct cfs_rq *); typedef void (*btf_trace_pelt_rt_tp)(void *, struct rq *); struct uclamp_bucket { long unsigned int value: 11; long unsigned int tasks: 53; }; struct uclamp_rq { unsigned int value; struct uclamp_bucket bucket[5]; }; struct rt_prio_array { long unsigned int bitmap[2]; struct list_head queue[100]; }; struct rt_rq { struct rt_prio_array active; unsigned int rt_nr_running; unsigned int rr_nr_running; struct { int curr; int next; } highest_prio; unsigned int rt_nr_migratory; unsigned int rt_nr_total; int overloaded; struct plist_head pushable_tasks; int rt_queued; int rt_throttled; u64 rt_time; u64 rt_runtime; raw_spinlock_t rt_runtime_lock; }; struct dl_rq { struct rb_root_cached root; unsigned int dl_nr_running; struct { u64 curr; u64 next; } earliest_dl; unsigned int dl_nr_migratory; int overloaded; struct rb_root_cached pushable_dl_tasks_root; u64 running_bw; u64 this_bw; u64 extra_bw; u64 bw_ratio; }; struct cpu_stop_done; struct cpu_stop_work { struct list_head list; cpu_stop_fn_t fn; long unsigned int caller; void *arg; struct cpu_stop_done *done; }; struct cpuidle_state; struct rq { raw_spinlock_t __lock; unsigned int nr_running; unsigned int nr_numa_running; unsigned int nr_preferred_running; unsigned int numa_migrate_on; long unsigned int last_blocked_load_update_tick; unsigned int has_blocked_load; long: 32; long: 64; long: 64; long: 64; call_single_data_t nohz_csd; unsigned int nohz_tick_stopped; atomic_t nohz_flags; unsigned int ttwu_pending; u64 nr_switches; long: 64; struct uclamp_rq uclamp[2]; unsigned int uclamp_flags; long: 32; long: 64; long: 64; long: 64; struct cfs_rq cfs; struct rt_rq rt; struct dl_rq dl; struct list_head leaf_cfs_rq_list; struct list_head *tmp_alone_branch; unsigned int nr_uninterruptible; struct task_struct *curr; struct task_struct *idle; struct task_struct *stop; long unsigned int next_balance; struct mm_struct *prev_mm; unsigned int clock_update_flags; u64 clock; long: 64; long: 64; long: 64; long: 64; long: 64; u64 clock_task; u64 clock_pelt; long unsigned int lost_idle_time; atomic_t nr_iowait; u64 last_seen_need_resched_ns; int ticks_without_resched; int membarrier_state; struct root_domain *rd; struct sched_domain *sd; long unsigned int cpu_capacity; long unsigned int cpu_capacity_orig; struct callback_head *balance_callback; unsigned char nohz_idle_balance; unsigned char idle_balance; long unsigned int misfit_task_load; int active_balance; int push_cpu; struct cpu_stop_work active_balance_work; int cpu; int online; struct list_head cfs_tasks; long: 64; struct sched_avg avg_rt; struct sched_avg avg_dl; struct sched_avg avg_irq; u64 idle_stamp; u64 avg_idle; long unsigned int wake_stamp; u64 wake_avg_idle; u64 max_idle_balance_cost; struct rcuwait hotplug_wait; u64 prev_irq_time; u64 prev_steal_time; u64 prev_steal_time_rq; long unsigned int calc_load_update; long int calc_load_active; long: 64; call_single_data_t hrtick_csd; struct hrtimer hrtick_timer; ktime_t hrtick_time; struct sched_info rq_sched_info; long long unsigned int rq_cpu_time; unsigned int yld_count; unsigned int sched_count; unsigned int sched_goidle; unsigned int ttwu_count; unsigned int ttwu_local; struct cpuidle_state *idle_state; unsigned int nr_pinned; unsigned int push_busy; struct cpu_stop_work push_work; struct rq *core; struct task_struct *core_pick; unsigned int core_enabled; unsigned int core_sched_seq; struct rb_root core_tree; unsigned int core_task_seq; unsigned int core_pick_seq; long unsigned int core_cookie; unsigned char core_forceidle; unsigned int core_forceidle_seq; }; typedef void (*btf_trace_pelt_dl_tp)(void *, struct rq *); typedef void (*btf_trace_pelt_thermal_tp)(void *, struct rq *); typedef void (*btf_trace_pelt_irq_tp)(void *, struct rq *); typedef void (*btf_trace_pelt_se_tp)(void *, struct sched_entity *); typedef void (*btf_trace_sched_cpu_capacity_tp)(void *, struct rq *); typedef void (*btf_trace_sched_overutilized_tp)(void *, struct root_domain *, bool); typedef void (*btf_trace_sched_util_est_cfs_tp)(void *, struct cfs_rq *); typedef void (*btf_trace_sched_util_est_se_tp)(void *, struct sched_entity *); typedef void (*btf_trace_sched_update_nr_running_tp)(void *, struct rq *, int); struct wake_q_head { struct wake_q_node *first; struct wake_q_node **lastp; }; struct sched_attr { __u32 size; __u32 sched_policy; __u64 sched_flags; __s32 sched_nice; __u32 sched_priority; __u64 sched_runtime; __u64 sched_deadline; __u64 sched_period; __u32 sched_util_min; __u32 sched_util_max; }; struct cpuidle_state_usage { long long unsigned int disable; long long unsigned int usage; u64 time_ns; long long unsigned int above; long long unsigned int below; long long unsigned int rejected; long long unsigned int s2idle_usage; long long unsigned int s2idle_time; }; struct cpuidle_device; struct cpuidle_driver; struct cpuidle_state { char name[16]; char desc[32]; s64 exit_latency_ns; s64 target_residency_ns; unsigned int flags; unsigned int exit_latency; int power_usage; unsigned int target_residency; int (*enter)(struct cpuidle_device *, struct cpuidle_driver *, int); int (*enter_dead)(struct cpuidle_device *, int); int (*enter_s2idle)(struct cpuidle_device *, struct cpuidle_driver *, int); }; struct cpuidle_driver_kobj; struct cpuidle_state_kobj; struct cpuidle_device_kobj; struct cpuidle_device { unsigned int registered: 1; unsigned int enabled: 1; unsigned int poll_time_limit: 1; unsigned int cpu; ktime_t next_hrtimer; int last_state_idx; u64 last_residency_ns; u64 poll_limit_ns; u64 forced_idle_latency_limit_ns; struct cpuidle_state_usage states_usage[10]; struct cpuidle_state_kobj *kobjs[10]; struct cpuidle_driver_kobj *kobj_driver; struct cpuidle_device_kobj *kobj_dev; struct list_head device_list; }; struct cpuidle_driver { const char *name; struct module *owner; unsigned int bctimer: 1; struct cpuidle_state states[10]; int state_count; int safe_state_index; struct cpumask *cpumask; const char *governor; }; struct cpudl_item { u64 dl; int cpu; int idx; }; struct rt_bandwidth { raw_spinlock_t rt_runtime_lock; ktime_t rt_period; u64 rt_runtime; struct hrtimer rt_period_timer; unsigned int rt_period_active; }; struct dl_bandwidth { raw_spinlock_t dl_runtime_lock; u64 dl_runtime; u64 dl_period; }; typedef int (*tg_visitor)(struct task_group *, void *); struct perf_domain { struct em_perf_domain *em_pd; struct perf_domain *next; struct callback_head rcu; }; struct rq_flags { long unsigned int flags; struct pin_cookie cookie; unsigned int clock_update_flags; }; enum { __SCHED_FEAT_GENTLE_FAIR_SLEEPERS = 0, __SCHED_FEAT_START_DEBIT = 1, __SCHED_FEAT_NEXT_BUDDY = 2, __SCHED_FEAT_LAST_BUDDY = 3, __SCHED_FEAT_CACHE_HOT_BUDDY = 4, __SCHED_FEAT_WAKEUP_PREEMPTION = 5, __SCHED_FEAT_HRTICK = 6, __SCHED_FEAT_HRTICK_DL = 7, __SCHED_FEAT_DOUBLE_TICK = 8, __SCHED_FEAT_NONTASK_CAPACITY = 9, __SCHED_FEAT_TTWU_QUEUE = 10, __SCHED_FEAT_SIS_PROP = 11, __SCHED_FEAT_WARN_DOUBLE_CLOCK = 12, __SCHED_FEAT_RT_PUSH_IPI = 13, __SCHED_FEAT_RT_RUNTIME_SHARE = 14, __SCHED_FEAT_LB_MIN = 15, __SCHED_FEAT_ATTACH_AGE_LOAD = 16, __SCHED_FEAT_WA_IDLE = 17, __SCHED_FEAT_WA_WEIGHT = 18, __SCHED_FEAT_WA_BIAS = 19, __SCHED_FEAT_UTIL_EST = 20, __SCHED_FEAT_UTIL_EST_FASTUP = 21, __SCHED_FEAT_LATENCY_WARN = 22, __SCHED_FEAT_ALT_PERIOD = 23, __SCHED_FEAT_BASE_SLICE = 24, __SCHED_FEAT_NR = 25, }; struct irqtime { u64 total; u64 tick_delta; u64 irq_start_time; struct u64_stats_sync sync; }; enum cpu_util_type { FREQUENCY_UTIL = 0, ENERGY_UTIL = 1, }; struct set_affinity_pending; struct migration_arg { struct task_struct *task; int dest_cpu; struct set_affinity_pending *pending; }; struct set_affinity_pending { refcount_t refs; unsigned int stop_pending; struct completion done; struct cpu_stop_work stop_work; struct migration_arg arg; }; struct migration_swap_arg { struct task_struct *src_task; struct task_struct *dst_task; int src_cpu; int dst_cpu; }; enum { preempt_dynamic_none = 0, preempt_dynamic_voluntary = 1, preempt_dynamic_full = 2, }; struct uclamp_request { s64 percent; u64 util; int ret; }; struct cfs_schedulable_data { struct task_group *tg; u64 period; u64 quota; }; enum { cpuset = 0, possible = 1, fail = 2, }; enum tick_dep_bits { TICK_DEP_BIT_POSIX_TIMER = 0, TICK_DEP_BIT_PERF_EVENTS = 1, TICK_DEP_BIT_SCHED = 2, TICK_DEP_BIT_CLOCK_UNSTABLE = 3, TICK_DEP_BIT_RCU = 4, TICK_DEP_BIT_RCU_EXP = 5, }; struct sched_clock_data { u64 tick_raw; u64 tick_gtod; u64 clock; }; enum s2idle_states { S2IDLE_STATE_NONE = 0, S2IDLE_STATE_ENTER = 1, S2IDLE_STATE_WAKE = 2, }; struct idle_timer { struct hrtimer timer; int done; }; struct numa_group { refcount_t refcount; spinlock_t lock; int nr_tasks; pid_t gid; int active_nodes; struct callback_head rcu; long unsigned int total_faults; long unsigned int max_faults_cpu; long unsigned int *faults_cpu; long unsigned int faults[0]; }; struct update_util_data { void (*func)(struct update_util_data *, u64, unsigned int); }; enum sched_tunable_scaling { SCHED_TUNABLESCALING_NONE = 0, SCHED_TUNABLESCALING_LOG = 1, SCHED_TUNABLESCALING_LINEAR = 2, SCHED_TUNABLESCALING_END = 3, }; enum numa_topology_type { NUMA_DIRECT = 0, NUMA_GLUELESS_MESH = 1, NUMA_BACKPLANE = 2, }; enum numa_faults_stats { NUMA_MEM = 0, NUMA_CPU = 1, NUMA_MEMBUF = 2, NUMA_CPUBUF = 3, }; enum numa_type { node_has_spare = 0, node_fully_busy = 1, node_overloaded = 2, }; struct numa_stats { long unsigned int load; long unsigned int runnable; long unsigned int util; long unsigned int compute_capacity; unsigned int nr_running; unsigned int weight; enum numa_type node_type; int idle_cpu; }; struct task_numa_env { struct task_struct *p; int src_cpu; int src_nid; int dst_cpu; int dst_nid; struct numa_stats src_stats; struct numa_stats dst_stats; int imbalance_pct; int dist; struct task_struct *best_task; long int best_imp; int best_cpu; }; enum fbq_type { regular = 0, remote = 1, all = 2, }; enum group_type { group_has_spare = 0, group_fully_busy = 1, group_misfit_task = 2, group_asym_packing = 3, group_imbalanced = 4, group_overloaded = 5, }; enum migration_type { migrate_load = 0, migrate_util = 1, migrate_task = 2, migrate_misfit = 3, }; struct lb_env { struct sched_domain *sd; struct rq *src_rq; int src_cpu; int dst_cpu; struct rq *dst_rq; struct cpumask *dst_grpmask; int new_dst_cpu; enum cpu_idle_type idle; long int imbalance; struct cpumask *cpus; unsigned int flags; unsigned int loop; unsigned int loop_break; unsigned int loop_max; enum fbq_type fbq_type; enum migration_type migration_type; struct list_head tasks; }; struct sg_lb_stats { long unsigned int avg_load; long unsigned int group_load; long unsigned int group_capacity; long unsigned int group_util; long unsigned int group_runnable; unsigned int sum_nr_running; unsigned int sum_h_nr_running; unsigned int idle_cpus; unsigned int group_weight; enum group_type group_type; unsigned int group_asym_packing; long unsigned int group_misfit_task_load; unsigned int nr_numa_running; unsigned int nr_preferred_running; }; struct sd_lb_stats { struct sched_group *busiest; struct sched_group *local; long unsigned int total_load; long unsigned int total_capacity; long unsigned int avg_load; unsigned int prefer_sibling; struct sg_lb_stats busiest_stat; struct sg_lb_stats local_stat; }; typedef struct rt_rq *rt_rq_iter_t; struct sd_flag_debug { unsigned int meta_flags; char *name; }; struct s_data { struct sched_domain **sd; struct root_domain *rd; }; enum s_alloc { sa_rootdomain = 0, sa_sd = 1, sa_sd_storage = 2, sa_none = 3, }; struct asym_cap_data { struct list_head link; long unsigned int capacity; long unsigned int cpus[0]; }; enum cpuacct_stat_index { CPUACCT_STAT_USER = 0, CPUACCT_STAT_SYSTEM = 1, CPUACCT_STAT_NSTATS = 2, }; struct cpuacct_usage { u64 usages[2]; }; struct cpuacct { struct cgroup_subsys_state css; struct cpuacct_usage *cpuusage; struct kernel_cpustat *cpustat; }; struct gov_attr_set { struct kobject kobj; struct list_head policy_list; struct mutex update_lock; int usage_count; }; struct governor_attr { struct attribute attr; ssize_t (*show)(struct gov_attr_set *, char *); ssize_t (*store)(struct gov_attr_set *, const char *, size_t); }; struct sugov_tunables { struct gov_attr_set attr_set; unsigned int rate_limit_us; }; struct sugov_policy { struct cpufreq_policy *policy; struct sugov_tunables *tunables; struct list_head tunables_hook; raw_spinlock_t update_lock; u64 last_freq_update_time; s64 freq_update_delay_ns; unsigned int next_freq; unsigned int cached_raw_freq; struct irq_work irq_work; struct kthread_work work; struct mutex work_lock; struct kthread_worker worker; struct task_struct *thread; bool work_in_progress; bool limits_changed; bool need_freq_update; }; struct sugov_cpu { struct update_util_data update_util; struct sugov_policy *sg_policy; unsigned int cpu; bool iowait_boost_pending; unsigned int iowait_boost; u64 last_update; long unsigned int util; long unsigned int bw_dl; long unsigned int max; long unsigned int saved_idle_calls; }; enum { MEMBARRIER_FLAG_SYNC_CORE = 1, MEMBARRIER_FLAG_RSEQ = 2, }; enum membarrier_cmd { MEMBARRIER_CMD_QUERY = 0, MEMBARRIER_CMD_GLOBAL = 1, MEMBARRIER_CMD_GLOBAL_EXPEDITED = 2, MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED = 4, MEMBARRIER_CMD_PRIVATE_EXPEDITED = 8, MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED = 16, MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE = 32, MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE = 64, MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ = 128, MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ = 256, MEMBARRIER_CMD_SHARED = 1, }; enum membarrier_cmd_flag { MEMBARRIER_CMD_FLAG_CPU = 1, }; enum psi_res { PSI_IO = 0, PSI_MEM = 1, PSI_CPU = 2, NR_PSI_RESOURCES = 3, }; struct psi_window { u64 size; u64 start_time; u64 start_value; u64 prev_growth; }; struct psi_trigger { enum psi_states state; u64 threshold; struct list_head node; struct psi_group *group; wait_queue_head_t event_wait; int event; struct psi_window win; u64 last_event_time; struct kref refcount; }; struct sched_core_cookie { refcount_t refcnt; }; struct ww_acquire_ctx; struct ww_mutex { struct mutex base; struct ww_acquire_ctx *ctx; }; struct ww_acquire_ctx { struct task_struct *task; long unsigned int stamp; unsigned int acquired; short unsigned int wounded; short unsigned int is_wait_die; }; struct mutex_waiter { struct list_head list; struct task_struct *task; struct ww_acquire_ctx *ww_ctx; }; struct semaphore { raw_spinlock_t lock; unsigned int count; struct list_head wait_list; }; struct semaphore_waiter { struct list_head list; struct task_struct *task; bool up; }; enum lock_events { LOCKEVENT_pv_hash_hops = 0, LOCKEVENT_pv_kick_unlock = 1, LOCKEVENT_pv_kick_wake = 2, LOCKEVENT_pv_latency_kick = 3, LOCKEVENT_pv_latency_wake = 4, LOCKEVENT_pv_lock_stealing = 5, LOCKEVENT_pv_spurious_wakeup = 6, LOCKEVENT_pv_wait_again = 7, LOCKEVENT_pv_wait_early = 8, LOCKEVENT_pv_wait_head = 9, LOCKEVENT_pv_wait_node = 10, LOCKEVENT_lock_pending = 11, LOCKEVENT_lock_slowpath = 12, LOCKEVENT_lock_use_node2 = 13, LOCKEVENT_lock_use_node3 = 14, LOCKEVENT_lock_use_node4 = 15, LOCKEVENT_lock_no_node = 16, LOCKEVENT_rwsem_sleep_reader = 17, LOCKEVENT_rwsem_sleep_writer = 18, LOCKEVENT_rwsem_wake_reader = 19, LOCKEVENT_rwsem_wake_writer = 20, LOCKEVENT_rwsem_opt_lock = 21, LOCKEVENT_rwsem_opt_fail = 22, LOCKEVENT_rwsem_opt_nospin = 23, LOCKEVENT_rwsem_rlock = 24, LOCKEVENT_rwsem_rlock_steal = 25, LOCKEVENT_rwsem_rlock_fast = 26, LOCKEVENT_rwsem_rlock_fail = 27, LOCKEVENT_rwsem_rlock_handoff = 28, LOCKEVENT_rwsem_wlock = 29, LOCKEVENT_rwsem_wlock_fail = 30, LOCKEVENT_rwsem_wlock_handoff = 31, lockevent_num = 32, LOCKEVENT_reset_cnts = 32, }; enum rwsem_waiter_type { RWSEM_WAITING_FOR_WRITE = 0, RWSEM_WAITING_FOR_READ = 1, }; struct rwsem_waiter { struct list_head list; struct task_struct *task; enum rwsem_waiter_type type; long unsigned int timeout; }; enum rwsem_wake_type { RWSEM_WAKE_ANY = 0, RWSEM_WAKE_READERS = 1, RWSEM_WAKE_READ_OWNED = 2, }; enum writer_wait_state { WRITER_NOT_FIRST = 0, WRITER_FIRST = 1, WRITER_HANDOFF = 2, }; enum owner_state { OWNER_NULL = 1, OWNER_WRITER = 2, OWNER_READER = 4, OWNER_NONSPINNABLE = 8, }; struct optimistic_spin_node { struct optimistic_spin_node *next; struct optimistic_spin_node *prev; int locked; int cpu; }; struct mcs_spinlock { struct mcs_spinlock *next; int locked; int count; }; struct qnode { struct mcs_spinlock mcs; long int reserved[2]; }; enum vcpu_state { vcpu_running = 0, vcpu_halted = 1, vcpu_hashed = 2, }; struct pv_node { struct mcs_spinlock mcs; int cpu; u8 state; }; struct pv_hash_entry { struct qspinlock *lock; struct pv_node *node; }; struct hrtimer_sleeper { struct hrtimer timer; struct task_struct *task; }; struct rt_mutex; struct rt_mutex_waiter { struct rb_node tree_entry; struct rb_node pi_tree_entry; struct task_struct *task; struct rt_mutex *lock; int prio; u64 deadline; }; struct rt_mutex { raw_spinlock_t wait_lock; struct rb_root_cached waiters; struct task_struct *owner; }; enum rtmutex_chainwalk { RT_MUTEX_MIN_CHAINWALK = 0, RT_MUTEX_FULL_CHAINWALK = 1, }; enum pm_qos_req_action { PM_QOS_ADD_REQ = 0, PM_QOS_UPDATE_REQ = 1, PM_QOS_REMOVE_REQ = 2, }; typedef int suspend_state_t; enum suspend_stat_step { SUSPEND_FREEZE = 1, SUSPEND_PREPARE = 2, SUSPEND_SUSPEND = 3, SUSPEND_SUSPEND_LATE = 4, SUSPEND_SUSPEND_NOIRQ = 5, SUSPEND_RESUME_NOIRQ = 6, SUSPEND_RESUME_EARLY = 7, SUSPEND_RESUME = 8, }; struct suspend_stats { int success; int fail; int failed_freeze; int failed_prepare; int failed_suspend; int failed_suspend_late; int failed_suspend_noirq; int failed_resume; int failed_resume_early; int failed_resume_noirq; int last_failed_dev; char failed_devs[80]; int last_failed_errno; int errno[2]; int last_failed_step; enum suspend_stat_step failed_steps[2]; }; enum { TEST_NONE = 0, TEST_CORE = 1, TEST_CPUS = 2, TEST_PLATFORM = 3, TEST_DEVICES = 4, TEST_FREEZER = 5, __TEST_AFTER_LAST = 6, }; struct pm_vt_switch { struct list_head head; struct device *dev; bool required; }; struct platform_suspend_ops { int (*valid)(suspend_state_t); int (*begin)(suspend_state_t); int (*prepare)(); int (*prepare_late)(); int (*enter)(suspend_state_t); void (*wake)(); void (*finish)(); bool (*suspend_again)(); void (*end)(); void (*recover)(); }; struct platform_s2idle_ops { int (*begin)(); int (*prepare)(); int (*prepare_late)(); bool (*wake)(); void (*restore_early)(); void (*restore)(); void (*end)(); }; struct platform_hibernation_ops { int (*begin)(pm_message_t); void (*end)(); int (*pre_snapshot)(); void (*finish)(); int (*prepare)(); int (*enter)(); void (*leave)(); int (*pre_restore)(); void (*restore_cleanup)(); void (*recover)(); }; enum { HIBERNATION_INVALID = 0, HIBERNATION_PLATFORM = 1, HIBERNATION_SHUTDOWN = 2, HIBERNATION_REBOOT = 3, HIBERNATION_SUSPEND = 4, HIBERNATION_TEST_RESUME = 5, __HIBERNATION_AFTER_LAST = 6, }; struct pbe { void *address; void *orig_address; struct pbe *next; }; struct swsusp_info { struct new_utsname uts; u32 version_code; long unsigned int num_physpages; int cpus; long unsigned int image_pages; long unsigned int pages; long unsigned int size; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct snapshot_handle { unsigned int cur; void *buffer; int sync_read; }; struct linked_page { struct linked_page *next; char data[4088]; }; struct chain_allocator { struct linked_page *chain; unsigned int used_space; gfp_t gfp_mask; int safe_needed; }; struct rtree_node { struct list_head list; long unsigned int *data; }; struct mem_zone_bm_rtree { struct list_head list; struct list_head nodes; struct list_head leaves; long unsigned int start_pfn; long unsigned int end_pfn; struct rtree_node *rtree; int levels; unsigned int blocks; }; struct bm_position { struct mem_zone_bm_rtree *zone; struct rtree_node *node; long unsigned int node_pfn; int node_bit; }; struct memory_bitmap { struct list_head zones; struct linked_page *p_list; struct bm_position cur; }; struct mem_extent { struct list_head hook; long unsigned int start; long unsigned int end; }; struct nosave_region { struct list_head list; long unsigned int start_pfn; long unsigned int end_pfn; }; typedef struct { long unsigned int val; } swp_entry_t; enum { BIO_NO_PAGE_REF = 0, BIO_CLONED = 1, BIO_BOUNCED = 2, BIO_WORKINGSET = 3, BIO_QUIET = 4, BIO_CHAIN = 5, BIO_REFFED = 6, BIO_THROTTLED = 7, BIO_TRACE_COMPLETION = 8, BIO_CGROUP_ACCT = 9, BIO_TRACKED = 10, BIO_REMAPPED = 11, BIO_ZONE_WRITE_LOCKED = 12, BIO_FLAG_LAST = 13, }; enum req_opf { REQ_OP_READ = 0, REQ_OP_WRITE = 1, REQ_OP_FLUSH = 2, REQ_OP_DISCARD = 3, REQ_OP_SECURE_ERASE = 5, REQ_OP_WRITE_SAME = 7, REQ_OP_WRITE_ZEROES = 9, REQ_OP_ZONE_OPEN = 10, REQ_OP_ZONE_CLOSE = 11, REQ_OP_ZONE_FINISH = 12, REQ_OP_ZONE_APPEND = 13, REQ_OP_ZONE_RESET = 15, REQ_OP_ZONE_RESET_ALL = 17, REQ_OP_DRV_IN = 34, REQ_OP_DRV_OUT = 35, REQ_OP_LAST = 36, }; enum req_flag_bits { __REQ_FAILFAST_DEV = 8, __REQ_FAILFAST_TRANSPORT = 9, __REQ_FAILFAST_DRIVER = 10, __REQ_SYNC = 11, __REQ_META = 12, __REQ_PRIO = 13, __REQ_NOMERGE = 14, __REQ_IDLE = 15, __REQ_INTEGRITY = 16, __REQ_FUA = 17, __REQ_PREFLUSH = 18, __REQ_RAHEAD = 19, __REQ_BACKGROUND = 20, __REQ_NOWAIT = 21, __REQ_CGROUP_PUNT = 22, __REQ_NOUNMAP = 23, __REQ_HIPRI = 24, __REQ_DRV = 25, __REQ_SWAP = 26, __REQ_NR_BITS = 27, }; struct swap_map_page { sector_t entries[511]; sector_t next_swap; }; struct swap_map_page_list { struct swap_map_page *map; struct swap_map_page_list *next; }; struct swap_map_handle { struct swap_map_page *cur; struct swap_map_page_list *maps; sector_t cur_swap; sector_t first_sector; unsigned int k; long unsigned int reqd_free_pages; u32 crc32; }; struct swsusp_header { char reserved[4060]; u32 crc32; sector_t image; unsigned int flags; char orig_sig[10]; char sig[10]; }; struct swsusp_extent { struct rb_node node; long unsigned int start; long unsigned int end; }; struct hib_bio_batch { atomic_t count; wait_queue_head_t wait; blk_status_t error; struct blk_plug plug; }; struct crc_data { struct task_struct *thr; atomic_t ready; atomic_t stop; unsigned int run_threads; wait_queue_head_t go; wait_queue_head_t done; u32 *crc32; size_t *unc_len[3]; unsigned char *unc[3]; }; struct cmp_data { struct task_struct *thr; atomic_t ready; atomic_t stop; int ret; wait_queue_head_t go; wait_queue_head_t done; size_t unc_len; size_t cmp_len; unsigned char unc[131072]; unsigned char cmp[143360]; unsigned char wrk[16384]; }; struct dec_data { struct task_struct *thr; atomic_t ready; atomic_t stop; int ret; wait_queue_head_t go; wait_queue_head_t done; size_t unc_len; size_t cmp_len; unsigned char unc[131072]; unsigned char cmp[143360]; }; typedef s64 compat_loff_t; struct resume_swap_area { __kernel_loff_t offset; __u32 dev; } __attribute__((packed)); struct snapshot_data { struct snapshot_handle handle; int swap; int mode; bool frozen; bool ready; bool platform_support; bool free_bitmaps; dev_t dev; }; struct compat_resume_swap_area { compat_loff_t offset; u32 dev; } __attribute__((packed)); struct sysrq_key_op { void (* const handler)(int); const char * const help_msg; const char * const action_msg; const int enable_mask; }; struct em_data_callback { int (*active_power)(long unsigned int *, long unsigned int *, struct device *); }; struct dev_printk_info { char subsystem[16]; char device[48]; }; struct kmsg_dump_iter { u64 cur_seq; u64 next_seq; }; struct kmsg_dumper { struct list_head list; void (*dump)(struct kmsg_dumper *, enum kmsg_dump_reason); enum kmsg_dump_reason max_reason; bool registered; }; struct trace_event_raw_console { struct trace_entry ent; u32 __data_loc_msg; char __data[0]; }; struct trace_event_data_offsets_console { u32 msg; }; typedef void (*btf_trace_console)(void *, const char *, size_t); struct printk_info { u64 seq; u64 ts_nsec; u16 text_len; u8 facility; u8 flags: 5; u8 level: 3; u32 caller_id; struct dev_printk_info dev_info; }; struct printk_record { struct printk_info *info; char *text_buf; unsigned int text_buf_size; }; struct prb_data_blk_lpos { long unsigned int begin; long unsigned int next; }; struct prb_desc { atomic_long_t state_var; struct prb_data_blk_lpos text_blk_lpos; }; struct prb_data_ring { unsigned int size_bits; char *data; atomic_long_t head_lpos; atomic_long_t tail_lpos; }; struct prb_desc_ring { unsigned int count_bits; struct prb_desc *descs; struct printk_info *infos; atomic_long_t head_id; atomic_long_t tail_id; }; struct printk_ringbuffer { struct prb_desc_ring desc_ring; struct prb_data_ring text_data_ring; atomic_long_t fail; }; struct prb_reserved_entry { struct printk_ringbuffer *rb; long unsigned int irqflags; long unsigned int id; unsigned int text_space; }; enum desc_state { desc_miss = 4294967295, desc_reserved = 0, desc_committed = 1, desc_finalized = 2, desc_reusable = 3, }; struct console_cmdline { char name[16]; int index; bool user_specified; char *options; char *brl_options; }; enum devkmsg_log_bits { __DEVKMSG_LOG_BIT_ON = 0, __DEVKMSG_LOG_BIT_OFF = 1, __DEVKMSG_LOG_BIT_LOCK = 2, }; enum devkmsg_log_masks { DEVKMSG_LOG_MASK_ON = 1, DEVKMSG_LOG_MASK_OFF = 2, DEVKMSG_LOG_MASK_LOCK = 4, }; enum con_msg_format_flags { MSG_FORMAT_DEFAULT = 0, MSG_FORMAT_SYSLOG = 1, }; enum log_flags { LOG_NEWLINE = 2, LOG_CONT = 8, }; struct latched_seq { seqcount_latch_t latch; u64 val[2]; }; struct devkmsg_user { atomic64_t seq; struct ratelimit_state rs; struct mutex lock; char buf[8192]; struct printk_info info; char text_buf[8192]; struct printk_record record; }; struct printk_safe_seq_buf { atomic_t len; atomic_t message_lost; struct irq_work work; unsigned char buffer[8160]; }; struct dev_printk_info___2; struct prb_data_block { long unsigned int id; char data[0]; }; enum { IRQS_AUTODETECT = 1, IRQS_SPURIOUS_DISABLED = 2, IRQS_POLL_INPROGRESS = 8, IRQS_ONESHOT = 32, IRQS_REPLAY = 64, IRQS_WAITING = 128, IRQS_PENDING = 512, IRQS_SUSPENDED = 2048, IRQS_TIMINGS = 4096, IRQS_NMI = 8192, }; enum { _IRQ_DEFAULT_INIT_FLAGS = 0, _IRQ_PER_CPU = 512, _IRQ_LEVEL = 256, _IRQ_NOPROBE = 1024, _IRQ_NOREQUEST = 2048, _IRQ_NOTHREAD = 65536, _IRQ_NOAUTOEN = 4096, _IRQ_MOVE_PCNTXT = 16384, _IRQ_NO_BALANCING = 8192, _IRQ_NESTED_THREAD = 32768, _IRQ_PER_CPU_DEVID = 131072, _IRQ_IS_POLLED = 262144, _IRQ_DISABLE_UNLAZY = 524288, _IRQ_HIDDEN = 1048576, _IRQ_NO_DEBUG = 2097152, _IRQF_MODIFY_MASK = 2096911, }; enum { IRQTF_RUNTHREAD = 0, IRQTF_WARNED = 1, IRQTF_AFFINITY = 2, IRQTF_FORCED_THREAD = 3, }; enum { IRQC_IS_HARDIRQ = 0, IRQC_IS_NESTED = 1, }; enum { IRQ_STARTUP_NORMAL = 0, IRQ_STARTUP_MANAGED = 1, IRQ_STARTUP_ABORT = 2, }; struct irq_devres { unsigned int irq; void *dev_id; }; struct irq_desc_devres { unsigned int from; unsigned int cnt; }; struct irq_generic_chip_devres { struct irq_chip_generic *gc; u32 msk; unsigned int clr; unsigned int set; }; struct irqchip_fwid { struct fwnode_handle fwnode; unsigned int type; char *name; phys_addr_t *pa; }; struct irq_sim_work_ctx { struct irq_work work; int irq_base; unsigned int irq_count; long unsigned int *pending; struct irq_domain *domain; }; struct irq_sim_irq_ctx { int irqnum; bool enabled; struct irq_sim_work_ctx *work_ctx; }; enum { AFFINITY = 0, AFFINITY_LIST = 1, EFFECTIVE = 2, EFFECTIVE_LIST = 3, }; struct irq_affinity { unsigned int pre_vectors; unsigned int post_vectors; unsigned int nr_sets; unsigned int set_size[4]; void (*calc_sets)(struct irq_affinity *, unsigned int); void *priv; }; struct node_vectors { unsigned int id; union { unsigned int nvectors; unsigned int ncpus; }; }; struct cpumap { unsigned int available; unsigned int allocated; unsigned int managed; unsigned int managed_allocated; bool initialized; bool online; long unsigned int alloc_map[4]; long unsigned int managed_map[4]; }; struct irq_matrix___2 { unsigned int matrix_bits; unsigned int alloc_start; unsigned int alloc_end; unsigned int alloc_size; unsigned int global_available; unsigned int global_reserved; unsigned int systembits_inalloc; unsigned int total_allocated; unsigned int online_maps; struct cpumap *maps; long unsigned int scratch_map[4]; long unsigned int system_map[4]; }; struct trace_event_raw_irq_matrix_global { struct trace_entry ent; unsigned int online_maps; unsigned int global_available; unsigned int global_reserved; unsigned int total_allocated; char __data[0]; }; struct trace_event_raw_irq_matrix_global_update { struct trace_entry ent; int bit; unsigned int online_maps; unsigned int global_available; unsigned int global_reserved; unsigned int total_allocated; char __data[0]; }; struct trace_event_raw_irq_matrix_cpu { struct trace_entry ent; int bit; unsigned int cpu; bool online; unsigned int available; unsigned int allocated; unsigned int managed; unsigned int online_maps; unsigned int global_available; unsigned int global_reserved; unsigned int total_allocated; char __data[0]; }; struct trace_event_data_offsets_irq_matrix_global {}; struct trace_event_data_offsets_irq_matrix_global_update {}; struct trace_event_data_offsets_irq_matrix_cpu {}; typedef void (*btf_trace_irq_matrix_online)(void *, struct irq_matrix___2 *); typedef void (*btf_trace_irq_matrix_offline)(void *, struct irq_matrix___2 *); typedef void (*btf_trace_irq_matrix_reserve)(void *, struct irq_matrix___2 *); typedef void (*btf_trace_irq_matrix_remove_reserved)(void *, struct irq_matrix___2 *); typedef void (*btf_trace_irq_matrix_assign_system)(void *, int, struct irq_matrix___2 *); typedef void (*btf_trace_irq_matrix_alloc_reserved)(void *, int, unsigned int, struct irq_matrix___2 *, struct cpumap *); typedef void (*btf_trace_irq_matrix_reserve_managed)(void *, int, unsigned int, struct irq_matrix___2 *, struct cpumap *); typedef void (*btf_trace_irq_matrix_remove_managed)(void *, int, unsigned int, struct irq_matrix___2 *, struct cpumap *); typedef void (*btf_trace_irq_matrix_alloc_managed)(void *, int, unsigned int, struct irq_matrix___2 *, struct cpumap *); typedef void (*btf_trace_irq_matrix_assign)(void *, int, unsigned int, struct irq_matrix___2 *, struct cpumap *); typedef void (*btf_trace_irq_matrix_alloc)(void *, int, unsigned int, struct irq_matrix___2 *, struct cpumap *); typedef void (*btf_trace_irq_matrix_free)(void *, int, unsigned int, struct irq_matrix___2 *, struct cpumap *); typedef void (*call_rcu_func_t)(struct callback_head *, rcu_callback_t); struct rcu_synchronize { struct callback_head head; struct completion completion; }; struct trace_event_raw_rcu_utilization { struct trace_entry ent; const char *s; char __data[0]; }; struct trace_event_raw_rcu_stall_warning { struct trace_entry ent; const char *rcuname; const char *msg; char __data[0]; }; struct trace_event_data_offsets_rcu_utilization {}; struct trace_event_data_offsets_rcu_stall_warning {}; typedef void (*btf_trace_rcu_utilization)(void *, const char *); typedef void (*btf_trace_rcu_stall_warning)(void *, const char *, const char *); struct rcu_tasks; typedef void (*rcu_tasks_gp_func_t)(struct rcu_tasks *); typedef void (*pregp_func_t)(); typedef void (*pertask_func_t)(struct task_struct *, struct list_head *); typedef void (*postscan_func_t)(struct list_head *); typedef void (*holdouts_func_t)(struct list_head *, bool, bool *); typedef void (*postgp_func_t)(struct rcu_tasks *); struct rcu_tasks { struct callback_head *cbs_head; struct callback_head **cbs_tail; struct wait_queue_head cbs_wq; raw_spinlock_t cbs_lock; int gp_state; int gp_sleep; int init_fract; long unsigned int gp_jiffies; long unsigned int gp_start; long unsigned int n_gps; long unsigned int n_ipis; long unsigned int n_ipis_fails; struct task_struct *kthread_ptr; rcu_tasks_gp_func_t gp_func; pregp_func_t pregp_func; pertask_func_t pertask_func; postscan_func_t postscan_func; holdouts_func_t holdouts_func; postgp_func_t postgp_func; call_rcu_func_t call_func; char *name; char *kname; }; enum { GP_IDLE = 0, GP_ENTER = 1, GP_PASSED = 2, GP_EXIT = 3, GP_REPLAY = 4, }; struct rcu_cblist { struct callback_head *head; struct callback_head **tail; long int len; }; enum rcutorture_type { RCU_FLAVOR = 0, RCU_TASKS_FLAVOR = 1, RCU_TASKS_RUDE_FLAVOR = 2, RCU_TASKS_TRACING_FLAVOR = 3, RCU_TRIVIAL_FLAVOR = 4, SRCU_FLAVOR = 5, INVALID_RCU_FLAVOR = 6, }; struct rcu_exp_work { long unsigned int rew_s; struct work_struct rew_work; }; struct rcu_node { raw_spinlock_t lock; long unsigned int gp_seq; long unsigned int gp_seq_needed; long unsigned int completedqs; long unsigned int qsmask; long unsigned int rcu_gp_init_mask; long unsigned int qsmaskinit; long unsigned int qsmaskinitnext; long unsigned int ofl_seq; long unsigned int expmask; long unsigned int expmaskinit; long unsigned int expmaskinitnext; long unsigned int cbovldmask; long unsigned int ffmask; long unsigned int grpmask; int grplo; int grphi; u8 grpnum; u8 level; bool wait_blkd_tasks; struct rcu_node *parent; struct list_head blkd_tasks; struct list_head *gp_tasks; struct list_head *exp_tasks; struct list_head *boost_tasks; struct rt_mutex boost_mtx; long unsigned int boost_time; struct task_struct *boost_kthread_task; unsigned int boost_kthread_status; long unsigned int n_boosts; long: 64; raw_spinlock_t fqslock; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; spinlock_t exp_lock; long unsigned int exp_seq_rq; wait_queue_head_t exp_wq[4]; struct rcu_exp_work rew; bool exp_need_flush; long: 56; long: 64; long: 64; long: 64; long: 64; }; union rcu_noqs { struct { u8 norm; u8 exp; } b; u16 s; }; struct rcu_data { long unsigned int gp_seq; long unsigned int gp_seq_needed; union rcu_noqs cpu_no_qs; bool core_needs_qs; bool beenonline; bool gpwrap; bool exp_deferred_qs; bool cpu_started; struct rcu_node *mynode; long unsigned int grpmask; long unsigned int ticks_this_gp; struct irq_work defer_qs_iw; bool defer_qs_iw_pending; struct work_struct strict_work; struct rcu_segcblist cblist; long int qlen_last_fqs_check; long unsigned int n_cbs_invoked; long unsigned int n_force_qs_snap; long int blimit; int dynticks_snap; long int dynticks_nesting; long int dynticks_nmi_nesting; atomic_t dynticks; bool rcu_need_heavy_qs; bool rcu_urgent_qs; bool rcu_forced_tick; bool rcu_forced_tick_exp; long unsigned int last_accelerate; long unsigned int last_advance_all; int tick_nohz_enabled_snap; struct callback_head barrier_head; int exp_dynticks_snap; struct task_struct *rcu_cpu_kthread_task; unsigned int rcu_cpu_kthread_status; char rcu_cpu_has_work; unsigned int softirq_snap; struct irq_work rcu_iw; bool rcu_iw_pending; long unsigned int rcu_iw_gp_seq; long unsigned int rcu_ofl_gp_seq; short int rcu_ofl_gp_flags; long unsigned int rcu_onl_gp_seq; short int rcu_onl_gp_flags; long unsigned int last_fqs_resched; int cpu; }; struct rcu_state { struct rcu_node node[21]; struct rcu_node *level[3]; int ncpus; int n_online_cpus; long: 64; long: 64; long: 64; long: 64; u8 boost; long unsigned int gp_seq; long unsigned int gp_max; struct task_struct *gp_kthread; struct swait_queue_head gp_wq; short int gp_flags; short int gp_state; long unsigned int gp_wake_time; long unsigned int gp_wake_seq; struct mutex barrier_mutex; atomic_t barrier_cpu_count; struct completion barrier_completion; long unsigned int barrier_sequence; struct mutex exp_mutex; struct mutex exp_wake_mutex; long unsigned int expedited_sequence; atomic_t expedited_need_qs; struct swait_queue_head expedited_wq; int ncpus_snap; u8 cbovld; u8 cbovldnext; long unsigned int jiffies_force_qs; long unsigned int jiffies_kick_kthreads; long unsigned int n_force_qs; long unsigned int gp_start; long unsigned int gp_end; long unsigned int gp_activity; long unsigned int gp_req_activity; long unsigned int jiffies_stall; long unsigned int jiffies_resched; long unsigned int n_force_qs_gpstart; const char *name; char abbr; long: 56; long: 64; long: 64; raw_spinlock_t ofl_lock; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct kvfree_rcu_bulk_data { long unsigned int nr_records; struct kvfree_rcu_bulk_data *next; void *records[0]; }; struct kfree_rcu_cpu; struct kfree_rcu_cpu_work { struct rcu_work rcu_work; struct callback_head *head_free; struct kvfree_rcu_bulk_data *bkvhead_free[2]; struct kfree_rcu_cpu *krcp; }; struct kfree_rcu_cpu { struct callback_head *head; struct kvfree_rcu_bulk_data *bkvhead[2]; struct kfree_rcu_cpu_work krw_arr[2]; raw_spinlock_t lock; struct delayed_work monitor_work; bool monitor_todo; bool initialized; int count; struct delayed_work page_cache_work; atomic_t backoff_page_cache_fill; atomic_t work_in_progress; struct hrtimer hrtimer; struct llist_head bkvcache; int nr_bkv_objs; }; struct rcu_stall_chk_rdr { int nesting; union rcu_special rs; bool on_blkd_list; }; struct io_tlb_slot { phys_addr_t orig_addr; size_t alloc_size; unsigned int list; }; struct io_tlb_mem { phys_addr_t start; phys_addr_t end; long unsigned int nslabs; long unsigned int used; unsigned int index; spinlock_t lock; struct dentry *debugfs; bool late_alloc; struct io_tlb_slot slots[0]; }; struct dma_sgt_handle { struct sg_table sgt; struct page **pages; }; struct dma_devres { size_t size; void *vaddr; dma_addr_t dma_handle; long unsigned int attrs; }; struct debugfs_u32_array { u32 *array; u32 n_elements; }; struct cma_kobject; struct cma { long unsigned int base_pfn; long unsigned int count; long unsigned int *bitmap; unsigned int order_per_bit; spinlock_t lock; struct hlist_head mem_head; spinlock_t mem_head_lock; struct debugfs_u32_array dfs_bitmap; char name[64]; atomic64_t nr_pages_succeeded; atomic64_t nr_pages_failed; struct cma_kobject *cma_kobj; }; struct trace_event_raw_swiotlb_bounced { struct trace_entry ent; u32 __data_loc_dev_name; u64 dma_mask; dma_addr_t dev_addr; size_t size; enum swiotlb_force swiotlb_force; char __data[0]; }; struct trace_event_data_offsets_swiotlb_bounced { u32 dev_name; }; typedef void (*btf_trace_swiotlb_bounced)(void *, struct device *, dma_addr_t, size_t, enum swiotlb_force); struct trace_event_raw_sys_enter { struct trace_entry ent; long int id; long unsigned int args[6]; char __data[0]; }; struct trace_event_raw_sys_exit { struct trace_entry ent; long int id; long int ret; char __data[0]; }; struct trace_event_data_offsets_sys_enter {}; struct trace_event_data_offsets_sys_exit {}; typedef void (*btf_trace_sys_enter)(void *, struct pt_regs *, long int); typedef void (*btf_trace_sys_exit)(void *, struct pt_regs *, long int); struct kvm_regs { __u64 rax; __u64 rbx; __u64 rcx; __u64 rdx; __u64 rsi; __u64 rdi; __u64 rsp; __u64 rbp; __u64 r8; __u64 r9; __u64 r10; __u64 r11; __u64 r12; __u64 r13; __u64 r14; __u64 r15; __u64 rip; __u64 rflags; }; struct kvm_segment { __u64 base; __u32 limit; __u16 selector; __u8 type; __u8 present; __u8 dpl; __u8 db; __u8 s; __u8 l; __u8 g; __u8 avl; __u8 unusable; __u8 padding; }; struct kvm_dtable { __u64 base; __u16 limit; __u16 padding[3]; }; struct kvm_sregs { struct kvm_segment cs; struct kvm_segment ds; struct kvm_segment es; struct kvm_segment fs; struct kvm_segment gs; struct kvm_segment ss; struct kvm_segment tr; struct kvm_segment ldt; struct kvm_dtable gdt; struct kvm_dtable idt; __u64 cr0; __u64 cr2; __u64 cr3; __u64 cr4; __u64 cr8; __u64 efer; __u64 apic_base; __u64 interrupt_bitmap[4]; }; struct kvm_msr_entry { __u32 index; __u32 reserved; __u64 data; }; struct kvm_cpuid_entry2 { __u32 function; __u32 index; __u32 flags; __u32 eax; __u32 ebx; __u32 ecx; __u32 edx; __u32 padding[3]; }; struct kvm_debug_exit_arch { __u32 exception; __u32 pad; __u64 pc; __u64 dr6; __u64 dr7; }; struct kvm_vcpu_events { struct { __u8 injected; __u8 nr; __u8 has_error_code; __u8 pending; __u32 error_code; } exception; struct { __u8 injected; __u8 nr; __u8 soft; __u8 shadow; } interrupt; struct { __u8 injected; __u8 pending; __u8 masked; __u8 pad; } nmi; __u32 sipi_vector; __u32 flags; struct { __u8 smm; __u8 pending; __u8 smm_inside_nmi; __u8 latched_init; } smi; __u8 reserved[27]; __u8 exception_has_payload; __u64 exception_payload; }; struct kvm_sync_regs { struct kvm_regs regs; struct kvm_sregs sregs; struct kvm_vcpu_events events; }; struct kvm_vmx_nested_state_data { __u8 vmcs12[4096]; __u8 shadow_vmcs12[4096]; }; struct kvm_vmx_nested_state_hdr { __u64 vmxon_pa; __u64 vmcs12_pa; struct { __u16 flags; } smm; __u16 pad; __u32 flags; __u64 preemption_timer_deadline; }; struct kvm_svm_nested_state_data { __u8 vmcb12[4096]; }; struct kvm_svm_nested_state_hdr { __u64 vmcb_pa; }; struct kvm_nested_state { __u16 flags; __u16 format; __u32 size; union { struct kvm_vmx_nested_state_hdr vmx; struct kvm_svm_nested_state_hdr svm; __u8 pad[120]; } hdr; union { struct kvm_vmx_nested_state_data vmx[0]; struct kvm_svm_nested_state_data svm[0]; } data; }; struct kvm_pmu_event_filter { __u32 action; __u32 nevents; __u32 fixed_counter_bitmap; __u32 flags; __u32 pad[4]; __u64 events[0]; }; struct kvm_hyperv_exit { __u32 type; __u32 pad1; union { struct { __u32 msr; __u32 pad2; __u64 control; __u64 evt_page; __u64 msg_page; } synic; struct { __u64 input; __u64 result; __u64 params[2]; } hcall; struct { __u32 msr; __u32 pad2; __u64 control; __u64 status; __u64 send_page; __u64 recv_page; __u64 pending_page; } syndbg; } u; }; struct kvm_xen_exit { __u32 type; union { struct { __u32 longmode; __u32 cpl; __u64 input; __u64 result; __u64 params[6]; } hcall; } u; }; struct kvm_run { __u8 request_interrupt_window; __u8 immediate_exit; __u8 padding1[6]; __u32 exit_reason; __u8 ready_for_interrupt_injection; __u8 if_flag; __u16 flags; __u64 cr8; __u64 apic_base; union { struct { __u64 hardware_exit_reason; } hw; struct { __u64 hardware_entry_failure_reason; __u32 cpu; } fail_entry; struct { __u32 exception; __u32 error_code; } ex; struct { __u8 direction; __u8 size; __u16 port; __u32 count; __u64 data_offset; } io; struct { struct kvm_debug_exit_arch arch; } debug; struct { __u64 phys_addr; __u8 data[8]; __u32 len; __u8 is_write; } mmio; struct { __u64 nr; __u64 args[6]; __u64 ret; __u32 longmode; __u32 pad; } hypercall; struct { __u64 rip; __u32 is_write; __u32 pad; } tpr_access; struct { __u8 icptcode; __u16 ipa; __u32 ipb; } s390_sieic; __u64 s390_reset_flags; struct { __u64 trans_exc_code; __u32 pgm_code; } s390_ucontrol; struct { __u32 dcrn; __u32 data; __u8 is_write; } dcr; struct { __u32 suberror; __u32 ndata; __u64 data[16]; } internal; struct { __u32 suberror; __u32 ndata; __u64 flags; __u8 insn_size; __u8 insn_bytes[15]; } emulation_failure; struct { __u64 gprs[32]; } osi; struct { __u64 nr; __u64 ret; __u64 args[9]; } papr_hcall; struct { __u16 subchannel_id; __u16 subchannel_nr; __u32 io_int_parm; __u32 io_int_word; __u32 ipb; __u8 dequeued; } s390_tsch; struct { __u32 epr; } epr; struct { __u32 type; __u64 flags; } system_event; struct { __u64 addr; __u8 ar; __u8 reserved; __u8 fc; __u8 sel1; __u16 sel2; } s390_stsi; struct { __u8 vector; } eoi; struct kvm_hyperv_exit hyperv; struct { __u64 esr_iss; __u64 fault_ipa; } arm_nisv; struct { __u8 error; __u8 pad[7]; __u32 reason; __u32 index; __u64 data; } msr; struct kvm_xen_exit xen; char padding[256]; }; __u64 kvm_valid_regs; __u64 kvm_dirty_regs; union { struct kvm_sync_regs regs; char padding[2048]; } s; }; struct kvm_coalesced_mmio { __u64 phys_addr; __u32 len; union { __u32 pad; __u32 pio; }; __u8 data[8]; }; struct kvm_coalesced_mmio_ring { __u32 first; __u32 last; struct kvm_coalesced_mmio coalesced_mmio[0]; }; struct kvm_xen_hvm_config { __u32 flags; __u32 msr; __u64 blob_addr_32; __u64 blob_addr_64; __u8 blob_size_32; __u8 blob_size_64; __u8 pad2[30]; }; struct kvm_enc_region { __u64 addr; __u64 size; }; struct kvm_dirty_gfn { __u32 flags; __u32 slot; __u64 offset; }; struct kvm_stats_desc { __u32 flags; __s16 exponent; __u16 size; __u32 offset; __u32 unused; char name[0]; }; typedef long unsigned int gva_t; typedef u64 gpa_t; typedef u64 gfn_t; typedef u64 hpa_t; typedef u64 hfn_t; typedef hfn_t kvm_pfn_t; struct kvm_memory_slot; struct gfn_to_hva_cache { u64 generation; gpa_t gpa; long unsigned int hva; long unsigned int len; struct kvm_memory_slot *memslot; }; struct kvm_rmap_head; struct kvm_lpage_info; struct kvm_arch_memory_slot { struct kvm_rmap_head *rmap[3]; struct kvm_lpage_info *lpage_info[2]; short unsigned int *gfn_track[1]; }; struct kvm_memory_slot { gfn_t base_gfn; long unsigned int npages; long unsigned int *dirty_bitmap; struct kvm_arch_memory_slot arch; long unsigned int userspace_addr; u32 flags; short int id; u16 as_id; }; struct gfn_to_pfn_cache { u64 generation; gfn_t gfn; kvm_pfn_t pfn; bool dirty; }; struct kvm_mmu_memory_cache { int nobjs; gfp_t gfp_zero; struct kmem_cache *kmem_cache; void *objects[40]; }; struct kvm_vm_stat_generic { u64 remote_tlb_flush; }; struct kvm_vcpu_stat_generic { u64 halt_successful_poll; u64 halt_attempted_poll; u64 halt_poll_invalid; u64 halt_wakeup; u64 halt_poll_success_ns; u64 halt_poll_fail_ns; }; struct hv_partition_assist_pg { u32 tlb_lock_count; }; union hv_message_flags { __u8 asu8; struct { __u8 msg_pending: 1; __u8 reserved: 7; }; }; union hv_port_id { __u32 asu32; struct { __u32 id: 24; __u32 reserved: 8; } u; }; struct hv_message_header { __u32 message_type; __u8 payload_size; union hv_message_flags message_flags; __u8 reserved[2]; union { __u64 sender; union hv_port_id port; }; }; struct hv_message { struct hv_message_header header; union { __u64 payload[30]; } u; }; union hv_stimer_config { u64 as_uint64; struct { u64 enable: 1; u64 periodic: 1; u64 lazy: 1; u64 auto_enable: 1; u64 apic_vector: 8; u64 direct_mode: 1; u64 reserved_z0: 3; u64 sintx: 4; u64 reserved_z1: 44; }; }; enum kvm_page_track_mode { KVM_PAGE_TRACK_WRITE = 0, KVM_PAGE_TRACK_MAX = 1, }; struct kvm_page_track_notifier_head { struct srcu_struct track_srcu; struct hlist_head track_notifier_list; }; struct kvm_vcpu; struct kvm; struct kvm_page_track_notifier_node { struct hlist_node node; void (*track_write)(struct kvm_vcpu *, gpa_t, const u8 *, int, struct kvm_page_track_notifier_node *); void (*track_flush_slot)(struct kvm *, struct kvm_memory_slot *, struct kvm_page_track_notifier_node *); }; struct kvm_mmio_fragment { gpa_t gpa; void *data; unsigned int len; }; struct kvm_lapic; struct x86_exception; struct kvm_mmu_page; union kvm_mmu_page_role { u32 word; struct { unsigned int level: 4; unsigned int gpte_is_8_bytes: 1; unsigned int quadrant: 2; unsigned int direct: 1; unsigned int access: 3; unsigned int invalid: 1; unsigned int efer_nx: 1; unsigned int cr0_wp: 1; unsigned int smep_andnot_wp: 1; unsigned int smap_andnot_wp: 1; unsigned int ad_disabled: 1; unsigned int guest_mode: 1; char: 6; unsigned int smm: 8; }; }; union kvm_mmu_extended_role { u32 word; struct { unsigned int valid: 1; unsigned int execonly: 1; unsigned int cr0_pg: 1; unsigned int cr4_pae: 1; unsigned int cr4_pse: 1; unsigned int cr4_pke: 1; unsigned int cr4_smap: 1; unsigned int cr4_smep: 1; unsigned int cr4_la57: 1; }; }; union kvm_mmu_role { u64 as_u64; struct { union kvm_mmu_page_role base; union kvm_mmu_extended_role ext; }; }; struct kvm_mmu_root_info { gpa_t pgd; hpa_t hpa; }; struct rsvd_bits_validate { u64 rsvd_bits_mask[10]; u64 bad_mt_xwr; }; struct kvm_mmu { long unsigned int (*get_guest_pgd)(struct kvm_vcpu *); u64 (*get_pdptr)(struct kvm_vcpu *, int); int (*page_fault)(struct kvm_vcpu *, gpa_t, u32, bool); void (*inject_page_fault)(struct kvm_vcpu *, struct x86_exception *); gpa_t (*gva_to_gpa)(struct kvm_vcpu *, gpa_t, u32, struct x86_exception *); gpa_t (*translate_gpa)(struct kvm_vcpu *, gpa_t, u32, struct x86_exception *); int (*sync_page)(struct kvm_vcpu *, struct kvm_mmu_page *); void (*invlpg)(struct kvm_vcpu *, gva_t, hpa_t); hpa_t root_hpa; gpa_t root_pgd; union kvm_mmu_role mmu_role; u8 root_level; u8 shadow_root_level; u8 ept_ad; bool direct_map; struct kvm_mmu_root_info prev_roots[3]; u8 permissions[16]; u32 pkru_mask; u64 *pae_root; u64 *pml4_root; struct rsvd_bits_validate shadow_zero_check; struct rsvd_bits_validate guest_rsvd_check; u64 pdptrs[4]; }; struct kvm_pio_request { long unsigned int linear_rip; long unsigned int count; int in; int port; int size; }; struct kvm_queued_exception { bool pending; bool injected; bool has_error_code; u8 nr; u32 error_code; long unsigned int payload; bool has_payload; u8 nested_apf; }; struct kvm_queued_interrupt { bool injected; bool soft; u8 nr; }; struct x86_emulate_ctxt; struct kvm_mtrr_range { u64 base; u64 mask; struct list_head node; }; struct kvm_mtrr { struct kvm_mtrr_range var_ranges[8]; mtrr_type fixed_ranges[88]; u64 deftype; struct list_head head; }; enum pmc_type { KVM_PMC_GP = 0, KVM_PMC_FIXED = 1, }; struct kvm_pmc { enum pmc_type type; u8 idx; u64 counter; u64 eventsel; struct perf_event *perf_event; struct kvm_vcpu *vcpu; u64 current_config; }; struct kvm_pmu { unsigned int nr_arch_gp_counters; unsigned int nr_arch_fixed_counters; unsigned int available_event_types; u64 fixed_ctr_ctrl; u64 global_ctrl; u64 global_status; u64 global_ovf_ctrl; u64 counter_bitmask[2]; u64 global_ctrl_mask; u64 global_ovf_ctrl_mask; u64 reserved_bits; u8 version; struct kvm_pmc gp_counters[32]; struct kvm_pmc fixed_counters[4]; struct irq_work irq_work; long unsigned int reprogram_pmi[1]; long unsigned int all_valid_pmc_idx[1]; long unsigned int pmc_in_use[1]; bool need_cleanup; u8 event_count; }; struct kvm_vcpu_xen { u64 hypercall_rip; u32 current_runstate; bool vcpu_info_set; bool vcpu_time_info_set; bool runstate_set; struct gfn_to_hva_cache vcpu_info_cache; struct gfn_to_hva_cache vcpu_time_info_cache; struct gfn_to_hva_cache runstate_cache; u64 last_steal; u64 runstate_entry_time; u64 runstate_times[4]; }; struct kvm_vcpu_hv; struct kvm_vcpu_arch { long unsigned int regs[17]; u32 regs_avail; u32 regs_dirty; long unsigned int cr0; long unsigned int cr0_guest_owned_bits; long unsigned int cr2; long unsigned int cr3; long unsigned int cr4; long unsigned int cr4_guest_owned_bits; long unsigned int cr4_guest_rsvd_bits; long unsigned int cr8; u32 host_pkru; u32 pkru; u32 hflags; u64 efer; u64 apic_base; struct kvm_lapic *apic; bool apicv_active; bool load_eoi_exitmap_pending; long unsigned int ioapic_handled_vectors[4]; long unsigned int apic_attention; int32_t apic_arb_prio; int mp_state; u64 ia32_misc_enable_msr; u64 smbase; u64 smi_count; bool tpr_access_reporting; bool xsaves_enabled; u64 ia32_xss; u64 microcode_version; u64 arch_capabilities; u64 perf_capabilities; struct kvm_mmu *mmu; struct kvm_mmu root_mmu; struct kvm_mmu guest_mmu; struct kvm_mmu nested_mmu; struct kvm_mmu *walk_mmu; struct kvm_mmu_memory_cache mmu_pte_list_desc_cache; struct kvm_mmu_memory_cache mmu_shadow_page_cache; struct kvm_mmu_memory_cache mmu_gfn_array_cache; struct kvm_mmu_memory_cache mmu_page_header_cache; struct fpu *user_fpu; struct fpu *guest_fpu; u64 xcr0; u64 guest_supported_xcr0; struct kvm_pio_request pio; void *pio_data; void *guest_ins_data; u8 event_exit_inst_len; struct kvm_queued_exception exception; struct kvm_queued_interrupt interrupt; int halt_request; int cpuid_nent; struct kvm_cpuid_entry2 *cpuid_entries; u64 reserved_gpa_bits; int maxphyaddr; int max_tdp_level; struct x86_emulate_ctxt *emulate_ctxt; bool emulate_regs_need_sync_to_vcpu; bool emulate_regs_need_sync_from_vcpu; int (*complete_userspace_io)(struct kvm_vcpu *); gpa_t time; struct pvclock_vcpu_time_info hv_clock; unsigned int hw_tsc_khz; struct gfn_to_hva_cache pv_time; bool pv_time_enabled; bool pvclock_set_guest_stopped_request; struct { u8 preempted; u64 msr_val; u64 last_steal; struct gfn_to_pfn_cache cache; } st; u64 l1_tsc_offset; u64 tsc_offset; u64 last_guest_tsc; u64 last_host_tsc; u64 tsc_offset_adjustment; u64 this_tsc_nsec; u64 this_tsc_write; u64 this_tsc_generation; bool tsc_catchup; bool tsc_always_catchup; s8 virtual_tsc_shift; u32 virtual_tsc_mult; u32 virtual_tsc_khz; s64 ia32_tsc_adjust_msr; u64 msr_ia32_power_ctl; u64 l1_tsc_scaling_ratio; u64 tsc_scaling_ratio; atomic_t nmi_queued; unsigned int nmi_pending; bool nmi_injected; bool smi_pending; struct kvm_mtrr mtrr_state; u64 pat; unsigned int switch_db_regs; long unsigned int db[4]; long unsigned int dr6; long unsigned int dr7; long unsigned int eff_db[4]; long unsigned int guest_debug_dr7; u64 msr_platform_info; u64 msr_misc_features_enables; u64 mcg_cap; u64 mcg_status; u64 mcg_ctl; u64 mcg_ext_ctl; u64 *mce_banks; u64 mmio_gva; unsigned int mmio_access; gfn_t mmio_gfn; u64 mmio_gen; struct kvm_pmu pmu; long unsigned int singlestep_rip; bool hyperv_enabled; struct kvm_vcpu_hv *hyperv; struct kvm_vcpu_xen xen; cpumask_var_t wbinvd_dirty_mask; long unsigned int last_retry_eip; long unsigned int last_retry_addr; struct { bool halted; gfn_t gfns[64]; struct gfn_to_hva_cache data; u64 msr_en_val; u64 msr_int_val; u16 vec; u32 id; bool send_user_only; u32 host_apf_flags; long unsigned int nested_apf_token; bool delivery_as_pf_vmexit; bool pageready_pending; } apf; struct { u64 length; u64 status; } osvw; struct { u64 msr_val; struct gfn_to_hva_cache data; } pv_eoi; u64 msr_kvm_poll_control; bool write_fault_to_shadow_pgtable; long unsigned int exit_qualification; struct { bool pv_unhalted; } pv; int pending_ioapic_eoi; int pending_external_vector; bool preempted_in_kernel; bool l1tf_flush_l1d; int last_vmentry_cpu; u64 msr_hwcr; struct { u32 features; bool enforce; } pv_cpuid; bool guest_state_protected; bool pdptrs_from_userspace; hpa_t hv_root_tdp; }; struct kvm_vcpu_stat { struct kvm_vcpu_stat_generic generic; u64 pf_fixed; u64 pf_guest; u64 tlb_flush; u64 invlpg; u64 exits; u64 io_exits; u64 mmio_exits; u64 signal_exits; u64 irq_window_exits; u64 nmi_window_exits; u64 l1d_flush; u64 halt_exits; u64 request_irq_exits; u64 irq_exits; u64 host_state_reload; u64 fpu_reload; u64 insn_emulation; u64 insn_emulation_fail; u64 hypercalls; u64 irq_injections; u64 nmi_injections; u64 req_event; u64 nested_run; u64 directed_yield_attempted; u64 directed_yield_successful; u64 guest_mode; }; struct kvm_dirty_ring { u32 dirty_index; u32 reset_index; u32 size; u32 soft_limit; struct kvm_dirty_gfn *dirty_gfns; int index; }; struct kvm_vcpu { struct kvm *kvm; struct preempt_notifier preempt_notifier; int cpu; int vcpu_id; int vcpu_idx; int srcu_idx; int mode; u64 requests; long unsigned int guest_debug; int pre_pcpu; struct list_head blocked_vcpu_list; struct mutex mutex; struct kvm_run *run; struct rcuwait wait; struct pid *pid; int sigset_active; sigset_t sigset; unsigned int halt_poll_ns; bool valid_wakeup; int mmio_needed; int mmio_read_completed; int mmio_is_write; int mmio_cur_fragment; int mmio_nr_fragments; struct kvm_mmio_fragment mmio_fragments[2]; struct { u32 queued; struct list_head queue; struct list_head done; spinlock_t lock; } async_pf; struct { bool in_spin_loop; bool dy_eligible; } spin_loop; bool preempted; bool ready; struct kvm_vcpu_arch arch; struct kvm_vcpu_stat stat; char stats_id[48]; struct kvm_dirty_ring dirty_ring; }; struct kvm_vm_stat { struct kvm_vm_stat_generic generic; u64 mmu_shadow_zapped; u64 mmu_pte_write; u64 mmu_pde_zapped; u64 mmu_flooded; u64 mmu_recycled; u64 mmu_cache_miss; u64 mmu_unsync; u64 lpages; u64 nx_lpage_splits; u64 max_mmu_page_hash_collisions; }; struct kvm_pic; struct kvm_ioapic; struct kvm_pit; enum hv_tsc_page_status { HV_TSC_PAGE_UNSET = 0, HV_TSC_PAGE_GUEST_CHANGED = 1, HV_TSC_PAGE_HOST_CHANGED = 2, HV_TSC_PAGE_SET = 3, HV_TSC_PAGE_UPDATING = 4, HV_TSC_PAGE_BROKEN = 5, }; struct kvm_hv_syndbg { struct { u64 control; u64 status; u64 send_page; u64 recv_page; u64 pending_page; } control; u64 options; }; struct kvm_hv { struct mutex hv_lock; u64 hv_guest_os_id; u64 hv_hypercall; u64 hv_tsc_page; enum hv_tsc_page_status hv_tsc_page_status; u64 hv_crash_param[5]; u64 hv_crash_ctl; struct ms_hyperv_tsc_page tsc_ref; struct idr conn_to_evt; u64 hv_reenlightenment_control; u64 hv_tsc_emulation_control; u64 hv_tsc_emulation_status; atomic_t num_mismatched_vp_indexes; struct hv_partition_assist_pg *hv_pa_pg; struct kvm_hv_syndbg hv_syndbg; }; struct kvm_xen { bool long_mode; bool shinfo_set; u8 upcall_vector; struct gfn_to_hva_cache shinfo_cache; }; enum kvm_irqchip_mode { KVM_IRQCHIP_NONE = 0, KVM_IRQCHIP_KERNEL = 1, KVM_IRQCHIP_SPLIT = 2, }; struct kvm_apic_map; struct kvm_x86_msr_filter; struct kvm_arch { long unsigned int n_used_mmu_pages; long unsigned int n_requested_mmu_pages; long unsigned int n_max_mmu_pages; unsigned int indirect_shadow_pages; u8 mmu_valid_gen; struct hlist_head mmu_page_hash[4096]; struct list_head active_mmu_pages; struct list_head zapped_obsolete_pages; struct list_head lpage_disallowed_mmu_pages; struct kvm_page_track_notifier_node mmu_sp_tracker; struct kvm_page_track_notifier_head track_notifier_head; spinlock_t mmu_unsync_pages_lock; struct list_head assigned_dev_head; struct iommu_domain *iommu_domain; bool iommu_noncoherent; atomic_t noncoherent_dma_count; atomic_t assigned_device_count; struct kvm_pic *vpic; struct kvm_ioapic *vioapic; struct kvm_pit *vpit; atomic_t vapics_in_nmi_mode; struct mutex apic_map_lock; struct kvm_apic_map *apic_map; atomic_t apic_map_dirty; bool apic_access_memslot_enabled; long unsigned int apicv_inhibit_reasons; gpa_t wall_clock; bool mwait_in_guest; bool hlt_in_guest; bool pause_in_guest; bool cstate_in_guest; long unsigned int irq_sources_bitmap; s64 kvmclock_offset; raw_spinlock_t tsc_write_lock; u64 last_tsc_nsec; u64 last_tsc_write; u32 last_tsc_khz; u64 cur_tsc_nsec; u64 cur_tsc_write; u64 cur_tsc_offset; u64 cur_tsc_generation; int nr_vcpus_matched_tsc; spinlock_t pvclock_gtod_sync_lock; bool use_master_clock; u64 master_kernel_ns; u64 master_cycle_now; struct delayed_work kvmclock_update_work; struct delayed_work kvmclock_sync_work; struct kvm_xen_hvm_config xen_hvm_config; struct hlist_head mask_notifier_list; struct kvm_hv hyperv; struct kvm_xen xen; int audit_point; bool backwards_tsc_observed; bool boot_vcpu_runs_old_kvmclock; u32 bsp_vcpu_id; u64 disabled_quirks; int cpu_dirty_logging_count; enum kvm_irqchip_mode irqchip_mode; u8 nr_reserved_ioapic_pins; bool disabled_lapic_found; bool x2apic_format; bool x2apic_broadcast_quirk_disabled; bool guest_can_read_msr_platform_info; bool exception_payload_enabled; bool bus_lock_detection_enabled; bool exit_on_emulation_error; u32 user_space_msr_mask; struct kvm_x86_msr_filter *msr_filter; u32 hypercall_exit_enabled; bool sgx_provisioning_allowed; struct kvm_pmu_event_filter *pmu_event_filter; struct task_struct *nx_lpage_recovery_thread; bool tdp_mmu_enabled; struct list_head tdp_mmu_roots; struct list_head tdp_mmu_pages; spinlock_t tdp_mmu_pages_lock; bool memslots_have_rmaps; hpa_t hv_root_tdp; spinlock_t hv_root_tdp_lock; }; struct kvm_memslots; struct kvm_io_bus; struct kvm_irq_routing_table; struct kvm_stat_data; struct kvm { rwlock_t mmu_lock; struct mutex slots_lock; struct mutex slots_arch_lock; struct mm_struct *mm; struct kvm_memslots *memslots[2]; struct kvm_vcpu *vcpus[288]; atomic_t online_vcpus; int created_vcpus; int last_boosted_vcpu; struct list_head vm_list; struct mutex lock; struct kvm_io_bus *buses[4]; struct { spinlock_t lock; struct list_head items; struct list_head resampler_list; struct mutex resampler_lock; } irqfds; struct list_head ioeventfds; struct kvm_vm_stat stat; struct kvm_arch arch; refcount_t users_count; struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; spinlock_t ring_lock; struct list_head coalesced_zones; struct mutex irq_lock; struct kvm_irq_routing_table *irq_routing; struct hlist_head irq_ack_notifier_list; struct mmu_notifier mmu_notifier; long unsigned int mmu_notifier_seq; long int mmu_notifier_count; long unsigned int mmu_notifier_range_start; long unsigned int mmu_notifier_range_end; long int tlbs_dirty; struct list_head devices; u64 manual_dirty_log_protect; struct dentry *debugfs_dentry; struct kvm_stat_data **debugfs_stat_data; struct srcu_struct srcu; struct srcu_struct irq_srcu; pid_t userspace_pid; unsigned int max_halt_poll_ns; u32 dirty_ring_size; struct notifier_block pm_notifier; char stats_id[48]; }; enum kvm_reg { VCPU_REGS_RAX = 0, VCPU_REGS_RCX = 1, VCPU_REGS_RDX = 2, VCPU_REGS_RBX = 3, VCPU_REGS_RSP = 4, VCPU_REGS_RBP = 5, VCPU_REGS_RSI = 6, VCPU_REGS_RDI = 7, VCPU_REGS_R8 = 8, VCPU_REGS_R9 = 9, VCPU_REGS_R10 = 10, VCPU_REGS_R11 = 11, VCPU_REGS_R12 = 12, VCPU_REGS_R13 = 13, VCPU_REGS_R14 = 14, VCPU_REGS_R15 = 15, VCPU_REGS_RIP = 16, NR_VCPU_REGS = 17, VCPU_EXREG_PDPTR = 17, VCPU_EXREG_CR0 = 18, VCPU_EXREG_CR3 = 19, VCPU_EXREG_CR4 = 20, VCPU_EXREG_RFLAGS = 21, VCPU_EXREG_SEGMENTS = 22, VCPU_EXREG_EXIT_INFO_1 = 23, VCPU_EXREG_EXIT_INFO_2 = 24, }; enum exit_fastpath_completion { EXIT_FASTPATH_NONE = 0, EXIT_FASTPATH_REENTER_GUEST = 1, EXIT_FASTPATH_EXIT_HANDLED = 2, }; struct kvm_rmap_head { long unsigned int val; }; struct kvm_tlb_range { u64 start_gfn; u64 pages; }; struct kvm_vcpu_hv_stimer { struct hrtimer timer; int index; union hv_stimer_config config; u64 count; u64 exp_time; struct hv_message msg; bool msg_pending; }; struct kvm_vcpu_hv_synic { u64 version; u64 control; u64 msg_page; u64 evt_page; atomic64_t sint[16]; atomic_t sint_to_gsi[16]; long unsigned int auto_eoi_bitmap[4]; long unsigned int vec_bitmap[4]; bool active; bool dont_zero_synic_pages; }; struct kvm_vcpu_hv { struct kvm_vcpu *vcpu; u32 vp_index; u64 hv_vapic; s64 runtime_offset; struct kvm_vcpu_hv_synic synic; struct kvm_hyperv_exit exit; struct kvm_vcpu_hv_stimer stimer[4]; long unsigned int stimer_pending_bitmap[1]; cpumask_t tlb_flush; bool enforce_cpuid; struct { u32 features_eax; u32 features_ebx; u32 features_edx; u32 enlightenments_eax; u32 enlightenments_ebx; u32 syndbg_cap_eax; } cpuid_cache; }; struct kvm_lpage_info { int disallow_lpage; }; struct kvm_apic_map { struct callback_head rcu; u8 mode; u32 max_apic_id; union { struct kvm_lapic *xapic_flat_map[8]; struct kvm_lapic *xapic_cluster_map[64]; }; struct kvm_lapic *phys_map[0]; }; struct msr_bitmap_range { u32 flags; u32 nmsrs; u32 base; long unsigned int *bitmap; }; struct kvm_x86_msr_filter { u8 count; bool default_allow: 1; struct msr_bitmap_range ranges[16]; }; struct msr_data { bool host_initiated; u32 index; u64 data; }; struct x86_instruction_info; enum x86_intercept_stage; struct kvm_pmu_ops; struct kvm_x86_nested_ops; struct kvm_x86_ops { int (*hardware_enable)(); void (*hardware_disable)(); void (*hardware_unsetup)(); bool (*cpu_has_accelerated_tpr)(); bool (*has_emulated_msr)(struct kvm *, u32); void (*vcpu_after_set_cpuid)(struct kvm_vcpu *); unsigned int vm_size; int (*vm_init)(struct kvm *); void (*vm_destroy)(struct kvm *); int (*vcpu_create)(struct kvm_vcpu *); void (*vcpu_free)(struct kvm_vcpu *); void (*vcpu_reset)(struct kvm_vcpu *, bool); void (*prepare_guest_switch)(struct kvm_vcpu *); void (*vcpu_load)(struct kvm_vcpu *, int); void (*vcpu_put)(struct kvm_vcpu *); void (*update_exception_bitmap)(struct kvm_vcpu *); int (*get_msr)(struct kvm_vcpu *, struct msr_data *); int (*set_msr)(struct kvm_vcpu *, struct msr_data *); u64 (*get_segment_base)(struct kvm_vcpu *, int); void (*get_segment)(struct kvm_vcpu *, struct kvm_segment *, int); int (*get_cpl)(struct kvm_vcpu *); void (*set_segment)(struct kvm_vcpu *, struct kvm_segment *, int); void (*get_cs_db_l_bits)(struct kvm_vcpu *, int *, int *); void (*set_cr0)(struct kvm_vcpu *, long unsigned int); bool (*is_valid_cr4)(struct kvm_vcpu *, long unsigned int); void (*set_cr4)(struct kvm_vcpu *, long unsigned int); int (*set_efer)(struct kvm_vcpu *, u64); void (*get_idt)(struct kvm_vcpu *, struct desc_ptr *); void (*set_idt)(struct kvm_vcpu *, struct desc_ptr *); void (*get_gdt)(struct kvm_vcpu *, struct desc_ptr *); void (*set_gdt)(struct kvm_vcpu *, struct desc_ptr *); void (*sync_dirty_debug_regs)(struct kvm_vcpu *); void (*set_dr7)(struct kvm_vcpu *, long unsigned int); void (*cache_reg)(struct kvm_vcpu *, enum kvm_reg); long unsigned int (*get_rflags)(struct kvm_vcpu *); void (*set_rflags)(struct kvm_vcpu *, long unsigned int); void (*tlb_flush_all)(struct kvm_vcpu *); void (*tlb_flush_current)(struct kvm_vcpu *); int (*tlb_remote_flush)(struct kvm *); int (*tlb_remote_flush_with_range)(struct kvm *, struct kvm_tlb_range *); void (*tlb_flush_gva)(struct kvm_vcpu *, gva_t); void (*tlb_flush_guest)(struct kvm_vcpu *); enum exit_fastpath_completion (*run)(struct kvm_vcpu *); int (*handle_exit)(struct kvm_vcpu *, enum exit_fastpath_completion); int (*skip_emulated_instruction)(struct kvm_vcpu *); void (*update_emulated_instruction)(struct kvm_vcpu *); void (*set_interrupt_shadow)(struct kvm_vcpu *, int); u32 (*get_interrupt_shadow)(struct kvm_vcpu *); void (*patch_hypercall)(struct kvm_vcpu *, unsigned char *); void (*set_irq)(struct kvm_vcpu *); void (*set_nmi)(struct kvm_vcpu *); void (*queue_exception)(struct kvm_vcpu *); void (*cancel_injection)(struct kvm_vcpu *); int (*interrupt_allowed)(struct kvm_vcpu *, bool); int (*nmi_allowed)(struct kvm_vcpu *, bool); bool (*get_nmi_mask)(struct kvm_vcpu *); void (*set_nmi_mask)(struct kvm_vcpu *, bool); void (*enable_nmi_window)(struct kvm_vcpu *); void (*enable_irq_window)(struct kvm_vcpu *); void (*update_cr8_intercept)(struct kvm_vcpu *, int, int); bool (*check_apicv_inhibit_reasons)(ulong); void (*pre_update_apicv_exec_ctrl)(struct kvm *, bool); void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *); void (*hwapic_irr_update)(struct kvm_vcpu *, int); void (*hwapic_isr_update)(struct kvm_vcpu *, int); bool (*guest_apic_has_interrupt)(struct kvm_vcpu *); void (*load_eoi_exitmap)(struct kvm_vcpu *, u64 *); void (*set_virtual_apic_mode)(struct kvm_vcpu *); void (*set_apic_access_page_addr)(struct kvm_vcpu *); int (*deliver_posted_interrupt)(struct kvm_vcpu *, int); int (*sync_pir_to_irr)(struct kvm_vcpu *); int (*set_tss_addr)(struct kvm *, unsigned int); int (*set_identity_map_addr)(struct kvm *, u64); u64 (*get_mt_mask)(struct kvm_vcpu *, gfn_t, bool); void (*load_mmu_pgd)(struct kvm_vcpu *, hpa_t, int); bool (*has_wbinvd_exit)(); u64 (*get_l2_tsc_offset)(struct kvm_vcpu *); u64 (*get_l2_tsc_multiplier)(struct kvm_vcpu *); void (*write_tsc_offset)(struct kvm_vcpu *, u64); void (*write_tsc_multiplier)(struct kvm_vcpu *, u64); void (*get_exit_info)(struct kvm_vcpu *, u64 *, u64 *, u32 *, u32 *); int (*check_intercept)(struct kvm_vcpu *, struct x86_instruction_info *, enum x86_intercept_stage, struct x86_exception *); void (*handle_exit_irqoff)(struct kvm_vcpu *); void (*request_immediate_exit)(struct kvm_vcpu *); void (*sched_in)(struct kvm_vcpu *, int); int cpu_dirty_log_size; void (*update_cpu_dirty_logging)(struct kvm_vcpu *); const struct kvm_pmu_ops *pmu_ops; const struct kvm_x86_nested_ops *nested_ops; int (*pre_block)(struct kvm_vcpu *); void (*post_block)(struct kvm_vcpu *); void (*vcpu_blocking)(struct kvm_vcpu *); void (*vcpu_unblocking)(struct kvm_vcpu *); int (*update_pi_irte)(struct kvm *, unsigned int, uint32_t, bool); void (*start_assignment)(struct kvm *); void (*apicv_post_state_restore)(struct kvm_vcpu *); bool (*dy_apicv_has_pending_interrupt)(struct kvm_vcpu *); int (*set_hv_timer)(struct kvm_vcpu *, u64, bool *); void (*cancel_hv_timer)(struct kvm_vcpu *); void (*setup_mce)(struct kvm_vcpu *); int (*smi_allowed)(struct kvm_vcpu *, bool); int (*enter_smm)(struct kvm_vcpu *, char *); int (*leave_smm)(struct kvm_vcpu *, const char *); void (*enable_smi_window)(struct kvm_vcpu *); int (*mem_enc_op)(struct kvm *, void *); int (*mem_enc_reg_region)(struct kvm *, struct kvm_enc_region *); int (*mem_enc_unreg_region)(struct kvm *, struct kvm_enc_region *); int (*vm_copy_enc_context_from)(struct kvm *, unsigned int); int (*get_msr_feature)(struct kvm_msr_entry *); bool (*can_emulate_instruction)(struct kvm_vcpu *, void *, int); bool (*apic_init_signal_blocked)(struct kvm_vcpu *); int (*enable_direct_tlbflush)(struct kvm_vcpu *); void (*migrate_timers)(struct kvm_vcpu *); void (*msr_filter_changed)(struct kvm_vcpu *); int (*complete_emulated_msr)(struct kvm_vcpu *, int); void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *, u8); }; struct kvm_x86_nested_ops { int (*check_events)(struct kvm_vcpu *); bool (*hv_timer_pending)(struct kvm_vcpu *); void (*triple_fault)(struct kvm_vcpu *); int (*get_state)(struct kvm_vcpu *, struct kvm_nested_state *, unsigned int); int (*set_state)(struct kvm_vcpu *, struct kvm_nested_state *, struct kvm_nested_state *); bool (*get_nested_state_pages)(struct kvm_vcpu *); int (*write_log_dirty)(struct kvm_vcpu *, gpa_t); int (*enable_evmcs)(struct kvm_vcpu *, uint16_t *); uint16_t (*get_evmcs_version)(struct kvm_vcpu *); }; struct kvm_io_device; struct kvm_io_range { gpa_t addr; int len; struct kvm_io_device *dev; }; struct kvm_io_bus { int dev_count; int ioeventfd_count; struct kvm_io_range range[0]; }; enum kvm_bus { KVM_MMIO_BUS = 0, KVM_PIO_BUS = 1, KVM_VIRTIO_CCW_NOTIFY_BUS = 2, KVM_FAST_MMIO_BUS = 3, KVM_NR_BUSES = 4, }; struct kvm_irq_routing_table { int chip[72]; u32 nr_rt_entries; struct hlist_head map[0]; }; struct kvm_memslots { u64 generation; short int id_to_index[32767]; atomic_t lru_slot; int used_slots; struct kvm_memory_slot memslots[0]; }; enum kvm_stat_kind { KVM_STAT_VM = 0, KVM_STAT_VCPU = 1, }; struct _kvm_stats_desc; struct kvm_stat_data { struct kvm *kvm; const struct _kvm_stats_desc *desc; enum kvm_stat_kind kind; }; struct _kvm_stats_desc { struct kvm_stats_desc desc; char name[48]; }; enum kcmp_type { KCMP_FILE = 0, KCMP_VM = 1, KCMP_FILES = 2, KCMP_FS = 3, KCMP_SIGHAND = 4, KCMP_IO = 5, KCMP_SYSVSEM = 6, KCMP_EPOLL_TFD = 7, KCMP_TYPES = 8, }; struct kcmp_epoll_slot { __u32 efd; __u32 tfd; __u32 toff; }; enum profile_type { PROFILE_TASK_EXIT = 0, PROFILE_MUNMAP = 1, }; struct profile_hit { u32 pc; u32 hits; }; struct stacktrace_cookie { long unsigned int *store; unsigned int size; unsigned int skip; unsigned int len; }; typedef __kernel_long_t __kernel_suseconds_t; typedef __kernel_long_t __kernel_old_time_t; typedef __kernel_suseconds_t suseconds_t; typedef __u64 timeu64_t; struct __kernel_itimerspec { struct __kernel_timespec it_interval; struct __kernel_timespec it_value; }; struct timezone { int tz_minuteswest; int tz_dsttime; }; struct itimerspec64 { struct timespec64 it_interval; struct timespec64 it_value; }; struct old_itimerspec32 { struct old_timespec32 it_interval; struct old_timespec32 it_value; }; struct old_timex32 { u32 modes; s32 offset; s32 freq; s32 maxerror; s32 esterror; s32 status; s32 constant; s32 precision; s32 tolerance; struct old_timeval32 time; s32 tick; s32 ppsfreq; s32 jitter; s32 shift; s32 stabil; s32 jitcnt; s32 calcnt; s32 errcnt; s32 stbcnt; s32 tai; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct __kernel_timex_timeval { __kernel_time64_t tv_sec; long long int tv_usec; }; struct __kernel_timex { unsigned int modes; long long int offset; long long int freq; long long int maxerror; long long int esterror; int status; long long int constant; long long int precision; long long int tolerance; struct __kernel_timex_timeval time; long long int tick; long long int ppsfreq; long long int jitter; int shift; long long int stabil; long long int jitcnt; long long int calcnt; long long int errcnt; long long int stbcnt; int tai; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct trace_event_raw_timer_class { struct trace_entry ent; void *timer; char __data[0]; }; struct trace_event_raw_timer_start { struct trace_entry ent; void *timer; void *function; long unsigned int expires; long unsigned int now; unsigned int flags; char __data[0]; }; struct trace_event_raw_timer_expire_entry { struct trace_entry ent; void *timer; long unsigned int now; void *function; long unsigned int baseclk; char __data[0]; }; struct trace_event_raw_hrtimer_init { struct trace_entry ent; void *hrtimer; clockid_t clockid; enum hrtimer_mode mode; char __data[0]; }; struct trace_event_raw_hrtimer_start { struct trace_entry ent; void *hrtimer; void *function; s64 expires; s64 softexpires; enum hrtimer_mode mode; char __data[0]; }; struct trace_event_raw_hrtimer_expire_entry { struct trace_entry ent; void *hrtimer; s64 now; void *function; char __data[0]; }; struct trace_event_raw_hrtimer_class { struct trace_entry ent; void *hrtimer; char __data[0]; }; struct trace_event_raw_itimer_state { struct trace_entry ent; int which; long long unsigned int expires; long int value_sec; long int value_nsec; long int interval_sec; long int interval_nsec; char __data[0]; }; struct trace_event_raw_itimer_expire { struct trace_entry ent; int which; pid_t pid; long long unsigned int now; char __data[0]; }; struct trace_event_raw_tick_stop { struct trace_entry ent; int success; int dependency; char __data[0]; }; struct trace_event_data_offsets_timer_class {}; struct trace_event_data_offsets_timer_start {}; struct trace_event_data_offsets_timer_expire_entry {}; struct trace_event_data_offsets_hrtimer_init {}; struct trace_event_data_offsets_hrtimer_start {}; struct trace_event_data_offsets_hrtimer_expire_entry {}; struct trace_event_data_offsets_hrtimer_class {}; struct trace_event_data_offsets_itimer_state {}; struct trace_event_data_offsets_itimer_expire {}; struct trace_event_data_offsets_tick_stop {}; typedef void (*btf_trace_timer_init)(void *, struct timer_list *); typedef void (*btf_trace_timer_start)(void *, struct timer_list *, long unsigned int, unsigned int); typedef void (*btf_trace_timer_expire_entry)(void *, struct timer_list *, long unsigned int); typedef void (*btf_trace_timer_expire_exit)(void *, struct timer_list *); typedef void (*btf_trace_timer_cancel)(void *, struct timer_list *); typedef void (*btf_trace_hrtimer_init)(void *, struct hrtimer *, clockid_t, enum hrtimer_mode); typedef void (*btf_trace_hrtimer_start)(void *, struct hrtimer *, enum hrtimer_mode); typedef void (*btf_trace_hrtimer_expire_entry)(void *, struct hrtimer *, ktime_t *); typedef void (*btf_trace_hrtimer_expire_exit)(void *, struct hrtimer *); typedef void (*btf_trace_hrtimer_cancel)(void *, struct hrtimer *); typedef void (*btf_trace_itimer_state)(void *, int, const struct itimerspec64 * const, long long unsigned int); typedef void (*btf_trace_itimer_expire)(void *, int, struct pid *, long long unsigned int); typedef void (*btf_trace_tick_stop)(void *, int, int); struct timer_base { raw_spinlock_t lock; struct timer_list *running_timer; long unsigned int clk; long unsigned int next_expiry; unsigned int cpu; bool next_expiry_recalc; bool is_idle; bool timers_pending; long unsigned int pending_map[9]; struct hlist_head vectors[576]; long: 64; long: 64; }; struct process_timer { struct timer_list timer; struct task_struct *task; }; enum tick_device_mode { TICKDEV_MODE_PERIODIC = 0, TICKDEV_MODE_ONESHOT = 1, }; struct tick_device { struct clock_event_device *evtdev; enum tick_device_mode mode; }; struct ktime_timestamps { u64 mono; u64 boot; u64 real; }; struct system_time_snapshot { u64 cycles; ktime_t real; ktime_t raw; enum clocksource_ids cs_id; unsigned int clock_was_set_seq; u8 cs_was_changed_seq; }; struct system_device_crosststamp { ktime_t device; ktime_t sys_realtime; ktime_t sys_monoraw; }; struct audit_ntp_val { long long int oldval; long long int newval; }; struct audit_ntp_data { struct audit_ntp_val vals[6]; }; enum timekeeping_adv_mode { TK_ADV_TICK = 0, TK_ADV_FREQ = 1, }; struct tk_fast { seqcount_latch_t seq; struct tk_read_base base[2]; }; struct rtc_wkalrm { unsigned char enabled; unsigned char pending; struct rtc_time time; }; struct rtc_class_ops { int (*ioctl)(struct device *, unsigned int, long unsigned int); int (*read_time)(struct device *, struct rtc_time *); int (*set_time)(struct device *, struct rtc_time *); int (*read_alarm)(struct device *, struct rtc_wkalrm *); int (*set_alarm)(struct device *, struct rtc_wkalrm *); int (*proc)(struct device *, struct seq_file *); int (*alarm_irq_enable)(struct device *, unsigned int); int (*read_offset)(struct device *, long int *); int (*set_offset)(struct device *, long int); }; struct rtc_device; struct rtc_timer { struct timerqueue_node node; ktime_t period; void (*func)(struct rtc_device *); struct rtc_device *rtc; int enabled; }; struct rtc_device { struct device dev; struct module *owner; int id; const struct rtc_class_ops *ops; struct mutex ops_lock; struct cdev char_dev; long unsigned int flags; long unsigned int irq_data; spinlock_t irq_lock; wait_queue_head_t irq_queue; struct fasync_struct *async_queue; int irq_freq; int max_user_freq; struct timerqueue_head timerqueue; struct rtc_timer aie_timer; struct rtc_timer uie_rtctimer; struct hrtimer pie_timer; int pie_enabled; struct work_struct irqwork; int uie_unsupported; long unsigned int set_offset_nsec; long unsigned int features[1]; time64_t range_min; timeu64_t range_max; time64_t start_secs; time64_t offset_secs; bool set_start_time; struct work_struct uie_task; struct timer_list uie_timer; unsigned int oldsecs; unsigned int uie_irq_active: 1; unsigned int stop_uie_polling: 1; unsigned int uie_task_active: 1; unsigned int uie_timer_active: 1; }; typedef s64 int64_t; enum tick_nohz_mode { NOHZ_MODE_INACTIVE = 0, NOHZ_MODE_LOWRES = 1, NOHZ_MODE_HIGHRES = 2, }; struct tick_sched { struct hrtimer sched_timer; long unsigned int check_clocks; enum tick_nohz_mode nohz_mode; unsigned int inidle: 1; unsigned int tick_stopped: 1; unsigned int idle_active: 1; unsigned int do_timer_last: 1; unsigned int got_idle_tick: 1; ktime_t last_tick; ktime_t next_tick; long unsigned int idle_jiffies; long unsigned int idle_calls; long unsigned int idle_sleeps; ktime_t idle_entrytime; ktime_t idle_waketime; ktime_t idle_exittime; ktime_t idle_sleeptime; ktime_t iowait_sleeptime; long unsigned int last_jiffies; u64 timer_expires; u64 timer_expires_base; u64 next_timer; ktime_t idle_expires; atomic_t tick_dep_mask; }; struct timer_list_iter { int cpu; bool second_pass; u64 now; }; struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; long int tm_year; int tm_wday; int tm_yday; }; struct cyclecounter { u64 (*read)(const struct cyclecounter *); u64 mask; u32 mult; u32 shift; }; struct timecounter { const struct cyclecounter *cc; u64 cycle_last; u64 nsec; u64 mask; u64 frac; }; typedef __kernel_timer_t timer_t; enum alarmtimer_type { ALARM_REALTIME = 0, ALARM_BOOTTIME = 1, ALARM_NUMTYPE = 2, ALARM_REALTIME_FREEZER = 3, ALARM_BOOTTIME_FREEZER = 4, }; enum alarmtimer_restart { ALARMTIMER_NORESTART = 0, ALARMTIMER_RESTART = 1, }; struct alarm { struct timerqueue_node node; struct hrtimer timer; enum alarmtimer_restart (*function)(struct alarm *, ktime_t); enum alarmtimer_type type; int state; void *data; }; struct cpu_timer { struct timerqueue_node node; struct timerqueue_head *head; struct pid *pid; struct list_head elist; int firing; }; struct k_clock; struct k_itimer { struct list_head list; struct hlist_node t_hash; spinlock_t it_lock; const struct k_clock *kclock; clockid_t it_clock; timer_t it_id; int it_active; s64 it_overrun; s64 it_overrun_last; int it_requeue_pending; int it_sigev_notify; ktime_t it_interval; struct signal_struct *it_signal; union { struct pid *it_pid; struct task_struct *it_process; }; struct sigqueue *sigq; union { struct { struct hrtimer timer; } real; struct cpu_timer cpu; struct { struct alarm alarmtimer; } alarm; } it; struct callback_head rcu; }; struct k_clock { int (*clock_getres)(const clockid_t, struct timespec64 *); int (*clock_set)(const clockid_t, const struct timespec64 *); int (*clock_get_timespec)(const clockid_t, struct timespec64 *); ktime_t (*clock_get_ktime)(const clockid_t); int (*clock_adj)(const clockid_t, struct __kernel_timex *); int (*timer_create)(struct k_itimer *); int (*nsleep)(const clockid_t, int, const struct timespec64 *); int (*timer_set)(struct k_itimer *, int, struct itimerspec64 *, struct itimerspec64 *); int (*timer_del)(struct k_itimer *); void (*timer_get)(struct k_itimer *, struct itimerspec64 *); void (*timer_rearm)(struct k_itimer *); s64 (*timer_forward)(struct k_itimer *, ktime_t); ktime_t (*timer_remaining)(struct k_itimer *, ktime_t); int (*timer_try_to_cancel)(struct k_itimer *); void (*timer_arm)(struct k_itimer *, ktime_t, bool, bool); void (*timer_wait_running)(struct k_itimer *); }; struct class_interface { struct list_head node; struct class *class; int (*add_dev)(struct device *, struct class_interface *); void (*remove_dev)(struct device *, struct class_interface *); }; struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t); int (*resume)(struct platform_device *); struct device_driver driver; const struct platform_device_id *id_table; bool prevent_deferred_probe; }; struct trace_event_raw_alarmtimer_suspend { struct trace_entry ent; s64 expires; unsigned char alarm_type; char __data[0]; }; struct trace_event_raw_alarm_class { struct trace_entry ent; void *alarm; unsigned char alarm_type; s64 expires; s64 now; char __data[0]; }; struct trace_event_data_offsets_alarmtimer_suspend {}; struct trace_event_data_offsets_alarm_class {}; typedef void (*btf_trace_alarmtimer_suspend)(void *, ktime_t, int); typedef void (*btf_trace_alarmtimer_fired)(void *, struct alarm *, ktime_t); typedef void (*btf_trace_alarmtimer_start)(void *, struct alarm *, ktime_t); typedef void (*btf_trace_alarmtimer_cancel)(void *, struct alarm *, ktime_t); struct alarm_base { spinlock_t lock; struct timerqueue_head timerqueue; ktime_t (*get_ktime)(); void (*get_timespec)(struct timespec64 *); clockid_t base_clockid; }; struct sigevent { sigval_t sigev_value; int sigev_signo; int sigev_notify; union { int _pad[12]; int _tid; struct { void (*_function)(sigval_t); void *_attribute; } _sigev_thread; } _sigev_un; }; typedef struct sigevent sigevent_t; struct compat_sigevent { compat_sigval_t sigev_value; compat_int_t sigev_signo; compat_int_t sigev_notify; union { compat_int_t _pad[13]; compat_int_t _tid; struct { compat_uptr_t _function; compat_uptr_t _attribute; } _sigev_thread; } _sigev_un; }; struct posix_clock; struct posix_clock_operations { struct module *owner; int (*clock_adjtime)(struct posix_clock *, struct __kernel_timex *); int (*clock_gettime)(struct posix_clock *, struct timespec64 *); int (*clock_getres)(struct posix_clock *, struct timespec64 *); int (*clock_settime)(struct posix_clock *, const struct timespec64 *); long int (*ioctl)(struct posix_clock *, unsigned int, long unsigned int); int (*open)(struct posix_clock *, fmode_t); __poll_t (*poll)(struct posix_clock *, struct file *, poll_table *); int (*release)(struct posix_clock *); ssize_t (*read)(struct posix_clock *, uint, char *, size_t); }; struct posix_clock { struct posix_clock_operations ops; struct cdev cdev; struct device *dev; struct rw_semaphore rwsem; bool zombie; }; struct posix_clock_desc { struct file *fp; struct posix_clock *clk; }; struct __kernel_old_itimerval { struct __kernel_old_timeval it_interval; struct __kernel_old_timeval it_value; }; struct old_itimerval32 { struct old_timeval32 it_interval; struct old_timeval32 it_value; }; struct ce_unbind { struct clock_event_device *ce; int res; }; struct proc_timens_offset { int clockid; struct timespec64 val; }; union futex_key { struct { u64 i_seq; long unsigned int pgoff; unsigned int offset; } shared; struct { union { struct mm_struct *mm; u64 __tmp; }; long unsigned int address; unsigned int offset; } private; struct { u64 ptr; long unsigned int word; unsigned int offset; } both; }; struct futex_pi_state { struct list_head list; struct rt_mutex pi_mutex; struct task_struct *owner; refcount_t refcount; union futex_key key; }; struct futex_q { struct plist_node list; struct task_struct *task; spinlock_t *lock_ptr; union futex_key key; struct futex_pi_state *pi_state; struct rt_mutex_waiter *rt_waiter; union futex_key *requeue_pi_key; u32 bitset; }; struct futex_hash_bucket { atomic_t waiters; spinlock_t lock; struct plist_head chain; long: 64; long: 64; long: 64; long: 64; long: 64; }; enum futex_access { FUTEX_READ = 0, FUTEX_WRITE = 1, }; struct dma_chan { int lock; const char *device_id; }; struct cfd_percpu { call_single_data_t csd; }; struct call_function_data { struct cfd_percpu *pcpu; cpumask_var_t cpumask; cpumask_var_t cpumask_ipi; }; struct smp_call_on_cpu_struct { struct work_struct work; struct completion done; int (*func)(void *); void *data; int ret; int cpu; }; struct latch_tree_root { seqcount_latch_t seq; struct rb_root tree[2]; }; struct latch_tree_ops { bool (*less)(struct latch_tree_node *, struct latch_tree_node *); int (*comp)(void *, struct latch_tree_node *); }; struct module_use { struct list_head source_list; struct list_head target_list; struct module *source; struct module *target; }; struct module_sect_attr { struct bin_attribute battr; long unsigned int address; }; struct module_sect_attrs { struct attribute_group grp; unsigned int nsections; struct module_sect_attr attrs[0]; }; struct module_notes_attrs { struct kobject *dir; unsigned int notes; struct bin_attribute attrs[0]; }; enum kernel_read_file_id { READING_UNKNOWN = 0, READING_FIRMWARE = 1, READING_MODULE = 2, READING_KEXEC_IMAGE = 3, READING_KEXEC_INITRAMFS = 4, READING_POLICY = 5, READING_X509_CERTIFICATE = 6, READING_MAX_ID = 7, }; enum kernel_load_data_id { LOADING_UNKNOWN = 0, LOADING_FIRMWARE = 1, LOADING_MODULE = 2, LOADING_KEXEC_IMAGE = 3, LOADING_KEXEC_INITRAMFS = 4, LOADING_POLICY = 5, LOADING_X509_CERTIFICATE = 6, LOADING_MAX_ID = 7, }; enum { PROC_ENTRY_PERMANENT = 1, }; struct load_info { const char *name; struct module *mod; Elf64_Ehdr *hdr; long unsigned int len; Elf64_Shdr *sechdrs; char *secstrings; char *strtab; long unsigned int symoffs; long unsigned int stroffs; long unsigned int init_typeoffs; long unsigned int core_typeoffs; struct _ddebug *debug; unsigned int num_debug; bool sig_ok; long unsigned int mod_kallsyms_init_off; struct { unsigned int sym; unsigned int str; unsigned int mod; unsigned int vers; unsigned int info; unsigned int pcpu; } index; }; struct trace_event_raw_module_load { struct trace_entry ent; unsigned int taints; u32 __data_loc_name; char __data[0]; }; struct trace_event_raw_module_free { struct trace_entry ent; u32 __data_loc_name; char __data[0]; }; struct trace_event_raw_module_refcnt { struct trace_entry ent; long unsigned int ip; int refcnt; u32 __data_loc_name; char __data[0]; }; struct trace_event_raw_module_request { struct trace_entry ent; long unsigned int ip; bool wait; u32 __data_loc_name; char __data[0]; }; struct trace_event_data_offsets_module_load { u32 name; }; struct trace_event_data_offsets_module_free { u32 name; }; struct trace_event_data_offsets_module_refcnt { u32 name; }; struct trace_event_data_offsets_module_request { u32 name; }; typedef void (*btf_trace_module_load)(void *, struct module *); typedef void (*btf_trace_module_free)(void *, struct module *); typedef void (*btf_trace_module_get)(void *, struct module *, long unsigned int); typedef void (*btf_trace_module_put)(void *, struct module *, long unsigned int); typedef void (*btf_trace_module_request)(void *, char *, bool, long unsigned int); struct mod_tree_root { struct latch_tree_root root; long unsigned int addr_min; long unsigned int addr_max; }; enum mod_license { NOT_GPL_ONLY = 0, GPL_ONLY = 1, }; struct symsearch { const struct kernel_symbol *start; const struct kernel_symbol *stop; const s32 *crcs; enum mod_license license; }; struct find_symbol_arg { const char *name; bool gplok; bool warn; struct module *owner; const s32 *crc; const struct kernel_symbol *sym; enum mod_license license; }; struct mod_initfree { struct llist_node node; void *module_init; }; struct module_signature { u8 algo; u8 hash; u8 id_type; u8 signer_len; u8 key_id_len; u8 __pad[3]; __be32 sig_len; }; enum pkey_id_type { PKEY_ID_PGP = 0, PKEY_ID_X509 = 1, PKEY_ID_PKCS7 = 2, }; struct kallsym_iter { loff_t pos; loff_t pos_arch_end; loff_t pos_mod_end; loff_t pos_ftrace_mod_end; loff_t pos_bpf_end; long unsigned int value; unsigned int nameoff; char type; char name[128]; char module_name[56]; int exported; int show_value; }; typedef __u16 comp_t; struct acct_v3 { char ac_flag; char ac_version; __u16 ac_tty; __u32 ac_exitcode; __u32 ac_uid; __u32 ac_gid; __u32 ac_pid; __u32 ac_ppid; __u32 ac_btime; __u32 ac_etime; comp_t ac_utime; comp_t ac_stime; comp_t ac_mem; comp_t ac_io; comp_t ac_rw; comp_t ac_minflt; comp_t ac_majflt; comp_t ac_swaps; char ac_comm[16]; }; typedef struct acct_v3 acct_t; struct fs_pin { wait_queue_head_t wait; int done; struct hlist_node s_list; struct hlist_node m_list; void (*kill)(struct fs_pin *); }; struct bsd_acct_struct { struct fs_pin pin; atomic_long_t count; struct callback_head rcu; struct mutex lock; int active; long unsigned int needcheck; struct file *file; struct pid_namespace *ns; struct work_struct work; struct completion done; }; struct elf64_note { Elf64_Word n_namesz; Elf64_Word n_descsz; Elf64_Word n_type; }; typedef long unsigned int elf_greg_t; typedef elf_greg_t elf_gregset_t[27]; struct elf_siginfo { int si_signo; int si_code; int si_errno; }; struct elf_prstatus_common { struct elf_siginfo pr_info; short int pr_cursig; long unsigned int pr_sigpend; long unsigned int pr_sighold; pid_t pr_pid; pid_t pr_ppid; pid_t pr_pgrp; pid_t pr_sid; struct __kernel_old_timeval pr_utime; struct __kernel_old_timeval pr_stime; struct __kernel_old_timeval pr_cutime; struct __kernel_old_timeval pr_cstime; }; struct elf_prstatus { struct elf_prstatus_common common; elf_gregset_t pr_reg; int pr_fpvalid; }; typedef u32 note_buf_t[92]; struct compat_kexec_segment { compat_uptr_t buf; compat_size_t bufsz; compat_ulong_t mem; compat_size_t memsz; }; struct elf64_phdr { Elf64_Word p_type; Elf64_Word p_flags; Elf64_Off p_offset; Elf64_Addr p_vaddr; Elf64_Addr p_paddr; Elf64_Xword p_filesz; Elf64_Xword p_memsz; Elf64_Xword p_align; }; typedef struct elf64_phdr Elf64_Phdr; struct shash_alg { int (*init)(struct shash_desc *); int (*update)(struct shash_desc *, const u8 *, unsigned int); int (*final)(struct shash_desc *, u8 *); int (*finup)(struct shash_desc *, const u8 *, unsigned int, u8 *); int (*digest)(struct shash_desc *, const u8 *, unsigned int, u8 *); int (*export)(struct shash_desc *, void *); int (*import)(struct shash_desc *, const void *); int (*setkey)(struct crypto_shash *, const u8 *, unsigned int); int (*init_tfm)(struct crypto_shash *); void (*exit_tfm)(struct crypto_shash *); unsigned int descsize; int: 32; unsigned int digestsize; unsigned int statesize; struct crypto_alg base; }; struct kexec_sha_region { long unsigned int start; long unsigned int len; }; enum migrate_reason { MR_COMPACTION = 0, MR_MEMORY_FAILURE = 1, MR_MEMORY_HOTPLUG = 2, MR_SYSCALL = 3, MR_MEMPOLICY_MBIND = 4, MR_NUMA_MISPLACED = 5, MR_CONTIG_RANGE = 6, MR_LONGTERM_PIN = 7, MR_TYPES = 8, }; typedef __kernel_ulong_t ino_t; enum bpf_link_type { BPF_LINK_TYPE_UNSPEC = 0, BPF_LINK_TYPE_RAW_TRACEPOINT = 1, BPF_LINK_TYPE_TRACING = 2, BPF_LINK_TYPE_CGROUP = 3, BPF_LINK_TYPE_ITER = 4, BPF_LINK_TYPE_NETNS = 5, BPF_LINK_TYPE_XDP = 6, MAX_BPF_LINK_TYPE = 7, }; struct bpf_link_info { __u32 type; __u32 id; __u32 prog_id; union { struct { __u64 tp_name; __u32 tp_name_len; } raw_tracepoint; struct { __u32 attach_type; __u32 target_obj_id; __u32 target_btf_id; } tracing; struct { __u64 cgroup_id; __u32 attach_type; } cgroup; struct { __u64 target_name; __u32 target_name_len; union { struct { __u32 map_id; } map; }; } iter; struct { __u32 netns_ino; __u32 attach_type; } netns; struct { __u32 ifindex; } xdp; }; }; struct bpf_link_ops; struct bpf_link { atomic64_t refcnt; u32 id; enum bpf_link_type type; const struct bpf_link_ops *ops; struct bpf_prog *prog; struct work_struct work; }; struct bpf_link_ops { void (*release)(struct bpf_link *); void (*dealloc)(struct bpf_link *); int (*detach)(struct bpf_link *); int (*update_prog)(struct bpf_link *, struct bpf_prog *, struct bpf_prog *); void (*show_fdinfo)(const struct bpf_link *, struct seq_file *); int (*fill_link_info)(const struct bpf_link *, struct bpf_link_info *); }; struct bpf_cgroup_link { struct bpf_link link; struct cgroup *cgroup; enum bpf_attach_type type; }; enum { CGRP_NOTIFY_ON_RELEASE = 0, CGRP_CPUSET_CLONE_CHILDREN = 1, CGRP_FREEZE = 2, CGRP_FROZEN = 3, CGRP_KILL = 4, }; enum { CGRP_ROOT_NOPREFIX = 2, CGRP_ROOT_XATTR = 4, CGRP_ROOT_NS_DELEGATE = 8, CGRP_ROOT_CPUSET_V2_MODE = 16, CGRP_ROOT_MEMORY_LOCAL_EVENTS = 32, CGRP_ROOT_MEMORY_RECURSIVE_PROT = 64, }; struct cgroup_taskset { struct list_head src_csets; struct list_head dst_csets; int nr_tasks; int ssid; struct list_head *csets; struct css_set *cur_cset; struct task_struct *cur_task; }; struct cgroup_fs_context { struct kernfs_fs_context kfc; struct cgroup_root *root; struct cgroup_namespace *ns; unsigned int flags; bool cpuset_clone_children; bool none; bool all_ss; u16 subsys_mask; char *name; char *release_agent; }; struct cgrp_cset_link { struct cgroup *cgrp; struct css_set *cset; struct list_head cset_link; struct list_head cgrp_link; }; struct cgroup_mgctx { struct list_head preloaded_src_csets; struct list_head preloaded_dst_csets; struct cgroup_taskset tset; u16 ss_mask; }; struct trace_event_raw_cgroup_root { struct trace_entry ent; int root; u16 ss_mask; u32 __data_loc_name; char __data[0]; }; struct trace_event_raw_cgroup { struct trace_entry ent; int root; int id; int level; u32 __data_loc_path; char __data[0]; }; struct trace_event_raw_cgroup_migrate { struct trace_entry ent; int dst_root; int dst_id; int dst_level; int pid; u32 __data_loc_dst_path; u32 __data_loc_comm; char __data[0]; }; struct trace_event_raw_cgroup_event { struct trace_entry ent; int root; int id; int level; u32 __data_loc_path; int val; char __data[0]; }; struct trace_event_data_offsets_cgroup_root { u32 name; }; struct trace_event_data_offsets_cgroup { u32 path; }; struct trace_event_data_offsets_cgroup_migrate { u32 dst_path; u32 comm; }; struct trace_event_data_offsets_cgroup_event { u32 path; }; typedef void (*btf_trace_cgroup_setup_root)(void *, struct cgroup_root *); typedef void (*btf_trace_cgroup_destroy_root)(void *, struct cgroup_root *); typedef void (*btf_trace_cgroup_remount)(void *, struct cgroup_root *); typedef void (*btf_trace_cgroup_mkdir)(void *, struct cgroup *, const char *); typedef void (*btf_trace_cgroup_rmdir)(void *, struct cgroup *, const char *); typedef void (*btf_trace_cgroup_release)(void *, struct cgroup *, const char *); typedef void (*btf_trace_cgroup_rename)(void *, struct cgroup *, const char *); typedef void (*btf_trace_cgroup_freeze)(void *, struct cgroup *, const char *); typedef void (*btf_trace_cgroup_unfreeze)(void *, struct cgroup *, const char *); typedef void (*btf_trace_cgroup_attach_task)(void *, struct cgroup *, const char *, struct task_struct *, bool); typedef void (*btf_trace_cgroup_transfer_tasks)(void *, struct cgroup *, const char *, struct task_struct *, bool); typedef void (*btf_trace_cgroup_notify_populated)(void *, struct cgroup *, const char *, int); typedef void (*btf_trace_cgroup_notify_frozen)(void *, struct cgroup *, const char *, int); enum cgroup_opt_features { OPT_FEATURE_PRESSURE = 0, OPT_FEATURE_COUNT = 1, }; enum cgroup2_param { Opt_nsdelegate = 0, Opt_memory_localevents = 1, Opt_memory_recursiveprot = 2, nr__cgroup2_params = 3, }; struct cgroupstats { __u64 nr_sleeping; __u64 nr_running; __u64 nr_stopped; __u64 nr_uninterruptible; __u64 nr_io_wait; }; enum cgroup_filetype { CGROUP_FILE_PROCS = 0, CGROUP_FILE_TASKS = 1, }; struct cgroup_pidlist { struct { enum cgroup_filetype type; struct pid_namespace *ns; } key; pid_t *list; int length; struct list_head links; struct cgroup *owner; struct delayed_work destroy_dwork; }; enum cgroup1_param { Opt_all = 0, Opt_clone_children = 1, Opt_cpuset_v2_mode = 2, Opt_name = 3, Opt_none = 4, Opt_noprefix = 5, Opt_release_agent = 6, Opt_xattr = 7, }; enum freezer_state_flags { CGROUP_FREEZER_ONLINE = 1, CGROUP_FREEZING_SELF = 2, CGROUP_FREEZING_PARENT = 4, CGROUP_FROZEN = 8, CGROUP_FREEZING = 6, }; struct freezer { struct cgroup_subsys_state css; unsigned int state; }; struct pids_cgroup { struct cgroup_subsys_state css; atomic64_t counter; atomic64_t limit; struct cgroup_file events_file; atomic64_t events_limit; }; typedef struct { char *from; char *to; } substring_t; enum rdmacg_resource_type { RDMACG_RESOURCE_HCA_HANDLE = 0, RDMACG_RESOURCE_HCA_OBJECT = 1, RDMACG_RESOURCE_MAX = 2, }; struct rdma_cgroup { struct cgroup_subsys_state css; struct list_head rpools; }; struct rdmacg_device { struct list_head dev_node; struct list_head rpools; char *name; }; enum rdmacg_file_type { RDMACG_RESOURCE_TYPE_MAX = 0, RDMACG_RESOURCE_TYPE_STAT = 1, }; struct rdmacg_resource { int max; int usage; }; struct rdmacg_resource_pool { struct rdmacg_device *device; struct rdmacg_resource resources[2]; struct list_head cg_node; struct list_head dev_node; u64 usage_sum; int num_max_cnt; }; struct root_domain___2; struct fmeter { int cnt; int val; time64_t time; spinlock_t lock; }; struct cpuset { struct cgroup_subsys_state css; long unsigned int flags; cpumask_var_t cpus_allowed; nodemask_t mems_allowed; cpumask_var_t effective_cpus; nodemask_t effective_mems; cpumask_var_t subparts_cpus; nodemask_t old_mems_allowed; struct fmeter fmeter; int attach_in_progress; int pn; int relax_domain_level; int nr_subparts_cpus; int partition_root_state; int use_parent_ecpus; int child_ecpus_count; }; struct tmpmasks { cpumask_var_t addmask; cpumask_var_t delmask; cpumask_var_t new_cpus; }; typedef enum { CS_ONLINE = 0, CS_CPU_EXCLUSIVE = 1, CS_MEM_EXCLUSIVE = 2, CS_MEM_HARDWALL = 3, CS_MEMORY_MIGRATE = 4, CS_SCHED_LOAD_BALANCE = 5, CS_SPREAD_PAGE = 6, CS_SPREAD_SLAB = 7, } cpuset_flagbits_t; enum subparts_cmd { partcmd_enable = 0, partcmd_disable = 1, partcmd_update = 2, }; struct cpuset_migrate_mm_work { struct work_struct work; struct mm_struct *mm; nodemask_t from; nodemask_t to; }; typedef enum { FILE_MEMORY_MIGRATE = 0, FILE_CPULIST = 1, FILE_MEMLIST = 2, FILE_EFFECTIVE_CPULIST = 3, FILE_EFFECTIVE_MEMLIST = 4, FILE_SUBPARTS_CPULIST = 5, FILE_CPU_EXCLUSIVE = 6, FILE_MEM_EXCLUSIVE = 7, FILE_MEM_HARDWALL = 8, FILE_SCHED_LOAD_BALANCE = 9, FILE_PARTITION_ROOT = 10, FILE_SCHED_RELAX_DOMAIN_LEVEL = 11, FILE_MEMORY_PRESSURE_ENABLED = 12, FILE_MEMORY_PRESSURE = 13, FILE_SPREAD_PAGE = 14, FILE_SPREAD_SLAB = 15, } cpuset_filetype_t; enum misc_res_type { MISC_CG_RES_SEV = 0, MISC_CG_RES_SEV_ES = 1, MISC_CG_RES_TYPES = 2, }; struct misc_res { long unsigned int max; atomic_long_t usage; bool failed; }; struct misc_cg { struct cgroup_subsys_state css; struct misc_res res[2]; }; struct kernel_pkey_query { __u32 supported_ops; __u32 key_size; __u16 max_data_size; __u16 max_sig_size; __u16 max_enc_size; __u16 max_dec_size; }; enum kernel_pkey_operation { kernel_pkey_encrypt = 0, kernel_pkey_decrypt = 1, kernel_pkey_sign = 2, kernel_pkey_verify = 3, }; struct kernel_pkey_params { struct key *key; const char *encoding; const char *hash_algo; char *info; __u32 in_len; union { __u32 out_len; __u32 in2_len; }; enum kernel_pkey_operation op: 8; }; struct key_preparsed_payload { const char *orig_description; char *description; union key_payload payload; const void *data; size_t datalen; size_t quotalen; time64_t expiry; }; struct key_match_data { bool (*cmp)(const struct key *, const struct key_match_data *); const void *raw_data; void *preparsed; unsigned int lookup_type; }; struct idmap_key { bool map_up; u32 id; u32 count; }; struct ctl_path { const char *procname; }; struct cpu_stop_done { atomic_t nr_todo; int ret; struct completion completion; }; struct cpu_stopper { struct task_struct *thread; raw_spinlock_t lock; bool enabled; struct list_head works; struct cpu_stop_work stop_work; long unsigned int caller; cpu_stop_fn_t fn; }; enum multi_stop_state { MULTI_STOP_NONE = 0, MULTI_STOP_PREPARE = 1, MULTI_STOP_DISABLE_IRQ = 2, MULTI_STOP_RUN = 3, MULTI_STOP_EXIT = 4, }; struct multi_stop_data { cpu_stop_fn_t fn; void *data; unsigned int num_threads; const struct cpumask *active_cpus; enum multi_stop_state state; atomic_t thread_ack; }; typedef int __kernel_mqd_t; typedef __kernel_mqd_t mqd_t; enum audit_state { AUDIT_STATE_DISABLED = 0, AUDIT_STATE_BUILD = 1, AUDIT_STATE_RECORD = 2, }; struct audit_cap_data { kernel_cap_t permitted; kernel_cap_t inheritable; union { unsigned int fE; kernel_cap_t effective; }; kernel_cap_t ambient; kuid_t rootid; }; struct audit_names { struct list_head list; struct filename *name; int name_len; bool hidden; long unsigned int ino; dev_t dev; umode_t mode; kuid_t uid; kgid_t gid; dev_t rdev; u32 osid; struct audit_cap_data fcap; unsigned int fcap_ver; unsigned char type; bool should_free; }; struct mq_attr { __kernel_long_t mq_flags; __kernel_long_t mq_maxmsg; __kernel_long_t mq_msgsize; __kernel_long_t mq_curmsgs; __kernel_long_t __reserved[4]; }; struct audit_proctitle { int len; char *value; }; struct audit_aux_data; struct __kernel_sockaddr_storage; struct audit_tree_refs; struct audit_context { int dummy; int in_syscall; enum audit_state state; enum audit_state current_state; unsigned int serial; int major; struct timespec64 ctime; long unsigned int argv[4]; long int return_code; u64 prio; int return_valid; struct audit_names preallocated_names[5]; int name_count; struct list_head names_list; char *filterkey; struct path pwd; struct audit_aux_data *aux; struct audit_aux_data *aux_pids; struct __kernel_sockaddr_storage *sockaddr; size_t sockaddr_len; pid_t pid; pid_t ppid; kuid_t uid; kuid_t euid; kuid_t suid; kuid_t fsuid; kgid_t gid; kgid_t egid; kgid_t sgid; kgid_t fsgid; long unsigned int personality; int arch; pid_t target_pid; kuid_t target_auid; kuid_t target_uid; unsigned int target_sessionid; u32 target_sid; char target_comm[16]; struct audit_tree_refs *trees; struct audit_tree_refs *first_trees; struct list_head killed_trees; int tree_count; int type; union { struct { int nargs; long int args[6]; } socketcall; struct { kuid_t uid; kgid_t gid; umode_t mode; u32 osid; int has_perm; uid_t perm_uid; gid_t perm_gid; umode_t perm_mode; long unsigned int qbytes; } ipc; struct { mqd_t mqdes; struct mq_attr mqstat; } mq_getsetattr; struct { mqd_t mqdes; int sigev_signo; } mq_notify; struct { mqd_t mqdes; size_t msg_len; unsigned int msg_prio; struct timespec64 abs_timeout; } mq_sendrecv; struct { int oflag; umode_t mode; struct mq_attr attr; } mq_open; struct { pid_t pid; struct audit_cap_data cap; } capset; struct { int fd; int flags; } mmap; struct { int argc; } execve; struct { char *name; } module; }; int fds[2]; struct audit_proctitle proctitle; }; struct __kernel_sockaddr_storage { union { struct { __kernel_sa_family_t ss_family; char __data[126]; }; void *__align; }; }; enum audit_nlgrps { AUDIT_NLGRP_NONE = 0, AUDIT_NLGRP_READLOG = 1, __AUDIT_NLGRP_MAX = 2, }; struct audit_status { __u32 mask; __u32 enabled; __u32 failure; __u32 pid; __u32 rate_limit; __u32 backlog_limit; __u32 lost; __u32 backlog; union { __u32 version; __u32 feature_bitmap; }; __u32 backlog_wait_time; __u32 backlog_wait_time_actual; }; struct audit_features { __u32 vers; __u32 mask; __u32 features; __u32 lock; }; struct audit_tty_status { __u32 enabled; __u32 log_passwd; }; struct audit_sig_info { uid_t uid; pid_t pid; char ctx[0]; }; struct net_generic { union { struct { unsigned int len; struct callback_head rcu; } s; void *ptr[0]; }; }; struct pernet_operations { struct list_head list; int (*init)(struct net *); void (*pre_exit)(struct net *); void (*exit)(struct net *); void (*exit_batch)(struct list_head *); unsigned int *id; size_t size; }; struct scm_creds { u32 pid; kuid_t uid; kgid_t gid; }; struct netlink_skb_parms { struct scm_creds creds; __u32 portid; __u32 dst_group; __u32 flags; struct sock *sk; bool nsid_is_set; int nsid; }; struct netlink_kernel_cfg { unsigned int groups; unsigned int flags; void (*input)(struct sk_buff *); struct mutex *cb_mutex; int (*bind)(struct net *, int); void (*unbind)(struct net *, int); bool (*compare)(struct net *, struct sock *); }; struct audit_netlink_list { __u32 portid; struct net *net; struct sk_buff_head q; }; struct audit_net { struct sock *sk; }; struct auditd_connection { struct pid *pid; u32 portid; struct net *net; struct callback_head rcu; }; struct audit_ctl_mutex { struct mutex lock; void *owner; }; struct audit_buffer { struct sk_buff *skb; struct audit_context *ctx; gfp_t gfp_mask; }; struct audit_reply { __u32 portid; struct net *net; struct sk_buff *skb; }; enum { Audit_equal = 0, Audit_not_equal = 1, Audit_bitmask = 2, Audit_bittest = 3, Audit_lt = 4, Audit_gt = 5, Audit_le = 6, Audit_ge = 7, Audit_bad = 8, }; struct audit_rule_data { __u32 flags; __u32 action; __u32 field_count; __u32 mask[64]; __u32 fields[64]; __u32 values[64]; __u32 fieldflags[64]; __u32 buflen; char buf[0]; }; struct audit_field; struct audit_watch; struct audit_tree; struct audit_fsnotify_mark; struct audit_krule { u32 pflags; u32 flags; u32 listnr; u32 action; u32 mask[64]; u32 buflen; u32 field_count; char *filterkey; struct audit_field *fields; struct audit_field *arch_f; struct audit_field *inode_f; struct audit_watch *watch; struct audit_tree *tree; struct audit_fsnotify_mark *exe; struct list_head rlist; struct list_head list; u64 prio; }; struct audit_field { u32 type; union { u32 val; kuid_t uid; kgid_t gid; struct { char *lsm_str; void *lsm_rule; }; }; u32 op; }; struct audit_entry { struct list_head list; struct callback_head rcu; struct audit_krule rule; }; struct audit_buffer___2; typedef int __kernel_key_t; typedef __kernel_key_t key_t; struct cpu_vfs_cap_data { __u32 magic_etc; kernel_cap_t permitted; kernel_cap_t inheritable; kuid_t rootid; }; struct kern_ipc_perm { spinlock_t lock; bool deleted; int id; key_t key; kuid_t uid; kgid_t gid; kuid_t cuid; kgid_t cgid; umode_t mode; long unsigned int seq; void *security; struct rhash_head khtnode; struct callback_head rcu; refcount_t refcount; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; }; typedef struct fsnotify_mark_connector *fsnotify_connp_t; struct fsnotify_mark_connector { spinlock_t lock; short unsigned int type; short unsigned int flags; __kernel_fsid_t fsid; union { fsnotify_connp_t *obj; struct fsnotify_mark_connector *destroy_next; }; struct hlist_head list; }; enum audit_nfcfgop { AUDIT_XT_OP_REGISTER = 0, AUDIT_XT_OP_REPLACE = 1, AUDIT_XT_OP_UNREGISTER = 2, AUDIT_NFT_OP_TABLE_REGISTER = 3, AUDIT_NFT_OP_TABLE_UNREGISTER = 4, AUDIT_NFT_OP_CHAIN_REGISTER = 5, AUDIT_NFT_OP_CHAIN_UNREGISTER = 6, AUDIT_NFT_OP_RULE_REGISTER = 7, AUDIT_NFT_OP_RULE_UNREGISTER = 8, AUDIT_NFT_OP_SET_REGISTER = 9, AUDIT_NFT_OP_SET_UNREGISTER = 10, AUDIT_NFT_OP_SETELEM_REGISTER = 11, AUDIT_NFT_OP_SETELEM_UNREGISTER = 12, AUDIT_NFT_OP_GEN_REGISTER = 13, AUDIT_NFT_OP_OBJ_REGISTER = 14, AUDIT_NFT_OP_OBJ_UNREGISTER = 15, AUDIT_NFT_OP_OBJ_RESET = 16, AUDIT_NFT_OP_FLOWTABLE_REGISTER = 17, AUDIT_NFT_OP_FLOWTABLE_UNREGISTER = 18, AUDIT_NFT_OP_INVALID = 19, }; enum fsnotify_obj_type { FSNOTIFY_OBJ_TYPE_INODE = 0, FSNOTIFY_OBJ_TYPE_PARENT = 1, FSNOTIFY_OBJ_TYPE_VFSMOUNT = 2, FSNOTIFY_OBJ_TYPE_SB = 3, FSNOTIFY_OBJ_TYPE_COUNT = 4, FSNOTIFY_OBJ_TYPE_DETACHED = 4, }; struct audit_aux_data { struct audit_aux_data *next; int type; }; struct audit_chunk; struct audit_tree_refs { struct audit_tree_refs *next; struct audit_chunk *c[31]; }; struct audit_aux_data_pids { struct audit_aux_data d; pid_t target_pid[16]; kuid_t target_auid[16]; kuid_t target_uid[16]; unsigned int target_sessionid[16]; u32 target_sid[16]; char target_comm[256]; int pid_count; }; struct audit_aux_data_bprm_fcaps { struct audit_aux_data d; struct audit_cap_data fcap; unsigned int fcap_ver; struct audit_cap_data old_pcap; struct audit_cap_data new_pcap; }; struct audit_nfcfgop_tab { enum audit_nfcfgop op; const char *s; }; struct audit_parent; struct audit_watch { refcount_t count; dev_t dev; char *path; long unsigned int ino; struct audit_parent *parent; struct list_head wlist; struct list_head rules; }; struct fsnotify_group; struct fsnotify_iter_info; struct fsnotify_mark; struct fsnotify_event; struct fsnotify_ops { int (*handle_event)(struct fsnotify_group *, u32, const void *, int, struct inode *, const struct qstr *, u32, struct fsnotify_iter_info *); int (*handle_inode_event)(struct fsnotify_mark *, u32, struct inode *, struct inode *, const struct qstr *, u32); void (*free_group_priv)(struct fsnotify_group *); void (*freeing_mark)(struct fsnotify_mark *, struct fsnotify_group *); void (*free_event)(struct fsnotify_event *); void (*free_mark)(struct fsnotify_mark *); }; struct inotify_group_private_data { spinlock_t idr_lock; struct idr idr; struct ucounts *ucounts; }; struct fanotify_group_private_data { struct hlist_head *merge_hash; struct list_head access_list; wait_queue_head_t access_waitq; int flags; int f_flags; struct ucounts *ucounts; }; struct fsnotify_group { const struct fsnotify_ops *ops; refcount_t refcnt; spinlock_t notification_lock; struct list_head notification_list; wait_queue_head_t notification_waitq; unsigned int q_len; unsigned int max_events; unsigned int priority; bool shutdown; struct mutex mark_mutex; atomic_t user_waits; struct list_head marks_list; struct fasync_struct *fsn_fa; struct fsnotify_event *overflow_event; struct mem_cgroup *memcg; union { void *private; struct inotify_group_private_data inotify_data; struct fanotify_group_private_data fanotify_data; }; }; struct fsnotify_iter_info { struct fsnotify_mark *marks[4]; unsigned int report_mask; int srcu_idx; }; struct fsnotify_mark { __u32 mask; refcount_t refcnt; struct fsnotify_group *group; struct list_head g_list; spinlock_t lock; struct hlist_node obj_list; struct fsnotify_mark_connector *connector; __u32 ignored_mask; unsigned int flags; }; struct fsnotify_event { struct list_head list; }; struct audit_parent { struct list_head watches; struct fsnotify_mark mark; }; struct audit_fsnotify_mark { dev_t dev; long unsigned int ino; char *path; struct fsnotify_mark mark; struct audit_krule *rule; }; struct audit_chunk___2; struct audit_tree { refcount_t count; int goner; struct audit_chunk___2 *root; struct list_head chunks; struct list_head rules; struct list_head list; struct list_head same_root; struct callback_head head; char pathname[0]; }; struct node { struct list_head list; struct audit_tree *owner; unsigned int index; }; struct audit_chunk___2 { struct list_head hash; long unsigned int key; struct fsnotify_mark *mark; struct list_head trees; int count; atomic_long_t refs; struct callback_head head; struct node owners[0]; }; struct audit_tree_mark { struct fsnotify_mark mark; struct audit_chunk___2 *chunk; }; enum { HASH_SIZE = 128, }; struct kprobe_blacklist_entry { struct list_head list; long unsigned int start_addr; long unsigned int end_addr; }; enum perf_record_ksymbol_type { PERF_RECORD_KSYMBOL_TYPE_UNKNOWN = 0, PERF_RECORD_KSYMBOL_TYPE_BPF = 1, PERF_RECORD_KSYMBOL_TYPE_OOL = 2, PERF_RECORD_KSYMBOL_TYPE_MAX = 3, }; struct kprobe_insn_page { struct list_head list; kprobe_opcode_t *insns; struct kprobe_insn_cache *cache; int nused; int ngarbage; char slot_used[0]; }; enum kprobe_slot_state { SLOT_CLEAN = 0, SLOT_DIRTY = 1, SLOT_USED = 2, }; struct seccomp_notif_sizes { __u16 seccomp_notif; __u16 seccomp_notif_resp; __u16 seccomp_data; }; struct seccomp_notif { __u64 id; __u32 pid; __u32 flags; struct seccomp_data data; }; struct seccomp_notif_resp { __u64 id; __s64 val; __s32 error; __u32 flags; }; struct seccomp_notif_addfd { __u64 id; __u32 flags; __u32 srcfd; __u32 newfd; __u32 newfd_flags; }; struct action_cache { long unsigned int allow_native[7]; long unsigned int allow_compat[7]; }; struct notification; struct seccomp_filter { refcount_t refs; refcount_t users; bool log; struct action_cache cache; struct seccomp_filter *prev; struct bpf_prog *prog; struct notification *notif; struct mutex notify_lock; wait_queue_head_t wqh; }; struct seccomp_metadata { __u64 filter_off; __u64 flags; }; struct sock_fprog { short unsigned int len; struct sock_filter *filter; }; struct compat_sock_fprog { u16 len; compat_uptr_t filter; }; typedef int (*bpf_aux_classic_check_t)(struct sock_filter *, unsigned int); enum notify_state { SECCOMP_NOTIFY_INIT = 0, SECCOMP_NOTIFY_SENT = 1, SECCOMP_NOTIFY_REPLIED = 2, }; struct seccomp_knotif { struct task_struct *task; u64 id; const struct seccomp_data *data; enum notify_state state; int error; long int val; u32 flags; struct completion ready; struct list_head list; struct list_head addfd; }; struct seccomp_kaddfd { struct file *file; int fd; unsigned int flags; __u32 ioctl_flags; union { bool setfd; int ret; }; struct completion completion; struct list_head list; }; struct notification { struct semaphore request; u64 next_id; struct list_head notifications; }; struct seccomp_log_name { u32 log; const char *name; }; struct rchan; struct rchan_buf { void *start; void *data; size_t offset; size_t subbufs_produced; size_t subbufs_consumed; struct rchan *chan; wait_queue_head_t read_wait; struct irq_work wakeup_work; struct dentry *dentry; struct kref kref; struct page **page_array; unsigned int page_count; unsigned int finalized; size_t *padding; size_t prev_padding; size_t bytes_consumed; size_t early_bytes; unsigned int cpu; long: 32; long: 64; long: 64; long: 64; }; struct rchan_callbacks; struct rchan { u32 version; size_t subbuf_size; size_t n_subbufs; size_t alloc_size; const struct rchan_callbacks *cb; struct kref kref; void *private_data; size_t last_toobig; struct rchan_buf **buf; int is_global; struct list_head list; struct dentry *parent; int has_base_filename; char base_filename[255]; }; struct rchan_callbacks { int (*subbuf_start)(struct rchan_buf *, void *, void *, size_t); struct dentry * (*create_buf_file)(const char *, struct dentry *, umode_t, struct rchan_buf *, int *); int (*remove_buf_file)(struct dentry *); }; struct partial_page { unsigned int offset; unsigned int len; long unsigned int private; }; struct splice_pipe_desc { struct page **pages; struct partial_page *partial; int nr_pages; unsigned int nr_pages_max; const struct pipe_buf_operations *ops; void (*spd_release)(struct splice_pipe_desc *, unsigned int); }; struct rchan_percpu_buf_dispatcher { struct rchan_buf *buf; struct dentry *dentry; }; enum { TASKSTATS_TYPE_UNSPEC = 0, TASKSTATS_TYPE_PID = 1, TASKSTATS_TYPE_TGID = 2, TASKSTATS_TYPE_STATS = 3, TASKSTATS_TYPE_AGGR_PID = 4, TASKSTATS_TYPE_AGGR_TGID = 5, TASKSTATS_TYPE_NULL = 6, __TASKSTATS_TYPE_MAX = 7, }; enum { TASKSTATS_CMD_ATTR_UNSPEC = 0, TASKSTATS_CMD_ATTR_PID = 1, TASKSTATS_CMD_ATTR_TGID = 2, TASKSTATS_CMD_ATTR_REGISTER_CPUMASK = 3, TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = 4, __TASKSTATS_CMD_ATTR_MAX = 5, }; enum { CGROUPSTATS_CMD_UNSPEC = 3, CGROUPSTATS_CMD_GET = 4, CGROUPSTATS_CMD_NEW = 5, __CGROUPSTATS_CMD_MAX = 6, }; enum { CGROUPSTATS_TYPE_UNSPEC = 0, CGROUPSTATS_TYPE_CGROUP_STATS = 1, __CGROUPSTATS_TYPE_MAX = 2, }; enum { CGROUPSTATS_CMD_ATTR_UNSPEC = 0, CGROUPSTATS_CMD_ATTR_FD = 1, __CGROUPSTATS_CMD_ATTR_MAX = 2, }; struct genlmsghdr { __u8 cmd; __u8 version; __u16 reserved; }; enum { NLA_UNSPEC = 0, NLA_U8 = 1, NLA_U16 = 2, NLA_U32 = 3, NLA_U64 = 4, NLA_STRING = 5, NLA_FLAG = 6, NLA_MSECS = 7, NLA_NESTED = 8, NLA_NESTED_ARRAY = 9, NLA_NUL_STRING = 10, NLA_BINARY = 11, NLA_S8 = 12, NLA_S16 = 13, NLA_S32 = 14, NLA_S64 = 15, NLA_BITFIELD32 = 16, NLA_REJECT = 17, __NLA_TYPE_MAX = 18, }; struct genl_multicast_group { char name[16]; u8 flags; }; struct genl_ops; struct genl_info; struct genl_small_ops; struct genl_family { int id; unsigned int hdrsize; char name[16]; unsigned int version; unsigned int maxattr; unsigned int mcgrp_offset; u8 netnsok: 1; u8 parallel_ops: 1; u8 n_ops; u8 n_small_ops; u8 n_mcgrps; const struct nla_policy *policy; int (*pre_doit)(const struct genl_ops *, struct sk_buff *, struct genl_info *); void (*post_doit)(const struct genl_ops *, struct sk_buff *, struct genl_info *); const struct genl_ops *ops; const struct genl_small_ops *small_ops; const struct genl_multicast_group *mcgrps; struct module *module; }; struct genl_ops { int (*doit)(struct sk_buff *, struct genl_info *); int (*start)(struct netlink_callback *); int (*dumpit)(struct sk_buff *, struct netlink_callback *); int (*done)(struct netlink_callback *); const struct nla_policy *policy; unsigned int maxattr; u8 cmd; u8 internal_flags; u8 flags; u8 validate; }; struct genl_info { u32 snd_seq; u32 snd_portid; struct nlmsghdr *nlhdr; struct genlmsghdr *genlhdr; void *userhdr; struct nlattr **attrs; possible_net_t _net; void *user_ptr[2]; struct netlink_ext_ack *extack; }; struct genl_small_ops { int (*doit)(struct sk_buff *, struct genl_info *); int (*dumpit)(struct sk_buff *, struct netlink_callback *); u8 cmd; u8 internal_flags; u8 flags; u8 validate; }; enum genl_validate_flags { GENL_DONT_VALIDATE_STRICT = 1, GENL_DONT_VALIDATE_DUMP = 2, GENL_DONT_VALIDATE_DUMP_STRICT = 4, }; struct listener { struct list_head list; pid_t pid; char valid; }; struct listener_list { struct rw_semaphore sem; struct list_head list; }; enum actions { REGISTER = 0, DEREGISTER = 1, CPU_DONT_CARE = 2, }; struct tp_module { struct list_head list; struct module *mod; }; enum tp_func_state { TP_FUNC_0 = 0, TP_FUNC_1 = 1, TP_FUNC_2 = 2, TP_FUNC_N = 3, }; enum tp_transition_sync { TP_TRANSITION_SYNC_1_0_1 = 0, TP_TRANSITION_SYNC_N_2_1 = 1, _NR_TP_TRANSITION_SYNC = 2, }; struct tp_transition_snapshot { long unsigned int rcu; long unsigned int srcu; bool ongoing; }; struct tp_probes { struct callback_head rcu; struct tracepoint_func probes[0]; }; struct ftrace_hash { long unsigned int size_bits; struct hlist_head *buckets; long unsigned int count; long unsigned int flags; struct callback_head rcu; }; struct ftrace_func_entry { struct hlist_node hlist; long unsigned int ip; long unsigned int direct; }; enum ftrace_bug_type { FTRACE_BUG_UNKNOWN = 0, FTRACE_BUG_INIT = 1, FTRACE_BUG_NOP = 2, FTRACE_BUG_CALL = 3, FTRACE_BUG_UPDATE = 4, }; enum { FTRACE_UPDATE_CALLS = 1, FTRACE_DISABLE_CALLS = 2, FTRACE_UPDATE_TRACE_FUNC = 4, FTRACE_START_FUNC_RET = 8, FTRACE_STOP_FUNC_RET = 16, FTRACE_MAY_SLEEP = 32, }; enum { FTRACE_ITER_FILTER = 1, FTRACE_ITER_NOTRACE = 2, FTRACE_ITER_PRINTALL = 4, FTRACE_ITER_DO_PROBES = 8, FTRACE_ITER_PROBE = 16, FTRACE_ITER_MOD = 32, FTRACE_ITER_ENABLED = 64, }; struct ftrace_graph_ent { long unsigned int func; int depth; } __attribute__((packed)); struct ftrace_graph_ret { long unsigned int func; int depth; unsigned int overrun; long long unsigned int calltime; long long unsigned int rettime; }; typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *); typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); struct fgraph_ops { trace_func_graph_ent_t entryfunc; trace_func_graph_ret_t retfunc; }; struct prog_entry; struct event_filter { struct prog_entry *prog; char *filter_string; }; struct trace_array_cpu; struct array_buffer { struct trace_array *tr; struct trace_buffer *buffer; struct trace_array_cpu *data; u64 time_start; int cpu; }; struct trace_pid_list; struct trace_options; struct cond_snapshot; struct trace_func_repeats; struct trace_array { struct list_head list; char *name; struct array_buffer array_buffer; struct array_buffer max_buffer; bool allocated_snapshot; long unsigned int max_latency; struct dentry *d_max_latency; struct work_struct fsnotify_work; struct irq_work fsnotify_irqwork; struct trace_pid_list *filtered_pids; struct trace_pid_list *filtered_no_pids; arch_spinlock_t max_lock; int buffer_disabled; int sys_refcount_enter; int sys_refcount_exit; struct trace_event_file *enter_syscall_files[448]; struct trace_event_file *exit_syscall_files[448]; int stop_count; int clock_id; int nr_topts; bool clear_trace; int buffer_percent; unsigned int n_err_log_entries; struct tracer *current_trace; unsigned int trace_flags; unsigned char trace_flags_index[32]; unsigned int flags; raw_spinlock_t start_lock; struct list_head err_log; struct dentry *dir; struct dentry *options; struct dentry *percpu_dir; struct dentry *event_dir; struct trace_options *topts; struct list_head systems; struct list_head events; struct trace_event_file *trace_marker_file; cpumask_var_t tracing_cpumask; int ref; int trace_ref; struct ftrace_ops *ops; struct trace_pid_list *function_pids; struct trace_pid_list *function_no_pids; struct list_head func_probes; struct list_head mod_trace; struct list_head mod_notrace; int function_enabled; int no_filter_buffering_ref; struct list_head hist_vars; struct cond_snapshot *cond_snapshot; struct trace_func_repeats *last_func_repeats; }; struct tracer_flags; struct tracer { const char *name; int (*init)(struct trace_array *); void (*reset)(struct trace_array *); void (*start)(struct trace_array *); void (*stop)(struct trace_array *); int (*update_thresh)(struct trace_array *); void (*open)(struct trace_iterator *); void (*pipe_open)(struct trace_iterator *); void (*close)(struct trace_iterator *); void (*pipe_close)(struct trace_iterator *); ssize_t (*read)(struct trace_iterator *, struct file *, char *, size_t, loff_t *); ssize_t (*splice_read)(struct trace_iterator *, struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); void (*print_header)(struct seq_file *); enum print_line_t (*print_line)(struct trace_iterator *); int (*set_flag)(struct trace_array *, u32, u32, int); int (*flag_changed)(struct trace_array *, u32, int); struct tracer *next; struct tracer_flags *flags; int enabled; bool print_max; bool allow_instances; bool use_max_tr; bool noboot; }; struct event_subsystem; struct trace_subsystem_dir { struct list_head list; struct event_subsystem *subsystem; struct trace_array *tr; struct dentry *entry; int ref_count; int nr_events; }; struct trace_array_cpu { atomic_t disabled; void *buffer_page; long unsigned int entries; long unsigned int saved_latency; long unsigned int critical_start; long unsigned int critical_end; long unsigned int critical_sequence; long unsigned int nice; long unsigned int policy; long unsigned int rt_priority; long unsigned int skipped_entries; u64 preempt_timestamp; pid_t pid; kuid_t uid; char comm[16]; int ftrace_ignore_pid; bool ignore_pid; }; struct trace_option_dentry; struct trace_options { struct tracer *tracer; struct trace_option_dentry *topts; }; struct tracer_opt; struct trace_option_dentry { struct tracer_opt *opt; struct tracer_flags *flags; struct trace_array *tr; struct dentry *entry; }; struct trace_pid_list { int pid_max; long unsigned int *pids; }; enum { TRACE_PIDS = 1, TRACE_NO_PIDS = 2, }; typedef bool (*cond_update_fn_t)(struct trace_array *, void *); struct cond_snapshot { void *cond_data; cond_update_fn_t update; }; struct trace_func_repeats { long unsigned int ip; long unsigned int parent_ip; long unsigned int count; u64 ts_last_call; }; enum { TRACE_ARRAY_FL_GLOBAL = 1, }; struct tracer_opt { const char *name; u32 bit; }; struct tracer_flags { u32 val; struct tracer_opt *opts; struct tracer *trace; }; struct ftrace_mod_load { struct list_head list; char *func; char *module; int enable; }; enum { FTRACE_HASH_FL_MOD = 1, }; struct ftrace_func_command { struct list_head list; char *name; int (*func)(struct trace_array *, struct ftrace_hash *, char *, char *, char *, int); }; struct ftrace_probe_ops { void (*func)(long unsigned int, long unsigned int, struct trace_array *, struct ftrace_probe_ops *, void *); int (*init)(struct ftrace_probe_ops *, struct trace_array *, long unsigned int, void *, void **); void (*free)(struct ftrace_probe_ops *, struct trace_array *, long unsigned int, void *); int (*print)(struct seq_file *, long unsigned int, struct ftrace_probe_ops *, void *); }; typedef int (*ftrace_mapper_func)(void *); struct trace_parser { bool cont; char *buffer; unsigned int idx; unsigned int size; }; enum trace_iterator_bits { TRACE_ITER_PRINT_PARENT_BIT = 0, TRACE_ITER_SYM_OFFSET_BIT = 1, TRACE_ITER_SYM_ADDR_BIT = 2, TRACE_ITER_VERBOSE_BIT = 3, TRACE_ITER_RAW_BIT = 4, TRACE_ITER_HEX_BIT = 5, TRACE_ITER_BIN_BIT = 6, TRACE_ITER_BLOCK_BIT = 7, TRACE_ITER_PRINTK_BIT = 8, TRACE_ITER_ANNOTATE_BIT = 9, TRACE_ITER_USERSTACKTRACE_BIT = 10, TRACE_ITER_SYM_USEROBJ_BIT = 11, TRACE_ITER_PRINTK_MSGONLY_BIT = 12, TRACE_ITER_CONTEXT_INFO_BIT = 13, TRACE_ITER_LATENCY_FMT_BIT = 14, TRACE_ITER_RECORD_CMD_BIT = 15, TRACE_ITER_RECORD_TGID_BIT = 16, TRACE_ITER_OVERWRITE_BIT = 17, TRACE_ITER_STOP_ON_FREE_BIT = 18, TRACE_ITER_IRQ_INFO_BIT = 19, TRACE_ITER_MARKERS_BIT = 20, TRACE_ITER_EVENT_FORK_BIT = 21, TRACE_ITER_PAUSE_ON_TRACE_BIT = 22, TRACE_ITER_HASH_PTR_BIT = 23, TRACE_ITER_FUNCTION_BIT = 24, TRACE_ITER_FUNC_FORK_BIT = 25, TRACE_ITER_DISPLAY_GRAPH_BIT = 26, TRACE_ITER_STACKTRACE_BIT = 27, TRACE_ITER_LAST_BIT = 28, }; struct event_subsystem { struct list_head list; const char *name; struct event_filter *filter; int ref_count; }; enum regex_type { MATCH_FULL = 0, MATCH_FRONT_ONLY = 1, MATCH_MIDDLE_ONLY = 2, MATCH_END_ONLY = 3, MATCH_GLOB = 4, MATCH_INDEX = 5, }; struct tracer_stat { const char *name; void * (*stat_start)(struct tracer_stat *); void * (*stat_next)(void *, int); cmp_func_t stat_cmp; int (*stat_show)(struct seq_file *, void *); void (*stat_release)(void *); int (*stat_headers)(struct seq_file *); }; enum { FTRACE_MODIFY_ENABLE_FL = 1, FTRACE_MODIFY_MAY_SLEEP_FL = 2, }; struct ftrace_profile { struct hlist_node node; long unsigned int ip; long unsigned int counter; long long unsigned int time; long long unsigned int time_squared; }; struct ftrace_profile_page { struct ftrace_profile_page *next; long unsigned int index; struct ftrace_profile records[0]; }; struct ftrace_profile_stat { atomic_t disabled; struct hlist_head *hash; struct ftrace_profile_page *pages; struct ftrace_profile_page *start; struct tracer_stat stat; }; struct ftrace_func_probe { struct ftrace_probe_ops *probe_ops; struct ftrace_ops ops; struct trace_array *tr; struct list_head list; void *data; int ref; }; struct ftrace_page { struct ftrace_page *next; struct dyn_ftrace *records; int index; int order; }; struct ftrace_rec_iter___2 { struct ftrace_page *pg; int index; }; struct ftrace_iterator { loff_t pos; loff_t func_pos; loff_t mod_pos; struct ftrace_page *pg; struct dyn_ftrace *func; struct ftrace_func_probe *probe; struct ftrace_func_entry *probe_entry; struct trace_parser parser; struct ftrace_hash *hash; struct ftrace_ops *ops; struct trace_array *tr; struct list_head *mod_list; int pidx; int idx; unsigned int flags; }; struct ftrace_glob { char *search; unsigned int len; int type; }; struct ftrace_func_map { struct ftrace_func_entry entry; void *data; }; struct ftrace_func_mapper { struct ftrace_hash hash; }; struct ftrace_direct_func { struct list_head next; long unsigned int addr; int count; }; enum graph_filter_type { GRAPH_FILTER_NOTRACE = 0, GRAPH_FILTER_FUNCTION = 1, }; struct ftrace_graph_data { struct ftrace_hash *hash; struct ftrace_func_entry *entry; int idx; enum graph_filter_type type; struct ftrace_hash *new_hash; const struct seq_operations *seq_ops; struct trace_parser parser; }; struct ftrace_mod_func { struct list_head list; char *name; long unsigned int ip; unsigned int size; }; struct ftrace_mod_map { struct callback_head rcu; struct list_head list; struct module *mod; long unsigned int start_addr; long unsigned int end_addr; struct list_head funcs; unsigned int num_funcs; }; struct ftrace_init_func { struct list_head list; long unsigned int ip; }; enum ring_buffer_type { RINGBUF_TYPE_DATA_TYPE_LEN_MAX = 28, RINGBUF_TYPE_PADDING = 29, RINGBUF_TYPE_TIME_EXTEND = 30, RINGBUF_TYPE_TIME_STAMP = 31, }; enum ring_buffer_flags { RB_FL_OVERWRITE = 1, }; struct ring_buffer_per_cpu; struct buffer_page; struct ring_buffer_iter { struct ring_buffer_per_cpu *cpu_buffer; long unsigned int head; long unsigned int next_event; struct buffer_page *head_page; struct buffer_page *cache_reader_page; long unsigned int cache_read; u64 read_stamp; u64 page_stamp; struct ring_buffer_event *event; int missed_events; }; struct rb_irq_work { struct irq_work work; wait_queue_head_t waiters; wait_queue_head_t full_waiters; bool waiters_pending; bool full_waiters_pending; bool wakeup_full; }; struct trace_buffer___2 { unsigned int flags; int cpus; atomic_t record_disabled; cpumask_var_t cpumask; struct lock_class_key *reader_lock_key; struct mutex mutex; struct ring_buffer_per_cpu **buffers; struct hlist_node node; u64 (*clock)(); struct rb_irq_work irq_work; bool time_stamp_abs; }; enum { RB_LEN_TIME_EXTEND = 8, RB_LEN_TIME_STAMP = 8, }; struct buffer_data_page { u64 time_stamp; local_t commit; unsigned char data[0]; }; struct buffer_page { struct list_head list; local_t write; unsigned int read; local_t entries; long unsigned int real_end; struct buffer_data_page *page; }; struct rb_event_info { u64 ts; u64 delta; u64 before; u64 after; long unsigned int length; struct buffer_page *tail_page; int add_timestamp; }; enum { RB_ADD_STAMP_NONE = 0, RB_ADD_STAMP_EXTEND = 2, RB_ADD_STAMP_ABSOLUTE = 4, RB_ADD_STAMP_FORCE = 8, }; enum { RB_CTX_TRANSITION = 0, RB_CTX_NMI = 1, RB_CTX_IRQ = 2, RB_CTX_SOFTIRQ = 3, RB_CTX_NORMAL = 4, RB_CTX_MAX = 5, }; struct rb_time_struct { local64_t time; }; typedef struct rb_time_struct rb_time_t; struct ring_buffer_per_cpu { int cpu; atomic_t record_disabled; atomic_t resize_disabled; struct trace_buffer___2 *buffer; raw_spinlock_t reader_lock; arch_spinlock_t lock; struct lock_class_key lock_key; struct buffer_data_page *free_page; long unsigned int nr_pages; unsigned int current_context; struct list_head *pages; struct buffer_page *head_page; struct buffer_page *tail_page; struct buffer_page *commit_page; struct buffer_page *reader_page; long unsigned int lost_events; long unsigned int last_overrun; long unsigned int nest; local_t entries_bytes; local_t entries; local_t overrun; local_t commit_overrun; local_t dropped_events; local_t committing; local_t commits; local_t pages_touched; local_t pages_read; long int last_pages_touch; size_t shortest_full; long unsigned int read; long unsigned int read_bytes; rb_time_t write_stamp; rb_time_t before_stamp; u64 event_stamp[5]; u64 read_stamp; long int nr_pages_to_update; struct list_head new_pages; struct work_struct update_pages_work; struct completion update_done; struct rb_irq_work irq_work; }; typedef struct vfsmount * (*debugfs_automount_t)(struct dentry *, void *); struct trace_export { struct trace_export *next; void (*write)(struct trace_export *, const void *, unsigned int); int flags; }; enum fsnotify_data_type { FSNOTIFY_EVENT_NONE = 0, FSNOTIFY_EVENT_PATH = 1, FSNOTIFY_EVENT_INODE = 2, }; enum trace_iter_flags { TRACE_FILE_LAT_FMT = 1, TRACE_FILE_ANNOTATE = 2, TRACE_FILE_TIME_IN_NS = 4, }; enum trace_flag_type { TRACE_FLAG_IRQS_OFF = 1, TRACE_FLAG_IRQS_NOSUPPORT = 2, TRACE_FLAG_NEED_RESCHED = 4, TRACE_FLAG_HARDIRQ = 8, TRACE_FLAG_SOFTIRQ = 16, TRACE_FLAG_PREEMPT_RESCHED = 32, TRACE_FLAG_NMI = 64, }; enum trace_type { __TRACE_FIRST_TYPE = 0, TRACE_FN = 1, TRACE_CTX = 2, TRACE_WAKE = 3, TRACE_STACK = 4, TRACE_PRINT = 5, TRACE_BPRINT = 6, TRACE_MMIO_RW = 7, TRACE_MMIO_MAP = 8, TRACE_BRANCH = 9, TRACE_GRAPH_RET = 10, TRACE_GRAPH_ENT = 11, TRACE_USER_STACK = 12, TRACE_BLK = 13, TRACE_BPUTS = 14, TRACE_HWLAT = 15, TRACE_OSNOISE = 16, TRACE_TIMERLAT = 17, TRACE_RAW_DATA = 18, TRACE_FUNC_REPEATS = 19, __TRACE_LAST_TYPE = 20, }; struct ftrace_entry { struct trace_entry ent; long unsigned int ip; long unsigned int parent_ip; }; struct stack_entry { struct trace_entry ent; int size; long unsigned int caller[8]; }; struct userstack_entry { struct trace_entry ent; unsigned int tgid; long unsigned int caller[8]; }; struct bprint_entry { struct trace_entry ent; long unsigned int ip; const char *fmt; u32 buf[0]; }; struct print_entry { struct trace_entry ent; long unsigned int ip; char buf[0]; }; struct raw_data_entry { struct trace_entry ent; unsigned int id; char buf[0]; }; struct bputs_entry { struct trace_entry ent; long unsigned int ip; const char *str; }; struct func_repeats_entry { struct trace_entry ent; long unsigned int ip; long unsigned int parent_ip; u16 count; u16 top_delta_ts; u32 bottom_delta_ts; }; enum trace_iterator_flags { TRACE_ITER_PRINT_PARENT = 1, TRACE_ITER_SYM_OFFSET = 2, TRACE_ITER_SYM_ADDR = 4, TRACE_ITER_VERBOSE = 8, TRACE_ITER_RAW = 16, TRACE_ITER_HEX = 32, TRACE_ITER_BIN = 64, TRACE_ITER_BLOCK = 128, TRACE_ITER_PRINTK = 256, TRACE_ITER_ANNOTATE = 512, TRACE_ITER_USERSTACKTRACE = 1024, TRACE_ITER_SYM_USEROBJ = 2048, TRACE_ITER_PRINTK_MSGONLY = 4096, TRACE_ITER_CONTEXT_INFO = 8192, TRACE_ITER_LATENCY_FMT = 16384, TRACE_ITER_RECORD_CMD = 32768, TRACE_ITER_RECORD_TGID = 65536, TRACE_ITER_OVERWRITE = 131072, TRACE_ITER_STOP_ON_FREE = 262144, TRACE_ITER_IRQ_INFO = 524288, TRACE_ITER_MARKERS = 1048576, TRACE_ITER_EVENT_FORK = 2097152, TRACE_ITER_PAUSE_ON_TRACE = 4194304, TRACE_ITER_HASH_PTR = 8388608, TRACE_ITER_FUNCTION = 16777216, TRACE_ITER_FUNC_FORK = 33554432, TRACE_ITER_DISPLAY_GRAPH = 67108864, TRACE_ITER_STACKTRACE = 134217728, }; struct trace_min_max_param { struct mutex *lock; u64 *val; u64 *min; u64 *max; }; struct saved_cmdlines_buffer { unsigned int map_pid_to_cmdline[32769]; unsigned int *map_cmdline_to_pid; unsigned int cmdline_num; int cmdline_idx; char *saved_cmdlines; }; struct ftrace_stack { long unsigned int calls[1024]; }; struct ftrace_stacks { struct ftrace_stack stacks[4]; }; struct trace_buffer_struct { int nesting; char buffer[4096]; }; struct ftrace_buffer_info { struct trace_iterator iter; void *spare; unsigned int spare_cpu; unsigned int read; }; struct err_info { const char **errs; u8 type; u8 pos; u64 ts; }; struct tracing_log_err { struct list_head list; struct err_info info; char loc[128]; char cmd[256]; }; struct buffer_ref { struct trace_buffer *buffer; void *page; int cpu; refcount_t refcount; }; struct ftrace_func_mapper___2; struct ctx_switch_entry { struct trace_entry ent; unsigned int prev_pid; unsigned int next_pid; unsigned int next_cpu; unsigned char prev_prio; unsigned char prev_state; unsigned char next_prio; unsigned char next_state; }; struct hwlat_entry { struct trace_entry ent; u64 duration; u64 outer_duration; u64 nmi_total_ts; struct timespec64 timestamp; unsigned int nmi_count; unsigned int seqnum; unsigned int count; }; struct osnoise_entry { struct trace_entry ent; u64 noise; u64 runtime; u64 max_sample; unsigned int hw_count; unsigned int nmi_count; unsigned int irq_count; unsigned int softirq_count; unsigned int thread_count; }; struct timerlat_entry { struct trace_entry ent; unsigned int seqnum; int context; u64 timer_latency; }; struct trace_mark { long long unsigned int val; char sym; }; struct stat_node { struct rb_node node; void *stat; }; struct stat_session { struct list_head session_list; struct tracer_stat *ts; struct rb_root stat_root; struct mutex stat_mutex; struct dentry *file; }; struct trace_bprintk_fmt { struct list_head list; const char *fmt; }; typedef int (*tracing_map_cmp_fn_t)(void *, void *); struct tracing_map_field { tracing_map_cmp_fn_t cmp_fn; union { atomic64_t sum; unsigned int offset; }; }; struct tracing_map; struct tracing_map_elt { struct tracing_map *map; struct tracing_map_field *fields; atomic64_t *vars; bool *var_set; void *key; void *private_data; }; struct tracing_map_sort_key { unsigned int field_idx; bool descending; }; struct tracing_map_array; struct tracing_map_ops; struct tracing_map { unsigned int key_size; unsigned int map_bits; unsigned int map_size; unsigned int max_elts; atomic_t next_elt; struct tracing_map_array *elts; struct tracing_map_array *map; const struct tracing_map_ops *ops; void *private_data; struct tracing_map_field fields[6]; unsigned int n_fields; int key_idx[3]; unsigned int n_keys; struct tracing_map_sort_key sort_key; unsigned int n_vars; atomic64_t hits; atomic64_t drops; }; struct tracing_map_entry { u32 key; struct tracing_map_elt *val; }; struct tracing_map_sort_entry { void *key; struct tracing_map_elt *elt; bool elt_copied; bool dup; }; struct tracing_map_array { unsigned int entries_per_page; unsigned int entry_size_shift; unsigned int entry_shift; unsigned int entry_mask; unsigned int n_pages; void **pages; }; struct tracing_map_ops { int (*elt_alloc)(struct tracing_map_elt *); void (*elt_free)(struct tracing_map_elt *); void (*elt_clear)(struct tracing_map_elt *); void (*elt_init)(struct tracing_map_elt *); }; enum { TRACE_FUNC_NO_OPTS = 0, TRACE_FUNC_OPT_STACK = 1, TRACE_FUNC_OPT_NO_REPEATS = 2, TRACE_FUNC_OPT_HIGHEST_BIT = 4, }; enum { MODE_NONE = 0, MODE_ROUND_ROBIN = 1, MODE_PER_CPU = 2, MODE_MAX = 3, }; struct hwlat_kthread_data { struct task_struct *kthread; u64 nmi_ts_start; u64 nmi_total_ts; int nmi_count; int nmi_cpu; }; struct hwlat_sample { u64 seqnum; u64 duration; u64 outer_duration; u64 nmi_total_ts; struct timespec64 timestamp; int nmi_count; int count; }; struct hwlat_data { struct mutex lock; u64 count; u64 sample_window; u64 sample_width; int thread_mode; }; struct trace_event_raw_thread_noise { struct trace_entry ent; char comm[16]; u64 start; u64 duration; pid_t pid; char __data[0]; }; struct trace_event_raw_softirq_noise { struct trace_entry ent; u64 start; u64 duration; int vector; char __data[0]; }; struct trace_event_raw_irq_noise { struct trace_entry ent; u64 start; u64 duration; u32 __data_loc_desc; int vector; char __data[0]; }; struct trace_event_raw_nmi_noise { struct trace_entry ent; u64 start; u64 duration; char __data[0]; }; struct trace_event_raw_sample_threshold { struct trace_entry ent; u64 start; u64 duration; u64 interference; char __data[0]; }; struct trace_event_data_offsets_thread_noise {}; struct trace_event_data_offsets_softirq_noise {}; struct trace_event_data_offsets_irq_noise { u32 desc; }; struct trace_event_data_offsets_nmi_noise {}; struct trace_event_data_offsets_sample_threshold {}; typedef void (*btf_trace_thread_noise)(void *, struct task_struct *, u64, u64); typedef void (*btf_trace_softirq_noise)(void *, int, u64, u64); typedef void (*btf_trace_irq_noise)(void *, int, const char *, u64, u64); typedef void (*btf_trace_nmi_noise)(void *, u64, u64); typedef void (*btf_trace_sample_threshold)(void *, u64, u64, u64); struct osn_nmi { u64 count; u64 delta_start; }; struct osn_irq { u64 count; u64 arrival_time; u64 delta_start; }; struct osn_softirq { u64 count; u64 arrival_time; u64 delta_start; }; struct osn_thread { u64 count; u64 arrival_time; u64 delta_start; }; struct osnoise_variables { struct task_struct *kthread; bool sampling; pid_t pid; struct osn_nmi nmi; struct osn_irq irq; struct osn_softirq softirq; struct osn_thread thread; local_t int_counter; }; struct timerlat_variables { struct task_struct *kthread; struct hrtimer timer; u64 rel_period; u64 abs_period; bool tracing_thread; u64 count; }; struct osnoise_sample { u64 runtime; u64 noise; u64 max_sample; int hw_count; int nmi_count; int irq_count; int softirq_count; int thread_count; }; struct timerlat_sample { u64 timer_latency; unsigned int seqnum; int context; }; struct osnoise_data { u64 sample_period; u64 sample_runtime; u64 stop_tracing; u64 stop_tracing_total; u64 timerlat_period; u64 print_stack; int timerlat_tracer; bool tainted; }; struct trace_stack { int stack_size; int nr_entries; long unsigned int calls[256]; }; enum { TRACE_NOP_OPT_ACCEPT = 1, TRACE_NOP_OPT_REFUSE = 2, }; struct trace_mmiotrace_rw { struct trace_entry ent; struct mmiotrace_rw rw; }; struct trace_mmiotrace_map { struct trace_entry ent; struct mmiotrace_map map; }; struct header_iter { struct pci_dev *dev; }; struct ftrace_graph_ent_entry { struct trace_entry ent; struct ftrace_graph_ent graph_ent; } __attribute__((packed)); struct ftrace_graph_ret_entry { struct trace_entry ent; struct ftrace_graph_ret ret; }; struct fgraph_cpu_data { pid_t last_pid; int depth; int depth_irq; int ignore; long unsigned int enter_funcs[50]; }; struct fgraph_data { struct fgraph_cpu_data *cpu_data; struct ftrace_graph_ent_entry ent; struct ftrace_graph_ret_entry ret; int failed; int cpu; int: 32; } __attribute__((packed)); enum { FLAGS_FILL_FULL = 268435456, FLAGS_FILL_START = 536870912, FLAGS_FILL_END = 805306368, }; struct disk_stats { u64 nsecs[4]; long unsigned int sectors[4]; long unsigned int ios[4]; long unsigned int merges[4]; long unsigned int io_ticks; local_t in_flight[2]; }; struct blk_crypto_key; struct bio_crypt_ctx { const struct blk_crypto_key *bc_key; u64 bc_dun[4]; }; typedef __u32 blk_mq_req_flags_t; struct blk_mq_ctxs; struct blk_mq_ctx { struct { spinlock_t lock; struct list_head rq_lists[3]; long: 64; }; unsigned int cpu; short unsigned int index_hw[3]; struct blk_mq_hw_ctx *hctxs[3]; long unsigned int rq_dispatched[2]; long unsigned int rq_merged; long unsigned int rq_completed[2]; struct request_queue *queue; struct blk_mq_ctxs *ctxs; struct kobject kobj; long: 64; long: 64; long: 64; long: 64; }; struct blk_mq_tags; struct blk_mq_hw_ctx { struct { spinlock_t lock; struct list_head dispatch; long unsigned int state; long: 64; long: 64; long: 64; long: 64; }; struct delayed_work run_work; cpumask_var_t cpumask; int next_cpu; int next_cpu_batch; long unsigned int flags; void *sched_data; struct request_queue *queue; struct blk_flush_queue *fq; void *driver_data; struct sbitmap ctx_map; struct blk_mq_ctx *dispatch_from; unsigned int dispatch_busy; short unsigned int type; short unsigned int nr_ctx; struct blk_mq_ctx **ctxs; spinlock_t dispatch_wait_lock; wait_queue_entry_t dispatch_wait; atomic_t wait_index; struct blk_mq_tags *tags; struct blk_mq_tags *sched_tags; long unsigned int queued; long unsigned int run; long unsigned int dispatched[7]; unsigned int numa_node; unsigned int queue_num; atomic_t nr_active; struct hlist_node cpuhp_online; struct hlist_node cpuhp_dead; struct kobject kobj; long unsigned int poll_considered; long unsigned int poll_invoked; long unsigned int poll_success; struct dentry *debugfs_dir; struct dentry *sched_debugfs_dir; struct list_head hctx_list; struct srcu_struct srcu[0]; long: 64; long: 64; long: 64; long: 64; }; struct blk_mq_alloc_data { struct request_queue *q; blk_mq_req_flags_t flags; unsigned int shallow_depth; unsigned int cmd_flags; struct blk_mq_ctx *ctx; struct blk_mq_hw_ctx *hctx; }; struct blk_stat_callback { struct list_head list; struct timer_list timer; struct blk_rq_stat *cpu_stat; int (*bucket_fn)(const struct request *); unsigned int buckets; struct blk_rq_stat *stat; void (*timer_fn)(struct blk_stat_callback *); void *data; struct callback_head rcu; }; struct blk_trace { int trace_state; struct rchan *rchan; long unsigned int *sequence; unsigned char *msg_data; u16 act_mask; u64 start_lba; u64 end_lba; u32 pid; u32 dev; struct dentry *dir; struct list_head running_list; atomic_t dropped; }; struct blk_flush_queue { unsigned int flush_pending_idx: 1; unsigned int flush_running_idx: 1; blk_status_t rq_status; long unsigned int flush_pending_since; struct list_head flush_queue[2]; struct list_head flush_data_in_flight; struct request *flush_rq; spinlock_t mq_flush_lock; }; struct blk_mq_queue_map { unsigned int *mq_map; unsigned int nr_queues; unsigned int queue_offset; }; struct blk_mq_tag_set { struct blk_mq_queue_map map[3]; unsigned int nr_maps; const struct blk_mq_ops *ops; unsigned int nr_hw_queues; unsigned int queue_depth; unsigned int reserved_tags; unsigned int cmd_size; int numa_node; unsigned int timeout; unsigned int flags; void *driver_data; atomic_t active_queues_shared_sbitmap; struct sbitmap_queue __bitmap_tags; struct sbitmap_queue __breserved_tags; struct blk_mq_tags **tags; struct mutex tag_list_lock; struct list_head tag_list; }; typedef u64 compat_u64; enum blktrace_cat { BLK_TC_READ = 1, BLK_TC_WRITE = 2, BLK_TC_FLUSH = 4, BLK_TC_SYNC = 8, BLK_TC_SYNCIO = 8, BLK_TC_QUEUE = 16, BLK_TC_REQUEUE = 32, BLK_TC_ISSUE = 64, BLK_TC_COMPLETE = 128, BLK_TC_FS = 256, BLK_TC_PC = 512, BLK_TC_NOTIFY = 1024, BLK_TC_AHEAD = 2048, BLK_TC_META = 4096, BLK_TC_DISCARD = 8192, BLK_TC_DRV_DATA = 16384, BLK_TC_FUA = 32768, BLK_TC_END = 32768, }; enum blktrace_act { __BLK_TA_QUEUE = 1, __BLK_TA_BACKMERGE = 2, __BLK_TA_FRONTMERGE = 3, __BLK_TA_GETRQ = 4, __BLK_TA_SLEEPRQ = 5, __BLK_TA_REQUEUE = 6, __BLK_TA_ISSUE = 7, __BLK_TA_COMPLETE = 8, __BLK_TA_PLUG = 9, __BLK_TA_UNPLUG_IO = 10, __BLK_TA_UNPLUG_TIMER = 11, __BLK_TA_INSERT = 12, __BLK_TA_SPLIT = 13, __BLK_TA_BOUNCE = 14, __BLK_TA_REMAP = 15, __BLK_TA_ABORT = 16, __BLK_TA_DRV_DATA = 17, __BLK_TA_CGROUP = 256, }; enum blktrace_notify { __BLK_TN_PROCESS = 0, __BLK_TN_TIMESTAMP = 1, __BLK_TN_MESSAGE = 2, __BLK_TN_CGROUP = 256, }; struct blk_io_trace { __u32 magic; __u32 sequence; __u64 time; __u64 sector; __u32 bytes; __u32 action; __u32 pid; __u32 device; __u32 cpu; __u16 error; __u16 pdu_len; }; struct blk_io_trace_remap { __be32 device_from; __be32 device_to; __be64 sector_from; }; enum { Blktrace_setup = 1, Blktrace_running = 2, Blktrace_stopped = 3, }; struct blk_user_trace_setup { char name[32]; __u16 act_mask; __u32 buf_size; __u32 buf_nr; __u64 start_lba; __u64 end_lba; __u32 pid; }; struct compat_blk_user_trace_setup { char name[32]; u16 act_mask; short: 16; u32 buf_size; u32 buf_nr; compat_u64 start_lba; compat_u64 end_lba; u32 pid; } __attribute__((packed)); struct blk_mq_tags { unsigned int nr_tags; unsigned int nr_reserved_tags; atomic_t active_queues; struct sbitmap_queue *bitmap_tags; struct sbitmap_queue *breserved_tags; struct sbitmap_queue __bitmap_tags; struct sbitmap_queue __breserved_tags; struct request **rqs; struct request **static_rqs; struct list_head page_list; spinlock_t lock; }; struct blk_mq_queue_data { struct request *rq; bool last; }; enum blk_crypto_mode_num { BLK_ENCRYPTION_MODE_INVALID = 0, BLK_ENCRYPTION_MODE_AES_256_XTS = 1, BLK_ENCRYPTION_MODE_AES_128_CBC_ESSIV = 2, BLK_ENCRYPTION_MODE_ADIANTUM = 3, BLK_ENCRYPTION_MODE_MAX = 4, }; struct blk_crypto_config { enum blk_crypto_mode_num crypto_mode; unsigned int data_unit_size; unsigned int dun_bytes; }; struct blk_crypto_key { struct blk_crypto_config crypto_cfg; unsigned int data_unit_size_bits; unsigned int size; u8 raw[64]; }; struct blk_mq_ctxs { struct kobject kobj; struct blk_mq_ctx *queue_ctx; }; typedef void blk_log_action_t(struct trace_iterator *, const char *, bool); struct ftrace_event_field { struct list_head link; const char *name; const char *type; int filter_type; int offset; int size; int is_signed; }; enum { FORMAT_HEADER = 1, FORMAT_FIELD_SEPERATOR = 2, FORMAT_PRINTFMT = 3, }; struct event_probe_data { struct trace_event_file *file; long unsigned int count; int ref; bool enable; }; struct syscall_trace_enter { struct trace_entry ent; int nr; long unsigned int args[0]; }; struct syscall_trace_exit { struct trace_entry ent; int nr; long int ret; }; struct syscall_tp_t { long long unsigned int regs; long unsigned int syscall_nr; long unsigned int ret; }; struct syscall_tp_t___2 { long long unsigned int regs; long unsigned int syscall_nr; long unsigned int args[6]; }; typedef long unsigned int perf_trace_t[256]; struct filter_pred; struct prog_entry { int target; int when_to_branch; struct filter_pred *pred; }; typedef int (*filter_pred_fn_t)(struct filter_pred *, void *); struct regex; typedef int (*regex_match_func)(char *, struct regex *, int); struct regex { char pattern[256]; int len; int field_len; regex_match_func match; }; struct filter_pred { filter_pred_fn_t fn; u64 val; struct regex regex; short unsigned int *ops; struct ftrace_event_field *field; int offset; int not; int op; }; enum filter_op_ids { OP_GLOB = 0, OP_NE = 1, OP_EQ = 2, OP_LE = 3, OP_LT = 4, OP_GE = 5, OP_GT = 6, OP_BAND = 7, OP_MAX = 8, }; enum { FILT_ERR_NONE = 0, FILT_ERR_INVALID_OP = 1, FILT_ERR_TOO_MANY_OPEN = 2, FILT_ERR_TOO_MANY_CLOSE = 3, FILT_ERR_MISSING_QUOTE = 4, FILT_ERR_OPERAND_TOO_LONG = 5, FILT_ERR_EXPECT_STRING = 6, FILT_ERR_EXPECT_DIGIT = 7, FILT_ERR_ILLEGAL_FIELD_OP = 8, FILT_ERR_FIELD_NOT_FOUND = 9, FILT_ERR_ILLEGAL_INTVAL = 10, FILT_ERR_BAD_SUBSYS_FILTER = 11, FILT_ERR_TOO_MANY_PREDS = 12, FILT_ERR_INVALID_FILTER = 13, FILT_ERR_IP_FIELD_ONLY = 14, FILT_ERR_INVALID_VALUE = 15, FILT_ERR_ERRNO = 16, FILT_ERR_NO_FILTER = 17, }; struct filter_parse_error { int lasterr; int lasterr_pos; }; typedef int (*parse_pred_fn)(const char *, void *, int, struct filter_parse_error *, struct filter_pred **); enum { INVERT = 1, PROCESS_AND = 2, PROCESS_OR = 4, }; enum { TOO_MANY_CLOSE = 4294967295, TOO_MANY_OPEN = 4294967294, MISSING_QUOTE = 4294967293, }; struct filter_list { struct list_head list; struct event_filter *filter; }; struct function_filter_data { struct ftrace_ops *ops; int first_filter; int first_notrace; }; struct event_trigger_ops; struct event_command; struct event_trigger_data { long unsigned int count; int ref; struct event_trigger_ops *ops; struct event_command *cmd_ops; struct event_filter *filter; char *filter_str; void *private_data; bool paused; bool paused_tmp; struct list_head list; char *name; struct list_head named_list; struct event_trigger_data *named_data; }; struct event_trigger_ops { void (*func)(struct event_trigger_data *, struct trace_buffer *, void *, struct ring_buffer_event *); int (*init)(struct event_trigger_ops *, struct event_trigger_data *); void (*free)(struct event_trigger_ops *, struct event_trigger_data *); int (*print)(struct seq_file *, struct event_trigger_ops *, struct event_trigger_data *); }; struct event_command { struct list_head list; char *name; enum event_trigger_type trigger_type; int flags; int (*func)(struct event_command *, struct trace_event_file *, char *, char *, char *); int (*reg)(char *, struct event_trigger_ops *, struct event_trigger_data *, struct trace_event_file *); void (*unreg)(char *, struct event_trigger_ops *, struct event_trigger_data *, struct trace_event_file *); void (*unreg_all)(struct trace_event_file *); int (*set_filter)(char *, struct event_trigger_data *, struct trace_event_file *); struct event_trigger_ops * (*get_trigger_ops)(char *, char *); }; struct enable_trigger_data { struct trace_event_file *file; bool enable; bool hist; }; enum event_command_flags { EVENT_CMD_FL_POST_TRIGGER = 1, EVENT_CMD_FL_NEEDS_REC = 2, }; enum dynevent_type { DYNEVENT_TYPE_SYNTH = 1, DYNEVENT_TYPE_KPROBE = 2, DYNEVENT_TYPE_NONE = 3, }; struct dynevent_cmd; typedef int (*dynevent_create_fn_t)(struct dynevent_cmd *); struct dynevent_cmd { struct seq_buf seq; const char *event_name; unsigned int n_fields; enum dynevent_type type; dynevent_create_fn_t run_command; void *private_data; }; struct synth_field_desc { const char *type; const char *name; }; struct synth_trace_event; struct synth_event; struct synth_event_trace_state { struct trace_event_buffer fbuffer; struct synth_trace_event *entry; struct trace_buffer *buffer; struct synth_event *event; unsigned int cur_field; unsigned int n_u64; bool disabled; bool add_next; bool add_name; }; struct synth_trace_event { struct trace_entry ent; u64 fields[0]; }; struct dyn_event_operations; struct dyn_event { struct list_head list; struct dyn_event_operations *ops; }; struct synth_field; struct synth_event { struct dyn_event devent; int ref; char *name; struct synth_field **fields; unsigned int n_fields; struct synth_field **dynamic_fields; unsigned int n_dynamic_fields; unsigned int n_u64; struct trace_event_class class; struct trace_event_call call; struct tracepoint *tp; struct module *mod; }; struct dyn_event_operations { struct list_head list; int (*create)(const char *); int (*show)(struct seq_file *, struct dyn_event *); bool (*is_busy)(struct dyn_event *); int (*free)(struct dyn_event *); bool (*match)(const char *, const char *, int, const char **, struct dyn_event *); }; typedef int (*dynevent_check_arg_fn_t)(void *); struct dynevent_arg { const char *str; char separator; }; struct dynevent_arg_pair { const char *lhs; const char *rhs; char operator; char separator; }; struct synth_field { char *type; char *name; size_t size; unsigned int offset; unsigned int field_pos; bool is_signed; bool is_string; bool is_dynamic; }; enum { SYNTH_ERR_BAD_NAME = 0, SYNTH_ERR_INVALID_CMD = 1, SYNTH_ERR_INVALID_DYN_CMD = 2, SYNTH_ERR_EVENT_EXISTS = 3, SYNTH_ERR_TOO_MANY_FIELDS = 4, SYNTH_ERR_INCOMPLETE_TYPE = 5, SYNTH_ERR_INVALID_TYPE = 6, SYNTH_ERR_INVALID_FIELD = 7, SYNTH_ERR_INVALID_ARRAY_SPEC = 8, }; enum { HIST_ERR_NONE = 0, HIST_ERR_DUPLICATE_VAR = 1, HIST_ERR_VAR_NOT_UNIQUE = 2, HIST_ERR_TOO_MANY_VARS = 3, HIST_ERR_MALFORMED_ASSIGNMENT = 4, HIST_ERR_NAMED_MISMATCH = 5, HIST_ERR_TRIGGER_EEXIST = 6, HIST_ERR_TRIGGER_ENOENT_CLEAR = 7, HIST_ERR_SET_CLOCK_FAIL = 8, HIST_ERR_BAD_FIELD_MODIFIER = 9, HIST_ERR_TOO_MANY_SUBEXPR = 10, HIST_ERR_TIMESTAMP_MISMATCH = 11, HIST_ERR_TOO_MANY_FIELD_VARS = 12, HIST_ERR_EVENT_FILE_NOT_FOUND = 13, HIST_ERR_HIST_NOT_FOUND = 14, HIST_ERR_HIST_CREATE_FAIL = 15, HIST_ERR_SYNTH_VAR_NOT_FOUND = 16, HIST_ERR_SYNTH_EVENT_NOT_FOUND = 17, HIST_ERR_SYNTH_TYPE_MISMATCH = 18, HIST_ERR_SYNTH_COUNT_MISMATCH = 19, HIST_ERR_FIELD_VAR_PARSE_FAIL = 20, HIST_ERR_VAR_CREATE_FIND_FAIL = 21, HIST_ERR_ONX_NOT_VAR = 22, HIST_ERR_ONX_VAR_NOT_FOUND = 23, HIST_ERR_ONX_VAR_CREATE_FAIL = 24, HIST_ERR_FIELD_VAR_CREATE_FAIL = 25, HIST_ERR_TOO_MANY_PARAMS = 26, HIST_ERR_PARAM_NOT_FOUND = 27, HIST_ERR_INVALID_PARAM = 28, HIST_ERR_ACTION_NOT_FOUND = 29, HIST_ERR_NO_SAVE_PARAMS = 30, HIST_ERR_TOO_MANY_SAVE_ACTIONS = 31, HIST_ERR_ACTION_MISMATCH = 32, HIST_ERR_NO_CLOSING_PAREN = 33, HIST_ERR_SUBSYS_NOT_FOUND = 34, HIST_ERR_INVALID_SUBSYS_EVENT = 35, HIST_ERR_INVALID_REF_KEY = 36, HIST_ERR_VAR_NOT_FOUND = 37, HIST_ERR_FIELD_NOT_FOUND = 38, HIST_ERR_EMPTY_ASSIGNMENT = 39, HIST_ERR_INVALID_SORT_MODIFIER = 40, HIST_ERR_EMPTY_SORT_FIELD = 41, HIST_ERR_TOO_MANY_SORT_FIELDS = 42, HIST_ERR_INVALID_SORT_FIELD = 43, HIST_ERR_INVALID_STR_OPERAND = 44, }; struct hist_field; typedef u64 (*hist_field_fn_t)(struct hist_field *, struct tracing_map_elt *, struct trace_buffer *, struct ring_buffer_event *, void *); struct hist_trigger_data; struct hist_var { char *name; struct hist_trigger_data *hist_data; unsigned int idx; }; enum field_op_id { FIELD_OP_NONE = 0, FIELD_OP_PLUS = 1, FIELD_OP_MINUS = 2, FIELD_OP_UNARY_MINUS = 3, }; struct hist_field { struct ftrace_event_field *field; long unsigned int flags; hist_field_fn_t fn; unsigned int ref; unsigned int size; unsigned int offset; unsigned int is_signed; const char *type; struct hist_field *operands[2]; struct hist_trigger_data *hist_data; struct hist_var var; enum field_op_id operator; char *system; char *event_name; char *name; unsigned int var_ref_idx; bool read_once; unsigned int var_str_idx; }; struct hist_trigger_attrs; struct action_data; struct field_var; struct field_var_hist; struct hist_trigger_data { struct hist_field *fields[22]; unsigned int n_vals; unsigned int n_keys; unsigned int n_fields; unsigned int n_vars; unsigned int n_var_str; unsigned int key_size; struct tracing_map_sort_key sort_keys[2]; unsigned int n_sort_keys; struct trace_event_file *event_file; struct hist_trigger_attrs *attrs; struct tracing_map *map; bool enable_timestamps; bool remove; struct hist_field *var_refs[16]; unsigned int n_var_refs; struct action_data *actions[8]; unsigned int n_actions; struct field_var *field_vars[32]; unsigned int n_field_vars; unsigned int n_field_var_str; struct field_var_hist *field_var_hists[32]; unsigned int n_field_var_hists; struct field_var *save_vars[32]; unsigned int n_save_vars; unsigned int n_save_var_str; }; enum hist_field_flags { HIST_FIELD_FL_HITCOUNT = 1, HIST_FIELD_FL_KEY = 2, HIST_FIELD_FL_STRING = 4, HIST_FIELD_FL_HEX = 8, HIST_FIELD_FL_SYM = 16, HIST_FIELD_FL_SYM_OFFSET = 32, HIST_FIELD_FL_EXECNAME = 64, HIST_FIELD_FL_SYSCALL = 128, HIST_FIELD_FL_STACKTRACE = 256, HIST_FIELD_FL_LOG2 = 512, HIST_FIELD_FL_TIMESTAMP = 1024, HIST_FIELD_FL_TIMESTAMP_USECS = 2048, HIST_FIELD_FL_VAR = 4096, HIST_FIELD_FL_EXPR = 8192, HIST_FIELD_FL_VAR_REF = 16384, HIST_FIELD_FL_CPU = 32768, HIST_FIELD_FL_ALIAS = 65536, }; struct var_defs { unsigned int n_vars; char *name[16]; char *expr[16]; }; struct hist_trigger_attrs { char *keys_str; char *vals_str; char *sort_key_str; char *name; char *clock; bool pause; bool cont; bool clear; bool ts_in_usecs; unsigned int map_bits; char *assignment_str[16]; unsigned int n_assignments; char *action_str[8]; unsigned int n_actions; struct var_defs var_defs; }; struct field_var { struct hist_field *var; struct hist_field *val; }; struct field_var_hist { struct hist_trigger_data *hist_data; char *cmd; }; enum handler_id { HANDLER_ONMATCH = 1, HANDLER_ONMAX = 2, HANDLER_ONCHANGE = 3, }; enum action_id { ACTION_SAVE = 1, ACTION_TRACE = 2, ACTION_SNAPSHOT = 3, }; typedef void (*action_fn_t)(struct hist_trigger_data *, struct tracing_map_elt *, struct trace_buffer *, void *, struct ring_buffer_event *, void *, struct action_data *, u64 *); typedef bool (*check_track_val_fn_t)(u64, u64); struct action_data { enum handler_id handler; enum action_id action; char *action_name; action_fn_t fn; unsigned int n_params; char *params[32]; unsigned int var_ref_idx[16]; struct synth_event *synth_event; bool use_trace_keyword; char *synth_event_name; union { struct { char *event; char *event_system; } match_data; struct { char *var_str; struct hist_field *var_ref; struct hist_field *track_var; check_track_val_fn_t check_val; action_fn_t save_data; } track_data; }; }; struct track_data { u64 track_val; bool updated; unsigned int key_len; void *key; struct tracing_map_elt elt; struct action_data *action_data; struct hist_trigger_data *hist_data; }; struct hist_elt_data { char *comm; u64 *var_ref_vals; char *field_var_str[32]; }; struct snapshot_context { struct tracing_map_elt *elt; void *key; }; typedef void (*synth_probe_func_t)(void *, u64 *, unsigned int *); struct hist_var_data { struct list_head list; struct hist_trigger_data *hist_data; }; enum bpf_func_id { BPF_FUNC_unspec = 0, BPF_FUNC_map_lookup_elem = 1, BPF_FUNC_map_update_elem = 2, BPF_FUNC_map_delete_elem = 3, BPF_FUNC_probe_read = 4, BPF_FUNC_ktime_get_ns = 5, BPF_FUNC_trace_printk = 6, BPF_FUNC_get_prandom_u32 = 7, BPF_FUNC_get_smp_processor_id = 8, BPF_FUNC_skb_store_bytes = 9, BPF_FUNC_l3_csum_replace = 10, BPF_FUNC_l4_csum_replace = 11, BPF_FUNC_tail_call = 12, BPF_FUNC_clone_redirect = 13, BPF_FUNC_get_current_pid_tgid = 14, BPF_FUNC_get_current_uid_gid = 15, BPF_FUNC_get_current_comm = 16, BPF_FUNC_get_cgroup_classid = 17, BPF_FUNC_skb_vlan_push = 18, BPF_FUNC_skb_vlan_pop = 19, BPF_FUNC_skb_get_tunnel_key = 20, BPF_FUNC_skb_set_tunnel_key = 21, BPF_FUNC_perf_event_read = 22, BPF_FUNC_redirect = 23, BPF_FUNC_get_route_realm = 24, BPF_FUNC_perf_event_output = 25, BPF_FUNC_skb_load_bytes = 26, BPF_FUNC_get_stackid = 27, BPF_FUNC_csum_diff = 28, BPF_FUNC_skb_get_tunnel_opt = 29, BPF_FUNC_skb_set_tunnel_opt = 30, BPF_FUNC_skb_change_proto = 31, BPF_FUNC_skb_change_type = 32, BPF_FUNC_skb_under_cgroup = 33, BPF_FUNC_get_hash_recalc = 34, BPF_FUNC_get_current_task = 35, BPF_FUNC_probe_write_user = 36, BPF_FUNC_current_task_under_cgroup = 37, BPF_FUNC_skb_change_tail = 38, BPF_FUNC_skb_pull_data = 39, BPF_FUNC_csum_update = 40, BPF_FUNC_set_hash_invalid = 41, BPF_FUNC_get_numa_node_id = 42, BPF_FUNC_skb_change_head = 43, BPF_FUNC_xdp_adjust_head = 44, BPF_FUNC_probe_read_str = 45, BPF_FUNC_get_socket_cookie = 46, BPF_FUNC_get_socket_uid = 47, BPF_FUNC_set_hash = 48, BPF_FUNC_setsockopt = 49, BPF_FUNC_skb_adjust_room = 50, BPF_FUNC_redirect_map = 51, BPF_FUNC_sk_redirect_map = 52, BPF_FUNC_sock_map_update = 53, BPF_FUNC_xdp_adjust_meta = 54, BPF_FUNC_perf_event_read_value = 55, BPF_FUNC_perf_prog_read_value = 56, BPF_FUNC_getsockopt = 57, BPF_FUNC_override_return = 58, BPF_FUNC_sock_ops_cb_flags_set = 59, BPF_FUNC_msg_redirect_map = 60, BPF_FUNC_msg_apply_bytes = 61, BPF_FUNC_msg_cork_bytes = 62, BPF_FUNC_msg_pull_data = 63, BPF_FUNC_bind = 64, BPF_FUNC_xdp_adjust_tail = 65, BPF_FUNC_skb_get_xfrm_state = 66, BPF_FUNC_get_stack = 67, BPF_FUNC_skb_load_bytes_relative = 68, BPF_FUNC_fib_lookup = 69, BPF_FUNC_sock_hash_update = 70, BPF_FUNC_msg_redirect_hash = 71, BPF_FUNC_sk_redirect_hash = 72, BPF_FUNC_lwt_push_encap = 73, BPF_FUNC_lwt_seg6_store_bytes = 74, BPF_FUNC_lwt_seg6_adjust_srh = 75, BPF_FUNC_lwt_seg6_action = 76, BPF_FUNC_rc_repeat = 77, BPF_FUNC_rc_keydown = 78, BPF_FUNC_skb_cgroup_id = 79, BPF_FUNC_get_current_cgroup_id = 80, BPF_FUNC_get_local_storage = 81, BPF_FUNC_sk_select_reuseport = 82, BPF_FUNC_skb_ancestor_cgroup_id = 83, BPF_FUNC_sk_lookup_tcp = 84, BPF_FUNC_sk_lookup_udp = 85, BPF_FUNC_sk_release = 86, BPF_FUNC_map_push_elem = 87, BPF_FUNC_map_pop_elem = 88, BPF_FUNC_map_peek_elem = 89, BPF_FUNC_msg_push_data = 90, BPF_FUNC_msg_pop_data = 91, BPF_FUNC_rc_pointer_rel = 92, BPF_FUNC_spin_lock = 93, BPF_FUNC_spin_unlock = 94, BPF_FUNC_sk_fullsock = 95, BPF_FUNC_tcp_sock = 96, BPF_FUNC_skb_ecn_set_ce = 97, BPF_FUNC_get_listener_sock = 98, BPF_FUNC_skc_lookup_tcp = 99, BPF_FUNC_tcp_check_syncookie = 100, BPF_FUNC_sysctl_get_name = 101, BPF_FUNC_sysctl_get_current_value = 102, BPF_FUNC_sysctl_get_new_value = 103, BPF_FUNC_sysctl_set_new_value = 104, BPF_FUNC_strtol = 105, BPF_FUNC_strtoul = 106, BPF_FUNC_sk_storage_get = 107, BPF_FUNC_sk_storage_delete = 108, BPF_FUNC_send_signal = 109, BPF_FUNC_tcp_gen_syncookie = 110, BPF_FUNC_skb_output = 111, BPF_FUNC_probe_read_user = 112, BPF_FUNC_probe_read_kernel = 113, BPF_FUNC_probe_read_user_str = 114, BPF_FUNC_probe_read_kernel_str = 115, BPF_FUNC_tcp_send_ack = 116, BPF_FUNC_send_signal_thread = 117, BPF_FUNC_jiffies64 = 118, BPF_FUNC_read_branch_records = 119, BPF_FUNC_get_ns_current_pid_tgid = 120, BPF_FUNC_xdp_output = 121, BPF_FUNC_get_netns_cookie = 122, BPF_FUNC_get_current_ancestor_cgroup_id = 123, BPF_FUNC_sk_assign = 124, BPF_FUNC_ktime_get_boot_ns = 125, BPF_FUNC_seq_printf = 126, BPF_FUNC_seq_write = 127, BPF_FUNC_sk_cgroup_id = 128, BPF_FUNC_sk_ancestor_cgroup_id = 129, BPF_FUNC_ringbuf_output = 130, BPF_FUNC_ringbuf_reserve = 131, BPF_FUNC_ringbuf_submit = 132, BPF_FUNC_ringbuf_discard = 133, BPF_FUNC_ringbuf_query = 134, BPF_FUNC_csum_level = 135, BPF_FUNC_skc_to_tcp6_sock = 136, BPF_FUNC_skc_to_tcp_sock = 137, BPF_FUNC_skc_to_tcp_timewait_sock = 138, BPF_FUNC_skc_to_tcp_request_sock = 139, BPF_FUNC_skc_to_udp6_sock = 140, BPF_FUNC_get_task_stack = 141, BPF_FUNC_load_hdr_opt = 142, BPF_FUNC_store_hdr_opt = 143, BPF_FUNC_reserve_hdr_opt = 144, BPF_FUNC_inode_storage_get = 145, BPF_FUNC_inode_storage_delete = 146, BPF_FUNC_d_path = 147, BPF_FUNC_copy_from_user = 148, BPF_FUNC_snprintf_btf = 149, BPF_FUNC_seq_printf_btf = 150, BPF_FUNC_skb_cgroup_classid = 151, BPF_FUNC_redirect_neigh = 152, BPF_FUNC_per_cpu_ptr = 153, BPF_FUNC_this_cpu_ptr = 154, BPF_FUNC_redirect_peer = 155, BPF_FUNC_task_storage_get = 156, BPF_FUNC_task_storage_delete = 157, BPF_FUNC_get_current_task_btf = 158, BPF_FUNC_bprm_opts_set = 159, BPF_FUNC_ktime_get_coarse_ns = 160, BPF_FUNC_ima_inode_hash = 161, BPF_FUNC_sock_from_file = 162, BPF_FUNC_check_mtu = 163, BPF_FUNC_for_each_map_elem = 164, BPF_FUNC_snprintf = 165, BPF_FUNC_sys_bpf = 166, BPF_FUNC_btf_find_by_name_kind = 167, BPF_FUNC_sys_close = 168, __BPF_FUNC_MAX_ID = 169, }; enum { BPF_F_INDEX_MASK = 4294967295, BPF_F_CURRENT_CPU = 4294967295, BPF_F_CTXLEN_MASK = 0, }; enum { BPF_F_GET_BRANCH_RECORDS_SIZE = 1, }; struct bpf_perf_event_value { __u64 counter; __u64 enabled; __u64 running; }; struct bpf_raw_tracepoint_args { __u64 args[0]; }; enum bpf_task_fd_type { BPF_FD_TYPE_RAW_TRACEPOINT = 0, BPF_FD_TYPE_TRACEPOINT = 1, BPF_FD_TYPE_KPROBE = 2, BPF_FD_TYPE_KRETPROBE = 3, BPF_FD_TYPE_UPROBE = 4, BPF_FD_TYPE_URETPROBE = 5, }; struct btf_ptr { void *ptr; __u32 type_id; __u32 flags; }; enum { BTF_F_COMPACT = 1, BTF_F_NONAME = 2, BTF_F_PTR_RAW = 4, BTF_F_ZERO = 8, }; struct bpf_local_storage_data; struct bpf_local_storage { struct bpf_local_storage_data *cache[16]; struct hlist_head list; void *owner; struct callback_head rcu; raw_spinlock_t lock; }; struct bpf_local_storage_map_bucket; struct bpf_local_storage_map { struct bpf_map map; struct bpf_local_storage_map_bucket *buckets; u32 bucket_log; u16 elem_size; u16 cache_idx; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; enum bpf_arg_type { ARG_DONTCARE = 0, ARG_CONST_MAP_PTR = 1, ARG_PTR_TO_MAP_KEY = 2, ARG_PTR_TO_MAP_VALUE = 3, ARG_PTR_TO_UNINIT_MAP_VALUE = 4, ARG_PTR_TO_MAP_VALUE_OR_NULL = 5, ARG_PTR_TO_MEM = 6, ARG_PTR_TO_MEM_OR_NULL = 7, ARG_PTR_TO_UNINIT_MEM = 8, ARG_CONST_SIZE = 9, ARG_CONST_SIZE_OR_ZERO = 10, ARG_PTR_TO_CTX = 11, ARG_PTR_TO_CTX_OR_NULL = 12, ARG_ANYTHING = 13, ARG_PTR_TO_SPIN_LOCK = 14, ARG_PTR_TO_SOCK_COMMON = 15, ARG_PTR_TO_INT = 16, ARG_PTR_TO_LONG = 17, ARG_PTR_TO_SOCKET = 18, ARG_PTR_TO_SOCKET_OR_NULL = 19, ARG_PTR_TO_BTF_ID = 20, ARG_PTR_TO_ALLOC_MEM = 21, ARG_PTR_TO_ALLOC_MEM_OR_NULL = 22, ARG_CONST_ALLOC_SIZE_OR_ZERO = 23, ARG_PTR_TO_BTF_ID_SOCK_COMMON = 24, ARG_PTR_TO_PERCPU_BTF_ID = 25, ARG_PTR_TO_FUNC = 26, ARG_PTR_TO_STACK_OR_NULL = 27, ARG_PTR_TO_CONST_STR = 28, __BPF_ARG_TYPE_MAX = 29, }; enum bpf_return_type { RET_INTEGER = 0, RET_VOID = 1, RET_PTR_TO_MAP_VALUE = 2, RET_PTR_TO_MAP_VALUE_OR_NULL = 3, RET_PTR_TO_SOCKET_OR_NULL = 4, RET_PTR_TO_TCP_SOCK_OR_NULL = 5, RET_PTR_TO_SOCK_COMMON_OR_NULL = 6, RET_PTR_TO_ALLOC_MEM_OR_NULL = 7, RET_PTR_TO_BTF_ID_OR_NULL = 8, RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL = 9, RET_PTR_TO_MEM_OR_BTF_ID = 10, RET_PTR_TO_BTF_ID = 11, }; struct bpf_func_proto { u64 (*func)(u64, u64, u64, u64, u64); bool gpl_only; bool pkt_access; enum bpf_return_type ret_type; union { struct { enum bpf_arg_type arg1_type; enum bpf_arg_type arg2_type; enum bpf_arg_type arg3_type; enum bpf_arg_type arg4_type; enum bpf_arg_type arg5_type; }; enum bpf_arg_type arg_type[5]; }; union { struct { u32 *arg1_btf_id; u32 *arg2_btf_id; u32 *arg3_btf_id; u32 *arg4_btf_id; u32 *arg5_btf_id; }; u32 *arg_btf_id[5]; }; int *ret_btf_id; bool (*allowed)(const struct bpf_prog *); }; enum bpf_access_type { BPF_READ = 1, BPF_WRITE = 2, }; struct bpf_verifier_log; struct bpf_insn_access_aux { enum bpf_reg_type reg_type; union { int ctx_field_size; struct { struct btf *btf; u32 btf_id; }; }; struct bpf_verifier_log *log; }; struct bpf_verifier_ops { const struct bpf_func_proto * (*get_func_proto)(enum bpf_func_id, const struct bpf_prog *); bool (*is_valid_access)(int, int, enum bpf_access_type, const struct bpf_prog *, struct bpf_insn_access_aux *); int (*gen_prologue)(struct bpf_insn *, bool, const struct bpf_prog *); int (*gen_ld_abs)(const struct bpf_insn *, struct bpf_insn *); u32 (*convert_ctx_access)(enum bpf_access_type, const struct bpf_insn *, struct bpf_insn *, struct bpf_prog *, u32 *); int (*btf_struct_access)(struct bpf_verifier_log *, const struct btf *, const struct btf_type *, int, int, enum bpf_access_type, u32 *); bool (*check_kfunc_call)(u32); }; struct bpf_event_entry { struct perf_event *event; struct file *perf_file; struct file *map_file; struct callback_head rcu; }; typedef long unsigned int (*bpf_ctx_copy_t)(void *, const void *, long unsigned int, long unsigned int); typedef struct pt_regs bpf_user_pt_regs_t; struct bpf_perf_event_data { bpf_user_pt_regs_t regs; __u64 sample_period; __u64 addr; }; struct perf_event_query_bpf { __u32 ids_len; __u32 prog_cnt; __u32 ids[0]; }; struct bpf_perf_event_data_kern { bpf_user_pt_regs_t *regs; struct perf_sample_data *data; struct perf_event *event; }; struct btf_id_set { u32 cnt; u32 ids[0]; }; struct bpf_local_storage_map_bucket { struct hlist_head list; raw_spinlock_t lock; }; struct bpf_local_storage_data { struct bpf_local_storage_map *smap; u8 data[0]; }; struct trace_event_raw_bpf_trace_printk { struct trace_entry ent; u32 __data_loc_bpf_string; char __data[0]; }; struct trace_event_data_offsets_bpf_trace_printk { u32 bpf_string; }; typedef void (*btf_trace_bpf_trace_printk)(void *, const char *); struct bpf_trace_module { struct module *module; struct list_head list; }; typedef u64 (*btf_bpf_override_return)(struct pt_regs *, long unsigned int); typedef u64 (*btf_bpf_probe_read_user)(void *, u32, const void *); typedef u64 (*btf_bpf_probe_read_user_str)(void *, u32, const void *); typedef u64 (*btf_bpf_probe_read_kernel)(void *, u32, const void *); typedef u64 (*btf_bpf_probe_read_kernel_str)(void *, u32, const void *); typedef u64 (*btf_bpf_probe_read_compat)(void *, u32, const void *); typedef u64 (*btf_bpf_probe_read_compat_str)(void *, u32, const void *); typedef u64 (*btf_bpf_probe_write_user)(void *, const void *, u32); typedef u64 (*btf_bpf_trace_printk)(char *, u32, u64, u64, u64); typedef u64 (*btf_bpf_seq_printf)(struct seq_file *, char *, u32, const void *, u32); typedef u64 (*btf_bpf_seq_write)(struct seq_file *, const void *, u32); typedef u64 (*btf_bpf_seq_printf_btf)(struct seq_file *, struct btf_ptr *, u32, u64); typedef u64 (*btf_bpf_perf_event_read)(struct bpf_map *, u64); typedef u64 (*btf_bpf_perf_event_read_value)(struct bpf_map *, u64, struct bpf_perf_event_value *, u32); struct bpf_trace_sample_data { struct perf_sample_data sds[3]; }; typedef u64 (*btf_bpf_perf_event_output)(struct pt_regs *, struct bpf_map *, u64, void *, u64); struct bpf_nested_pt_regs { struct pt_regs regs[3]; }; typedef u64 (*btf_bpf_get_current_task)(); typedef u64 (*btf_bpf_get_current_task_btf)(); typedef u64 (*btf_bpf_current_task_under_cgroup)(struct bpf_map *, u32); struct send_signal_irq_work { struct irq_work irq_work; struct task_struct *task; u32 sig; enum pid_type type; }; typedef u64 (*btf_bpf_send_signal)(u32); typedef u64 (*btf_bpf_send_signal_thread)(u32); typedef u64 (*btf_bpf_d_path)(struct path *, char *, u32); typedef u64 (*btf_bpf_snprintf_btf)(char *, u32, struct btf_ptr *, u32, u64); typedef u64 (*btf_bpf_perf_event_output_tp)(void *, struct bpf_map *, u64, void *, u64); typedef u64 (*btf_bpf_get_stackid_tp)(void *, struct bpf_map *, u64); typedef u64 (*btf_bpf_get_stack_tp)(void *, void *, u32, u64); typedef u64 (*btf_bpf_perf_prog_read_value)(struct bpf_perf_event_data_kern *, struct bpf_perf_event_value *, u32); typedef u64 (*btf_bpf_read_branch_records)(struct bpf_perf_event_data_kern *, void *, u32, u64); struct bpf_raw_tp_regs { struct pt_regs regs[3]; }; typedef u64 (*btf_bpf_perf_event_output_raw_tp)(struct bpf_raw_tracepoint_args *, struct bpf_map *, u64, void *, u64); typedef u64 (*btf_bpf_get_stackid_raw_tp)(struct bpf_raw_tracepoint_args *, struct bpf_map *, u64); typedef u64 (*btf_bpf_get_stack_raw_tp)(struct bpf_raw_tracepoint_args *, void *, u32, u64); struct kprobe_trace_entry_head { struct trace_entry ent; long unsigned int ip; }; struct kretprobe_trace_entry_head { struct trace_entry ent; long unsigned int func; long unsigned int ret_ip; }; typedef int (*print_type_func_t)(struct trace_seq *, void *, void *); enum fetch_op { FETCH_OP_NOP = 0, FETCH_OP_REG = 1, FETCH_OP_STACK = 2, FETCH_OP_STACKP = 3, FETCH_OP_RETVAL = 4, FETCH_OP_IMM = 5, FETCH_OP_COMM = 6, FETCH_OP_ARG = 7, FETCH_OP_FOFFS = 8, FETCH_OP_DATA = 9, FETCH_OP_DEREF = 10, FETCH_OP_UDEREF = 11, FETCH_OP_ST_RAW = 12, FETCH_OP_ST_MEM = 13, FETCH_OP_ST_UMEM = 14, FETCH_OP_ST_STRING = 15, FETCH_OP_ST_USTRING = 16, FETCH_OP_MOD_BF = 17, FETCH_OP_LP_ARRAY = 18, FETCH_OP_END = 19, FETCH_NOP_SYMBOL = 20, }; struct fetch_insn { enum fetch_op op; union { unsigned int param; struct { unsigned int size; int offset; }; struct { unsigned char basesize; unsigned char lshift; unsigned char rshift; }; long unsigned int immediate; void *data; }; }; struct fetch_type { const char *name; size_t size; int is_signed; print_type_func_t print; const char *fmt; const char *fmttype; }; struct probe_arg { struct fetch_insn *code; bool dynamic; unsigned int offset; unsigned int count; const char *name; const char *comm; char *fmt; const struct fetch_type *type; }; struct trace_uprobe_filter { rwlock_t rwlock; int nr_systemwide; struct list_head perf_events; }; struct trace_probe_event { unsigned int flags; struct trace_event_class class; struct trace_event_call call; struct list_head files; struct list_head probes; struct trace_uprobe_filter filter[0]; }; struct trace_probe { struct list_head list; struct trace_probe_event *event; ssize_t size; unsigned int nr_args; struct probe_arg args[0]; }; struct event_file_link { struct trace_event_file *file; struct list_head list; }; enum { TP_ERR_FILE_NOT_FOUND = 0, TP_ERR_NO_REGULAR_FILE = 1, TP_ERR_BAD_REFCNT = 2, TP_ERR_REFCNT_OPEN_BRACE = 3, TP_ERR_BAD_REFCNT_SUFFIX = 4, TP_ERR_BAD_UPROBE_OFFS = 5, TP_ERR_MAXACT_NO_KPROBE = 6, TP_ERR_BAD_MAXACT = 7, TP_ERR_MAXACT_TOO_BIG = 8, TP_ERR_BAD_PROBE_ADDR = 9, TP_ERR_BAD_RETPROBE = 10, TP_ERR_BAD_ADDR_SUFFIX = 11, TP_ERR_NO_GROUP_NAME = 12, TP_ERR_GROUP_TOO_LONG = 13, TP_ERR_BAD_GROUP_NAME = 14, TP_ERR_NO_EVENT_NAME = 15, TP_ERR_EVENT_TOO_LONG = 16, TP_ERR_BAD_EVENT_NAME = 17, TP_ERR_RETVAL_ON_PROBE = 18, TP_ERR_BAD_STACK_NUM = 19, TP_ERR_BAD_ARG_NUM = 20, TP_ERR_BAD_VAR = 21, TP_ERR_BAD_REG_NAME = 22, TP_ERR_BAD_MEM_ADDR = 23, TP_ERR_BAD_IMM = 24, TP_ERR_IMMSTR_NO_CLOSE = 25, TP_ERR_FILE_ON_KPROBE = 26, TP_ERR_BAD_FILE_OFFS = 27, TP_ERR_SYM_ON_UPROBE = 28, TP_ERR_TOO_MANY_OPS = 29, TP_ERR_DEREF_NEED_BRACE = 30, TP_ERR_BAD_DEREF_OFFS = 31, TP_ERR_DEREF_OPEN_BRACE = 32, TP_ERR_COMM_CANT_DEREF = 33, TP_ERR_BAD_FETCH_ARG = 34, TP_ERR_ARRAY_NO_CLOSE = 35, TP_ERR_BAD_ARRAY_SUFFIX = 36, TP_ERR_BAD_ARRAY_NUM = 37, TP_ERR_ARRAY_TOO_BIG = 38, TP_ERR_BAD_TYPE = 39, TP_ERR_BAD_STRING = 40, TP_ERR_BAD_BITFIELD = 41, TP_ERR_ARG_NAME_TOO_LONG = 42, TP_ERR_NO_ARG_NAME = 43, TP_ERR_BAD_ARG_NAME = 44, TP_ERR_USED_ARG_NAME = 45, TP_ERR_ARG_TOO_LONG = 46, TP_ERR_NO_ARG_BODY = 47, TP_ERR_BAD_INSN_BNDRY = 48, TP_ERR_FAIL_REG_PROBE = 49, TP_ERR_DIFF_PROBE_TYPE = 50, TP_ERR_DIFF_ARG_TYPE = 51, TP_ERR_SAME_PROBE = 52, }; struct trace_kprobe { struct dyn_event devent; struct kretprobe rp; long unsigned int *nhit; const char *symbol; struct trace_probe tp; }; enum error_detector { ERROR_DETECTOR_KFENCE = 0, ERROR_DETECTOR_KASAN = 1, }; struct trace_event_raw_error_report_template { struct trace_entry ent; enum error_detector error_detector; long unsigned int id; char __data[0]; }; struct trace_event_data_offsets_error_report_template {}; typedef void (*btf_trace_error_report_end)(void *, enum error_detector, long unsigned int); struct trace_event_raw_cpu { struct trace_entry ent; u32 state; u32 cpu_id; char __data[0]; }; struct trace_event_raw_powernv_throttle { struct trace_entry ent; int chip_id; u32 __data_loc_reason; int pmax; char __data[0]; }; struct trace_event_raw_pstate_sample { struct trace_entry ent; u32 core_busy; u32 scaled_busy; u32 from; u32 to; u64 mperf; u64 aperf; u64 tsc; u32 freq; u32 io_boost; char __data[0]; }; struct trace_event_raw_cpu_frequency_limits { struct trace_entry ent; u32 min_freq; u32 max_freq; u32 cpu_id; char __data[0]; }; struct trace_event_raw_device_pm_callback_start { struct trace_entry ent; u32 __data_loc_device; u32 __data_loc_driver; u32 __data_loc_parent; u32 __data_loc_pm_ops; int event; char __data[0]; }; struct trace_event_raw_device_pm_callback_end { struct trace_entry ent; u32 __data_loc_device; u32 __data_loc_driver; int error; char __data[0]; }; struct trace_event_raw_suspend_resume { struct trace_entry ent; const char *action; int val; bool start; char __data[0]; }; struct trace_event_raw_wakeup_source { struct trace_entry ent; u32 __data_loc_name; u64 state; char __data[0]; }; struct trace_event_raw_clock { struct trace_entry ent; u32 __data_loc_name; u64 state; u64 cpu_id; char __data[0]; }; struct trace_event_raw_power_domain { struct trace_entry ent; u32 __data_loc_name; u64 state; u64 cpu_id; char __data[0]; }; struct trace_event_raw_cpu_latency_qos_request { struct trace_entry ent; s32 value; char __data[0]; }; struct trace_event_raw_pm_qos_update { struct trace_entry ent; enum pm_qos_req_action action; int prev_value; int curr_value; char __data[0]; }; struct trace_event_raw_dev_pm_qos_request { struct trace_entry ent; u32 __data_loc_name; enum dev_pm_qos_req_type type; s32 new_value; char __data[0]; }; struct trace_event_data_offsets_cpu {}; struct trace_event_data_offsets_powernv_throttle { u32 reason; }; struct trace_event_data_offsets_pstate_sample {}; struct trace_event_data_offsets_cpu_frequency_limits {}; struct trace_event_data_offsets_device_pm_callback_start { u32 device; u32 driver; u32 parent; u32 pm_ops; }; struct trace_event_data_offsets_device_pm_callback_end { u32 device; u32 driver; }; struct trace_event_data_offsets_suspend_resume {}; struct trace_event_data_offsets_wakeup_source { u32 name; }; struct trace_event_data_offsets_clock { u32 name; }; struct trace_event_data_offsets_power_domain { u32 name; }; struct trace_event_data_offsets_cpu_latency_qos_request {}; struct trace_event_data_offsets_pm_qos_update {}; struct trace_event_data_offsets_dev_pm_qos_request { u32 name; }; typedef void (*btf_trace_cpu_idle)(void *, unsigned int, unsigned int); typedef void (*btf_trace_powernv_throttle)(void *, int, const char *, int); typedef void (*btf_trace_pstate_sample)(void *, u32, u32, u32, u32, u64, u64, u64, u32, u32); typedef void (*btf_trace_cpu_frequency)(void *, unsigned int, unsigned int); typedef void (*btf_trace_cpu_frequency_limits)(void *, struct cpufreq_policy *); typedef void (*btf_trace_device_pm_callback_start)(void *, struct device *, const char *, int); typedef void (*btf_trace_device_pm_callback_end)(void *, struct device *, int); typedef void (*btf_trace_suspend_resume)(void *, const char *, int, bool); typedef void (*btf_trace_wakeup_source_activate)(void *, const char *, unsigned int); typedef void (*btf_trace_wakeup_source_deactivate)(void *, const char *, unsigned int); typedef void (*btf_trace_clock_enable)(void *, const char *, unsigned int, unsigned int); typedef void (*btf_trace_clock_disable)(void *, const char *, unsigned int, unsigned int); typedef void (*btf_trace_clock_set_rate)(void *, const char *, unsigned int, unsigned int); typedef void (*btf_trace_power_domain_target)(void *, const char *, unsigned int, unsigned int); typedef void (*btf_trace_pm_qos_add_request)(void *, s32); typedef void (*btf_trace_pm_qos_update_request)(void *, s32); typedef void (*btf_trace_pm_qos_remove_request)(void *, s32); typedef void (*btf_trace_pm_qos_update_target)(void *, enum pm_qos_req_action, int, int); typedef void (*btf_trace_pm_qos_update_flags)(void *, enum pm_qos_req_action, int, int); typedef void (*btf_trace_dev_pm_qos_add_request)(void *, const char *, enum dev_pm_qos_req_type, s32); typedef void (*btf_trace_dev_pm_qos_update_request)(void *, const char *, enum dev_pm_qos_req_type, s32); typedef void (*btf_trace_dev_pm_qos_remove_request)(void *, const char *, enum dev_pm_qos_req_type, s32); struct trace_event_raw_rpm_internal { struct trace_entry ent; u32 __data_loc_name; int flags; int usage_count; int disable_depth; int runtime_auto; int request_pending; int irq_safe; int child_count; char __data[0]; }; struct trace_event_raw_rpm_return_int { struct trace_entry ent; u32 __data_loc_name; long unsigned int ip; int ret; char __data[0]; }; struct trace_event_data_offsets_rpm_internal { u32 name; }; struct trace_event_data_offsets_rpm_return_int { u32 name; }; typedef void (*btf_trace_rpm_suspend)(void *, struct device *, int); typedef void (*btf_trace_rpm_resume)(void *, struct device *, int); typedef void (*btf_trace_rpm_idle)(void *, struct device *, int); typedef void (*btf_trace_rpm_usage)(void *, struct device *, int); typedef void (*btf_trace_rpm_return_int)(void *, struct device *, long unsigned int, int); struct trace_probe_log { const char *subsystem; const char **argv; int argc; int index; }; enum uprobe_filter_ctx { UPROBE_FILTER_REGISTER = 0, UPROBE_FILTER_UNREGISTER = 1, UPROBE_FILTER_MMAP = 2, }; struct uprobe_consumer { int (*handler)(struct uprobe_consumer *, struct pt_regs *); int (*ret_handler)(struct uprobe_consumer *, long unsigned int, struct pt_regs *); bool (*filter)(struct uprobe_consumer *, enum uprobe_filter_ctx, struct mm_struct *); struct uprobe_consumer *next; }; struct uprobe_trace_entry_head { struct trace_entry ent; long unsigned int vaddr[0]; }; struct trace_uprobe { struct dyn_event devent; struct uprobe_consumer consumer; struct path path; struct inode *inode; char *filename; long unsigned int offset; long unsigned int ref_ctr_offset; long unsigned int nhit; struct trace_probe tp; }; struct uprobe_dispatch_data { struct trace_uprobe *tu; long unsigned int bp_addr; }; struct uprobe_cpu_buffer { struct mutex mutex; void *buf; }; typedef bool (*filter_func_t)(struct uprobe_consumer *, enum uprobe_filter_ctx, struct mm_struct *); struct rhash_lock_head; struct bucket_table { unsigned int size; unsigned int nest; u32 hash_rnd; struct list_head walkers; struct callback_head rcu; struct bucket_table *future_tbl; struct lockdep_map dep_map; long: 64; struct rhash_lock_head *buckets[0]; }; enum xdp_action { XDP_ABORTED = 0, XDP_DROP = 1, XDP_PASS = 2, XDP_TX = 3, XDP_REDIRECT = 4, }; enum xdp_mem_type { MEM_TYPE_PAGE_SHARED = 0, MEM_TYPE_PAGE_ORDER0 = 1, MEM_TYPE_PAGE_POOL = 2, MEM_TYPE_XSK_BUFF_POOL = 3, MEM_TYPE_MAX = 4, }; struct xdp_cpumap_stats { unsigned int redirect; unsigned int pass; unsigned int drop; }; struct bpf_prog_dummy { struct bpf_prog prog; }; typedef u64 (*btf_bpf_user_rnd_u32)(); typedef u64 (*btf_bpf_get_raw_cpu_id)(); struct _bpf_dtab_netdev { struct net_device *dev; }; struct rhash_lock_head {}; struct zero_copy_allocator; struct xdp_mem_allocator { struct xdp_mem_info mem; union { void *allocator; struct page_pool *page_pool; struct zero_copy_allocator *zc_alloc; }; struct rhash_head node; struct callback_head rcu; }; struct trace_event_raw_xdp_exception { struct trace_entry ent; int prog_id; u32 act; int ifindex; char __data[0]; }; struct trace_event_raw_xdp_bulk_tx { struct trace_entry ent; int ifindex; u32 act; int drops; int sent; int err; char __data[0]; }; struct trace_event_raw_xdp_redirect_template { struct trace_entry ent; int prog_id; u32 act; int ifindex; int err; int to_ifindex; u32 map_id; int map_index; char __data[0]; }; struct trace_event_raw_xdp_cpumap_kthread { struct trace_entry ent; int map_id; u32 act; int cpu; unsigned int drops; unsigned int processed; int sched; unsigned int xdp_pass; unsigned int xdp_drop; unsigned int xdp_redirect; char __data[0]; }; struct trace_event_raw_xdp_cpumap_enqueue { struct trace_entry ent; int map_id; u32 act; int cpu; unsigned int drops; unsigned int processed; int to_cpu; char __data[0]; }; struct trace_event_raw_xdp_devmap_xmit { struct trace_entry ent; int from_ifindex; u32 act; int to_ifindex; int drops; int sent; int err; char __data[0]; }; struct trace_event_raw_mem_disconnect { struct trace_entry ent; const struct xdp_mem_allocator *xa; u32 mem_id; u32 mem_type; const void *allocator; char __data[0]; }; struct trace_event_raw_mem_connect { struct trace_entry ent; const struct xdp_mem_allocator *xa; u32 mem_id; u32 mem_type; const void *allocator; const struct xdp_rxq_info *rxq; int ifindex; char __data[0]; }; struct trace_event_raw_mem_return_failed { struct trace_entry ent; const struct page *page; u32 mem_id; u32 mem_type; char __data[0]; }; struct trace_event_data_offsets_xdp_exception {}; struct trace_event_data_offsets_xdp_bulk_tx {}; struct trace_event_data_offsets_xdp_redirect_template {}; struct trace_event_data_offsets_xdp_cpumap_kthread {}; struct trace_event_data_offsets_xdp_cpumap_enqueue {}; struct trace_event_data_offsets_xdp_devmap_xmit {}; struct trace_event_data_offsets_mem_disconnect {}; struct trace_event_data_offsets_mem_connect {}; struct trace_event_data_offsets_mem_return_failed {}; typedef void (*btf_trace_xdp_exception)(void *, const struct net_device *, const struct bpf_prog *, u32); typedef void (*btf_trace_xdp_bulk_tx)(void *, const struct net_device *, int, int, int); typedef void (*btf_trace_xdp_redirect)(void *, const struct net_device *, const struct bpf_prog *, const void *, int, enum bpf_map_type, u32, u32); typedef void (*btf_trace_xdp_redirect_err)(void *, const struct net_device *, const struct bpf_prog *, const void *, int, enum bpf_map_type, u32, u32); typedef void (*btf_trace_xdp_redirect_map)(void *, const struct net_device *, const struct bpf_prog *, const void *, int, enum bpf_map_type, u32, u32); typedef void (*btf_trace_xdp_redirect_map_err)(void *, const struct net_device *, const struct bpf_prog *, const void *, int, enum bpf_map_type, u32, u32); typedef void (*btf_trace_xdp_cpumap_kthread)(void *, int, unsigned int, unsigned int, int, struct xdp_cpumap_stats *); typedef void (*btf_trace_xdp_cpumap_enqueue)(void *, int, unsigned int, unsigned int, int); typedef void (*btf_trace_xdp_devmap_xmit)(void *, const struct net_device *, const struct net_device *, int, int, int); typedef void (*btf_trace_mem_disconnect)(void *, const struct xdp_mem_allocator *); typedef void (*btf_trace_mem_connect)(void *, const struct xdp_mem_allocator *, const struct xdp_rxq_info *); typedef void (*btf_trace_mem_return_failed)(void *, const struct xdp_mem_info *, const struct page *); enum bpf_cmd { BPF_MAP_CREATE = 0, BPF_MAP_LOOKUP_ELEM = 1, BPF_MAP_UPDATE_ELEM = 2, BPF_MAP_DELETE_ELEM = 3, BPF_MAP_GET_NEXT_KEY = 4, BPF_PROG_LOAD = 5, BPF_OBJ_PIN = 6, BPF_OBJ_GET = 7, BPF_PROG_ATTACH = 8, BPF_PROG_DETACH = 9, BPF_PROG_TEST_RUN = 10, BPF_PROG_RUN = 10, BPF_PROG_GET_NEXT_ID = 11, BPF_MAP_GET_NEXT_ID = 12, BPF_PROG_GET_FD_BY_ID = 13, BPF_MAP_GET_FD_BY_ID = 14, BPF_OBJ_GET_INFO_BY_FD = 15, BPF_PROG_QUERY = 16, BPF_RAW_TRACEPOINT_OPEN = 17, BPF_BTF_LOAD = 18, BPF_BTF_GET_FD_BY_ID = 19, BPF_TASK_FD_QUERY = 20, BPF_MAP_LOOKUP_AND_DELETE_ELEM = 21, BPF_MAP_FREEZE = 22, BPF_BTF_GET_NEXT_ID = 23, BPF_MAP_LOOKUP_BATCH = 24, BPF_MAP_LOOKUP_AND_DELETE_BATCH = 25, BPF_MAP_UPDATE_BATCH = 26, BPF_MAP_DELETE_BATCH = 27, BPF_LINK_CREATE = 28, BPF_LINK_UPDATE = 29, BPF_LINK_GET_FD_BY_ID = 30, BPF_LINK_GET_NEXT_ID = 31, BPF_ENABLE_STATS = 32, BPF_ITER_CREATE = 33, BPF_LINK_DETACH = 34, BPF_PROG_BIND_MAP = 35, }; enum { BPF_ANY = 0, BPF_NOEXIST = 1, BPF_EXIST = 2, BPF_F_LOCK = 4, }; enum { BPF_F_NO_PREALLOC = 1, BPF_F_NO_COMMON_LRU = 2, BPF_F_NUMA_NODE = 4, BPF_F_RDONLY = 8, BPF_F_WRONLY = 16, BPF_F_STACK_BUILD_ID = 32, BPF_F_ZERO_SEED = 64, BPF_F_RDONLY_PROG = 128, BPF_F_WRONLY_PROG = 256, BPF_F_CLONE = 512, BPF_F_MMAPABLE = 1024, BPF_F_PRESERVE_ELEMS = 2048, BPF_F_INNER_MAP = 4096, }; enum bpf_stats_type { BPF_STATS_RUN_TIME = 0, }; struct bpf_prog_info { __u32 type; __u32 id; __u8 tag[8]; __u32 jited_prog_len; __u32 xlated_prog_len; __u64 jited_prog_insns; __u64 xlated_prog_insns; __u64 load_time; __u32 created_by_uid; __u32 nr_map_ids; __u64 map_ids; char name[16]; __u32 ifindex; __u32 gpl_compatible: 1; __u64 netns_dev; __u64 netns_ino; __u32 nr_jited_ksyms; __u32 nr_jited_func_lens; __u64 jited_ksyms; __u64 jited_func_lens; __u32 btf_id; __u32 func_info_rec_size; __u64 func_info; __u32 nr_func_info; __u32 nr_line_info; __u64 line_info; __u64 jited_line_info; __u32 nr_jited_line_info; __u32 line_info_rec_size; __u32 jited_line_info_rec_size; __u32 nr_prog_tags; __u64 prog_tags; __u64 run_time_ns; __u64 run_cnt; __u64 recursion_misses; }; struct bpf_map_info { __u32 type; __u32 id; __u32 key_size; __u32 value_size; __u32 max_entries; __u32 map_flags; char name[16]; __u32 ifindex; __u32 btf_vmlinux_value_type_id; __u64 netns_dev; __u64 netns_ino; __u32 btf_id; __u32 btf_key_type_id; __u32 btf_value_type_id; }; struct bpf_btf_info { __u64 btf; __u32 btf_size; __u32 id; __u64 name; __u32 name_len; __u32 kernel_btf; }; struct bpf_spin_lock { __u32 val; }; typedef sockptr_t bpfptr_t; struct bpf_verifier_log { u32 level; char kbuf[1024]; char *ubuf; u32 len_used; u32 len_total; }; struct bpf_subprog_info { u32 start; u32 linfo_idx; u16 stack_depth; bool has_tail_call; bool tail_call_reachable; bool has_ld_abs; }; struct bpf_id_pair { u32 old; u32 cur; }; struct bpf_verifier_stack_elem; struct bpf_verifier_state; struct bpf_verifier_state_list; struct bpf_insn_aux_data; struct bpf_verifier_env { u32 insn_idx; u32 prev_insn_idx; struct bpf_prog *prog; const struct bpf_verifier_ops *ops; struct bpf_verifier_stack_elem *head; int stack_size; bool strict_alignment; bool test_state_freq; struct bpf_verifier_state *cur_state; struct bpf_verifier_state_list **explored_states; struct bpf_verifier_state_list *free_list; struct bpf_map *used_maps[64]; struct btf_mod_pair used_btfs[64]; u32 used_map_cnt; u32 used_btf_cnt; u32 id_gen; bool explore_alu_limits; bool allow_ptr_leaks; bool allow_uninit_stack; bool allow_ptr_to_map_access; bool bpf_capable; bool bypass_spec_v1; bool bypass_spec_v4; bool seen_direct_write; struct bpf_insn_aux_data *insn_aux_data; const struct bpf_line_info *prev_linfo; struct bpf_verifier_log log; struct bpf_subprog_info subprog_info[257]; struct bpf_id_pair idmap_scratch[75]; struct { int *insn_state; int *insn_stack; int cur_stack; } cfg; u32 pass_cnt; u32 subprog_cnt; u32 prev_insn_processed; u32 insn_processed; u32 prev_jmps_processed; u32 jmps_processed; u64 verification_time; u32 max_states_per_insn; u32 total_states; u32 peak_states; u32 longest_mark_read_walk; bpfptr_t fd_array; }; struct tnum { u64 value; u64 mask; }; enum bpf_reg_liveness { REG_LIVE_NONE = 0, REG_LIVE_READ32 = 1, REG_LIVE_READ64 = 2, REG_LIVE_READ = 3, REG_LIVE_WRITTEN = 4, REG_LIVE_DONE = 8, }; struct bpf_reg_state { enum bpf_reg_type type; s32 off; union { int range; struct bpf_map *map_ptr; struct { struct btf *btf; u32 btf_id; }; u32 mem_size; struct { long unsigned int raw1; long unsigned int raw2; } raw; u32 subprogno; }; u32 id; u32 ref_obj_id; struct tnum var_off; s64 smin_value; s64 smax_value; u64 umin_value; u64 umax_value; s32 s32_min_value; s32 s32_max_value; u32 u32_min_value; u32 u32_max_value; struct bpf_reg_state *parent; u32 frameno; s32 subreg_def; enum bpf_reg_liveness live; bool precise; }; struct bpf_reference_state; struct bpf_stack_state; struct bpf_func_state { struct bpf_reg_state regs[11]; int callsite; u32 frameno; u32 subprogno; int acquired_refs; struct bpf_reference_state *refs; int allocated_stack; bool in_callback_fn; struct bpf_stack_state *stack; }; struct bpf_attach_target_info { struct btf_func_model fmodel; long int tgt_addr; const char *tgt_name; const struct btf_type *tgt_type; }; struct bpf_link_primer { struct bpf_link *link; struct file *file; int fd; u32 id; }; struct bpf_stack_state { struct bpf_reg_state spilled_ptr; u8 slot_type[8]; }; struct bpf_reference_state { int id; int insn_idx; }; struct bpf_idx_pair { u32 prev_idx; u32 idx; }; struct bpf_verifier_state { struct bpf_func_state *frame[8]; struct bpf_verifier_state *parent; u32 branches; u32 insn_idx; u32 curframe; u32 active_spin_lock; bool speculative; u32 first_insn_idx; u32 last_insn_idx; struct bpf_idx_pair *jmp_history; u32 jmp_history_cnt; }; struct bpf_verifier_state_list { struct bpf_verifier_state state; struct bpf_verifier_state_list *next; int miss_cnt; int hit_cnt; }; struct bpf_insn_aux_data { union { enum bpf_reg_type ptr_type; long unsigned int map_ptr_state; s32 call_imm; u32 alu_limit; struct { u32 map_index; u32 map_off; }; struct { enum bpf_reg_type reg_type; union { struct { struct btf *btf; u32 btf_id; }; u32 mem_size; }; } btf_var; }; u64 map_key_state; int ctx_field_size; u32 seen; bool sanitize_stack_spill; bool zext_dst; u8 alu_state; unsigned int orig_idx; bool prune_point; }; enum perf_bpf_event_type { PERF_BPF_EVENT_UNKNOWN = 0, PERF_BPF_EVENT_PROG_LOAD = 1, PERF_BPF_EVENT_PROG_UNLOAD = 2, PERF_BPF_EVENT_MAX = 3, }; enum bpf_audit { BPF_AUDIT_LOAD = 0, BPF_AUDIT_UNLOAD = 1, BPF_AUDIT_MAX = 2, }; struct bpf_tracing_link { struct bpf_link link; enum bpf_attach_type attach_type; struct bpf_trampoline *trampoline; struct bpf_prog *tgt_prog; }; struct bpf_raw_tp_link { struct bpf_link link; struct bpf_raw_event_map *btp; }; typedef u64 (*btf_bpf_sys_bpf)(int, void *, u32); typedef u64 (*btf_bpf_sys_close)(u32); struct btf_member { __u32 name_off; __u32 type; __u32 offset; }; struct btf_param { __u32 name_off; __u32 type; }; enum btf_func_linkage { BTF_FUNC_STATIC = 0, BTF_FUNC_GLOBAL = 1, BTF_FUNC_EXTERN = 2, }; struct btf_var_secinfo { __u32 type; __u32 offset; __u32 size; }; enum sk_action { SK_DROP = 0, SK_PASS = 1, }; struct bpf_kfunc_desc { struct btf_func_model func_model; u32 func_id; s32 imm; }; struct bpf_kfunc_desc_tab { struct bpf_kfunc_desc descs[256]; u32 nr_descs; }; struct bpf_struct_ops { const struct bpf_verifier_ops *verifier_ops; int (*init)(struct btf *); int (*check_member)(const struct btf_type *, const struct btf_member *); int (*init_member)(const struct btf_type *, const struct btf_member *, void *, const void *); int (*reg)(void *); void (*unreg)(void *); const struct btf_type *type; const struct btf_type *value_type; const char *name; struct btf_func_model func_models[64]; u32 type_id; u32 value_id; }; typedef u32 (*bpf_convert_ctx_access_t)(enum bpf_access_type, const struct bpf_insn *, struct bpf_insn *, struct bpf_prog *, u32 *); enum bpf_stack_slot_type { STACK_INVALID = 0, STACK_SPILL = 1, STACK_MISC = 2, STACK_ZERO = 3, }; struct bpf_verifier_stack_elem { struct bpf_verifier_state st; int insn_idx; int prev_insn_idx; struct bpf_verifier_stack_elem *next; u32 log_pos; }; enum { BTF_SOCK_TYPE_INET = 0, BTF_SOCK_TYPE_INET_CONN = 1, BTF_SOCK_TYPE_INET_REQ = 2, BTF_SOCK_TYPE_INET_TW = 3, BTF_SOCK_TYPE_REQ = 4, BTF_SOCK_TYPE_SOCK = 5, BTF_SOCK_TYPE_SOCK_COMMON = 6, BTF_SOCK_TYPE_TCP = 7, BTF_SOCK_TYPE_TCP_REQ = 8, BTF_SOCK_TYPE_TCP_TW = 9, BTF_SOCK_TYPE_TCP6 = 10, BTF_SOCK_TYPE_UDP = 11, BTF_SOCK_TYPE_UDP6 = 12, MAX_BTF_SOCK_TYPE = 13, }; typedef void (*bpf_insn_print_t)(void *, const char *, ...); typedef const char * (*bpf_insn_revmap_call_t)(void *, const struct bpf_insn *); typedef const char * (*bpf_insn_print_imm_t)(void *, const struct bpf_insn *, __u64); struct bpf_insn_cbs { bpf_insn_print_t cb_print; bpf_insn_revmap_call_t cb_call; bpf_insn_print_imm_t cb_imm; void *private_data; }; struct bpf_call_arg_meta { struct bpf_map *map_ptr; bool raw_mode; bool pkt_access; int regno; int access_size; int mem_size; u64 msize_max_value; int ref_obj_id; int func_id; struct btf *btf; u32 btf_id; struct btf *ret_btf; u32 ret_btf_id; u32 subprogno; }; enum reg_arg_type { SRC_OP = 0, DST_OP = 1, DST_OP_NO_MARK = 2, }; enum stack_access_src { ACCESS_DIRECT = 1, ACCESS_HELPER = 2, }; struct bpf_reg_types { const enum bpf_reg_type types[10]; u32 *btf_id; }; enum { AT_PKT_END = 4294967295, BEYOND_PKT_END = 4294967294, }; typedef int (*set_callee_state_fn)(struct bpf_verifier_env *, struct bpf_func_state *, struct bpf_func_state *, int); enum { REASON_BOUNDS = 4294967295, REASON_TYPE = 4294967294, REASON_PATHS = 4294967293, REASON_LIMIT = 4294967292, REASON_STACK = 4294967291, }; struct bpf_sanitize_info { struct bpf_insn_aux_data aux; bool mask_to_left; }; enum { DISCOVERED = 16, EXPLORED = 32, FALLTHROUGH = 1, BRANCH = 2, }; enum { DONE_EXPLORING = 0, KEEP_EXPLORING = 1, }; struct tree_descr { const char *name; const struct file_operations *ops; int mode; }; struct bpf_preload_info { char link_name[16]; int link_id; }; struct bpf_preload_ops { struct umd_info info; int (*preload)(struct bpf_preload_info *); int (*finish)(); struct module *owner; }; enum bpf_type { BPF_TYPE_UNSPEC = 0, BPF_TYPE_PROG = 1, BPF_TYPE_MAP = 2, BPF_TYPE_LINK = 3, }; struct map_iter { void *key; bool done; }; enum { OPT_MODE = 0, }; struct bpf_mount_opts { umode_t mode; }; struct bpf_pidns_info { __u32 pid; __u32 tgid; }; struct bpf_cgroup_storage_info { struct task_struct *task; struct bpf_cgroup_storage *storage[2]; }; typedef u64 (*btf_bpf_map_lookup_elem)(struct bpf_map *, void *); typedef u64 (*btf_bpf_map_update_elem)(struct bpf_map *, void *, void *, u64); typedef u64 (*btf_bpf_map_delete_elem)(struct bpf_map *, void *); typedef u64 (*btf_bpf_map_push_elem)(struct bpf_map *, void *, u64); typedef u64 (*btf_bpf_map_pop_elem)(struct bpf_map *, void *); typedef u64 (*btf_bpf_map_peek_elem)(struct bpf_map *, void *); typedef u64 (*btf_bpf_get_smp_processor_id)(); typedef u64 (*btf_bpf_get_numa_node_id)(); typedef u64 (*btf_bpf_ktime_get_ns)(); typedef u64 (*btf_bpf_ktime_get_boot_ns)(); typedef u64 (*btf_bpf_ktime_get_coarse_ns)(); typedef u64 (*btf_bpf_get_current_pid_tgid)(); typedef u64 (*btf_bpf_get_current_uid_gid)(); typedef u64 (*btf_bpf_get_current_comm)(char *, u32); typedef u64 (*btf_bpf_spin_lock)(struct bpf_spin_lock *); typedef u64 (*btf_bpf_spin_unlock)(struct bpf_spin_lock *); typedef u64 (*btf_bpf_jiffies64)(); typedef u64 (*btf_bpf_get_current_cgroup_id)(); typedef u64 (*btf_bpf_get_current_ancestor_cgroup_id)(int); typedef u64 (*btf_bpf_get_local_storage)(struct bpf_map *, u64); typedef u64 (*btf_bpf_strtol)(const char *, size_t, u64, long int *); typedef u64 (*btf_bpf_strtoul)(const char *, size_t, u64, long unsigned int *); typedef u64 (*btf_bpf_get_ns_current_pid_tgid)(u64, u64, struct bpf_pidns_info *, u32); typedef u64 (*btf_bpf_event_output_data)(void *, struct bpf_map *, u64, void *, u64); typedef u64 (*btf_bpf_copy_from_user)(void *, u32, const void *); typedef u64 (*btf_bpf_per_cpu_ptr)(const void *, u32); typedef u64 (*btf_bpf_this_cpu_ptr)(const void *); struct bpf_bprintf_buffers { char tmp_bufs[1536]; }; typedef u64 (*btf_bpf_snprintf)(char *, u32, char *, const void *, u32); union bpf_iter_link_info { struct { __u32 map_fd; } map; }; typedef int (*bpf_iter_attach_target_t)(struct bpf_prog *, union bpf_iter_link_info *, struct bpf_iter_aux_info *); typedef void (*bpf_iter_detach_target_t)(struct bpf_iter_aux_info *); typedef void (*bpf_iter_show_fdinfo_t)(const struct bpf_iter_aux_info *, struct seq_file *); typedef int (*bpf_iter_fill_link_info_t)(const struct bpf_iter_aux_info *, struct bpf_link_info *); enum bpf_iter_feature { BPF_ITER_RESCHED = 1, }; struct bpf_iter_reg { const char *target; bpf_iter_attach_target_t attach_target; bpf_iter_detach_target_t detach_target; bpf_iter_show_fdinfo_t show_fdinfo; bpf_iter_fill_link_info_t fill_link_info; u32 ctx_arg_info_size; u32 feature; struct bpf_ctx_arg_aux ctx_arg_info[2]; const struct bpf_iter_seq_info *seq_info; }; struct bpf_iter_meta { union { struct seq_file *seq; }; u64 session_id; u64 seq_num; }; struct bpf_iter_target_info { struct list_head list; const struct bpf_iter_reg *reg_info; u32 btf_id; }; struct bpf_iter_link { struct bpf_link link; struct bpf_iter_aux_info aux; struct bpf_iter_target_info *tinfo; }; struct bpf_iter_priv_data { struct bpf_iter_target_info *tinfo; const struct bpf_iter_seq_info *seq_info; struct bpf_prog *prog; u64 session_id; u64 seq_num; bool done_stop; long: 56; u8 target_private[0]; }; typedef u64 (*btf_bpf_for_each_map_elem)(struct bpf_map *, void *, void *, u64); struct bpf_iter_seq_map_info { u32 map_id; }; struct bpf_iter__bpf_map { union { struct bpf_iter_meta *meta; }; union { struct bpf_map *map; }; }; struct bpf_iter_seq_task_common { struct pid_namespace *ns; }; struct bpf_iter_seq_task_info { struct bpf_iter_seq_task_common common; u32 tid; }; struct bpf_iter__task { union { struct bpf_iter_meta *meta; }; union { struct task_struct *task; }; }; struct bpf_iter_seq_task_file_info { struct bpf_iter_seq_task_common common; struct task_struct *task; u32 tid; u32 fd; }; struct bpf_iter__task_file { union { struct bpf_iter_meta *meta; }; union { struct task_struct *task; }; u32 fd; union { struct file *file; }; }; struct bpf_iter_seq_task_vma_info { struct bpf_iter_seq_task_common common; struct task_struct *task; struct vm_area_struct *vma; u32 tid; long unsigned int prev_vm_start; long unsigned int prev_vm_end; }; enum bpf_task_vma_iter_find_op { task_vma_iter_first_vma = 0, task_vma_iter_next_vma = 1, task_vma_iter_find_vma = 2, }; struct bpf_iter__task_vma { union { struct bpf_iter_meta *meta; }; union { struct task_struct *task; }; union { struct vm_area_struct *vma; }; }; struct bpf_iter_seq_prog_info { u32 prog_id; }; struct bpf_iter__bpf_prog { union { struct bpf_iter_meta *meta; }; union { struct bpf_prog *prog; }; }; struct bpf_iter__bpf_map_elem { union { struct bpf_iter_meta *meta; }; union { struct bpf_map *map; }; union { void *key; }; union { void *value; }; }; struct pcpu_freelist_node; struct pcpu_freelist_head { struct pcpu_freelist_node *first; raw_spinlock_t lock; }; struct pcpu_freelist_node { struct pcpu_freelist_node *next; }; struct pcpu_freelist { struct pcpu_freelist_head *freelist; struct pcpu_freelist_head extralist; }; struct bpf_lru_node { struct list_head list; u16 cpu; u8 type; u8 ref; }; struct bpf_lru_list { struct list_head lists[3]; unsigned int counts[2]; struct list_head *next_inactive_rotation; raw_spinlock_t lock; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct bpf_lru_locallist { struct list_head lists[2]; u16 next_steal; raw_spinlock_t lock; }; struct bpf_common_lru { struct bpf_lru_list lru_list; struct bpf_lru_locallist *local_list; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; typedef bool (*del_from_htab_func)(void *, struct bpf_lru_node *); struct bpf_lru { union { struct bpf_common_lru common_lru; struct bpf_lru_list *percpu_lru; }; del_from_htab_func del_from_htab; void *del_arg; unsigned int hash_offset; unsigned int nr_scans; bool percpu; long: 56; long: 64; long: 64; long: 64; long: 64; }; struct bucket { struct hlist_nulls_head head; union { raw_spinlock_t raw_lock; spinlock_t lock; }; }; struct htab_elem; struct bpf_htab { struct bpf_map map; struct bucket *buckets; void *elems; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; union { struct pcpu_freelist freelist; struct bpf_lru lru; }; struct htab_elem **extra_elems; atomic_t count; u32 n_buckets; u32 elem_size; u32 hashrnd; struct lock_class_key lockdep_key; int *map_locked[8]; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct htab_elem { union { struct hlist_nulls_node hash_node; struct { void *padding; union { struct bpf_htab *htab; struct pcpu_freelist_node fnode; struct htab_elem *batch_flink; }; }; }; union { struct callback_head rcu; struct bpf_lru_node lru_node; }; u32 hash; int: 32; char key[0]; }; struct bpf_iter_seq_hash_map_info { struct bpf_map *map; struct bpf_htab *htab; void *percpu_value_buf; u32 bucket_id; u32 skip_elems; }; struct bpf_iter_seq_array_map_info { struct bpf_map *map; void *percpu_value_buf; u32 index; }; struct prog_poke_elem { struct list_head list; struct bpf_prog_aux *aux; }; enum bpf_lru_list_type { BPF_LRU_LIST_T_ACTIVE = 0, BPF_LRU_LIST_T_INACTIVE = 1, BPF_LRU_LIST_T_FREE = 2, BPF_LRU_LOCAL_LIST_T_FREE = 3, BPF_LRU_LOCAL_LIST_T_PENDING = 4, }; struct bpf_lpm_trie_key { __u32 prefixlen; __u8 data[0]; }; struct lpm_trie_node { struct callback_head rcu; struct lpm_trie_node *child[2]; u32 prefixlen; u32 flags; u8 data[0]; }; struct lpm_trie { struct bpf_map map; struct lpm_trie_node *root; size_t n_entries; size_t max_prefixlen; size_t data_size; spinlock_t lock; long: 32; long: 64; long: 64; long: 64; }; struct bpf_cgroup_storage_map { struct bpf_map map; spinlock_t lock; struct rb_root root; struct list_head list; long: 64; long: 64; long: 64; long: 64; }; struct bpf_queue_stack { struct bpf_map map; raw_spinlock_t lock; u32 head; u32 tail; u32 size; char elements[0]; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; enum { BPF_RB_NO_WAKEUP = 1, BPF_RB_FORCE_WAKEUP = 2, }; enum { BPF_RB_AVAIL_DATA = 0, BPF_RB_RING_SIZE = 1, BPF_RB_CONS_POS = 2, BPF_RB_PROD_POS = 3, }; enum { BPF_RINGBUF_BUSY_BIT = 2147483648, BPF_RINGBUF_DISCARD_BIT = 1073741824, BPF_RINGBUF_HDR_SZ = 8, }; struct bpf_ringbuf { wait_queue_head_t waitq; struct irq_work work; u64 mask; struct page **pages; int nr_pages; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; spinlock_t spinlock; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long unsigned int consumer_pos; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long unsigned int producer_pos; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; char data[0]; }; struct bpf_ringbuf_map { struct bpf_map map; struct bpf_ringbuf *rb; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct bpf_ringbuf_hdr { u32 len; u32 pg_off; }; typedef u64 (*btf_bpf_ringbuf_reserve)(struct bpf_map *, u64, u64); typedef u64 (*btf_bpf_ringbuf_submit)(void *, u64); typedef u64 (*btf_bpf_ringbuf_discard)(void *, u64); typedef u64 (*btf_bpf_ringbuf_output)(struct bpf_map *, void *, u64, u64); typedef u64 (*btf_bpf_ringbuf_query)(struct bpf_map *, u64); struct bpf_local_storage_elem { struct hlist_node map_node; struct hlist_node snode; struct bpf_local_storage *local_storage; struct callback_head rcu; long: 64; struct bpf_local_storage_data sdata; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct bpf_local_storage_cache { spinlock_t idx_lock; u64 idx_usage_counts[16]; }; enum { BPF_LOCAL_STORAGE_GET_F_CREATE = 1, BPF_SK_STORAGE_GET_F_CREATE = 1, }; typedef u64 (*btf_bpf_task_storage_get)(struct bpf_map *, struct task_struct *, void *, u64); typedef u64 (*btf_bpf_task_storage_delete)(struct bpf_map *, struct task_struct *); struct lsm_blob_sizes { int lbs_cred; int lbs_file; int lbs_inode; int lbs_superblock; int lbs_ipc; int lbs_msg_msg; int lbs_task; }; struct bpf_storage_blob { struct bpf_local_storage *storage; }; typedef u64 (*btf_bpf_inode_storage_get)(struct bpf_map *, struct inode *, void *, u64); typedef u64 (*btf_bpf_inode_storage_delete)(struct bpf_map *, struct inode *); struct btf_enum { __u32 name_off; __s32 val; }; struct btf_array { __u32 type; __u32 index_type; __u32 nelems; }; enum { BTF_VAR_STATIC = 0, BTF_VAR_GLOBAL_ALLOCATED = 1, BTF_VAR_GLOBAL_EXTERN = 2, }; struct btf_var { __u32 linkage; }; struct bpf_flow_keys { __u16 nhoff; __u16 thoff; __u16 addr_proto; __u8 is_frag; __u8 is_first_frag; __u8 is_encap; __u8 ip_proto; __be16 n_proto; __be16 sport; __be16 dport; union { struct { __be32 ipv4_src; __be32 ipv4_dst; }; struct { __u32 ipv6_src[4]; __u32 ipv6_dst[4]; }; }; __u32 flags; __be32 flow_label; }; struct bpf_sock { __u32 bound_dev_if; __u32 family; __u32 type; __u32 protocol; __u32 mark; __u32 priority; __u32 src_ip4; __u32 src_ip6[4]; __u32 src_port; __u32 dst_port; __u32 dst_ip4; __u32 dst_ip6[4]; __u32 state; __s32 rx_queue_mapping; }; struct __sk_buff { __u32 len; __u32 pkt_type; __u32 mark; __u32 queue_mapping; __u32 protocol; __u32 vlan_present; __u32 vlan_tci; __u32 vlan_proto; __u32 priority; __u32 ingress_ifindex; __u32 ifindex; __u32 tc_index; __u32 cb[5]; __u32 hash; __u32 tc_classid; __u32 data; __u32 data_end; __u32 napi_id; __u32 family; __u32 remote_ip4; __u32 local_ip4; __u32 remote_ip6[4]; __u32 local_ip6[4]; __u32 remote_port; __u32 local_port; __u32 data_meta; union { struct bpf_flow_keys *flow_keys; }; __u64 tstamp; __u32 wire_len; __u32 gso_segs; union { struct bpf_sock *sk; }; __u32 gso_size; }; struct xdp_md { __u32 data; __u32 data_end; __u32 data_meta; __u32 ingress_ifindex; __u32 rx_queue_index; __u32 egress_ifindex; }; struct sk_msg_md { union { void *data; }; union { void *data_end; }; __u32 family; __u32 remote_ip4; __u32 local_ip4; __u32 remote_ip6[4]; __u32 local_ip6[4]; __u32 remote_port; __u32 local_port; __u32 size; union { struct bpf_sock *sk; }; }; struct sk_reuseport_md { union { void *data; }; union { void *data_end; }; __u32 len; __u32 eth_protocol; __u32 ip_protocol; __u32 bind_inany; __u32 hash; union { struct bpf_sock *sk; }; union { struct bpf_sock *migrating_sk; }; }; struct bpf_sock_addr { __u32 user_family; __u32 user_ip4; __u32 user_ip6[4]; __u32 user_port; __u32 family; __u32 type; __u32 protocol; __u32 msg_src_ip4; __u32 msg_src_ip6[4]; union { struct bpf_sock *sk; }; }; struct bpf_sock_ops { __u32 op; union { __u32 args[4]; __u32 reply; __u32 replylong[4]; }; __u32 family; __u32 remote_ip4; __u32 local_ip4; __u32 remote_ip6[4]; __u32 local_ip6[4]; __u32 remote_port; __u32 local_port; __u32 is_fullsock; __u32 snd_cwnd; __u32 srtt_us; __u32 bpf_sock_ops_cb_flags; __u32 state; __u32 rtt_min; __u32 snd_ssthresh; __u32 rcv_nxt; __u32 snd_nxt; __u32 snd_una; __u32 mss_cache; __u32 ecn_flags; __u32 rate_delivered; __u32 rate_interval_us; __u32 packets_out; __u32 retrans_out; __u32 total_retrans; __u32 segs_in; __u32 data_segs_in; __u32 segs_out; __u32 data_segs_out; __u32 lost_out; __u32 sacked_out; __u32 sk_txhash; __u64 bytes_received; __u64 bytes_acked; union { struct bpf_sock *sk; }; union { void *skb_data; }; union { void *skb_data_end; }; __u32 skb_len; __u32 skb_tcp_flags; }; struct bpf_cgroup_dev_ctx { __u32 access_type; __u32 major; __u32 minor; }; struct bpf_sysctl { __u32 write; __u32 file_pos; }; struct bpf_sockopt { union { struct bpf_sock *sk; }; union { void *optval; }; union { void *optval_end; }; __s32 level; __s32 optname; __s32 optlen; __s32 retval; }; struct bpf_sk_lookup { union { union { struct bpf_sock *sk; }; __u64 cookie; }; __u32 family; __u32 protocol; __u32 remote_ip4; __u32 remote_ip6[4]; __u32 remote_port; __u32 local_ip4; __u32 local_ip6[4]; __u32 local_port; }; struct sk_reuseport_kern { struct sk_buff *skb; struct sock *sk; struct sock *selected_sk; struct sock *migrating_sk; void *data_end; u32 hash; u32 reuseport_id; bool bind_inany; }; struct bpf_flow_dissector { struct bpf_flow_keys *flow_keys; const struct sk_buff *skb; const void *data; const void *data_end; }; struct inet_listen_hashbucket { spinlock_t lock; unsigned int count; union { struct hlist_head head; struct hlist_nulls_head nulls_head; }; }; struct inet_ehash_bucket; struct inet_bind_hashbucket; struct inet_hashinfo { struct inet_ehash_bucket *ehash; spinlock_t *ehash_locks; unsigned int ehash_mask; unsigned int ehash_locks_mask; struct kmem_cache *bind_bucket_cachep; struct inet_bind_hashbucket *bhash; unsigned int bhash_size; unsigned int lhash2_mask; struct inet_listen_hashbucket *lhash2; long: 64; struct inet_listen_hashbucket listening_hash[32]; }; struct ip_ra_chain { struct ip_ra_chain *next; struct sock *sk; union { void (*destructor)(struct sock *); struct sock *saved_sk; }; struct callback_head rcu; }; struct fib_table { struct hlist_node tb_hlist; u32 tb_id; int tb_num_default; struct callback_head rcu; long unsigned int *tb_data; long unsigned int __data[0]; }; struct inet_peer_base { struct rb_root rb_root; seqlock_t lock; int total; }; struct tcp_fastopen_context { siphash_key_t key[2]; int num; struct callback_head rcu; }; struct xdp_txq_info { struct net_device *dev; }; struct xdp_buff { void *data; void *data_end; void *data_meta; void *data_hard_start; struct xdp_rxq_info *rxq; struct xdp_txq_info *txq; u32 frame_sz; }; struct bpf_sock_addr_kern { struct sock *sk; struct sockaddr *uaddr; u64 tmp_reg; void *t_ctx; }; struct bpf_sock_ops_kern { struct sock *sk; union { u32 args[4]; u32 reply; u32 replylong[4]; }; struct sk_buff *syn_skb; struct sk_buff *skb; void *skb_data_end; u8 op; u8 is_fullsock; u8 remaining_opt_len; u64 temp; }; struct bpf_sysctl_kern { struct ctl_table_header *head; struct ctl_table *table; void *cur_val; size_t cur_len; void *new_val; size_t new_len; int new_updated; int write; loff_t *ppos; u64 tmp_reg; }; struct bpf_sockopt_kern { struct sock *sk; u8 *optval; u8 *optval_end; s32 level; s32 optname; s32 optlen; s32 retval; }; struct bpf_sk_lookup_kern { u16 family; u16 protocol; __be16 sport; u16 dport; struct { __be32 saddr; __be32 daddr; } v4; struct { const struct in6_addr *saddr; const struct in6_addr *daddr; } v6; struct sock *selected_sk; bool no_reuseport; }; struct lwtunnel_state { __u16 type; __u16 flags; __u16 headroom; atomic_t refcnt; int (*orig_output)(struct net *, struct sock *, struct sk_buff *); int (*orig_input)(struct sk_buff *); struct callback_head rcu; __u8 data[0]; }; struct sock_reuseport { struct callback_head rcu; u16 max_socks; u16 num_socks; u16 num_closed_socks; unsigned int synq_overflow_ts; unsigned int reuseport_id; unsigned int bind_inany: 1; unsigned int has_conns: 1; struct bpf_prog *prog; struct sock *socks[0]; }; struct sk_psock_progs { struct bpf_prog *msg_parser; struct bpf_prog *stream_parser; struct bpf_prog *stream_verdict; struct bpf_prog *skb_verdict; }; struct strp_stats { long long unsigned int msgs; long long unsigned int bytes; unsigned int mem_fail; unsigned int need_more_hdr; unsigned int msg_too_big; unsigned int msg_timeouts; unsigned int bad_hdr_len; }; struct strparser; struct strp_callbacks { int (*parse_msg)(struct strparser *, struct sk_buff *); void (*rcv_msg)(struct strparser *, struct sk_buff *); int (*read_sock_done)(struct strparser *, int); void (*abort_parser)(struct strparser *, int); void (*lock)(struct strparser *); void (*unlock)(struct strparser *); }; struct strparser { struct sock *sk; u32 stopped: 1; u32 paused: 1; u32 aborted: 1; u32 interrupted: 1; u32 unrecov_intr: 1; struct sk_buff **skb_nextp; struct sk_buff *skb_head; unsigned int need_bytes; struct delayed_work msg_timer_work; struct work_struct work; struct strp_stats stats; struct strp_callbacks cb; }; struct sk_psock_work_state { struct sk_buff *skb; u32 len; u32 off; }; struct sk_msg; struct sk_psock { struct sock *sk; struct sock *sk_redir; u32 apply_bytes; u32 cork_bytes; u32 eval; struct sk_msg *cork; struct sk_psock_progs progs; struct strparser strp; struct sk_buff_head ingress_skb; struct list_head ingress_msg; spinlock_t ingress_lock; long unsigned int state; struct list_head link; spinlock_t link_lock; refcount_t refcnt; void (*saved_unhash)(struct sock *); void (*saved_close)(struct sock *, long int); void (*saved_write_space)(struct sock *); void (*saved_data_ready)(struct sock *); int (*psock_update_sk_prot)(struct sock *, struct sk_psock *, bool); struct proto *sk_proto; struct mutex work_mutex; struct sk_psock_work_state work_state; struct work_struct work; struct rcu_work rwork; }; struct inet_ehash_bucket { struct hlist_nulls_head chain; }; struct inet_bind_hashbucket { spinlock_t lock; struct hlist_head chain; }; struct ack_sample { u32 pkts_acked; s32 rtt_us; u32 in_flight; }; struct rate_sample { u64 prior_mstamp; u32 prior_delivered; s32 delivered; long int interval_us; u32 snd_interval_us; u32 rcv_interval_us; long int rtt_us; int losses; u32 acked_sacked; u32 prior_in_flight; bool is_app_limited; bool is_retrans; bool is_ack_delayed; }; struct sk_msg_sg { u32 start; u32 curr; u32 end; u32 size; u32 copybreak; long unsigned int copy; struct scatterlist data[19]; }; struct sk_msg { struct sk_msg_sg sg; void *data; void *data_end; u32 apply_bytes; u32 cork_bytes; u32 flags; struct sk_buff *skb; struct sock *sk_redir; struct sock *sk; struct list_head list; }; enum verifier_phase { CHECK_META = 0, CHECK_TYPE = 1, }; struct resolve_vertex { const struct btf_type *t; u32 type_id; u16 next_member; }; enum visit_state { NOT_VISITED = 0, VISITED = 1, RESOLVED = 2, }; enum resolve_mode { RESOLVE_TBD = 0, RESOLVE_PTR = 1, RESOLVE_STRUCT_OR_ARRAY = 2, }; struct btf_sec_info { u32 off; u32 len; }; struct btf_verifier_env { struct btf *btf; u8 *visit_states; struct resolve_vertex stack[32]; struct bpf_verifier_log log; u32 log_type_id; u32 top_stack; enum verifier_phase phase; enum resolve_mode resolve_mode; }; struct btf_show { u64 flags; void *target; void (*showfn)(struct btf_show *, const char *, struct __va_list_tag *); const struct btf *btf; struct { u8 depth; u8 depth_to_show; u8 depth_check; u8 array_member: 1; u8 array_terminated: 1; u16 array_encoding; u32 type_id; int status; const struct btf_type *type; const struct btf_member *member; char name[80]; } state; struct { u32 size; void *head; void *data; u8 safe[32]; } obj; }; struct btf_kind_operations { s32 (*check_meta)(struct btf_verifier_env *, const struct btf_type *, u32); int (*resolve)(struct btf_verifier_env *, const struct resolve_vertex *); int (*check_member)(struct btf_verifier_env *, const struct btf_type *, const struct btf_member *, const struct btf_type *); int (*check_kflag_member)(struct btf_verifier_env *, const struct btf_type *, const struct btf_member *, const struct btf_type *); void (*log_details)(struct btf_verifier_env *, const struct btf_type *); void (*show)(const struct btf *, const struct btf_type *, u32, void *, u8, struct btf_show *); }; struct bpf_ctx_convert { struct __sk_buff BPF_PROG_TYPE_SOCKET_FILTER_prog; struct sk_buff BPF_PROG_TYPE_SOCKET_FILTER_kern; struct __sk_buff BPF_PROG_TYPE_SCHED_CLS_prog; struct sk_buff BPF_PROG_TYPE_SCHED_CLS_kern; struct __sk_buff BPF_PROG_TYPE_SCHED_ACT_prog; struct sk_buff BPF_PROG_TYPE_SCHED_ACT_kern; struct xdp_md BPF_PROG_TYPE_XDP_prog; struct xdp_buff BPF_PROG_TYPE_XDP_kern; struct __sk_buff BPF_PROG_TYPE_CGROUP_SKB_prog; struct sk_buff BPF_PROG_TYPE_CGROUP_SKB_kern; struct bpf_sock BPF_PROG_TYPE_CGROUP_SOCK_prog; struct sock BPF_PROG_TYPE_CGROUP_SOCK_kern; struct bpf_sock_addr BPF_PROG_TYPE_CGROUP_SOCK_ADDR_prog; struct bpf_sock_addr_kern BPF_PROG_TYPE_CGROUP_SOCK_ADDR_kern; struct __sk_buff BPF_PROG_TYPE_LWT_IN_prog; struct sk_buff BPF_PROG_TYPE_LWT_IN_kern; struct __sk_buff BPF_PROG_TYPE_LWT_OUT_prog; struct sk_buff BPF_PROG_TYPE_LWT_OUT_kern; struct __sk_buff BPF_PROG_TYPE_LWT_XMIT_prog; struct sk_buff BPF_PROG_TYPE_LWT_XMIT_kern; struct __sk_buff BPF_PROG_TYPE_LWT_SEG6LOCAL_prog; struct sk_buff BPF_PROG_TYPE_LWT_SEG6LOCAL_kern; struct bpf_sock_ops BPF_PROG_TYPE_SOCK_OPS_prog; struct bpf_sock_ops_kern BPF_PROG_TYPE_SOCK_OPS_kern; struct __sk_buff BPF_PROG_TYPE_SK_SKB_prog; struct sk_buff BPF_PROG_TYPE_SK_SKB_kern; struct sk_msg_md BPF_PROG_TYPE_SK_MSG_prog; struct sk_msg BPF_PROG_TYPE_SK_MSG_kern; struct __sk_buff BPF_PROG_TYPE_FLOW_DISSECTOR_prog; struct bpf_flow_dissector BPF_PROG_TYPE_FLOW_DISSECTOR_kern; bpf_user_pt_regs_t BPF_PROG_TYPE_KPROBE_prog; struct pt_regs BPF_PROG_TYPE_KPROBE_kern; __u64 BPF_PROG_TYPE_TRACEPOINT_prog; u64 BPF_PROG_TYPE_TRACEPOINT_kern; struct bpf_perf_event_data BPF_PROG_TYPE_PERF_EVENT_prog; struct bpf_perf_event_data_kern BPF_PROG_TYPE_PERF_EVENT_kern; struct bpf_raw_tracepoint_args BPF_PROG_TYPE_RAW_TRACEPOINT_prog; u64 BPF_PROG_TYPE_RAW_TRACEPOINT_kern; struct bpf_raw_tracepoint_args BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE_prog; u64 BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE_kern; void *BPF_PROG_TYPE_TRACING_prog; void *BPF_PROG_TYPE_TRACING_kern; struct bpf_cgroup_dev_ctx BPF_PROG_TYPE_CGROUP_DEVICE_prog; struct bpf_cgroup_dev_ctx BPF_PROG_TYPE_CGROUP_DEVICE_kern; struct bpf_sysctl BPF_PROG_TYPE_CGROUP_SYSCTL_prog; struct bpf_sysctl_kern BPF_PROG_TYPE_CGROUP_SYSCTL_kern; struct bpf_sockopt BPF_PROG_TYPE_CGROUP_SOCKOPT_prog; struct bpf_sockopt_kern BPF_PROG_TYPE_CGROUP_SOCKOPT_kern; __u32 BPF_PROG_TYPE_LIRC_MODE2_prog; u32 BPF_PROG_TYPE_LIRC_MODE2_kern; struct sk_reuseport_md BPF_PROG_TYPE_SK_REUSEPORT_prog; struct sk_reuseport_kern BPF_PROG_TYPE_SK_REUSEPORT_kern; struct bpf_sk_lookup BPF_PROG_TYPE_SK_LOOKUP_prog; struct bpf_sk_lookup_kern BPF_PROG_TYPE_SK_LOOKUP_kern; void *BPF_PROG_TYPE_STRUCT_OPS_prog; void *BPF_PROG_TYPE_STRUCT_OPS_kern; void *BPF_PROG_TYPE_EXT_prog; void *BPF_PROG_TYPE_EXT_kern; void *BPF_PROG_TYPE_LSM_prog; void *BPF_PROG_TYPE_LSM_kern; void *BPF_PROG_TYPE_SYSCALL_prog; void *BPF_PROG_TYPE_SYSCALL_kern; }; enum { __ctx_convertBPF_PROG_TYPE_SOCKET_FILTER = 0, __ctx_convertBPF_PROG_TYPE_SCHED_CLS = 1, __ctx_convertBPF_PROG_TYPE_SCHED_ACT = 2, __ctx_convertBPF_PROG_TYPE_XDP = 3, __ctx_convertBPF_PROG_TYPE_CGROUP_SKB = 4, __ctx_convertBPF_PROG_TYPE_CGROUP_SOCK = 5, __ctx_convertBPF_PROG_TYPE_CGROUP_SOCK_ADDR = 6, __ctx_convertBPF_PROG_TYPE_LWT_IN = 7, __ctx_convertBPF_PROG_TYPE_LWT_OUT = 8, __ctx_convertBPF_PROG_TYPE_LWT_XMIT = 9, __ctx_convertBPF_PROG_TYPE_LWT_SEG6LOCAL = 10, __ctx_convertBPF_PROG_TYPE_SOCK_OPS = 11, __ctx_convertBPF_PROG_TYPE_SK_SKB = 12, __ctx_convertBPF_PROG_TYPE_SK_MSG = 13, __ctx_convertBPF_PROG_TYPE_FLOW_DISSECTOR = 14, __ctx_convertBPF_PROG_TYPE_KPROBE = 15, __ctx_convertBPF_PROG_TYPE_TRACEPOINT = 16, __ctx_convertBPF_PROG_TYPE_PERF_EVENT = 17, __ctx_convertBPF_PROG_TYPE_RAW_TRACEPOINT = 18, __ctx_convertBPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE = 19, __ctx_convertBPF_PROG_TYPE_TRACING = 20, __ctx_convertBPF_PROG_TYPE_CGROUP_DEVICE = 21, __ctx_convertBPF_PROG_TYPE_CGROUP_SYSCTL = 22, __ctx_convertBPF_PROG_TYPE_CGROUP_SOCKOPT = 23, __ctx_convertBPF_PROG_TYPE_LIRC_MODE2 = 24, __ctx_convertBPF_PROG_TYPE_SK_REUSEPORT = 25, __ctx_convertBPF_PROG_TYPE_SK_LOOKUP = 26, __ctx_convertBPF_PROG_TYPE_STRUCT_OPS = 27, __ctx_convertBPF_PROG_TYPE_EXT = 28, __ctx_convertBPF_PROG_TYPE_LSM = 29, __ctx_convertBPF_PROG_TYPE_SYSCALL = 30, __ctx_convert_unused = 31, }; enum bpf_struct_walk_result { WALK_SCALAR = 0, WALK_PTR = 1, WALK_STRUCT = 2, }; struct btf_show_snprintf { struct btf_show show; int len_left; int len; }; struct btf_module { struct list_head list; struct module *module; struct btf *btf; struct bin_attribute *sysfs_attr; }; typedef u64 (*btf_bpf_btf_find_by_name_kind)(char *, int, u32, int); struct bpf_dispatcher_prog { struct bpf_prog *prog; refcount_t users; }; struct bpf_dispatcher { struct mutex mutex; void *func; struct bpf_dispatcher_prog progs[48]; int num_progs; void *image; u32 image_off; struct bpf_ksym ksym; }; enum { BPF_F_BROADCAST = 8, BPF_F_EXCLUDE_INGRESS = 16, }; struct bpf_devmap_val { __u32 ifindex; union { int fd; __u32 id; } bpf_prog; }; enum net_device_flags { IFF_UP = 1, IFF_BROADCAST = 2, IFF_DEBUG = 4, IFF_LOOPBACK = 8, IFF_POINTOPOINT = 16, IFF_NOTRAILERS = 32, IFF_RUNNING = 64, IFF_NOARP = 128, IFF_PROMISC = 256, IFF_ALLMULTI = 512, IFF_MASTER = 1024, IFF_SLAVE = 2048, IFF_MULTICAST = 4096, IFF_PORTSEL = 8192, IFF_AUTOMEDIA = 16384, IFF_DYNAMIC = 32768, IFF_LOWER_UP = 65536, IFF_DORMANT = 131072, IFF_ECHO = 262144, }; struct xdp_dev_bulk_queue { struct xdp_frame *q[16]; struct list_head flush_node; struct net_device *dev; struct net_device *dev_rx; struct bpf_prog *xdp_prog; unsigned int count; }; enum netdev_cmd { NETDEV_UP = 1, NETDEV_DOWN = 2, NETDEV_REBOOT = 3, NETDEV_CHANGE = 4, NETDEV_REGISTER = 5, NETDEV_UNREGISTER = 6, NETDEV_CHANGEMTU = 7, NETDEV_CHANGEADDR = 8, NETDEV_PRE_CHANGEADDR = 9, NETDEV_GOING_DOWN = 10, NETDEV_CHANGENAME = 11, NETDEV_FEAT_CHANGE = 12, NETDEV_BONDING_FAILOVER = 13, NETDEV_PRE_UP = 14, NETDEV_PRE_TYPE_CHANGE = 15, NETDEV_POST_TYPE_CHANGE = 16, NETDEV_POST_INIT = 17, NETDEV_RELEASE = 18, NETDEV_NOTIFY_PEERS = 19, NETDEV_JOIN = 20, NETDEV_CHANGEUPPER = 21, NETDEV_RESEND_IGMP = 22, NETDEV_PRECHANGEMTU = 23, NETDEV_CHANGEINFODATA = 24, NETDEV_BONDING_INFO = 25, NETDEV_PRECHANGEUPPER = 26, NETDEV_CHANGELOWERSTATE = 27, NETDEV_UDP_TUNNEL_PUSH_INFO = 28, NETDEV_UDP_TUNNEL_DROP_INFO = 29, NETDEV_CHANGE_TX_QUEUE_LEN = 30, NETDEV_CVLAN_FILTER_PUSH_INFO = 31, NETDEV_CVLAN_FILTER_DROP_INFO = 32, NETDEV_SVLAN_FILTER_PUSH_INFO = 33, NETDEV_SVLAN_FILTER_DROP_INFO = 34, }; struct netdev_notifier_info { struct net_device *dev; struct netlink_ext_ack *extack; }; struct bpf_nh_params { u32 nh_family; union { u32 ipv4_nh; struct in6_addr ipv6_nh; }; }; struct bpf_redirect_info { u32 flags; u32 tgt_index; void *tgt_value; struct bpf_map *map; u32 map_id; enum bpf_map_type map_type; u32 kern_flags; struct bpf_nh_params nh; }; struct bpf_dtab; struct bpf_dtab_netdev { struct net_device *dev; struct hlist_node index_hlist; struct bpf_dtab *dtab; struct bpf_prog *xdp_prog; struct callback_head rcu; unsigned int idx; struct bpf_devmap_val val; }; struct bpf_dtab { struct bpf_map map; struct bpf_dtab_netdev **netdev_map; struct list_head list; struct hlist_head *dev_index_head; spinlock_t index_lock; unsigned int items; u32 n_buckets; long: 32; long: 64; long: 64; }; struct bpf_cpumap_val { __u32 qsize; union { int fd; __u32 id; } bpf_prog; }; struct bpf_cpu_map_entry; struct xdp_bulk_queue { void *q[8]; struct list_head flush_node; struct bpf_cpu_map_entry *obj; unsigned int count; }; struct bpf_cpu_map; struct bpf_cpu_map_entry { u32 cpu; int map_id; struct xdp_bulk_queue *bulkq; struct bpf_cpu_map *cmap; struct ptr_ring *queue; struct task_struct *kthread; struct bpf_cpumap_val value; struct bpf_prog *prog; atomic_t refcnt; struct callback_head rcu; struct work_struct kthread_stop_wq; }; struct bpf_cpu_map { struct bpf_map map; struct bpf_cpu_map_entry **cpu_map; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct rhlist_head { struct rhash_head rhead; struct rhlist_head *next; }; struct bpf_prog_offload_ops { int (*insn_hook)(struct bpf_verifier_env *, int, int); int (*finalize)(struct bpf_verifier_env *); int (*replace_insn)(struct bpf_verifier_env *, u32, struct bpf_insn *); int (*remove_insns)(struct bpf_verifier_env *, u32, u32); int (*prepare)(struct bpf_prog *); int (*translate)(struct bpf_prog *); void (*destroy)(struct bpf_prog *); }; struct bpf_offload_dev { const struct bpf_prog_offload_ops *ops; struct list_head netdevs; void *priv; }; typedef struct ns_common *ns_get_path_helper_t(void *); struct bpf_offload_netdev { struct rhash_head l; struct net_device *netdev; struct bpf_offload_dev *offdev; struct list_head progs; struct list_head maps; struct list_head offdev_netdevs; }; struct ns_get_path_bpf_prog_args { struct bpf_prog *prog; struct bpf_prog_info *info; }; struct ns_get_path_bpf_map_args { struct bpf_offloaded_map *offmap; struct bpf_map_info *info; }; struct bpf_netns_link { struct bpf_link link; enum bpf_attach_type type; enum netns_bpf_attach_type netns_type; struct net *net; struct list_head node; }; enum bpf_stack_build_id_status { BPF_STACK_BUILD_ID_EMPTY = 0, BPF_STACK_BUILD_ID_VALID = 1, BPF_STACK_BUILD_ID_IP = 2, }; struct bpf_stack_build_id { __s32 status; unsigned char build_id[20]; union { __u64 offset; __u64 ip; }; }; enum { BPF_F_SKIP_FIELD_MASK = 255, BPF_F_USER_STACK = 256, BPF_F_FAST_STACK_CMP = 512, BPF_F_REUSE_STACKID = 1024, BPF_F_USER_BUILD_ID = 2048, }; enum perf_callchain_context { PERF_CONTEXT_HV = 4294967264, PERF_CONTEXT_KERNEL = 4294967168, PERF_CONTEXT_USER = 4294966784, PERF_CONTEXT_GUEST = 4294965248, PERF_CONTEXT_GUEST_KERNEL = 4294965120, PERF_CONTEXT_GUEST_USER = 4294964736, PERF_CONTEXT_MAX = 4294963201, }; struct stack_map_bucket { struct pcpu_freelist_node fnode; u32 hash; u32 nr; u64 data[0]; }; struct bpf_stack_map { struct bpf_map map; void *elems; struct pcpu_freelist freelist; u32 n_buckets; struct stack_map_bucket *buckets[0]; long: 64; long: 64; long: 64; }; struct stack_map_irq_work { struct irq_work irq_work; struct mm_struct *mm; }; typedef u64 (*btf_bpf_get_stackid)(struct pt_regs *, struct bpf_map *, u64); typedef u64 (*btf_bpf_get_stackid_pe)(struct bpf_perf_event_data_kern *, struct bpf_map *, u64); typedef u64 (*btf_bpf_get_stack)(struct pt_regs *, void *, u32, u64); typedef u64 (*btf_bpf_get_task_stack)(struct task_struct *, void *, u32, u64); typedef u64 (*btf_bpf_get_stack_pe)(struct bpf_perf_event_data_kern *, void *, u32, u64); enum { BPF_F_SYSCTL_BASE_NAME = 1, }; struct bpf_prog_list { struct list_head node; struct bpf_prog *prog; struct bpf_cgroup_link *link; struct bpf_cgroup_storage *storage[2]; }; struct qdisc_skb_cb { struct { unsigned int pkt_len; u16 slave_dev_queue_mapping; u16 tc_classid; }; unsigned char data[20]; u16 mru; bool post_ct; }; struct bpf_skb_data_end { struct qdisc_skb_cb qdisc_cb; void *data_meta; void *data_end; }; struct bpf_sockopt_buf { u8 data[32]; }; enum { TCPF_ESTABLISHED = 2, TCPF_SYN_SENT = 4, TCPF_SYN_RECV = 8, TCPF_FIN_WAIT1 = 16, TCPF_FIN_WAIT2 = 32, TCPF_TIME_WAIT = 64, TCPF_CLOSE = 128, TCPF_CLOSE_WAIT = 256, TCPF_LAST_ACK = 512, TCPF_LISTEN = 1024, TCPF_CLOSING = 2048, TCPF_NEW_SYN_RECV = 4096, }; typedef u64 (*btf_bpf_sysctl_get_name)(struct bpf_sysctl_kern *, char *, size_t, u64); typedef u64 (*btf_bpf_sysctl_get_current_value)(struct bpf_sysctl_kern *, char *, size_t); typedef u64 (*btf_bpf_sysctl_get_new_value)(struct bpf_sysctl_kern *, char *, size_t); typedef u64 (*btf_bpf_sysctl_set_new_value)(struct bpf_sysctl_kern *, const char *, size_t); enum sock_type { SOCK_STREAM = 1, SOCK_DGRAM = 2, SOCK_RAW = 3, SOCK_RDM = 4, SOCK_SEQPACKET = 5, SOCK_DCCP = 6, SOCK_PACKET = 10, }; enum { IPPROTO_IP = 0, IPPROTO_ICMP = 1, IPPROTO_IGMP = 2, IPPROTO_IPIP = 4, IPPROTO_TCP = 6, IPPROTO_EGP = 8, IPPROTO_PUP = 12, IPPROTO_UDP = 17, IPPROTO_IDP = 22, IPPROTO_TP = 29, IPPROTO_DCCP = 33, IPPROTO_IPV6 = 41, IPPROTO_RSVP = 46, IPPROTO_GRE = 47, IPPROTO_ESP = 50, IPPROTO_AH = 51, IPPROTO_MTP = 92, IPPROTO_BEETPH = 94, IPPROTO_ENCAP = 98, IPPROTO_PIM = 103, IPPROTO_COMP = 108, IPPROTO_SCTP = 132, IPPROTO_UDPLITE = 136, IPPROTO_MPLS = 137, IPPROTO_ETHERNET = 143, IPPROTO_RAW = 255, IPPROTO_MPTCP = 262, IPPROTO_MAX = 263, }; enum sock_flags { SOCK_DEAD = 0, SOCK_DONE = 1, SOCK_URGINLINE = 2, SOCK_KEEPOPEN = 3, SOCK_LINGER = 4, SOCK_DESTROY = 5, SOCK_BROADCAST = 6, SOCK_TIMESTAMP = 7, SOCK_ZAPPED = 8, SOCK_USE_WRITE_QUEUE = 9, SOCK_DBG = 10, SOCK_RCVTSTAMP = 11, SOCK_RCVTSTAMPNS = 12, SOCK_LOCALROUTE = 13, SOCK_MEMALLOC = 14, SOCK_TIMESTAMPING_RX_SOFTWARE = 15, SOCK_FASYNC = 16, SOCK_RXQ_OVFL = 17, SOCK_ZEROCOPY = 18, SOCK_WIFI_STATUS = 19, SOCK_NOFCS = 20, SOCK_FILTER_LOCKED = 21, SOCK_SELECT_ERR_QUEUE = 22, SOCK_RCU_FREE = 23, SOCK_TXTIME = 24, SOCK_XDP = 25, SOCK_TSTAMP_NEW = 26, }; struct reuseport_array { struct bpf_map map; struct sock *ptrs[0]; }; enum bpf_struct_ops_state { BPF_STRUCT_OPS_STATE_INIT = 0, BPF_STRUCT_OPS_STATE_INUSE = 1, BPF_STRUCT_OPS_STATE_TOBEFREE = 2, }; struct bpf_struct_ops_value { refcount_t refcnt; enum bpf_struct_ops_state state; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; char data[0]; }; struct bpf_struct_ops_map { struct bpf_map map; const struct bpf_struct_ops *st_ops; struct mutex lock; struct bpf_prog **progs; void *image; struct bpf_struct_ops_value *uvalue; struct bpf_struct_ops_value kvalue; }; struct bpf_struct_ops_tcp_congestion_ops { refcount_t refcnt; enum bpf_struct_ops_state state; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; struct tcp_congestion_ops data; }; struct sembuf { short unsigned int sem_num; short int sem_op; short int sem_flg; }; enum key_need_perm { KEY_NEED_UNSPECIFIED = 0, KEY_NEED_VIEW = 1, KEY_NEED_READ = 2, KEY_NEED_WRITE = 3, KEY_NEED_SEARCH = 4, KEY_NEED_LINK = 5, KEY_NEED_SETATTR = 6, KEY_NEED_UNLINK = 7, KEY_SYSADMIN_OVERRIDE = 8, KEY_AUTHTOKEN_OVERRIDE = 9, KEY_DEFER_PERM_CHECK = 10, }; struct __key_reference_with_attributes; typedef struct __key_reference_with_attributes *key_ref_t; struct xfrm_sec_ctx { __u8 ctx_doi; __u8 ctx_alg; __u16 ctx_len; __u32 ctx_sid; char ctx_str[0]; }; struct xfrm_user_sec_ctx { __u16 len; __u16 exttype; __u8 ctx_alg; __u8 ctx_doi; __u16 ctx_len; }; enum { BPF_F_BPRM_SECUREEXEC = 1, }; typedef u64 (*btf_bpf_bprm_opts_set)(struct linux_binprm *, u64); typedef u64 (*btf_bpf_ima_inode_hash)(struct inode *, void *, u32); struct static_call_tramp_key { s32 tramp; s32 key; }; enum perf_event_read_format { PERF_FORMAT_TOTAL_TIME_ENABLED = 1, PERF_FORMAT_TOTAL_TIME_RUNNING = 2, PERF_FORMAT_ID = 4, PERF_FORMAT_GROUP = 8, PERF_FORMAT_MAX = 16, }; enum perf_event_ioc_flags { PERF_IOC_FLAG_GROUP = 1, }; struct perf_ns_link_info { __u64 dev; __u64 ino; }; enum { NET_NS_INDEX = 0, UTS_NS_INDEX = 1, IPC_NS_INDEX = 2, PID_NS_INDEX = 3, USER_NS_INDEX = 4, MNT_NS_INDEX = 5, CGROUP_NS_INDEX = 6, NR_NAMESPACES = 7, }; enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_TEXT_POKE = 20, PERF_RECORD_MAX = 21, }; struct swevent_hlist { struct hlist_head heads[256]; struct callback_head callback_head; }; struct pmu_event_list { raw_spinlock_t lock; struct list_head list; }; struct perf_buffer { refcount_t refcount; struct callback_head callback_head; int nr_pages; int overwrite; int paused; atomic_t poll; local_t head; unsigned int nest; local_t events; local_t wakeup; local_t lost; long int watermark; long int aux_watermark; spinlock_t event_lock; struct list_head event_list; atomic_t mmap_count; long unsigned int mmap_locked; struct user_struct *mmap_user; long int aux_head; unsigned int aux_nest; long int aux_wakeup; long unsigned int aux_pgoff; int aux_nr_pages; int aux_overwrite; atomic_t aux_mmap_count; long unsigned int aux_mmap_locked; void (*free_aux)(void *); refcount_t aux_refcount; int aux_in_sampling; void **aux_pages; void *aux_priv; struct perf_event_mmap_page *user_page; void *data_pages[0]; }; struct match_token { int token; const char *pattern; }; enum { MAX_OPT_ARGS = 3, }; struct min_heap { void *data; int nr; int size; }; struct min_heap_callbacks { int elem_size; bool (*less)(const void *, const void *); void (*swp)(void *, void *); }; typedef int (*remote_function_f)(void *); struct remote_function_call { struct task_struct *p; remote_function_f func; void *info; int ret; }; typedef void (*event_f)(struct perf_event *, struct perf_cpu_context *, struct perf_event_context *, void *); struct event_function_struct { struct perf_event *event; event_f func; void *data; }; enum event_type_t { EVENT_FLEXIBLE = 1, EVENT_PINNED = 2, EVENT_TIME = 4, EVENT_CPU = 8, EVENT_ALL = 3, }; struct __group_key { int cpu; struct cgroup *cgroup; }; struct stop_event_data { struct perf_event *event; unsigned int restart; }; struct perf_read_data { struct perf_event *event; bool group; int ret; }; struct perf_read_event { struct perf_event_header header; u32 pid; u32 tid; }; typedef void perf_iterate_f(struct perf_event *, void *); struct remote_output { struct perf_buffer *rb; int err; }; struct perf_task_event { struct task_struct *task; struct perf_event_context *task_ctx; struct { struct perf_event_header header; u32 pid; u32 ppid; u32 tid; u32 ptid; u64 time; } event_id; }; struct perf_comm_event { struct task_struct *task; char *comm; int comm_size; struct { struct perf_event_header header; u32 pid; u32 tid; } event_id; }; struct perf_namespaces_event { struct task_struct *task; struct { struct perf_event_header header; u32 pid; u32 tid; u64 nr_namespaces; struct perf_ns_link_info link_info[7]; } event_id; }; struct perf_cgroup_event { char *path; int path_size; struct { struct perf_event_header header; u64 id; char path[0]; } event_id; }; struct perf_mmap_event { struct vm_area_struct *vma; const char *file_name; int file_size; int maj; int min; u64 ino; u64 ino_generation; u32 prot; u32 flags; u8 build_id[20]; u32 build_id_size; struct { struct perf_event_header header; u32 pid; u32 tid; u64 start; u64 len; u64 pgoff; } event_id; }; struct perf_switch_event { struct task_struct *task; struct task_struct *next_prev; struct { struct perf_event_header header; u32 next_prev_pid; u32 next_prev_tid; } event_id; }; struct perf_ksymbol_event { const char *name; int name_len; struct { struct perf_event_header header; u64 addr; u32 len; u16 ksym_type; u16 flags; } event_id; }; struct perf_bpf_event { struct bpf_prog *prog; struct { struct perf_event_header header; u16 type; u16 flags; u32 id; u8 tag[8]; } event_id; }; struct perf_text_poke_event { const void *old_bytes; const void *new_bytes; size_t pad; u16 old_len; u16 new_len; struct { struct perf_event_header header; u64 addr; } event_id; }; struct swevent_htable { struct swevent_hlist *swevent_hlist; struct mutex hlist_mutex; int hlist_refcount; int recursion[4]; }; enum perf_probe_config { PERF_PROBE_CONFIG_IS_RETPROBE = 1, PERF_UPROBE_REF_CTR_OFFSET_BITS = 32, PERF_UPROBE_REF_CTR_OFFSET_SHIFT = 32, }; enum { IF_ACT_NONE = 4294967295, IF_ACT_FILTER = 0, IF_ACT_START = 1, IF_ACT_STOP = 2, IF_SRC_FILE = 3, IF_SRC_KERNEL = 4, IF_SRC_FILEADDR = 5, IF_SRC_KERNELADDR = 6, }; enum { IF_STATE_ACTION = 0, IF_STATE_SOURCE = 1, IF_STATE_END = 2, }; struct perf_aux_event { struct perf_event_header header; u32 pid; u32 tid; }; struct perf_aux_event___2 { struct perf_event_header header; u64 offset; u64 size; u64 flags; }; struct callchain_cpus_entries { struct callback_head callback_head; struct perf_callchain_entry *cpu_entries[0]; }; enum bp_type_idx { TYPE_INST = 0, TYPE_DATA = 0, TYPE_MAX = 1, }; struct bp_cpuinfo { unsigned int cpu_pinned; unsigned int *tsk_pinned; unsigned int flexible; }; struct bp_busy_slots { unsigned int pinned; unsigned int flexible; }; typedef u8 uprobe_opcode_t; struct uprobe { struct rb_node rb_node; refcount_t ref; struct rw_semaphore register_rwsem; struct rw_semaphore consumer_rwsem; struct list_head pending_list; struct uprobe_consumer *consumers; struct inode *inode; loff_t offset; loff_t ref_ctr_offset; long unsigned int flags; struct arch_uprobe arch; }; struct xol_area { wait_queue_head_t wq; atomic_t slot_count; long unsigned int *bitmap; struct vm_special_mapping xol_mapping; struct page *pages[2]; long unsigned int vaddr; }; struct compact_control; struct capture_control { struct compact_control *cc; struct page *page; }; typedef int filler_t(void *, struct page *); struct page_vma_mapped_walk { struct page *page; struct vm_area_struct *vma; long unsigned int address; pmd_t *pmd; pte_t *pte; spinlock_t *ptl; unsigned int flags; }; struct compact_control { struct list_head freepages; struct list_head migratepages; unsigned int nr_freepages; unsigned int nr_migratepages; long unsigned int free_pfn; long unsigned int migrate_pfn; long unsigned int fast_start_pfn; struct zone *zone; long unsigned int total_migrate_scanned; long unsigned int total_free_scanned; short unsigned int fast_search_fail; short int search_order; const gfp_t gfp_mask; int order; int migratetype; const unsigned int alloc_flags; const int highest_zoneidx; enum migrate_mode mode; bool ignore_skip_hint; bool no_set_skip_hint; bool ignore_block_suitable; bool direct_compaction; bool proactive_compaction; bool whole_zone; bool contended; bool rescan; bool alloc_contig; }; struct delayed_uprobe { struct list_head list; struct uprobe *uprobe; struct mm_struct *mm; }; struct __uprobe_key { struct inode *inode; loff_t offset; }; struct map_info { struct map_info *next; struct mm_struct *mm; long unsigned int vaddr; }; struct user_return_notifier { void (*on_user_return)(struct user_return_notifier *); struct hlist_node link; }; struct parallel_data; struct padata_priv { struct list_head list; struct parallel_data *pd; int cb_cpu; unsigned int seq_nr; int info; void (*parallel)(struct padata_priv *); void (*serial)(struct padata_priv *); }; struct padata_cpumask { cpumask_var_t pcpu; cpumask_var_t cbcpu; }; struct padata_shell; struct padata_list; struct padata_serial_queue; struct parallel_data { struct padata_shell *ps; struct padata_list *reorder_list; struct padata_serial_queue *squeue; atomic_t refcnt; unsigned int seq_nr; unsigned int processed; int cpu; struct padata_cpumask cpumask; struct work_struct reorder_work; long: 64; long: 64; long: 64; long: 64; long: 64; spinlock_t lock; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct padata_list { struct list_head list; spinlock_t lock; }; struct padata_serial_queue { struct padata_list serial; struct work_struct work; struct parallel_data *pd; }; struct padata_instance; struct padata_shell { struct padata_instance *pinst; struct parallel_data *pd; struct parallel_data *opd; struct list_head list; }; struct padata_instance { struct hlist_node cpu_online_node; struct hlist_node cpu_dead_node; struct workqueue_struct *parallel_wq; struct workqueue_struct *serial_wq; struct list_head pslist; struct padata_cpumask cpumask; struct kobject kobj; struct mutex lock; u8 flags; }; struct padata_mt_job { void (*thread_fn)(long unsigned int, long unsigned int, void *); void *fn_arg; long unsigned int start; long unsigned int size; long unsigned int align; long unsigned int min_chunk; int max_threads; }; struct padata_work { struct work_struct pw_work; struct list_head pw_list; void *pw_data; }; struct padata_mt_job_state { spinlock_t lock; struct completion completion; struct padata_mt_job *job; int nworks; int nworks_fini; long unsigned int chunk_size; }; struct padata_sysfs_entry { struct attribute attr; ssize_t (*show)(struct padata_instance *, struct attribute *, char *); ssize_t (*store)(struct padata_instance *, struct attribute *, const char *, size_t); }; struct static_key_mod { struct static_key_mod *next; struct jump_entry *entries; struct module *mod; }; struct static_key_deferred { struct static_key key; long unsigned int timeout; struct delayed_work work; }; enum rseq_cpu_id_state { RSEQ_CPU_ID_UNINITIALIZED = 4294967295, RSEQ_CPU_ID_REGISTRATION_FAILED = 4294967294, }; enum rseq_flags { RSEQ_FLAG_UNREGISTER = 1, }; enum rseq_cs_flags { RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT = 1, RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL = 2, RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE = 4, }; struct rseq_cs { __u32 version; __u32 flags; __u64 start_ip; __u64 post_commit_offset; __u64 abort_ip; }; struct trace_event_raw_rseq_update { struct trace_entry ent; s32 cpu_id; char __data[0]; }; struct trace_event_raw_rseq_ip_fixup { struct trace_entry ent; long unsigned int regs_ip; long unsigned int start_ip; long unsigned int post_commit_offset; long unsigned int abort_ip; char __data[0]; }; struct trace_event_data_offsets_rseq_update {}; struct trace_event_data_offsets_rseq_ip_fixup {}; typedef void (*btf_trace_rseq_update)(void *, struct task_struct *); typedef void (*btf_trace_rseq_ip_fixup)(void *, long unsigned int, long unsigned int, long unsigned int, long unsigned int); struct watch; struct watch_list { struct callback_head rcu; struct hlist_head watchers; void (*release_watch)(struct watch *); spinlock_t lock; }; enum watch_notification_type { WATCH_TYPE_META = 0, WATCH_TYPE_KEY_NOTIFY = 1, WATCH_TYPE__NR = 2, }; enum watch_meta_notification_subtype { WATCH_META_REMOVAL_NOTIFICATION = 0, WATCH_META_LOSS_NOTIFICATION = 1, }; struct watch_notification { __u32 type: 24; __u32 subtype: 8; __u32 info; }; struct watch_notification_type_filter { __u32 type; __u32 info_filter; __u32 info_mask; __u32 subtype_filter[8]; }; struct watch_notification_filter { __u32 nr_filters; __u32 __reserved; struct watch_notification_type_filter filters[0]; }; struct watch_notification_removal { struct watch_notification watch; __u64 id; }; struct watch_type_filter { enum watch_notification_type type; __u32 subtype_filter[1]; __u32 info_filter; __u32 info_mask; }; struct watch_filter { union { struct callback_head rcu; long unsigned int type_filter[2]; }; u32 nr_filters; struct watch_type_filter filters[0]; }; struct watch_queue { struct callback_head rcu; struct watch_filter *filter; struct pipe_inode_info *pipe; struct hlist_head watches; struct page **notes; long unsigned int *notes_bitmap; struct kref usage; spinlock_t lock; unsigned int nr_notes; unsigned int nr_pages; bool defunct; }; struct watch { union { struct callback_head rcu; u32 info_id; }; struct watch_queue *queue; struct hlist_node queue_node; struct watch_list *watch_list; struct hlist_node list_node; const struct cred *cred; void *private; u64 id; struct kref usage; }; struct pkcs7_message; typedef int __kernel_rwf_t; enum positive_aop_returns { AOP_WRITEPAGE_ACTIVATE = 524288, AOP_TRUNCATED_PAGE = 524289, }; enum iter_type { ITER_IOVEC = 0, ITER_KVEC = 1, ITER_BVEC = 2, ITER_PIPE = 3, ITER_XARRAY = 4, ITER_DISCARD = 5, }; enum mapping_flags { AS_EIO = 0, AS_ENOSPC = 1, AS_MM_ALL_LOCKS = 2, AS_UNEVICTABLE = 3, AS_EXITING = 4, AS_NO_WRITEBACK_TAGS = 5, AS_THP_SUPPORT = 6, }; struct wait_page_key { struct page *page; int bit_nr; int page_match; }; struct pagevec { unsigned char nr; bool percpu_pvec_drained; struct page *pages[15]; }; struct fid { union { struct { u32 ino; u32 gen; u32 parent_ino; u32 parent_gen; } i32; struct { u32 block; u16 partref; u16 parent_partref; u32 generation; u32 parent_block; u32 parent_generation; } udf; __u32 raw[0]; }; }; struct trace_event_raw_mm_filemap_op_page_cache { struct trace_entry ent; long unsigned int pfn; long unsigned int i_ino; long unsigned int index; dev_t s_dev; char __data[0]; }; struct trace_event_raw_filemap_set_wb_err { struct trace_entry ent; long unsigned int i_ino; dev_t s_dev; errseq_t errseq; char __data[0]; }; struct trace_event_raw_file_check_and_advance_wb_err { struct trace_entry ent; struct file *file; long unsigned int i_ino; dev_t s_dev; errseq_t old; errseq_t new; char __data[0]; }; struct trace_event_data_offsets_mm_filemap_op_page_cache {}; struct trace_event_data_offsets_filemap_set_wb_err {}; struct trace_event_data_offsets_file_check_and_advance_wb_err {}; typedef void (*btf_trace_mm_filemap_delete_from_page_cache)(void *, struct page *); typedef void (*btf_trace_mm_filemap_add_to_page_cache)(void *, struct page *); typedef void (*btf_trace_filemap_set_wb_err)(void *, struct address_space *, errseq_t); typedef void (*btf_trace_file_check_and_advance_wb_err)(void *, struct file *, errseq_t); enum behavior { EXCLUSIVE = 0, SHARED = 1, DROP = 2, }; struct reciprocal_value { u32 m; u8 sh1; u8 sh2; }; struct kmem_cache_order_objects { unsigned int x; }; struct kmem_cache_cpu; struct kmem_cache_node; struct kmem_cache { struct kmem_cache_cpu *cpu_slab; slab_flags_t flags; long unsigned int min_partial; unsigned int size; unsigned int object_size; struct reciprocal_value reciprocal_size; unsigned int offset; unsigned int cpu_partial; struct kmem_cache_order_objects oo; struct kmem_cache_order_objects max; struct kmem_cache_order_objects min; gfp_t allocflags; int refcount; void (*ctor)(void *); unsigned int inuse; unsigned int align; unsigned int red_left_pad; const char *name; struct list_head list; struct kobject kobj; long unsigned int random; unsigned int remote_node_defrag_ratio; unsigned int *random_seq; unsigned int useroffset; unsigned int usersize; struct kmem_cache_node *node[32]; }; struct kmem_cache_cpu { void **freelist; long unsigned int tid; struct page *page; struct page *partial; }; struct kmem_cache_node { spinlock_t list_lock; long unsigned int nr_partial; struct list_head partial; atomic_long_t nr_slabs; atomic_long_t total_objects; struct list_head full; }; struct zap_details { struct address_space *check_mapping; long unsigned int first_index; long unsigned int last_index; struct page *single_page; }; enum oom_constraint { CONSTRAINT_NONE = 0, CONSTRAINT_CPUSET = 1, CONSTRAINT_MEMORY_POLICY = 2, CONSTRAINT_MEMCG = 3, }; struct oom_control { struct zonelist *zonelist; nodemask_t *nodemask; struct mem_cgroup *memcg; const gfp_t gfp_mask; const int order; long unsigned int totalpages; struct task_struct *chosen; long int chosen_points; enum oom_constraint constraint; }; enum compact_priority { COMPACT_PRIO_SYNC_FULL = 0, MIN_COMPACT_PRIORITY = 0, COMPACT_PRIO_SYNC_LIGHT = 1, MIN_COMPACT_COSTLY_PRIORITY = 1, DEF_COMPACT_PRIORITY = 1, COMPACT_PRIO_ASYNC = 2, INIT_COMPACT_PRIORITY = 2, }; enum compact_result { COMPACT_NOT_SUITABLE_ZONE = 0, COMPACT_SKIPPED = 1, COMPACT_DEFERRED = 2, COMPACT_NO_SUITABLE_PAGE = 3, COMPACT_CONTINUE = 4, COMPACT_COMPLETE = 5, COMPACT_PARTIAL_SKIPPED = 6, COMPACT_CONTENDED = 7, COMPACT_SUCCESS = 8, }; struct trace_event_raw_oom_score_adj_update { struct trace_entry ent; pid_t pid; char comm[16]; short int oom_score_adj; char __data[0]; }; struct trace_event_raw_reclaim_retry_zone { struct trace_entry ent; int node; int zone_idx; int order; long unsigned int reclaimable; long unsigned int available; long unsigned int min_wmark; int no_progress_loops; bool wmark_check; char __data[0]; }; struct trace_event_raw_mark_victim { struct trace_entry ent; int pid; char __data[0]; }; struct trace_event_raw_wake_reaper { struct trace_entry ent; int pid; char __data[0]; }; struct trace_event_raw_start_task_reaping { struct trace_entry ent; int pid; char __data[0]; }; struct trace_event_raw_finish_task_reaping { struct trace_entry ent; int pid; char __data[0]; }; struct trace_event_raw_skip_task_reaping { struct trace_entry ent; int pid; char __data[0]; }; struct trace_event_raw_compact_retry { struct trace_entry ent; int order; int priority; int result; int retries; int max_retries; bool ret; char __data[0]; }; struct trace_event_data_offsets_oom_score_adj_update {}; struct trace_event_data_offsets_reclaim_retry_zone {}; struct trace_event_data_offsets_mark_victim {}; struct trace_event_data_offsets_wake_reaper {}; struct trace_event_data_offsets_start_task_reaping {}; struct trace_event_data_offsets_finish_task_reaping {}; struct trace_event_data_offsets_skip_task_reaping {}; struct trace_event_data_offsets_compact_retry {}; typedef void (*btf_trace_oom_score_adj_update)(void *, struct task_struct *); typedef void (*btf_trace_reclaim_retry_zone)(void *, struct zoneref *, int, long unsigned int, long unsigned int, long unsigned int, int, bool); typedef void (*btf_trace_mark_victim)(void *, int); typedef void (*btf_trace_wake_reaper)(void *, int); typedef void (*btf_trace_start_task_reaping)(void *, int); typedef void (*btf_trace_finish_task_reaping)(void *, int); typedef void (*btf_trace_skip_task_reaping)(void *, int); typedef void (*btf_trace_compact_retry)(void *, int, enum compact_priority, enum compact_result, int, int, bool); enum wb_congested_state { WB_async_congested = 0, WB_sync_congested = 1, }; enum wb_state { WB_registered = 0, WB_writeback_running = 1, WB_has_dirty_io = 2, WB_start_all = 3, }; enum { BLK_RW_ASYNC = 0, BLK_RW_SYNC = 1, }; struct wb_lock_cookie { bool locked; long unsigned int flags; }; typedef int (*writepage_t)(struct page *, struct writeback_control *, void *); enum page_memcg_data_flags { MEMCG_DATA_OBJCGS = 1, MEMCG_DATA_KMEM = 2, __NR_MEMCG_DATA_FLAGS = 4, }; struct dirty_throttle_control { struct wb_domain *dom; struct dirty_throttle_control *gdtc; struct bdi_writeback *wb; struct fprop_local_percpu *wb_completions; long unsigned int avail; long unsigned int dirty; long unsigned int thresh; long unsigned int bg_thresh; long unsigned int wb_dirty; long unsigned int wb_thresh; long unsigned int wb_bg_thresh; long unsigned int pos_ratio; }; typedef void compound_page_dtor(struct page *); struct trace_event_raw_mm_lru_insertion { struct trace_entry ent; struct page *page; long unsigned int pfn; enum lru_list lru; long unsigned int flags; char __data[0]; }; struct trace_event_raw_mm_lru_activate { struct trace_entry ent; struct page *page; long unsigned int pfn; char __data[0]; }; struct trace_event_data_offsets_mm_lru_insertion {}; struct trace_event_data_offsets_mm_lru_activate {}; typedef void (*btf_trace_mm_lru_insertion)(void *, struct page *); typedef void (*btf_trace_mm_lru_activate)(void *, struct page *); struct lru_rotate { local_lock_t lock; struct pagevec pvec; }; struct lru_pvecs { local_lock_t lock; struct pagevec lru_add; struct pagevec lru_deactivate_file; struct pagevec lru_deactivate; struct pagevec lru_lazyfree; struct pagevec activate_page; }; enum lruvec_flags { LRUVEC_CONGESTED = 0, }; enum pgdat_flags { PGDAT_DIRTY = 0, PGDAT_WRITEBACK = 1, PGDAT_RECLAIM_LOCKED = 2, }; enum zone_flags { ZONE_BOOSTED_WATERMARK = 0, ZONE_RECLAIM_ACTIVE = 1, }; struct reclaim_stat { unsigned int nr_dirty; unsigned int nr_unqueued_dirty; unsigned int nr_congested; unsigned int nr_writeback; unsigned int nr_immediate; unsigned int nr_pageout; unsigned int nr_activate[2]; unsigned int nr_ref_keep; unsigned int nr_unmap_fail; unsigned int nr_lazyfree_fail; }; struct mem_cgroup_reclaim_cookie { pg_data_t *pgdat; unsigned int generation; }; enum ttu_flags { TTU_SPLIT_HUGE_PMD = 4, TTU_IGNORE_MLOCK = 8, TTU_SYNC = 16, TTU_IGNORE_HWPOISON = 32, TTU_BATCH_FLUSH = 64, TTU_RMAP_LOCKED = 128, }; struct trace_event_raw_mm_vmscan_kswapd_sleep { struct trace_entry ent; int nid; char __data[0]; }; struct trace_event_raw_mm_vmscan_kswapd_wake { struct trace_entry ent; int nid; int zid; int order; char __data[0]; }; struct trace_event_raw_mm_vmscan_wakeup_kswapd { struct trace_entry ent; int nid; int zid; int order; gfp_t gfp_flags; char __data[0]; }; struct trace_event_raw_mm_vmscan_direct_reclaim_begin_template { struct trace_entry ent; int order; gfp_t gfp_flags; char __data[0]; }; struct trace_event_raw_mm_vmscan_direct_reclaim_end_template { struct trace_entry ent; long unsigned int nr_reclaimed; char __data[0]; }; struct trace_event_raw_mm_shrink_slab_start { struct trace_entry ent; struct shrinker *shr; void *shrink; int nid; long int nr_objects_to_shrink; gfp_t gfp_flags; long unsigned int cache_items; long long unsigned int delta; long unsigned int total_scan; int priority; char __data[0]; }; struct trace_event_raw_mm_shrink_slab_end { struct trace_entry ent; struct shrinker *shr; int nid; void *shrink; long int unused_scan; long int new_scan; int retval; long int total_scan; char __data[0]; }; struct trace_event_raw_mm_vmscan_lru_isolate { struct trace_entry ent; int highest_zoneidx; int order; long unsigned int nr_requested; long unsigned int nr_scanned; long unsigned int nr_skipped; long unsigned int nr_taken; isolate_mode_t isolate_mode; int lru; char __data[0]; }; struct trace_event_raw_mm_vmscan_writepage { struct trace_entry ent; long unsigned int pfn; int reclaim_flags; char __data[0]; }; struct trace_event_raw_mm_vmscan_lru_shrink_inactive { struct trace_entry ent; int nid; long unsigned int nr_scanned; long unsigned int nr_reclaimed; long unsigned int nr_dirty; long unsigned int nr_writeback; long unsigned int nr_congested; long unsigned int nr_immediate; unsigned int nr_activate0; unsigned int nr_activate1; long unsigned int nr_ref_keep; long unsigned int nr_unmap_fail; int priority; int reclaim_flags; char __data[0]; }; struct trace_event_raw_mm_vmscan_lru_shrink_active { struct trace_entry ent; int nid; long unsigned int nr_taken; long unsigned int nr_active; long unsigned int nr_deactivated; long unsigned int nr_referenced; int priority; int reclaim_flags; char __data[0]; }; struct trace_event_raw_mm_vmscan_node_reclaim_begin { struct trace_entry ent; int nid; int order; gfp_t gfp_flags; char __data[0]; }; struct trace_event_data_offsets_mm_vmscan_kswapd_sleep {}; struct trace_event_data_offsets_mm_vmscan_kswapd_wake {}; struct trace_event_data_offsets_mm_vmscan_wakeup_kswapd {}; struct trace_event_data_offsets_mm_vmscan_direct_reclaim_begin_template {}; struct trace_event_data_offsets_mm_vmscan_direct_reclaim_end_template {}; struct trace_event_data_offsets_mm_shrink_slab_start {}; struct trace_event_data_offsets_mm_shrink_slab_end {}; struct trace_event_data_offsets_mm_vmscan_lru_isolate {}; struct trace_event_data_offsets_mm_vmscan_writepage {}; struct trace_event_data_offsets_mm_vmscan_lru_shrink_inactive {}; struct trace_event_data_offsets_mm_vmscan_lru_shrink_active {}; struct trace_event_data_offsets_mm_vmscan_node_reclaim_begin {}; typedef void (*btf_trace_mm_vmscan_kswapd_sleep)(void *, int); typedef void (*btf_trace_mm_vmscan_kswapd_wake)(void *, int, int, int); typedef void (*btf_trace_mm_vmscan_wakeup_kswapd)(void *, int, int, int, gfp_t); typedef void (*btf_trace_mm_vmscan_direct_reclaim_begin)(void *, int, gfp_t); typedef void (*btf_trace_mm_vmscan_memcg_reclaim_begin)(void *, int, gfp_t); typedef void (*btf_trace_mm_vmscan_memcg_softlimit_reclaim_begin)(void *, int, gfp_t); typedef void (*btf_trace_mm_vmscan_direct_reclaim_end)(void *, long unsigned int); typedef void (*btf_trace_mm_vmscan_memcg_reclaim_end)(void *, long unsigned int); typedef void (*btf_trace_mm_vmscan_memcg_softlimit_reclaim_end)(void *, long unsigned int); typedef void (*btf_trace_mm_shrink_slab_start)(void *, struct shrinker *, struct shrink_control *, long int, long unsigned int, long long unsigned int, long unsigned int, int); typedef void (*btf_trace_mm_shrink_slab_end)(void *, struct shrinker *, int, int, long int, long int, long int); typedef void (*btf_trace_mm_vmscan_lru_isolate)(void *, int, int, long unsigned int, long unsigned int, long unsigned int, long unsigned int, isolate_mode_t, int); typedef void (*btf_trace_mm_vmscan_writepage)(void *, struct page *); typedef void (*btf_trace_mm_vmscan_lru_shrink_inactive)(void *, int, long unsigned int, long unsigned int, struct reclaim_stat *, int, int); typedef void (*btf_trace_mm_vmscan_lru_shrink_active)(void *, int, long unsigned int, long unsigned int, long unsigned int, long unsigned int, int, int); typedef void (*btf_trace_mm_vmscan_node_reclaim_begin)(void *, int, int, gfp_t); typedef void (*btf_trace_mm_vmscan_node_reclaim_end)(void *, long unsigned int); struct scan_control { long unsigned int nr_to_reclaim; nodemask_t *nodemask; struct mem_cgroup *target_mem_cgroup; long unsigned int anon_cost; long unsigned int file_cost; unsigned int may_deactivate: 2; unsigned int force_deactivate: 1; unsigned int skipped_deactivate: 1; unsigned int may_writepage: 1; unsigned int may_unmap: 1; unsigned int may_swap: 1; unsigned int memcg_low_reclaim: 1; unsigned int memcg_low_skipped: 1; unsigned int hibernation_mode: 1; unsigned int compaction_ready: 1; unsigned int cache_trim_mode: 1; unsigned int file_is_tiny: 1; s8 order; s8 priority; s8 reclaim_idx; gfp_t gfp_mask; long unsigned int nr_scanned; long unsigned int nr_reclaimed; struct { unsigned int dirty; unsigned int unqueued_dirty; unsigned int congested; unsigned int writeback; unsigned int immediate; unsigned int file_taken; unsigned int taken; } nr; struct reclaim_state reclaim_state; }; typedef enum { PAGE_KEEP = 0, PAGE_ACTIVATE = 1, PAGE_SUCCESS = 2, PAGE_CLEAN = 3, } pageout_t; enum page_references { PAGEREF_RECLAIM = 0, PAGEREF_RECLAIM_CLEAN = 1, PAGEREF_KEEP = 2, PAGEREF_ACTIVATE = 3, }; enum scan_balance { SCAN_EQUAL = 0, SCAN_FRACT = 1, SCAN_ANON = 2, SCAN_FILE = 3, }; typedef __u64 __le64; enum transparent_hugepage_flag { TRANSPARENT_HUGEPAGE_NEVER_DAX = 0, TRANSPARENT_HUGEPAGE_FLAG = 1, TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG = 2, TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG = 3, TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG = 4, TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG = 5, TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG = 6, TRANSPARENT_HUGEPAGE_DEFRAG_KHUGEPAGED_FLAG = 7, TRANSPARENT_HUGEPAGE_USE_ZERO_PAGE_FLAG = 8, }; struct xattr; typedef int (*initxattrs)(struct inode *, const struct xattr *, void *); struct xattr { const char *name; void *value; size_t value_len; }; struct constant_table { const char *name; int value; }; enum { MPOL_DEFAULT = 0, MPOL_PREFERRED = 1, MPOL_BIND = 2, MPOL_INTERLEAVE = 3, MPOL_LOCAL = 4, MPOL_MAX = 5, }; struct shared_policy { struct rb_root root; rwlock_t lock; }; struct simple_xattrs { struct list_head head; spinlock_t lock; }; struct simple_xattr { struct list_head list; char *name; size_t size; char value[0]; }; struct shmem_inode_info { spinlock_t lock; unsigned int seals; long unsigned int flags; long unsigned int alloced; long unsigned int swapped; struct list_head shrinklist; struct list_head swaplist; struct shared_policy policy; struct simple_xattrs xattrs; atomic_t stop_eviction; struct inode vfs_inode; }; struct shmem_sb_info { long unsigned int max_blocks; struct percpu_counter used_blocks; long unsigned int max_inodes; long unsigned int free_inodes; spinlock_t stat_lock; umode_t mode; unsigned char huge; kuid_t uid; kgid_t gid; bool full_inums; ino_t next_ino; ino_t *ino_batch; struct mempolicy *mpol; spinlock_t shrinklist_lock; struct list_head shrinklist; long unsigned int shrinklist_len; }; enum sgp_type { SGP_READ = 0, SGP_CACHE = 1, SGP_NOHUGE = 2, SGP_HUGE = 3, SGP_WRITE = 4, SGP_FALLOC = 5, }; enum fid_type { FILEID_ROOT = 0, FILEID_INO32_GEN = 1, FILEID_INO32_GEN_PARENT = 2, FILEID_BTRFS_WITHOUT_PARENT = 77, FILEID_BTRFS_WITH_PARENT = 78, FILEID_BTRFS_WITH_PARENT_ROOT = 79, FILEID_UDF_WITHOUT_PARENT = 81, FILEID_UDF_WITH_PARENT = 82, FILEID_NILFS_WITHOUT_PARENT = 97, FILEID_NILFS_WITH_PARENT = 98, FILEID_FAT_WITHOUT_PARENT = 113, FILEID_FAT_WITH_PARENT = 114, FILEID_LUSTRE = 151, FILEID_KERNFS = 254, FILEID_INVALID = 255, }; struct shmem_falloc { wait_queue_head_t *waitq; long unsigned int start; long unsigned int next; long unsigned int nr_falloced; long unsigned int nr_unswapped; }; struct shmem_options { long long unsigned int blocks; long long unsigned int inodes; struct mempolicy *mpol; kuid_t uid; kgid_t gid; umode_t mode; bool full_inums; int huge; int seen; }; enum shmem_param { Opt_gid = 0, Opt_huge = 1, Opt_mode = 2, Opt_mpol = 3, Opt_nr_blocks = 4, Opt_nr_inodes = 5, Opt_size = 6, Opt_uid = 7, Opt_inode32 = 8, Opt_inode64 = 9, }; enum writeback_stat_item { NR_DIRTY_THRESHOLD = 0, NR_DIRTY_BG_THRESHOLD = 1, NR_VM_WRITEBACK_STAT_ITEMS = 2, }; struct contig_page_info { long unsigned int free_pages; long unsigned int free_blocks_total; long unsigned int free_blocks_suitable; }; struct radix_tree_iter { long unsigned int index; long unsigned int next_index; long unsigned int tags; struct xa_node *node; }; enum { RADIX_TREE_ITER_TAG_MASK = 15, RADIX_TREE_ITER_TAGGED = 16, RADIX_TREE_ITER_CONTIG = 32, }; enum mminit_level { MMINIT_WARNING = 0, MMINIT_VERIFY = 1, MMINIT_TRACE = 2, }; struct pcpu_group_info { int nr_units; long unsigned int base_offset; unsigned int *cpu_map; }; struct pcpu_alloc_info { size_t static_size; size_t reserved_size; size_t dyn_size; size_t unit_size; size_t atom_size; size_t alloc_size; size_t __ai_size; int nr_groups; struct pcpu_group_info groups[0]; }; struct trace_event_raw_percpu_alloc_percpu { struct trace_entry ent; bool reserved; bool is_atomic; size_t size; size_t align; void *base_addr; int off; void *ptr; char __data[0]; }; struct trace_event_raw_percpu_free_percpu { struct trace_entry ent; void *base_addr; int off; void *ptr; char __data[0]; }; struct trace_event_raw_percpu_alloc_percpu_fail { struct trace_entry ent; bool reserved; bool is_atomic; size_t size; size_t align; char __data[0]; }; struct trace_event_raw_percpu_create_chunk { struct trace_entry ent; void *base_addr; char __data[0]; }; struct trace_event_raw_percpu_destroy_chunk { struct trace_entry ent; void *base_addr; char __data[0]; }; struct trace_event_data_offsets_percpu_alloc_percpu {}; struct trace_event_data_offsets_percpu_free_percpu {}; struct trace_event_data_offsets_percpu_alloc_percpu_fail {}; struct trace_event_data_offsets_percpu_create_chunk {}; struct trace_event_data_offsets_percpu_destroy_chunk {}; typedef void (*btf_trace_percpu_alloc_percpu)(void *, bool, bool, size_t, size_t, void *, int, void *); typedef void (*btf_trace_percpu_free_percpu)(void *, void *, int, void *); typedef void (*btf_trace_percpu_alloc_percpu_fail)(void *, bool, bool, size_t, size_t); typedef void (*btf_trace_percpu_create_chunk)(void *, void *); typedef void (*btf_trace_percpu_destroy_chunk)(void *, void *); struct pcpu_block_md { int scan_hint; int scan_hint_start; int contig_hint; int contig_hint_start; int left_free; int right_free; int first_free; int nr_bits; }; struct pcpu_chunk { struct list_head list; int free_bytes; struct pcpu_block_md chunk_md; void *base_addr; long unsigned int *alloc_map; long unsigned int *bound_map; struct pcpu_block_md *md_blocks; void *data; bool immutable; bool isolated; int start_offset; int end_offset; struct obj_cgroup **obj_cgroups; int nr_pages; int nr_populated; int nr_empty_pop_pages; long unsigned int populated[0]; }; struct trace_event_raw_kmem_alloc { struct trace_entry ent; long unsigned int call_site; const void *ptr; size_t bytes_req; size_t bytes_alloc; gfp_t gfp_flags; char __data[0]; }; struct trace_event_raw_kmem_alloc_node { struct trace_entry ent; long unsigned int call_site; const void *ptr; size_t bytes_req; size_t bytes_alloc; gfp_t gfp_flags; int node; char __data[0]; }; struct trace_event_raw_kfree { struct trace_entry ent; long unsigned int call_site; const void *ptr; char __data[0]; }; struct trace_event_raw_kmem_cache_free { struct trace_entry ent; long unsigned int call_site; const void *ptr; u32 __data_loc_name; char __data[0]; }; struct trace_event_raw_mm_page_free { struct trace_entry ent; long unsigned int pfn; unsigned int order; char __data[0]; }; struct trace_event_raw_mm_page_free_batched { struct trace_entry ent; long unsigned int pfn; char __data[0]; }; struct trace_event_raw_mm_page_alloc { struct trace_entry ent; long unsigned int pfn; unsigned int order; gfp_t gfp_flags; int migratetype; char __data[0]; }; struct trace_event_raw_mm_page { struct trace_entry ent; long unsigned int pfn; unsigned int order; int migratetype; char __data[0]; }; struct trace_event_raw_mm_page_pcpu_drain { struct trace_entry ent; long unsigned int pfn; unsigned int order; int migratetype; char __data[0]; }; struct trace_event_raw_mm_page_alloc_extfrag { struct trace_entry ent; long unsigned int pfn; int alloc_order; int fallback_order; int alloc_migratetype; int fallback_migratetype; int change_ownership; char __data[0]; }; struct trace_event_raw_rss_stat { struct trace_entry ent; unsigned int mm_id; unsigned int curr; int member; long int size; char __data[0]; }; struct trace_event_data_offsets_kmem_alloc {}; struct trace_event_data_offsets_kmem_alloc_node {}; struct trace_event_data_offsets_kfree {}; struct trace_event_data_offsets_kmem_cache_free { u32 name; }; struct trace_event_data_offsets_mm_page_free {}; struct trace_event_data_offsets_mm_page_free_batched {}; struct trace_event_data_offsets_mm_page_alloc {}; struct trace_event_data_offsets_mm_page {}; struct trace_event_data_offsets_mm_page_pcpu_drain {}; struct trace_event_data_offsets_mm_page_alloc_extfrag {}; struct trace_event_data_offsets_rss_stat {}; typedef void (*btf_trace_kmalloc)(void *, long unsigned int, const void *, size_t, size_t, gfp_t); typedef void (*btf_trace_kmem_cache_alloc)(void *, long unsigned int, const void *, size_t, size_t, gfp_t); typedef void (*btf_trace_kmalloc_node)(void *, long unsigned int, const void *, size_t, size_t, gfp_t, int); typedef void (*btf_trace_kmem_cache_alloc_node)(void *, long unsigned int, const void *, size_t, size_t, gfp_t, int); typedef void (*btf_trace_kfree)(void *, long unsigned int, const void *); typedef void (*btf_trace_kmem_cache_free)(void *, long unsigned int, const void *, const char *); typedef void (*btf_trace_mm_page_free)(void *, struct page *, unsigned int); typedef void (*btf_trace_mm_page_free_batched)(void *, struct page *); typedef void (*btf_trace_mm_page_alloc)(void *, struct page *, unsigned int, gfp_t, int); typedef void (*btf_trace_mm_page_alloc_zone_locked)(void *, struct page *, unsigned int, int); typedef void (*btf_trace_mm_page_pcpu_drain)(void *, struct page *, unsigned int, int); typedef void (*btf_trace_mm_page_alloc_extfrag)(void *, struct page *, int, int, int, int); typedef void (*btf_trace_rss_stat)(void *, struct mm_struct *, int, long int); enum slab_state { DOWN = 0, PARTIAL = 1, PARTIAL_NODE = 2, UP = 3, FULL = 4, }; struct kmalloc_info_struct { const char *name[4]; unsigned int size; }; struct slabinfo { long unsigned int active_objs; long unsigned int num_objs; long unsigned int active_slabs; long unsigned int num_slabs; long unsigned int shared_avail; unsigned int limit; unsigned int batchcount; unsigned int shared; unsigned int objects_per_slab; unsigned int cache_order; }; struct kmem_obj_info { void *kp_ptr; struct page *kp_page; void *kp_objp; long unsigned int kp_data_offset; struct kmem_cache *kp_slab_cache; void *kp_ret; void *kp_stack[16]; void *kp_free_stack[16]; }; enum pageblock_bits { PB_migrate = 0, PB_migrate_end = 2, PB_migrate_skip = 3, NR_PAGEBLOCK_BITS = 4, }; struct node___2 { struct device dev; struct list_head access_list; struct work_struct node_work; struct list_head cache_attrs; struct device *cache_dev; }; typedef struct page *new_page_t(struct page *, long unsigned int); typedef void free_page_t(struct page *, long unsigned int); struct alloc_context { struct zonelist *zonelist; nodemask_t *nodemask; struct zoneref *preferred_zoneref; int migratetype; enum zone_type highest_zoneidx; bool spread_dirty_pages; }; struct trace_event_raw_mm_compaction_isolate_template { struct trace_entry ent; long unsigned int start_pfn; long unsigned int end_pfn; long unsigned int nr_scanned; long unsigned int nr_taken; char __data[0]; }; struct trace_event_raw_mm_compaction_migratepages { struct trace_entry ent; long unsigned int nr_migrated; long unsigned int nr_failed; char __data[0]; }; struct trace_event_raw_mm_compaction_begin { struct trace_entry ent; long unsigned int zone_start; long unsigned int migrate_pfn; long unsigned int free_pfn; long unsigned int zone_end; bool sync; char __data[0]; }; struct trace_event_raw_mm_compaction_end { struct trace_entry ent; long unsigned int zone_start; long unsigned int migrate_pfn; long unsigned int free_pfn; long unsigned int zone_end; bool sync; int status; char __data[0]; }; struct trace_event_raw_mm_compaction_try_to_compact_pages { struct trace_entry ent; int order; gfp_t gfp_mask; int prio; char __data[0]; }; struct trace_event_raw_mm_compaction_suitable_template { struct trace_entry ent; int nid; enum zone_type idx; int order; int ret; char __data[0]; }; struct trace_event_raw_mm_compaction_defer_template { struct trace_entry ent; int nid; enum zone_type idx; int order; unsigned int considered; unsigned int defer_shift; int order_failed; char __data[0]; }; struct trace_event_raw_mm_compaction_kcompactd_sleep { struct trace_entry ent; int nid; char __data[0]; }; struct trace_event_raw_kcompactd_wake_template { struct trace_entry ent; int nid; int order; enum zone_type highest_zoneidx; char __data[0]; }; struct trace_event_data_offsets_mm_compaction_isolate_template {}; struct trace_event_data_offsets_mm_compaction_migratepages {}; struct trace_event_data_offsets_mm_compaction_begin {}; struct trace_event_data_offsets_mm_compaction_end {}; struct trace_event_data_offsets_mm_compaction_try_to_compact_pages {}; struct trace_event_data_offsets_mm_compaction_suitable_template {}; struct trace_event_data_offsets_mm_compaction_defer_template {}; struct trace_event_data_offsets_mm_compaction_kcompactd_sleep {}; struct trace_event_data_offsets_kcompactd_wake_template {}; typedef void (*btf_trace_mm_compaction_isolate_migratepages)(void *, long unsigned int, long unsigned int, long unsigned int, long unsigned int); typedef void (*btf_trace_mm_compaction_isolate_freepages)(void *, long unsigned int, long unsigned int, long unsigned int, long unsigned int); typedef void (*btf_trace_mm_compaction_migratepages)(void *, long unsigned int, int, struct list_head *); typedef void (*btf_trace_mm_compaction_begin)(void *, long unsigned int, long unsigned int, long unsigned int, long unsigned int, bool); typedef void (*btf_trace_mm_compaction_end)(void *, long unsigned int, long unsigned int, long unsigned int, long unsigned int, bool, int); typedef void (*btf_trace_mm_compaction_try_to_compact_pages)(void *, int, gfp_t, int); typedef void (*btf_trace_mm_compaction_finished)(void *, struct zone *, int, int); typedef void (*btf_trace_mm_compaction_suitable)(void *, struct zone *, int, int); typedef void (*btf_trace_mm_compaction_deferred)(void *, struct zone *, int); typedef void (*btf_trace_mm_compaction_defer_compaction)(void *, struct zone *, int); typedef void (*btf_trace_mm_compaction_defer_reset)(void *, struct zone *, int); typedef void (*btf_trace_mm_compaction_kcompactd_sleep)(void *, int); typedef void (*btf_trace_mm_compaction_wakeup_kcompactd)(void *, int, int, enum zone_type); typedef void (*btf_trace_mm_compaction_kcompactd_wake)(void *, int, int, enum zone_type); typedef enum { ISOLATE_ABORT = 0, ISOLATE_NONE = 1, ISOLATE_SUCCESS = 2, } isolate_migrate_t; struct anon_vma_chain { struct vm_area_struct *vma; struct anon_vma *anon_vma; struct list_head same_vma; struct rb_node rb; long unsigned int rb_subtree_last; }; enum lru_status { LRU_REMOVED = 0, LRU_REMOVED_RETRY = 1, LRU_ROTATE = 2, LRU_SKIP = 3, LRU_RETRY = 4, }; typedef enum lru_status (*list_lru_walk_cb)(struct list_head *, struct list_lru_one *, spinlock_t *, void *); typedef struct { long unsigned int pd; } hugepd_t; struct migration_target_control { int nid; nodemask_t *nmask; gfp_t gfp_mask; }; struct follow_page_context { struct dev_pagemap *pgmap; unsigned int page_mask; }; struct trace_event_raw_mmap_lock_start_locking { struct trace_entry ent; struct mm_struct *mm; u32 __data_loc_memcg_path; bool write; char __data[0]; }; struct trace_event_raw_mmap_lock_acquire_returned { struct trace_entry ent; struct mm_struct *mm; u32 __data_loc_memcg_path; bool write; bool success; char __data[0]; }; struct trace_event_raw_mmap_lock_released { struct trace_entry ent; struct mm_struct *mm; u32 __data_loc_memcg_path; bool write; char __data[0]; }; struct trace_event_data_offsets_mmap_lock_start_locking { u32 memcg_path; }; struct trace_event_data_offsets_mmap_lock_acquire_returned { u32 memcg_path; }; struct trace_event_data_offsets_mmap_lock_released { u32 memcg_path; }; typedef void (*btf_trace_mmap_lock_start_locking)(void *, struct mm_struct *, const char *, bool); typedef void (*btf_trace_mmap_lock_acquire_returned)(void *, struct mm_struct *, const char *, bool, bool); typedef void (*btf_trace_mmap_lock_released)(void *, struct mm_struct *, const char *, bool); struct memcg_path { local_lock_t lock; char *buf; local_t buf_idx; }; typedef unsigned int pgtbl_mod_mask; enum { SWP_USED = 1, SWP_WRITEOK = 2, SWP_DISCARDABLE = 4, SWP_DISCARDING = 8, SWP_SOLIDSTATE = 16, SWP_CONTINUED = 32, SWP_BLKDEV = 64, SWP_ACTIVATED = 128, SWP_FS_OPS = 256, SWP_AREA_DISCARD = 512, SWP_PAGE_DISCARD = 1024, SWP_STABLE_WRITES = 2048, SWP_SYNCHRONOUS_IO = 4096, SWP_SCANNING = 16384, }; struct copy_subpage_arg { struct page *dst; struct page *src; struct vm_area_struct *vma; }; struct mm_walk; struct mm_walk_ops { int (*pgd_entry)(pgd_t *, long unsigned int, long unsigned int, struct mm_walk *); int (*p4d_entry)(p4d_t *, long unsigned int, long unsigned int, struct mm_walk *); int (*pud_entry)(pud_t *, long unsigned int, long unsigned int, struct mm_walk *); int (*pmd_entry)(pmd_t *, long unsigned int, long unsigned int, struct mm_walk *); int (*pte_entry)(pte_t *, long unsigned int, long unsigned int, struct mm_walk *); int (*pte_hole)(long unsigned int, long unsigned int, int, struct mm_walk *); int (*hugetlb_entry)(pte_t *, long unsigned int, long unsigned int, long unsigned int, struct mm_walk *); int (*test_walk)(long unsigned int, long unsigned int, struct mm_walk *); int (*pre_vma)(long unsigned int, long unsigned int, struct mm_walk *); void (*post_vma)(struct mm_walk *); }; enum page_walk_action { ACTION_SUBTREE = 0, ACTION_CONTINUE = 1, ACTION_AGAIN = 2, }; struct mm_walk { const struct mm_walk_ops *ops; struct mm_struct *mm; pgd_t *pgd; struct vm_area_struct *vma; enum page_walk_action action; bool no_vma; void *private; }; enum { HUGETLB_SHMFS_INODE = 1, HUGETLB_ANONHUGE_INODE = 2, }; struct trace_event_raw_vm_unmapped_area { struct trace_entry ent; long unsigned int addr; long unsigned int total_vm; long unsigned int flags; long unsigned int length; long unsigned int low_limit; long unsigned int high_limit; long unsigned int align_mask; long unsigned int align_offset; char __data[0]; }; struct trace_event_data_offsets_vm_unmapped_area {}; typedef void (*btf_trace_vm_unmapped_area)(void *, long unsigned int, struct vm_unmapped_area_info *); enum pgt_entry { NORMAL_PMD = 0, HPAGE_PMD = 1, NORMAL_PUD = 2, HPAGE_PUD = 3, }; struct rmap_walk_control { void *arg; bool (*rmap_one)(struct page *, struct vm_area_struct *, long unsigned int, void *); int (*done)(struct page *); struct anon_vma * (*anon_lock)(struct page *); bool (*invalid_vma)(struct vm_area_struct *, void *); }; struct page_referenced_arg { int mapcount; int referenced; long unsigned int vm_flags; struct mem_cgroup *memcg; }; struct make_exclusive_args { struct mm_struct *mm; long unsigned int address; void *owner; bool valid; }; struct vmap_area { long unsigned int va_start; long unsigned int va_end; struct rb_node rb_node; struct list_head list; union { long unsigned int subtree_max_size; struct vm_struct *vm; }; }; struct vfree_deferred { struct llist_head list; struct work_struct wq; }; enum fit_type { NOTHING_FIT = 0, FL_FIT_TYPE = 1, LE_FIT_TYPE = 2, RE_FIT_TYPE = 3, NE_FIT_TYPE = 4, }; struct vmap_block_queue { spinlock_t lock; struct list_head free; }; struct vmap_block { spinlock_t lock; struct vmap_area *va; long unsigned int free; long unsigned int dirty; long unsigned int dirty_min; long unsigned int dirty_max; struct list_head free_list; struct callback_head callback_head; struct list_head purge; }; struct vmap_pfn_data { long unsigned int *pfns; pgprot_t prot; unsigned int idx; }; struct page_frag_cache { void *va; __u16 offset; __u16 size; unsigned int pagecnt_bias; bool pfmemalloc; }; typedef int fpi_t; struct pagesets { local_lock_t lock; }; struct pcpu_drain { struct zone *zone; struct work_struct work; }; struct mminit_pfnnid_cache { long unsigned int last_start; long unsigned int last_end; int last_nid; }; enum { MMOP_OFFLINE = 0, MMOP_ONLINE = 1, MMOP_ONLINE_KERNEL = 2, MMOP_ONLINE_MOVABLE = 3, }; typedef int mhp_t; typedef void (*online_page_callback_t)(struct page *, unsigned int); struct memory_block { long unsigned int start_section_nr; long unsigned int state; int online_type; int nid; struct device dev; long unsigned int nr_vmemmap_pages; }; struct memory_notify { long unsigned int start_pfn; long unsigned int nr_pages; int status_change_nid_normal; int status_change_nid_high; int status_change_nid; }; typedef int (*walk_memory_blocks_func_t)(struct memory_block *, void *); enum hugetlb_page_flags { HPG_restore_reserve = 0, HPG_migratable = 1, HPG_temporary = 2, HPG_freed = 3, HPG_vmemmap_optimized = 4, __NR_HPAGEFLAGS = 5, }; struct madvise_walk_private { struct mmu_gather *tlb; bool pageout; }; struct vma_swap_readahead { short unsigned int win; short unsigned int offset; short unsigned int nr_pte; pte_t *ptes; }; enum { PERCPU_REF_INIT_ATOMIC = 1, PERCPU_REF_INIT_DEAD = 2, PERCPU_REF_ALLOW_REINIT = 4, }; union swap_header { struct { char reserved[4086]; char magic[10]; } magic; struct { char bootbits[1024]; __u32 version; __u32 last_page; __u32 nr_badpages; unsigned char sws_uuid[16]; unsigned char sws_volume[16]; __u32 padding[117]; __u32 badpages[1]; } info; }; struct swap_extent { struct rb_node rb_node; long unsigned int start_page; long unsigned int nr_pages; sector_t start_block; }; struct swap_slots_cache { bool lock_initialized; struct mutex alloc_lock; swp_entry_t *slots; int nr; int cur; spinlock_t free_lock; swp_entry_t *slots_ret; int n_ret; }; struct frontswap_ops { void (*init)(unsigned int); int (*store)(unsigned int, long unsigned int, struct page *); int (*load)(unsigned int, long unsigned int, struct page *); void (*invalidate_page)(unsigned int, long unsigned int); void (*invalidate_area)(unsigned int); struct frontswap_ops *next; }; struct crypto_async_request; typedef void (*crypto_completion_t)(struct crypto_async_request *, int); struct crypto_async_request { struct list_head list; crypto_completion_t complete; void *data; struct crypto_tfm *tfm; u32 flags; }; struct crypto_wait { struct completion completion; int err; }; struct zpool; struct zpool_ops { int (*evict)(struct zpool *, long unsigned int); }; enum zpool_mapmode { ZPOOL_MM_RW = 0, ZPOOL_MM_RO = 1, ZPOOL_MM_WO = 2, ZPOOL_MM_DEFAULT = 0, }; struct acomp_req { struct crypto_async_request base; struct scatterlist *src; struct scatterlist *dst; unsigned int slen; unsigned int dlen; u32 flags; void *__ctx[0]; }; struct crypto_acomp { int (*compress)(struct acomp_req *); int (*decompress)(struct acomp_req *); void (*dst_free)(struct scatterlist *); unsigned int reqsize; struct crypto_tfm base; }; struct crypto_acomp_ctx { struct crypto_acomp *acomp; struct acomp_req *req; struct crypto_wait wait; u8 *dstmem; struct mutex *mutex; }; struct zswap_pool { struct zpool *zpool; struct crypto_acomp_ctx *acomp_ctx; struct kref kref; struct list_head list; struct work_struct release_work; struct work_struct shrink_work; struct hlist_node node; char tfm_name[128]; }; struct zswap_entry { struct rb_node rbnode; long unsigned int offset; int refcount; unsigned int length; struct zswap_pool *pool; union { long unsigned int handle; long unsigned int value; }; }; struct zswap_header { swp_entry_t swpentry; }; struct zswap_tree { struct rb_root rbroot; spinlock_t lock; }; enum zswap_get_swap_ret { ZSWAP_SWAPCACHE_NEW = 0, ZSWAP_SWAPCACHE_EXIST = 1, ZSWAP_SWAPCACHE_FAIL = 2, }; struct dma_pool { struct list_head page_list; spinlock_t lock; size_t size; struct device *dev; size_t allocation; size_t boundary; char name[32]; struct list_head pools; }; struct dma_page { struct list_head page_list; void *vaddr; dma_addr_t dma; unsigned int in_use; unsigned int offset; }; enum string_size_units { STRING_UNITS_10 = 0, STRING_UNITS_2 = 1, }; typedef void (*node_registration_func_t)(struct node___2 *); enum mcopy_atomic_mode { MCOPY_ATOMIC_NORMAL = 0, MCOPY_ATOMIC_ZEROPAGE = 1, MCOPY_ATOMIC_CONTINUE = 2, }; enum { SUBPAGE_INDEX_SUBPOOL = 1, SUBPAGE_INDEX_CGROUP = 2, SUBPAGE_INDEX_CGROUP_RSVD = 3, __MAX_CGROUP_SUBPAGE_INDEX = 3, __NR_USED_SUBPAGE = 4, }; struct resv_map { struct kref refs; spinlock_t lock; struct list_head regions; long int adds_in_progress; struct list_head region_cache; long int region_cache_count; struct page_counter *reservation_counter; long unsigned int pages_per_hpage; struct cgroup_subsys_state *css; }; struct file_region { struct list_head link; long int from; long int to; struct page_counter *reservation_counter; struct cgroup_subsys_state *css; }; struct huge_bootmem_page { struct list_head list; struct hstate *hstate; }; enum hugetlb_memory_event { HUGETLB_MAX = 0, HUGETLB_NR_MEMORY_EVENTS = 1, }; struct hugetlb_cgroup { struct cgroup_subsys_state css; struct page_counter hugepage[2]; struct page_counter rsvd_hugepage[2]; atomic_long_t events[2]; atomic_long_t events_local[2]; struct cgroup_file events_file[2]; struct cgroup_file events_local_file[2]; }; enum vma_resv_mode { VMA_NEEDS_RESV = 0, VMA_COMMIT_RESV = 1, VMA_END_RESV = 2, VMA_ADD_RESV = 3, VMA_DEL_RESV = 4, }; struct node_hstate { struct kobject *hugepages_kobj; struct kobject *hstate_kobjs[2]; }; struct nodemask_scratch { nodemask_t mask1; nodemask_t mask2; }; struct sp_node { struct rb_node nd; long unsigned int start; long unsigned int end; struct mempolicy *policy; }; struct mempolicy_operations { int (*create)(struct mempolicy *, const nodemask_t *); void (*rebind)(struct mempolicy *, const nodemask_t *); }; struct queue_pages { struct list_head *pagelist; long unsigned int flags; nodemask_t *nmask; long unsigned int start; long unsigned int end; struct vm_area_struct *first; }; struct vmemmap_remap_walk { void (*remap_pte)(pte_t *, long unsigned int, struct vmemmap_remap_walk *); long unsigned int nr_walked; struct page *reuse_page; long unsigned int reuse_addr; struct list_head *vmemmap_pages; }; struct mmu_notifier_subscriptions { struct hlist_head list; bool has_itree; spinlock_t lock; long unsigned int invalidate_seq; long unsigned int active_invalidate_ranges; struct rb_root_cached itree; wait_queue_head_t wq; struct hlist_head deferred_list; }; struct interval_tree_node { struct rb_node rb; long unsigned int start; long unsigned int last; long unsigned int __subtree_last; }; struct mmu_interval_notifier; struct mmu_interval_notifier_ops { bool (*invalidate)(struct mmu_interval_notifier *, const struct mmu_notifier_range *, long unsigned int); }; struct mmu_interval_notifier { struct interval_tree_node interval_tree; const struct mmu_interval_notifier_ops *ops; struct mm_struct *mm; struct hlist_node deferred_item; long unsigned int invalidate_seq; }; struct rmap_item; struct mm_slot { struct hlist_node link; struct list_head mm_list; struct rmap_item *rmap_list; struct mm_struct *mm; }; struct stable_node; struct rmap_item { struct rmap_item *rmap_list; union { struct anon_vma *anon_vma; int nid; }; struct mm_struct *mm; long unsigned int address; unsigned int oldchecksum; union { struct rb_node node; struct { struct stable_node *head; struct hlist_node hlist; }; }; }; struct ksm_scan { struct mm_slot *mm_slot; long unsigned int address; struct rmap_item **rmap_list; long unsigned int seqnr; }; struct stable_node { union { struct rb_node node; struct { struct list_head *head; struct { struct hlist_node hlist_dup; struct list_head list; }; }; }; struct hlist_head hlist; union { long unsigned int kpfn; long unsigned int chain_prune_time; }; int rmap_hlist_len; int nid; }; enum get_ksm_page_flags { GET_KSM_PAGE_NOLOCK = 0, GET_KSM_PAGE_LOCK = 1, GET_KSM_PAGE_TRYLOCK = 2, }; enum stat_item { ALLOC_FASTPATH = 0, ALLOC_SLOWPATH = 1, FREE_FASTPATH = 2, FREE_SLOWPATH = 3, FREE_FROZEN = 4, FREE_ADD_PARTIAL = 5, FREE_REMOVE_PARTIAL = 6, ALLOC_FROM_PARTIAL = 7, ALLOC_SLAB = 8, ALLOC_REFILL = 9, ALLOC_NODE_MISMATCH = 10, FREE_SLAB = 11, CPUSLAB_FLUSH = 12, DEACTIVATE_FULL = 13, DEACTIVATE_EMPTY = 14, DEACTIVATE_TO_HEAD = 15, DEACTIVATE_TO_TAIL = 16, DEACTIVATE_REMOTE_FREES = 17, DEACTIVATE_BYPASS = 18, ORDER_FALLBACK = 19, CMPXCHG_DOUBLE_CPU_FAIL = 20, CMPXCHG_DOUBLE_FAIL = 21, CPU_PARTIAL_ALLOC = 22, CPU_PARTIAL_FREE = 23, CPU_PARTIAL_NODE = 24, CPU_PARTIAL_DRAIN = 25, NR_SLUB_STAT_ITEMS = 26, }; struct track { long unsigned int addr; long unsigned int addrs[16]; int cpu; int pid; long unsigned int when; }; enum track_item { TRACK_ALLOC = 0, TRACK_FREE = 1, }; struct detached_freelist { struct page *page; void *tail; void *freelist; int cnt; struct kmem_cache *s; }; struct location { long unsigned int count; long unsigned int addr; long long int sum_time; long int min_time; long int max_time; long int min_pid; long int max_pid; long unsigned int cpus[5]; nodemask_t nodes; }; struct loc_track { long unsigned int max; long unsigned int count; struct location *loc; }; enum slab_stat_type { SL_ALL = 0, SL_PARTIAL = 1, SL_CPU = 2, SL_OBJECTS = 3, SL_TOTAL = 4, }; struct slab_attribute { struct attribute attr; ssize_t (*show)(struct kmem_cache *, char *); ssize_t (*store)(struct kmem_cache *, const char *, size_t); }; struct saved_alias { struct kmem_cache *s; const char *name; struct saved_alias *next; }; enum slab_modes { M_NONE = 0, M_PARTIAL = 1, M_FULL = 2, M_FREE = 3, }; struct kcsan_scoped_access {}; enum kfence_object_state { KFENCE_OBJECT_UNUSED = 0, KFENCE_OBJECT_ALLOCATED = 1, KFENCE_OBJECT_FREED = 2, }; struct kfence_track { pid_t pid; int num_stack_entries; long unsigned int stack_entries[64]; }; struct kfence_metadata { struct list_head list; struct callback_head callback_head; raw_spinlock_t lock; enum kfence_object_state state; long unsigned int addr; size_t size; struct kmem_cache *cache; long unsigned int unprotected_page; struct kfence_track alloc_track; struct kfence_track free_track; }; enum kfence_error_type { KFENCE_ERROR_OOB = 0, KFENCE_ERROR_UAF = 1, KFENCE_ERROR_CORRUPTION = 2, KFENCE_ERROR_INVALID = 3, KFENCE_ERROR_INVALID_FREE = 4, }; enum kfence_counter_id { KFENCE_COUNTER_ALLOCATED = 0, KFENCE_COUNTER_ALLOCS = 1, KFENCE_COUNTER_FREES = 2, KFENCE_COUNTER_ZOMBIES = 3, KFENCE_COUNTER_BUGS = 4, KFENCE_COUNTER_COUNT = 5, }; typedef __kernel_long_t __kernel_ptrdiff_t; typedef __kernel_ptrdiff_t ptrdiff_t; struct buffer_head; typedef void bh_end_io_t(struct buffer_head *, int); struct buffer_head { long unsigned int b_state; struct buffer_head *b_this_page; struct page *b_page; sector_t b_blocknr; size_t b_size; char *b_data; struct block_device *b_bdev; bh_end_io_t *b_end_io; void *b_private; struct list_head b_assoc_buffers; struct address_space *b_assoc_map; atomic_t b_count; spinlock_t b_uptodate_lock; }; enum migrate_vma_direction { MIGRATE_VMA_SELECT_SYSTEM = 1, MIGRATE_VMA_SELECT_DEVICE_PRIVATE = 2, }; struct migrate_vma { struct vm_area_struct *vma; long unsigned int *dst; long unsigned int *src; long unsigned int cpages; long unsigned int npages; long unsigned int start; long unsigned int end; void *pgmap_owner; long unsigned int flags; }; enum bh_state_bits { BH_Uptodate = 0, BH_Dirty = 1, BH_Lock = 2, BH_Req = 3, BH_Mapped = 4, BH_New = 5, BH_Async_Read = 6, BH_Async_Write = 7, BH_Delay = 8, BH_Boundary = 9, BH_Write_EIO = 10, BH_Unwritten = 11, BH_Quiet = 12, BH_Meta = 13, BH_Prio = 14, BH_Defer_Completion = 15, BH_PrivateStart = 16, }; struct trace_event_raw_mm_migrate_pages { struct trace_entry ent; long unsigned int succeeded; long unsigned int failed; long unsigned int thp_succeeded; long unsigned int thp_failed; long unsigned int thp_split; enum migrate_mode mode; int reason; char __data[0]; }; struct trace_event_raw_mm_migrate_pages_start { struct trace_entry ent; enum migrate_mode mode; int reason; char __data[0]; }; struct trace_event_data_offsets_mm_migrate_pages {}; struct trace_event_data_offsets_mm_migrate_pages_start {}; typedef void (*btf_trace_mm_migrate_pages)(void *, long unsigned int, long unsigned int, long unsigned int, long unsigned int, long unsigned int, enum migrate_mode, int); typedef void (*btf_trace_mm_migrate_pages_start)(void *, enum migrate_mode, int); enum scan_result { SCAN_FAIL = 0, SCAN_SUCCEED = 1, SCAN_PMD_NULL = 2, SCAN_EXCEED_NONE_PTE = 3, SCAN_EXCEED_SWAP_PTE = 4, SCAN_EXCEED_SHARED_PTE = 5, SCAN_PTE_NON_PRESENT = 6, SCAN_PTE_UFFD_WP = 7, SCAN_PAGE_RO = 8, SCAN_LACK_REFERENCED_PAGE = 9, SCAN_PAGE_NULL = 10, SCAN_SCAN_ABORT = 11, SCAN_PAGE_COUNT = 12, SCAN_PAGE_LRU = 13, SCAN_PAGE_LOCK = 14, SCAN_PAGE_ANON = 15, SCAN_PAGE_COMPOUND = 16, SCAN_ANY_PROCESS = 17, SCAN_VMA_NULL = 18, SCAN_VMA_CHECK = 19, SCAN_ADDRESS_RANGE = 20, SCAN_SWAP_CACHE_PAGE = 21, SCAN_DEL_PAGE_LRU = 22, SCAN_ALLOC_HUGE_PAGE_FAIL = 23, SCAN_CGROUP_CHARGE_FAIL = 24, SCAN_TRUNCATED = 25, SCAN_PAGE_HAS_PRIVATE = 26, }; struct trace_event_raw_mm_khugepaged_scan_pmd { struct trace_entry ent; struct mm_struct *mm; long unsigned int pfn; bool writable; int referenced; int none_or_zero; int status; int unmapped; char __data[0]; }; struct trace_event_raw_mm_collapse_huge_page { struct trace_entry ent; struct mm_struct *mm; int isolated; int status; char __data[0]; }; struct trace_event_raw_mm_collapse_huge_page_isolate { struct trace_entry ent; long unsigned int pfn; int none_or_zero; int referenced; bool writable; int status; char __data[0]; }; struct trace_event_raw_mm_collapse_huge_page_swapin { struct trace_entry ent; struct mm_struct *mm; int swapped_in; int referenced; int ret; char __data[0]; }; struct trace_event_data_offsets_mm_khugepaged_scan_pmd {}; struct trace_event_data_offsets_mm_collapse_huge_page {}; struct trace_event_data_offsets_mm_collapse_huge_page_isolate {}; struct trace_event_data_offsets_mm_collapse_huge_page_swapin {}; typedef void (*btf_trace_mm_khugepaged_scan_pmd)(void *, struct mm_struct *, struct page *, bool, int, int, int, int); typedef void (*btf_trace_mm_collapse_huge_page)(void *, struct mm_struct *, int, int); typedef void (*btf_trace_mm_collapse_huge_page_isolate)(void *, struct page *, int, int, bool, int); typedef void (*btf_trace_mm_collapse_huge_page_swapin)(void *, struct mm_struct *, int, int, int); struct mm_slot___2 { struct hlist_node hash; struct list_head mm_node; struct mm_struct *mm; int nr_pte_mapped_thp; long unsigned int pte_mapped_thp[8]; }; struct khugepaged_scan { struct list_head mm_head; struct mm_slot___2 *mm_slot; long unsigned int address; }; struct mem_cgroup_tree_per_node { struct rb_root rb_root; struct rb_node *rb_rightmost; spinlock_t lock; }; struct mem_cgroup_tree { struct mem_cgroup_tree_per_node *rb_tree_per_node[32]; }; struct mem_cgroup_eventfd_list { struct list_head list; struct eventfd_ctx *eventfd; }; struct mem_cgroup_event { struct mem_cgroup *memcg; struct eventfd_ctx *eventfd; struct list_head list; int (*register_event)(struct mem_cgroup *, struct eventfd_ctx *, const char *); void (*unregister_event)(struct mem_cgroup *, struct eventfd_ctx *); poll_table pt; wait_queue_head_t *wqh; wait_queue_entry_t wait; struct work_struct remove; }; struct move_charge_struct { spinlock_t lock; struct mm_struct *mm; struct mem_cgroup *from; struct mem_cgroup *to; long unsigned int flags; long unsigned int precharge; long unsigned int moved_charge; long unsigned int moved_swap; struct task_struct *moving_task; wait_queue_head_t waitq; }; enum res_type { _MEM = 0, _MEMSWAP = 1, _OOM_TYPE = 2, _KMEM = 3, _TCP = 4, }; struct memory_stat { const char *name; unsigned int idx; }; struct oom_wait_info { struct mem_cgroup *memcg; wait_queue_entry_t wait; }; enum oom_status { OOM_SUCCESS = 0, OOM_FAILED = 1, OOM_ASYNC = 2, OOM_SKIPPED = 3, }; struct obj_stock { struct obj_cgroup *cached_objcg; struct pglist_data *cached_pgdat; unsigned int nr_bytes; int nr_slab_reclaimable_b; int nr_slab_unreclaimable_b; }; struct memcg_stock_pcp { struct mem_cgroup *cached; unsigned int nr_pages; struct obj_stock task_obj; struct obj_stock irq_obj; struct work_struct work; long unsigned int flags; }; enum { RES_USAGE = 0, RES_LIMIT = 1, RES_MAX_USAGE = 2, RES_FAILCNT = 3, RES_SOFT_LIMIT = 4, }; union mc_target { struct page *page; swp_entry_t ent; }; enum mc_target_type { MC_TARGET_NONE = 0, MC_TARGET_PAGE = 1, MC_TARGET_SWAP = 2, MC_TARGET_DEVICE = 3, }; struct uncharge_gather { struct mem_cgroup *memcg; long unsigned int nr_memory; long unsigned int pgpgout; long unsigned int nr_kmem; struct page *dummy_page; }; struct numa_stat { const char *name; unsigned int lru_mask; }; enum vmpressure_levels { VMPRESSURE_LOW = 0, VMPRESSURE_MEDIUM = 1, VMPRESSURE_CRITICAL = 2, VMPRESSURE_NUM_LEVELS = 3, }; enum vmpressure_modes { VMPRESSURE_NO_PASSTHROUGH = 0, VMPRESSURE_HIERARCHY = 1, VMPRESSURE_LOCAL = 2, VMPRESSURE_NUM_MODES = 3, }; struct vmpressure_event { struct eventfd_ctx *efd; enum vmpressure_levels level; enum vmpressure_modes mode; struct list_head node; }; struct swap_cgroup_ctrl { struct page **map; long unsigned int length; spinlock_t lock; }; struct swap_cgroup { short unsigned int id; }; enum { RES_USAGE___2 = 0, RES_RSVD_USAGE = 1, RES_LIMIT___2 = 2, RES_RSVD_LIMIT = 3, RES_MAX_USAGE___2 = 4, RES_RSVD_MAX_USAGE = 5, RES_FAILCNT___2 = 6, RES_RSVD_FAILCNT = 7, }; enum mf_result { MF_IGNORED = 0, MF_FAILED = 1, MF_DELAYED = 2, MF_RECOVERED = 3, }; enum mf_action_page_type { MF_MSG_KERNEL = 0, MF_MSG_KERNEL_HIGH_ORDER = 1, MF_MSG_SLAB = 2, MF_MSG_DIFFERENT_COMPOUND = 3, MF_MSG_POISONED_HUGE = 4, MF_MSG_HUGE = 5, MF_MSG_FREE_HUGE = 6, MF_MSG_NON_PMD_HUGE = 7, MF_MSG_UNMAP_FAILED = 8, MF_MSG_DIRTY_SWAPCACHE = 9, MF_MSG_CLEAN_SWAPCACHE = 10, MF_MSG_DIRTY_MLOCKED_LRU = 11, MF_MSG_CLEAN_MLOCKED_LRU = 12, MF_MSG_DIRTY_UNEVICTABLE_LRU = 13, MF_MSG_CLEAN_UNEVICTABLE_LRU = 14, MF_MSG_DIRTY_LRU = 15, MF_MSG_CLEAN_LRU = 16, MF_MSG_TRUNCATED_LRU = 17, MF_MSG_BUDDY = 18, MF_MSG_BUDDY_2ND = 19, MF_MSG_DAX = 20, MF_MSG_UNSPLIT_THP = 21, MF_MSG_UNKNOWN = 22, }; typedef long unsigned int dax_entry_t; struct __kfifo { unsigned int in; unsigned int out; unsigned int mask; unsigned int esize; void *data; }; struct to_kill { struct list_head nd; struct task_struct *tsk; long unsigned int addr; short int size_shift; }; struct hwp_walk { struct to_kill tk; long unsigned int pfn; int flags; }; struct page_state { long unsigned int mask; long unsigned int res; enum mf_action_page_type type; int (*action)(struct page *, long unsigned int); }; struct memory_failure_entry { long unsigned int pfn; int flags; }; struct memory_failure_cpu { struct { union { struct __kfifo kfifo; struct memory_failure_entry *type; const struct memory_failure_entry *const_type; char (*rectype)[0]; struct memory_failure_entry *ptr; const struct memory_failure_entry *ptr_const; }; struct memory_failure_entry buf[16]; } fifo; spinlock_t lock; struct work_struct work; }; struct cleancache_filekey { union { ino_t ino; __u32 fh[6]; u32 key[6]; } u; }; struct cleancache_ops { int (*init_fs)(size_t); int (*init_shared_fs)(uuid_t *, size_t); int (*get_page)(int, struct cleancache_filekey, long unsigned int, struct page *); void (*put_page)(int, struct cleancache_filekey, long unsigned int, struct page *); void (*invalidate_page)(int, struct cleancache_filekey, long unsigned int); void (*invalidate_inode)(int, struct cleancache_filekey); void (*invalidate_fs)(int); }; struct trace_event_raw_test_pages_isolated { struct trace_entry ent; long unsigned int start_pfn; long unsigned int end_pfn; long unsigned int fin_pfn; char __data[0]; }; struct trace_event_data_offsets_test_pages_isolated {}; typedef void (*btf_trace_test_pages_isolated)(void *, long unsigned int, long unsigned int, long unsigned int); struct zpool_driver; struct zpool { struct zpool_driver *driver; void *pool; const struct zpool_ops *ops; bool evictable; bool can_sleep_mapped; struct list_head list; }; struct zpool_driver { char *type; struct module *owner; atomic_t refcount; struct list_head list; void * (*create)(const char *, gfp_t, const struct zpool_ops *, struct zpool *); void (*destroy)(void *); bool malloc_support_movable; int (*malloc)(void *, size_t, gfp_t, long unsigned int *); void (*free)(void *, long unsigned int); int (*shrink)(void *, unsigned int, unsigned int *); bool sleep_mapped; void * (*map)(void *, long unsigned int, enum zpool_mapmode); void (*unmap)(void *, long unsigned int); u64 (*total_size)(void *); }; struct zbud_pool; struct zbud_ops { int (*evict)(struct zbud_pool *, long unsigned int); }; struct zbud_pool { spinlock_t lock; union { struct list_head buddied; struct list_head unbuddied[63]; }; struct list_head lru; u64 pages_nr; const struct zbud_ops *ops; struct zpool *zpool; const struct zpool_ops *zpool_ops; }; struct zbud_header { struct list_head buddy; struct list_head lru; unsigned int first_chunks; unsigned int last_chunks; bool under_reclaim; }; enum buddy { FIRST = 0, LAST = 1, }; enum zs_mapmode { ZS_MM_RW = 0, ZS_MM_RO = 1, ZS_MM_WO = 2, }; struct zs_pool_stats { atomic_long_t pages_compacted; }; enum fullness_group { ZS_EMPTY = 0, ZS_ALMOST_EMPTY = 1, ZS_ALMOST_FULL = 2, ZS_FULL = 3, NR_ZS_FULLNESS = 4, }; enum zs_stat_type { CLASS_EMPTY = 0, CLASS_ALMOST_EMPTY = 1, CLASS_ALMOST_FULL = 2, CLASS_FULL = 3, OBJ_ALLOCATED = 4, OBJ_USED = 5, NR_ZS_STAT_TYPE = 6, }; struct zs_size_stat { long unsigned int objs[6]; }; struct size_class { spinlock_t lock; struct list_head fullness_list[4]; int size; int objs_per_zspage; int pages_per_zspage; unsigned int index; struct zs_size_stat stats; }; struct link_free { union { long unsigned int next; long unsigned int handle; }; }; struct zs_pool { const char *name; struct size_class *size_class[255]; struct kmem_cache *handle_cachep; struct kmem_cache *zspage_cachep; atomic_long_t pages_allocated; struct zs_pool_stats stats; struct shrinker shrinker; struct inode *inode; struct work_struct free_work; struct wait_queue_head migration_wait; atomic_long_t isolated_pages; bool destroying; }; struct zspage { struct { unsigned int fullness: 2; unsigned int class: 9; unsigned int isolated: 3; unsigned int magic: 8; }; unsigned int inuse; unsigned int freeobj; struct page *first_page; struct list_head list; rwlock_t lock; }; struct mapping_area { char *vm_buf; char *vm_addr; enum zs_mapmode vm_mm; }; struct zs_compact_control { struct page *s_page; struct page *d_page; int obj_idx; }; struct z3fold_pool; struct z3fold_ops { int (*evict)(struct z3fold_pool *, long unsigned int); }; struct z3fold_pool { const char *name; spinlock_t lock; spinlock_t stale_lock; struct list_head *unbuddied; struct list_head lru; struct list_head stale; atomic64_t pages_nr; struct kmem_cache *c_handle; const struct z3fold_ops *ops; struct zpool *zpool; const struct zpool_ops *zpool_ops; struct workqueue_struct *compact_wq; struct workqueue_struct *release_wq; struct work_struct work; struct inode *inode; }; enum buddy___2 { HEADLESS = 0, FIRST___2 = 1, MIDDLE = 2, LAST___2 = 3, BUDDIES_MAX = 3, }; struct z3fold_buddy_slots { long unsigned int slot[4]; long unsigned int pool; rwlock_t lock; }; struct z3fold_header { struct list_head buddy; spinlock_t page_lock; struct kref refcount; struct work_struct work; struct z3fold_buddy_slots *slots; struct z3fold_pool *pool; short int cpu; short unsigned int first_chunks; short unsigned int middle_chunks; short unsigned int last_chunks; short unsigned int start_middle; short unsigned int first_num: 2; short unsigned int mapped_count: 2; short unsigned int foreign_handles: 2; }; enum z3fold_page_flags { PAGE_HEADLESS = 0, MIDDLE_CHUNK_MAPPED = 1, NEEDS_COMPACTING = 2, PAGE_STALE = 3, PAGE_CLAIMED = 4, }; enum z3fold_handle_flags { HANDLES_NOFREE = 0, }; struct trace_event_raw_cma_alloc_class { struct trace_entry ent; u32 __data_loc_name; long unsigned int pfn; const struct page *page; long unsigned int count; unsigned int align; char __data[0]; }; struct trace_event_raw_cma_release { struct trace_entry ent; u32 __data_loc_name; long unsigned int pfn; const struct page *page; long unsigned int count; char __data[0]; }; struct trace_event_raw_cma_alloc_start { struct trace_entry ent; u32 __data_loc_name; long unsigned int count; unsigned int align; char __data[0]; }; struct trace_event_data_offsets_cma_alloc_class { u32 name; }; struct trace_event_data_offsets_cma_release { u32 name; }; struct trace_event_data_offsets_cma_alloc_start { u32 name; }; typedef void (*btf_trace_cma_release)(void *, const char *, long unsigned int, const struct page *, long unsigned int); typedef void (*btf_trace_cma_alloc_start)(void *, const char *, long unsigned int, unsigned int); typedef void (*btf_trace_cma_alloc_finish)(void *, const char *, long unsigned int, const struct page *, long unsigned int, unsigned int); typedef void (*btf_trace_cma_alloc_busy_retry)(void *, const char *, long unsigned int, const struct page *, long unsigned int, unsigned int); struct cma_kobject { struct kobject kobj; struct cma *cma; }; struct balloon_dev_info { long unsigned int isolated_pages; spinlock_t pages_lock; struct list_head pages; int (*migratepage)(struct balloon_dev_info *, struct page *, struct page *, enum migrate_mode); struct inode *inode; }; struct cma_mem { struct hlist_node node; struct page *p; long unsigned int n; }; enum { BAD_STACK = 4294967295, NOT_STACK = 0, GOOD_FRAME = 1, GOOD_STACK = 2, }; enum hmm_pfn_flags { HMM_PFN_VALID = 0, HMM_PFN_WRITE = 0, HMM_PFN_ERROR = 0, HMM_PFN_ORDER_SHIFT = 56, HMM_PFN_REQ_FAULT = 0, HMM_PFN_REQ_WRITE = 0, HMM_PFN_FLAGS = 0, }; struct hmm_range { struct mmu_interval_notifier *notifier; long unsigned int notifier_seq; long unsigned int start; long unsigned int end; long unsigned int *hmm_pfns; long unsigned int default_flags; long unsigned int pfn_flags_mask; void *dev_private_owner; }; struct hmm_vma_walk { struct hmm_range *range; long unsigned int last; }; enum { HMM_NEED_FAULT = 1, HMM_NEED_WRITE_FAULT = 2, HMM_NEED_ALL_BITS = 3, }; struct hugetlbfs_inode_info { struct shared_policy policy; struct inode vfs_inode; unsigned int seals; }; struct wp_walk { struct mmu_notifier_range range; long unsigned int tlbflush_start; long unsigned int tlbflush_end; long unsigned int total; }; struct clean_walk { struct wp_walk base; long unsigned int bitmap_pgoff; long unsigned int *bitmap; long unsigned int start; long unsigned int end; }; struct page_reporting_dev_info { int (*report)(struct page_reporting_dev_info *, struct scatterlist *, unsigned int); struct delayed_work work; atomic_t state; unsigned int order; }; enum { PAGE_REPORTING_IDLE = 0, PAGE_REPORTING_REQUESTED = 1, PAGE_REPORTING_ACTIVE = 2, }; struct open_how { __u64 flags; __u64 mode; __u64 resolve; }; typedef s32 compat_off_t; struct open_flags { int open_flag; umode_t mode; int acc_mode; int intent; int lookup_flags; }; typedef __kernel_long_t __kernel_off_t; typedef __kernel_off_t off_t; typedef __kernel_rwf_t rwf_t; struct fscrypt_policy_v1 { __u8 version; __u8 contents_encryption_mode; __u8 filenames_encryption_mode; __u8 flags; __u8 master_key_descriptor[8]; }; struct fscrypt_policy_v2 { __u8 version; __u8 contents_encryption_mode; __u8 filenames_encryption_mode; __u8 flags; __u8 __reserved[4]; __u8 master_key_identifier[16]; }; union fscrypt_policy { u8 version; struct fscrypt_policy_v1 v1; struct fscrypt_policy_v2 v2; }; enum vfs_get_super_keying { vfs_get_single_super = 0, vfs_get_single_reconf_super = 1, vfs_get_keyed_super = 2, vfs_get_independent_super = 3, }; typedef struct kobject *kobj_probe_t(dev_t, int *, void *); struct kobj_map; struct char_device_struct { struct char_device_struct *next; unsigned int major; unsigned int baseminor; int minorct; char name[64]; struct cdev *cdev; }; struct stat { __kernel_ulong_t st_dev; __kernel_ulong_t st_ino; __kernel_ulong_t st_nlink; unsigned int st_mode; unsigned int st_uid; unsigned int st_gid; unsigned int __pad0; __kernel_ulong_t st_rdev; __kernel_long_t st_size; __kernel_long_t st_blksize; __kernel_long_t st_blocks; __kernel_ulong_t st_atime; __kernel_ulong_t st_atime_nsec; __kernel_ulong_t st_mtime; __kernel_ulong_t st_mtime_nsec; __kernel_ulong_t st_ctime; __kernel_ulong_t st_ctime_nsec; __kernel_long_t __unused[3]; }; struct __old_kernel_stat { short unsigned int st_dev; short unsigned int st_ino; short unsigned int st_mode; short unsigned int st_nlink; short unsigned int st_uid; short unsigned int st_gid; short unsigned int st_rdev; unsigned int st_size; unsigned int st_atime; unsigned int st_mtime; unsigned int st_ctime; }; struct statx_timestamp { __s64 tv_sec; __u32 tv_nsec; __s32 __reserved; }; struct statx { __u32 stx_mask; __u32 stx_blksize; __u64 stx_attributes; __u32 stx_nlink; __u32 stx_uid; __u32 stx_gid; __u16 stx_mode; __u16 __spare0[1]; __u64 stx_ino; __u64 stx_size; __u64 stx_blocks; __u64 stx_attributes_mask; struct statx_timestamp stx_atime; struct statx_timestamp stx_btime; struct statx_timestamp stx_ctime; struct statx_timestamp stx_mtime; __u32 stx_rdev_major; __u32 stx_rdev_minor; __u32 stx_dev_major; __u32 stx_dev_minor; __u64 stx_mnt_id; __u64 __spare2; __u64 __spare3[12]; }; struct mount; struct mnt_namespace { struct ns_common ns; struct mount *root; struct list_head list; spinlock_t ns_lock; struct user_namespace *user_ns; struct ucounts *ucounts; u64 seq; wait_queue_head_t poll; u64 event; unsigned int mounts; unsigned int pending_mounts; }; typedef u32 compat_ino_t; typedef u16 __compat_uid_t; typedef u16 __compat_gid_t; typedef u16 compat_mode_t; typedef u16 compat_dev_t; typedef u16 compat_nlink_t; struct compat_stat { compat_dev_t st_dev; u16 __pad1; compat_ino_t st_ino; compat_mode_t st_mode; compat_nlink_t st_nlink; __compat_uid_t st_uid; __compat_gid_t st_gid; compat_dev_t st_rdev; u16 __pad2; u32 st_size; u32 st_blksize; u32 st_blocks; u32 st_atime; u32 st_atime_nsec; u32 st_mtime; u32 st_mtime_nsec; u32 st_ctime; u32 st_ctime_nsec; u32 __unused4; u32 __unused5; }; struct mnt_pcp; struct mountpoint; struct mount { struct hlist_node mnt_hash; struct mount *mnt_parent; struct dentry *mnt_mountpoint; struct vfsmount mnt; union { struct callback_head mnt_rcu; struct llist_node mnt_llist; }; struct mnt_pcp *mnt_pcp; struct list_head mnt_mounts; struct list_head mnt_child; struct list_head mnt_instance; const char *mnt_devname; struct list_head mnt_list; struct list_head mnt_expire; struct list_head mnt_share; struct list_head mnt_slave_list; struct list_head mnt_slave; struct mount *mnt_master; struct mnt_namespace *mnt_ns; struct mountpoint *mnt_mp; union { struct hlist_node mnt_mp_list; struct hlist_node mnt_umount; }; struct list_head mnt_umounting; struct fsnotify_mark_connector *mnt_fsnotify_marks; __u32 mnt_fsnotify_mask; int mnt_id; int mnt_group_id; int mnt_expiry_mark; struct hlist_head mnt_pins; struct hlist_head mnt_stuck_children; }; struct mnt_pcp { int mnt_count; int mnt_writers; }; struct mountpoint { struct hlist_node m_hash; struct dentry *m_dentry; struct hlist_head m_list; int m_count; }; typedef short unsigned int ushort; struct user_arg_ptr { bool is_compat; union { const char * const *native; const compat_uptr_t *compat; } ptr; }; enum inode_i_mutex_lock_class { I_MUTEX_NORMAL = 0, I_MUTEX_PARENT = 1, I_MUTEX_CHILD = 2, I_MUTEX_XATTR = 3, I_MUTEX_NONDIR2 = 4, I_MUTEX_PARENT2 = 5, }; struct name_snapshot { struct qstr name; unsigned char inline_name[32]; }; struct saved { struct path link; struct delayed_call done; const char *name; unsigned int seq; }; struct nameidata { struct path path; struct qstr last; struct path root; struct inode *inode; unsigned int flags; unsigned int state; unsigned int seq; unsigned int m_seq; unsigned int r_seq; int last_type; unsigned int depth; int total_link_count; struct saved *stack; struct saved internal[2]; struct filename *name; struct nameidata *saved; unsigned int root_seq; int dfd; kuid_t dir_uid; umode_t dir_mode; }; struct renamedata { struct user_namespace *old_mnt_userns; struct inode *old_dir; struct dentry *old_dentry; struct user_namespace *new_mnt_userns; struct inode *new_dir; struct dentry *new_dentry; struct inode **delegated_inode; unsigned int flags; }; enum { LAST_NORM = 0, LAST_ROOT = 1, LAST_DOT = 2, LAST_DOTDOT = 3, }; enum { WALK_TRAILING = 1, WALK_MORE = 2, WALK_NOFOLLOW = 4, }; struct word_at_a_time { const long unsigned int one_bits; const long unsigned int high_bits; }; struct f_owner_ex { int type; __kernel_pid_t pid; }; struct flock { short int l_type; short int l_whence; __kernel_off_t l_start; __kernel_off_t l_len; __kernel_pid_t l_pid; }; struct compat_flock { short int l_type; short int l_whence; compat_off_t l_start; compat_off_t l_len; compat_pid_t l_pid; }; struct compat_flock64 { short int l_type; short int l_whence; compat_loff_t l_start; compat_loff_t l_len; compat_pid_t l_pid; } __attribute__((packed)); struct file_clone_range { __s64 src_fd; __u64 src_offset; __u64 src_length; __u64 dest_offset; }; struct file_dedupe_range_info { __s64 dest_fd; __u64 dest_offset; __u64 bytes_deduped; __s32 status; __u32 reserved; }; struct file_dedupe_range { __u64 src_offset; __u64 src_length; __u16 dest_count; __u16 reserved1; __u32 reserved2; struct file_dedupe_range_info info[0]; }; struct fsxattr { __u32 fsx_xflags; __u32 fsx_extsize; __u32 fsx_nextents; __u32 fsx_projid; __u32 fsx_cowextsize; unsigned char fsx_pad[8]; }; typedef int get_block_t(struct inode *, sector_t, struct buffer_head *, int); struct fiemap_extent; struct fiemap_extent_info { unsigned int fi_flags; unsigned int fi_extents_mapped; unsigned int fi_extents_max; struct fiemap_extent *fi_extents_start; }; struct fileattr { u32 flags; u32 fsx_xflags; u32 fsx_extsize; u32 fsx_nextents; u32 fsx_projid; u32 fsx_cowextsize; bool flags_valid: 1; bool fsx_valid: 1; }; struct space_resv { __s16 l_type; __s16 l_whence; __s64 l_start; __s64 l_len; __s32 l_sysid; __u32 l_pid; __s32 l_pad[4]; }; struct space_resv_32 { __s16 l_type; __s16 l_whence; __s64 l_start; __s64 l_len; __s32 l_sysid; __u32 l_pid; __s32 l_pad[4]; } __attribute__((packed)); struct fiemap_extent { __u64 fe_logical; __u64 fe_physical; __u64 fe_length; __u64 fe_reserved64[2]; __u32 fe_flags; __u32 fe_reserved[3]; }; struct fiemap { __u64 fm_start; __u64 fm_length; __u32 fm_flags; __u32 fm_mapped_extents; __u32 fm_extent_count; __u32 fm_reserved; struct fiemap_extent fm_extents[0]; }; struct linux_dirent64 { u64 d_ino; s64 d_off; short unsigned int d_reclen; unsigned char d_type; char d_name[0]; }; struct old_linux_dirent { long unsigned int d_ino; long unsigned int d_offset; short unsigned int d_namlen; char d_name[1]; }; struct readdir_callback { struct dir_context ctx; struct old_linux_dirent *dirent; int result; }; struct linux_dirent { long unsigned int d_ino; long unsigned int d_off; short unsigned int d_reclen; char d_name[1]; }; struct getdents_callback { struct dir_context ctx; struct linux_dirent *current_dir; int prev_reclen; int count; int error; }; struct getdents_callback64 { struct dir_context ctx; struct linux_dirent64 *current_dir; int prev_reclen; int count; int error; }; struct compat_old_linux_dirent { compat_ulong_t d_ino; compat_ulong_t d_offset; short unsigned int d_namlen; char d_name[1]; }; struct compat_readdir_callback { struct dir_context ctx; struct compat_old_linux_dirent *dirent; int result; }; struct compat_linux_dirent { compat_ulong_t d_ino; compat_ulong_t d_off; short unsigned int d_reclen; char d_name[1]; }; struct compat_getdents_callback { struct dir_context ctx; struct compat_linux_dirent *current_dir; int prev_reclen; int count; int error; }; typedef struct { long unsigned int fds_bits[16]; } __kernel_fd_set; typedef __kernel_fd_set fd_set; struct poll_table_entry { struct file *filp; __poll_t key; wait_queue_entry_t wait; wait_queue_head_t *wait_address; }; struct poll_table_page; struct poll_wqueues { poll_table pt; struct poll_table_page *table; struct task_struct *polling_task; int triggered; int error; int inline_index; struct poll_table_entry inline_entries[9]; }; struct poll_table_page { struct poll_table_page *next; struct poll_table_entry *entry; struct poll_table_entry entries[0]; }; enum poll_time_type { PT_TIMEVAL = 0, PT_OLD_TIMEVAL = 1, PT_TIMESPEC = 2, PT_OLD_TIMESPEC = 3, }; typedef struct { long unsigned int *in; long unsigned int *out; long unsigned int *ex; long unsigned int *res_in; long unsigned int *res_out; long unsigned int *res_ex; } fd_set_bits; struct sigset_argpack { sigset_t *p; size_t size; }; struct poll_list { struct poll_list *next; int len; struct pollfd entries[0]; }; struct compat_sel_arg_struct { compat_ulong_t n; compat_uptr_t inp; compat_uptr_t outp; compat_uptr_t exp; compat_uptr_t tvp; }; struct compat_sigset_argpack { compat_uptr_t p; compat_size_t size; }; enum dentry_d_lock_class { DENTRY_D_LOCK_NORMAL = 0, DENTRY_D_LOCK_NESTED = 1, }; struct external_name { union { atomic_t count; struct callback_head head; } u; unsigned char name[0]; }; enum d_walk_ret { D_WALK_CONTINUE = 0, D_WALK_QUIT = 1, D_WALK_NORETRY = 2, D_WALK_SKIP = 3, }; struct check_mount { struct vfsmount *mnt; unsigned int mounted; }; struct select_data { struct dentry *start; union { long int found; struct dentry *victim; }; struct list_head dispose; }; enum file_time_flags { S_ATIME = 1, S_MTIME = 2, S_CTIME = 4, S_VERSION = 8, }; struct mount_attr { __u64 attr_set; __u64 attr_clr; __u64 propagation; __u64 userns_fd; }; struct proc_mounts { struct mnt_namespace *ns; struct path root; int (*show)(struct seq_file *, struct vfsmount *); struct mount cursor; }; struct mount_kattr { unsigned int attr_set; unsigned int attr_clr; unsigned int propagation; unsigned int lookup_flags; bool recurse; struct user_namespace *mnt_userns; }; enum umount_tree_flags { UMOUNT_SYNC = 1, UMOUNT_PROPAGATE = 2, UMOUNT_CONNECTED = 4, }; struct unicode_map { const char *charset; int version; }; struct simple_transaction_argresp { ssize_t size; char data[0]; }; struct simple_attr { int (*get)(void *, u64 *); int (*set)(void *, u64); char get_buf[24]; char set_buf[24]; void *data; const char *fmt; struct mutex mutex; }; struct wb_writeback_work { long int nr_pages; struct super_block *sb; enum writeback_sync_modes sync_mode; unsigned int tagged_writepages: 1; unsigned int for_kupdate: 1; unsigned int range_cyclic: 1; unsigned int for_background: 1; unsigned int for_sync: 1; unsigned int auto_free: 1; enum wb_reason reason; struct list_head list; struct wb_completion *done; }; struct trace_event_raw_writeback_page_template { struct trace_entry ent; char name[32]; ino_t ino; long unsigned int index; char __data[0]; }; struct trace_event_raw_writeback_dirty_inode_template { struct trace_entry ent; char name[32]; ino_t ino; long unsigned int state; long unsigned int flags; char __data[0]; }; struct trace_event_raw_inode_foreign_history { struct trace_entry ent; char name[32]; ino_t ino; ino_t cgroup_ino; unsigned int history; char __data[0]; }; struct trace_event_raw_inode_switch_wbs { struct trace_entry ent; char name[32]; ino_t ino; ino_t old_cgroup_ino; ino_t new_cgroup_ino; char __data[0]; }; struct trace_event_raw_track_foreign_dirty { struct trace_entry ent; char name[32]; u64 bdi_id; ino_t ino; unsigned int memcg_id; ino_t cgroup_ino; ino_t page_cgroup_ino; char __data[0]; }; struct trace_event_raw_flush_foreign { struct trace_entry ent; char name[32]; ino_t cgroup_ino; unsigned int frn_bdi_id; unsigned int frn_memcg_id; char __data[0]; }; struct trace_event_raw_writeback_write_inode_template { struct trace_entry ent; char name[32]; ino_t ino; int sync_mode; ino_t cgroup_ino; char __data[0]; }; struct trace_event_raw_writeback_work_class { struct trace_entry ent; char name[32]; long int nr_pages; dev_t sb_dev; int sync_mode; int for_kupdate; int range_cyclic; int for_background; int reason; ino_t cgroup_ino; char __data[0]; }; struct trace_event_raw_writeback_pages_written { struct trace_entry ent; long int pages; char __data[0]; }; struct trace_event_raw_writeback_class { struct trace_entry ent; char name[32]; ino_t cgroup_ino; char __data[0]; }; struct trace_event_raw_writeback_bdi_register { struct trace_entry ent; char name[32]; char __data[0]; }; struct trace_event_raw_wbc_class { struct trace_entry ent; char name[32]; long int nr_to_write; long int pages_skipped; int sync_mode; int for_kupdate; int for_background; int for_reclaim; int range_cyclic; long int range_start; long int range_end; ino_t cgroup_ino; char __data[0]; }; struct trace_event_raw_writeback_queue_io { struct trace_entry ent; char name[32]; long unsigned int older; long int age; int moved; int reason; ino_t cgroup_ino; char __data[0]; }; struct trace_event_raw_global_dirty_state { struct trace_entry ent; long unsigned int nr_dirty; long unsigned int nr_writeback; long unsigned int background_thresh; long unsigned int dirty_thresh; long unsigned int dirty_limit; long unsigned int nr_dirtied; long unsigned int nr_written; char __data[0]; }; struct trace_event_raw_bdi_dirty_ratelimit { struct trace_entry ent; char bdi[32]; long unsigned int write_bw; long unsigned int avg_write_bw; long unsigned int dirty_rate; long unsigned int dirty_ratelimit; long unsigned int task_ratelimit; long unsigned int balanced_dirty_ratelimit; ino_t cgroup_ino; char __data[0]; }; struct trace_event_raw_balance_dirty_pages { struct trace_entry ent; char bdi[32]; long unsigned int limit; long unsigned int setpoint; long unsigned int dirty; long unsigned int bdi_setpoint; long unsigned int bdi_dirty; long unsigned int dirty_ratelimit; long unsigned int task_ratelimit; unsigned int dirtied; unsigned int dirtied_pause; long unsigned int paused; long int pause; long unsigned int period; long int think; ino_t cgroup_ino; char __data[0]; }; struct trace_event_raw_writeback_sb_inodes_requeue { struct trace_entry ent; char name[32]; ino_t ino; long unsigned int state; long unsigned int dirtied_when; ino_t cgroup_ino; char __data[0]; }; struct trace_event_raw_writeback_congest_waited_template { struct trace_entry ent; unsigned int usec_timeout; unsigned int usec_delayed; char __data[0]; }; struct trace_event_raw_writeback_single_inode_template { struct trace_entry ent; char name[32]; ino_t ino; long unsigned int state; long unsigned int dirtied_when; long unsigned int writeback_index; long int nr_to_write; long unsigned int wrote; ino_t cgroup_ino; char __data[0]; }; struct trace_event_raw_writeback_inode_template { struct trace_entry ent; dev_t dev; ino_t ino; long unsigned int state; __u16 mode; long unsigned int dirtied_when; char __data[0]; }; struct trace_event_data_offsets_writeback_page_template {}; struct trace_event_data_offsets_writeback_dirty_inode_template {}; struct trace_event_data_offsets_inode_foreign_history {}; struct trace_event_data_offsets_inode_switch_wbs {}; struct trace_event_data_offsets_track_foreign_dirty {}; struct trace_event_data_offsets_flush_foreign {}; struct trace_event_data_offsets_writeback_write_inode_template {}; struct trace_event_data_offsets_writeback_work_class {}; struct trace_event_data_offsets_writeback_pages_written {}; struct trace_event_data_offsets_writeback_class {}; struct trace_event_data_offsets_writeback_bdi_register {}; struct trace_event_data_offsets_wbc_class {}; struct trace_event_data_offsets_writeback_queue_io {}; struct trace_event_data_offsets_global_dirty_state {}; struct trace_event_data_offsets_bdi_dirty_ratelimit {}; struct trace_event_data_offsets_balance_dirty_pages {}; struct trace_event_data_offsets_writeback_sb_inodes_requeue {}; struct trace_event_data_offsets_writeback_congest_waited_template {}; struct trace_event_data_offsets_writeback_single_inode_template {}; struct trace_event_data_offsets_writeback_inode_template {}; typedef void (*btf_trace_writeback_dirty_page)(void *, struct page *, struct address_space *); typedef void (*btf_trace_wait_on_page_writeback)(void *, struct page *, struct address_space *); typedef void (*btf_trace_writeback_mark_inode_dirty)(void *, struct inode *, int); typedef void (*btf_trace_writeback_dirty_inode_start)(void *, struct inode *, int); typedef void (*btf_trace_writeback_dirty_inode)(void *, struct inode *, int); typedef void (*btf_trace_inode_foreign_history)(void *, struct inode *, struct writeback_control *, unsigned int); typedef void (*btf_trace_inode_switch_wbs)(void *, struct inode *, struct bdi_writeback *, struct bdi_writeback *); typedef void (*btf_trace_track_foreign_dirty)(void *, struct page *, struct bdi_writeback *); typedef void (*btf_trace_flush_foreign)(void *, struct bdi_writeback *, unsigned int, unsigned int); typedef void (*btf_trace_writeback_write_inode_start)(void *, struct inode *, struct writeback_control *); typedef void (*btf_trace_writeback_write_inode)(void *, struct inode *, struct writeback_control *); typedef void (*btf_trace_writeback_queue)(void *, struct bdi_writeback *, struct wb_writeback_work *); typedef void (*btf_trace_writeback_exec)(void *, struct bdi_writeback *, struct wb_writeback_work *); typedef void (*btf_trace_writeback_start)(void *, struct bdi_writeback *, struct wb_writeback_work *); typedef void (*btf_trace_writeback_written)(void *, struct bdi_writeback *, struct wb_writeback_work *); typedef void (*btf_trace_writeback_wait)(void *, struct bdi_writeback *, struct wb_writeback_work *); typedef void (*btf_trace_writeback_pages_written)(void *, long int); typedef void (*btf_trace_writeback_wake_background)(void *, struct bdi_writeback *); typedef void (*btf_trace_writeback_bdi_register)(void *, struct backing_dev_info *); typedef void (*btf_trace_wbc_writepage)(void *, struct writeback_control *, struct backing_dev_info *); typedef void (*btf_trace_writeback_queue_io)(void *, struct bdi_writeback *, struct wb_writeback_work *, long unsigned int, int); typedef void (*btf_trace_global_dirty_state)(void *, long unsigned int, long unsigned int); typedef void (*btf_trace_bdi_dirty_ratelimit)(void *, struct bdi_writeback *, long unsigned int, long unsigned int); typedef void (*btf_trace_balance_dirty_pages)(void *, struct bdi_writeback *, long unsigned int, long unsigned int, long unsigned int, long unsigned int, long unsigned int, long unsigned int, long unsigned int, long unsigned int, long unsigned int, long int, long unsigned int); typedef void (*btf_trace_writeback_sb_inodes_requeue)(void *, struct inode *); typedef void (*btf_trace_writeback_congestion_wait)(void *, unsigned int, unsigned int); typedef void (*btf_trace_writeback_wait_iff_congested)(void *, unsigned int, unsigned int); typedef void (*btf_trace_writeback_single_inode_start)(void *, struct inode *, struct writeback_control *, long unsigned int); typedef void (*btf_trace_writeback_single_inode)(void *, struct inode *, struct writeback_control *, long unsigned int); typedef void (*btf_trace_writeback_lazytime)(void *, struct inode *); typedef void (*btf_trace_writeback_lazytime_iput)(void *, struct inode *); typedef void (*btf_trace_writeback_dirty_inode_enqueue)(void *, struct inode *); typedef void (*btf_trace_sb_mark_inode_writeback)(void *, struct inode *); typedef void (*btf_trace_sb_clear_inode_writeback)(void *, struct inode *); struct inode_switch_wbs_context { struct rcu_work work; struct bdi_writeback *new_wb; struct inode *inodes[0]; }; struct splice_desc { size_t total_len; unsigned int len; unsigned int flags; union { void *userptr; struct file *file; void *data; } u; loff_t pos; loff_t *opos; size_t num_spliced; bool need_wakeup; }; typedef int splice_actor(struct pipe_inode_info *, struct pipe_buffer *, struct splice_desc *); typedef int splice_direct_actor(struct pipe_inode_info *, struct splice_desc *); struct old_utimbuf32 { old_time32_t actime; old_time32_t modtime; }; struct utimbuf { __kernel_old_time_t actime; __kernel_old_time_t modtime; }; struct prepend_buffer { char *buf; int len; }; typedef int __kernel_daddr_t; struct ustat { __kernel_daddr_t f_tfree; long unsigned int f_tinode; char f_fname[6]; char f_fpack[6]; }; typedef s32 compat_daddr_t; typedef __kernel_fsid_t compat_fsid_t; struct compat_statfs { int f_type; int f_bsize; int f_blocks; int f_bfree; int f_bavail; int f_files; int f_ffree; compat_fsid_t f_fsid; int f_namelen; int f_frsize; int f_flags; int f_spare[4]; }; struct compat_ustat { compat_daddr_t f_tfree; compat_ino_t f_tinode; char f_fname[6]; char f_fpack[6]; }; struct statfs { __kernel_long_t f_type; __kernel_long_t f_bsize; __kernel_long_t f_blocks; __kernel_long_t f_bfree; __kernel_long_t f_bavail; __kernel_long_t f_files; __kernel_long_t f_ffree; __kernel_fsid_t f_fsid; __kernel_long_t f_namelen; __kernel_long_t f_frsize; __kernel_long_t f_flags; __kernel_long_t f_spare[4]; }; struct statfs64 { __kernel_long_t f_type; __kernel_long_t f_bsize; __u64 f_blocks; __u64 f_bfree; __u64 f_bavail; __u64 f_files; __u64 f_ffree; __kernel_fsid_t f_fsid; __kernel_long_t f_namelen; __kernel_long_t f_frsize; __kernel_long_t f_flags; __kernel_long_t f_spare[4]; }; struct compat_statfs64 { __u32 f_type; __u32 f_bsize; __u64 f_blocks; __u64 f_bfree; __u64 f_bavail; __u64 f_files; __u64 f_ffree; __kernel_fsid_t f_fsid; __u32 f_namelen; __u32 f_frsize; __u32 f_flags; __u32 f_spare[4]; } __attribute__((packed)); struct ns_get_path_task_args { const struct proc_ns_operations *ns_ops; struct task_struct *task; }; enum legacy_fs_param { LEGACY_FS_UNSET_PARAMS = 0, LEGACY_FS_MONOLITHIC_PARAMS = 1, LEGACY_FS_INDIVIDUAL_PARAMS = 2, }; struct legacy_fs_context { char *legacy_data; size_t data_size; enum legacy_fs_param param_type; }; enum fsconfig_command { FSCONFIG_SET_FLAG = 0, FSCONFIG_SET_STRING = 1, FSCONFIG_SET_BINARY = 2, FSCONFIG_SET_PATH = 3, FSCONFIG_SET_PATH_EMPTY = 4, FSCONFIG_SET_FD = 5, FSCONFIG_CMD_CREATE = 6, FSCONFIG_CMD_RECONFIGURE = 7, }; struct dax_device; struct iomap_page_ops; struct iomap___2 { u64 addr; loff_t offset; u64 length; u16 type; u16 flags; struct block_device *bdev; struct dax_device *dax_dev; void *inline_data; void *private; const struct iomap_page_ops *page_ops; }; struct iomap_page_ops { int (*page_prepare)(struct inode *, loff_t, unsigned int, struct iomap___2 *); void (*page_done)(struct inode *, loff_t, unsigned int, struct page *, struct iomap___2 *); }; struct decrypt_bh_ctx { struct work_struct work; struct buffer_head *bh; }; struct bh_lru { struct buffer_head *bhs[16]; }; struct bh_accounting { int nr; int ratelimit; }; enum stat_group { STAT_READ = 0, STAT_WRITE = 1, STAT_DISCARD = 2, STAT_FLUSH = 3, NR_STAT_GROUPS = 4, }; enum { DISK_EVENT_MEDIA_CHANGE = 1, DISK_EVENT_EJECT_REQUEST = 2, }; enum { BIOSET_NEED_BVECS = 1, BIOSET_NEED_RESCUER = 2, }; struct bdev_inode { struct block_device bdev; struct inode vfs_inode; }; struct blkdev_dio { union { struct kiocb *iocb; struct task_struct *waiter; }; size_t size; atomic_t ref; bool multi_bio: 1; bool should_dirty: 1; bool is_sync: 1; struct bio bio; }; struct bd_holder_disk { struct list_head list; struct gendisk *disk; int refcnt; }; typedef int dio_iodone_t(struct kiocb *, loff_t, ssize_t, void *); typedef void dio_submit_t(struct bio *, struct inode *, loff_t); enum { DIO_LOCKING = 1, DIO_SKIP_HOLES = 2, }; struct dio_submit { struct bio *bio; unsigned int blkbits; unsigned int blkfactor; unsigned int start_zero_done; int pages_in_io; sector_t block_in_file; unsigned int blocks_available; int reap_counter; sector_t final_block_in_request; int boundary; get_block_t *get_block; dio_submit_t *submit_io; loff_t logical_offset_in_bio; sector_t final_block_in_bio; sector_t next_block_for_io; struct page *cur_page; unsigned int cur_page_offset; unsigned int cur_page_len; sector_t cur_page_block; loff_t cur_page_fs_offset; struct iov_iter *iter; unsigned int head; unsigned int tail; size_t from; size_t to; }; struct dio { int flags; int op; int op_flags; blk_qc_t bio_cookie; struct gendisk *bio_disk; struct inode *inode; loff_t i_size; dio_iodone_t *end_io; void *private; spinlock_t bio_lock; int page_errors; int is_async; bool defer_completion; bool should_dirty; int io_error; long unsigned int refcount; struct bio *bio_list; struct task_struct *waiter; struct kiocb *iocb; ssize_t result; union { struct page *pages[64]; struct work_struct complete_work; }; long: 64; }; struct bvec_iter_all { struct bio_vec bv; int idx; unsigned int done; }; struct mpage_readpage_args { struct bio *bio; struct page *page; unsigned int nr_pages; bool is_readahead; sector_t last_block_in_bio; struct buffer_head map_bh; long unsigned int first_logical_block; get_block_t *get_block; }; struct mpage_data { struct bio *bio; sector_t last_block_in_bio; get_block_t *get_block; unsigned int use_writepage; }; typedef u32 nlink_t; typedef int (*proc_write_t)(struct file *, char *, size_t); struct proc_dir_entry { atomic_t in_use; refcount_t refcnt; struct list_head pde_openers; spinlock_t pde_unload_lock; struct completion *pde_unload_completion; const struct inode_operations *proc_iops; union { const struct proc_ops *proc_ops; const struct file_operations *proc_dir_ops; }; const struct dentry_operations *proc_dops; union { const struct seq_operations *seq_ops; int (*single_show)(struct seq_file *, void *); }; proc_write_t write; void *data; unsigned int state_size; unsigned int low_ino; nlink_t nlink; kuid_t uid; kgid_t gid; loff_t size; struct proc_dir_entry *parent; struct rb_root subdir; struct rb_node subdir_node; char *name; umode_t mode; u8 flags; u8 namelen; char inline_name[0]; }; union proc_op { int (*proc_get_link)(struct dentry *, struct path *); int (*proc_show)(struct seq_file *, struct pid_namespace *, struct pid *, struct task_struct *); const char *lsm; }; struct proc_inode { struct pid *pid; unsigned int fd; union proc_op op; struct proc_dir_entry *pde; struct ctl_table_header *sysctl; struct ctl_table *sysctl_entry; struct hlist_node sibling_inodes; const struct proc_ns_operations *ns_ops; struct inode vfs_inode; }; struct proc_fs_opts { int flag; const char *str; }; struct file_handle { __u32 handle_bytes; int handle_type; unsigned char f_handle[0]; }; struct inotify_inode_mark { struct fsnotify_mark fsn_mark; int wd; }; struct dnotify_struct { struct dnotify_struct *dn_next; __u32 dn_mask; int dn_fd; struct file *dn_filp; fl_owner_t dn_owner; }; struct dnotify_mark { struct fsnotify_mark fsn_mark; struct dnotify_struct *dn; }; struct inotify_event_info { struct fsnotify_event fse; u32 mask; int wd; u32 sync_cookie; int name_len; char name[0]; }; struct inotify_event { __s32 wd; __u32 mask; __u32 cookie; __u32 len; char name[0]; }; enum { FAN_EVENT_INIT = 0, FAN_EVENT_REPORTED = 1, FAN_EVENT_ANSWERED = 2, FAN_EVENT_CANCELED = 3, }; struct fanotify_fh { u8 type; u8 len; u8 flags; u8 pad; unsigned char buf[0]; }; struct fanotify_info { u8 dir_fh_totlen; u8 file_fh_totlen; u8 name_len; u8 pad; unsigned char buf[0]; }; enum fanotify_event_type { FANOTIFY_EVENT_TYPE_FID = 0, FANOTIFY_EVENT_TYPE_FID_NAME = 1, FANOTIFY_EVENT_TYPE_PATH = 2, FANOTIFY_EVENT_TYPE_PATH_PERM = 3, FANOTIFY_EVENT_TYPE_OVERFLOW = 4, __FANOTIFY_EVENT_TYPE_NUM = 5, }; struct fanotify_event { struct fsnotify_event fse; struct hlist_node merge_list; u32 mask; struct { unsigned int type: 3; unsigned int hash: 29; }; struct pid *pid; }; struct fanotify_fid_event { struct fanotify_event fae; __kernel_fsid_t fsid; struct fanotify_fh object_fh; unsigned char _inline_fh_buf[12]; }; struct fanotify_name_event { struct fanotify_event fae; __kernel_fsid_t fsid; struct fanotify_info info; }; struct fanotify_path_event { struct fanotify_event fae; struct path path; }; struct fanotify_perm_event { struct fanotify_event fae; struct path path; short unsigned int response; short unsigned int state; int fd; }; struct fanotify_event_metadata { __u32 event_len; __u8 vers; __u8 reserved; __u16 metadata_len; __u64 mask; __s32 fd; __s32 pid; }; struct fanotify_event_info_header { __u8 info_type; __u8 pad; __u16 len; }; struct fanotify_event_info_fid { struct fanotify_event_info_header hdr; __kernel_fsid_t fsid; unsigned char handle[0]; }; struct fanotify_response { __s32 fd; __u32 response; }; struct epoll_event { __poll_t events; __u64 data; } __attribute__((packed)); struct epoll_filefd { struct file *file; int fd; } __attribute__((packed)); struct epitem; struct eppoll_entry { struct eppoll_entry *next; struct epitem *base; wait_queue_entry_t wait; wait_queue_head_t *whead; }; struct eventpoll; struct epitem { union { struct rb_node rbn; struct callback_head rcu; }; struct list_head rdllink; struct epitem *next; struct epoll_filefd ffd; struct eppoll_entry *pwqlist; struct eventpoll *ep; struct hlist_node fllink; struct wakeup_source *ws; struct epoll_event event; }; struct eventpoll { struct mutex mtx; wait_queue_head_t wq; wait_queue_head_t poll_wait; struct list_head rdllist; rwlock_t lock; struct rb_root_cached rbr; struct epitem *ovflist; struct wakeup_source *ws; struct user_struct *user; struct file *file; u64 gen; struct hlist_head refs; unsigned int napi_id; }; struct ep_pqueue { poll_table pt; struct epitem *epi; }; struct epitems_head { struct hlist_head epitems; struct epitems_head *next; }; struct signalfd_siginfo { __u32 ssi_signo; __s32 ssi_errno; __s32 ssi_code; __u32 ssi_pid; __u32 ssi_uid; __s32 ssi_fd; __u32 ssi_tid; __u32 ssi_band; __u32 ssi_overrun; __u32 ssi_trapno; __s32 ssi_status; __s32 ssi_int; __u64 ssi_ptr; __u64 ssi_utime; __u64 ssi_stime; __u64 ssi_addr; __u16 ssi_addr_lsb; __u16 __pad2; __s32 ssi_syscall; __u64 ssi_call_addr; __u32 ssi_arch; __u8 __pad[28]; }; struct signalfd_ctx { sigset_t sigmask; }; struct timerfd_ctx { union { struct hrtimer tmr; struct alarm alarm; } t; ktime_t tintv; ktime_t moffs; wait_queue_head_t wqh; u64 ticks; int clockid; short unsigned int expired; short unsigned int settime_flags; struct callback_head rcu; struct list_head clist; spinlock_t cancel_lock; bool might_cancel; }; struct eventfd_ctx___2 { struct kref kref; wait_queue_head_t wqh; __u64 count; unsigned int flags; int id; }; enum userfaultfd_state { UFFD_STATE_WAIT_API = 0, UFFD_STATE_RUNNING = 1, }; struct userfaultfd_ctx { wait_queue_head_t fault_pending_wqh; wait_queue_head_t fault_wqh; wait_queue_head_t fd_wqh; wait_queue_head_t event_wqh; seqcount_spinlock_t refile_seq; refcount_t refcount; unsigned int flags; unsigned int features; enum userfaultfd_state state; bool released; bool mmap_changing; struct mm_struct *mm; }; struct uffd_msg { __u8 event; __u8 reserved1; __u16 reserved2; __u32 reserved3; union { struct { __u64 flags; __u64 address; union { __u32 ptid; } feat; } pagefault; struct { __u32 ufd; } fork; struct { __u64 from; __u64 to; __u64 len; } remap; struct { __u64 start; __u64 end; } remove; struct { __u64 reserved1; __u64 reserved2; __u64 reserved3; } reserved; } arg; }; struct uffdio_api { __u64 api; __u64 features; __u64 ioctls; }; struct uffdio_range { __u64 start; __u64 len; }; struct uffdio_register { struct uffdio_range range; __u64 mode; __u64 ioctls; }; struct uffdio_copy { __u64 dst; __u64 src; __u64 len; __u64 mode; __s64 copy; }; struct uffdio_zeropage { struct uffdio_range range; __u64 mode; __s64 zeropage; }; struct uffdio_writeprotect { struct uffdio_range range; __u64 mode; }; struct uffdio_continue { struct uffdio_range range; __u64 mode; __s64 mapped; }; struct userfaultfd_fork_ctx { struct userfaultfd_ctx *orig; struct userfaultfd_ctx *new; struct list_head list; }; struct userfaultfd_unmap_ctx { struct userfaultfd_ctx *ctx; long unsigned int start; long unsigned int end; struct list_head list; }; struct userfaultfd_wait_queue { struct uffd_msg msg; wait_queue_entry_t wq; struct userfaultfd_ctx *ctx; bool waken; }; struct userfaultfd_wake_range { long unsigned int start; long unsigned int len; }; struct kioctx; struct kioctx_table { struct callback_head rcu; unsigned int nr; struct kioctx *table[0]; }; typedef __kernel_ulong_t aio_context_t; enum { IOCB_CMD_PREAD = 0, IOCB_CMD_PWRITE = 1, IOCB_CMD_FSYNC = 2, IOCB_CMD_FDSYNC = 3, IOCB_CMD_POLL = 5, IOCB_CMD_NOOP = 6, IOCB_CMD_PREADV = 7, IOCB_CMD_PWRITEV = 8, }; struct io_event { __u64 data; __u64 obj; __s64 res; __s64 res2; }; struct iocb { __u64 aio_data; __u32 aio_key; __kernel_rwf_t aio_rw_flags; __u16 aio_lio_opcode; __s16 aio_reqprio; __u32 aio_fildes; __u64 aio_buf; __u64 aio_nbytes; __s64 aio_offset; __u64 aio_reserved2; __u32 aio_flags; __u32 aio_resfd; }; typedef u32 compat_aio_context_t; typedef int kiocb_cancel_fn(struct kiocb *); struct aio_ring { unsigned int id; unsigned int nr; unsigned int head; unsigned int tail; unsigned int magic; unsigned int compat_features; unsigned int incompat_features; unsigned int header_length; struct io_event io_events[0]; }; struct kioctx_cpu; struct ctx_rq_wait; struct kioctx { struct percpu_ref users; atomic_t dead; struct percpu_ref reqs; long unsigned int user_id; struct kioctx_cpu *cpu; unsigned int req_batch; unsigned int max_reqs; unsigned int nr_events; long unsigned int mmap_base; long unsigned int mmap_size; struct page **ring_pages; long int nr_pages; struct rcu_work free_rwork; struct ctx_rq_wait *rq_wait; long: 64; long: 64; long: 64; struct { atomic_t reqs_available; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct { spinlock_t ctx_lock; struct list_head active_reqs; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct { struct mutex ring_lock; wait_queue_head_t wait; long: 64; }; struct { unsigned int tail; unsigned int completed_events; spinlock_t completion_lock; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct page *internal_pages[8]; struct file *aio_ring_file; unsigned int id; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct kioctx_cpu { unsigned int reqs_available; }; struct ctx_rq_wait { struct completion comp; atomic_t count; }; struct fsync_iocb { struct file *file; struct work_struct work; bool datasync; struct cred *creds; }; struct poll_iocb { struct file *file; struct wait_queue_head *head; __poll_t events; bool done; bool cancelled; struct wait_queue_entry wait; struct work_struct work; }; struct aio_kiocb { union { struct file *ki_filp; struct kiocb rw; struct fsync_iocb fsync; struct poll_iocb poll; }; struct kioctx *ki_ctx; kiocb_cancel_fn *ki_cancel; struct io_event ki_res; struct list_head ki_list; refcount_t ki_refcnt; struct eventfd_ctx *ki_eventfd; }; struct aio_poll_table { struct poll_table_struct pt; struct aio_kiocb *iocb; int error; }; struct __aio_sigset { const sigset_t *sigmask; size_t sigsetsize; }; struct __compat_aio_sigset { compat_uptr_t sigmask; compat_size_t sigsetsize; }; struct xa_limit { u32 max; u32 min; }; struct io_wq; struct io_wq_work_node; struct io_wq_work_list { struct io_wq_work_node *first; struct io_wq_work_node *last; }; struct io_ring_ctx; struct io_uring_task { int cached_refs; struct xarray xa; struct wait_queue_head wait; const struct io_ring_ctx *last; struct io_wq *io_wq; struct percpu_counter inflight; atomic_t inflight_tracked; atomic_t in_idle; spinlock_t task_lock; struct io_wq_work_list task_list; long unsigned int task_state; struct callback_head task_work; }; struct user_msghdr { void *msg_name; int msg_namelen; struct iovec *msg_iov; __kernel_size_t msg_iovlen; void *msg_control; __kernel_size_t msg_controllen; unsigned int msg_flags; }; typedef s32 compat_ssize_t; struct compat_msghdr { compat_uptr_t msg_name; compat_int_t msg_namelen; compat_uptr_t msg_iov; compat_size_t msg_iovlen; compat_uptr_t msg_control; compat_size_t msg_controllen; compat_uint_t msg_flags; }; struct scm_fp_list { short int count; short int max; struct user_struct *user; struct file *fp[253]; }; struct unix_skb_parms { struct pid *pid; kuid_t uid; kgid_t gid; struct scm_fp_list *fp; u32 secid; u32 consumed; }; struct trace_event_raw_io_uring_create { struct trace_entry ent; int fd; void *ctx; u32 sq_entries; u32 cq_entries; u32 flags; char __data[0]; }; struct trace_event_raw_io_uring_register { struct trace_entry ent; void *ctx; unsigned int opcode; unsigned int nr_files; unsigned int nr_bufs; bool eventfd; long int ret; char __data[0]; }; struct trace_event_raw_io_uring_file_get { struct trace_entry ent; void *ctx; int fd; char __data[0]; }; struct io_wq_work; struct trace_event_raw_io_uring_queue_async_work { struct trace_entry ent; void *ctx; int rw; void *req; struct io_wq_work *work; unsigned int flags; char __data[0]; }; struct io_wq_work_node { struct io_wq_work_node *next; }; struct io_wq_work { struct io_wq_work_node list; unsigned int flags; }; struct trace_event_raw_io_uring_defer { struct trace_entry ent; void *ctx; void *req; long long unsigned int data; char __data[0]; }; struct trace_event_raw_io_uring_link { struct trace_entry ent; void *ctx; void *req; void *target_req; char __data[0]; }; struct trace_event_raw_io_uring_cqring_wait { struct trace_entry ent; void *ctx; int min_events; char __data[0]; }; struct trace_event_raw_io_uring_fail_link { struct trace_entry ent; void *req; void *link; char __data[0]; }; struct trace_event_raw_io_uring_complete { struct trace_entry ent; void *ctx; u64 user_data; long int res; unsigned int cflags; char __data[0]; }; struct trace_event_raw_io_uring_submit_sqe { struct trace_entry ent; void *ctx; void *req; u8 opcode; u64 user_data; u32 flags; bool force_nonblock; bool sq_thread; char __data[0]; }; struct trace_event_raw_io_uring_poll_arm { struct trace_entry ent; void *ctx; void *req; u8 opcode; u64 user_data; int mask; int events; char __data[0]; }; struct trace_event_raw_io_uring_poll_wake { struct trace_entry ent; void *ctx; u8 opcode; u64 user_data; int mask; char __data[0]; }; struct trace_event_raw_io_uring_task_add { struct trace_entry ent; void *ctx; u8 opcode; u64 user_data; int mask; char __data[0]; }; struct trace_event_raw_io_uring_task_run { struct trace_entry ent; void *ctx; void *req; u8 opcode; u64 user_data; char __data[0]; }; struct trace_event_data_offsets_io_uring_create {}; struct trace_event_data_offsets_io_uring_register {}; struct trace_event_data_offsets_io_uring_file_get {}; struct trace_event_data_offsets_io_uring_queue_async_work {}; struct trace_event_data_offsets_io_uring_defer {}; struct trace_event_data_offsets_io_uring_link {}; struct trace_event_data_offsets_io_uring_cqring_wait {}; struct trace_event_data_offsets_io_uring_fail_link {}; struct trace_event_data_offsets_io_uring_complete {}; struct trace_event_data_offsets_io_uring_submit_sqe {}; struct trace_event_data_offsets_io_uring_poll_arm {}; struct trace_event_data_offsets_io_uring_poll_wake {}; struct trace_event_data_offsets_io_uring_task_add {}; struct trace_event_data_offsets_io_uring_task_run {}; typedef void (*btf_trace_io_uring_create)(void *, int, void *, u32, u32, u32); typedef void (*btf_trace_io_uring_register)(void *, void *, unsigned int, unsigned int, unsigned int, bool, long int); typedef void (*btf_trace_io_uring_file_get)(void *, void *, int); typedef void (*btf_trace_io_uring_queue_async_work)(void *, void *, int, void *, struct io_wq_work *, unsigned int); typedef void (*btf_trace_io_uring_defer)(void *, void *, void *, long long unsigned int); typedef void (*btf_trace_io_uring_link)(void *, void *, void *, void *); typedef void (*btf_trace_io_uring_cqring_wait)(void *, void *, int); typedef void (*btf_trace_io_uring_fail_link)(void *, void *, void *); typedef void (*btf_trace_io_uring_complete)(void *, void *, u64, long int, unsigned int); typedef void (*btf_trace_io_uring_submit_sqe)(void *, void *, void *, u8, u64, u32, bool, bool); typedef void (*btf_trace_io_uring_poll_arm)(void *, void *, void *, u8, u64, int, int); typedef void (*btf_trace_io_uring_poll_wake)(void *, void *, u8, u64, int); typedef void (*btf_trace_io_uring_task_add)(void *, void *, u8, u64, int); typedef void (*btf_trace_io_uring_task_run)(void *, void *, void *, u8, u64); struct io_uring_sqe { __u8 opcode; __u8 flags; __u16 ioprio; __s32 fd; union { __u64 off; __u64 addr2; }; union { __u64 addr; __u64 splice_off_in; }; __u32 len; union { __kernel_rwf_t rw_flags; __u32 fsync_flags; __u16 poll_events; __u32 poll32_events; __u32 sync_range_flags; __u32 msg_flags; __u32 timeout_flags; __u32 accept_flags; __u32 cancel_flags; __u32 open_flags; __u32 statx_flags; __u32 fadvise_advice; __u32 splice_flags; __u32 rename_flags; __u32 unlink_flags; }; __u64 user_data; union { __u16 buf_index; __u16 buf_group; }; __u16 personality; __s32 splice_fd_in; __u64 __pad2[2]; }; enum { IOSQE_FIXED_FILE_BIT = 0, IOSQE_IO_DRAIN_BIT = 1, IOSQE_IO_LINK_BIT = 2, IOSQE_IO_HARDLINK_BIT = 3, IOSQE_ASYNC_BIT = 4, IOSQE_BUFFER_SELECT_BIT = 5, }; enum { IORING_OP_NOP = 0, IORING_OP_READV = 1, IORING_OP_WRITEV = 2, IORING_OP_FSYNC = 3, IORING_OP_READ_FIXED = 4, IORING_OP_WRITE_FIXED = 5, IORING_OP_POLL_ADD = 6, IORING_OP_POLL_REMOVE = 7, IORING_OP_SYNC_FILE_RANGE = 8, IORING_OP_SENDMSG = 9, IORING_OP_RECVMSG = 10, IORING_OP_TIMEOUT = 11, IORING_OP_TIMEOUT_REMOVE = 12, IORING_OP_ACCEPT = 13, IORING_OP_ASYNC_CANCEL = 14, IORING_OP_LINK_TIMEOUT = 15, IORING_OP_CONNECT = 16, IORING_OP_FALLOCATE = 17, IORING_OP_OPENAT = 18, IORING_OP_CLOSE = 19, IORING_OP_FILES_UPDATE = 20, IORING_OP_STATX = 21, IORING_OP_READ = 22, IORING_OP_WRITE = 23, IORING_OP_FADVISE = 24, IORING_OP_MADVISE = 25, IORING_OP_SEND = 26, IORING_OP_RECV = 27, IORING_OP_OPENAT2 = 28, IORING_OP_EPOLL_CTL = 29, IORING_OP_SPLICE = 30, IORING_OP_PROVIDE_BUFFERS = 31, IORING_OP_REMOVE_BUFFERS = 32, IORING_OP_TEE = 33, IORING_OP_SHUTDOWN = 34, IORING_OP_RENAMEAT = 35, IORING_OP_UNLINKAT = 36, IORING_OP_LAST = 37, }; struct io_uring_cqe { __u64 user_data; __s32 res; __u32 flags; }; enum { IORING_CQE_BUFFER_SHIFT = 16, }; struct io_sqring_offsets { __u32 head; __u32 tail; __u32 ring_mask; __u32 ring_entries; __u32 flags; __u32 dropped; __u32 array; __u32 resv1; __u64 resv2; }; struct io_cqring_offsets { __u32 head; __u32 tail; __u32 ring_mask; __u32 ring_entries; __u32 overflow; __u32 cqes; __u32 flags; __u32 resv1; __u64 resv2; }; struct io_uring_params { __u32 sq_entries; __u32 cq_entries; __u32 flags; __u32 sq_thread_cpu; __u32 sq_thread_idle; __u32 features; __u32 wq_fd; __u32 resv[3]; struct io_sqring_offsets sq_off; struct io_cqring_offsets cq_off; }; enum { IORING_REGISTER_BUFFERS = 0, IORING_UNREGISTER_BUFFERS = 1, IORING_REGISTER_FILES = 2, IORING_UNREGISTER_FILES = 3, IORING_REGISTER_EVENTFD = 4, IORING_UNREGISTER_EVENTFD = 5, IORING_REGISTER_FILES_UPDATE = 6, IORING_REGISTER_EVENTFD_ASYNC = 7, IORING_REGISTER_PROBE = 8, IORING_REGISTER_PERSONALITY = 9, IORING_UNREGISTER_PERSONALITY = 10, IORING_REGISTER_RESTRICTIONS = 11, IORING_REGISTER_ENABLE_RINGS = 12, IORING_REGISTER_FILES2 = 13, IORING_REGISTER_FILES_UPDATE2 = 14, IORING_REGISTER_BUFFERS2 = 15, IORING_REGISTER_BUFFERS_UPDATE = 16, IORING_REGISTER_IOWQ_AFF = 17, IORING_UNREGISTER_IOWQ_AFF = 18, IORING_REGISTER_LAST = 19, }; struct io_uring_rsrc_register { __u32 nr; __u32 resv; __u64 resv2; __u64 data; __u64 tags; }; struct io_uring_rsrc_update2 { __u32 offset; __u32 resv; __u64 data; __u64 tags; __u32 nr; __u32 resv2; }; struct io_uring_probe_op { __u8 op; __u8 resv; __u16 flags; __u32 resv2; }; struct io_uring_probe { __u8 last_op; __u8 ops_len; __u16 resv; __u32 resv2[3]; struct io_uring_probe_op ops[0]; }; struct io_uring_restriction { __u16 opcode; union { __u8 register_op; __u8 sqe_op; __u8 sqe_flags; }; __u8 resv; __u32 resv2[3]; }; enum { IORING_RESTRICTION_REGISTER_OP = 0, IORING_RESTRICTION_SQE_OP = 1, IORING_RESTRICTION_SQE_FLAGS_ALLOWED = 2, IORING_RESTRICTION_SQE_FLAGS_REQUIRED = 3, IORING_RESTRICTION_LAST = 4, }; struct io_uring_getevents_arg { __u64 sigmask; __u32 sigmask_sz; __u32 pad; __u64 ts; }; enum { IO_WQ_WORK_CANCEL = 1, IO_WQ_WORK_HASHED = 2, IO_WQ_WORK_UNBOUND = 4, IO_WQ_WORK_CONCURRENT = 16, IO_WQ_HASH_SHIFT = 24, }; enum io_wq_cancel { IO_WQ_CANCEL_OK = 0, IO_WQ_CANCEL_RUNNING = 1, IO_WQ_CANCEL_NOTFOUND = 2, }; typedef struct io_wq_work *free_work_fn(struct io_wq_work *); typedef void io_wq_work_fn(struct io_wq_work *); struct io_wq_hash { refcount_t refs; long unsigned int map; struct wait_queue_head wait; }; struct io_wq_data { struct io_wq_hash *hash; struct task_struct *task; io_wq_work_fn *do_work; free_work_fn *free_work; }; typedef bool work_cancel_fn(struct io_wq_work *, void *); struct io_uring { u32 head; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; u32 tail; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct io_rings { struct io_uring sq; struct io_uring cq; u32 sq_ring_mask; u32 cq_ring_mask; u32 sq_ring_entries; u32 cq_ring_entries; u32 sq_dropped; u32 sq_flags; u32 cq_flags; u32 cq_overflow; long: 64; long: 64; long: 64; long: 64; struct io_uring_cqe cqes[0]; }; enum io_uring_cmd_flags { IO_URING_F_NONBLOCK = 1, IO_URING_F_COMPLETE_DEFER = 2, }; struct io_mapped_ubuf { u64 ubuf; u64 ubuf_end; unsigned int nr_bvecs; long unsigned int acct_pages; struct bio_vec bvec[0]; }; struct io_overflow_cqe { struct io_uring_cqe cqe; struct list_head list; }; struct io_fixed_file { long unsigned int file_ptr; }; struct io_rsrc_put { struct list_head list; u64 tag; union { void *rsrc; struct file *file; struct io_mapped_ubuf *buf; }; }; struct io_file_table { struct io_fixed_file **files; }; struct io_rsrc_data; struct io_rsrc_node { struct percpu_ref refs; struct list_head node; struct list_head rsrc_list; struct io_rsrc_data *rsrc_data; struct llist_node llist; bool done; }; typedef void rsrc_put_fn(struct io_ring_ctx *, struct io_rsrc_put *); struct io_rsrc_data { struct io_ring_ctx *ctx; u64 **tags; unsigned int nr; rsrc_put_fn *do_put; atomic_t refs; struct completion done; bool quiesce; }; struct io_kiocb; struct io_submit_link { struct io_kiocb *head; struct io_kiocb *last; }; struct io_comp_state { struct io_kiocb *reqs[32]; unsigned int nr; struct list_head free_list; }; struct io_submit_state { struct blk_plug plug; struct io_submit_link link; void *reqs[32]; unsigned int free_reqs; bool plug_started; struct io_comp_state comp; struct file *file; unsigned int fd; unsigned int file_refs; unsigned int ios_left; }; struct io_restriction { long unsigned int register_op[1]; long unsigned int sqe_op[1]; u8 sqe_flags_allowed; u8 sqe_flags_required; bool registered; }; struct io_sq_data; struct io_ring_ctx { struct { struct percpu_ref refs; struct io_rings *rings; unsigned int flags; unsigned int compat: 1; unsigned int drain_next: 1; unsigned int eventfd_async: 1; unsigned int restricted: 1; unsigned int off_timeout_used: 1; unsigned int drain_active: 1; long: 26; long: 64; long: 64; long: 64; long: 64; }; struct { struct mutex uring_lock; u32 *sq_array; struct io_uring_sqe *sq_sqes; unsigned int cached_sq_head; unsigned int sq_entries; struct list_head defer_list; struct io_rsrc_node *rsrc_node; struct io_file_table file_table; unsigned int nr_user_files; unsigned int nr_user_bufs; struct io_mapped_ubuf **user_bufs; struct io_submit_state submit_state; struct list_head timeout_list; struct list_head cq_overflow_list; struct xarray io_buffers; struct xarray personalities; u32 pers_next; unsigned int sq_thread_idle; long: 64; long: 64; long: 64; long: 64; }; struct list_head locked_free_list; unsigned int locked_free_nr; const struct cred *sq_creds; struct io_sq_data *sq_data; struct wait_queue_head sqo_sq_wait; struct list_head sqd_list; long unsigned int check_cq_overflow; long: 64; long: 64; long: 64; long: 64; long: 64; struct { unsigned int cached_cq_tail; unsigned int cq_entries; struct eventfd_ctx *cq_ev_fd; struct wait_queue_head poll_wait; struct wait_queue_head cq_wait; unsigned int cq_extra; atomic_t cq_timeouts; struct fasync_struct *cq_fasync; unsigned int cq_last_tm_flush; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct { spinlock_t completion_lock; struct list_head iopoll_list; struct hlist_head *cancel_hash; unsigned int cancel_hash_bits; bool poll_multi_queue; long: 24; long: 64; long: 64; long: 64; }; struct io_restriction restrictions; struct { struct io_rsrc_node *rsrc_backup_node; struct io_mapped_ubuf *dummy_ubuf; struct io_rsrc_data *file_data; struct io_rsrc_data *buf_data; struct delayed_work rsrc_put_work; struct llist_head rsrc_put_llist; struct list_head rsrc_ref_list; spinlock_t rsrc_ref_lock; }; struct { struct socket *ring_sock; struct io_wq_hash *hash_map; struct user_struct *user; struct mm_struct *mm_account; struct llist_head fallback_llist; struct delayed_work fallback_work; struct work_struct exit_work; struct list_head tctx_list; struct completion ref_comp; }; }; struct io_buffer { struct list_head list; __u64 addr; __u32 len; __u16 bid; }; enum { IO_SQ_THREAD_SHOULD_STOP = 0, IO_SQ_THREAD_SHOULD_PARK = 1, }; struct io_sq_data { refcount_t refs; atomic_t park_pending; struct mutex lock; struct list_head ctx_list; struct task_struct *thread; struct wait_queue_head wait; unsigned int sq_thread_idle; int sq_cpu; pid_t task_pid; pid_t task_tgid; long unsigned int state; struct completion exited; }; struct io_rw { struct kiocb kiocb; u64 addr; u64 len; }; struct io_poll_iocb { struct file *file; struct wait_queue_head *head; __poll_t events; bool done; bool canceled; struct wait_queue_entry wait; }; struct io_poll_update { struct file *file; u64 old_user_data; u64 new_user_data; __poll_t events; bool update_events; bool update_user_data; }; struct io_accept { struct file *file; struct sockaddr *addr; int *addr_len; int flags; long unsigned int nofile; }; struct io_sync { struct file *file; loff_t len; loff_t off; int flags; int mode; }; struct io_cancel { struct file *file; u64 addr; }; struct io_timeout { struct file *file; u32 off; u32 target_seq; struct list_head list; struct io_kiocb *head; }; struct io_timeout_rem { struct file *file; u64 addr; struct timespec64 ts; u32 flags; }; struct io_connect { struct file *file; struct sockaddr *addr; int addr_len; }; struct io_sr_msg { struct file *file; union { struct compat_msghdr *umsg_compat; struct user_msghdr *umsg; void *buf; }; int msg_flags; int bgid; size_t len; struct io_buffer *kbuf; }; struct io_open { struct file *file; int dfd; struct filename *filename; struct open_how how; long unsigned int nofile; }; struct io_close { struct file *file; int fd; }; struct io_rsrc_update { struct file *file; u64 arg; u32 nr_args; u32 offset; }; struct io_fadvise { struct file *file; u64 offset; u32 len; u32 advice; }; struct io_madvise { struct file *file; u64 addr; u32 len; u32 advice; }; struct io_epoll { struct file *file; int epfd; int op; int fd; struct epoll_event event; } __attribute__((packed)); struct io_splice { struct file *file_out; struct file *file_in; loff_t off_out; loff_t off_in; u64 len; unsigned int flags; }; struct io_provide_buf { struct file *file; __u64 addr; __u32 len; __u32 bgid; __u16 nbufs; __u16 bid; }; struct io_statx { struct file *file; int dfd; unsigned int mask; unsigned int flags; const char *filename; struct statx *buffer; }; struct io_shutdown { struct file *file; int how; }; struct io_rename { struct file *file; int old_dfd; int new_dfd; struct filename *oldpath; struct filename *newpath; int flags; }; struct io_unlink { struct file *file; int dfd; int flags; struct filename *filename; }; struct io_completion { struct file *file; struct list_head list; u32 cflags; }; typedef void (*io_req_tw_func_t)(struct io_kiocb *); struct io_task_work { union { struct io_wq_work_node node; struct llist_node fallback_node; }; io_req_tw_func_t func; }; struct async_poll; struct io_kiocb { union { struct file *file; struct io_rw rw; struct io_poll_iocb poll; struct io_poll_update poll_update; struct io_accept accept; struct io_sync sync; struct io_cancel cancel; struct io_timeout timeout; struct io_timeout_rem timeout_rem; struct io_connect connect; struct io_sr_msg sr_msg; struct io_open open; struct io_close close; struct io_rsrc_update rsrc_update; struct io_fadvise fadvise; struct io_madvise madvise; struct io_epoll epoll; struct io_splice splice; struct io_provide_buf pbuf; struct io_statx statx; struct io_shutdown shutdown; struct io_rename rename; struct io_unlink unlink; struct io_completion compl; }; void *async_data; u8 opcode; u8 iopoll_completed; u16 buf_index; u32 result; struct io_ring_ctx *ctx; unsigned int flags; atomic_t refs; struct task_struct *task; u64 user_data; struct io_kiocb *link; struct percpu_ref *fixed_rsrc_refs; struct list_head inflight_entry; struct io_task_work io_task_work; struct hlist_node hash_node; struct async_poll *apoll; struct io_wq_work work; const struct cred *creds; struct io_mapped_ubuf *imu; }; struct io_timeout_data { struct io_kiocb *req; struct hrtimer timer; struct timespec64 ts; enum hrtimer_mode mode; }; struct io_async_connect { struct __kernel_sockaddr_storage address; }; struct io_async_msghdr { struct iovec fast_iov[8]; struct iovec *free_iov; struct sockaddr *uaddr; struct msghdr msg; struct __kernel_sockaddr_storage addr; }; struct io_async_rw { struct iovec fast_iov[8]; const struct iovec *free_iovec; struct iov_iter iter; size_t bytes_done; struct wait_page_queue wpq; }; enum { REQ_F_FIXED_FILE_BIT = 0, REQ_F_IO_DRAIN_BIT = 1, REQ_F_LINK_BIT = 2, REQ_F_HARDLINK_BIT = 3, REQ_F_FORCE_ASYNC_BIT = 4, REQ_F_BUFFER_SELECT_BIT = 5, REQ_F_FAIL_BIT = 8, REQ_F_INFLIGHT_BIT = 9, REQ_F_CUR_POS_BIT = 10, REQ_F_NOWAIT_BIT = 11, REQ_F_LINK_TIMEOUT_BIT = 12, REQ_F_NEED_CLEANUP_BIT = 13, REQ_F_POLLED_BIT = 14, REQ_F_BUFFER_SELECTED_BIT = 15, REQ_F_LTIMEOUT_ACTIVE_BIT = 16, REQ_F_COMPLETE_INLINE_BIT = 17, REQ_F_REISSUE_BIT = 18, REQ_F_DONT_REISSUE_BIT = 19, REQ_F_CREDS_BIT = 20, REQ_F_ASYNC_READ_BIT = 21, REQ_F_ASYNC_WRITE_BIT = 22, REQ_F_ISREG_BIT = 23, __REQ_F_LAST_BIT = 24, }; enum { REQ_F_FIXED_FILE = 1, REQ_F_IO_DRAIN = 2, REQ_F_LINK = 4, REQ_F_HARDLINK = 8, REQ_F_FORCE_ASYNC = 16, REQ_F_BUFFER_SELECT = 32, REQ_F_FAIL = 256, REQ_F_INFLIGHT = 512, REQ_F_CUR_POS = 1024, REQ_F_NOWAIT = 2048, REQ_F_LINK_TIMEOUT = 4096, REQ_F_NEED_CLEANUP = 8192, REQ_F_POLLED = 16384, REQ_F_BUFFER_SELECTED = 32768, REQ_F_LTIMEOUT_ACTIVE = 65536, REQ_F_COMPLETE_INLINE = 131072, REQ_F_REISSUE = 262144, REQ_F_DONT_REISSUE = 524288, REQ_F_ASYNC_READ = 2097152, REQ_F_ASYNC_WRITE = 4194304, REQ_F_ISREG = 8388608, REQ_F_CREDS = 1048576, }; struct async_poll { struct io_poll_iocb poll; struct io_poll_iocb *double_poll; }; enum { IORING_RSRC_FILE = 0, IORING_RSRC_BUFFER = 1, }; struct io_tctx_node { struct list_head ctx_node; struct task_struct *task; struct io_ring_ctx *ctx; }; struct io_defer_entry { struct list_head list; struct io_kiocb *req; u32 seq; }; struct io_op_def { unsigned int needs_file: 1; unsigned int hash_reg_file: 1; unsigned int unbound_nonreg_file: 1; unsigned int not_supported: 1; unsigned int pollin: 1; unsigned int pollout: 1; unsigned int buffer_select: 1; unsigned int needs_async_setup: 1; unsigned int plug: 1; short unsigned int async_size; }; struct req_batch { struct task_struct *task; int task_refs; int ctx_refs; }; struct io_poll_table { struct poll_table_struct pt; struct io_kiocb *req; int nr_entries; int error; }; enum { IO_APOLL_OK = 0, IO_APOLL_ABORTED = 1, IO_APOLL_READY = 2, }; struct io_cancel_data { struct io_ring_ctx *ctx; u64 user_data; }; struct io_wait_queue { struct wait_queue_entry wq; struct io_ring_ctx *ctx; unsigned int to_wait; unsigned int nr_timeouts; }; struct io_tctx_exit { struct callback_head task_work; struct completion completion; struct io_ring_ctx *ctx; }; struct io_task_cancel { struct task_struct *task; bool all; }; struct creds; enum { IO_WORKER_F_UP = 1, IO_WORKER_F_RUNNING = 2, IO_WORKER_F_FREE = 4, IO_WORKER_F_FIXED = 8, IO_WORKER_F_BOUND = 16, }; enum { IO_WQ_BIT_EXIT = 0, }; enum { IO_WQE_FLAG_STALLED = 1, }; struct io_wqe; struct io_worker { refcount_t ref; unsigned int flags; struct hlist_nulls_node nulls_node; struct list_head all_list; struct task_struct *task; struct io_wqe *wqe; struct io_wq_work *cur_work; spinlock_t lock; struct completion ref_done; struct callback_head rcu; }; struct io_wqe_acct { unsigned int nr_workers; unsigned int max_workers; int index; atomic_t nr_running; }; struct io_wq___2; struct io_wqe { struct { raw_spinlock_t lock; struct io_wq_work_list work_list; unsigned int flags; long: 32; long: 64; long: 64; long: 64; long: 64; }; int node; struct io_wqe_acct acct[2]; struct hlist_nulls_head free_list; struct list_head all_list; struct wait_queue_entry wait; struct io_wq___2 *wq; struct io_wq_work *hash_tail[64]; cpumask_var_t cpu_mask; long: 64; long: 64; long: 64; long: 64; long: 64; }; enum { IO_WQ_ACCT_BOUND = 0, IO_WQ_ACCT_UNBOUND = 1, }; struct io_wq___2 { long unsigned int state; free_work_fn *free_work; io_wq_work_fn *do_work; struct io_wq_hash *hash; atomic_t worker_refs; struct completion worker_done; struct hlist_node cpuhp_node; struct task_struct *task; struct io_wqe *wqes[0]; }; struct io_cb_cancel_data { work_cancel_fn *fn; void *data; int nr_running; int nr_pending; bool cancel_all; }; struct create_worker_data { struct callback_head work; struct io_wqe *wqe; int index; }; struct online_data { unsigned int cpu; bool online; }; struct iomap_ops { int (*iomap_begin)(struct inode *, loff_t, loff_t, unsigned int, struct iomap___2 *, struct iomap___2 *); int (*iomap_end)(struct inode *, loff_t, loff_t, ssize_t, unsigned int, struct iomap___2 *); }; typedef loff_t (*iomap_actor_t)(struct inode *, loff_t, loff_t, void *, struct iomap___2 *, struct iomap___2 *); struct trace_event_raw_dax_pmd_fault_class { struct trace_entry ent; long unsigned int ino; long unsigned int vm_start; long unsigned int vm_end; long unsigned int vm_flags; long unsigned int address; long unsigned int pgoff; long unsigned int max_pgoff; dev_t dev; unsigned int flags; int result; char __data[0]; }; struct trace_event_raw_dax_pmd_load_hole_class { struct trace_entry ent; long unsigned int ino; long unsigned int vm_flags; long unsigned int address; struct page *zero_page; void *radix_entry; dev_t dev; char __data[0]; }; struct trace_event_raw_dax_pmd_insert_mapping_class { struct trace_entry ent; long unsigned int ino; long unsigned int vm_flags; long unsigned int address; long int length; u64 pfn_val; void *radix_entry; dev_t dev; int write; char __data[0]; }; struct trace_event_raw_dax_pte_fault_class { struct trace_entry ent; long unsigned int ino; long unsigned int vm_flags; long unsigned int address; long unsigned int pgoff; dev_t dev; unsigned int flags; int result; char __data[0]; }; struct trace_event_raw_dax_insert_mapping { struct trace_entry ent; long unsigned int ino; long unsigned int vm_flags; long unsigned int address; void *radix_entry; dev_t dev; int write; char __data[0]; }; struct trace_event_raw_dax_writeback_range_class { struct trace_entry ent; long unsigned int ino; long unsigned int start_index; long unsigned int end_index; dev_t dev; char __data[0]; }; struct trace_event_raw_dax_writeback_one { struct trace_entry ent; long unsigned int ino; long unsigned int pgoff; long unsigned int pglen; dev_t dev; char __data[0]; }; struct trace_event_data_offsets_dax_pmd_fault_class {}; struct trace_event_data_offsets_dax_pmd_load_hole_class {}; struct trace_event_data_offsets_dax_pmd_insert_mapping_class {}; struct trace_event_data_offsets_dax_pte_fault_class {}; struct trace_event_data_offsets_dax_insert_mapping {}; struct trace_event_data_offsets_dax_writeback_range_class {}; struct trace_event_data_offsets_dax_writeback_one {}; typedef void (*btf_trace_dax_pmd_fault)(void *, struct inode *, struct vm_fault *, long unsigned int, int); typedef void (*btf_trace_dax_pmd_fault_done)(void *, struct inode *, struct vm_fault *, long unsigned int, int); typedef void (*btf_trace_dax_pmd_load_hole)(void *, struct inode *, struct vm_fault *, struct page *, void *); typedef void (*btf_trace_dax_pmd_load_hole_fallback)(void *, struct inode *, struct vm_fault *, struct page *, void *); typedef void (*btf_trace_dax_pmd_insert_mapping)(void *, struct inode *, struct vm_fault *, long int, pfn_t, void *); typedef void (*btf_trace_dax_pte_fault)(void *, struct inode *, struct vm_fault *, int); typedef void (*btf_trace_dax_pte_fault_done)(void *, struct inode *, struct vm_fault *, int); typedef void (*btf_trace_dax_load_hole)(void *, struct inode *, struct vm_fault *, int); typedef void (*btf_trace_dax_insert_pfn_mkwrite_no_entry)(void *, struct inode *, struct vm_fault *, int); typedef void (*btf_trace_dax_insert_pfn_mkwrite)(void *, struct inode *, struct vm_fault *, int); typedef void (*btf_trace_dax_insert_mapping)(void *, struct inode *, struct vm_fault *, void *); typedef void (*btf_trace_dax_writeback_range)(void *, struct inode *, long unsigned int, long unsigned int); typedef void (*btf_trace_dax_writeback_range_done)(void *, struct inode *, long unsigned int, long unsigned int); typedef void (*btf_trace_dax_writeback_one)(void *, struct inode *, long unsigned int, long unsigned int); struct exceptional_entry_key { struct xarray *xa; long unsigned int entry_start; }; struct wait_exceptional_entry_queue { wait_queue_entry_t wait; struct exceptional_entry_key key; }; enum dax_wake_mode { WAKE_ALL = 0, WAKE_NEXT = 1, }; struct crypto_skcipher; struct fscrypt_blk_crypto_key; struct fscrypt_prepared_key { struct crypto_skcipher *tfm; struct fscrypt_blk_crypto_key *blk_key; }; struct fscrypt_mode; struct fscrypt_direct_key; struct fscrypt_info { struct fscrypt_prepared_key ci_enc_key; bool ci_owns_key; bool ci_inlinecrypt; struct fscrypt_mode *ci_mode; struct inode *ci_inode; struct key *ci_master_key; struct list_head ci_master_key_link; struct fscrypt_direct_key *ci_direct_key; siphash_key_t ci_dirhash_key; bool ci_dirhash_key_initialized; union fscrypt_policy ci_policy; u8 ci_nonce[16]; u32 ci_hashed_ino; }; struct skcipher_request { unsigned int cryptlen; u8 *iv; struct scatterlist *src; struct scatterlist *dst; struct crypto_async_request base; void *__ctx[0]; }; struct crypto_skcipher { unsigned int reqsize; struct crypto_tfm base; }; struct fscrypt_mode { const char *friendly_name; const char *cipher_str; int keysize; int ivsize; int logged_impl_name; enum blk_crypto_mode_num blk_crypto_mode; }; typedef enum { FS_DECRYPT = 0, FS_ENCRYPT = 1, } fscrypt_direction_t; union fscrypt_iv { struct { __le64 lblk_num; u8 nonce[16]; }; u8 raw[32]; __le64 dun[4]; }; struct fscrypt_str { unsigned char *name; u32 len; }; struct fscrypt_name { const struct qstr *usr_fname; struct fscrypt_str disk_name; u32 hash; u32 minor_hash; struct fscrypt_str crypto_buf; bool is_nokey_name; }; struct fscrypt_nokey_name { u32 dirhash[2]; u8 bytes[149]; u8 sha256[32]; }; struct fscrypt_hkdf { struct crypto_shash *hmac_tfm; }; struct fscrypt_key_specifier { __u32 type; __u32 __reserved; union { __u8 __reserved[32]; __u8 descriptor[8]; __u8 identifier[16]; } u; }; struct fscrypt_symlink_data { __le16 len; char encrypted_path[1]; } __attribute__((packed)); struct fscrypt_master_key_secret { struct fscrypt_hkdf hkdf; u32 size; u8 raw[64]; }; struct fscrypt_master_key { struct fscrypt_master_key_secret mk_secret; struct fscrypt_key_specifier mk_spec; struct key *mk_users; refcount_t mk_refcount; struct list_head mk_decrypted_inodes; spinlock_t mk_decrypted_inodes_lock; struct fscrypt_prepared_key mk_direct_keys[10]; struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[10]; struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[10]; siphash_key_t mk_ino_hash_key; bool mk_ino_hash_key_initialized; }; enum key_state { KEY_IS_UNINSTANTIATED = 0, KEY_IS_POSITIVE = 1, }; struct fscrypt_provisioning_key_payload { __u32 type; __u32 __reserved; __u8 raw[0]; }; struct fscrypt_add_key_arg { struct fscrypt_key_specifier key_spec; __u32 raw_size; __u32 key_id; __u32 __reserved[8]; __u8 raw[0]; }; struct fscrypt_remove_key_arg { struct fscrypt_key_specifier key_spec; __u32 removal_status_flags; __u32 __reserved[5]; }; struct fscrypt_get_key_status_arg { struct fscrypt_key_specifier key_spec; __u32 __reserved[6]; __u32 status; __u32 status_flags; __u32 user_count; __u32 __out_reserved[13]; }; struct skcipher_alg { int (*setkey)(struct crypto_skcipher *, const u8 *, unsigned int); int (*encrypt)(struct skcipher_request *); int (*decrypt)(struct skcipher_request *); int (*init)(struct crypto_skcipher *); void (*exit)(struct crypto_skcipher *); unsigned int min_keysize; unsigned int max_keysize; unsigned int ivsize; unsigned int chunksize; unsigned int walksize; struct crypto_alg base; }; struct fscrypt_context_v1 { u8 version; u8 contents_encryption_mode; u8 filenames_encryption_mode; u8 flags; u8 master_key_descriptor[8]; u8 nonce[16]; }; struct fscrypt_context_v2 { u8 version; u8 contents_encryption_mode; u8 filenames_encryption_mode; u8 flags; u8 __reserved[4]; u8 master_key_identifier[16]; u8 nonce[16]; }; union fscrypt_context { u8 version; struct fscrypt_context_v1 v1; struct fscrypt_context_v2 v2; }; struct crypto_template; struct crypto_spawn; struct crypto_instance { struct crypto_alg alg; struct crypto_template *tmpl; union { struct hlist_node list; struct crypto_spawn *spawns; }; void *__ctx[0]; }; struct crypto_spawn { struct list_head list; struct crypto_alg *alg; union { struct crypto_instance *inst; struct crypto_spawn *next; }; const struct crypto_type *frontend; u32 mask; bool dead; bool registered; }; struct rtattr; struct crypto_template { struct list_head list; struct hlist_head instances; struct module *module; int (*create)(struct crypto_template *, struct rtattr **); char name[128]; }; struct user_key_payload { struct callback_head rcu; short unsigned int datalen; long: 48; char data[0]; }; struct fscrypt_key { __u32 mode; __u8 raw[64]; __u32 size; }; struct fscrypt_direct_key { struct hlist_node dk_node; refcount_t dk_refcount; const struct fscrypt_mode *dk_mode; struct fscrypt_prepared_key dk_key; u8 dk_descriptor[8]; u8 dk_raw[64]; }; struct fscrypt_get_policy_ex_arg { __u64 policy_size; union { __u8 version; struct fscrypt_policy_v1 v1; struct fscrypt_policy_v2 v2; } policy; }; struct fscrypt_dummy_policy { const union fscrypt_policy *policy; }; struct fscrypt_blk_crypto_key { struct blk_crypto_key base; int num_devs; struct request_queue *devs[0]; }; struct fsverity_hash_alg; struct merkle_tree_params { struct fsverity_hash_alg *hash_alg; const u8 *hashstate; unsigned int digest_size; unsigned int block_size; unsigned int hashes_per_block; unsigned int log_blocksize; unsigned int log_arity; unsigned int num_levels; u64 tree_size; long unsigned int level0_blocks; u64 level_start[8]; }; struct fsverity_info { struct merkle_tree_params tree_params; u8 root_hash[64]; u8 file_digest[64]; const struct inode *inode; }; struct fsverity_enable_arg { __u32 version; __u32 hash_algorithm; __u32 block_size; __u32 salt_size; __u64 salt_ptr; __u32 sig_size; __u32 __reserved1; __u64 sig_ptr; __u64 __reserved2[11]; }; struct fsverity_descriptor { __u8 version; __u8 hash_algorithm; __u8 log_blocksize; __u8 salt_size; __le32 sig_size; __le64 data_size; __u8 root_hash[64]; __u8 salt[32]; __u8 __reserved[144]; __u8 signature[0]; }; struct crypto_ahash; struct fsverity_hash_alg { struct crypto_ahash *tfm; const char *name; unsigned int digest_size; unsigned int block_size; mempool_t req_pool; }; struct ahash_request; struct crypto_ahash { int (*init)(struct ahash_request *); int (*update)(struct ahash_request *); int (*final)(struct ahash_request *); int (*finup)(struct ahash_request *); int (*digest)(struct ahash_request *); int (*export)(struct ahash_request *, void *); int (*import)(struct ahash_request *, const void *); int (*setkey)(struct crypto_ahash *, const u8 *, unsigned int); unsigned int reqsize; struct crypto_tfm base; }; struct ahash_request { struct crypto_async_request base; unsigned int nbytes; struct scatterlist *src; u8 *result; void *priv; void *__ctx[0]; }; struct hash_alg_common { unsigned int digestsize; unsigned int statesize; struct crypto_alg base; }; struct fsverity_digest { __u16 digest_algorithm; __u16 digest_size; __u8 digest[0]; }; struct fsverity_read_metadata_arg { __u64 metadata_type; __u64 offset; __u64 length; __u64 buf_ptr; __u64 __reserved; }; struct fsverity_formatted_digest { char magic[8]; __le16 digest_algorithm; __le16 digest_size; __u8 digest[0]; }; struct flock64 { short int l_type; short int l_whence; __kernel_loff_t l_start; __kernel_loff_t l_len; __kernel_pid_t l_pid; }; struct trace_event_raw_locks_get_lock_context { struct trace_entry ent; long unsigned int i_ino; dev_t s_dev; unsigned char type; struct file_lock_context *ctx; char __data[0]; }; struct trace_event_raw_filelock_lock { struct trace_entry ent; struct file_lock *fl; long unsigned int i_ino; dev_t s_dev; struct file_lock *fl_blocker; fl_owner_t fl_owner; unsigned int fl_pid; unsigned int fl_flags; unsigned char fl_type; loff_t fl_start; loff_t fl_end; int ret; char __data[0]; }; struct trace_event_raw_filelock_lease { struct trace_entry ent; struct file_lock *fl; long unsigned int i_ino; dev_t s_dev; struct file_lock *fl_blocker; fl_owner_t fl_owner; unsigned int fl_flags; unsigned char fl_type; long unsigned int fl_break_time; long unsigned int fl_downgrade_time; char __data[0]; }; struct trace_event_raw_generic_add_lease { struct trace_entry ent; long unsigned int i_ino; int wcount; int rcount; int icount; dev_t s_dev; fl_owner_t fl_owner; unsigned int fl_flags; unsigned char fl_type; char __data[0]; }; struct trace_event_raw_leases_conflict { struct trace_entry ent; void *lease; void *breaker; unsigned int l_fl_flags; unsigned int b_fl_flags; unsigned char l_fl_type; unsigned char b_fl_type; bool conflict; char __data[0]; }; struct trace_event_data_offsets_locks_get_lock_context {}; struct trace_event_data_offsets_filelock_lock {}; struct trace_event_data_offsets_filelock_lease {}; struct trace_event_data_offsets_generic_add_lease {}; struct trace_event_data_offsets_leases_conflict {}; typedef void (*btf_trace_locks_get_lock_context)(void *, struct inode *, int, struct file_lock_context *); typedef void (*btf_trace_posix_lock_inode)(void *, struct inode *, struct file_lock *, int); typedef void (*btf_trace_fcntl_setlk)(void *, struct inode *, struct file_lock *, int); typedef void (*btf_trace_locks_remove_posix)(void *, struct inode *, struct file_lock *, int); typedef void (*btf_trace_flock_lock_inode)(void *, struct inode *, struct file_lock *, int); typedef void (*btf_trace_break_lease_noblock)(void *, struct inode *, struct file_lock *); typedef void (*btf_trace_break_lease_block)(void *, struct inode *, struct file_lock *); typedef void (*btf_trace_break_lease_unblock)(void *, struct inode *, struct file_lock *); typedef void (*btf_trace_generic_delete_lease)(void *, struct inode *, struct file_lock *); typedef void (*btf_trace_time_out_leases)(void *, struct inode *, struct file_lock *); typedef void (*btf_trace_generic_add_lease)(void *, struct inode *, struct file_lock *); typedef void (*btf_trace_leases_conflict)(void *, bool, struct file_lock *, struct file_lock *); struct file_lock_list_struct { spinlock_t lock; struct hlist_head hlist; }; struct locks_iterator { int li_cpu; loff_t li_pos; }; enum { VERBOSE_STATUS = 1, }; enum { Enabled = 0, Magic = 1, }; typedef struct { struct list_head list; long unsigned int flags; int offset; int size; char *magic; char *mask; const char *interpreter; char *name; struct dentry *dentry; struct file *interp_file; } Node; typedef unsigned int __kernel_uid_t; typedef unsigned int __kernel_gid_t; struct elf_prpsinfo { char pr_state; char pr_sname; char pr_zomb; char pr_nice; long unsigned int pr_flag; __kernel_uid_t pr_uid; __kernel_gid_t pr_gid; pid_t pr_pid; pid_t pr_ppid; pid_t pr_pgrp; pid_t pr_sid; char pr_fname[16]; char pr_psargs[80]; }; struct core_vma_metadata { long unsigned int start; long unsigned int end; long unsigned int flags; long unsigned int dump_size; }; struct arch_elf_state {}; struct memelfnote { const char *name; int type; unsigned int datasz; void *data; }; struct elf_thread_core_info { struct elf_thread_core_info *next; struct task_struct *task; struct elf_prstatus prstatus; struct memelfnote notes[0]; }; struct elf_note_info { struct elf_thread_core_info *thread; struct memelfnote psinfo; struct memelfnote signote; struct memelfnote auxv; struct memelfnote files; siginfo_t csigdata; size_t size; int thread_notes; }; struct user_regs_struct { long unsigned int r15; long unsigned int r14; long unsigned int r13; long unsigned int r12; long unsigned int bp; long unsigned int bx; long unsigned int r11; long unsigned int r10; long unsigned int r9; long unsigned int r8; long unsigned int ax; long unsigned int cx; long unsigned int dx; long unsigned int si; long unsigned int di; long unsigned int orig_ax; long unsigned int ip; long unsigned int cs; long unsigned int flags; long unsigned int sp; long unsigned int ss; long unsigned int fs_base; long unsigned int gs_base; long unsigned int ds; long unsigned int es; long unsigned int fs; long unsigned int gs; }; typedef __u32 Elf32_Addr; typedef __u16 Elf32_Half; typedef __u32 Elf32_Off; struct elf32_hdr { unsigned char e_ident[16]; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff; Elf32_Off e_shoff; Elf32_Word e_flags; Elf32_Half e_ehsize; Elf32_Half e_phentsize; Elf32_Half e_phnum; Elf32_Half e_shentsize; Elf32_Half e_shnum; Elf32_Half e_shstrndx; }; struct elf32_phdr { Elf32_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word p_flags; Elf32_Word p_align; }; struct elf32_shdr { Elf32_Word sh_name; Elf32_Word sh_type; Elf32_Word sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; Elf32_Word sh_size; Elf32_Word sh_link; Elf32_Word sh_info; Elf32_Word sh_addralign; Elf32_Word sh_entsize; }; struct user_regs_struct32 { __u32 ebx; __u32 ecx; __u32 edx; __u32 esi; __u32 edi; __u32 ebp; __u32 eax; short unsigned int ds; short unsigned int __ds; short unsigned int es; short unsigned int __es; short unsigned int fs; short unsigned int __fs; short unsigned int gs; short unsigned int __gs; __u32 orig_eax; __u32 eip; short unsigned int cs; short unsigned int __cs; __u32 eflags; __u32 esp; short unsigned int ss; short unsigned int __ss; }; struct compat_elf_siginfo { compat_int_t si_signo; compat_int_t si_code; compat_int_t si_errno; }; struct compat_elf_prstatus_common { struct compat_elf_siginfo pr_info; short int pr_cursig; compat_ulong_t pr_sigpend; compat_ulong_t pr_sighold; compat_pid_t pr_pid; compat_pid_t pr_ppid; compat_pid_t pr_pgrp; compat_pid_t pr_sid; struct old_timeval32 pr_utime; struct old_timeval32 pr_stime; struct old_timeval32 pr_cutime; struct old_timeval32 pr_cstime; }; struct compat_elf_prpsinfo { char pr_state; char pr_sname; char pr_zomb; char pr_nice; compat_ulong_t pr_flag; __compat_uid_t pr_uid; __compat_gid_t pr_gid; compat_pid_t pr_pid; compat_pid_t pr_ppid; compat_pid_t pr_pgrp; compat_pid_t pr_sid; char pr_fname[16]; char pr_psargs[80]; }; typedef struct user_regs_struct compat_elf_gregset_t; struct i386_elf_prstatus { struct compat_elf_prstatus_common common; struct user_regs_struct32 pr_reg; compat_int_t pr_fpvalid; }; struct compat_elf_prstatus { struct compat_elf_prstatus_common common; compat_elf_gregset_t pr_reg; compat_int_t pr_fpvalid; }; struct elf_thread_core_info___2 { struct elf_thread_core_info___2 *next; struct task_struct *task; struct compat_elf_prstatus prstatus; struct memelfnote notes[0]; }; struct elf_note_info___2 { struct elf_thread_core_info___2 *thread; struct memelfnote psinfo; struct memelfnote signote; struct memelfnote auxv; struct memelfnote files; compat_siginfo_t csigdata; size_t size; int thread_notes; }; struct posix_acl_xattr_entry { __le16 e_tag; __le16 e_perm; __le32 e_id; }; struct posix_acl_xattr_header { __le32 a_version; }; struct rpc_timer { struct list_head list; long unsigned int expires; struct delayed_work dwork; }; struct rpc_wait_queue { spinlock_t lock; struct list_head tasks[4]; unsigned char maxpriority; unsigned char priority; unsigned char nr; short unsigned int qlen; struct rpc_timer timer_list; const char *name; }; struct nfs_seqid_counter { ktime_t create_time; int owner_id; int flags; u32 counter; spinlock_t lock; struct list_head list; struct rpc_wait_queue wait; }; struct nfs4_stateid_struct { union { char data[16]; struct { __be32 seqid; char other[12]; }; }; enum { NFS4_INVALID_STATEID_TYPE = 0, NFS4_SPECIAL_STATEID_TYPE = 1, NFS4_OPEN_STATEID_TYPE = 2, NFS4_LOCK_STATEID_TYPE = 3, NFS4_DELEGATION_STATEID_TYPE = 4, NFS4_LAYOUT_STATEID_TYPE = 5, NFS4_PNFS_DS_STATEID_TYPE = 6, NFS4_REVOKED_STATEID_TYPE = 7, } type; }; typedef struct nfs4_stateid_struct nfs4_stateid; struct nfs4_state; struct nfs4_lock_state { struct list_head ls_locks; struct nfs4_state *ls_state; long unsigned int ls_flags; struct nfs_seqid_counter ls_seqid; nfs4_stateid ls_stateid; refcount_t ls_count; fl_owner_t ls_owner; }; struct xdr_netobj { unsigned int len; u8 *data; }; struct xdr_buf { struct kvec head[1]; struct kvec tail[1]; struct bio_vec *bvec; struct page **pages; unsigned int page_base; unsigned int page_len; unsigned int flags; unsigned int buflen; unsigned int len; }; struct rpc_rqst; struct xdr_stream { __be32 *p; struct xdr_buf *buf; __be32 *end; struct kvec *iov; struct kvec scratch; struct page **page_ptr; unsigned int nwords; struct rpc_rqst *rqst; }; struct rpc_xprt; struct rpc_task; struct rpc_cred; struct rpc_rqst { struct rpc_xprt *rq_xprt; struct xdr_buf rq_snd_buf; struct xdr_buf rq_rcv_buf; struct rpc_task *rq_task; struct rpc_cred *rq_cred; __be32 rq_xid; int rq_cong; u32 rq_seqno; int rq_enc_pages_num; struct page **rq_enc_pages; void (*rq_release_snd_buf)(struct rpc_rqst *); union { struct list_head rq_list; struct rb_node rq_recv; }; struct list_head rq_xmit; struct list_head rq_xmit2; void *rq_buffer; size_t rq_callsize; void *rq_rbuffer; size_t rq_rcvsize; size_t rq_xmit_bytes_sent; size_t rq_reply_bytes_recvd; struct xdr_buf rq_private_buf; long unsigned int rq_majortimeo; long unsigned int rq_minortimeo; long unsigned int rq_timeout; ktime_t rq_rtt; unsigned int rq_retries; unsigned int rq_connect_cookie; atomic_t rq_pin; u32 rq_bytes_sent; ktime_t rq_xtime; int rq_ntrans; struct list_head rq_bc_list; long unsigned int rq_bc_pa_state; struct list_head rq_bc_pa_list; }; typedef void (*kxdreproc_t)(struct rpc_rqst *, struct xdr_stream *, const void *); typedef int (*kxdrdproc_t)(struct rpc_rqst *, struct xdr_stream *, void *); struct rpc_procinfo; struct rpc_message { const struct rpc_procinfo *rpc_proc; void *rpc_argp; void *rpc_resp; const struct cred *rpc_cred; }; struct rpc_procinfo { u32 p_proc; kxdreproc_t p_encode; kxdrdproc_t p_decode; unsigned int p_arglen; unsigned int p_replen; unsigned int p_timer; u32 p_statidx; const char *p_name; }; struct rpc_wait { struct list_head list; struct list_head links; struct list_head timer_list; }; struct rpc_call_ops; struct rpc_clnt; struct rpc_task { atomic_t tk_count; int tk_status; struct list_head tk_task; void (*tk_callback)(struct rpc_task *); void (*tk_action)(struct rpc_task *); long unsigned int tk_timeout; long unsigned int tk_runstate; struct rpc_wait_queue *tk_waitqueue; union { struct work_struct tk_work; struct rpc_wait tk_wait; } u; int tk_rpc_status; struct rpc_message tk_msg; void *tk_calldata; const struct rpc_call_ops *tk_ops; struct rpc_clnt *tk_client; struct rpc_xprt *tk_xprt; struct rpc_cred *tk_op_cred; struct rpc_rqst *tk_rqstp; struct workqueue_struct *tk_workqueue; ktime_t tk_start; pid_t tk_owner; short unsigned int tk_flags; short unsigned int tk_timeouts; short unsigned int tk_pid; unsigned char tk_priority: 2; unsigned char tk_garb_retry: 2; unsigned char tk_cred_retry: 2; unsigned char tk_rebind_retry: 2; }; struct rpc_call_ops { void (*rpc_call_prepare)(struct rpc_task *, void *); void (*rpc_call_done)(struct rpc_task *, void *); void (*rpc_count_stats)(struct rpc_task *, void *); void (*rpc_release)(void *); }; struct rpc_iostats; struct rpc_pipe_dir_head { struct list_head pdh_entries; struct dentry *pdh_dentry; }; struct rpc_rtt { long unsigned int timeo; long unsigned int srtt[5]; long unsigned int sdrtt[5]; int ntimeouts[5]; }; struct rpc_timeout { long unsigned int to_initval; long unsigned int to_maxval; long unsigned int to_increment; unsigned int to_retries; unsigned char to_exponential; }; struct rpc_sysfs_client; struct rpc_xprt_switch; struct rpc_xprt_iter_ops; struct rpc_xprt_iter { struct rpc_xprt_switch *xpi_xpswitch; struct rpc_xprt *xpi_cursor; const struct rpc_xprt_iter_ops *xpi_ops; }; struct rpc_auth; struct rpc_stat; struct rpc_program; struct rpc_clnt { atomic_t cl_count; unsigned int cl_clid; struct list_head cl_clients; struct list_head cl_tasks; spinlock_t cl_lock; struct rpc_xprt *cl_xprt; const struct rpc_procinfo *cl_procinfo; u32 cl_prog; u32 cl_vers; u32 cl_maxproc; struct rpc_auth *cl_auth; struct rpc_stat *cl_stats; struct rpc_iostats *cl_metrics; unsigned int cl_softrtry: 1; unsigned int cl_softerr: 1; unsigned int cl_discrtry: 1; unsigned int cl_noretranstimeo: 1; unsigned int cl_autobind: 1; unsigned int cl_chatty: 1; struct rpc_rtt *cl_rtt; const struct rpc_timeout *cl_timeout; atomic_t cl_swapper; int cl_nodelen; char cl_nodename[65]; struct rpc_pipe_dir_head cl_pipedir_objects; struct rpc_clnt *cl_parent; struct rpc_rtt cl_rtt_default; struct rpc_timeout cl_timeout_default; const struct rpc_program *cl_program; const char *cl_principal; struct dentry *cl_debugfs; struct rpc_sysfs_client *cl_sysfs; union { struct rpc_xprt_iter cl_xpi; struct work_struct cl_work; }; const struct cred *cl_cred; }; struct svc_xprt; struct rpc_sysfs_xprt; struct rpc_xprt_ops; struct svc_serv; struct xprt_class; struct rpc_xprt { struct kref kref; const struct rpc_xprt_ops *ops; unsigned int id; const struct rpc_timeout *timeout; struct __kernel_sockaddr_storage addr; size_t addrlen; int prot; long unsigned int cong; long unsigned int cwnd; size_t max_payload; struct rpc_wait_queue binding; struct rpc_wait_queue sending; struct rpc_wait_queue pending; struct rpc_wait_queue backlog; struct list_head free; unsigned int max_reqs; unsigned int min_reqs; unsigned int num_reqs; long unsigned int state; unsigned char resvport: 1; unsigned char reuseport: 1; atomic_t swapper; unsigned int bind_index; struct list_head xprt_switch; long unsigned int bind_timeout; long unsigned int reestablish_timeout; unsigned int connect_cookie; struct work_struct task_cleanup; struct timer_list timer; long unsigned int last_used; long unsigned int idle_timeout; long unsigned int connect_timeout; long unsigned int max_reconnect_timeout; atomic_long_t queuelen; spinlock_t transport_lock; spinlock_t reserve_lock; spinlock_t queue_lock; u32 xid; struct rpc_task *snd_task; struct list_head xmit_queue; atomic_long_t xmit_queuelen; struct svc_xprt *bc_xprt; struct svc_serv *bc_serv; unsigned int bc_alloc_max; unsigned int bc_alloc_count; atomic_t bc_slot_count; spinlock_t bc_pa_lock; struct list_head bc_pa_list; struct rb_root recv_queue; struct { long unsigned int bind_count; long unsigned int connect_count; long unsigned int connect_start; long unsigned int connect_time; long unsigned int sends; long unsigned int recvs; long unsigned int bad_xids; long unsigned int max_slots; long long unsigned int req_u; long long unsigned int bklog_u; long long unsigned int sending_u; long long unsigned int pending_u; } stat; struct net *xprt_net; const char *servername; const char *address_strings[6]; struct dentry *debugfs; atomic_t inject_disconnect; struct callback_head rcu; const struct xprt_class *xprt_class; struct rpc_sysfs_xprt *xprt_sysfs; bool main; }; struct rpc_credops; struct rpc_cred { struct hlist_node cr_hash; struct list_head cr_lru; struct callback_head cr_rcu; struct rpc_auth *cr_auth; const struct rpc_credops *cr_ops; long unsigned int cr_expire; long unsigned int cr_flags; refcount_t cr_count; const struct cred *cr_cred; }; typedef u32 rpc_authflavor_t; struct auth_cred { const struct cred *cred; const char *principal; }; struct rpc_cred_cache; struct rpc_authops; struct rpc_auth { unsigned int au_cslack; unsigned int au_rslack; unsigned int au_verfsize; unsigned int au_ralign; long unsigned int au_flags; const struct rpc_authops *au_ops; rpc_authflavor_t au_flavor; refcount_t au_count; struct rpc_cred_cache *au_credcache; }; struct rpc_credops { const char *cr_name; int (*cr_init)(struct rpc_auth *, struct rpc_cred *); void (*crdestroy)(struct rpc_cred *); int (*crmatch)(struct auth_cred *, struct rpc_cred *, int); int (*crmarshal)(struct rpc_task *, struct xdr_stream *); int (*crrefresh)(struct rpc_task *); int (*crvalidate)(struct rpc_task *, struct xdr_stream *); int (*crwrap_req)(struct rpc_task *, struct xdr_stream *); int (*crunwrap_resp)(struct rpc_task *, struct xdr_stream *); int (*crkey_timeout)(struct rpc_cred *); char * (*crstringify_acceptor)(struct rpc_cred *); bool (*crneed_reencode)(struct rpc_task *); }; struct rpc_auth_create_args; struct rpcsec_gss_info; struct rpc_authops { struct module *owner; rpc_authflavor_t au_flavor; char *au_name; struct rpc_auth * (*create)(const struct rpc_auth_create_args *, struct rpc_clnt *); void (*destroy)(struct rpc_auth *); int (*hash_cred)(struct auth_cred *, unsigned int); struct rpc_cred * (*lookup_cred)(struct rpc_auth *, struct auth_cred *, int); struct rpc_cred * (*crcreate)(struct rpc_auth *, struct auth_cred *, int, gfp_t); rpc_authflavor_t (*info2flavor)(struct rpcsec_gss_info *); int (*flavor2info)(rpc_authflavor_t, struct rpcsec_gss_info *); int (*key_timeout)(struct rpc_auth *, struct rpc_cred *); }; struct rpc_auth_create_args { rpc_authflavor_t pseudoflavor; const char *target_name; }; struct rpcsec_gss_oid { unsigned int len; u8 data[32]; }; struct rpcsec_gss_info { struct rpcsec_gss_oid oid; u32 qop; u32 service; }; struct rpc_xprt_ops { void (*set_buffer_size)(struct rpc_xprt *, size_t, size_t); int (*reserve_xprt)(struct rpc_xprt *, struct rpc_task *); void (*release_xprt)(struct rpc_xprt *, struct rpc_task *); void (*alloc_slot)(struct rpc_xprt *, struct rpc_task *); void (*free_slot)(struct rpc_xprt *, struct rpc_rqst *); void (*rpcbind)(struct rpc_task *); void (*set_port)(struct rpc_xprt *, short unsigned int); void (*connect)(struct rpc_xprt *, struct rpc_task *); int (*buf_alloc)(struct rpc_task *); void (*buf_free)(struct rpc_task *); void (*prepare_request)(struct rpc_rqst *); int (*send_request)(struct rpc_rqst *); void (*wait_for_reply_request)(struct rpc_task *); void (*timer)(struct rpc_xprt *, struct rpc_task *); void (*release_request)(struct rpc_task *); void (*close)(struct rpc_xprt *); void (*destroy)(struct rpc_xprt *); void (*set_connect_timeout)(struct rpc_xprt *, long unsigned int, long unsigned int); void (*print_stats)(struct rpc_xprt *, struct seq_file *); int (*enable_swap)(struct rpc_xprt *); void (*disable_swap)(struct rpc_xprt *); void (*inject_disconnect)(struct rpc_xprt *); int (*bc_setup)(struct rpc_xprt *, unsigned int); size_t (*bc_maxpayload)(struct rpc_xprt *); unsigned int (*bc_num_slots)(struct rpc_xprt *); void (*bc_free_rqst)(struct rpc_rqst *); void (*bc_destroy)(struct rpc_xprt *, unsigned int); }; struct svc_program; struct svc_stat; struct svc_pool; struct svc_serv_ops; struct svc_serv { struct svc_program *sv_program; struct svc_stat *sv_stats; spinlock_t sv_lock; unsigned int sv_nrthreads; unsigned int sv_maxconn; unsigned int sv_max_payload; unsigned int sv_max_mesg; unsigned int sv_xdrsize; struct list_head sv_permsocks; struct list_head sv_tempsocks; int sv_tmpcnt; struct timer_list sv_temptimer; char *sv_name; unsigned int sv_nrpools; struct svc_pool *sv_pools; const struct svc_serv_ops *sv_ops; struct list_head sv_cb_list; spinlock_t sv_cb_lock; wait_queue_head_t sv_cb_waitq; bool sv_bc_enabled; }; struct xprt_create; struct xprt_class { struct list_head list; int ident; struct rpc_xprt * (*setup)(struct xprt_create *); struct module *owner; char name[32]; const char *netid[0]; }; struct xprt_create { int ident; struct net *net; struct sockaddr *srcaddr; struct sockaddr *dstaddr; size_t addrlen; const char *servername; struct svc_xprt *bc_xprt; struct rpc_xprt_switch *bc_xps; unsigned int flags; }; struct rpc_sysfs_xprt_switch; struct rpc_xprt_switch { spinlock_t xps_lock; struct kref xps_kref; unsigned int xps_id; unsigned int xps_nxprts; unsigned int xps_nactive; atomic_long_t xps_queuelen; struct list_head xps_xprt_list; struct net *xps_net; const struct rpc_xprt_iter_ops *xps_iter_ops; struct rpc_sysfs_xprt_switch *xps_sysfs; struct callback_head xps_rcu; }; struct rpc_stat { const struct rpc_program *program; unsigned int netcnt; unsigned int netudpcnt; unsigned int nettcpcnt; unsigned int nettcpconn; unsigned int netreconn; unsigned int rpccnt; unsigned int rpcretrans; unsigned int rpcauthrefresh; unsigned int rpcgarbage; }; struct rpc_version; struct rpc_program { const char *name; u32 number; unsigned int nrvers; const struct rpc_version **version; struct rpc_stat *stats; const char *pipe_dir_name; }; struct svc_stat { struct svc_program *program; unsigned int netcnt; unsigned int netudpcnt; unsigned int nettcpcnt; unsigned int nettcpconn; unsigned int rpccnt; unsigned int rpcbadfmt; unsigned int rpcbadauth; unsigned int rpcbadclnt; }; struct svc_version; struct svc_rqst; struct svc_process_info; struct svc_program { struct svc_program *pg_next; u32 pg_prog; unsigned int pg_lovers; unsigned int pg_hivers; unsigned int pg_nvers; const struct svc_version **pg_vers; char *pg_name; char *pg_class; struct svc_stat *pg_stats; int (*pg_authenticate)(struct svc_rqst *); __be32 (*pg_init_request)(struct svc_rqst *, const struct svc_program *, struct svc_process_info *); int (*pg_rpcbind_set)(struct net *, const struct svc_program *, u32, int, short unsigned int, short unsigned int); }; struct rpc_xprt_iter_ops { void (*xpi_rewind)(struct rpc_xprt_iter *); struct rpc_xprt * (*xpi_xprt)(struct rpc_xprt_iter *); struct rpc_xprt * (*xpi_next)(struct rpc_xprt_iter *); }; struct rpc_version { u32 number; unsigned int nrprocs; const struct rpc_procinfo *procs; unsigned int *counts; }; struct nfs_fh { short unsigned int size; unsigned char data[128]; }; enum nfs3_stable_how { NFS_UNSTABLE = 0, NFS_DATA_SYNC = 1, NFS_FILE_SYNC = 2, NFS_INVALID_STABLE_HOW = 4294967295, }; struct nfs4_label { uint32_t lfs; uint32_t pi; u32 len; char *label; }; typedef struct { char data[8]; } nfs4_verifier; enum nfs4_change_attr_type { NFS4_CHANGE_TYPE_IS_MONOTONIC_INCR = 0, NFS4_CHANGE_TYPE_IS_VERSION_COUNTER = 1, NFS4_CHANGE_TYPE_IS_VERSION_COUNTER_NOPNFS = 2, NFS4_CHANGE_TYPE_IS_TIME_METADATA = 3, NFS4_CHANGE_TYPE_IS_UNDEFINED = 4, }; struct gss_api_mech; struct gss_ctx { struct gss_api_mech *mech_type; void *internal_ctx_id; unsigned int slack; unsigned int align; }; struct gss_api_ops; struct pf_desc; struct gss_api_mech { struct list_head gm_list; struct module *gm_owner; struct rpcsec_gss_oid gm_oid; char *gm_name; const struct gss_api_ops *gm_ops; int gm_pf_num; struct pf_desc *gm_pfs; const char *gm_upcall_enctypes; }; struct auth_domain; struct pf_desc { u32 pseudoflavor; u32 qop; u32 service; char *name; char *auth_domain_name; struct auth_domain *domain; bool datatouch; }; struct auth_ops; struct auth_domain { struct kref ref; struct hlist_node hash; char *name; struct auth_ops *flavour; struct callback_head callback_head; }; struct gss_api_ops { int (*gss_import_sec_context)(const void *, size_t, struct gss_ctx *, time64_t *, gfp_t); u32 (*gss_get_mic)(struct gss_ctx *, struct xdr_buf *, struct xdr_netobj *); u32 (*gss_verify_mic)(struct gss_ctx *, struct xdr_buf *, struct xdr_netobj *); u32 (*gss_wrap)(struct gss_ctx *, int, struct xdr_buf *, struct page **); u32 (*gss_unwrap)(struct gss_ctx *, int, int, struct xdr_buf *); void (*gss_delete_sec_context)(void *); }; struct nfs4_string { unsigned int len; char *data; }; struct nfs_fsid { uint64_t major; uint64_t minor; }; struct nfs4_threshold { __u32 bm; __u32 l_type; __u64 rd_sz; __u64 wr_sz; __u64 rd_io_sz; __u64 wr_io_sz; }; struct nfs_fattr { unsigned int valid; umode_t mode; __u32 nlink; kuid_t uid; kgid_t gid; dev_t rdev; __u64 size; union { struct { __u32 blocksize; __u32 blocks; } nfs2; struct { __u64 used; } nfs3; } du; struct nfs_fsid fsid; __u64 fileid; __u64 mounted_on_fileid; struct timespec64 atime; struct timespec64 mtime; struct timespec64 ctime; __u64 change_attr; __u64 pre_change_attr; __u64 pre_size; struct timespec64 pre_mtime; struct timespec64 pre_ctime; long unsigned int time_start; long unsigned int gencount; struct nfs4_string *owner_name; struct nfs4_string *group_name; struct nfs4_threshold *mdsthreshold; struct nfs4_label *label; }; struct nfs_fsinfo { struct nfs_fattr *fattr; __u32 rtmax; __u32 rtpref; __u32 rtmult; __u32 wtmax; __u32 wtpref; __u32 wtmult; __u32 dtpref; __u64 maxfilesize; struct timespec64 time_delta; __u32 lease_time; __u32 nlayouttypes; __u32 layouttype[8]; __u32 blksize; __u32 clone_blksize; enum nfs4_change_attr_type change_attr_type; __u32 xattr_support; }; struct nfs_fsstat { struct nfs_fattr *fattr; __u64 tbytes; __u64 fbytes; __u64 abytes; __u64 tfiles; __u64 ffiles; __u64 afiles; }; struct nfs_pathconf { struct nfs_fattr *fattr; __u32 max_link; __u32 max_namelen; }; struct nfs4_change_info { u32 atomic; u64 before; u64 after; }; struct nfs4_slot; struct nfs4_sequence_args { struct nfs4_slot *sa_slot; u8 sa_cache_this: 1; u8 sa_privileged: 1; }; struct nfs4_sequence_res { struct nfs4_slot *sr_slot; long unsigned int sr_timestamp; int sr_status; u32 sr_status_flags; u32 sr_highest_slotid; u32 sr_target_highest_slotid; }; struct nfs_open_context; struct nfs_lock_context { refcount_t count; struct list_head list; struct nfs_open_context *open_context; fl_owner_t lockowner; atomic_t io_count; struct callback_head callback_head; }; struct nfs_open_context { struct nfs_lock_context lock_context; fl_owner_t flock_owner; struct dentry *dentry; const struct cred *cred; struct rpc_cred *ll_cred; struct nfs4_state *state; fmode_t mode; long unsigned int flags; int error; struct list_head list; struct nfs4_threshold *mdsthreshold; struct callback_head callback_head; }; struct nlm_host; struct nfs_iostats; struct nfs_auth_info { unsigned int flavor_len; rpc_authflavor_t flavors[12]; }; struct nfs_fscache_key; struct fscache_cookie; struct pnfs_layoutdriver_type; struct nfs_client; struct nfs_server { struct nfs_client *nfs_client; struct list_head client_link; struct list_head master_link; struct rpc_clnt *client; struct rpc_clnt *client_acl; struct nlm_host *nlm_host; struct nfs_iostats *io_stats; atomic_long_t writeback; unsigned int flags; unsigned int fattr_valid; unsigned int caps; unsigned int rsize; unsigned int rpages; unsigned int wsize; unsigned int wpages; unsigned int wtmult; unsigned int dtsize; short unsigned int port; unsigned int bsize; unsigned int gxasize; unsigned int sxasize; unsigned int lxasize; unsigned int acregmin; unsigned int acregmax; unsigned int acdirmin; unsigned int acdirmax; unsigned int namelen; unsigned int options; unsigned int clone_blksize; enum nfs4_change_attr_type change_attr_type; struct nfs_fsid fsid; __u64 maxfilesize; struct timespec64 time_delta; long unsigned int mount_time; struct super_block *super; dev_t s_dev; struct nfs_auth_info auth_info; struct nfs_fscache_key *fscache_key; struct fscache_cookie *fscache; u32 pnfs_blksize; u32 attr_bitmask[3]; u32 attr_bitmask_nl[3]; u32 exclcreat_bitmask[3]; u32 cache_consistency_bitmask[3]; u32 acl_bitmask; u32 fh_expire_type; struct pnfs_layoutdriver_type *pnfs_curr_ld; struct rpc_wait_queue roc_rpcwaitq; void *pnfs_ld_data; struct rb_root state_owners; struct ida openowner_id; struct ida lockowner_id; struct list_head state_owners_lru; struct list_head layouts; struct list_head delegations; struct list_head ss_copies; long unsigned int mig_gen; long unsigned int mig_status; void (*destroy)(struct nfs_server *); atomic_t active; struct __kernel_sockaddr_storage mountd_address; size_t mountd_addrlen; u32 mountd_version; short unsigned int mountd_port; short unsigned int mountd_protocol; struct rpc_wait_queue uoc_rpcwaitq; unsigned int read_hdrsize; const struct cred *cred; bool has_sec_mnt_opts; }; struct nfs_subversion; struct idmap; struct nfs4_slot_table; struct nfs4_session; struct nfs_rpc_ops; struct nfs4_minor_version_ops; struct nfs41_server_owner; struct nfs41_server_scope; struct nfs41_impl_id; struct nfs_client { refcount_t cl_count; atomic_t cl_mds_count; int cl_cons_state; long unsigned int cl_res_state; long unsigned int cl_flags; struct __kernel_sockaddr_storage cl_addr; size_t cl_addrlen; char *cl_hostname; char *cl_acceptor; struct list_head cl_share_link; struct list_head cl_superblocks; struct rpc_clnt *cl_rpcclient; const struct nfs_rpc_ops *rpc_ops; int cl_proto; struct nfs_subversion *cl_nfs_mod; u32 cl_minorversion; unsigned int cl_nconnect; const char *cl_principal; struct list_head cl_ds_clients; u64 cl_clientid; nfs4_verifier cl_confirm; long unsigned int cl_state; spinlock_t cl_lock; long unsigned int cl_lease_time; long unsigned int cl_last_renewal; struct delayed_work cl_renewd; struct rpc_wait_queue cl_rpcwaitq; struct idmap *cl_idmap; const char *cl_owner_id; u32 cl_cb_ident; const struct nfs4_minor_version_ops *cl_mvops; long unsigned int cl_mig_gen; struct nfs4_slot_table *cl_slot_tbl; u32 cl_seqid; u32 cl_exchange_flags; struct nfs4_session *cl_session; bool cl_preserve_clid; struct nfs41_server_owner *cl_serverowner; struct nfs41_server_scope *cl_serverscope; struct nfs41_impl_id *cl_implid; long unsigned int cl_sp4_flags; wait_queue_head_t cl_lock_waitq; char cl_ipaddr[48]; struct fscache_cookie *fscache; struct net *cl_net; struct list_head pending_cb_stateids; }; struct pnfs_layout_segment; struct nfs_seqid { struct nfs_seqid_counter *sequence; struct list_head list; struct rpc_task *task; }; struct nfs_write_verifier { char data[8]; }; struct nfs_writeverf { struct nfs_write_verifier verifier; enum nfs3_stable_how committed; }; struct nfs_pgio_args { struct nfs4_sequence_args seq_args; struct nfs_fh *fh; struct nfs_open_context *context; struct nfs_lock_context *lock_context; nfs4_stateid stateid; __u64 offset; __u32 count; unsigned int pgbase; struct page **pages; union { unsigned int replen; struct { const u32 *bitmask; u32 bitmask_store[3]; enum nfs3_stable_how stable; }; }; }; struct nfs_pgio_res { struct nfs4_sequence_res seq_res; struct nfs_fattr *fattr; __u64 count; __u32 op_status; union { struct { unsigned int replen; int eof; }; struct { struct nfs_writeverf *verf; const struct nfs_server *server; }; }; }; struct nfs_commitargs { struct nfs4_sequence_args seq_args; struct nfs_fh *fh; __u64 offset; __u32 count; const u32 *bitmask; }; struct nfs_commitres { struct nfs4_sequence_res seq_res; __u32 op_status; struct nfs_fattr *fattr; struct nfs_writeverf *verf; const struct nfs_server *server; }; struct nfs_removeargs { struct nfs4_sequence_args seq_args; const struct nfs_fh *fh; struct qstr name; }; struct nfs_removeres { struct nfs4_sequence_res seq_res; struct nfs_server *server; struct nfs_fattr *dir_attr; struct nfs4_change_info cinfo; }; struct nfs_renameargs { struct nfs4_sequence_args seq_args; const struct nfs_fh *old_dir; const struct nfs_fh *new_dir; const struct qstr *old_name; const struct qstr *new_name; }; struct nfs_renameres { struct nfs4_sequence_res seq_res; struct nfs_server *server; struct nfs4_change_info old_cinfo; struct nfs_fattr *old_fattr; struct nfs4_change_info new_cinfo; struct nfs_fattr *new_fattr; }; struct nfs_entry { __u64 ino; __u64 cookie; __u64 prev_cookie; const char *name; unsigned int len; int eof; struct nfs_fh *fh; struct nfs_fattr *fattr; struct nfs4_label *label; unsigned char d_type; struct nfs_server *server; }; struct nfs_readdir_arg { struct dentry *dentry; const struct cred *cred; __be32 *verf; u64 cookie; struct page **pages; unsigned int page_len; bool plus; }; struct nfs_readdir_res { __be32 *verf; }; struct nfs4_pathname { unsigned int ncomponents; struct nfs4_string components[512]; }; struct nfs4_fs_location { unsigned int nservers; struct nfs4_string servers[10]; struct nfs4_pathname rootpath; }; struct nfs4_fs_locations { struct nfs_fattr fattr; const struct nfs_server *server; struct nfs4_pathname fs_path; int nlocations; struct nfs4_fs_location locations[10]; }; struct nfstime4 { u64 seconds; u32 nseconds; }; struct pnfs_commit_ops; struct pnfs_ds_commit_info { struct list_head commits; unsigned int nwritten; unsigned int ncommitting; const struct pnfs_commit_ops *ops; }; struct nfs41_server_owner { uint64_t minor_id; uint32_t major_id_sz; char major_id[1024]; }; struct nfs41_server_scope { uint32_t server_scope_sz; char server_scope[1024]; }; struct nfs41_impl_id { char domain[1025]; char name[1025]; struct nfstime4 date; }; struct nfs_page_array { struct page **pagevec; unsigned int npages; struct page *page_array[8]; }; struct nfs_page; struct nfs_rw_ops; struct nfs_io_completion; struct nfs_direct_req; struct nfs_pgio_completion_ops; struct nfs_pgio_header { struct inode *inode; const struct cred *cred; struct list_head pages; struct nfs_page *req; struct nfs_writeverf verf; fmode_t rw_mode; struct pnfs_layout_segment *lseg; loff_t io_start; const struct rpc_call_ops *mds_ops; void (*release)(struct nfs_pgio_header *); const struct nfs_pgio_completion_ops *completion_ops; const struct nfs_rw_ops *rw_ops; struct nfs_io_completion *io_completion; struct nfs_direct_req *dreq; int pnfs_error; int error; unsigned int good_bytes; long unsigned int flags; struct rpc_task task; struct nfs_fattr fattr; struct nfs_pgio_args args; struct nfs_pgio_res res; long unsigned int timestamp; int (*pgio_done_cb)(struct rpc_task *, struct nfs_pgio_header *); __u64 mds_offset; struct nfs_page_array page_array; struct nfs_client *ds_clp; u32 ds_commit_idx; u32 pgio_mirror_idx; }; struct nfs_pgio_completion_ops { void (*error_cleanup)(struct list_head *, int); void (*init_hdr)(struct nfs_pgio_header *); void (*completion)(struct nfs_pgio_header *); void (*reschedule_io)(struct nfs_pgio_header *); }; struct nfs_mds_commit_info { atomic_t rpcs_out; atomic_long_t ncommit; struct list_head list; }; struct nfs_commit_data; struct nfs_commit_info; struct nfs_commit_completion_ops { void (*completion)(struct nfs_commit_data *); void (*resched_write)(struct nfs_commit_info *, struct nfs_page *); }; struct nfs_commit_data { struct rpc_task task; struct inode *inode; const struct cred *cred; struct nfs_fattr fattr; struct nfs_writeverf verf; struct list_head pages; struct list_head list; struct nfs_direct_req *dreq; struct nfs_commitargs args; struct nfs_commitres res; struct nfs_open_context *context; struct pnfs_layout_segment *lseg; struct nfs_client *ds_clp; int ds_commit_index; loff_t lwb; const struct rpc_call_ops *mds_ops; const struct nfs_commit_completion_ops *completion_ops; int (*commit_done_cb)(struct rpc_task *, struct nfs_commit_data *); long unsigned int flags; }; struct nfs_commit_info { struct inode *inode; struct nfs_mds_commit_info *mds; struct pnfs_ds_commit_info *ds; struct nfs_direct_req *dreq; const struct nfs_commit_completion_ops *completion_ops; }; struct nfs_unlinkdata { struct nfs_removeargs args; struct nfs_removeres res; struct dentry *dentry; wait_queue_head_t wq; const struct cred *cred; struct nfs_fattr dir_attr; long int timeout; }; struct nfs_renamedata { struct nfs_renameargs args; struct nfs_renameres res; const struct cred *cred; struct inode *old_dir; struct dentry *old_dentry; struct nfs_fattr old_fattr; struct inode *new_dir; struct dentry *new_dentry; struct nfs_fattr new_fattr; void (*complete)(struct rpc_task *, struct nfs_renamedata *); long int timeout; bool cancelled; }; struct nlmclnt_operations; struct nfs_client_initdata; struct nfs_access_entry; struct nfs_rpc_ops { u32 version; const struct dentry_operations *dentry_ops; const struct inode_operations *dir_inode_ops; const struct inode_operations *file_inode_ops; const struct file_operations *file_ops; const struct nlmclnt_operations *nlmclnt_ops; int (*getroot)(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); int (*submount)(struct fs_context *, struct nfs_server *); int (*try_get_tree)(struct fs_context *); int (*getattr)(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *, struct inode *); int (*setattr)(struct dentry *, struct nfs_fattr *, struct iattr *); int (*lookup)(struct inode *, struct dentry *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *); int (*lookupp)(struct inode *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *); int (*access)(struct inode *, struct nfs_access_entry *); int (*readlink)(struct inode *, struct page *, unsigned int, unsigned int); int (*create)(struct inode *, struct dentry *, struct iattr *, int); int (*remove)(struct inode *, struct dentry *); void (*unlink_setup)(struct rpc_message *, struct dentry *, struct inode *); void (*unlink_rpc_prepare)(struct rpc_task *, struct nfs_unlinkdata *); int (*unlink_done)(struct rpc_task *, struct inode *); void (*rename_setup)(struct rpc_message *, struct dentry *, struct dentry *); void (*rename_rpc_prepare)(struct rpc_task *, struct nfs_renamedata *); int (*rename_done)(struct rpc_task *, struct inode *, struct inode *); int (*link)(struct inode *, struct inode *, const struct qstr *); int (*symlink)(struct inode *, struct dentry *, struct page *, unsigned int, struct iattr *); int (*mkdir)(struct inode *, struct dentry *, struct iattr *); int (*rmdir)(struct inode *, const struct qstr *); int (*readdir)(struct nfs_readdir_arg *, struct nfs_readdir_res *); int (*mknod)(struct inode *, struct dentry *, struct iattr *, dev_t); int (*statfs)(struct nfs_server *, struct nfs_fh *, struct nfs_fsstat *); int (*fsinfo)(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); int (*pathconf)(struct nfs_server *, struct nfs_fh *, struct nfs_pathconf *); int (*set_capabilities)(struct nfs_server *, struct nfs_fh *); int (*decode_dirent)(struct xdr_stream *, struct nfs_entry *, bool); int (*pgio_rpc_prepare)(struct rpc_task *, struct nfs_pgio_header *); void (*read_setup)(struct nfs_pgio_header *, struct rpc_message *); int (*read_done)(struct rpc_task *, struct nfs_pgio_header *); void (*write_setup)(struct nfs_pgio_header *, struct rpc_message *, struct rpc_clnt **); int (*write_done)(struct rpc_task *, struct nfs_pgio_header *); void (*commit_setup)(struct nfs_commit_data *, struct rpc_message *, struct rpc_clnt **); void (*commit_rpc_prepare)(struct rpc_task *, struct nfs_commit_data *); int (*commit_done)(struct rpc_task *, struct nfs_commit_data *); int (*lock)(struct file *, int, struct file_lock *); int (*lock_check_bounds)(const struct file_lock *); void (*clear_acl_cache)(struct inode *); void (*close_context)(struct nfs_open_context *, int); struct inode * (*open_context)(struct inode *, struct nfs_open_context *, int, struct iattr *, int *); int (*have_delegation)(struct inode *, fmode_t); struct nfs_client * (*alloc_client)(const struct nfs_client_initdata *); struct nfs_client * (*init_client)(struct nfs_client *, const struct nfs_client_initdata *); void (*free_client)(struct nfs_client *); struct nfs_server * (*create_server)(struct fs_context *); struct nfs_server * (*clone_server)(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, rpc_authflavor_t); }; struct nfs_access_entry { struct rb_node rb_node; struct list_head lru; const struct cred *cred; __u32 mask; struct callback_head callback_head; }; struct nfs4_state_recovery_ops; struct nfs4_state_maintenance_ops; struct nfs4_mig_recovery_ops; struct nfs4_minor_version_ops { u32 minor_version; unsigned int init_caps; int (*init_client)(struct nfs_client *); void (*shutdown_client)(struct nfs_client *); bool (*match_stateid)(const nfs4_stateid *, const nfs4_stateid *); int (*find_root_sec)(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); void (*free_lock_state)(struct nfs_server *, struct nfs4_lock_state *); int (*test_and_free_expired)(struct nfs_server *, nfs4_stateid *, const struct cred *); struct nfs_seqid * (*alloc_seqid)(struct nfs_seqid_counter *, gfp_t); void (*session_trunk)(struct rpc_clnt *, struct rpc_xprt *, void *); const struct rpc_call_ops *call_sync_ops; const struct nfs4_state_recovery_ops *reboot_recovery_ops; const struct nfs4_state_recovery_ops *nograce_recovery_ops; const struct nfs4_state_maintenance_ops *state_renewal_ops; const struct nfs4_mig_recovery_ops *mig_recovery_ops; }; struct nfs4_state_owner; struct nfs4_state { struct list_head open_states; struct list_head inode_states; struct list_head lock_states; struct nfs4_state_owner *owner; struct inode *inode; long unsigned int flags; spinlock_t state_lock; seqlock_t seqlock; nfs4_stateid stateid; nfs4_stateid open_stateid; unsigned int n_rdonly; unsigned int n_wronly; unsigned int n_rdwr; fmode_t state; refcount_t count; wait_queue_head_t waitq; struct callback_head callback_head; }; struct cache_head { struct hlist_node cache_list; time64_t expiry_time; time64_t last_refresh; struct kref ref; long unsigned int flags; }; struct cache_deferred_req; struct cache_req { struct cache_deferred_req * (*defer)(struct cache_req *); int thread_wait; }; struct cache_deferred_req { struct hlist_node hash; struct list_head recent; struct cache_head *item; void *owner; void (*revisit)(struct cache_deferred_req *, int); }; struct svc_cred { kuid_t cr_uid; kgid_t cr_gid; struct group_info *cr_group_info; u32 cr_flavor; char *cr_raw_principal; char *cr_principal; char *cr_targ_princ; struct gss_api_mech *cr_gss_mech; }; struct auth_ops { char *name; struct module *owner; int flavour; int (*accept)(struct svc_rqst *, __be32 *); int (*release)(struct svc_rqst *); void (*domain_release)(struct auth_domain *); int (*set_client)(struct svc_rqst *); }; struct svc_cacherep; struct svc_procedure; struct svc_deferred_req; struct svc_rqst { struct list_head rq_all; struct callback_head rq_rcu_head; struct svc_xprt *rq_xprt; struct __kernel_sockaddr_storage rq_addr; size_t rq_addrlen; struct __kernel_sockaddr_storage rq_daddr; size_t rq_daddrlen; struct svc_serv *rq_server; struct svc_pool *rq_pool; const struct svc_procedure *rq_procinfo; struct auth_ops *rq_authop; struct svc_cred rq_cred; void *rq_xprt_ctxt; struct svc_deferred_req *rq_deferred; size_t rq_xprt_hlen; struct xdr_buf rq_arg; struct xdr_stream rq_arg_stream; struct xdr_stream rq_res_stream; struct page *rq_scratch_page; struct xdr_buf rq_res; struct page *rq_pages[260]; struct page **rq_respages; struct page **rq_next_page; struct page **rq_page_end; struct kvec rq_vec[259]; struct bio_vec rq_bvec[259]; __be32 rq_xid; u32 rq_prog; u32 rq_vers; u32 rq_proc; u32 rq_prot; int rq_cachetype; long unsigned int rq_flags; ktime_t rq_qtime; void *rq_argp; void *rq_resp; void *rq_auth_data; int rq_auth_slack; int rq_reserved; ktime_t rq_stime; struct cache_req rq_chandle; struct auth_domain *rq_client; struct auth_domain *rq_gssclient; struct svc_cacherep *rq_cacherep; struct task_struct *rq_task; spinlock_t rq_lock; struct net *rq_bc_net; void **rq_lease_breaker; }; struct svc_pool_stats { atomic_long_t packets; long unsigned int sockets_queued; atomic_long_t threads_woken; atomic_long_t threads_timedout; }; struct svc_pool { unsigned int sp_id; spinlock_t sp_lock; struct list_head sp_sockets; unsigned int sp_nrthreads; struct list_head sp_all_threads; struct svc_pool_stats sp_stats; long unsigned int sp_flags; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct svc_serv_ops { void (*svo_shutdown)(struct svc_serv *, struct net *); int (*svo_function)(void *); void (*svo_enqueue_xprt)(struct svc_xprt *); int (*svo_setup)(struct svc_serv *, struct svc_pool *, int); struct module *svo_module; }; struct svc_procedure { __be32 (*pc_func)(struct svc_rqst *); int (*pc_decode)(struct svc_rqst *, __be32 *); int (*pc_encode)(struct svc_rqst *, __be32 *); void (*pc_release)(struct svc_rqst *); unsigned int pc_argsize; unsigned int pc_ressize; unsigned int pc_cachetype; unsigned int pc_xdrressize; const char *pc_name; }; struct svc_deferred_req { u32 prot; struct svc_xprt *xprt; struct __kernel_sockaddr_storage addr; size_t addrlen; struct __kernel_sockaddr_storage daddr; size_t daddrlen; struct cache_deferred_req handle; size_t xprt_hlen; int argslen; __be32 args[0]; }; struct svc_process_info { union { int (*dispatch)(struct svc_rqst *, __be32 *); struct { unsigned int lovers; unsigned int hivers; } mismatch; }; }; struct svc_version { u32 vs_vers; u32 vs_nproc; const struct svc_procedure *vs_proc; unsigned int *vs_count; u32 vs_xdrsize; bool vs_hidden; bool vs_rpcb_optnl; bool vs_need_cong_ctrl; int (*vs_dispatch)(struct svc_rqst *, __be32 *); }; struct nfs4_ssc_client_ops; struct nfs_ssc_client_ops; struct nfs_ssc_client_ops_tbl { const struct nfs4_ssc_client_ops *ssc_nfs4_ops; const struct nfs_ssc_client_ops *ssc_nfs_ops; }; struct nfs4_ssc_client_ops { struct file * (*sco_open)(struct vfsmount *, struct nfs_fh *, nfs4_stateid *); void (*sco_close)(struct file *); }; struct nfs_ssc_client_ops { void (*sco_sb_deactive)(struct super_block *); }; struct nfs4_state_recovery_ops { int owner_flag_bit; int state_flag_bit; int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *); int (*recover_lock)(struct nfs4_state *, struct file_lock *); int (*establish_clid)(struct nfs_client *, const struct cred *); int (*reclaim_complete)(struct nfs_client *, const struct cred *); int (*detect_trunking)(struct nfs_client *, struct nfs_client **, const struct cred *); }; struct nfs4_state_maintenance_ops { int (*sched_state_renewal)(struct nfs_client *, const struct cred *, unsigned int); const struct cred * (*get_state_renewal_cred)(struct nfs_client *); int (*renew_lease)(struct nfs_client *, const struct cred *); }; struct nfs4_mig_recovery_ops { int (*get_locations)(struct inode *, struct nfs4_fs_locations *, struct page *, const struct cred *); int (*fsid_present)(struct inode *, const struct cred *); }; struct nfs4_state_owner { struct nfs_server *so_server; struct list_head so_lru; long unsigned int so_expires; struct rb_node so_server_node; const struct cred *so_cred; spinlock_t so_lock; atomic_t so_count; long unsigned int so_flags; struct list_head so_states; struct nfs_seqid_counter so_seqid; seqcount_spinlock_t so_reclaim_seqcount; struct mutex so_delegreturn_mutex; }; struct core_name { char *corename; int used; int size; }; struct trace_event_raw_iomap_readpage_class { struct trace_entry ent; dev_t dev; u64 ino; int nr_pages; char __data[0]; }; struct trace_event_raw_iomap_range_class { struct trace_entry ent; dev_t dev; u64 ino; loff_t size; long unsigned int offset; unsigned int length; char __data[0]; }; struct trace_event_raw_iomap_class { struct trace_entry ent; dev_t dev; u64 ino; u64 addr; loff_t offset; u64 length; u16 type; u16 flags; dev_t bdev; char __data[0]; }; struct trace_event_raw_iomap_apply { struct trace_entry ent; dev_t dev; u64 ino; loff_t pos; loff_t length; unsigned int flags; const void *ops; void *actor; long unsigned int caller; char __data[0]; }; struct trace_event_data_offsets_iomap_readpage_class {}; struct trace_event_data_offsets_iomap_range_class {}; struct trace_event_data_offsets_iomap_class {}; struct trace_event_data_offsets_iomap_apply {}; typedef void (*btf_trace_iomap_readpage)(void *, struct inode *, int); typedef void (*btf_trace_iomap_readahead)(void *, struct inode *, int); typedef void (*btf_trace_iomap_writepage)(void *, struct inode *, long unsigned int, unsigned int); typedef void (*btf_trace_iomap_releasepage)(void *, struct inode *, long unsigned int, unsigned int); typedef void (*btf_trace_iomap_invalidatepage)(void *, struct inode *, long unsigned int, unsigned int); typedef void (*btf_trace_iomap_dio_invalidate_fail)(void *, struct inode *, long unsigned int, unsigned int); typedef void (*btf_trace_iomap_apply_dstmap)(void *, struct inode *, struct iomap___2 *); typedef void (*btf_trace_iomap_apply_srcmap)(void *, struct inode *, struct iomap___2 *); typedef void (*btf_trace_iomap_apply)(void *, struct inode *, loff_t, loff_t, unsigned int, const void *, void *, long unsigned int); struct iomap_ioend { struct list_head io_list; u16 io_type; u16 io_flags; struct inode *io_inode; size_t io_size; loff_t io_offset; struct bio *io_bio; struct bio io_inline_bio; }; struct iomap_writepage_ctx; struct iomap_writeback_ops { int (*map_blocks)(struct iomap_writepage_ctx *, struct inode *, loff_t); int (*prepare_ioend)(struct iomap_ioend *, int); void (*discard_page)(struct page *, loff_t); }; struct iomap_writepage_ctx { struct iomap___2 iomap; struct iomap_ioend *ioend; const struct iomap_writeback_ops *ops; }; typedef int (*list_cmp_func_t)(void *, const struct list_head *, const struct list_head *); struct iomap_page { atomic_t read_bytes_pending; atomic_t write_bytes_pending; spinlock_t uptodate_lock; long unsigned int uptodate[0]; }; struct iomap_readpage_ctx { struct page *cur_page; bool cur_page_in_bio; struct bio *bio; struct readahead_control *rac; }; enum { IOMAP_WRITE_F_UNSHARE = 1, }; struct iomap_dio_ops { int (*end_io)(struct kiocb *, ssize_t, int, unsigned int); blk_qc_t (*submit_io)(struct inode *, struct iomap___2 *, struct bio *, loff_t); }; struct iomap_dio { struct kiocb *iocb; const struct iomap_dio_ops *dops; loff_t i_size; loff_t size; atomic_t ref; unsigned int flags; int error; bool wait_for_completion; union { struct { struct iov_iter *iter; struct task_struct *waiter; struct request_queue *last_queue; blk_qc_t cookie; } submit; struct { struct work_struct work; } aio; }; }; struct fiemap_ctx { struct fiemap_extent_info *fi; struct iomap___2 prev; }; struct iomap_swapfile_info { struct iomap___2 iomap; struct swap_info_struct *sis; uint64_t lowest_ppage; uint64_t highest_ppage; long unsigned int nr_pages; int nr_extents; struct file *file; }; enum { QIF_BLIMITS_B = 0, QIF_SPACE_B = 1, QIF_ILIMITS_B = 2, QIF_INODES_B = 3, QIF_BTIME_B = 4, QIF_ITIME_B = 5, }; typedef __kernel_uid32_t qid_t; enum { DQF_INFO_DIRTY_B = 17, }; struct dqstats { long unsigned int stat[8]; struct percpu_counter counter[8]; }; enum { _DQUOT_USAGE_ENABLED = 0, _DQUOT_LIMITS_ENABLED = 1, _DQUOT_SUSPENDED = 2, _DQUOT_STATE_FLAGS = 3, }; struct quota_module_name { int qm_fmt_id; char *qm_mod_name; }; struct dquot_warn { struct super_block *w_sb; struct kqid w_dq_id; short int w_type; }; struct fs_disk_quota { __s8 d_version; __s8 d_flags; __u16 d_fieldmask; __u32 d_id; __u64 d_blk_hardlimit; __u64 d_blk_softlimit; __u64 d_ino_hardlimit; __u64 d_ino_softlimit; __u64 d_bcount; __u64 d_icount; __s32 d_itimer; __s32 d_btimer; __u16 d_iwarns; __u16 d_bwarns; __s8 d_itimer_hi; __s8 d_btimer_hi; __s8 d_rtbtimer_hi; __s8 d_padding2; __u64 d_rtb_hardlimit; __u64 d_rtb_softlimit; __u64 d_rtbcount; __s32 d_rtbtimer; __u16 d_rtbwarns; __s16 d_padding3; char d_padding4[8]; }; struct fs_qfilestat { __u64 qfs_ino; __u64 qfs_nblks; __u32 qfs_nextents; }; typedef struct fs_qfilestat fs_qfilestat_t; struct fs_quota_stat { __s8 qs_version; __u16 qs_flags; __s8 qs_pad; fs_qfilestat_t qs_uquota; fs_qfilestat_t qs_gquota; __u32 qs_incoredqs; __s32 qs_btimelimit; __s32 qs_itimelimit; __s32 qs_rtbtimelimit; __u16 qs_bwarnlimit; __u16 qs_iwarnlimit; }; struct fs_qfilestatv { __u64 qfs_ino; __u64 qfs_nblks; __u32 qfs_nextents; __u32 qfs_pad; }; struct fs_quota_statv { __s8 qs_version; __u8 qs_pad1; __u16 qs_flags; __u32 qs_incoredqs; struct fs_qfilestatv qs_uquota; struct fs_qfilestatv qs_gquota; struct fs_qfilestatv qs_pquota; __s32 qs_btimelimit; __s32 qs_itimelimit; __s32 qs_rtbtimelimit; __u16 qs_bwarnlimit; __u16 qs_iwarnlimit; __u16 qs_rtbwarnlimit; __u16 qs_pad3; __u32 qs_pad4; __u64 qs_pad2[7]; }; struct if_dqblk { __u64 dqb_bhardlimit; __u64 dqb_bsoftlimit; __u64 dqb_curspace; __u64 dqb_ihardlimit; __u64 dqb_isoftlimit; __u64 dqb_curinodes; __u64 dqb_btime; __u64 dqb_itime; __u32 dqb_valid; }; struct if_nextdqblk { __u64 dqb_bhardlimit; __u64 dqb_bsoftlimit; __u64 dqb_curspace; __u64 dqb_ihardlimit; __u64 dqb_isoftlimit; __u64 dqb_curinodes; __u64 dqb_btime; __u64 dqb_itime; __u32 dqb_valid; __u32 dqb_id; }; struct if_dqinfo { __u64 dqi_bgrace; __u64 dqi_igrace; __u32 dqi_flags; __u32 dqi_valid; }; struct compat_if_dqblk { compat_u64 dqb_bhardlimit; compat_u64 dqb_bsoftlimit; compat_u64 dqb_curspace; compat_u64 dqb_ihardlimit; compat_u64 dqb_isoftlimit; compat_u64 dqb_curinodes; compat_u64 dqb_btime; compat_u64 dqb_itime; compat_uint_t dqb_valid; } __attribute__((packed)); struct compat_fs_qfilestat { compat_u64 dqb_bhardlimit; compat_u64 qfs_nblks; compat_uint_t qfs_nextents; } __attribute__((packed)); struct compat_fs_quota_stat { __s8 qs_version; char: 8; __u16 qs_flags; __s8 qs_pad; int: 24; struct compat_fs_qfilestat qs_uquota; struct compat_fs_qfilestat qs_gquota; compat_uint_t qs_incoredqs; compat_int_t qs_btimelimit; compat_int_t qs_itimelimit; compat_int_t qs_rtbtimelimit; __u16 qs_bwarnlimit; __u16 qs_iwarnlimit; } __attribute__((packed)); enum { QUOTA_NL_C_UNSPEC = 0, QUOTA_NL_C_WARNING = 1, __QUOTA_NL_C_MAX = 2, }; enum { QUOTA_NL_A_UNSPEC = 0, QUOTA_NL_A_QTYPE = 1, QUOTA_NL_A_EXCESS_ID = 2, QUOTA_NL_A_WARNING = 3, QUOTA_NL_A_DEV_MAJOR = 4, QUOTA_NL_A_DEV_MINOR = 5, QUOTA_NL_A_CAUSED_ID = 6, QUOTA_NL_A_PAD = 7, __QUOTA_NL_A_MAX = 8, }; struct proc_maps_private { struct inode *inode; struct task_struct *task; struct mm_struct *mm; struct vm_area_struct *tail_vma; struct mempolicy *task_mempolicy; }; struct mem_size_stats { long unsigned int resident; long unsigned int shared_clean; long unsigned int shared_dirty; long unsigned int private_clean; long unsigned int private_dirty; long unsigned int referenced; long unsigned int anonymous; long unsigned int lazyfree; long unsigned int anonymous_thp; long unsigned int shmem_thp; long unsigned int file_thp; long unsigned int swap; long unsigned int shared_hugetlb; long unsigned int private_hugetlb; u64 pss; u64 pss_anon; u64 pss_file; u64 pss_shmem; u64 pss_locked; u64 swap_pss; bool check_shmem_swap; }; enum clear_refs_types { CLEAR_REFS_ALL = 1, CLEAR_REFS_ANON = 2, CLEAR_REFS_MAPPED = 3, CLEAR_REFS_SOFT_DIRTY = 4, CLEAR_REFS_MM_HIWATER_RSS = 5, CLEAR_REFS_LAST = 6, }; struct clear_refs_private { enum clear_refs_types type; }; typedef struct { u64 pme; } pagemap_entry_t; struct pagemapread { int pos; int len; pagemap_entry_t *buffer; bool show_pfn; }; struct numa_maps { long unsigned int pages; long unsigned int anon; long unsigned int active; long unsigned int writeback; long unsigned int mapcount_max; long unsigned int dirty; long unsigned int swapcache; long unsigned int node[32]; }; struct numa_maps_private { struct proc_maps_private proc_maps; struct numa_maps md; }; struct pde_opener { struct list_head lh; struct file *file; bool closing; struct completion *c; }; enum { BIAS = 2147483648, }; struct proc_fs_context { struct pid_namespace *pid_ns; unsigned int mask; enum proc_hidepid hidepid; int gid; enum proc_pidonly pidonly; }; enum proc_param { Opt_gid___2 = 0, Opt_hidepid = 1, Opt_subset = 2, }; struct genradix_root; struct __genradix { struct genradix_root *root; }; struct syscall_info { __u64 sp; struct seccomp_data data; }; typedef struct dentry *instantiate_t(struct dentry *, struct task_struct *, const void *); struct pid_entry { const char *name; unsigned int len; umode_t mode; const struct inode_operations *iop; const struct file_operations *fop; union proc_op op; }; struct limit_names { const char *name; const char *unit; }; struct map_files_info { long unsigned int start; long unsigned int end; fmode_t mode; }; struct timers_private { struct pid *pid; struct task_struct *task; struct sighand_struct *sighand; struct pid_namespace *ns; long unsigned int flags; }; struct tgid_iter { unsigned int tgid; struct task_struct *task; }; struct fd_data { fmode_t mode; unsigned int fd; }; struct sysctl_alias { const char *kernel_param; const char *sysctl_param; }; struct seq_net_private { struct net *net; }; struct bpf_iter_aux_info___2; struct vmcore { struct list_head list; long long unsigned int paddr; long long unsigned int size; loff_t offset; }; struct vmcoredd_node { struct list_head list; void *buf; unsigned int size; }; typedef struct elf32_hdr Elf32_Ehdr; typedef struct elf32_phdr Elf32_Phdr; typedef struct elf32_note Elf32_Nhdr; typedef struct elf64_note Elf64_Nhdr; struct vmcoredd_header { __u32 n_namesz; __u32 n_descsz; __u32 n_type; __u8 name[8]; __u8 dump_name[44]; }; struct vmcoredd_data { char dump_name[44]; unsigned int size; int (*vmcoredd_callback)(struct vmcoredd_data *, void *); }; struct kernfs_iattrs { kuid_t ia_uid; kgid_t ia_gid; struct timespec64 ia_atime; struct timespec64 ia_mtime; struct timespec64 ia_ctime; struct simple_xattrs xattrs; atomic_t nr_user_xattrs; atomic_t user_xattr_size; }; struct kernfs_super_info { struct super_block *sb; struct kernfs_root *root; const void *ns; struct list_head node; }; enum kernfs_node_flag { KERNFS_ACTIVATED = 16, KERNFS_NS = 32, KERNFS_HAS_SEQ_SHOW = 64, KERNFS_HAS_MMAP = 128, KERNFS_LOCKDEP = 256, KERNFS_SUICIDAL = 1024, KERNFS_SUICIDED = 2048, KERNFS_EMPTY_DIR = 4096, KERNFS_HAS_RELEASE = 8192, }; struct kernfs_open_node { atomic_t refcnt; atomic_t event; wait_queue_head_t poll; struct list_head files; }; struct config_group; struct config_item_type; struct config_item { char *ci_name; char ci_namebuf[20]; struct kref ci_kref; struct list_head ci_entry; struct config_item *ci_parent; struct config_group *ci_group; const struct config_item_type *ci_type; struct dentry *ci_dentry; }; struct configfs_subsystem; struct config_group { struct config_item cg_item; struct list_head cg_children; struct configfs_subsystem *cg_subsys; struct list_head default_groups; struct list_head group_entry; }; struct configfs_item_operations; struct configfs_group_operations; struct configfs_attribute; struct configfs_bin_attribute; struct config_item_type { struct module *ct_owner; struct configfs_item_operations *ct_item_ops; struct configfs_group_operations *ct_group_ops; struct configfs_attribute **ct_attrs; struct configfs_bin_attribute **ct_bin_attrs; }; struct configfs_item_operations { void (*release)(struct config_item *); int (*allow_link)(struct config_item *, struct config_item *); void (*drop_link)(struct config_item *, struct config_item *); }; struct configfs_group_operations { struct config_item * (*make_item)(struct config_group *, const char *); struct config_group * (*make_group)(struct config_group *, const char *); int (*commit_item)(struct config_item *); void (*disconnect_notify)(struct config_group *, struct config_item *); void (*drop_item)(struct config_group *, struct config_item *); }; struct configfs_attribute { const char *ca_name; struct module *ca_owner; umode_t ca_mode; ssize_t (*show)(struct config_item *, char *); ssize_t (*store)(struct config_item *, const char *, size_t); }; struct configfs_bin_attribute { struct configfs_attribute cb_attr; void *cb_private; size_t cb_max_size; ssize_t (*read)(struct config_item *, void *, size_t); ssize_t (*write)(struct config_item *, const void *, size_t); }; struct configfs_subsystem { struct config_group su_group; struct mutex su_mutex; }; struct configfs_fragment { atomic_t frag_count; struct rw_semaphore frag_sem; bool frag_dead; }; struct configfs_dirent { atomic_t s_count; int s_dependent_count; struct list_head s_sibling; struct list_head s_children; int s_links; void *s_element; int s_type; umode_t s_mode; struct dentry *s_dentry; struct iattr *s_iattr; struct configfs_fragment *s_frag; }; struct configfs_buffer { size_t count; loff_t pos; char *page; struct configfs_item_operations *ops; struct mutex mutex; int needs_read_fill; bool read_in_progress; bool write_in_progress; char *bin_buffer; int bin_buffer_size; int cb_max_size; struct config_item *item; struct module *owner; union { struct configfs_attribute *attr; struct configfs_bin_attribute *bin_attr; }; }; struct pts_mount_opts { int setuid; int setgid; kuid_t uid; kgid_t gid; umode_t mode; umode_t ptmxmode; int reserve; int max; }; enum { Opt_uid___2 = 0, Opt_gid___3 = 1, Opt_mode___2 = 2, Opt_ptmxmode = 3, Opt_newinstance = 4, Opt_max = 5, Opt_err = 6, }; struct pts_fs_info { struct ida allocated_ptys; struct pts_mount_opts mount_opts; struct super_block *sb; struct dentry *ptmx_dentry; }; struct ramfs_mount_opts { umode_t mode; }; struct ramfs_fs_info { struct ramfs_mount_opts mount_opts; }; enum ramfs_param { Opt_mode___3 = 0, }; enum hugetlbfs_size_type { NO_SIZE = 0, SIZE_STD = 1, SIZE_PERCENT = 2, }; struct hugetlbfs_fs_context { struct hstate *hstate; long long unsigned int max_size_opt; long long unsigned int min_size_opt; long int max_hpages; long int nr_inodes; long int min_hpages; enum hugetlbfs_size_type max_val_type; enum hugetlbfs_size_type min_val_type; kuid_t uid; kgid_t gid; umode_t mode; }; enum hugetlb_param { Opt_gid___4 = 0, Opt_min_size = 1, Opt_mode___4 = 2, Opt_nr_inodes___2 = 3, Opt_pagesize = 4, Opt_size___2 = 5, Opt_uid___3 = 6, }; struct getdents_callback___2 { struct dir_context ctx; char *name; u64 ino; int found; int sequence; }; typedef u16 wchar_t; typedef u32 unicode_t; struct nls_table { const char *charset; const char *alias; int (*uni2char)(wchar_t, unsigned char *, int); int (*char2uni)(const unsigned char *, int, wchar_t *); const unsigned char *charset2lower; const unsigned char *charset2upper; struct module *owner; struct nls_table *next; }; enum utf16_endian { UTF16_HOST_ENDIAN = 0, UTF16_LITTLE_ENDIAN = 1, UTF16_BIG_ENDIAN = 2, }; struct utf8_table { int cmask; int cval; int shift; long int lmask; long int lval; }; struct utf8data; struct utf8cursor { const struct utf8data *data; const char *s; const char *p; const char *ss; const char *sp; unsigned int len; unsigned int slen; short int ccc; short int nccc; unsigned char hangul[12]; }; struct utf8data { unsigned int maxage; unsigned int offset; }; typedef const unsigned char utf8trie_t; typedef const unsigned char utf8leaf_t; typedef unsigned int autofs_wqt_t; struct autofs_sb_info; struct autofs_info { struct dentry *dentry; struct inode *inode; int flags; struct completion expire_complete; struct list_head active; struct list_head expiring; struct autofs_sb_info *sbi; long unsigned int last_used; int count; kuid_t uid; kgid_t gid; struct callback_head rcu; }; struct autofs_wait_queue; struct autofs_sb_info { u32 magic; int pipefd; struct file *pipe; struct pid *oz_pgrp; int version; int sub_version; int min_proto; int max_proto; unsigned int flags; long unsigned int exp_timeout; unsigned int type; struct super_block *sb; struct mutex wq_mutex; struct mutex pipe_mutex; spinlock_t fs_lock; struct autofs_wait_queue *queues; spinlock_t lookup_lock; struct list_head active_list; struct list_head expiring_list; struct callback_head rcu; }; struct autofs_wait_queue { wait_queue_head_t queue; struct autofs_wait_queue *next; autofs_wqt_t wait_queue_token; struct qstr name; u32 offset; u32 dev; u64 ino; kuid_t uid; kgid_t gid; pid_t pid; pid_t tgid; int status; unsigned int wait_ctr; }; enum { Opt_err___2 = 0, Opt_fd = 1, Opt_uid___4 = 2, Opt_gid___5 = 3, Opt_pgrp = 4, Opt_minproto = 5, Opt_maxproto = 6, Opt_indirect = 7, Opt_direct = 8, Opt_offset = 9, Opt_strictexpire = 10, Opt_ignore = 11, }; struct autofs_packet_hdr { int proto_version; int type; }; struct autofs_packet_expire { struct autofs_packet_hdr hdr; int len; char name[256]; }; enum { AUTOFS_IOC_READY_CMD = 96, AUTOFS_IOC_FAIL_CMD = 97, AUTOFS_IOC_CATATONIC_CMD = 98, AUTOFS_IOC_PROTOVER_CMD = 99, AUTOFS_IOC_SETTIMEOUT_CMD = 100, AUTOFS_IOC_EXPIRE_CMD = 101, }; enum autofs_notify { NFY_NONE = 0, NFY_MOUNT = 1, NFY_EXPIRE = 2, }; enum { AUTOFS_IOC_EXPIRE_MULTI_CMD = 102, AUTOFS_IOC_PROTOSUBVER_CMD = 103, AUTOFS_IOC_ASKUMOUNT_CMD = 112, }; struct autofs_packet_missing { struct autofs_packet_hdr hdr; autofs_wqt_t wait_queue_token; int len; char name[256]; }; struct autofs_packet_expire_multi { struct autofs_packet_hdr hdr; autofs_wqt_t wait_queue_token; int len; char name[256]; }; union autofs_packet_union { struct autofs_packet_hdr hdr; struct autofs_packet_missing missing; struct autofs_packet_expire expire; struct autofs_packet_expire_multi expire_multi; }; struct autofs_v5_packet { struct autofs_packet_hdr hdr; autofs_wqt_t wait_queue_token; __u32 dev; __u64 ino; __u32 uid; __u32 gid; __u32 pid; __u32 tgid; __u32 len; char name[256]; }; typedef struct autofs_v5_packet autofs_packet_missing_indirect_t; typedef struct autofs_v5_packet autofs_packet_expire_indirect_t; typedef struct autofs_v5_packet autofs_packet_missing_direct_t; typedef struct autofs_v5_packet autofs_packet_expire_direct_t; union autofs_v5_packet_union { struct autofs_packet_hdr hdr; struct autofs_v5_packet v5_packet; autofs_packet_missing_indirect_t missing_indirect; autofs_packet_expire_indirect_t expire_indirect; autofs_packet_missing_direct_t missing_direct; autofs_packet_expire_direct_t expire_direct; }; struct args_protover { __u32 version; }; struct args_protosubver { __u32 sub_version; }; struct args_openmount { __u32 devid; }; struct args_ready { __u32 token; }; struct args_fail { __u32 token; __s32 status; }; struct args_setpipefd { __s32 pipefd; }; struct args_timeout { __u64 timeout; }; struct args_requester { __u32 uid; __u32 gid; }; struct args_expire { __u32 how; }; struct args_askumount { __u32 may_umount; }; struct args_in { __u32 type; }; struct args_out { __u32 devid; __u32 magic; }; struct args_ismountpoint { union { struct args_in in; struct args_out out; }; }; struct autofs_dev_ioctl { __u32 ver_major; __u32 ver_minor; __u32 size; __s32 ioctlfd; union { struct args_protover protover; struct args_protosubver protosubver; struct args_openmount openmount; struct args_ready ready; struct args_fail fail; struct args_setpipefd setpipefd; struct args_timeout timeout; struct args_requester requester; struct args_expire expire; struct args_askumount askumount; struct args_ismountpoint ismountpoint; }; char path[0]; }; enum { AUTOFS_DEV_IOCTL_VERSION_CMD = 113, AUTOFS_DEV_IOCTL_PROTOVER_CMD = 114, AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD = 115, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD = 116, AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD = 117, AUTOFS_DEV_IOCTL_READY_CMD = 118, AUTOFS_DEV_IOCTL_FAIL_CMD = 119, AUTOFS_DEV_IOCTL_SETPIPEFD_CMD = 120, AUTOFS_DEV_IOCTL_CATATONIC_CMD = 121, AUTOFS_DEV_IOCTL_TIMEOUT_CMD = 122, AUTOFS_DEV_IOCTL_REQUESTER_CMD = 123, AUTOFS_DEV_IOCTL_EXPIRE_CMD = 124, AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD = 125, AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD = 126, }; typedef int (*ioctl_fn)(struct file *, struct autofs_sb_info *, struct autofs_dev_ioctl *); struct debugfs_fsdata { const struct file_operations *real_fops; refcount_t active_users; struct completion active_users_drained; }; struct debugfs_mount_opts { kuid_t uid; kgid_t gid; umode_t mode; }; enum { Opt_uid___5 = 0, Opt_gid___6 = 1, Opt_mode___5 = 2, Opt_err___3 = 3, }; struct debugfs_fs_info { struct debugfs_mount_opts mount_opts; }; struct debugfs_reg32 { char *name; long unsigned int offset; }; struct debugfs_regset32 { const struct debugfs_reg32 *regs; int nregs; void *base; struct device *dev; }; struct debugfs_devm_entry { int (*read)(struct seq_file *, void *); struct device *dev; }; struct tracefs_dir_ops { int (*mkdir)(const char *); int (*rmdir)(const char *); }; struct tracefs_mount_opts { kuid_t uid; kgid_t gid; umode_t mode; }; struct tracefs_fs_info { struct tracefs_mount_opts mount_opts; }; enum pstore_type_id { PSTORE_TYPE_DMESG = 0, PSTORE_TYPE_MCE = 1, PSTORE_TYPE_CONSOLE = 2, PSTORE_TYPE_FTRACE = 3, PSTORE_TYPE_PPC_RTAS = 4, PSTORE_TYPE_PPC_OF = 5, PSTORE_TYPE_PPC_COMMON = 6, PSTORE_TYPE_PMSG = 7, PSTORE_TYPE_PPC_OPAL = 8, PSTORE_TYPE_MAX = 9, }; struct pstore_info; struct pstore_record { struct pstore_info *psi; enum pstore_type_id type; u64 id; struct timespec64 time; char *buf; ssize_t size; ssize_t ecc_notice_size; int count; enum kmsg_dump_reason reason; unsigned int part; bool compressed; }; struct pstore_info { struct module *owner; const char *name; struct semaphore buf_lock; char *buf; size_t bufsize; struct mutex read_mutex; int flags; int max_reason; void *data; int (*open)(struct pstore_info *); int (*close)(struct pstore_info *); ssize_t (*read)(struct pstore_record *); int (*write)(struct pstore_record *); int (*write_user)(struct pstore_record *, const char *); int (*erase)(struct pstore_record *); }; struct pstore_ftrace_record { long unsigned int ip; long unsigned int parent_ip; u64 ts; }; struct pstore_private { struct list_head list; struct dentry *dentry; struct pstore_record *record; size_t total_size; }; struct pstore_ftrace_seq_data { const void *ptr; size_t off; size_t size; }; enum { Opt_kmsg_bytes = 0, Opt_err___4 = 1, }; struct crypto_comp { struct crypto_tfm base; }; struct pstore_zbackend { int (*zbufsize)(size_t); const char *name; }; struct efi_variable { efi_char16_t VariableName[512]; efi_guid_t VendorGuid; long unsigned int DataSize; __u8 Data[1024]; efi_status_t Status; __u32 Attributes; } __attribute__((packed)); struct efivar_entry { struct efi_variable var; struct list_head list; struct kobject kobj; bool scanning; bool deleting; }; typedef unsigned int __kernel_mode_t; struct ipc64_perm { __kernel_key_t key; __kernel_uid32_t uid; __kernel_gid32_t gid; __kernel_uid32_t cuid; __kernel_gid32_t cgid; __kernel_mode_t mode; unsigned char __pad1[0]; short unsigned int seq; short unsigned int __pad2; __kernel_ulong_t __unused1; __kernel_ulong_t __unused2; }; typedef s32 compat_key_t; typedef u32 __compat_gid32_t; struct compat_ipc64_perm { compat_key_t key; __compat_uid32_t uid; __compat_gid32_t gid; __compat_uid32_t cuid; __compat_gid32_t cgid; short unsigned int mode; short unsigned int __pad1; short unsigned int seq; short unsigned int __pad2; compat_ulong_t unused1; compat_ulong_t unused2; }; struct compat_ipc_perm { key_t key; __compat_uid_t uid; __compat_gid_t gid; __compat_uid_t cuid; __compat_gid_t cgid; compat_mode_t mode; short unsigned int seq; }; struct ipc_perm { __kernel_key_t key; __kernel_uid_t uid; __kernel_gid_t gid; __kernel_uid_t cuid; __kernel_gid_t cgid; __kernel_mode_t mode; short unsigned int seq; }; struct ipc_params { key_t key; int flg; union { size_t size; int nsems; } u; }; struct ipc_ops { int (*getnew)(struct ipc_namespace *, struct ipc_params *); int (*associate)(struct kern_ipc_perm *, int); int (*more_checks)(struct kern_ipc_perm *, struct ipc_params *); }; struct ipc_proc_iface { const char *path; const char *header; int ids; int (*show)(struct seq_file *, void *); }; struct ipc_proc_iter { struct ipc_namespace *ns; struct pid_namespace *pid_ns; struct ipc_proc_iface *iface; }; struct msg_msgseg; struct msg_msg { struct list_head m_list; long int m_type; size_t m_ts; struct msg_msgseg *next; void *security; }; struct msg_msgseg { struct msg_msgseg *next; }; typedef int __kernel_ipc_pid_t; struct msgbuf { __kernel_long_t mtype; char mtext[1]; }; struct msg; struct msqid_ds { struct ipc_perm msg_perm; struct msg *msg_first; struct msg *msg_last; __kernel_old_time_t msg_stime; __kernel_old_time_t msg_rtime; __kernel_old_time_t msg_ctime; long unsigned int msg_lcbytes; long unsigned int msg_lqbytes; short unsigned int msg_cbytes; short unsigned int msg_qnum; short unsigned int msg_qbytes; __kernel_ipc_pid_t msg_lspid; __kernel_ipc_pid_t msg_lrpid; }; struct msqid64_ds { struct ipc64_perm msg_perm; long int msg_stime; long int msg_rtime; long int msg_ctime; long unsigned int msg_cbytes; long unsigned int msg_qnum; long unsigned int msg_qbytes; __kernel_pid_t msg_lspid; __kernel_pid_t msg_lrpid; long unsigned int __unused4; long unsigned int __unused5; }; struct msginfo { int msgpool; int msgmap; int msgmax; int msgmnb; int msgmni; int msgssz; int msgtql; short unsigned int msgseg; }; typedef u16 compat_ipc_pid_t; struct compat_msqid64_ds { struct compat_ipc64_perm msg_perm; compat_ulong_t msg_stime; compat_ulong_t msg_stime_high; compat_ulong_t msg_rtime; compat_ulong_t msg_rtime_high; compat_ulong_t msg_ctime; compat_ulong_t msg_ctime_high; compat_ulong_t msg_cbytes; compat_ulong_t msg_qnum; compat_ulong_t msg_qbytes; compat_pid_t msg_lspid; compat_pid_t msg_lrpid; compat_ulong_t __unused4; compat_ulong_t __unused5; }; struct msg_queue { struct kern_ipc_perm q_perm; time64_t q_stime; time64_t q_rtime; time64_t q_ctime; long unsigned int q_cbytes; long unsigned int q_qnum; long unsigned int q_qbytes; struct pid *q_lspid; struct pid *q_lrpid; struct list_head q_messages; struct list_head q_receivers; struct list_head q_senders; long: 64; long: 64; }; struct msg_receiver { struct list_head r_list; struct task_struct *r_tsk; int r_mode; long int r_msgtype; long int r_maxsize; struct msg_msg *r_msg; }; struct msg_sender { struct list_head list; struct task_struct *tsk; size_t msgsz; }; struct compat_msqid_ds { struct compat_ipc_perm msg_perm; compat_uptr_t msg_first; compat_uptr_t msg_last; old_time32_t msg_stime; old_time32_t msg_rtime; old_time32_t msg_ctime; compat_ulong_t msg_lcbytes; compat_ulong_t msg_lqbytes; short unsigned int msg_cbytes; short unsigned int msg_qnum; short unsigned int msg_qbytes; compat_ipc_pid_t msg_lspid; compat_ipc_pid_t msg_lrpid; }; struct compat_msgbuf { compat_long_t mtype; char mtext[1]; }; struct sem; struct sem_queue; struct sem_undo; struct semid_ds { struct ipc_perm sem_perm; __kernel_old_time_t sem_otime; __kernel_old_time_t sem_ctime; struct sem *sem_base; struct sem_queue *sem_pending; struct sem_queue **sem_pending_last; struct sem_undo *undo; short unsigned int sem_nsems; }; struct sem { int semval; struct pid *sempid; spinlock_t lock; struct list_head pending_alter; struct list_head pending_const; time64_t sem_otime; }; struct sem_queue { struct list_head list; struct task_struct *sleeper; struct sem_undo *undo; struct pid *pid; int status; struct sembuf *sops; struct sembuf *blocking; int nsops; bool alter; bool dupsop; }; struct sem_undo { struct list_head list_proc; struct callback_head rcu; struct sem_undo_list *ulp; struct list_head list_id; int semid; short int *semadj; }; struct semid64_ds { struct ipc64_perm sem_perm; __kernel_long_t sem_otime; __kernel_ulong_t __unused1; __kernel_long_t sem_ctime; __kernel_ulong_t __unused2; __kernel_ulong_t sem_nsems; __kernel_ulong_t __unused3; __kernel_ulong_t __unused4; }; struct seminfo { int semmap; int semmni; int semmns; int semmnu; int semmsl; int semopm; int semume; int semusz; int semvmx; int semaem; }; struct sem_undo_list { refcount_t refcnt; spinlock_t lock; struct list_head list_proc; }; struct compat_semid64_ds { struct compat_ipc64_perm sem_perm; compat_ulong_t sem_otime; compat_ulong_t sem_otime_high; compat_ulong_t sem_ctime; compat_ulong_t sem_ctime_high; compat_ulong_t sem_nsems; compat_ulong_t __unused3; compat_ulong_t __unused4; }; struct sem_array { struct kern_ipc_perm sem_perm; time64_t sem_ctime; struct list_head pending_alter; struct list_head pending_const; struct list_head list_id; int sem_nsems; int complex_count; unsigned int use_global_lock; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; struct sem sems[0]; }; struct compat_semid_ds { struct compat_ipc_perm sem_perm; old_time32_t sem_otime; old_time32_t sem_ctime; compat_uptr_t sem_base; compat_uptr_t sem_pending; compat_uptr_t sem_pending_last; compat_uptr_t undo; short unsigned int sem_nsems; }; struct shmid_ds { struct ipc_perm shm_perm; int shm_segsz; __kernel_old_time_t shm_atime; __kernel_old_time_t shm_dtime; __kernel_old_time_t shm_ctime; __kernel_ipc_pid_t shm_cpid; __kernel_ipc_pid_t shm_lpid; short unsigned int shm_nattch; short unsigned int shm_unused; void *shm_unused2; void *shm_unused3; }; struct shmid64_ds { struct ipc64_perm shm_perm; size_t shm_segsz; long int shm_atime; long int shm_dtime; long int shm_ctime; __kernel_pid_t shm_cpid; __kernel_pid_t shm_lpid; long unsigned int shm_nattch; long unsigned int __unused4; long unsigned int __unused5; }; struct shminfo64 { long unsigned int shmmax; long unsigned int shmmin; long unsigned int shmmni; long unsigned int shmseg; long unsigned int shmall; long unsigned int __unused1; long unsigned int __unused2; long unsigned int __unused3; long unsigned int __unused4; }; struct shminfo { int shmmax; int shmmin; int shmmni; int shmseg; int shmall; }; struct shm_info { int used_ids; __kernel_ulong_t shm_tot; __kernel_ulong_t shm_rss; __kernel_ulong_t shm_swp; __kernel_ulong_t swap_attempts; __kernel_ulong_t swap_successes; }; struct compat_shmid64_ds { struct compat_ipc64_perm shm_perm; compat_size_t shm_segsz; compat_ulong_t shm_atime; compat_ulong_t shm_atime_high; compat_ulong_t shm_dtime; compat_ulong_t shm_dtime_high; compat_ulong_t shm_ctime; compat_ulong_t shm_ctime_high; compat_pid_t shm_cpid; compat_pid_t shm_lpid; compat_ulong_t shm_nattch; compat_ulong_t __unused4; compat_ulong_t __unused5; }; struct shmid_kernel { struct kern_ipc_perm shm_perm; struct file *shm_file; long unsigned int shm_nattch; long unsigned int shm_segsz; time64_t shm_atim; time64_t shm_dtim; time64_t shm_ctim; struct pid *shm_cprid; struct pid *shm_lprid; struct ucounts *mlock_ucounts; struct task_struct *shm_creator; struct list_head shm_clist; long: 64; long: 64; long: 64; long: 64; }; struct shm_file_data { int id; struct ipc_namespace *ns; struct file *file; const struct vm_operations_struct *vm_ops; }; struct compat_shmid_ds { struct compat_ipc_perm shm_perm; int shm_segsz; old_time32_t shm_atime; old_time32_t shm_dtime; old_time32_t shm_ctime; compat_ipc_pid_t shm_cpid; compat_ipc_pid_t shm_lpid; short unsigned int shm_nattch; short unsigned int shm_unused; compat_uptr_t shm_unused2; compat_uptr_t shm_unused3; }; struct compat_shminfo64 { compat_ulong_t shmmax; compat_ulong_t shmmin; compat_ulong_t shmmni; compat_ulong_t shmseg; compat_ulong_t shmall; compat_ulong_t __unused1; compat_ulong_t __unused2; compat_ulong_t __unused3; compat_ulong_t __unused4; }; struct compat_shm_info { compat_int_t used_ids; compat_ulong_t shm_tot; compat_ulong_t shm_rss; compat_ulong_t shm_swp; compat_ulong_t swap_attempts; compat_ulong_t swap_successes; }; struct compat_ipc_kludge { compat_uptr_t msgp; compat_long_t msgtyp; }; struct mqueue_fs_context { struct ipc_namespace *ipc_ns; }; struct posix_msg_tree_node { struct rb_node rb_node; struct list_head msg_list; int priority; }; struct ext_wait_queue { struct task_struct *task; struct list_head list; struct msg_msg *msg; int state; }; struct mqueue_inode_info { spinlock_t lock; struct inode vfs_inode; wait_queue_head_t wait_q; struct rb_root msg_tree; struct rb_node *msg_tree_rightmost; struct posix_msg_tree_node *node_cache; struct mq_attr attr; struct sigevent notify; struct pid *notify_owner; u32 notify_self_exec_id; struct user_namespace *notify_user_ns; struct ucounts *ucounts; struct sock *notify_sock; struct sk_buff *notify_cookie; struct ext_wait_queue e_wait_q[2]; long unsigned int qsize; }; struct compat_mq_attr { compat_long_t mq_flags; compat_long_t mq_maxmsg; compat_long_t mq_msgsize; compat_long_t mq_curmsgs; compat_long_t __reserved[4]; }; struct key_user { struct rb_node node; struct mutex cons_lock; spinlock_t lock; refcount_t usage; atomic_t nkeys; atomic_t nikeys; kuid_t uid; int qnkeys; int qnbytes; }; enum key_notification_subtype { NOTIFY_KEY_INSTANTIATED = 0, NOTIFY_KEY_UPDATED = 1, NOTIFY_KEY_LINKED = 2, NOTIFY_KEY_UNLINKED = 3, NOTIFY_KEY_CLEARED = 4, NOTIFY_KEY_REVOKED = 5, NOTIFY_KEY_INVALIDATED = 6, NOTIFY_KEY_SETATTR = 7, }; struct key_notification { struct watch_notification watch; __u32 key_id; __u32 aux; }; struct assoc_array_edit; struct assoc_array_ops { long unsigned int (*get_key_chunk)(const void *, int); long unsigned int (*get_object_key_chunk)(const void *, int); bool (*compare_object)(const void *, const void *); int (*diff_objects)(const void *, const void *); void (*free_object)(void *); }; struct assoc_array_node { struct assoc_array_ptr *back_pointer; u8 parent_slot; struct assoc_array_ptr *slots[16]; long unsigned int nr_leaves_on_branch; }; struct assoc_array_shortcut { struct assoc_array_ptr *back_pointer; int parent_slot; int skip_to_level; struct assoc_array_ptr *next_node; long unsigned int index_key[0]; }; struct assoc_array_edit___2 { struct callback_head rcu; struct assoc_array *array; const struct assoc_array_ops *ops; const struct assoc_array_ops *ops_for_excised_subtree; struct assoc_array_ptr *leaf; struct assoc_array_ptr **leaf_p; struct assoc_array_ptr *dead_leaf; struct assoc_array_ptr *new_meta[3]; struct assoc_array_ptr *excised_meta[1]; struct assoc_array_ptr *excised_subtree; struct assoc_array_ptr **set_backpointers[16]; struct assoc_array_ptr *set_backpointers_to; struct assoc_array_node *adjust_count_on; long int adjust_count_by; struct { struct assoc_array_ptr **ptr; struct assoc_array_ptr *to; } set[2]; struct { u8 *p; u8 to; } set_parent_slot[1]; u8 segment_cache[17]; }; struct keyring_search_context { struct keyring_index_key index_key; const struct cred *cred; struct key_match_data match_data; unsigned int flags; int (*iterator)(const void *, void *); int skipped_ret; bool possessed; key_ref_t result; time64_t now; }; struct keyring_read_iterator_context { size_t buflen; size_t count; key_serial_t *buffer; }; struct keyctl_dh_params { union { __s32 private; __s32 priv; }; __s32 prime; __s32 base; }; struct keyctl_kdf_params { char *hashname; char *otherinfo; __u32 otherinfolen; __u32 __spare[8]; }; struct keyctl_pkey_query { __u32 supported_ops; __u32 key_size; __u16 max_data_size; __u16 max_sig_size; __u16 max_enc_size; __u16 max_dec_size; __u32 __spare[10]; }; struct keyctl_pkey_params { __s32 key_id; __u32 in_len; union { __u32 out_len; __u32 in2_len; }; __u32 __spare[7]; }; struct request_key_auth { struct callback_head rcu; struct key *target_key; struct key *dest_keyring; const struct cred *cred; void *callout_info; size_t callout_len; pid_t pid; char op[8]; }; struct compat_keyctl_kdf_params { compat_uptr_t hashname; compat_uptr_t otherinfo; __u32 otherinfolen; __u32 __spare[8]; }; struct kpp_request { struct crypto_async_request base; struct scatterlist *src; struct scatterlist *dst; unsigned int src_len; unsigned int dst_len; void *__ctx[0]; }; struct crypto_kpp { struct crypto_tfm base; }; struct kpp_alg { int (*set_secret)(struct crypto_kpp *, const void *, unsigned int); int (*generate_public_key)(struct kpp_request *); int (*compute_shared_secret)(struct kpp_request *); unsigned int (*max_size)(struct crypto_kpp *); int (*init)(struct crypto_kpp *); void (*exit)(struct crypto_kpp *); unsigned int reqsize; struct crypto_alg base; }; struct dh { void *key; void *p; void *q; void *g; unsigned int key_size; unsigned int p_size; unsigned int q_size; unsigned int g_size; }; struct dh_completion { struct completion completion; int err; }; struct kdf_sdesc { struct shash_desc shash; char ctx[0]; }; enum { Opt_err___5 = 0, Opt_enc = 1, Opt_hash = 2, }; struct vfs_cap_data { __le32 magic_etc; struct { __le32 permitted; __le32 inheritable; } data[2]; }; struct vfs_ns_cap_data { __le32 magic_etc; struct { __le32 permitted; __le32 inheritable; } data[2]; __le32 rootid; }; struct sctp_endpoint; union security_list_options { int (*binder_set_context_mgr)(struct task_struct *); int (*binder_transaction)(struct task_struct *, struct task_struct *); int (*binder_transfer_binder)(struct task_struct *, struct task_struct *); int (*binder_transfer_file)(struct task_struct *, struct task_struct *, struct file *); int (*ptrace_access_check)(struct task_struct *, unsigned int); int (*ptrace_traceme)(struct task_struct *); int (*capget)(struct task_struct *, kernel_cap_t *, kernel_cap_t *, kernel_cap_t *); int (*capset)(struct cred *, const struct cred *, const kernel_cap_t *, const kernel_cap_t *, const kernel_cap_t *); int (*capable)(const struct cred *, struct user_namespace *, int, unsigned int); int (*quotactl)(int, int, int, struct super_block *); int (*quota_on)(struct dentry *); int (*syslog)(int); int (*settime)(const struct timespec64 *, const struct timezone *); int (*vm_enough_memory)(struct mm_struct *, long int); int (*bprm_creds_for_exec)(struct linux_binprm *); int (*bprm_creds_from_file)(struct linux_binprm *, struct file *); int (*bprm_check_security)(struct linux_binprm *); void (*bprm_committing_creds)(struct linux_binprm *); void (*bprm_committed_creds)(struct linux_binprm *); int (*fs_context_dup)(struct fs_context *, struct fs_context *); int (*fs_context_parse_param)(struct fs_context *, struct fs_parameter *); int (*sb_alloc_security)(struct super_block *); void (*sb_delete)(struct super_block *); void (*sb_free_security)(struct super_block *); void (*sb_free_mnt_opts)(void *); int (*sb_eat_lsm_opts)(char *, void **); int (*sb_mnt_opts_compat)(struct super_block *, void *); int (*sb_remount)(struct super_block *, void *); int (*sb_kern_mount)(struct super_block *); int (*sb_show_options)(struct seq_file *, struct super_block *); int (*sb_statfs)(struct dentry *); int (*sb_mount)(const char *, const struct path *, const char *, long unsigned int, void *); int (*sb_umount)(struct vfsmount *, int); int (*sb_pivotroot)(const struct path *, const struct path *); int (*sb_set_mnt_opts)(struct super_block *, void *, long unsigned int, long unsigned int *); int (*sb_clone_mnt_opts)(const struct super_block *, struct super_block *, long unsigned int, long unsigned int *); int (*sb_add_mnt_opt)(const char *, const char *, int, void **); int (*move_mount)(const struct path *, const struct path *); int (*dentry_init_security)(struct dentry *, int, const struct qstr *, void **, u32 *); int (*dentry_create_files_as)(struct dentry *, int, struct qstr *, const struct cred *, struct cred *); int (*path_unlink)(const struct path *, struct dentry *); int (*path_mkdir)(const struct path *, struct dentry *, umode_t); int (*path_rmdir)(const struct path *, struct dentry *); int (*path_mknod)(const struct path *, struct dentry *, umode_t, unsigned int); int (*path_truncate)(const struct path *); int (*path_symlink)(const struct path *, struct dentry *, const char *); int (*path_link)(struct dentry *, const struct path *, struct dentry *); int (*path_rename)(const struct path *, struct dentry *, const struct path *, struct dentry *); int (*path_chmod)(const struct path *, umode_t); int (*path_chown)(const struct path *, kuid_t, kgid_t); int (*path_chroot)(const struct path *); int (*path_notify)(const struct path *, u64, unsigned int); int (*inode_alloc_security)(struct inode *); void (*inode_free_security)(struct inode *); int (*inode_init_security)(struct inode *, struct inode *, const struct qstr *, const char **, void **, size_t *); int (*inode_init_security_anon)(struct inode *, const struct qstr *, const struct inode *); int (*inode_create)(struct inode *, struct dentry *, umode_t); int (*inode_link)(struct dentry *, struct inode *, struct dentry *); int (*inode_unlink)(struct inode *, struct dentry *); int (*inode_symlink)(struct inode *, struct dentry *, const char *); int (*inode_mkdir)(struct inode *, struct dentry *, umode_t); int (*inode_rmdir)(struct inode *, struct dentry *); int (*inode_mknod)(struct inode *, struct dentry *, umode_t, dev_t); int (*inode_rename)(struct inode *, struct dentry *, struct inode *, struct dentry *); int (*inode_readlink)(struct dentry *); int (*inode_follow_link)(struct dentry *, struct inode *, bool); int (*inode_permission)(struct inode *, int); int (*inode_setattr)(struct dentry *, struct iattr *); int (*inode_getattr)(const struct path *); int (*inode_setxattr)(struct user_namespace *, struct dentry *, const char *, const void *, size_t, int); void (*inode_post_setxattr)(struct dentry *, const char *, const void *, size_t, int); int (*inode_getxattr)(struct dentry *, const char *); int (*inode_listxattr)(struct dentry *); int (*inode_removexattr)(struct user_namespace *, struct dentry *, const char *); int (*inode_need_killpriv)(struct dentry *); int (*inode_killpriv)(struct user_namespace *, struct dentry *); int (*inode_getsecurity)(struct user_namespace *, struct inode *, const char *, void **, bool); int (*inode_setsecurity)(struct inode *, const char *, const void *, size_t, int); int (*inode_listsecurity)(struct inode *, char *, size_t); void (*inode_getsecid)(struct inode *, u32 *); int (*inode_copy_up)(struct dentry *, struct cred **); int (*inode_copy_up_xattr)(const char *); int (*kernfs_init_security)(struct kernfs_node *, struct kernfs_node *); int (*file_permission)(struct file *, int); int (*file_alloc_security)(struct file *); void (*file_free_security)(struct file *); int (*file_ioctl)(struct file *, unsigned int, long unsigned int); int (*mmap_addr)(long unsigned int); int (*mmap_file)(struct file *, long unsigned int, long unsigned int, long unsigned int); int (*file_mprotect)(struct vm_area_struct *, long unsigned int, long unsigned int); int (*file_lock)(struct file *, unsigned int); int (*file_fcntl)(struct file *, unsigned int, long unsigned int); void (*file_set_fowner)(struct file *); int (*file_send_sigiotask)(struct task_struct *, struct fown_struct *, int); int (*file_receive)(struct file *); int (*file_open)(struct file *); int (*task_alloc)(struct task_struct *, long unsigned int); void (*task_free)(struct task_struct *); int (*cred_alloc_blank)(struct cred *, gfp_t); void (*cred_free)(struct cred *); int (*cred_prepare)(struct cred *, const struct cred *, gfp_t); void (*cred_transfer)(struct cred *, const struct cred *); void (*cred_getsecid)(const struct cred *, u32 *); int (*kernel_act_as)(struct cred *, u32); int (*kernel_create_files_as)(struct cred *, struct inode *); int (*kernel_module_request)(char *); int (*kernel_load_data)(enum kernel_load_data_id, bool); int (*kernel_post_load_data)(char *, loff_t, enum kernel_load_data_id, char *); int (*kernel_read_file)(struct file *, enum kernel_read_file_id, bool); int (*kernel_post_read_file)(struct file *, char *, loff_t, enum kernel_read_file_id); int (*task_fix_setuid)(struct cred *, const struct cred *, int); int (*task_fix_setgid)(struct cred *, const struct cred *, int); int (*task_setpgid)(struct task_struct *, pid_t); int (*task_getpgid)(struct task_struct *); int (*task_getsid)(struct task_struct *); void (*task_getsecid_subj)(struct task_struct *, u32 *); void (*task_getsecid_obj)(struct task_struct *, u32 *); int (*task_setnice)(struct task_struct *, int); int (*task_setioprio)(struct task_struct *, int); int (*task_getioprio)(struct task_struct *); int (*task_prlimit)(const struct cred *, const struct cred *, unsigned int); int (*task_setrlimit)(struct task_struct *, unsigned int, struct rlimit *); int (*task_setscheduler)(struct task_struct *); int (*task_getscheduler)(struct task_struct *); int (*task_movememory)(struct task_struct *); int (*task_kill)(struct task_struct *, struct kernel_siginfo *, int, const struct cred *); int (*task_prctl)(int, long unsigned int, long unsigned int, long unsigned int, long unsigned int); void (*task_to_inode)(struct task_struct *, struct inode *); int (*ipc_permission)(struct kern_ipc_perm *, short int); void (*ipc_getsecid)(struct kern_ipc_perm *, u32 *); int (*msg_msg_alloc_security)(struct msg_msg *); void (*msg_msg_free_security)(struct msg_msg *); int (*msg_queue_alloc_security)(struct kern_ipc_perm *); void (*msg_queue_free_security)(struct kern_ipc_perm *); int (*msg_queue_associate)(struct kern_ipc_perm *, int); int (*msg_queue_msgctl)(struct kern_ipc_perm *, int); int (*msg_queue_msgsnd)(struct kern_ipc_perm *, struct msg_msg *, int); int (*msg_queue_msgrcv)(struct kern_ipc_perm *, struct msg_msg *, struct task_struct *, long int, int); int (*shm_alloc_security)(struct kern_ipc_perm *); void (*shm_free_security)(struct kern_ipc_perm *); int (*shm_associate)(struct kern_ipc_perm *, int); int (*shm_shmctl)(struct kern_ipc_perm *, int); int (*shm_shmat)(struct kern_ipc_perm *, char *, int); int (*sem_alloc_security)(struct kern_ipc_perm *); void (*sem_free_security)(struct kern_ipc_perm *); int (*sem_associate)(struct kern_ipc_perm *, int); int (*sem_semctl)(struct kern_ipc_perm *, int); int (*sem_semop)(struct kern_ipc_perm *, struct sembuf *, unsigned int, int); int (*netlink_send)(struct sock *, struct sk_buff *); void (*d_instantiate)(struct dentry *, struct inode *); int (*getprocattr)(struct task_struct *, char *, char **); int (*setprocattr)(const char *, void *, size_t); int (*ismaclabel)(const char *); int (*secid_to_secctx)(u32, char **, u32 *); int (*secctx_to_secid)(const char *, u32, u32 *); void (*release_secctx)(char *, u32); void (*inode_invalidate_secctx)(struct inode *); int (*inode_notifysecctx)(struct inode *, void *, u32); int (*inode_setsecctx)(struct dentry *, void *, u32); int (*inode_getsecctx)(struct inode *, void **, u32 *); int (*post_notification)(const struct cred *, const struct cred *, struct watch_notification *); int (*watch_key)(struct key *); int (*unix_stream_connect)(struct sock *, struct sock *, struct sock *); int (*unix_may_send)(struct socket *, struct socket *); int (*socket_create)(int, int, int, int); int (*socket_post_create)(struct socket *, int, int, int, int); int (*socket_socketpair)(struct socket *, struct socket *); int (*socket_bind)(struct socket *, struct sockaddr *, int); int (*socket_connect)(struct socket *, struct sockaddr *, int); int (*socket_listen)(struct socket *, int); int (*socket_accept)(struct socket *, struct socket *); int (*socket_sendmsg)(struct socket *, struct msghdr *, int); int (*socket_recvmsg)(struct socket *, struct msghdr *, int, int); int (*socket_getsockname)(struct socket *); int (*socket_getpeername)(struct socket *); int (*socket_getsockopt)(struct socket *, int, int); int (*socket_setsockopt)(struct socket *, int, int); int (*socket_shutdown)(struct socket *, int); int (*socket_sock_rcv_skb)(struct sock *, struct sk_buff *); int (*socket_getpeersec_stream)(struct socket *, char *, int *, unsigned int); int (*socket_getpeersec_dgram)(struct socket *, struct sk_buff *, u32 *); int (*sk_alloc_security)(struct sock *, int, gfp_t); void (*sk_free_security)(struct sock *); void (*sk_clone_security)(const struct sock *, struct sock *); void (*sk_getsecid)(struct sock *, u32 *); void (*sock_graft)(struct sock *, struct socket *); int (*inet_conn_request)(const struct sock *, struct sk_buff *, struct request_sock *); void (*inet_csk_clone)(struct sock *, const struct request_sock *); void (*inet_conn_established)(struct sock *, struct sk_buff *); int (*secmark_relabel_packet)(u32); void (*secmark_refcount_inc)(); void (*secmark_refcount_dec)(); void (*req_classify_flow)(const struct request_sock *, struct flowi_common *); int (*tun_dev_alloc_security)(void **); void (*tun_dev_free_security)(void *); int (*tun_dev_create)(); int (*tun_dev_attach_queue)(void *); int (*tun_dev_attach)(struct sock *, void *); int (*tun_dev_open)(void *); int (*sctp_assoc_request)(struct sctp_endpoint *, struct sk_buff *); int (*sctp_bind_connect)(struct sock *, int, struct sockaddr *, int); void (*sctp_sk_clone)(struct sctp_endpoint *, struct sock *, struct sock *); int (*ib_pkey_access)(void *, u64, u16); int (*ib_endport_manage_subnet)(void *, const char *, u8); int (*ib_alloc_security)(void **); void (*ib_free_security)(void *); int (*xfrm_policy_alloc_security)(struct xfrm_sec_ctx **, struct xfrm_user_sec_ctx *, gfp_t); int (*xfrm_policy_clone_security)(struct xfrm_sec_ctx *, struct xfrm_sec_ctx **); void (*xfrm_policy_free_security)(struct xfrm_sec_ctx *); int (*xfrm_policy_delete_security)(struct xfrm_sec_ctx *); int (*xfrm_state_alloc)(struct xfrm_state *, struct xfrm_user_sec_ctx *); int (*xfrm_state_alloc_acquire)(struct xfrm_state *, struct xfrm_sec_ctx *, u32); void (*xfrm_state_free_security)(struct xfrm_state *); int (*xfrm_state_delete_security)(struct xfrm_state *); int (*xfrm_policy_lookup)(struct xfrm_sec_ctx *, u32); int (*xfrm_state_pol_flow_match)(struct xfrm_state *, struct xfrm_policy *, const struct flowi_common *); int (*xfrm_decode_session)(struct sk_buff *, u32 *, int); int (*key_alloc)(struct key *, const struct cred *, long unsigned int); void (*key_free)(struct key *); int (*key_permission)(key_ref_t, const struct cred *, enum key_need_perm); int (*key_getsecurity)(struct key *, char **); int (*audit_rule_init)(u32, u32, char *, void **); int (*audit_rule_known)(struct audit_krule *); int (*audit_rule_match)(u32, u32, u32, void *); void (*audit_rule_free)(void *); int (*bpf)(int, union bpf_attr *, unsigned int); int (*bpf_map)(struct bpf_map *, fmode_t); int (*bpf_prog)(struct bpf_prog *); int (*bpf_map_alloc_security)(struct bpf_map *); void (*bpf_map_free_security)(struct bpf_map *); int (*bpf_prog_alloc_security)(struct bpf_prog_aux *); void (*bpf_prog_free_security)(struct bpf_prog_aux *); int (*locked_down)(enum lockdown_reason); int (*perf_event_open)(struct perf_event_attr *, int); int (*perf_event_alloc)(struct perf_event *); void (*perf_event_free)(struct perf_event *); int (*perf_event_read)(struct perf_event *); int (*perf_event_write)(struct perf_event *); }; struct security_hook_heads { struct hlist_head binder_set_context_mgr; struct hlist_head binder_transaction; struct hlist_head binder_transfer_binder; struct hlist_head binder_transfer_file; struct hlist_head ptrace_access_check; struct hlist_head ptrace_traceme; struct hlist_head capget; struct hlist_head capset; struct hlist_head capable; struct hlist_head quotactl; struct hlist_head quota_on; struct hlist_head syslog; struct hlist_head settime; struct hlist_head vm_enough_memory; struct hlist_head bprm_creds_for_exec; struct hlist_head bprm_creds_from_file; struct hlist_head bprm_check_security; struct hlist_head bprm_committing_creds; struct hlist_head bprm_committed_creds; struct hlist_head fs_context_dup; struct hlist_head fs_context_parse_param; struct hlist_head sb_alloc_security; struct hlist_head sb_delete; struct hlist_head sb_free_security; struct hlist_head sb_free_mnt_opts; struct hlist_head sb_eat_lsm_opts; struct hlist_head sb_mnt_opts_compat; struct hlist_head sb_remount; struct hlist_head sb_kern_mount; struct hlist_head sb_show_options; struct hlist_head sb_statfs; struct hlist_head sb_mount; struct hlist_head sb_umount; struct hlist_head sb_pivotroot; struct hlist_head sb_set_mnt_opts; struct hlist_head sb_clone_mnt_opts; struct hlist_head sb_add_mnt_opt; struct hlist_head move_mount; struct hlist_head dentry_init_security; struct hlist_head dentry_create_files_as; struct hlist_head path_unlink; struct hlist_head path_mkdir; struct hlist_head path_rmdir; struct hlist_head path_mknod; struct hlist_head path_truncate; struct hlist_head path_symlink; struct hlist_head path_link; struct hlist_head path_rename; struct hlist_head path_chmod; struct hlist_head path_chown; struct hlist_head path_chroot; struct hlist_head path_notify; struct hlist_head inode_alloc_security; struct hlist_head inode_free_security; struct hlist_head inode_init_security; struct hlist_head inode_init_security_anon; struct hlist_head inode_create; struct hlist_head inode_link; struct hlist_head inode_unlink; struct hlist_head inode_symlink; struct hlist_head inode_mkdir; struct hlist_head inode_rmdir; struct hlist_head inode_mknod; struct hlist_head inode_rename; struct hlist_head inode_readlink; struct hlist_head inode_follow_link; struct hlist_head inode_permission; struct hlist_head inode_setattr; struct hlist_head inode_getattr; struct hlist_head inode_setxattr; struct hlist_head inode_post_setxattr; struct hlist_head inode_getxattr; struct hlist_head inode_listxattr; struct hlist_head inode_removexattr; struct hlist_head inode_need_killpriv; struct hlist_head inode_killpriv; struct hlist_head inode_getsecurity; struct hlist_head inode_setsecurity; struct hlist_head inode_listsecurity; struct hlist_head inode_getsecid; struct hlist_head inode_copy_up; struct hlist_head inode_copy_up_xattr; struct hlist_head kernfs_init_security; struct hlist_head file_permission; struct hlist_head file_alloc_security; struct hlist_head file_free_security; struct hlist_head file_ioctl; struct hlist_head mmap_addr; struct hlist_head mmap_file; struct hlist_head file_mprotect; struct hlist_head file_lock; struct hlist_head file_fcntl; struct hlist_head file_set_fowner; struct hlist_head file_send_sigiotask; struct hlist_head file_receive; struct hlist_head file_open; struct hlist_head task_alloc; struct hlist_head task_free; struct hlist_head cred_alloc_blank; struct hlist_head cred_free; struct hlist_head cred_prepare; struct hlist_head cred_transfer; struct hlist_head cred_getsecid; struct hlist_head kernel_act_as; struct hlist_head kernel_create_files_as; struct hlist_head kernel_module_request; struct hlist_head kernel_load_data; struct hlist_head kernel_post_load_data; struct hlist_head kernel_read_file; struct hlist_head kernel_post_read_file; struct hlist_head task_fix_setuid; struct hlist_head task_fix_setgid; struct hlist_head task_setpgid; struct hlist_head task_getpgid; struct hlist_head task_getsid; struct hlist_head task_getsecid_subj; struct hlist_head task_getsecid_obj; struct hlist_head task_setnice; struct hlist_head task_setioprio; struct hlist_head task_getioprio; struct hlist_head task_prlimit; struct hlist_head task_setrlimit; struct hlist_head task_setscheduler; struct hlist_head task_getscheduler; struct hlist_head task_movememory; struct hlist_head task_kill; struct hlist_head task_prctl; struct hlist_head task_to_inode; struct hlist_head ipc_permission; struct hlist_head ipc_getsecid; struct hlist_head msg_msg_alloc_security; struct hlist_head msg_msg_free_security; struct hlist_head msg_queue_alloc_security; struct hlist_head msg_queue_free_security; struct hlist_head msg_queue_associate; struct hlist_head msg_queue_msgctl; struct hlist_head msg_queue_msgsnd; struct hlist_head msg_queue_msgrcv; struct hlist_head shm_alloc_security; struct hlist_head shm_free_security; struct hlist_head shm_associate; struct hlist_head shm_shmctl; struct hlist_head shm_shmat; struct hlist_head sem_alloc_security; struct hlist_head sem_free_security; struct hlist_head sem_associate; struct hlist_head sem_semctl; struct hlist_head sem_semop; struct hlist_head netlink_send; struct hlist_head d_instantiate; struct hlist_head getprocattr; struct hlist_head setprocattr; struct hlist_head ismaclabel; struct hlist_head secid_to_secctx; struct hlist_head secctx_to_secid; struct hlist_head release_secctx; struct hlist_head inode_invalidate_secctx; struct hlist_head inode_notifysecctx; struct hlist_head inode_setsecctx; struct hlist_head inode_getsecctx; struct hlist_head post_notification; struct hlist_head watch_key; struct hlist_head unix_stream_connect; struct hlist_head unix_may_send; struct hlist_head socket_create; struct hlist_head socket_post_create; struct hlist_head socket_socketpair; struct hlist_head socket_bind; struct hlist_head socket_connect; struct hlist_head socket_listen; struct hlist_head socket_accept; struct hlist_head socket_sendmsg; struct hlist_head socket_recvmsg; struct hlist_head socket_getsockname; struct hlist_head socket_getpeername; struct hlist_head socket_getsockopt; struct hlist_head socket_setsockopt; struct hlist_head socket_shutdown; struct hlist_head socket_sock_rcv_skb; struct hlist_head socket_getpeersec_stream; struct hlist_head socket_getpeersec_dgram; struct hlist_head sk_alloc_security; struct hlist_head sk_free_security; struct hlist_head sk_clone_security; struct hlist_head sk_getsecid; struct hlist_head sock_graft; struct hlist_head inet_conn_request; struct hlist_head inet_csk_clone; struct hlist_head inet_conn_established; struct hlist_head secmark_relabel_packet; struct hlist_head secmark_refcount_inc; struct hlist_head secmark_refcount_dec; struct hlist_head req_classify_flow; struct hlist_head tun_dev_alloc_security; struct hlist_head tun_dev_free_security; struct hlist_head tun_dev_create; struct hlist_head tun_dev_attach_queue; struct hlist_head tun_dev_attach; struct hlist_head tun_dev_open; struct hlist_head sctp_assoc_request; struct hlist_head sctp_bind_connect; struct hlist_head sctp_sk_clone; struct hlist_head ib_pkey_access; struct hlist_head ib_endport_manage_subnet; struct hlist_head ib_alloc_security; struct hlist_head ib_free_security; struct hlist_head xfrm_policy_alloc_security; struct hlist_head xfrm_policy_clone_security; struct hlist_head xfrm_policy_free_security; struct hlist_head xfrm_policy_delete_security; struct hlist_head xfrm_state_alloc; struct hlist_head xfrm_state_alloc_acquire; struct hlist_head xfrm_state_free_security; struct hlist_head xfrm_state_delete_security; struct hlist_head xfrm_policy_lookup; struct hlist_head xfrm_state_pol_flow_match; struct hlist_head xfrm_decode_session; struct hlist_head key_alloc; struct hlist_head key_free; struct hlist_head key_permission; struct hlist_head key_getsecurity; struct hlist_head audit_rule_init; struct hlist_head audit_rule_known; struct hlist_head audit_rule_match; struct hlist_head audit_rule_free; struct hlist_head bpf; struct hlist_head bpf_map; struct hlist_head bpf_prog; struct hlist_head bpf_map_alloc_security; struct hlist_head bpf_map_free_security; struct hlist_head bpf_prog_alloc_security; struct hlist_head bpf_prog_free_security; struct hlist_head locked_down; struct hlist_head perf_event_open; struct hlist_head perf_event_alloc; struct hlist_head perf_event_free; struct hlist_head perf_event_read; struct hlist_head perf_event_write; }; struct security_hook_list { struct hlist_node list; struct hlist_head *head; union security_list_options hook; char *lsm; }; enum lsm_order { LSM_ORDER_FIRST = 4294967295, LSM_ORDER_MUTABLE = 0, }; struct lsm_info { const char *name; enum lsm_order order; long unsigned int flags; int *enabled; int (*init)(); struct lsm_blob_sizes *blobs; }; enum lsm_event { LSM_POLICY_CHANGE = 0, }; struct ethhdr { unsigned char h_dest[6]; unsigned char h_source[6]; __be16 h_proto; }; struct ethtool_drvinfo { __u32 cmd; char driver[32]; char version[32]; char fw_version[32]; char bus_info[32]; char erom_version[32]; char reserved2[12]; __u32 n_priv_flags; __u32 n_stats; __u32 testinfo_len; __u32 eedump_len; __u32 regdump_len; }; struct ethtool_wolinfo { __u32 cmd; __u32 supported; __u32 wolopts; __u8 sopass[6]; }; struct ethtool_tunable { __u32 cmd; __u32 id; __u32 type_id; __u32 len; void *data[0]; }; struct ethtool_regs { __u32 cmd; __u32 version; __u32 len; __u8 data[0]; }; struct ethtool_eeprom { __u32 cmd; __u32 magic; __u32 offset; __u32 len; __u8 data[0]; }; struct ethtool_eee { __u32 cmd; __u32 supported; __u32 advertised; __u32 lp_advertised; __u32 eee_active; __u32 eee_enabled; __u32 tx_lpi_enabled; __u32 tx_lpi_timer; __u32 reserved[2]; }; struct ethtool_modinfo { __u32 cmd; __u32 type; __u32 eeprom_len; __u32 reserved[8]; }; struct ethtool_coalesce { __u32 cmd; __u32 rx_coalesce_usecs; __u32 rx_max_coalesced_frames; __u32 rx_coalesce_usecs_irq; __u32 rx_max_coalesced_frames_irq; __u32 tx_coalesce_usecs; __u32 tx_max_coalesced_frames; __u32 tx_coalesce_usecs_irq; __u32 tx_max_coalesced_frames_irq; __u32 stats_block_coalesce_usecs; __u32 use_adaptive_rx_coalesce; __u32 use_adaptive_tx_coalesce; __u32 pkt_rate_low; __u32 rx_coalesce_usecs_low; __u32 rx_max_coalesced_frames_low; __u32 tx_coalesce_usecs_low; __u32 tx_max_coalesced_frames_low; __u32 pkt_rate_high; __u32 rx_coalesce_usecs_high; __u32 rx_max_coalesced_frames_high; __u32 tx_coalesce_usecs_high; __u32 tx_max_coalesced_frames_high; __u32 rate_sample_interval; }; struct ethtool_ringparam { __u32 cmd; __u32 rx_max_pending; __u32 rx_mini_max_pending; __u32 rx_jumbo_max_pending; __u32 tx_max_pending; __u32 rx_pending; __u32 rx_mini_pending; __u32 rx_jumbo_pending; __u32 tx_pending; }; struct ethtool_channels { __u32 cmd; __u32 max_rx; __u32 max_tx; __u32 max_other; __u32 max_combined; __u32 rx_count; __u32 tx_count; __u32 other_count; __u32 combined_count; }; struct ethtool_pauseparam { __u32 cmd; __u32 autoneg; __u32 rx_pause; __u32 tx_pause; }; enum ethtool_link_ext_state { ETHTOOL_LINK_EXT_STATE_AUTONEG = 0, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE = 1, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH = 2, ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY = 3, ETHTOOL_LINK_EXT_STATE_NO_CABLE = 4, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE = 5, ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE = 6, ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE = 7, ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED = 8, ETHTOOL_LINK_EXT_STATE_OVERHEAT = 9, }; enum ethtool_link_ext_substate_autoneg { ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED = 1, ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED = 2, ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED = 3, ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE = 4, ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE = 5, ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD = 6, }; enum ethtool_link_ext_substate_link_training { ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED = 1, ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT = 2, ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY = 3, ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT = 4, }; enum ethtool_link_ext_substate_link_logical_mismatch { ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK = 1, ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK = 2, ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS = 3, ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED = 4, ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED = 5, }; enum ethtool_link_ext_substate_bad_signal_integrity { ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS = 1, ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE = 2, }; enum ethtool_link_ext_substate_cable_issue { ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE = 1, ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE = 2, }; struct ethtool_test { __u32 cmd; __u32 flags; __u32 reserved; __u32 len; __u64 data[0]; }; struct ethtool_stats { __u32 cmd; __u32 n_stats; __u64 data[0]; }; struct ethtool_tcpip4_spec { __be32 ip4src; __be32 ip4dst; __be16 psrc; __be16 pdst; __u8 tos; }; struct ethtool_ah_espip4_spec { __be32 ip4src; __be32 ip4dst; __be32 spi; __u8 tos; }; struct ethtool_usrip4_spec { __be32 ip4src; __be32 ip4dst; __be32 l4_4_bytes; __u8 tos; __u8 ip_ver; __u8 proto; }; struct ethtool_tcpip6_spec { __be32 ip6src[4]; __be32 ip6dst[4]; __be16 psrc; __be16 pdst; __u8 tclass; }; struct ethtool_ah_espip6_spec { __be32 ip6src[4]; __be32 ip6dst[4]; __be32 spi; __u8 tclass; }; struct ethtool_usrip6_spec { __be32 ip6src[4]; __be32 ip6dst[4]; __be32 l4_4_bytes; __u8 tclass; __u8 l4_proto; }; union ethtool_flow_union { struct ethtool_tcpip4_spec tcp_ip4_spec; struct ethtool_tcpip4_spec udp_ip4_spec; struct ethtool_tcpip4_spec sctp_ip4_spec; struct ethtool_ah_espip4_spec ah_ip4_spec; struct ethtool_ah_espip4_spec esp_ip4_spec; struct ethtool_usrip4_spec usr_ip4_spec; struct ethtool_tcpip6_spec tcp_ip6_spec; struct ethtool_tcpip6_spec udp_ip6_spec; struct ethtool_tcpip6_spec sctp_ip6_spec; struct ethtool_ah_espip6_spec ah_ip6_spec; struct ethtool_ah_espip6_spec esp_ip6_spec; struct ethtool_usrip6_spec usr_ip6_spec; struct ethhdr ether_spec; __u8 hdata[52]; }; struct ethtool_flow_ext { __u8 padding[2]; unsigned char h_dest[6]; __be16 vlan_etype; __be16 vlan_tci; __be32 data[2]; }; struct ethtool_rx_flow_spec { __u32 flow_type; union ethtool_flow_union h_u; struct ethtool_flow_ext h_ext; union ethtool_flow_union m_u; struct ethtool_flow_ext m_ext; __u64 ring_cookie; __u32 location; }; struct ethtool_rxnfc { __u32 cmd; __u32 flow_type; __u64 data; struct ethtool_rx_flow_spec fs; union { __u32 rule_cnt; __u32 rss_context; }; __u32 rule_locs[0]; }; struct ethtool_flash { __u32 cmd; __u32 region; char data[128]; }; struct ethtool_dump { __u32 cmd; __u32 version; __u32 flag; __u32 len; __u8 data[0]; }; struct ethtool_ts_info { __u32 cmd; __u32 so_timestamping; __s32 phc_index; __u32 tx_types; __u32 tx_reserved[3]; __u32 rx_filters; __u32 rx_reserved[3]; }; struct ethtool_fecparam { __u32 cmd; __u32 active_fec; __u32 fec; __u32 reserved; }; enum ethtool_link_mode_bit_indices { ETHTOOL_LINK_MODE_10baseT_Half_BIT = 0, ETHTOOL_LINK_MODE_10baseT_Full_BIT = 1, ETHTOOL_LINK_MODE_100baseT_Half_BIT = 2, ETHTOOL_LINK_MODE_100baseT_Full_BIT = 3, ETHTOOL_LINK_MODE_1000baseT_Half_BIT = 4, ETHTOOL_LINK_MODE_1000baseT_Full_BIT = 5, ETHTOOL_LINK_MODE_Autoneg_BIT = 6, ETHTOOL_LINK_MODE_TP_BIT = 7, ETHTOOL_LINK_MODE_AUI_BIT = 8, ETHTOOL_LINK_MODE_MII_BIT = 9, ETHTOOL_LINK_MODE_FIBRE_BIT = 10, ETHTOOL_LINK_MODE_BNC_BIT = 11, ETHTOOL_LINK_MODE_10000baseT_Full_BIT = 12, ETHTOOL_LINK_MODE_Pause_BIT = 13, ETHTOOL_LINK_MODE_Asym_Pause_BIT = 14, ETHTOOL_LINK_MODE_2500baseX_Full_BIT = 15, ETHTOOL_LINK_MODE_Backplane_BIT = 16, ETHTOOL_LINK_MODE_1000baseKX_Full_BIT = 17, ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT = 18, ETHTOOL_LINK_MODE_10000baseKR_Full_BIT = 19, ETHTOOL_LINK_MODE_10000baseR_FEC_BIT = 20, ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT = 21, ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT = 22, ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT = 23, ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT = 24, ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT = 25, ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT = 26, ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT = 27, ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT = 28, ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT = 29, ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT = 30, ETHTOOL_LINK_MODE_25000baseCR_Full_BIT = 31, ETHTOOL_LINK_MODE_25000baseKR_Full_BIT = 32, ETHTOOL_LINK_MODE_25000baseSR_Full_BIT = 33, ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT = 34, ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT = 35, ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT = 36, ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT = 37, ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT = 38, ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT = 39, ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT = 40, ETHTOOL_LINK_MODE_1000baseX_Full_BIT = 41, ETHTOOL_LINK_MODE_10000baseCR_Full_BIT = 42, ETHTOOL_LINK_MODE_10000baseSR_Full_BIT = 43, ETHTOOL_LINK_MODE_10000baseLR_Full_BIT = 44, ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT = 45, ETHTOOL_LINK_MODE_10000baseER_Full_BIT = 46, ETHTOOL_LINK_MODE_2500baseT_Full_BIT = 47, ETHTOOL_LINK_MODE_5000baseT_Full_BIT = 48, ETHTOOL_LINK_MODE_FEC_NONE_BIT = 49, ETHTOOL_LINK_MODE_FEC_RS_BIT = 50, ETHTOOL_LINK_MODE_FEC_BASER_BIT = 51, ETHTOOL_LINK_MODE_50000baseKR_Full_BIT = 52, ETHTOOL_LINK_MODE_50000baseSR_Full_BIT = 53, ETHTOOL_LINK_MODE_50000baseCR_Full_BIT = 54, ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT = 55, ETHTOOL_LINK_MODE_50000baseDR_Full_BIT = 56, ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT = 57, ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT = 58, ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT = 59, ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT = 60, ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT = 61, ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT = 62, ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT = 63, ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT = 64, ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT = 65, ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT = 66, ETHTOOL_LINK_MODE_100baseT1_Full_BIT = 67, ETHTOOL_LINK_MODE_1000baseT1_Full_BIT = 68, ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT = 69, ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT = 70, ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71, ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT = 72, ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT = 73, ETHTOOL_LINK_MODE_FEC_LLRS_BIT = 74, ETHTOOL_LINK_MODE_100000baseKR_Full_BIT = 75, ETHTOOL_LINK_MODE_100000baseSR_Full_BIT = 76, ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT = 77, ETHTOOL_LINK_MODE_100000baseCR_Full_BIT = 78, ETHTOOL_LINK_MODE_100000baseDR_Full_BIT = 79, ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT = 80, ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT = 81, ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT = 82, ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT = 83, ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT = 84, ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT = 85, ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT = 86, ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT = 87, ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT = 88, ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT = 89, ETHTOOL_LINK_MODE_100baseFX_Half_BIT = 90, ETHTOOL_LINK_MODE_100baseFX_Full_BIT = 91, __ETHTOOL_LINK_MODE_MASK_NBITS = 92, }; struct ethtool_link_settings { __u32 cmd; __u32 speed; __u8 duplex; __u8 port; __u8 phy_address; __u8 autoneg; __u8 mdio_support; __u8 eth_tp_mdix; __u8 eth_tp_mdix_ctrl; __s8 link_mode_masks_nwords; __u8 transceiver; __u8 master_slave_cfg; __u8 master_slave_state; __u8 reserved1[1]; __u32 reserved[7]; __u32 link_mode_masks[0]; }; struct ethtool_link_ext_state_info { enum ethtool_link_ext_state link_ext_state; union { enum ethtool_link_ext_substate_autoneg autoneg; enum ethtool_link_ext_substate_link_training link_training; enum ethtool_link_ext_substate_link_logical_mismatch link_logical_mismatch; enum ethtool_link_ext_substate_bad_signal_integrity bad_signal_integrity; enum ethtool_link_ext_substate_cable_issue cable_issue; u8 __link_ext_substate; }; }; struct ethtool_link_ksettings { struct ethtool_link_settings base; struct { long unsigned int supported[2]; long unsigned int advertising[2]; long unsigned int lp_advertising[2]; } link_modes; u32 lanes; }; struct ethtool_eth_mac_stats { u64 FramesTransmittedOK; u64 SingleCollisionFrames; u64 MultipleCollisionFrames; u64 FramesReceivedOK; u64 FrameCheckSequenceErrors; u64 AlignmentErrors; u64 OctetsTransmittedOK; u64 FramesWithDeferredXmissions; u64 LateCollisions; u64 FramesAbortedDueToXSColls; u64 FramesLostDueToIntMACXmitError; u64 CarrierSenseErrors; u64 OctetsReceivedOK; u64 FramesLostDueToIntMACRcvError; u64 MulticastFramesXmittedOK; u64 BroadcastFramesXmittedOK; u64 FramesWithExcessiveDeferral; u64 MulticastFramesReceivedOK; u64 BroadcastFramesReceivedOK; u64 InRangeLengthErrors; u64 OutOfRangeLengthField; u64 FrameTooLongErrors; }; struct ethtool_eth_phy_stats { u64 SymbolErrorDuringCarrier; }; struct ethtool_eth_ctrl_stats { u64 MACControlFramesTransmitted; u64 MACControlFramesReceived; u64 UnsupportedOpcodesReceived; }; struct ethtool_pause_stats { u64 tx_pause_frames; u64 rx_pause_frames; }; struct ethtool_fec_stat { u64 total; u64 lanes[8]; }; struct ethtool_fec_stats { struct ethtool_fec_stat corrected_blocks; struct ethtool_fec_stat uncorrectable_blocks; struct ethtool_fec_stat corrected_bits; }; struct ethtool_rmon_hist_range { u16 low; u16 high; }; struct ethtool_rmon_stats { u64 undersize_pkts; u64 oversize_pkts; u64 fragments; u64 jabbers; u64 hist[10]; u64 hist_tx[10]; }; struct ethtool_module_eeprom { u32 offset; u32 length; u8 page; u8 bank; u8 i2c_address; u8 *data; }; enum ib_uverbs_write_cmds { IB_USER_VERBS_CMD_GET_CONTEXT = 0, IB_USER_VERBS_CMD_QUERY_DEVICE = 1, IB_USER_VERBS_CMD_QUERY_PORT = 2, IB_USER_VERBS_CMD_ALLOC_PD = 3, IB_USER_VERBS_CMD_DEALLOC_PD = 4, IB_USER_VERBS_CMD_CREATE_AH = 5, IB_USER_VERBS_CMD_MODIFY_AH = 6, IB_USER_VERBS_CMD_QUERY_AH = 7, IB_USER_VERBS_CMD_DESTROY_AH = 8, IB_USER_VERBS_CMD_REG_MR = 9, IB_USER_VERBS_CMD_REG_SMR = 10, IB_USER_VERBS_CMD_REREG_MR = 11, IB_USER_VERBS_CMD_QUERY_MR = 12, IB_USER_VERBS_CMD_DEREG_MR = 13, IB_USER_VERBS_CMD_ALLOC_MW = 14, IB_USER_VERBS_CMD_BIND_MW = 15, IB_USER_VERBS_CMD_DEALLOC_MW = 16, IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL = 17, IB_USER_VERBS_CMD_CREATE_CQ = 18, IB_USER_VERBS_CMD_RESIZE_CQ = 19, IB_USER_VERBS_CMD_DESTROY_CQ = 20, IB_USER_VERBS_CMD_POLL_CQ = 21, IB_USER_VERBS_CMD_PEEK_CQ = 22, IB_USER_VERBS_CMD_REQ_NOTIFY_CQ = 23, IB_USER_VERBS_CMD_CREATE_QP = 24, IB_USER_VERBS_CMD_QUERY_QP = 25, IB_USER_VERBS_CMD_MODIFY_QP = 26, IB_USER_VERBS_CMD_DESTROY_QP = 27, IB_USER_VERBS_CMD_POST_SEND = 28, IB_USER_VERBS_CMD_POST_RECV = 29, IB_USER_VERBS_CMD_ATTACH_MCAST = 30, IB_USER_VERBS_CMD_DETACH_MCAST = 31, IB_USER_VERBS_CMD_CREATE_SRQ = 32, IB_USER_VERBS_CMD_MODIFY_SRQ = 33, IB_USER_VERBS_CMD_QUERY_SRQ = 34, IB_USER_VERBS_CMD_DESTROY_SRQ = 35, IB_USER_VERBS_CMD_POST_SRQ_RECV = 36, IB_USER_VERBS_CMD_OPEN_XRCD = 37, IB_USER_VERBS_CMD_CLOSE_XRCD = 38, IB_USER_VERBS_CMD_CREATE_XSRQ = 39, IB_USER_VERBS_CMD_OPEN_QP = 40, }; enum ib_uverbs_wc_opcode { IB_UVERBS_WC_SEND = 0, IB_UVERBS_WC_RDMA_WRITE = 1, IB_UVERBS_WC_RDMA_READ = 2, IB_UVERBS_WC_COMP_SWAP = 3, IB_UVERBS_WC_FETCH_ADD = 4, IB_UVERBS_WC_BIND_MW = 5, IB_UVERBS_WC_LOCAL_INV = 6, IB_UVERBS_WC_TSO = 7, }; enum ib_uverbs_create_qp_mask { IB_UVERBS_CREATE_QP_MASK_IND_TABLE = 1, }; enum ib_uverbs_wr_opcode { IB_UVERBS_WR_RDMA_WRITE = 0, IB_UVERBS_WR_RDMA_WRITE_WITH_IMM = 1, IB_UVERBS_WR_SEND = 2, IB_UVERBS_WR_SEND_WITH_IMM = 3, IB_UVERBS_WR_RDMA_READ = 4, IB_UVERBS_WR_ATOMIC_CMP_AND_SWP = 5, IB_UVERBS_WR_ATOMIC_FETCH_AND_ADD = 6, IB_UVERBS_WR_LOCAL_INV = 7, IB_UVERBS_WR_BIND_MW = 8, IB_UVERBS_WR_SEND_WITH_INV = 9, IB_UVERBS_WR_TSO = 10, IB_UVERBS_WR_RDMA_READ_WITH_INV = 11, IB_UVERBS_WR_MASKED_ATOMIC_CMP_AND_SWP = 12, IB_UVERBS_WR_MASKED_ATOMIC_FETCH_AND_ADD = 13, }; enum ib_uverbs_access_flags { IB_UVERBS_ACCESS_LOCAL_WRITE = 1, IB_UVERBS_ACCESS_REMOTE_WRITE = 2, IB_UVERBS_ACCESS_REMOTE_READ = 4, IB_UVERBS_ACCESS_REMOTE_ATOMIC = 8, IB_UVERBS_ACCESS_MW_BIND = 16, IB_UVERBS_ACCESS_ZERO_BASED = 32, IB_UVERBS_ACCESS_ON_DEMAND = 64, IB_UVERBS_ACCESS_HUGETLB = 128, IB_UVERBS_ACCESS_RELAXED_ORDERING = 1048576, IB_UVERBS_ACCESS_OPTIONAL_RANGE = 1072693248, }; enum ib_uverbs_srq_type { IB_UVERBS_SRQT_BASIC = 0, IB_UVERBS_SRQT_XRC = 1, IB_UVERBS_SRQT_TM = 2, }; enum ib_uverbs_wq_type { IB_UVERBS_WQT_RQ = 0, }; enum ib_uverbs_wq_flags { IB_UVERBS_WQ_FLAGS_CVLAN_STRIPPING = 1, IB_UVERBS_WQ_FLAGS_SCATTER_FCS = 2, IB_UVERBS_WQ_FLAGS_DELAY_DROP = 4, IB_UVERBS_WQ_FLAGS_PCI_WRITE_END_PADDING = 8, }; enum ib_uverbs_qp_type { IB_UVERBS_QPT_RC = 2, IB_UVERBS_QPT_UC = 3, IB_UVERBS_QPT_UD = 4, IB_UVERBS_QPT_RAW_PACKET = 8, IB_UVERBS_QPT_XRC_INI = 9, IB_UVERBS_QPT_XRC_TGT = 10, IB_UVERBS_QPT_DRIVER = 255, }; enum ib_uverbs_qp_create_flags { IB_UVERBS_QP_CREATE_BLOCK_MULTICAST_LOOPBACK = 2, IB_UVERBS_QP_CREATE_SCATTER_FCS = 256, IB_UVERBS_QP_CREATE_CVLAN_STRIPPING = 512, IB_UVERBS_QP_CREATE_PCI_WRITE_END_PADDING = 2048, IB_UVERBS_QP_CREATE_SQ_SIG_ALL = 4096, }; enum ib_uverbs_gid_type { IB_UVERBS_GID_TYPE_IB = 0, IB_UVERBS_GID_TYPE_ROCE_V1 = 1, IB_UVERBS_GID_TYPE_ROCE_V2 = 2, }; enum ib_poll_context { IB_POLL_SOFTIRQ = 0, IB_POLL_WORKQUEUE = 1, IB_POLL_UNBOUND_WORKQUEUE = 2, IB_POLL_LAST_POOL_TYPE = 2, IB_POLL_DIRECT = 3, }; struct lsm_network_audit { int netif; const struct sock *sk; u16 family; __be16 dport; __be16 sport; union { struct { __be32 daddr; __be32 saddr; } v4; struct { struct in6_addr daddr; struct in6_addr saddr; } v6; } fam; }; struct lsm_ioctlop_audit { struct path path; u16 cmd; }; struct lsm_ibpkey_audit { u64 subnet_prefix; u16 pkey; }; struct lsm_ibendport_audit { const char *dev_name; u8 port; }; struct selinux_state; struct selinux_audit_data { u32 ssid; u32 tsid; u16 tclass; u32 requested; u32 audited; u32 denied; int result; struct selinux_state *state; }; struct smack_audit_data; struct apparmor_audit_data; struct common_audit_data { char type; union { struct path path; struct dentry *dentry; struct inode *inode; struct lsm_network_audit *net; int cap; int ipc_id; struct task_struct *tsk; struct { key_serial_t key; char *key_desc; } key_struct; char *kmod_name; struct lsm_ioctlop_audit *op; struct file *file; struct lsm_ibpkey_audit *ibpkey; struct lsm_ibendport_audit *ibendport; int reason; } u; union { struct smack_audit_data *smack_audit_data; struct selinux_audit_data *selinux_audit_data; struct apparmor_audit_data *apparmor_audit_data; }; }; enum { POLICYDB_CAPABILITY_NETPEER = 0, POLICYDB_CAPABILITY_OPENPERM = 1, POLICYDB_CAPABILITY_EXTSOCKCLASS = 2, POLICYDB_CAPABILITY_ALWAYSNETWORK = 3, POLICYDB_CAPABILITY_CGROUPSECLABEL = 4, POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION = 5, POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS = 6, __POLICYDB_CAPABILITY_MAX = 7, }; struct selinux_avc; struct selinux_policy; struct selinux_state { bool enforcing; bool checkreqprot; bool initialized; bool policycap[7]; struct page *status_page; struct mutex status_lock; struct selinux_avc *avc; struct selinux_policy *policy; struct mutex policy_mutex; }; struct avc_cache { struct hlist_head slots[512]; spinlock_t slots_lock[512]; atomic_t lru_hint; atomic_t active_nodes; u32 latest_notif; }; struct selinux_avc { unsigned int avc_cache_threshold; struct avc_cache avc_cache; }; struct av_decision { u32 allowed; u32 auditallow; u32 auditdeny; u32 seqno; u32 flags; }; struct extended_perms_data { u32 p[8]; }; struct extended_perms_decision { u8 used; u8 driver; struct extended_perms_data *allowed; struct extended_perms_data *auditallow; struct extended_perms_data *dontaudit; }; struct extended_perms { u16 len; struct extended_perms_data drivers; }; struct avc_cache_stats { unsigned int lookups; unsigned int misses; unsigned int allocations; unsigned int reclaims; unsigned int frees; }; struct security_class_mapping { const char *name; const char *perms[33]; }; struct trace_event_raw_selinux_audited { struct trace_entry ent; u32 requested; u32 denied; u32 audited; int result; u32 __data_loc_scontext; u32 __data_loc_tcontext; u32 __data_loc_tclass; char __data[0]; }; struct trace_event_data_offsets_selinux_audited { u32 scontext; u32 tcontext; u32 tclass; }; typedef void (*btf_trace_selinux_audited)(void *, struct selinux_audit_data *, char *, char *, const char *); struct avc_xperms_node; struct avc_entry { u32 ssid; u32 tsid; u16 tclass; struct av_decision avd; struct avc_xperms_node *xp_node; }; struct avc_xperms_node { struct extended_perms xp; struct list_head xpd_head; }; struct avc_node { struct avc_entry ae; struct hlist_node list; struct callback_head rhead; }; struct avc_xperms_decision_node { struct extended_perms_decision xpd; struct list_head xpd_list; }; struct avc_callback_node { int (*callback)(u32); u32 events; struct avc_callback_node *next; }; typedef __u16 __sum16; enum sctp_endpoint_type { SCTP_EP_TYPE_SOCKET = 0, SCTP_EP_TYPE_ASSOCIATION = 1, }; struct sctp_chunk; struct sctp_inq { struct list_head in_chunk_list; struct sctp_chunk *in_progress; struct work_struct immediate; }; struct sctp_bind_addr { __u16 port; struct list_head address_list; }; struct sctp_ep_common { struct hlist_node node; int hashent; enum sctp_endpoint_type type; refcount_t refcnt; bool dead; struct sock *sk; struct net *net; struct sctp_inq inqueue; struct sctp_bind_addr bind_addr; }; struct crypto_shash___2; struct sctp_hmac_algo_param; struct sctp_chunks_param; struct sctp_endpoint { struct sctp_ep_common base; struct list_head asocs; __u8 secret_key[32]; __u8 *digest; __u32 sndbuf_policy; __u32 rcvbuf_policy; struct crypto_shash___2 **auth_hmacs; struct sctp_hmac_algo_param *auth_hmacs_list; struct sctp_chunks_param *auth_chunk_list; struct list_head endpoint_shared_keys; __u16 active_key_id; __u8 ecn_enable: 1; __u8 auth_enable: 1; __u8 intl_enable: 1; __u8 prsctp_enable: 1; __u8 asconf_enable: 1; __u8 reconf_enable: 1; __u8 strreset_enable; u32 secid; u32 peer_secid; }; struct sockaddr_in6 { short unsigned int sin6_family; __be16 sin6_port; __be32 sin6_flowinfo; struct in6_addr sin6_addr; __u32 sin6_scope_id; }; struct in_addr { __be32 s_addr; }; struct sockaddr_in { __kernel_sa_family_t sin_family; __be16 sin_port; struct in_addr sin_addr; unsigned char __pad[8]; }; struct nf_hook_state; typedef unsigned int nf_hookfn(void *, struct sk_buff *, const struct nf_hook_state *); struct nf_hook_entry { nf_hookfn *hook; void *priv; }; struct nf_hook_entries { u16 num_hook_entries; struct nf_hook_entry hooks[0]; }; struct nf_hook_state { u8 hook; u8 pf; struct net_device *in; struct net_device *out; struct sock *sk; struct net *net; int (*okfn)(struct net *, struct sock *, struct sk_buff *); }; enum nf_hook_ops_type { NF_HOOK_OP_UNDEFINED = 0, NF_HOOK_OP_NF_TABLES = 1, }; struct nf_hook_ops { nf_hookfn *hook; struct net_device *dev; void *priv; u8 pf; enum nf_hook_ops_type hook_ops_type: 8; unsigned int hooknum; int priority; }; enum nf_ip_hook_priorities { NF_IP_PRI_FIRST = 2147483648, NF_IP_PRI_RAW_BEFORE_DEFRAG = 4294966846, NF_IP_PRI_CONNTRACK_DEFRAG = 4294966896, NF_IP_PRI_RAW = 4294966996, NF_IP_PRI_SELINUX_FIRST = 4294967071, NF_IP_PRI_CONNTRACK = 4294967096, NF_IP_PRI_MANGLE = 4294967146, NF_IP_PRI_NAT_DST = 4294967196, NF_IP_PRI_FILTER = 0, NF_IP_PRI_SECURITY = 50, NF_IP_PRI_NAT_SRC = 100, NF_IP_PRI_SELINUX_LAST = 225, NF_IP_PRI_CONNTRACK_HELPER = 300, NF_IP_PRI_CONNTRACK_CONFIRM = 2147483647, NF_IP_PRI_LAST = 2147483647, }; enum nf_ip6_hook_priorities { NF_IP6_PRI_FIRST = 2147483648, NF_IP6_PRI_RAW_BEFORE_DEFRAG = 4294966846, NF_IP6_PRI_CONNTRACK_DEFRAG = 4294966896, NF_IP6_PRI_RAW = 4294966996, NF_IP6_PRI_SELINUX_FIRST = 4294967071, NF_IP6_PRI_CONNTRACK = 4294967096, NF_IP6_PRI_MANGLE = 4294967146, NF_IP6_PRI_NAT_DST = 4294967196, NF_IP6_PRI_FILTER = 0, NF_IP6_PRI_SECURITY = 50, NF_IP6_PRI_NAT_SRC = 100, NF_IP6_PRI_SELINUX_LAST = 225, NF_IP6_PRI_CONNTRACK_HELPER = 300, NF_IP6_PRI_LAST = 2147483647, }; struct socket_alloc { struct socket socket; struct inode vfs_inode; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct ip_options { __be32 faddr; __be32 nexthop; unsigned char optlen; unsigned char srr; unsigned char rr; unsigned char ts; unsigned char is_strictroute: 1; unsigned char srr_is_hit: 1; unsigned char is_changed: 1; unsigned char rr_needaddr: 1; unsigned char ts_needtime: 1; unsigned char ts_needaddr: 1; unsigned char router_alert; unsigned char cipso; unsigned char __pad2; unsigned char __data[0]; }; struct ip_options_rcu { struct callback_head rcu; struct ip_options opt; }; struct ipv6_opt_hdr; struct ipv6_rt_hdr; struct ipv6_txoptions { refcount_t refcnt; int tot_len; __u16 opt_flen; __u16 opt_nflen; struct ipv6_opt_hdr *hopopt; struct ipv6_opt_hdr *dst0opt; struct ipv6_rt_hdr *srcrt; struct ipv6_opt_hdr *dst1opt; struct callback_head rcu; }; struct inet_cork { unsigned int flags; __be32 addr; struct ip_options *opt; unsigned int fragsize; int length; struct dst_entry *dst; u8 tx_flags; __u8 ttl; __s16 tos; char priority; __u16 gso_size; u64 transmit_time; u32 mark; }; struct inet_cork_full { struct inet_cork base; struct flowi fl; }; struct ipv6_pinfo; struct ip_mc_socklist; struct inet_sock { struct sock sk; struct ipv6_pinfo *pinet6; __be32 inet_saddr; __s16 uc_ttl; __u16 cmsg_flags; __be16 inet_sport; __u16 inet_id; struct ip_options_rcu *inet_opt; int rx_dst_ifindex; __u8 tos; __u8 min_ttl; __u8 mc_ttl; __u8 pmtudisc; __u8 recverr: 1; __u8 is_icsk: 1; __u8 freebind: 1; __u8 hdrincl: 1; __u8 mc_loop: 1; __u8 transparent: 1; __u8 mc_all: 1; __u8 nodefrag: 1; __u8 bind_address_no_port: 1; __u8 recverr_rfc4884: 1; __u8 defer_connect: 1; __u8 rcv_tos; __u8 convert_csum; int uc_index; int mc_index; __be32 mc_addr; struct ip_mc_socklist *mc_list; struct inet_cork_full cork; }; struct in6_pktinfo { struct in6_addr ipi6_addr; int ipi6_ifindex; }; struct inet6_cork { struct ipv6_txoptions *opt; u8 hop_limit; u8 tclass; }; struct ipv6_mc_socklist; struct ipv6_ac_socklist; struct ipv6_fl_socklist; struct ipv6_pinfo { struct in6_addr saddr; struct in6_pktinfo sticky_pktinfo; const struct in6_addr *daddr_cache; const struct in6_addr *saddr_cache; __be32 flow_label; __u32 frag_size; __u16 __unused_1: 7; __s16 hop_limit: 9; __u16 mc_loop: 1; __u16 __unused_2: 6; __s16 mcast_hops: 9; int ucast_oif; int mcast_oif; union { struct { __u16 srcrt: 1; __u16 osrcrt: 1; __u16 rxinfo: 1; __u16 rxoinfo: 1; __u16 rxhlim: 1; __u16 rxohlim: 1; __u16 hopopts: 1; __u16 ohopopts: 1; __u16 dstopts: 1; __u16 odstopts: 1; __u16 rxflow: 1; __u16 rxtclass: 1; __u16 rxpmtu: 1; __u16 rxorigdstaddr: 1; __u16 recvfragsize: 1; } bits; __u16 all; } rxopt; __u16 recverr: 1; __u16 sndflow: 1; __u16 repflow: 1; __u16 pmtudisc: 3; __u16 padding: 1; __u16 srcprefs: 3; __u16 dontfrag: 1; __u16 autoflowlabel: 1; __u16 autoflowlabel_set: 1; __u16 mc_all: 1; __u16 recverr_rfc4884: 1; __u16 rtalert_isolate: 1; __u8 min_hopcount; __u8 tclass; __be32 rcv_flowinfo; __u32 dst_cookie; __u32 rx_dst_cookie; struct ipv6_mc_socklist *ipv6_mc_list; struct ipv6_ac_socklist *ipv6_ac_list; struct ipv6_fl_socklist *ipv6_fl_list; struct ipv6_txoptions *opt; struct sk_buff *pktoptions; struct sk_buff *rxpmtu; struct inet6_cork cork; }; struct tcphdr { __be16 source; __be16 dest; __be32 seq; __be32 ack_seq; __u16 res1: 4; __u16 doff: 4; __u16 fin: 1; __u16 syn: 1; __u16 rst: 1; __u16 psh: 1; __u16 ack: 1; __u16 urg: 1; __u16 ece: 1; __u16 cwr: 1; __be16 window; __sum16 check; __be16 urg_ptr; }; struct iphdr { __u8 ihl: 4; __u8 version: 4; __u8 tos; __be16 tot_len; __be16 id; __be16 frag_off; __u8 ttl; __u8 protocol; __sum16 check; __be32 saddr; __be32 daddr; }; struct ipv6_rt_hdr { __u8 nexthdr; __u8 hdrlen; __u8 type; __u8 segments_left; }; struct ipv6_opt_hdr { __u8 nexthdr; __u8 hdrlen; }; struct ipv6hdr { __u8 priority: 4; __u8 version: 4; __u8 flow_lbl[3]; __be16 payload_len; __u8 nexthdr; __u8 hop_limit; struct in6_addr saddr; struct in6_addr daddr; }; struct udphdr { __be16 source; __be16 dest; __be16 len; __sum16 check; }; struct inet6_skb_parm { int iif; __be16 ra; __u16 dst0; __u16 srcrt; __u16 dst1; __u16 lastopt; __u16 nhoff; __u16 flags; __u16 dsthao; __u16 frag_max_size; }; struct ip6_sf_socklist; struct ipv6_mc_socklist { struct in6_addr addr; int ifindex; unsigned int sfmode; struct ipv6_mc_socklist *next; struct ip6_sf_socklist *sflist; struct callback_head rcu; }; struct ipv6_ac_socklist { struct in6_addr acl_addr; int acl_ifindex; struct ipv6_ac_socklist *acl_next; }; struct ip6_flowlabel; struct ipv6_fl_socklist { struct ipv6_fl_socklist *next; struct ip6_flowlabel *fl; struct callback_head rcu; }; struct ip6_sf_socklist { unsigned int sl_max; unsigned int sl_count; struct callback_head rcu; struct in6_addr sl_addr[0]; }; struct ip6_flowlabel { struct ip6_flowlabel *next; __be32 label; atomic_t users; struct in6_addr dst; struct ipv6_txoptions *opt; long unsigned int linger; struct callback_head rcu; u8 share; union { struct pid *pid; kuid_t uid; } owner; long unsigned int lastuse; long unsigned int expires; struct net *fl_net; }; struct inet_skb_parm { int iif; struct ip_options opt; u16 flags; u16 frag_max_size; }; struct tty_file_private { struct tty_struct *tty; struct file *file; struct list_head list; }; struct netlbl_lsm_cache { refcount_t refcount; void (*free)(const void *); void *data; }; struct netlbl_lsm_catmap { u32 startbit; u64 bitmap[4]; struct netlbl_lsm_catmap *next; }; struct netlbl_lsm_secattr { u32 flags; u32 type; char *domain; struct netlbl_lsm_cache *cache; struct { struct { struct netlbl_lsm_catmap *cat; u32 lvl; } mls; u32 secid; } attr; }; struct dccp_hdr { __be16 dccph_sport; __be16 dccph_dport; __u8 dccph_doff; __u8 dccph_cscov: 4; __u8 dccph_ccval: 4; __sum16 dccph_checksum; __u8 dccph_x: 1; __u8 dccph_type: 4; __u8 dccph_reserved: 3; __u8 dccph_seq2; __be16 dccph_seq; }; enum dccp_state { DCCP_OPEN = 1, DCCP_REQUESTING = 2, DCCP_LISTEN = 10, DCCP_RESPOND = 3, DCCP_ACTIVE_CLOSEREQ = 4, DCCP_PASSIVE_CLOSE = 8, DCCP_CLOSING = 11, DCCP_TIME_WAIT = 6, DCCP_CLOSED = 7, DCCP_NEW_SYN_RECV = 12, DCCP_PARTOPEN = 13, DCCP_PASSIVE_CLOSEREQ = 14, DCCP_MAX_STATES = 15, }; typedef __s32 sctp_assoc_t; enum sctp_msg_flags { MSG_NOTIFICATION = 32768, }; struct sctp_initmsg { __u16 sinit_num_ostreams; __u16 sinit_max_instreams; __u16 sinit_max_attempts; __u16 sinit_max_init_timeo; }; struct sctp_sndrcvinfo { __u16 sinfo_stream; __u16 sinfo_ssn; __u16 sinfo_flags; __u32 sinfo_ppid; __u32 sinfo_context; __u32 sinfo_timetolive; __u32 sinfo_tsn; __u32 sinfo_cumtsn; sctp_assoc_t sinfo_assoc_id; }; struct sctp_rtoinfo { sctp_assoc_t srto_assoc_id; __u32 srto_initial; __u32 srto_max; __u32 srto_min; }; struct sctp_assocparams { sctp_assoc_t sasoc_assoc_id; __u16 sasoc_asocmaxrxt; __u16 sasoc_number_peer_destinations; __u32 sasoc_peer_rwnd; __u32 sasoc_local_rwnd; __u32 sasoc_cookie_life; }; struct sctp_paddrparams { sctp_assoc_t spp_assoc_id; struct __kernel_sockaddr_storage spp_address; __u32 spp_hbinterval; __u16 spp_pathmaxrxt; __u32 spp_pathmtu; __u32 spp_sackdelay; __u32 spp_flags; __u32 spp_ipv6_flowlabel; __u8 spp_dscp; char: 8; } __attribute__((packed)); struct sctphdr { __be16 source; __be16 dest; __be32 vtag; __le32 checksum; }; struct sctp_chunkhdr { __u8 type; __u8 flags; __be16 length; }; enum sctp_cid { SCTP_CID_DATA = 0, SCTP_CID_INIT = 1, SCTP_CID_INIT_ACK = 2, SCTP_CID_SACK = 3, SCTP_CID_HEARTBEAT = 4, SCTP_CID_HEARTBEAT_ACK = 5, SCTP_CID_ABORT = 6, SCTP_CID_SHUTDOWN = 7, SCTP_CID_SHUTDOWN_ACK = 8, SCTP_CID_ERROR = 9, SCTP_CID_COOKIE_ECHO = 10, SCTP_CID_COOKIE_ACK = 11, SCTP_CID_ECN_ECNE = 12, SCTP_CID_ECN_CWR = 13, SCTP_CID_SHUTDOWN_COMPLETE = 14, SCTP_CID_AUTH = 15, SCTP_CID_I_DATA = 64, SCTP_CID_FWD_TSN = 192, SCTP_CID_ASCONF = 193, SCTP_CID_I_FWD_TSN = 194, SCTP_CID_ASCONF_ACK = 128, SCTP_CID_RECONF = 130, SCTP_CID_PAD = 132, }; struct sctp_paramhdr { __be16 type; __be16 length; }; enum sctp_param { SCTP_PARAM_HEARTBEAT_INFO = 256, SCTP_PARAM_IPV4_ADDRESS = 1280, SCTP_PARAM_IPV6_ADDRESS = 1536, SCTP_PARAM_STATE_COOKIE = 1792, SCTP_PARAM_UNRECOGNIZED_PARAMETERS = 2048, SCTP_PARAM_COOKIE_PRESERVATIVE = 2304, SCTP_PARAM_HOST_NAME_ADDRESS = 2816, SCTP_PARAM_SUPPORTED_ADDRESS_TYPES = 3072, SCTP_PARAM_ECN_CAPABLE = 128, SCTP_PARAM_RANDOM = 640, SCTP_PARAM_CHUNKS = 896, SCTP_PARAM_HMAC_ALGO = 1152, SCTP_PARAM_SUPPORTED_EXT = 2176, SCTP_PARAM_FWD_TSN_SUPPORT = 192, SCTP_PARAM_ADD_IP = 448, SCTP_PARAM_DEL_IP = 704, SCTP_PARAM_ERR_CAUSE = 960, SCTP_PARAM_SET_PRIMARY = 1216, SCTP_PARAM_SUCCESS_REPORT = 1472, SCTP_PARAM_ADAPTATION_LAYER_IND = 1728, SCTP_PARAM_RESET_OUT_REQUEST = 3328, SCTP_PARAM_RESET_IN_REQUEST = 3584, SCTP_PARAM_RESET_TSN_REQUEST = 3840, SCTP_PARAM_RESET_RESPONSE = 4096, SCTP_PARAM_RESET_ADD_OUT_STREAMS = 4352, SCTP_PARAM_RESET_ADD_IN_STREAMS = 4608, }; struct sctp_datahdr { __be32 tsn; __be16 stream; __be16 ssn; __u32 ppid; __u8 payload[0]; }; struct sctp_idatahdr { __be32 tsn; __be16 stream; __be16 reserved; __be32 mid; union { __u32 ppid; __be32 fsn; }; __u8 payload[0]; }; struct sctp_inithdr { __be32 init_tag; __be32 a_rwnd; __be16 num_outbound_streams; __be16 num_inbound_streams; __be32 initial_tsn; __u8 params[0]; }; struct sctp_init_chunk { struct sctp_chunkhdr chunk_hdr; struct sctp_inithdr init_hdr; }; struct sctp_ipv4addr_param { struct sctp_paramhdr param_hdr; struct in_addr addr; }; struct sctp_ipv6addr_param { struct sctp_paramhdr param_hdr; struct in6_addr addr; }; struct sctp_cookie_preserve_param { struct sctp_paramhdr param_hdr; __be32 lifespan_increment; }; struct sctp_hostname_param { struct sctp_paramhdr param_hdr; uint8_t hostname[0]; }; struct sctp_supported_addrs_param { struct sctp_paramhdr param_hdr; __be16 types[0]; }; struct sctp_adaptation_ind_param { struct sctp_paramhdr param_hdr; __be32 adaptation_ind; }; struct sctp_supported_ext_param { struct sctp_paramhdr param_hdr; __u8 chunks[0]; }; struct sctp_random_param { struct sctp_paramhdr param_hdr; __u8 random_val[0]; }; struct sctp_chunks_param { struct sctp_paramhdr param_hdr; __u8 chunks[0]; }; struct sctp_hmac_algo_param { struct sctp_paramhdr param_hdr; __be16 hmac_ids[0]; }; struct sctp_cookie_param { struct sctp_paramhdr p; __u8 body[0]; }; struct sctp_gap_ack_block { __be16 start; __be16 end; }; union sctp_sack_variable { struct sctp_gap_ack_block gab; __be32 dup; }; struct sctp_sackhdr { __be32 cum_tsn_ack; __be32 a_rwnd; __be16 num_gap_ack_blocks; __be16 num_dup_tsns; union sctp_sack_variable variable[0]; }; struct sctp_heartbeathdr { struct sctp_paramhdr info; }; struct sctp_shutdownhdr { __be32 cum_tsn_ack; }; struct sctp_errhdr { __be16 cause; __be16 length; __u8 variable[0]; }; struct sctp_ecnehdr { __be32 lowest_tsn; }; struct sctp_cwrhdr { __be32 lowest_tsn; }; struct sctp_fwdtsn_skip { __be16 stream; __be16 ssn; }; struct sctp_fwdtsn_hdr { __be32 new_cum_tsn; struct sctp_fwdtsn_skip skip[0]; }; struct sctp_ifwdtsn_skip { __be16 stream; __u8 reserved; __u8 flags; __be32 mid; }; struct sctp_ifwdtsn_hdr { __be32 new_cum_tsn; struct sctp_ifwdtsn_skip skip[0]; }; struct sctp_addip_param { struct sctp_paramhdr param_hdr; __be32 crr_id; }; struct sctp_addiphdr { __be32 serial; __u8 params[0]; }; struct sctp_authhdr { __be16 shkey_id; __be16 hmac_id; __u8 hmac[0]; }; union sctp_addr { struct sockaddr_in v4; struct sockaddr_in6 v6; struct sockaddr sa; }; struct sctp_cookie { __u32 my_vtag; __u32 peer_vtag; __u32 my_ttag; __u32 peer_ttag; ktime_t expiration; __u16 sinit_num_ostreams; __u16 sinit_max_instreams; __u32 initial_tsn; union sctp_addr peer_addr; __u16 my_port; __u8 prsctp_capable; __u8 padding; __u32 adaptation_ind; __u8 auth_random[36]; __u8 auth_hmacs[10]; __u8 auth_chunks[20]; __u32 raw_addr_list_len; struct sctp_init_chunk peer_init[0]; }; struct sctp_tsnmap { long unsigned int *tsn_map; __u32 base_tsn; __u32 cumulative_tsn_ack_point; __u32 max_tsn_seen; __u16 len; __u16 pending_data; __u16 num_dup_tsns; __be32 dup_tsns[16]; }; struct sctp_inithdr_host { __u32 init_tag; __u32 a_rwnd; __u16 num_outbound_streams; __u16 num_inbound_streams; __u32 initial_tsn; }; enum sctp_state { SCTP_STATE_CLOSED = 0, SCTP_STATE_COOKIE_WAIT = 1, SCTP_STATE_COOKIE_ECHOED = 2, SCTP_STATE_ESTABLISHED = 3, SCTP_STATE_SHUTDOWN_PENDING = 4, SCTP_STATE_SHUTDOWN_SENT = 5, SCTP_STATE_SHUTDOWN_RECEIVED = 6, SCTP_STATE_SHUTDOWN_ACK_SENT = 7, }; struct sctp_stream_out_ext; struct sctp_stream_out { union { __u32 mid; __u16 ssn; }; __u32 mid_uo; struct sctp_stream_out_ext *ext; __u8 state; }; struct sctp_stream_in { union { __u32 mid; __u16 ssn; }; __u32 mid_uo; __u32 fsn; __u32 fsn_uo; char pd_mode; char pd_mode_uo; }; struct sctp_stream_interleave; struct sctp_stream { struct { struct __genradix tree; struct sctp_stream_out type[0]; } out; struct { struct __genradix tree; struct sctp_stream_in type[0]; } in; __u16 outcnt; __u16 incnt; struct sctp_stream_out *out_curr; union { struct { struct list_head prio_list; }; struct { struct list_head rr_list; struct sctp_stream_out_ext *rr_next; }; }; struct sctp_stream_interleave *si; }; struct sctp_sched_ops; struct sctp_association; struct sctp_outq { struct sctp_association *asoc; struct list_head out_chunk_list; struct sctp_sched_ops *sched; unsigned int out_qlen; unsigned int error; struct list_head control_chunk_list; struct list_head sacked; struct list_head retransmit; struct list_head abandoned; __u32 outstanding_bytes; char fast_rtx; char cork; }; struct sctp_ulpq { char pd_mode; struct sctp_association *asoc; struct sk_buff_head reasm; struct sk_buff_head reasm_uo; struct sk_buff_head lobby; }; struct sctp_priv_assoc_stats { struct __kernel_sockaddr_storage obs_rto_ipaddr; __u64 max_obs_rto; __u64 isacks; __u64 osacks; __u64 opackets; __u64 ipackets; __u64 rtxchunks; __u64 outofseqtsns; __u64 idupchunks; __u64 gapcnt; __u64 ouodchunks; __u64 iuodchunks; __u64 oodchunks; __u64 iodchunks; __u64 octrlchunks; __u64 ictrlchunks; }; struct sctp_transport; struct sctp_auth_bytes; struct sctp_shared_key; struct sctp_association { struct sctp_ep_common base; struct list_head asocs; sctp_assoc_t assoc_id; struct sctp_endpoint *ep; struct sctp_cookie c; struct { struct list_head transport_addr_list; __u32 rwnd; __u16 transport_count; __u16 port; struct sctp_transport *primary_path; union sctp_addr primary_addr; struct sctp_transport *active_path; struct sctp_transport *retran_path; struct sctp_transport *last_sent_to; struct sctp_transport *last_data_from; struct sctp_tsnmap tsn_map; __be16 addip_disabled_mask; __u16 ecn_capable: 1; __u16 ipv4_address: 1; __u16 ipv6_address: 1; __u16 hostname_address: 1; __u16 asconf_capable: 1; __u16 prsctp_capable: 1; __u16 reconf_capable: 1; __u16 intl_capable: 1; __u16 auth_capable: 1; __u16 sack_needed: 1; __u16 sack_generation: 1; __u16 zero_window_announced: 1; __u32 sack_cnt; __u32 adaptation_ind; struct sctp_inithdr_host i; void *cookie; int cookie_len; __u32 addip_serial; struct sctp_random_param *peer_random; struct sctp_chunks_param *peer_chunks; struct sctp_hmac_algo_param *peer_hmacs; } peer; enum sctp_state state; int overall_error_count; ktime_t cookie_life; long unsigned int rto_initial; long unsigned int rto_max; long unsigned int rto_min; int max_burst; int max_retrans; __u16 pf_retrans; __u16 ps_retrans; __u16 max_init_attempts; __u16 init_retries; long unsigned int max_init_timeo; long unsigned int hbinterval; long unsigned int probe_interval; __be16 encap_port; __u16 pathmaxrxt; __u32 flowlabel; __u8 dscp; __u8 pmtu_pending; __u32 pathmtu; __u32 param_flags; __u32 sackfreq; long unsigned int sackdelay; long unsigned int timeouts[12]; struct timer_list timers[12]; struct sctp_transport *shutdown_last_sent_to; struct sctp_transport *init_last_sent_to; int shutdown_retries; __u32 next_tsn; __u32 ctsn_ack_point; __u32 adv_peer_ack_point; __u32 highest_sacked; __u32 fast_recovery_exit; __u8 fast_recovery; __u16 unack_data; __u32 rtx_data_chunks; __u32 rwnd; __u32 a_rwnd; __u32 rwnd_over; __u32 rwnd_press; int sndbuf_used; atomic_t rmem_alloc; wait_queue_head_t wait; __u32 frag_point; __u32 user_frag; int init_err_counter; int init_cycle; __u16 default_stream; __u16 default_flags; __u32 default_ppid; __u32 default_context; __u32 default_timetolive; __u32 default_rcv_context; struct sctp_stream stream; struct sctp_outq outqueue; struct sctp_ulpq ulpq; __u32 last_ecne_tsn; __u32 last_cwr_tsn; int numduptsns; struct sctp_chunk *addip_last_asconf; struct list_head asconf_ack_list; struct list_head addip_chunk_list; __u32 addip_serial; int src_out_of_asoc_ok; union sctp_addr *asconf_addr_del_pending; struct sctp_transport *new_transport; struct list_head endpoint_shared_keys; struct sctp_auth_bytes *asoc_shared_key; struct sctp_shared_key *shkey; __u16 default_hmac_id; __u16 active_key_id; __u8 need_ecne: 1; __u8 temp: 1; __u8 pf_expose: 2; __u8 force_delay: 1; __u8 strreset_enable; __u8 strreset_outstanding; __u32 strreset_outseq; __u32 strreset_inseq; __u32 strreset_result[2]; struct sctp_chunk *strreset_chunk; struct sctp_priv_assoc_stats stats; int sent_cnt_removable; __u16 subscribe; __u64 abandoned_unsent[3]; __u64 abandoned_sent[3]; struct callback_head rcu; }; struct sctp_auth_bytes { refcount_t refcnt; __u32 len; __u8 data[0]; }; struct sctp_shared_key { struct list_head key_list; struct sctp_auth_bytes *key; refcount_t refcnt; __u16 key_id; __u8 deactivated; }; enum { SCTP_MAX_STREAM = 65535, }; enum sctp_event_timeout { SCTP_EVENT_TIMEOUT_NONE = 0, SCTP_EVENT_TIMEOUT_T1_COOKIE = 1, SCTP_EVENT_TIMEOUT_T1_INIT = 2, SCTP_EVENT_TIMEOUT_T2_SHUTDOWN = 3, SCTP_EVENT_TIMEOUT_T3_RTX = 4, SCTP_EVENT_TIMEOUT_T4_RTO = 5, SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD = 6, SCTP_EVENT_TIMEOUT_HEARTBEAT = 7, SCTP_EVENT_TIMEOUT_RECONF = 8, SCTP_EVENT_TIMEOUT_PROBE = 9, SCTP_EVENT_TIMEOUT_SACK = 10, SCTP_EVENT_TIMEOUT_AUTOCLOSE = 11, }; enum { SCTP_MAX_DUP_TSNS = 16, }; enum sctp_scope { SCTP_SCOPE_GLOBAL = 0, SCTP_SCOPE_PRIVATE = 1, SCTP_SCOPE_LINK = 2, SCTP_SCOPE_LOOPBACK = 3, SCTP_SCOPE_UNUSABLE = 4, }; enum { SCTP_AUTH_HMAC_ID_RESERVED_0 = 0, SCTP_AUTH_HMAC_ID_SHA1 = 1, SCTP_AUTH_HMAC_ID_RESERVED_2 = 2, SCTP_AUTH_HMAC_ID_SHA256 = 3, __SCTP_AUTH_HMAC_MAX = 4, }; struct sctp_ulpevent { struct sctp_association *asoc; struct sctp_chunk *chunk; unsigned int rmem_len; union { __u32 mid; __u16 ssn; }; union { __u32 ppid; __u32 fsn; }; __u32 tsn; __u32 cumtsn; __u16 stream; __u16 flags; __u16 msg_flags; } __attribute__((packed)); union sctp_addr_param; union sctp_params { void *v; struct sctp_paramhdr *p; struct sctp_cookie_preserve_param *life; struct sctp_hostname_param *dns; struct sctp_cookie_param *cookie; struct sctp_supported_addrs_param *sat; struct sctp_ipv4addr_param *v4; struct sctp_ipv6addr_param *v6; union sctp_addr_param *addr; struct sctp_adaptation_ind_param *aind; struct sctp_supported_ext_param *ext; struct sctp_random_param *random; struct sctp_chunks_param *chunks; struct sctp_hmac_algo_param *hmac_algo; struct sctp_addip_param *addip; }; struct sctp_sender_hb_info; struct sctp_signed_cookie; struct sctp_datamsg; struct sctp_chunk { struct list_head list; refcount_t refcnt; int sent_count; union { struct list_head transmitted_list; struct list_head stream_list; }; struct list_head frag_list; struct sk_buff *skb; union { struct sk_buff *head_skb; struct sctp_shared_key *shkey; }; union sctp_params param_hdr; union { __u8 *v; struct sctp_datahdr *data_hdr; struct sctp_inithdr *init_hdr; struct sctp_sackhdr *sack_hdr; struct sctp_heartbeathdr *hb_hdr; struct sctp_sender_hb_info *hbs_hdr; struct sctp_shutdownhdr *shutdown_hdr; struct sctp_signed_cookie *cookie_hdr; struct sctp_ecnehdr *ecne_hdr; struct sctp_cwrhdr *ecn_cwr_hdr; struct sctp_errhdr *err_hdr; struct sctp_addiphdr *addip_hdr; struct sctp_fwdtsn_hdr *fwdtsn_hdr; struct sctp_authhdr *auth_hdr; struct sctp_idatahdr *idata_hdr; struct sctp_ifwdtsn_hdr *ifwdtsn_hdr; } subh; __u8 *chunk_end; struct sctp_chunkhdr *chunk_hdr; struct sctphdr *sctp_hdr; struct sctp_sndrcvinfo sinfo; struct sctp_association *asoc; struct sctp_ep_common *rcvr; long unsigned int sent_at; union sctp_addr source; union sctp_addr dest; struct sctp_datamsg *msg; struct sctp_transport *transport; struct sk_buff *auth_chunk; __u16 rtt_in_progress: 1; __u16 has_tsn: 1; __u16 has_ssn: 1; __u16 singleton: 1; __u16 end_of_packet: 1; __u16 ecn_ce_done: 1; __u16 pdiscard: 1; __u16 tsn_gap_acked: 1; __u16 data_accepted: 1; __u16 auth: 1; __u16 has_asconf: 1; __u16 pmtu_probe: 1; __u16 tsn_missing_report: 2; __u16 fast_retransmit: 2; }; struct sctp_stream_interleave { __u16 data_chunk_len; __u16 ftsn_chunk_len; struct sctp_chunk * (*make_datafrag)(const struct sctp_association *, const struct sctp_sndrcvinfo *, int, __u8, gfp_t); void (*assign_number)(struct sctp_chunk *); bool (*validate_data)(struct sctp_chunk *); int (*ulpevent_data)(struct sctp_ulpq *, struct sctp_chunk *, gfp_t); int (*enqueue_event)(struct sctp_ulpq *, struct sctp_ulpevent *); void (*renege_events)(struct sctp_ulpq *, struct sctp_chunk *, gfp_t); void (*start_pd)(struct sctp_ulpq *, gfp_t); void (*abort_pd)(struct sctp_ulpq *, gfp_t); void (*generate_ftsn)(struct sctp_outq *, __u32); bool (*validate_ftsn)(struct sctp_chunk *); void (*report_ftsn)(struct sctp_ulpq *, __u32); void (*handle_ftsn)(struct sctp_ulpq *, struct sctp_chunk *); }; struct sctp_bind_bucket { short unsigned int port; signed char fastreuse; signed char fastreuseport; kuid_t fastuid; struct hlist_node node; struct hlist_head owner; struct net *net; }; enum sctp_socket_type { SCTP_SOCKET_UDP = 0, SCTP_SOCKET_UDP_HIGH_BANDWIDTH = 1, SCTP_SOCKET_TCP = 2, }; struct sctp_pf; struct sctp_sock { struct inet_sock inet; enum sctp_socket_type type; int: 32; struct sctp_pf *pf; struct crypto_shash___2 *hmac; char *sctp_hmac_alg; struct sctp_endpoint *ep; struct sctp_bind_bucket *bind_hash; __u16 default_stream; short: 16; __u32 default_ppid; __u16 default_flags; short: 16; __u32 default_context; __u32 default_timetolive; __u32 default_rcv_context; int max_burst; __u32 hbinterval; __u32 probe_interval; __be16 udp_port; __be16 encap_port; __u16 pathmaxrxt; short: 16; __u32 flowlabel; __u8 dscp; char: 8; __u16 pf_retrans; __u16 ps_retrans; short: 16; __u32 pathmtu; __u32 sackdelay; __u32 sackfreq; __u32 param_flags; __u32 default_ss; struct sctp_rtoinfo rtoinfo; struct sctp_paddrparams paddrparam; struct sctp_assocparams assocparams; __u16 subscribe; struct sctp_initmsg initmsg; short: 16; int user_frag; __u32 autoclose; __u32 adaptation_ind; __u32 pd_point; __u16 nodelay: 1; __u16 pf_expose: 2; __u16 reuse: 1; __u16 disable_fragments: 1; __u16 v4mapped: 1; __u16 frag_interleave: 1; __u16 recvrcvinfo: 1; __u16 recvnxtinfo: 1; __u16 data_ready_signalled: 1; int: 22; atomic_t pd_mode; struct sk_buff_head pd_lobby; struct list_head auto_asconf_list; int do_auto_asconf; int: 32; } __attribute__((packed)); struct sctp_af; struct sctp_pf { void (*event_msgname)(struct sctp_ulpevent *, char *, int *); void (*skb_msgname)(struct sk_buff *, char *, int *); int (*af_supported)(sa_family_t, struct sctp_sock *); int (*cmp_addr)(const union sctp_addr *, const union sctp_addr *, struct sctp_sock *); int (*bind_verify)(struct sctp_sock *, union sctp_addr *); int (*send_verify)(struct sctp_sock *, union sctp_addr *); int (*supported_addrs)(const struct sctp_sock *, __be16 *); struct sock * (*create_accept_sk)(struct sock *, struct sctp_association *, bool); int (*addr_to_user)(struct sctp_sock *, union sctp_addr *); void (*to_sk_saddr)(union sctp_addr *, struct sock *); void (*to_sk_daddr)(union sctp_addr *, struct sock *); void (*copy_ip_options)(struct sock *, struct sock *); struct sctp_af *af; }; struct sctp_signed_cookie { __u8 signature[32]; __u32 __pad; struct sctp_cookie c; } __attribute__((packed)); union sctp_addr_param { struct sctp_paramhdr p; struct sctp_ipv4addr_param v4; struct sctp_ipv6addr_param v6; }; struct sctp_sender_hb_info { struct sctp_paramhdr param_hdr; union sctp_addr daddr; long unsigned int sent_at; __u64 hb_nonce; __u32 probe_size; }; struct sctp_af { int (*sctp_xmit)(struct sk_buff *, struct sctp_transport *); int (*setsockopt)(struct sock *, int, int, sockptr_t, unsigned int); int (*getsockopt)(struct sock *, int, int, char *, int *); void (*get_dst)(struct sctp_transport *, union sctp_addr *, struct flowi *, struct sock *); void (*get_saddr)(struct sctp_sock *, struct sctp_transport *, struct flowi *); void (*copy_addrlist)(struct list_head *, struct net_device *); int (*cmp_addr)(const union sctp_addr *, const union sctp_addr *); void (*addr_copy)(union sctp_addr *, union sctp_addr *); void (*from_skb)(union sctp_addr *, struct sk_buff *, int); void (*from_sk)(union sctp_addr *, struct sock *); bool (*from_addr_param)(union sctp_addr *, union sctp_addr_param *, __be16, int); int (*to_addr_param)(const union sctp_addr *, union sctp_addr_param *); int (*addr_valid)(union sctp_addr *, struct sctp_sock *, const struct sk_buff *); enum sctp_scope (*scope)(union sctp_addr *); void (*inaddr_any)(union sctp_addr *, __be16); int (*is_any)(const union sctp_addr *); int (*available)(union sctp_addr *, struct sctp_sock *); int (*skb_iif)(const struct sk_buff *); int (*is_ce)(const struct sk_buff *); void (*seq_dump_addr)(struct seq_file *, union sctp_addr *); void (*ecn_capable)(struct sock *); __u16 net_header_len; int sockaddr_len; int (*ip_options_len)(struct sock *); sa_family_t sa_family; struct list_head list; }; struct sctp_packet { __u16 source_port; __u16 destination_port; __u32 vtag; struct list_head chunk_list; size_t overhead; size_t size; size_t max_size; struct sctp_transport *transport; struct sctp_chunk *auth; u8 has_cookie_echo: 1; u8 has_sack: 1; u8 has_auth: 1; u8 has_data: 1; u8 ipfragok: 1; }; struct sctp_transport { struct list_head transports; struct rhlist_head node; refcount_t refcnt; __u32 rto_pending: 1; __u32 hb_sent: 1; __u32 pmtu_pending: 1; __u32 dst_pending_confirm: 1; __u32 sack_generation: 1; u32 dst_cookie; struct flowi fl; union sctp_addr ipaddr; struct sctp_af *af_specific; struct sctp_association *asoc; long unsigned int rto; __u32 rtt; __u32 rttvar; __u32 srtt; __u32 cwnd; __u32 ssthresh; __u32 partial_bytes_acked; __u32 flight_size; __u32 burst_limited; struct dst_entry *dst; union sctp_addr saddr; long unsigned int hbinterval; long unsigned int probe_interval; long unsigned int sackdelay; __u32 sackfreq; atomic_t mtu_info; ktime_t last_time_heard; long unsigned int last_time_sent; long unsigned int last_time_ecne_reduced; __be16 encap_port; __u16 pathmaxrxt; __u32 flowlabel; __u8 dscp; __u16 pf_retrans; __u16 ps_retrans; __u32 pathmtu; __u32 param_flags; int init_sent_count; int state; short unsigned int error_count; struct timer_list T3_rtx_timer; struct timer_list hb_timer; struct timer_list proto_unreach_timer; struct timer_list reconf_timer; struct timer_list probe_timer; struct list_head transmitted; struct sctp_packet packet; struct list_head send_ready; struct { __u32 next_tsn_at_change; char changeover_active; char cycling_changeover; char cacc_saw_newack; } cacc; struct { __u32 last_rtx_chunks; __u16 pmtu; __u16 probe_size; __u16 probe_high; __u8 probe_count: 3; __u8 raise_count: 5; __u8 state; } pl; __u64 hb_nonce; struct callback_head rcu; }; struct sctp_datamsg { struct list_head chunks; refcount_t refcnt; long unsigned int expires_at; int send_error; u8 send_failed: 1; u8 can_delay: 1; u8 abandoned: 1; }; struct sctp_stream_priorities { struct list_head prio_sched; struct list_head active; struct sctp_stream_out_ext *next; __u16 prio; }; struct sctp_stream_out_ext { __u64 abandoned_unsent[3]; __u64 abandoned_sent[3]; struct list_head outq; union { struct { struct list_head prio_list; struct sctp_stream_priorities *prio_head; }; struct { struct list_head rr_list; }; }; }; struct task_security_struct { u32 osid; u32 sid; u32 exec_sid; u32 create_sid; u32 keycreate_sid; u32 sockcreate_sid; }; enum label_initialized { LABEL_INVALID = 0, LABEL_INITIALIZED = 1, LABEL_PENDING = 2, }; struct inode_security_struct { struct inode *inode; struct list_head list; u32 task_sid; u32 sid; u16 sclass; unsigned char initialized; spinlock_t lock; }; struct file_security_struct { u32 sid; u32 fown_sid; u32 isid; u32 pseqno; }; struct superblock_security_struct { u32 sid; u32 def_sid; u32 mntpoint_sid; short unsigned int behavior; short unsigned int flags; struct mutex lock; struct list_head isec_head; spinlock_t isec_lock; }; struct msg_security_struct { u32 sid; }; struct ipc_security_struct { u16 sclass; u32 sid; }; struct sk_security_struct { enum { NLBL_UNSET = 0, NLBL_REQUIRE = 1, NLBL_LABELED = 2, NLBL_REQSKB = 3, NLBL_CONNLABELED = 4, } nlbl_state; struct netlbl_lsm_secattr *nlbl_secattr; u32 sid; u32 peer_sid; u16 sclass; enum { SCTP_ASSOC_UNSET = 0, SCTP_ASSOC_SET = 1, } sctp_assoc_state; }; struct tun_security_struct { u32 sid; }; struct key_security_struct { u32 sid; }; struct ib_security_struct { u32 sid; }; struct bpf_security_struct { u32 sid; }; struct perf_event_security_struct { u32 sid; }; struct selinux_mnt_opts { const char *fscontext; const char *context; const char *rootcontext; const char *defcontext; }; enum { Opt_error = 4294967295, Opt_context = 0, Opt_defcontext = 1, Opt_fscontext = 2, Opt_rootcontext = 3, Opt_seclabel = 4, }; struct selinux_policy_convert_data; struct selinux_load_state { struct selinux_policy *policy; struct selinux_policy_convert_data *convert_data; }; enum sel_inos { SEL_ROOT_INO = 2, SEL_LOAD = 3, SEL_ENFORCE = 4, SEL_CONTEXT = 5, SEL_ACCESS = 6, SEL_CREATE = 7, SEL_RELABEL = 8, SEL_USER = 9, SEL_POLICYVERS = 10, SEL_COMMIT_BOOLS = 11, SEL_MLS = 12, SEL_DISABLE = 13, SEL_MEMBER = 14, SEL_CHECKREQPROT = 15, SEL_COMPAT_NET = 16, SEL_REJECT_UNKNOWN = 17, SEL_DENY_UNKNOWN = 18, SEL_STATUS = 19, SEL_POLICY = 20, SEL_VALIDATE_TRANS = 21, SEL_INO_NEXT = 22, }; struct selinux_fs_info { struct dentry *bool_dir; unsigned int bool_num; char **bool_pending_names; unsigned int *bool_pending_values; struct dentry *class_dir; long unsigned int last_class_ino; bool policy_opened; struct dentry *policycap_dir; long unsigned int last_ino; struct selinux_state *state; struct super_block *sb; }; struct policy_load_memory { size_t len; void *data; }; enum { SELNL_MSG_SETENFORCE = 16, SELNL_MSG_POLICYLOAD = 17, SELNL_MSG_MAX = 18, }; enum selinux_nlgroups { SELNLGRP_NONE = 0, SELNLGRP_AVC = 1, __SELNLGRP_MAX = 2, }; struct selnl_msg_setenforce { __s32 val; }; struct selnl_msg_policyload { __u32 seqno; }; enum { XFRM_MSG_BASE = 16, XFRM_MSG_NEWSA = 16, XFRM_MSG_DELSA = 17, XFRM_MSG_GETSA = 18, XFRM_MSG_NEWPOLICY = 19, XFRM_MSG_DELPOLICY = 20, XFRM_MSG_GETPOLICY = 21, XFRM_MSG_ALLOCSPI = 22, XFRM_MSG_ACQUIRE = 23, XFRM_MSG_EXPIRE = 24, XFRM_MSG_UPDPOLICY = 25, XFRM_MSG_UPDSA = 26, XFRM_MSG_POLEXPIRE = 27, XFRM_MSG_FLUSHSA = 28, XFRM_MSG_FLUSHPOLICY = 29, XFRM_MSG_NEWAE = 30, XFRM_MSG_GETAE = 31, XFRM_MSG_REPORT = 32, XFRM_MSG_MIGRATE = 33, XFRM_MSG_NEWSADINFO = 34, XFRM_MSG_GETSADINFO = 35, XFRM_MSG_NEWSPDINFO = 36, XFRM_MSG_GETSPDINFO = 37, XFRM_MSG_MAPPING = 38, __XFRM_MSG_MAX = 39, }; enum { RTM_BASE = 16, RTM_NEWLINK = 16, RTM_DELLINK = 17, RTM_GETLINK = 18, RTM_SETLINK = 19, RTM_NEWADDR = 20, RTM_DELADDR = 21, RTM_GETADDR = 22, RTM_NEWROUTE = 24, RTM_DELROUTE = 25, RTM_GETROUTE = 26, RTM_NEWNEIGH = 28, RTM_DELNEIGH = 29, RTM_GETNEIGH = 30, RTM_NEWRULE = 32, RTM_DELRULE = 33, RTM_GETRULE = 34, RTM_NEWQDISC = 36, RTM_DELQDISC = 37, RTM_GETQDISC = 38, RTM_NEWTCLASS = 40, RTM_DELTCLASS = 41, RTM_GETTCLASS = 42, RTM_NEWTFILTER = 44, RTM_DELTFILTER = 45, RTM_GETTFILTER = 46, RTM_NEWACTION = 48, RTM_DELACTION = 49, RTM_GETACTION = 50, RTM_NEWPREFIX = 52, RTM_GETMULTICAST = 58, RTM_GETANYCAST = 62, RTM_NEWNEIGHTBL = 64, RTM_GETNEIGHTBL = 66, RTM_SETNEIGHTBL = 67, RTM_NEWNDUSEROPT = 68, RTM_NEWADDRLABEL = 72, RTM_DELADDRLABEL = 73, RTM_GETADDRLABEL = 74, RTM_GETDCB = 78, RTM_SETDCB = 79, RTM_NEWNETCONF = 80, RTM_DELNETCONF = 81, RTM_GETNETCONF = 82, RTM_NEWMDB = 84, RTM_DELMDB = 85, RTM_GETMDB = 86, RTM_NEWNSID = 88, RTM_DELNSID = 89, RTM_GETNSID = 90, RTM_NEWSTATS = 92, RTM_GETSTATS = 94, RTM_NEWCACHEREPORT = 96, RTM_NEWCHAIN = 100, RTM_DELCHAIN = 101, RTM_GETCHAIN = 102, RTM_NEWNEXTHOP = 104, RTM_DELNEXTHOP = 105, RTM_GETNEXTHOP = 106, RTM_NEWLINKPROP = 108, RTM_DELLINKPROP = 109, RTM_GETLINKPROP = 110, RTM_NEWVLAN = 112, RTM_DELVLAN = 113, RTM_GETVLAN = 114, RTM_NEWNEXTHOPBUCKET = 116, RTM_DELNEXTHOPBUCKET = 117, RTM_GETNEXTHOPBUCKET = 118, __RTM_MAX = 119, }; struct nlmsg_perm { u16 nlmsg_type; u32 perm; }; struct netif_security_struct { struct net *ns; int ifindex; u32 sid; }; struct sel_netif { struct list_head list; struct netif_security_struct nsec; struct callback_head callback_head; }; struct netnode_security_struct { union { __be32 ipv4; struct in6_addr ipv6; } addr; u32 sid; u16 family; }; struct sel_netnode_bkt { unsigned int size; struct list_head list; }; struct sel_netnode { struct netnode_security_struct nsec; struct list_head list; struct callback_head rcu; }; struct netport_security_struct { u32 sid; u16 port; u8 protocol; }; struct sel_netport_bkt { int size; struct list_head list; }; struct sel_netport { struct netport_security_struct psec; struct list_head list; struct callback_head rcu; }; struct selinux_kernel_status { u32 version; u32 sequence; u32 enforcing; u32 policyload; u32 deny_unknown; }; struct ebitmap_node { struct ebitmap_node *next; long unsigned int maps[6]; u32 startbit; }; struct ebitmap { struct ebitmap_node *node; u32 highbit; }; struct policy_file { char *data; size_t len; }; struct hashtab_node { void *key; void *datum; struct hashtab_node *next; }; struct hashtab { struct hashtab_node **htable; u32 size; u32 nel; }; struct hashtab_info { u32 slots_used; u32 max_chain_len; }; struct hashtab_key_params { u32 (*hash)(const void *); int (*cmp)(const void *, const void *); }; struct symtab { struct hashtab table; u32 nprim; }; struct mls_level { u32 sens; struct ebitmap cat; }; struct mls_range { struct mls_level level[2]; }; struct context___2 { u32 user; u32 role; u32 type; u32 len; struct mls_range range; char *str; }; struct sidtab_str_cache; struct sidtab_entry { u32 sid; u32 hash; struct context___2 context; struct sidtab_str_cache *cache; struct hlist_node list; }; struct sidtab_str_cache { struct callback_head rcu_member; struct list_head lru_member; struct sidtab_entry *parent; u32 len; char str[0]; }; struct sidtab_node_inner; struct sidtab_node_leaf; union sidtab_entry_inner { struct sidtab_node_inner *ptr_inner; struct sidtab_node_leaf *ptr_leaf; }; struct sidtab_node_inner { union sidtab_entry_inner entries[512]; }; struct sidtab_node_leaf { struct sidtab_entry entries[39]; }; struct sidtab_isid_entry { int set; struct sidtab_entry entry; }; struct sidtab; struct sidtab_convert_params { int (*func)(struct context___2 *, struct context___2 *, void *); void *args; struct sidtab *target; }; struct sidtab { union sidtab_entry_inner roots[4]; u32 count; struct sidtab_convert_params *convert; bool frozen; spinlock_t lock; u32 cache_free_slots; struct list_head cache_lru_list; spinlock_t cache_lock; struct sidtab_isid_entry isids[27]; struct hlist_head context_to_sid[512]; }; struct avtab_key { u16 source_type; u16 target_type; u16 target_class; u16 specified; }; struct avtab_extended_perms { u8 specified; u8 driver; struct extended_perms_data perms; }; struct avtab_datum { union { u32 data; struct avtab_extended_perms *xperms; } u; }; struct avtab_node { struct avtab_key key; struct avtab_datum datum; struct avtab_node *next; }; struct avtab { struct avtab_node **htable; u32 nel; u32 nslot; u32 mask; }; struct type_set; struct constraint_expr { u32 expr_type; u32 attr; u32 op; struct ebitmap names; struct type_set *type_names; struct constraint_expr *next; }; struct type_set { struct ebitmap types; struct ebitmap negset; u32 flags; }; struct constraint_node { u32 permissions; struct constraint_expr *expr; struct constraint_node *next; }; struct common_datum { u32 value; struct symtab permissions; }; struct class_datum { u32 value; char *comkey; struct common_datum *comdatum; struct symtab permissions; struct constraint_node *constraints; struct constraint_node *validatetrans; char default_user; char default_role; char default_type; char default_range; }; struct role_datum { u32 value; u32 bounds; struct ebitmap dominates; struct ebitmap types; }; struct role_allow { u32 role; u32 new_role; struct role_allow *next; }; struct type_datum { u32 value; u32 bounds; unsigned char primary; unsigned char attribute; }; struct user_datum { u32 value; u32 bounds; struct ebitmap roles; struct mls_range range; struct mls_level dfltlevel; }; struct cond_bool_datum { __u32 value; int state; }; struct ocontext { union { char *name; struct { u8 protocol; u16 low_port; u16 high_port; } port; struct { u32 addr; u32 mask; } node; struct { u32 addr[4]; u32 mask[4]; } node6; struct { u64 subnet_prefix; u16 low_pkey; u16 high_pkey; } ibpkey; struct { char *dev_name; u8 port; } ibendport; } u; union { u32 sclass; u32 behavior; } v; struct context___2 context[2]; u32 sid[2]; struct ocontext *next; }; struct genfs { char *fstype; struct ocontext *head; struct genfs *next; }; struct cond_node; struct policydb { int mls_enabled; struct symtab symtab[8]; char **sym_val_to_name[8]; struct class_datum **class_val_to_struct; struct role_datum **role_val_to_struct; struct user_datum **user_val_to_struct; struct type_datum **type_val_to_struct; struct avtab te_avtab; struct hashtab role_tr; struct ebitmap filename_trans_ttypes; struct hashtab filename_trans; u32 compat_filename_trans_count; struct cond_bool_datum **bool_val_to_struct; struct avtab te_cond_avtab; struct cond_node *cond_list; u32 cond_list_len; struct role_allow *role_allow; struct ocontext *ocontexts[9]; struct genfs *genfs; struct hashtab range_tr; struct ebitmap *type_attr_map_array; struct ebitmap policycaps; struct ebitmap permissive_map; size_t len; unsigned int policyvers; unsigned int reject_unknown: 1; unsigned int allow_unknown: 1; u16 process_class; u32 process_trans_perms; }; struct perm_datum { u32 value; }; struct role_trans_key { u32 role; u32 type; u32 tclass; }; struct role_trans_datum { u32 new_role; }; struct filename_trans_key { u32 ttype; u16 tclass; const char *name; }; struct filename_trans_datum { struct ebitmap stypes; u32 otype; struct filename_trans_datum *next; }; struct level_datum { struct mls_level *level; unsigned char isalias; }; struct cat_datum { u32 value; unsigned char isalias; }; struct range_trans { u32 source_type; u32 target_type; u32 target_class; }; struct cond_expr_node; struct cond_expr { struct cond_expr_node *nodes; u32 len; }; struct cond_av_list { struct avtab_node **nodes; u32 len; }; struct cond_node { int cur_state; struct cond_expr expr; struct cond_av_list true_list; struct cond_av_list false_list; }; struct policy_data { struct policydb *p; void *fp; }; struct cond_expr_node { u32 expr_type; u32 bool; }; struct policydb_compat_info { int version; int sym_num; int ocon_num; }; struct selinux_mapping; struct selinux_map { struct selinux_mapping *mapping; u16 size; }; struct selinux_policy { struct sidtab *sidtab; struct policydb policydb; struct selinux_map map; u32 latest_granting; }; struct convert_context_args { struct selinux_state *state; struct policydb *oldp; struct policydb *newp; }; struct selinux_policy_convert_data { struct convert_context_args args; struct sidtab_convert_params sidtab_params; }; struct selinux_mapping { u16 value; unsigned int num_perms; u32 perms[32]; }; struct selinux_audit_rule { u32 au_seqno; struct context___2 au_ctxt; }; struct cond_insertf_data { struct policydb *p; struct avtab_node **dst; struct cond_av_list *other; }; struct rt6key { struct in6_addr addr; int plen; }; struct rtable; struct fnhe_hash_bucket; struct fib_nh_common { struct net_device *nhc_dev; int nhc_oif; unsigned char nhc_scope; u8 nhc_family; u8 nhc_gw_family; unsigned char nhc_flags; struct lwtunnel_state *nhc_lwtstate; union { __be32 ipv4; struct in6_addr ipv6; } nhc_gw; int nhc_weight; atomic_t nhc_upper_bound; struct rtable **nhc_pcpu_rth_output; struct rtable *nhc_rth_input; struct fnhe_hash_bucket *nhc_exceptions; }; struct rt6_exception_bucket; struct fib6_nh { struct fib_nh_common nh_common; long unsigned int last_probe; struct rt6_info **rt6i_pcpu; struct rt6_exception_bucket *rt6i_exception_bucket; }; struct fib6_node; struct dst_metrics; struct nexthop; struct fib6_info { struct fib6_table *fib6_table; struct fib6_info *fib6_next; struct fib6_node *fib6_node; union { struct list_head fib6_siblings; struct list_head nh_list; }; unsigned int fib6_nsiblings; refcount_t fib6_ref; long unsigned int expires; struct dst_metrics *fib6_metrics; struct rt6key fib6_dst; u32 fib6_flags; struct rt6key fib6_src; struct rt6key fib6_prefsrc; u32 fib6_metric; u8 fib6_protocol; u8 fib6_type; u8 should_flush: 1; u8 dst_nocount: 1; u8 dst_nopolicy: 1; u8 fib6_destroying: 1; u8 offload: 1; u8 trap: 1; u8 offload_failed: 1; u8 unused: 1; struct callback_head rcu; struct nexthop *nh; struct fib6_nh fib6_nh[0]; }; struct uncached_list; struct rt6_info { struct dst_entry dst; struct fib6_info *from; int sernum; struct rt6key rt6i_dst; struct rt6key rt6i_src; struct in6_addr rt6i_gateway; struct inet6_dev *rt6i_idev; u32 rt6i_flags; struct list_head rt6i_uncached; struct uncached_list *rt6i_uncached_list; short unsigned int rt6i_nfheader_len; }; struct rt6_statistics { __u32 fib_nodes; __u32 fib_route_nodes; __u32 fib_rt_entries; __u32 fib_rt_cache; __u32 fib_discarded_routes; atomic_t fib_rt_alloc; atomic_t fib_rt_uncache; }; struct fib6_node { struct fib6_node *parent; struct fib6_node *left; struct fib6_node *right; struct fib6_node *subtree; struct fib6_info *leaf; __u16 fn_bit; __u16 fn_flags; int fn_sernum; struct fib6_info *rr_ptr; struct callback_head rcu; }; struct fib6_table { struct hlist_node tb6_hlist; u32 tb6_id; spinlock_t tb6_lock; struct fib6_node tb6_root; struct inet_peer_base tb6_peers; unsigned int flags; unsigned int fib_seq; }; typedef union { __be32 a4; __be32 a6[4]; struct in6_addr in6; } xfrm_address_t; struct xfrm_id { xfrm_address_t daddr; __be32 spi; __u8 proto; }; struct xfrm_selector { xfrm_address_t daddr; xfrm_address_t saddr; __be16 dport; __be16 dport_mask; __be16 sport; __be16 sport_mask; __u16 family; __u8 prefixlen_d; __u8 prefixlen_s; __u8 proto; int ifindex; __kernel_uid32_t user; }; struct xfrm_lifetime_cfg { __u64 soft_byte_limit; __u64 hard_byte_limit; __u64 soft_packet_limit; __u64 hard_packet_limit; __u64 soft_add_expires_seconds; __u64 hard_add_expires_seconds; __u64 soft_use_expires_seconds; __u64 hard_use_expires_seconds; }; struct xfrm_lifetime_cur { __u64 bytes; __u64 packets; __u64 add_time; __u64 use_time; }; struct xfrm_replay_state { __u32 oseq; __u32 seq; __u32 bitmap; }; struct xfrm_replay_state_esn { unsigned int bmp_len; __u32 oseq; __u32 seq; __u32 oseq_hi; __u32 seq_hi; __u32 replay_window; __u32 bmp[0]; }; struct xfrm_algo { char alg_name[64]; unsigned int alg_key_len; char alg_key[0]; }; struct xfrm_algo_auth { char alg_name[64]; unsigned int alg_key_len; unsigned int alg_trunc_len; char alg_key[0]; }; struct xfrm_algo_aead { char alg_name[64]; unsigned int alg_key_len; unsigned int alg_icv_len; char alg_key[0]; }; struct xfrm_stats { __u32 replay_window; __u32 replay; __u32 integrity_failed; }; enum { XFRM_POLICY_TYPE_MAIN = 0, XFRM_POLICY_TYPE_SUB = 1, XFRM_POLICY_TYPE_MAX = 2, XFRM_POLICY_TYPE_ANY = 255, }; struct xfrm_encap_tmpl { __u16 encap_type; __be16 encap_sport; __be16 encap_dport; xfrm_address_t encap_oa; }; enum xfrm_attr_type_t { XFRMA_UNSPEC = 0, XFRMA_ALG_AUTH = 1, XFRMA_ALG_CRYPT = 2, XFRMA_ALG_COMP = 3, XFRMA_ENCAP = 4, XFRMA_TMPL = 5, XFRMA_SA = 6, XFRMA_POLICY = 7, XFRMA_SEC_CTX = 8, XFRMA_LTIME_VAL = 9, XFRMA_REPLAY_VAL = 10, XFRMA_REPLAY_THRESH = 11, XFRMA_ETIMER_THRESH = 12, XFRMA_SRCADDR = 13, XFRMA_COADDR = 14, XFRMA_LASTUSED = 15, XFRMA_POLICY_TYPE = 16, XFRMA_MIGRATE = 17, XFRMA_ALG_AEAD = 18, XFRMA_KMADDRESS = 19, XFRMA_ALG_AUTH_TRUNC = 20, XFRMA_MARK = 21, XFRMA_TFCPAD = 22, XFRMA_REPLAY_ESN_VAL = 23, XFRMA_SA_EXTRA_FLAGS = 24, XFRMA_PROTO = 25, XFRMA_ADDRESS_FILTER = 26, XFRMA_PAD = 27, XFRMA_OFFLOAD_DEV = 28, XFRMA_SET_MARK = 29, XFRMA_SET_MARK_MASK = 30, XFRMA_IF_ID = 31, __XFRMA_MAX = 32, }; struct xfrm_mark { __u32 v; __u32 m; }; struct xfrm_address_filter { xfrm_address_t saddr; xfrm_address_t daddr; __u16 family; __u8 splen; __u8 dplen; }; struct xfrm_state_walk { struct list_head all; u8 state; u8 dying; u8 proto; u32 seq; struct xfrm_address_filter *filter; }; enum xfrm_replay_mode { XFRM_REPLAY_MODE_LEGACY = 0, XFRM_REPLAY_MODE_BMP = 1, XFRM_REPLAY_MODE_ESN = 2, }; struct xfrm_state_offload { struct net_device *dev; struct net_device *real_dev; long unsigned int offload_handle; unsigned int num_exthdrs; u8 flags; }; struct xfrm_mode { u8 encap; u8 family; u8 flags; }; struct xfrm_type; struct xfrm_type_offload; struct xfrm_state { possible_net_t xs_net; union { struct hlist_node gclist; struct hlist_node bydst; }; struct hlist_node bysrc; struct hlist_node byspi; struct hlist_node byseq; refcount_t refcnt; spinlock_t lock; struct xfrm_id id; struct xfrm_selector sel; struct xfrm_mark mark; u32 if_id; u32 tfcpad; u32 genid; struct xfrm_state_walk km; struct { u32 reqid; u8 mode; u8 replay_window; u8 aalgo; u8 ealgo; u8 calgo; u8 flags; u16 family; xfrm_address_t saddr; int header_len; int trailer_len; u32 extra_flags; struct xfrm_mark smark; } props; struct xfrm_lifetime_cfg lft; struct xfrm_algo_auth *aalg; struct xfrm_algo *ealg; struct xfrm_algo *calg; struct xfrm_algo_aead *aead; const char *geniv; struct xfrm_encap_tmpl *encap; struct sock *encap_sk; xfrm_address_t *coaddr; struct xfrm_state *tunnel; atomic_t tunnel_users; struct xfrm_replay_state replay; struct xfrm_replay_state_esn *replay_esn; struct xfrm_replay_state preplay; struct xfrm_replay_state_esn *preplay_esn; enum xfrm_replay_mode repl_mode; u32 xflags; u32 replay_maxage; u32 replay_maxdiff; struct timer_list rtimer; struct xfrm_stats stats; struct xfrm_lifetime_cur curlft; struct hrtimer mtimer; struct xfrm_state_offload xso; long int saved_tmo; time64_t lastused; struct page_frag xfrag; const struct xfrm_type *type; struct xfrm_mode inner_mode; struct xfrm_mode inner_mode_iaf; struct xfrm_mode outer_mode; const struct xfrm_type_offload *type_offload; struct xfrm_sec_ctx *security; void *data; }; struct dst_metrics { u32 metrics[17]; refcount_t refcnt; }; struct xfrm_policy_walk_entry { struct list_head all; u8 dead; }; struct xfrm_policy_queue { struct sk_buff_head hold_queue; struct timer_list hold_timer; long unsigned int timeout; }; struct xfrm_tmpl { struct xfrm_id id; xfrm_address_t saddr; short unsigned int encap_family; u32 reqid; u8 mode; u8 share; u8 optional; u8 allalgs; u32 aalgos; u32 ealgos; u32 calgos; }; struct xfrm_policy { possible_net_t xp_net; struct hlist_node bydst; struct hlist_node byidx; rwlock_t lock; refcount_t refcnt; u32 pos; struct timer_list timer; atomic_t genid; u32 priority; u32 index; u32 if_id; struct xfrm_mark mark; struct xfrm_selector selector; struct xfrm_lifetime_cfg lft; struct xfrm_lifetime_cur curlft; struct xfrm_policy_walk_entry walk; struct xfrm_policy_queue polq; bool bydst_reinsert; u8 type; u8 action; u8 flags; u8 xfrm_nr; u16 family; struct xfrm_sec_ctx *security; struct xfrm_tmpl xfrm_vec[6]; struct hlist_node bydst_inexact_list; struct callback_head rcu; }; struct udp_hslot; struct udp_table { struct udp_hslot *hash; struct udp_hslot *hash2; unsigned int mask; unsigned int log; }; struct fib_nh_exception { struct fib_nh_exception *fnhe_next; int fnhe_genid; __be32 fnhe_daddr; u32 fnhe_pmtu; bool fnhe_mtu_locked; __be32 fnhe_gw; long unsigned int fnhe_expires; struct rtable *fnhe_rth_input; struct rtable *fnhe_rth_output; long unsigned int fnhe_stamp; struct callback_head rcu; }; struct rtable { struct dst_entry dst; int rt_genid; unsigned int rt_flags; __u16 rt_type; __u8 rt_is_input; __u8 rt_uses_gateway; int rt_iif; u8 rt_gw_family; union { __be32 rt_gw4; struct in6_addr rt_gw6; }; u32 rt_mtu_locked: 1; u32 rt_pmtu: 31; struct list_head rt_uncached; struct uncached_list *rt_uncached_list; }; struct fnhe_hash_bucket { struct fib_nh_exception *chain; }; struct rt6_exception_bucket { struct hlist_head chain; int depth; }; struct xfrm_type { struct module *owner; u8 proto; u8 flags; int (*init_state)(struct xfrm_state *); void (*destructor)(struct xfrm_state *); int (*input)(struct xfrm_state *, struct sk_buff *); int (*output)(struct xfrm_state *, struct sk_buff *); int (*reject)(struct xfrm_state *, struct sk_buff *, const struct flowi *); }; struct xfrm_type_offload { struct module *owner; u8 proto; void (*encap)(struct xfrm_state *, struct sk_buff *); int (*input_tail)(struct xfrm_state *, struct sk_buff *); int (*xmit)(struct xfrm_state *, struct sk_buff *, netdev_features_t); }; struct xfrm_dst { union { struct dst_entry dst; struct rtable rt; struct rt6_info rt6; } u; struct dst_entry *route; struct dst_entry *child; struct dst_entry *path; struct xfrm_policy *pols[2]; int num_pols; int num_xfrms; u32 xfrm_genid; u32 policy_genid; u32 route_mtu_cached; u32 child_mtu_cached; u32 route_cookie; u32 path_cookie; }; struct xfrm_offload { struct { __u32 low; __u32 hi; } seq; __u32 flags; __u32 status; __u8 proto; __u8 inner_ipproto; }; struct sec_path { int len; int olen; struct xfrm_state *xvec[6]; struct xfrm_offload ovec[1]; }; struct udp_hslot { struct hlist_head head; int count; spinlock_t lock; }; struct pkey_security_struct { u64 subnet_prefix; u16 pkey; u32 sid; }; struct sel_ib_pkey_bkt { int size; struct list_head list; }; struct sel_ib_pkey { struct pkey_security_struct psec; struct list_head list; struct callback_head rcu; }; struct smack_audit_data { const char *function; char *subject; char *object; char *request; int result; }; struct smack_known { struct list_head list; struct hlist_node smk_hashed; char *smk_known; u32 smk_secid; struct netlbl_lsm_secattr smk_netlabel; struct list_head smk_rules; struct mutex smk_rules_lock; }; struct superblock_smack { struct smack_known *smk_root; struct smack_known *smk_floor; struct smack_known *smk_hat; struct smack_known *smk_default; int smk_flags; }; struct socket_smack { struct smack_known *smk_out; struct smack_known *smk_in; struct smack_known *smk_packet; int smk_state; }; struct inode_smack { struct smack_known *smk_inode; struct smack_known *smk_task; struct smack_known *smk_mmap; int smk_flags; }; struct task_smack { struct smack_known *smk_task; struct smack_known *smk_forked; struct list_head smk_rules; struct mutex smk_rules_lock; struct list_head smk_relabel; }; struct smack_rule { struct list_head list; struct smack_known *smk_subject; struct smack_known *smk_object; int smk_access; }; struct smk_net4addr { struct list_head list; struct in_addr smk_host; struct in_addr smk_mask; int smk_masks; struct smack_known *smk_label; }; struct smk_net6addr { struct list_head list; struct in6_addr smk_host; struct in6_addr smk_mask; int smk_masks; struct smack_known *smk_label; }; struct smack_known_list_elem { struct list_head list; struct smack_known *smk_label; }; enum { Opt_error___2 = 4294967295, Opt_fsdefault = 0, Opt_fsfloor = 1, Opt_fshat = 2, Opt_fsroot = 3, Opt_fstransmute = 4, }; struct smk_audit_info { struct common_audit_data a; struct smack_audit_data sad; }; struct smack_mnt_opts { const char *fsdefault; const char *fsfloor; const char *fshat; const char *fsroot; const char *fstransmute; }; struct netlbl_audit { u32 secid; kuid_t loginuid; unsigned int sessionid; }; struct cipso_v4_std_map_tbl { struct { u32 *cipso; u32 *local; u32 cipso_size; u32 local_size; } lvl; struct { u32 *cipso; u32 *local; u32 cipso_size; u32 local_size; } cat; }; struct cipso_v4_doi { u32 doi; u32 type; union { struct cipso_v4_std_map_tbl *std; } map; u8 tags[5]; refcount_t refcount; struct list_head list; struct callback_head rcu; }; enum smk_inos { SMK_ROOT_INO = 2, SMK_LOAD = 3, SMK_CIPSO = 4, SMK_DOI = 5, SMK_DIRECT = 6, SMK_AMBIENT = 7, SMK_NET4ADDR = 8, SMK_ONLYCAP = 9, SMK_LOGGING = 10, SMK_LOAD_SELF = 11, SMK_ACCESSES = 12, SMK_MAPPED = 13, SMK_LOAD2 = 14, SMK_LOAD_SELF2 = 15, SMK_ACCESS2 = 16, SMK_CIPSO2 = 17, SMK_REVOKE_SUBJ = 18, SMK_CHANGE_RULE = 19, SMK_SYSLOG = 20, SMK_PTRACE = 21, SMK_UNCONFINED = 22, SMK_NET6ADDR = 23, SMK_RELABEL_SELF = 24, }; struct smack_parsed_rule { struct smack_known *smk_subject; struct smack_known *smk_object; int smk_access1; int smk_access2; }; struct sockaddr_un { __kernel_sa_family_t sun_family; char sun_path[108]; }; struct unix_address { refcount_t refcnt; int len; unsigned int hash; struct sockaddr_un name[0]; }; struct scm_stat { atomic_t nr_fds; }; struct unix_sock { struct sock sk; struct unix_address *addr; struct path path; struct mutex iolock; struct mutex bindlock; struct sock *peer; struct list_head link; atomic_long_t inflight; spinlock_t lock; long unsigned int gc_flags; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; struct socket_wq peer_wq; wait_queue_entry_t peer_wake; struct scm_stat scm_stat; long: 32; long: 64; long: 64; }; enum tomoyo_conditions_index { TOMOYO_TASK_UID = 0, TOMOYO_TASK_EUID = 1, TOMOYO_TASK_SUID = 2, TOMOYO_TASK_FSUID = 3, TOMOYO_TASK_GID = 4, TOMOYO_TASK_EGID = 5, TOMOYO_TASK_SGID = 6, TOMOYO_TASK_FSGID = 7, TOMOYO_TASK_PID = 8, TOMOYO_TASK_PPID = 9, TOMOYO_EXEC_ARGC = 10, TOMOYO_EXEC_ENVC = 11, TOMOYO_TYPE_IS_SOCKET = 12, TOMOYO_TYPE_IS_SYMLINK = 13, TOMOYO_TYPE_IS_FILE = 14, TOMOYO_TYPE_IS_BLOCK_DEV = 15, TOMOYO_TYPE_IS_DIRECTORY = 16, TOMOYO_TYPE_IS_CHAR_DEV = 17, TOMOYO_TYPE_IS_FIFO = 18, TOMOYO_MODE_SETUID = 19, TOMOYO_MODE_SETGID = 20, TOMOYO_MODE_STICKY = 21, TOMOYO_MODE_OWNER_READ = 22, TOMOYO_MODE_OWNER_WRITE = 23, TOMOYO_MODE_OWNER_EXECUTE = 24, TOMOYO_MODE_GROUP_READ = 25, TOMOYO_MODE_GROUP_WRITE = 26, TOMOYO_MODE_GROUP_EXECUTE = 27, TOMOYO_MODE_OTHERS_READ = 28, TOMOYO_MODE_OTHERS_WRITE = 29, TOMOYO_MODE_OTHERS_EXECUTE = 30, TOMOYO_EXEC_REALPATH = 31, TOMOYO_SYMLINK_TARGET = 32, TOMOYO_PATH1_UID = 33, TOMOYO_PATH1_GID = 34, TOMOYO_PATH1_INO = 35, TOMOYO_PATH1_MAJOR = 36, TOMOYO_PATH1_MINOR = 37, TOMOYO_PATH1_PERM = 38, TOMOYO_PATH1_TYPE = 39, TOMOYO_PATH1_DEV_MAJOR = 40, TOMOYO_PATH1_DEV_MINOR = 41, TOMOYO_PATH2_UID = 42, TOMOYO_PATH2_GID = 43, TOMOYO_PATH2_INO = 44, TOMOYO_PATH2_MAJOR = 45, TOMOYO_PATH2_MINOR = 46, TOMOYO_PATH2_PERM = 47, TOMOYO_PATH2_TYPE = 48, TOMOYO_PATH2_DEV_MAJOR = 49, TOMOYO_PATH2_DEV_MINOR = 50, TOMOYO_PATH1_PARENT_UID = 51, TOMOYO_PATH1_PARENT_GID = 52, TOMOYO_PATH1_PARENT_INO = 53, TOMOYO_PATH1_PARENT_PERM = 54, TOMOYO_PATH2_PARENT_UID = 55, TOMOYO_PATH2_PARENT_GID = 56, TOMOYO_PATH2_PARENT_INO = 57, TOMOYO_PATH2_PARENT_PERM = 58, TOMOYO_MAX_CONDITION_KEYWORD = 59, TOMOYO_NUMBER_UNION = 60, TOMOYO_NAME_UNION = 61, TOMOYO_ARGV_ENTRY = 62, TOMOYO_ENVP_ENTRY = 63, }; enum tomoyo_path_stat_index { TOMOYO_PATH1 = 0, TOMOYO_PATH1_PARENT = 1, TOMOYO_PATH2 = 2, TOMOYO_PATH2_PARENT = 3, TOMOYO_MAX_PATH_STAT = 4, }; enum tomoyo_mode_index { TOMOYO_CONFIG_DISABLED = 0, TOMOYO_CONFIG_LEARNING = 1, TOMOYO_CONFIG_PERMISSIVE = 2, TOMOYO_CONFIG_ENFORCING = 3, TOMOYO_CONFIG_MAX_MODE = 4, TOMOYO_CONFIG_WANT_REJECT_LOG = 64, TOMOYO_CONFIG_WANT_GRANT_LOG = 128, TOMOYO_CONFIG_USE_DEFAULT = 255, }; enum tomoyo_policy_id { TOMOYO_ID_GROUP = 0, TOMOYO_ID_ADDRESS_GROUP = 1, TOMOYO_ID_PATH_GROUP = 2, TOMOYO_ID_NUMBER_GROUP = 3, TOMOYO_ID_TRANSITION_CONTROL = 4, TOMOYO_ID_AGGREGATOR = 5, TOMOYO_ID_MANAGER = 6, TOMOYO_ID_CONDITION = 7, TOMOYO_ID_NAME = 8, TOMOYO_ID_ACL = 9, TOMOYO_ID_DOMAIN = 10, TOMOYO_MAX_POLICY = 11, }; enum tomoyo_domain_info_flags_index { TOMOYO_DIF_QUOTA_WARNED = 0, TOMOYO_DIF_TRANSITION_FAILED = 1, TOMOYO_MAX_DOMAIN_INFO_FLAGS = 2, }; enum tomoyo_grant_log { TOMOYO_GRANTLOG_AUTO = 0, TOMOYO_GRANTLOG_NO = 1, TOMOYO_GRANTLOG_YES = 2, }; enum tomoyo_group_id { TOMOYO_PATH_GROUP = 0, TOMOYO_NUMBER_GROUP = 1, TOMOYO_ADDRESS_GROUP = 2, TOMOYO_MAX_GROUP = 3, }; enum tomoyo_path_acl_index { TOMOYO_TYPE_EXECUTE = 0, TOMOYO_TYPE_READ = 1, TOMOYO_TYPE_WRITE = 2, TOMOYO_TYPE_APPEND = 3, TOMOYO_TYPE_UNLINK = 4, TOMOYO_TYPE_GETATTR = 5, TOMOYO_TYPE_RMDIR = 6, TOMOYO_TYPE_TRUNCATE = 7, TOMOYO_TYPE_SYMLINK = 8, TOMOYO_TYPE_CHROOT = 9, TOMOYO_TYPE_UMOUNT = 10, TOMOYO_MAX_PATH_OPERATION = 11, }; enum tomoyo_memory_stat_type { TOMOYO_MEMORY_POLICY = 0, TOMOYO_MEMORY_AUDIT = 1, TOMOYO_MEMORY_QUERY = 2, TOMOYO_MAX_MEMORY_STAT = 3, }; enum tomoyo_mkdev_acl_index { TOMOYO_TYPE_MKBLOCK = 0, TOMOYO_TYPE_MKCHAR = 1, TOMOYO_MAX_MKDEV_OPERATION = 2, }; enum tomoyo_network_acl_index { TOMOYO_NETWORK_BIND = 0, TOMOYO_NETWORK_LISTEN = 1, TOMOYO_NETWORK_CONNECT = 2, TOMOYO_NETWORK_SEND = 3, TOMOYO_MAX_NETWORK_OPERATION = 4, }; enum tomoyo_path2_acl_index { TOMOYO_TYPE_LINK = 0, TOMOYO_TYPE_RENAME = 1, TOMOYO_TYPE_PIVOT_ROOT = 2, TOMOYO_MAX_PATH2_OPERATION = 3, }; enum tomoyo_path_number_acl_index { TOMOYO_TYPE_CREATE = 0, TOMOYO_TYPE_MKDIR = 1, TOMOYO_TYPE_MKFIFO = 2, TOMOYO_TYPE_MKSOCK = 3, TOMOYO_TYPE_IOCTL = 4, TOMOYO_TYPE_CHMOD = 5, TOMOYO_TYPE_CHOWN = 6, TOMOYO_TYPE_CHGRP = 7, TOMOYO_MAX_PATH_NUMBER_OPERATION = 8, }; enum tomoyo_securityfs_interface_index { TOMOYO_DOMAINPOLICY = 0, TOMOYO_EXCEPTIONPOLICY = 1, TOMOYO_PROCESS_STATUS = 2, TOMOYO_STAT = 3, TOMOYO_AUDIT = 4, TOMOYO_VERSION = 5, TOMOYO_PROFILE = 6, TOMOYO_QUERY = 7, TOMOYO_MANAGER = 8, }; enum tomoyo_mac_index { TOMOYO_MAC_FILE_EXECUTE = 0, TOMOYO_MAC_FILE_OPEN = 1, TOMOYO_MAC_FILE_CREATE = 2, TOMOYO_MAC_FILE_UNLINK = 3, TOMOYO_MAC_FILE_GETATTR = 4, TOMOYO_MAC_FILE_MKDIR = 5, TOMOYO_MAC_FILE_RMDIR = 6, TOMOYO_MAC_FILE_MKFIFO = 7, TOMOYO_MAC_FILE_MKSOCK = 8, TOMOYO_MAC_FILE_TRUNCATE = 9, TOMOYO_MAC_FILE_SYMLINK = 10, TOMOYO_MAC_FILE_MKBLOCK = 11, TOMOYO_MAC_FILE_MKCHAR = 12, TOMOYO_MAC_FILE_LINK = 13, TOMOYO_MAC_FILE_RENAME = 14, TOMOYO_MAC_FILE_CHMOD = 15, TOMOYO_MAC_FILE_CHOWN = 16, TOMOYO_MAC_FILE_CHGRP = 17, TOMOYO_MAC_FILE_IOCTL = 18, TOMOYO_MAC_FILE_CHROOT = 19, TOMOYO_MAC_FILE_MOUNT = 20, TOMOYO_MAC_FILE_UMOUNT = 21, TOMOYO_MAC_FILE_PIVOT_ROOT = 22, TOMOYO_MAC_NETWORK_INET_STREAM_BIND = 23, TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN = 24, TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT = 25, TOMOYO_MAC_NETWORK_INET_DGRAM_BIND = 26, TOMOYO_MAC_NETWORK_INET_DGRAM_SEND = 27, TOMOYO_MAC_NETWORK_INET_RAW_BIND = 28, TOMOYO_MAC_NETWORK_INET_RAW_SEND = 29, TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND = 30, TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN = 31, TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT = 32, TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND = 33, TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND = 34, TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND = 35, TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN = 36, TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT = 37, TOMOYO_MAC_ENVIRON = 38, TOMOYO_MAX_MAC_INDEX = 39, }; enum tomoyo_mac_category_index { TOMOYO_MAC_CATEGORY_FILE = 0, TOMOYO_MAC_CATEGORY_NETWORK = 1, TOMOYO_MAC_CATEGORY_MISC = 2, TOMOYO_MAX_MAC_CATEGORY_INDEX = 3, }; enum tomoyo_pref_index { TOMOYO_PREF_MAX_AUDIT_LOG = 0, TOMOYO_PREF_MAX_LEARNING_ENTRY = 1, TOMOYO_MAX_PREF = 2, }; struct tomoyo_shared_acl_head { struct list_head list; atomic_t users; } __attribute__((packed)); struct tomoyo_path_info { const char *name; u32 hash; u16 const_len; bool is_dir; bool is_patterned; }; struct tomoyo_obj_info; struct tomoyo_execve; struct tomoyo_domain_info; struct tomoyo_acl_info; struct tomoyo_request_info { struct tomoyo_obj_info *obj; struct tomoyo_execve *ee; struct tomoyo_domain_info *domain; union { struct { const struct tomoyo_path_info *filename; const struct tomoyo_path_info *matched_path; u8 operation; } path; struct { const struct tomoyo_path_info *filename1; const struct tomoyo_path_info *filename2; u8 operation; } path2; struct { const struct tomoyo_path_info *filename; unsigned int mode; unsigned int major; unsigned int minor; u8 operation; } mkdev; struct { const struct tomoyo_path_info *filename; long unsigned int number; u8 operation; } path_number; struct { const struct tomoyo_path_info *name; } environ; struct { const __be32 *address; u16 port; u8 protocol; u8 operation; bool is_ipv6; } inet_network; struct { const struct tomoyo_path_info *address; u8 protocol; u8 operation; } unix_network; struct { const struct tomoyo_path_info *type; const struct tomoyo_path_info *dir; const struct tomoyo_path_info *dev; long unsigned int flags; int need_dev; } mount; struct { const struct tomoyo_path_info *domainname; } task; } param; struct tomoyo_acl_info *matched_acl; u8 param_type; bool granted; u8 retry; u8 profile; u8 mode; u8 type; }; struct tomoyo_mini_stat { kuid_t uid; kgid_t gid; ino_t ino; umode_t mode; dev_t dev; dev_t rdev; }; struct tomoyo_obj_info { bool validate_done; bool stat_valid[4]; struct path path1; struct path path2; struct tomoyo_mini_stat stat[4]; struct tomoyo_path_info *symlink_target; }; struct tomoyo_page_dump { struct page *page; char *data; }; struct tomoyo_execve { struct tomoyo_request_info r; struct tomoyo_obj_info obj; struct linux_binprm *bprm; const struct tomoyo_path_info *transition; struct tomoyo_page_dump dump; char *tmp; }; struct tomoyo_policy_namespace; struct tomoyo_domain_info { struct list_head list; struct list_head acl_info_list; const struct tomoyo_path_info *domainname; struct tomoyo_policy_namespace *ns; long unsigned int group[4]; u8 profile; bool is_deleted; bool flags[2]; atomic_t users; }; struct tomoyo_condition; struct tomoyo_acl_info { struct list_head list; struct tomoyo_condition *cond; s8 is_deleted; u8 type; } __attribute__((packed)); struct tomoyo_condition { struct tomoyo_shared_acl_head head; u32 size; u16 condc; u16 numbers_count; u16 names_count; u16 argc; u16 envc; u8 grant_log; const struct tomoyo_path_info *transit; }; struct tomoyo_profile; struct tomoyo_policy_namespace { struct tomoyo_profile *profile_ptr[256]; struct list_head group_list[3]; struct list_head policy_list[11]; struct list_head acl_group[256]; struct list_head namespace_list; unsigned int profile_version; const char *name; }; struct tomoyo_io_buffer { void (*read)(struct tomoyo_io_buffer *); int (*write)(struct tomoyo_io_buffer *); __poll_t (*poll)(struct file *, poll_table *); struct mutex io_sem; char *read_user_buf; size_t read_user_buf_avail; struct { struct list_head *ns; struct list_head *domain; struct list_head *group; struct list_head *acl; size_t avail; unsigned int step; unsigned int query_index; u16 index; u16 cond_index; u8 acl_group_index; u8 cond_step; u8 bit; u8 w_pos; bool eof; bool print_this_domain_only; bool print_transition_related_only; bool print_cond_part; const char *w[64]; } r; struct { struct tomoyo_policy_namespace *ns; struct tomoyo_domain_info *domain; size_t avail; bool is_delete; } w; char *read_buf; size_t readbuf_size; char *write_buf; size_t writebuf_size; enum tomoyo_securityfs_interface_index type; u8 users; struct list_head list; }; struct tomoyo_preference { unsigned int learning_max_entry; bool enforcing_verbose; bool learning_verbose; bool permissive_verbose; }; struct tomoyo_profile { const struct tomoyo_path_info *comment; struct tomoyo_preference *learning; struct tomoyo_preference *permissive; struct tomoyo_preference *enforcing; struct tomoyo_preference preference; u8 default_config; u8 config[42]; unsigned int pref[2]; }; struct tomoyo_time { u16 year; u8 month; u8 day; u8 hour; u8 min; u8 sec; }; struct tomoyo_log { struct list_head list; char *log; int size; }; enum tomoyo_value_type { TOMOYO_VALUE_TYPE_INVALID = 0, TOMOYO_VALUE_TYPE_DECIMAL = 1, TOMOYO_VALUE_TYPE_OCTAL = 2, TOMOYO_VALUE_TYPE_HEXADECIMAL = 3, }; enum tomoyo_transition_type { TOMOYO_TRANSITION_CONTROL_NO_RESET = 0, TOMOYO_TRANSITION_CONTROL_RESET = 1, TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE = 2, TOMOYO_TRANSITION_CONTROL_INITIALIZE = 3, TOMOYO_TRANSITION_CONTROL_NO_KEEP = 4, TOMOYO_TRANSITION_CONTROL_KEEP = 5, TOMOYO_MAX_TRANSITION_TYPE = 6, }; enum tomoyo_acl_entry_type_index { TOMOYO_TYPE_PATH_ACL = 0, TOMOYO_TYPE_PATH2_ACL = 1, TOMOYO_TYPE_PATH_NUMBER_ACL = 2, TOMOYO_TYPE_MKDEV_ACL = 3, TOMOYO_TYPE_MOUNT_ACL = 4, TOMOYO_TYPE_INET_ACL = 5, TOMOYO_TYPE_UNIX_ACL = 6, TOMOYO_TYPE_ENV_ACL = 7, TOMOYO_TYPE_MANUAL_TASK_ACL = 8, }; enum tomoyo_policy_stat_type { TOMOYO_STAT_POLICY_UPDATES = 0, TOMOYO_STAT_POLICY_LEARNING = 1, TOMOYO_STAT_POLICY_PERMISSIVE = 2, TOMOYO_STAT_POLICY_ENFORCING = 3, TOMOYO_MAX_POLICY_STAT = 4, }; struct tomoyo_acl_head { struct list_head list; s8 is_deleted; } __attribute__((packed)); struct tomoyo_name { struct tomoyo_shared_acl_head head; struct tomoyo_path_info entry; }; struct tomoyo_group; struct tomoyo_name_union { const struct tomoyo_path_info *filename; struct tomoyo_group *group; }; struct tomoyo_group { struct tomoyo_shared_acl_head head; const struct tomoyo_path_info *group_name; struct list_head member_list; }; struct tomoyo_number_union { long unsigned int values[2]; struct tomoyo_group *group; u8 value_type[2]; }; struct tomoyo_ipaddr_union { struct in6_addr ip[2]; struct tomoyo_group *group; bool is_ipv6; }; struct tomoyo_path_group { struct tomoyo_acl_head head; const struct tomoyo_path_info *member_name; }; struct tomoyo_number_group { struct tomoyo_acl_head head; struct tomoyo_number_union number; }; struct tomoyo_address_group { struct tomoyo_acl_head head; struct tomoyo_ipaddr_union address; }; struct tomoyo_argv { long unsigned int index; const struct tomoyo_path_info *value; bool is_not; }; struct tomoyo_envp { const struct tomoyo_path_info *name; const struct tomoyo_path_info *value; bool is_not; }; struct tomoyo_condition_element { u8 left; u8 right; bool equals; }; struct tomoyo_task_acl { struct tomoyo_acl_info head; const struct tomoyo_path_info *domainname; }; struct tomoyo_path_acl { struct tomoyo_acl_info head; u16 perm; struct tomoyo_name_union name; }; struct tomoyo_path_number_acl { struct tomoyo_acl_info head; u8 perm; struct tomoyo_name_union name; struct tomoyo_number_union number; }; struct tomoyo_mkdev_acl { struct tomoyo_acl_info head; u8 perm; struct tomoyo_name_union name; struct tomoyo_number_union mode; struct tomoyo_number_union major; struct tomoyo_number_union minor; }; struct tomoyo_path2_acl { struct tomoyo_acl_info head; u8 perm; struct tomoyo_name_union name1; struct tomoyo_name_union name2; }; struct tomoyo_mount_acl { struct tomoyo_acl_info head; struct tomoyo_name_union dev_name; struct tomoyo_name_union dir_name; struct tomoyo_name_union fs_type; struct tomoyo_number_union flags; }; struct tomoyo_env_acl { struct tomoyo_acl_info head; const struct tomoyo_path_info *env; }; struct tomoyo_inet_acl { struct tomoyo_acl_info head; u8 protocol; u8 perm; struct tomoyo_ipaddr_union address; struct tomoyo_number_union port; }; struct tomoyo_unix_acl { struct tomoyo_acl_info head; u8 protocol; u8 perm; struct tomoyo_name_union name; }; struct tomoyo_acl_param { char *data; struct list_head *list; struct tomoyo_policy_namespace *ns; bool is_delete; }; struct tomoyo_transition_control { struct tomoyo_acl_head head; u8 type; bool is_last_name; const struct tomoyo_path_info *domainname; const struct tomoyo_path_info *program; }; struct tomoyo_aggregator { struct tomoyo_acl_head head; const struct tomoyo_path_info *original_name; const struct tomoyo_path_info *aggregated_name; }; struct tomoyo_manager { struct tomoyo_acl_head head; const struct tomoyo_path_info *manager; }; struct tomoyo_task { struct tomoyo_domain_info *domain_info; struct tomoyo_domain_info *old_domain_info; }; struct tomoyo_query { struct list_head list; struct tomoyo_domain_info *domain; char *query; size_t query_len; unsigned int serial; u8 timer; u8 answer; u8 retry; }; enum tomoyo_special_mount { TOMOYO_MOUNT_BIND = 0, TOMOYO_MOUNT_MOVE = 1, TOMOYO_MOUNT_REMOUNT = 2, TOMOYO_MOUNT_MAKE_UNBINDABLE = 3, TOMOYO_MOUNT_MAKE_PRIVATE = 4, TOMOYO_MOUNT_MAKE_SLAVE = 5, TOMOYO_MOUNT_MAKE_SHARED = 6, TOMOYO_MAX_SPECIAL_MOUNT = 7, }; struct tomoyo_inet_addr_info { __be16 port; const __be32 *address; bool is_ipv6; }; struct tomoyo_unix_addr_info { u8 *addr; unsigned int addr_len; }; struct tomoyo_addr_info { u8 protocol; u8 operation; struct tomoyo_inet_addr_info inet; struct tomoyo_unix_addr_info unix0; }; typedef unsigned char Byte; typedef long unsigned int uLong; struct internal_state; struct z_stream_s { const Byte *next_in; uLong avail_in; uLong total_in; Byte *next_out; uLong avail_out; uLong total_out; char *msg; struct internal_state *state; void *workspace; int data_type; uLong adler; uLong reserved; }; struct internal_state { int dummy; }; typedef struct z_stream_s z_stream; typedef z_stream *z_streamp; enum audit_mode { AUDIT_NORMAL = 0, AUDIT_QUIET_DENIED = 1, AUDIT_QUIET = 2, AUDIT_NOQUIET = 3, AUDIT_ALL = 4, }; enum aa_sfs_type { AA_SFS_TYPE_BOOLEAN = 0, AA_SFS_TYPE_STRING = 1, AA_SFS_TYPE_U64 = 2, AA_SFS_TYPE_FOPS = 3, AA_SFS_TYPE_DIR = 4, }; struct aa_sfs_entry { const char *name; struct dentry *dentry; umode_t mode; enum aa_sfs_type v_type; union { bool boolean; char *string; long unsigned int u64; struct aa_sfs_entry *files; } v; const struct file_operations *file_ops; }; enum aafs_ns_type { AAFS_NS_DIR = 0, AAFS_NS_PROFS = 1, AAFS_NS_NS = 2, AAFS_NS_RAW_DATA = 3, AAFS_NS_LOAD = 4, AAFS_NS_REPLACE = 5, AAFS_NS_REMOVE = 6, AAFS_NS_REVISION = 7, AAFS_NS_COUNT = 8, AAFS_NS_MAX_COUNT = 9, AAFS_NS_SIZE = 10, AAFS_NS_MAX_SIZE = 11, AAFS_NS_OWNER = 12, AAFS_NS_SIZEOF = 13, }; enum aafs_prof_type { AAFS_PROF_DIR = 0, AAFS_PROF_PROFS = 1, AAFS_PROF_NAME = 2, AAFS_PROF_MODE = 3, AAFS_PROF_ATTACH = 4, AAFS_PROF_HASH = 5, AAFS_PROF_RAW_DATA = 6, AAFS_PROF_RAW_HASH = 7, AAFS_PROF_RAW_ABI = 8, AAFS_PROF_SIZEOF = 9, }; struct table_header { u16 td_id; u16 td_flags; u32 td_hilen; u32 td_lolen; char td_data[0]; }; struct aa_dfa { struct kref count; u16 flags; u32 max_oob; struct table_header *tables[8]; }; struct aa_policy { const char *name; char *hname; struct list_head list; struct list_head profiles; }; struct aa_labelset { rwlock_t lock; struct rb_root root; }; enum label_flags { FLAG_HAT = 1, FLAG_UNCONFINED = 2, FLAG_NULL = 4, FLAG_IX_ON_NAME_ERROR = 8, FLAG_IMMUTIBLE = 16, FLAG_USER_DEFINED = 32, FLAG_NO_LIST_REF = 64, FLAG_NS_COUNT = 128, FLAG_IN_TREE = 256, FLAG_PROFILE = 512, FLAG_EXPLICIT = 1024, FLAG_STALE = 2048, FLAG_RENAMED = 4096, FLAG_REVOKED = 8192, }; struct aa_label; struct aa_proxy { struct kref count; struct aa_label *label; }; struct aa_profile; struct aa_label { struct kref count; struct rb_node node; struct callback_head rcu; struct aa_proxy *proxy; char *hname; long int flags; u32 secid; int size; struct aa_profile *vec[0]; }; struct label_it { int i; int j; }; struct aa_policydb { struct aa_dfa *dfa; unsigned int start[17]; }; struct aa_domain { int size; char **table; }; struct aa_file_rules { unsigned int start; struct aa_dfa *dfa; struct aa_domain trans; }; struct aa_caps { kernel_cap_t allow; kernel_cap_t audit; kernel_cap_t denied; kernel_cap_t quiet; kernel_cap_t kill; kernel_cap_t extended; }; struct aa_rlimit { unsigned int mask; struct rlimit limits[16]; }; struct aa_ns; struct aa_secmark; struct aa_loaddata; struct aa_profile { struct aa_policy base; struct aa_profile *parent; struct aa_ns *ns; const char *rename; const char *attach; struct aa_dfa *xmatch; int xmatch_len; enum audit_mode audit; long int mode; u32 path_flags; const char *disconnected; int size; struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; int xattr_count; char **xattrs; struct aa_rlimit rlimits; int secmark_count; struct aa_secmark *secmark; struct aa_loaddata *rawdata; unsigned char *hash; char *dirname; struct dentry *dents[9]; struct rhashtable *data; struct aa_label label; }; struct aa_perms { u32 allow; u32 audit; u32 deny; u32 quiet; u32 kill; u32 stop; u32 complain; u32 cond; u32 hide; u32 prompt; u16 xindex; }; struct path_cond { kuid_t uid; umode_t mode; }; struct aa_secmark { u8 audit; u8 deny; u32 secid; char *label; }; enum profile_mode { APPARMOR_ENFORCE = 0, APPARMOR_COMPLAIN = 1, APPARMOR_KILL = 2, APPARMOR_UNCONFINED = 3, }; struct aa_data { char *key; u32 size; char *data; struct rhash_head head; }; struct aa_ns_acct { int max_size; int max_count; int size; int count; }; struct aa_ns { struct aa_policy base; struct aa_ns *parent; struct mutex lock; struct aa_ns_acct acct; struct aa_profile *unconfined; struct list_head sub_ns; atomic_t uniq_null; long int uniq_id; int level; long int revision; wait_queue_head_t wait; struct aa_labelset labels; struct list_head rawdata_list; struct dentry *dents[13]; }; struct aa_loaddata { struct kref count; struct list_head list; struct work_struct work; struct dentry *dents[6]; struct aa_ns *ns; char *name; size_t size; size_t compressed_size; long int revision; int abi; unsigned char *hash; char *data; }; enum { AAFS_LOADDATA_ABI = 0, AAFS_LOADDATA_REVISION = 1, AAFS_LOADDATA_HASH = 2, AAFS_LOADDATA_DATA = 3, AAFS_LOADDATA_COMPRESSED_SIZE = 4, AAFS_LOADDATA_DIR = 5, AAFS_LOADDATA_NDENTS = 6, }; struct rawdata_f_data { struct aa_loaddata *loaddata; }; struct aa_revision { struct aa_ns *ns; long int last_read; }; struct multi_transaction { struct kref count; ssize_t size; char data[0]; }; struct apparmor_audit_data { int error; int type; const char *op; struct aa_label *label; const char *name; const char *info; u32 request; u32 denied; union { struct { struct aa_label *peer; union { struct { const char *target; kuid_t ouid; } fs; struct { int rlim; long unsigned int max; } rlim; struct { int signal; int unmappedsig; }; struct { int type; int protocol; struct sock *peer_sk; void *addr; int addrlen; } net; }; }; struct { struct aa_profile *profile; const char *ns; long int pos; } iface; struct { const char *src_name; const char *type; const char *trans; const char *data; long unsigned int flags; } mnt; }; }; enum audit_type { AUDIT_APPARMOR_AUDIT = 0, AUDIT_APPARMOR_ALLOWED = 1, AUDIT_APPARMOR_DENIED = 2, AUDIT_APPARMOR_HINT = 3, AUDIT_APPARMOR_STATUS = 4, AUDIT_APPARMOR_ERROR = 5, AUDIT_APPARMOR_KILL = 6, AUDIT_APPARMOR_AUTO = 7, }; struct aa_audit_rule { struct aa_label *label; }; struct audit_cache { struct aa_profile *profile; kernel_cap_t caps; }; struct aa_task_ctx { struct aa_label *nnp; struct aa_label *onexec; struct aa_label *previous; u64 token; }; struct counted_str { struct kref count; char name[0]; }; struct match_workbuf { unsigned int count; unsigned int pos; unsigned int len; unsigned int size; unsigned int history[24]; }; enum path_flags { PATH_IS_DIR = 1, PATH_CONNECT_PATH = 4, PATH_CHROOT_REL = 8, PATH_CHROOT_NSCONNECT = 16, PATH_DELEGATE_DELETED = 32768, PATH_MEDIATE_DELETED = 65536, }; struct aa_load_ent { struct list_head list; struct aa_profile *new; struct aa_profile *old; struct aa_profile *rename; const char *ns_name; }; enum aa_code { AA_U8 = 0, AA_U16 = 1, AA_U32 = 2, AA_U64 = 3, AA_NAME = 4, AA_STRING = 5, AA_BLOB = 6, AA_STRUCT = 7, AA_STRUCTEND = 8, AA_LIST = 9, AA_LISTEND = 10, AA_ARRAY = 11, AA_ARRAYEND = 12, }; struct aa_ext { void *start; void *end; void *pos; u32 version; }; struct aa_file_ctx { spinlock_t lock; struct aa_label *label; u32 allow; }; struct aa_sk_ctx { struct aa_label *label; struct aa_label *peer; }; union aa_buffer { struct list_head list; char buffer[1]; }; struct ptrace_relation { struct task_struct *tracer; struct task_struct *tracee; bool invalid; struct list_head node; struct callback_head rcu; }; struct access_report_info { struct callback_head work; const char *access; struct task_struct *target; struct task_struct *agent; }; enum sid_policy_type { SIDPOL_DEFAULT = 0, SIDPOL_CONSTRAINED = 1, SIDPOL_ALLOWED = 2, }; typedef union { kuid_t uid; kgid_t gid; } kid_t; enum setid_type { UID = 0, GID = 1, }; struct setid_rule { struct hlist_node next; kid_t src_id; kid_t dst_id; enum setid_type type; }; struct setid_ruleset { struct hlist_head rules[256]; char *policy_str; struct callback_head rcu; enum setid_type type; }; enum devcg_behavior { DEVCG_DEFAULT_NONE = 0, DEVCG_DEFAULT_ALLOW = 1, DEVCG_DEFAULT_DENY = 2, }; struct dev_exception_item { u32 major; u32 minor; short int type; short int access; struct list_head list; struct callback_head rcu; }; struct dev_cgroup { struct cgroup_subsys_state css; struct list_head exceptions; enum devcg_behavior behavior; }; struct landlock_ruleset_attr { __u64 handled_access_fs; }; enum landlock_rule_type { LANDLOCK_RULE_PATH_BENEATH = 1, }; struct landlock_path_beneath_attr { __u64 allowed_access; __s32 parent_fd; } __attribute__((packed)); struct landlock_hierarchy { struct landlock_hierarchy *parent; refcount_t usage; }; struct landlock_ruleset { struct rb_root root; struct landlock_hierarchy *hierarchy; union { struct work_struct work_free; struct { struct mutex lock; refcount_t usage; u32 num_rules; u32 num_layers; u16 fs_access_masks[0]; }; }; }; struct landlock_cred_security { struct landlock_ruleset *domain; }; struct landlock_object; struct landlock_object_underops { void (*release)(struct landlock_object * const); }; struct landlock_object { refcount_t usage; spinlock_t lock; void *underobj; union { struct callback_head rcu_free; const struct landlock_object_underops *underops; }; }; struct landlock_layer { u16 level; u16 access; }; struct landlock_rule { struct rb_node node; struct landlock_object *object; u32 num_layers; struct landlock_layer layers[0]; }; struct landlock_inode_security { struct landlock_object *object; }; struct landlock_superblock_security { atomic_long_t inode_refs; }; enum { CRYPTO_MSG_ALG_REQUEST = 0, CRYPTO_MSG_ALG_REGISTER = 1, CRYPTO_MSG_ALG_LOADED = 2, }; struct crypto_larval { struct crypto_alg alg; struct crypto_alg *adult; struct completion completion; u32 mask; }; struct crypto_cipher { struct crypto_tfm base; }; struct rtattr { short unsigned int rta_len; short unsigned int rta_type; }; struct crypto_queue { struct list_head list; struct list_head *backlog; unsigned int qlen; unsigned int max_qlen; }; struct crypto_attr_alg { char name[128]; }; struct crypto_attr_type { u32 type; u32 mask; }; enum { NAPI_STATE_SCHED = 0, NAPI_STATE_MISSED = 1, NAPI_STATE_DISABLE = 2, NAPI_STATE_NPSVC = 3, NAPI_STATE_LISTED = 4, NAPI_STATE_NO_BUSY_POLL = 5, NAPI_STATE_IN_BUSY_POLL = 6, NAPI_STATE_PREFER_BUSY_POLL = 7, NAPI_STATE_THREADED = 8, NAPI_STATE_SCHED_THREADED = 9, }; enum xps_map_type { XPS_CPUS = 0, XPS_RXQS = 1, XPS_MAPS_MAX = 2, }; enum bpf_xdp_mode { XDP_MODE_SKB = 0, XDP_MODE_DRV = 1, XDP_MODE_HW = 2, __MAX_XDP_MODE = 3, }; enum { NETIF_MSG_DRV_BIT = 0, NETIF_MSG_PROBE_BIT = 1, NETIF_MSG_LINK_BIT = 2, NETIF_MSG_TIMER_BIT = 3, NETIF_MSG_IFDOWN_BIT = 4, NETIF_MSG_IFUP_BIT = 5, NETIF_MSG_RX_ERR_BIT = 6, NETIF_MSG_TX_ERR_BIT = 7, NETIF_MSG_TX_QUEUED_BIT = 8, NETIF_MSG_INTR_BIT = 9, NETIF_MSG_TX_DONE_BIT = 10, NETIF_MSG_RX_STATUS_BIT = 11, NETIF_MSG_PKTDATA_BIT = 12, NETIF_MSG_HW_BIT = 13, NETIF_MSG_WOL_BIT = 14, NETIF_MSG_CLASS_COUNT = 15, }; enum { CRYPTOA_UNSPEC = 0, CRYPTOA_ALG = 1, CRYPTOA_TYPE = 2, __CRYPTOA_MAX = 3, }; struct scatter_walk { struct scatterlist *sg; unsigned int offset; }; struct aead_request { struct crypto_async_request base; unsigned int assoclen; unsigned int cryptlen; u8 *iv; struct scatterlist *src; struct scatterlist *dst; void *__ctx[0]; }; struct crypto_aead; struct aead_alg { int (*setkey)(struct crypto_aead *, const u8 *, unsigned int); int (*setauthsize)(struct crypto_aead *, unsigned int); int (*encrypt)(struct aead_request *); int (*decrypt)(struct aead_request *); int (*init)(struct crypto_aead *); void (*exit)(struct crypto_aead *); unsigned int ivsize; unsigned int maxauthsize; unsigned int chunksize; struct crypto_alg base; }; struct crypto_aead { unsigned int authsize; unsigned int reqsize; struct crypto_tfm base; }; struct aead_instance { void (*free)(struct aead_instance *); union { struct { char head[64]; struct crypto_instance base; } s; struct aead_alg alg; }; }; struct crypto_aead_spawn { struct crypto_spawn base; }; enum crypto_attr_type_t { CRYPTOCFGA_UNSPEC = 0, CRYPTOCFGA_PRIORITY_VAL = 1, CRYPTOCFGA_REPORT_LARVAL = 2, CRYPTOCFGA_REPORT_HASH = 3, CRYPTOCFGA_REPORT_BLKCIPHER = 4, CRYPTOCFGA_REPORT_AEAD = 5, CRYPTOCFGA_REPORT_COMPRESS = 6, CRYPTOCFGA_REPORT_RNG = 7, CRYPTOCFGA_REPORT_CIPHER = 8, CRYPTOCFGA_REPORT_AKCIPHER = 9, CRYPTOCFGA_REPORT_KPP = 10, CRYPTOCFGA_REPORT_ACOMP = 11, CRYPTOCFGA_STAT_LARVAL = 12, CRYPTOCFGA_STAT_HASH = 13, CRYPTOCFGA_STAT_BLKCIPHER = 14, CRYPTOCFGA_STAT_AEAD = 15, CRYPTOCFGA_STAT_COMPRESS = 16, CRYPTOCFGA_STAT_RNG = 17, CRYPTOCFGA_STAT_CIPHER = 18, CRYPTOCFGA_STAT_AKCIPHER = 19, CRYPTOCFGA_STAT_KPP = 20, CRYPTOCFGA_STAT_ACOMP = 21, __CRYPTOCFGA_MAX = 22, }; struct crypto_report_aead { char type[64]; char geniv[64]; unsigned int blocksize; unsigned int maxauthsize; unsigned int ivsize; }; struct crypto_sync_skcipher; struct aead_geniv_ctx { spinlock_t lock; struct crypto_aead *child; struct crypto_sync_skcipher *sknull; u8 salt[0]; }; struct crypto_rng; struct rng_alg { int (*generate)(struct crypto_rng *, const u8 *, unsigned int, u8 *, unsigned int); int (*seed)(struct crypto_rng *, const u8 *, unsigned int); void (*set_ent)(struct crypto_rng *, const u8 *, unsigned int); unsigned int seedsize; struct crypto_alg base; }; struct crypto_rng { struct crypto_tfm base; }; struct crypto_cipher_spawn { struct crypto_spawn base; }; struct crypto_sync_skcipher___2 { struct crypto_skcipher base; }; struct skcipher_instance { void (*free)(struct skcipher_instance *); union { struct { char head[64]; struct crypto_instance base; } s; struct skcipher_alg alg; }; }; struct crypto_skcipher_spawn { struct crypto_spawn base; }; struct skcipher_walk { union { struct { struct page *page; long unsigned int offset; } phys; struct { u8 *page; void *addr; } virt; } src; union { struct { struct page *page; long unsigned int offset; } phys; struct { u8 *page; void *addr; } virt; } dst; struct scatter_walk in; unsigned int nbytes; struct scatter_walk out; unsigned int total; struct list_head buffers; u8 *page; u8 *buffer; u8 *oiv; void *iv; unsigned int ivsize; int flags; unsigned int blocksize; unsigned int stride; unsigned int alignmask; }; struct skcipher_ctx_simple { struct crypto_cipher *cipher; }; struct crypto_report_blkcipher { char type[64]; char geniv[64]; unsigned int blocksize; unsigned int min_keysize; unsigned int max_keysize; unsigned int ivsize; }; enum { SKCIPHER_WALK_PHYS = 1, SKCIPHER_WALK_SLOW = 2, SKCIPHER_WALK_COPY = 4, SKCIPHER_WALK_DIFF = 8, SKCIPHER_WALK_SLEEP = 16, }; struct skcipher_walk_buffer { struct list_head entry; struct scatter_walk dst; unsigned int len; u8 *data; u8 buffer[0]; }; struct ahash_alg { int (*init)(struct ahash_request *); int (*update)(struct ahash_request *); int (*final)(struct ahash_request *); int (*finup)(struct ahash_request *); int (*digest)(struct ahash_request *); int (*export)(struct ahash_request *, void *); int (*import)(struct ahash_request *, const void *); int (*setkey)(struct crypto_ahash *, const u8 *, unsigned int); int (*init_tfm)(struct crypto_ahash *); void (*exit_tfm)(struct crypto_ahash *); struct hash_alg_common halg; }; struct crypto_hash_walk { char *data; unsigned int offset; unsigned int alignmask; struct page *pg; unsigned int entrylen; unsigned int total; struct scatterlist *sg; unsigned int flags; }; struct ahash_instance { void (*free)(struct ahash_instance *); union { struct { char head[88]; struct crypto_instance base; } s; struct ahash_alg alg; }; }; struct crypto_ahash_spawn { struct crypto_spawn base; }; struct crypto_report_hash { char type[64]; unsigned int blocksize; unsigned int digestsize; }; struct ahash_request_priv { crypto_completion_t complete; void *data; u8 *result; u32 flags; void *ubuf[0]; }; struct shash_instance { void (*free)(struct shash_instance *); union { struct { char head[96]; struct crypto_instance base; } s; struct shash_alg alg; }; }; struct crypto_shash_spawn { struct crypto_spawn base; }; struct crypto_report_akcipher { char type[64]; }; struct akcipher_request { struct crypto_async_request base; struct scatterlist *src; struct scatterlist *dst; unsigned int src_len; unsigned int dst_len; void *__ctx[0]; }; struct crypto_akcipher { struct crypto_tfm base; }; struct akcipher_alg { int (*sign)(struct akcipher_request *); int (*verify)(struct akcipher_request *); int (*encrypt)(struct akcipher_request *); int (*decrypt)(struct akcipher_request *); int (*set_pub_key)(struct crypto_akcipher *, const void *, unsigned int); int (*set_priv_key)(struct crypto_akcipher *, const void *, unsigned int); unsigned int (*max_size)(struct crypto_akcipher *); int (*init)(struct crypto_akcipher *); void (*exit)(struct crypto_akcipher *); unsigned int reqsize; struct crypto_alg base; }; struct akcipher_instance { void (*free)(struct akcipher_instance *); union { struct { char head[80]; struct crypto_instance base; } s; struct akcipher_alg alg; }; }; struct crypto_akcipher_spawn { struct crypto_spawn base; }; struct crypto_report_kpp { char type[64]; }; typedef long unsigned int mpi_limb_t; struct gcry_mpi { int alloced; int nlimbs; int nbits; int sign; unsigned int flags; mpi_limb_t *d; }; typedef struct gcry_mpi *MPI; struct dh_ctx { MPI p; MPI q; MPI g; MPI xa; }; enum { CRYPTO_KPP_SECRET_TYPE_UNKNOWN = 0, CRYPTO_KPP_SECRET_TYPE_DH = 1, CRYPTO_KPP_SECRET_TYPE_ECDH = 2, }; struct kpp_secret { short unsigned int type; short unsigned int len; }; enum asn1_class { ASN1_UNIV = 0, ASN1_APPL = 1, ASN1_CONT = 2, ASN1_PRIV = 3, }; enum asn1_method { ASN1_PRIM = 0, ASN1_CONS = 1, }; enum asn1_tag { ASN1_EOC = 0, ASN1_BOOL = 1, ASN1_INT = 2, ASN1_BTS = 3, ASN1_OTS = 4, ASN1_NULL = 5, ASN1_OID = 6, ASN1_ODE = 7, ASN1_EXT = 8, ASN1_REAL = 9, ASN1_ENUM = 10, ASN1_EPDV = 11, ASN1_UTF8STR = 12, ASN1_RELOID = 13, ASN1_SEQ = 16, ASN1_SET = 17, ASN1_NUMSTR = 18, ASN1_PRNSTR = 19, ASN1_TEXSTR = 20, ASN1_VIDSTR = 21, ASN1_IA5STR = 22, ASN1_UNITIM = 23, ASN1_GENTIM = 24, ASN1_GRASTR = 25, ASN1_VISSTR = 26, ASN1_GENSTR = 27, ASN1_UNISTR = 28, ASN1_CHRSTR = 29, ASN1_BMPSTR = 30, ASN1_LONG_TAG = 31, }; typedef int (*asn1_action_t)(void *, size_t, unsigned char, const void *, size_t); struct asn1_decoder { const unsigned char *machine; size_t machlen; const asn1_action_t *actions; }; enum asn1_opcode { ASN1_OP_MATCH = 0, ASN1_OP_MATCH_OR_SKIP = 1, ASN1_OP_MATCH_ACT = 2, ASN1_OP_MATCH_ACT_OR_SKIP = 3, ASN1_OP_MATCH_JUMP = 4, ASN1_OP_MATCH_JUMP_OR_SKIP = 5, ASN1_OP_MATCH_ANY = 8, ASN1_OP_MATCH_ANY_OR_SKIP = 9, ASN1_OP_MATCH_ANY_ACT = 10, ASN1_OP_MATCH_ANY_ACT_OR_SKIP = 11, ASN1_OP_COND_MATCH_OR_SKIP = 17, ASN1_OP_COND_MATCH_ACT_OR_SKIP = 19, ASN1_OP_COND_MATCH_JUMP_OR_SKIP = 21, ASN1_OP_COND_MATCH_ANY = 24, ASN1_OP_COND_MATCH_ANY_OR_SKIP = 25, ASN1_OP_COND_MATCH_ANY_ACT = 26, ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP = 27, ASN1_OP_COND_FAIL = 28, ASN1_OP_COMPLETE = 29, ASN1_OP_ACT = 30, ASN1_OP_MAYBE_ACT = 31, ASN1_OP_END_SEQ = 32, ASN1_OP_END_SET = 33, ASN1_OP_END_SEQ_OF = 34, ASN1_OP_END_SET_OF = 35, ASN1_OP_END_SEQ_ACT = 36, ASN1_OP_END_SET_ACT = 37, ASN1_OP_END_SEQ_OF_ACT = 38, ASN1_OP_END_SET_OF_ACT = 39, ASN1_OP_RETURN = 40, ASN1_OP__NR = 41, }; enum rsapubkey_actions { ACT_rsa_get_e = 0, ACT_rsa_get_n = 1, NR__rsapubkey_actions = 2, }; enum rsaprivkey_actions { ACT_rsa_get_d = 0, ACT_rsa_get_dp = 1, ACT_rsa_get_dq = 2, ACT_rsa_get_e___2 = 3, ACT_rsa_get_n___2 = 4, ACT_rsa_get_p = 5, ACT_rsa_get_q = 6, ACT_rsa_get_qinv = 7, NR__rsaprivkey_actions = 8, }; struct rsa_key { const u8 *n; const u8 *e; const u8 *d; const u8 *p; const u8 *q; const u8 *dp; const u8 *dq; const u8 *qinv; size_t n_sz; size_t e_sz; size_t d_sz; size_t p_sz; size_t q_sz; size_t dp_sz; size_t dq_sz; size_t qinv_sz; }; struct rsa_mpi_key { MPI n; MPI e; MPI d; }; struct asn1_decoder___2; struct rsa_asn1_template { const char *name; const u8 *data; size_t size; }; struct pkcs1pad_ctx { struct crypto_akcipher *child; unsigned int key_size; }; struct pkcs1pad_inst_ctx { struct crypto_akcipher_spawn spawn; const struct rsa_asn1_template *digest_info; }; struct pkcs1pad_request { struct scatterlist in_sg[2]; struct scatterlist out_sg[1]; uint8_t *in_buf; uint8_t *out_buf; struct akcipher_request child_req; }; struct crypto_report_acomp { char type[64]; }; struct acomp_alg { int (*compress)(struct acomp_req *); int (*decompress)(struct acomp_req *); void (*dst_free)(struct scatterlist *); int (*init)(struct crypto_acomp *); void (*exit)(struct crypto_acomp *); unsigned int reqsize; struct crypto_alg base; }; struct crypto_report_comp { char type[64]; }; struct crypto_scomp { struct crypto_tfm base; }; struct scomp_alg { void * (*alloc_ctx)(struct crypto_scomp *); void (*free_ctx)(struct crypto_scomp *, void *); int (*compress)(struct crypto_scomp *, const u8 *, unsigned int, u8 *, unsigned int *, void *); int (*decompress)(struct crypto_scomp *, const u8 *, unsigned int, u8 *, unsigned int *, void *); struct crypto_alg base; }; struct scomp_scratch { spinlock_t lock; void *src; void *dst; }; struct cryptomgr_param { struct rtattr *tb[34]; struct { struct rtattr attr; struct crypto_attr_type data; } type; struct { struct rtattr attr; struct crypto_attr_alg data; } attrs[32]; char template[128]; struct crypto_larval *larval; u32 otype; u32 omask; }; struct crypto_test_param { char driver[128]; char alg[128]; u32 type; }; struct hmac_ctx { struct crypto_shash *hash; }; struct md5_state { u32 hash[4]; u32 block[16]; u64 byte_count; }; struct sha1_state { u32 state[5]; u64 count; u8 buffer[64]; }; typedef void sha1_block_fn(struct sha1_state *, const u8 *, int); struct sha256_state { u32 state[8]; u64 count; u8 buf[64]; }; struct sha512_state { u64 state[8]; u64 count[2]; u8 buf[128]; }; typedef void sha512_block_fn(struct sha512_state *, const u8 *, int); struct crypto_rfc3686_ctx { struct crypto_skcipher *child; u8 nonce[4]; }; struct crypto_rfc3686_req_ctx { u8 iv[16]; struct skcipher_request subreq; }; struct crypto_aes_ctx { u32 key_enc[60]; u32 key_dec[60]; u32 key_length; }; struct chksum_desc_ctx { __u16 crc; }; struct lz4_ctx { void *lz4_comp_mem; }; struct crypto_report_rng { char type[64]; unsigned int seedsize; }; struct rand_data { __u64 data; __u64 old_data; __u64 prev_time; __u64 last_delta; __s64 last_delta2; unsigned int osr; unsigned char *mem; unsigned int memlocation; unsigned int memblocks; unsigned int memblocksize; unsigned int memaccessloops; int rct_count; unsigned int apt_observations; unsigned int apt_count; unsigned int apt_base; unsigned int apt_base_set: 1; unsigned int health_failure: 1; }; struct rand_data___2; struct jitterentropy { spinlock_t jent_lock; struct rand_data___2 *entropy_collector; unsigned int reset_cnt; }; typedef enum { ZSTD_fast = 0, ZSTD_dfast = 1, ZSTD_greedy = 2, ZSTD_lazy = 3, ZSTD_lazy2 = 4, ZSTD_btlazy2 = 5, ZSTD_btopt = 6, ZSTD_btopt2 = 7, } ZSTD_strategy; typedef struct { unsigned int windowLog; unsigned int chainLog; unsigned int hashLog; unsigned int searchLog; unsigned int searchLength; unsigned int targetLength; ZSTD_strategy strategy; } ZSTD_compressionParameters; typedef struct { unsigned int contentSizeFlag; unsigned int checksumFlag; unsigned int noDictIDFlag; } ZSTD_frameParameters; typedef struct { ZSTD_compressionParameters cParams; ZSTD_frameParameters fParams; } ZSTD_parameters; struct ZSTD_CCtx_s; typedef struct ZSTD_CCtx_s ZSTD_CCtx; struct ZSTD_DCtx_s; typedef struct ZSTD_DCtx_s ZSTD_DCtx; struct zstd_ctx { ZSTD_CCtx *cctx; ZSTD_DCtx *dctx; void *cwksp; void *dwksp; }; enum asymmetric_payload_bits { asym_crypto = 0, asym_subtype = 1, asym_key_ids = 2, asym_auth = 3, }; struct asymmetric_key_id { short unsigned int len; unsigned char data[0]; }; struct asymmetric_key_ids { void *id[2]; }; struct public_key_signature; struct asymmetric_key_subtype { struct module *owner; const char *name; short unsigned int name_len; void (*describe)(const struct key *, struct seq_file *); void (*destroy)(void *, void *); int (*query)(const struct kernel_pkey_params *, struct kernel_pkey_query *); int (*eds_op)(struct kernel_pkey_params *, const void *, void *); int (*verify_signature)(const struct key *, const struct public_key_signature *); }; struct public_key_signature { struct asymmetric_key_id *auth_ids[2]; u8 *s; u32 s_size; u8 *digest; u8 digest_size; const char *pkey_algo; const char *hash_algo; const char *encoding; const void *data; unsigned int data_size; }; struct asymmetric_key_parser { struct list_head link; struct module *owner; const char *name; int (*parse)(struct key_preparsed_payload *); }; enum OID { OID_id_dsa_with_sha1 = 0, OID_id_dsa = 1, OID_id_ecPublicKey = 2, OID_id_prime192v1 = 3, OID_id_prime256v1 = 4, OID_id_ecdsa_with_sha1 = 5, OID_id_ecdsa_with_sha224 = 6, OID_id_ecdsa_with_sha256 = 7, OID_id_ecdsa_with_sha384 = 8, OID_id_ecdsa_with_sha512 = 9, OID_rsaEncryption = 10, OID_md2WithRSAEncryption = 11, OID_md3WithRSAEncryption = 12, OID_md4WithRSAEncryption = 13, OID_sha1WithRSAEncryption = 14, OID_sha256WithRSAEncryption = 15, OID_sha384WithRSAEncryption = 16, OID_sha512WithRSAEncryption = 17, OID_sha224WithRSAEncryption = 18, OID_data = 19, OID_signed_data = 20, OID_email_address = 21, OID_contentType = 22, OID_messageDigest = 23, OID_signingTime = 24, OID_smimeCapabilites = 25, OID_smimeAuthenticatedAttrs = 26, OID_md2 = 27, OID_md4 = 28, OID_md5 = 29, OID_mskrb5 = 30, OID_krb5 = 31, OID_krb5u2u = 32, OID_msIndirectData = 33, OID_msStatementType = 34, OID_msSpOpusInfo = 35, OID_msPeImageDataObjId = 36, OID_msIndividualSPKeyPurpose = 37, OID_msOutlookExpress = 38, OID_ntlmssp = 39, OID_spnego = 40, OID_certAuthInfoAccess = 41, OID_sha1 = 42, OID_id_ansip384r1 = 43, OID_sha256 = 44, OID_sha384 = 45, OID_sha512 = 46, OID_sha224 = 47, OID_commonName = 48, OID_surname = 49, OID_countryName = 50, OID_locality = 51, OID_stateOrProvinceName = 52, OID_organizationName = 53, OID_organizationUnitName = 54, OID_title = 55, OID_description = 56, OID_name = 57, OID_givenName = 58, OID_initials = 59, OID_generationalQualifier = 60, OID_subjectKeyIdentifier = 61, OID_keyUsage = 62, OID_subjectAltName = 63, OID_issuerAltName = 64, OID_basicConstraints = 65, OID_crlDistributionPoints = 66, OID_certPolicies = 67, OID_authorityKeyIdentifier = 68, OID_extKeyUsage = 69, OID_gostCPSignA = 70, OID_gostCPSignB = 71, OID_gostCPSignC = 72, OID_gost2012PKey256 = 73, OID_gost2012PKey512 = 74, OID_gost2012Digest256 = 75, OID_gost2012Digest512 = 76, OID_gost2012Signature256 = 77, OID_gost2012Signature512 = 78, OID_gostTC26Sign256A = 79, OID_gostTC26Sign256B = 80, OID_gostTC26Sign256C = 81, OID_gostTC26Sign256D = 82, OID_gostTC26Sign512A = 83, OID_gostTC26Sign512B = 84, OID_gostTC26Sign512C = 85, OID_sm2 = 86, OID_sm3 = 87, OID_SM2_with_SM3 = 88, OID_sm3WithRSAEncryption = 89, OID_TPMLoadableKey = 90, OID_TPMImportableKey = 91, OID_TPMSealedData = 92, OID__NR = 93, }; struct public_key { void *key; u32 keylen; enum OID algo; void *params; u32 paramlen; bool key_is_private; const char *id_type; const char *pkey_algo; }; enum x509_actions { ACT_x509_extract_key_data = 0, ACT_x509_extract_name_segment = 1, ACT_x509_note_OID = 2, ACT_x509_note_issuer = 3, ACT_x509_note_not_after = 4, ACT_x509_note_not_before = 5, ACT_x509_note_params = 6, ACT_x509_note_pkey_algo = 7, ACT_x509_note_serial = 8, ACT_x509_note_signature = 9, ACT_x509_note_subject = 10, ACT_x509_note_tbs_certificate = 11, ACT_x509_process_extension = 12, NR__x509_actions = 13, }; enum x509_akid_actions { ACT_x509_akid_note_kid = 0, ACT_x509_akid_note_name = 1, ACT_x509_akid_note_serial = 2, ACT_x509_extract_name_segment___2 = 3, ACT_x509_note_OID___2 = 4, NR__x509_akid_actions = 5, }; struct x509_certificate { struct x509_certificate *next; struct x509_certificate *signer; struct public_key *pub; struct public_key_signature *sig; char *issuer; char *subject; struct asymmetric_key_id *id; struct asymmetric_key_id *skid; time64_t valid_from; time64_t valid_to; const void *tbs; unsigned int tbs_size; unsigned int raw_sig_size; const void *raw_sig; const void *raw_serial; unsigned int raw_serial_size; unsigned int raw_issuer_size; const void *raw_issuer; const void *raw_subject; unsigned int raw_subject_size; unsigned int raw_skid_size; const void *raw_skid; unsigned int index; bool seen; bool verified; bool self_signed; bool unsupported_key; bool unsupported_sig; bool blacklisted; }; struct x509_parse_context { struct x509_certificate *cert; long unsigned int data; const void *cert_start; const void *key; size_t key_size; const void *params; size_t params_size; enum OID key_algo; enum OID last_oid; enum OID algo_oid; unsigned char nr_mpi; u8 o_size; u8 cn_size; u8 email_size; u16 o_offset; u16 cn_offset; u16 email_offset; unsigned int raw_akid_size; const void *raw_akid; const void *akid_raw_issuer; unsigned int akid_raw_issuer_size; }; enum pkcs7_actions { ACT_pkcs7_check_content_type = 0, ACT_pkcs7_extract_cert = 1, ACT_pkcs7_note_OID = 2, ACT_pkcs7_note_certificate_list = 3, ACT_pkcs7_note_content = 4, ACT_pkcs7_note_data = 5, ACT_pkcs7_note_signed_info = 6, ACT_pkcs7_note_signeddata_version = 7, ACT_pkcs7_note_signerinfo_version = 8, ACT_pkcs7_sig_note_authenticated_attr = 9, ACT_pkcs7_sig_note_digest_algo = 10, ACT_pkcs7_sig_note_issuer = 11, ACT_pkcs7_sig_note_pkey_algo = 12, ACT_pkcs7_sig_note_serial = 13, ACT_pkcs7_sig_note_set_of_authattrs = 14, ACT_pkcs7_sig_note_signature = 15, ACT_pkcs7_sig_note_skid = 16, NR__pkcs7_actions = 17, }; struct pkcs7_signed_info { struct pkcs7_signed_info *next; struct x509_certificate *signer; unsigned int index; bool unsupported_crypto; bool blacklisted; const void *msgdigest; unsigned int msgdigest_len; unsigned int authattrs_len; const void *authattrs; long unsigned int aa_set; time64_t signing_time; struct public_key_signature *sig; }; struct pkcs7_message___2 { struct x509_certificate *certs; struct x509_certificate *crl; struct pkcs7_signed_info *signed_infos; u8 version; bool have_authattrs; enum OID data_type; size_t data_len; size_t data_hdrlen; const void *data; }; struct pkcs7_parse_context { struct pkcs7_message___2 *msg; struct pkcs7_signed_info *sinfo; struct pkcs7_signed_info **ppsinfo; struct x509_certificate *certs; struct x509_certificate **ppcerts; long unsigned int data; enum OID last_oid; unsigned int x509_index; unsigned int sinfo_index; const void *raw_serial; unsigned int raw_serial_size; unsigned int raw_issuer_size; const void *raw_issuer; const void *raw_skid; unsigned int raw_skid_size; bool expect_skid; }; enum hash_algo { HASH_ALGO_MD4 = 0, HASH_ALGO_MD5 = 1, HASH_ALGO_SHA1 = 2, HASH_ALGO_RIPE_MD_160 = 3, HASH_ALGO_SHA256 = 4, HASH_ALGO_SHA384 = 5, HASH_ALGO_SHA512 = 6, HASH_ALGO_SHA224 = 7, HASH_ALGO_RIPE_MD_128 = 8, HASH_ALGO_RIPE_MD_256 = 9, HASH_ALGO_RIPE_MD_320 = 10, HASH_ALGO_WP_256 = 11, HASH_ALGO_WP_384 = 12, HASH_ALGO_WP_512 = 13, HASH_ALGO_TGR_128 = 14, HASH_ALGO_TGR_160 = 15, HASH_ALGO_TGR_192 = 16, HASH_ALGO_SM3_256 = 17, HASH_ALGO_STREEBOG_256 = 18, HASH_ALGO_STREEBOG_512 = 19, HASH_ALGO__LAST = 20, }; struct mz_hdr { uint16_t magic; uint16_t lbsize; uint16_t blocks; uint16_t relocs; uint16_t hdrsize; uint16_t min_extra_pps; uint16_t max_extra_pps; uint16_t ss; uint16_t sp; uint16_t checksum; uint16_t ip; uint16_t cs; uint16_t reloc_table_offset; uint16_t overlay_num; uint16_t reserved0[4]; uint16_t oem_id; uint16_t oem_info; uint16_t reserved1[10]; uint32_t peaddr; char message[0]; }; struct pe_hdr { uint32_t magic; uint16_t machine; uint16_t sections; uint32_t timestamp; uint32_t symbol_table; uint32_t symbols; uint16_t opt_hdr_size; uint16_t flags; }; struct pe32_opt_hdr { uint16_t magic; uint8_t ld_major; uint8_t ld_minor; uint32_t text_size; uint32_t data_size; uint32_t bss_size; uint32_t entry_point; uint32_t code_base; uint32_t data_base; uint32_t image_base; uint32_t section_align; uint32_t file_align; uint16_t os_major; uint16_t os_minor; uint16_t image_major; uint16_t image_minor; uint16_t subsys_major; uint16_t subsys_minor; uint32_t win32_version; uint32_t image_size; uint32_t header_size; uint32_t csum; uint16_t subsys; uint16_t dll_flags; uint32_t stack_size_req; uint32_t stack_size; uint32_t heap_size_req; uint32_t heap_size; uint32_t loader_flags; uint32_t data_dirs; }; struct pe32plus_opt_hdr { uint16_t magic; uint8_t ld_major; uint8_t ld_minor; uint32_t text_size; uint32_t data_size; uint32_t bss_size; uint32_t entry_point; uint32_t code_base; uint64_t image_base; uint32_t section_align; uint32_t file_align; uint16_t os_major; uint16_t os_minor; uint16_t image_major; uint16_t image_minor; uint16_t subsys_major; uint16_t subsys_minor; uint32_t win32_version; uint32_t image_size; uint32_t header_size; uint32_t csum; uint16_t subsys; uint16_t dll_flags; uint64_t stack_size_req; uint64_t stack_size; uint64_t heap_size_req; uint64_t heap_size; uint32_t loader_flags; uint32_t data_dirs; }; struct data_dirent { uint32_t virtual_address; uint32_t size; }; struct data_directory { struct data_dirent exports; struct data_dirent imports; struct data_dirent resources; struct data_dirent exceptions; struct data_dirent certs; struct data_dirent base_relocations; struct data_dirent debug; struct data_dirent arch; struct data_dirent global_ptr; struct data_dirent tls; struct data_dirent load_config; struct data_dirent bound_imports; struct data_dirent import_addrs; struct data_dirent delay_imports; struct data_dirent clr_runtime_hdr; struct data_dirent reserved; }; struct section_header { char name[8]; uint32_t virtual_size; uint32_t virtual_address; uint32_t raw_data_size; uint32_t data_addr; uint32_t relocs; uint32_t line_numbers; uint16_t num_relocs; uint16_t num_lin_numbers; uint32_t flags; }; struct win_certificate { uint32_t length; uint16_t revision; uint16_t cert_type; }; struct pefile_context { unsigned int header_size; unsigned int image_checksum_offset; unsigned int cert_dirent_offset; unsigned int n_data_dirents; unsigned int n_sections; unsigned int certs_size; unsigned int sig_offset; unsigned int sig_len; const struct section_header *secs; const void *digest; unsigned int digest_len; const char *digest_algo; }; enum mscode_actions { ACT_mscode_note_content_type = 0, ACT_mscode_note_digest = 1, ACT_mscode_note_digest_algo = 2, NR__mscode_actions = 3, }; enum rq_qos_id { RQ_QOS_WBT = 0, RQ_QOS_LATENCY = 1, RQ_QOS_COST = 2, RQ_QOS_IOPRIO = 3, }; struct rq_qos_ops; struct rq_qos { struct rq_qos_ops *ops; struct request_queue *q; enum rq_qos_id id; struct rq_qos *next; struct dentry *debugfs_dir; }; enum hctx_type { HCTX_TYPE_DEFAULT = 0, HCTX_TYPE_READ = 1, HCTX_TYPE_POLL = 2, HCTX_MAX_TYPES = 3, }; struct rq_qos_ops { void (*throttle)(struct rq_qos *, struct bio *); void (*track)(struct rq_qos *, struct request *, struct bio *); void (*merge)(struct rq_qos *, struct request *, struct bio *); void (*issue)(struct rq_qos *, struct request *); void (*requeue)(struct rq_qos *, struct request *); void (*done)(struct rq_qos *, struct request *); void (*done_bio)(struct rq_qos *, struct bio *); void (*cleanup)(struct rq_qos *, struct bio *); void (*queue_depth_changed)(struct rq_qos *); void (*exit)(struct rq_qos *); const struct blk_mq_debugfs_attr *debugfs_attrs; }; struct biovec_slab { int nr_vecs; char *name; struct kmem_cache *slab; }; struct bio_slab { struct kmem_cache *slab; unsigned int slab_ref; unsigned int slab_size; char name[8]; }; enum { BLK_MQ_F_SHOULD_MERGE = 1, BLK_MQ_F_TAG_QUEUE_SHARED = 2, BLK_MQ_F_STACKING = 4, BLK_MQ_F_TAG_HCTX_SHARED = 8, BLK_MQ_F_BLOCKING = 32, BLK_MQ_F_NO_SCHED = 64, BLK_MQ_F_ALLOC_POLICY_START_BIT = 8, BLK_MQ_F_ALLOC_POLICY_BITS = 1, BLK_MQ_S_STOPPED = 0, BLK_MQ_S_TAG_ACTIVE = 1, BLK_MQ_S_SCHED_RESTART = 2, BLK_MQ_S_INACTIVE = 3, BLK_MQ_MAX_DEPTH = 10240, BLK_MQ_CPU_WORK_BATCH = 8, }; enum { WBT_RWQ_BG = 0, WBT_RWQ_KSWAPD = 1, WBT_RWQ_DISCARD = 2, WBT_NUM_RWQ = 3, }; struct blk_plug_cb; typedef void (*blk_plug_cb_fn)(struct blk_plug_cb *, bool); struct blk_plug_cb { struct list_head list; blk_plug_cb_fn callback; void *data; }; enum { BLK_MQ_REQ_NOWAIT = 1, BLK_MQ_REQ_RESERVED = 2, BLK_MQ_REQ_PM = 4, }; struct trace_event_raw_block_buffer { struct trace_entry ent; dev_t dev; sector_t sector; size_t size; char __data[0]; }; struct trace_event_raw_block_rq_requeue { struct trace_entry ent; dev_t dev; sector_t sector; unsigned int nr_sector; char rwbs[8]; u32 __data_loc_cmd; char __data[0]; }; struct trace_event_raw_block_rq_complete { struct trace_entry ent; dev_t dev; sector_t sector; unsigned int nr_sector; int error; char rwbs[8]; u32 __data_loc_cmd; char __data[0]; }; struct trace_event_raw_block_rq { struct trace_entry ent; dev_t dev; sector_t sector; unsigned int nr_sector; unsigned int bytes; char rwbs[8]; char comm[16]; u32 __data_loc_cmd; char __data[0]; }; struct trace_event_raw_block_bio_complete { struct trace_entry ent; dev_t dev; sector_t sector; unsigned int nr_sector; int error; char rwbs[8]; char __data[0]; }; struct trace_event_raw_block_bio { struct trace_entry ent; dev_t dev; sector_t sector; unsigned int nr_sector; char rwbs[8]; char comm[16]; char __data[0]; }; struct trace_event_raw_block_plug { struct trace_entry ent; char comm[16]; char __data[0]; }; struct trace_event_raw_block_unplug { struct trace_entry ent; int nr_rq; char comm[16]; char __data[0]; }; struct trace_event_raw_block_split { struct trace_entry ent; dev_t dev; sector_t sector; sector_t new_sector; char rwbs[8]; char comm[16]; char __data[0]; }; struct trace_event_raw_block_bio_remap { struct trace_entry ent; dev_t dev; sector_t sector; unsigned int nr_sector; dev_t old_dev; sector_t old_sector; char rwbs[8]; char __data[0]; }; struct trace_event_raw_block_rq_remap { struct trace_entry ent; dev_t dev; sector_t sector; unsigned int nr_sector; dev_t old_dev; sector_t old_sector; unsigned int nr_bios; char rwbs[8]; char __data[0]; }; struct trace_event_data_offsets_block_buffer {}; struct trace_event_data_offsets_block_rq_requeue { u32 cmd; }; struct trace_event_data_offsets_block_rq_complete { u32 cmd; }; struct trace_event_data_offsets_block_rq { u32 cmd; }; struct trace_event_data_offsets_block_bio_complete {}; struct trace_event_data_offsets_block_bio {}; struct trace_event_data_offsets_block_plug {}; struct trace_event_data_offsets_block_unplug {}; struct trace_event_data_offsets_block_split {}; struct trace_event_data_offsets_block_bio_remap {}; struct trace_event_data_offsets_block_rq_remap {}; typedef void (*btf_trace_block_touch_buffer)(void *, struct buffer_head *); typedef void (*btf_trace_block_dirty_buffer)(void *, struct buffer_head *); typedef void (*btf_trace_block_rq_requeue)(void *, struct request *); typedef void (*btf_trace_block_rq_complete)(void *, struct request *, int, unsigned int); typedef void (*btf_trace_block_rq_insert)(void *, struct request *); typedef void (*btf_trace_block_rq_issue)(void *, struct request *); typedef void (*btf_trace_block_rq_merge)(void *, struct request *); typedef void (*btf_trace_block_bio_complete)(void *, struct request_queue *, struct bio *); typedef void (*btf_trace_block_bio_bounce)(void *, struct bio *); typedef void (*btf_trace_block_bio_backmerge)(void *, struct bio *); typedef void (*btf_trace_block_bio_frontmerge)(void *, struct bio *); typedef void (*btf_trace_block_bio_queue)(void *, struct bio *); typedef void (*btf_trace_block_getrq)(void *, struct bio *); typedef void (*btf_trace_block_plug)(void *, struct request_queue *); typedef void (*btf_trace_block_unplug)(void *, struct request_queue *, unsigned int, bool); typedef void (*btf_trace_block_split)(void *, struct bio *, unsigned int); typedef void (*btf_trace_block_bio_remap)(void *, struct bio *, dev_t, sector_t); typedef void (*btf_trace_block_rq_remap)(void *, struct request *, dev_t, sector_t); enum { BLK_MQ_NO_TAG = 4294967295, BLK_MQ_TAG_MIN = 1, BLK_MQ_TAG_MAX = 4294967294, }; struct queue_sysfs_entry { struct attribute attr; ssize_t (*show)(struct request_queue *, char *); ssize_t (*store)(struct request_queue *, const char *, size_t); }; enum { REQ_FSEQ_PREFLUSH = 1, REQ_FSEQ_DATA = 2, REQ_FSEQ_POSTFLUSH = 4, REQ_FSEQ_DONE = 8, REQ_FSEQ_ACTIONS = 7, FLUSH_PENDING_TIMEOUT = 1500, }; enum blk_default_limits { BLK_MAX_SEGMENTS = 128, BLK_SAFE_MAX_SECTORS = 255, BLK_DEF_MAX_SECTORS = 2560, BLK_MAX_SEGMENT_SIZE = 65536, BLK_SEG_BOUNDARY_MASK = 4294967295, }; enum { ICQ_EXITED = 4, ICQ_DESTROYED = 8, }; struct rq_map_data { struct page **pages; int page_order; int nr_entries; long unsigned int offset; int null_mapped; int from_user; }; struct bio_map_data { bool is_our_pages: 1; bool is_null_mapped: 1; struct iov_iter iter; struct iovec iov[0]; }; struct req_iterator { struct bvec_iter iter; struct bio *bio; }; enum bio_merge_status { BIO_MERGE_OK = 0, BIO_MERGE_NONE = 1, BIO_MERGE_FAILED = 2, }; typedef bool (*sb_for_each_fn)(struct sbitmap *, unsigned int, void *); typedef bool busy_iter_fn(struct blk_mq_hw_ctx *, struct request *, void *, bool); typedef bool busy_tag_iter_fn(struct request *, void *, bool); enum { BLK_MQ_UNIQUE_TAG_BITS = 16, BLK_MQ_UNIQUE_TAG_MASK = 65535, }; struct mq_inflight { struct block_device *part; unsigned int inflight[2]; }; struct flush_busy_ctx_data { struct blk_mq_hw_ctx *hctx; struct list_head *list; }; struct dispatch_rq_data { struct blk_mq_hw_ctx *hctx; struct request *rq; }; enum prep_dispatch { PREP_DISPATCH_OK = 0, PREP_DISPATCH_NO_TAG = 1, PREP_DISPATCH_NO_BUDGET = 2, }; struct rq_iter_data { struct blk_mq_hw_ctx *hctx; bool has_rq; }; struct blk_mq_qe_pair { struct list_head node; struct request_queue *q; struct elevator_type *type; }; struct sbq_wait { struct sbitmap_queue *sbq; struct wait_queue_entry wait; }; struct bt_iter_data { struct blk_mq_hw_ctx *hctx; busy_iter_fn *fn; void *data; bool reserved; }; struct bt_tags_iter_data { struct blk_mq_tags *tags; busy_tag_iter_fn *fn; void *data; unsigned int flags; }; struct blk_queue_stats { struct list_head callbacks; spinlock_t lock; bool enable_accounting; }; struct blk_mq_ctx_sysfs_entry { struct attribute attr; ssize_t (*show)(struct blk_mq_ctx *, char *); ssize_t (*store)(struct blk_mq_ctx *, const char *, size_t); }; struct blk_mq_hw_ctx_sysfs_entry { struct attribute attr; ssize_t (*show)(struct blk_mq_hw_ctx *, char *); ssize_t (*store)(struct blk_mq_hw_ctx *, const char *, size_t); }; typedef u32 compat_caddr_t; struct hd_geometry { unsigned char heads; unsigned char sectors; short unsigned int cylinders; long unsigned int start; }; struct blkpg_ioctl_arg { int op; int flags; int datalen; void *data; }; struct blkpg_partition { long long int start; long long int length; int pno; char devname[64]; char volname[64]; }; struct pr_reservation { __u64 key; __u32 type; __u32 flags; }; struct pr_registration { __u64 old_key; __u64 new_key; __u32 flags; __u32 __pad; }; struct pr_preempt { __u64 old_key; __u64 new_key; __u32 type; __u32 flags; }; struct pr_clear { __u64 key; __u32 flags; __u32 __pad; }; struct compat_blkpg_ioctl_arg { compat_int_t op; compat_int_t flags; compat_int_t datalen; compat_caddr_t data; }; struct compat_hd_geometry { unsigned char heads; unsigned char sectors; short unsigned int cylinders; u32 start; }; struct klist_node; struct klist { spinlock_t k_lock; struct list_head k_list; void (*get)(struct klist_node *); void (*put)(struct klist_node *); }; struct klist_node { void *n_klist; struct list_head n_node; struct kref n_ref; }; struct klist_iter { struct klist *i_klist; struct klist_node *i_cur; }; struct class_dev_iter { struct klist_iter ki; const struct device_type *type; }; struct badblocks { struct device *dev; int count; int unacked_exist; int shift; u64 *page; int changed; seqlock_t lock; sector_t sector; sector_t size; }; struct blk_major_name { struct blk_major_name *next; int major; char name[16]; void (*probe)(dev_t); }; enum { IOPRIO_WHO_PROCESS = 1, IOPRIO_WHO_PGRP = 2, IOPRIO_WHO_USER = 3, }; struct parsed_partitions { struct block_device *bdev; char name[32]; struct { sector_t from; sector_t size; int flags; bool has_info; struct partition_meta_info info; } *parts; int next; int limit; bool access_beyond_eod; char *pp_buf; }; typedef struct { struct page *v; } Sector; struct lvm_rec { char lvm_id[4]; char reserved4[16]; __be32 lvmarea_len; __be32 vgda_len; __be32 vgda_psn[2]; char reserved36[10]; __be16 pp_size; char reserved46[12]; __be16 version; }; struct vgda { __be32 secs; __be32 usec; char reserved8[16]; __be16 numlvs; __be16 maxlvs; __be16 pp_size; __be16 numpvs; __be16 total_vgdas; __be16 vgda_size; }; struct lvd { __be16 lv_ix; __be16 res2; __be16 res4; __be16 maxsize; __be16 lv_state; __be16 mirror; __be16 mirror_policy; __be16 num_lps; __be16 res10[8]; }; struct lvname { char name[64]; }; struct ppe { __be16 lv_ix; short unsigned int res2; short unsigned int res4; __be16 lp_ix; short unsigned int res8[12]; }; struct pvd { char reserved0[16]; __be16 pp_count; char reserved18[2]; __be32 psn_part1; char reserved24[8]; struct ppe ppe[1016]; }; struct lv_info { short unsigned int pps_per_lv; short unsigned int pps_found; unsigned char lv_is_contiguous; }; struct mac_partition { __be16 signature; __be16 res1; __be32 map_count; __be32 start_block; __be32 block_count; char name[32]; char type[32]; __be32 data_start; __be32 data_count; __be32 status; __be32 boot_start; __be32 boot_size; __be32 boot_load; __be32 boot_load2; __be32 boot_entry; __be32 boot_entry2; __be32 boot_cksum; char processor[16]; }; struct mac_driver_desc { __be16 signature; __be16 block_size; __be32 block_count; }; struct msdos_partition { u8 boot_ind; u8 head; u8 sector; u8 cyl; u8 sys_ind; u8 end_head; u8 end_sector; u8 end_cyl; __le32 start_sect; __le32 nr_sects; }; struct frag { struct list_head list; u32 group; u8 num; u8 rec; u8 map; u8 data[0]; }; struct privhead { u16 ver_major; u16 ver_minor; u64 logical_disk_start; u64 logical_disk_size; u64 config_start; u64 config_size; uuid_t disk_id; }; struct tocblock { u8 bitmap1_name[16]; u64 bitmap1_start; u64 bitmap1_size; u8 bitmap2_name[16]; u64 bitmap2_start; u64 bitmap2_size; }; struct vmdb { u16 ver_major; u16 ver_minor; u32 vblk_size; u32 vblk_offset; u32 last_vblk_seq; }; struct vblk_comp { u8 state[16]; u64 parent_id; u8 type; u8 children; u16 chunksize; }; struct vblk_dgrp { u8 disk_id[64]; }; struct vblk_disk { uuid_t disk_id; u8 alt_name[128]; }; struct vblk_part { u64 start; u64 size; u64 volume_offset; u64 parent_id; u64 disk_id; u8 partnum; }; struct vblk_volu { u8 volume_type[16]; u8 volume_state[16]; u8 guid[16]; u8 drive_hint[4]; u64 size; u8 partition_type; }; struct vblk { u8 name[64]; u64 obj_id; u32 sequence; u8 flags; u8 type; union { struct vblk_comp comp; struct vblk_dgrp dgrp; struct vblk_disk disk; struct vblk_part part; struct vblk_volu volu; } vblk; struct list_head list; }; struct ldmdb { struct privhead ph; struct tocblock toc; struct vmdb vm; struct list_head v_dgrp; struct list_head v_disk; struct list_head v_volu; struct list_head v_comp; struct list_head v_part; }; struct fat_boot_sector { __u8 ignored[3]; __u8 system_id[8]; __u8 sector_size[2]; __u8 sec_per_clus; __le16 reserved; __u8 fats; __u8 dir_entries[2]; __u8 sectors[2]; __u8 media; __le16 fat_length; __le16 secs_track; __le16 heads; __le32 hidden; __le32 total_sect; union { struct { __u8 drive_number; __u8 state; __u8 signature; __u8 vol_id[4]; __u8 vol_label[11]; __u8 fs_type[8]; } fat16; struct { __le32 length; __le16 flags; __u8 version[2]; __le32 root_cluster; __le16 info_sector; __le16 backup_boot; __le16 reserved2[6]; __u8 drive_number; __u8 state; __u8 signature; __u8 vol_id[4]; __u8 vol_label[11]; __u8 fs_type[8]; } fat32; }; }; enum msdos_sys_ind { DOS_EXTENDED_PARTITION = 5, LINUX_EXTENDED_PARTITION = 133, WIN98_EXTENDED_PARTITION = 15, LINUX_DATA_PARTITION = 131, LINUX_LVM_PARTITION = 142, LINUX_RAID_PARTITION = 253, SOLARIS_X86_PARTITION = 130, NEW_SOLARIS_X86_PARTITION = 191, DM6_AUX1PARTITION = 81, DM6_AUX3PARTITION = 83, DM6_PARTITION = 84, EZD_PARTITION = 85, FREEBSD_PARTITION = 165, OPENBSD_PARTITION = 166, NETBSD_PARTITION = 169, BSDI_PARTITION = 183, MINIX_PARTITION = 129, UNIXWARE_PARTITION = 99, }; struct solaris_x86_slice { __le16 s_tag; __le16 s_flag; __le32 s_start; __le32 s_size; }; struct solaris_x86_vtoc { unsigned int v_bootinfo[3]; __le32 v_sanity; __le32 v_version; char v_volume[8]; __le16 v_sectorsz; __le16 v_nparts; unsigned int v_reserved[10]; struct solaris_x86_slice v_slice[16]; unsigned int timestamp[16]; char v_asciilabel[128]; }; struct bsd_partition { __le32 p_size; __le32 p_offset; __le32 p_fsize; __u8 p_fstype; __u8 p_frag; __le16 p_cpg; }; struct bsd_disklabel { __le32 d_magic; __s16 d_type; __s16 d_subtype; char d_typename[16]; char d_packname[16]; __u32 d_secsize; __u32 d_nsectors; __u32 d_ntracks; __u32 d_ncylinders; __u32 d_secpercyl; __u32 d_secperunit; __u16 d_sparespertrack; __u16 d_sparespercyl; __u32 d_acylinders; __u16 d_rpm; __u16 d_interleave; __u16 d_trackskew; __u16 d_cylskew; __u32 d_headswitch; __u32 d_trkseek; __u32 d_flags; __u32 d_drivedata[5]; __u32 d_spare[5]; __le32 d_magic2; __le16 d_checksum; __le16 d_npartitions; __le32 d_bbsize; __le32 d_sbsize; struct bsd_partition d_partitions[16]; }; struct _gpt_header { __le64 signature; __le32 revision; __le32 header_size; __le32 header_crc32; __le32 reserved1; __le64 my_lba; __le64 alternate_lba; __le64 first_usable_lba; __le64 last_usable_lba; efi_guid_t disk_guid; __le64 partition_entry_lba; __le32 num_partition_entries; __le32 sizeof_partition_entry; __le32 partition_entry_array_crc32; } __attribute__((packed)); typedef struct _gpt_header gpt_header; struct _gpt_entry_attributes { u64 required_to_function: 1; u64 reserved: 47; u64 type_guid_specific: 16; }; typedef struct _gpt_entry_attributes gpt_entry_attributes; struct _gpt_entry { efi_guid_t partition_type_guid; efi_guid_t unique_partition_guid; __le64 starting_lba; __le64 ending_lba; gpt_entry_attributes attributes; __le16 partition_name[36]; }; typedef struct _gpt_entry gpt_entry; struct _gpt_mbr_record { u8 boot_indicator; u8 start_head; u8 start_sector; u8 start_track; u8 os_type; u8 end_head; u8 end_sector; u8 end_track; __le32 starting_lba; __le32 size_in_lba; }; typedef struct _gpt_mbr_record gpt_mbr_record; struct _legacy_mbr { u8 boot_code[440]; __le32 unique_mbr_signature; __le16 unknown; gpt_mbr_record partition_record[4]; __le16 signature; } __attribute__((packed)); typedef struct _legacy_mbr legacy_mbr; struct d_partition { __le32 p_res; u8 p_fstype; u8 p_res2[3]; __le32 p_offset; __le32 p_size; }; struct disklabel { u8 d_reserved[270]; struct d_partition d_partitions[2]; u8 d_blank[208]; __le16 d_magic; } __attribute__((packed)); struct rq_wait { wait_queue_head_t wait; atomic_t inflight; }; struct rq_depth { unsigned int max_depth; int scale_step; bool scaled_max; unsigned int queue_depth; unsigned int default_depth; }; typedef bool acquire_inflight_cb_t(struct rq_wait *, void *); typedef void cleanup_cb_t(struct rq_wait *, void *); struct rq_qos_wait_data { struct wait_queue_entry wq; struct task_struct *task; struct rq_wait *rqw; acquire_inflight_cb_t *cb; void *private_data; bool got_token; }; enum { DISK_EVENT_FLAG_POLL = 1, DISK_EVENT_FLAG_UEVENT = 2, }; struct disk_events { struct list_head node; struct gendisk *disk; spinlock_t lock; struct mutex block_mutex; int block; unsigned int pending; unsigned int clearing; long int poll_msecs; struct delayed_work dwork; }; struct cdrom_device_ops; struct cdrom_device_info { const struct cdrom_device_ops *ops; struct list_head list; struct gendisk *disk; void *handle; int mask; int speed; int capacity; unsigned int options: 30; unsigned int mc_flags: 2; unsigned int vfs_events; unsigned int ioctl_events; int use_count; char name[20]; __u8 sanyo_slot: 2; __u8 keeplocked: 1; __u8 reserved: 5; int cdda_method; __u8 last_sense; __u8 media_written; short unsigned int mmc3_profile; int for_data; int (*exit)(struct cdrom_device_info *); int mrw_mode_page; }; enum sam_status { SAM_STAT_GOOD = 0, SAM_STAT_CHECK_CONDITION = 2, SAM_STAT_CONDITION_MET = 4, SAM_STAT_BUSY = 8, SAM_STAT_INTERMEDIATE = 16, SAM_STAT_INTERMEDIATE_CONDITION_MET = 20, SAM_STAT_RESERVATION_CONFLICT = 24, SAM_STAT_COMMAND_TERMINATED = 34, SAM_STAT_TASK_SET_FULL = 40, SAM_STAT_ACA_ACTIVE = 48, SAM_STAT_TASK_ABORTED = 64, }; struct scsi_sense_hdr { u8 response_code; u8 sense_key; u8 asc; u8 ascq; u8 byte4; u8 byte5; u8 byte6; u8 additional_length; }; struct cdrom_msf0 { __u8 minute; __u8 second; __u8 frame; }; union cdrom_addr { struct cdrom_msf0 msf; int lba; }; struct cdrom_multisession { union cdrom_addr addr; __u8 xa_flag; __u8 addr_format; }; struct cdrom_mcn { __u8 medium_catalog_number[14]; }; struct request_sense; struct cdrom_generic_command { unsigned char cmd[12]; unsigned char *buffer; unsigned int buflen; int stat; struct request_sense *sense; unsigned char data_direction; int quiet; int timeout; union { void *reserved[1]; void *unused; }; }; struct request_sense { __u8 error_code: 7; __u8 valid: 1; __u8 segment_number; __u8 sense_key: 4; __u8 reserved2: 1; __u8 ili: 1; __u8 reserved1: 2; __u8 information[4]; __u8 add_sense_len; __u8 command_info[4]; __u8 asc; __u8 ascq; __u8 fruc; __u8 sks[3]; __u8 asb[46]; }; struct packet_command { unsigned char cmd[12]; unsigned char *buffer; unsigned int buflen; int stat; struct scsi_sense_hdr *sshdr; unsigned char data_direction; int quiet; int timeout; void *reserved[1]; }; struct cdrom_device_ops { int (*open)(struct cdrom_device_info *, int); void (*release)(struct cdrom_device_info *); int (*drive_status)(struct cdrom_device_info *, int); unsigned int (*check_events)(struct cdrom_device_info *, unsigned int, int); int (*tray_move)(struct cdrom_device_info *, int); int (*lock_door)(struct cdrom_device_info *, int); int (*select_speed)(struct cdrom_device_info *, int); int (*select_disc)(struct cdrom_device_info *, int); int (*get_last_session)(struct cdrom_device_info *, struct cdrom_multisession *); int (*get_mcn)(struct cdrom_device_info *, struct cdrom_mcn *); int (*reset)(struct cdrom_device_info *); int (*audio_ioctl)(struct cdrom_device_info *, unsigned int, void *); const int capability; int (*generic_packet)(struct cdrom_device_info *, struct packet_command *); }; enum scsi_msg_byte { COMMAND_COMPLETE = 0, EXTENDED_MESSAGE = 1, SAVE_POINTERS = 2, RESTORE_POINTERS = 3, DISCONNECT = 4, INITIATOR_ERROR = 5, ABORT_TASK_SET = 6, MESSAGE_REJECT = 7, NOP___2 = 8, MSG_PARITY_ERROR = 9, LINKED_CMD_COMPLETE = 10, LINKED_FLG_CMD_COMPLETE = 11, TARGET_RESET = 12, ABORT_TASK = 13, CLEAR_TASK_SET = 14, INITIATE_RECOVERY = 15, RELEASE_RECOVERY = 16, TERMINATE_IO_PROC = 17, CLEAR_ACA = 22, LOGICAL_UNIT_RESET = 23, SIMPLE_QUEUE_TAG = 32, HEAD_OF_QUEUE_TAG = 33, ORDERED_QUEUE_TAG = 34, IGNORE_WIDE_RESIDUE = 35, ACA = 36, QAS_REQUEST = 85, BUS_DEVICE_RESET = 12, ABORT = 6, }; struct scsi_ioctl_command { unsigned int inlen; unsigned int outlen; unsigned char data[0]; }; enum scsi_device_event { SDEV_EVT_MEDIA_CHANGE = 1, SDEV_EVT_INQUIRY_CHANGE_REPORTED = 2, SDEV_EVT_CAPACITY_CHANGE_REPORTED = 3, SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED = 4, SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED = 5, SDEV_EVT_LUN_CHANGE_REPORTED = 6, SDEV_EVT_ALUA_STATE_CHANGE_REPORTED = 7, SDEV_EVT_POWER_ON_RESET_OCCURRED = 8, SDEV_EVT_FIRST = 1, SDEV_EVT_LAST = 8, SDEV_EVT_MAXBITS = 9, }; struct scsi_request { unsigned char __cmd[16]; unsigned char *cmd; short unsigned int cmd_len; int result; unsigned int sense_len; unsigned int resid_len; int retries; void *sense; }; struct sg_io_hdr { int interface_id; int dxfer_direction; unsigned char cmd_len; unsigned char mx_sb_len; short unsigned int iovec_count; unsigned int dxfer_len; void *dxferp; unsigned char *cmdp; void *sbp; unsigned int timeout; unsigned int flags; int pack_id; void *usr_ptr; unsigned char status; unsigned char masked_status; unsigned char msg_status; unsigned char sb_len_wr; short unsigned int host_status; short unsigned int driver_status; int resid; unsigned int duration; unsigned int info; }; struct compat_sg_io_hdr { compat_int_t interface_id; compat_int_t dxfer_direction; unsigned char cmd_len; unsigned char mx_sb_len; short unsigned int iovec_count; compat_uint_t dxfer_len; compat_uint_t dxferp; compat_uptr_t cmdp; compat_uptr_t sbp; compat_uint_t timeout; compat_uint_t flags; compat_int_t pack_id; compat_uptr_t usr_ptr; unsigned char status; unsigned char masked_status; unsigned char msg_status; unsigned char sb_len_wr; short unsigned int host_status; short unsigned int driver_status; compat_int_t resid; compat_uint_t duration; compat_uint_t info; }; struct blk_cmd_filter { long unsigned int read_ok[4]; long unsigned int write_ok[4]; }; struct compat_cdrom_generic_command { unsigned char cmd[12]; compat_caddr_t buffer; compat_uint_t buflen; compat_int_t stat; compat_caddr_t sense; unsigned char data_direction; unsigned char pad[3]; compat_int_t quiet; compat_int_t timeout; compat_caddr_t unused; }; enum { OMAX_SB_LEN = 16, }; struct bsg_device { struct request_queue *queue; spinlock_t lock; struct hlist_node dev_list; refcount_t ref_count; char name[20]; int max_queue; }; struct bsg_job; typedef int bsg_job_fn(struct bsg_job *); struct bsg_buffer { unsigned int payload_len; int sg_cnt; struct scatterlist *sg_list; }; struct bsg_job { struct device *dev; struct kref kref; unsigned int timeout; void *request; void *reply; unsigned int request_len; unsigned int reply_len; struct bsg_buffer request_payload; struct bsg_buffer reply_payload; int result; unsigned int reply_payload_rcv_len; struct request *bidi_rq; struct bio *bidi_bio; void *dd_data; }; typedef enum blk_eh_timer_return bsg_timeout_fn(struct request *); struct bsg_set { struct blk_mq_tag_set tag_set; bsg_job_fn *job_fn; bsg_timeout_fn *timeout_fn; }; typedef struct blkcg_policy_data *blkcg_pol_alloc_cpd_fn(gfp_t); typedef void blkcg_pol_init_cpd_fn(struct blkcg_policy_data *); typedef void blkcg_pol_free_cpd_fn(struct blkcg_policy_data *); typedef void blkcg_pol_bind_cpd_fn(struct blkcg_policy_data *); typedef struct blkg_policy_data *blkcg_pol_alloc_pd_fn(gfp_t, struct request_queue *, struct blkcg *); typedef void blkcg_pol_init_pd_fn(struct blkg_policy_data *); typedef void blkcg_pol_online_pd_fn(struct blkg_policy_data *); typedef void blkcg_pol_offline_pd_fn(struct blkg_policy_data *); typedef void blkcg_pol_free_pd_fn(struct blkg_policy_data *); typedef void blkcg_pol_reset_pd_stats_fn(struct blkg_policy_data *); typedef size_t blkcg_pol_stat_pd_fn(struct blkg_policy_data *, char *, size_t); struct blkcg_policy { int plid; struct cftype *dfl_cftypes; struct cftype *legacy_cftypes; blkcg_pol_alloc_cpd_fn *cpd_alloc_fn; blkcg_pol_init_cpd_fn *cpd_init_fn; blkcg_pol_free_cpd_fn *cpd_free_fn; blkcg_pol_bind_cpd_fn *cpd_bind_fn; blkcg_pol_alloc_pd_fn *pd_alloc_fn; blkcg_pol_init_pd_fn *pd_init_fn; blkcg_pol_online_pd_fn *pd_online_fn; blkcg_pol_offline_pd_fn *pd_offline_fn; blkcg_pol_free_pd_fn *pd_free_fn; blkcg_pol_reset_pd_stats_fn *pd_reset_stats_fn; blkcg_pol_stat_pd_fn *pd_stat_fn; }; struct blkg_conf_ctx { struct block_device *bdev; struct blkcg_gq *blkg; char *body; }; enum blkg_rwstat_type { BLKG_RWSTAT_READ = 0, BLKG_RWSTAT_WRITE = 1, BLKG_RWSTAT_SYNC = 2, BLKG_RWSTAT_ASYNC = 3, BLKG_RWSTAT_DISCARD = 4, BLKG_RWSTAT_NR = 5, BLKG_RWSTAT_TOTAL = 5, }; struct blkg_rwstat { struct percpu_counter cpu_cnt[5]; atomic64_t aux_cnt[5]; }; struct blkg_rwstat_sample { u64 cnt[5]; }; struct throtl_service_queue { struct throtl_service_queue *parent_sq; struct list_head queued[2]; unsigned int nr_queued[2]; struct rb_root_cached pending_tree; unsigned int nr_pending; long unsigned int first_pending_disptime; struct timer_list pending_timer; }; struct latency_bucket { long unsigned int total_latency; int samples; }; struct avg_latency_bucket { long unsigned int latency; bool valid; }; struct throtl_data { struct throtl_service_queue service_queue; struct request_queue *queue; unsigned int nr_queued[2]; unsigned int throtl_slice; struct work_struct dispatch_work; unsigned int limit_index; bool limit_valid[2]; long unsigned int low_upgrade_time; long unsigned int low_downgrade_time; unsigned int scale; struct latency_bucket tmp_buckets[18]; struct avg_latency_bucket avg_buckets[18]; struct latency_bucket *latency_buckets[2]; long unsigned int last_calculate_time; long unsigned int filtered_latency; bool track_bio_latency; }; struct throtl_grp; struct throtl_qnode { struct list_head node; struct bio_list bios; struct throtl_grp *tg; }; struct throtl_grp { struct blkg_policy_data pd; struct rb_node rb_node; struct throtl_data *td; struct throtl_service_queue service_queue; struct throtl_qnode qnode_on_self[2]; struct throtl_qnode qnode_on_parent[2]; long unsigned int disptime; unsigned int flags; bool has_rules[2]; uint64_t bps[4]; uint64_t bps_conf[4]; unsigned int iops[4]; unsigned int iops_conf[4]; uint64_t bytes_disp[2]; unsigned int io_disp[2]; long unsigned int last_low_overflow_time[2]; uint64_t last_bytes_disp[2]; unsigned int last_io_disp[2]; long unsigned int last_check_time; long unsigned int latency_target; long unsigned int latency_target_conf; long unsigned int slice_start[2]; long unsigned int slice_end[2]; long unsigned int last_finish_time; long unsigned int checked_last_finish_time; long unsigned int avg_idletime; long unsigned int idletime_threshold; long unsigned int idletime_threshold_conf; unsigned int bio_cnt; unsigned int bad_bio_cnt; long unsigned int bio_cnt_reset_time; struct blkg_rwstat stat_bytes; struct blkg_rwstat stat_ios; }; enum tg_state_flags { THROTL_TG_PENDING = 1, THROTL_TG_WAS_EMPTY = 2, }; enum { LIMIT_LOW = 0, LIMIT_MAX = 1, LIMIT_CNT = 2, }; enum prio_policy { POLICY_NO_CHANGE = 0, POLICY_NONE_TO_RT = 1, POLICY_RESTRICT_TO_BE = 2, POLICY_ALL_TO_IDLE = 3, }; struct ioprio_blkg { struct blkg_policy_data pd; }; struct ioprio_blkcg { struct blkcg_policy_data cpd; enum prio_policy prio_policy; }; struct blk_ioprio { struct rq_qos rqos; }; struct blk_iolatency { struct rq_qos rqos; struct timer_list timer; atomic_t enabled; }; struct iolatency_grp; struct child_latency_info { spinlock_t lock; u64 last_scale_event; u64 scale_lat; u64 nr_samples; struct iolatency_grp *scale_grp; atomic_t scale_cookie; }; struct percentile_stats { u64 total; u64 missed; }; struct latency_stat { union { struct percentile_stats ps; struct blk_rq_stat rqs; }; }; struct iolatency_grp { struct blkg_policy_data pd; struct latency_stat *stats; struct latency_stat cur_stat; struct blk_iolatency *blkiolat; struct rq_depth rq_depth; struct rq_wait rq_wait; atomic64_t window_start; atomic_t scale_cookie; u64 min_lat_nsec; u64 cur_win_nsec; u64 lat_avg; u64 nr_samples; bool ssd; struct child_latency_info child_lat; }; enum { MILLION = 1000000, MIN_PERIOD = 1000, MAX_PERIOD = 1000000, MARGIN_MIN_PCT = 10, MARGIN_LOW_PCT = 20, MARGIN_TARGET_PCT = 50, INUSE_ADJ_STEP_PCT = 25, TIMER_SLACK_PCT = 1, WEIGHT_ONE = 65536, VTIME_PER_SEC_SHIFT = 37, VTIME_PER_SEC = 0, VTIME_PER_USEC = 137438, VTIME_PER_NSEC = 137, VRATE_MIN_PPM = 10000, VRATE_MAX_PPM = 100000000, VRATE_MIN = 1374, VRATE_CLAMP_ADJ_PCT = 4, RQ_WAIT_BUSY_PCT = 5, UNBUSY_THR_PCT = 75, MIN_DELAY_THR_PCT = 500, MAX_DELAY_THR_PCT = 25000, MIN_DELAY = 250, MAX_DELAY = 250000, DFGV_USAGE_PCT = 50, DFGV_PERIOD = 100000, MAX_LAGGING_PERIODS = 10, AUTOP_CYCLE_NSEC = 1410065408, IOC_PAGE_SHIFT = 12, IOC_PAGE_SIZE = 4096, IOC_SECT_TO_PAGE_SHIFT = 3, LCOEF_RANDIO_PAGES = 4096, }; enum ioc_running { IOC_IDLE = 0, IOC_RUNNING = 1, IOC_STOP = 2, }; enum { QOS_ENABLE = 0, QOS_CTRL = 1, NR_QOS_CTRL_PARAMS = 2, }; enum { QOS_RPPM = 0, QOS_RLAT = 1, QOS_WPPM = 2, QOS_WLAT = 3, QOS_MIN = 4, QOS_MAX = 5, NR_QOS_PARAMS = 6, }; enum { COST_CTRL = 0, COST_MODEL = 1, NR_COST_CTRL_PARAMS = 2, }; enum { I_LCOEF_RBPS = 0, I_LCOEF_RSEQIOPS = 1, I_LCOEF_RRANDIOPS = 2, I_LCOEF_WBPS = 3, I_LCOEF_WSEQIOPS = 4, I_LCOEF_WRANDIOPS = 5, NR_I_LCOEFS = 6, }; enum { LCOEF_RPAGE = 0, LCOEF_RSEQIO = 1, LCOEF_RRANDIO = 2, LCOEF_WPAGE = 3, LCOEF_WSEQIO = 4, LCOEF_WRANDIO = 5, NR_LCOEFS = 6, }; enum { AUTOP_INVALID = 0, AUTOP_HDD = 1, AUTOP_SSD_QD1 = 2, AUTOP_SSD_DFL = 3, AUTOP_SSD_FAST = 4, }; struct ioc_params { u32 qos[6]; u64 i_lcoefs[6]; u64 lcoefs[6]; u32 too_fast_vrate_pct; u32 too_slow_vrate_pct; }; struct ioc_margins { s64 min; s64 low; s64 target; }; struct ioc_missed { local_t nr_met; local_t nr_missed; u32 last_met; u32 last_missed; }; struct ioc_pcpu_stat { struct ioc_missed missed[2]; local64_t rq_wait_ns; u64 last_rq_wait_ns; }; struct ioc { struct rq_qos rqos; bool enabled; struct ioc_params params; struct ioc_margins margins; u32 period_us; u32 timer_slack_ns; u64 vrate_min; u64 vrate_max; spinlock_t lock; struct timer_list timer; struct list_head active_iocgs; struct ioc_pcpu_stat *pcpu_stat; enum ioc_running running; atomic64_t vtime_rate; u64 vtime_base_rate; s64 vtime_err; seqcount_spinlock_t period_seqcount; u64 period_at; u64 period_at_vtime; atomic64_t cur_period; int busy_level; bool weights_updated; atomic_t hweight_gen; u64 dfgv_period_at; u64 dfgv_period_rem; u64 dfgv_usage_us_sum; u64 autop_too_fast_at; u64 autop_too_slow_at; int autop_idx; bool user_qos_params: 1; bool user_cost_model: 1; }; struct iocg_pcpu_stat { local64_t abs_vusage; }; struct iocg_stat { u64 usage_us; u64 wait_us; u64 indebt_us; u64 indelay_us; }; struct ioc_gq { struct blkg_policy_data pd; struct ioc *ioc; u32 cfg_weight; u32 weight; u32 active; u32 inuse; u32 last_inuse; s64 saved_margin; sector_t cursor; atomic64_t vtime; atomic64_t done_vtime; u64 abs_vdebt; u64 delay; u64 delay_at; atomic64_t active_period; struct list_head active_list; u64 child_active_sum; u64 child_inuse_sum; u64 child_adjusted_sum; int hweight_gen; u32 hweight_active; u32 hweight_inuse; u32 hweight_donating; u32 hweight_after_donation; struct list_head walk_list; struct list_head surplus_list; struct wait_queue_head waitq; struct hrtimer waitq_timer; u64 activated_at; struct iocg_pcpu_stat *pcpu_stat; struct iocg_stat local_stat; struct iocg_stat desc_stat; struct iocg_stat last_stat; u64 last_stat_abs_vusage; u64 usage_delta_us; u64 wait_since; u64 indebt_since; u64 indelay_since; int level; struct ioc_gq *ancestors[0]; }; struct ioc_cgrp { struct blkcg_policy_data cpd; unsigned int dfl_weight; }; struct ioc_now { u64 now_ns; u64 now; u64 vnow; u64 vrate; }; struct iocg_wait { struct wait_queue_entry wait; struct bio *bio; u64 abs_cost; bool committed; }; struct iocg_wake_ctx { struct ioc_gq *iocg; u32 hw_inuse; s64 vbudget; }; struct trace_event_raw_iocost_iocg_state { struct trace_entry ent; u32 __data_loc_devname; u32 __data_loc_cgroup; u64 now; u64 vnow; u64 vrate; u64 last_period; u64 cur_period; u64 vtime; u32 weight; u32 inuse; u64 hweight_active; u64 hweight_inuse; char __data[0]; }; struct trace_event_raw_iocg_inuse_update { struct trace_entry ent; u32 __data_loc_devname; u32 __data_loc_cgroup; u64 now; u32 old_inuse; u32 new_inuse; u64 old_hweight_inuse; u64 new_hweight_inuse; char __data[0]; }; struct trace_event_raw_iocost_ioc_vrate_adj { struct trace_entry ent; u32 __data_loc_devname; u64 old_vrate; u64 new_vrate; int busy_level; u32 read_missed_ppm; u32 write_missed_ppm; u32 rq_wait_pct; int nr_lagging; int nr_shortages; char __data[0]; }; struct trace_event_raw_iocost_iocg_forgive_debt { struct trace_entry ent; u32 __data_loc_devname; u32 __data_loc_cgroup; u64 now; u64 vnow; u32 usage_pct; u64 old_debt; u64 new_debt; u64 old_delay; u64 new_delay; char __data[0]; }; struct trace_event_data_offsets_iocost_iocg_state { u32 devname; u32 cgroup; }; struct trace_event_data_offsets_iocg_inuse_update { u32 devname; u32 cgroup; }; struct trace_event_data_offsets_iocost_ioc_vrate_adj { u32 devname; }; struct trace_event_data_offsets_iocost_iocg_forgive_debt { u32 devname; u32 cgroup; }; typedef void (*btf_trace_iocost_iocg_activate)(void *, struct ioc_gq *, const char *, struct ioc_now *, u64, u64, u64); typedef void (*btf_trace_iocost_iocg_idle)(void *, struct ioc_gq *, const char *, struct ioc_now *, u64, u64, u64); typedef void (*btf_trace_iocost_inuse_shortage)(void *, struct ioc_gq *, const char *, struct ioc_now *, u32, u32, u64, u64); typedef void (*btf_trace_iocost_inuse_transfer)(void *, struct ioc_gq *, const char *, struct ioc_now *, u32, u32, u64, u64); typedef void (*btf_trace_iocost_inuse_adjust)(void *, struct ioc_gq *, const char *, struct ioc_now *, u32, u32, u64, u64); typedef void (*btf_trace_iocost_ioc_vrate_adj)(void *, struct ioc *, u64, u32 *, u32, int, int); typedef void (*btf_trace_iocost_iocg_forgive_debt)(void *, struct ioc_gq *, const char *, struct ioc_now *, u32, u64, u64, u64, u64); enum dd_data_dir { DD_READ = 0, DD_WRITE = 1, }; enum { DD_DIR_COUNT = 2, }; enum dd_prio { DD_RT_PRIO = 0, DD_BE_PRIO = 1, DD_IDLE_PRIO = 2, DD_PRIO_MAX = 2, }; enum { DD_PRIO_COUNT = 3, }; struct io_stats_per_prio { local_t inserted; local_t merged; local_t dispatched; local_t completed; }; struct io_stats { struct io_stats_per_prio stats[3]; }; struct dd_per_prio { struct list_head dispatch; struct rb_root sort_list[2]; struct list_head fifo_list[2]; struct request *next_rq[2]; }; struct deadline_data { struct dd_per_prio per_prio[3]; enum dd_data_dir last_dir; unsigned int batching; unsigned int starved; struct io_stats *stats; int fifo_expire[2]; int fifo_batch; int writes_starved; int front_merges; u32 async_depth; spinlock_t lock; spinlock_t zone_lock; }; struct trace_event_raw_kyber_latency { struct trace_entry ent; dev_t dev; char domain[16]; char type[8]; u8 percentile; u8 numerator; u8 denominator; unsigned int samples; char __data[0]; }; struct trace_event_raw_kyber_adjust { struct trace_entry ent; dev_t dev; char domain[16]; unsigned int depth; char __data[0]; }; struct trace_event_raw_kyber_throttled { struct trace_entry ent; dev_t dev; char domain[16]; char __data[0]; }; struct trace_event_data_offsets_kyber_latency {}; struct trace_event_data_offsets_kyber_adjust {}; struct trace_event_data_offsets_kyber_throttled {}; typedef void (*btf_trace_kyber_latency)(void *, struct request_queue *, const char *, const char *, unsigned int, unsigned int, unsigned int, unsigned int); typedef void (*btf_trace_kyber_adjust)(void *, struct request_queue *, const char *, unsigned int); typedef void (*btf_trace_kyber_throttled)(void *, struct request_queue *, const char *); enum { KYBER_READ = 0, KYBER_WRITE = 1, KYBER_DISCARD = 2, KYBER_OTHER = 3, KYBER_NUM_DOMAINS = 4, }; enum { KYBER_ASYNC_PERCENT = 75, }; enum { KYBER_LATENCY_SHIFT = 2, KYBER_GOOD_BUCKETS = 4, KYBER_LATENCY_BUCKETS = 8, }; enum { KYBER_TOTAL_LATENCY = 0, KYBER_IO_LATENCY = 1, }; struct kyber_cpu_latency { atomic_t buckets[48]; }; struct kyber_ctx_queue { spinlock_t lock; struct list_head rq_list[4]; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct kyber_queue_data { struct request_queue *q; struct sbitmap_queue domain_tokens[4]; unsigned int async_depth; struct kyber_cpu_latency *cpu_latency; struct timer_list timer; unsigned int latency_buckets[48]; long unsigned int latency_timeout[3]; int domain_p99[3]; u64 latency_targets[3]; }; struct kyber_hctx_data { spinlock_t lock; struct list_head rqs[4]; unsigned int cur_domain; unsigned int batching; struct kyber_ctx_queue *kcqs; struct sbitmap kcq_map[4]; struct sbq_wait domain_wait[4]; struct sbq_wait_state *domain_ws[4]; atomic_t wait_index[4]; }; struct flush_kcq_data { struct kyber_hctx_data *khd; unsigned int sched_domain; struct list_head *list; }; struct bfq_entity; struct bfq_service_tree { struct rb_root active; struct rb_root idle; struct bfq_entity *first_idle; struct bfq_entity *last_idle; u64 vtime; long unsigned int wsum; }; struct bfq_sched_data; struct bfq_queue; struct bfq_entity { struct rb_node rb_node; bool on_st_or_in_serv; u64 start; u64 finish; struct rb_root *tree; u64 min_start; int service; int budget; int dev_weight; int weight; int new_weight; int orig_weight; struct bfq_entity *parent; struct bfq_sched_data *my_sched_data; struct bfq_sched_data *sched_data; int prio_changed; bool in_groups_with_pending_reqs; struct bfq_queue *last_bfqq_created; }; struct bfq_sched_data { struct bfq_entity *in_service_entity; struct bfq_entity *next_in_service; struct bfq_service_tree service_tree[3]; long unsigned int bfq_class_idle_last_service; }; struct bfq_weight_counter { unsigned int weight; unsigned int num_active; struct rb_node weights_node; }; struct bfq_ttime { u64 last_end_request; u64 ttime_total; long unsigned int ttime_samples; u64 ttime_mean; }; struct bfq_data; struct bfq_io_cq; struct bfq_queue { int ref; int stable_ref; struct bfq_data *bfqd; short unsigned int ioprio; short unsigned int ioprio_class; short unsigned int new_ioprio; short unsigned int new_ioprio_class; u64 last_serv_time_ns; unsigned int inject_limit; long unsigned int decrease_time_jif; struct bfq_queue *new_bfqq; struct rb_node pos_node; struct rb_root *pos_root; struct rb_root sort_list; struct request *next_rq; int queued[2]; int allocated; int meta_pending; struct list_head fifo; struct bfq_entity entity; struct bfq_weight_counter *weight_counter; int max_budget; long unsigned int budget_timeout; int dispatched; long unsigned int flags; struct list_head bfqq_list; struct bfq_ttime ttime; u64 io_start_time; u64 tot_idle_time; u32 seek_history; struct hlist_node burst_list_node; sector_t last_request_pos; unsigned int requests_within_timer; pid_t pid; struct bfq_io_cq *bic; long unsigned int wr_cur_max_time; long unsigned int soft_rt_next_start; long unsigned int last_wr_start_finish; unsigned int wr_coeff; long unsigned int last_idle_bklogged; long unsigned int service_from_backlogged; long unsigned int service_from_wr; long unsigned int wr_start_at_switch_to_srt; long unsigned int split_time; long unsigned int first_IO_time; long unsigned int creation_time; u32 max_service_rate; struct bfq_queue *waker_bfqq; struct bfq_queue *tentative_waker_bfqq; unsigned int num_waker_detections; struct hlist_node woken_list_node; struct hlist_head woken_list; }; struct bfq_group; struct bfq_data { struct request_queue *queue; struct list_head dispatch; struct bfq_group *root_group; struct rb_root_cached queue_weights_tree; unsigned int num_groups_with_pending_reqs; unsigned int busy_queues[3]; int wr_busy_queues; int queued; int rq_in_driver; bool nonrot_with_queueing; int max_rq_in_driver; int hw_tag_samples; int hw_tag; int budgets_assigned; struct hrtimer idle_slice_timer; struct bfq_queue *in_service_queue; sector_t last_position; sector_t in_serv_last_pos; u64 last_completion; struct bfq_queue *last_completed_rq_bfqq; struct bfq_queue *last_bfqq_created; u64 last_empty_occupied_ns; bool wait_dispatch; struct request *waited_rq; bool rqs_injected; u64 first_dispatch; u64 last_dispatch; ktime_t last_budget_start; ktime_t last_idling_start; long unsigned int last_idling_start_jiffies; int peak_rate_samples; u32 sequential_samples; u64 tot_sectors_dispatched; u32 last_rq_max_size; u64 delta_from_first; u32 peak_rate; int bfq_max_budget; struct list_head active_list; struct list_head idle_list; u64 bfq_fifo_expire[2]; unsigned int bfq_back_penalty; unsigned int bfq_back_max; u32 bfq_slice_idle; int bfq_user_max_budget; unsigned int bfq_timeout; bool strict_guarantees; long unsigned int last_ins_in_burst; long unsigned int bfq_burst_interval; int burst_size; struct bfq_entity *burst_parent_entity; long unsigned int bfq_large_burst_thresh; bool large_burst; struct hlist_head burst_list; bool low_latency; unsigned int bfq_wr_coeff; unsigned int bfq_wr_max_time; unsigned int bfq_wr_rt_max_time; unsigned int bfq_wr_min_idle_time; long unsigned int bfq_wr_min_inter_arr_async; unsigned int bfq_wr_max_softrt_rate; u64 rate_dur_prod; struct bfq_queue oom_bfqq; spinlock_t lock; struct bfq_io_cq *bio_bic; struct bfq_queue *bio_bfqq; unsigned int word_depths[4]; }; struct bfq_io_cq { struct io_cq icq; struct bfq_queue *bfqq[2]; int ioprio; uint64_t blkcg_serial_nr; bool saved_has_short_ttime; bool saved_IO_bound; u64 saved_io_start_time; u64 saved_tot_idle_time; bool saved_in_large_burst; bool was_in_burst_list; unsigned int saved_weight; long unsigned int saved_wr_coeff; long unsigned int saved_last_wr_start_finish; long unsigned int saved_service_from_wr; long unsigned int saved_wr_start_at_switch_to_srt; unsigned int saved_wr_cur_max_time; struct bfq_ttime saved_ttime; u64 saved_last_serv_time_ns; unsigned int saved_inject_limit; long unsigned int saved_decrease_time_jif; struct bfq_queue *stable_merge_bfqq; bool stably_merged; }; struct bfqg_stats { struct blkg_rwstat bytes; struct blkg_rwstat ios; }; struct bfq_group { struct blkg_policy_data pd; char blkg_path[128]; int ref; struct bfq_entity entity; struct bfq_sched_data sched_data; void *bfqd; struct bfq_queue *async_bfqq[16]; struct bfq_queue *async_idle_bfqq; struct bfq_entity *my_entity; int active_entities; struct rb_root rq_pos_tree; struct bfqg_stats stats; }; enum bfqq_state_flags { BFQQF_just_created = 0, BFQQF_busy = 1, BFQQF_wait_request = 2, BFQQF_non_blocking_wait_rq = 3, BFQQF_fifo_expire = 4, BFQQF_has_short_ttime = 5, BFQQF_sync = 6, BFQQF_IO_bound = 7, BFQQF_in_large_burst = 8, BFQQF_softrt_update = 9, BFQQF_coop = 10, BFQQF_split_coop = 11, }; enum bfqq_expiration { BFQQE_TOO_IDLE = 0, BFQQE_BUDGET_TIMEOUT = 1, BFQQE_BUDGET_EXHAUSTED = 2, BFQQE_NO_MORE_REQUESTS = 3, BFQQE_PREEMPTED = 4, }; struct bfq_group_data { struct blkcg_policy_data pd; unsigned int weight; }; enum bip_flags { BIP_BLOCK_INTEGRITY = 1, BIP_MAPPED_INTEGRITY = 2, BIP_CTRL_NOCHECK = 4, BIP_DISK_NOCHECK = 8, BIP_IP_CHECKSUM = 16, }; enum blk_integrity_flags { BLK_INTEGRITY_VERIFY = 1, BLK_INTEGRITY_GENERATE = 2, BLK_INTEGRITY_DEVICE_CAPABLE = 4, BLK_INTEGRITY_IP_CHECKSUM = 8, }; struct integrity_sysfs_entry { struct attribute attr; ssize_t (*show)(struct blk_integrity *, char *); ssize_t (*store)(struct blk_integrity *, const char *, size_t); }; enum t10_dif_type { T10_PI_TYPE0_PROTECTION = 0, T10_PI_TYPE1_PROTECTION = 1, T10_PI_TYPE2_PROTECTION = 2, T10_PI_TYPE3_PROTECTION = 3, }; struct t10_pi_tuple { __be16 guard_tag; __be16 app_tag; __be32 ref_tag; }; typedef __be16 csum_fn(void *, unsigned int); struct virtio_device_id { __u32 device; __u32 vendor; }; struct virtio_device; struct virtqueue { struct list_head list; void (*callback)(struct virtqueue *); const char *name; struct virtio_device *vdev; unsigned int index; unsigned int num_free; void *priv; }; struct vringh_config_ops; struct virtio_config_ops; struct virtio_device { int index; bool failed; bool config_enabled; bool config_change_pending; spinlock_t config_lock; spinlock_t vqs_list_lock; struct device dev; struct virtio_device_id id; const struct virtio_config_ops *config; const struct vringh_config_ops *vringh_config; struct list_head vqs; u64 features; void *priv; }; typedef void vq_callback_t(struct virtqueue *); struct irq_affinity___2; struct virtio_shm_region; struct virtio_config_ops { void (*get)(struct virtio_device *, unsigned int, void *, unsigned int); void (*set)(struct virtio_device *, unsigned int, const void *, unsigned int); u32 (*generation)(struct virtio_device *); u8 (*get_status)(struct virtio_device *); void (*set_status)(struct virtio_device *, u8); void (*reset)(struct virtio_device *); int (*find_vqs)(struct virtio_device *, unsigned int, struct virtqueue **, vq_callback_t **, const char * const *, const bool *, struct irq_affinity___2 *); void (*del_vqs)(struct virtio_device *); u64 (*get_features)(struct virtio_device *); int (*finalize_features)(struct virtio_device *); const char * (*bus_name)(struct virtio_device *); int (*set_vq_affinity)(struct virtqueue *, const struct cpumask *); const struct cpumask * (*get_vq_affinity)(struct virtio_device *, int); bool (*get_shm_region)(struct virtio_device *, struct virtio_shm_region *, u8); }; struct virtio_shm_region { u64 addr; u64 len; }; struct irq_poll; typedef int irq_poll_fn(struct irq_poll *, int); struct irq_poll { struct list_head list; long unsigned int state; int weight; irq_poll_fn *poll; }; struct dim_sample { ktime_t time; u32 pkt_ctr; u32 byte_ctr; u16 event_ctr; u32 comp_ctr; }; struct dim_stats { int ppms; int bpms; int epms; int cpms; int cpe_ratio; }; struct dim { u8 state; struct dim_stats prev_stats; struct dim_sample start_sample; struct dim_sample measuring_sample; struct work_struct work; void *priv; u8 profile_ix; u8 mode; u8 tune_state; u8 steps_right; u8 steps_left; u8 tired; }; enum rdma_nl_counter_mode { RDMA_COUNTER_MODE_NONE = 0, RDMA_COUNTER_MODE_AUTO = 1, RDMA_COUNTER_MODE_MANUAL = 2, RDMA_COUNTER_MODE_MAX = 3, }; enum rdma_nl_counter_mask { RDMA_COUNTER_MASK_QP_TYPE = 1, RDMA_COUNTER_MASK_PID = 2, }; enum rdma_restrack_type { RDMA_RESTRACK_PD = 0, RDMA_RESTRACK_CQ = 1, RDMA_RESTRACK_QP = 2, RDMA_RESTRACK_CM_ID = 3, RDMA_RESTRACK_MR = 4, RDMA_RESTRACK_CTX = 5, RDMA_RESTRACK_COUNTER = 6, RDMA_RESTRACK_SRQ = 7, RDMA_RESTRACK_MAX = 8, }; struct rdma_restrack_entry { bool valid; u8 no_track: 1; struct kref kref; struct completion comp; struct task_struct *task; const char *kern_name; enum rdma_restrack_type type; bool user; u32 id; }; struct rdma_link_ops { struct list_head list; const char *type; int (*newlink)(const char *, struct net_device *); }; struct auto_mode_param { int qp_type; }; struct rdma_counter_mode { enum rdma_nl_counter_mode mode; enum rdma_nl_counter_mask mask; struct auto_mode_param param; }; struct rdma_hw_stats; struct rdma_port_counter { struct rdma_counter_mode mode; struct rdma_hw_stats *hstats; unsigned int num_counters; struct mutex lock; }; struct rdma_hw_stats { struct mutex lock; long unsigned int timestamp; long unsigned int lifespan; const char * const *names; int num_counters; u64 value[0]; }; struct ib_device; struct rdma_counter { struct rdma_restrack_entry res; struct ib_device *device; uint32_t id; struct kref kref; struct rdma_counter_mode mode; struct mutex lock; struct rdma_hw_stats *stats; u32 port; }; enum rdma_driver_id { RDMA_DRIVER_UNKNOWN = 0, RDMA_DRIVER_MLX5 = 1, RDMA_DRIVER_MLX4 = 2, RDMA_DRIVER_CXGB3 = 3, RDMA_DRIVER_CXGB4 = 4, RDMA_DRIVER_MTHCA = 5, RDMA_DRIVER_BNXT_RE = 6, RDMA_DRIVER_OCRDMA = 7, RDMA_DRIVER_NES = 8, RDMA_DRIVER_I40IW = 9, RDMA_DRIVER_IRDMA = 9, RDMA_DRIVER_VMW_PVRDMA = 10, RDMA_DRIVER_QEDR = 11, RDMA_DRIVER_HNS = 12, RDMA_DRIVER_USNIC = 13, RDMA_DRIVER_RXE = 14, RDMA_DRIVER_HFI1 = 15, RDMA_DRIVER_QIB = 16, RDMA_DRIVER_EFA = 17, RDMA_DRIVER_SIW = 18, }; enum ib_cq_notify_flags { IB_CQ_SOLICITED = 1, IB_CQ_NEXT_COMP = 2, IB_CQ_SOLICITED_MASK = 3, IB_CQ_REPORT_MISSED_EVENTS = 4, }; struct ib_mad; enum rdma_link_layer { IB_LINK_LAYER_UNSPECIFIED = 0, IB_LINK_LAYER_INFINIBAND = 1, IB_LINK_LAYER_ETHERNET = 2, }; enum rdma_netdev_t { RDMA_NETDEV_OPA_VNIC = 0, RDMA_NETDEV_IPOIB = 1, }; enum ib_srq_attr_mask { IB_SRQ_MAX_WR = 1, IB_SRQ_LIMIT = 2, }; enum ib_mr_type { IB_MR_TYPE_MEM_REG = 0, IB_MR_TYPE_SG_GAPS = 1, IB_MR_TYPE_DM = 2, IB_MR_TYPE_USER = 3, IB_MR_TYPE_DMA = 4, IB_MR_TYPE_INTEGRITY = 5, }; enum ib_uverbs_advise_mr_advice { IB_UVERBS_ADVISE_MR_ADVICE_PREFETCH = 0, IB_UVERBS_ADVISE_MR_ADVICE_PREFETCH_WRITE = 1, IB_UVERBS_ADVISE_MR_ADVICE_PREFETCH_NO_FAULT = 2, }; struct uverbs_attr_bundle; struct rdma_cm_id; struct iw_cm_id; struct iw_cm_conn_param; struct ib_qp; struct ib_send_wr; struct ib_recv_wr; struct ib_cq; struct ib_wc; struct ib_srq; struct ib_grh; struct ib_device_attr; struct ib_udata; struct ib_device_modify; struct ib_port_attr; struct ib_port_modify; struct ib_port_immutable; struct rdma_netdev_alloc_params; union ib_gid; struct ib_gid_attr; struct ib_ucontext; struct rdma_user_mmap_entry; struct ib_pd; struct ib_ah; struct rdma_ah_init_attr; struct rdma_ah_attr; struct ib_srq_init_attr; struct ib_srq_attr; struct ib_qp_init_attr; struct ib_qp_attr; struct ib_cq_init_attr; struct ib_mr; struct ib_sge; struct ib_mr_status; struct ib_mw; struct ib_xrcd; struct ib_flow; struct ib_flow_attr; struct ib_flow_action; struct ib_flow_action_attrs_esp; struct ib_wq; struct ib_wq_init_attr; struct ib_wq_attr; struct ib_rwq_ind_table; struct ib_rwq_ind_table_init_attr; struct ib_dm; struct ib_dm_alloc_attr; struct ib_dm_mr_attr; struct ib_counters; struct ib_counters_read_attr; struct ib_device_ops { struct module *owner; enum rdma_driver_id driver_id; u32 uverbs_abi_ver; unsigned int uverbs_no_driver_id_binding: 1; const struct attribute_group *device_group; const struct attribute_group **port_groups; int (*post_send)(struct ib_qp *, const struct ib_send_wr *, const struct ib_send_wr **); int (*post_recv)(struct ib_qp *, const struct ib_recv_wr *, const struct ib_recv_wr **); void (*drain_rq)(struct ib_qp *); void (*drain_sq)(struct ib_qp *); int (*poll_cq)(struct ib_cq *, int, struct ib_wc *); int (*peek_cq)(struct ib_cq *, int); int (*req_notify_cq)(struct ib_cq *, enum ib_cq_notify_flags); int (*post_srq_recv)(struct ib_srq *, const struct ib_recv_wr *, const struct ib_recv_wr **); int (*process_mad)(struct ib_device *, int, u32, const struct ib_wc *, const struct ib_grh *, const struct ib_mad *, struct ib_mad *, size_t *, u16 *); int (*query_device)(struct ib_device *, struct ib_device_attr *, struct ib_udata *); int (*modify_device)(struct ib_device *, int, struct ib_device_modify *); void (*get_dev_fw_str)(struct ib_device *, char *); const struct cpumask * (*get_vector_affinity)(struct ib_device *, int); int (*query_port)(struct ib_device *, u32, struct ib_port_attr *); int (*modify_port)(struct ib_device *, u32, int, struct ib_port_modify *); int (*get_port_immutable)(struct ib_device *, u32, struct ib_port_immutable *); enum rdma_link_layer (*get_link_layer)(struct ib_device *, u32); struct net_device * (*get_netdev)(struct ib_device *, u32); struct net_device * (*alloc_rdma_netdev)(struct ib_device *, u32, enum rdma_netdev_t, const char *, unsigned char, void (*)(struct net_device *)); int (*rdma_netdev_get_params)(struct ib_device *, u32, enum rdma_netdev_t, struct rdma_netdev_alloc_params *); int (*query_gid)(struct ib_device *, u32, int, union ib_gid *); int (*add_gid)(const struct ib_gid_attr *, void **); int (*del_gid)(const struct ib_gid_attr *, void **); int (*query_pkey)(struct ib_device *, u32, u16, u16 *); int (*alloc_ucontext)(struct ib_ucontext *, struct ib_udata *); void (*dealloc_ucontext)(struct ib_ucontext *); int (*mmap)(struct ib_ucontext *, struct vm_area_struct *); void (*mmap_free)(struct rdma_user_mmap_entry *); void (*disassociate_ucontext)(struct ib_ucontext *); int (*alloc_pd)(struct ib_pd *, struct ib_udata *); int (*dealloc_pd)(struct ib_pd *, struct ib_udata *); int (*create_ah)(struct ib_ah *, struct rdma_ah_init_attr *, struct ib_udata *); int (*create_user_ah)(struct ib_ah *, struct rdma_ah_init_attr *, struct ib_udata *); int (*modify_ah)(struct ib_ah *, struct rdma_ah_attr *); int (*query_ah)(struct ib_ah *, struct rdma_ah_attr *); int (*destroy_ah)(struct ib_ah *, u32); int (*create_srq)(struct ib_srq *, struct ib_srq_init_attr *, struct ib_udata *); int (*modify_srq)(struct ib_srq *, struct ib_srq_attr *, enum ib_srq_attr_mask, struct ib_udata *); int (*query_srq)(struct ib_srq *, struct ib_srq_attr *); int (*destroy_srq)(struct ib_srq *, struct ib_udata *); struct ib_qp * (*create_qp)(struct ib_pd *, struct ib_qp_init_attr *, struct ib_udata *); int (*modify_qp)(struct ib_qp *, struct ib_qp_attr *, int, struct ib_udata *); int (*query_qp)(struct ib_qp *, struct ib_qp_attr *, int, struct ib_qp_init_attr *); int (*destroy_qp)(struct ib_qp *, struct ib_udata *); int (*create_cq)(struct ib_cq *, const struct ib_cq_init_attr *, struct ib_udata *); int (*modify_cq)(struct ib_cq *, u16, u16); int (*destroy_cq)(struct ib_cq *, struct ib_udata *); int (*resize_cq)(struct ib_cq *, int, struct ib_udata *); struct ib_mr * (*get_dma_mr)(struct ib_pd *, int); struct ib_mr * (*reg_user_mr)(struct ib_pd *, u64, u64, u64, int, struct ib_udata *); struct ib_mr * (*reg_user_mr_dmabuf)(struct ib_pd *, u64, u64, u64, int, int, struct ib_udata *); struct ib_mr * (*rereg_user_mr)(struct ib_mr *, int, u64, u64, u64, int, struct ib_pd *, struct ib_udata *); int (*dereg_mr)(struct ib_mr *, struct ib_udata *); struct ib_mr * (*alloc_mr)(struct ib_pd *, enum ib_mr_type, u32); struct ib_mr * (*alloc_mr_integrity)(struct ib_pd *, u32, u32); int (*advise_mr)(struct ib_pd *, enum ib_uverbs_advise_mr_advice, u32, struct ib_sge *, u32, struct uverbs_attr_bundle *); int (*map_mr_sg)(struct ib_mr *, struct scatterlist *, int, unsigned int *); int (*check_mr_status)(struct ib_mr *, u32, struct ib_mr_status *); int (*alloc_mw)(struct ib_mw *, struct ib_udata *); int (*dealloc_mw)(struct ib_mw *); int (*attach_mcast)(struct ib_qp *, union ib_gid *, u16); int (*detach_mcast)(struct ib_qp *, union ib_gid *, u16); int (*alloc_xrcd)(struct ib_xrcd *, struct ib_udata *); int (*dealloc_xrcd)(struct ib_xrcd *, struct ib_udata *); struct ib_flow * (*create_flow)(struct ib_qp *, struct ib_flow_attr *, struct ib_udata *); int (*destroy_flow)(struct ib_flow *); struct ib_flow_action * (*create_flow_action_esp)(struct ib_device *, const struct ib_flow_action_attrs_esp *, struct uverbs_attr_bundle *); int (*destroy_flow_action)(struct ib_flow_action *); int (*modify_flow_action_esp)(struct ib_flow_action *, const struct ib_flow_action_attrs_esp *, struct uverbs_attr_bundle *); int (*set_vf_link_state)(struct ib_device *, int, u32, int); int (*get_vf_config)(struct ib_device *, int, u32, struct ifla_vf_info *); int (*get_vf_stats)(struct ib_device *, int, u32, struct ifla_vf_stats *); int (*get_vf_guid)(struct ib_device *, int, u32, struct ifla_vf_guid *, struct ifla_vf_guid *); int (*set_vf_guid)(struct ib_device *, int, u32, u64, int); struct ib_wq * (*create_wq)(struct ib_pd *, struct ib_wq_init_attr *, struct ib_udata *); int (*destroy_wq)(struct ib_wq *, struct ib_udata *); int (*modify_wq)(struct ib_wq *, struct ib_wq_attr *, u32, struct ib_udata *); int (*create_rwq_ind_table)(struct ib_rwq_ind_table *, struct ib_rwq_ind_table_init_attr *, struct ib_udata *); int (*destroy_rwq_ind_table)(struct ib_rwq_ind_table *); struct ib_dm * (*alloc_dm)(struct ib_device *, struct ib_ucontext *, struct ib_dm_alloc_attr *, struct uverbs_attr_bundle *); int (*dealloc_dm)(struct ib_dm *, struct uverbs_attr_bundle *); struct ib_mr * (*reg_dm_mr)(struct ib_pd *, struct ib_dm *, struct ib_dm_mr_attr *, struct uverbs_attr_bundle *); int (*create_counters)(struct ib_counters *, struct uverbs_attr_bundle *); int (*destroy_counters)(struct ib_counters *); int (*read_counters)(struct ib_counters *, struct ib_counters_read_attr *, struct uverbs_attr_bundle *); int (*map_mr_sg_pi)(struct ib_mr *, struct scatterlist *, int, unsigned int *, struct scatterlist *, int, unsigned int *); struct rdma_hw_stats * (*alloc_hw_device_stats)(struct ib_device *); struct rdma_hw_stats * (*alloc_hw_port_stats)(struct ib_device *, u32); int (*get_hw_stats)(struct ib_device *, struct rdma_hw_stats *, u32, int); int (*fill_res_mr_entry)(struct sk_buff *, struct ib_mr *); int (*fill_res_mr_entry_raw)(struct sk_buff *, struct ib_mr *); int (*fill_res_cq_entry)(struct sk_buff *, struct ib_cq *); int (*fill_res_cq_entry_raw)(struct sk_buff *, struct ib_cq *); int (*fill_res_qp_entry)(struct sk_buff *, struct ib_qp *); int (*fill_res_qp_entry_raw)(struct sk_buff *, struct ib_qp *); int (*fill_res_cm_id_entry)(struct sk_buff *, struct rdma_cm_id *); int (*enable_driver)(struct ib_device *); void (*dealloc_driver)(struct ib_device *); void (*iw_add_ref)(struct ib_qp *); void (*iw_rem_ref)(struct ib_qp *); struct ib_qp * (*iw_get_qp)(struct ib_device *, int); int (*iw_connect)(struct iw_cm_id *, struct iw_cm_conn_param *); int (*iw_accept)(struct iw_cm_id *, struct iw_cm_conn_param *); int (*iw_reject)(struct iw_cm_id *, const void *, u8); int (*iw_create_listen)(struct iw_cm_id *, int); int (*iw_destroy_listen)(struct iw_cm_id *); int (*counter_bind_qp)(struct rdma_counter *, struct ib_qp *); int (*counter_unbind_qp)(struct ib_qp *); int (*counter_dealloc)(struct rdma_counter *); struct rdma_hw_stats * (*counter_alloc_stats)(struct rdma_counter *); int (*counter_update_stats)(struct rdma_counter *); int (*fill_stat_mr_entry)(struct sk_buff *, struct ib_mr *); int (*query_ucontext)(struct ib_ucontext *, struct uverbs_attr_bundle *); size_t size_ib_ah; size_t size_ib_counters; size_t size_ib_cq; size_t size_ib_mw; size_t size_ib_pd; size_t size_ib_rwq_ind_table; size_t size_ib_srq; size_t size_ib_ucontext; size_t size_ib_xrcd; }; struct ib_core_device { struct device dev; possible_net_t rdma_net; struct kobject *ports_kobj; struct list_head port_list; struct ib_device *owner; }; enum ib_atomic_cap { IB_ATOMIC_NONE = 0, IB_ATOMIC_HCA = 1, IB_ATOMIC_GLOB = 2, }; struct ib_odp_caps { uint64_t general_caps; struct { uint32_t rc_odp_caps; uint32_t uc_odp_caps; uint32_t ud_odp_caps; uint32_t xrc_odp_caps; } per_transport_caps; }; struct ib_rss_caps { u32 supported_qpts; u32 max_rwq_indirection_tables; u32 max_rwq_indirection_table_size; }; struct ib_tm_caps { u32 max_rndv_hdr_size; u32 max_num_tags; u32 flags; u32 max_ops; u32 max_sge; }; struct ib_cq_caps { u16 max_cq_moderation_count; u16 max_cq_moderation_period; }; struct ib_device_attr { u64 fw_ver; __be64 sys_image_guid; u64 max_mr_size; u64 page_size_cap; u32 vendor_id; u32 vendor_part_id; u32 hw_ver; int max_qp; int max_qp_wr; u64 device_cap_flags; int max_send_sge; int max_recv_sge; int max_sge_rd; int max_cq; int max_cqe; int max_mr; int max_pd; int max_qp_rd_atom; int max_ee_rd_atom; int max_res_rd_atom; int max_qp_init_rd_atom; int max_ee_init_rd_atom; enum ib_atomic_cap atomic_cap; enum ib_atomic_cap masked_atomic_cap; int max_ee; int max_rdd; int max_mw; int max_raw_ipv6_qp; int max_raw_ethy_qp; int max_mcast_grp; int max_mcast_qp_attach; int max_total_mcast_qp_attach; int max_ah; int max_srq; int max_srq_wr; int max_srq_sge; unsigned int max_fast_reg_page_list_len; unsigned int max_pi_fast_reg_page_list_len; u16 max_pkeys; u8 local_ca_ack_delay; int sig_prot_cap; int sig_guard_cap; struct ib_odp_caps odp_caps; uint64_t timestamp_mask; uint64_t hca_core_clock; struct ib_rss_caps rss_caps; u32 max_wq_type_rq; u32 raw_packet_caps; struct ib_tm_caps tm_caps; struct ib_cq_caps cq_caps; u64 max_dm_size; u32 max_sgl_rd; }; struct hw_stats_device_data; struct rdma_restrack_root; struct uapi_definition; struct ib_port_data; struct ib_device { struct device *dma_device; struct ib_device_ops ops; char name[64]; struct callback_head callback_head; struct list_head event_handler_list; struct rw_semaphore event_handler_rwsem; spinlock_t qp_open_list_lock; struct rw_semaphore client_data_rwsem; struct xarray client_data; struct mutex unregistration_lock; rwlock_t cache_lock; struct ib_port_data *port_data; int num_comp_vectors; union { struct device dev; struct ib_core_device coredev; }; const struct attribute_group *groups[4]; u64 uverbs_cmd_mask; char node_desc[64]; __be64 node_guid; u32 local_dma_lkey; u16 is_switch: 1; u16 kverbs_provider: 1; u16 use_cq_dim: 1; u8 node_type; u32 phys_port_cnt; struct ib_device_attr attrs; struct hw_stats_device_data *hw_stats_data; struct rdmacg_device cg_device; u32 index; spinlock_t cq_pools_lock; struct list_head cq_pools[3]; struct rdma_restrack_root *res; const struct uapi_definition *driver_def; refcount_t refcount; struct completion unreg_completion; struct work_struct unregistration_work; const struct rdma_link_ops *link_ops; struct mutex compat_devs_mutex; struct xarray compat_devs; char iw_ifname[16]; u32 iw_driver_flags; u32 lag_flags; }; enum ib_signature_type { IB_SIG_TYPE_NONE = 0, IB_SIG_TYPE_T10_DIF = 1, }; enum ib_t10_dif_bg_type { IB_T10DIF_CRC = 0, IB_T10DIF_CSUM = 1, }; struct ib_t10_dif_domain { enum ib_t10_dif_bg_type bg_type; u16 pi_interval; u16 bg; u16 app_tag; u32 ref_tag; bool ref_remap; bool app_escape; bool ref_escape; u16 apptag_check_mask; }; struct ib_sig_domain { enum ib_signature_type sig_type; union { struct ib_t10_dif_domain dif; } sig; }; struct ib_sig_attrs { u8 check_mask; struct ib_sig_domain mem; struct ib_sig_domain wire; int meta_length; }; enum ib_sig_err_type { IB_SIG_BAD_GUARD = 0, IB_SIG_BAD_REFTAG = 1, IB_SIG_BAD_APPTAG = 2, }; struct ib_sig_err { enum ib_sig_err_type err_type; u32 expected; u32 actual; u64 sig_err_offset; u32 key; }; enum ib_uverbs_flow_action_esp_keymat { IB_UVERBS_FLOW_ACTION_ESP_KEYMAT_AES_GCM = 0, }; struct ib_uverbs_flow_action_esp_keymat_aes_gcm { __u64 iv; __u32 iv_algo; __u32 salt; __u32 icv_len; __u32 key_len; __u32 aes_key[8]; }; enum ib_uverbs_flow_action_esp_replay { IB_UVERBS_FLOW_ACTION_ESP_REPLAY_NONE = 0, IB_UVERBS_FLOW_ACTION_ESP_REPLAY_BMP = 1, }; struct ib_uverbs_flow_action_esp_replay_bmp { __u32 size; }; union ib_gid { u8 raw[16]; struct { __be64 subnet_prefix; __be64 interface_id; } global; }; enum ib_gid_type { IB_GID_TYPE_IB = 0, IB_GID_TYPE_ROCE = 1, IB_GID_TYPE_ROCE_UDP_ENCAP = 2, IB_GID_TYPE_SIZE = 3, }; struct ib_gid_attr { struct net_device *ndev; struct ib_device *device; union ib_gid gid; enum ib_gid_type gid_type; u16 index; u32 port_num; }; struct ib_cq_init_attr { unsigned int cqe; u32 comp_vector; u32 flags; }; struct ib_dm_mr_attr { u64 length; u64 offset; u32 access_flags; }; struct ib_dm_alloc_attr { u64 length; u32 alignment; u32 flags; }; enum ib_mtu { IB_MTU_256 = 1, IB_MTU_512 = 2, IB_MTU_1024 = 3, IB_MTU_2048 = 4, IB_MTU_4096 = 5, }; enum ib_port_state { IB_PORT_NOP = 0, IB_PORT_DOWN = 1, IB_PORT_INIT = 2, IB_PORT_ARMED = 3, IB_PORT_ACTIVE = 4, IB_PORT_ACTIVE_DEFER = 5, }; struct ib_port_attr { u64 subnet_prefix; enum ib_port_state state; enum ib_mtu max_mtu; enum ib_mtu active_mtu; u32 phys_mtu; int gid_tbl_len; unsigned int ip_gids: 1; u32 port_cap_flags; u32 max_msg_sz; u32 bad_pkey_cntr; u32 qkey_viol_cntr; u16 pkey_tbl_len; u32 sm_lid; u32 lid; u8 lmc; u8 max_vl_num; u8 sm_sl; u8 subnet_timeout; u8 init_type_reply; u8 active_width; u16 active_speed; u8 phys_state; u16 port_cap_flags2; }; struct ib_device_modify { u64 sys_image_guid; char node_desc[64]; }; struct ib_port_modify { u32 set_port_cap_mask; u32 clr_port_cap_mask; u8 init_type; }; enum ib_event_type { IB_EVENT_CQ_ERR = 0, IB_EVENT_QP_FATAL = 1, IB_EVENT_QP_REQ_ERR = 2, IB_EVENT_QP_ACCESS_ERR = 3, IB_EVENT_COMM_EST = 4, IB_EVENT_SQ_DRAINED = 5, IB_EVENT_PATH_MIG = 6, IB_EVENT_PATH_MIG_ERR = 7, IB_EVENT_DEVICE_FATAL = 8, IB_EVENT_PORT_ACTIVE = 9, IB_EVENT_PORT_ERR = 10, IB_EVENT_LID_CHANGE = 11, IB_EVENT_PKEY_CHANGE = 12, IB_EVENT_SM_CHANGE = 13, IB_EVENT_SRQ_ERR = 14, IB_EVENT_SRQ_LIMIT_REACHED = 15, IB_EVENT_QP_LAST_WQE_REACHED = 16, IB_EVENT_CLIENT_REREGISTER = 17, IB_EVENT_GID_CHANGE = 18, IB_EVENT_WQ_FATAL = 19, }; struct ib_ucq_object; typedef void (*ib_comp_handler)(struct ib_cq *, void *); struct ib_event; struct ib_cq { struct ib_device *device; struct ib_ucq_object *uobject; ib_comp_handler comp_handler; void (*event_handler)(struct ib_event *, void *); void *cq_context; int cqe; unsigned int cqe_used; atomic_t usecnt; enum ib_poll_context poll_ctx; struct ib_wc *wc; struct list_head pool_entry; union { struct irq_poll iop; struct work_struct work; }; struct workqueue_struct *comp_wq; struct dim *dim; ktime_t timestamp; u8 interrupt: 1; u8 shared: 1; unsigned int comp_vector; struct rdma_restrack_entry res; }; struct ib_uqp_object; enum ib_qp_type { IB_QPT_SMI = 0, IB_QPT_GSI = 1, IB_QPT_RC = 2, IB_QPT_UC = 3, IB_QPT_UD = 4, IB_QPT_RAW_IPV6 = 5, IB_QPT_RAW_ETHERTYPE = 6, IB_QPT_RAW_PACKET = 8, IB_QPT_XRC_INI = 9, IB_QPT_XRC_TGT = 10, IB_QPT_MAX = 11, IB_QPT_DRIVER = 255, IB_QPT_RESERVED1 = 4096, IB_QPT_RESERVED2 = 4097, IB_QPT_RESERVED3 = 4098, IB_QPT_RESERVED4 = 4099, IB_QPT_RESERVED5 = 4100, IB_QPT_RESERVED6 = 4101, IB_QPT_RESERVED7 = 4102, IB_QPT_RESERVED8 = 4103, IB_QPT_RESERVED9 = 4104, IB_QPT_RESERVED10 = 4105, }; struct ib_qp_security; struct ib_qp { struct ib_device *device; struct ib_pd *pd; struct ib_cq *send_cq; struct ib_cq *recv_cq; spinlock_t mr_lock; int mrs_used; struct list_head rdma_mrs; struct list_head sig_mrs; struct ib_srq *srq; struct ib_xrcd *xrcd; struct list_head xrcd_list; atomic_t usecnt; struct list_head open_list; struct ib_qp *real_qp; struct ib_uqp_object *uobject; void (*event_handler)(struct ib_event *, void *); void *qp_context; const struct ib_gid_attr *av_sgid_attr; const struct ib_gid_attr *alt_path_sgid_attr; u32 qp_num; u32 max_write_sge; u32 max_read_sge; enum ib_qp_type qp_type; struct ib_rwq_ind_table *rwq_ind_tbl; struct ib_qp_security *qp_sec; u32 port; bool integrity_en; struct rdma_restrack_entry res; struct rdma_counter *counter; }; struct ib_usrq_object; enum ib_srq_type { IB_SRQT_BASIC = 0, IB_SRQT_XRC = 1, IB_SRQT_TM = 2, }; struct ib_srq { struct ib_device *device; struct ib_pd *pd; struct ib_usrq_object *uobject; void (*event_handler)(struct ib_event *, void *); void *srq_context; enum ib_srq_type srq_type; atomic_t usecnt; struct { struct ib_cq *cq; union { struct { struct ib_xrcd *xrcd; u32 srq_num; } xrc; }; } ext; struct rdma_restrack_entry res; }; struct ib_uwq_object; enum ib_wq_state { IB_WQS_RESET = 0, IB_WQS_RDY = 1, IB_WQS_ERR = 2, }; enum ib_wq_type { IB_WQT_RQ = 0, }; struct ib_wq { struct ib_device *device; struct ib_uwq_object *uobject; void *wq_context; void (*event_handler)(struct ib_event *, void *); struct ib_pd *pd; struct ib_cq *cq; u32 wq_num; enum ib_wq_state state; enum ib_wq_type wq_type; atomic_t usecnt; }; struct ib_event { struct ib_device *device; union { struct ib_cq *cq; struct ib_qp *qp; struct ib_srq *srq; struct ib_wq *wq; u32 port_num; } element; enum ib_event_type event; }; struct ib_global_route { const struct ib_gid_attr *sgid_attr; union ib_gid dgid; u32 flow_label; u8 sgid_index; u8 hop_limit; u8 traffic_class; }; struct ib_grh { __be32 version_tclass_flow; __be16 paylen; u8 next_hdr; u8 hop_limit; union ib_gid sgid; union ib_gid dgid; }; struct ib_mr_status { u32 fail_status; struct ib_sig_err sig_err; }; struct rdma_ah_init_attr { struct rdma_ah_attr *ah_attr; u32 flags; struct net_device *xmit_slave; }; enum rdma_ah_attr_type { RDMA_AH_ATTR_TYPE_UNDEFINED = 0, RDMA_AH_ATTR_TYPE_IB = 1, RDMA_AH_ATTR_TYPE_ROCE = 2, RDMA_AH_ATTR_TYPE_OPA = 3, }; struct ib_ah_attr { u16 dlid; u8 src_path_bits; }; struct roce_ah_attr { u8 dmac[6]; }; struct opa_ah_attr { u32 dlid; u8 src_path_bits; bool make_grd; }; struct rdma_ah_attr { struct ib_global_route grh; u8 sl; u8 static_rate; u32 port_num; u8 ah_flags; enum rdma_ah_attr_type type; union { struct ib_ah_attr ib; struct roce_ah_attr roce; struct opa_ah_attr opa; }; }; enum ib_wc_status { IB_WC_SUCCESS = 0, IB_WC_LOC_LEN_ERR = 1, IB_WC_LOC_QP_OP_ERR = 2, IB_WC_LOC_EEC_OP_ERR = 3, IB_WC_LOC_PROT_ERR = 4, IB_WC_WR_FLUSH_ERR = 5, IB_WC_MW_BIND_ERR = 6, IB_WC_BAD_RESP_ERR = 7, IB_WC_LOC_ACCESS_ERR = 8, IB_WC_REM_INV_REQ_ERR = 9, IB_WC_REM_ACCESS_ERR = 10, IB_WC_REM_OP_ERR = 11, IB_WC_RETRY_EXC_ERR = 12, IB_WC_RNR_RETRY_EXC_ERR = 13, IB_WC_LOC_RDD_VIOL_ERR = 14, IB_WC_REM_INV_RD_REQ_ERR = 15, IB_WC_REM_ABORT_ERR = 16, IB_WC_INV_EECN_ERR = 17, IB_WC_INV_EEC_STATE_ERR = 18, IB_WC_FATAL_ERR = 19, IB_WC_RESP_TIMEOUT_ERR = 20, IB_WC_GENERAL_ERR = 21, }; enum ib_wc_opcode { IB_WC_SEND = 0, IB_WC_RDMA_WRITE = 1, IB_WC_RDMA_READ = 2, IB_WC_COMP_SWAP = 3, IB_WC_FETCH_ADD = 4, IB_WC_BIND_MW = 5, IB_WC_LOCAL_INV = 6, IB_WC_LSO = 7, IB_WC_REG_MR = 8, IB_WC_MASKED_COMP_SWAP = 9, IB_WC_MASKED_FETCH_ADD = 10, IB_WC_RECV = 128, IB_WC_RECV_RDMA_WITH_IMM = 129, }; struct ib_cqe { void (*done)(struct ib_cq *, struct ib_wc *); }; struct ib_wc { union { u64 wr_id; struct ib_cqe *wr_cqe; }; enum ib_wc_status status; enum ib_wc_opcode opcode; u32 vendor_err; u32 byte_len; struct ib_qp *qp; union { __be32 imm_data; u32 invalidate_rkey; } ex; u32 src_qp; u32 slid; int wc_flags; u16 pkey_index; u8 sl; u8 dlid_path_bits; u32 port_num; u8 smac[6]; u16 vlan_id; u8 network_hdr_type; }; struct ib_srq_attr { u32 max_wr; u32 max_sge; u32 srq_limit; }; struct ib_xrcd { struct ib_device *device; atomic_t usecnt; struct inode *inode; struct rw_semaphore tgt_qps_rwsem; struct xarray tgt_qps; }; struct ib_srq_init_attr { void (*event_handler)(struct ib_event *, void *); void *srq_context; struct ib_srq_attr attr; enum ib_srq_type srq_type; struct { struct ib_cq *cq; union { struct { struct ib_xrcd *xrcd; } xrc; struct { u32 max_num_tags; } tag_matching; }; } ext; }; struct ib_qp_cap { u32 max_send_wr; u32 max_recv_wr; u32 max_send_sge; u32 max_recv_sge; u32 max_inline_data; u32 max_rdma_ctxs; }; enum ib_sig_type { IB_SIGNAL_ALL_WR = 0, IB_SIGNAL_REQ_WR = 1, }; struct ib_qp_init_attr { void (*event_handler)(struct ib_event *, void *); void *qp_context; struct ib_cq *send_cq; struct ib_cq *recv_cq; struct ib_srq *srq; struct ib_xrcd *xrcd; struct ib_qp_cap cap; enum ib_sig_type sq_sig_type; enum ib_qp_type qp_type; u32 create_flags; u32 port_num; struct ib_rwq_ind_table *rwq_ind_tbl; u32 source_qpn; }; struct ib_uobject; struct ib_rwq_ind_table { struct ib_device *device; struct ib_uobject *uobject; atomic_t usecnt; u32 ind_tbl_num; u32 log_ind_tbl_size; struct ib_wq **ind_tbl; }; enum ib_qp_state { IB_QPS_RESET = 0, IB_QPS_INIT = 1, IB_QPS_RTR = 2, IB_QPS_RTS = 3, IB_QPS_SQD = 4, IB_QPS_SQE = 5, IB_QPS_ERR = 6, }; enum ib_mig_state { IB_MIG_MIGRATED = 0, IB_MIG_REARM = 1, IB_MIG_ARMED = 2, }; enum ib_mw_type { IB_MW_TYPE_1 = 1, IB_MW_TYPE_2 = 2, }; struct ib_qp_attr { enum ib_qp_state qp_state; enum ib_qp_state cur_qp_state; enum ib_mtu path_mtu; enum ib_mig_state path_mig_state; u32 qkey; u32 rq_psn; u32 sq_psn; u32 dest_qp_num; int qp_access_flags; struct ib_qp_cap cap; struct rdma_ah_attr ah_attr; struct rdma_ah_attr alt_ah_attr; u16 pkey_index; u16 alt_pkey_index; u8 en_sqd_async_notify; u8 sq_draining; u8 max_rd_atomic; u8 max_dest_rd_atomic; u8 min_rnr_timer; u32 port_num; u8 timeout; u8 retry_cnt; u8 rnr_retry; u32 alt_port_num; u8 alt_timeout; u32 rate_limit; struct net_device *xmit_slave; }; enum ib_wr_opcode { IB_WR_RDMA_WRITE = 0, IB_WR_RDMA_WRITE_WITH_IMM = 1, IB_WR_SEND = 2, IB_WR_SEND_WITH_IMM = 3, IB_WR_RDMA_READ = 4, IB_WR_ATOMIC_CMP_AND_SWP = 5, IB_WR_ATOMIC_FETCH_AND_ADD = 6, IB_WR_BIND_MW = 8, IB_WR_LSO = 10, IB_WR_SEND_WITH_INV = 9, IB_WR_RDMA_READ_WITH_INV = 11, IB_WR_LOCAL_INV = 7, IB_WR_MASKED_ATOMIC_CMP_AND_SWP = 12, IB_WR_MASKED_ATOMIC_FETCH_AND_ADD = 13, IB_WR_REG_MR = 32, IB_WR_REG_MR_INTEGRITY = 33, IB_WR_RESERVED1 = 240, IB_WR_RESERVED2 = 241, IB_WR_RESERVED3 = 242, IB_WR_RESERVED4 = 243, IB_WR_RESERVED5 = 244, IB_WR_RESERVED6 = 245, IB_WR_RESERVED7 = 246, IB_WR_RESERVED8 = 247, IB_WR_RESERVED9 = 248, IB_WR_RESERVED10 = 249, }; struct ib_sge { u64 addr; u32 length; u32 lkey; }; struct ib_send_wr { struct ib_send_wr *next; union { u64 wr_id; struct ib_cqe *wr_cqe; }; struct ib_sge *sg_list; int num_sge; enum ib_wr_opcode opcode; int send_flags; union { __be32 imm_data; u32 invalidate_rkey; } ex; }; struct ib_ah { struct ib_device *device; struct ib_pd *pd; struct ib_uobject *uobject; const struct ib_gid_attr *sgid_attr; enum rdma_ah_attr_type type; }; struct ib_mr { struct ib_device *device; struct ib_pd *pd; u32 lkey; u32 rkey; u64 iova; u64 length; unsigned int page_size; enum ib_mr_type type; bool need_inval; union { struct ib_uobject *uobject; struct list_head qp_entry; }; struct ib_dm *dm; struct ib_sig_attrs *sig_attrs; struct rdma_restrack_entry res; }; struct ib_recv_wr { struct ib_recv_wr *next; union { u64 wr_id; struct ib_cqe *wr_cqe; }; struct ib_sge *sg_list; int num_sge; }; struct ib_rdmacg_object { struct rdma_cgroup *cg; }; struct ib_uverbs_file; struct ib_ucontext { struct ib_device *device; struct ib_uverbs_file *ufile; struct ib_rdmacg_object cg_obj; struct rdma_restrack_entry res; struct xarray mmap_xa; }; struct uverbs_api_object; struct ib_uobject { u64 user_handle; struct ib_uverbs_file *ufile; struct ib_ucontext *context; void *object; struct list_head list; struct ib_rdmacg_object cg_obj; int id; struct kref ref; atomic_t usecnt; struct callback_head rcu; const struct uverbs_api_object *uapi_object; }; struct ib_udata { const void *inbuf; void *outbuf; size_t inlen; size_t outlen; }; struct ib_pd { u32 local_dma_lkey; u32 flags; struct ib_device *device; struct ib_uobject *uobject; atomic_t usecnt; u32 unsafe_global_rkey; struct ib_mr *__internal_mr; struct rdma_restrack_entry res; }; struct ib_wq_init_attr { void *wq_context; enum ib_wq_type wq_type; u32 max_wr; u32 max_sge; struct ib_cq *cq; void (*event_handler)(struct ib_event *, void *); u32 create_flags; }; struct ib_wq_attr { enum ib_wq_state wq_state; enum ib_wq_state curr_wq_state; u32 flags; u32 flags_mask; }; struct ib_rwq_ind_table_init_attr { u32 log_ind_tbl_size; struct ib_wq **ind_tbl; }; enum port_pkey_state { IB_PORT_PKEY_NOT_VALID = 0, IB_PORT_PKEY_VALID = 1, IB_PORT_PKEY_LISTED = 2, }; struct ib_port_pkey { enum port_pkey_state state; u16 pkey_index; u32 port_num; struct list_head qp_list; struct list_head to_error_list; struct ib_qp_security *sec; }; struct ib_ports_pkeys; struct ib_qp_security { struct ib_qp *qp; struct ib_device *dev; struct mutex mutex; struct ib_ports_pkeys *ports_pkeys; struct list_head shared_qp_list; void *security; bool destroying; atomic_t error_list_count; struct completion error_complete; int error_comps_pending; }; struct ib_ports_pkeys { struct ib_port_pkey main; struct ib_port_pkey alt; }; struct ib_dm { struct ib_device *device; u32 length; u32 flags; struct ib_uobject *uobject; atomic_t usecnt; }; struct ib_mw { struct ib_device *device; struct ib_pd *pd; struct ib_uobject *uobject; u32 rkey; enum ib_mw_type type; }; enum ib_flow_attr_type { IB_FLOW_ATTR_NORMAL = 0, IB_FLOW_ATTR_ALL_DEFAULT = 1, IB_FLOW_ATTR_MC_DEFAULT = 2, IB_FLOW_ATTR_SNIFFER = 3, }; enum ib_flow_spec_type { IB_FLOW_SPEC_ETH = 32, IB_FLOW_SPEC_IB = 34, IB_FLOW_SPEC_IPV4 = 48, IB_FLOW_SPEC_IPV6 = 49, IB_FLOW_SPEC_ESP = 52, IB_FLOW_SPEC_TCP = 64, IB_FLOW_SPEC_UDP = 65, IB_FLOW_SPEC_VXLAN_TUNNEL = 80, IB_FLOW_SPEC_GRE = 81, IB_FLOW_SPEC_MPLS = 96, IB_FLOW_SPEC_INNER = 256, IB_FLOW_SPEC_ACTION_TAG = 4096, IB_FLOW_SPEC_ACTION_DROP = 4097, IB_FLOW_SPEC_ACTION_HANDLE = 4098, IB_FLOW_SPEC_ACTION_COUNT = 4099, }; struct ib_flow_eth_filter { u8 dst_mac[6]; u8 src_mac[6]; __be16 ether_type; __be16 vlan_tag; u8 real_sz[0]; }; struct ib_flow_spec_eth { u32 type; u16 size; struct ib_flow_eth_filter val; struct ib_flow_eth_filter mask; }; struct ib_flow_ib_filter { __be16 dlid; __u8 sl; u8 real_sz[0]; }; struct ib_flow_spec_ib { u32 type; u16 size; struct ib_flow_ib_filter val; struct ib_flow_ib_filter mask; }; struct ib_flow_ipv4_filter { __be32 src_ip; __be32 dst_ip; u8 proto; u8 tos; u8 ttl; u8 flags; u8 real_sz[0]; }; struct ib_flow_spec_ipv4 { u32 type; u16 size; struct ib_flow_ipv4_filter val; struct ib_flow_ipv4_filter mask; }; struct ib_flow_ipv6_filter { u8 src_ip[16]; u8 dst_ip[16]; __be32 flow_label; u8 next_hdr; u8 traffic_class; u8 hop_limit; u8 real_sz[0]; }; struct ib_flow_spec_ipv6 { u32 type; u16 size; struct ib_flow_ipv6_filter val; struct ib_flow_ipv6_filter mask; }; struct ib_flow_tcp_udp_filter { __be16 dst_port; __be16 src_port; u8 real_sz[0]; }; struct ib_flow_spec_tcp_udp { u32 type; u16 size; struct ib_flow_tcp_udp_filter val; struct ib_flow_tcp_udp_filter mask; }; struct ib_flow_tunnel_filter { __be32 tunnel_id; u8 real_sz[0]; }; struct ib_flow_spec_tunnel { u32 type; u16 size; struct ib_flow_tunnel_filter val; struct ib_flow_tunnel_filter mask; }; struct ib_flow_esp_filter { __be32 spi; __be32 seq; u8 real_sz[0]; }; struct ib_flow_spec_esp { u32 type; u16 size; struct ib_flow_esp_filter val; struct ib_flow_esp_filter mask; }; struct ib_flow_gre_filter { __be16 c_ks_res0_ver; __be16 protocol; __be32 key; u8 real_sz[0]; }; struct ib_flow_spec_gre { u32 type; u16 size; struct ib_flow_gre_filter val; struct ib_flow_gre_filter mask; }; struct ib_flow_mpls_filter { __be32 tag; u8 real_sz[0]; }; struct ib_flow_spec_mpls { u32 type; u16 size; struct ib_flow_mpls_filter val; struct ib_flow_mpls_filter mask; }; struct ib_flow_spec_action_tag { enum ib_flow_spec_type type; u16 size; u32 tag_id; }; struct ib_flow_spec_action_drop { enum ib_flow_spec_type type; u16 size; }; struct ib_flow_spec_action_handle { enum ib_flow_spec_type type; u16 size; struct ib_flow_action *act; }; enum ib_flow_action_type { IB_FLOW_ACTION_UNSPECIFIED = 0, IB_FLOW_ACTION_ESP = 1, }; struct ib_flow_action { struct ib_device *device; struct ib_uobject *uobject; enum ib_flow_action_type type; atomic_t usecnt; }; struct ib_flow_spec_action_count { enum ib_flow_spec_type type; u16 size; struct ib_counters *counters; }; struct ib_counters { struct ib_device *device; struct ib_uobject *uobject; atomic_t usecnt; }; union ib_flow_spec { struct { u32 type; u16 size; }; struct ib_flow_spec_eth eth; struct ib_flow_spec_ib ib; struct ib_flow_spec_ipv4 ipv4; struct ib_flow_spec_tcp_udp tcp_udp; struct ib_flow_spec_ipv6 ipv6; struct ib_flow_spec_tunnel tunnel; struct ib_flow_spec_esp esp; struct ib_flow_spec_gre gre; struct ib_flow_spec_mpls mpls; struct ib_flow_spec_action_tag flow_tag; struct ib_flow_spec_action_drop drop; struct ib_flow_spec_action_handle action; struct ib_flow_spec_action_count flow_count; }; struct ib_flow_attr { enum ib_flow_attr_type type; u16 size; u16 priority; u32 flags; u8 num_of_specs; u32 port; union ib_flow_spec flows[0]; }; struct ib_flow { struct ib_qp *qp; struct ib_device *device; struct ib_uobject *uobject; }; struct ib_flow_action_attrs_esp_keymats { enum ib_uverbs_flow_action_esp_keymat protocol; union { struct ib_uverbs_flow_action_esp_keymat_aes_gcm aes_gcm; } keymat; }; struct ib_flow_action_attrs_esp_replays { enum ib_uverbs_flow_action_esp_replay protocol; union { struct ib_uverbs_flow_action_esp_replay_bmp bmp; } replay; }; struct ib_flow_spec_list { struct ib_flow_spec_list *next; union ib_flow_spec spec; }; struct ib_flow_action_attrs_esp { struct ib_flow_action_attrs_esp_keymats *keymat; struct ib_flow_action_attrs_esp_replays *replay; struct ib_flow_spec_list *encap; u32 esn; u32 spi; u32 seq; u32 tfc_pad; u64 flags; u64 hard_limit_pkts; }; struct ib_pkey_cache; struct ib_gid_table; struct ib_port_cache { u64 subnet_prefix; struct ib_pkey_cache *pkey; struct ib_gid_table *gid; u8 lmc; enum ib_port_state port_state; }; struct ib_port_immutable { int pkey_tbl_len; int gid_tbl_len; u32 core_cap_flags; u32 max_mad_size; }; struct ib_port; struct ib_port_data { struct ib_device *ib_dev; struct ib_port_immutable immutable; spinlock_t pkey_list_lock; spinlock_t netdev_lock; struct list_head pkey_list; struct ib_port_cache cache; struct net_device *netdev; struct hlist_node ndev_hash_link; struct rdma_port_counter port_counter; struct ib_port *sysfs; }; struct rdma_netdev_alloc_params { size_t sizeof_priv; unsigned int txqs; unsigned int rxqs; void *param; int (*initialize_rdma_netdev)(struct ib_device *, u32, struct net_device *, void *); }; struct ib_counters_read_attr { u64 *counters_buff; u32 ncounters; u32 flags; }; struct rdma_user_mmap_entry { struct kref ref; struct ib_ucontext *ucontext; long unsigned int start_pgoff; size_t npages; bool driver_removed; }; enum blk_zone_type { BLK_ZONE_TYPE_CONVENTIONAL = 1, BLK_ZONE_TYPE_SEQWRITE_REQ = 2, BLK_ZONE_TYPE_SEQWRITE_PREF = 3, }; enum blk_zone_cond { BLK_ZONE_COND_NOT_WP = 0, BLK_ZONE_COND_EMPTY = 1, BLK_ZONE_COND_IMP_OPEN = 2, BLK_ZONE_COND_EXP_OPEN = 3, BLK_ZONE_COND_CLOSED = 4, BLK_ZONE_COND_READONLY = 13, BLK_ZONE_COND_FULL = 14, BLK_ZONE_COND_OFFLINE = 15, }; enum blk_zone_report_flags { BLK_ZONE_REP_CAPACITY = 1, }; struct blk_zone_report { __u64 sector; __u32 nr_zones; __u32 flags; struct blk_zone zones[0]; }; struct blk_zone_range { __u64 sector; __u64 nr_sectors; }; struct zone_report_args { struct blk_zone *zones; }; struct blk_revalidate_zone_args { struct gendisk *disk; long unsigned int *conv_zones_bitmap; long unsigned int *seq_zones_wlock; unsigned int nr_zones; sector_t zone_sectors; sector_t sector; }; enum wbt_flags { WBT_TRACKED = 1, WBT_READ = 2, WBT_KSWAPD = 4, WBT_DISCARD = 8, WBT_NR_BITS = 4, }; enum { WBT_STATE_ON_DEFAULT = 1, WBT_STATE_ON_MANUAL = 2, WBT_STATE_OFF_DEFAULT = 3, }; struct rq_wb { unsigned int wb_background; unsigned int wb_normal; short int enable_state; unsigned int unknown_cnt; u64 win_nsec; u64 cur_win_nsec; struct blk_stat_callback *cb; u64 sync_issue; void *sync_cookie; unsigned int wc; long unsigned int last_issue; long unsigned int last_comp; long unsigned int min_lat_nsec; struct rq_qos rqos; struct rq_wait rq_wait[3]; struct rq_depth rq_depth; }; struct trace_event_raw_wbt_stat { struct trace_entry ent; char name[32]; s64 rmean; u64 rmin; u64 rmax; s64 rnr_samples; s64 rtime; s64 wmean; u64 wmin; u64 wmax; s64 wnr_samples; s64 wtime; char __data[0]; }; struct trace_event_raw_wbt_lat { struct trace_entry ent; char name[32]; long unsigned int lat; char __data[0]; }; struct trace_event_raw_wbt_step { struct trace_entry ent; char name[32]; const char *msg; int step; long unsigned int window; unsigned int bg; unsigned int normal; unsigned int max; char __data[0]; }; struct trace_event_raw_wbt_timer { struct trace_entry ent; char name[32]; unsigned int status; int step; unsigned int inflight; char __data[0]; }; struct trace_event_data_offsets_wbt_stat {}; struct trace_event_data_offsets_wbt_lat {}; struct trace_event_data_offsets_wbt_step {}; struct trace_event_data_offsets_wbt_timer {}; typedef void (*btf_trace_wbt_stat)(void *, struct backing_dev_info *, struct blk_rq_stat *); typedef void (*btf_trace_wbt_lat)(void *, struct backing_dev_info *, long unsigned int); typedef void (*btf_trace_wbt_step)(void *, struct backing_dev_info *, const char *, int, long unsigned int, unsigned int, unsigned int, unsigned int); typedef void (*btf_trace_wbt_timer)(void *, struct backing_dev_info *, unsigned int, int, unsigned int); enum { RWB_DEF_DEPTH = 16, RWB_WINDOW_NSEC = 100000000, RWB_MIN_WRITE_SAMPLES = 3, RWB_UNKNOWN_BUMP = 5, }; enum { LAT_OK = 1, LAT_UNKNOWN = 2, LAT_UNKNOWN_WRITES = 3, LAT_EXCEEDED = 4, }; struct wbt_wait_data { struct rq_wb *rwb; enum wbt_flags wb_acct; long unsigned int rw; }; struct show_busy_params { struct seq_file *m; struct blk_mq_hw_ctx *hctx; }; enum opal_mbr { OPAL_MBR_ENABLE = 0, OPAL_MBR_DISABLE = 1, }; enum opal_mbr_done_flag { OPAL_MBR_NOT_DONE = 0, OPAL_MBR_DONE = 1, }; enum opal_user { OPAL_ADMIN1 = 0, OPAL_USER1 = 1, OPAL_USER2 = 2, OPAL_USER3 = 3, OPAL_USER4 = 4, OPAL_USER5 = 5, OPAL_USER6 = 6, OPAL_USER7 = 7, OPAL_USER8 = 8, OPAL_USER9 = 9, }; enum opal_lock_state { OPAL_RO = 1, OPAL_RW = 2, OPAL_LK = 4, }; struct opal_key { __u8 lr; __u8 key_len; __u8 __align[6]; __u8 key[256]; }; struct opal_lr_act { struct opal_key key; __u32 sum; __u8 num_lrs; __u8 lr[9]; __u8 align[2]; }; struct opal_session_info { __u32 sum; __u32 who; struct opal_key opal_key; }; struct opal_user_lr_setup { __u64 range_start; __u64 range_length; __u32 RLE; __u32 WLE; struct opal_session_info session; }; struct opal_lock_unlock { struct opal_session_info session; __u32 l_state; __u8 __align[4]; }; struct opal_new_pw { struct opal_session_info session; struct opal_session_info new_user_pw; }; struct opal_mbr_data { struct opal_key key; __u8 enable_disable; __u8 __align[7]; }; struct opal_mbr_done { struct opal_key key; __u8 done_flag; __u8 __align[7]; }; struct opal_shadow_mbr { struct opal_key key; const __u64 data; __u64 offset; __u64 size; }; enum opal_table_ops { OPAL_READ_TABLE = 0, OPAL_WRITE_TABLE = 1, }; struct opal_read_write_table { struct opal_key key; const __u64 data; const __u8 table_uid[8]; __u64 offset; __u64 size; __u64 flags; __u64 priv; }; typedef int sec_send_recv(void *, u16, u8, void *, size_t, bool); enum { TCG_SECP_00 = 0, TCG_SECP_01 = 1, }; enum opal_response_token { OPAL_DTA_TOKENID_BYTESTRING = 224, OPAL_DTA_TOKENID_SINT = 225, OPAL_DTA_TOKENID_UINT = 226, OPAL_DTA_TOKENID_TOKEN = 227, OPAL_DTA_TOKENID_INVALID = 0, }; enum opal_uid { OPAL_SMUID_UID = 0, OPAL_THISSP_UID = 1, OPAL_ADMINSP_UID = 2, OPAL_LOCKINGSP_UID = 3, OPAL_ENTERPRISE_LOCKINGSP_UID = 4, OPAL_ANYBODY_UID = 5, OPAL_SID_UID = 6, OPAL_ADMIN1_UID = 7, OPAL_USER1_UID = 8, OPAL_USER2_UID = 9, OPAL_PSID_UID = 10, OPAL_ENTERPRISE_BANDMASTER0_UID = 11, OPAL_ENTERPRISE_ERASEMASTER_UID = 12, OPAL_TABLE_TABLE = 13, OPAL_LOCKINGRANGE_GLOBAL = 14, OPAL_LOCKINGRANGE_ACE_RDLOCKED = 15, OPAL_LOCKINGRANGE_ACE_WRLOCKED = 16, OPAL_MBRCONTROL = 17, OPAL_MBR = 18, OPAL_AUTHORITY_TABLE = 19, OPAL_C_PIN_TABLE = 20, OPAL_LOCKING_INFO_TABLE = 21, OPAL_ENTERPRISE_LOCKING_INFO_TABLE = 22, OPAL_DATASTORE = 23, OPAL_C_PIN_MSID = 24, OPAL_C_PIN_SID = 25, OPAL_C_PIN_ADMIN1 = 26, OPAL_HALF_UID_AUTHORITY_OBJ_REF = 27, OPAL_HALF_UID_BOOLEAN_ACE = 28, OPAL_UID_HEXFF = 29, }; enum opal_method { OPAL_PROPERTIES = 0, OPAL_STARTSESSION = 1, OPAL_REVERT = 2, OPAL_ACTIVATE = 3, OPAL_EGET = 4, OPAL_ESET = 5, OPAL_NEXT = 6, OPAL_EAUTHENTICATE = 7, OPAL_GETACL = 8, OPAL_GENKEY = 9, OPAL_REVERTSP = 10, OPAL_GET = 11, OPAL_SET = 12, OPAL_AUTHENTICATE = 13, OPAL_RANDOM = 14, OPAL_ERASE = 15, }; enum opal_token { OPAL_TRUE = 1, OPAL_FALSE = 0, OPAL_BOOLEAN_EXPR = 3, OPAL_TABLE = 0, OPAL_STARTROW = 1, OPAL_ENDROW = 2, OPAL_STARTCOLUMN = 3, OPAL_ENDCOLUMN = 4, OPAL_VALUES = 1, OPAL_TABLE_UID = 0, OPAL_TABLE_NAME = 1, OPAL_TABLE_COMMON = 2, OPAL_TABLE_TEMPLATE = 3, OPAL_TABLE_KIND = 4, OPAL_TABLE_COLUMN = 5, OPAL_TABLE_COLUMNS = 6, OPAL_TABLE_ROWS = 7, OPAL_TABLE_ROWS_FREE = 8, OPAL_TABLE_ROW_BYTES = 9, OPAL_TABLE_LASTID = 10, OPAL_TABLE_MIN = 11, OPAL_TABLE_MAX = 12, OPAL_PIN = 3, OPAL_RANGESTART = 3, OPAL_RANGELENGTH = 4, OPAL_READLOCKENABLED = 5, OPAL_WRITELOCKENABLED = 6, OPAL_READLOCKED = 7, OPAL_WRITELOCKED = 8, OPAL_ACTIVEKEY = 10, OPAL_LIFECYCLE = 6, OPAL_MAXRANGES = 4, OPAL_MBRENABLE = 1, OPAL_MBRDONE = 2, OPAL_HOSTPROPERTIES = 0, OPAL_STARTLIST = 240, OPAL_ENDLIST = 241, OPAL_STARTNAME = 242, OPAL_ENDNAME = 243, OPAL_CALL = 248, OPAL_ENDOFDATA = 249, OPAL_ENDOFSESSION = 250, OPAL_STARTTRANSACTON = 251, OPAL_ENDTRANSACTON = 252, OPAL_EMPTYATOM = 255, OPAL_WHERE = 0, }; enum opal_parameter { OPAL_SUM_SET_LIST = 393216, }; struct opal_compacket { __be32 reserved0; u8 extendedComID[4]; __be32 outstandingData; __be32 minTransfer; __be32 length; }; struct opal_packet { __be32 tsn; __be32 hsn; __be32 seq_number; __be16 reserved0; __be16 ack_type; __be32 acknowledgment; __be32 length; }; struct opal_data_subpacket { u8 reserved0[6]; __be16 kind; __be32 length; }; struct opal_header { struct opal_compacket cp; struct opal_packet pkt; struct opal_data_subpacket subpkt; }; struct d0_header { __be32 length; __be32 revision; __be32 reserved01; __be32 reserved02; u8 ignored[32]; }; struct d0_tper_features { u8 supported_features; u8 reserved01[3]; __be32 reserved02; __be32 reserved03; }; struct d0_locking_features { u8 supported_features; u8 reserved01[3]; __be32 reserved02; __be32 reserved03; }; struct d0_geometry_features { u8 header[4]; u8 reserved01; u8 reserved02[7]; __be32 logical_block_size; __be64 alignment_granularity; __be64 lowest_aligned_lba; }; struct d0_opal_v100 { __be16 baseComID; __be16 numComIDs; }; struct d0_single_user_mode { __be32 num_locking_objects; u8 reserved01; u8 reserved02; __be16 reserved03; __be32 reserved04; }; struct d0_opal_v200 { __be16 baseComID; __be16 numComIDs; u8 range_crossing; u8 num_locking_admin_auth[2]; u8 num_locking_user_auth[2]; u8 initialPIN; u8 revertedPIN; u8 reserved01; __be32 reserved02; }; struct d0_features { __be16 code; u8 r_version; u8 length; u8 features[0]; }; struct opal_dev; struct opal_step { int (*fn)(struct opal_dev *, void *); void *data; }; enum opal_atom_width { OPAL_WIDTH_TINY = 0, OPAL_WIDTH_SHORT = 1, OPAL_WIDTH_MEDIUM = 2, OPAL_WIDTH_LONG = 3, OPAL_WIDTH_TOKEN = 4, }; struct opal_resp_tok { const u8 *pos; size_t len; enum opal_response_token type; enum opal_atom_width width; union { u64 u; s64 s; } stored; }; struct parsed_resp { int num; struct opal_resp_tok toks[64]; }; struct opal_dev { bool supported; bool mbr_enabled; void *data; sec_send_recv *send_recv; struct mutex dev_lock; u16 comid; u32 hsn; u32 tsn; u64 align; u64 lowest_lba; size_t pos; u8 cmd[2048]; u8 resp[2048]; struct parsed_resp parsed; size_t prev_d_len; void *prev_data; struct list_head unlk_lst; }; typedef int cont_fn(struct opal_dev *); struct opal_suspend_data { struct opal_lock_unlock unlk; u8 lr; struct list_head node; }; struct blk_ksm_keyslot { atomic_t slot_refs; struct list_head idle_slot_node; struct hlist_node hash_node; const struct blk_crypto_key *key; struct blk_keyslot_manager *ksm; }; struct blk_ksm_ll_ops { int (*keyslot_program)(struct blk_keyslot_manager *, const struct blk_crypto_key *, unsigned int); int (*keyslot_evict)(struct blk_keyslot_manager *, const struct blk_crypto_key *, unsigned int); }; struct blk_keyslot_manager { struct blk_ksm_ll_ops ksm_ll_ops; unsigned int max_dun_bytes_supported; unsigned int crypto_modes_supported[4]; struct device *dev; unsigned int num_slots; struct rw_semaphore lock; wait_queue_head_t idle_slots_wait_queue; struct list_head idle_slots; spinlock_t idle_slots_lock; struct hlist_head *slot_hashtable; unsigned int log_slot_ht_size; struct blk_ksm_keyslot *slots; }; struct blk_crypto_mode { const char *cipher_str; unsigned int keysize; unsigned int ivsize; }; struct bio_fallback_crypt_ctx { struct bio_crypt_ctx crypt_ctx; struct bvec_iter crypt_iter; union { struct { struct work_struct work; struct bio *bio; }; struct { void *bi_private_orig; bio_end_io_t *bi_end_io_orig; }; }; }; struct blk_crypto_keyslot { enum blk_crypto_mode_num crypto_mode; struct crypto_skcipher *tfms[4]; }; union blk_crypto_iv { __le64 dun[4]; u8 bytes[32]; }; typedef int (*cmp_r_func_t)(const void *, const void *, const void *); struct random_ready_callback { struct list_head list; void (*func)(struct random_ready_callback *); struct module *owner; }; struct siprand_state { long unsigned int v0; long unsigned int v1; long unsigned int v2; long unsigned int v3; }; struct region { unsigned int start; unsigned int off; unsigned int group_len; unsigned int end; unsigned int nbits; }; enum { REG_OP_ISFREE = 0, REG_OP_ALLOC = 1, REG_OP_RELEASE = 2, }; typedef struct scatterlist *sg_alloc_fn(unsigned int, gfp_t); typedef void sg_free_fn(struct scatterlist *, unsigned int); struct sg_page_iter { struct scatterlist *sg; unsigned int sg_pgoffset; unsigned int __nents; int __pg_advance; }; struct sg_dma_page_iter { struct sg_page_iter base; }; struct sg_mapping_iter { struct page *page; void *addr; size_t length; size_t consumed; struct sg_page_iter piter; unsigned int __offset; unsigned int __remaining; unsigned int __flags; }; struct csum_state { __wsum csum; size_t off; }; struct rhltable { struct rhashtable ht; }; struct rhashtable_walker { struct list_head list; struct bucket_table *tbl; }; struct rhashtable_iter { struct rhashtable *ht; struct rhash_head *p; struct rhlist_head *list; struct rhashtable_walker walker; unsigned int slot; unsigned int skip; bool end_of_table; }; union nested_table { union nested_table *table; struct rhash_lock_head *bucket; }; struct once_work { struct work_struct work; struct static_key_true *key; struct module *module; }; struct genradix_iter { size_t offset; size_t pos; }; struct genradix_node { union { struct genradix_node *children[512]; u8 data[4096]; }; }; struct reciprocal_value_adv { u32 m; u8 sh; u8 exp; bool is_wide_m; }; enum devm_ioremap_type { DEVM_IOREMAP = 0, DEVM_IOREMAP_UC = 1, DEVM_IOREMAP_WC = 2, DEVM_IOREMAP_NP = 3, }; struct pcim_iomap_devres { void *table[6]; }; struct btree_head { long unsigned int *node; mempool_t *mempool; int height; }; struct btree_geo { int keylen; int no_pairs; int no_longs; }; typedef void (*visitor128_t)(void *, long unsigned int, u64, u64, size_t); typedef void (*visitorl_t)(void *, long unsigned int, long unsigned int, size_t); typedef void (*visitor32_t)(void *, long unsigned int, u32, size_t); typedef void (*visitor64_t)(void *, long unsigned int, u64, size_t); enum assoc_array_walk_status { assoc_array_walk_tree_empty = 0, assoc_array_walk_found_terminal_node = 1, assoc_array_walk_found_wrong_shortcut = 2, }; struct assoc_array_walk_result { struct { struct assoc_array_node *node; int level; int slot; } terminal_node; struct { struct assoc_array_shortcut *shortcut; int level; int sc_level; long unsigned int sc_segments; long unsigned int dissimilarity; } wrong_shortcut; }; struct assoc_array_delete_collapse_context { struct assoc_array_node *node; const void *skip_leaf; int slot; }; struct linear_range { unsigned int min; unsigned int min_sel; unsigned int max_sel; unsigned int step; }; enum packing_op { PACK = 0, UNPACK = 1, }; struct xxh32_state { uint32_t total_len_32; uint32_t large_len; uint32_t v1; uint32_t v2; uint32_t v3; uint32_t v4; uint32_t mem32[4]; uint32_t memsize; }; struct xxh64_state { uint64_t total_len; uint64_t v1; uint64_t v2; uint64_t v3; uint64_t v4; uint64_t mem64[4]; uint32_t memsize; }; struct gen_pool_chunk { struct list_head next_chunk; atomic_long_t avail; phys_addr_t phys_addr; void *owner; long unsigned int start_addr; long unsigned int end_addr; long unsigned int bits[0]; }; struct genpool_data_align { int align; }; struct genpool_data_fixed { long unsigned int offset; }; typedef struct { unsigned char op; unsigned char bits; short unsigned int val; } code; typedef enum { HEAD = 0, FLAGS = 1, TIME = 2, OS = 3, EXLEN = 4, EXTRA = 5, NAME = 6, COMMENT = 7, HCRC = 8, DICTID = 9, DICT = 10, TYPE = 11, TYPEDO = 12, STORED = 13, COPY = 14, TABLE = 15, LENLENS = 16, CODELENS = 17, LEN = 18, LENEXT = 19, DIST = 20, DISTEXT = 21, MATCH = 22, LIT = 23, CHECK = 24, LENGTH = 25, DONE = 26, BAD = 27, MEM = 28, SYNC = 29, } inflate_mode; struct inflate_state { inflate_mode mode; int last; int wrap; int havedict; int flags; unsigned int dmax; long unsigned int check; long unsigned int total; unsigned int wbits; unsigned int wsize; unsigned int whave; unsigned int write; unsigned char *window; long unsigned int hold; unsigned int bits; unsigned int length; unsigned int offset; unsigned int extra; const code *lencode; const code *distcode; unsigned int lenbits; unsigned int distbits; unsigned int ncode; unsigned int nlen; unsigned int ndist; unsigned int have; code *next; short unsigned int lens[320]; short unsigned int work[288]; code codes[2048]; }; union uu { short unsigned int us; unsigned char b[2]; }; typedef unsigned int uInt; typedef enum { CODES = 0, LENS = 1, DISTS = 2, } codetype; struct inflate_workspace { struct inflate_state inflate_state; unsigned char working_window[32768]; }; typedef unsigned char uch; typedef short unsigned int ush; typedef long unsigned int ulg; struct ct_data_s { union { ush freq; ush code; } fc; union { ush dad; ush len; } dl; }; typedef struct ct_data_s ct_data; struct static_tree_desc_s { const ct_data *static_tree; const int *extra_bits; int extra_base; int elems; int max_length; }; typedef struct static_tree_desc_s static_tree_desc; struct tree_desc_s { ct_data *dyn_tree; int max_code; static_tree_desc *stat_desc; }; typedef ush Pos; typedef unsigned int IPos; struct deflate_state { z_streamp strm; int status; Byte *pending_buf; ulg pending_buf_size; Byte *pending_out; int pending; int noheader; Byte data_type; Byte method; int last_flush; uInt w_size; uInt w_bits; uInt w_mask; Byte *window; ulg window_size; Pos *prev; Pos *head; uInt ins_h; uInt hash_size; uInt hash_bits; uInt hash_mask; uInt hash_shift; long int block_start; uInt match_length; IPos prev_match; int match_available; uInt strstart; uInt match_start; uInt lookahead; uInt prev_length; uInt max_chain_length; uInt max_lazy_match; int level; int strategy; uInt good_match; int nice_match; struct ct_data_s dyn_ltree[573]; struct ct_data_s dyn_dtree[61]; struct ct_data_s bl_tree[39]; struct tree_desc_s l_desc; struct tree_desc_s d_desc; struct tree_desc_s bl_desc; ush bl_count[16]; int heap[573]; int heap_len; int heap_max; uch depth[573]; uch *l_buf; uInt lit_bufsize; uInt last_lit; ush *d_buf; ulg opt_len; ulg static_len; ulg compressed_len; uInt matches; int last_eob_len; ush bi_buf; int bi_valid; }; typedef struct deflate_state deflate_state; typedef enum { need_more = 0, block_done = 1, finish_started = 2, finish_done = 3, } block_state; typedef block_state (*compress_func)(deflate_state *, int); struct deflate_workspace { deflate_state deflate_memory; Byte *window_memory; Pos *prev_memory; Pos *head_memory; char *overlay_memory; }; typedef struct deflate_workspace deflate_workspace; struct config_s { ush good_length; ush max_lazy; ush nice_length; ush max_chain; compress_func func; }; typedef struct config_s config; typedef struct tree_desc_s tree_desc; typedef struct { uint32_t hashTable[4096]; uint32_t currentOffset; uint32_t initCheck; const uint8_t *dictionary; uint8_t *bufferStart; uint32_t dictSize; } LZ4_stream_t_internal; typedef union { long long unsigned int table[2052]; LZ4_stream_t_internal internal_donotuse; } LZ4_stream_t; typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; typedef uint64_t U64; typedef uintptr_t uptrval; typedef enum { noLimit = 0, limitedOutput = 1, } limitedOutput_directive; typedef enum { byPtr = 0, byU32 = 1, byU16 = 2, } tableType_t; typedef enum { noDict = 0, withPrefix64k = 1, usingExtDict = 2, } dict_directive; typedef enum { noDictIssue = 0, dictSmall = 1, } dictIssue_directive; typedef struct { const uint8_t *externalDict; size_t extDictSize; const uint8_t *prefixEnd; size_t prefixSize; } LZ4_streamDecode_t_internal; typedef union { long long unsigned int table[4]; LZ4_streamDecode_t_internal internal_donotuse; } LZ4_streamDecode_t; typedef enum { endOnOutputSize = 0, endOnInputSize = 1, } endCondition_directive; typedef enum { decode_full_block = 0, partial_decode = 1, } earlyEnd_directive; typedef struct { size_t bitContainer; int bitPos; char *startPtr; char *ptr; char *endPtr; } BIT_CStream_t; typedef unsigned int FSE_CTable; typedef struct { ptrdiff_t value; const void *stateTable; const void *symbolTT; unsigned int stateLog; } FSE_CState_t; typedef struct { int deltaFindState; U32 deltaNbBits; } FSE_symbolCompressionTransform; typedef int16_t S16; struct HUF_CElt_s { U16 val; BYTE nbBits; }; typedef struct HUF_CElt_s HUF_CElt; typedef enum { HUF_repeat_none = 0, HUF_repeat_check = 1, HUF_repeat_valid = 2, } HUF_repeat; struct nodeElt_s { U32 count; U16 parent; BYTE byte; BYTE nbBits; }; typedef struct nodeElt_s nodeElt; typedef struct { U32 base; U32 curr; } rankPos; typedef enum { ZSTDcs_created = 0, ZSTDcs_init = 1, ZSTDcs_ongoing = 2, ZSTDcs_ending = 3, } ZSTD_compressionStage_e; typedef void * (*ZSTD_allocFunction)(void *, size_t); typedef void (*ZSTD_freeFunction)(void *, void *); typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void *opaque; } ZSTD_customMem; typedef struct { U32 price; U32 off; U32 mlen; U32 litlen; U32 rep[3]; } ZSTD_optimal_t; typedef struct { U32 off; U32 len; } ZSTD_match_t; struct seqDef_s; typedef struct seqDef_s seqDef; typedef struct { seqDef *sequencesStart; seqDef *sequences; BYTE *litStart; BYTE *lit; BYTE *llCode; BYTE *mlCode; BYTE *ofCode; U32 longLengthID; U32 longLengthPos; ZSTD_optimal_t *priceTable; ZSTD_match_t *matchTable; U32 *matchLengthFreq; U32 *litLengthFreq; U32 *litFreq; U32 *offCodeFreq; U32 matchLengthSum; U32 matchSum; U32 litLengthSum; U32 litSum; U32 offCodeSum; U32 log2matchLengthSum; U32 log2matchSum; U32 log2litLengthSum; U32 log2litSum; U32 log2offCodeSum; U32 factor; U32 staticPrices; U32 cachedPrice; U32 cachedLitLength; const BYTE *cachedLiterals; } seqStore_t; struct HUF_CElt_s___2; typedef struct HUF_CElt_s___2 HUF_CElt___2; struct ZSTD_CCtx_s___2 { const BYTE *nextSrc; const BYTE *base; const BYTE *dictBase; U32 dictLimit; U32 lowLimit; U32 nextToUpdate; U32 nextToUpdate3; U32 hashLog3; U32 loadedDictEnd; U32 forceWindow; U32 forceRawDict; ZSTD_compressionStage_e stage; U32 rep[3]; U32 repToConfirm[3]; U32 dictID; ZSTD_parameters params; void *workSpace; size_t workSpaceSize; size_t blockSize; U64 frameContentSize; struct xxh64_state xxhState; ZSTD_customMem customMem; seqStore_t seqStore; U32 *hashTable; U32 *hashTable3; U32 *chainTable; HUF_CElt___2 *hufTable; U32 flagStaticTables; HUF_repeat flagStaticHufTable; FSE_CTable offcodeCTable[187]; FSE_CTable matchlengthCTable[363]; FSE_CTable litlengthCTable[329]; unsigned int tmpCounters[1536]; }; typedef struct ZSTD_CCtx_s___2 ZSTD_CCtx___2; struct ZSTD_CDict_s { void *dictBuffer; const void *dictContent; size_t dictContentSize; ZSTD_CCtx___2 *refContext; }; typedef struct ZSTD_CDict_s ZSTD_CDict; struct ZSTD_inBuffer_s { const void *src; size_t size; size_t pos; }; typedef struct ZSTD_inBuffer_s ZSTD_inBuffer; struct ZSTD_outBuffer_s { void *dst; size_t size; size_t pos; }; typedef struct ZSTD_outBuffer_s ZSTD_outBuffer; typedef enum { zcss_init = 0, zcss_load = 1, zcss_flush = 2, zcss_final = 3, } ZSTD_cStreamStage; struct ZSTD_CStream_s { ZSTD_CCtx___2 *cctx; ZSTD_CDict *cdictLocal; const ZSTD_CDict *cdict; char *inBuff; size_t inBuffSize; size_t inToCompress; size_t inBuffPos; size_t inBuffTarget; size_t blockSize; char *outBuff; size_t outBuffSize; size_t outBuffContentSize; size_t outBuffFlushedSize; ZSTD_cStreamStage stage; U32 checksum; U32 frameEnded; U64 pledgedSrcSize; U64 inputProcessed; ZSTD_parameters params; ZSTD_customMem customMem; }; typedef struct ZSTD_CStream_s ZSTD_CStream; typedef int32_t S32; typedef enum { set_basic = 0, set_rle = 1, set_compressed = 2, set_repeat = 3, } symbolEncodingType_e; struct seqDef_s { U32 offset; U16 litLength; U16 matchLength; }; typedef enum { ZSTDcrp_continue = 0, ZSTDcrp_noMemset = 1, ZSTDcrp_fullReset = 2, } ZSTD_compResetPolicy_e; typedef void (*ZSTD_blockCompressor)(ZSTD_CCtx___2 *, const void *, size_t); typedef enum { zsf_gather = 0, zsf_flush = 1, zsf_end = 2, } ZSTD_flush_e; typedef size_t (*searchMax_f)(ZSTD_CCtx___2 *, const BYTE *, const BYTE *, size_t *, U32, U32); typedef struct { size_t bitContainer; unsigned int bitsConsumed; const char *ptr; const char *start; } BIT_DStream_t; typedef enum { BIT_DStream_unfinished = 0, BIT_DStream_endOfBuffer = 1, BIT_DStream_completed = 2, BIT_DStream_overflow = 3, } BIT_DStream_status; typedef unsigned int FSE_DTable; typedef struct { size_t state; const void *table; } FSE_DState_t; typedef struct { U16 tableLog; U16 fastMode; } FSE_DTableHeader; typedef struct { short unsigned int newState; unsigned char symbol; unsigned char nbBits; } FSE_decode_t; typedef struct { void *ptr; const void *end; } ZSTD_stack; typedef U32 HUF_DTable; typedef struct { BYTE maxTableLog; BYTE tableType; BYTE tableLog; BYTE reserved; } DTableDesc; typedef struct { BYTE byte; BYTE nbBits; } HUF_DEltX2; typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX4; typedef struct { BYTE symbol; BYTE weight; } sortedSymbol_t; typedef U32 rankValCol_t[13]; typedef struct { U32 tableTime; U32 decode256Time; } algo_time_t; typedef struct { FSE_DTable LLTable[513]; FSE_DTable OFTable[257]; FSE_DTable MLTable[513]; HUF_DTable hufTable[4097]; U64 workspace[384]; U32 rep[3]; } ZSTD_entropyTables_t; typedef struct { long long unsigned int frameContentSize; unsigned int windowSize; unsigned int dictID; unsigned int checksumFlag; } ZSTD_frameParams; typedef enum { bt_raw = 0, bt_rle = 1, bt_compressed = 2, bt_reserved = 3, } blockType_e; typedef enum { ZSTDds_getFrameHeaderSize = 0, ZSTDds_decodeFrameHeader = 1, ZSTDds_decodeBlockHeader = 2, ZSTDds_decompressBlock = 3, ZSTDds_decompressLastBlock = 4, ZSTDds_checkChecksum = 5, ZSTDds_decodeSkippableHeader = 6, ZSTDds_skipFrame = 7, } ZSTD_dStage; struct ZSTD_DCtx_s___2 { const FSE_DTable *LLTptr; const FSE_DTable *MLTptr; const FSE_DTable *OFTptr; const HUF_DTable *HUFptr; ZSTD_entropyTables_t entropy; const void *previousDstEnd; const void *base; const void *vBase; const void *dictEnd; size_t expected; ZSTD_frameParams fParams; blockType_e bType; ZSTD_dStage stage; U32 litEntropy; U32 fseEntropy; struct xxh64_state xxhState; size_t headerSize; U32 dictID; const BYTE *litPtr; ZSTD_customMem customMem; size_t litSize; size_t rleSize; BYTE litBuffer[131080]; BYTE headerBuffer[18]; }; typedef struct ZSTD_DCtx_s___2 ZSTD_DCtx___2; struct ZSTD_DDict_s { void *dictBuffer; const void *dictContent; size_t dictSize; ZSTD_entropyTables_t entropy; U32 dictID; U32 entropyPresent; ZSTD_customMem cMem; }; typedef struct ZSTD_DDict_s ZSTD_DDict; typedef enum { zdss_init = 0, zdss_loadHeader = 1, zdss_read = 2, zdss_load = 3, zdss_flush = 4, } ZSTD_dStreamStage; struct ZSTD_DStream_s { ZSTD_DCtx___2 *dctx; ZSTD_DDict *ddictLocal; const ZSTD_DDict *ddict; ZSTD_frameParams fParams; ZSTD_dStreamStage stage; char *inBuff; size_t inBuffSize; size_t inPos; size_t maxWindowSize; char *outBuff; size_t outBuffSize; size_t outStart; size_t outEnd; size_t blockSize; BYTE headerBuffer[18]; size_t lhSize; ZSTD_customMem customMem; void *legacyContext; U32 previousLegacyVersion; U32 legacyVersion; U32 hostageByte; }; typedef struct ZSTD_DStream_s ZSTD_DStream; typedef enum { ZSTDnit_frameHeader = 0, ZSTDnit_blockHeader = 1, ZSTDnit_block = 2, ZSTDnit_lastBlock = 3, ZSTDnit_checksum = 4, ZSTDnit_skippableFrame = 5, } ZSTD_nextInputType_e; typedef uintptr_t uPtrDiff; typedef struct { blockType_e blockType; U32 lastBlock; U32 origSize; } blockProperties_t; typedef union { FSE_decode_t realData; U32 alignedBy4; } FSE_decode_t4; typedef struct { size_t litLength; size_t matchLength; size_t offset; const BYTE *match; } seq_t; typedef struct { BIT_DStream_t DStream; FSE_DState_t stateLL; FSE_DState_t stateOffb; FSE_DState_t stateML; size_t prevOffset[3]; const BYTE *base; size_t pos; uPtrDiff gotoDict; } seqState_t; enum xz_mode { XZ_SINGLE = 0, XZ_PREALLOC = 1, XZ_DYNALLOC = 2, }; enum xz_ret { XZ_OK = 0, XZ_STREAM_END = 1, XZ_UNSUPPORTED_CHECK = 2, XZ_MEM_ERROR = 3, XZ_MEMLIMIT_ERROR = 4, XZ_FORMAT_ERROR = 5, XZ_OPTIONS_ERROR = 6, XZ_DATA_ERROR = 7, XZ_BUF_ERROR = 8, }; struct xz_buf { const uint8_t *in; size_t in_pos; size_t in_size; uint8_t *out; size_t out_pos; size_t out_size; }; struct xz_dec; typedef uint64_t vli_type; enum xz_check { XZ_CHECK_NONE = 0, XZ_CHECK_CRC32 = 1, XZ_CHECK_CRC64 = 4, XZ_CHECK_SHA256 = 10, }; struct xz_dec_hash { vli_type unpadded; vli_type uncompressed; uint32_t crc32; }; struct xz_dec_lzma2; struct xz_dec_bcj; struct xz_dec___2 { enum { SEQ_STREAM_HEADER = 0, SEQ_BLOCK_START = 1, SEQ_BLOCK_HEADER = 2, SEQ_BLOCK_UNCOMPRESS = 3, SEQ_BLOCK_PADDING = 4, SEQ_BLOCK_CHECK = 5, SEQ_INDEX = 6, SEQ_INDEX_PADDING = 7, SEQ_INDEX_CRC32 = 8, SEQ_STREAM_FOOTER = 9, } sequence; uint32_t pos; vli_type vli; size_t in_start; size_t out_start; uint32_t crc32; enum xz_check check_type; enum xz_mode mode; bool allow_buf_error; struct { vli_type compressed; vli_type uncompressed; uint32_t size; } block_header; struct { vli_type compressed; vli_type uncompressed; vli_type count; struct xz_dec_hash hash; } block; struct { enum { SEQ_INDEX_COUNT = 0, SEQ_INDEX_UNPADDED = 1, SEQ_INDEX_UNCOMPRESSED = 2, } sequence; vli_type size; vli_type count; struct xz_dec_hash hash; } index; struct { size_t pos; size_t size; uint8_t buf[1024]; } temp; struct xz_dec_lzma2 *lzma2; struct xz_dec_bcj *bcj; bool bcj_active; }; enum lzma_state { STATE_LIT_LIT = 0, STATE_MATCH_LIT_LIT = 1, STATE_REP_LIT_LIT = 2, STATE_SHORTREP_LIT_LIT = 3, STATE_MATCH_LIT = 4, STATE_REP_LIT = 5, STATE_SHORTREP_LIT = 6, STATE_LIT_MATCH = 7, STATE_LIT_LONGREP = 8, STATE_LIT_SHORTREP = 9, STATE_NONLIT_MATCH = 10, STATE_NONLIT_REP = 11, }; struct dictionary { uint8_t *buf; size_t start; size_t pos; size_t full; size_t limit; size_t end; uint32_t size; uint32_t size_max; uint32_t allocated; enum xz_mode mode; }; struct rc_dec { uint32_t range; uint32_t code; uint32_t init_bytes_left; const uint8_t *in; size_t in_pos; size_t in_limit; }; struct lzma_len_dec { uint16_t choice; uint16_t choice2; uint16_t low[128]; uint16_t mid[128]; uint16_t high[256]; }; struct lzma_dec { uint32_t rep0; uint32_t rep1; uint32_t rep2; uint32_t rep3; enum lzma_state state; uint32_t len; uint32_t lc; uint32_t literal_pos_mask; uint32_t pos_mask; uint16_t is_match[192]; uint16_t is_rep[12]; uint16_t is_rep0[12]; uint16_t is_rep1[12]; uint16_t is_rep2[12]; uint16_t is_rep0_long[192]; uint16_t dist_slot[256]; uint16_t dist_special[114]; uint16_t dist_align[16]; struct lzma_len_dec match_len_dec; struct lzma_len_dec rep_len_dec; uint16_t literal[12288]; }; enum lzma2_seq { SEQ_CONTROL = 0, SEQ_UNCOMPRESSED_1 = 1, SEQ_UNCOMPRESSED_2 = 2, SEQ_COMPRESSED_0 = 3, SEQ_COMPRESSED_1 = 4, SEQ_PROPERTIES = 5, SEQ_LZMA_PREPARE = 6, SEQ_LZMA_RUN = 7, SEQ_COPY = 8, }; struct lzma2_dec { enum lzma2_seq sequence; enum lzma2_seq next_sequence; uint32_t uncompressed; uint32_t compressed; bool need_dict_reset; bool need_props; }; struct xz_dec_lzma2___2 { struct rc_dec rc; struct dictionary dict; struct lzma2_dec lzma2; struct lzma_dec lzma; struct { uint32_t size; uint8_t buf[63]; } temp; }; struct xz_dec_bcj___2 { enum { BCJ_X86 = 4, BCJ_POWERPC = 5, BCJ_IA64 = 6, BCJ_ARM = 7, BCJ_ARMTHUMB = 8, BCJ_SPARC = 9, } type; enum xz_ret ret; bool single_call; uint32_t pos; uint32_t x86_prev_mask; uint8_t *out; size_t out_pos; size_t out_size; struct { size_t filtered; size_t size; uint8_t buf[16]; } temp; }; struct ts_state { unsigned int offset; char cb[48]; }; struct ts_config; struct ts_ops { const char *name; struct ts_config * (*init)(const void *, unsigned int, gfp_t, int); unsigned int (*find)(struct ts_config *, struct ts_state *); void (*destroy)(struct ts_config *); void * (*get_pattern)(struct ts_config *); unsigned int (*get_pattern_len)(struct ts_config *); struct module *owner; struct list_head list; }; struct ts_config { struct ts_ops *ops; int flags; unsigned int (*get_next_block)(unsigned int, const u8 **, struct ts_config *, struct ts_state *); void (*finish)(struct ts_config *, struct ts_state *); }; struct ts_linear_state { unsigned int len; const void *data; }; struct ei_entry { struct list_head list; long unsigned int start_addr; long unsigned int end_addr; int etype; void *priv; }; struct ddebug_table { struct list_head link; const char *mod_name; unsigned int num_ddebugs; struct _ddebug *ddebugs; }; struct ddebug_query { const char *filename; const char *module; const char *function; const char *format; unsigned int first_lineno; unsigned int last_lineno; }; struct ddebug_iter { struct ddebug_table *table; unsigned int idx; }; struct flag_settings { unsigned int flags; unsigned int mask; }; struct flagsbuf { char buf[7]; }; struct nla_bitfield32 { __u32 value; __u32 selector; }; enum nla_policy_validation { NLA_VALIDATE_NONE = 0, NLA_VALIDATE_RANGE = 1, NLA_VALIDATE_RANGE_WARN_TOO_LONG = 2, NLA_VALIDATE_MIN = 3, NLA_VALIDATE_MAX = 4, NLA_VALIDATE_MASK = 5, NLA_VALIDATE_RANGE_PTR = 6, NLA_VALIDATE_FUNCTION = 7, }; enum netlink_validation { NL_VALIDATE_LIBERAL = 0, NL_VALIDATE_TRAILING = 1, NL_VALIDATE_MAXTYPE = 2, NL_VALIDATE_UNSPEC = 4, NL_VALIDATE_STRICT_ATTRS = 8, NL_VALIDATE_NESTED = 16, }; struct cpu_rmap { struct kref refcount; u16 size; u16 used; void **obj; struct { u16 index; u16 dist; } near[0]; }; struct irq_glue { struct irq_affinity_notify notify; struct cpu_rmap *rmap; u16 index; }; typedef mpi_limb_t *mpi_ptr_t; typedef int mpi_size_t; typedef mpi_limb_t UWtype; typedef unsigned int UHWtype; enum gcry_mpi_constants { MPI_C_ZERO = 0, MPI_C_ONE = 1, MPI_C_TWO = 2, MPI_C_THREE = 3, MPI_C_FOUR = 4, MPI_C_EIGHT = 5, }; struct barrett_ctx_s; typedef struct barrett_ctx_s *mpi_barrett_t; struct gcry_mpi_point { MPI x; MPI y; MPI z; }; typedef struct gcry_mpi_point *MPI_POINT; enum gcry_mpi_ec_models { MPI_EC_WEIERSTRASS = 0, MPI_EC_MONTGOMERY = 1, MPI_EC_EDWARDS = 2, }; enum ecc_dialects { ECC_DIALECT_STANDARD = 0, ECC_DIALECT_ED25519 = 1, ECC_DIALECT_SAFECURVE = 2, }; struct mpi_ec_ctx { enum gcry_mpi_ec_models model; enum ecc_dialects dialect; int flags; unsigned int nbits; MPI p; MPI a; MPI b; MPI_POINT G; MPI n; unsigned int h; MPI_POINT Q; MPI d; const char *name; struct { struct { unsigned int a_is_pminus3: 1; unsigned int two_inv_p: 1; } valid; int a_is_pminus3; MPI two_inv_p; mpi_barrett_t p_barrett; MPI scratch[11]; } t; void (*addm)(MPI, MPI, MPI, struct mpi_ec_ctx *); void (*subm)(MPI, MPI, MPI, struct mpi_ec_ctx *); void (*mulm)(MPI, MPI, MPI, struct mpi_ec_ctx *); void (*pow2)(MPI, const MPI, struct mpi_ec_ctx *); void (*mul2)(MPI, MPI, struct mpi_ec_ctx *); }; struct field_table { const char *p; void (*addm)(MPI, MPI, MPI, struct mpi_ec_ctx *); void (*subm)(MPI, MPI, MPI, struct mpi_ec_ctx *); void (*mulm)(MPI, MPI, MPI, struct mpi_ec_ctx *); void (*mul2)(MPI, MPI, struct mpi_ec_ctx *); void (*pow2)(MPI, const MPI, struct mpi_ec_ctx *); }; enum gcry_mpi_format { GCRYMPI_FMT_NONE = 0, GCRYMPI_FMT_STD = 1, GCRYMPI_FMT_PGP = 2, GCRYMPI_FMT_SSH = 3, GCRYMPI_FMT_HEX = 4, GCRYMPI_FMT_USG = 5, GCRYMPI_FMT_OPAQUE = 8, }; struct barrett_ctx_s___2; typedef struct barrett_ctx_s___2 *mpi_barrett_t___2; struct barrett_ctx_s___2 { MPI m; int m_copied; int k; MPI y; MPI r1; MPI r2; MPI r3; }; struct karatsuba_ctx { struct karatsuba_ctx *next; mpi_ptr_t tspace; mpi_size_t tspace_size; mpi_ptr_t tp; mpi_size_t tp_size; }; typedef long int mpi_limb_signed_t; enum dim_tune_state { DIM_PARKING_ON_TOP = 0, DIM_PARKING_TIRED = 1, DIM_GOING_RIGHT = 2, DIM_GOING_LEFT = 3, }; struct dim_cq_moder { u16 usec; u16 pkts; u16 comps; u8 cq_period_mode; }; enum dim_cq_period_mode { DIM_CQ_PERIOD_MODE_START_FROM_EQE = 0, DIM_CQ_PERIOD_MODE_START_FROM_CQE = 1, DIM_CQ_PERIOD_NUM_MODES = 2, }; enum dim_state { DIM_START_MEASURE = 0, DIM_MEASURE_IN_PROGRESS = 1, DIM_APPLY_NEW_PROFILE = 2, }; enum dim_stats_state { DIM_STATS_WORSE = 0, DIM_STATS_SAME = 1, DIM_STATS_BETTER = 2, }; enum dim_step_result { DIM_STEPPED = 0, DIM_TOO_TIRED = 1, DIM_ON_EDGE = 2, }; struct sg_pool { size_t size; char *name; struct kmem_cache *slab; mempool_t *pool; }; enum { IRQ_POLL_F_SCHED = 0, IRQ_POLL_F_DISABLE = 1, }; struct font_desc { int idx; const char *name; unsigned int width; unsigned int height; unsigned int charcount; const void *data; int pref; }; struct font_data { unsigned int extra[4]; const unsigned char data[0]; }; struct pldmfw_record { struct list_head entry; struct list_head descs; const u8 *version_string; u8 version_type; u8 version_len; u16 package_data_len; u32 device_update_flags; const u8 *package_data; long unsigned int *component_bitmap; u16 component_bitmap_len; }; struct pldmfw_desc_tlv { struct list_head entry; const u8 *data; u16 type; u16 size; }; struct pldmfw_component { struct list_head entry; u16 classification; u16 identifier; u16 options; u16 activation_method; u32 comparison_stamp; u32 component_size; const u8 *component_data; const u8 *version_string; u8 version_type; u8 version_len; u8 index; }; struct pldmfw_ops; struct pldmfw { const struct pldmfw_ops *ops; struct device *dev; }; struct pldmfw_ops { bool (*match_record)(struct pldmfw *, struct pldmfw_record *); int (*send_package_data)(struct pldmfw *, const u8 *, u16); int (*send_component_table)(struct pldmfw *, struct pldmfw_component *, u8); int (*flash_component)(struct pldmfw *, struct pldmfw_component *); int (*finalize_update)(struct pldmfw *); }; struct __pldm_timestamp { u8 b[13]; }; struct __pldm_header { uuid_t id; u8 revision; __le16 size; struct __pldm_timestamp release_date; __le16 component_bitmap_len; u8 version_type; u8 version_len; u8 version_string[0]; } __attribute__((packed)); struct __pldmfw_record_info { __le16 record_len; u8 descriptor_count; __le32 device_update_flags; u8 version_type; u8 version_len; __le16 package_data_len; u8 variable_record_data[0]; } __attribute__((packed)); struct __pldmfw_desc_tlv { __le16 type; __le16 size; u8 data[0]; }; struct __pldmfw_record_area { u8 record_count; u8 records[0]; }; struct __pldmfw_component_info { __le16 classification; __le16 identifier; __le32 comparison_stamp; __le16 options; __le16 activation_method; __le32 location_offset; __le32 size; u8 version_type; u8 version_len; u8 version_string[0]; } __attribute__((packed)); struct __pldmfw_component_area { __le16 component_image_count; u8 components[0]; }; struct pldmfw_priv { struct pldmfw *context; const struct firmware *fw; size_t offset; struct list_head records; struct list_head components; const struct __pldm_header *header; u16 total_header_size; u16 component_bitmap_len; u16 bitmap_size; u16 component_count; const u8 *component_start; const u8 *record_start; u8 record_count; u32 header_crc; struct pldmfw_record *matching_record; }; struct pldm_pci_record_id { int vendor; int device; int subsystem_vendor; int subsystem_device; }; struct msr { union { struct { u32 l; u32 h; }; u64 q; }; }; struct msr_info { u32 msr_no; struct msr reg; struct msr *msrs; int err; }; struct msr_regs_info { u32 *regs; int err; }; struct msr_info_completion { struct msr_info msr; struct completion done; }; struct trace_event_raw_msr_trace_class { struct trace_entry ent; unsigned int msr; u64 val; int failed; char __data[0]; }; struct trace_event_data_offsets_msr_trace_class {}; typedef void (*btf_trace_read_msr)(void *, unsigned int, u64, int); typedef void (*btf_trace_write_msr)(void *, unsigned int, u64, int); typedef void (*btf_trace_rdpmc)(void *, unsigned int, u64, int); struct warn_args___2; struct compress_format { unsigned char magic[2]; const char *name; decompress_fn decompressor; }; struct group_data { int limit[21]; int base[20]; int permute[258]; int minLen; int maxLen; }; struct bunzip_data { int writeCopies; int writePos; int writeRunCountdown; int writeCount; int writeCurrent; long int (*fill)(void *, long unsigned int); long int inbufCount; long int inbufPos; unsigned char *inbuf; unsigned int inbufBitCount; unsigned int inbufBits; unsigned int crc32Table[256]; unsigned int headerCRC; unsigned int totalCRC; unsigned int writeCRC; unsigned int *dbuf; unsigned int dbufSize; unsigned char selectors[32768]; struct group_data groups[6]; int io_error; int byteCount[256]; unsigned char symToByte[256]; unsigned char mtfSymbol[256]; }; struct rc { long int (*fill)(void *, long unsigned int); uint8_t *ptr; uint8_t *buffer; uint8_t *buffer_end; long int buffer_size; uint32_t code; uint32_t range; uint32_t bound; void (*error)(char *); }; struct lzma_header { uint8_t pos; uint32_t dict_size; uint64_t dst_size; } __attribute__((packed)); struct writer { uint8_t *buffer; uint8_t previous_byte; size_t buffer_pos; int bufsize; size_t global_pos; long int (*flush)(void *, long unsigned int); struct lzma_header *header; }; struct cstate { int state; uint32_t rep0; uint32_t rep1; uint32_t rep2; uint32_t rep3; }; typedef enum { ZSTD_error_no_error = 0, ZSTD_error_GENERIC = 1, ZSTD_error_prefix_unknown = 2, ZSTD_error_version_unsupported = 3, ZSTD_error_parameter_unknown = 4, ZSTD_error_frameParameter_unsupported = 5, ZSTD_error_frameParameter_unsupportedBy32bits = 6, ZSTD_error_frameParameter_windowTooLarge = 7, ZSTD_error_compressionParameter_unsupported = 8, ZSTD_error_init_missing = 9, ZSTD_error_memory_allocation = 10, ZSTD_error_stage_wrong = 11, ZSTD_error_dstSize_tooSmall = 12, ZSTD_error_srcSize_wrong = 13, ZSTD_error_corruption_detected = 14, ZSTD_error_checksum_wrong = 15, ZSTD_error_tableLog_tooLarge = 16, ZSTD_error_maxSymbolValue_tooLarge = 17, ZSTD_error_maxSymbolValue_tooSmall = 18, ZSTD_error_dictionary_corrupted = 19, ZSTD_error_dictionary_wrong = 20, ZSTD_error_dictionaryCreation_failed = 21, ZSTD_error_maxCode = 22, } ZSTD_ErrorCode; struct ZSTD_DStream_s___2; typedef struct ZSTD_DStream_s___2 ZSTD_DStream___2; enum cpio_fields { C_MAGIC = 0, C_INO = 1, C_MODE = 2, C_UID = 3, C_GID = 4, C_NLINK = 5, C_MTIME = 6, C_FILESIZE = 7, C_MAJ = 8, C_MIN = 9, C_RMAJ = 10, C_RMIN = 11, C_NAMESIZE = 12, C_CHKSUM = 13, C_NFIELDS = 14, }; struct fprop_local_single { long unsigned int events; unsigned int period; raw_spinlock_t lock; }; struct ida_bitmap { long unsigned int bitmap[16]; }; struct klist_waiter { struct list_head list; struct klist_node *node; struct task_struct *process; int woken; }; struct uevent_sock { struct list_head list; struct sock *sk; }; enum { LOGIC_PIO_INDIRECT = 0, LOGIC_PIO_CPU_MMIO = 1, }; struct logic_pio_host_ops; struct logic_pio_hwaddr { struct list_head list; struct fwnode_handle *fwnode; resource_size_t hw_start; resource_size_t io_start; resource_size_t size; long unsigned int flags; void *hostdata; const struct logic_pio_host_ops *ops; }; struct logic_pio_host_ops { u32 (*in)(void *, long unsigned int, size_t); void (*out)(void *, long unsigned int, u32, size_t); u32 (*ins)(void *, long unsigned int, void *, size_t, unsigned int); void (*outs)(void *, long unsigned int, const void *, size_t, unsigned int); }; typedef struct { long unsigned int key[2]; } hsiphash_key_t; struct clk_core; struct clk { struct clk_core *core; struct device *dev; const char *dev_id; const char *con_id; long unsigned int min_rate; long unsigned int max_rate; unsigned int exclusive_count; struct hlist_node clks_node; }; enum format_type { FORMAT_TYPE_NONE = 0, FORMAT_TYPE_WIDTH = 1, FORMAT_TYPE_PRECISION = 2, FORMAT_TYPE_CHAR = 3, FORMAT_TYPE_STR = 4, FORMAT_TYPE_PTR = 5, FORMAT_TYPE_PERCENT_CHAR = 6, FORMAT_TYPE_INVALID = 7, FORMAT_TYPE_LONG_LONG = 8, FORMAT_TYPE_ULONG = 9, FORMAT_TYPE_LONG = 10, FORMAT_TYPE_UBYTE = 11, FORMAT_TYPE_BYTE = 12, FORMAT_TYPE_USHORT = 13, FORMAT_TYPE_SHORT = 14, FORMAT_TYPE_UINT = 15, FORMAT_TYPE_INT = 16, FORMAT_TYPE_SIZE_T = 17, FORMAT_TYPE_PTRDIFF = 18, }; struct printf_spec { unsigned int type: 8; int field_width: 24; unsigned int flags: 8; unsigned int base: 8; int precision: 16; }; struct page_flags_fields { int width; int shift; int mask; const struct printf_spec *spec; const char *name; }; struct minmax_sample { u32 t; u32 v; }; struct minmax { struct minmax_sample s[3]; }; enum { st_wordstart = 0, st_wordcmp = 1, st_wordskip = 2, st_bufcpy = 3, }; enum { st_wordstart___2 = 0, st_wordcmp___2 = 1, st_wordskip___2 = 2, }; struct in6_addr___2; enum reg_type { REG_TYPE_RM = 0, REG_TYPE_REG = 1, REG_TYPE_INDEX = 2, REG_TYPE_BASE = 3, }; enum device_link_state { DL_STATE_NONE = 4294967295, DL_STATE_DORMANT = 0, DL_STATE_AVAILABLE = 1, DL_STATE_CONSUMER_PROBE = 2, DL_STATE_ACTIVE = 3, DL_STATE_SUPPLIER_UNBIND = 4, }; struct device_link { struct device *supplier; struct list_head s_node; struct device *consumer; struct list_head c_node; struct device link_dev; enum device_link_state status; u32 flags; refcount_t rpm_active; struct kref kref; struct work_struct rm_work; bool supplier_preactivated; }; struct phy_configure_opts_dp { unsigned int link_rate; unsigned int lanes; unsigned int voltage[4]; unsigned int pre[4]; u8 ssc: 1; u8 set_rate: 1; u8 set_lanes: 1; u8 set_voltages: 1; }; struct phy_configure_opts_mipi_dphy { unsigned int clk_miss; unsigned int clk_post; unsigned int clk_pre; unsigned int clk_prepare; unsigned int clk_settle; unsigned int clk_term_en; unsigned int clk_trail; unsigned int clk_zero; unsigned int d_term_en; unsigned int eot; unsigned int hs_exit; unsigned int hs_prepare; unsigned int hs_settle; unsigned int hs_skip; unsigned int hs_trail; unsigned int hs_zero; unsigned int init; unsigned int lpx; unsigned int ta_get; unsigned int ta_go; unsigned int ta_sure; unsigned int wakeup; long unsigned int hs_clk_rate; long unsigned int lp_clk_rate; unsigned char lanes; }; enum phy_mode { PHY_MODE_INVALID = 0, PHY_MODE_USB_HOST = 1, PHY_MODE_USB_HOST_LS = 2, PHY_MODE_USB_HOST_FS = 3, PHY_MODE_USB_HOST_HS = 4, PHY_MODE_USB_HOST_SS = 5, PHY_MODE_USB_DEVICE = 6, PHY_MODE_USB_DEVICE_LS = 7, PHY_MODE_USB_DEVICE_FS = 8, PHY_MODE_USB_DEVICE_HS = 9, PHY_MODE_USB_DEVICE_SS = 10, PHY_MODE_USB_OTG = 11, PHY_MODE_UFS_HS_A = 12, PHY_MODE_UFS_HS_B = 13, PHY_MODE_PCIE = 14, PHY_MODE_ETHERNET = 15, PHY_MODE_MIPI_DPHY = 16, PHY_MODE_SATA = 17, PHY_MODE_LVDS = 18, PHY_MODE_DP = 19, }; enum phy_media { PHY_MEDIA_DEFAULT = 0, PHY_MEDIA_SR = 1, PHY_MEDIA_DAC = 2, }; union phy_configure_opts { struct phy_configure_opts_mipi_dphy mipi_dphy; struct phy_configure_opts_dp dp; }; struct phy; struct phy_ops { int (*init)(struct phy *); int (*exit)(struct phy *); int (*power_on)(struct phy *); int (*power_off)(struct phy *); int (*set_mode)(struct phy *, enum phy_mode, int); int (*set_media)(struct phy *, enum phy_media); int (*set_speed)(struct phy *, int); int (*configure)(struct phy *, union phy_configure_opts *); int (*validate)(struct phy *, enum phy_mode, int, union phy_configure_opts *); int (*reset)(struct phy *); int (*calibrate)(struct phy *); void (*release)(struct phy *); struct module *owner; }; struct phy_attrs { u32 bus_width; u32 max_link_rate; enum phy_mode mode; }; struct regulator; struct phy { struct device dev; int id; const struct phy_ops *ops; struct mutex mutex; int init_count; int power_count; struct phy_attrs attrs; struct regulator *pwr; }; struct phy_provider { struct device *dev; struct device_node *children; struct module *owner; struct list_head list; struct phy * (*of_xlate)(struct device *, struct of_phandle_args *); }; struct phy_lookup { struct list_head node; const char *dev_id; const char *con_id; struct phy *phy; }; struct pinctrl; struct pinctrl_state; struct dev_pin_info { struct pinctrl *p; struct pinctrl_state *default_state; struct pinctrl_state *init_state; struct pinctrl_state *sleep_state; struct pinctrl_state *idle_state; }; struct pinctrl { struct list_head node; struct device *dev; struct list_head states; struct pinctrl_state *state; struct list_head dt_maps; struct kref users; }; struct pinctrl_state { struct list_head node; const char *name; struct list_head settings; }; struct pinctrl_pin_desc { unsigned int number; const char *name; void *drv_data; }; struct gpio_chip; struct pinctrl_gpio_range { struct list_head node; const char *name; unsigned int id; unsigned int base; unsigned int pin_base; unsigned int npins; const unsigned int *pins; struct gpio_chip *gc; }; struct gpio_irq_chip { struct irq_chip *chip; struct irq_domain *domain; const struct irq_domain_ops *domain_ops; struct fwnode_handle *fwnode; struct irq_domain *parent_domain; int (*child_to_parent_hwirq)(struct gpio_chip *, unsigned int, unsigned int, unsigned int *, unsigned int *); void * (*populate_parent_alloc_arg)(struct gpio_chip *, unsigned int, unsigned int); unsigned int (*child_offset_to_irq)(struct gpio_chip *, unsigned int); struct irq_domain_ops child_irq_domain_ops; irq_flow_handler_t handler; unsigned int default_type; struct lock_class_key *lock_key; struct lock_class_key *request_key; irq_flow_handler_t parent_handler; void *parent_handler_data; unsigned int num_parents; unsigned int *parents; unsigned int *map; bool threaded; int (*init_hw)(struct gpio_chip *); void (*init_valid_mask)(struct gpio_chip *, long unsigned int *, unsigned int); long unsigned int *valid_mask; unsigned int first; void (*irq_enable)(struct irq_data *); void (*irq_disable)(struct irq_data *); void (*irq_unmask)(struct irq_data *); void (*irq_mask)(struct irq_data *); }; struct gpio_device; struct gpio_chip { const char *label; struct gpio_device *gpiodev; struct device *parent; struct module *owner; int (*request)(struct gpio_chip *, unsigned int); void (*free)(struct gpio_chip *, unsigned int); int (*get_direction)(struct gpio_chip *, unsigned int); int (*direction_input)(struct gpio_chip *, unsigned int); int (*direction_output)(struct gpio_chip *, unsigned int, int); int (*get)(struct gpio_chip *, unsigned int); int (*get_multiple)(struct gpio_chip *, long unsigned int *, long unsigned int *); void (*set)(struct gpio_chip *, unsigned int, int); void (*set_multiple)(struct gpio_chip *, long unsigned int *, long unsigned int *); int (*set_config)(struct gpio_chip *, unsigned int, long unsigned int); int (*to_irq)(struct gpio_chip *, unsigned int); void (*dbg_show)(struct seq_file *, struct gpio_chip *); int (*init_valid_mask)(struct gpio_chip *, long unsigned int *, unsigned int); int (*add_pin_ranges)(struct gpio_chip *); int base; u16 ngpio; const char * const *names; bool can_sleep; long unsigned int (*read_reg)(void *); void (*write_reg)(void *, long unsigned int); bool be_bits; void *reg_dat; void *reg_set; void *reg_clr; void *reg_dir_out; void *reg_dir_in; bool bgpio_dir_unreadable; int bgpio_bits; spinlock_t bgpio_lock; long unsigned int bgpio_data; long unsigned int bgpio_dir; struct gpio_irq_chip irq; long unsigned int *valid_mask; }; struct pinctrl_dev; struct pinctrl_map; struct pinctrl_ops { int (*get_groups_count)(struct pinctrl_dev *); const char * (*get_group_name)(struct pinctrl_dev *, unsigned int); int (*get_group_pins)(struct pinctrl_dev *, unsigned int, const unsigned int **, unsigned int *); void (*pin_dbg_show)(struct pinctrl_dev *, struct seq_file *, unsigned int); int (*dt_node_to_map)(struct pinctrl_dev *, struct device_node *, struct pinctrl_map **, unsigned int *); void (*dt_free_map)(struct pinctrl_dev *, struct pinctrl_map *, unsigned int); }; struct pinctrl_desc; struct pinctrl_dev { struct list_head node; struct pinctrl_desc *desc; struct xarray pin_desc_tree; struct list_head gpio_ranges; struct device *dev; struct module *owner; void *driver_data; struct pinctrl *p; struct pinctrl_state *hog_default; struct pinctrl_state *hog_sleep; struct mutex mutex; struct dentry *device_root; }; enum pinctrl_map_type { PIN_MAP_TYPE_INVALID = 0, PIN_MAP_TYPE_DUMMY_STATE = 1, PIN_MAP_TYPE_MUX_GROUP = 2, PIN_MAP_TYPE_CONFIGS_PIN = 3, PIN_MAP_TYPE_CONFIGS_GROUP = 4, }; struct pinctrl_map_mux { const char *group; const char *function; }; struct pinctrl_map_configs { const char *group_or_pin; long unsigned int *configs; unsigned int num_configs; }; struct pinctrl_map { const char *dev_name; const char *name; enum pinctrl_map_type type; const char *ctrl_dev_name; union { struct pinctrl_map_mux mux; struct pinctrl_map_configs configs; } data; }; struct pinmux_ops; struct pinconf_ops; struct pinconf_generic_params; struct pin_config_item; struct pinctrl_desc { const char *name; const struct pinctrl_pin_desc *pins; unsigned int npins; const struct pinctrl_ops *pctlops; const struct pinmux_ops *pmxops; const struct pinconf_ops *confops; struct module *owner; unsigned int num_custom_params; const struct pinconf_generic_params *custom_params; const struct pin_config_item *custom_conf_items; bool link_consumers; }; struct pinmux_ops { int (*request)(struct pinctrl_dev *, unsigned int); int (*free)(struct pinctrl_dev *, unsigned int); int (*get_functions_count)(struct pinctrl_dev *); const char * (*get_function_name)(struct pinctrl_dev *, unsigned int); int (*get_function_groups)(struct pinctrl_dev *, unsigned int, const char * const **, unsigned int *); int (*set_mux)(struct pinctrl_dev *, unsigned int, unsigned int); int (*gpio_request_enable)(struct pinctrl_dev *, struct pinctrl_gpio_range *, unsigned int); void (*gpio_disable_free)(struct pinctrl_dev *, struct pinctrl_gpio_range *, unsigned int); int (*gpio_set_direction)(struct pinctrl_dev *, struct pinctrl_gpio_range *, unsigned int, bool); bool strict; }; struct pinconf_ops { bool is_generic; int (*pin_config_get)(struct pinctrl_dev *, unsigned int, long unsigned int *); int (*pin_config_set)(struct pinctrl_dev *, unsigned int, long unsigned int *, unsigned int); int (*pin_config_group_get)(struct pinctrl_dev *, unsigned int, long unsigned int *); int (*pin_config_group_set)(struct pinctrl_dev *, unsigned int, long unsigned int *, unsigned int); void (*pin_config_dbg_show)(struct pinctrl_dev *, struct seq_file *, unsigned int); void (*pin_config_group_dbg_show)(struct pinctrl_dev *, struct seq_file *, unsigned int); void (*pin_config_config_dbg_show)(struct pinctrl_dev *, struct seq_file *, long unsigned int); }; enum pin_config_param { PIN_CONFIG_BIAS_BUS_HOLD = 0, PIN_CONFIG_BIAS_DISABLE = 1, PIN_CONFIG_BIAS_HIGH_IMPEDANCE = 2, PIN_CONFIG_BIAS_PULL_DOWN = 3, PIN_CONFIG_BIAS_PULL_PIN_DEFAULT = 4, PIN_CONFIG_BIAS_PULL_UP = 5, PIN_CONFIG_DRIVE_OPEN_DRAIN = 6, PIN_CONFIG_DRIVE_OPEN_SOURCE = 7, PIN_CONFIG_DRIVE_PUSH_PULL = 8, PIN_CONFIG_DRIVE_STRENGTH = 9, PIN_CONFIG_DRIVE_STRENGTH_UA = 10, PIN_CONFIG_INPUT_DEBOUNCE = 11, PIN_CONFIG_INPUT_ENABLE = 12, PIN_CONFIG_INPUT_SCHMITT = 13, PIN_CONFIG_INPUT_SCHMITT_ENABLE = 14, PIN_CONFIG_MODE_LOW_POWER = 15, PIN_CONFIG_MODE_PWM = 16, PIN_CONFIG_OUTPUT = 17, PIN_CONFIG_OUTPUT_ENABLE = 18, PIN_CONFIG_PERSIST_STATE = 19, PIN_CONFIG_POWER_SOURCE = 20, PIN_CONFIG_SKEW_DELAY = 21, PIN_CONFIG_SLEEP_HARDWARE_STATE = 22, PIN_CONFIG_SLEW_RATE = 23, PIN_CONFIG_END = 127, PIN_CONFIG_MAX = 255, }; struct pinconf_generic_params { const char * const property; enum pin_config_param param; u32 default_value; }; struct pin_config_item { const enum pin_config_param param; const char * const display; const char * const format; bool has_arg; }; struct gpio_desc___2; struct gpio_device { int id; struct device dev; struct cdev chrdev; struct device *mockdev; struct module *owner; struct gpio_chip *chip; struct gpio_desc___2 *descs; int base; u16 ngpio; const char *label; void *data; struct list_head list; struct blocking_notifier_head notifier; struct list_head pin_ranges; }; struct gpio_desc___2 { struct gpio_device *gdev; long unsigned int flags; const char *label; const char *name; unsigned int debounce_period_us; }; struct pinctrl_setting_mux { unsigned int group; unsigned int func; }; struct pinctrl_setting_configs { unsigned int group_or_pin; long unsigned int *configs; unsigned int num_configs; }; struct pinctrl_setting { struct list_head node; enum pinctrl_map_type type; struct pinctrl_dev *pctldev; const char *dev_name; union { struct pinctrl_setting_mux mux; struct pinctrl_setting_configs configs; } data; }; struct pin_desc { struct pinctrl_dev *pctldev; const char *name; bool dynamic_name; void *drv_data; unsigned int mux_usecount; const char *mux_owner; const struct pinctrl_setting_mux *mux_setting; const char *gpio_owner; }; struct pinctrl_maps { struct list_head node; const struct pinctrl_map *maps; unsigned int num_maps; }; struct pctldev; enum regcache_type { REGCACHE_NONE = 0, REGCACHE_RBTREE = 1, REGCACHE_COMPRESSED = 2, REGCACHE_FLAT = 3, }; struct reg_default { unsigned int reg; unsigned int def; }; enum regmap_endian { REGMAP_ENDIAN_DEFAULT = 0, REGMAP_ENDIAN_BIG = 1, REGMAP_ENDIAN_LITTLE = 2, REGMAP_ENDIAN_NATIVE = 3, }; struct regmap_range { unsigned int range_min; unsigned int range_max; }; struct regmap_access_table { const struct regmap_range *yes_ranges; unsigned int n_yes_ranges; const struct regmap_range *no_ranges; unsigned int n_no_ranges; }; typedef void (*regmap_lock)(void *); typedef void (*regmap_unlock)(void *); struct regmap_range_cfg; struct regmap_config { const char *name; int reg_bits; int reg_stride; int pad_bits; int val_bits; bool (*writeable_reg)(struct device *, unsigned int); bool (*readable_reg)(struct device *, unsigned int); bool (*volatile_reg)(struct device *, unsigned int); bool (*precious_reg)(struct device *, unsigned int); bool (*writeable_noinc_reg)(struct device *, unsigned int); bool (*readable_noinc_reg)(struct device *, unsigned int); bool disable_locking; regmap_lock lock; regmap_unlock unlock; void *lock_arg; int (*reg_read)(void *, unsigned int, unsigned int *); int (*reg_write)(void *, unsigned int, unsigned int); bool fast_io; unsigned int max_register; const struct regmap_access_table *wr_table; const struct regmap_access_table *rd_table; const struct regmap_access_table *volatile_table; const struct regmap_access_table *precious_table; const struct regmap_access_table *wr_noinc_table; const struct regmap_access_table *rd_noinc_table; const struct reg_default *reg_defaults; unsigned int num_reg_defaults; enum regcache_type cache_type; const void *reg_defaults_raw; unsigned int num_reg_defaults_raw; long unsigned int read_flag_mask; long unsigned int write_flag_mask; bool zero_flag_mask; bool use_single_read; bool use_single_write; bool use_relaxed_mmio; bool can_multi_write; enum regmap_endian reg_format_endian; enum regmap_endian val_format_endian; const struct regmap_range_cfg *ranges; unsigned int num_ranges; bool use_hwlock; unsigned int hwlock_id; unsigned int hwlock_mode; bool can_sleep; }; struct regmap_range_cfg { const char *name; unsigned int range_min; unsigned int range_max; unsigned int selector_reg; unsigned int selector_mask; int selector_shift; unsigned int window_start; unsigned int window_len; }; typedef int (*regmap_hw_write)(void *, const void *, size_t); typedef int (*regmap_hw_gather_write)(void *, const void *, size_t, const void *, size_t); struct regmap_async; typedef int (*regmap_hw_async_write)(void *, const void *, size_t, const void *, size_t, struct regmap_async *); typedef int (*regmap_hw_read)(void *, const void *, size_t, void *, size_t); typedef int (*regmap_hw_reg_read)(void *, unsigned int, unsigned int *); typedef int (*regmap_hw_reg_write)(void *, unsigned int, unsigned int); typedef int (*regmap_hw_reg_update_bits)(void *, unsigned int, unsigned int, unsigned int); typedef struct regmap_async * (*regmap_hw_async_alloc)(); typedef void (*regmap_hw_free_context)(void *); struct regmap_bus { bool fast_io; regmap_hw_write write; regmap_hw_gather_write gather_write; regmap_hw_async_write async_write; regmap_hw_reg_write reg_write; regmap_hw_reg_update_bits reg_update_bits; regmap_hw_read read; regmap_hw_reg_read reg_read; regmap_hw_free_context free_context; regmap_hw_async_alloc async_alloc; u8 read_flag_mask; enum regmap_endian reg_format_endian_default; enum regmap_endian val_format_endian_default; size_t max_raw_read; size_t max_raw_write; bool free_on_exit; }; struct i2c_device_id { char name[20]; kernel_ulong_t driver_data; }; struct software_node { const char *name; const struct software_node *parent; const struct property_entry *properties; }; struct i2c_msg { __u16 addr; __u16 flags; __u16 len; __u8 *buf; }; union i2c_smbus_data { __u8 byte; __u16 word; __u8 block[34]; }; enum i2c_slave_event { I2C_SLAVE_READ_REQUESTED = 0, I2C_SLAVE_WRITE_REQUESTED = 1, I2C_SLAVE_READ_PROCESSED = 2, I2C_SLAVE_WRITE_RECEIVED = 3, I2C_SLAVE_STOP = 4, }; struct i2c_client; typedef int (*i2c_slave_cb_t)(struct i2c_client *, enum i2c_slave_event, u8 *); struct i2c_adapter; struct i2c_client { short unsigned int flags; short unsigned int addr; char name[20]; struct i2c_adapter *adapter; struct device dev; int init_irq; int irq; struct list_head detected; i2c_slave_cb_t slave_cb; void *devres_group_id; }; enum i2c_alert_protocol { I2C_PROTOCOL_SMBUS_ALERT = 0, I2C_PROTOCOL_SMBUS_HOST_NOTIFY = 1, }; struct i2c_board_info; struct i2c_driver { unsigned int class; int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); int (*probe_new)(struct i2c_client *); void (*shutdown)(struct i2c_client *); void (*alert)(struct i2c_client *, enum i2c_alert_protocol, unsigned int); int (*command)(struct i2c_client *, unsigned int, void *); struct device_driver driver; const struct i2c_device_id *id_table; int (*detect)(struct i2c_client *, struct i2c_board_info *); const short unsigned int *address_list; struct list_head clients; }; struct i2c_board_info { char type[20]; short unsigned int flags; short unsigned int addr; const char *dev_name; void *platform_data; struct device_node *of_node; struct fwnode_handle *fwnode; const struct software_node *swnode; const struct resource *resources; unsigned int num_resources; int irq; }; struct i2c_algorithm; struct i2c_lock_operations; struct i2c_bus_recovery_info; struct i2c_adapter_quirks; struct i2c_adapter { struct module *owner; unsigned int class; const struct i2c_algorithm *algo; void *algo_data; const struct i2c_lock_operations *lock_ops; struct rt_mutex bus_lock; struct rt_mutex mux_lock; int timeout; int retries; struct device dev; long unsigned int locked_flags; int nr; char name[48]; struct completion dev_released; struct mutex userspace_clients_lock; struct list_head userspace_clients; struct i2c_bus_recovery_info *bus_recovery_info; const struct i2c_adapter_quirks *quirks; struct irq_domain *host_notify_domain; struct regulator *bus_regulator; }; struct i2c_algorithm { int (*master_xfer)(struct i2c_adapter *, struct i2c_msg *, int); int (*master_xfer_atomic)(struct i2c_adapter *, struct i2c_msg *, int); int (*smbus_xfer)(struct i2c_adapter *, u16, short unsigned int, char, u8, int, union i2c_smbus_data *); int (*smbus_xfer_atomic)(struct i2c_adapter *, u16, short unsigned int, char, u8, int, union i2c_smbus_data *); u32 (*functionality)(struct i2c_adapter *); int (*reg_slave)(struct i2c_client *); int (*unreg_slave)(struct i2c_client *); }; struct i2c_lock_operations { void (*lock_bus)(struct i2c_adapter *, unsigned int); int (*trylock_bus)(struct i2c_adapter *, unsigned int); void (*unlock_bus)(struct i2c_adapter *, unsigned int); }; struct i2c_bus_recovery_info { int (*recover_bus)(struct i2c_adapter *); int (*get_scl)(struct i2c_adapter *); void (*set_scl)(struct i2c_adapter *, int); int (*get_sda)(struct i2c_adapter *); void (*set_sda)(struct i2c_adapter *, int); int (*get_bus_free)(struct i2c_adapter *); void (*prepare_recovery)(struct i2c_adapter *); void (*unprepare_recovery)(struct i2c_adapter *); struct gpio_desc *scl_gpiod; struct gpio_desc *sda_gpiod; struct pinctrl *pinctrl; struct pinctrl_state *pins_default; struct pinctrl_state *pins_gpio; }; struct i2c_adapter_quirks { u64 flags; int max_num_msgs; u16 max_write_len; u16 max_read_len; u16 max_comb_1st_msg_len; u16 max_comb_2nd_msg_len; }; enum { SX150X_123 = 0, SX150X_456 = 1, SX150X_789 = 2, }; enum { SX150X_789_REG_MISC_AUTOCLEAR_OFF = 1, SX150X_MAX_REGISTER = 173, SX150X_IRQ_TYPE_EDGE_RISING = 1, SX150X_IRQ_TYPE_EDGE_FALLING = 2, SX150X_789_RESET_KEY1 = 18, SX150X_789_RESET_KEY2 = 52, }; struct sx150x_123_pri { u8 reg_pld_mode; u8 reg_pld_table0; u8 reg_pld_table1; u8 reg_pld_table2; u8 reg_pld_table3; u8 reg_pld_table4; u8 reg_advanced; }; struct sx150x_456_pri { u8 reg_pld_mode; u8 reg_pld_table0; u8 reg_pld_table1; u8 reg_pld_table2; u8 reg_pld_table3; u8 reg_pld_table4; u8 reg_advanced; }; struct sx150x_789_pri { u8 reg_drain; u8 reg_polarity; u8 reg_clock; u8 reg_misc; u8 reg_reset; u8 ngpios; }; struct sx150x_device_data { u8 model; u8 reg_pullup; u8 reg_pulldn; u8 reg_dir; u8 reg_data; u8 reg_irq_mask; u8 reg_irq_src; u8 reg_sense; u8 ngpios; union { struct sx150x_123_pri x123; struct sx150x_456_pri x456; struct sx150x_789_pri x789; } pri; const struct pinctrl_pin_desc *pins; unsigned int npins; }; struct regmap; struct sx150x_pinctrl { struct device *dev; struct i2c_client *client; struct pinctrl_dev *pctldev; struct pinctrl_desc pinctrl_desc; struct gpio_chip gpio; struct irq_chip irq_chip; struct regmap *regmap; struct { u32 sense; u32 masked; } irq; struct mutex lock; const struct sx150x_device_data *data; }; struct intel_pingroup { const char *name; const unsigned int *pins; size_t npins; short unsigned int mode; const unsigned int *modes; }; struct intel_function { const char *name; const char * const *groups; size_t ngroups; }; struct intel_padgroup { unsigned int reg_num; unsigned int base; unsigned int size; int gpio_base; unsigned int padown_num; }; struct intel_community { unsigned int barno; unsigned int padown_offset; unsigned int padcfglock_offset; unsigned int hostown_offset; unsigned int is_offset; unsigned int ie_offset; unsigned int features; unsigned int pin_base; size_t npins; unsigned int gpp_size; unsigned int gpp_num_padown_regs; const struct intel_padgroup *gpps; size_t ngpps; const unsigned int *pad_map; short unsigned int nirqs; short unsigned int acpi_space_id; void *regs; void *pad_regs; }; struct intel_pinctrl_soc_data { const char *uid; const struct pinctrl_pin_desc *pins; size_t npins; const struct intel_pingroup *groups; size_t ngroups; const struct intel_function *functions; size_t nfunctions; const struct intel_community *communities; size_t ncommunities; }; struct intel_pad_context; struct intel_community_context; struct intel_pinctrl_context { struct intel_pad_context *pads; struct intel_community_context *communities; }; struct intel_pad_context { u32 conf0; u32 val; }; struct intel_pinctrl { struct device *dev; raw_spinlock_t lock; struct pinctrl_desc pctldesc; struct pinctrl_dev *pctldev; struct gpio_chip chip; struct irq_chip irqchip; const struct intel_pinctrl_soc_data *soc; struct intel_community *communities; size_t ncommunities; struct intel_pinctrl_context context; int irq; }; typedef acpi_status (*acpi_adr_space_handler)(u32, acpi_physical_address, u32, u64 *, void *, void *); typedef acpi_status (*acpi_adr_space_setup)(acpi_handle, u32, void *, void **); struct acpi_hotplug_profile { struct kobject kobj; int (*scan_dependent)(struct acpi_device *); void (*notify_online)(struct acpi_device *); bool enabled: 1; bool demand_offline: 1; }; struct acpi_device_status { u32 present: 1; u32 enabled: 1; u32 show_in_ui: 1; u32 functional: 1; u32 battery_present: 1; u32 reserved: 27; }; struct acpi_device_flags { u32 dynamic_status: 1; u32 removable: 1; u32 ejectable: 1; u32 power_manageable: 1; u32 match_driver: 1; u32 initialized: 1; u32 visited: 1; u32 hotplug_notify: 1; u32 is_dock_station: 1; u32 of_compatible_ok: 1; u32 coherent_dma: 1; u32 cca_seen: 1; u32 enumeration_by_parent: 1; u32 reserved: 19; }; typedef char acpi_bus_id[8]; struct acpi_pnp_type { u32 hardware_id: 1; u32 bus_address: 1; u32 platform_id: 1; u32 reserved: 29; }; typedef u64 acpi_bus_address; typedef char acpi_device_name[40]; typedef char acpi_device_class[20]; struct acpi_device_pnp { acpi_bus_id bus_id; int instance_no; struct acpi_pnp_type type; acpi_bus_address bus_address; char *unique_id; struct list_head ids; acpi_device_name device_name; acpi_device_class device_class; union acpi_object *str_obj; }; struct acpi_device_power_flags { u32 explicit_get: 1; u32 power_resources: 1; u32 inrush_current: 1; u32 power_removed: 1; u32 ignore_parent: 1; u32 dsw_present: 1; u32 reserved: 26; }; struct acpi_device_power_state { struct { u8 valid: 1; u8 explicit_set: 1; u8 reserved: 6; } flags; int power; int latency; struct list_head resources; }; struct acpi_device_power { int state; struct acpi_device_power_flags flags; struct acpi_device_power_state states[5]; }; struct acpi_device_wakeup_flags { u8 valid: 1; u8 notifier_present: 1; }; struct acpi_device_wakeup_context { void (*func)(struct acpi_device_wakeup_context *); struct device *dev; }; struct acpi_device_wakeup { acpi_handle gpe_device; u64 gpe_number; u64 sleep_state; struct list_head resources; struct acpi_device_wakeup_flags flags; struct acpi_device_wakeup_context context; struct wakeup_source *ws; int prepare_count; int enable_count; }; struct acpi_device_perf_flags { u8 reserved: 8; }; struct acpi_device_perf_state; struct acpi_device_perf { int state; struct acpi_device_perf_flags flags; int state_count; struct acpi_device_perf_state *states; }; struct acpi_device_dir { struct proc_dir_entry *entry; }; struct acpi_device_data { const union acpi_object *pointer; struct list_head properties; const union acpi_object *of_compatible; struct list_head subnodes; }; struct acpi_scan_handler; struct acpi_hotplug_context; struct acpi_driver; struct acpi_gpio_mapping; struct acpi_device { int device_type; acpi_handle handle; struct fwnode_handle fwnode; struct acpi_device *parent; struct list_head children; struct list_head node; struct list_head wakeup_list; struct list_head del_list; struct acpi_device_status status; struct acpi_device_flags flags; struct acpi_device_pnp pnp; struct acpi_device_power power; struct acpi_device_wakeup wakeup; struct acpi_device_perf performance; struct acpi_device_dir dir; struct acpi_device_data data; struct acpi_scan_handler *handler; struct acpi_hotplug_context *hp; struct acpi_driver *driver; const struct acpi_gpio_mapping *driver_gpios; void *driver_data; struct device dev; unsigned int physical_node_count; unsigned int dep_unmet; struct list_head physical_node_list; struct mutex physical_node_lock; void (*remove)(struct acpi_device *); }; struct acpi_scan_handler { const struct acpi_device_id *ids; struct list_head list_node; bool (*match)(const char *, const struct acpi_device_id **); int (*attach)(struct acpi_device *, const struct acpi_device_id *); void (*detach)(struct acpi_device *); void (*bind)(struct device *); void (*unbind)(struct device *); struct acpi_hotplug_profile hotplug; }; struct acpi_hotplug_context { struct acpi_device *self; int (*notify)(struct acpi_device *, u32); void (*uevent)(struct acpi_device *, u32); void (*fixup)(struct acpi_device *); }; typedef int (*acpi_op_add)(struct acpi_device *); typedef int (*acpi_op_remove)(struct acpi_device *); typedef void (*acpi_op_notify)(struct acpi_device *, u32); struct acpi_device_ops { acpi_op_add add; acpi_op_remove remove; acpi_op_notify notify; }; struct acpi_driver { char name[80]; char class[80]; const struct acpi_device_id *ids; unsigned int flags; struct acpi_device_ops ops; struct device_driver drv; struct module *owner; }; struct acpi_device_perf_state { struct { u8 valid: 1; u8 reserved: 7; } flags; u8 power; u8 performance; int latency; }; struct acpi_gpio_params; struct acpi_gpio_mapping { const char *name; const struct acpi_gpio_params *data; unsigned int size; unsigned int quirks; }; struct intel_pad_context___2; struct intel_pinctrl_context___2 { struct intel_pad_context___2 *pads; struct intel_community_context *communities; }; struct intel_pad_context___2 { u32 padctrl0; u32 padctrl1; }; struct intel_community_context { unsigned int intr_lines[16]; u32 saved_intmask; }; struct intel_pinctrl___2 { struct device *dev; raw_spinlock_t lock; struct pinctrl_desc pctldesc; struct pinctrl_dev *pctldev; struct gpio_chip chip; struct irq_chip irqchip; const struct intel_pinctrl_soc_data *soc; struct intel_community *communities; size_t ncommunities; struct intel_pinctrl_context___2 context; int irq; }; enum { INTEL_GPIO_BASE_ZERO = 4294967294, INTEL_GPIO_BASE_NOMAP = 4294967295, INTEL_GPIO_BASE_MATCH = 0, }; struct intel_pad_context___3; struct intel_community_context___2; struct intel_pinctrl_context___3 { struct intel_pad_context___3 *pads; struct intel_community_context___2 *communities; }; struct intel_pad_context___3 { u32 padcfg0; u32 padcfg1; u32 padcfg2; }; struct intel_community_context___2 { u32 *intmask; u32 *hostown; }; struct intel_pinctrl___3 { struct device *dev; raw_spinlock_t lock; struct pinctrl_desc pctldesc; struct pinctrl_dev *pctldev; struct gpio_chip chip; struct irq_chip irqchip; const struct intel_pinctrl_soc_data *soc; struct intel_community *communities; size_t ncommunities; struct intel_pinctrl_context___3 context; int irq; }; enum { PAD_UNLOCKED = 0, PAD_LOCKED = 1, PAD_LOCKED_TX = 2, PAD_LOCKED_FULL = 3, }; struct gpio_pin_range { struct list_head node; struct pinctrl_dev *pctldev; struct pinctrl_gpio_range range; }; struct gpio_array; struct gpio_descs { struct gpio_array *info; unsigned int ndescs; struct gpio_desc___2 *desc[0]; }; struct gpio_array { struct gpio_desc___2 **desc; unsigned int size; struct gpio_chip *chip; long unsigned int *get_mask; long unsigned int *set_mask; long unsigned int invert_mask[0]; }; enum gpiod_flags { GPIOD_ASIS = 0, GPIOD_IN = 1, GPIOD_OUT_LOW = 3, GPIOD_OUT_HIGH = 7, GPIOD_OUT_LOW_OPEN_DRAIN = 11, GPIOD_OUT_HIGH_OPEN_DRAIN = 15, }; struct acpi_gpio_params { unsigned int crs_entry_index; unsigned int line_index; bool active_low; }; enum gpio_lookup_flags { GPIO_ACTIVE_HIGH = 0, GPIO_ACTIVE_LOW = 1, GPIO_OPEN_DRAIN = 2, GPIO_OPEN_SOURCE = 4, GPIO_PERSISTENT = 0, GPIO_TRANSITORY = 8, GPIO_PULL_UP = 16, GPIO_PULL_DOWN = 32, GPIO_LOOKUP_FLAGS_DEFAULT = 0, }; struct gpiod_lookup { const char *key; u16 chip_hwnum; const char *con_id; unsigned int idx; long unsigned int flags; }; struct gpiod_lookup_table { struct list_head list; const char *dev_id; struct gpiod_lookup table[0]; }; struct gpiod_hog { struct list_head list; const char *chip_label; u16 chip_hwnum; const char *line_name; long unsigned int lflags; int dflags; }; enum { GPIOLINE_CHANGED_REQUESTED = 1, GPIOLINE_CHANGED_RELEASED = 2, GPIOLINE_CHANGED_CONFIG = 3, }; struct acpi_gpio_info { struct acpi_device *adev; enum gpiod_flags flags; bool gpioint; int pin_config; int polarity; int triggering; unsigned int debounce; unsigned int quirks; }; struct trace_event_raw_gpio_direction { struct trace_entry ent; unsigned int gpio; int in; int err; char __data[0]; }; struct trace_event_raw_gpio_value { struct trace_entry ent; unsigned int gpio; int get; int value; char __data[0]; }; struct trace_event_data_offsets_gpio_direction {}; struct trace_event_data_offsets_gpio_value {}; typedef void (*btf_trace_gpio_direction)(void *, unsigned int, int, int); typedef void (*btf_trace_gpio_value)(void *, unsigned int, int, int); struct devres; struct gpio { unsigned int gpio; long unsigned int flags; const char *label; }; struct gpiochip_info { char name[32]; char label[32]; __u32 lines; }; enum gpio_v2_line_flag { GPIO_V2_LINE_FLAG_USED = 1, GPIO_V2_LINE_FLAG_ACTIVE_LOW = 2, GPIO_V2_LINE_FLAG_INPUT = 4, GPIO_V2_LINE_FLAG_OUTPUT = 8, GPIO_V2_LINE_FLAG_EDGE_RISING = 16, GPIO_V2_LINE_FLAG_EDGE_FALLING = 32, GPIO_V2_LINE_FLAG_OPEN_DRAIN = 64, GPIO_V2_LINE_FLAG_OPEN_SOURCE = 128, GPIO_V2_LINE_FLAG_BIAS_PULL_UP = 256, GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN = 512, GPIO_V2_LINE_FLAG_BIAS_DISABLED = 1024, GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME = 2048, }; struct gpio_v2_line_values { __u64 bits; __u64 mask; }; enum gpio_v2_line_attr_id { GPIO_V2_LINE_ATTR_ID_FLAGS = 1, GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES = 2, GPIO_V2_LINE_ATTR_ID_DEBOUNCE = 3, }; struct gpio_v2_line_attribute { __u32 id; __u32 padding; union { __u64 flags; __u64 values; __u32 debounce_period_us; }; }; struct gpio_v2_line_config_attribute { struct gpio_v2_line_attribute attr; __u64 mask; }; struct gpio_v2_line_config { __u64 flags; __u32 num_attrs; __u32 padding[5]; struct gpio_v2_line_config_attribute attrs[10]; }; struct gpio_v2_line_request { __u32 offsets[64]; char consumer[32]; struct gpio_v2_line_config config; __u32 num_lines; __u32 event_buffer_size; __u32 padding[5]; __s32 fd; }; struct gpio_v2_line_info { char name[32]; char consumer[32]; __u32 offset; __u32 num_attrs; __u64 flags; struct gpio_v2_line_attribute attrs[10]; __u32 padding[4]; }; enum gpio_v2_line_changed_type { GPIO_V2_LINE_CHANGED_REQUESTED = 1, GPIO_V2_LINE_CHANGED_RELEASED = 2, GPIO_V2_LINE_CHANGED_CONFIG = 3, }; struct gpio_v2_line_info_changed { struct gpio_v2_line_info info; __u64 timestamp_ns; __u32 event_type; __u32 padding[5]; }; enum gpio_v2_line_event_id { GPIO_V2_LINE_EVENT_RISING_EDGE = 1, GPIO_V2_LINE_EVENT_FALLING_EDGE = 2, }; struct gpio_v2_line_event { __u64 timestamp_ns; __u32 id; __u32 offset; __u32 seqno; __u32 line_seqno; __u32 padding[6]; }; struct gpioline_info { __u32 line_offset; __u32 flags; char name[32]; char consumer[32]; }; struct gpioline_info_changed { struct gpioline_info info; __u64 timestamp; __u32 event_type; __u32 padding[5]; }; struct gpiohandle_request { __u32 lineoffsets[64]; __u32 flags; __u8 default_values[64]; char consumer_label[32]; __u32 lines; int fd; }; struct gpiohandle_config { __u32 flags; __u8 default_values[64]; __u32 padding[4]; }; struct gpiohandle_data { __u8 values[64]; }; struct gpioevent_request { __u32 lineoffset; __u32 handleflags; __u32 eventflags; char consumer_label[32]; int fd; }; struct gpioevent_data { __u64 timestamp; __u32 id; }; struct linehandle_state { struct gpio_device *gdev; const char *label; struct gpio_desc___2 *descs[64]; u32 num_descs; }; struct linereq; struct line { struct gpio_desc___2 *desc; struct linereq *req; unsigned int irq; u64 eflags; u64 timestamp_ns; u32 req_seqno; u32 line_seqno; struct delayed_work work; unsigned int sw_debounced; unsigned int level; }; struct linereq { struct gpio_device *gdev; const char *label; u32 num_lines; wait_queue_head_t wait; u32 event_buffer_size; struct { union { struct __kfifo kfifo; struct gpio_v2_line_event *type; const struct gpio_v2_line_event *const_type; char (*rectype)[0]; struct gpio_v2_line_event *ptr; const struct gpio_v2_line_event *ptr_const; }; struct gpio_v2_line_event buf[0]; } events; atomic_t seqno; struct mutex config_mutex; struct line lines[0]; }; struct lineevent_state { struct gpio_device *gdev; const char *label; struct gpio_desc___2 *desc; u32 eflags; int irq; wait_queue_head_t wait; struct { union { struct __kfifo kfifo; struct gpioevent_data *type; const struct gpioevent_data *const_type; char (*rectype)[0]; struct gpioevent_data *ptr; const struct gpioevent_data *ptr_const; }; struct gpioevent_data buf[16]; } events; u64 timestamp; }; struct gpio_chardev_data { struct gpio_device *gdev; wait_queue_head_t wait; struct { union { struct __kfifo kfifo; struct gpio_v2_line_info_changed *type; const struct gpio_v2_line_info_changed *const_type; char (*rectype)[0]; struct gpio_v2_line_info_changed *ptr; const struct gpio_v2_line_info_changed *ptr_const; }; struct gpio_v2_line_info_changed buf[32]; } events; struct notifier_block lineinfo_changed_nb; long unsigned int *watched_lines; atomic_t watch_abi_version; }; typedef u64 acpi_size; struct acpi_buffer { acpi_size length; void *pointer; }; typedef void (*acpi_object_handler)(acpi_handle, void *); struct acpi_connection_info { u8 *connection; u16 length; u8 access_length; }; struct acpi_resource_irq { u8 descriptor_length; u8 triggering; u8 polarity; u8 shareable; u8 wake_capable; u8 interrupt_count; u8 interrupts[1]; }; struct acpi_resource_dma { u8 type; u8 bus_master; u8 transfer; u8 channel_count; u8 channels[1]; }; struct acpi_resource_start_dependent { u8 descriptor_length; u8 compatibility_priority; u8 performance_robustness; }; struct acpi_resource_io { u8 io_decode; u8 alignment; u8 address_length; u16 minimum; u16 maximum; } __attribute__((packed)); struct acpi_resource_fixed_io { u16 address; u8 address_length; } __attribute__((packed)); struct acpi_resource_fixed_dma { u16 request_lines; u16 channels; u8 width; } __attribute__((packed)); struct acpi_resource_vendor { u16 byte_length; u8 byte_data[1]; } __attribute__((packed)); struct acpi_resource_vendor_typed { u16 byte_length; u8 uuid_subtype; u8 uuid[16]; u8 byte_data[1]; }; struct acpi_resource_end_tag { u8 checksum; }; struct acpi_resource_memory24 { u8 write_protect; u16 minimum; u16 maximum; u16 alignment; u16 address_length; } __attribute__((packed)); struct acpi_resource_memory32 { u8 write_protect; u32 minimum; u32 maximum; u32 alignment; u32 address_length; } __attribute__((packed)); struct acpi_resource_fixed_memory32 { u8 write_protect; u32 address; u32 address_length; } __attribute__((packed)); struct acpi_memory_attribute { u8 write_protect; u8 caching; u8 range_type; u8 translation; }; struct acpi_io_attribute { u8 range_type; u8 translation; u8 translation_type; u8 reserved1; }; union acpi_resource_attribute { struct acpi_memory_attribute mem; struct acpi_io_attribute io; u8 type_specific; }; struct acpi_resource_label { u16 string_length; char *string_ptr; } __attribute__((packed)); struct acpi_resource_source { u8 index; u16 string_length; char *string_ptr; } __attribute__((packed)); struct acpi_address16_attribute { u16 granularity; u16 minimum; u16 maximum; u16 translation_offset; u16 address_length; }; struct acpi_address32_attribute { u32 granularity; u32 minimum; u32 maximum; u32 translation_offset; u32 address_length; }; struct acpi_address64_attribute { u64 granularity; u64 minimum; u64 maximum; u64 translation_offset; u64 address_length; }; struct acpi_resource_address { u8 resource_type; u8 producer_consumer; u8 decode; u8 min_address_fixed; u8 max_address_fixed; union acpi_resource_attribute info; }; struct acpi_resource_address16 { u8 resource_type; u8 producer_consumer; u8 decode; u8 min_address_fixed; u8 max_address_fixed; union acpi_resource_attribute info; struct acpi_address16_attribute address; struct acpi_resource_source resource_source; } __attribute__((packed)); struct acpi_resource_address32 { u8 resource_type; u8 producer_consumer; u8 decode; u8 min_address_fixed; u8 max_address_fixed; union acpi_resource_attribute info; struct acpi_address32_attribute address; struct acpi_resource_source resource_source; } __attribute__((packed)); struct acpi_resource_address64 { u8 resource_type; u8 producer_consumer; u8 decode; u8 min_address_fixed; u8 max_address_fixed; union acpi_resource_attribute info; struct acpi_address64_attribute address; struct acpi_resource_source resource_source; } __attribute__((packed)); struct acpi_resource_extended_address64 { u8 resource_type; u8 producer_consumer; u8 decode; u8 min_address_fixed; u8 max_address_fixed; union acpi_resource_attribute info; u8 revision_ID; struct acpi_address64_attribute address; u64 type_specific; } __attribute__((packed)); struct acpi_resource_extended_irq { u8 producer_consumer; u8 triggering; u8 polarity; u8 shareable; u8 wake_capable; u8 interrupt_count; struct acpi_resource_source resource_source; u32 interrupts[1]; } __attribute__((packed)); struct acpi_resource_generic_register { u8 space_id; u8 bit_width; u8 bit_offset; u8 access_size; u64 address; } __attribute__((packed)); struct acpi_resource_gpio { u8 revision_id; u8 connection_type; u8 producer_consumer; u8 pin_config; u8 shareable; u8 wake_capable; u8 io_restriction; u8 triggering; u8 polarity; u16 drive_strength; u16 debounce_timeout; u16 pin_table_length; u16 vendor_length; struct acpi_resource_source resource_source; u16 *pin_table; u8 *vendor_data; } __attribute__((packed)); struct acpi_resource_common_serialbus { u8 revision_id; u8 type; u8 producer_consumer; u8 slave_mode; u8 connection_sharing; u8 type_revision_id; u16 type_data_length; u16 vendor_length; struct acpi_resource_source resource_source; u8 *vendor_data; } __attribute__((packed)); struct acpi_resource_i2c_serialbus { u8 revision_id; u8 type; u8 producer_consumer; u8 slave_mode; u8 connection_sharing; u8 type_revision_id; u16 type_data_length; u16 vendor_length; struct acpi_resource_source resource_source; u8 *vendor_data; u8 access_mode; u16 slave_address; u32 connection_speed; } __attribute__((packed)); struct acpi_resource_spi_serialbus { u8 revision_id; u8 type; u8 producer_consumer; u8 slave_mode; u8 connection_sharing; u8 type_revision_id; u16 type_data_length; u16 vendor_length; struct acpi_resource_source resource_source; u8 *vendor_data; u8 wire_mode; u8 device_polarity; u8 data_bit_length; u8 clock_phase; u8 clock_polarity; u16 device_selection; u32 connection_speed; } __attribute__((packed)); struct acpi_resource_uart_serialbus { u8 revision_id; u8 type; u8 producer_consumer; u8 slave_mode; u8 connection_sharing; u8 type_revision_id; u16 type_data_length; u16 vendor_length; struct acpi_resource_source resource_source; u8 *vendor_data; u8 endian; u8 data_bits; u8 stop_bits; u8 flow_control; u8 parity; u8 lines_enabled; u16 rx_fifo_size; u16 tx_fifo_size; u32 default_baud_rate; } __attribute__((packed)); struct acpi_resource_csi2_serialbus { u8 revision_id; u8 type; u8 producer_consumer; u8 slave_mode; u8 connection_sharing; u8 type_revision_id; u16 type_data_length; u16 vendor_length; struct acpi_resource_source resource_source; u8 *vendor_data; u8 local_port_instance; u8 phy_type; } __attribute__((packed)); struct acpi_resource_pin_function { u8 revision_id; u8 pin_config; u8 shareable; u16 function_number; u16 pin_table_length; u16 vendor_length; struct acpi_resource_source resource_source; u16 *pin_table; u8 *vendor_data; } __attribute__((packed)); struct acpi_resource_pin_config { u8 revision_id; u8 producer_consumer; u8 shareable; u8 pin_config_type; u32 pin_config_value; u16 pin_table_length; u16 vendor_length; struct acpi_resource_source resource_source; u16 *pin_table; u8 *vendor_data; } __attribute__((packed)); struct acpi_resource_pin_group { u8 revision_id; u8 producer_consumer; u16 pin_table_length; u16 vendor_length; u16 *pin_table; struct acpi_resource_label resource_label; u8 *vendor_data; } __attribute__((packed)); struct acpi_resource_pin_group_function { u8 revision_id; u8 producer_consumer; u8 shareable; u16 function_number; u16 vendor_length; struct acpi_resource_source resource_source; struct acpi_resource_label resource_source_label; u8 *vendor_data; } __attribute__((packed)); struct acpi_resource_pin_group_config { u8 revision_id; u8 producer_consumer; u8 shareable; u8 pin_config_type; u32 pin_config_value; u16 vendor_length; struct acpi_resource_source resource_source; struct acpi_resource_label resource_source_label; u8 *vendor_data; } __attribute__((packed)); union acpi_resource_data { struct acpi_resource_irq irq; struct acpi_resource_dma dma; struct acpi_resource_start_dependent start_dpf; struct acpi_resource_io io; struct acpi_resource_fixed_io fixed_io; struct acpi_resource_fixed_dma fixed_dma; struct acpi_resource_vendor vendor; struct acpi_resource_vendor_typed vendor_typed; struct acpi_resource_end_tag end_tag; struct acpi_resource_memory24 memory24; struct acpi_resource_memory32 memory32; struct acpi_resource_fixed_memory32 fixed_memory32; struct acpi_resource_address16 address16; struct acpi_resource_address32 address32; struct acpi_resource_address64 address64; struct acpi_resource_extended_address64 ext_address64; struct acpi_resource_extended_irq extended_irq; struct acpi_resource_generic_register generic_reg; struct acpi_resource_gpio gpio; struct acpi_resource_i2c_serialbus i2c_serial_bus; struct acpi_resource_spi_serialbus spi_serial_bus; struct acpi_resource_uart_serialbus uart_serial_bus; struct acpi_resource_csi2_serialbus csi2_serial_bus; struct acpi_resource_common_serialbus common_serial_bus; struct acpi_resource_pin_function pin_function; struct acpi_resource_pin_config pin_config; struct acpi_resource_pin_group pin_group; struct acpi_resource_pin_group_function pin_group_function; struct acpi_resource_pin_group_config pin_group_config; struct acpi_resource_address address; }; struct acpi_resource { u32 type; u32 length; union acpi_resource_data data; } __attribute__((packed)); typedef acpi_status (*acpi_walk_resource_callback)(struct acpi_resource *, void *); struct acpi_gpiolib_dmi_quirk { bool no_edge_events_on_boot; char *ignore_wake; }; struct acpi_gpio_event { struct list_head node; acpi_handle handle; irq_handler_t handler; unsigned int pin; unsigned int irq; long unsigned int irqflags; bool irq_is_wake; bool irq_requested; struct gpio_desc___2 *desc; }; struct acpi_gpio_connection { struct list_head node; unsigned int pin; struct gpio_desc___2 *desc; }; struct acpi_gpio_chip { struct acpi_connection_info conn_info; struct list_head conns; struct mutex conn_lock; struct gpio_chip *chip; struct list_head events; struct list_head deferred_req_irqs_list_entry; }; struct acpi_gpio_lookup { struct acpi_gpio_info info; int index; u16 pin_index; bool active_low; struct gpio_desc___2 *desc; int n; }; struct extcon_dev; struct regulator_dev; struct regulator_ops { int (*list_voltage)(struct regulator_dev *, unsigned int); int (*set_voltage)(struct regulator_dev *, int, int, unsigned int *); int (*map_voltage)(struct regulator_dev *, int, int); int (*set_voltage_sel)(struct regulator_dev *, unsigned int); int (*get_voltage)(struct regulator_dev *); int (*get_voltage_sel)(struct regulator_dev *); int (*set_current_limit)(struct regulator_dev *, int, int); int (*get_current_limit)(struct regulator_dev *); int (*set_input_current_limit)(struct regulator_dev *, int); int (*set_over_current_protection)(struct regulator_dev *, int, int, bool); int (*set_over_voltage_protection)(struct regulator_dev *, int, int, bool); int (*set_under_voltage_protection)(struct regulator_dev *, int, int, bool); int (*set_thermal_protection)(struct regulator_dev *, int, int, bool); int (*set_active_discharge)(struct regulator_dev *, bool); int (*enable)(struct regulator_dev *); int (*disable)(struct regulator_dev *); int (*is_enabled)(struct regulator_dev *); int (*set_mode)(struct regulator_dev *, unsigned int); unsigned int (*get_mode)(struct regulator_dev *); int (*get_error_flags)(struct regulator_dev *, unsigned int *); int (*enable_time)(struct regulator_dev *); int (*set_ramp_delay)(struct regulator_dev *, int); int (*set_voltage_time)(struct regulator_dev *, int, int); int (*set_voltage_time_sel)(struct regulator_dev *, unsigned int, unsigned int); int (*set_soft_start)(struct regulator_dev *); int (*get_status)(struct regulator_dev *); unsigned int (*get_optimum_mode)(struct regulator_dev *, int, int, int); int (*set_load)(struct regulator_dev *, int); int (*set_bypass)(struct regulator_dev *, bool); int (*get_bypass)(struct regulator_dev *, bool *); int (*set_suspend_voltage)(struct regulator_dev *, int); int (*set_suspend_enable)(struct regulator_dev *); int (*set_suspend_disable)(struct regulator_dev *); int (*set_suspend_mode)(struct regulator_dev *, unsigned int); int (*resume)(struct regulator_dev *); int (*set_pull_down)(struct regulator_dev *); }; struct regulator_coupler; struct coupling_desc { struct regulator_dev **coupled_rdevs; struct regulator_coupler *coupler; int n_resolved; int n_coupled; }; struct regulator_desc; struct regulation_constraints; struct regulator_enable_gpio; struct regulator_dev { const struct regulator_desc *desc; int exclusive; u32 use_count; u32 open_count; u32 bypass_count; struct list_head list; struct list_head consumer_list; struct coupling_desc coupling_desc; struct blocking_notifier_head notifier; struct ww_mutex mutex; struct task_struct *mutex_owner; int ref_cnt; struct module *owner; struct device dev; struct regulation_constraints *constraints; struct regulator *supply; const char *supply_name; struct regmap *regmap; struct delayed_work disable_work; void *reg_data; struct dentry *debugfs; struct regulator_enable_gpio *ena_pin; unsigned int ena_gpio_state: 1; unsigned int is_switch: 1; ktime_t last_off; int cached_err; bool use_cached_err; spinlock_t err_lock; }; enum regulator_type { REGULATOR_VOLTAGE = 0, REGULATOR_CURRENT = 1, }; struct regulator_config; struct regulator_desc { const char *name; const char *supply_name; const char *of_match; bool of_match_full_name; const char *regulators_node; int (*of_parse_cb)(struct device_node *, const struct regulator_desc *, struct regulator_config *); int id; unsigned int continuous_voltage_range: 1; unsigned int n_voltages; unsigned int n_current_limits; const struct regulator_ops *ops; int irq; enum regulator_type type; struct module *owner; unsigned int min_uV; unsigned int uV_step; unsigned int linear_min_sel; int fixed_uV; unsigned int ramp_delay; int min_dropout_uV; const struct linear_range *linear_ranges; const unsigned int *linear_range_selectors; int n_linear_ranges; const unsigned int *volt_table; const unsigned int *curr_table; unsigned int vsel_range_reg; unsigned int vsel_range_mask; unsigned int vsel_reg; unsigned int vsel_mask; unsigned int vsel_step; unsigned int csel_reg; unsigned int csel_mask; unsigned int apply_reg; unsigned int apply_bit; unsigned int enable_reg; unsigned int enable_mask; unsigned int enable_val; unsigned int disable_val; bool enable_is_inverted; unsigned int bypass_reg; unsigned int bypass_mask; unsigned int bypass_val_on; unsigned int bypass_val_off; unsigned int active_discharge_on; unsigned int active_discharge_off; unsigned int active_discharge_mask; unsigned int active_discharge_reg; unsigned int soft_start_reg; unsigned int soft_start_mask; unsigned int soft_start_val_on; unsigned int pull_down_reg; unsigned int pull_down_mask; unsigned int pull_down_val_on; unsigned int ramp_reg; unsigned int ramp_mask; const unsigned int *ramp_delay_table; unsigned int n_ramp_values; unsigned int enable_time; unsigned int off_on_delay; unsigned int poll_enabled_time; unsigned int (*of_map_mode)(unsigned int); }; struct regulator_init_data; struct regulator_config { struct device *dev; const struct regulator_init_data *init_data; void *driver_data; struct device_node *of_node; struct regmap *regmap; struct gpio_desc *ena_gpiod; }; struct regulator_state { int uV; int min_uV; int max_uV; unsigned int mode; int enabled; bool changeable; }; struct notification_limit { int prot; int err; int warn; }; struct regulation_constraints { const char *name; int min_uV; int max_uV; int uV_offset; int min_uA; int max_uA; int ilim_uA; int system_load; u32 *max_spread; int max_uV_step; unsigned int valid_modes_mask; unsigned int valid_ops_mask; int input_uV; struct regulator_state state_disk; struct regulator_state state_mem; struct regulator_state state_standby; struct notification_limit over_curr_limits; struct notification_limit over_voltage_limits; struct notification_limit under_voltage_limits; struct notification_limit temp_limits; suspend_state_t initial_state; unsigned int initial_mode; unsigned int ramp_delay; unsigned int settling_time; unsigned int settling_time_up; unsigned int settling_time_down; unsigned int enable_time; unsigned int active_discharge; unsigned int always_on: 1; unsigned int boot_on: 1; unsigned int apply_uV: 1; unsigned int ramp_disable: 1; unsigned int soft_start: 1; unsigned int pull_down: 1; unsigned int over_current_protection: 1; unsigned int over_current_detection: 1; unsigned int over_voltage_detection: 1; unsigned int under_voltage_detection: 1; unsigned int over_temp_detection: 1; }; struct regulator_consumer_supply; struct regulator_init_data { const char *supply_regulator; struct regulation_constraints constraints; int num_consumer_supplies; struct regulator_consumer_supply *consumer_supplies; int (*regulator_init)(void *); void *driver_data; }; enum palmas_usb_state { PALMAS_USB_STATE_DISCONNECT = 0, PALMAS_USB_STATE_VBUS = 1, PALMAS_USB_STATE_ID = 2, }; struct regmap_irq_chip_data; struct palmas_gpadc; struct palmas_pmic_driver_data; struct palmas_pmic; struct palmas_resource; struct palmas_usb; struct palmas { struct device *dev; struct i2c_client *i2c_clients[3]; struct regmap *regmap[3]; int id; unsigned int features; int irq; u32 irq_mask; struct mutex irq_lock; struct regmap_irq_chip_data *irq_data; struct palmas_pmic_driver_data *pmic_ddata; struct palmas_pmic *pmic; struct palmas_gpadc *gpadc; struct palmas_resource *resource; struct palmas_usb *usb; u8 gpio_muxed; u8 led_muxed; u8 pwm_muxed; }; struct of_regulator_match; struct palmas_regs_info; struct palmas_sleep_requestor_info; struct palmas_pmic_platform_data; struct palmas_pmic_driver_data { int smps_start; int smps_end; int ldo_begin; int ldo_end; int max_reg; bool has_regen3; struct palmas_regs_info *palmas_regs_info; struct of_regulator_match *palmas_matches; struct palmas_sleep_requestor_info *sleep_req_info; int (*smps_register)(struct palmas_pmic *, struct palmas_pmic_driver_data *, struct palmas_pmic_platform_data *, const char *, struct regulator_config); int (*ldo_register)(struct palmas_pmic *, struct palmas_pmic_driver_data *, struct palmas_pmic_platform_data *, const char *, struct regulator_config); }; struct palmas_pmic { struct palmas *palmas; struct device *dev; struct regulator_desc desc[27]; struct mutex mutex; int smps123; int smps457; int smps12; int range[10]; unsigned int ramp_delay[10]; unsigned int current_reg_mode[10]; }; struct palmas_resource { struct palmas *palmas; struct device *dev; }; struct palmas_usb { struct palmas *palmas; struct device *dev; struct extcon_dev *edev; int id_otg_irq; int id_irq; int vbus_otg_irq; int vbus_irq; int gpio_id_irq; int gpio_vbus_irq; struct gpio_desc *id_gpiod; struct gpio_desc *vbus_gpiod; long unsigned int sw_debounce_jiffies; struct delayed_work wq_detectid; enum palmas_usb_state linkstat; int wakeup; bool enable_vbus_detection; bool enable_id_detection; bool enable_gpio_id_detection; bool enable_gpio_vbus_detection; }; struct palmas_sleep_requestor_info { int id; int reg_offset; int bit_pos; }; struct palmas_regs_info { char *name; char *sname; u8 vsel_addr; u8 ctrl_addr; u8 tstep_addr; int sleep_id; }; struct palmas_reg_init; struct palmas_pmic_platform_data { struct regulator_init_data *reg_data[27]; struct palmas_reg_init *reg_init[27]; int ldo6_vibrator; bool enable_ldo8_tracking; }; struct palmas_adc_wakeup_property { int adc_channel_number; int adc_high_threshold; int adc_low_threshold; }; struct palmas_gpadc_platform_data { int ch3_current; int ch0_current; bool extended_delay; int bat_removal; int start_polarity; int auto_conversion_period_ms; struct palmas_adc_wakeup_property *adc_wakeup1_data; struct palmas_adc_wakeup_property *adc_wakeup2_data; }; struct palmas_reg_init { int warm_reset; int roof_floor; int mode_sleep; u8 vsel; }; enum palmas_regulators { PALMAS_REG_SMPS12 = 0, PALMAS_REG_SMPS123 = 1, PALMAS_REG_SMPS3 = 2, PALMAS_REG_SMPS45 = 3, PALMAS_REG_SMPS457 = 4, PALMAS_REG_SMPS6 = 5, PALMAS_REG_SMPS7 = 6, PALMAS_REG_SMPS8 = 7, PALMAS_REG_SMPS9 = 8, PALMAS_REG_SMPS10_OUT2 = 9, PALMAS_REG_SMPS10_OUT1 = 10, PALMAS_REG_LDO1 = 11, PALMAS_REG_LDO2 = 12, PALMAS_REG_LDO3 = 13, PALMAS_REG_LDO4 = 14, PALMAS_REG_LDO5 = 15, PALMAS_REG_LDO6 = 16, PALMAS_REG_LDO7 = 17, PALMAS_REG_LDO8 = 18, PALMAS_REG_LDO9 = 19, PALMAS_REG_LDOLN = 20, PALMAS_REG_LDOUSB = 21, PALMAS_REG_REGEN1 = 22, PALMAS_REG_REGEN2 = 23, PALMAS_REG_REGEN3 = 24, PALMAS_REG_SYSEN1 = 25, PALMAS_REG_SYSEN2 = 26, PALMAS_NUM_REGS = 27, }; struct palmas_usb_platform_data { int wakeup; }; struct palmas_resource_platform_data { int regen1_mode_sleep; int regen2_mode_sleep; int sysen1_mode_sleep; int sysen2_mode_sleep; u8 nsleep_res; u8 nsleep_smps; u8 nsleep_ldo1; u8 nsleep_ldo2; u8 enable1_res; u8 enable1_smps; u8 enable1_ldo1; u8 enable1_ldo2; u8 enable2_res; u8 enable2_smps; u8 enable2_ldo1; u8 enable2_ldo2; }; struct palmas_clk_platform_data { int clk32kg_mode_sleep; int clk32kgaudio_mode_sleep; }; struct palmas_platform_data { int irq_flags; int gpio_base; u8 power_ctrl; int mux_from_pdata; u8 pad1; u8 pad2; bool pm_off; struct palmas_pmic_platform_data *pmic_pdata; struct palmas_gpadc_platform_data *gpadc_pdata; struct palmas_usb_platform_data *usb_pdata; struct palmas_resource_platform_data *resource_pdata; struct palmas_clk_platform_data *clk_pdata; }; enum palmas_irqs { PALMAS_CHARG_DET_N_VBUS_OVV_IRQ = 0, PALMAS_PWRON_IRQ = 1, PALMAS_LONG_PRESS_KEY_IRQ = 2, PALMAS_RPWRON_IRQ = 3, PALMAS_PWRDOWN_IRQ = 4, PALMAS_HOTDIE_IRQ = 5, PALMAS_VSYS_MON_IRQ = 6, PALMAS_VBAT_MON_IRQ = 7, PALMAS_RTC_ALARM_IRQ = 8, PALMAS_RTC_TIMER_IRQ = 9, PALMAS_WDT_IRQ = 10, PALMAS_BATREMOVAL_IRQ = 11, PALMAS_RESET_IN_IRQ = 12, PALMAS_FBI_BB_IRQ = 13, PALMAS_SHORT_IRQ = 14, PALMAS_VAC_ACOK_IRQ = 15, PALMAS_GPADC_AUTO_0_IRQ = 16, PALMAS_GPADC_AUTO_1_IRQ = 17, PALMAS_GPADC_EOC_SW_IRQ = 18, PALMAS_GPADC_EOC_RT_IRQ = 19, PALMAS_ID_OTG_IRQ = 20, PALMAS_ID_IRQ = 21, PALMAS_VBUS_OTG_IRQ = 22, PALMAS_VBUS_IRQ = 23, PALMAS_GPIO_0_IRQ = 24, PALMAS_GPIO_1_IRQ = 25, PALMAS_GPIO_2_IRQ = 26, PALMAS_GPIO_3_IRQ = 27, PALMAS_GPIO_4_IRQ = 28, PALMAS_GPIO_5_IRQ = 29, PALMAS_GPIO_6_IRQ = 30, PALMAS_GPIO_7_IRQ = 31, PALMAS_NUM_IRQ = 32, }; struct palmas_gpio { struct gpio_chip gpio_chip; struct palmas *palmas; }; struct palmas_device_data { int ngpio; }; enum { RC5T583_IRQ_ONKEY = 0, RC5T583_IRQ_ACOK = 1, RC5T583_IRQ_LIDOPEN = 2, RC5T583_IRQ_PREOT = 3, RC5T583_IRQ_CLKSTP = 4, RC5T583_IRQ_ONKEY_OFF = 5, RC5T583_IRQ_WD = 6, RC5T583_IRQ_EN_PWRREQ1 = 7, RC5T583_IRQ_EN_PWRREQ2 = 8, RC5T583_IRQ_PRE_VINDET = 9, RC5T583_IRQ_DC0LIM = 10, RC5T583_IRQ_DC1LIM = 11, RC5T583_IRQ_DC2LIM = 12, RC5T583_IRQ_DC3LIM = 13, RC5T583_IRQ_CTC = 14, RC5T583_IRQ_YALE = 15, RC5T583_IRQ_DALE = 16, RC5T583_IRQ_WALE = 17, RC5T583_IRQ_AIN1L = 18, RC5T583_IRQ_AIN2L = 19, RC5T583_IRQ_AIN3L = 20, RC5T583_IRQ_VBATL = 21, RC5T583_IRQ_VIN3L = 22, RC5T583_IRQ_VIN8L = 23, RC5T583_IRQ_AIN1H = 24, RC5T583_IRQ_AIN2H = 25, RC5T583_IRQ_AIN3H = 26, RC5T583_IRQ_VBATH = 27, RC5T583_IRQ_VIN3H = 28, RC5T583_IRQ_VIN8H = 29, RC5T583_IRQ_ADCEND = 30, RC5T583_IRQ_GPIO0 = 31, RC5T583_IRQ_GPIO1 = 32, RC5T583_IRQ_GPIO2 = 33, RC5T583_IRQ_GPIO3 = 34, RC5T583_IRQ_GPIO4 = 35, RC5T583_IRQ_GPIO5 = 36, RC5T583_IRQ_GPIO6 = 37, RC5T583_IRQ_GPIO7 = 38, RC5T583_MAX_IRQS = 39, }; enum { RC5T583_GPIO0 = 0, RC5T583_GPIO1 = 1, RC5T583_GPIO2 = 2, RC5T583_GPIO3 = 3, RC5T583_GPIO4 = 4, RC5T583_GPIO5 = 5, RC5T583_GPIO6 = 6, RC5T583_GPIO7 = 7, RC5T583_MAX_GPIO = 8, }; enum { RC5T583_REGULATOR_DC0 = 0, RC5T583_REGULATOR_DC1 = 1, RC5T583_REGULATOR_DC2 = 2, RC5T583_REGULATOR_DC3 = 3, RC5T583_REGULATOR_LDO0 = 4, RC5T583_REGULATOR_LDO1 = 5, RC5T583_REGULATOR_LDO2 = 6, RC5T583_REGULATOR_LDO3 = 7, RC5T583_REGULATOR_LDO4 = 8, RC5T583_REGULATOR_LDO5 = 9, RC5T583_REGULATOR_LDO6 = 10, RC5T583_REGULATOR_LDO7 = 11, RC5T583_REGULATOR_LDO8 = 12, RC5T583_REGULATOR_LDO9 = 13, RC5T583_REGULATOR_MAX = 14, }; struct rc5t583 { struct device *dev; struct regmap *regmap; int chip_irq; int irq_base; struct mutex irq_lock; long unsigned int group_irq_en[5]; uint8_t intc_inten_reg; uint8_t irq_en_reg[8]; uint8_t gpedge_reg[2]; }; struct rc5t583_platform_data { int irq_base; int gpio_base; bool enable_shutdown; int regulator_deepsleep_slot[14]; long unsigned int regulator_ext_pwr_control[14]; struct regulator_init_data *reg_init_data[14]; }; struct rc5t583_gpio { struct gpio_chip gpio_chip; struct rc5t583 *rc5t583; }; enum { TPS6586X_ID_SYS = 0, TPS6586X_ID_SM_0 = 1, TPS6586X_ID_SM_1 = 2, TPS6586X_ID_SM_2 = 3, TPS6586X_ID_LDO_0 = 4, TPS6586X_ID_LDO_1 = 5, TPS6586X_ID_LDO_2 = 6, TPS6586X_ID_LDO_3 = 7, TPS6586X_ID_LDO_4 = 8, TPS6586X_ID_LDO_5 = 9, TPS6586X_ID_LDO_6 = 10, TPS6586X_ID_LDO_7 = 11, TPS6586X_ID_LDO_8 = 12, TPS6586X_ID_LDO_9 = 13, TPS6586X_ID_LDO_RTC = 14, TPS6586X_ID_MAX_REGULATOR = 15, }; enum { TPS6586X_INT_PLDO_0 = 0, TPS6586X_INT_PLDO_1 = 1, TPS6586X_INT_PLDO_2 = 2, TPS6586X_INT_PLDO_3 = 3, TPS6586X_INT_PLDO_4 = 4, TPS6586X_INT_PLDO_5 = 5, TPS6586X_INT_PLDO_6 = 6, TPS6586X_INT_PLDO_7 = 7, TPS6586X_INT_COMP_DET = 8, TPS6586X_INT_ADC = 9, TPS6586X_INT_PLDO_8 = 10, TPS6586X_INT_PLDO_9 = 11, TPS6586X_INT_PSM_0 = 12, TPS6586X_INT_PSM_1 = 13, TPS6586X_INT_PSM_2 = 14, TPS6586X_INT_PSM_3 = 15, TPS6586X_INT_RTC_ALM1 = 16, TPS6586X_INT_ACUSB_OVP = 17, TPS6586X_INT_USB_DET = 18, TPS6586X_INT_AC_DET = 19, TPS6586X_INT_BAT_DET = 20, TPS6586X_INT_CHG_STAT = 21, TPS6586X_INT_CHG_TEMP = 22, TPS6586X_INT_PP = 23, TPS6586X_INT_RESUME = 24, TPS6586X_INT_LOW_SYS = 25, TPS6586X_INT_RTC_ALM2 = 26, }; struct tps6586x_subdev_info { int id; const char *name; void *platform_data; struct device_node *of_node; }; struct tps6586x_platform_data { int num_subdevs; struct tps6586x_subdev_info *subdevs; int gpio_base; int irq_base; bool pm_off; struct regulator_init_data *reg_init_data[15]; }; struct tps6586x_gpio { struct gpio_chip gpio_chip; struct device *parent; }; struct tps65910_sleep_keepon_data { unsigned int therm_keepon: 1; unsigned int clkout32k_keepon: 1; unsigned int i2chs_keepon: 1; }; struct tps65910_board { int gpio_base; int irq; int irq_base; int vmbch_threshold; int vmbch2_threshold; bool en_ck32k_xtal; bool en_dev_slp; bool pm_off; struct tps65910_sleep_keepon_data slp_keepon; bool en_gpio_sleep[9]; long unsigned int regulator_ext_sleep_control[14]; struct regulator_init_data *tps65910_pmic_init_data[14]; }; struct tps65910 { struct device *dev; struct i2c_client *i2c_client; struct regmap *regmap; long unsigned int id; struct tps65910_board *of_plat_data; int chip_irq; struct regmap_irq_chip_data *irq_data; }; struct tps65910_gpio { struct gpio_chip gpio_chip; struct tps65910 *tps65910; }; struct tps68470_gpio_data { struct regmap *tps68470_regmap; struct gpio_chip gc; }; enum pwm_polarity { PWM_POLARITY_NORMAL = 0, PWM_POLARITY_INVERSED = 1, }; struct pwm_args { u64 period; enum pwm_polarity polarity; }; enum { PWMF_REQUESTED = 1, PWMF_EXPORTED = 2, }; struct pwm_state { u64 period; u64 duty_cycle; enum pwm_polarity polarity; bool enabled; bool usage_power; }; struct pwm_chip; struct pwm_device { const char *label; long unsigned int flags; unsigned int hwpwm; unsigned int pwm; struct pwm_chip *chip; void *chip_data; struct pwm_args args; struct pwm_state state; struct pwm_state last; }; struct pwm_ops; struct pwm_chip { struct device *dev; const struct pwm_ops *ops; int base; unsigned int npwm; struct pwm_device * (*of_xlate)(struct pwm_chip *, const struct of_phandle_args *); unsigned int of_pwm_n_cells; struct list_head list; struct pwm_device *pwms; }; struct pwm_capture; struct pwm_ops { int (*request)(struct pwm_chip *, struct pwm_device *); void (*free)(struct pwm_chip *, struct pwm_device *); int (*capture)(struct pwm_chip *, struct pwm_device *, struct pwm_capture *, long unsigned int); int (*apply)(struct pwm_chip *, struct pwm_device *, const struct pwm_state *); void (*get_state)(struct pwm_chip *, struct pwm_device *, struct pwm_state *); struct module *owner; int (*config)(struct pwm_chip *, struct pwm_device *, int, int); int (*set_polarity)(struct pwm_chip *, struct pwm_device *, enum pwm_polarity); int (*enable)(struct pwm_chip *, struct pwm_device *); void (*disable)(struct pwm_chip *, struct pwm_device *); }; struct pwm_capture { unsigned int period; unsigned int duty_cycle; }; struct pwm_lookup { struct list_head list; const char *provider; unsigned int index; const char *dev_id; const char *con_id; unsigned int period; enum pwm_polarity polarity; const char *module; }; struct trace_event_raw_pwm { struct trace_entry ent; struct pwm_device *pwm; u64 period; u64 duty_cycle; enum pwm_polarity polarity; bool enabled; char __data[0]; }; struct trace_event_data_offsets_pwm {}; typedef void (*btf_trace_pwm_apply)(void *, struct pwm_device *, const struct pwm_state *); typedef void (*btf_trace_pwm_get)(void *, struct pwm_device *, const struct pwm_state *); struct pwm_export { struct device child; struct pwm_device *pwm; struct mutex lock; struct pwm_state suspend; }; struct intel_scu_ipc_dev; struct intel_soc_pmic { int irq; struct regmap *regmap; struct regmap_irq_chip_data *irq_chip_data; struct regmap_irq_chip_data *irq_chip_data_pwrbtn; struct regmap_irq_chip_data *irq_chip_data_tmu; struct regmap_irq_chip_data *irq_chip_data_bcu; struct regmap_irq_chip_data *irq_chip_data_adc; struct regmap_irq_chip_data *irq_chip_data_chgr; struct regmap_irq_chip_data *irq_chip_data_crit; struct device *dev; struct intel_scu_ipc_dev *scu; }; struct crystalcove_pwm { struct pwm_chip chip; struct regmap *regmap; }; enum { pci_channel_io_normal = 1, pci_channel_io_frozen = 2, pci_channel_io_perm_failure = 3, }; struct pci_sriov { int pos; int nres; u32 cap; u16 ctrl; u16 total_VFs; u16 initial_VFs; u16 num_VFs; u16 offset; u16 stride; u16 vf_device; u32 pgsz; u8 link; u8 max_VF_buses; u16 driver_max_VFs; struct pci_dev *dev; struct pci_dev *self; u32 class; u8 hdr_type; u16 subsystem_vendor; u16 subsystem_device; resource_size_t barsz[6]; bool drivers_autoprobe; }; struct rcec_ea { u8 nextbusn; u8 lastbusn; u32 bitmap; }; struct pci_bus_resource { struct list_head list; struct resource *res; unsigned int flags; }; typedef u64 pci_bus_addr_t; struct pci_bus_region { pci_bus_addr_t start; pci_bus_addr_t end; }; enum pci_fixup_pass { pci_fixup_early = 0, pci_fixup_header = 1, pci_fixup_final = 2, pci_fixup_enable = 3, pci_fixup_resume = 4, pci_fixup_suspend = 5, pci_fixup_resume_early = 6, pci_fixup_suspend_late = 7, }; struct hotplug_slot_ops; struct hotplug_slot { const struct hotplug_slot_ops *ops; struct list_head slot_list; struct pci_slot *pci_slot; struct module *owner; const char *mod_name; }; enum pci_dev_flags { PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = 1, PCI_DEV_FLAGS_NO_D3 = 2, PCI_DEV_FLAGS_ASSIGNED = 4, PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = 8, PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS = 32, PCI_DEV_FLAGS_NO_BUS_RESET = 64, PCI_DEV_FLAGS_NO_PM_RESET = 128, PCI_DEV_FLAGS_VPD_REF_F0 = 256, PCI_DEV_FLAGS_BRIDGE_XLATE_ROOT = 512, PCI_DEV_FLAGS_NO_FLR_RESET = 1024, PCI_DEV_FLAGS_NO_RELAXED_ORDERING = 2048, }; enum pci_bus_flags { PCI_BUS_FLAGS_NO_MSI = 1, PCI_BUS_FLAGS_NO_MMRBC = 2, PCI_BUS_FLAGS_NO_AERSID = 4, PCI_BUS_FLAGS_NO_EXTCFG = 8, }; enum pci_bus_speed { PCI_SPEED_33MHz = 0, PCI_SPEED_66MHz = 1, PCI_SPEED_66MHz_PCIX = 2, PCI_SPEED_100MHz_PCIX = 3, PCI_SPEED_133MHz_PCIX = 4, PCI_SPEED_66MHz_PCIX_ECC = 5, PCI_SPEED_100MHz_PCIX_ECC = 6, PCI_SPEED_133MHz_PCIX_ECC = 7, PCI_SPEED_66MHz_PCIX_266 = 9, PCI_SPEED_100MHz_PCIX_266 = 10, PCI_SPEED_133MHz_PCIX_266 = 11, AGP_UNKNOWN = 12, AGP_1X = 13, AGP_2X = 14, AGP_4X = 15, AGP_8X = 16, PCI_SPEED_66MHz_PCIX_533 = 17, PCI_SPEED_100MHz_PCIX_533 = 18, PCI_SPEED_133MHz_PCIX_533 = 19, PCIE_SPEED_2_5GT = 20, PCIE_SPEED_5_0GT = 21, PCIE_SPEED_8_0GT = 22, PCIE_SPEED_16_0GT = 23, PCIE_SPEED_32_0GT = 24, PCIE_SPEED_64_0GT = 25, PCI_SPEED_UNKNOWN = 255, }; struct pci_host_bridge { struct device dev; struct pci_bus *bus; struct pci_ops *ops; struct pci_ops *child_ops; void *sysdata; int busnr; struct list_head windows; struct list_head dma_ranges; u8 (*swizzle_irq)(struct pci_dev *, u8 *); int (*map_irq)(const struct pci_dev *, u8, u8); void (*release_fn)(struct pci_host_bridge *); void *release_data; unsigned int ignore_reset_delay: 1; unsigned int no_ext_tags: 1; unsigned int native_aer: 1; unsigned int native_pcie_hotplug: 1; unsigned int native_shpc_hotplug: 1; unsigned int native_pme: 1; unsigned int native_ltr: 1; unsigned int native_dpc: 1; unsigned int preserve_config: 1; unsigned int size_windows: 1; unsigned int msi_domain: 1; resource_size_t (*align_resource)(struct pci_dev *, const struct resource *, resource_size_t, resource_size_t, resource_size_t); long: 64; long: 64; long: 64; long: 64; long unsigned int private[0]; }; enum { PCI_REASSIGN_ALL_RSRC = 1, PCI_REASSIGN_ALL_BUS = 2, PCI_PROBE_ONLY = 4, PCI_CAN_SKIP_ISA_ALIGN = 8, PCI_ENABLE_PROC_DOMAINS = 16, PCI_COMPAT_DOMAIN_0 = 32, PCI_SCAN_ALL_PCIE_DEVS = 64, }; enum pcie_bus_config_types { PCIE_BUS_TUNE_OFF = 0, PCIE_BUS_DEFAULT = 1, PCIE_BUS_SAFE = 2, PCIE_BUS_PERFORMANCE = 3, PCIE_BUS_PEER2PEER = 4, }; struct hotplug_slot_ops { int (*enable_slot)(struct hotplug_slot *); int (*disable_slot)(struct hotplug_slot *); int (*set_attention_status)(struct hotplug_slot *, u8); int (*hardware_test)(struct hotplug_slot *, u32); int (*get_power_status)(struct hotplug_slot *, u8 *); int (*get_attention_status)(struct hotplug_slot *, u8 *); int (*get_latch_status)(struct hotplug_slot *, u8 *); int (*get_adapter_status)(struct hotplug_slot *, u8 *); int (*reset_slot)(struct hotplug_slot *, int); }; enum pci_bar_type { pci_bar_unknown = 0, pci_bar_io = 1, pci_bar_mem32 = 2, pci_bar_mem64 = 3, }; struct pci_domain_busn_res { struct list_head list; struct resource res; int domain_nr; }; struct bus_attribute { struct attribute attr; ssize_t (*show)(struct bus_type *, char *); ssize_t (*store)(struct bus_type *, const char *, size_t); }; enum pcie_reset_state { pcie_deassert_reset = 1, pcie_warm_reset = 2, pcie_hot_reset = 3, }; enum pcie_link_width { PCIE_LNK_WIDTH_RESRV = 0, PCIE_LNK_X1 = 1, PCIE_LNK_X2 = 2, PCIE_LNK_X4 = 4, PCIE_LNK_X8 = 8, PCIE_LNK_X12 = 12, PCIE_LNK_X16 = 16, PCIE_LNK_X32 = 32, PCIE_LNK_WIDTH_UNKNOWN = 255, }; struct pci_cap_saved_data { u16 cap_nr; bool cap_extended; unsigned int size; u32 data[0]; }; struct pci_cap_saved_state { struct hlist_node next; struct pci_cap_saved_data cap; }; typedef int (*arch_set_vga_state_t)(struct pci_dev *, bool, unsigned int, u32); struct pci_platform_pm_ops { bool (*bridge_d3)(struct pci_dev *); bool (*is_manageable)(struct pci_dev *); int (*set_state)(struct pci_dev *, pci_power_t); pci_power_t (*get_state)(struct pci_dev *); void (*refresh_state)(struct pci_dev *); pci_power_t (*choose_state)(struct pci_dev *); int (*set_wakeup)(struct pci_dev *, bool); bool (*need_resume)(struct pci_dev *); }; struct pci_pme_device { struct list_head list; struct pci_dev *dev; }; struct pci_saved_state { u32 config_space[16]; struct pci_cap_saved_data cap[0]; }; struct pci_devres { unsigned int enabled: 1; unsigned int pinned: 1; unsigned int orig_intx: 1; unsigned int restore_intx: 1; unsigned int mwi: 1; u32 region_mask; }; struct driver_attribute { struct attribute attr; ssize_t (*show)(struct device_driver *, char *); ssize_t (*store)(struct device_driver *, const char *, size_t); }; enum pci_ers_result { PCI_ERS_RESULT_NONE = 1, PCI_ERS_RESULT_CAN_RECOVER = 2, PCI_ERS_RESULT_NEED_RESET = 3, PCI_ERS_RESULT_DISCONNECT = 4, PCI_ERS_RESULT_RECOVERED = 5, PCI_ERS_RESULT_NO_AER_DRIVER = 6, }; enum dev_dma_attr { DEV_DMA_NOT_SUPPORTED = 0, DEV_DMA_NON_COHERENT = 1, DEV_DMA_COHERENT = 2, }; struct pcie_device { int irq; struct pci_dev *port; u32 service; void *priv_data; struct device device; }; struct pcie_port_service_driver { const char *name; int (*probe)(struct pcie_device *); void (*remove)(struct pcie_device *); int (*suspend)(struct pcie_device *); int (*resume_noirq)(struct pcie_device *); int (*resume)(struct pcie_device *); int (*runtime_suspend)(struct pcie_device *); int (*runtime_resume)(struct pcie_device *); void (*error_resume)(struct pci_dev *); int port_type; u32 service; struct device_driver driver; }; struct pci_dynid { struct list_head node; struct pci_device_id id; }; struct drv_dev_and_id { struct pci_driver *drv; struct pci_dev *dev; const struct pci_device_id *id; }; enum pci_mmap_state { pci_mmap_io = 0, pci_mmap_mem = 1, }; enum pci_mmap_api { PCI_MMAP_SYSFS = 0, PCI_MMAP_PROCFS = 1, }; struct pci_vpd_ops; struct pci_vpd { const struct pci_vpd_ops *ops; struct mutex lock; unsigned int len; u16 flag; u8 cap; unsigned int busy: 1; unsigned int valid: 1; }; struct pci_vpd_ops { ssize_t (*read)(struct pci_dev *, loff_t, size_t, void *); ssize_t (*write)(struct pci_dev *, loff_t, size_t, const void *); }; struct pci_dev_resource { struct list_head list; struct resource *res; struct pci_dev *dev; resource_size_t start; resource_size_t end; resource_size_t add_size; resource_size_t min_align; long unsigned int flags; }; enum release_type { leaf_only = 0, whole_subtree = 1, }; enum enable_type { undefined = 4294967295, user_disabled = 0, auto_disabled = 1, user_enabled = 2, auto_enabled = 3, }; struct msix_entry { u32 vector; u16 entry; }; struct portdrv_service_data { struct pcie_port_service_driver *drv; struct device *dev; u32 service; }; typedef int (*pcie_pm_callback_t)(struct pcie_device *); struct walk_rcec_data { struct pci_dev *rcec; int (*user_callback)(struct pci_dev *, void *); void *user_data; }; struct aspm_latency { u32 l0s; u32 l1; }; struct pcie_link_state { struct pci_dev *pdev; struct pci_dev *downstream; struct pcie_link_state *root; struct pcie_link_state *parent; struct list_head sibling; u32 aspm_support: 7; u32 aspm_enabled: 7; u32 aspm_capable: 7; u32 aspm_default: 7; char: 4; u32 aspm_disable: 7; u32 clkpm_capable: 1; u32 clkpm_enabled: 1; u32 clkpm_default: 1; u32 clkpm_disable: 1; struct aspm_latency latency_up; struct aspm_latency latency_dw; struct aspm_latency acceptable[8]; }; struct aer_stats { u64 dev_cor_errs[16]; u64 dev_fatal_errs[27]; u64 dev_nonfatal_errs[27]; u64 dev_total_cor_errs; u64 dev_total_fatal_errs; u64 dev_total_nonfatal_errs; u64 rootport_total_cor_errs; u64 rootport_total_fatal_errs; u64 rootport_total_nonfatal_errs; }; struct aer_header_log_regs { unsigned int dw0; unsigned int dw1; unsigned int dw2; unsigned int dw3; }; struct aer_capability_regs { u32 header; u32 uncor_status; u32 uncor_mask; u32 uncor_severity; u32 cor_status; u32 cor_mask; u32 cap_control; struct aer_header_log_regs header_log; u32 root_command; u32 root_status; u16 cor_err_source; u16 uncor_err_source; }; struct aer_err_info { struct pci_dev *dev[5]; int error_dev_num; unsigned int id: 16; unsigned int severity: 2; unsigned int __pad1: 5; unsigned int multi_error_valid: 1; unsigned int first_error: 5; unsigned int __pad2: 2; unsigned int tlp_header_valid: 1; unsigned int status; unsigned int mask; struct aer_header_log_regs tlp; }; struct aer_err_source { unsigned int status; unsigned int id; }; struct aer_rpc { struct pci_dev *rpd; struct { union { struct __kfifo kfifo; struct aer_err_source *type; const struct aer_err_source *const_type; char (*rectype)[0]; struct aer_err_source *ptr; const struct aer_err_source *ptr_const; }; struct aer_err_source buf[128]; } aer_fifo; }; struct aer_recover_entry { u8 bus; u8 devfn; u16 domain; int severity; struct aer_capability_regs *regs; }; struct pcie_pme_service_data { spinlock_t lock; struct pcie_device *srv; struct work_struct work; bool noirq; }; typedef void (*acpi_notify_handler)(acpi_handle, u32, void *); struct pci_filp_private { enum pci_mmap_state mmap_state; int write_combine; }; struct pci_slot_attribute { struct attribute attr; ssize_t (*show)(struct pci_slot *, char *); ssize_t (*store)(struct pci_slot *, const char *, size_t); }; struct acpi_bus_type { struct list_head list; const char *name; bool (*match)(struct device *); struct acpi_device * (*find_companion)(struct device *); void (*setup)(struct device *); void (*cleanup)(struct device *); }; struct acpi_pci_root { struct acpi_device *device; struct pci_bus *bus; u16 segment; struct resource secondary; u32 osc_support_set; u32 osc_control_set; phys_addr_t mcfg_addr; }; enum pm_qos_flags_status { PM_QOS_FLAGS_UNDEFINED = 4294967295, PM_QOS_FLAGS_NONE = 0, PM_QOS_FLAGS_SOME = 1, PM_QOS_FLAGS_ALL = 2, }; struct hpx_type0 { u32 revision; u8 cache_line_size; u8 latency_timer; u8 enable_serr; u8 enable_perr; }; struct hpx_type1 { u32 revision; u8 max_mem_read; u8 avg_max_split; u16 tot_max_split; }; struct hpx_type2 { u32 revision; u32 unc_err_mask_and; u32 unc_err_mask_or; u32 unc_err_sever_and; u32 unc_err_sever_or; u32 cor_err_mask_and; u32 cor_err_mask_or; u32 adv_err_cap_and; u32 adv_err_cap_or; u16 pci_exp_devctl_and; u16 pci_exp_devctl_or; u16 pci_exp_lnkctl_and; u16 pci_exp_lnkctl_or; u32 sec_unc_err_sever_and; u32 sec_unc_err_sever_or; u32 sec_unc_err_mask_and; u32 sec_unc_err_mask_or; }; struct hpx_type3 { u16 device_type; u16 function_type; u16 config_space_location; u16 pci_exp_cap_id; u16 pci_exp_cap_ver; u16 pci_exp_vendor_id; u16 dvsec_id; u16 dvsec_rev; u16 match_offset; u32 match_mask_and; u32 match_value; u16 reg_offset; u32 reg_mask_and; u32 reg_mask_or; }; enum hpx_type3_dev_type { HPX_TYPE_ENDPOINT = 1, HPX_TYPE_LEG_END = 2, HPX_TYPE_RC_END = 4, HPX_TYPE_RC_EC = 8, HPX_TYPE_ROOT_PORT = 16, HPX_TYPE_UPSTREAM = 32, HPX_TYPE_DOWNSTREAM = 64, HPX_TYPE_PCI_BRIDGE = 128, HPX_TYPE_PCIE_BRIDGE = 256, }; enum hpx_type3_fn_type { HPX_FN_NORMAL = 1, HPX_FN_SRIOV_PHYS = 2, HPX_FN_SRIOV_VIRT = 4, }; enum hpx_type3_cfg_loc { HPX_CFG_PCICFG = 0, HPX_CFG_PCIE_CAP = 1, HPX_CFG_PCIE_CAP_EXT = 2, HPX_CFG_VEND_CAP = 3, HPX_CFG_DVSEC = 4, HPX_CFG_MAX = 5, }; enum pci_irq_reroute_variant { INTEL_IRQ_REROUTE_VARIANT = 1, MAX_IRQ_REROUTE_VARIANTS = 3, }; struct pci_fixup { u16 vendor; u16 device; u32 class; unsigned int class_shift; int hook_offset; }; enum { NVME_REG_CAP = 0, NVME_REG_VS = 8, NVME_REG_INTMS = 12, NVME_REG_INTMC = 16, NVME_REG_CC = 20, NVME_REG_CSTS = 28, NVME_REG_NSSR = 32, NVME_REG_AQA = 36, NVME_REG_ASQ = 40, NVME_REG_ACQ = 48, NVME_REG_CMBLOC = 56, NVME_REG_CMBSZ = 60, NVME_REG_BPINFO = 64, NVME_REG_BPRSEL = 68, NVME_REG_BPMBL = 72, NVME_REG_CMBMSC = 80, NVME_REG_PMRCAP = 3584, NVME_REG_PMRCTL = 3588, NVME_REG_PMRSTS = 3592, NVME_REG_PMREBS = 3596, NVME_REG_PMRSWTP = 3600, NVME_REG_DBS = 4096, }; enum { NVME_CC_ENABLE = 1, NVME_CC_EN_SHIFT = 0, NVME_CC_CSS_SHIFT = 4, NVME_CC_MPS_SHIFT = 7, NVME_CC_AMS_SHIFT = 11, NVME_CC_SHN_SHIFT = 14, NVME_CC_IOSQES_SHIFT = 16, NVME_CC_IOCQES_SHIFT = 20, NVME_CC_CSS_NVM = 0, NVME_CC_CSS_CSI = 96, NVME_CC_CSS_MASK = 112, NVME_CC_AMS_RR = 0, NVME_CC_AMS_WRRU = 2048, NVME_CC_AMS_VS = 14336, NVME_CC_SHN_NONE = 0, NVME_CC_SHN_NORMAL = 16384, NVME_CC_SHN_ABRUPT = 32768, NVME_CC_SHN_MASK = 49152, NVME_CC_IOSQES = 393216, NVME_CC_IOCQES = 4194304, NVME_CAP_CSS_NVM = 1, NVME_CAP_CSS_CSI = 64, NVME_CSTS_RDY = 1, NVME_CSTS_CFS = 2, NVME_CSTS_NSSRO = 16, NVME_CSTS_PP = 32, NVME_CSTS_SHST_NORMAL = 0, NVME_CSTS_SHST_OCCUR = 4, NVME_CSTS_SHST_CMPLT = 8, NVME_CSTS_SHST_MASK = 12, NVME_CMBMSC_CRE = 1, NVME_CMBMSC_CMSE = 2, }; enum { NVME_AEN_BIT_NS_ATTR = 8, NVME_AEN_BIT_FW_ACT = 9, NVME_AEN_BIT_ANA_CHANGE = 11, NVME_AEN_BIT_DISC_CHANGE = 31, }; enum { SWITCHTEC_GAS_MRPC_OFFSET = 0, SWITCHTEC_GAS_TOP_CFG_OFFSET = 4096, SWITCHTEC_GAS_SW_EVENT_OFFSET = 6144, SWITCHTEC_GAS_SYS_INFO_OFFSET = 8192, SWITCHTEC_GAS_FLASH_INFO_OFFSET = 8704, SWITCHTEC_GAS_PART_CFG_OFFSET = 16384, SWITCHTEC_GAS_NTB_OFFSET = 65536, SWITCHTEC_GAS_PFF_CSR_OFFSET = 1261568, }; enum { SWITCHTEC_NTB_REG_INFO_OFFSET = 0, SWITCHTEC_NTB_REG_CTRL_OFFSET = 16384, SWITCHTEC_NTB_REG_DBMSG_OFFSET = 409600, }; struct nt_partition_info { u32 xlink_enabled; u32 target_part_low; u32 target_part_high; u32 reserved; }; struct ntb_info_regs { u8 partition_count; u8 partition_id; u16 reserved1; u64 ep_map; u16 requester_id; u16 reserved2; u32 reserved3[4]; struct nt_partition_info ntp_info[48]; } __attribute__((packed)); struct ntb_ctrl_regs { u32 partition_status; u32 partition_op; u32 partition_ctrl; u32 bar_setup; u32 bar_error; u16 lut_table_entries; u16 lut_table_offset; u32 lut_error; u16 req_id_table_size; u16 req_id_table_offset; u32 req_id_error; u32 reserved1[7]; struct { u32 ctl; u32 win_size; u64 xlate_addr; } bar_entry[6]; struct { u32 win_size; u32 reserved[3]; } bar_ext_entry[6]; u32 reserved2[192]; u32 req_id_table[512]; u32 reserved3[256]; u64 lut_entry[512]; }; struct pci_dev_reset_methods { u16 vendor; u16 device; int (*reset)(struct pci_dev *, int); }; struct pci_dev_acs_enabled { u16 vendor; u16 device; int (*acs_enabled)(struct pci_dev *, u16); }; struct pci_dev_acs_ops { u16 vendor; u16 device; int (*enable_acs)(struct pci_dev *); int (*disable_acs_redir)(struct pci_dev *); }; struct slot { u8 number; unsigned int devfn; struct pci_bus *bus; struct pci_dev *dev; unsigned int latch_status: 1; unsigned int adapter_status: 1; unsigned int extracting; struct hotplug_slot hotplug_slot; struct list_head slot_list; }; struct cpci_hp_controller_ops { int (*query_enum)(); int (*enable_irq)(); int (*disable_irq)(); int (*check_irq)(void *); int (*hardware_test)(struct slot *, u32); u8 (*get_power)(struct slot *); int (*set_power)(struct slot *, int); }; struct cpci_hp_controller { unsigned int irq; long unsigned int irq_flags; char *devname; void *dev_id; char *name; struct cpci_hp_controller_ops *ops; }; typedef acpi_status (*acpi_walk_callback)(acpi_handle, u32, void *, void **); struct controller { struct pcie_device *pcie; u32 slot_cap; unsigned int inband_presence_disabled: 1; u16 slot_ctrl; struct mutex ctrl_lock; long unsigned int cmd_started; unsigned int cmd_busy: 1; wait_queue_head_t queue; atomic_t pending_events; unsigned int notification_enabled: 1; unsigned int power_fault_detected; struct task_struct *poll_thread; u8 state; struct mutex state_lock; struct delayed_work button_work; struct hotplug_slot hotplug_slot; struct rw_semaphore reset_lock; unsigned int ist_running; int request_result; wait_queue_head_t requester; }; struct controller___2; struct hpc_ops; struct slot___2 { u8 bus; u8 device; u16 status; u32 number; u8 is_a_board; u8 state; u8 attention_save; u8 presence_save; u8 latch_save; u8 pwr_save; struct controller___2 *ctrl; const struct hpc_ops *hpc_ops; struct hotplug_slot hotplug_slot; struct list_head slot_list; struct delayed_work work; struct mutex lock; struct workqueue_struct *wq; u8 hp_slot; }; struct controller___2 { struct mutex crit_sect; struct mutex cmd_lock; int num_slots; int slot_num_inc; struct pci_dev *pci_dev; struct list_head slot_list; const struct hpc_ops *hpc_ops; wait_queue_head_t queue; u8 slot_device_offset; u32 pcix_misc2_reg; u32 first_slot; u32 cap_offset; long unsigned int mmio_base; long unsigned int mmio_size; void *creg; struct timer_list poll_timer; }; struct hpc_ops { int (*power_on_slot)(struct slot___2 *); int (*slot_enable)(struct slot___2 *); int (*slot_disable)(struct slot___2 *); int (*set_bus_speed_mode)(struct slot___2 *, enum pci_bus_speed); int (*get_power_status)(struct slot___2 *, u8 *); int (*get_attention_status)(struct slot___2 *, u8 *); int (*set_attention_status)(struct slot___2 *, u8); int (*get_latch_status)(struct slot___2 *, u8 *); int (*get_adapter_status)(struct slot___2 *, u8 *); int (*get_adapter_speed)(struct slot___2 *, enum pci_bus_speed *); int (*get_mode1_ECC_cap)(struct slot___2 *, u8 *); int (*get_prog_int)(struct slot___2 *, u8 *); int (*query_power_fault)(struct slot___2 *); void (*green_led_on)(struct slot___2 *); void (*green_led_off)(struct slot___2 *); void (*green_led_blink)(struct slot___2 *); void (*release_ctlr)(struct controller___2 *); int (*check_cmd_status)(struct controller___2 *); }; struct event_info { u32 event_type; struct slot___2 *p_slot; struct work_struct work; }; struct pushbutton_work_info { struct slot___2 *p_slot; struct work_struct work; }; enum ctrl_offsets { BASE_OFFSET = 0, SLOT_AVAIL1 = 4, SLOT_AVAIL2 = 8, SLOT_CONFIG = 12, SEC_BUS_CONFIG = 16, MSI_CTRL = 18, PROG_INTERFACE = 19, CMD = 20, CMD_STATUS = 22, INTR_LOC = 24, SERR_LOC = 28, SERR_INTR_ENABLE = 32, SLOT1 = 36, }; struct acpiphp_slot; struct slot___3 { struct hotplug_slot hotplug_slot; struct acpiphp_slot *acpi_slot; unsigned int sun; }; struct acpiphp_slot { struct list_head node; struct pci_bus *bus; struct list_head funcs; struct slot___3 *slot; u8 device; u32 flags; }; struct acpiphp_attention_info { int (*set_attn)(struct hotplug_slot *, u8); int (*get_attn)(struct hotplug_slot *, u8 *); struct module *owner; }; struct acpiphp_context; struct acpiphp_bridge { struct list_head list; struct list_head slots; struct kref ref; struct acpiphp_context *context; int nr_slots; struct pci_bus *pci_bus; struct pci_dev *pci_dev; bool is_going_away; }; struct acpiphp_func { struct acpiphp_bridge *parent; struct acpiphp_slot *slot; struct list_head sibling; u8 function; u32 flags; }; struct acpiphp_context { struct acpi_hotplug_context hp; struct acpiphp_func func; struct acpiphp_bridge *bridge; unsigned int refcount; }; struct acpiphp_root_context { struct acpi_hotplug_context hp; struct acpiphp_bridge *root_bridge; }; enum dmi_device_type { DMI_DEV_TYPE_ANY = 0, DMI_DEV_TYPE_OTHER = 1, DMI_DEV_TYPE_UNKNOWN = 2, DMI_DEV_TYPE_VIDEO = 3, DMI_DEV_TYPE_SCSI = 4, DMI_DEV_TYPE_ETHERNET = 5, DMI_DEV_TYPE_TOKENRING = 6, DMI_DEV_TYPE_SOUND = 7, DMI_DEV_TYPE_PATA = 8, DMI_DEV_TYPE_SATA = 9, DMI_DEV_TYPE_SAS = 10, DMI_DEV_TYPE_IPMI = 4294967295, DMI_DEV_TYPE_OEM_STRING = 4294967294, DMI_DEV_TYPE_DEV_ONBOARD = 4294967293, DMI_DEV_TYPE_DEV_SLOT = 4294967292, }; struct dmi_device { struct list_head list; int type; const char *name; void *device_data; }; struct dmi_dev_onboard { struct dmi_device dev; int instance; int segment; int bus; int devfn; }; enum smbios_attr_enum { SMBIOS_ATTR_NONE = 0, SMBIOS_ATTR_LABEL_SHOW = 1, SMBIOS_ATTR_INSTANCE_SHOW = 2, }; enum acpi_attr_enum { ACPI_ATTR_LABEL_SHOW = 0, ACPI_ATTR_INDEX_SHOW = 1, }; struct pci_p2pdma { struct gen_pool *pool; bool p2pmem_published; struct xarray map_types; }; enum pci_p2pdma_map_type { PCI_P2PDMA_MAP_UNKNOWN = 0, PCI_P2PDMA_MAP_NOT_SUPPORTED = 1, PCI_P2PDMA_MAP_BUS_ADDR = 2, PCI_P2PDMA_MAP_THRU_HOST_BRIDGE = 3, }; struct pci_p2pdma_pagemap { struct dev_pagemap pgmap; struct pci_dev *provider; u64 bus_offset; }; struct pci_p2pdma_whitelist_entry { short unsigned int vendor; short unsigned int device; enum { REQ_SAME_HOST_BRIDGE = 1, } flags; }; enum pci_interrupt_pin { PCI_INTERRUPT_UNKNOWN = 0, PCI_INTERRUPT_INTA = 1, PCI_INTERRUPT_INTB = 2, PCI_INTERRUPT_INTC = 3, PCI_INTERRUPT_INTD = 4, }; enum pci_barno { NO_BAR = 4294967295, BAR_0 = 0, BAR_1 = 1, BAR_2 = 2, BAR_3 = 3, BAR_4 = 4, BAR_5 = 5, }; struct pci_epf_header { u16 vendorid; u16 deviceid; u8 revid; u8 progif_code; u8 subclass_code; u8 baseclass_code; u8 cache_line_size; u16 subsys_vendor_id; u16 subsys_id; enum pci_interrupt_pin interrupt_pin; }; struct pci_epf_bar { dma_addr_t phys_addr; void *addr; size_t size; enum pci_barno barno; int flags; }; struct pci_epc_ops; struct pci_epc_mem; struct pci_epc { struct device dev; struct list_head pci_epf; const struct pci_epc_ops *ops; struct pci_epc_mem **windows; struct pci_epc_mem *mem; unsigned int num_windows; u8 max_functions; struct config_group *group; struct mutex lock; long unsigned int function_num_map; struct atomic_notifier_head notifier; }; enum pci_epc_irq_type { PCI_EPC_IRQ_UNKNOWN = 0, PCI_EPC_IRQ_LEGACY = 1, PCI_EPC_IRQ_MSI = 2, PCI_EPC_IRQ_MSIX = 3, }; struct pci_epc_features; struct pci_epc_ops { int (*write_header)(struct pci_epc *, u8, struct pci_epf_header *); int (*set_bar)(struct pci_epc *, u8, struct pci_epf_bar *); void (*clear_bar)(struct pci_epc *, u8, struct pci_epf_bar *); int (*map_addr)(struct pci_epc *, u8, phys_addr_t, u64, size_t); void (*unmap_addr)(struct pci_epc *, u8, phys_addr_t); int (*set_msi)(struct pci_epc *, u8, u8); int (*get_msi)(struct pci_epc *, u8); int (*set_msix)(struct pci_epc *, u8, u16, enum pci_barno, u32); int (*get_msix)(struct pci_epc *, u8); int (*raise_irq)(struct pci_epc *, u8, enum pci_epc_irq_type, u16); int (*map_msi_irq)(struct pci_epc *, u8, phys_addr_t, u8, u32, u32 *, u32 *); int (*start)(struct pci_epc *); void (*stop)(struct pci_epc *); const struct pci_epc_features * (*get_features)(struct pci_epc *, u8); struct module *owner; }; struct pci_epc_features { unsigned int linkup_notifier: 1; unsigned int core_init_notifier: 1; unsigned int msi_capable: 1; unsigned int msix_capable: 1; u8 reserved_bar; u8 bar_fixed_64bit; u64 bar_fixed_size[6]; size_t align; }; struct pci_epc_mem_window { phys_addr_t phys_base; size_t size; size_t page_size; }; struct pci_epc_mem { struct pci_epc_mem_window window; long unsigned int *bitmap; int pages; struct mutex lock; }; enum dw_pcie_region_type { DW_PCIE_REGION_UNKNOWN = 0, DW_PCIE_REGION_INBOUND = 1, DW_PCIE_REGION_OUTBOUND = 2, }; struct pcie_port; struct dw_pcie_host_ops { int (*host_init)(struct pcie_port *); int (*msi_host_init)(struct pcie_port *); }; struct pcie_port { bool has_msi_ctrl: 1; u64 cfg0_base; void *va_cfg0_base; u32 cfg0_size; resource_size_t io_base; phys_addr_t io_bus_addr; u32 io_size; int irq; const struct dw_pcie_host_ops *ops; int msi_irq; struct irq_domain *irq_domain; struct irq_domain *msi_domain; u16 msi_msg; dma_addr_t msi_data; struct irq_chip *msi_irq_chip; u32 num_vectors; u32 irq_mask[8]; struct pci_host_bridge *bridge; raw_spinlock_t lock; long unsigned int msi_irq_in_use[4]; }; enum dw_pcie_as_type { DW_PCIE_AS_UNKNOWN = 0, DW_PCIE_AS_MEM = 1, DW_PCIE_AS_IO = 2, }; struct dw_pcie_ep; struct dw_pcie_ep_ops { void (*ep_init)(struct dw_pcie_ep *); int (*raise_irq)(struct dw_pcie_ep *, u8, enum pci_epc_irq_type, u16); const struct pci_epc_features * (*get_features)(struct dw_pcie_ep *); unsigned int (*func_conf_select)(struct dw_pcie_ep *, u8); }; struct dw_pcie_ep { struct pci_epc *epc; struct list_head func_list; const struct dw_pcie_ep_ops *ops; phys_addr_t phys_base; size_t addr_size; size_t page_size; u8 bar_to_atu[6]; phys_addr_t *outbound_addr; long unsigned int *ib_window_map; long unsigned int *ob_window_map; void *msi_mem; phys_addr_t msi_mem_phys; struct pci_epf_bar *epf_bar[6]; }; struct dw_pcie; struct dw_pcie_ops { u64 (*cpu_addr_fixup)(struct dw_pcie *, u64); u32 (*read_dbi)(struct dw_pcie *, void *, u32, size_t); void (*write_dbi)(struct dw_pcie *, void *, u32, size_t, u32); void (*write_dbi2)(struct dw_pcie *, void *, u32, size_t, u32); int (*link_up)(struct dw_pcie *); int (*start_link)(struct dw_pcie *); void (*stop_link)(struct dw_pcie *); }; struct dw_pcie { struct device *dev; void *dbi_base; void *dbi_base2; void *atu_base; size_t atu_size; u32 num_ib_windows; u32 num_ob_windows; struct pcie_port pp; struct dw_pcie_ep ep; const struct dw_pcie_ops *ops; unsigned int version; int num_lanes; int link_gen; u8 n_fts[2]; bool iatu_unroll_enabled: 1; bool io_cfg_atu_shared: 1; }; enum dw_pcie_device_mode { DW_PCIE_UNKNOWN_TYPE = 0, DW_PCIE_EP_TYPE = 1, DW_PCIE_LEG_EP_TYPE = 2, DW_PCIE_RC_TYPE = 3, }; struct dw_plat_pcie { struct dw_pcie *pci; struct regmap *regmap; enum dw_pcie_device_mode mode; }; struct dw_plat_pcie_of_data { enum dw_pcie_device_mode mode; }; struct reset_control; enum pcie_data_rate { PCIE_GEN1 = 0, PCIE_GEN2 = 1, PCIE_GEN3 = 2, PCIE_GEN4 = 3, }; struct meson_pcie_clk_res { struct clk *clk; struct clk *port_clk; struct clk *general_clk; }; struct meson_pcie_rc_reset { struct reset_control *port; struct reset_control *apb; }; struct meson_pcie { struct dw_pcie pci; void *cfg_base; struct meson_pcie_clk_res clk_res; struct meson_pcie_rc_reset mrst; struct gpio_desc *reset_gpio; struct phy *phy; }; enum hdmi_infoframe_type { HDMI_INFOFRAME_TYPE_VENDOR = 129, HDMI_INFOFRAME_TYPE_AVI = 130, HDMI_INFOFRAME_TYPE_SPD = 131, HDMI_INFOFRAME_TYPE_AUDIO = 132, HDMI_INFOFRAME_TYPE_DRM = 135, }; struct hdmi_any_infoframe { enum hdmi_infoframe_type type; unsigned char version; unsigned char length; }; enum hdmi_colorspace { HDMI_COLORSPACE_RGB = 0, HDMI_COLORSPACE_YUV422 = 1, HDMI_COLORSPACE_YUV444 = 2, HDMI_COLORSPACE_YUV420 = 3, HDMI_COLORSPACE_RESERVED4 = 4, HDMI_COLORSPACE_RESERVED5 = 5, HDMI_COLORSPACE_RESERVED6 = 6, HDMI_COLORSPACE_IDO_DEFINED = 7, }; enum hdmi_scan_mode { HDMI_SCAN_MODE_NONE = 0, HDMI_SCAN_MODE_OVERSCAN = 1, HDMI_SCAN_MODE_UNDERSCAN = 2, HDMI_SCAN_MODE_RESERVED = 3, }; enum hdmi_colorimetry { HDMI_COLORIMETRY_NONE = 0, HDMI_COLORIMETRY_ITU_601 = 1, HDMI_COLORIMETRY_ITU_709 = 2, HDMI_COLORIMETRY_EXTENDED = 3, }; enum hdmi_picture_aspect { HDMI_PICTURE_ASPECT_NONE = 0, HDMI_PICTURE_ASPECT_4_3 = 1, HDMI_PICTURE_ASPECT_16_9 = 2, HDMI_PICTURE_ASPECT_64_27 = 3, HDMI_PICTURE_ASPECT_256_135 = 4, HDMI_PICTURE_ASPECT_RESERVED = 5, }; enum hdmi_active_aspect { HDMI_ACTIVE_ASPECT_16_9_TOP = 2, HDMI_ACTIVE_ASPECT_14_9_TOP = 3, HDMI_ACTIVE_ASPECT_16_9_CENTER = 4, HDMI_ACTIVE_ASPECT_PICTURE = 8, HDMI_ACTIVE_ASPECT_4_3 = 9, HDMI_ACTIVE_ASPECT_16_9 = 10, HDMI_ACTIVE_ASPECT_14_9 = 11, HDMI_ACTIVE_ASPECT_4_3_SP_14_9 = 13, HDMI_ACTIVE_ASPECT_16_9_SP_14_9 = 14, HDMI_ACTIVE_ASPECT_16_9_SP_4_3 = 15, }; enum hdmi_extended_colorimetry { HDMI_EXTENDED_COLORIMETRY_XV_YCC_601 = 0, HDMI_EXTENDED_COLORIMETRY_XV_YCC_709 = 1, HDMI_EXTENDED_COLORIMETRY_S_YCC_601 = 2, HDMI_EXTENDED_COLORIMETRY_OPYCC_601 = 3, HDMI_EXTENDED_COLORIMETRY_OPRGB = 4, HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM = 5, HDMI_EXTENDED_COLORIMETRY_BT2020 = 6, HDMI_EXTENDED_COLORIMETRY_RESERVED = 7, }; enum hdmi_quantization_range { HDMI_QUANTIZATION_RANGE_DEFAULT = 0, HDMI_QUANTIZATION_RANGE_LIMITED = 1, HDMI_QUANTIZATION_RANGE_FULL = 2, HDMI_QUANTIZATION_RANGE_RESERVED = 3, }; enum hdmi_nups { HDMI_NUPS_UNKNOWN = 0, HDMI_NUPS_HORIZONTAL = 1, HDMI_NUPS_VERTICAL = 2, HDMI_NUPS_BOTH = 3, }; enum hdmi_ycc_quantization_range { HDMI_YCC_QUANTIZATION_RANGE_LIMITED = 0, HDMI_YCC_QUANTIZATION_RANGE_FULL = 1, }; enum hdmi_content_type { HDMI_CONTENT_TYPE_GRAPHICS = 0, HDMI_CONTENT_TYPE_PHOTO = 1, HDMI_CONTENT_TYPE_CINEMA = 2, HDMI_CONTENT_TYPE_GAME = 3, }; enum hdmi_metadata_type { HDMI_STATIC_METADATA_TYPE1 = 0, }; enum hdmi_eotf { HDMI_EOTF_TRADITIONAL_GAMMA_SDR = 0, HDMI_EOTF_TRADITIONAL_GAMMA_HDR = 1, HDMI_EOTF_SMPTE_ST2084 = 2, HDMI_EOTF_BT_2100_HLG = 3, }; struct hdmi_avi_infoframe { enum hdmi_infoframe_type type; unsigned char version; unsigned char length; enum hdmi_colorspace colorspace; enum hdmi_scan_mode scan_mode; enum hdmi_colorimetry colorimetry; enum hdmi_picture_aspect picture_aspect; enum hdmi_active_aspect active_aspect; bool itc; enum hdmi_extended_colorimetry extended_colorimetry; enum hdmi_quantization_range quantization_range; enum hdmi_nups nups; unsigned char video_code; enum hdmi_ycc_quantization_range ycc_quantization_range; enum hdmi_content_type content_type; unsigned char pixel_repeat; short unsigned int top_bar; short unsigned int bottom_bar; short unsigned int left_bar; short unsigned int right_bar; }; struct hdmi_drm_infoframe { enum hdmi_infoframe_type type; unsigned char version; unsigned char length; enum hdmi_eotf eotf; enum hdmi_metadata_type metadata_type; struct { u16 x; u16 y; } display_primaries[3]; struct { u16 x; u16 y; } white_point; u16 max_display_mastering_luminance; u16 min_display_mastering_luminance; u16 max_cll; u16 max_fall; }; enum hdmi_spd_sdi { HDMI_SPD_SDI_UNKNOWN = 0, HDMI_SPD_SDI_DSTB = 1, HDMI_SPD_SDI_DVDP = 2, HDMI_SPD_SDI_DVHS = 3, HDMI_SPD_SDI_HDDVR = 4, HDMI_SPD_SDI_DVC = 5, HDMI_SPD_SDI_DSC = 6, HDMI_SPD_SDI_VCD = 7, HDMI_SPD_SDI_GAME = 8, HDMI_SPD_SDI_PC = 9, HDMI_SPD_SDI_BD = 10, HDMI_SPD_SDI_SACD = 11, HDMI_SPD_SDI_HDDVD = 12, HDMI_SPD_SDI_PMP = 13, }; struct hdmi_spd_infoframe { enum hdmi_infoframe_type type; unsigned char version; unsigned char length; char vendor[8]; char product[16]; enum hdmi_spd_sdi sdi; }; enum hdmi_audio_coding_type { HDMI_AUDIO_CODING_TYPE_STREAM = 0, HDMI_AUDIO_CODING_TYPE_PCM = 1, HDMI_AUDIO_CODING_TYPE_AC3 = 2, HDMI_AUDIO_CODING_TYPE_MPEG1 = 3, HDMI_AUDIO_CODING_TYPE_MP3 = 4, HDMI_AUDIO_CODING_TYPE_MPEG2 = 5, HDMI_AUDIO_CODING_TYPE_AAC_LC = 6, HDMI_AUDIO_CODING_TYPE_DTS = 7, HDMI_AUDIO_CODING_TYPE_ATRAC = 8, HDMI_AUDIO_CODING_TYPE_DSD = 9, HDMI_AUDIO_CODING_TYPE_EAC3 = 10, HDMI_AUDIO_CODING_TYPE_DTS_HD = 11, HDMI_AUDIO_CODING_TYPE_MLP = 12, HDMI_AUDIO_CODING_TYPE_DST = 13, HDMI_AUDIO_CODING_TYPE_WMA_PRO = 14, HDMI_AUDIO_CODING_TYPE_CXT = 15, }; enum hdmi_audio_sample_size { HDMI_AUDIO_SAMPLE_SIZE_STREAM = 0, HDMI_AUDIO_SAMPLE_SIZE_16 = 1, HDMI_AUDIO_SAMPLE_SIZE_20 = 2, HDMI_AUDIO_SAMPLE_SIZE_24 = 3, }; enum hdmi_audio_sample_frequency { HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM = 0, HDMI_AUDIO_SAMPLE_FREQUENCY_32000 = 1, HDMI_AUDIO_SAMPLE_FREQUENCY_44100 = 2, HDMI_AUDIO_SAMPLE_FREQUENCY_48000 = 3, HDMI_AUDIO_SAMPLE_FREQUENCY_88200 = 4, HDMI_AUDIO_SAMPLE_FREQUENCY_96000 = 5, HDMI_AUDIO_SAMPLE_FREQUENCY_176400 = 6, HDMI_AUDIO_SAMPLE_FREQUENCY_192000 = 7, }; enum hdmi_audio_coding_type_ext { HDMI_AUDIO_CODING_TYPE_EXT_CT = 0, HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC = 1, HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC_V2 = 2, HDMI_AUDIO_CODING_TYPE_EXT_MPEG_SURROUND = 3, HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC = 4, HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_V2 = 5, HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC = 6, HDMI_AUDIO_CODING_TYPE_EXT_DRA = 7, HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_SURROUND = 8, HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC_SURROUND = 10, }; struct hdmi_audio_infoframe { enum hdmi_infoframe_type type; unsigned char version; unsigned char length; unsigned char channels; enum hdmi_audio_coding_type coding_type; enum hdmi_audio_sample_size sample_size; enum hdmi_audio_sample_frequency sample_frequency; enum hdmi_audio_coding_type_ext coding_type_ext; unsigned char channel_allocation; unsigned char level_shift_value; bool downmix_inhibit; }; enum hdmi_3d_structure { HDMI_3D_STRUCTURE_INVALID = 4294967295, HDMI_3D_STRUCTURE_FRAME_PACKING = 0, HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE = 1, HDMI_3D_STRUCTURE_LINE_ALTERNATIVE = 2, HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL = 3, HDMI_3D_STRUCTURE_L_DEPTH = 4, HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH = 5, HDMI_3D_STRUCTURE_TOP_AND_BOTTOM = 6, HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF = 8, }; struct hdmi_vendor_infoframe { enum hdmi_infoframe_type type; unsigned char version; unsigned char length; unsigned int oui; u8 vic; enum hdmi_3d_structure s3d_struct; unsigned int s3d_ext_data; }; union hdmi_vendor_any_infoframe { struct { enum hdmi_infoframe_type type; unsigned char version; unsigned char length; unsigned int oui; } any; struct hdmi_vendor_infoframe hdmi; }; union hdmi_infoframe { struct hdmi_any_infoframe any; struct hdmi_avi_infoframe avi; struct hdmi_spd_infoframe spd; union hdmi_vendor_any_infoframe vendor; struct hdmi_audio_infoframe audio; struct hdmi_drm_infoframe drm; }; struct vc { struct vc_data *d; struct work_struct SAK_work; }; struct vgastate { void *vgabase; long unsigned int membase; __u32 memsize; __u32 flags; __u32 depth; __u32 num_attr; __u32 num_crtc; __u32 num_gfx; __u32 num_seq; void *vidstate; }; struct fb_fix_screeninfo { char id[16]; long unsigned int smem_start; __u32 smem_len; __u32 type; __u32 type_aux; __u32 visual; __u16 xpanstep; __u16 ypanstep; __u16 ywrapstep; __u32 line_length; long unsigned int mmio_start; __u32 mmio_len; __u32 accel; __u16 capabilities; __u16 reserved[2]; }; struct fb_bitfield { __u32 offset; __u32 length; __u32 msb_right; }; struct fb_var_screeninfo { __u32 xres; __u32 yres; __u32 xres_virtual; __u32 yres_virtual; __u32 xoffset; __u32 yoffset; __u32 bits_per_pixel; __u32 grayscale; struct fb_bitfield red; struct fb_bitfield green; struct fb_bitfield blue; struct fb_bitfield transp; __u32 nonstd; __u32 activate; __u32 height; __u32 width; __u32 accel_flags; __u32 pixclock; __u32 left_margin; __u32 right_margin; __u32 upper_margin; __u32 lower_margin; __u32 hsync_len; __u32 vsync_len; __u32 sync; __u32 vmode; __u32 rotate; __u32 colorspace; __u32 reserved[4]; }; struct fb_cmap { __u32 start; __u32 len; __u16 *red; __u16 *green; __u16 *blue; __u16 *transp; }; enum { FB_BLANK_UNBLANK = 0, FB_BLANK_NORMAL = 1, FB_BLANK_VSYNC_SUSPEND = 2, FB_BLANK_HSYNC_SUSPEND = 3, FB_BLANK_POWERDOWN = 4, }; struct fb_copyarea { __u32 dx; __u32 dy; __u32 width; __u32 height; __u32 sx; __u32 sy; }; struct fb_fillrect { __u32 dx; __u32 dy; __u32 width; __u32 height; __u32 color; __u32 rop; }; struct fb_image { __u32 dx; __u32 dy; __u32 width; __u32 height; __u32 fg_color; __u32 bg_color; __u8 depth; const char *data; struct fb_cmap cmap; }; struct fbcurpos { __u16 x; __u16 y; }; struct fb_cursor { __u16 set; __u16 enable; __u16 rop; const char *mask; struct fbcurpos hot; struct fb_image image; }; struct fb_chroma { __u32 redx; __u32 greenx; __u32 bluex; __u32 whitex; __u32 redy; __u32 greeny; __u32 bluey; __u32 whitey; }; struct fb_videomode; struct fb_monspecs { struct fb_chroma chroma; struct fb_videomode *modedb; __u8 manufacturer[4]; __u8 monitor[14]; __u8 serial_no[14]; __u8 ascii[14]; __u32 modedb_len; __u32 model; __u32 serial; __u32 year; __u32 week; __u32 hfmin; __u32 hfmax; __u32 dclkmin; __u32 dclkmax; __u16 input; __u16 dpms; __u16 signal; __u16 vfmin; __u16 vfmax; __u16 gamma; __u16 gtf: 1; __u16 misc; __u8 version; __u8 revision; __u8 max_x; __u8 max_y; }; struct fb_videomode { const char *name; u32 refresh; u32 xres; u32 yres; u32 pixclock; u32 left_margin; u32 right_margin; u32 upper_margin; u32 lower_margin; u32 hsync_len; u32 vsync_len; u32 sync; u32 vmode; u32 flag; }; struct fb_info; struct fb_event { struct fb_info *info; void *data; }; struct fb_pixmap { u8 *addr; u32 size; u32 offset; u32 buf_align; u32 scan_align; u32 access_align; u32 flags; u32 blit_x; u32 blit_y; void (*writeio)(struct fb_info *, void *, void *, unsigned int); void (*readio)(struct fb_info *, void *, void *, unsigned int); }; struct fb_deferred_io; struct fb_ops; struct fb_tile_ops; struct apertures_struct; struct fb_info { atomic_t count; int node; int flags; int fbcon_rotate_hint; struct mutex lock; struct mutex mm_lock; struct fb_var_screeninfo var; struct fb_fix_screeninfo fix; struct fb_monspecs monspecs; struct work_struct queue; struct fb_pixmap pixmap; struct fb_pixmap sprite; struct fb_cmap cmap; struct list_head modelist; struct fb_videomode *mode; struct delayed_work deferred_work; struct fb_deferred_io *fbdefio; const struct fb_ops *fbops; struct device *device; struct device *dev; int class_flag; struct fb_tile_ops *tileops; union { char *screen_base; char *screen_buffer; }; long unsigned int screen_size; void *pseudo_palette; u32 state; void *fbcon_par; void *par; struct apertures_struct *apertures; bool skip_vt_switch; }; struct fb_blit_caps { u32 x; u32 y; u32 len; u32 flags; }; struct fb_deferred_io { long unsigned int delay; struct mutex lock; struct list_head pagelist; void (*first_io)(struct fb_info *); void (*deferred_io)(struct fb_info *, struct list_head *); }; struct fb_ops { struct module *owner; int (*fb_open)(struct fb_info *, int); int (*fb_release)(struct fb_info *, int); ssize_t (*fb_read)(struct fb_info *, char *, size_t, loff_t *); ssize_t (*fb_write)(struct fb_info *, const char *, size_t, loff_t *); int (*fb_check_var)(struct fb_var_screeninfo *, struct fb_info *); int (*fb_set_par)(struct fb_info *); int (*fb_setcolreg)(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, struct fb_info *); int (*fb_setcmap)(struct fb_cmap *, struct fb_info *); int (*fb_blank)(int, struct fb_info *); int (*fb_pan_display)(struct fb_var_screeninfo *, struct fb_info *); void (*fb_fillrect)(struct fb_info *, const struct fb_fillrect *); void (*fb_copyarea)(struct fb_info *, const struct fb_copyarea *); void (*fb_imageblit)(struct fb_info *, const struct fb_image *); int (*fb_cursor)(struct fb_info *, struct fb_cursor *); int (*fb_sync)(struct fb_info *); int (*fb_ioctl)(struct fb_info *, unsigned int, long unsigned int); int (*fb_compat_ioctl)(struct fb_info *, unsigned int, long unsigned int); int (*fb_mmap)(struct fb_info *, struct vm_area_struct *); void (*fb_get_caps)(struct fb_info *, struct fb_blit_caps *, struct fb_var_screeninfo *); void (*fb_destroy)(struct fb_info *); int (*fb_debug_enter)(struct fb_info *); int (*fb_debug_leave)(struct fb_info *); }; struct fb_tilemap { __u32 width; __u32 height; __u32 depth; __u32 length; const __u8 *data; }; struct fb_tilerect { __u32 sx; __u32 sy; __u32 width; __u32 height; __u32 index; __u32 fg; __u32 bg; __u32 rop; }; struct fb_tilearea { __u32 sx; __u32 sy; __u32 dx; __u32 dy; __u32 width; __u32 height; }; struct fb_tileblit { __u32 sx; __u32 sy; __u32 width; __u32 height; __u32 fg; __u32 bg; __u32 length; __u32 *indices; }; struct fb_tilecursor { __u32 sx; __u32 sy; __u32 mode; __u32 shape; __u32 fg; __u32 bg; }; struct fb_tile_ops { void (*fb_settile)(struct fb_info *, struct fb_tilemap *); void (*fb_tilecopy)(struct fb_info *, struct fb_tilearea *); void (*fb_tilefill)(struct fb_info *, struct fb_tilerect *); void (*fb_tileblit)(struct fb_info *, struct fb_tileblit *); void (*fb_tilecursor)(struct fb_info *, struct fb_tilecursor *); int (*fb_get_tilemax)(struct fb_info *); }; struct aperture { resource_size_t base; resource_size_t size; }; struct apertures_struct { unsigned int count; struct aperture ranges[0]; }; enum backlight_update_reason { BACKLIGHT_UPDATE_HOTKEY = 0, BACKLIGHT_UPDATE_SYSFS = 1, }; enum backlight_type { BACKLIGHT_RAW = 1, BACKLIGHT_PLATFORM = 2, BACKLIGHT_FIRMWARE = 3, BACKLIGHT_TYPE_MAX = 4, }; enum backlight_notification { BACKLIGHT_REGISTERED = 0, BACKLIGHT_UNREGISTERED = 1, }; enum backlight_scale { BACKLIGHT_SCALE_UNKNOWN = 0, BACKLIGHT_SCALE_LINEAR = 1, BACKLIGHT_SCALE_NON_LINEAR = 2, }; struct backlight_device; struct backlight_ops { unsigned int options; int (*update_status)(struct backlight_device *); int (*get_brightness)(struct backlight_device *); int (*check_fb)(struct backlight_device *, struct fb_info *); }; struct backlight_properties { int brightness; int max_brightness; int power; int fb_blank; enum backlight_type type; unsigned int state; enum backlight_scale scale; }; struct backlight_device { struct backlight_properties props; struct mutex update_lock; struct mutex ops_lock; const struct backlight_ops *ops; struct notifier_block fb_notif; struct list_head entry; struct device dev; bool fb_bl_on[32]; int use_count; }; struct fb_cmap_user { __u32 start; __u32 len; __u16 *red; __u16 *green; __u16 *blue; __u16 *transp; }; struct fb_modelist { struct list_head list; struct fb_videomode mode; }; struct fb_fix_screeninfo32 { char id[16]; compat_caddr_t smem_start; u32 smem_len; u32 type; u32 type_aux; u32 visual; u16 xpanstep; u16 ypanstep; u16 ywrapstep; u32 line_length; compat_caddr_t mmio_start; u32 mmio_len; u32 accel; u16 reserved[3]; }; struct fb_cmap32 { u32 start; u32 len; compat_caddr_t red; compat_caddr_t green; compat_caddr_t blue; compat_caddr_t transp; }; struct dmt_videomode { u32 dmt_id; u32 std_2byte_code; u32 cvt_3byte_code; const struct fb_videomode *mode; }; enum display_flags { DISPLAY_FLAGS_HSYNC_LOW = 1, DISPLAY_FLAGS_HSYNC_HIGH = 2, DISPLAY_FLAGS_VSYNC_LOW = 4, DISPLAY_FLAGS_VSYNC_HIGH = 8, DISPLAY_FLAGS_DE_LOW = 16, DISPLAY_FLAGS_DE_HIGH = 32, DISPLAY_FLAGS_PIXDATA_POSEDGE = 64, DISPLAY_FLAGS_PIXDATA_NEGEDGE = 128, DISPLAY_FLAGS_INTERLACED = 256, DISPLAY_FLAGS_DOUBLESCAN = 512, DISPLAY_FLAGS_DOUBLECLK = 1024, DISPLAY_FLAGS_SYNC_POSEDGE = 2048, DISPLAY_FLAGS_SYNC_NEGEDGE = 4096, }; struct videomode { long unsigned int pixelclock; u32 hactive; u32 hfront_porch; u32 hback_porch; u32 hsync_len; u32 vactive; u32 vfront_porch; u32 vback_porch; u32 vsync_len; enum display_flags flags; }; struct broken_edid { u8 manufacturer[4]; u32 model; u32 fix; }; struct __fb_timings { u32 dclk; u32 hfreq; u32 vfreq; u32 hactive; u32 vactive; u32 hblank; u32 vblank; u32 htotal; u32 vtotal; }; struct fb_cvt_data { u32 xres; u32 yres; u32 refresh; u32 f_refresh; u32 pixclock; u32 hperiod; u32 hblank; u32 hfreq; u32 htotal; u32 vtotal; u32 vsync; u32 hsync; u32 h_front_porch; u32 h_back_porch; u32 v_front_porch; u32 v_back_porch; u32 h_margin; u32 v_margin; u32 interlace; u32 aspect_ratio; u32 active_pixels; u32 flags; u32 status; }; typedef unsigned char u_char; typedef short unsigned int u_short; struct fb_con2fbmap { __u32 console; __u32 framebuffer; }; struct fbcon_display { const u_char *fontdata; int userfont; u_short scrollmode; u_short inverse; short int yscroll; int vrows; int cursor_shape; int con_rotate; u32 xres_virtual; u32 yres_virtual; u32 height; u32 width; u32 bits_per_pixel; u32 grayscale; u32 nonstd; u32 accel_flags; u32 rotate; struct fb_bitfield red; struct fb_bitfield green; struct fb_bitfield blue; struct fb_bitfield transp; const struct fb_videomode *mode; }; struct fbcon_ops { void (*bmove)(struct vc_data *, struct fb_info *, int, int, int, int, int, int); void (*clear)(struct vc_data *, struct fb_info *, int, int, int, int); void (*putcs)(struct vc_data *, struct fb_info *, const short unsigned int *, int, int, int, int, int); void (*clear_margins)(struct vc_data *, struct fb_info *, int, int); void (*cursor)(struct vc_data *, struct fb_info *, int, int, int); int (*update_start)(struct fb_info *); int (*rotate_font)(struct fb_info *, struct vc_data *); struct fb_var_screeninfo var; struct timer_list cursor_timer; struct fb_cursor cursor_state; struct fbcon_display *p; struct fb_info *info; int currcon; int cur_blink_jiffies; int cursor_flash; int cursor_reset; int blank_state; int graphics; int save_graphics; int flags; int rotate; int cur_rotate; char *cursor_data; u8 *fontbuffer; u8 *fontdata; u8 *cursor_src; u32 cursor_size; u32 fd_size; }; enum { FBCON_LOGO_CANSHOW = 4294967295, FBCON_LOGO_DRAW = 4294967294, FBCON_LOGO_DONTSHOW = 4294967293, }; struct vesafb_par { u32 pseudo_palette[256]; int wc_cookie; struct resource *region; }; struct acpi_table_bgrt { struct acpi_table_header header; u16 version; u8 status; u8 image_type; u64 image_address; u32 image_offset_x; u32 image_offset_y; }; enum drm_panel_orientation { DRM_MODE_PANEL_ORIENTATION_UNKNOWN = 4294967295, DRM_MODE_PANEL_ORIENTATION_NORMAL = 0, DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP = 1, DRM_MODE_PANEL_ORIENTATION_LEFT_UP = 2, DRM_MODE_PANEL_ORIENTATION_RIGHT_UP = 3, }; struct bmp_file_header { u16 id; u32 file_size; u32 reserved; u32 bitmap_offset; } __attribute__((packed)); struct bmp_dib_header { u32 dib_header_size; s32 width; s32 height; u16 planes; u16 bpp; u32 compression; u32 bitmap_size; u32 horz_resolution; u32 vert_resolution; u32 colors_used; u32 colors_important; }; struct simplefb_format { const char *name; u32 bits_per_pixel; struct fb_bitfield red; struct fb_bitfield green; struct fb_bitfield blue; struct fb_bitfield transp; u32 fourcc; }; struct simplefb_params { u32 width; u32 height; u32 stride; struct simplefb_format *format; }; struct simplefb_par { u32 palette[16]; }; struct timing_entry { u32 min; u32 typ; u32 max; }; struct display_timing { struct timing_entry pixelclock; struct timing_entry hactive; struct timing_entry hfront_porch; struct timing_entry hback_porch; struct timing_entry hsync_len; struct timing_entry vactive; struct timing_entry vfront_porch; struct timing_entry vback_porch; struct timing_entry vsync_len; enum display_flags flags; }; struct display_timings { unsigned int num_timings; unsigned int native_mode; struct display_timing **timings; }; struct thermal_cooling_device_ops; struct thermal_cooling_device { int id; char *type; struct device device; struct device_node *np; void *devdata; void *stats; const struct thermal_cooling_device_ops *ops; bool updated; struct mutex lock; struct list_head thermal_instances; struct list_head node; }; struct idle_cpu { struct cpuidle_state *state_table; long unsigned int auto_demotion_disable_flags; bool byt_auto_demotion_disable_flag; bool disable_promotion_to_c1e; bool use_acpi; }; struct thermal_cooling_device_ops { int (*get_max_state)(struct thermal_cooling_device *, long unsigned int *); int (*get_cur_state)(struct thermal_cooling_device *, long unsigned int *); int (*set_cur_state)(struct thermal_cooling_device *, long unsigned int); int (*get_requested_power)(struct thermal_cooling_device *, u32 *); int (*state2power)(struct thermal_cooling_device *, long unsigned int, u32 *); int (*power2state)(struct thermal_cooling_device *, u32, long unsigned int *); }; struct acpi_lpi_state { u32 min_residency; u32 wake_latency; u32 flags; u32 arch_flags; u32 res_cnt_freq; u32 enable_parent_state; u64 address; u8 index; u8 entry_method; char desc[32]; }; struct acpi_processor_power { int count; union { struct acpi_processor_cx states[8]; struct acpi_lpi_state lpi_states[8]; }; int timer_broadcast_on_state; }; struct acpi_psd_package { u64 num_entries; u64 revision; u64 domain; u64 coord_type; u64 num_processors; }; struct acpi_pct_register { u8 descriptor; u16 length; u8 space_id; u8 bit_width; u8 bit_offset; u8 reserved; u64 address; } __attribute__((packed)); struct acpi_processor_px { u64 core_frequency; u64 power; u64 transition_latency; u64 bus_master_latency; u64 control; u64 status; }; struct acpi_processor_performance { unsigned int state; unsigned int platform_limit; struct acpi_pct_register control_register; struct acpi_pct_register status_register; short: 16; unsigned int state_count; int: 32; struct acpi_processor_px *states; struct acpi_psd_package domain_info; cpumask_var_t shared_cpu_map; unsigned int shared_type; int: 32; } __attribute__((packed)); struct acpi_tsd_package { u64 num_entries; u64 revision; u64 domain; u64 coord_type; u64 num_processors; }; struct acpi_processor_tx_tss { u64 freqpercentage; u64 power; u64 transition_latency; u64 control; u64 status; }; struct acpi_processor_tx { u16 power; u16 performance; }; struct acpi_processor; struct acpi_processor_throttling { unsigned int state; unsigned int platform_limit; struct acpi_pct_register control_register; struct acpi_pct_register status_register; short: 16; unsigned int state_count; int: 32; struct acpi_processor_tx_tss *states_tss; struct acpi_tsd_package domain_info; cpumask_var_t shared_cpu_map; int (*acpi_processor_get_throttling)(struct acpi_processor *); int (*acpi_processor_set_throttling)(struct acpi_processor *, int, bool); u32 address; u8 duty_offset; u8 duty_width; u8 tsd_valid_flag; char: 8; unsigned int shared_type; struct acpi_processor_tx states[16]; int: 32; } __attribute__((packed)); struct acpi_processor_lx { int px; int tx; }; struct acpi_processor_limit { struct acpi_processor_lx state; struct acpi_processor_lx thermal; struct acpi_processor_lx user; }; struct acpi_processor { acpi_handle handle; u32 acpi_id; phys_cpuid_t phys_id; u32 id; u32 pblk; int performance_platform_limit; int throttling_platform_limit; struct acpi_processor_flags flags; struct acpi_processor_power power; struct acpi_processor_performance *performance; struct acpi_processor_throttling throttling; struct acpi_processor_limit limit; struct thermal_cooling_device *cdev; struct device *dev; struct freq_qos_request perflib_req; struct freq_qos_request thermal_req; }; enum ipmi_addr_src { SI_INVALID = 0, SI_HOTMOD = 1, SI_HARDCODED = 2, SI_SPMI = 3, SI_ACPI = 4, SI_SMBIOS = 5, SI_PCI = 6, SI_DEVICETREE = 7, SI_PLATFORM = 8, SI_LAST = 9, }; struct dmi_header { u8 type; u8 length; u16 handle; }; enum si_type { SI_TYPE_INVALID = 0, SI_KCS = 1, SI_SMIC = 2, SI_BT = 3, SI_TYPE_MAX = 4, }; enum ipmi_addr_space { IPMI_IO_ADDR_SPACE = 0, IPMI_MEM_ADDR_SPACE = 1, }; enum ipmi_plat_interface_type { IPMI_PLAT_IF_SI = 0, IPMI_PLAT_IF_SSIF = 1, }; struct ipmi_plat_data { enum ipmi_plat_interface_type iftype; unsigned int type; unsigned int space; long unsigned int addr; unsigned int regspacing; unsigned int regsize; unsigned int regshift; unsigned int irq; unsigned int slave_addr; enum ipmi_addr_src addr_source; }; struct ipmi_dmi_info { enum si_type si_type; unsigned int space; long unsigned int addr; u8 slave_addr; struct ipmi_dmi_info *next; }; typedef u16 acpi_owner_id; union acpi_name_union { u32 integer; char ascii[4]; }; struct acpi_table_desc { acpi_physical_address address; struct acpi_table_header *pointer; u32 length; union acpi_name_union signature; acpi_owner_id owner_id; u8 flags; u16 validation_count; }; struct acpi_madt_io_sapic { struct acpi_subtable_header header; u8 id; u8 reserved; u32 global_irq_base; u64 address; }; struct acpi_madt_interrupt_source { struct acpi_subtable_header header; u16 inti_flags; u8 type; u8 id; u8 eid; u8 io_sapic_vector; u32 global_irq; u32 flags; }; struct acpi_madt_generic_interrupt { struct acpi_subtable_header header; u16 reserved; u32 cpu_interface_number; u32 uid; u32 flags; u32 parking_version; u32 performance_interrupt; u64 parked_address; u64 base_address; u64 gicv_base_address; u64 gich_base_address; u32 vgic_interrupt; u64 gicr_base_address; u64 arm_mpidr; u8 efficiency_class; u8 reserved2[1]; u16 spe_interrupt; } __attribute__((packed)); struct acpi_madt_generic_distributor { struct acpi_subtable_header header; u16 reserved; u32 gic_id; u64 base_address; u32 global_irq_base; u8 version; u8 reserved2[3]; }; enum acpi_subtable_type { ACPI_SUBTABLE_COMMON = 0, ACPI_SUBTABLE_HMAT = 1, ACPI_SUBTABLE_PRMT = 2, }; struct acpi_subtable_entry { union acpi_subtable_headers *hdr; enum acpi_subtable_type type; }; enum acpi_predicate { all_versions = 0, less_than_or_equal = 1, equal = 2, greater_than_or_equal = 3, }; struct acpi_platform_list { char oem_id[7]; char oem_table_id[9]; u32 oem_revision; char *table; enum acpi_predicate pred; char *reason; u32 data; }; typedef u32 (*acpi_interface_handler)(acpi_string, u32); struct acpi_osi_entry { char string[64]; bool enable; }; struct acpi_osi_config { u8 default_disabling; unsigned int linux_enable: 1; unsigned int linux_dmi: 1; unsigned int linux_cmdline: 1; unsigned int darwin_enable: 1; unsigned int darwin_dmi: 1; unsigned int darwin_cmdline: 1; }; struct acpi_predefined_names { const char *name; u8 type; char *val; }; typedef u32 (*acpi_osd_handler)(void *); typedef void (*acpi_osd_exec_callback)(void *); typedef u32 (*acpi_gpe_handler)(acpi_handle, u32, void *); struct acpi_pci_id { u16 segment; u16 bus; u16 device; u16 function; }; struct acpi_mem_mapping { acpi_physical_address physical_address; u8 *logical_address; acpi_size length; struct acpi_mem_mapping *next_mm; }; struct acpi_mem_space_context { u32 length; acpi_physical_address address; struct acpi_mem_mapping *cur_mm; struct acpi_mem_mapping *first_mm; }; typedef enum { OSL_GLOBAL_LOCK_HANDLER = 0, OSL_NOTIFY_HANDLER = 1, OSL_GPE_HANDLER = 2, OSL_DEBUGGER_MAIN_THREAD = 3, OSL_DEBUGGER_EXEC_THREAD = 4, OSL_EC_POLL_HANDLER = 5, OSL_EC_BURST_HANDLER = 6, } acpi_execute_type; union acpi_operand_object; struct acpi_namespace_node { union acpi_operand_object *object; u8 descriptor_type; u8 type; u16 flags; union acpi_name_union name; struct acpi_namespace_node *parent; struct acpi_namespace_node *child; struct acpi_namespace_node *peer; acpi_owner_id owner_id; }; struct acpi_object_common { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; }; struct acpi_object_integer { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 fill[3]; u64 value; }; struct acpi_object_string { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; char *pointer; u32 length; }; struct acpi_object_buffer { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 *pointer; u32 length; u32 aml_length; u8 *aml_start; struct acpi_namespace_node *node; }; struct acpi_object_package { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; struct acpi_namespace_node *node; union acpi_operand_object **elements; u8 *aml_start; u32 aml_length; u32 count; }; struct acpi_object_event { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; void *os_semaphore; }; struct acpi_walk_state; typedef acpi_status (*acpi_internal_method)(struct acpi_walk_state *); struct acpi_object_method { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 info_flags; u8 param_count; u8 sync_level; union acpi_operand_object *mutex; union acpi_operand_object *node; u8 *aml_start; union { acpi_internal_method implementation; union acpi_operand_object *handler; } dispatch; u32 aml_length; acpi_owner_id owner_id; u8 thread_count; }; struct acpi_thread_state; struct acpi_object_mutex { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 sync_level; u16 acquisition_depth; void *os_mutex; u64 thread_id; struct acpi_thread_state *owner_thread; union acpi_operand_object *prev; union acpi_operand_object *next; struct acpi_namespace_node *node; u8 original_sync_level; }; struct acpi_object_region { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 space_id; struct acpi_namespace_node *node; union acpi_operand_object *handler; union acpi_operand_object *next; acpi_physical_address address; u32 length; }; struct acpi_object_notify_common { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; union acpi_operand_object *notify_list[2]; union acpi_operand_object *handler; }; struct acpi_gpe_block_info; struct acpi_object_device { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; union acpi_operand_object *notify_list[2]; union acpi_operand_object *handler; struct acpi_gpe_block_info *gpe_block; }; struct acpi_object_power_resource { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; union acpi_operand_object *notify_list[2]; union acpi_operand_object *handler; u32 system_level; u32 resource_order; }; struct acpi_object_processor { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 proc_id; u8 length; union acpi_operand_object *notify_list[2]; union acpi_operand_object *handler; acpi_io_address address; }; struct acpi_object_thermal_zone { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; union acpi_operand_object *notify_list[2]; union acpi_operand_object *handler; }; struct acpi_object_field_common { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 field_flags; u8 attribute; u8 access_byte_width; struct acpi_namespace_node *node; u32 bit_length; u32 base_byte_offset; u32 value; u8 start_field_bit_offset; u8 access_length; union acpi_operand_object *region_obj; }; struct acpi_object_region_field { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 field_flags; u8 attribute; u8 access_byte_width; struct acpi_namespace_node *node; u32 bit_length; u32 base_byte_offset; u32 value; u8 start_field_bit_offset; u8 access_length; u16 resource_length; union acpi_operand_object *region_obj; u8 *resource_buffer; u16 pin_number_index; u8 *internal_pcc_buffer; }; struct acpi_object_buffer_field { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 field_flags; u8 attribute; u8 access_byte_width; struct acpi_namespace_node *node; u32 bit_length; u32 base_byte_offset; u32 value; u8 start_field_bit_offset; u8 access_length; u8 is_create_field; union acpi_operand_object *buffer_obj; }; struct acpi_object_bank_field { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 field_flags; u8 attribute; u8 access_byte_width; struct acpi_namespace_node *node; u32 bit_length; u32 base_byte_offset; u32 value; u8 start_field_bit_offset; u8 access_length; union acpi_operand_object *region_obj; union acpi_operand_object *bank_obj; }; struct acpi_object_index_field { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 field_flags; u8 attribute; u8 access_byte_width; struct acpi_namespace_node *node; u32 bit_length; u32 base_byte_offset; u32 value; u8 start_field_bit_offset; u8 access_length; union acpi_operand_object *index_obj; union acpi_operand_object *data_obj; }; struct acpi_object_notify_handler { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; struct acpi_namespace_node *node; u32 handler_type; acpi_notify_handler handler; void *context; union acpi_operand_object *next[2]; }; struct acpi_object_addr_handler { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 space_id; u8 handler_flags; acpi_adr_space_handler handler; struct acpi_namespace_node *node; void *context; void *context_mutex; acpi_adr_space_setup setup; union acpi_operand_object *region_list; union acpi_operand_object *next; }; struct acpi_object_reference { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; u8 class; u8 target_type; u8 resolved; void *object; struct acpi_namespace_node *node; union acpi_operand_object **where; u8 *index_pointer; u8 *aml; u32 value; }; struct acpi_object_extra { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; struct acpi_namespace_node *method_REG; struct acpi_namespace_node *scope_node; void *region_context; u8 *aml_start; u32 aml_length; }; struct acpi_object_data { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; acpi_object_handler handler; void *pointer; }; struct acpi_object_cache_list { union acpi_operand_object *next_object; u8 descriptor_type; u8 type; u16 reference_count; u8 flags; union acpi_operand_object *next; }; union acpi_operand_object { struct acpi_object_common common; struct acpi_object_integer integer; struct acpi_object_string string; struct acpi_object_buffer buffer; struct acpi_object_package package; struct acpi_object_event event; struct acpi_object_method method; struct acpi_object_mutex mutex; struct acpi_object_region region; struct acpi_object_notify_common common_notify; struct acpi_object_device device; struct acpi_object_power_resource power_resource; struct acpi_object_processor processor; struct acpi_object_thermal_zone thermal_zone; struct acpi_object_field_common common_field; struct acpi_object_region_field field; struct acpi_object_buffer_field buffer_field; struct acpi_object_bank_field bank_field; struct acpi_object_index_field index_field; struct acpi_object_notify_handler notify; struct acpi_object_addr_handler address_space; struct acpi_object_reference reference; struct acpi_object_extra extra; struct acpi_object_data data; struct acpi_object_cache_list cache; struct acpi_namespace_node node; }; union acpi_parse_object; union acpi_generic_state; struct acpi_parse_state { u8 *aml_start; u8 *aml; u8 *aml_end; u8 *pkg_start; u8 *pkg_end; union acpi_parse_object *start_op; struct acpi_namespace_node *start_node; union acpi_generic_state *scope; union acpi_parse_object *start_scope; u32 aml_size; }; typedef acpi_status (*acpi_parse_downwards)(struct acpi_walk_state *, union acpi_parse_object **); typedef acpi_status (*acpi_parse_upwards)(struct acpi_walk_state *); struct acpi_opcode_info; struct acpi_walk_state { struct acpi_walk_state *next; u8 descriptor_type; u8 walk_type; u16 opcode; u8 next_op_info; u8 num_operands; u8 operand_index; acpi_owner_id owner_id; u8 last_predicate; u8 current_result; u8 return_used; u8 scope_depth; u8 pass_number; u8 namespace_override; u8 result_size; u8 result_count; u8 *aml; u32 arg_types; u32 method_breakpoint; u32 user_breakpoint; u32 parse_flags; struct acpi_parse_state parser_state; u32 prev_arg_types; u32 arg_count; u16 method_nesting_depth; u8 method_is_nested; struct acpi_namespace_node arguments[7]; struct acpi_namespace_node local_variables[8]; union acpi_operand_object *operands[9]; union acpi_operand_object **params; u8 *aml_last_while; union acpi_operand_object **caller_return_desc; union acpi_generic_state *control_state; struct acpi_namespace_node *deferred_node; union acpi_operand_object *implicit_return_obj; struct acpi_namespace_node *method_call_node; union acpi_parse_object *method_call_op; union acpi_operand_object *method_desc; struct acpi_namespace_node *method_node; char *method_pathname; union acpi_parse_object *op; const struct acpi_opcode_info *op_info; union acpi_parse_object *origin; union acpi_operand_object *result_obj; union acpi_generic_state *results; union acpi_operand_object *return_desc; union acpi_generic_state *scope_info; union acpi_parse_object *prev_op; union acpi_parse_object *next_op; struct acpi_thread_state *thread; acpi_parse_downwards descending_callback; acpi_parse_upwards ascending_callback; }; struct acpi_gpe_handler_info { acpi_gpe_handler address; void *context; struct acpi_namespace_node *method_node; u8 original_flags; u8 originally_enabled; }; struct acpi_gpe_notify_info { struct acpi_namespace_node *device_node; struct acpi_gpe_notify_info *next; }; union acpi_gpe_dispatch_info { struct acpi_namespace_node *method_node; struct acpi_gpe_handler_info *handler; struct acpi_gpe_notify_info *notify_list; }; struct acpi_gpe_register_info; struct acpi_gpe_event_info { union acpi_gpe_dispatch_info dispatch; struct acpi_gpe_register_info *register_info; u8 flags; u8 gpe_number; u8 runtime_count; u8 disable_for_dispatch; }; struct acpi_gpe_address { u8 space_id; u64 address; }; struct acpi_gpe_register_info { struct acpi_gpe_address status_address; struct acpi_gpe_address enable_address; u16 base_gpe_number; u8 enable_for_wake; u8 enable_for_run; u8 mask_for_run; u8 enable_mask; }; struct acpi_gpe_xrupt_info; struct acpi_gpe_block_info { struct acpi_namespace_node *node; struct acpi_gpe_block_info *previous; struct acpi_gpe_block_info *next; struct acpi_gpe_xrupt_info *xrupt_block; struct acpi_gpe_register_info *register_info; struct acpi_gpe_event_info *event_info; u64 address; u32 register_count; u16 gpe_count; u16 block_base_number; u8 space_id; u8 initialized; }; struct acpi_gpe_xrupt_info { struct acpi_gpe_xrupt_info *previous; struct acpi_gpe_xrupt_info *next; struct acpi_gpe_block_info *gpe_block_list_head; u32 interrupt_number; }; struct acpi_common_state { void *next; u8 descriptor_type; u8 flags; u16 value; u16 state; }; struct acpi_update_state { void *next; u8 descriptor_type; u8 flags; u16 value; u16 state; union acpi_operand_object *object; }; struct acpi_pkg_state { void *next; u8 descriptor_type; u8 flags; u16 value; u16 state; u32 index; union acpi_operand_object *source_object; union acpi_operand_object *dest_object; struct acpi_walk_state *walk_state; void *this_target_obj; u32 num_packages; }; struct acpi_control_state { void *next; u8 descriptor_type; u8 flags; u16 value; u16 state; u16 opcode; union acpi_parse_object *predicate_op; u8 *aml_predicate_start; u8 *package_end; u64 loop_timeout; }; union acpi_parse_value { u64 integer; u32 size; char *string; u8 *buffer; char *name; union acpi_parse_object *arg; }; struct acpi_parse_obj_common { union acpi_parse_object *parent; u8 descriptor_type; u8 flags; u16 aml_opcode; u8 *aml; union acpi_parse_object *next; struct acpi_namespace_node *node; union acpi_parse_value value; u8 arg_list_length; u16 disasm_flags; u8 disasm_opcode; char *operator_symbol; char aml_op_name[16]; }; struct acpi_parse_obj_named { union acpi_parse_object *parent; u8 descriptor_type; u8 flags; u16 aml_opcode; u8 *aml; union acpi_parse_object *next; struct acpi_namespace_node *node; union acpi_parse_value value; u8 arg_list_length; u16 disasm_flags; u8 disasm_opcode; char *operator_symbol; char aml_op_name[16]; char *path; u8 *data; u32 length; u32 name; }; struct acpi_parse_obj_asl { union acpi_parse_object *parent; u8 descriptor_type; u8 flags; u16 aml_opcode; u8 *aml; union acpi_parse_object *next; struct acpi_namespace_node *node; union acpi_parse_value value; u8 arg_list_length; u16 disasm_flags; u8 disasm_opcode; char *operator_symbol; char aml_op_name[16]; union acpi_parse_object *child; union acpi_parse_object *parent_method; char *filename; u8 file_changed; char *parent_filename; char *external_name; char *namepath; char name_seg[4]; u32 extra_value; u32 column; u32 line_number; u32 logical_line_number; u32 logical_byte_offset; u32 end_line; u32 end_logical_line; u32 acpi_btype; u32 aml_length; u32 aml_subtree_length; u32 final_aml_length; u32 final_aml_offset; u32 compile_flags; u16 parse_opcode; u8 aml_opcode_length; u8 aml_pkg_len_bytes; u8 extra; char parse_op_name[20]; }; union acpi_parse_object { struct acpi_parse_obj_common common; struct acpi_parse_obj_named named; struct acpi_parse_obj_asl asl; }; struct acpi_scope_state { void *next; u8 descriptor_type; u8 flags; u16 value; u16 state; struct acpi_namespace_node *node; }; struct acpi_pscope_state { void *next; u8 descriptor_type; u8 flags; u16 value; u16 state; u32 arg_count; union acpi_parse_object *op; u8 *arg_end; u8 *pkg_end; u32 arg_list; }; struct acpi_thread_state { void *next; u8 descriptor_type; u8 flags; u16 value; u16 state; u8 current_sync_level; struct acpi_walk_state *walk_state_list; union acpi_operand_object *acquired_mutex_list; u64 thread_id; }; struct acpi_result_values { void *next; u8 descriptor_type; u8 flags; u16 value; u16 state; union acpi_operand_object *obj_desc[8]; }; struct acpi_global_notify_handler { acpi_notify_handler handler; void *context; }; struct acpi_notify_info { void *next; u8 descriptor_type; u8 flags; u16 value; u16 state; u8 handler_list_id; struct acpi_namespace_node *node; union acpi_operand_object *handler_list_head; struct acpi_global_notify_handler *global; }; union acpi_generic_state { struct acpi_common_state common; struct acpi_control_state control; struct acpi_update_state update; struct acpi_scope_state scope; struct acpi_pscope_state parse_scope; struct acpi_pkg_state pkg; struct acpi_thread_state thread; struct acpi_result_values results; struct acpi_notify_info notify; }; struct acpi_opcode_info { char *name; u32 parse_args; u32 runtime_args; u16 flags; u8 object_type; u8 class; u8 type; }; struct acpi_os_dpc { acpi_osd_exec_callback function; void *context; struct work_struct work; }; struct acpi_ioremap { struct list_head list; void *virt; acpi_physical_address phys; acpi_size size; union { long unsigned int refcount; struct rcu_work rwork; } track; }; struct acpi_hp_work { struct work_struct work; struct acpi_device *adev; u32 src; }; struct acpi_pld_info { u8 revision; u8 ignore_color; u8 red; u8 green; u8 blue; u16 width; u16 height; u8 user_visible; u8 dock; u8 lid; u8 panel; u8 vertical_position; u8 horizontal_position; u8 shape; u8 group_orientation; u8 group_token; u8 group_position; u8 bay; u8 ejectable; u8 ospm_eject_required; u8 cabinet_number; u8 card_cage_number; u8 reference; u8 rotation; u8 order; u8 reserved; u16 vertical_offset; u16 horizontal_offset; }; struct acpi_handle_list { u32 count; acpi_handle handles[10]; }; struct acpi_device_bus_id { const char *bus_id; struct ida instance_ida; struct list_head node; }; struct acpi_dev_match_info { struct acpi_device_id hid[2]; const char *uid; s64 hrv; }; struct nvs_region { __u64 phys_start; __u64 size; struct list_head node; }; struct nvs_page { long unsigned int phys_start; unsigned int size; void *kaddr; void *data; bool unmap; struct list_head node; }; struct acpi_wakeup_handler { struct list_head list_node; bool (*wakeup)(void *); void *context; }; typedef u32 acpi_event_status; struct acpi_table_facs { char signature[4]; u32 length; u32 hardware_signature; u32 firmware_waking_vector; u32 global_lock; u32 flags; u64 xfirmware_waking_vector; u8 version; u8 reserved[3]; u32 ospm_flags; u8 reserved1[24]; }; struct acpi_hardware_id { struct list_head list; const char *id; }; struct acpi_data_node { const char *name; acpi_handle handle; struct fwnode_handle fwnode; struct fwnode_handle *parent; struct acpi_device_data data; struct list_head sibling; struct kobject kobj; struct completion kobj_done; }; struct acpi_data_node_attr { struct attribute attr; ssize_t (*show)(struct acpi_data_node *, char *); ssize_t (*store)(struct acpi_data_node *, const char *, size_t); }; struct pm_domain_data { struct list_head list_node; struct device *dev; }; struct acpi_device_physical_node { unsigned int node_id; struct list_head node; struct device *dev; bool put_online: 1; }; typedef u32 (*acpi_event_handler)(void *); typedef acpi_status (*acpi_table_handler)(u32, void *, void *); enum acpi_bus_device_type { ACPI_BUS_TYPE_DEVICE = 0, ACPI_BUS_TYPE_POWER = 1, ACPI_BUS_TYPE_PROCESSOR = 2, ACPI_BUS_TYPE_THERMAL = 3, ACPI_BUS_TYPE_POWER_BUTTON = 4, ACPI_BUS_TYPE_SLEEP_BUTTON = 5, ACPI_BUS_TYPE_ECDT_EC = 6, ACPI_BUS_DEVICE_TYPE_COUNT = 7, }; struct acpi_osc_context { char *uuid_str; int rev; struct acpi_buffer cap; struct acpi_buffer ret; }; struct acpi_pnp_device_id { u32 length; char *string; }; struct acpi_pnp_device_id_list { u32 count; u32 list_size; struct acpi_pnp_device_id ids[0]; }; struct acpi_device_info { u32 info_size; u32 name; acpi_object_type type; u8 param_count; u16 valid; u8 flags; u8 highest_dstates[4]; u8 lowest_dstates[5]; u64 address; struct acpi_pnp_device_id hardware_id; struct acpi_pnp_device_id unique_id; struct acpi_pnp_device_id class_code; struct acpi_pnp_device_id_list compatible_id_list; }; struct acpi_table_spcr { struct acpi_table_header header; u8 interface_type; u8 reserved[3]; struct acpi_generic_address serial_port; u8 interrupt_type; u8 pc_interrupt; u32 interrupt; u8 baud_rate; u8 parity; u8 stop_bits; u8 flow_control; u8 terminal_type; u8 reserved1; u16 pci_device_id; u16 pci_vendor_id; u8 pci_bus; u8 pci_device; u8 pci_function; u32 pci_flags; u8 pci_segment; u32 reserved2; } __attribute__((packed)); struct acpi_table_stao { struct acpi_table_header header; u8 ignore_uart; } __attribute__((packed)); struct acpi_dep_data { struct list_head node; acpi_handle supplier; acpi_handle consumer; }; enum acpi_reconfig_event { ACPI_RECONFIG_DEVICE_ADD = 0, ACPI_RECONFIG_DEVICE_REMOVE = 1, }; struct acpi_probe_entry; typedef bool (*acpi_probe_entry_validate_subtbl)(struct acpi_subtable_header *, struct acpi_probe_entry *); struct acpi_probe_entry { __u8 id[5]; __u8 type; acpi_probe_entry_validate_subtbl subtable_valid; union { acpi_tbl_table_handler probe_table; acpi_tbl_entry_handler probe_subtbl; }; kernel_ulong_t driver_data; }; struct acpi_scan_clear_dep_work { struct work_struct work; struct acpi_device *adev; }; struct resource_win { struct resource res; resource_size_t offset; }; struct res_proc_context { struct list_head *list; int (*preproc)(struct acpi_resource *, void *); void *preproc_data; int count; int error; }; struct acpi_processor_errata { u8 smp; struct { u8 throttle: 1; u8 fdma: 1; u8 reserved: 6; u32 bmisx; } piix4; }; struct acpi_table_ecdt { struct acpi_table_header header; struct acpi_generic_address control; struct acpi_generic_address data; u32 uid; u8 gpe; u8 id[1]; } __attribute__((packed)); struct transaction; struct acpi_ec { acpi_handle handle; int gpe; int irq; long unsigned int command_addr; long unsigned int data_addr; bool global_lock; long unsigned int flags; long unsigned int reference_count; struct mutex mutex; wait_queue_head_t wait; struct list_head list; struct transaction *curr; spinlock_t lock; struct work_struct work; long unsigned int timestamp; long unsigned int nr_pending_queries; bool busy_polling; unsigned int polling_guard; }; struct transaction { const u8 *wdata; u8 *rdata; short unsigned int irq_count; u8 command; u8 wi; u8 ri; u8 wlen; u8 rlen; u8 flags; }; typedef int (*acpi_ec_query_func)(void *); enum ec_command { ACPI_EC_COMMAND_READ = 128, ACPI_EC_COMMAND_WRITE = 129, ACPI_EC_BURST_ENABLE = 130, ACPI_EC_BURST_DISABLE = 131, ACPI_EC_COMMAND_QUERY = 132, }; enum { EC_FLAGS_QUERY_ENABLED = 0, EC_FLAGS_QUERY_PENDING = 1, EC_FLAGS_QUERY_GUARDING = 2, EC_FLAGS_EVENT_HANDLER_INSTALLED = 3, EC_FLAGS_EC_HANDLER_INSTALLED = 4, EC_FLAGS_QUERY_METHODS_INSTALLED = 5, EC_FLAGS_STARTED = 6, EC_FLAGS_STOPPED = 7, EC_FLAGS_EVENTS_MASKED = 8, }; struct acpi_ec_query_handler { struct list_head node; acpi_ec_query_func func; acpi_handle handle; void *data; u8 query_bit; struct kref kref; }; struct acpi_ec_query { struct transaction transaction; struct work_struct work; struct acpi_ec_query_handler *handler; }; struct dock_station { acpi_handle handle; long unsigned int last_dock_time; u32 flags; struct list_head dependent_devices; struct list_head sibling; struct platform_device *dock_device; }; struct dock_dependent_device { struct list_head list; struct acpi_device *adev; }; enum dock_callback_type { DOCK_CALL_HANDLER = 0, DOCK_CALL_FIXUP = 1, DOCK_CALL_UEVENT = 2, }; struct acpi_pci_root_ops; struct acpi_pci_root_info { struct acpi_pci_root *root; struct acpi_device *bridge; struct acpi_pci_root_ops *ops; struct list_head resources; char name[16]; }; struct acpi_pci_root_ops { struct pci_ops *pci_ops; int (*init_info)(struct acpi_pci_root_info *); void (*release_info)(struct acpi_pci_root_info *); int (*prepare_resources)(struct acpi_pci_root_info *); }; struct pci_osc_bit_struct { u32 bit; char *desc; }; struct acpi_handle_node { struct list_head node; acpi_handle handle; }; struct acpi_pci_link_irq { u32 active; u8 triggering; u8 polarity; u8 resource_type; u8 possible_count; u32 possible[16]; u8 initialized: 1; u8 reserved: 7; }; struct acpi_pci_link { struct list_head list; struct acpi_device *device; struct acpi_pci_link_irq irq; int refcnt; }; struct acpi_pci_routing_table { u32 length; u32 pin; u64 address; u32 source_index; char source[4]; }; struct acpi_prt_entry { struct acpi_pci_id id; u8 pin; acpi_handle link; u32 index; }; struct prt_quirk { const struct dmi_system_id *system; unsigned int segment; unsigned int bus; unsigned int device; unsigned char pin; const char *source; const char *actual_source; }; struct lpss_clk_data { const char *name; struct clk *clk; }; struct lpss_private_data; struct lpss_device_desc { unsigned int flags; const char *clk_con_id; unsigned int prv_offset; size_t prv_size_override; struct property_entry *properties; void (*setup)(struct lpss_private_data *); bool resume_from_noirq; }; struct lpss_private_data { struct acpi_device *adev; void *mmio_base; resource_size_t mmio_size; unsigned int fixed_clk_rate; struct clk *clk; const struct lpss_device_desc *dev_desc; u32 prv_reg_ctx[9]; }; struct lpss_device_links { const char *supplier_hid; const char *supplier_uid; const char *consumer_hid; const char *consumer_uid; u32 flags; const struct dmi_system_id *dep_missing_ids; }; struct hid_uid { const char *hid; const char *uid; }; struct fch_clk_data { void *base; u32 is_rv; }; struct apd_private_data; struct apd_device_desc { unsigned int fixed_clk_rate; struct property_entry *properties; int (*setup)(struct apd_private_data *); }; struct apd_private_data { struct clk *clk; struct acpi_device *adev; const struct apd_device_desc *dev_desc; }; struct acpi_power_dependent_device { struct device *dev; struct list_head node; }; struct acpi_power_resource { struct acpi_device device; struct list_head list_node; char *name; u32 system_level; u32 order; unsigned int ref_count; u8 state; bool wakeup_enabled; struct mutex resource_lock; struct list_head dependents; }; struct acpi_power_resource_entry { struct list_head node; struct acpi_power_resource *resource; }; struct acpi_bus_event { struct list_head node; acpi_device_class device_class; acpi_bus_id bus_id; u32 type; u32 data; }; struct acpi_genl_event { acpi_device_class device_class; char bus_id[15]; u32 type; u32 data; }; enum { ACPI_GENL_ATTR_UNSPEC = 0, ACPI_GENL_ATTR_EVENT = 1, __ACPI_GENL_ATTR_MAX = 2, }; enum { ACPI_GENL_CMD_UNSPEC = 0, ACPI_GENL_CMD_EVENT = 1, __ACPI_GENL_CMD_MAX = 2, }; struct acpi_ged_device { struct device *dev; struct list_head event_list; }; struct acpi_ged_event { struct list_head node; struct device *dev; unsigned int gsi; unsigned int irq; acpi_handle handle; }; typedef void (*acpi_gbl_event_handler)(u32, acpi_handle, u32, void *); struct acpi_table_bert { struct acpi_table_header header; u32 region_length; u64 address; }; struct acpi_dlayer { const char *name; long unsigned int value; }; struct acpi_dlevel { const char *name; long unsigned int value; }; struct acpi_table_attr { struct bin_attribute attr; char name[4]; int instance; char filename[8]; struct list_head node; }; struct acpi_data_attr { struct bin_attribute attr; u64 addr; }; struct acpi_data_obj { char *name; int (*fn)(void *, struct acpi_data_attr *); }; struct event_counter { u32 count; u32 flags; }; struct acpi_device_properties { const guid_t *guid; const union acpi_object *properties; struct list_head list; }; struct always_present_id { struct acpi_device_id hid[2]; struct x86_cpu_id cpu_ids[2]; struct dmi_system_id dmi_ids[2]; const char *uid; }; struct lpi_device_info { char *name; int enabled; union acpi_object *package; }; struct lpi_device_constraint { int uid; int min_dstate; int function_states; }; struct lpi_constraints { acpi_handle handle; int min_dstate; }; struct lpi_device_constraint_amd { char *name; int enabled; int function_states; int min_dstate; }; struct acpi_lpat { int temp; int raw; }; struct acpi_lpat_conversion_table { struct acpi_lpat *lpat; int lpat_count; }; enum fpdt_subtable_type { SUBTABLE_FBPT = 0, SUBTABLE_S3PT = 1, }; struct fpdt_subtable_entry { u16 type; u8 length; u8 revision; u32 reserved; u64 address; }; struct fpdt_subtable_header { u32 signature; u32 length; }; enum fpdt_record_type { RECORD_S3_RESUME = 0, RECORD_S3_SUSPEND = 1, RECORD_BOOT = 2, }; struct fpdt_record_header { u16 type; u8 length; u8 revision; }; struct resume_performance_record { struct fpdt_record_header header; u32 resume_count; u64 resume_prev; u64 resume_avg; }; struct boot_performance_record { struct fpdt_record_header header; u32 reserved; u64 firmware_start; u64 bootloader_load; u64 bootloader_launch; u64 exitbootservice_start; u64 exitbootservice_end; }; struct suspend_performance_record { struct fpdt_record_header header; u64 suspend_start; u64 suspend_end; } __attribute__((packed)); struct acpi_table_lpit { struct acpi_table_header header; }; struct acpi_lpit_header { u32 type; u32 length; u16 unique_id; u16 reserved; u32 flags; }; struct acpi_lpit_native { struct acpi_lpit_header header; struct acpi_generic_address entry_trigger; u32 residency; u32 latency; struct acpi_generic_address residency_counter; u64 counter_frequency; } __attribute__((packed)); struct lpit_residency_info { struct acpi_generic_address gaddr; u64 frequency; void *iomem_addr; }; struct acpi_table_wdat { struct acpi_table_header header; u32 header_length; u16 pci_segment; u8 pci_bus; u8 pci_device; u8 pci_function; u8 reserved[3]; u32 timer_period; u32 max_count; u32 min_count; u8 flags; u8 reserved2[3]; u32 entries; }; struct acpi_wdat_entry { u8 action; u8 instruction; u16 reserved; struct acpi_generic_address register_region; u32 value; u32 mask; } __attribute__((packed)); typedef u64 acpi_integer; struct acpi_prmt_module_info { u16 revision; u16 length; u8 module_guid[16]; u16 major_rev; u16 minor_rev; u16 handler_info_count; u32 handler_info_offset; u64 mmio_list_pointer; } __attribute__((packed)); struct acpi_prmt_handler_info { u16 revision; u16 length; u8 handler_guid[16]; u64 handler_address; u64 static_data_buffer_address; u64 acpi_param_buffer_address; } __attribute__((packed)); struct prm_mmio_addr_range { u64 phys_addr; u64 virt_addr; u32 length; } __attribute__((packed)); struct prm_mmio_info { u64 mmio_count; struct prm_mmio_addr_range addr_ranges[0]; }; struct prm_buffer { u8 prm_status; u64 efi_status; u8 prm_cmd; guid_t handler_guid; } __attribute__((packed)); struct prm_context_buffer { char signature[4]; u16 revision; u16 reserved; guid_t identifier; u64 static_data_buffer; struct prm_mmio_info *mmio_ranges; }; struct prm_handler_info { guid_t guid; u64 handler_addr; u64 static_data_buffer_addr; u64 acpi_param_buffer_addr; struct list_head handler_list; }; struct prm_module_info { guid_t guid; u16 major_rev; u16 minor_rev; u16 handler_count; struct prm_mmio_info *mmio_info; bool updatable; struct list_head module_list; struct prm_handler_info handlers[0]; }; struct acpi_name_info { char name[4]; u16 argument_list; u8 expected_btypes; } __attribute__((packed)); struct acpi_package_info { u8 type; u8 object_type1; u8 count1; u8 object_type2; u8 count2; u16 reserved; } __attribute__((packed)); struct acpi_package_info2 { u8 type; u8 count; u8 object_type[4]; u8 reserved; }; struct acpi_package_info3 { u8 type; u8 count; u8 object_type[2]; u8 tail_object_type; u16 reserved; } __attribute__((packed)); struct acpi_package_info4 { u8 type; u8 object_type1; u8 count1; u8 sub_object_types; u8 pkg_count; u16 reserved; } __attribute__((packed)); union acpi_predefined_info { struct acpi_name_info info; struct acpi_package_info ret_info; struct acpi_package_info2 ret_info2; struct acpi_package_info3 ret_info3; struct acpi_package_info4 ret_info4; }; struct acpi_evaluate_info { struct acpi_namespace_node *prefix_node; const char *relative_pathname; union acpi_operand_object **parameters; struct acpi_namespace_node *node; union acpi_operand_object *obj_desc; char *full_pathname; const union acpi_predefined_info *predefined; union acpi_operand_object *return_object; union acpi_operand_object *parent_package; u32 return_flags; u32 return_btype; u16 param_count; u16 node_flags; u8 pass_number; u8 return_object_type; u8 flags; }; enum { ACPI_REFCLASS_LOCAL = 0, ACPI_REFCLASS_ARG = 1, ACPI_REFCLASS_REFOF = 2, ACPI_REFCLASS_INDEX = 3, ACPI_REFCLASS_TABLE = 4, ACPI_REFCLASS_NAME = 5, ACPI_REFCLASS_DEBUG = 6, ACPI_REFCLASS_MAX = 6, }; struct acpi_common_descriptor { void *common_pointer; u8 descriptor_type; }; union acpi_descriptor { struct acpi_common_descriptor common; union acpi_operand_object object; struct acpi_namespace_node node; union acpi_parse_object op; }; typedef enum { ACPI_IMODE_LOAD_PASS1 = 1, ACPI_IMODE_LOAD_PASS2 = 2, ACPI_IMODE_EXECUTE = 3, } acpi_interpreter_mode; struct acpi_create_field_info { struct acpi_namespace_node *region_node; struct acpi_namespace_node *field_node; struct acpi_namespace_node *register_node; struct acpi_namespace_node *data_register_node; struct acpi_namespace_node *connection_node; u8 *resource_buffer; u32 bank_value; u32 field_bit_position; u32 field_bit_length; u16 resource_length; u16 pin_number_index; u8 field_flags; u8 attribute; u8 field_type; u8 access_length; }; struct acpi_init_walk_info { u32 table_index; u32 object_count; u32 method_count; u32 serial_method_count; u32 non_serial_method_count; u32 serialized_method_count; u32 device_count; u32 op_region_count; u32 field_count; u32 buffer_count; u32 package_count; u32 op_region_init; u32 field_init; u32 buffer_init; u32 package_init; acpi_owner_id owner_id; }; typedef u32 acpi_name; typedef acpi_status (*acpi_exception_handler)(acpi_status, acpi_name, u16, u32, void *); enum { AML_FIELD_ACCESS_ANY = 0, AML_FIELD_ACCESS_BYTE = 1, AML_FIELD_ACCESS_WORD = 2, AML_FIELD_ACCESS_DWORD = 3, AML_FIELD_ACCESS_QWORD = 4, AML_FIELD_ACCESS_BUFFER = 5, }; typedef acpi_status (*acpi_execute_op)(struct acpi_walk_state *); struct acpi_fixed_event_handler { acpi_event_handler handler; void *context; }; struct acpi_fixed_event_info { u8 status_register_id; u8 enable_register_id; u16 status_bit_mask; u16 enable_bit_mask; }; typedef u32 acpi_mutex_handle; struct acpi_gpe_walk_info { struct acpi_namespace_node *gpe_device; struct acpi_gpe_block_info *gpe_block; u16 count; acpi_owner_id owner_id; u8 execute_by_owner_id; }; struct acpi_gpe_device_info { u32 index; u32 next_block_base_index; acpi_status status; struct acpi_namespace_node *gpe_device; }; typedef acpi_status (*acpi_gpe_callback)(struct acpi_gpe_xrupt_info *, struct acpi_gpe_block_info *, void *); struct acpi_reg_walk_info { u32 function; u32 reg_run_count; acpi_adr_space_type space_id; }; typedef u32 (*acpi_sci_handler)(void *); struct acpi_sci_handler_info { struct acpi_sci_handler_info *next; acpi_sci_handler address; void *context; }; struct acpi_exdump_info { u8 opcode; u8 offset; const char *name; } __attribute__((packed)); enum { AML_FIELD_UPDATE_PRESERVE = 0, AML_FIELD_UPDATE_WRITE_AS_ONES = 32, AML_FIELD_UPDATE_WRITE_AS_ZEROS = 64, }; struct acpi_signal_fatal_info { u32 type; u32 code; u32 argument; }; enum { MATCH_MTR = 0, MATCH_MEQ = 1, MATCH_MLE = 2, MATCH_MLT = 3, MATCH_MGE = 4, MATCH_MGT = 5, }; enum { AML_FIELD_ATTRIB_QUICK = 2, AML_FIELD_ATTRIB_SEND_RECEIVE = 4, AML_FIELD_ATTRIB_BYTE = 6, AML_FIELD_ATTRIB_WORD = 8, AML_FIELD_ATTRIB_BLOCK = 10, AML_FIELD_ATTRIB_BYTES = 11, AML_FIELD_ATTRIB_PROCESS_CALL = 12, AML_FIELD_ATTRIB_BLOCK_PROCESS_CALL = 13, AML_FIELD_ATTRIB_RAW_BYTES = 14, AML_FIELD_ATTRIB_RAW_PROCESS_BYTES = 15, }; typedef enum { ACPI_TRACE_AML_METHOD = 0, ACPI_TRACE_AML_OPCODE = 1, ACPI_TRACE_AML_REGION = 2, } acpi_trace_event_type; struct acpi_gpe_block_status_context { struct acpi_gpe_register_info *gpe_skip_register_info; u8 gpe_skip_mask; u8 retval; }; struct acpi_bit_register_info { u8 parent_register; u8 bit_position; u16 access_bit_mask; }; struct acpi_port_info { char *name; u16 start; u16 end; u8 osi_dependency; }; struct acpi_pci_device { acpi_handle device; struct acpi_pci_device *next; }; struct acpi_walk_info { u32 debug_level; u32 count; acpi_owner_id owner_id; u8 display_type; }; typedef acpi_status (*acpi_init_handler)(acpi_handle, u32); struct acpi_device_walk_info { struct acpi_table_desc *table_desc; struct acpi_evaluate_info *evaluate_info; u32 device_count; u32 num_STA; u32 num_INI; }; typedef acpi_status (*acpi_pkg_callback)(u8, union acpi_operand_object *, union acpi_generic_state *, void *); struct acpi_table_list { struct acpi_table_desc *tables; u32 current_table_count; u32 max_table_count; u8 flags; }; enum acpi_return_package_types { ACPI_PTYPE1_FIXED = 1, ACPI_PTYPE1_VAR = 2, ACPI_PTYPE1_OPTION = 3, ACPI_PTYPE2 = 4, ACPI_PTYPE2_COUNT = 5, ACPI_PTYPE2_PKG_COUNT = 6, ACPI_PTYPE2_FIXED = 7, ACPI_PTYPE2_MIN = 8, ACPI_PTYPE2_REV_FIXED = 9, ACPI_PTYPE2_FIX_VAR = 10, ACPI_PTYPE2_VAR_VAR = 11, ACPI_PTYPE2_UUID_PAIR = 12, ACPI_PTYPE_CUSTOM = 13, }; typedef acpi_status (*acpi_object_converter)(struct acpi_namespace_node *, union acpi_operand_object *, union acpi_operand_object **); struct acpi_simple_repair_info { char name[4]; u32 unexpected_btypes; u32 package_index; acpi_object_converter object_converter; }; typedef acpi_status (*acpi_repair_function)(struct acpi_evaluate_info *, union acpi_operand_object **); struct acpi_repair_info { char name[4]; acpi_repair_function repair_function; }; struct acpi_namestring_info { const char *external_name; const char *next_external_char; char *internal_name; u32 length; u32 num_segments; u32 num_carats; u8 fully_qualified; }; struct acpi_rw_lock { void *writer_mutex; void *reader_mutex; u32 num_readers; }; struct acpi_get_devices_info { acpi_walk_callback user_function; void *context; const char *hid; }; struct aml_resource_small_header { u8 descriptor_type; }; struct aml_resource_irq { u8 descriptor_type; u16 irq_mask; u8 flags; } __attribute__((packed)); struct aml_resource_dma { u8 descriptor_type; u8 dma_channel_mask; u8 flags; }; struct aml_resource_start_dependent { u8 descriptor_type; u8 flags; }; struct aml_resource_end_dependent { u8 descriptor_type; }; struct aml_resource_io { u8 descriptor_type; u8 flags; u16 minimum; u16 maximum; u8 alignment; u8 address_length; }; struct aml_resource_fixed_io { u8 descriptor_type; u16 address; u8 address_length; } __attribute__((packed)); struct aml_resource_vendor_small { u8 descriptor_type; }; struct aml_resource_end_tag { u8 descriptor_type; u8 checksum; }; struct aml_resource_fixed_dma { u8 descriptor_type; u16 request_lines; u16 channels; u8 width; } __attribute__((packed)); struct aml_resource_large_header { u8 descriptor_type; u16 resource_length; } __attribute__((packed)); struct aml_resource_memory24 { u8 descriptor_type; u16 resource_length; u8 flags; u16 minimum; u16 maximum; u16 alignment; u16 address_length; } __attribute__((packed)); struct aml_resource_vendor_large { u8 descriptor_type; u16 resource_length; } __attribute__((packed)); struct aml_resource_memory32 { u8 descriptor_type; u16 resource_length; u8 flags; u32 minimum; u32 maximum; u32 alignment; u32 address_length; } __attribute__((packed)); struct aml_resource_fixed_memory32 { u8 descriptor_type; u16 resource_length; u8 flags; u32 address; u32 address_length; } __attribute__((packed)); struct aml_resource_address { u8 descriptor_type; u16 resource_length; u8 resource_type; u8 flags; u8 specific_flags; } __attribute__((packed)); struct aml_resource_extended_address64 { u8 descriptor_type; u16 resource_length; u8 resource_type; u8 flags; u8 specific_flags; u8 revision_ID; u8 reserved; u64 granularity; u64 minimum; u64 maximum; u64 translation_offset; u64 address_length; u64 type_specific; } __attribute__((packed)); struct aml_resource_address64 { u8 descriptor_type; u16 resource_length; u8 resource_type; u8 flags; u8 specific_flags; u64 granularity; u64 minimum; u64 maximum; u64 translation_offset; u64 address_length; } __attribute__((packed)); struct aml_resource_address32 { u8 descriptor_type; u16 resource_length; u8 resource_type; u8 flags; u8 specific_flags; u32 granularity; u32 minimum; u32 maximum; u32 translation_offset; u32 address_length; } __attribute__((packed)); struct aml_resource_address16 { u8 descriptor_type; u16 resource_length; u8 resource_type; u8 flags; u8 specific_flags; u16 granularity; u16 minimum; u16 maximum; u16 translation_offset; u16 address_length; } __attribute__((packed)); struct aml_resource_extended_irq { u8 descriptor_type; u16 resource_length; u8 flags; u8 interrupt_count; u32 interrupts[1]; } __attribute__((packed)); struct aml_resource_generic_register { u8 descriptor_type; u16 resource_length; u8 address_space_id; u8 bit_width; u8 bit_offset; u8 access_size; u64 address; } __attribute__((packed)); struct aml_resource_gpio { u8 descriptor_type; u16 resource_length; u8 revision_id; u8 connection_type; u16 flags; u16 int_flags; u8 pin_config; u16 drive_strength; u16 debounce_timeout; u16 pin_table_offset; u8 res_source_index; u16 res_source_offset; u16 vendor_offset; u16 vendor_length; } __attribute__((packed)); struct aml_resource_common_serialbus { u8 descriptor_type; u16 resource_length; u8 revision_id; u8 res_source_index; u8 type; u8 flags; u16 type_specific_flags; u8 type_revision_id; u16 type_data_length; } __attribute__((packed)); struct aml_resource_csi2_serialbus { u8 descriptor_type; u16 resource_length; u8 revision_id; u8 res_source_index; u8 type; u8 flags; u16 type_specific_flags; u8 type_revision_id; u16 type_data_length; } __attribute__((packed)); struct aml_resource_i2c_serialbus { u8 descriptor_type; u16 resource_length; u8 revision_id; u8 res_source_index; u8 type; u8 flags; u16 type_specific_flags; u8 type_revision_id; u16 type_data_length; u32 connection_speed; u16 slave_address; } __attribute__((packed)); struct aml_resource_spi_serialbus { u8 descriptor_type; u16 resource_length; u8 revision_id; u8 res_source_index; u8 type; u8 flags; u16 type_specific_flags; u8 type_revision_id; u16 type_data_length; u32 connection_speed; u8 data_bit_length; u8 clock_phase; u8 clock_polarity; u16 device_selection; } __attribute__((packed)); struct aml_resource_uart_serialbus { u8 descriptor_type; u16 resource_length; u8 revision_id; u8 res_source_index; u8 type; u8 flags; u16 type_specific_flags; u8 type_revision_id; u16 type_data_length; u32 default_baud_rate; u16 rx_fifo_size; u16 tx_fifo_size; u8 parity; u8 lines_enabled; } __attribute__((packed)); struct aml_resource_pin_function { u8 descriptor_type; u16 resource_length; u8 revision_id; u16 flags; u8 pin_config; u16 function_number; u16 pin_table_offset; u8 res_source_index; u16 res_source_offset; u16 vendor_offset; u16 vendor_length; } __attribute__((packed)); struct aml_resource_pin_config { u8 descriptor_type; u16 resource_length; u8 revision_id; u16 flags; u8 pin_config_type; u32 pin_config_value; u16 pin_table_offset; u8 res_source_index; u16 res_source_offset; u16 vendor_offset; u16 vendor_length; } __attribute__((packed)); struct aml_resource_pin_group { u8 descriptor_type; u16 resource_length; u8 revision_id; u16 flags; u16 pin_table_offset; u16 label_offset; u16 vendor_offset; u16 vendor_length; } __attribute__((packed)); struct aml_resource_pin_group_function { u8 descriptor_type; u16 resource_length; u8 revision_id; u16 flags; u16 function_number; u8 res_source_index; u16 res_source_offset; u16 res_source_label_offset; u16 vendor_offset; u16 vendor_length; } __attribute__((packed)); struct aml_resource_pin_group_config { u8 descriptor_type; u16 resource_length; u8 revision_id; u16 flags; u8 pin_config_type; u32 pin_config_value; u8 res_source_index; u16 res_source_offset; u16 res_source_label_offset; u16 vendor_offset; u16 vendor_length; } __attribute__((packed)); union aml_resource { u8 descriptor_type; struct aml_resource_small_header small_header; struct aml_resource_large_header large_header; struct aml_resource_irq irq; struct aml_resource_dma dma; struct aml_resource_start_dependent start_dpf; struct aml_resource_end_dependent end_dpf; struct aml_resource_io io; struct aml_resource_fixed_io fixed_io; struct aml_resource_fixed_dma fixed_dma; struct aml_resource_vendor_small vendor_small; struct aml_resource_end_tag end_tag; struct aml_resource_memory24 memory24; struct aml_resource_generic_register generic_reg; struct aml_resource_vendor_large vendor_large; struct aml_resource_memory32 memory32; struct aml_resource_fixed_memory32 fixed_memory32; struct aml_resource_address16 address16; struct aml_resource_address32 address32; struct aml_resource_address64 address64; struct aml_resource_extended_address64 ext_address64; struct aml_resource_extended_irq extended_irq; struct aml_resource_gpio gpio; struct aml_resource_i2c_serialbus i2c_serial_bus; struct aml_resource_spi_serialbus spi_serial_bus; struct aml_resource_uart_serialbus uart_serial_bus; struct aml_resource_csi2_serialbus csi2_serial_bus; struct aml_resource_common_serialbus common_serial_bus; struct aml_resource_pin_function pin_function; struct aml_resource_pin_config pin_config; struct aml_resource_pin_group pin_group; struct aml_resource_pin_group_function pin_group_function; struct aml_resource_pin_group_config pin_group_config; struct aml_resource_address address; u32 dword_item; u16 word_item; u8 byte_item; }; struct acpi_rsconvert_info { u8 opcode; u8 resource_offset; u8 aml_offset; u8 value; }; enum { ACPI_RSC_INITGET = 0, ACPI_RSC_INITSET = 1, ACPI_RSC_FLAGINIT = 2, ACPI_RSC_1BITFLAG = 3, ACPI_RSC_2BITFLAG = 4, ACPI_RSC_3BITFLAG = 5, ACPI_RSC_6BITFLAG = 6, ACPI_RSC_ADDRESS = 7, ACPI_RSC_BITMASK = 8, ACPI_RSC_BITMASK16 = 9, ACPI_RSC_COUNT = 10, ACPI_RSC_COUNT16 = 11, ACPI_RSC_COUNT_GPIO_PIN = 12, ACPI_RSC_COUNT_GPIO_RES = 13, ACPI_RSC_COUNT_GPIO_VEN = 14, ACPI_RSC_COUNT_SERIAL_RES = 15, ACPI_RSC_COUNT_SERIAL_VEN = 16, ACPI_RSC_DATA8 = 17, ACPI_RSC_EXIT_EQ = 18, ACPI_RSC_EXIT_LE = 19, ACPI_RSC_EXIT_NE = 20, ACPI_RSC_LENGTH = 21, ACPI_RSC_MOVE_GPIO_PIN = 22, ACPI_RSC_MOVE_GPIO_RES = 23, ACPI_RSC_MOVE_SERIAL_RES = 24, ACPI_RSC_MOVE_SERIAL_VEN = 25, ACPI_RSC_MOVE8 = 26, ACPI_RSC_MOVE16 = 27, ACPI_RSC_MOVE32 = 28, ACPI_RSC_MOVE64 = 29, ACPI_RSC_SET8 = 30, ACPI_RSC_SOURCE = 31, ACPI_RSC_SOURCEX = 32, }; typedef u16 acpi_rs_length; typedef acpi_status (*acpi_walk_aml_callback)(u8 *, u32, u32, u8, void **); struct acpi_rsdump_info { u8 opcode; u8 offset; const char *name; const char **pointer; } __attribute__((packed)); enum { ACPI_RSD_TITLE = 0, ACPI_RSD_1BITFLAG = 1, ACPI_RSD_2BITFLAG = 2, ACPI_RSD_3BITFLAG = 3, ACPI_RSD_6BITFLAG = 4, ACPI_RSD_ADDRESS = 5, ACPI_RSD_DWORDLIST = 6, ACPI_RSD_LITERAL = 7, ACPI_RSD_LONGLIST = 8, ACPI_RSD_SHORTLIST = 9, ACPI_RSD_SHORTLISTX = 10, ACPI_RSD_SOURCE = 11, ACPI_RSD_STRING = 12, ACPI_RSD_UINT8 = 13, ACPI_RSD_UINT16 = 14, ACPI_RSD_UINT32 = 15, ACPI_RSD_UINT64 = 16, ACPI_RSD_WORDLIST = 17, ACPI_RSD_LABEL = 18, ACPI_RSD_SOURCE_LABEL = 19, }; typedef u32 acpi_rsdesc_size; struct acpi_vendor_uuid { u8 subtype; u8 data[16]; }; struct acpi_vendor_walk_info { struct acpi_vendor_uuid *uuid; struct acpi_buffer *buffer; acpi_status status; }; struct acpi_fadt_info { const char *name; u16 address64; u16 address32; u16 length; u8 default_length; u8 flags; }; struct acpi_fadt_pm_info { struct acpi_generic_address *target; u16 source; u8 register_num; }; struct acpi_table_rsdp { char signature[8]; u8 checksum; char oem_id[6]; u8 revision; u32 rsdt_physical_address; u32 length; u64 xsdt_physical_address; u8 extended_checksum; u8 reserved[3]; } __attribute__((packed)); struct acpi_address_range { struct acpi_address_range *next; struct acpi_namespace_node *region_node; acpi_physical_address start_address; acpi_physical_address end_address; }; struct acpi_pkg_info { u8 *free_space; acpi_size length; u32 object_space; u32 num_packages; }; struct acpi_exception_info { char *name; }; struct acpi_mutex_info { void *mutex; u32 use_count; u64 thread_id; }; struct acpi_comment_node { char *comment; struct acpi_comment_node *next; }; struct acpi_interface_info { char *name; struct acpi_interface_info *next; u8 flags; u8 value; }; enum led_brightness { LED_OFF = 0, LED_ON = 1, LED_HALF = 127, LED_FULL = 255, }; struct led_hw_trigger_type { int dummy; }; struct led_pattern; struct led_trigger; struct led_classdev { const char *name; unsigned int brightness; unsigned int max_brightness; int flags; long unsigned int work_flags; void (*brightness_set)(struct led_classdev *, enum led_brightness); int (*brightness_set_blocking)(struct led_classdev *, enum led_brightness); enum led_brightness (*brightness_get)(struct led_classdev *); int (*blink_set)(struct led_classdev *, long unsigned int *, long unsigned int *); int (*pattern_set)(struct led_classdev *, struct led_pattern *, u32, int); int (*pattern_clear)(struct led_classdev *); struct device *dev; const struct attribute_group **groups; struct list_head node; const char *default_trigger; long unsigned int blink_delay_on; long unsigned int blink_delay_off; struct timer_list blink_timer; int blink_brightness; int new_blink_brightness; void (*flash_resume)(struct led_classdev *); struct work_struct set_brightness_work; int delayed_set_value; struct rw_semaphore trigger_lock; struct led_trigger *trigger; struct list_head trig_list; void *trigger_data; bool activated; struct led_hw_trigger_type *trigger_type; int brightness_hw_changed; struct kernfs_node *brightness_hw_changed_kn; struct mutex led_access; }; struct led_pattern { u32 delta_t; int brightness; }; struct led_trigger { const char *name; int (*activate)(struct led_classdev *); void (*deactivate)(struct led_classdev *); struct led_hw_trigger_type *trigger_type; rwlock_t leddev_list_lock; struct list_head led_cdevs; struct list_head next_trig; const struct attribute_group **groups; }; enum power_supply_property { POWER_SUPPLY_PROP_STATUS = 0, POWER_SUPPLY_PROP_CHARGE_TYPE = 1, POWER_SUPPLY_PROP_HEALTH = 2, POWER_SUPPLY_PROP_PRESENT = 3, POWER_SUPPLY_PROP_ONLINE = 4, POWER_SUPPLY_PROP_AUTHENTIC = 5, POWER_SUPPLY_PROP_TECHNOLOGY = 6, POWER_SUPPLY_PROP_CYCLE_COUNT = 7, POWER_SUPPLY_PROP_VOLTAGE_MAX = 8, POWER_SUPPLY_PROP_VOLTAGE_MIN = 9, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN = 10, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN = 11, POWER_SUPPLY_PROP_VOLTAGE_NOW = 12, POWER_SUPPLY_PROP_VOLTAGE_AVG = 13, POWER_SUPPLY_PROP_VOLTAGE_OCV = 14, POWER_SUPPLY_PROP_VOLTAGE_BOOT = 15, POWER_SUPPLY_PROP_CURRENT_MAX = 16, POWER_SUPPLY_PROP_CURRENT_NOW = 17, POWER_SUPPLY_PROP_CURRENT_AVG = 18, POWER_SUPPLY_PROP_CURRENT_BOOT = 19, POWER_SUPPLY_PROP_POWER_NOW = 20, POWER_SUPPLY_PROP_POWER_AVG = 21, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN = 22, POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN = 23, POWER_SUPPLY_PROP_CHARGE_FULL = 24, POWER_SUPPLY_PROP_CHARGE_EMPTY = 25, POWER_SUPPLY_PROP_CHARGE_NOW = 26, POWER_SUPPLY_PROP_CHARGE_AVG = 27, POWER_SUPPLY_PROP_CHARGE_COUNTER = 28, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT = 29, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX = 30, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE = 31, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX = 32, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT = 33, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX = 34, POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD = 35, POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD = 36, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT = 37, POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT = 38, POWER_SUPPLY_PROP_INPUT_POWER_LIMIT = 39, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN = 40, POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN = 41, POWER_SUPPLY_PROP_ENERGY_FULL = 42, POWER_SUPPLY_PROP_ENERGY_EMPTY = 43, POWER_SUPPLY_PROP_ENERGY_NOW = 44, POWER_SUPPLY_PROP_ENERGY_AVG = 45, POWER_SUPPLY_PROP_CAPACITY = 46, POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN = 47, POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX = 48, POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN = 49, POWER_SUPPLY_PROP_CAPACITY_LEVEL = 50, POWER_SUPPLY_PROP_TEMP = 51, POWER_SUPPLY_PROP_TEMP_MAX = 52, POWER_SUPPLY_PROP_TEMP_MIN = 53, POWER_SUPPLY_PROP_TEMP_ALERT_MIN = 54, POWER_SUPPLY_PROP_TEMP_ALERT_MAX = 55, POWER_SUPPLY_PROP_TEMP_AMBIENT = 56, POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN = 57, POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX = 58, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW = 59, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG = 60, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW = 61, POWER_SUPPLY_PROP_TIME_TO_FULL_AVG = 62, POWER_SUPPLY_PROP_TYPE = 63, POWER_SUPPLY_PROP_USB_TYPE = 64, POWER_SUPPLY_PROP_SCOPE = 65, POWER_SUPPLY_PROP_PRECHARGE_CURRENT = 66, POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT = 67, POWER_SUPPLY_PROP_CALIBRATE = 68, POWER_SUPPLY_PROP_MANUFACTURE_YEAR = 69, POWER_SUPPLY_PROP_MANUFACTURE_MONTH = 70, POWER_SUPPLY_PROP_MANUFACTURE_DAY = 71, POWER_SUPPLY_PROP_MODEL_NAME = 72, POWER_SUPPLY_PROP_MANUFACTURER = 73, POWER_SUPPLY_PROP_SERIAL_NUMBER = 74, }; enum power_supply_type { POWER_SUPPLY_TYPE_UNKNOWN = 0, POWER_SUPPLY_TYPE_BATTERY = 1, POWER_SUPPLY_TYPE_UPS = 2, POWER_SUPPLY_TYPE_MAINS = 3, POWER_SUPPLY_TYPE_USB = 4, POWER_SUPPLY_TYPE_USB_DCP = 5, POWER_SUPPLY_TYPE_USB_CDP = 6, POWER_SUPPLY_TYPE_USB_ACA = 7, POWER_SUPPLY_TYPE_USB_TYPE_C = 8, POWER_SUPPLY_TYPE_USB_PD = 9, POWER_SUPPLY_TYPE_USB_PD_DRP = 10, POWER_SUPPLY_TYPE_APPLE_BRICK_ID = 11, POWER_SUPPLY_TYPE_WIRELESS = 12, }; enum power_supply_usb_type { POWER_SUPPLY_USB_TYPE_UNKNOWN = 0, POWER_SUPPLY_USB_TYPE_SDP = 1, POWER_SUPPLY_USB_TYPE_DCP = 2, POWER_SUPPLY_USB_TYPE_CDP = 3, POWER_SUPPLY_USB_TYPE_ACA = 4, POWER_SUPPLY_USB_TYPE_C = 5, POWER_SUPPLY_USB_TYPE_PD = 6, POWER_SUPPLY_USB_TYPE_PD_DRP = 7, POWER_SUPPLY_USB_TYPE_PD_PPS = 8, POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID = 9, }; union power_supply_propval { int intval; const char *strval; }; struct power_supply_config { struct device_node *of_node; struct fwnode_handle *fwnode; void *drv_data; const struct attribute_group **attr_grp; char **supplied_to; size_t num_supplicants; }; struct power_supply; struct power_supply_desc { const char *name; enum power_supply_type type; const enum power_supply_usb_type *usb_types; size_t num_usb_types; const enum power_supply_property *properties; size_t num_properties; int (*get_property)(struct power_supply *, enum power_supply_property, union power_supply_propval *); int (*set_property)(struct power_supply *, enum power_supply_property, const union power_supply_propval *); int (*property_is_writeable)(struct power_supply *, enum power_supply_property); void (*external_power_changed)(struct power_supply *); void (*set_charged)(struct power_supply *); bool no_thermal; int use_for_apm; }; struct thermal_zone_device; struct power_supply { const struct power_supply_desc *desc; char **supplied_to; size_t num_supplicants; char **supplied_from; size_t num_supplies; struct device_node *of_node; void *drv_data; struct device dev; struct work_struct changed_work; struct delayed_work deferred_register_work; spinlock_t changed_lock; bool changed; bool initialized; bool removing; atomic_t use_cnt; struct thermal_zone_device *tzd; struct thermal_cooling_device *tcd; struct led_trigger *charging_full_trig; char *charging_full_trig_name; struct led_trigger *charging_trig; char *charging_trig_name; struct led_trigger *full_trig; char *full_trig_name; struct led_trigger *online_trig; char *online_trig_name; struct led_trigger *charging_blink_full_solid_trig; char *charging_blink_full_solid_trig_name; }; struct acpi_ac_bl { const char *hid; int hrv; }; struct acpi_ac { struct power_supply *charger; struct power_supply_desc charger_desc; struct acpi_device *device; long long unsigned int state; struct notifier_block battery_nb; }; struct input_id { __u16 bustype; __u16 vendor; __u16 product; __u16 version; }; struct input_absinfo { __s32 value; __s32 minimum; __s32 maximum; __s32 fuzz; __s32 flat; __s32 resolution; }; struct input_keymap_entry { __u8 flags; __u8 len; __u16 index; __u32 keycode; __u8 scancode[32]; }; struct ff_replay { __u16 length; __u16 delay; }; struct ff_trigger { __u16 button; __u16 interval; }; struct ff_envelope { __u16 attack_length; __u16 attack_level; __u16 fade_length; __u16 fade_level; }; struct ff_constant_effect { __s16 level; struct ff_envelope envelope; }; struct ff_ramp_effect { __s16 start_level; __s16 end_level; struct ff_envelope envelope; }; struct ff_condition_effect { __u16 right_saturation; __u16 left_saturation; __s16 right_coeff; __s16 left_coeff; __u16 deadband; __s16 center; }; struct ff_periodic_effect { __u16 waveform; __u16 period; __s16 magnitude; __s16 offset; __u16 phase; struct ff_envelope envelope; __u32 custom_len; __s16 *custom_data; }; struct ff_rumble_effect { __u16 strong_magnitude; __u16 weak_magnitude; }; struct ff_effect { __u16 type; __s16 id; __u16 direction; struct ff_trigger trigger; struct ff_replay replay; union { struct ff_constant_effect constant; struct ff_ramp_effect ramp; struct ff_periodic_effect periodic; struct ff_condition_effect condition[2]; struct ff_rumble_effect rumble; } u; }; struct input_device_id { kernel_ulong_t flags; __u16 bustype; __u16 vendor; __u16 product; __u16 version; kernel_ulong_t evbit[1]; kernel_ulong_t keybit[12]; kernel_ulong_t relbit[1]; kernel_ulong_t absbit[1]; kernel_ulong_t mscbit[1]; kernel_ulong_t ledbit[1]; kernel_ulong_t sndbit[1]; kernel_ulong_t ffbit[2]; kernel_ulong_t swbit[1]; kernel_ulong_t propbit[1]; kernel_ulong_t driver_info; }; struct input_value { __u16 type; __u16 code; __s32 value; }; enum input_clock_type { INPUT_CLK_REAL = 0, INPUT_CLK_MONO = 1, INPUT_CLK_BOOT = 2, INPUT_CLK_MAX = 3, }; struct ff_device; struct input_dev_poller; struct input_mt; struct input_handle; struct input_dev { const char *name; const char *phys; const char *uniq; struct input_id id; long unsigned int propbit[1]; long unsigned int evbit[1]; long unsigned int keybit[12]; long unsigned int relbit[1]; long unsigned int absbit[1]; long unsigned int mscbit[1]; long unsigned int ledbit[1]; long unsigned int sndbit[1]; long unsigned int ffbit[2]; long unsigned int swbit[1]; unsigned int hint_events_per_packet; unsigned int keycodemax; unsigned int keycodesize; void *keycode; int (*setkeycode)(struct input_dev *, const struct input_keymap_entry *, unsigned int *); int (*getkeycode)(struct input_dev *, struct input_keymap_entry *); struct ff_device *ff; struct input_dev_poller *poller; unsigned int repeat_key; struct timer_list timer; int rep[2]; struct input_mt *mt; struct input_absinfo *absinfo; long unsigned int key[12]; long unsigned int led[1]; long unsigned int snd[1]; long unsigned int sw[1]; int (*open)(struct input_dev *); void (*close)(struct input_dev *); int (*flush)(struct input_dev *, struct file *); int (*event)(struct input_dev *, unsigned int, unsigned int, int); struct input_handle *grab; spinlock_t event_lock; struct mutex mutex; unsigned int users; bool going_away; struct device dev; struct list_head h_list; struct list_head node; unsigned int num_vals; unsigned int max_vals; struct input_value *vals; bool devres_managed; ktime_t timestamp[3]; bool inhibited; }; struct ff_device { int (*upload)(struct input_dev *, struct ff_effect *, struct ff_effect *); int (*erase)(struct input_dev *, int); int (*playback)(struct input_dev *, int, int); void (*set_gain)(struct input_dev *, u16); void (*set_autocenter)(struct input_dev *, u16); void (*destroy)(struct ff_device *); void *private; long unsigned int ffbit[2]; struct mutex mutex; int max_effects; struct ff_effect *effects; struct file *effect_owners[0]; }; struct input_handler; struct input_handle { void *private; int open; const char *name; struct input_dev *dev; struct input_handler *handler; struct list_head d_node; struct list_head h_node; }; struct input_handler { void *private; void (*event)(struct input_handle *, unsigned int, unsigned int, int); void (*events)(struct input_handle *, const struct input_value *, unsigned int); bool (*filter)(struct input_handle *, unsigned int, unsigned int, int); bool (*match)(struct input_handler *, struct input_dev *); int (*connect)(struct input_handler *, struct input_dev *, const struct input_device_id *); void (*disconnect)(struct input_handle *); void (*start)(struct input_handle *); bool legacy_minors; int minor; const char *name; const struct input_device_id *id_table; struct list_head h_list; struct list_head node; }; enum { ACPI_BUTTON_LID_INIT_IGNORE = 0, ACPI_BUTTON_LID_INIT_OPEN = 1, ACPI_BUTTON_LID_INIT_METHOD = 2, ACPI_BUTTON_LID_INIT_DISABLED = 3, }; struct acpi_button { unsigned int type; struct input_dev *input; char phys[32]; long unsigned int pushed; int last_state; ktime_t last_time; bool suspended; bool lid_state_initialized; }; struct acpi_fan_fps { u64 control; u64 trip_point; u64 speed; u64 noise_level; u64 power; char name[20]; struct device_attribute dev_attr; }; struct acpi_fan_fif { u64 revision; u64 fine_grain_ctrl; u64 step_size; u64 low_speed_notification; }; struct acpi_fan { bool acpi4; struct acpi_fan_fif fif; struct acpi_fan_fps *fps; int fps_count; struct thermal_cooling_device *cdev; }; struct acpi_pci_slot { struct pci_slot *pci_slot; struct list_head list; }; struct acpi_lpi_states_array { unsigned int size; unsigned int composite_states_size; struct acpi_lpi_state *entries; struct acpi_lpi_state *composite_states[8]; }; struct throttling_tstate { unsigned int cpu; int target_state; }; struct acpi_processor_throttling_arg { struct acpi_processor *pr; int target_state; bool force; }; struct container_dev { struct device dev; int (*offline)(struct container_dev *); }; enum thermal_device_mode { THERMAL_DEVICE_DISABLED = 0, THERMAL_DEVICE_ENABLED = 1, }; enum thermal_trip_type { THERMAL_TRIP_ACTIVE = 0, THERMAL_TRIP_PASSIVE = 1, THERMAL_TRIP_HOT = 2, THERMAL_TRIP_CRITICAL = 3, }; enum thermal_trend { THERMAL_TREND_STABLE = 0, THERMAL_TREND_RAISING = 1, THERMAL_TREND_DROPPING = 2, THERMAL_TREND_RAISE_FULL = 3, THERMAL_TREND_DROP_FULL = 4, }; enum thermal_notify_event { THERMAL_EVENT_UNSPECIFIED = 0, THERMAL_EVENT_TEMP_SAMPLE = 1, THERMAL_TRIP_VIOLATED = 2, THERMAL_TRIP_CHANGED = 3, THERMAL_DEVICE_DOWN = 4, THERMAL_DEVICE_UP = 5, THERMAL_DEVICE_POWER_CAPABILITY_CHANGED = 6, THERMAL_TABLE_CHANGED = 7, THERMAL_EVENT_KEEP_ALIVE = 8, }; struct thermal_zone_device_ops { int (*bind)(struct thermal_zone_device *, struct thermal_cooling_device *); int (*unbind)(struct thermal_zone_device *, struct thermal_cooling_device *); int (*get_temp)(struct thermal_zone_device *, int *); int (*set_trips)(struct thermal_zone_device *, int, int); int (*change_mode)(struct thermal_zone_device *, enum thermal_device_mode); int (*get_trip_type)(struct thermal_zone_device *, int, enum thermal_trip_type *); int (*get_trip_temp)(struct thermal_zone_device *, int, int *); int (*set_trip_temp)(struct thermal_zone_device *, int, int); int (*get_trip_hyst)(struct thermal_zone_device *, int, int *); int (*set_trip_hyst)(struct thermal_zone_device *, int, int); int (*get_crit_temp)(struct thermal_zone_device *, int *); int (*set_emul_temp)(struct thermal_zone_device *, int); int (*get_trend)(struct thermal_zone_device *, int, enum thermal_trend *); void (*hot)(struct thermal_zone_device *); void (*critical)(struct thermal_zone_device *); }; struct thermal_attr; struct thermal_zone_params; struct thermal_governor; struct thermal_zone_device { int id; char type[20]; struct device device; struct attribute_group trips_attribute_group; struct thermal_attr *trip_temp_attrs; struct thermal_attr *trip_type_attrs; struct thermal_attr *trip_hyst_attrs; enum thermal_device_mode mode; void *devdata; int trips; long unsigned int trips_disabled; long unsigned int passive_delay_jiffies; long unsigned int polling_delay_jiffies; int temperature; int last_temperature; int emul_temperature; int passive; int prev_low_trip; int prev_high_trip; atomic_t need_update; struct thermal_zone_device_ops *ops; struct thermal_zone_params *tzp; struct thermal_governor *governor; void *governor_data; struct list_head thermal_instances; struct ida ida; struct mutex lock; struct list_head node; struct delayed_work poll_queue; enum thermal_notify_event notify_event; }; struct thermal_bind_params; struct thermal_zone_params { char governor_name[20]; bool no_hwmon; int num_tbps; struct thermal_bind_params *tbp; u32 sustainable_power; s32 k_po; s32 k_pu; s32 k_i; s32 k_d; s32 integral_cutoff; int slope; int offset; }; struct thermal_governor { char name[20]; int (*bind_to_tz)(struct thermal_zone_device *); void (*unbind_from_tz)(struct thermal_zone_device *); int (*throttle)(struct thermal_zone_device *, int); struct list_head governor_list; }; struct thermal_bind_params { struct thermal_cooling_device *cdev; int weight; int trip_mask; long unsigned int *binding_limits; int (*match)(struct thermal_zone_device *, struct thermal_cooling_device *); }; struct acpi_thermal_state { u8 critical: 1; u8 hot: 1; u8 passive: 1; u8 active: 1; u8 reserved: 4; int active_index; }; struct acpi_thermal_state_flags { u8 valid: 1; u8 enabled: 1; u8 reserved: 6; }; struct acpi_thermal_critical { struct acpi_thermal_state_flags flags; long unsigned int temperature; }; struct acpi_thermal_hot { struct acpi_thermal_state_flags flags; long unsigned int temperature; }; struct acpi_thermal_passive { struct acpi_thermal_state_flags flags; long unsigned int temperature; long unsigned int tc1; long unsigned int tc2; long unsigned int tsp; struct acpi_handle_list devices; }; struct acpi_thermal_active { struct acpi_thermal_state_flags flags; long unsigned int temperature; struct acpi_handle_list devices; }; struct acpi_thermal_trips { struct acpi_thermal_critical critical; struct acpi_thermal_hot hot; struct acpi_thermal_passive passive; struct acpi_thermal_active active[10]; }; struct acpi_thermal_flags { u8 cooling_mode: 1; u8 devices: 1; u8 reserved: 6; }; struct acpi_thermal { struct acpi_device *device; acpi_bus_id name; long unsigned int temperature; long unsigned int last_temperature; long unsigned int polling_frequency; volatile u8 zombie; struct acpi_thermal_flags flags; struct acpi_thermal_state state; struct acpi_thermal_trips trips; struct acpi_handle_list devices; struct thermal_zone_device *thermal_zone; int kelvin_offset; struct work_struct thermal_check_work; struct mutex thermal_check_lock; refcount_t thermal_check_count; }; struct acpi_table_slit { struct acpi_table_header header; u64 locality_count; u8 entry[1]; } __attribute__((packed)); struct acpi_table_srat { struct acpi_table_header header; u32 table_revision; u64 reserved; }; enum acpi_srat_type { ACPI_SRAT_TYPE_CPU_AFFINITY = 0, ACPI_SRAT_TYPE_MEMORY_AFFINITY = 1, ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY = 2, ACPI_SRAT_TYPE_GICC_AFFINITY = 3, ACPI_SRAT_TYPE_GIC_ITS_AFFINITY = 4, ACPI_SRAT_TYPE_GENERIC_AFFINITY = 5, ACPI_SRAT_TYPE_RESERVED = 6, }; struct acpi_srat_mem_affinity { struct acpi_subtable_header header; u32 proximity_domain; u16 reserved; u64 base_address; u64 length; u32 reserved1; u32 flags; u64 reserved2; } __attribute__((packed)); struct acpi_srat_gicc_affinity { struct acpi_subtable_header header; u32 proximity_domain; u32 acpi_processor_uid; u32 flags; u32 clock_domain; } __attribute__((packed)); struct acpi_srat_generic_affinity { struct acpi_subtable_header header; u8 reserved; u8 device_handle_type; u32 proximity_domain; u8 device_handle[16]; u32 flags; u32 reserved1; }; enum acpi_hmat_type { ACPI_HMAT_TYPE_PROXIMITY = 0, ACPI_HMAT_TYPE_LOCALITY = 1, ACPI_HMAT_TYPE_CACHE = 2, ACPI_HMAT_TYPE_RESERVED = 3, }; struct acpi_hmat_proximity_domain { struct acpi_hmat_structure header; u16 flags; u16 reserved1; u32 processor_PD; u32 memory_PD; u32 reserved2; u64 reserved3; u64 reserved4; }; struct acpi_hmat_locality { struct acpi_hmat_structure header; u8 flags; u8 data_type; u8 min_transfer_size; u8 reserved1; u32 number_of_initiator_Pds; u32 number_of_target_Pds; u32 reserved2; u64 entry_base_unit; }; struct acpi_hmat_cache { struct acpi_hmat_structure header; u32 memory_PD; u32 reserved1; u64 cache_size; u32 cache_attributes; u16 reserved2; u16 number_of_SMBIOShandles; }; struct node_hmem_attrs { unsigned int read_bandwidth; unsigned int write_bandwidth; unsigned int read_latency; unsigned int write_latency; }; enum cache_indexing { NODE_CACHE_DIRECT_MAP = 0, NODE_CACHE_INDEXED = 1, NODE_CACHE_OTHER = 2, }; enum cache_write_policy { NODE_CACHE_WRITE_BACK = 0, NODE_CACHE_WRITE_THROUGH = 1, NODE_CACHE_WRITE_OTHER = 2, }; struct node_cache_attrs { enum cache_indexing indexing; enum cache_write_policy write_policy; u64 size; u16 line_size; u8 level; }; enum locality_types { WRITE_LATENCY = 0, READ_LATENCY = 1, WRITE_BANDWIDTH = 2, READ_BANDWIDTH = 3, }; struct memory_locality { struct list_head node; struct acpi_hmat_locality *hmat_loc; }; struct target_cache { struct list_head node; struct node_cache_attrs cache_attrs; }; struct memory_target { struct list_head node; unsigned int memory_pxm; unsigned int processor_pxm; struct resource memregions; struct node_hmem_attrs hmem_attrs[2]; struct list_head caches; struct node_cache_attrs cache_attrs; bool registered; }; struct memory_initiator { struct list_head node; unsigned int processor_pxm; bool has_cpu; }; struct acpi_memory_info { struct list_head list; u64 start_addr; u64 length; short unsigned int caching; short unsigned int write_protect; unsigned int enabled: 1; }; struct acpi_memory_device { struct acpi_device *device; struct list_head res_list; }; struct acpi_pci_ioapic { acpi_handle root_handle; acpi_handle handle; u32 gsi_base; struct resource res; struct pci_dev *pdev; struct list_head list; }; enum dmi_entry_type { DMI_ENTRY_BIOS = 0, DMI_ENTRY_SYSTEM = 1, DMI_ENTRY_BASEBOARD = 2, DMI_ENTRY_CHASSIS = 3, DMI_ENTRY_PROCESSOR = 4, DMI_ENTRY_MEM_CONTROLLER = 5, DMI_ENTRY_MEM_MODULE = 6, DMI_ENTRY_CACHE = 7, DMI_ENTRY_PORT_CONNECTOR = 8, DMI_ENTRY_SYSTEM_SLOT = 9, DMI_ENTRY_ONBOARD_DEVICE = 10, DMI_ENTRY_OEMSTRINGS = 11, DMI_ENTRY_SYSCONF = 12, DMI_ENTRY_BIOS_LANG = 13, DMI_ENTRY_GROUP_ASSOC = 14, DMI_ENTRY_SYSTEM_EVENT_LOG = 15, DMI_ENTRY_PHYS_MEM_ARRAY = 16, DMI_ENTRY_MEM_DEVICE = 17, DMI_ENTRY_32_MEM_ERROR = 18, DMI_ENTRY_MEM_ARRAY_MAPPED_ADDR = 19, DMI_ENTRY_MEM_DEV_MAPPED_ADDR = 20, DMI_ENTRY_BUILTIN_POINTING_DEV = 21, DMI_ENTRY_PORTABLE_BATTERY = 22, DMI_ENTRY_SYSTEM_RESET = 23, DMI_ENTRY_HW_SECURITY = 24, DMI_ENTRY_SYSTEM_POWER_CONTROLS = 25, DMI_ENTRY_VOLTAGE_PROBE = 26, DMI_ENTRY_COOLING_DEV = 27, DMI_ENTRY_TEMP_PROBE = 28, DMI_ENTRY_ELECTRICAL_CURRENT_PROBE = 29, DMI_ENTRY_OOB_REMOTE_ACCESS = 30, DMI_ENTRY_BIS_ENTRY = 31, DMI_ENTRY_SYSTEM_BOOT = 32, DMI_ENTRY_MGMT_DEV = 33, DMI_ENTRY_MGMT_DEV_COMPONENT = 34, DMI_ENTRY_MGMT_DEV_THRES = 35, DMI_ENTRY_MEM_CHANNEL = 36, DMI_ENTRY_IPMI_DEV = 37, DMI_ENTRY_SYS_POWER_SUPPLY = 38, DMI_ENTRY_ADDITIONAL = 39, DMI_ENTRY_ONBOARD_DEV_EXT = 40, DMI_ENTRY_MGMT_CONTROLLER_HOST = 41, DMI_ENTRY_INACTIVE = 126, DMI_ENTRY_END_OF_TABLE = 127, }; enum { POWER_SUPPLY_STATUS_UNKNOWN = 0, POWER_SUPPLY_STATUS_CHARGING = 1, POWER_SUPPLY_STATUS_DISCHARGING = 2, POWER_SUPPLY_STATUS_NOT_CHARGING = 3, POWER_SUPPLY_STATUS_FULL = 4, }; enum { POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0, POWER_SUPPLY_TECHNOLOGY_NiMH = 1, POWER_SUPPLY_TECHNOLOGY_LION = 2, POWER_SUPPLY_TECHNOLOGY_LIPO = 3, POWER_SUPPLY_TECHNOLOGY_LiFe = 4, POWER_SUPPLY_TECHNOLOGY_NiCd = 5, POWER_SUPPLY_TECHNOLOGY_LiMn = 6, }; enum { POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0, POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL = 1, POWER_SUPPLY_CAPACITY_LEVEL_LOW = 2, POWER_SUPPLY_CAPACITY_LEVEL_NORMAL = 3, POWER_SUPPLY_CAPACITY_LEVEL_HIGH = 4, POWER_SUPPLY_CAPACITY_LEVEL_FULL = 5, }; struct acpi_battery_hook { const char *name; int (*add_battery)(struct power_supply *); int (*remove_battery)(struct power_supply *); struct list_head list; }; enum { ACPI_BATTERY_ALARM_PRESENT = 0, ACPI_BATTERY_XINFO_PRESENT = 1, ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY = 2, ACPI_BATTERY_QUIRK_THINKPAD_MAH = 3, ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE = 4, }; struct acpi_battery { struct mutex lock; struct mutex sysfs_lock; struct power_supply *bat; struct power_supply_desc bat_desc; struct acpi_device *device; struct notifier_block pm_nb; struct list_head list; long unsigned int update_time; int revision; int rate_now; int capacity_now; int voltage_now; int design_capacity; int full_charge_capacity; int technology; int design_voltage; int design_capacity_warning; int design_capacity_low; int cycle_count; int measurement_accuracy; int max_sampling_time; int min_sampling_time; int max_averaging_interval; int min_averaging_interval; int capacity_granularity_1; int capacity_granularity_2; int alarm; char model_number[32]; char serial_number[32]; char type[32]; char oem_info[32]; int state; int power_unit; long unsigned int flags; }; struct acpi_offsets { size_t offset; u8 mode; }; struct acpi_pcct_hw_reduced { struct acpi_subtable_header header; u32 platform_interrupt; u8 flags; u8 reserved; u64 base_address; u64 length; struct acpi_generic_address doorbell_register; u64 preserve_mask; u64 write_mask; u32 latency; u32 max_access_rate; u16 min_turnaround_time; } __attribute__((packed)); struct acpi_pcct_shared_memory { u32 signature; u16 command; u16 status; }; struct mbox_chan; struct mbox_chan_ops { int (*send_data)(struct mbox_chan *, void *); int (*flush)(struct mbox_chan *, long unsigned int); int (*startup)(struct mbox_chan *); void (*shutdown)(struct mbox_chan *); bool (*last_tx_done)(struct mbox_chan *); bool (*peek_data)(struct mbox_chan *); }; struct mbox_controller; struct mbox_client; struct mbox_chan { struct mbox_controller *mbox; unsigned int txdone_method; struct mbox_client *cl; struct completion tx_complete; void *active_req; unsigned int msg_count; unsigned int msg_free; void *msg_data[20]; spinlock_t lock; void *con_priv; }; struct mbox_controller { struct device *dev; const struct mbox_chan_ops *ops; struct mbox_chan *chans; int num_chans; bool txdone_irq; bool txdone_poll; unsigned int txpoll_period; struct mbox_chan * (*of_xlate)(struct mbox_controller *, const struct of_phandle_args *); struct hrtimer poll_hrt; struct list_head node; }; struct mbox_client { struct device *dev; bool tx_block; long unsigned int tx_tout; bool knows_txdone; void (*rx_callback)(struct mbox_client *, void *); void (*tx_prepare)(struct mbox_client *, void *); void (*tx_done)(struct mbox_client *, void *, int); }; struct cpc_register_resource { acpi_object_type type; u64 *sys_mem_vaddr; union { struct cpc_reg reg; u64 int_value; } cpc_entry; }; struct cpc_desc { int num_entries; int version; int cpu_id; int write_cmd_status; int write_cmd_id; struct cpc_register_resource cpc_regs[21]; struct acpi_psd_package domain_info; struct kobject kobj; }; enum cppc_regs { HIGHEST_PERF = 0, NOMINAL_PERF = 1, LOW_NON_LINEAR_PERF = 2, LOWEST_PERF = 3, GUARANTEED_PERF = 4, DESIRED_PERF = 5, MIN_PERF = 6, MAX_PERF = 7, PERF_REDUC_TOLERANCE = 8, TIME_WINDOW = 9, CTR_WRAP_TIME = 10, REFERENCE_CTR = 11, DELIVERED_CTR = 12, PERF_LIMITED = 13, ENABLE = 14, AUTO_SEL_ENABLE = 15, AUTO_ACT_WINDOW = 16, ENERGY_PERF = 17, REFERENCE_PERF = 18, LOWEST_FREQ = 19, NOMINAL_FREQ = 20, }; struct cppc_perf_ctrls { u32 max_perf; u32 min_perf; u32 desired_perf; }; struct cppc_perf_fb_ctrs { u64 reference; u64 delivered; u64 reference_perf; u64 wraparound_time; }; struct cppc_cpudata { struct list_head node; struct cppc_perf_caps perf_caps; struct cppc_perf_ctrls perf_ctrls; struct cppc_perf_fb_ctrs perf_fb_ctrs; unsigned int shared_type; cpumask_var_t shared_cpu_map; }; struct cppc_pcc_data { struct mbox_chan *pcc_channel; void *pcc_comm_addr; bool pcc_channel_acquired; unsigned int deadline_us; unsigned int pcc_mpar; unsigned int pcc_mrtt; unsigned int pcc_nominal; bool pending_pcc_write_cmd; bool platform_owns_pcc; unsigned int pcc_write_cnt; struct rw_semaphore pcc_lock; wait_queue_head_t pcc_write_wait_q; ktime_t last_cmd_cmpl_time; ktime_t last_mpar_reset; int mpar_count; int refcount; }; struct acpi_whea_header { u8 action; u8 instruction; u8 flags; u8 reserved; struct acpi_generic_address register_region; u64 value; u64 mask; } __attribute__((packed)); struct apei_exec_context; typedef int (*apei_exec_ins_func_t)(struct apei_exec_context *, struct acpi_whea_header *); struct apei_exec_ins_type; struct apei_exec_context { u32 ip; u64 value; u64 var1; u64 var2; u64 src_base; u64 dst_base; struct apei_exec_ins_type *ins_table; u32 instructions; struct acpi_whea_header *action_table; u32 entries; }; struct apei_exec_ins_type { u32 flags; apei_exec_ins_func_t run; }; struct apei_resources { struct list_head iomem; struct list_head ioport; }; typedef int (*apei_exec_entry_func_t)(struct apei_exec_context *, struct acpi_whea_header *, void *); struct apei_res { struct list_head list; long unsigned int start; long unsigned int end; }; struct acpi_table_hest { struct acpi_table_header header; u32 error_source_count; }; enum acpi_hest_types { ACPI_HEST_TYPE_IA32_CHECK = 0, ACPI_HEST_TYPE_IA32_CORRECTED_CHECK = 1, ACPI_HEST_TYPE_IA32_NMI = 2, ACPI_HEST_TYPE_NOT_USED3 = 3, ACPI_HEST_TYPE_NOT_USED4 = 4, ACPI_HEST_TYPE_NOT_USED5 = 5, ACPI_HEST_TYPE_AER_ROOT_PORT = 6, ACPI_HEST_TYPE_AER_ENDPOINT = 7, ACPI_HEST_TYPE_AER_BRIDGE = 8, ACPI_HEST_TYPE_GENERIC_ERROR = 9, ACPI_HEST_TYPE_GENERIC_ERROR_V2 = 10, ACPI_HEST_TYPE_IA32_DEFERRED_CHECK = 11, ACPI_HEST_TYPE_RESERVED = 12, }; struct acpi_hest_ia_machine_check { struct acpi_hest_header header; u16 reserved1; u8 flags; u8 enabled; u32 records_to_preallocate; u32 max_sections_per_record; u64 global_capability_data; u64 global_control_data; u8 num_hardware_banks; u8 reserved3[7]; }; struct acpi_hest_generic { struct acpi_hest_header header; u16 related_source_id; u8 reserved; u8 enabled; u32 records_to_preallocate; u32 max_sections_per_record; u32 max_raw_data_length; struct acpi_generic_address error_status_address; struct acpi_hest_notify notify; u32 error_block_length; } __attribute__((packed)); struct acpi_hest_ia_deferred_check { struct acpi_hest_header header; u16 reserved1; u8 flags; u8 enabled; u32 records_to_preallocate; u32 max_sections_per_record; struct acpi_hest_notify notify; u8 num_hardware_banks; u8 reserved2[3]; }; enum hest_status { HEST_ENABLED = 0, HEST_DISABLED = 1, HEST_NOT_FOUND = 2, }; typedef int (*apei_hest_func_t)(struct acpi_hest_header *, void *); struct ghes_arr { struct platform_device **ghes_devs; unsigned int count; }; struct acpi_table_erst { struct acpi_table_header header; u32 header_length; u32 reserved; u32 entries; }; enum acpi_erst_actions { ACPI_ERST_BEGIN_WRITE = 0, ACPI_ERST_BEGIN_READ = 1, ACPI_ERST_BEGIN_CLEAR = 2, ACPI_ERST_END = 3, ACPI_ERST_SET_RECORD_OFFSET = 4, ACPI_ERST_EXECUTE_OPERATION = 5, ACPI_ERST_CHECK_BUSY_STATUS = 6, ACPI_ERST_GET_COMMAND_STATUS = 7, ACPI_ERST_GET_RECORD_ID = 8, ACPI_ERST_SET_RECORD_ID = 9, ACPI_ERST_GET_RECORD_COUNT = 10, ACPI_ERST_BEGIN_DUMMY_WRIITE = 11, ACPI_ERST_NOT_USED = 12, ACPI_ERST_GET_ERROR_RANGE = 13, ACPI_ERST_GET_ERROR_LENGTH = 14, ACPI_ERST_GET_ERROR_ATTRIBUTES = 15, ACPI_ERST_EXECUTE_TIMINGS = 16, ACPI_ERST_ACTION_RESERVED = 17, }; enum acpi_erst_instructions { ACPI_ERST_READ_REGISTER = 0, ACPI_ERST_READ_REGISTER_VALUE = 1, ACPI_ERST_WRITE_REGISTER = 2, ACPI_ERST_WRITE_REGISTER_VALUE = 3, ACPI_ERST_NOOP = 4, ACPI_ERST_LOAD_VAR1 = 5, ACPI_ERST_LOAD_VAR2 = 6, ACPI_ERST_STORE_VAR1 = 7, ACPI_ERST_ADD = 8, ACPI_ERST_SUBTRACT = 9, ACPI_ERST_ADD_VALUE = 10, ACPI_ERST_SUBTRACT_VALUE = 11, ACPI_ERST_STALL = 12, ACPI_ERST_STALL_WHILE_TRUE = 13, ACPI_ERST_SKIP_NEXT_IF_TRUE = 14, ACPI_ERST_GOTO = 15, ACPI_ERST_SET_SRC_ADDRESS_BASE = 16, ACPI_ERST_SET_DST_ADDRESS_BASE = 17, ACPI_ERST_MOVE_DATA = 18, ACPI_ERST_INSTRUCTION_RESERVED = 19, }; struct erst_erange { u64 base; u64 size; void *vaddr; u32 attr; }; struct erst_record_id_cache { struct mutex lock; u64 *entries; int len; int size; int refcount; }; struct cper_pstore_record { struct cper_record_header hdr; struct cper_section_descriptor sec_hdr; char data[0]; }; struct acpi_bert_region { u32 block_status; u32 raw_data_offset; u32 raw_data_length; u32 data_length; u32 error_severity; }; struct acpi_hest_generic_status { u32 block_status; u32 raw_data_offset; u32 raw_data_length; u32 data_length; u32 error_severity; }; enum acpi_hest_notify_types { ACPI_HEST_NOTIFY_POLLED = 0, ACPI_HEST_NOTIFY_EXTERNAL = 1, ACPI_HEST_NOTIFY_LOCAL = 2, ACPI_HEST_NOTIFY_SCI = 3, ACPI_HEST_NOTIFY_NMI = 4, ACPI_HEST_NOTIFY_CMCI = 5, ACPI_HEST_NOTIFY_MCE = 6, ACPI_HEST_NOTIFY_GPIO = 7, ACPI_HEST_NOTIFY_SEA = 8, ACPI_HEST_NOTIFY_SEI = 9, ACPI_HEST_NOTIFY_GSIV = 10, ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED = 11, ACPI_HEST_NOTIFY_RESERVED = 12, }; struct acpi_hest_generic_v2 { struct acpi_hest_header header; u16 related_source_id; u8 reserved; u8 enabled; u32 records_to_preallocate; u32 max_sections_per_record; u32 max_raw_data_length; struct acpi_generic_address error_status_address; struct acpi_hest_notify notify; u32 error_block_length; struct acpi_generic_address read_ack_register; u64 read_ack_preserve; u64 read_ack_write; } __attribute__((packed)); struct acpi_hest_generic_data { u8 section_type[16]; u32 error_severity; u16 revision; u8 validation_bits; u8 flags; u32 error_data_length; u8 fru_id[16]; u8 fru_text[20]; }; struct acpi_hest_generic_data_v300 { u8 section_type[16]; u32 error_severity; u16 revision; u8 validation_bits; u8 flags; u32 error_data_length; u8 fru_id[16]; u8 fru_text[20]; u64 time_stamp; }; struct cper_sec_proc_arm { u32 validation_bits; u16 err_info_num; u16 context_info_num; u32 section_length; u8 affinity_level; u8 reserved[3]; u64 mpidr; u64 midr; u32 running_state; u32 psci_state; }; struct cper_arm_err_info { u8 version; u8 length; u16 validation_bits; u8 type; u16 multiple_error; u8 flags; u64 error_info; u64 virt_fault_addr; u64 physical_fault_addr; } __attribute__((packed)); struct cper_sec_pcie { u64 validation_bits; u32 port_type; struct { u8 minor; u8 major; u8 reserved[2]; } version; u16 command; u16 status; u32 reserved; struct { u16 vendor_id; u16 device_id; u8 class_code[3]; u8 function; u8 device; u16 segment; u8 bus; u8 secondary_bus; u16 slot; u8 reserved; } __attribute__((packed)) device_id; struct { u32 lower; u32 upper; } serial_number; struct { u16 secondary_status; u16 control; } bridge; u8 capability[60]; u8 aer_info[96]; }; struct ghes { union { struct acpi_hest_generic *generic; struct acpi_hest_generic_v2 *generic_v2; }; struct acpi_hest_generic_status *estatus; long unsigned int flags; union { struct list_head list; struct timer_list timer; unsigned int irq; }; }; struct ghes_estatus_node { struct llist_node llnode; struct acpi_hest_generic *generic; struct ghes *ghes; int task_work_cpu; struct callback_head task_work; }; struct ghes_estatus_cache { u32 estatus_len; atomic_t count; struct acpi_hest_generic *generic; long long unsigned int time_in; struct callback_head rcu; }; struct ghes_vendor_record_entry { struct work_struct work; int error_severity; char vendor_record[0]; }; struct pmic_table { int address; int reg; int bit; }; struct intel_pmic_opregion_data { int (*get_power)(struct regmap *, int, int, u64 *); int (*update_power)(struct regmap *, int, int, bool); int (*get_raw_temp)(struct regmap *, int); int (*update_aux)(struct regmap *, int, int); int (*get_policy)(struct regmap *, int, int, u64 *); int (*update_policy)(struct regmap *, int, int, int); int (*exec_mipi_pmic_seq_element)(struct regmap *, u16, u32, u32, u32); struct pmic_table *power_table; int power_table_count; struct pmic_table *thermal_table; int thermal_table_count; int pmic_i2c_address; }; struct intel_pmic_regs_handler_ctx { unsigned int val; u16 addr; }; struct intel_pmic_opregion { struct mutex lock; struct acpi_lpat_conversion_table *lpat_table; struct regmap *regmap; struct intel_pmic_opregion_data *data; struct intel_pmic_regs_handler_ctx ctx; }; struct regmap_irq_type { unsigned int type_reg_offset; unsigned int type_reg_mask; unsigned int type_rising_val; unsigned int type_falling_val; unsigned int type_level_low_val; unsigned int type_level_high_val; unsigned int types_supported; }; struct regmap_irq { unsigned int reg_offset; unsigned int mask; struct regmap_irq_type type; }; struct regmap_irq_sub_irq_map { unsigned int num_regs; unsigned int *offset; }; struct regmap_irq_chip { const char *name; unsigned int main_status; unsigned int num_main_status_bits; struct regmap_irq_sub_irq_map *sub_reg_offsets; int num_main_regs; unsigned int status_base; unsigned int mask_base; unsigned int unmask_base; unsigned int ack_base; unsigned int wake_base; unsigned int type_base; unsigned int *virt_reg_base; unsigned int irq_reg_stride; bool mask_writeonly: 1; bool init_ack_masked: 1; bool mask_invert: 1; bool use_ack: 1; bool ack_invert: 1; bool clear_ack: 1; bool wake_invert: 1; bool runtime_pm: 1; bool type_invert: 1; bool type_in_mask: 1; bool clear_on_unmask: 1; bool not_fixed_stride: 1; bool status_invert: 1; int num_regs; const struct regmap_irq *irqs; int num_irqs; int num_type_reg; int num_virt_regs; unsigned int type_reg_stride; int (*handle_pre_irq)(void *); int (*handle_post_irq)(void *); int (*set_type_virt)(unsigned int **, unsigned int, long unsigned int, int); void *irq_drv_data; }; struct axp20x_dev { struct device *dev; int irq; long unsigned int irq_flags; struct regmap *regmap; struct regmap_irq_chip_data *regmap_irqc; long int variant; int nr_cells; const struct mfd_cell *cells; const struct regmap_config *regmap_cfg; const struct regmap_irq_chip *regmap_irq_chip; }; struct mfd_cell_acpi_match; struct mfd_cell { const char *name; int id; int level; int (*enable)(struct platform_device *); int (*disable)(struct platform_device *); int (*suspend)(struct platform_device *); int (*resume)(struct platform_device *); void *platform_data; size_t pdata_size; const struct software_node *swnode; const char *of_compatible; const u64 of_reg; bool use_of_reg; const struct mfd_cell_acpi_match *acpi_match; int num_resources; const struct resource *resources; bool ignore_resource_conflicts; bool pm_runtime_no_callbacks; const char * const *parent_supplies; int num_parent_supplies; }; struct tps68470_pmic_table { u32 address; u32 reg; u32 bitmask; }; struct tps68470_pmic_opregion { struct mutex lock; struct regmap *regmap; }; struct acpi_table_viot { struct acpi_table_header header; u16 node_count; u16 node_offset; u8 reserved[8]; }; struct acpi_viot_header { u8 type; u8 reserved; u16 length; }; enum acpi_viot_node_type { ACPI_VIOT_NODE_PCI_RANGE = 1, ACPI_VIOT_NODE_MMIO = 2, ACPI_VIOT_NODE_VIRTIO_IOMMU_PCI = 3, ACPI_VIOT_NODE_VIRTIO_IOMMU_MMIO = 4, ACPI_VIOT_RESERVED = 5, }; struct acpi_viot_pci_range { struct acpi_viot_header header; u32 endpoint_start; u16 segment_start; u16 segment_end; u16 bdf_start; u16 bdf_end; u16 output_node; u8 reserved[6]; }; struct acpi_viot_mmio { struct acpi_viot_header header; u32 endpoint; u64 base_address; u16 output_node; u8 reserved[6]; }; struct acpi_viot_virtio_iommu_pci { struct acpi_viot_header header; u16 segment; u16 bdf; u8 reserved[8]; }; struct acpi_viot_virtio_iommu_mmio { struct acpi_viot_header header; u8 reserved[4]; u64 base_address; }; struct viot_iommu { unsigned int offset; struct fwnode_handle *fwnode; struct list_head list; }; struct viot_endpoint { union { struct { u16 segment_start; u16 segment_end; u16 bdf_start; u16 bdf_end; }; u64 address; }; u32 endpoint_id; struct viot_iommu *viommu; struct list_head list; }; struct pnp_resource { struct list_head list; struct resource res; }; struct pnp_port { resource_size_t min; resource_size_t max; resource_size_t align; resource_size_t size; unsigned char flags; }; typedef struct { long unsigned int bits[4]; } pnp_irq_mask_t; struct pnp_irq { pnp_irq_mask_t map; unsigned char flags; }; struct pnp_dma { unsigned char map; unsigned char flags; }; struct pnp_mem { resource_size_t min; resource_size_t max; resource_size_t align; resource_size_t size; unsigned char flags; }; struct pnp_option { struct list_head list; unsigned int flags; long unsigned int type; union { struct pnp_port port; struct pnp_irq irq; struct pnp_dma dma; struct pnp_mem mem; } u; }; struct pnp_info_buffer { char *buffer; char *curr; long unsigned int size; long unsigned int len; int stop; int error; }; typedef struct pnp_info_buffer pnp_info_buffer_t; struct pnp_fixup { char id[7]; void (*quirk_function)(struct pnp_dev *); }; struct acpipnp_parse_option_s { struct pnp_dev *dev; unsigned int option_flags; }; struct clk_bulk_data { const char *id; struct clk *clk; }; struct clk_bulk_devres { struct clk_bulk_data *clks; int num_clks; }; struct clk_hw; struct clk_lookup { struct list_head node; const char *dev_id; const char *con_id; struct clk *clk; struct clk_hw *clk_hw; }; struct clk_init_data; struct clk_hw { struct clk_core *core; struct clk *clk; const struct clk_init_data *init; }; struct clk_rate_request { long unsigned int rate; long unsigned int min_rate; long unsigned int max_rate; long unsigned int best_parent_rate; struct clk_hw *best_parent_hw; }; struct clk_duty { unsigned int num; unsigned int den; }; struct clk_ops { int (*prepare)(struct clk_hw *); void (*unprepare)(struct clk_hw *); int (*is_prepared)(struct clk_hw *); void (*unprepare_unused)(struct clk_hw *); int (*enable)(struct clk_hw *); void (*disable)(struct clk_hw *); int (*is_enabled)(struct clk_hw *); void (*disable_unused)(struct clk_hw *); int (*save_context)(struct clk_hw *); void (*restore_context)(struct clk_hw *); long unsigned int (*recalc_rate)(struct clk_hw *, long unsigned int); long int (*round_rate)(struct clk_hw *, long unsigned int, long unsigned int *); int (*determine_rate)(struct clk_hw *, struct clk_rate_request *); int (*set_parent)(struct clk_hw *, u8); u8 (*get_parent)(struct clk_hw *); int (*set_rate)(struct clk_hw *, long unsigned int, long unsigned int); int (*set_rate_and_parent)(struct clk_hw *, long unsigned int, long unsigned int, u8); long unsigned int (*recalc_accuracy)(struct clk_hw *, long unsigned int); int (*get_phase)(struct clk_hw *); int (*set_phase)(struct clk_hw *, int); int (*get_duty_cycle)(struct clk_hw *, struct clk_duty *); int (*set_duty_cycle)(struct clk_hw *, struct clk_duty *); int (*init)(struct clk_hw *); void (*terminate)(struct clk_hw *); void (*debug_init)(struct clk_hw *, struct dentry *); }; struct clk_parent_data { const struct clk_hw *hw; const char *fw_name; const char *name; int index; }; struct clk_init_data { const char *name; const struct clk_ops *ops; const char * const *parent_names; const struct clk_parent_data *parent_data; const struct clk_hw **parent_hws; u8 num_parents; long unsigned int flags; }; struct clk_lookup_alloc { struct clk_lookup cl; char dev_id[20]; char con_id[16]; }; struct clk_notifier { struct clk *clk; struct srcu_notifier_head notifier_head; struct list_head node; }; struct clk_notifier_data { struct clk *clk; long unsigned int old_rate; long unsigned int new_rate; }; struct clk_parent_map; struct clk_core { const char *name; const struct clk_ops *ops; struct clk_hw *hw; struct module *owner; struct device *dev; struct device_node *of_node; struct clk_core *parent; struct clk_parent_map *parents; u8 num_parents; u8 new_parent_index; long unsigned int rate; long unsigned int req_rate; long unsigned int new_rate; struct clk_core *new_parent; struct clk_core *new_child; long unsigned int flags; bool orphan; bool rpm_enabled; unsigned int enable_count; unsigned int prepare_count; unsigned int protect_count; long unsigned int min_rate; long unsigned int max_rate; long unsigned int accuracy; int phase; struct clk_duty duty; struct hlist_head children; struct hlist_node child_node; struct hlist_head clks; unsigned int notifier_count; struct dentry *dentry; struct hlist_node debug_node; struct kref ref; }; struct clk_parent_map { const struct clk_hw *hw; struct clk_core *core; const char *fw_name; const char *name; int index; }; struct trace_event_raw_clk { struct trace_entry ent; u32 __data_loc_name; char __data[0]; }; struct trace_event_raw_clk_rate { struct trace_entry ent; u32 __data_loc_name; long unsigned int rate; char __data[0]; }; struct trace_event_raw_clk_rate_range { struct trace_entry ent; u32 __data_loc_name; long unsigned int min; long unsigned int max; char __data[0]; }; struct trace_event_raw_clk_parent { struct trace_entry ent; u32 __data_loc_name; u32 __data_loc_pname; char __data[0]; }; struct trace_event_raw_clk_phase { struct trace_entry ent; u32 __data_loc_name; int phase; char __data[0]; }; struct trace_event_raw_clk_duty_cycle { struct trace_entry ent; u32 __data_loc_name; unsigned int num; unsigned int den; char __data[0]; }; struct trace_event_data_offsets_clk { u32 name; }; struct trace_event_data_offsets_clk_rate { u32 name; }; struct trace_event_data_offsets_clk_rate_range { u32 name; }; struct trace_event_data_offsets_clk_parent { u32 name; u32 pname; }; struct trace_event_data_offsets_clk_phase { u32 name; }; struct trace_event_data_offsets_clk_duty_cycle { u32 name; }; typedef void (*btf_trace_clk_enable)(void *, struct clk_core *); typedef void (*btf_trace_clk_enable_complete)(void *, struct clk_core *); typedef void (*btf_trace_clk_disable)(void *, struct clk_core *); typedef void (*btf_trace_clk_disable_complete)(void *, struct clk_core *); typedef void (*btf_trace_clk_prepare)(void *, struct clk_core *); typedef void (*btf_trace_clk_prepare_complete)(void *, struct clk_core *); typedef void (*btf_trace_clk_unprepare)(void *, struct clk_core *); typedef void (*btf_trace_clk_unprepare_complete)(void *, struct clk_core *); typedef void (*btf_trace_clk_set_rate)(void *, struct clk_core *, long unsigned int); typedef void (*btf_trace_clk_set_rate_complete)(void *, struct clk_core *, long unsigned int); typedef void (*btf_trace_clk_set_min_rate)(void *, struct clk_core *, long unsigned int); typedef void (*btf_trace_clk_set_max_rate)(void *, struct clk_core *, long unsigned int); typedef void (*btf_trace_clk_set_rate_range)(void *, struct clk_core *, long unsigned int, long unsigned int); typedef void (*btf_trace_clk_set_parent)(void *, struct clk_core *, struct clk_core *); typedef void (*btf_trace_clk_set_parent_complete)(void *, struct clk_core *, struct clk_core *); typedef void (*btf_trace_clk_set_phase)(void *, struct clk_core *, int); typedef void (*btf_trace_clk_set_phase_complete)(void *, struct clk_core *, int); typedef void (*btf_trace_clk_set_duty_cycle)(void *, struct clk_core *, struct clk_duty *); typedef void (*btf_trace_clk_set_duty_cycle_complete)(void *, struct clk_core *, struct clk_duty *); struct clk_notifier_devres { struct clk *clk; struct notifier_block *nb; }; struct clk_div_table { unsigned int val; unsigned int div; }; struct clk_divider { struct clk_hw hw; void *reg; u8 shift; u8 width; u8 flags; const struct clk_div_table *table; spinlock_t *lock; }; struct clk_fixed_factor { struct clk_hw hw; unsigned int mult; unsigned int div; }; struct clk_fixed_rate { struct clk_hw hw; long unsigned int fixed_rate; long unsigned int fixed_accuracy; long unsigned int flags; }; struct clk_gate { struct clk_hw hw; void *reg; u8 bit_idx; u8 flags; spinlock_t *lock; }; struct clk_multiplier { struct clk_hw hw; void *reg; u8 shift; u8 width; u8 flags; spinlock_t *lock; }; struct clk_mux { struct clk_hw hw; void *reg; u32 *table; u32 mask; u8 shift; u8 flags; spinlock_t *lock; }; struct clk_composite { struct clk_hw hw; struct clk_ops ops; struct clk_hw *mux_hw; struct clk_hw *rate_hw; struct clk_hw *gate_hw; const struct clk_ops *mux_ops; const struct clk_ops *rate_ops; const struct clk_ops *gate_ops; }; struct clk_fractional_divider { struct clk_hw hw; void *reg; u8 mshift; u8 mwidth; u32 mmask; u8 nshift; u8 nwidth; u32 nmask; u8 flags; void (*approximation)(struct clk_hw *, long unsigned int, long unsigned int *, long unsigned int *, long unsigned int *); spinlock_t *lock; }; struct clk_gpio { struct clk_hw hw; struct gpio_desc *gpiod; }; struct pmc_clk { const char *name; long unsigned int freq; const char *parent_name; }; struct pmc_clk_data { void *base; const struct pmc_clk *clks; bool critical; }; struct clk_plt_fixed { struct clk_hw *clk; struct clk_lookup *lookup; }; struct clk_plt { struct clk_hw hw; void *reg; struct clk_lookup *lookup; spinlock_t lock; }; struct clk_plt_data { struct clk_plt_fixed **parents; u8 nparents; struct clk_plt *clks[6]; struct clk_lookup *mclk_lookup; struct clk_lookup *ether_clk_lookup; }; typedef s32 dma_cookie_t; enum dma_status { DMA_COMPLETE = 0, DMA_IN_PROGRESS = 1, DMA_PAUSED = 2, DMA_ERROR = 3, DMA_OUT_OF_ORDER = 4, }; enum dma_transaction_type { DMA_MEMCPY = 0, DMA_XOR = 1, DMA_PQ = 2, DMA_XOR_VAL = 3, DMA_PQ_VAL = 4, DMA_MEMSET = 5, DMA_MEMSET_SG = 6, DMA_INTERRUPT = 7, DMA_PRIVATE = 8, DMA_ASYNC_TX = 9, DMA_SLAVE = 10, DMA_CYCLIC = 11, DMA_INTERLEAVE = 12, DMA_COMPLETION_NO_ORDER = 13, DMA_REPEAT = 14, DMA_LOAD_EOT = 15, DMA_TX_TYPE_END = 16, }; enum dma_transfer_direction { DMA_MEM_TO_MEM = 0, DMA_MEM_TO_DEV = 1, DMA_DEV_TO_MEM = 2, DMA_DEV_TO_DEV = 3, DMA_TRANS_NONE = 4, }; struct data_chunk { size_t size; size_t icg; size_t dst_icg; size_t src_icg; }; struct dma_interleaved_template { dma_addr_t src_start; dma_addr_t dst_start; enum dma_transfer_direction dir; bool src_inc; bool dst_inc; bool src_sgl; bool dst_sgl; size_t numf; size_t frame_size; struct data_chunk sgl[0]; }; enum dma_ctrl_flags { DMA_PREP_INTERRUPT = 1, DMA_CTRL_ACK = 2, DMA_PREP_PQ_DISABLE_P = 4, DMA_PREP_PQ_DISABLE_Q = 8, DMA_PREP_CONTINUE = 16, DMA_PREP_FENCE = 32, DMA_CTRL_REUSE = 64, DMA_PREP_CMD = 128, DMA_PREP_REPEAT = 256, DMA_PREP_LOAD_EOT = 512, }; enum sum_check_bits { SUM_CHECK_P = 0, SUM_CHECK_Q = 1, }; enum sum_check_flags { SUM_CHECK_P_RESULT = 1, SUM_CHECK_Q_RESULT = 2, }; typedef struct { long unsigned int bits[1]; } dma_cap_mask_t; enum dma_desc_metadata_mode { DESC_METADATA_NONE = 0, DESC_METADATA_CLIENT = 1, DESC_METADATA_ENGINE = 2, }; struct dma_chan_percpu { long unsigned int memcpy_count; long unsigned int bytes_transferred; }; struct dma_router { struct device *dev; void (*route_free)(struct device *, void *); }; struct dma_device; struct dma_chan_dev; struct dma_chan___2 { struct dma_device *device; struct device *slave; dma_cookie_t cookie; dma_cookie_t completed_cookie; int chan_id; struct dma_chan_dev *dev; const char *name; char *dbg_client_name; struct list_head device_node; struct dma_chan_percpu *local; int client_count; int table_count; struct dma_router *router; void *route_data; void *private; }; typedef bool (*dma_filter_fn)(struct dma_chan___2 *, void *); struct dma_slave_map; struct dma_filter { dma_filter_fn fn; int mapcnt; const struct dma_slave_map *map; }; enum dmaengine_alignment { DMAENGINE_ALIGN_1_BYTE = 0, DMAENGINE_ALIGN_2_BYTES = 1, DMAENGINE_ALIGN_4_BYTES = 2, DMAENGINE_ALIGN_8_BYTES = 3, DMAENGINE_ALIGN_16_BYTES = 4, DMAENGINE_ALIGN_32_BYTES = 5, DMAENGINE_ALIGN_64_BYTES = 6, DMAENGINE_ALIGN_128_BYTES = 7, DMAENGINE_ALIGN_256_BYTES = 8, }; enum dma_residue_granularity { DMA_RESIDUE_GRANULARITY_DESCRIPTOR = 0, DMA_RESIDUE_GRANULARITY_SEGMENT = 1, DMA_RESIDUE_GRANULARITY_BURST = 2, }; struct dma_async_tx_descriptor; struct dma_slave_caps; struct dma_slave_config; struct dma_tx_state; struct dma_device { struct kref ref; unsigned int chancnt; unsigned int privatecnt; struct list_head channels; struct list_head global_node; struct dma_filter filter; dma_cap_mask_t cap_mask; enum dma_desc_metadata_mode desc_metadata_modes; short unsigned int max_xor; short unsigned int max_pq; enum dmaengine_alignment copy_align; enum dmaengine_alignment xor_align; enum dmaengine_alignment pq_align; enum dmaengine_alignment fill_align; int dev_id; struct device *dev; struct module *owner; struct ida chan_ida; struct mutex chan_mutex; u32 src_addr_widths; u32 dst_addr_widths; u32 directions; u32 min_burst; u32 max_burst; u32 max_sg_burst; bool descriptor_reuse; enum dma_residue_granularity residue_granularity; int (*device_alloc_chan_resources)(struct dma_chan___2 *); int (*device_router_config)(struct dma_chan___2 *); void (*device_free_chan_resources)(struct dma_chan___2 *); struct dma_async_tx_descriptor * (*device_prep_dma_memcpy)(struct dma_chan___2 *, dma_addr_t, dma_addr_t, size_t, long unsigned int); struct dma_async_tx_descriptor * (*device_prep_dma_xor)(struct dma_chan___2 *, dma_addr_t, dma_addr_t *, unsigned int, size_t, long unsigned int); struct dma_async_tx_descriptor * (*device_prep_dma_xor_val)(struct dma_chan___2 *, dma_addr_t *, unsigned int, size_t, enum sum_check_flags *, long unsigned int); struct dma_async_tx_descriptor * (*device_prep_dma_pq)(struct dma_chan___2 *, dma_addr_t *, dma_addr_t *, unsigned int, const unsigned char *, size_t, long unsigned int); struct dma_async_tx_descriptor * (*device_prep_dma_pq_val)(struct dma_chan___2 *, dma_addr_t *, dma_addr_t *, unsigned int, const unsigned char *, size_t, enum sum_check_flags *, long unsigned int); struct dma_async_tx_descriptor * (*device_prep_dma_memset)(struct dma_chan___2 *, dma_addr_t, int, size_t, long unsigned int); struct dma_async_tx_descriptor * (*device_prep_dma_memset_sg)(struct dma_chan___2 *, struct scatterlist *, unsigned int, int, long unsigned int); struct dma_async_tx_descriptor * (*device_prep_dma_interrupt)(struct dma_chan___2 *, long unsigned int); struct dma_async_tx_descriptor * (*device_prep_slave_sg)(struct dma_chan___2 *, struct scatterlist *, unsigned int, enum dma_transfer_direction, long unsigned int, void *); struct dma_async_tx_descriptor * (*device_prep_dma_cyclic)(struct dma_chan___2 *, dma_addr_t, size_t, size_t, enum dma_transfer_direction, long unsigned int); struct dma_async_tx_descriptor * (*device_prep_interleaved_dma)(struct dma_chan___2 *, struct dma_interleaved_template *, long unsigned int); struct dma_async_tx_descriptor * (*device_prep_dma_imm_data)(struct dma_chan___2 *, dma_addr_t, u64, long unsigned int); void (*device_caps)(struct dma_chan___2 *, struct dma_slave_caps *); int (*device_config)(struct dma_chan___2 *, struct dma_slave_config *); int (*device_pause)(struct dma_chan___2 *); int (*device_resume)(struct dma_chan___2 *); int (*device_terminate_all)(struct dma_chan___2 *); void (*device_synchronize)(struct dma_chan___2 *); enum dma_status (*device_tx_status)(struct dma_chan___2 *, dma_cookie_t, struct dma_tx_state *); void (*device_issue_pending)(struct dma_chan___2 *); void (*device_release)(struct dma_device *); void (*dbg_summary_show)(struct seq_file *, struct dma_device *); struct dentry *dbg_dev_root; }; struct dma_chan_dev { struct dma_chan___2 *chan; struct device device; int dev_id; bool chan_dma_dev; }; enum dma_slave_buswidth { DMA_SLAVE_BUSWIDTH_UNDEFINED = 0, DMA_SLAVE_BUSWIDTH_1_BYTE = 1, DMA_SLAVE_BUSWIDTH_2_BYTES = 2, DMA_SLAVE_BUSWIDTH_3_BYTES = 3, DMA_SLAVE_BUSWIDTH_4_BYTES = 4, DMA_SLAVE_BUSWIDTH_8_BYTES = 8, DMA_SLAVE_BUSWIDTH_16_BYTES = 16, DMA_SLAVE_BUSWIDTH_32_BYTES = 32, DMA_SLAVE_BUSWIDTH_64_BYTES = 64, }; struct dma_slave_config { enum dma_transfer_direction direction; phys_addr_t src_addr; phys_addr_t dst_addr; enum dma_slave_buswidth src_addr_width; enum dma_slave_buswidth dst_addr_width; u32 src_maxburst; u32 dst_maxburst; u32 src_port_window_size; u32 dst_port_window_size; bool device_fc; unsigned int slave_id; void *peripheral_config; size_t peripheral_size; }; struct dma_slave_caps { u32 src_addr_widths; u32 dst_addr_widths; u32 directions; u32 min_burst; u32 max_burst; u32 max_sg_burst; bool cmd_pause; bool cmd_resume; bool cmd_terminate; enum dma_residue_granularity residue_granularity; bool descriptor_reuse; }; typedef void (*dma_async_tx_callback)(void *); enum dmaengine_tx_result { DMA_TRANS_NOERROR = 0, DMA_TRANS_READ_FAILED = 1, DMA_TRANS_WRITE_FAILED = 2, DMA_TRANS_ABORTED = 3, }; struct dmaengine_result { enum dmaengine_tx_result result; u32 residue; }; typedef void (*dma_async_tx_callback_result)(void *, const struct dmaengine_result *); struct dmaengine_unmap_data { u16 map_cnt; u8 to_cnt; u8 from_cnt; u8 bidi_cnt; struct device *dev; struct kref kref; size_t len; dma_addr_t addr[0]; }; struct dma_descriptor_metadata_ops { int (*attach)(struct dma_async_tx_descriptor *, void *, size_t); void * (*get_ptr)(struct dma_async_tx_descriptor *, size_t *, size_t *); int (*set_len)(struct dma_async_tx_descriptor *, size_t); }; struct dma_async_tx_descriptor { dma_cookie_t cookie; enum dma_ctrl_flags flags; dma_addr_t phys; struct dma_chan___2 *chan; dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *); int (*desc_free)(struct dma_async_tx_descriptor *); dma_async_tx_callback callback; dma_async_tx_callback_result callback_result; void *callback_param; struct dmaengine_unmap_data *unmap; enum dma_desc_metadata_mode desc_metadata_mode; struct dma_descriptor_metadata_ops *metadata_ops; }; struct dma_tx_state { dma_cookie_t last; dma_cookie_t used; u32 residue; u32 in_flight_bytes; }; struct dma_slave_map { const char *devname; const char *slave; void *param; }; struct dma_chan_tbl_ent { struct dma_chan___2 *chan; }; struct dmaengine_unmap_pool { struct kmem_cache *cache; const char *name; mempool_t *pool; size_t size; }; struct dmaengine_desc_callback { dma_async_tx_callback callback; dma_async_tx_callback_result callback_result; void *callback_param; }; struct virt_dma_desc { struct dma_async_tx_descriptor tx; struct dmaengine_result tx_result; struct list_head node; }; struct virt_dma_chan { struct dma_chan___2 chan; struct tasklet_struct task; void (*desc_free)(struct virt_dma_desc *); spinlock_t lock; struct list_head desc_allocated; struct list_head desc_submitted; struct list_head desc_issued; struct list_head desc_completed; struct list_head desc_terminated; struct virt_dma_desc *cyclic; }; struct acpi_table_csrt { struct acpi_table_header header; }; struct acpi_csrt_group { u32 length; u32 vendor_id; u32 subvendor_id; u16 device_id; u16 subdevice_id; u16 revision; u16 reserved; u32 shared_info_length; }; struct acpi_csrt_shared_info { u16 major_version; u16 minor_version; u32 mmio_base_low; u32 mmio_base_high; u32 gsi_interrupt; u8 interrupt_polarity; u8 interrupt_mode; u8 num_channels; u8 dma_address_width; u16 base_request_line; u16 num_handshake_signals; u32 max_block_size; }; struct acpi_dma_spec { int chan_id; int slave_id; struct device *dev; }; struct acpi_dma { struct list_head dma_controllers; struct device *dev; struct dma_chan___2 * (*acpi_dma_xlate)(struct acpi_dma_spec *, struct acpi_dma *); void *data; short unsigned int base_request_line; short unsigned int end_request_line; }; struct acpi_dma_filter_info { dma_cap_mask_t dma_cap; dma_filter_fn filter_fn; }; struct acpi_dma_parser_data { struct acpi_dma_spec dma_spec; size_t index; size_t n; }; struct dw_dma_slave { struct device *dma_dev; u8 src_id; u8 dst_id; u8 m_master; u8 p_master; u8 channels; bool hs_polarity; }; struct dw_dma_platform_data { unsigned int nr_channels; unsigned char chan_allocation_order; unsigned char chan_priority; unsigned int block_size; unsigned char nr_masters; unsigned char data_width[4]; unsigned char multi_block[8]; u32 max_burst[8]; unsigned char protctl; }; struct dw_dma; struct dw_dma_chip { struct device *dev; int id; int irq; void *regs; struct clk *clk; struct dw_dma *dw; const struct dw_dma_platform_data *pdata; }; struct dma_pool___2; struct dw_dma_chan; struct dw_dma { struct dma_device dma; char name[20]; void *regs; struct dma_pool___2 *desc_pool; struct tasklet_struct tasklet; struct dw_dma_chan *chan; u8 all_chan_mask; u8 in_use; void (*initialize_chan)(struct dw_dma_chan *); void (*suspend_chan)(struct dw_dma_chan *, bool); void (*resume_chan)(struct dw_dma_chan *, bool); u32 (*prepare_ctllo)(struct dw_dma_chan *); void (*encode_maxburst)(struct dw_dma_chan *, u32 *); u32 (*bytes2block)(struct dw_dma_chan *, size_t, unsigned int, size_t *); size_t (*block2bytes)(struct dw_dma_chan *, u32, u32); void (*set_device_name)(struct dw_dma *, int); void (*disable)(struct dw_dma *); void (*enable)(struct dw_dma *); struct dw_dma_platform_data *pdata; }; enum dw_dma_fc { DW_DMA_FC_D_M2M = 0, DW_DMA_FC_D_M2P = 1, DW_DMA_FC_D_P2M = 2, DW_DMA_FC_D_P2P = 3, DW_DMA_FC_P_P2M = 4, DW_DMA_FC_SP_P2P = 5, DW_DMA_FC_P_M2P = 6, DW_DMA_FC_DP_P2P = 7, }; struct dw_dma_chan_regs { u32 SAR; u32 __pad_SAR; u32 DAR; u32 __pad_DAR; u32 LLP; u32 __pad_LLP; u32 CTL_LO; u32 CTL_HI; u32 SSTAT; u32 __pad_SSTAT; u32 DSTAT; u32 __pad_DSTAT; u32 SSTATAR; u32 __pad_SSTATAR; u32 DSTATAR; u32 __pad_DSTATAR; u32 CFG_LO; u32 CFG_HI; u32 SGR; u32 __pad_SGR; u32 DSR; u32 __pad_DSR; }; struct dw_dma_irq_regs { u32 XFER; u32 __pad_XFER; u32 BLOCK; u32 __pad_BLOCK; u32 SRC_TRAN; u32 __pad_SRC_TRAN; u32 DST_TRAN; u32 __pad_DST_TRAN; u32 ERROR; u32 __pad_ERROR; }; struct dw_dma_regs { struct dw_dma_chan_regs CHAN[8]; struct dw_dma_irq_regs RAW; struct dw_dma_irq_regs STATUS; struct dw_dma_irq_regs MASK; struct dw_dma_irq_regs CLEAR; u32 STATUS_INT; u32 __pad_STATUS_INT; u32 REQ_SRC; u32 __pad_REQ_SRC; u32 REQ_DST; u32 __pad_REQ_DST; u32 SGL_REQ_SRC; u32 __pad_SGL_REQ_SRC; u32 SGL_REQ_DST; u32 __pad_SGL_REQ_DST; u32 LAST_SRC; u32 __pad_LAST_SRC; u32 LAST_DST; u32 __pad_LAST_DST; u32 CFG; u32 __pad_CFG; u32 CH_EN; u32 __pad_CH_EN; u32 ID; u32 __pad_ID; u32 TEST; u32 __pad_TEST; u32 CLASS_PRIORITY0; u32 __pad_CLASS_PRIORITY0; u32 CLASS_PRIORITY1; u32 __pad_CLASS_PRIORITY1; u32 __reserved; u32 DWC_PARAMS[8]; u32 MULTI_BLK_TYPE; u32 MAX_BLK_SIZE; u32 DW_PARAMS; u32 COMP_TYPE; u32 COMP_VERSION; u32 FIFO_PARTITION0; u32 __pad_FIFO_PARTITION0; u32 FIFO_PARTITION1; u32 __pad_FIFO_PARTITION1; u32 SAI_ERR; u32 __pad_SAI_ERR; u32 GLOBAL_CFG; u32 __pad_GLOBAL_CFG; }; enum dw_dmac_flags { DW_DMA_IS_CYCLIC = 0, DW_DMA_IS_SOFT_LLP = 1, DW_DMA_IS_PAUSED = 2, DW_DMA_IS_INITIALIZED = 3, }; struct dw_dma_chan { struct dma_chan___2 chan; void *ch_regs; u8 mask; u8 priority; enum dma_transfer_direction direction; struct list_head *tx_node_active; spinlock_t lock; long unsigned int flags; struct list_head active_list; struct list_head queue; unsigned int descs_allocated; unsigned int block_size; bool nollp; u32 max_burst; struct dw_dma_slave dws; struct dma_slave_config dma_sconfig; }; struct dw_lli { __le32 sar; __le32 dar; __le32 llp; __le32 ctllo; __le32 ctlhi; __le32 sstat; __le32 dstat; }; struct dw_desc { struct dw_lli lli; struct list_head desc_node; struct list_head tx_list; struct dma_async_tx_descriptor txd; size_t len; size_t total_len; u32 residue; }; struct dw_dma_chip_pdata { const struct dw_dma_platform_data *pdata; int (*probe)(struct dw_dma_chip *); int (*remove)(struct dw_dma_chip *); struct dw_dma_chip *chip; }; struct hsu_dma; struct hsu_dma_chip { struct device *dev; int irq; void *regs; unsigned int length; unsigned int offset; struct hsu_dma *hsu; }; struct hsu_dma_chan; struct hsu_dma { struct dma_device dma; struct hsu_dma_chan *chan; short unsigned int nr_channels; }; struct hsu_dma_sg { dma_addr_t addr; unsigned int len; }; struct hsu_dma_desc { struct virt_dma_desc vdesc; enum dma_transfer_direction direction; struct hsu_dma_sg *sg; unsigned int nents; size_t length; unsigned int active; enum dma_status status; }; struct hsu_dma_chan { struct virt_dma_chan vchan; void *reg; enum dma_transfer_direction direction; struct dma_slave_config config; struct hsu_dma_desc *desc; }; struct of_dma { struct list_head of_dma_controllers; struct device_node *of_node; struct dma_chan___2 * (*of_dma_xlate)(struct of_phandle_args *, struct of_dma *); void * (*of_dma_route_allocate)(struct of_phandle_args *, struct of_dma *); struct dma_router *dma_router; void *of_dma_data; }; enum ldma_chan_on_off { DMA_CH_OFF = 0, DMA_CH_ON = 1, }; enum { DMA_TYPE_TX = 0, DMA_TYPE_RX = 1, DMA_TYPE_MCPY = 2, }; struct ldma_port; struct dw2_desc_sw; struct ldma_chan { struct virt_dma_chan vchan; struct ldma_port *port; char name[8]; int nr; u32 flags; enum ldma_chan_on_off onoff; dma_addr_t desc_phys; void *desc_base; u32 desc_cnt; int rst; u32 hdrm_len; bool hdrm_csum; u32 boff_len; u32 data_endian; u32 desc_endian; bool pden; bool desc_rx_np; bool data_endian_en; bool desc_endian_en; bool abc_en; bool desc_init; struct dma_pool___2 *desc_pool; u32 desc_num; struct dw2_desc_sw *ds; struct work_struct work; struct dma_slave_config config; }; struct ldma_dev; struct ldma_port { struct ldma_dev *ldev; u32 portid; u32 rxbl; u32 txbl; u32 rxendi; u32 txendi; u32 pkt_drop; }; struct dw2_desc; struct dw2_desc_sw { struct virt_dma_desc vdesc; struct ldma_chan *chan; dma_addr_t desc_phys; size_t desc_cnt; size_t size; struct dw2_desc *desc_hw; }; struct ldma_inst_data; struct ldma_dev { struct device *dev; void *base; struct reset_control *rst; struct clk *core_clk; struct dma_device dma_dev; u32 ver; int irq; struct ldma_port *ports; struct ldma_chan *chans; spinlock_t dev_lock; u32 chan_nrs; u32 port_nrs; u32 channels_mask; u32 flags; u32 pollcnt; const struct ldma_inst_data *inst; struct workqueue_struct *wq; }; struct ldma_inst_data { bool desc_in_sram; bool chan_fc; bool desc_fod; bool valid_desc_fetch_ack; u32 orrc; const char *name; u32 type; }; struct dw2_desc { u32 field; u32 addr; }; struct virtio_driver { struct device_driver driver; const struct virtio_device_id *id_table; const unsigned int *feature_table; unsigned int feature_table_size; const unsigned int *feature_table_legacy; unsigned int feature_table_size_legacy; int (*validate)(struct virtio_device *); int (*probe)(struct virtio_device *); void (*scan)(struct virtio_device *); void (*remove)(struct virtio_device *); void (*config_changed)(struct virtio_device *); int (*freeze)(struct virtio_device *); int (*restore)(struct virtio_device *); }; typedef __u16 __virtio16; typedef __u32 __virtio32; typedef __u64 __virtio64; struct vring_desc { __virtio64 addr; __virtio32 len; __virtio16 flags; __virtio16 next; }; struct vring_avail { __virtio16 flags; __virtio16 idx; __virtio16 ring[0]; }; struct vring_used_elem { __virtio32 id; __virtio32 len; }; typedef struct vring_used_elem vring_used_elem_t; struct vring_used { __virtio16 flags; __virtio16 idx; vring_used_elem_t ring[0]; }; typedef struct vring_desc vring_desc_t; typedef struct vring_avail vring_avail_t; typedef struct vring_used vring_used_t; struct vring { unsigned int num; vring_desc_t *desc; vring_avail_t *avail; vring_used_t *used; }; struct vring_packed_desc_event { __le16 off_wrap; __le16 flags; }; struct vring_packed_desc { __le64 addr; __le32 len; __le16 id; __le16 flags; }; struct vring_desc_state_split { void *data; struct vring_desc *indir_desc; }; struct vring_desc_state_packed { void *data; struct vring_packed_desc *indir_desc; u16 num; u16 last; }; struct vring_desc_extra { dma_addr_t addr; u32 len; u16 flags; u16 next; }; struct vring_virtqueue { struct virtqueue vq; bool packed_ring; bool use_dma_api; bool weak_barriers; bool broken; bool indirect; bool event; unsigned int free_head; unsigned int num_added; u16 last_used_idx; bool event_triggered; union { struct { struct vring vring; u16 avail_flags_shadow; u16 avail_idx_shadow; struct vring_desc_state_split *desc_state; struct vring_desc_extra *desc_extra; dma_addr_t queue_dma_addr; size_t queue_size_in_bytes; } split; struct { struct { unsigned int num; struct vring_packed_desc *desc; struct vring_packed_desc_event *driver; struct vring_packed_desc_event *device; } vring; bool avail_wrap_counter; bool used_wrap_counter; u16 avail_used_flags; u16 next_avail_idx; u16 event_flags_shadow; struct vring_desc_state_packed *desc_state; struct vring_desc_extra *desc_extra; dma_addr_t ring_dma_addr; dma_addr_t driver_event_dma_addr; dma_addr_t device_event_dma_addr; size_t ring_size_in_bytes; size_t event_size_in_bytes; } packed; }; bool (*notify)(struct virtqueue *); bool we_own_ring; }; struct xsd_errors { int errnum; const char *errstring; }; struct xenbus_watch { struct list_head list; const char *node; unsigned int nr_pending; bool (*will_handle)(struct xenbus_watch *, const char *, const char *); void (*callback)(struct xenbus_watch *, const char *, const char *); }; struct xenbus_transaction { u32 id; }; struct grant_entry_v1 { uint16_t flags; domid_t domid; uint32_t frame; }; struct grant_entry_header { uint16_t flags; domid_t domid; }; union grant_entry_v2 { struct grant_entry_header hdr; struct { struct grant_entry_header hdr; uint32_t pad0; uint64_t frame; } full_page; struct { struct grant_entry_header hdr; uint16_t page_off; uint16_t length; uint64_t frame; } sub_page; struct { struct grant_entry_header hdr; domid_t trans_domid; uint16_t pad0; grant_ref_t gref; } transitive; uint32_t __spacer[4]; }; struct gnttab_setup_table { domid_t dom; uint32_t nr_frames; int16_t status; __guest_handle_xen_pfn_t frame_list; }; struct gnttab_copy { struct { union { grant_ref_t ref; xen_pfn_t gmfn; } u; domid_t domid; uint16_t offset; } source; struct { union { grant_ref_t ref; xen_pfn_t gmfn; } u; domid_t domid; uint16_t offset; } dest; uint16_t len; uint16_t flags; int16_t status; }; struct gnttab_query_size { domid_t dom; uint32_t nr_frames; uint32_t max_nr_frames; int16_t status; }; struct gnttab_set_version { uint32_t version; }; struct gnttab_get_status_frames { uint32_t nr_frames; domid_t dom; int16_t status; __guest_handle_uint64_t frame_list; }; struct gnttab_free_callback { struct gnttab_free_callback *next; void (*fn)(void *); void *arg; u16 count; }; struct gntab_unmap_queue_data; typedef void (*gnttab_unmap_refs_done)(int, struct gntab_unmap_queue_data *); struct gntab_unmap_queue_data { struct delayed_work gnttab_work; void *data; gnttab_unmap_refs_done done; struct gnttab_unmap_grant_ref *unmap_ops; struct gnttab_unmap_grant_ref *kunmap_ops; struct page **pages; unsigned int count; unsigned int age; }; struct gnttab_page_cache { spinlock_t lock; struct page *pages; unsigned int num_pages; }; struct gnttab_dma_alloc_args { struct device *dev; bool coherent; int nr_pages; struct page **pages; xen_pfn_t *frames; void *vaddr; dma_addr_t dev_bus_addr; }; struct xen_page_foreign { domid_t domid; grant_ref_t gref; }; typedef void (*xen_grant_fn_t)(long unsigned int, unsigned int, unsigned int, void *); struct gnttab_ops { unsigned int version; unsigned int grefs_per_grant_frame; int (*map_frames)(xen_pfn_t *, unsigned int); void (*unmap_frames)(); void (*update_entry)(grant_ref_t, domid_t, long unsigned int, unsigned int); int (*end_foreign_access_ref)(grant_ref_t, int); long unsigned int (*end_foreign_transfer_ref)(grant_ref_t); int (*query_foreign_access)(grant_ref_t); }; struct unmap_refs_callback_data { struct completion completion; int result; }; struct deferred_entry { struct list_head list; grant_ref_t ref; bool ro; uint16_t warn_delay; struct page *page; }; struct xen_feature_info { unsigned int submap_idx; uint32_t submap; }; struct balloon_stats { long unsigned int current_pages; long unsigned int target_pages; long unsigned int target_unpopulated; long unsigned int balloon_low; long unsigned int balloon_high; long unsigned int total_pages; long unsigned int schedule_delay; long unsigned int max_schedule_delay; long unsigned int retry_count; long unsigned int max_retry_count; }; enum bp_state { BP_DONE = 0, BP_WAIT = 1, BP_EAGAIN = 2, BP_ECANCELED = 3, }; enum shutdown_state { SHUTDOWN_INVALID = 4294967295, SHUTDOWN_POWEROFF = 0, SHUTDOWN_SUSPEND = 2, SHUTDOWN_HALT = 4, }; struct suspend_info { int cancelled; }; struct shutdown_handler { const char command[11]; bool flag; void (*cb)(); }; struct vcpu_runstate_info { int state; uint64_t state_entry_time; uint64_t time[4]; }; typedef struct vcpu_runstate_info *__guest_handle_vcpu_runstate_info; struct vcpu_register_runstate_memory_area { union { __guest_handle_vcpu_runstate_info h; struct vcpu_runstate_info *v; uint64_t p; } addr; }; typedef uint32_t evtchn_port_t; typedef evtchn_port_t *__guest_handle_evtchn_port_t; struct evtchn_bind_interdomain { domid_t remote_dom; evtchn_port_t remote_port; evtchn_port_t local_port; }; struct evtchn_bind_virq { uint32_t virq; uint32_t vcpu; evtchn_port_t port; }; struct evtchn_bind_pirq { uint32_t pirq; uint32_t flags; evtchn_port_t port; }; struct evtchn_bind_ipi { uint32_t vcpu; evtchn_port_t port; }; struct evtchn_close { evtchn_port_t port; }; struct evtchn_send { evtchn_port_t port; }; struct evtchn_status { domid_t dom; evtchn_port_t port; uint32_t status; uint32_t vcpu; union { struct { domid_t dom; } unbound; struct { domid_t dom; evtchn_port_t port; } interdomain; uint32_t pirq; uint32_t virq; } u; }; struct evtchn_bind_vcpu { evtchn_port_t port; uint32_t vcpu; }; struct evtchn_set_priority { evtchn_port_t port; uint32_t priority; }; struct sched_poll { __guest_handle_evtchn_port_t ports; unsigned int nr_ports; uint64_t timeout; }; struct physdev_eoi { uint32_t irq; }; struct physdev_pirq_eoi_gmfn { xen_ulong_t gmfn; }; struct physdev_irq_status_query { uint32_t irq; uint32_t flags; }; struct physdev_irq { uint32_t irq; uint32_t vector; }; struct physdev_map_pirq { domid_t domid; int type; int index; int pirq; int bus; int devfn; int entry_nr; uint64_t table_base; }; struct physdev_unmap_pirq { domid_t domid; int pirq; }; struct physdev_get_free_pirq { int type; uint32_t pirq; }; enum xenbus_state { XenbusStateUnknown = 0, XenbusStateInitialising = 1, XenbusStateInitWait = 2, XenbusStateInitialised = 3, XenbusStateConnected = 4, XenbusStateClosing = 5, XenbusStateClosed = 6, XenbusStateReconfiguring = 7, XenbusStateReconfigured = 8, }; struct xenbus_device { const char *devicetype; const char *nodename; const char *otherend; int otherend_id; struct xenbus_watch otherend_watch; struct device dev; enum xenbus_state state; struct completion down; struct work_struct work; struct semaphore reclaim_sem; atomic_t event_channels; atomic_t events; atomic_t spurious_events; atomic_t jiffies_eoi_delayed; unsigned int spurious_threshold; }; struct evtchn_loop_ctrl; struct evtchn_ops { unsigned int (*max_channels)(); unsigned int (*nr_channels)(); int (*setup)(evtchn_port_t); void (*remove)(evtchn_port_t, unsigned int); void (*bind_to_cpu)(evtchn_port_t, unsigned int, unsigned int); void (*clear_pending)(evtchn_port_t); void (*set_pending)(evtchn_port_t); bool (*is_pending)(evtchn_port_t); void (*mask)(evtchn_port_t); void (*unmask)(evtchn_port_t); void (*handle_events)(unsigned int, struct evtchn_loop_ctrl *); void (*resume)(); int (*percpu_init)(unsigned int); int (*percpu_deinit)(unsigned int); }; struct evtchn_loop_ctrl { ktime_t timeout; unsigned int count; bool defer_eoi; }; enum xen_irq_type { IRQT_UNBOUND = 0, IRQT_PIRQ = 1, IRQT_VIRQ = 2, IRQT_IPI = 3, IRQT_EVTCHN = 4, }; struct irq_info { struct list_head list; struct list_head eoi_list; short int refcnt; u8 spurious_cnt; u8 is_accounted; short int type; u8 mask_reason; u8 is_active; unsigned int irq; evtchn_port_t evtchn; short unsigned int cpu; short unsigned int eoi_cpu; unsigned int irq_epoch; u64 eoi_time; raw_spinlock_t lock; union { short unsigned int virq; enum ipi_vector ipi; struct { short unsigned int pirq; short unsigned int gsi; unsigned char vector; unsigned char flags; uint16_t domid; } pirq; struct xenbus_device *interdomain; } u; }; struct lateeoi_work { struct delayed_work delayed; spinlock_t eoi_list_lock; struct list_head eoi_list; }; struct evtchn_unmask { evtchn_port_t port; }; struct evtchn_init_control { uint64_t control_gfn; uint32_t offset; uint32_t vcpu; uint8_t link_bits; uint8_t _pad[7]; }; struct evtchn_expand_array { uint64_t array_gfn; }; typedef uint32_t event_word_t; struct evtchn_fifo_control_block { uint32_t ready; uint32_t _rsvd; event_word_t head[16]; }; struct evtchn_fifo_queue { uint32_t head[16]; }; struct evtchn_alloc_unbound { domid_t dom; domid_t remote_dom; evtchn_port_t port; }; struct xenbus_map_node { struct list_head next; union { struct { struct vm_struct *area; } pv; struct { struct page *pages[16]; long unsigned int addrs[16]; void *addr; } hvm; }; grant_handle_t handles[16]; unsigned int nr_handles; }; struct map_ring_valloc { struct xenbus_map_node *node; long unsigned int addrs[16]; phys_addr_t phys_addrs[16]; struct gnttab_map_grant_ref map[16]; struct gnttab_unmap_grant_ref unmap[16]; unsigned int idx; }; struct xenbus_ring_ops { int (*map)(struct xenbus_device *, struct map_ring_valloc *, grant_ref_t *, unsigned int, void **); int (*unmap)(struct xenbus_device *, void *); }; struct unmap_ring_hvm { unsigned int idx; long unsigned int addrs[16]; }; enum xsd_sockmsg_type { XS_DEBUG = 0, XS_DIRECTORY = 1, XS_READ = 2, XS_GET_PERMS = 3, XS_WATCH = 4, XS_UNWATCH = 5, XS_TRANSACTION_START = 6, XS_TRANSACTION_END = 7, XS_INTRODUCE = 8, XS_RELEASE = 9, XS_GET_DOMAIN_PATH = 10, XS_WRITE = 11, XS_MKDIR = 12, XS_RM = 13, XS_SET_PERMS = 14, XS_WATCH_EVENT = 15, XS_ERROR = 16, XS_IS_DOMAIN_INTRODUCED = 17, XS_RESUME = 18, XS_SET_TARGET = 19, XS_RESTRICT = 20, XS_RESET_WATCHES = 21, }; struct xsd_sockmsg { uint32_t type; uint32_t req_id; uint32_t tx_id; uint32_t len; }; typedef uint32_t XENSTORE_RING_IDX; struct xenstore_domain_interface { char req[1024]; char rsp[1024]; XENSTORE_RING_IDX req_cons; XENSTORE_RING_IDX req_prod; XENSTORE_RING_IDX rsp_cons; XENSTORE_RING_IDX rsp_prod; }; struct xs_watch_event { struct list_head list; unsigned int len; struct xenbus_watch *handle; const char *path; const char *token; char body[0]; }; enum xb_req_state { xb_req_state_queued = 0, xb_req_state_wait_reply = 1, xb_req_state_got_reply = 2, xb_req_state_aborted = 3, }; struct xb_req_data { struct list_head list; wait_queue_head_t wq; struct xsd_sockmsg msg; uint32_t caller_req_id; enum xsd_sockmsg_type type; char *body; const struct kvec *vec; int num_vecs; int err; enum xb_req_state state; bool user_req; void (*cb)(struct xb_req_data *); void *par; }; enum xenstore_init { XS_UNKNOWN = 0, XS_PV = 1, XS_HVM = 2, XS_LOCAL = 3, }; struct xenbus_device_id { char devicetype[32]; }; struct xenbus_driver { const char *name; const struct xenbus_device_id *ids; bool allow_rebind; int (*probe)(struct xenbus_device *, const struct xenbus_device_id *); void (*otherend_changed)(struct xenbus_device *, enum xenbus_state); int (*remove)(struct xenbus_device *); int (*suspend)(struct xenbus_device *); int (*resume)(struct xenbus_device *); int (*uevent)(struct xenbus_device *, struct kobj_uevent_env *); struct device_driver driver; int (*read_otherend_details)(struct xenbus_device *); int (*is_ready)(struct xenbus_device *); void (*reclaim_memory)(struct xenbus_device *); }; struct xen_hvm_param { domid_t domid; uint32_t index; uint64_t value; }; struct xen_bus_type { char *root; unsigned int levels; int (*get_bus_id)(char *, const char *); int (*probe)(struct xen_bus_type *, const char *, const char *); bool (*otherend_will_handle)(struct xenbus_watch *, const char *, const char *); void (*otherend_changed)(struct xenbus_watch *, const char *, const char *); struct bus_type bus; }; struct xb_find_info { struct xenbus_device *dev; const char *nodename; }; struct xenbus_transaction_holder { struct list_head list; struct xenbus_transaction handle; unsigned int generation_id; }; struct read_buffer { struct list_head list; unsigned int cons; unsigned int len; char msg[0]; }; struct xenbus_file_priv { struct mutex msgbuffer_mutex; struct list_head transactions; struct list_head watches; unsigned int len; union { struct xsd_sockmsg msg; char buffer[4096]; } u; struct mutex reply_mutex; struct list_head read_buffers; wait_queue_head_t read_waitq; struct kref kref; struct work_struct wq; }; struct watch_adapter { struct list_head list; struct xenbus_watch watch; struct xenbus_file_priv *dev_data; char *token; }; struct physdev_manage_pci { uint8_t bus; uint8_t devfn; }; struct physdev_manage_pci_ext { uint8_t bus; uint8_t devfn; unsigned int is_extfn; unsigned int is_virtfn; struct { uint8_t bus; uint8_t devfn; } physfn; }; struct physdev_pci_mmcfg_reserved { uint64_t address; uint16_t segment; uint8_t start_bus; uint8_t end_bus; uint32_t flags; }; struct physdev_pci_device_add { uint16_t seg; uint8_t bus; uint8_t devfn; uint32_t flags; struct { uint8_t bus; uint8_t devfn; } physfn; uint32_t optarr[0]; }; struct physdev_pci_device { uint16_t seg; uint8_t bus; uint8_t devfn; }; struct usb_device_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 bcdUSB; __u8 bDeviceClass; __u8 bDeviceSubClass; __u8 bDeviceProtocol; __u8 bMaxPacketSize0; __le16 idVendor; __le16 idProduct; __le16 bcdDevice; __u8 iManufacturer; __u8 iProduct; __u8 iSerialNumber; __u8 bNumConfigurations; }; struct usb_config_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 wTotalLength; __u8 bNumInterfaces; __u8 bConfigurationValue; __u8 iConfiguration; __u8 bmAttributes; __u8 bMaxPower; } __attribute__((packed)); struct usb_interface_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bInterfaceNumber; __u8 bAlternateSetting; __u8 bNumEndpoints; __u8 bInterfaceClass; __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; __u8 iInterface; }; struct usb_endpoint_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bEndpointAddress; __u8 bmAttributes; __le16 wMaxPacketSize; __u8 bInterval; __u8 bRefresh; __u8 bSynchAddress; } __attribute__((packed)); struct usb_ssp_isoc_ep_comp_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 wReseved; __le32 dwBytesPerInterval; }; struct usb_ss_ep_comp_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bMaxBurst; __u8 bmAttributes; __le16 wBytesPerInterval; }; struct usb_interface_assoc_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bFirstInterface; __u8 bInterfaceCount; __u8 bFunctionClass; __u8 bFunctionSubClass; __u8 bFunctionProtocol; __u8 iFunction; }; struct usb_bos_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 wTotalLength; __u8 bNumDeviceCaps; } __attribute__((packed)); struct usb_ext_cap_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bDevCapabilityType; __le32 bmAttributes; } __attribute__((packed)); struct usb_ss_cap_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bDevCapabilityType; __u8 bmAttributes; __le16 wSpeedSupported; __u8 bFunctionalitySupport; __u8 bU1devExitLat; __le16 bU2DevExitLat; }; struct usb_ss_container_id_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bDevCapabilityType; __u8 bReserved; __u8 ContainerID[16]; }; struct usb_ssp_cap_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bDevCapabilityType; __u8 bReserved; __le32 bmAttributes; __le16 wFunctionalitySupport; __le16 wReserved; __le32 bmSublinkSpeedAttr[1]; }; struct usb_ptm_cap_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bDevCapabilityType; }; enum usb_device_speed { USB_SPEED_UNKNOWN = 0, USB_SPEED_LOW = 1, USB_SPEED_FULL = 2, USB_SPEED_HIGH = 3, USB_SPEED_WIRELESS = 4, USB_SPEED_SUPER = 5, USB_SPEED_SUPER_PLUS = 6, }; enum usb_device_state { USB_STATE_NOTATTACHED = 0, USB_STATE_ATTACHED = 1, USB_STATE_POWERED = 2, USB_STATE_RECONNECTING = 3, USB_STATE_UNAUTHENTICATED = 4, USB_STATE_DEFAULT = 5, USB_STATE_ADDRESS = 6, USB_STATE_CONFIGURED = 7, USB_STATE_SUSPENDED = 8, }; enum usb3_link_state { USB3_LPM_U0 = 0, USB3_LPM_U1 = 1, USB3_LPM_U2 = 2, USB3_LPM_U3 = 3, }; enum usb_ssp_rate { USB_SSP_GEN_UNKNOWN = 0, USB_SSP_GEN_2x1 = 1, USB_SSP_GEN_1x2 = 2, USB_SSP_GEN_2x2 = 3, }; struct ep_device; struct usb_host_endpoint { struct usb_endpoint_descriptor desc; struct usb_ss_ep_comp_descriptor ss_ep_comp; struct usb_ssp_isoc_ep_comp_descriptor ssp_isoc_ep_comp; char: 8; struct list_head urb_list; void *hcpriv; struct ep_device *ep_dev; unsigned char *extra; int extralen; int enabled; int streams; int: 32; } __attribute__((packed)); struct usb_host_interface { struct usb_interface_descriptor desc; int extralen; unsigned char *extra; struct usb_host_endpoint *endpoint; char *string; }; enum usb_interface_condition { USB_INTERFACE_UNBOUND = 0, USB_INTERFACE_BINDING = 1, USB_INTERFACE_BOUND = 2, USB_INTERFACE_UNBINDING = 3, }; struct usb_interface { struct usb_host_interface *altsetting; struct usb_host_interface *cur_altsetting; unsigned int num_altsetting; struct usb_interface_assoc_descriptor *intf_assoc; int minor; enum usb_interface_condition condition; unsigned int sysfs_files_created: 1; unsigned int ep_devs_created: 1; unsigned int unregistering: 1; unsigned int needs_remote_wakeup: 1; unsigned int needs_altsetting0: 1; unsigned int needs_binding: 1; unsigned int resetting_device: 1; unsigned int authorized: 1; struct device dev; struct device *usb_dev; struct work_struct reset_ws; }; struct usb_interface_cache { unsigned int num_altsetting; struct kref ref; struct usb_host_interface altsetting[0]; }; struct usb_host_config { struct usb_config_descriptor desc; char *string; struct usb_interface_assoc_descriptor *intf_assoc[16]; struct usb_interface *interface[32]; struct usb_interface_cache *intf_cache[32]; unsigned char *extra; int extralen; }; struct usb_host_bos { struct usb_bos_descriptor *desc; struct usb_ext_cap_descriptor *ext_cap; struct usb_ss_cap_descriptor *ss_cap; struct usb_ssp_cap_descriptor *ssp_cap; struct usb_ss_container_id_descriptor *ss_id; struct usb_ptm_cap_descriptor *ptm_cap; }; struct usb_devmap { long unsigned int devicemap[2]; }; struct mon_bus; struct usb_device; struct usb_bus { struct device *controller; struct device *sysdev; int busnum; const char *bus_name; u8 uses_pio_for_control; u8 otg_port; unsigned int is_b_host: 1; unsigned int b_hnp_enable: 1; unsigned int no_stop_on_short: 1; unsigned int no_sg_constraint: 1; unsigned int sg_tablesize; int devnum_next; struct mutex devnum_next_mutex; struct usb_devmap devmap; struct usb_device *root_hub; struct usb_bus *hs_companion; int bandwidth_allocated; int bandwidth_int_reqs; int bandwidth_isoc_reqs; unsigned int resuming_ports; struct mon_bus *mon_bus; int monitored; }; struct wusb_dev; struct usb2_lpm_parameters { unsigned int besl; int timeout; }; struct usb3_lpm_parameters { unsigned int mel; unsigned int pel; unsigned int sel; int timeout; }; struct usb_tt; struct usb_device { int devnum; char devpath[16]; u32 route; enum usb_device_state state; enum usb_device_speed speed; unsigned int rx_lanes; unsigned int tx_lanes; enum usb_ssp_rate ssp_rate; struct usb_tt *tt; int ttport; unsigned int toggle[2]; struct usb_device *parent; struct usb_bus *bus; struct usb_host_endpoint ep0; struct device dev; struct usb_device_descriptor descriptor; struct usb_host_bos *bos; struct usb_host_config *config; struct usb_host_config *actconfig; struct usb_host_endpoint *ep_in[16]; struct usb_host_endpoint *ep_out[16]; char **rawdescriptors; short unsigned int bus_mA; u8 portnum; u8 level; u8 devaddr; unsigned int can_submit: 1; unsigned int persist_enabled: 1; unsigned int have_langid: 1; unsigned int authorized: 1; unsigned int authenticated: 1; unsigned int wusb: 1; unsigned int lpm_capable: 1; unsigned int usb2_hw_lpm_capable: 1; unsigned int usb2_hw_lpm_besl_capable: 1; unsigned int usb2_hw_lpm_enabled: 1; unsigned int usb2_hw_lpm_allowed: 1; unsigned int usb3_lpm_u1_enabled: 1; unsigned int usb3_lpm_u2_enabled: 1; int string_langid; char *product; char *manufacturer; char *serial; struct list_head filelist; int maxchild; u32 quirks; atomic_t urbnum; long unsigned int active_duration; long unsigned int connect_time; unsigned int do_remote_wakeup: 1; unsigned int reset_resume: 1; unsigned int port_is_suspended: 1; struct wusb_dev *wusb_dev; int slot_id; struct usb2_lpm_parameters l1_params; struct usb3_lpm_parameters u1_params; struct usb3_lpm_parameters u2_params; unsigned int lpm_disable_count; u16 hub_delay; unsigned int use_generic_driver: 1; }; struct usb_tt { struct usb_device *hub; int multi; unsigned int think_time; void *hcpriv; spinlock_t lock; struct list_head clear_list; struct work_struct clear_work; }; struct usb_iso_packet_descriptor { unsigned int offset; unsigned int length; unsigned int actual_length; int status; }; struct usb_anchor { struct list_head urb_list; wait_queue_head_t wait; spinlock_t lock; atomic_t suspend_wakeups; unsigned int poisoned: 1; }; struct urb; typedef void (*usb_complete_t)(struct urb *); struct urb { struct kref kref; int unlinked; void *hcpriv; atomic_t use_count; atomic_t reject; struct list_head urb_list; struct list_head anchor_list; struct usb_anchor *anchor; struct usb_device *dev; struct usb_host_endpoint *ep; unsigned int pipe; unsigned int stream_id; int status; unsigned int transfer_flags; void *transfer_buffer; dma_addr_t transfer_dma; struct scatterlist *sg; int num_mapped_sgs; int num_sgs; u32 transfer_buffer_length; u32 actual_length; unsigned char *setup_packet; dma_addr_t setup_dma; int start_frame; int number_of_packets; int interval; int error_count; void *context; usb_complete_t complete; struct usb_iso_packet_descriptor iso_frame_desc[0]; }; struct giveback_urb_bh { bool running; spinlock_t lock; struct list_head head; struct tasklet_struct bh; struct usb_host_endpoint *completing_ep; }; enum usb_dev_authorize_policy { USB_DEVICE_AUTHORIZE_NONE = 0, USB_DEVICE_AUTHORIZE_ALL = 1, USB_DEVICE_AUTHORIZE_INTERNAL = 2, }; struct usb_phy_roothub; struct hc_driver; struct usb_phy; struct usb_hcd { struct usb_bus self; struct kref kref; const char *product_desc; int speed; char irq_descr[24]; struct timer_list rh_timer; struct urb *status_urb; struct work_struct wakeup_work; struct work_struct died_work; const struct hc_driver *driver; struct usb_phy *usb_phy; struct usb_phy_roothub *phy_roothub; long unsigned int flags; enum usb_dev_authorize_policy dev_policy; unsigned int rh_registered: 1; unsigned int rh_pollable: 1; unsigned int msix_enabled: 1; unsigned int msi_enabled: 1; unsigned int skip_phy_initialization: 1; unsigned int uses_new_polling: 1; unsigned int wireless: 1; unsigned int has_tt: 1; unsigned int amd_resume_bug: 1; unsigned int can_do_streams: 1; unsigned int tpl_support: 1; unsigned int cant_recv_wakeups: 1; unsigned int irq; void *regs; resource_size_t rsrc_start; resource_size_t rsrc_len; unsigned int power_budget; struct giveback_urb_bh high_prio_bh; struct giveback_urb_bh low_prio_bh; struct mutex *address0_mutex; struct mutex *bandwidth_mutex; struct usb_hcd *shared_hcd; struct usb_hcd *primary_hcd; struct dma_pool___2 *pool[4]; int state; struct gen_pool *localmem_pool; long unsigned int hcd_priv[0]; }; struct hc_driver { const char *description; const char *product_desc; size_t hcd_priv_size; irqreturn_t (*irq)(struct usb_hcd *); int flags; int (*reset)(struct usb_hcd *); int (*start)(struct usb_hcd *); int (*pci_suspend)(struct usb_hcd *, bool); int (*pci_resume)(struct usb_hcd *, bool); void (*stop)(struct usb_hcd *); void (*shutdown)(struct usb_hcd *); int (*get_frame_number)(struct usb_hcd *); int (*urb_enqueue)(struct usb_hcd *, struct urb *, gfp_t); int (*urb_dequeue)(struct usb_hcd *, struct urb *, int); int (*map_urb_for_dma)(struct usb_hcd *, struct urb *, gfp_t); void (*unmap_urb_for_dma)(struct usb_hcd *, struct urb *); void (*endpoint_disable)(struct usb_hcd *, struct usb_host_endpoint *); void (*endpoint_reset)(struct usb_hcd *, struct usb_host_endpoint *); int (*hub_status_data)(struct usb_hcd *, char *); int (*hub_control)(struct usb_hcd *, u16, u16, u16, char *, u16); int (*bus_suspend)(struct usb_hcd *); int (*bus_resume)(struct usb_hcd *); int (*start_port_reset)(struct usb_hcd *, unsigned int); long unsigned int (*get_resuming_ports)(struct usb_hcd *); void (*relinquish_port)(struct usb_hcd *, int); int (*port_handed_over)(struct usb_hcd *, int); void (*clear_tt_buffer_complete)(struct usb_hcd *, struct usb_host_endpoint *); int (*alloc_dev)(struct usb_hcd *, struct usb_device *); void (*free_dev)(struct usb_hcd *, struct usb_device *); int (*alloc_streams)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint **, unsigned int, unsigned int, gfp_t); int (*free_streams)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint **, unsigned int, gfp_t); int (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *); int (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *); int (*check_bandwidth)(struct usb_hcd *, struct usb_device *); void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *); int (*address_device)(struct usb_hcd *, struct usb_device *); int (*enable_device)(struct usb_hcd *, struct usb_device *); int (*update_hub_device)(struct usb_hcd *, struct usb_device *, struct usb_tt *, gfp_t); int (*reset_device)(struct usb_hcd *, struct usb_device *); int (*update_device)(struct usb_hcd *, struct usb_device *); int (*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int); int (*enable_usb3_lpm_timeout)(struct usb_hcd *, struct usb_device *, enum usb3_link_state); int (*disable_usb3_lpm_timeout)(struct usb_hcd *, struct usb_device *, enum usb3_link_state); int (*find_raw_port_number)(struct usb_hcd *, int); int (*port_power)(struct usb_hcd *, int, bool); int (*submit_single_step_set_feature)(struct usb_hcd *, struct urb *, int); }; struct physdev_dbgp_op { uint8_t op; uint8_t bus; union { struct physdev_pci_device pci; } u; }; struct pcpu { struct list_head list; struct device dev; uint32_t cpu_id; uint32_t flags; }; typedef uint8_t xen_domain_handle_t[16]; struct xen_compile_info { char compiler[64]; char compile_by[16]; char compile_domain[32]; char compile_date[32]; }; struct xen_platform_parameters { xen_ulong_t virt_start; }; struct xen_build_id { uint32_t len; unsigned char buf[0]; }; struct hyp_sysfs_attr { struct attribute attr; ssize_t (*show)(struct hyp_sysfs_attr *, char *); ssize_t (*store)(struct hyp_sysfs_attr *, const char *, size_t); void *hyp_attr_data; }; struct pmu_mode { const char *name; uint32_t mode; }; enum xen_swiotlb_err { XEN_SWIOTLB_UNKNOWN = 0, XEN_SWIOTLB_ENOMEM = 1, XEN_SWIOTLB_EFIXUP = 2, }; struct mcinfo_common { uint16_t type; uint16_t size; }; struct mcinfo_global { struct mcinfo_common common; uint16_t mc_domid; uint16_t mc_vcpuid; uint32_t mc_socketid; uint16_t mc_coreid; uint16_t mc_core_threadid; uint32_t mc_apicid; uint32_t mc_flags; uint64_t mc_gstatus; }; struct mcinfo_bank { struct mcinfo_common common; uint16_t mc_bank; uint16_t mc_domid; uint64_t mc_status; uint64_t mc_addr; uint64_t mc_misc; uint64_t mc_ctrl2; uint64_t mc_tsc; }; struct mcinfo_msr { uint64_t reg; uint64_t value; }; struct mc_info { uint32_t mi_nentries; uint32_t flags; uint64_t mi_data[95]; }; typedef struct mc_info *__guest_handle_mc_info; struct mcinfo_logical_cpu { uint32_t mc_cpunr; uint32_t mc_chipid; uint16_t mc_coreid; uint16_t mc_threadid; uint32_t mc_apicid; uint32_t mc_clusterid; uint32_t mc_ncores; uint32_t mc_ncores_active; uint32_t mc_nthreads; uint32_t mc_cpuid_level; uint32_t mc_family; uint32_t mc_vendor; uint32_t mc_model; uint32_t mc_step; char mc_vendorid[16]; char mc_brandid[64]; uint32_t mc_cpu_caps[7]; uint32_t mc_cache_size; uint32_t mc_cache_alignment; uint32_t mc_nmsrvals; struct mcinfo_msr mc_msrvalues[8]; }; typedef struct mcinfo_logical_cpu *__guest_handle_mcinfo_logical_cpu; struct xen_mc_fetch { uint32_t flags; uint32_t _pad0; uint64_t fetch_id; __guest_handle_mc_info data; }; struct xen_mc_notifydomain { uint16_t mc_domid; uint16_t mc_vcpuid; uint32_t flags; }; struct xen_mc_physcpuinfo { uint32_t ncpus; uint32_t _pad0; __guest_handle_mcinfo_logical_cpu info; }; struct xen_mc_msrinject { uint32_t mcinj_cpunr; uint32_t mcinj_flags; uint32_t mcinj_count; uint32_t _pad0; struct mcinfo_msr mcinj_msr[8]; }; struct xen_mc_mceinject { unsigned int mceinj_cpunr; }; struct xen_mc { uint32_t cmd; uint32_t interface_version; union { struct xen_mc_fetch mc_fetch; struct xen_mc_notifydomain mc_notifydomain; struct xen_mc_physcpuinfo mc_physcpuinfo; struct xen_mc_msrinject mc_msrinject; struct xen_mc_mceinject mc_mceinject; } u; }; struct xen_mce { __u64 status; __u64 misc; __u64 addr; __u64 mcgstatus; __u64 ip; __u64 tsc; __u64 time; __u8 cpuvendor; __u8 inject_flags; __u16 pad; __u32 cpuid; __u8 cs; __u8 bank; __u8 cpu; __u8 finished; __u32 extcpu; __u32 socketid; __u32 apicid; __u64 mcgcap; __u64 synd; __u64 ipid; __u64 ppin; }; struct xen_mce_log { char signature[12]; unsigned int len; unsigned int next; unsigned int flags; unsigned int recordlen; struct xen_mce entry[32]; }; typedef int *__guest_handle_int; typedef xen_ulong_t *__guest_handle_xen_ulong_t; struct xen_add_to_physmap_range { domid_t domid; uint16_t space; uint16_t size; domid_t foreign_domid; __guest_handle_xen_ulong_t idxs; __guest_handle_xen_pfn_t gpfns; __guest_handle_int errs; }; struct xen_remove_from_physmap { domid_t domid; xen_pfn_t gpfn; }; typedef void (*xen_gfn_fn_t)(long unsigned int, void *); struct xen_remap_gfn_info; struct remap_data___2 { xen_pfn_t *fgfn; int nr_fgfn; pgprot_t prot; domid_t domid; struct vm_area_struct *vma; int index; struct page **pages; struct xen_remap_gfn_info *info; int *err_ptr; int mapped; int h_errs[1]; xen_ulong_t h_idxs[1]; xen_pfn_t h_gpfns[1]; int h_iter; }; struct map_balloon_pages { xen_pfn_t *pfns; unsigned int idx; }; struct remap_pfn { struct mm_struct *mm; struct page **pages; pgprot_t prot; long unsigned int i; }; struct fastopen_queue { struct request_sock *rskq_rst_head; struct request_sock *rskq_rst_tail; spinlock_t lock; int qlen; int max_qlen; struct tcp_fastopen_context *ctx; }; struct request_sock_queue { spinlock_t rskq_lock; u8 rskq_defer_accept; u32 synflood_warned; atomic_t qlen; atomic_t young; struct request_sock *rskq_accept_head; struct request_sock *rskq_accept_tail; struct fastopen_queue fastopenq; }; struct inet_connection_sock_af_ops { int (*queue_xmit)(struct sock *, struct sk_buff *, struct flowi *); void (*send_check)(struct sock *, struct sk_buff *); int (*rebuild_header)(struct sock *); void (*sk_rx_dst_set)(struct sock *, const struct sk_buff *); int (*conn_request)(struct sock *, struct sk_buff *); struct sock * (*syn_recv_sock)(const struct sock *, struct sk_buff *, struct request_sock *, struct dst_entry *, struct request_sock *, bool *); u16 net_header_len; u16 net_frag_header_len; u16 sockaddr_len; int (*setsockopt)(struct sock *, int, int, sockptr_t, unsigned int); int (*getsockopt)(struct sock *, int, int, char *, int *); void (*addr2sockaddr)(struct sock *, struct sockaddr *); void (*mtu_reduced)(struct sock *); }; struct inet_bind_bucket; struct tcp_ulp_ops; struct inet_connection_sock { struct inet_sock icsk_inet; struct request_sock_queue icsk_accept_queue; struct inet_bind_bucket *icsk_bind_hash; long unsigned int icsk_timeout; struct timer_list icsk_retransmit_timer; struct timer_list icsk_delack_timer; __u32 icsk_rto; __u32 icsk_rto_min; __u32 icsk_delack_max; __u32 icsk_pmtu_cookie; const struct tcp_congestion_ops *icsk_ca_ops; const struct inet_connection_sock_af_ops *icsk_af_ops; const struct tcp_ulp_ops *icsk_ulp_ops; void *icsk_ulp_data; void (*icsk_clean_acked)(struct sock *, u32); struct hlist_node icsk_listen_portaddr_node; unsigned int (*icsk_sync_mss)(struct sock *, u32); __u8 icsk_ca_state: 5; __u8 icsk_ca_initialized: 1; __u8 icsk_ca_setsockopt: 1; __u8 icsk_ca_dst_locked: 1; __u8 icsk_retransmits; __u8 icsk_pending; __u8 icsk_backoff; __u8 icsk_syn_retries; __u8 icsk_probes_out; __u16 icsk_ext_hdr_len; struct { __u8 pending; __u8 quick; __u8 pingpong; __u8 retry; __u32 ato; long unsigned int timeout; __u32 lrcvtime; __u16 last_seg_size; __u16 rcv_mss; } icsk_ack; struct { int search_high; int search_low; u32 probe_size: 31; u32 enabled: 1; u32 probe_timestamp; } icsk_mtup; u32 icsk_probes_tstamp; u32 icsk_user_timeout; u64 icsk_ca_priv[13]; }; struct tcp_ulp_ops { struct list_head list; int (*init)(struct sock *); void (*update)(struct sock *, struct proto *, void (*)(struct sock *)); void (*release)(struct sock *); int (*get_info)(const struct sock *, struct sk_buff *); size_t (*get_info_size)(const struct sock *); void (*clone)(const struct request_sock *, struct sock *, const gfp_t); char name[16]; struct module *owner; }; typedef unsigned int RING_IDX; struct pvcalls_data_intf { RING_IDX in_cons; RING_IDX in_prod; RING_IDX in_error; uint8_t pad1[52]; RING_IDX out_cons; RING_IDX out_prod; RING_IDX out_error; uint8_t pad2[52]; RING_IDX ring_order; grant_ref_t ref[0]; }; struct pvcalls_data { unsigned char *in; unsigned char *out; }; struct xen_pvcalls_socket { uint64_t id; uint32_t domain; uint32_t type; uint32_t protocol; }; struct xen_pvcalls_connect { uint64_t id; uint8_t addr[28]; uint32_t len; uint32_t flags; grant_ref_t ref; uint32_t evtchn; }; struct xen_pvcalls_release { uint64_t id; uint8_t reuse; }; struct xen_pvcalls_bind { uint64_t id; uint8_t addr[28]; uint32_t len; }; struct xen_pvcalls_listen { uint64_t id; uint32_t backlog; }; struct xen_pvcalls_accept { uint64_t id; uint64_t id_new; grant_ref_t ref; uint32_t evtchn; }; struct xen_pvcalls_poll { uint64_t id; }; struct xen_pvcalls_dummy { uint8_t dummy[56]; }; struct xen_pvcalls_request { uint32_t req_id; uint32_t cmd; union { struct xen_pvcalls_socket socket; struct xen_pvcalls_connect connect; struct xen_pvcalls_release release; struct xen_pvcalls_bind bind; struct xen_pvcalls_listen listen; struct xen_pvcalls_accept accept; struct xen_pvcalls_poll poll; struct xen_pvcalls_dummy dummy; } u; }; struct _xen_pvcalls_socket { uint64_t id; }; struct _xen_pvcalls_connect { uint64_t id; }; struct _xen_pvcalls_release { uint64_t id; }; struct _xen_pvcalls_bind { uint64_t id; }; struct _xen_pvcalls_listen { uint64_t id; }; struct _xen_pvcalls_accept { uint64_t id; }; struct _xen_pvcalls_poll { uint64_t id; }; struct _xen_pvcalls_dummy { uint8_t dummy[8]; }; struct xen_pvcalls_response { uint32_t req_id; uint32_t cmd; int32_t ret; uint32_t pad; union { struct _xen_pvcalls_socket socket; struct _xen_pvcalls_connect connect; struct _xen_pvcalls_release release; struct _xen_pvcalls_bind bind; struct _xen_pvcalls_listen listen; struct _xen_pvcalls_accept accept; struct _xen_pvcalls_poll poll; struct _xen_pvcalls_dummy dummy; } u; }; union xen_pvcalls_sring_entry { struct xen_pvcalls_request req; struct xen_pvcalls_response rsp; }; struct xen_pvcalls_sring { RING_IDX req_prod; RING_IDX req_event; RING_IDX rsp_prod; RING_IDX rsp_event; uint8_t __pad[48]; union xen_pvcalls_sring_entry ring[1]; }; struct xen_pvcalls_back_ring { RING_IDX rsp_prod_pvt; RING_IDX req_cons; unsigned int nr_ents; struct xen_pvcalls_sring *sring; }; struct pvcalls_back_global { struct list_head frontends; struct semaphore frontends_lock; }; struct pvcalls_fedata { struct list_head list; struct xenbus_device *dev; struct xen_pvcalls_sring *sring; struct xen_pvcalls_back_ring ring; int irq; struct list_head socket_mappings; struct xarray socketpass_mappings; struct semaphore socket_lock; }; struct pvcalls_ioworker { struct work_struct register_work; struct workqueue_struct *wq; }; struct sockpass_mapping; struct sock_mapping { struct list_head list; struct pvcalls_fedata *fedata; struct sockpass_mapping *sockpass; struct socket *sock; uint64_t id; grant_ref_t ref; struct pvcalls_data_intf *ring; void *bytes; struct pvcalls_data data; uint32_t ring_order; int irq; atomic_t read; atomic_t write; atomic_t io; atomic_t release; atomic_t eoi; void (*saved_data_ready)(struct sock *); struct pvcalls_ioworker ioworker; }; struct sockpass_mapping { struct list_head list; struct pvcalls_fedata *fedata; struct socket *sock; uint64_t id; struct xen_pvcalls_request reqcopy; spinlock_t copy_lock; struct workqueue_struct *wq; struct work_struct register_work; void (*saved_data_ready)(struct sock *); }; struct ww_class { atomic_long_t stamp; struct lock_class_key acquire_key; struct lock_class_key mutex_key; const char *acquire_name; const char *mutex_name; unsigned int is_wait_die; }; struct pre_voltage_change_data { long unsigned int old_uV; long unsigned int min_uV; long unsigned int max_uV; }; struct regulator_bulk_data { const char *supply; struct regulator *consumer; int ret; }; struct regulator_voltage { int min_uV; int max_uV; }; struct regulator { struct device *dev; struct list_head list; unsigned int always_on: 1; unsigned int bypass: 1; unsigned int device_link: 1; int uA_load; unsigned int enable_count; unsigned int deferred_disables; struct regulator_voltage voltage[5]; const char *supply_name; struct device_attribute dev_attr; struct regulator_dev *rdev; struct dentry *debugfs; }; struct regulator_coupler { struct list_head list; int (*attach_regulator)(struct regulator_coupler *, struct regulator_dev *); int (*detach_regulator)(struct regulator_coupler *, struct regulator_dev *); int (*balance_voltage)(struct regulator_coupler *, struct regulator_dev *, suspend_state_t); }; enum regulator_status { REGULATOR_STATUS_OFF = 0, REGULATOR_STATUS_ON = 1, REGULATOR_STATUS_ERROR = 2, REGULATOR_STATUS_FAST = 3, REGULATOR_STATUS_NORMAL = 4, REGULATOR_STATUS_IDLE = 5, REGULATOR_STATUS_STANDBY = 6, REGULATOR_STATUS_BYPASS = 7, REGULATOR_STATUS_UNDEFINED = 8, }; enum regulator_detection_severity { REGULATOR_SEVERITY_PROT = 0, REGULATOR_SEVERITY_ERR = 1, REGULATOR_SEVERITY_WARN = 2, }; struct regulator_enable_gpio { struct list_head list; struct gpio_desc *gpiod; u32 enable_count; u32 request_count; }; enum regulator_active_discharge { REGULATOR_ACTIVE_DISCHARGE_DEFAULT = 0, REGULATOR_ACTIVE_DISCHARGE_DISABLE = 1, REGULATOR_ACTIVE_DISCHARGE_ENABLE = 2, }; struct regulator_consumer_supply { const char *dev_name; const char *supply; }; struct trace_event_raw_regulator_basic { struct trace_entry ent; u32 __data_loc_name; char __data[0]; }; struct trace_event_raw_regulator_range { struct trace_entry ent; u32 __data_loc_name; int min; int max; char __data[0]; }; struct trace_event_raw_regulator_value { struct trace_entry ent; u32 __data_loc_name; unsigned int val; char __data[0]; }; struct trace_event_data_offsets_regulator_basic { u32 name; }; struct trace_event_data_offsets_regulator_range { u32 name; }; struct trace_event_data_offsets_regulator_value { u32 name; }; typedef void (*btf_trace_regulator_enable)(void *, const char *); typedef void (*btf_trace_regulator_enable_delay)(void *, const char *); typedef void (*btf_trace_regulator_enable_complete)(void *, const char *); typedef void (*btf_trace_regulator_disable)(void *, const char *); typedef void (*btf_trace_regulator_disable_complete)(void *, const char *); typedef void (*btf_trace_regulator_bypass_enable)(void *, const char *); typedef void (*btf_trace_regulator_bypass_enable_complete)(void *, const char *); typedef void (*btf_trace_regulator_bypass_disable)(void *, const char *); typedef void (*btf_trace_regulator_bypass_disable_complete)(void *, const char *); typedef void (*btf_trace_regulator_set_voltage)(void *, const char *, int, int); typedef void (*btf_trace_regulator_set_voltage_complete)(void *, const char *, unsigned int); enum regulator_get_type { NORMAL_GET = 0, EXCLUSIVE_GET = 1, OPTIONAL_GET = 2, MAX_GET_TYPE = 3, }; struct regulator_map { struct list_head list; const char *dev_name; const char *supply; struct regulator_dev *regulator; }; struct regulator_supply_alias { struct list_head list; struct device *src_dev; const char *src_supply; struct device *alias_dev; const char *alias_supply; }; struct summary_data { struct seq_file *s; struct regulator_dev *parent; int level; }; struct summary_lock_data { struct ww_acquire_ctx *ww_ctx; struct regulator_dev **new_contended_rdev; struct regulator_dev **old_contended_rdev; }; struct fixed_voltage_config { const char *supply_name; const char *input_supply; int microvolts; unsigned int startup_delay; unsigned int off_on_delay; unsigned int enabled_at_boot: 1; struct regulator_init_data *init_data; }; struct fixed_regulator_data { struct fixed_voltage_config cfg; struct regulator_init_data init_data; struct platform_device pdev; }; struct regulator_err_state { struct regulator_dev *rdev; long unsigned int notifs; long unsigned int errors; int possible_errs; }; struct regulator_irq_data { struct regulator_err_state *states; int num_states; void *data; long int opaque; }; struct regulator_irq_desc { const char *name; int irq_flags; int fatal_cnt; int reread_ms; int irq_off_ms; bool skip_off; bool high_prio; void *data; int (*die)(struct regulator_irq_data *); int (*map_event)(int, struct regulator_irq_data *, long unsigned int *); int (*renable)(struct regulator_irq_data *); }; struct regulator_bulk_devres { struct regulator_bulk_data *consumers; int num_consumers; }; struct regulator_supply_alias_match { struct device *dev; const char *id; }; struct regulator_notifier_match { struct regulator *regulator; struct notifier_block *nb; }; enum { REGULATOR_ERROR_CLEARED = 0, REGULATOR_FAILED_RETRY = 1, REGULATOR_ERROR_ON = 2, }; struct regulator_irq { struct regulator_irq_data rdata; struct regulator_irq_desc desc; int irq; int retry_cnt; struct delayed_work isr_work; }; struct reset_control___2; struct reset_control_bulk_data { const char *id; struct reset_control___2 *rstc; }; struct reset_controller_dev; struct reset_control___2 { struct reset_controller_dev *rcdev; struct list_head list; unsigned int id; struct kref refcnt; bool acquired; bool shared; bool array; atomic_t deassert_count; atomic_t triggered_count; }; struct reset_control_ops { int (*reset)(struct reset_controller_dev *, long unsigned int); int (*assert)(struct reset_controller_dev *, long unsigned int); int (*deassert)(struct reset_controller_dev *, long unsigned int); int (*status)(struct reset_controller_dev *, long unsigned int); }; struct reset_controller_dev { const struct reset_control_ops *ops; struct module *owner; struct list_head list; struct list_head reset_control_head; struct device *dev; struct device_node *of_node; int of_reset_n_cells; int (*of_xlate)(struct reset_controller_dev *, const struct of_phandle_args *); unsigned int nr_resets; }; struct reset_control_lookup { struct list_head list; const char *provider; unsigned int index; const char *dev_id; const char *con_id; }; struct reset_control_array { struct reset_control___2 base; unsigned int num_rstcs; struct reset_control___2 *rstc[0]; }; struct reset_control_bulk_devres { int num_rstcs; struct reset_control_bulk_data *rstcs; }; struct serial_struct32 { compat_int_t type; compat_int_t line; compat_uint_t port; compat_int_t irq; compat_int_t flags; compat_int_t xmit_fifo_size; compat_int_t custom_divisor; compat_int_t baud_base; short unsigned int close_delay; char io_type; char reserved_char; compat_int_t hub6; short unsigned int closing_wait; short unsigned int closing_wait2; compat_uint_t iomem_base; short unsigned int iomem_reg_shift; unsigned int port_high; compat_int_t reserved; }; struct n_tty_data { size_t read_head; size_t commit_head; size_t canon_head; size_t echo_head; size_t echo_commit; size_t echo_mark; long unsigned int char_map[4]; long unsigned int overrun_time; int num_overrun; bool no_room; unsigned char lnext: 1; unsigned char erasing: 1; unsigned char raw: 1; unsigned char real_raw: 1; unsigned char icanon: 1; unsigned char push: 1; char read_buf[4096]; long unsigned int read_flags[64]; unsigned char echo_buf[4096]; size_t read_tail; size_t line_start; unsigned int column; unsigned int canon_column; size_t echo_tail; struct mutex atomic_read_lock; struct mutex output_lock; }; enum { ERASE = 0, WERASE = 1, KILL = 2, }; struct termios { tcflag_t c_iflag; tcflag_t c_oflag; tcflag_t c_cflag; tcflag_t c_lflag; cc_t c_line; cc_t c_cc[19]; }; struct termios2 { tcflag_t c_iflag; tcflag_t c_oflag; tcflag_t c_cflag; tcflag_t c_lflag; cc_t c_line; cc_t c_cc[19]; speed_t c_ispeed; speed_t c_ospeed; }; struct termio { short unsigned int c_iflag; short unsigned int c_oflag; short unsigned int c_cflag; short unsigned int c_lflag; unsigned char c_line; unsigned char c_cc[8]; }; struct ldsem_waiter { struct list_head list; struct task_struct *task; }; struct pts_fs_info___2; struct tty_audit_buf { struct mutex mutex; dev_t dev; unsigned int icanon: 1; size_t valid; unsigned char *data; }; struct sysrq_state { struct input_handle handle; struct work_struct reinject_work; long unsigned int key_down[12]; unsigned int alt; unsigned int alt_use; unsigned int shift; unsigned int shift_use; bool active; bool need_reinject; bool reinjecting; bool reset_canceled; bool reset_requested; long unsigned int reset_keybit[12]; int reset_seq_len; int reset_seq_cnt; int reset_seq_version; struct timer_list keyreset_timer; }; struct unipair { short unsigned int unicode; short unsigned int fontpos; }; struct unimapdesc { short unsigned int entry_ct; struct unipair *entries; }; struct kbentry { unsigned char kb_table; unsigned char kb_index; short unsigned int kb_value; }; struct kbsentry { unsigned char kb_func; unsigned char kb_string[512]; }; struct kbkeycode { unsigned int scancode; unsigned int keycode; }; struct kbd_repeat { int delay; int period; }; struct console_font_op { unsigned int op; unsigned int flags; unsigned int width; unsigned int height; unsigned int charcount; unsigned char *data; }; struct vt_stat { short unsigned int v_active; short unsigned int v_signal; short unsigned int v_state; }; struct vt_sizes { short unsigned int v_rows; short unsigned int v_cols; short unsigned int v_scrollsize; }; struct vt_consize { short unsigned int v_rows; short unsigned int v_cols; short unsigned int v_vlin; short unsigned int v_clin; short unsigned int v_vcol; short unsigned int v_ccol; }; struct vt_event { unsigned int event; unsigned int oldev; unsigned int newev; unsigned int pad[4]; }; struct vt_setactivate { unsigned int console; struct vt_mode mode; }; struct vt_spawn_console { spinlock_t lock; struct pid *pid; int sig; }; struct vt_event_wait { struct list_head list; struct vt_event event; int done; }; struct compat_console_font_op { compat_uint_t op; compat_uint_t flags; compat_uint_t width; compat_uint_t height; compat_uint_t charcount; compat_caddr_t data; }; struct compat_unimapdesc { short unsigned int entry_ct; compat_caddr_t entries; }; struct vt_notifier_param { struct vc_data *vc; unsigned int c; }; struct vcs_poll_data { struct notifier_block notifier; unsigned int cons_num; int event; wait_queue_head_t waitq; struct fasync_struct *fasync; }; struct tiocl_selection { short unsigned int xs; short unsigned int ys; short unsigned int xe; short unsigned int ye; short unsigned int sel_mode; }; struct vc_selection { struct mutex lock; struct vc_data *cons; char *buffer; unsigned int buf_len; volatile int start; int end; }; struct kbdiacr { unsigned char diacr; unsigned char base; unsigned char result; }; struct kbdiacrs { unsigned int kb_cnt; struct kbdiacr kbdiacr[256]; }; struct kbdiacruc { unsigned int diacr; unsigned int base; unsigned int result; }; struct kbdiacrsuc { unsigned int kb_cnt; struct kbdiacruc kbdiacruc[256]; }; struct keyboard_notifier_param { struct vc_data *vc; int down; int shift; int ledstate; unsigned int value; }; struct kbd_struct { unsigned char lockstate; unsigned char slockstate; unsigned char ledmode: 1; unsigned char ledflagstate: 4; char: 3; unsigned char default_ledflagstate: 4; unsigned char kbdmode: 3; char: 1; unsigned char modeflags: 5; }; typedef void k_handler_fn(struct vc_data *, unsigned char, char); typedef void fn_handler_fn(struct vc_data *); struct getset_keycode_data { struct input_keymap_entry ke; int error; }; struct kbd_led_trigger { struct led_trigger trigger; unsigned int mask; }; struct uni_pagedir { u16 **uni_pgdir[32]; long unsigned int refcount; long unsigned int sum; unsigned char *inverse_translations[4]; u16 *inverse_trans_unicode; }; typedef uint32_t char32_t; struct uni_screen { char32_t *lines[0]; }; struct con_driver { const struct consw *con; const char *desc; struct device *dev; int node; int first; int last; int flag; }; enum { blank_off = 0, blank_normal_wait = 1, blank_vesa_wait = 2, }; enum { EPecma = 0, EPdec = 1, EPeq = 2, EPgt = 3, EPlt = 4, }; struct rgb { u8 r; u8 g; u8 b; }; enum { ESnormal = 0, ESesc = 1, ESsquare = 2, ESgetpars = 3, ESfunckey = 4, EShash = 5, ESsetG0 = 6, ESsetG1 = 7, ESpercent = 8, EScsiignore = 9, ESnonstd = 10, ESpalette = 11, ESosc = 12, }; struct interval { uint32_t first; uint32_t last; }; struct vc_draw_region { long unsigned int from; long unsigned int to; int x; }; struct hv_ops; struct hvc_struct { struct tty_port port; spinlock_t lock; int index; int do_wakeup; char *outbuf; int outbuf_size; int n_outbuf; uint32_t vtermno; const struct hv_ops *ops; int irq_requested; int data; struct winsize ws; struct work_struct tty_resize; struct list_head next; long unsigned int flags; }; struct hv_ops { int (*get_chars)(uint32_t, char *, int); int (*put_chars)(uint32_t, const char *, int); int (*flush)(uint32_t, bool); int (*notifier_add)(struct hvc_struct *, int); void (*notifier_del)(struct hvc_struct *, int); void (*notifier_hangup)(struct hvc_struct *, int); int (*tiocmget)(struct hvc_struct *); int (*tiocmset)(struct hvc_struct *, unsigned int, unsigned int); void (*dtr_rts)(struct hvc_struct *, int); }; struct earlycon_device { struct console *con; struct uart_port port; char options[16]; unsigned int baud; }; struct earlycon_id { char name[15]; char name_term; char compatible[128]; int (*setup)(struct earlycon_device *, const char *); }; typedef uint32_t XENCONS_RING_IDX; struct xencons_interface { char in[1024]; char out[2048]; XENCONS_RING_IDX in_cons; XENCONS_RING_IDX in_prod; XENCONS_RING_IDX out_cons; XENCONS_RING_IDX out_prod; }; struct xencons_info { struct list_head list; struct xenbus_device *xbdev; struct xencons_interface *intf; unsigned int evtchn; struct hvc_struct *hvc; int irq; int vtermno; grant_ref_t gntref; }; struct uart_driver { struct module *owner; const char *driver_name; const char *dev_name; int major; int minor; int nr; struct console *cons; struct uart_state *state; struct tty_driver *tty_driver; }; struct uart_match { struct uart_port *port; struct uart_driver *driver; }; enum hwparam_type { hwparam_ioport = 0, hwparam_iomem = 1, hwparam_ioport_or_iomem = 2, hwparam_irq = 3, hwparam_dma = 4, hwparam_dma_addr = 5, hwparam_other = 6, }; struct plat_serial8250_port { long unsigned int iobase; void *membase; resource_size_t mapbase; unsigned int irq; long unsigned int irqflags; unsigned int uartclk; void *private_data; unsigned char regshift; unsigned char iotype; unsigned char hub6; unsigned char has_sysrq; upf_t flags; unsigned int type; unsigned int (*serial_in)(struct uart_port *, int); void (*serial_out)(struct uart_port *, int, int); void (*set_termios)(struct uart_port *, struct ktermios *, struct ktermios *); void (*set_ldisc)(struct uart_port *, struct ktermios *); unsigned int (*get_mctrl)(struct uart_port *); int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int, unsigned int); void (*handle_break)(struct uart_port *); }; enum { PLAT8250_DEV_LEGACY = 4294967295, PLAT8250_DEV_PLATFORM = 0, PLAT8250_DEV_PLATFORM1 = 1, PLAT8250_DEV_PLATFORM2 = 2, PLAT8250_DEV_FOURPORT = 3, PLAT8250_DEV_ACCENT = 4, PLAT8250_DEV_BOCA = 5, PLAT8250_DEV_EXAR_ST16C554 = 6, PLAT8250_DEV_HUB6 = 7, PLAT8250_DEV_AU1X00 = 8, PLAT8250_DEV_SM501 = 9, }; struct uart_8250_port; struct uart_8250_ops { int (*setup_irq)(struct uart_8250_port *); void (*release_irq)(struct uart_8250_port *); }; struct mctrl_gpios; struct uart_8250_dma; struct uart_8250_em485; struct uart_8250_port { struct uart_port port; struct timer_list timer; struct list_head list; u32 capabilities; short unsigned int bugs; bool fifo_bug; unsigned int tx_loadsz; unsigned char acr; unsigned char fcr; unsigned char ier; unsigned char lcr; unsigned char mcr; unsigned char mcr_mask; unsigned char mcr_force; unsigned char cur_iotype; unsigned int rpm_tx_active; unsigned char canary; unsigned char probe; struct mctrl_gpios *gpios; unsigned char lsr_saved_flags; unsigned char msr_saved_flags; struct uart_8250_dma *dma; const struct uart_8250_ops *ops; int (*dl_read)(struct uart_8250_port *); void (*dl_write)(struct uart_8250_port *, int); struct uart_8250_em485 *em485; void (*rs485_start_tx)(struct uart_8250_port *); void (*rs485_stop_tx)(struct uart_8250_port *); struct delayed_work overrun_backoff; u32 overrun_backoff_time_ms; }; struct uart_8250_em485 { struct hrtimer start_tx_timer; struct hrtimer stop_tx_timer; struct hrtimer *active_timer; struct uart_8250_port *port; unsigned int tx_stopped: 1; }; struct uart_8250_dma { int (*tx_dma)(struct uart_8250_port *); int (*rx_dma)(struct uart_8250_port *); dma_filter_fn fn; void *rx_param; void *tx_param; struct dma_slave_config rxconf; struct dma_slave_config txconf; struct dma_chan___2 *rxchan; struct dma_chan___2 *txchan; phys_addr_t rx_dma_addr; phys_addr_t tx_dma_addr; dma_addr_t rx_addr; dma_addr_t tx_addr; dma_cookie_t rx_cookie; dma_cookie_t tx_cookie; void *rx_buf; size_t rx_size; size_t tx_size; unsigned char tx_running; unsigned char tx_err; unsigned char rx_running; }; struct old_serial_port { unsigned int uart; unsigned int baud_base; unsigned int port; unsigned int irq; upf_t flags; unsigned char io_type; unsigned char *iomem_base; short unsigned int iomem_reg_shift; }; struct irq_info___2 { struct hlist_node node; int irq; spinlock_t lock; struct list_head *head; }; struct serial8250_config { const char *name; short unsigned int fifo_size; short unsigned int tx_loadsz; unsigned char fcr; unsigned char rxtrig_bytes[4]; unsigned int flags; }; struct dw8250_port_data { int line; struct uart_8250_dma dma; u8 dlf_size; }; struct fintek_8250 { u16 pid; u16 base_port; u8 index; u8 key; }; struct pciserial_board { unsigned int flags; unsigned int num_ports; unsigned int base_baud; unsigned int uart_offset; unsigned int reg_shift; unsigned int first_offset; }; struct serial_private; struct pci_serial_quirk { u32 vendor; u32 device; u32 subvendor; u32 subdevice; int (*probe)(struct pci_dev *); int (*init)(struct pci_dev *); int (*setup)(struct serial_private *, const struct pciserial_board *, struct uart_8250_port *, int); void (*exit)(struct pci_dev *); }; struct serial_private { struct pci_dev *dev; unsigned int nr; struct pci_serial_quirk *quirk; const struct pciserial_board *board; int line[0]; }; struct f815xxa_data { spinlock_t lock; int idx; }; struct timedia_struct { int num; const short unsigned int *ids; }; struct quatech_feature { u16 devid; bool amcc; }; enum pci_board_num_t { pbn_default = 0, pbn_b0_1_115200 = 1, pbn_b0_2_115200 = 2, pbn_b0_4_115200 = 3, pbn_b0_5_115200 = 4, pbn_b0_8_115200 = 5, pbn_b0_1_921600 = 6, pbn_b0_2_921600 = 7, pbn_b0_4_921600 = 8, pbn_b0_2_1130000 = 9, pbn_b0_4_1152000 = 10, pbn_b0_4_1250000 = 11, pbn_b0_2_1843200 = 12, pbn_b0_4_1843200 = 13, pbn_b0_1_3906250 = 14, pbn_b0_bt_1_115200 = 15, pbn_b0_bt_2_115200 = 16, pbn_b0_bt_4_115200 = 17, pbn_b0_bt_8_115200 = 18, pbn_b0_bt_1_460800 = 19, pbn_b0_bt_2_460800 = 20, pbn_b0_bt_4_460800 = 21, pbn_b0_bt_1_921600 = 22, pbn_b0_bt_2_921600 = 23, pbn_b0_bt_4_921600 = 24, pbn_b0_bt_8_921600 = 25, pbn_b1_1_115200 = 26, pbn_b1_2_115200 = 27, pbn_b1_4_115200 = 28, pbn_b1_8_115200 = 29, pbn_b1_16_115200 = 30, pbn_b1_1_921600 = 31, pbn_b1_2_921600 = 32, pbn_b1_4_921600 = 33, pbn_b1_8_921600 = 34, pbn_b1_2_1250000 = 35, pbn_b1_bt_1_115200 = 36, pbn_b1_bt_2_115200 = 37, pbn_b1_bt_4_115200 = 38, pbn_b1_bt_2_921600 = 39, pbn_b1_1_1382400 = 40, pbn_b1_2_1382400 = 41, pbn_b1_4_1382400 = 42, pbn_b1_8_1382400 = 43, pbn_b2_1_115200 = 44, pbn_b2_2_115200 = 45, pbn_b2_4_115200 = 46, pbn_b2_8_115200 = 47, pbn_b2_1_460800 = 48, pbn_b2_4_460800 = 49, pbn_b2_8_460800 = 50, pbn_b2_16_460800 = 51, pbn_b2_1_921600 = 52, pbn_b2_4_921600 = 53, pbn_b2_8_921600 = 54, pbn_b2_8_1152000 = 55, pbn_b2_bt_1_115200 = 56, pbn_b2_bt_2_115200 = 57, pbn_b2_bt_4_115200 = 58, pbn_b2_bt_2_921600 = 59, pbn_b2_bt_4_921600 = 60, pbn_b3_2_115200 = 61, pbn_b3_4_115200 = 62, pbn_b3_8_115200 = 63, pbn_b4_bt_2_921600 = 64, pbn_b4_bt_4_921600 = 65, pbn_b4_bt_8_921600 = 66, pbn_panacom = 67, pbn_panacom2 = 68, pbn_panacom4 = 69, pbn_plx_romulus = 70, pbn_endrun_2_4000000 = 71, pbn_oxsemi = 72, pbn_oxsemi_1_3906250 = 73, pbn_oxsemi_2_3906250 = 74, pbn_oxsemi_4_3906250 = 75, pbn_oxsemi_8_3906250 = 76, pbn_intel_i960 = 77, pbn_sgi_ioc3 = 78, pbn_computone_4 = 79, pbn_computone_6 = 80, pbn_computone_8 = 81, pbn_sbsxrsio = 82, pbn_pasemi_1682M = 83, pbn_ni8430_2 = 84, pbn_ni8430_4 = 85, pbn_ni8430_8 = 86, pbn_ni8430_16 = 87, pbn_ADDIDATA_PCIe_1_3906250 = 88, pbn_ADDIDATA_PCIe_2_3906250 = 89, pbn_ADDIDATA_PCIe_4_3906250 = 90, pbn_ADDIDATA_PCIe_8_3906250 = 91, pbn_ce4100_1_115200 = 92, pbn_omegapci = 93, pbn_NETMOS9900_2s_115200 = 94, pbn_brcm_trumanage = 95, pbn_fintek_4 = 96, pbn_fintek_8 = 97, pbn_fintek_12 = 98, pbn_fintek_F81504A = 99, pbn_fintek_F81508A = 100, pbn_fintek_F81512A = 101, pbn_wch382_2 = 102, pbn_wch384_4 = 103, pbn_wch384_8 = 104, pbn_pericom_PI7C9X7951 = 105, pbn_pericom_PI7C9X7952 = 106, pbn_pericom_PI7C9X7954 = 107, pbn_pericom_PI7C9X7958 = 108, pbn_sunix_pci_1s = 109, pbn_sunix_pci_2s = 110, pbn_sunix_pci_4s = 111, pbn_sunix_pci_8s = 112, pbn_sunix_pci_16s = 113, pbn_titan_1_4000000 = 114, pbn_titan_2_4000000 = 115, pbn_titan_4_4000000 = 116, pbn_titan_8_4000000 = 117, pbn_moxa8250_2p = 118, pbn_moxa8250_4p = 119, pbn_moxa8250_8p = 120, }; struct lpss8250; struct lpss8250_board { long unsigned int freq; unsigned int base_baud; int (*setup)(struct lpss8250 *, struct uart_port *); void (*exit)(struct lpss8250 *); }; struct lpss8250 { struct dw8250_port_data data; struct lpss8250_board *board; struct dw_dma_chip dma_chip; struct dw_dma_slave dma_param; u8 dma_maxburst; }; struct hsu_dma_slave { struct device *dma_dev; int chan_id; }; struct mid8250; struct mid8250_board { unsigned int flags; long unsigned int freq; unsigned int base_baud; int (*setup)(struct mid8250 *, struct uart_port *); void (*exit)(struct mid8250 *); }; struct mid8250 { int line; int dma_index; struct pci_dev *dma_dev; struct uart_8250_dma dma; struct mid8250_board *board; struct hsu_dma_chip dma_chip; }; struct gpio_array___2; enum mctrl_gpio_idx { UART_GPIO_CTS = 0, UART_GPIO_DSR = 1, UART_GPIO_DCD = 2, UART_GPIO_RNG = 3, UART_GPIO_RI = 3, UART_GPIO_RTS = 4, UART_GPIO_DTR = 5, UART_GPIO_MAX = 6, }; struct mctrl_gpios___2 { struct uart_port *port; struct gpio_desc *gpio[6]; int irq[6]; unsigned int mctrl_prev; bool mctrl_on; }; struct serdev_device; struct serdev_device_ops { int (*receive_buf)(struct serdev_device *, const unsigned char *, size_t); void (*write_wakeup)(struct serdev_device *); }; struct serdev_controller; struct serdev_device { struct device dev; int nr; struct serdev_controller *ctrl; const struct serdev_device_ops *ops; struct completion write_comp; struct mutex write_lock; }; struct serdev_controller_ops; struct serdev_controller { struct device dev; unsigned int nr; struct serdev_device *serdev; const struct serdev_controller_ops *ops; }; struct serdev_device_driver { struct device_driver driver; int (*probe)(struct serdev_device *); void (*remove)(struct serdev_device *); }; enum serdev_parity { SERDEV_PARITY_NONE = 0, SERDEV_PARITY_EVEN = 1, SERDEV_PARITY_ODD = 2, }; struct serdev_controller_ops { int (*write_buf)(struct serdev_controller *, const unsigned char *, size_t); void (*write_flush)(struct serdev_controller *); int (*write_room)(struct serdev_controller *); int (*open)(struct serdev_controller *); void (*close)(struct serdev_controller *); void (*set_flow_control)(struct serdev_controller *, bool); int (*set_parity)(struct serdev_controller *, enum serdev_parity); unsigned int (*set_baudrate)(struct serdev_controller *, unsigned int); void (*wait_until_sent)(struct serdev_controller *, long int); int (*get_tiocm)(struct serdev_controller *); int (*set_tiocm)(struct serdev_controller *, unsigned int, unsigned int); }; struct acpi_serdev_lookup { acpi_handle device_handle; acpi_handle controller_handle; int n; int index; }; struct serport { struct tty_port *port; struct tty_struct *tty; struct tty_driver *tty_drv; int tty_idx; long unsigned int flags; }; struct memdev { const char *name; umode_t mode; const struct file_operations *fops; fmode_t fmode; }; struct timer_rand_state { cycles_t last_time; long int last_delta; long int last_delta2; }; struct trace_event_raw_add_device_randomness { struct trace_entry ent; int bytes; long unsigned int IP; char __data[0]; }; struct trace_event_raw_random__mix_pool_bytes { struct trace_entry ent; const char *pool_name; int bytes; long unsigned int IP; char __data[0]; }; struct trace_event_raw_credit_entropy_bits { struct trace_entry ent; const char *pool_name; int bits; int entropy_count; long unsigned int IP; char __data[0]; }; struct trace_event_raw_debit_entropy { struct trace_entry ent; const char *pool_name; int debit_bits; char __data[0]; }; struct trace_event_raw_add_input_randomness { struct trace_entry ent; int input_bits; char __data[0]; }; struct trace_event_raw_add_disk_randomness { struct trace_entry ent; dev_t dev; int input_bits; char __data[0]; }; struct trace_event_raw_random__get_random_bytes { struct trace_entry ent; int nbytes; long unsigned int IP; char __data[0]; }; struct trace_event_raw_random__extract_entropy { struct trace_entry ent; const char *pool_name; int nbytes; int entropy_count; long unsigned int IP; char __data[0]; }; struct trace_event_raw_urandom_read { struct trace_entry ent; int got_bits; int pool_left; int input_left; char __data[0]; }; struct trace_event_raw_prandom_u32 { struct trace_entry ent; unsigned int ret; char __data[0]; }; struct trace_event_data_offsets_add_device_randomness {}; struct trace_event_data_offsets_random__mix_pool_bytes {}; struct trace_event_data_offsets_credit_entropy_bits {}; struct trace_event_data_offsets_debit_entropy {}; struct trace_event_data_offsets_add_input_randomness {}; struct trace_event_data_offsets_add_disk_randomness {}; struct trace_event_data_offsets_random__get_random_bytes {}; struct trace_event_data_offsets_random__extract_entropy {}; struct trace_event_data_offsets_urandom_read {}; struct trace_event_data_offsets_prandom_u32 {}; typedef void (*btf_trace_add_device_randomness)(void *, int, long unsigned int); typedef void (*btf_trace_mix_pool_bytes)(void *, const char *, int, long unsigned int); typedef void (*btf_trace_mix_pool_bytes_nolock)(void *, const char *, int, long unsigned int); typedef void (*btf_trace_credit_entropy_bits)(void *, const char *, int, int, long unsigned int); typedef void (*btf_trace_debit_entropy)(void *, const char *, int); typedef void (*btf_trace_add_input_randomness)(void *, int); typedef void (*btf_trace_add_disk_randomness)(void *, dev_t, int); typedef void (*btf_trace_get_random_bytes)(void *, int, long unsigned int); typedef void (*btf_trace_get_random_bytes_arch)(void *, int, long unsigned int); typedef void (*btf_trace_extract_entropy)(void *, const char *, int, int, long unsigned int); typedef void (*btf_trace_urandom_read)(void *, int, int, int); typedef void (*btf_trace_prandom_u32)(void *, unsigned int); struct poolinfo { int poolbitshift; int poolwords; int poolbytes; int poolfracbits; int tap1; int tap2; int tap3; int tap4; int tap5; }; struct crng_state { __u32 state[16]; long unsigned int init_time; spinlock_t lock; }; struct entropy_store { const struct poolinfo *poolinfo; __u32 *pool; const char *name; spinlock_t lock; short unsigned int add_ptr; short unsigned int input_rotate; int entropy_count; unsigned int last_data_init: 1; __u8 last_data[10]; }; struct fast_pool { __u32 pool[4]; long unsigned int last; short unsigned int reg_idx; unsigned char count; }; struct batched_entropy { union { u64 entropy_u64[8]; u32 entropy_u32[16]; }; unsigned int position; spinlock_t batch_lock; }; struct hpet_info { long unsigned int hi_ireqfreq; long unsigned int hi_flags; short unsigned int hi_hpet; short unsigned int hi_timer; }; struct hpet_timer { u64 hpet_config; union { u64 _hpet_hc64; u32 _hpet_hc32; long unsigned int _hpet_compare; } _u1; u64 hpet_fsb[2]; }; struct hpet { u64 hpet_cap; u64 res0; u64 hpet_config; u64 res1; u64 hpet_isr; u64 res2[25]; union { u64 _hpet_mc64; u32 _hpet_mc32; long unsigned int _hpet_mc; } _u0; u64 res3; struct hpet_timer hpet_timers[1]; }; struct hpets; struct hpet_dev { struct hpets *hd_hpets; struct hpet *hd_hpet; struct hpet_timer *hd_timer; long unsigned int hd_ireqfreq; long unsigned int hd_irqdata; wait_queue_head_t hd_waitqueue; struct fasync_struct *hd_async_queue; unsigned int hd_flags; unsigned int hd_irq; unsigned int hd_hdwirq; char hd_name[7]; }; struct hpets { struct hpets *hp_next; struct hpet *hp_hpet; long unsigned int hp_hpet_phys; struct clocksource *hp_clocksource; long long unsigned int hp_tick_freq; long unsigned int hp_delta; unsigned int hp_ntimer; unsigned int hp_which; struct hpet_dev hp_dev[0]; }; struct compat_hpet_info { compat_ulong_t hi_ireqfreq; compat_ulong_t hi_flags; short unsigned int hi_hpet; short unsigned int hi_timer; }; struct nvram_ops { ssize_t (*get_size)(); unsigned char (*read_byte)(int); void (*write_byte)(unsigned char, int); ssize_t (*read)(char *, size_t, loff_t *); ssize_t (*write)(char *, size_t, loff_t *); long int (*initialize)(); long int (*set_checksum)(); }; struct vcpu_data; struct amd_iommu_pi_data { u32 ga_tag; u32 prev_ga_tag; u64 base; bool is_guest_mode; struct vcpu_data *vcpu_data; void *ir_data; }; struct vcpu_data { u64 pi_desc_addr; u32 vector; }; struct amd_iommu_device_info { int max_pasids; u32 flags; }; enum io_pgtable_fmt { ARM_32_LPAE_S1 = 0, ARM_32_LPAE_S2 = 1, ARM_64_LPAE_S1 = 2, ARM_64_LPAE_S2 = 3, ARM_V7S = 4, ARM_MALI_LPAE = 5, AMD_IOMMU_V1 = 6, IO_PGTABLE_NUM_FMTS = 7, }; struct iommu_flush_ops { void (*tlb_flush_all)(void *); void (*tlb_flush_walk)(long unsigned int, size_t, size_t, void *); void (*tlb_add_page)(struct iommu_iotlb_gather *, long unsigned int, size_t, void *); }; struct io_pgtable_cfg { long unsigned int quirks; long unsigned int pgsize_bitmap; unsigned int ias; unsigned int oas; bool coherent_walk; const struct iommu_flush_ops *tlb; struct device *iommu_dev; union { struct { u64 ttbr; struct { u32 ips: 3; u32 tg: 2; u32 sh: 2; u32 orgn: 2; u32 irgn: 2; u32 tsz: 6; } tcr; u64 mair; } arm_lpae_s1_cfg; struct { u64 vttbr; struct { u32 ps: 3; u32 tg: 2; u32 sh: 2; u32 orgn: 2; u32 irgn: 2; u32 sl: 2; u32 tsz: 6; } vtcr; } arm_lpae_s2_cfg; struct { u32 ttbr; u32 tcr; u32 nmrr; u32 prrr; } arm_v7s_cfg; struct { u64 transtab; u64 memattr; } arm_mali_lpae_cfg; }; }; struct io_pgtable_ops { int (*map)(struct io_pgtable_ops *, long unsigned int, phys_addr_t, size_t, int, gfp_t); size_t (*unmap)(struct io_pgtable_ops *, long unsigned int, size_t, struct iommu_iotlb_gather *); phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *, long unsigned int); }; struct io_pgtable { enum io_pgtable_fmt fmt; void *cookie; struct io_pgtable_cfg cfg; struct io_pgtable_ops ops; }; struct irq_remap_table { raw_spinlock_t lock; unsigned int min_index; u32 *table; }; struct amd_iommu_fault { u64 address; u32 pasid; u16 device_id; u16 tag; u16 flags; }; struct amd_io_pgtable { struct io_pgtable_cfg pgtbl_cfg; struct io_pgtable iop; int mode; u64 *root; atomic64_t pt_root; }; struct protection_domain { struct list_head dev_list; struct iommu_domain domain; struct amd_io_pgtable iop; spinlock_t lock; u16 id; int glx; u64 *gcr3_tbl; long unsigned int flags; unsigned int dev_cnt; unsigned int dev_iommu[32]; }; struct amd_irte_ops; struct amd_iommu___2 { struct list_head list; int index; raw_spinlock_t lock; struct pci_dev *dev; struct pci_dev *root_pdev; u64 mmio_phys; u64 mmio_phys_end; u8 *mmio_base; u32 cap; u8 acpi_flags; u64 features; bool is_iommu_v2; u16 devid; u16 cap_ptr; u16 pci_seg; u64 exclusion_start; u64 exclusion_length; u8 *cmd_buf; u32 cmd_buf_head; u32 cmd_buf_tail; u8 *evt_buf; u8 *ppr_log; u8 *ga_log; u8 *ga_log_tail; bool int_enabled; bool need_sync; struct iommu_device iommu; u32 stored_addr_lo; u32 stored_addr_hi; u32 stored_l1[108]; u32 stored_l2[131]; u8 max_banks; u8 max_counters; struct irq_domain *ir_domain; struct irq_domain *msi_domain; struct amd_irte_ops *irte_ops; u32 flags; volatile u64 *cmd_sem; u64 cmd_sem_val; struct irq_affinity_notify intcapxt_notify; }; struct amd_irte_ops { void (*prepare)(void *, u32, bool, u8, u32, int); void (*activate)(void *, u16, u16); void (*deactivate)(void *, u16, u16); void (*set_affinity)(void *, u16, u16, u8, u32); void * (*get)(struct irq_remap_table *, int); void (*set_allocated)(struct irq_remap_table *, int); bool (*is_allocated)(struct irq_remap_table *, int); void (*clear_allocated)(struct irq_remap_table *, int); }; struct acpihid_map_entry { struct list_head list; u8 uid[256]; u8 hid[9]; u16 devid; u16 root_devid; bool cmd_line; struct iommu_group *group; }; struct devid_map { struct list_head list; u8 id; u16 devid; bool cmd_line; }; struct iommu_dev_data { spinlock_t lock; struct list_head list; struct llist_node dev_data_list; struct protection_domain *domain; struct pci_dev *pdev; u16 devid; bool iommu_v2; struct { bool enabled; int qdep; } ats; bool pri_tlp; bool use_vapic; bool defer_attach; struct ratelimit_state rs; }; struct dev_table_entry { u64 data[4]; }; struct unity_map_entry { struct list_head list; u16 devid_start; u16 devid_end; u64 address_start; u64 address_end; int prot; }; enum amd_iommu_intr_mode_type { AMD_IOMMU_GUEST_IR_LEGACY = 0, AMD_IOMMU_GUEST_IR_LEGACY_GA = 1, AMD_IOMMU_GUEST_IR_VAPIC = 2, }; union irte { u32 val; struct { u32 valid: 1; u32 no_fault: 1; u32 int_type: 3; u32 rq_eoi: 1; u32 dm: 1; u32 rsvd_1: 1; u32 destination: 8; u32 vector: 8; u32 rsvd_2: 8; } fields; }; union irte_ga_lo { u64 val; struct { u64 valid: 1; u64 no_fault: 1; u64 int_type: 3; u64 rq_eoi: 1; u64 dm: 1; u64 guest_mode: 1; u64 destination: 24; u64 ga_tag: 32; } fields_remap; struct { u64 valid: 1; u64 no_fault: 1; u64 ga_log_intr: 1; u64 rsvd1: 3; u64 is_run: 1; u64 guest_mode: 1; u64 destination: 24; u64 ga_tag: 32; } fields_vapic; }; union irte_ga_hi { u64 val; struct { u64 vector: 8; u64 rsvd_1: 4; u64 ga_root_ptr: 40; u64 rsvd_2: 4; u64 destination: 8; } fields; }; struct irte_ga { union irte_ga_lo lo; union irte_ga_hi hi; }; struct irq_2_irte { u16 devid; u16 index; }; struct amd_ir_data { u32 cached_ga_tag; struct irq_2_irte irq_2_irte; struct msi_msg msi_entry; void *entry; void *ref; struct irq_cfg *cfg; int ga_vector; int ga_root_ptr; int ga_tag; }; struct irq_remap_ops { int capability; int (*prepare)(); int (*enable)(); void (*disable)(); int (*reenable)(int); int (*enable_faulting)(); }; struct iommu_cmd { u32 data[4]; }; enum irq_remap_cap { IRQ_POSTING_CAP = 0, }; struct ivhd_header { u8 type; u8 flags; u16 length; u16 devid; u16 cap_ptr; u64 mmio_phys; u16 pci_seg; u16 info; u32 efr_attr; u64 efr_reg; u64 res; }; struct ivhd_entry { u8 type; u16 devid; u8 flags; u32 ext; u32 hidh; u64 cid; u8 uidf; u8 uidl; u8 uid; } __attribute__((packed)); struct ivmd_header { u8 type; u8 flags; u16 length; u16 devid; u16 aux; u64 resv; u64 range_start; u64 range_length; }; enum iommu_init_state { IOMMU_START_STATE = 0, IOMMU_IVRS_DETECTED = 1, IOMMU_ACPI_FINISHED = 2, IOMMU_ENABLED = 3, IOMMU_PCI_INIT = 4, IOMMU_INTERRUPTS_EN = 5, IOMMU_INITIALIZED = 6, IOMMU_NOT_FOUND = 7, IOMMU_INIT_ERROR = 8, IOMMU_CMDLINE_DISABLED = 9, }; union intcapxt { u64 capxt; struct { u64 reserved_0: 2; u64 dest_mode_logical: 1; u64 reserved_1: 5; u64 destid_0_23: 24; u64 vector: 8; u64 reserved_2: 16; u64 destid_24_31: 8; }; }; struct ivrs_quirk_entry { u8 id; u16 devid; }; enum { DELL_INSPIRON_7375 = 0, DELL_LATITUDE_5495 = 1, LENOVO_IDEAPAD_330S_15ARR = 2, }; struct io_pgtable_init_fns { struct io_pgtable * (*alloc)(struct io_pgtable_cfg *, void *); void (*free)(struct io_pgtable *); }; typedef int (*amd_iommu_invalid_ppr_cb)(struct pci_dev *, u32, long unsigned int, u16); typedef void (*amd_iommu_invalidate_ctx)(struct pci_dev *, u32); struct pri_queue { atomic_t inflight; bool finish; int status; }; struct device_state; struct pasid_state { struct list_head list; atomic_t count; unsigned int mmu_notifier_count; struct mm_struct *mm; struct mmu_notifier mn; struct pri_queue pri[512]; struct device_state *device_state; u32 pasid; bool invalid; spinlock_t lock; wait_queue_head_t wq; }; struct device_state { struct list_head list; u16 devid; atomic_t count; struct pci_dev *pdev; struct pasid_state **states; struct iommu_domain *domain; int pasid_levels; int max_pasids; amd_iommu_invalid_ppr_cb inv_ppr_cb; amd_iommu_invalidate_ctx inv_ctx_cb; spinlock_t lock; wait_queue_head_t wq; }; struct fault { struct work_struct work; struct device_state *dev_state; struct pasid_state *state; struct mm_struct *mm; u64 address; u16 devid; u32 pasid; u16 tag; u16 finish; u16 flags; }; struct acpi_table_dmar { struct acpi_table_header header; u8 width; u8 flags; u8 reserved[10]; }; struct acpi_dmar_header { u16 type; u16 length; }; enum acpi_dmar_type { ACPI_DMAR_TYPE_HARDWARE_UNIT = 0, ACPI_DMAR_TYPE_RESERVED_MEMORY = 1, ACPI_DMAR_TYPE_ROOT_ATS = 2, ACPI_DMAR_TYPE_HARDWARE_AFFINITY = 3, ACPI_DMAR_TYPE_NAMESPACE = 4, ACPI_DMAR_TYPE_SATC = 5, ACPI_DMAR_TYPE_RESERVED = 6, }; struct acpi_dmar_device_scope { u8 entry_type; u8 length; u16 reserved; u8 enumeration_id; u8 bus; }; enum acpi_dmar_scope_type { ACPI_DMAR_SCOPE_TYPE_NOT_USED = 0, ACPI_DMAR_SCOPE_TYPE_ENDPOINT = 1, ACPI_DMAR_SCOPE_TYPE_BRIDGE = 2, ACPI_DMAR_SCOPE_TYPE_IOAPIC = 3, ACPI_DMAR_SCOPE_TYPE_HPET = 4, ACPI_DMAR_SCOPE_TYPE_NAMESPACE = 5, ACPI_DMAR_SCOPE_TYPE_RESERVED = 6, }; struct acpi_dmar_pci_path { u8 device; u8 function; }; struct acpi_dmar_hardware_unit { struct acpi_dmar_header header; u8 flags; u8 reserved; u16 segment; u64 address; }; struct acpi_dmar_reserved_memory { struct acpi_dmar_header header; u16 reserved; u16 segment; u64 base_address; u64 end_address; }; struct acpi_dmar_atsr { struct acpi_dmar_header header; u8 flags; u8 reserved; u16 segment; }; struct acpi_dmar_rhsa { struct acpi_dmar_header header; u32 reserved; u64 base_address; u32 proximity_domain; } __attribute__((packed)); struct acpi_dmar_andd { struct acpi_dmar_header header; u8 reserved[3]; u8 device_number; char device_name[1]; } __attribute__((packed)); struct acpi_dmar_satc { struct acpi_dmar_header header; u8 flags; u8 reserved; u16 segment; }; struct dmar_dev_scope { struct device *dev; u8 bus; u8 devfn; }; struct intel_iommu; struct dmar_drhd_unit { struct list_head list; struct acpi_dmar_header *hdr; u64 reg_base_addr; struct dmar_dev_scope *devices; int devices_cnt; u16 segment; u8 ignored: 1; u8 include_all: 1; u8 gfx_dedicated: 1; struct intel_iommu *iommu; }; struct iommu_flush { void (*flush_context)(struct intel_iommu *, u16, u16, u8, u64); void (*flush_iotlb)(struct intel_iommu *, u16, u64, unsigned int, u64); }; typedef unsigned int ioasid_t; typedef ioasid_t (*ioasid_alloc_fn_t)(ioasid_t, ioasid_t, void *); typedef void (*ioasid_free_fn_t)(ioasid_t, void *); struct ioasid_allocator_ops { ioasid_alloc_fn_t alloc; ioasid_free_fn_t free; struct list_head list; void *pdata; }; struct iopf_queue; struct dmar_domain; struct root_entry; struct page_req_dsc; struct q_inval; struct ir_table; struct intel_iommu { void *reg; u64 reg_phys; u64 reg_size; u64 cap; u64 ecap; u64 vccap; u32 gcmd; raw_spinlock_t register_lock; int seq_id; int agaw; int msagaw; unsigned int irq; unsigned int pr_irq; u16 segment; unsigned char name[13]; long unsigned int *domain_ids; struct dmar_domain ***domains; spinlock_t lock; struct root_entry *root_entry; struct iommu_flush flush; struct page_req_dsc *prq; unsigned char prq_name[16]; struct completion prq_complete; struct ioasid_allocator_ops pasid_allocator; struct iopf_queue *iopf_queue; unsigned char iopfq_name[16]; struct q_inval *qi; u32 *iommu_state; struct ir_table *ir_table; struct irq_domain *ir_domain; struct irq_domain *ir_msi_domain; struct iommu_device iommu; int node; u32 flags; struct dmar_drhd_unit *drhd; void *perf_statistic; }; struct dmar_pci_path { u8 bus; u8 device; u8 function; }; struct dmar_pci_notify_info { struct pci_dev *dev; long unsigned int event; int bus; u16 seg; u16 level; struct dmar_pci_path path[0]; }; struct irte___2 { union { struct { __u64 present: 1; __u64 fpd: 1; __u64 __res0: 6; __u64 avail: 4; __u64 __res1: 3; __u64 pst: 1; __u64 vector: 8; __u64 __res2: 40; }; struct { __u64 r_present: 1; __u64 r_fpd: 1; __u64 dst_mode: 1; __u64 redir_hint: 1; __u64 trigger_mode: 1; __u64 dlvry_mode: 3; __u64 r_avail: 4; __u64 r_res0: 4; __u64 r_vector: 8; __u64 r_res1: 8; __u64 dest_id: 32; }; struct { __u64 p_present: 1; __u64 p_fpd: 1; __u64 p_res0: 6; __u64 p_avail: 4; __u64 p_res1: 2; __u64 p_urgent: 1; __u64 p_pst: 1; __u64 p_vector: 8; __u64 p_res2: 14; __u64 pda_l: 26; }; __u64 low; }; union { struct { __u64 sid: 16; __u64 sq: 2; __u64 svt: 2; __u64 __res3: 44; }; struct { __u64 p_sid: 16; __u64 p_sq: 2; __u64 p_svt: 2; __u64 p_res3: 12; __u64 pda_h: 32; }; __u64 high; }; }; struct iova { struct rb_node node; long unsigned int pfn_hi; long unsigned int pfn_lo; }; struct iova_magazine; struct iova_cpu_rcache; struct iova_rcache { spinlock_t lock; long unsigned int depot_size; struct iova_magazine *depot[32]; struct iova_cpu_rcache *cpu_rcaches; }; struct iova_domain; typedef void (*iova_flush_cb)(struct iova_domain *); typedef void (*iova_entry_dtor)(long unsigned int); struct iova_fq; struct iova_domain { spinlock_t iova_rbtree_lock; struct rb_root rbroot; struct rb_node *cached_node; struct rb_node *cached32_node; long unsigned int granule; long unsigned int start_pfn; long unsigned int dma_32bit_pfn; long unsigned int max32_alloc_size; struct iova_fq *fq; atomic64_t fq_flush_start_cnt; atomic64_t fq_flush_finish_cnt; struct iova anchor; struct iova_rcache rcaches[6]; iova_flush_cb flush_cb; iova_entry_dtor entry_dtor; struct timer_list fq_timer; atomic_t fq_timer_on; struct hlist_node cpuhp_dead; }; struct iova_fq_entry { long unsigned int iova_pfn; long unsigned int pages; long unsigned int data; u64 counter; }; struct iova_fq { struct iova_fq_entry entries[256]; unsigned int head; unsigned int tail; spinlock_t lock; }; enum { QI_FREE = 0, QI_IN_USE = 1, QI_DONE = 2, QI_ABORT = 3, }; struct qi_desc { u64 qw0; u64 qw1; u64 qw2; u64 qw3; }; struct q_inval { raw_spinlock_t q_lock; void *desc; int *desc_status; int free_head; int free_tail; int free_cnt; }; struct ir_table { struct irte___2 *base; long unsigned int *bitmap; }; struct root_entry { u64 lo; u64 hi; }; struct dma_pte; struct dmar_domain { int nid; unsigned int iommu_refcnt[128]; u16 iommu_did[128]; u8 has_iotlb_device: 1; u8 iommu_coherency: 1; u8 iommu_snooping: 1; struct list_head devices; struct list_head subdevices; struct iova_domain iovad; struct dma_pte *pgd; int gaw; int agaw; int flags; int iommu_superpage; u64 max_addr; u32 default_pasid; struct iommu_domain domain; }; struct dma_pte { u64 val; }; enum latency_type { DMAR_LATENCY_INV_IOTLB = 0, DMAR_LATENCY_INV_DEVTLB = 1, DMAR_LATENCY_INV_IEC = 2, DMAR_LATENCY_PRQ = 3, DMAR_LATENCY_NUM = 4, }; enum latency_count { COUNTS_10e2 = 0, COUNTS_10e3 = 1, COUNTS_10e4 = 2, COUNTS_10e5 = 3, COUNTS_10e6 = 4, COUNTS_10e7 = 5, COUNTS_10e8_plus = 6, COUNTS_MIN = 7, COUNTS_MAX = 8, COUNTS_SUM = 9, COUNTS_NUM = 10, }; typedef int (*dmar_res_handler_t)(struct acpi_dmar_header *, void *); struct dmar_res_callback { dmar_res_handler_t cb[6]; void *arg[6]; bool ignore_unhandled; bool print_entry; }; enum faulttype { DMA_REMAP = 0, INTR_REMAP = 1, UNKNOWN = 2, }; struct ioasid_set { int dummy; }; enum iommu_inv_granularity { IOMMU_INV_GRANU_DOMAIN = 0, IOMMU_INV_GRANU_PASID = 1, IOMMU_INV_GRANU_ADDR = 2, IOMMU_INV_GRANU_NR = 3, }; enum { SR_DMAR_FECTL_REG = 0, SR_DMAR_FEDATA_REG = 1, SR_DMAR_FEADDR_REG = 2, SR_DMAR_FEUADDR_REG = 3, MAX_SR_DMAR_REGS = 4, }; struct context_entry { u64 lo; u64 hi; }; struct subdev_domain_info { struct list_head link_phys; struct list_head link_domain; struct device *pdev; struct dmar_domain *domain; int users; }; struct pasid_table; struct device_domain_info { struct list_head link; struct list_head global; struct list_head table; struct list_head subdevices; u32 segment; u8 bus; u8 devfn; u16 pfsid; u8 pasid_supported: 3; u8 pasid_enabled: 1; u8 pri_supported: 1; u8 pri_enabled: 1; u8 ats_supported: 1; u8 ats_enabled: 1; u8 auxd_enabled: 1; u8 ats_qdep; struct device *dev; struct intel_iommu *iommu; struct dmar_domain *domain; struct pasid_table *pasid_table; }; struct pasid_table { void *table; int order; u32 max_pasid; struct list_head dev; }; enum cap_audit_type { CAP_AUDIT_STATIC_DMAR = 0, CAP_AUDIT_STATIC_IRQR = 1, CAP_AUDIT_HOTPLUG_DMAR = 2, CAP_AUDIT_HOTPLUG_IRQR = 3, }; struct dmar_rmrr_unit { struct list_head list; struct acpi_dmar_header *hdr; u64 base_address; u64 end_address; struct dmar_dev_scope *devices; int devices_cnt; }; struct dmar_atsr_unit { struct list_head list; struct acpi_dmar_header *hdr; struct dmar_dev_scope *devices; int devices_cnt; u8 include_all: 1; }; struct dmar_satc_unit { struct list_head list; struct acpi_dmar_header *hdr; struct dmar_dev_scope *devices; struct intel_iommu *iommu; int devices_cnt; u8 atc_required: 1; }; struct domain_context_mapping_data { struct dmar_domain *domain; struct intel_iommu *iommu; struct pasid_table *table; }; struct pasid_dir_entry { u64 val; }; struct pasid_entry { u64 val[8]; }; struct pasid_table_opaque { struct pasid_table **pasid_table; int segment; int bus; int devfn; }; struct trace_event_raw_qi_submit { struct trace_entry ent; u64 qw0; u64 qw1; u64 qw2; u64 qw3; u32 __data_loc_iommu; char __data[0]; }; struct trace_event_raw_prq_report { struct trace_entry ent; u64 dw0; u64 dw1; u64 dw2; u64 dw3; long unsigned int seq; u32 __data_loc_iommu; u32 __data_loc_dev; u32 __data_loc_buff; char __data[0]; }; struct trace_event_data_offsets_qi_submit { u32 iommu; }; struct trace_event_data_offsets_prq_report { u32 iommu; u32 dev; u32 buff; }; typedef void (*btf_trace_qi_submit)(void *, struct intel_iommu *, u64, u64, u64, u64); typedef void (*btf_trace_prq_report)(void *, struct intel_iommu *, struct device *, u64, u64, u64, u64, long unsigned int); enum iommu_fault_type { IOMMU_FAULT_DMA_UNRECOV = 1, IOMMU_FAULT_PAGE_REQ = 2, }; struct page_req_dsc { union { struct { u64 type: 8; u64 pasid_present: 1; u64 priv_data_present: 1; u64 rsvd: 6; u64 rid: 16; u64 pasid: 20; u64 exe_req: 1; u64 pm_req: 1; u64 rsvd2: 10; }; u64 qw_0; }; union { struct { u64 rd_req: 1; u64 wr_req: 1; u64 lpig: 1; u64 prg_index: 9; u64 addr: 52; }; u64 qw_1; }; u64 priv_data[2]; }; struct intel_svm_dev { struct list_head list; struct callback_head rcu; struct device *dev; struct intel_iommu *iommu; struct iommu_sva sva; long unsigned int prq_seq_number; u32 pasid; int users; u16 did; u16 dev_iotlb: 1; u16 sid; u16 qdep; }; struct intel_svm { struct mmu_notifier notifier; struct mm_struct *mm; unsigned int flags; u32 pasid; int gpasid; struct list_head devs; }; enum irq_mode { IRQ_REMAPPING = 0, IRQ_POSTING = 1, }; struct ioapic_scope { struct intel_iommu *iommu; unsigned int id; unsigned int bus; unsigned int devfn; }; struct hpet_scope { struct intel_iommu *iommu; u8 id; unsigned int bus; unsigned int devfn; }; struct irq_2_iommu { struct intel_iommu *iommu; u16 irte_index; u16 sub_handle; u8 irte_mask; enum irq_mode mode; }; struct intel_ir_data { struct irq_2_iommu irq_2_iommu; struct irte___2 irte_entry; union { struct msi_msg msi_entry; }; }; struct set_msi_sid_data { struct pci_dev *pdev; u16 alias; int count; int busmatch_count; }; struct iommu_group { struct kobject kobj; struct kobject *devices_kobj; struct list_head devices; struct mutex mutex; struct blocking_notifier_head notifier; void *iommu_data; void (*iommu_data_release)(void *); char *name; int id; struct iommu_domain *default_domain; struct iommu_domain *domain; struct list_head entry; }; struct fsl_mc_obj_desc { char type[16]; int id; u16 vendor; u16 ver_major; u16 ver_minor; u8 irq_count; u8 region_count; u32 state; char label[16]; u16 flags; }; struct fsl_mc_io; struct fsl_mc_device_irq; struct fsl_mc_resource; struct fsl_mc_device { struct device dev; u64 dma_mask; u16 flags; u32 icid; u16 mc_handle; struct fsl_mc_io *mc_io; struct fsl_mc_obj_desc obj_desc; struct resource *regions; struct fsl_mc_device_irq **irqs; struct fsl_mc_resource *resource; struct device_link *consumer_link; char *driver_override; }; enum fsl_mc_pool_type { FSL_MC_POOL_DPMCP = 0, FSL_MC_POOL_DPBP = 1, FSL_MC_POOL_DPCON = 2, FSL_MC_POOL_IRQ = 3, FSL_MC_NUM_POOL_TYPES = 4, }; struct fsl_mc_resource_pool; struct fsl_mc_resource { enum fsl_mc_pool_type type; s32 id; void *data; struct fsl_mc_resource_pool *parent_pool; struct list_head node; }; struct fsl_mc_device_irq { struct msi_desc *msi_desc; struct fsl_mc_device *mc_dev; u8 dev_irq_index; struct fsl_mc_resource resource; }; struct fsl_mc_io { struct device *dev; u16 flags; u32 portal_size; phys_addr_t portal_phys_addr; void *portal_virt_addr; struct fsl_mc_device *dpmcp_dev; union { struct mutex mutex; raw_spinlock_t spinlock; }; }; struct group_device { struct list_head list; struct device *dev; char *name; }; struct iommu_group_attribute { struct attribute attr; ssize_t (*show)(struct iommu_group *, char *); ssize_t (*store)(struct iommu_group *, const char *, size_t); }; struct group_for_pci_data { struct pci_dev *pdev; struct iommu_group *group; }; struct __group_domain_type { struct device *dev; unsigned int type; }; struct trace_event_raw_iommu_group_event { struct trace_entry ent; int gid; u32 __data_loc_device; char __data[0]; }; struct trace_event_raw_iommu_device_event { struct trace_entry ent; u32 __data_loc_device; char __data[0]; }; struct trace_event_raw_map { struct trace_entry ent; u64 iova; u64 paddr; size_t size; char __data[0]; }; struct trace_event_raw_unmap { struct trace_entry ent; u64 iova; size_t size; size_t unmapped_size; char __data[0]; }; struct trace_event_raw_iommu_error { struct trace_entry ent; u32 __data_loc_device; u32 __data_loc_driver; u64 iova; int flags; char __data[0]; }; struct trace_event_data_offsets_iommu_group_event { u32 device; }; struct trace_event_data_offsets_iommu_device_event { u32 device; }; struct trace_event_data_offsets_map {}; struct trace_event_data_offsets_unmap {}; struct trace_event_data_offsets_iommu_error { u32 device; u32 driver; }; typedef void (*btf_trace_add_device_to_group)(void *, int, struct device *); typedef void (*btf_trace_remove_device_from_group)(void *, int, struct device *); typedef void (*btf_trace_attach_device_to_domain)(void *, struct device *); typedef void (*btf_trace_detach_device_from_domain)(void *, struct device *); typedef void (*btf_trace_map)(void *, long unsigned int, phys_addr_t, size_t); typedef void (*btf_trace_unmap)(void *, long unsigned int, size_t, size_t); typedef void (*btf_trace_io_page_fault)(void *, struct device *, long unsigned int, int); struct iommu_dma_msi_page { struct list_head list; dma_addr_t iova; phys_addr_t phys; }; enum iommu_dma_cookie_type { IOMMU_DMA_IOVA_COOKIE = 0, IOMMU_DMA_MSI_COOKIE = 1, }; struct iommu_dma_cookie { enum iommu_dma_cookie_type type; union { struct iova_domain iovad; dma_addr_t msi_iova; }; struct list_head msi_page_list; struct iommu_domain *fq_domain; }; struct ioasid_data { ioasid_t id; struct ioasid_set *set; void *private; struct callback_head rcu; refcount_t refs; }; struct ioasid_allocator_data { struct ioasid_allocator_ops *ops; struct list_head list; struct list_head slist; long unsigned int flags; struct xarray xa; struct callback_head rcu; }; struct iova_magazine { long unsigned int size; long unsigned int pfns[128]; }; struct iova_cpu_rcache { spinlock_t lock; struct iova_magazine *loaded; struct iova_magazine *prev; }; struct hyperv_root_ir_data { u8 ioapic_id; bool is_level; struct hv_interrupt_entry entry; } __attribute__((packed)); enum iommu_page_response_code { IOMMU_PAGE_RESP_SUCCESS = 0, IOMMU_PAGE_RESP_INVALID = 1, IOMMU_PAGE_RESP_FAILURE = 2, }; struct iopf_queue___2; struct iopf_device_param { struct device *dev; struct iopf_queue___2 *queue; struct list_head queue_list; struct list_head partial; }; struct iopf_queue___2 { struct workqueue_struct *wq; struct list_head devices; struct mutex lock; }; struct iopf_fault { struct iommu_fault fault; struct list_head list; }; struct iopf_group { struct iopf_fault last_fault; struct list_head faults; struct work_struct work; struct device *dev; }; struct mipi_dsi_msg { u8 channel; u8 type; u16 flags; size_t tx_len; const void *tx_buf; size_t rx_len; void *rx_buf; }; struct mipi_dsi_packet { size_t size; u8 header[4]; size_t payload_length; const u8 *payload; }; struct mipi_dsi_host; struct mipi_dsi_device; struct mipi_dsi_host_ops { int (*attach)(struct mipi_dsi_host *, struct mipi_dsi_device *); int (*detach)(struct mipi_dsi_host *, struct mipi_dsi_device *); ssize_t (*transfer)(struct mipi_dsi_host *, const struct mipi_dsi_msg *); }; struct mipi_dsi_host { struct device *dev; const struct mipi_dsi_host_ops *ops; struct list_head list; }; enum mipi_dsi_pixel_format { MIPI_DSI_FMT_RGB888 = 0, MIPI_DSI_FMT_RGB666 = 1, MIPI_DSI_FMT_RGB666_PACKED = 2, MIPI_DSI_FMT_RGB565 = 3, }; struct mipi_dsi_device { struct mipi_dsi_host *host; struct device dev; char name[20]; unsigned int channel; unsigned int lanes; enum mipi_dsi_pixel_format format; long unsigned int mode_flags; long unsigned int hs_rate; long unsigned int lp_rate; }; struct mipi_dsi_device_info { char type[20]; u32 channel; struct device_node *node; }; enum mipi_dsi_dcs_tear_mode { MIPI_DSI_DCS_TEAR_MODE_VBLANK = 0, MIPI_DSI_DCS_TEAR_MODE_VHBLANK = 1, }; struct mipi_dsi_driver { struct device_driver driver; int (*probe)(struct mipi_dsi_device *); int (*remove)(struct mipi_dsi_device *); void (*shutdown)(struct mipi_dsi_device *); }; struct drm_dsc_picture_parameter_set { u8 dsc_version; u8 pps_identifier; u8 pps_reserved; u8 pps_3; u8 pps_4; u8 bits_per_pixel_low; __be16 pic_height; __be16 pic_width; __be16 slice_height; __be16 slice_width; __be16 chunk_size; u8 initial_xmit_delay_high; u8 initial_xmit_delay_low; __be16 initial_dec_delay; u8 pps20_reserved; u8 initial_scale_value; __be16 scale_increment_interval; u8 scale_decrement_interval_high; u8 scale_decrement_interval_low; u8 pps26_reserved; u8 first_line_bpg_offset; __be16 nfl_bpg_offset; __be16 slice_bpg_offset; __be16 initial_offset; __be16 final_offset; u8 flatness_min_qp; u8 flatness_max_qp; __be16 rc_model_size; u8 rc_edge_factor; u8 rc_quant_incr_limit0; u8 rc_quant_incr_limit1; u8 rc_tgt_offset; u8 rc_buf_thresh[14]; __be16 rc_range_parameters[15]; u8 native_422_420; u8 second_line_bpg_offset; __be16 nsl_bpg_offset; __be16 second_line_offset_adj; u32 pps_long_94_reserved; u32 pps_long_98_reserved; u32 pps_long_102_reserved; u32 pps_long_106_reserved; u32 pps_long_110_reserved; u32 pps_long_114_reserved; u32 pps_long_118_reserved; u32 pps_long_122_reserved; __be16 pps_short_126_reserved; } __attribute__((packed)); enum { MIPI_DSI_V_SYNC_START = 1, MIPI_DSI_V_SYNC_END = 17, MIPI_DSI_H_SYNC_START = 33, MIPI_DSI_H_SYNC_END = 49, MIPI_DSI_COMPRESSION_MODE = 7, MIPI_DSI_END_OF_TRANSMISSION = 8, MIPI_DSI_COLOR_MODE_OFF = 2, MIPI_DSI_COLOR_MODE_ON = 18, MIPI_DSI_SHUTDOWN_PERIPHERAL = 34, MIPI_DSI_TURN_ON_PERIPHERAL = 50, MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM = 3, MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM = 19, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM = 35, MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM = 4, MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM = 20, MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM = 36, MIPI_DSI_DCS_SHORT_WRITE = 5, MIPI_DSI_DCS_SHORT_WRITE_PARAM = 21, MIPI_DSI_DCS_READ = 6, MIPI_DSI_EXECUTE_QUEUE = 22, MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE = 55, MIPI_DSI_NULL_PACKET = 9, MIPI_DSI_BLANKING_PACKET = 25, MIPI_DSI_GENERIC_LONG_WRITE = 41, MIPI_DSI_DCS_LONG_WRITE = 57, MIPI_DSI_PICTURE_PARAMETER_SET = 10, MIPI_DSI_COMPRESSED_PIXEL_STREAM = 11, MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20 = 12, MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24 = 28, MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16 = 44, MIPI_DSI_PACKED_PIXEL_STREAM_30 = 13, MIPI_DSI_PACKED_PIXEL_STREAM_36 = 29, MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12 = 61, MIPI_DSI_PACKED_PIXEL_STREAM_16 = 14, MIPI_DSI_PACKED_PIXEL_STREAM_18 = 30, MIPI_DSI_PIXEL_STREAM_3BYTE_18 = 46, MIPI_DSI_PACKED_PIXEL_STREAM_24 = 62, }; enum { MIPI_DCS_NOP = 0, MIPI_DCS_SOFT_RESET = 1, MIPI_DCS_GET_COMPRESSION_MODE = 3, MIPI_DCS_GET_DISPLAY_ID = 4, MIPI_DCS_GET_ERROR_COUNT_ON_DSI = 5, MIPI_DCS_GET_RED_CHANNEL = 6, MIPI_DCS_GET_GREEN_CHANNEL = 7, MIPI_DCS_GET_BLUE_CHANNEL = 8, MIPI_DCS_GET_DISPLAY_STATUS = 9, MIPI_DCS_GET_POWER_MODE = 10, MIPI_DCS_GET_ADDRESS_MODE = 11, MIPI_DCS_GET_PIXEL_FORMAT = 12, MIPI_DCS_GET_DISPLAY_MODE = 13, MIPI_DCS_GET_SIGNAL_MODE = 14, MIPI_DCS_GET_DIAGNOSTIC_RESULT = 15, MIPI_DCS_ENTER_SLEEP_MODE = 16, MIPI_DCS_EXIT_SLEEP_MODE = 17, MIPI_DCS_ENTER_PARTIAL_MODE = 18, MIPI_DCS_ENTER_NORMAL_MODE = 19, MIPI_DCS_GET_IMAGE_CHECKSUM_RGB = 20, MIPI_DCS_GET_IMAGE_CHECKSUM_CT = 21, MIPI_DCS_EXIT_INVERT_MODE = 32, MIPI_DCS_ENTER_INVERT_MODE = 33, MIPI_DCS_SET_GAMMA_CURVE = 38, MIPI_DCS_SET_DISPLAY_OFF = 40, MIPI_DCS_SET_DISPLAY_ON = 41, MIPI_DCS_SET_COLUMN_ADDRESS = 42, MIPI_DCS_SET_PAGE_ADDRESS = 43, MIPI_DCS_WRITE_MEMORY_START = 44, MIPI_DCS_WRITE_LUT = 45, MIPI_DCS_READ_MEMORY_START = 46, MIPI_DCS_SET_PARTIAL_ROWS = 48, MIPI_DCS_SET_PARTIAL_COLUMNS = 49, MIPI_DCS_SET_SCROLL_AREA = 51, MIPI_DCS_SET_TEAR_OFF = 52, MIPI_DCS_SET_TEAR_ON = 53, MIPI_DCS_SET_ADDRESS_MODE = 54, MIPI_DCS_SET_SCROLL_START = 55, MIPI_DCS_EXIT_IDLE_MODE = 56, MIPI_DCS_ENTER_IDLE_MODE = 57, MIPI_DCS_SET_PIXEL_FORMAT = 58, MIPI_DCS_WRITE_MEMORY_CONTINUE = 60, MIPI_DCS_SET_3D_CONTROL = 61, MIPI_DCS_READ_MEMORY_CONTINUE = 62, MIPI_DCS_GET_3D_CONTROL = 63, MIPI_DCS_SET_VSYNC_TIMING = 64, MIPI_DCS_SET_TEAR_SCANLINE = 68, MIPI_DCS_GET_SCANLINE = 69, MIPI_DCS_SET_DISPLAY_BRIGHTNESS = 81, MIPI_DCS_GET_DISPLAY_BRIGHTNESS = 82, MIPI_DCS_WRITE_CONTROL_DISPLAY = 83, MIPI_DCS_GET_CONTROL_DISPLAY = 84, MIPI_DCS_WRITE_POWER_SAVE = 85, MIPI_DCS_GET_POWER_SAVE = 86, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS = 94, MIPI_DCS_GET_CABC_MIN_BRIGHTNESS = 95, MIPI_DCS_READ_DDB_START = 161, MIPI_DCS_READ_PPS_START = 162, MIPI_DCS_READ_DDB_CONTINUE = 168, MIPI_DCS_READ_PPS_CONTINUE = 169, }; struct drm_dmi_panel_orientation_data { int width; int height; const char * const *bios_dates; int orientation; }; struct vga_device { struct list_head list; struct pci_dev *pdev; unsigned int decodes; unsigned int owns; unsigned int locks; unsigned int io_lock_cnt; unsigned int mem_lock_cnt; unsigned int io_norm_cnt; unsigned int mem_norm_cnt; bool bridge_has_one_vga; void *cookie; void (*irq_set_state)(void *, bool); unsigned int (*set_vga_decode)(void *, bool); }; struct vga_arb_user_card { struct pci_dev *pdev; unsigned int mem_cnt; unsigned int io_cnt; }; struct vga_arb_private { struct list_head list; struct pci_dev *target; struct vga_arb_user_card cards[10]; spinlock_t lock; }; enum vga_switcheroo_handler_flags_t { VGA_SWITCHEROO_CAN_SWITCH_DDC = 1, VGA_SWITCHEROO_NEEDS_EDP_CONFIG = 2, }; enum vga_switcheroo_state { VGA_SWITCHEROO_OFF = 0, VGA_SWITCHEROO_ON = 1, VGA_SWITCHEROO_NOT_FOUND = 2, }; enum vga_switcheroo_client_id { VGA_SWITCHEROO_UNKNOWN_ID = 4096, VGA_SWITCHEROO_IGD = 0, VGA_SWITCHEROO_DIS = 1, VGA_SWITCHEROO_MAX_CLIENTS = 2, }; struct vga_switcheroo_handler { int (*init)(); int (*switchto)(enum vga_switcheroo_client_id); int (*switch_ddc)(enum vga_switcheroo_client_id); int (*power_state)(enum vga_switcheroo_client_id, enum vga_switcheroo_state); enum vga_switcheroo_client_id (*get_client_id)(struct pci_dev *); }; struct vga_switcheroo_client_ops { void (*set_gpu_state)(struct pci_dev *, enum vga_switcheroo_state); void (*reprobe)(struct pci_dev *); bool (*can_switch)(struct pci_dev *); void (*gpu_bound)(struct pci_dev *, enum vga_switcheroo_client_id); }; struct vga_switcheroo_client { struct pci_dev *pdev; struct fb_info *fb_info; enum vga_switcheroo_state pwr_state; const struct vga_switcheroo_client_ops *ops; enum vga_switcheroo_client_id id; bool active; bool driver_power_control; struct list_head list; struct pci_dev *vga_dev; }; struct vgasr_priv { bool active; bool delayed_switch_active; enum vga_switcheroo_client_id delayed_client_id; struct dentry *debugfs_root; int registered_clients; struct list_head clients; const struct vga_switcheroo_handler *handler; enum vga_switcheroo_handler_flags_t handler_flags; struct mutex mux_hw_lock; int old_ddc_owner; }; struct cb_id { __u32 idx; __u32 val; }; struct cn_msg { struct cb_id id; __u32 seq; __u32 ack; __u16 len; __u16 flags; __u8 data[0]; }; struct cn_queue_dev { atomic_t refcnt; unsigned char name[32]; struct list_head queue_list; spinlock_t queue_lock; struct sock *nls; }; struct cn_callback_id { unsigned char name[32]; struct cb_id id; }; struct cn_callback_entry { struct list_head callback_entry; refcount_t refcnt; struct cn_queue_dev *pdev; struct cn_callback_id id; void (*callback)(struct cn_msg *, struct netlink_skb_parms *); u32 seq; u32 group; }; struct cn_dev { struct cb_id id; u32 seq; u32 groups; struct sock *nls; struct cn_queue_dev *cbdev; }; enum proc_cn_mcast_op { PROC_CN_MCAST_LISTEN = 1, PROC_CN_MCAST_IGNORE = 2, }; struct fork_proc_event { __kernel_pid_t parent_pid; __kernel_pid_t parent_tgid; __kernel_pid_t child_pid; __kernel_pid_t child_tgid; }; struct exec_proc_event { __kernel_pid_t process_pid; __kernel_pid_t process_tgid; }; struct id_proc_event { __kernel_pid_t process_pid; __kernel_pid_t process_tgid; union { __u32 ruid; __u32 rgid; } r; union { __u32 euid; __u32 egid; } e; }; struct sid_proc_event { __kernel_pid_t process_pid; __kernel_pid_t process_tgid; }; struct ptrace_proc_event { __kernel_pid_t process_pid; __kernel_pid_t process_tgid; __kernel_pid_t tracer_pid; __kernel_pid_t tracer_tgid; }; struct comm_proc_event { __kernel_pid_t process_pid; __kernel_pid_t process_tgid; char comm[16]; }; struct coredump_proc_event { __kernel_pid_t process_pid; __kernel_pid_t process_tgid; __kernel_pid_t parent_pid; __kernel_pid_t parent_tgid; }; struct exit_proc_event { __kernel_pid_t process_pid; __kernel_pid_t process_tgid; __u32 exit_code; __u32 exit_signal; __kernel_pid_t parent_pid; __kernel_pid_t parent_tgid; }; struct proc_event { enum what what; __u32 cpu; __u64 timestamp_ns; union { struct { __u32 err; } ack; struct fork_proc_event fork; struct exec_proc_event exec; struct id_proc_event id; struct sid_proc_event sid; struct ptrace_proc_event ptrace; struct comm_proc_event comm; struct coredump_proc_event coredump; struct exit_proc_event exit; } event_data; }; struct local_event { local_lock_t lock; __u32 count; }; struct nvm_ioctl_info_tgt { __u32 version[3]; __u32 reserved; char tgtname[48]; }; struct nvm_ioctl_info { __u32 version[3]; __u16 tgtsize; __u16 reserved16; __u32 reserved[12]; struct nvm_ioctl_info_tgt tgts[63]; }; struct nvm_ioctl_device_info { char devname[32]; char bmname[48]; __u32 bmversion[3]; __u32 flags; __u32 reserved[8]; }; struct nvm_ioctl_get_devices { __u32 nr_devices; __u32 reserved[31]; struct nvm_ioctl_device_info info[31]; }; struct nvm_ioctl_create_simple { __u32 lun_begin; __u32 lun_end; }; struct nvm_ioctl_create_extended { __u16 lun_begin; __u16 lun_end; __u16 op; __u16 rsv; }; enum { NVM_CONFIG_TYPE_SIMPLE = 0, NVM_CONFIG_TYPE_EXTENDED = 1, }; struct nvm_ioctl_create_conf { __u32 type; union { struct nvm_ioctl_create_simple s; struct nvm_ioctl_create_extended e; }; }; enum { NVM_TARGET_FACTORY = 1, }; struct nvm_ioctl_create { char dev[32]; char tgttype[48]; char tgtname[32]; __u32 flags; struct nvm_ioctl_create_conf conf; }; struct nvm_ioctl_remove { char tgtname[32]; __u32 flags; }; struct nvm_ioctl_dev_init { char dev[32]; char mmtype[8]; __u32 flags; }; enum { NVM_FACTORY_ERASE_ONLY_USER = 1, NVM_FACTORY_RESET_HOST_BLKS = 2, NVM_FACTORY_RESET_GRWN_BBLKS = 4, NVM_FACTORY_NR_BITS = 8, }; struct nvm_ioctl_dev_factory { char dev[32]; __u32 flags; }; enum { NVM_INFO_CMD = 32, NVM_GET_DEVICES_CMD = 33, NVM_DEV_CREATE_CMD = 34, NVM_DEV_REMOVE_CMD = 35, NVM_DEV_INIT_CMD = 36, NVM_DEV_FACTORY_CMD = 37, NVM_DEV_VIO_ADMIN_CMD = 65, NVM_DEV_VIO_CMD = 66, NVM_DEV_VIO_USER_CMD = 67, }; enum { NVM_OCSSD_SPEC_12 = 12, NVM_OCSSD_SPEC_20 = 20, }; struct ppa_addr { union { struct { u64 ch: 8; u64 lun: 8; u64 blk: 16; u64 reserved: 32; } a; struct { u64 ch: 8; u64 lun: 8; u64 blk: 16; u64 pg: 16; u64 pl: 4; u64 sec: 4; u64 reserved: 8; } g; struct { u64 grp: 8; u64 pu: 8; u64 chk: 16; u64 sec: 24; u64 reserved: 8; } m; struct { u64 line: 63; u64 is_cached: 1; } c; u64 ppa; }; }; struct nvm_dev; typedef int nvm_id_fn(struct nvm_dev *); struct nvm_addrf { u8 ch_len; u8 lun_len; u8 chk_len; u8 sec_len; u8 rsv_len[2]; u8 ch_offset; u8 lun_offset; u8 chk_offset; u8 sec_offset; u8 rsv_off[2]; u64 ch_mask; u64 lun_mask; u64 chk_mask; u64 sec_mask; u64 rsv_mask[2]; }; struct nvm_geo { u8 major_ver_id; u8 minor_ver_id; u8 version; int num_ch; int num_lun; int all_luns; int all_chunks; int op; sector_t total_secs; u32 num_chk; u32 clba; u16 csecs; u16 sos; bool ext; u32 mdts; u32 ws_min; u32 ws_opt; u32 mw_cunits; u32 maxoc; u32 maxocpu; u32 mccap; u32 trdt; u32 trdm; u32 tprt; u32 tprm; u32 tbet; u32 tbem; struct nvm_addrf addrf; u8 vmnt; u32 cap; u32 dom; u8 mtype; u8 fmtype; u16 cpar; u32 mpos; u8 num_pln; u8 pln_mode; u16 num_pg; u16 fpg_sz; }; struct nvm_dev_ops; struct nvm_dev { struct nvm_dev_ops *ops; struct list_head devices; struct nvm_geo geo; long unsigned int *lun_map; void *dma_pool; struct request_queue *q; char name[32]; void *private_data; struct kref ref; void *rmap; struct mutex mlock; spinlock_t lock; struct list_head area_list; struct list_head targets; }; typedef int nvm_op_bb_tbl_fn(struct nvm_dev *, struct ppa_addr, u8 *); typedef int nvm_op_set_bb_fn(struct nvm_dev *, struct ppa_addr *, int, int); struct nvm_chk_meta; typedef int nvm_get_chk_meta_fn(struct nvm_dev *, sector_t, int, struct nvm_chk_meta *); struct nvm_chk_meta { u8 state; u8 type; u8 wi; u8 rsvd[5]; u64 slba; u64 cnlb; u64 wp; }; struct nvm_rq; typedef int nvm_submit_io_fn(struct nvm_dev *, struct nvm_rq *, void *); typedef void nvm_end_io_fn(struct nvm_rq *); struct nvm_tgt_dev; struct nvm_rq { struct nvm_tgt_dev *dev; struct bio *bio; union { struct ppa_addr ppa_addr; dma_addr_t dma_ppa_list; }; struct ppa_addr *ppa_list; void *meta_list; dma_addr_t dma_meta_list; nvm_end_io_fn *end_io; uint8_t opcode; uint16_t nr_ppas; uint16_t flags; u64 ppa_status; int error; int is_seq; void *private; }; typedef void *nvm_create_dma_pool_fn(struct nvm_dev *, char *, int); typedef void nvm_destroy_dma_pool_fn(void *); typedef void *nvm_dev_dma_alloc_fn(struct nvm_dev *, void *, gfp_t, dma_addr_t *); typedef void nvm_dev_dma_free_fn(void *, void *, dma_addr_t); struct nvm_dev_ops { nvm_id_fn *identity; nvm_op_bb_tbl_fn *get_bb_tbl; nvm_op_set_bb_fn *set_bb_tbl; nvm_get_chk_meta_fn *get_chk_meta; nvm_submit_io_fn *submit_io; nvm_create_dma_pool_fn *create_dma_pool; nvm_destroy_dma_pool_fn *destroy_dma_pool; nvm_dev_dma_alloc_fn *dev_dma_alloc; nvm_dev_dma_free_fn *dev_dma_free; }; enum { NVM_RSP_L2P = 1, NVM_RSP_ECC = 2, NVM_ADDRMODE_LINEAR = 0, NVM_ADDRMODE_CHANNEL = 1, NVM_PLANE_SINGLE = 1, NVM_PLANE_DOUBLE = 2, NVM_PLANE_QUAD = 4, NVM_RSP_SUCCESS = 0, NVM_RSP_NOT_CHANGEABLE = 1, NVM_RSP_ERR_FAILWRITE = 16639, NVM_RSP_ERR_EMPTYPAGE = 17151, NVM_RSP_ERR_FAILECC = 17025, NVM_RSP_ERR_FAILCRC = 16388, NVM_RSP_WARN_HIGHECC = 18176, NVM_OP_PWRITE = 145, NVM_OP_PREAD = 146, NVM_OP_ERASE = 144, NVM_IO_SNGL_ACCESS = 0, NVM_IO_DUAL_ACCESS = 1, NVM_IO_QUAD_ACCESS = 2, NVM_IO_SUSPEND = 128, NVM_IO_SLC_MODE = 256, NVM_IO_SCRAMBLE_ENABLE = 512, NVM_BLK_T_FREE = 0, NVM_BLK_T_BAD = 1, NVM_BLK_T_GRWN_BAD = 2, NVM_BLK_T_DEV = 4, NVM_BLK_T_HOST = 8, NVM_ID_CAP_SLC = 1, NVM_ID_CAP_CMD_SUSPEND = 2, NVM_ID_CAP_SCRAMBLE = 4, NVM_ID_CAP_ENCRYPT = 8, NVM_ID_FMTYPE_SLC = 0, NVM_ID_FMTYPE_MLC = 1, NVM_ID_DCAP_BBLKMGMT = 1, NVM_UD_DCAP_ECC = 2, }; struct nvm_addrf_12 { u8 ch_len; u8 lun_len; u8 blk_len; u8 pg_len; u8 pln_len; u8 sec_len; u8 ch_offset; u8 lun_offset; u8 blk_offset; u8 pg_offset; u8 pln_offset; u8 sec_offset; u64 ch_mask; u64 lun_mask; u64 blk_mask; u64 pg_mask; u64 pln_mask; u64 sec_mask; }; enum { NVM_CHK_ST_FREE = 1, NVM_CHK_ST_CLOSED = 2, NVM_CHK_ST_OPEN = 4, NVM_CHK_ST_OFFLINE = 8, NVM_CHK_TP_W_SEQ = 1, NVM_CHK_TP_W_RAN = 2, NVM_CHK_TP_SZ_SPEC = 16, }; struct nvm_tgt_type; struct nvm_target { struct list_head list; struct nvm_tgt_dev *dev; struct nvm_tgt_type *type; struct gendisk *disk; }; struct nvm_tgt_dev { struct nvm_geo geo; struct ppa_addr *luns; struct request_queue *q; struct nvm_dev *parent; void *map; }; typedef sector_t nvm_tgt_capacity_fn(void *); typedef void *nvm_tgt_init_fn(struct nvm_tgt_dev *, struct gendisk *, int); typedef void nvm_tgt_exit_fn(void *, bool); typedef int nvm_tgt_sysfs_init_fn(struct gendisk *); typedef void nvm_tgt_sysfs_exit_fn(struct gendisk *); struct nvm_tgt_type { const char *name; unsigned int version[3]; int flags; const struct block_device_operations *bops; nvm_tgt_capacity_fn *capacity; nvm_tgt_init_fn *init; nvm_tgt_exit_fn *exit; nvm_tgt_sysfs_init_fn *sysfs_init; nvm_tgt_sysfs_exit_fn *sysfs_exit; struct list_head list; struct module *owner; }; enum { NVM_TGT_F_DEV_L2P = 0, NVM_TGT_F_HOST_L2P = 1, }; struct nvm_ch_map { int ch_off; int num_lun; int *lun_offs; }; struct nvm_dev_map { struct nvm_ch_map *chnls; int num_ch; }; struct component_ops { int (*bind)(struct device *, struct device *, void *); void (*unbind)(struct device *, struct device *, void *); }; struct component_master_ops { int (*bind)(struct device *); void (*unbind)(struct device *); }; struct component; struct component_match_array { void *data; int (*compare)(struct device *, void *); int (*compare_typed)(struct device *, int, void *); void (*release)(struct device *, void *); struct component *component; bool duplicate; }; struct master; struct component { struct list_head node; struct master *master; bool bound; const struct component_ops *ops; int subcomponent; struct device *dev; }; struct component_match { size_t alloc; size_t num; struct component_match_array *compare; }; struct master { struct list_head node; bool bound; const struct component_master_ops *ops; struct device *parent; struct component_match *match; }; struct fwnode_link { struct fwnode_handle *supplier; struct list_head s_hook; struct fwnode_handle *consumer; struct list_head c_hook; }; struct wake_irq { struct device *dev; unsigned int status; int irq; const char *name; }; enum dpm_order { DPM_ORDER_NONE = 0, DPM_ORDER_DEV_AFTER_PARENT = 1, DPM_ORDER_PARENT_BEFORE_DEV = 2, DPM_ORDER_DEV_LAST = 3, }; struct subsys_private { struct kset subsys; struct kset *devices_kset; struct list_head interfaces; struct mutex mutex; struct kset *drivers_kset; struct klist klist_devices; struct klist klist_drivers; struct blocking_notifier_head bus_notifier; unsigned int drivers_autoprobe: 1; struct bus_type *bus; struct kset glue_dirs; struct class *class; }; struct driver_private { struct kobject kobj; struct klist klist_devices; struct klist_node knode_bus; struct module_kobject *mkobj; struct device_driver *driver; }; struct device_private { struct klist klist_children; struct klist_node knode_parent; struct klist_node knode_driver; struct klist_node knode_bus; struct klist_node knode_class; struct list_head deferred_probe; struct device_driver *async_driver; char *deferred_probe_reason; struct device *device; u8 dead: 1; }; union device_attr_group_devres { const struct attribute_group *group; const struct attribute_group **groups; }; struct class_dir { struct kobject kobj; struct class *class; }; struct root_device { struct device dev; struct module *owner; }; struct subsys_dev_iter { struct klist_iter ki; const struct device_type *type; }; struct device_attach_data { struct device *dev; bool check_async; bool want_async; bool have_async; }; struct class_attribute { struct attribute attr; ssize_t (*show)(struct class *, struct class_attribute *, char *); ssize_t (*store)(struct class *, struct class_attribute *, const char *, size_t); }; struct class_attribute_string { struct class_attribute attr; char *str; }; struct class_compat { struct kobject *kobj; }; struct irq_affinity_devres { unsigned int count; unsigned int irq[0]; }; struct platform_object { struct platform_device pdev; char name[0]; }; struct cpu_attr { struct device_attribute attr; const struct cpumask * const map; }; struct probe { struct probe *next; dev_t dev; long unsigned int range; struct module *owner; kobj_probe_t *get; int (*lock)(dev_t, void *); void *data; }; struct kobj_map___2 { struct probe *probes[255]; struct mutex *lock; }; struct devres_node { struct list_head entry; dr_release_t release; const char *name; size_t size; }; struct devres___2 { struct devres_node node; u8 data[0]; }; struct devres_group { struct devres_node node[2]; void *id; int color; }; struct action_devres { void *data; void (*action)(void *); }; struct pages_devres { long unsigned int addr; unsigned int order; }; struct attribute_container { struct list_head node; struct klist containers; struct class *class; const struct attribute_group *grp; struct device_attribute **attrs; int (*match)(struct attribute_container *, struct device *); long unsigned int flags; }; struct internal_container { struct klist_node node; struct attribute_container *cont; struct device classdev; }; struct transport_container; struct transport_class { struct class class; int (*setup)(struct transport_container *, struct device *, struct device *); int (*configure)(struct transport_container *, struct device *, struct device *); int (*remove)(struct transport_container *, struct device *, struct device *); }; struct transport_container { struct attribute_container ac; const struct attribute_group *statistics; }; struct anon_transport_class { struct transport_class tclass; struct attribute_container container; }; typedef void * (*devcon_match_fn_t)(struct fwnode_handle *, const char *, void *); struct mii_bus; struct mdio_device { struct device dev; struct mii_bus *bus; char modalias[32]; int (*bus_match)(struct device *, struct device_driver *); void (*device_free)(struct mdio_device *); void (*device_remove)(struct mdio_device *); int addr; int flags; struct gpio_desc *reset_gpio; struct reset_control *reset_ctrl; unsigned int reset_assert_delay; unsigned int reset_deassert_delay; }; struct phy_c45_device_ids { u32 devices_in_package; u32 mmds_present; u32 device_ids[32]; }; enum phy_state { PHY_DOWN = 0, PHY_READY = 1, PHY_HALTED = 2, PHY_UP = 3, PHY_RUNNING = 4, PHY_NOLINK = 5, PHY_CABLETEST = 6, }; typedef enum { PHY_INTERFACE_MODE_NA = 0, PHY_INTERFACE_MODE_INTERNAL = 1, PHY_INTERFACE_MODE_MII = 2, PHY_INTERFACE_MODE_GMII = 3, PHY_INTERFACE_MODE_SGMII = 4, PHY_INTERFACE_MODE_TBI = 5, PHY_INTERFACE_MODE_REVMII = 6, PHY_INTERFACE_MODE_RMII = 7, PHY_INTERFACE_MODE_REVRMII = 8, PHY_INTERFACE_MODE_RGMII = 9, PHY_INTERFACE_MODE_RGMII_ID = 10, PHY_INTERFACE_MODE_RGMII_RXID = 11, PHY_INTERFACE_MODE_RGMII_TXID = 12, PHY_INTERFACE_MODE_RTBI = 13, PHY_INTERFACE_MODE_SMII = 14, PHY_INTERFACE_MODE_XGMII = 15, PHY_INTERFACE_MODE_XLGMII = 16, PHY_INTERFACE_MODE_MOCA = 17, PHY_INTERFACE_MODE_QSGMII = 18, PHY_INTERFACE_MODE_TRGMII = 19, PHY_INTERFACE_MODE_100BASEX = 20, PHY_INTERFACE_MODE_1000BASEX = 21, PHY_INTERFACE_MODE_2500BASEX = 22, PHY_INTERFACE_MODE_5GBASER = 23, PHY_INTERFACE_MODE_RXAUI = 24, PHY_INTERFACE_MODE_XAUI = 25, PHY_INTERFACE_MODE_10GBASER = 26, PHY_INTERFACE_MODE_25GBASER = 27, PHY_INTERFACE_MODE_USXGMII = 28, PHY_INTERFACE_MODE_10GKR = 29, PHY_INTERFACE_MODE_MAX = 30, } phy_interface_t; struct phy_led_trigger; struct phylink; struct phy_driver; struct phy_package_shared; struct mii_timestamper; struct phy_device { struct mdio_device mdio; struct phy_driver *drv; u32 phy_id; struct phy_c45_device_ids c45_ids; unsigned int is_c45: 1; unsigned int is_internal: 1; unsigned int is_pseudo_fixed_link: 1; unsigned int is_gigabit_capable: 1; unsigned int has_fixups: 1; unsigned int suspended: 1; unsigned int suspended_by_mdio_bus: 1; unsigned int sysfs_links: 1; unsigned int loopback_enabled: 1; unsigned int downshifted_rate: 1; unsigned int is_on_sfp_module: 1; unsigned int mac_managed_pm: 1; unsigned int autoneg: 1; unsigned int link: 1; unsigned int autoneg_complete: 1; unsigned int interrupts: 1; enum phy_state state; u32 dev_flags; phy_interface_t interface; int speed; int duplex; int port; int pause; int asym_pause; u8 master_slave_get; u8 master_slave_set; u8 master_slave_state; long unsigned int supported[2]; long unsigned int advertising[2]; long unsigned int lp_advertising[2]; long unsigned int adv_old[2]; u32 eee_broken_modes; struct phy_led_trigger *phy_led_triggers; unsigned int phy_num_led_triggers; struct phy_led_trigger *last_triggered; struct phy_led_trigger *led_link_trigger; int irq; void *priv; struct phy_package_shared *shared; struct sk_buff *skb; void *ehdr; struct nlattr *nest; struct delayed_work state_queue; struct mutex lock; bool sfp_bus_attached; struct sfp_bus *sfp_bus; struct phylink *phylink; struct net_device *attached_dev; struct mii_timestamper *mii_ts; u8 mdix; u8 mdix_ctrl; void (*phy_link_change)(struct phy_device *, bool); void (*adjust_link)(struct net_device *); const struct macsec_ops *macsec_ops; }; struct phy_tdr_config { u32 first; u32 last; u32 step; s8 pair; }; struct mdio_bus_stats { u64_stats_t transfers; u64_stats_t errors; u64_stats_t writes; u64_stats_t reads; struct u64_stats_sync syncp; }; struct mii_bus { struct module *owner; const char *name; char id[61]; void *priv; int (*read)(struct mii_bus *, int, int); int (*write)(struct mii_bus *, int, int, u16); int (*reset)(struct mii_bus *); struct mdio_bus_stats stats[32]; struct mutex mdio_lock; struct device *parent; enum { MDIOBUS_ALLOCATED = 1, MDIOBUS_REGISTERED = 2, MDIOBUS_UNREGISTERED = 3, MDIOBUS_RELEASED = 4, } state; struct device dev; struct mdio_device *mdio_map[32]; u32 phy_mask; u32 phy_ignore_ta_mask; int irq[32]; int reset_delay_us; int reset_post_delay_us; struct gpio_desc *reset_gpiod; enum { MDIOBUS_NO_CAP = 0, MDIOBUS_C22 = 1, MDIOBUS_C45 = 2, MDIOBUS_C22_C45 = 3, } probe_capabilities; struct mutex shared_lock; struct phy_package_shared *shared[32]; }; struct mdio_driver_common { struct device_driver driver; int flags; }; struct mii_timestamper { bool (*rxtstamp)(struct mii_timestamper *, struct sk_buff *, int); void (*txtstamp)(struct mii_timestamper *, struct sk_buff *, int); int (*hwtstamp)(struct mii_timestamper *, struct ifreq *); void (*link_state)(struct mii_timestamper *, struct phy_device *); int (*ts_info)(struct mii_timestamper *, struct ethtool_ts_info *); struct device *device; }; struct phy_package_shared { int addr; refcount_t refcnt; long unsigned int flags; size_t priv_size; void *priv; }; struct phy_driver { struct mdio_driver_common mdiodrv; u32 phy_id; char *name; u32 phy_id_mask; const long unsigned int * const features; u32 flags; const void *driver_data; int (*soft_reset)(struct phy_device *); int (*config_init)(struct phy_device *); int (*probe)(struct phy_device *); int (*get_features)(struct phy_device *); int (*suspend)(struct phy_device *); int (*resume)(struct phy_device *); int (*config_aneg)(struct phy_device *); int (*aneg_done)(struct phy_device *); int (*read_status)(struct phy_device *); int (*config_intr)(struct phy_device *); irqreturn_t (*handle_interrupt)(struct phy_device *); void (*remove)(struct phy_device *); int (*match_phy_device)(struct phy_device *); int (*set_wol)(struct phy_device *, struct ethtool_wolinfo *); void (*get_wol)(struct phy_device *, struct ethtool_wolinfo *); void (*link_change_notify)(struct phy_device *); int (*read_mmd)(struct phy_device *, int, u16); int (*write_mmd)(struct phy_device *, int, u16, u16); int (*read_page)(struct phy_device *); int (*write_page)(struct phy_device *, int); int (*module_info)(struct phy_device *, struct ethtool_modinfo *); int (*module_eeprom)(struct phy_device *, struct ethtool_eeprom *, u8 *); int (*cable_test_start)(struct phy_device *); int (*cable_test_tdr_start)(struct phy_device *, const struct phy_tdr_config *); int (*cable_test_get_status)(struct phy_device *, bool *); int (*get_sset_count)(struct phy_device *); void (*get_strings)(struct phy_device *, u8 *); void (*get_stats)(struct phy_device *, struct ethtool_stats *, u64 *); int (*get_tunable)(struct phy_device *, struct ethtool_tunable *, void *); int (*set_tunable)(struct phy_device *, struct ethtool_tunable *, const void *); int (*set_loopback)(struct phy_device *, bool); int (*get_sqi)(struct phy_device *); int (*get_sqi_max)(struct phy_device *); }; struct software_node_ref_args { const struct software_node *node; unsigned int nargs; u64 args[8]; }; struct swnode { struct kobject kobj; struct fwnode_handle fwnode; const struct software_node *node; int id; struct ida child_ids; struct list_head entry; struct list_head children; struct swnode *parent; unsigned int allocated: 1; unsigned int managed: 1; }; struct auxiliary_device_id { char name[32]; kernel_ulong_t driver_data; }; struct auxiliary_device { struct device dev; const char *name; u32 id; }; struct auxiliary_driver { int (*probe)(struct auxiliary_device *, const struct auxiliary_device_id *); void (*remove)(struct auxiliary_device *); void (*shutdown)(struct auxiliary_device *); int (*suspend)(struct auxiliary_device *, pm_message_t); int (*resume)(struct auxiliary_device *); const char *name; struct device_driver driver; const struct auxiliary_device_id *id_table; }; struct req { struct req *next; struct completion done; int err; const char *name; umode_t mode; kuid_t uid; kgid_t gid; struct device *dev; }; typedef int (*pm_callback_t)(struct device *); enum gpd_status { GENPD_STATE_ON = 0, GENPD_STATE_OFF = 1, }; enum genpd_notication { GENPD_NOTIFY_PRE_OFF = 0, GENPD_NOTIFY_OFF = 1, GENPD_NOTIFY_PRE_ON = 2, GENPD_NOTIFY_ON = 3, }; struct dev_power_governor { bool (*power_down_ok)(struct dev_pm_domain *); bool (*suspend_ok)(struct device *); }; struct gpd_dev_ops { int (*start)(struct device *); int (*stop)(struct device *); }; struct genpd_power_state { s64 power_off_latency_ns; s64 power_on_latency_ns; s64 residency_ns; u64 usage; u64 rejected; struct fwnode_handle *fwnode; ktime_t idle_time; void *data; }; struct opp_table; struct dev_pm_opp; struct genpd_lock_ops; struct generic_pm_domain { struct device dev; struct dev_pm_domain domain; struct list_head gpd_list_node; struct list_head parent_links; struct list_head child_links; struct list_head dev_list; struct dev_power_governor *gov; struct work_struct power_off_work; struct fwnode_handle *provider; bool has_provider; const char *name; atomic_t sd_count; enum gpd_status status; unsigned int device_count; unsigned int suspended_count; unsigned int prepared_count; unsigned int performance_state; cpumask_var_t cpus; int (*power_off)(struct generic_pm_domain *); int (*power_on)(struct generic_pm_domain *); struct raw_notifier_head power_notifiers; struct opp_table *opp_table; unsigned int (*opp_to_performance_state)(struct generic_pm_domain *, struct dev_pm_opp *); int (*set_performance_state)(struct generic_pm_domain *, unsigned int); struct gpd_dev_ops dev_ops; s64 max_off_time_ns; ktime_t next_wakeup; bool max_off_time_changed; bool cached_power_down_ok; bool cached_power_down_state_idx; int (*attach_dev)(struct generic_pm_domain *, struct device *); void (*detach_dev)(struct generic_pm_domain *, struct device *); unsigned int flags; struct genpd_power_state *states; void (*free_states)(struct genpd_power_state *, unsigned int); unsigned int state_count; unsigned int state_idx; ktime_t on_time; ktime_t accounting_time; const struct genpd_lock_ops *lock_ops; union { struct mutex mlock; struct { spinlock_t slock; long unsigned int lock_flags; }; }; }; struct genpd_lock_ops { void (*lock)(struct generic_pm_domain *); void (*lock_nested)(struct generic_pm_domain *, int); int (*lock_interruptible)(struct generic_pm_domain *); void (*unlock)(struct generic_pm_domain *); }; struct gpd_link { struct generic_pm_domain *parent; struct list_head parent_node; struct generic_pm_domain *child; struct list_head child_node; unsigned int performance_state; unsigned int prev_performance_state; }; struct gpd_timing_data { s64 suspend_latency_ns; s64 resume_latency_ns; s64 effective_constraint_ns; bool constraint_changed; bool cached_suspend_ok; }; struct generic_pm_domain_data { struct pm_domain_data base; struct gpd_timing_data td; struct notifier_block nb; struct notifier_block *power_nb; int cpu; unsigned int performance_state; unsigned int rpm_pstate; ktime_t next_wakeup; void *data; }; struct pm_clk_notifier_block { struct notifier_block nb; struct dev_pm_domain *pm_domain; char *con_ids[0]; }; enum pce_status { PCE_STATUS_NONE = 0, PCE_STATUS_ACQUIRED = 1, PCE_STATUS_PREPARED = 2, PCE_STATUS_ENABLED = 3, PCE_STATUS_ERROR = 4, }; struct pm_clock_entry { struct list_head node; char *con_id; struct clk *clk; enum pce_status status; bool enabled_when_prepared; }; struct isa_driver { int (*match)(struct device *, unsigned int); int (*probe)(struct device *, unsigned int); void (*remove)(struct device *, unsigned int); void (*shutdown)(struct device *, unsigned int); int (*suspend)(struct device *, unsigned int, pm_message_t); int (*resume)(struct device *, unsigned int); struct device_driver driver; struct device *devices; }; struct isa_dev { struct device dev; struct device *next; unsigned int id; }; enum fw_opt { FW_OPT_UEVENT = 1, FW_OPT_NOWAIT = 2, FW_OPT_USERHELPER = 4, FW_OPT_NO_WARN = 8, FW_OPT_NOCACHE = 16, FW_OPT_NOFALLBACK_SYSFS = 32, FW_OPT_FALLBACK_PLATFORM = 64, FW_OPT_PARTIAL = 128, }; enum fw_status { FW_STATUS_UNKNOWN = 0, FW_STATUS_LOADING = 1, FW_STATUS_DONE = 2, FW_STATUS_ABORTED = 3, }; struct fw_state { struct completion completion; enum fw_status status; }; struct firmware_cache; struct fw_priv { struct kref ref; struct list_head list; struct firmware_cache *fwc; struct fw_state fw_st; void *data; size_t size; size_t allocated_size; size_t offset; u32 opt_flags; bool is_paged_buf; struct page **pages; int nr_pages; int page_array_size; const char *fw_name; }; struct firmware_cache { spinlock_t lock; struct list_head head; int state; spinlock_t name_lock; struct list_head fw_names; struct delayed_work work; struct notifier_block pm_notify; }; struct fw_cache_entry { struct list_head list; const char *name; }; struct fw_name_devm { long unsigned int magic; const char *name; }; struct firmware_work { struct work_struct work; struct module *module; const char *name; struct device *device; void *context; void (*cont)(const struct firmware *, void *); u32 opt_flags; }; struct node_access_nodes { struct device dev; struct list_head list_node; unsigned int access; struct node_hmem_attrs hmem_attrs; }; struct node_cache_info { struct device dev; struct list_head node; struct node_cache_attrs cache_attrs; }; struct node_attr { struct device_attribute attr; enum node_states state; }; struct for_each_memory_block_cb_data { walk_memory_blocks_func_t func; void *arg; }; struct reg_sequence { unsigned int reg; unsigned int def; unsigned int delay_us; }; struct regmap___2; struct regmap_async { struct list_head list; struct regmap___2 *map; void *work_buf; }; struct reg_field { unsigned int reg; unsigned int lsb; unsigned int msb; unsigned int id_size; unsigned int id_offset; }; struct regmap_format { size_t buf_size; size_t reg_bytes; size_t pad_bytes; size_t val_bytes; void (*format_write)(struct regmap___2 *, unsigned int, unsigned int); void (*format_reg)(void *, unsigned int, unsigned int); void (*format_val)(void *, unsigned int, unsigned int); unsigned int (*parse_val)(const void *); void (*parse_inplace)(void *); }; struct hwspinlock; struct regcache_ops; struct regmap___2 { union { struct mutex mutex; struct { spinlock_t spinlock; long unsigned int spinlock_flags; }; }; regmap_lock lock; regmap_unlock unlock; void *lock_arg; gfp_t alloc_flags; struct device *dev; void *work_buf; struct regmap_format format; const struct regmap_bus *bus; void *bus_context; const char *name; bool async; spinlock_t async_lock; wait_queue_head_t async_waitq; struct list_head async_list; struct list_head async_free; int async_ret; bool debugfs_disable; struct dentry *debugfs; const char *debugfs_name; unsigned int debugfs_reg_len; unsigned int debugfs_val_len; unsigned int debugfs_tot_len; struct list_head debugfs_off_cache; struct mutex cache_lock; unsigned int max_register; bool (*writeable_reg)(struct device *, unsigned int); bool (*readable_reg)(struct device *, unsigned int); bool (*volatile_reg)(struct device *, unsigned int); bool (*precious_reg)(struct device *, unsigned int); bool (*writeable_noinc_reg)(struct device *, unsigned int); bool (*readable_noinc_reg)(struct device *, unsigned int); const struct regmap_access_table *wr_table; const struct regmap_access_table *rd_table; const struct regmap_access_table *volatile_table; const struct regmap_access_table *precious_table; const struct regmap_access_table *wr_noinc_table; const struct regmap_access_table *rd_noinc_table; int (*reg_read)(void *, unsigned int, unsigned int *); int (*reg_write)(void *, unsigned int, unsigned int); int (*reg_update_bits)(void *, unsigned int, unsigned int, unsigned int); bool defer_caching; long unsigned int read_flag_mask; long unsigned int write_flag_mask; int reg_shift; int reg_stride; int reg_stride_order; const struct regcache_ops *cache_ops; enum regcache_type cache_type; unsigned int cache_size_raw; unsigned int cache_word_size; unsigned int num_reg_defaults; unsigned int num_reg_defaults_raw; bool cache_only; bool cache_bypass; bool cache_free; struct reg_default *reg_defaults; const void *reg_defaults_raw; void *cache; bool cache_dirty; bool no_sync_defaults; struct reg_sequence *patch; int patch_regs; bool use_single_read; bool use_single_write; bool can_multi_write; size_t max_raw_read; size_t max_raw_write; struct rb_root range_tree; void *selector_work_buf; struct hwspinlock *hwlock; bool can_sleep; }; struct regcache_ops { const char *name; enum regcache_type type; int (*init)(struct regmap___2 *); int (*exit)(struct regmap___2 *); void (*debugfs_init)(struct regmap___2 *); int (*read)(struct regmap___2 *, unsigned int, unsigned int *); int (*write)(struct regmap___2 *, unsigned int, unsigned int); int (*sync)(struct regmap___2 *, unsigned int, unsigned int); int (*drop)(struct regmap___2 *, unsigned int, unsigned int); }; struct regmap_range_node { struct rb_node node; const char *name; struct regmap___2 *map; unsigned int range_min; unsigned int range_max; unsigned int selector_reg; unsigned int selector_mask; int selector_shift; unsigned int window_start; unsigned int window_len; }; struct regmap_field { struct regmap___2 *regmap; unsigned int mask; unsigned int shift; unsigned int reg; unsigned int id_size; unsigned int id_offset; }; struct trace_event_raw_regmap_reg { struct trace_entry ent; u32 __data_loc_name; unsigned int reg; unsigned int val; char __data[0]; }; struct trace_event_raw_regmap_block { struct trace_entry ent; u32 __data_loc_name; unsigned int reg; int count; char __data[0]; }; struct trace_event_raw_regcache_sync { struct trace_entry ent; u32 __data_loc_name; u32 __data_loc_status; u32 __data_loc_type; char __data[0]; }; struct trace_event_raw_regmap_bool { struct trace_entry ent; u32 __data_loc_name; int flag; char __data[0]; }; struct trace_event_raw_regmap_async { struct trace_entry ent; u32 __data_loc_name; char __data[0]; }; struct trace_event_raw_regcache_drop_region { struct trace_entry ent; u32 __data_loc_name; unsigned int from; unsigned int to; char __data[0]; }; struct trace_event_data_offsets_regmap_reg { u32 name; }; struct trace_event_data_offsets_regmap_block { u32 name; }; struct trace_event_data_offsets_regcache_sync { u32 name; u32 status; u32 type; }; struct trace_event_data_offsets_regmap_bool { u32 name; }; struct trace_event_data_offsets_regmap_async { u32 name; }; struct trace_event_data_offsets_regcache_drop_region { u32 name; }; typedef void (*btf_trace_regmap_reg_write)(void *, struct regmap___2 *, unsigned int, unsigned int); typedef void (*btf_trace_regmap_reg_read)(void *, struct regmap___2 *, unsigned int, unsigned int); typedef void (*btf_trace_regmap_reg_read_cache)(void *, struct regmap___2 *, unsigned int, unsigned int); typedef void (*btf_trace_regmap_hw_read_start)(void *, struct regmap___2 *, unsigned int, int); typedef void (*btf_trace_regmap_hw_read_done)(void *, struct regmap___2 *, unsigned int, int); typedef void (*btf_trace_regmap_hw_write_start)(void *, struct regmap___2 *, unsigned int, int); typedef void (*btf_trace_regmap_hw_write_done)(void *, struct regmap___2 *, unsigned int, int); typedef void (*btf_trace_regcache_sync)(void *, struct regmap___2 *, const char *, const char *); typedef void (*btf_trace_regmap_cache_only)(void *, struct regmap___2 *, bool); typedef void (*btf_trace_regmap_cache_bypass)(void *, struct regmap___2 *, bool); typedef void (*btf_trace_regmap_async_write_start)(void *, struct regmap___2 *, unsigned int, int); typedef void (*btf_trace_regmap_async_io_complete)(void *, struct regmap___2 *); typedef void (*btf_trace_regmap_async_complete_start)(void *, struct regmap___2 *); typedef void (*btf_trace_regmap_async_complete_done)(void *, struct regmap___2 *); typedef void (*btf_trace_regcache_drop_region)(void *, struct regmap___2 *, unsigned int, unsigned int); struct regcache_rbtree_node { void *block; long int *cache_present; unsigned int base_reg; unsigned int blklen; struct rb_node node; }; struct regcache_rbtree_ctx { struct rb_root root; struct regcache_rbtree_node *cached_rbnode; }; struct regmap_debugfs_off_cache { struct list_head list; off_t min; off_t max; unsigned int base_reg; unsigned int max_reg; }; struct regmap_debugfs_node { struct regmap___2 *map; struct list_head link; }; struct ptp_system_timestamp { struct timespec64 pre_ts; struct timespec64 post_ts; }; struct spi_statistics { spinlock_t lock; long unsigned int messages; long unsigned int transfers; long unsigned int errors; long unsigned int timedout; long unsigned int spi_sync; long unsigned int spi_sync_immediate; long unsigned int spi_async; long long unsigned int bytes; long long unsigned int bytes_rx; long long unsigned int bytes_tx; long unsigned int transfer_bytes_histo[17]; long unsigned int transfers_split_maxsize; }; struct spi_delay { u16 value; u8 unit; }; struct spi_controller; struct spi_device { struct device dev; struct spi_controller *controller; struct spi_controller *master; u32 max_speed_hz; u8 chip_select; u8 bits_per_word; bool rt; u32 mode; int irq; void *controller_state; void *controller_data; char modalias[32]; const char *driver_override; int cs_gpio; struct gpio_desc *cs_gpiod; struct spi_delay word_delay; struct spi_statistics statistics; }; struct spi_message; struct spi_transfer; struct spi_controller_mem_ops; struct spi_controller { struct device dev; struct list_head list; s16 bus_num; u16 num_chipselect; u16 dma_alignment; u32 mode_bits; u32 buswidth_override_bits; u32 bits_per_word_mask; u32 min_speed_hz; u32 max_speed_hz; u16 flags; bool devm_allocated; bool slave; size_t (*max_transfer_size)(struct spi_device *); size_t (*max_message_size)(struct spi_device *); struct mutex io_mutex; spinlock_t bus_lock_spinlock; struct mutex bus_lock_mutex; bool bus_lock_flag; int (*setup)(struct spi_device *); int (*set_cs_timing)(struct spi_device *, struct spi_delay *, struct spi_delay *, struct spi_delay *); int (*transfer)(struct spi_device *, struct spi_message *); void (*cleanup)(struct spi_device *); bool (*can_dma)(struct spi_controller *, struct spi_device *, struct spi_transfer *); struct device *dma_map_dev; bool queued; struct kthread_worker *kworker; struct kthread_work pump_messages; spinlock_t queue_lock; struct list_head queue; struct spi_message *cur_msg; bool idling; bool busy; bool running; bool rt; bool auto_runtime_pm; bool cur_msg_prepared; bool cur_msg_mapped; bool last_cs_enable; bool last_cs_mode_high; bool fallback; struct completion xfer_completion; size_t max_dma_len; int (*prepare_transfer_hardware)(struct spi_controller *); int (*transfer_one_message)(struct spi_controller *, struct spi_message *); int (*unprepare_transfer_hardware)(struct spi_controller *); int (*prepare_message)(struct spi_controller *, struct spi_message *); int (*unprepare_message)(struct spi_controller *, struct spi_message *); int (*slave_abort)(struct spi_controller *); void (*set_cs)(struct spi_device *, bool); int (*transfer_one)(struct spi_controller *, struct spi_device *, struct spi_transfer *); void (*handle_err)(struct spi_controller *, struct spi_message *); const struct spi_controller_mem_ops *mem_ops; struct spi_delay cs_setup; struct spi_delay cs_hold; struct spi_delay cs_inactive; int *cs_gpios; struct gpio_desc **cs_gpiods; bool use_gpio_descriptors; s8 unused_native_cs; s8 max_native_cs; struct spi_statistics statistics; struct dma_chan___2 *dma_tx; struct dma_chan___2 *dma_rx; void *dummy_rx; void *dummy_tx; int (*fw_translate_cs)(struct spi_controller *, unsigned int); bool ptp_sts_supported; long unsigned int irq_flags; }; struct spi_message { struct list_head transfers; struct spi_device *spi; unsigned int is_dma_mapped: 1; void (*complete)(void *); void *context; unsigned int frame_length; unsigned int actual_length; int status; struct list_head queue; void *state; struct list_head resources; }; struct spi_transfer { const void *tx_buf; void *rx_buf; unsigned int len; dma_addr_t tx_dma; dma_addr_t rx_dma; struct sg_table tx_sg; struct sg_table rx_sg; unsigned int dummy_data: 1; unsigned int cs_change: 1; unsigned int tx_nbits: 3; unsigned int rx_nbits: 3; u8 bits_per_word; struct spi_delay delay; struct spi_delay cs_change_delay; struct spi_delay word_delay; u32 speed_hz; u32 effective_speed_hz; unsigned int ptp_sts_word_pre; unsigned int ptp_sts_word_post; struct ptp_system_timestamp *ptp_sts; bool timestamped; struct list_head transfer_list; u16 error; }; struct spi_mem; struct spi_mem_op; struct spi_mem_dirmap_desc; struct spi_controller_mem_ops { int (*adjust_op_size)(struct spi_mem *, struct spi_mem_op *); bool (*supports_op)(struct spi_mem *, const struct spi_mem_op *); int (*exec_op)(struct spi_mem *, const struct spi_mem_op *); const char * (*get_name)(struct spi_mem *); int (*dirmap_create)(struct spi_mem_dirmap_desc *); void (*dirmap_destroy)(struct spi_mem_dirmap_desc *); ssize_t (*dirmap_read)(struct spi_mem_dirmap_desc *, u64, size_t, void *); ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *, u64, size_t, const void *); int (*poll_status)(struct spi_mem *, const struct spi_mem_op *, u16, u16, long unsigned int, long unsigned int, long unsigned int); }; struct regmap_async_spi { struct regmap_async core; struct spi_message m; struct spi_transfer t[2]; }; struct regmap_mmio_context { void *regs; unsigned int val_bytes; bool relaxed_mmio; bool attached_clk; struct clk *clk; void (*reg_write)(struct regmap_mmio_context *, unsigned int, unsigned int); unsigned int (*reg_read)(struct regmap_mmio_context *, unsigned int); }; struct regmap_irq_chip_data___2 { struct mutex lock; struct irq_chip irq_chip; struct regmap___2 *map; const struct regmap_irq_chip *chip; int irq_base; struct irq_domain *domain; int irq; int wake_count; void *status_reg_buf; unsigned int *main_status_buf; unsigned int *status_buf; unsigned int *mask_buf; unsigned int *mask_buf_def; unsigned int *wake_buf; unsigned int *type_buf; unsigned int *type_buf_def; unsigned int **virt_buf; unsigned int irq_reg_stride; unsigned int type_reg_stride; bool clear_status: 1; }; struct devcd_entry { struct device devcd_dev; void *data; size_t datalen; struct module *owner; ssize_t (*read)(char *, loff_t, size_t, void *, size_t); void (*free)(void *); struct delayed_work del_wk; struct device *failing_dev; }; typedef void (*irq_write_msi_msg_t)(struct msi_desc *, struct msi_msg *); struct platform_msi_priv_data { struct device *dev; void *host_data; msi_alloc_info_t arg; irq_write_msi_msg_t write_msg; int devid; }; struct trace_event_raw_devres { struct trace_entry ent; u32 __data_loc_devname; struct device *dev; const char *op; void *node; const char *name; size_t size; char __data[0]; }; struct trace_event_data_offsets_devres { u32 devname; }; typedef void (*btf_trace_devres_log)(void *, struct device *, const char *, void *, const char *, size_t); struct mfd_cell_acpi_match { const char *pnpid; const long long unsigned int adr; }; enum { CHIP_INVALID = 0, CHIP_PM8606 = 1, CHIP_PM8607 = 2, CHIP_MAX = 3, }; enum pm8606_ref_gp_and_osc_clients { REF_GP_NO_CLIENTS = 0, WLED1_DUTY = 1, WLED2_DUTY = 2, WLED3_DUTY = 4, RGB1_ENABLE = 8, RGB2_ENABLE = 16, LDO_VBR_EN = 32, REF_GP_MAX_CLIENT = 65535, }; enum { PM8607_IRQ_ONKEY = 0, PM8607_IRQ_EXTON = 1, PM8607_IRQ_CHG = 2, PM8607_IRQ_BAT = 3, PM8607_IRQ_RTC = 4, PM8607_IRQ_CC = 5, PM8607_IRQ_VBAT = 6, PM8607_IRQ_VCHG = 7, PM8607_IRQ_VSYS = 8, PM8607_IRQ_TINT = 9, PM8607_IRQ_GPADC0 = 10, PM8607_IRQ_GPADC1 = 11, PM8607_IRQ_GPADC2 = 12, PM8607_IRQ_GPADC3 = 13, PM8607_IRQ_AUDIO_SHORT = 14, PM8607_IRQ_PEN = 15, PM8607_IRQ_HEADSET = 16, PM8607_IRQ_HOOK = 17, PM8607_IRQ_MICIN = 18, PM8607_IRQ_CHG_FAIL = 19, PM8607_IRQ_CHG_DONE = 20, PM8607_IRQ_CHG_FAULT = 21, }; struct pm860x_chip { struct device *dev; struct mutex irq_lock; struct mutex osc_lock; struct i2c_client *client; struct i2c_client *companion; struct regmap *regmap; struct regmap *regmap_companion; int buck3_double; int companion_addr; short unsigned int osc_vote; int id; int irq_mode; int irq_base; int core_irq; unsigned char chip_version; unsigned char osc_status; unsigned int wakeup_flag; }; enum { GI2C_PORT = 0, PI2C_PORT = 1, }; struct pm860x_backlight_pdata { int pwm; int iset; }; struct pm860x_led_pdata { int iset; }; struct pm860x_rtc_pdata { int (*sync)(unsigned int); int vrtc; }; struct pm860x_touch_pdata { int gpadc_prebias; int slot_cycle; int off_scale; int sw_cal; int tsi_prebias; int pen_prebias; int pen_prechg; int res_x; long unsigned int flags; }; struct pm860x_power_pdata { int max_capacity; int resistor; }; struct charger_desc; struct pm860x_platform_data { struct pm860x_backlight_pdata *backlight; struct pm860x_led_pdata *led; struct pm860x_rtc_pdata *rtc; struct pm860x_touch_pdata *touch; struct pm860x_power_pdata *power; struct regulator_init_data *buck1; struct regulator_init_data *buck2; struct regulator_init_data *buck3; struct regulator_init_data *ldo1; struct regulator_init_data *ldo2; struct regulator_init_data *ldo3; struct regulator_init_data *ldo4; struct regulator_init_data *ldo5; struct regulator_init_data *ldo6; struct regulator_init_data *ldo7; struct regulator_init_data *ldo8; struct regulator_init_data *ldo9; struct regulator_init_data *ldo10; struct regulator_init_data *ldo12; struct regulator_init_data *ldo_vibrator; struct regulator_init_data *ldo14; struct charger_desc *chg_desc; int companion_addr; int i2c_port; int irq_mode; int irq_base; int num_leds; int num_backlights; }; enum polling_modes { CM_POLL_DISABLE = 0, CM_POLL_ALWAYS = 1, CM_POLL_EXTERNAL_POWER_ONLY = 2, CM_POLL_CHARGING_ONLY = 3, }; enum data_source { CM_BATTERY_PRESENT = 0, CM_NO_BATTERY = 1, CM_FUEL_GAUGE = 2, CM_CHARGER_STAT = 3, }; struct charger_regulator; struct charger_desc { const char *psy_name; enum polling_modes polling_mode; unsigned int polling_interval_ms; unsigned int fullbatt_vchkdrop_uV; unsigned int fullbatt_uV; unsigned int fullbatt_soc; unsigned int fullbatt_full_capacity; enum data_source battery_present; const char **psy_charger_stat; int num_charger_regulators; struct charger_regulator *charger_regulators; const struct attribute_group **sysfs_groups; const char *psy_fuel_gauge; const char *thermal_zone; int temp_min; int temp_max; int temp_diff; bool measure_battery_temp; u32 charging_max_duration_ms; u32 discharging_max_duration_ms; }; struct charger_manager; struct charger_cable { const char *extcon_name; const char *name; struct extcon_dev *extcon_dev; u64 extcon_type; struct work_struct wq; struct notifier_block nb; bool attached; struct charger_regulator *charger; int min_uA; int max_uA; struct charger_manager *cm; }; struct charger_regulator { const char *regulator_name; struct regulator *consumer; int externally_control; struct charger_cable *cables; int num_cables; struct attribute_group attr_grp; struct device_attribute attr_name; struct device_attribute attr_state; struct device_attribute attr_externally_control; struct attribute *attrs[4]; struct charger_manager *cm; }; struct charger_manager { struct list_head entry; struct device *dev; struct charger_desc *desc; struct thermal_zone_device *tzd_batt; bool charger_enabled; int emergency_stop; char psy_name_buf[31]; struct power_supply_desc charger_psy_desc; struct power_supply *charger_psy; u64 charging_start_time; u64 charging_end_time; int battery_status; }; struct pm860x_irq_data { int reg; int mask_reg; int enable; int offs; }; struct htcpld_chip_platform_data { unsigned int addr; unsigned int reset; unsigned int num_gpios; unsigned int gpio_out_base; unsigned int gpio_in_base; unsigned int irq_base; unsigned int num_irqs; }; struct htcpld_core_platform_data { unsigned int int_reset_gpio_hi; unsigned int int_reset_gpio_lo; unsigned int i2c_adapter_id; struct htcpld_chip_platform_data *chip; unsigned int num_chip; }; struct htcpld_chip { spinlock_t lock; u8 reset; u8 addr; struct device *dev; struct i2c_client *client; u8 cache_out; struct gpio_chip chip_out; u8 cache_in; struct gpio_chip chip_in; u16 irqs_enabled; uint irq_start; int nirqs; unsigned int flow_type; struct work_struct set_val_work; }; struct htcpld_data { u16 irqs_enabled; uint irq_start; int nirqs; uint chained_irq; unsigned int int_reset_gpio_hi; unsigned int int_reset_gpio_lo; struct htcpld_chip *chip; unsigned int nchips; }; struct wm8400_platform_data { int (*platform_init)(struct device *); }; struct wm8400 { struct device *dev; struct regmap *regmap; struct platform_device regulators[6]; }; enum wm831x_auxadc { WM831X_AUX_CAL = 15, WM831X_AUX_BKUP_BATT = 10, WM831X_AUX_WALL = 9, WM831X_AUX_BATT = 8, WM831X_AUX_USB = 7, WM831X_AUX_SYSVDD = 6, WM831X_AUX_BATT_TEMP = 5, WM831X_AUX_CHIP_TEMP = 4, WM831X_AUX_AUX4 = 3, WM831X_AUX_AUX3 = 2, WM831X_AUX_AUX2 = 1, WM831X_AUX_AUX1 = 0, }; struct wm831x_backlight_pdata { int isink; int max_uA; }; struct wm831x_backup_pdata { int charger_enable; int no_constant_voltage; int vlim; int ilim; }; struct wm831x_battery_pdata { int enable; int fast_enable; int off_mask; int trickle_ilim; int vsel; int eoc_iterm; int fast_ilim; int timeout; }; enum wm831x_status_src { WM831X_STATUS_PRESERVE = 0, WM831X_STATUS_OTP = 1, WM831X_STATUS_POWER = 2, WM831X_STATUS_CHARGER = 3, WM831X_STATUS_MANUAL = 4, }; struct wm831x_status_pdata { enum wm831x_status_src default_src; const char *name; const char *default_trigger; }; struct wm831x_touch_pdata { int fivewire; int isel; int rpu; int pressure; unsigned int data_irq; int data_irqf; unsigned int pd_irq; int pd_irqf; }; enum wm831x_watchdog_action { WM831X_WDOG_NONE = 0, WM831X_WDOG_INTERRUPT = 1, WM831X_WDOG_RESET = 2, WM831X_WDOG_WAKE = 3, }; struct wm831x_watchdog_pdata { enum wm831x_watchdog_action primary; enum wm831x_watchdog_action secondary; unsigned int software: 1; }; struct wm831x; struct wm831x_pdata { int wm831x_num; int (*pre_init)(struct wm831x *); int (*post_init)(struct wm831x *); bool irq_cmos; bool disable_touch; bool soft_shutdown; int irq_base; int gpio_base; int gpio_defaults[16]; struct wm831x_backlight_pdata *backlight; struct wm831x_backup_pdata *backup; struct wm831x_battery_pdata *battery; struct wm831x_touch_pdata *touch; struct wm831x_watchdog_pdata *watchdog; struct wm831x_status_pdata *status[2]; struct regulator_init_data *dcdc[4]; struct regulator_init_data *epe[2]; struct regulator_init_data *ldo[11]; struct regulator_init_data *isink[2]; }; enum wm831x_parent { WM8310 = 33552, WM8311 = 33553, WM8312 = 33554, WM8320 = 33568, WM8321 = 33569, WM8325 = 33573, WM8326 = 33574, }; typedef int (*wm831x_auxadc_read_fn)(struct wm831x *, enum wm831x_auxadc); struct wm831x { struct mutex io_lock; struct device *dev; struct regmap *regmap; struct wm831x_pdata pdata; enum wm831x_parent type; int irq; struct mutex irq_lock; struct irq_domain *irq_domain; int irq_masks_cur[5]; int irq_masks_cache[5]; bool soft_shutdown; unsigned int has_gpio_ena: 1; unsigned int has_cs_sts: 1; unsigned int charger_irq_wake: 1; int num_gpio; int gpio_update[16]; bool gpio_level_high[16]; bool gpio_level_low[16]; struct mutex auxadc_lock; struct list_head auxadc_pending; u16 auxadc_active; wm831x_auxadc_read_fn auxadc_read; struct mutex key_lock; unsigned int locked: 1; }; struct wm831x_irq_data { int primary; int reg; int mask; }; struct wm831x_auxadc_req { struct list_head list; enum wm831x_auxadc input; int val; struct completion done; }; struct spi_device_id { char name[32]; kernel_ulong_t driver_data; }; struct spi_driver { const struct spi_device_id *id_table; int (*probe)(struct spi_device *); int (*remove)(struct spi_device *); void (*shutdown)(struct spi_device *); struct device_driver driver; }; struct wm8350_audio_platform_data { int vmid_discharge_msecs; int drain_msecs; int cap_discharge_msecs; int vmid_charge_msecs; u32 vmid_s_curve: 2; u32 dis_out4: 2; u32 dis_out3: 2; u32 dis_out2: 2; u32 dis_out1: 2; u32 vroi_out4: 1; u32 vroi_out3: 1; u32 vroi_out2: 1; u32 vroi_out1: 1; u32 vroi_enable: 1; u32 codec_current_on: 2; u32 codec_current_standby: 2; u32 codec_current_charge: 2; }; struct wm8350_codec { struct platform_device *pdev; struct wm8350_audio_platform_data *platform_data; }; struct wm8350_gpio { struct platform_device *pdev; }; struct wm8350_led { struct platform_device *pdev; struct work_struct work; spinlock_t value_lock; enum led_brightness value; struct led_classdev cdev; int max_uA_index; int enabled; struct regulator *isink; struct regulator_consumer_supply isink_consumer; struct regulator_init_data isink_init; struct regulator *dcdc; struct regulator_consumer_supply dcdc_consumer; struct regulator_init_data dcdc_init; }; struct wm8350_pmic { int max_dcdc; int max_isink; int isink_A_dcdc; int isink_B_dcdc; u16 dcdc1_hib_mode; u16 dcdc3_hib_mode; u16 dcdc4_hib_mode; u16 dcdc6_hib_mode; struct platform_device *pdev[12]; struct wm8350_led led[2]; }; struct rtc_device___2; struct wm8350_rtc { struct platform_device *pdev; struct rtc_device___2 *rtc; int alarm_enabled; int update_enabled; }; struct wm8350_charger_policy { int eoc_mA; int charge_mV; int fast_limit_mA; int fast_limit_USB_mA; int charge_timeout; int trickle_start_mV; int trickle_charge_mA; int trickle_charge_USB_mA; }; struct wm8350_power { struct platform_device *pdev; struct power_supply *battery; struct power_supply *usb; struct power_supply *ac; struct wm8350_charger_policy *policy; int rev_g_coeff; }; struct wm8350_wdt { struct platform_device *pdev; }; struct wm8350_hwmon { struct platform_device *pdev; struct device *classdev; }; struct wm8350 { struct device *dev; struct regmap *regmap; bool unlocked; struct mutex auxadc_mutex; struct completion auxadc_done; struct mutex irq_lock; int chip_irq; int irq_base; u16 irq_masks[7]; struct wm8350_codec codec; struct wm8350_gpio gpio; struct wm8350_hwmon hwmon; struct wm8350_pmic pmic; struct wm8350_power power; struct wm8350_rtc rtc; struct wm8350_wdt wdt; }; struct wm8350_platform_data { int (*init)(struct wm8350 *); int irq_high; int irq_base; int gpio_base; }; struct wm8350_reg_access { u16 readable; u16 writable; u16 vol; }; struct wm8350_irq_data { int primary; int reg; int mask; int primary_only; }; struct tps65910_platform_data { int irq; int irq_base; }; enum chips { TPS80031 = 1, TPS80032 = 2, }; enum { TPS80031_INT_PWRON = 0, TPS80031_INT_RPWRON = 1, TPS80031_INT_SYS_VLOW = 2, TPS80031_INT_RTC_ALARM = 3, TPS80031_INT_RTC_PERIOD = 4, TPS80031_INT_HOT_DIE = 5, TPS80031_INT_VXX_SHORT = 6, TPS80031_INT_SPDURATION = 7, TPS80031_INT_WATCHDOG = 8, TPS80031_INT_BAT = 9, TPS80031_INT_SIM = 10, TPS80031_INT_MMC = 11, TPS80031_INT_RES = 12, TPS80031_INT_GPADC_RT = 13, TPS80031_INT_GPADC_SW2_EOC = 14, TPS80031_INT_CC_AUTOCAL = 15, TPS80031_INT_ID_WKUP = 16, TPS80031_INT_VBUSS_WKUP = 17, TPS80031_INT_ID = 18, TPS80031_INT_VBUS = 19, TPS80031_INT_CHRG_CTRL = 20, TPS80031_INT_EXT_CHRG = 21, TPS80031_INT_INT_CHRG = 22, TPS80031_INT_RES2 = 23, TPS80031_INT_BAT_TEMP_OVRANGE = 24, TPS80031_INT_BAT_REMOVED = 25, TPS80031_INT_VBUS_DET = 26, TPS80031_INT_VAC_DET = 27, TPS80031_INT_FAULT_WDG = 28, TPS80031_INT_LINCH_GATED = 29, TPS80031_INT_NR = 30, }; enum { TPS80031_REGULATOR_VIO = 0, TPS80031_REGULATOR_SMPS1 = 1, TPS80031_REGULATOR_SMPS2 = 2, TPS80031_REGULATOR_SMPS3 = 3, TPS80031_REGULATOR_SMPS4 = 4, TPS80031_REGULATOR_VANA = 5, TPS80031_REGULATOR_LDO1 = 6, TPS80031_REGULATOR_LDO2 = 7, TPS80031_REGULATOR_LDO3 = 8, TPS80031_REGULATOR_LDO4 = 9, TPS80031_REGULATOR_LDO5 = 10, TPS80031_REGULATOR_LDO6 = 11, TPS80031_REGULATOR_LDO7 = 12, TPS80031_REGULATOR_LDOLN = 13, TPS80031_REGULATOR_LDOUSB = 14, TPS80031_REGULATOR_VBUS = 15, TPS80031_REGULATOR_REGEN1 = 16, TPS80031_REGULATOR_REGEN2 = 17, TPS80031_REGULATOR_SYSEN = 18, TPS80031_REGULATOR_MAX = 19, }; enum tps80031_ext_control { TPS80031_PWR_REQ_INPUT_NONE = 0, TPS80031_PWR_REQ_INPUT_PREQ1 = 1, TPS80031_PWR_REQ_INPUT_PREQ2 = 2, TPS80031_PWR_REQ_INPUT_PREQ3 = 4, TPS80031_PWR_OFF_ON_SLEEP = 8, TPS80031_PWR_ON_ON_SLEEP = 16, }; enum tps80031_pupd_pins { TPS80031_PREQ1 = 0, TPS80031_PREQ2A = 1, TPS80031_PREQ2B = 2, TPS80031_PREQ2C = 3, TPS80031_PREQ3 = 4, TPS80031_NRES_WARM = 5, TPS80031_PWM_FORCE = 6, TPS80031_CHRG_EXT_CHRG_STATZ = 7, TPS80031_SIM = 8, TPS80031_MMC = 9, TPS80031_GPADC_START = 10, TPS80031_DVSI2C_SCL = 11, TPS80031_DVSI2C_SDA = 12, TPS80031_CTLI2C_SCL = 13, TPS80031_CTLI2C_SDA = 14, }; enum tps80031_pupd_settings { TPS80031_PUPD_NORMAL = 0, TPS80031_PUPD_PULLDOWN = 1, TPS80031_PUPD_PULLUP = 2, }; struct tps80031 { struct device *dev; long unsigned int chip_info; int es_version; struct i2c_client *clients[4]; struct regmap *regmap[4]; struct regmap_irq_chip_data *irq_data; }; struct tps80031_pupd_init_data { int input_pin; int setting; }; struct tps80031_regulator_platform_data { struct regulator_init_data *reg_init_data; unsigned int ext_ctrl_flag; unsigned int config_flags; }; struct tps80031_platform_data { int irq_base; bool use_power_off; struct tps80031_pupd_init_data *pupd_init_data; int pupd_init_data_size; struct tps80031_regulator_platform_data *regulator_pdata[19]; }; struct tps80031_pupd_data { u8 reg; u8 pullup_bit; u8 pulldown_bit; }; struct of_dev_auxdata { char *compatible; resource_size_t phys_addr; char *name; void *platform_data; }; struct matrix_keymap_data { const uint32_t *keymap; unsigned int keymap_size; }; enum twl_module_ids { TWL_MODULE_USB = 0, TWL_MODULE_PIH = 1, TWL_MODULE_MAIN_CHARGE = 2, TWL_MODULE_PM_MASTER = 3, TWL_MODULE_PM_RECEIVER = 4, TWL_MODULE_RTC = 5, TWL_MODULE_PWM = 6, TWL_MODULE_LED = 7, TWL_MODULE_SECURED_REG = 8, TWL_MODULE_LAST = 9, }; enum twl4030_module_ids { TWL4030_MODULE_AUDIO_VOICE = 9, TWL4030_MODULE_GPIO = 10, TWL4030_MODULE_INTBR = 11, TWL4030_MODULE_TEST = 12, TWL4030_MODULE_KEYPAD = 13, TWL4030_MODULE_MADC = 14, TWL4030_MODULE_INTERRUPTS = 15, TWL4030_MODULE_PRECHARGE = 16, TWL4030_MODULE_BACKUP = 17, TWL4030_MODULE_INT = 18, TWL5031_MODULE_ACCESSORY = 19, TWL5031_MODULE_INTERRUPTS = 20, TWL4030_MODULE_LAST = 21, }; enum twl6030_module_ids { TWL6030_MODULE_ID0 = 9, TWL6030_MODULE_ID1 = 10, TWL6030_MODULE_ID2 = 11, TWL6030_MODULE_GPADC = 12, TWL6030_MODULE_GASGAUGE = 13, TWL6030_MODULE_LAST = 14, }; struct twl4030_clock_init_data { bool ck32k_lowpwr_enable; }; struct twl4030_bci_platform_data { int *battery_tmp_tbl; unsigned int tblsize; int bb_uvolt; int bb_uamp; }; struct twl4030_gpio_platform_data { bool use_leds; u8 mmc_cd; u32 debounce; u32 pullups; u32 pulldowns; int (*setup)(struct device *, unsigned int, unsigned int); int (*teardown)(struct device *, unsigned int, unsigned int); }; struct twl4030_madc_platform_data { int irq_line; }; struct twl4030_keypad_data { const struct matrix_keymap_data *keymap_data; unsigned int rows; unsigned int cols; bool rep; }; enum twl4030_usb_mode { T2_USB_MODE_ULPI = 1, T2_USB_MODE_CEA2011_3PIN = 2, }; struct twl4030_usb_data { enum twl4030_usb_mode usb_mode; long unsigned int features; int (*phy_init)(struct device *); int (*phy_exit)(struct device *); int (*phy_power)(struct device *, int, int); int (*phy_set_clock)(struct device *, int); int (*phy_suspend)(struct device *, int); }; struct twl4030_ins { u16 pmb_message; u8 delay; }; struct twl4030_script { struct twl4030_ins *script; unsigned int size; u8 flags; }; struct twl4030_resconfig { u8 resource; u8 devgroup; u8 type; u8 type2; u8 remap_off; u8 remap_sleep; }; struct twl4030_power_data { struct twl4030_script **scripts; unsigned int num; struct twl4030_resconfig *resource_config; struct twl4030_resconfig *board_config; bool use_poweroff; bool ac_charger_quirk; }; struct twl4030_codec_data { unsigned int digimic_delay; unsigned int ramp_delay_value; unsigned int offset_cncl_path; unsigned int hs_extmute: 1; int hs_extmute_gpio; }; struct twl4030_vibra_data { unsigned int coexist; }; struct twl4030_audio_data { unsigned int audio_mclk; struct twl4030_codec_data *codec; struct twl4030_vibra_data *vibra; int audpwron_gpio; int naudint_irq; unsigned int irq_base; }; struct twl4030_platform_data { struct twl4030_clock_init_data *clock; struct twl4030_bci_platform_data *bci; struct twl4030_gpio_platform_data *gpio; struct twl4030_madc_platform_data *madc; struct twl4030_keypad_data *keypad; struct twl4030_usb_data *usb; struct twl4030_power_data *power; struct twl4030_audio_data *audio; struct regulator_init_data *vdac; struct regulator_init_data *vaux1; struct regulator_init_data *vaux2; struct regulator_init_data *vaux3; struct regulator_init_data *vdd1; struct regulator_init_data *vdd2; struct regulator_init_data *vdd3; struct regulator_init_data *vpll1; struct regulator_init_data *vpll2; struct regulator_init_data *vmmc1; struct regulator_init_data *vmmc2; struct regulator_init_data *vsim; struct regulator_init_data *vaux4; struct regulator_init_data *vio; struct regulator_init_data *vintana1; struct regulator_init_data *vintana2; struct regulator_init_data *vintdig; struct regulator_init_data *vmmc; struct regulator_init_data *vpp; struct regulator_init_data *vusim; struct regulator_init_data *vana; struct regulator_init_data *vcxio; struct regulator_init_data *vusb; struct regulator_init_data *clk32kg; struct regulator_init_data *v1v8; struct regulator_init_data *v2v1; struct regulator_init_data *ldo1; struct regulator_init_data *ldo2; struct regulator_init_data *ldo3; struct regulator_init_data *ldo4; struct regulator_init_data *ldo5; struct regulator_init_data *ldo6; struct regulator_init_data *ldo7; struct regulator_init_data *ldoln; struct regulator_init_data *ldousb; struct regulator_init_data *smps3; struct regulator_init_data *smps4; struct regulator_init_data *vio6025; }; struct twl_regulator_driver_data { int (*set_voltage)(void *, int); int (*get_voltage)(void *); void *data; long unsigned int features; }; struct twl_client { struct i2c_client *client; struct regmap *regmap; }; struct twl_mapping { unsigned char sid; unsigned char base; }; struct twl_private { bool ready; u32 twl_idcode; unsigned int twl_id; struct twl_mapping *twl_map; struct twl_client *twl_modules; }; struct sih_irq_data { u8 isr_offset; u8 imr_offset; }; struct sih { char name[8]; u8 module; u8 control_offset; bool set_cor; u8 bits; u8 bytes_ixr; u8 edr_offset; u8 bytes_edr; u8 irq_lines; struct sih_irq_data mask[2]; }; struct sih_agent { int irq_base; const struct sih *sih; u32 imr; bool imr_change_pending; u32 edge_change; struct mutex irq_lock; char *irq_name; }; struct twl6030_irq { unsigned int irq_base; int twl_irq; bool irq_wake_enabled; atomic_t wakeirqs; struct notifier_block pm_nb; struct irq_chip irq_chip; struct irq_domain *irq_domain; const int *irq_mapping_tbl; }; enum twl4030_audio_res { TWL4030_AUDIO_RES_POWER = 0, TWL4030_AUDIO_RES_APLL = 1, TWL4030_AUDIO_RES_MAX = 2, }; struct twl4030_audio_resource { int request_count; u8 reg; u8 mask; }; struct twl4030_audio { unsigned int audio_mclk; struct mutex mutex; struct twl4030_audio_resource resource[2]; struct mfd_cell cells[2]; }; enum of_gpio_flags { OF_GPIO_ACTIVE_LOW = 1, OF_GPIO_SINGLE_ENDED = 2, OF_GPIO_OPEN_DRAIN = 4, OF_GPIO_TRANSITORY = 8, OF_GPIO_PULL_UP = 16, OF_GPIO_PULL_DOWN = 32, }; struct twl6040 { struct device *dev; struct regmap *regmap; struct regmap_irq_chip_data *irq_data; struct regulator_bulk_data supplies[2]; struct clk *clk32k; struct clk *mclk; struct mutex mutex; struct mutex irq_mutex; struct mfd_cell cells[4]; struct completion ready; int audpwron; int power_count; int rev; int pll; unsigned int sysclk_rate; unsigned int mclk_rate; unsigned int irq; unsigned int irq_ready; unsigned int irq_th; }; struct mfd_of_node_entry { struct list_head list; struct device *dev; struct device_node *np; }; struct pcap_subdev { int id; const char *name; void *platform_data; }; struct pcap_platform_data { unsigned int irq_base; unsigned int config; int gpio; void (*init)(void *); int num_subdevs; struct pcap_subdev *subdevs; }; struct pcap_adc_request { u8 bank; u8 ch[2]; u32 flags; void (*callback)(void *, u16 *); void *data; }; struct pcap_adc_sync_request { u16 res[2]; struct completion completion; }; struct pcap_chip { struct spi_device *spi; u32 buf; spinlock_t io_lock; unsigned int irq_base; u32 msr; struct work_struct isr_work; struct work_struct msr_work; struct workqueue_struct *workqueue; struct pcap_adc_request *adc_queue[8]; u8 adc_head; u8 adc_tail; spinlock_t adc_lock; }; struct da903x_subdev_info { int id; const char *name; void *platform_data; }; struct da903x_platform_data { int num_subdevs; struct da903x_subdev_info *subdevs; }; struct da903x_chip; struct da903x_chip_ops { int (*init_chip)(struct da903x_chip *); int (*unmask_events)(struct da903x_chip *, unsigned int); int (*mask_events)(struct da903x_chip *, unsigned int); int (*read_events)(struct da903x_chip *, unsigned int *); int (*read_status)(struct da903x_chip *, unsigned int *); }; struct da903x_chip { struct i2c_client *client; struct device *dev; const struct da903x_chip_ops *ops; int type; uint32_t events_mask; struct mutex lock; struct work_struct irq_work; struct blocking_notifier_head notifier_list; }; struct da9052 { struct device *dev; struct regmap *regmap; struct mutex auxadc_lock; struct completion done; int irq_base; struct regmap_irq_chip_data *irq_data; u8 chip_id; int chip_irq; int (*fix_io)(struct da9052 *, unsigned char); }; struct led_platform_data; struct da9052_pdata { struct led_platform_data *pled; int (*init)(struct da9052 *); int irq_base; int gpio_base; int use_for_apm; struct regulator_init_data *regulators[14]; }; enum da9052_chip_id { DA9052 = 0, DA9053_AA = 1, DA9053_BA = 2, DA9053_BB = 3, DA9053_BC = 4, }; enum lp8788_int_id { LP8788_INT_TSDL = 0, LP8788_INT_TSDH = 1, LP8788_INT_UVLO = 2, LP8788_INT_FLAGMON = 3, LP8788_INT_PWRON_TIME = 4, LP8788_INT_PWRON = 5, LP8788_INT_COMP1 = 6, LP8788_INT_COMP2 = 7, LP8788_INT_CHG_INPUT_STATE = 8, LP8788_INT_CHG_STATE = 9, LP8788_INT_EOC = 10, LP8788_INT_CHG_RESTART = 11, LP8788_INT_RESTART_TIMEOUT = 12, LP8788_INT_FULLCHG_TIMEOUT = 13, LP8788_INT_PRECHG_TIMEOUT = 14, LP8788_INT_RTC_ALARM1 = 17, LP8788_INT_RTC_ALARM2 = 18, LP8788_INT_ENTER_SYS_SUPPORT = 19, LP8788_INT_EXIT_SYS_SUPPORT = 20, LP8788_INT_BATT_LOW = 21, LP8788_INT_NO_BATT = 22, LP8788_INT_MAX = 24, }; enum lp8788_dvs_sel { DVS_SEL_V0 = 0, DVS_SEL_V1 = 1, DVS_SEL_V2 = 2, DVS_SEL_V3 = 3, }; enum lp8788_charger_event { NO_CHARGER = 0, CHARGER_DETECTED = 1, }; enum lp8788_bl_ctrl_mode { LP8788_BL_REGISTER_ONLY = 0, LP8788_BL_COMB_PWM_BASED = 1, LP8788_BL_COMB_REGISTER_BASED = 2, }; enum lp8788_bl_dim_mode { LP8788_DIM_EXPONENTIAL = 0, LP8788_DIM_LINEAR = 1, }; enum lp8788_bl_full_scale_current { LP8788_FULLSCALE_5000uA = 0, LP8788_FULLSCALE_8500uA = 1, LP8788_FULLSCALE_1200uA = 2, LP8788_FULLSCALE_1550uA = 3, LP8788_FULLSCALE_1900uA = 4, LP8788_FULLSCALE_2250uA = 5, LP8788_FULLSCALE_2600uA = 6, LP8788_FULLSCALE_2950uA = 7, }; enum lp8788_bl_ramp_step { LP8788_RAMP_8us = 0, LP8788_RAMP_1024us = 1, LP8788_RAMP_2048us = 2, LP8788_RAMP_4096us = 3, LP8788_RAMP_8192us = 4, LP8788_RAMP_16384us = 5, LP8788_RAMP_32768us = 6, LP8788_RAMP_65538us = 7, }; enum lp8788_isink_scale { LP8788_ISINK_SCALE_100mA = 0, LP8788_ISINK_SCALE_120mA = 1, }; enum lp8788_isink_number { LP8788_ISINK_1 = 0, LP8788_ISINK_2 = 1, LP8788_ISINK_3 = 2, }; enum lp8788_alarm_sel { LP8788_ALARM_1 = 0, LP8788_ALARM_2 = 1, LP8788_ALARM_MAX = 2, }; struct lp8788_buck1_dvs { int gpio; enum lp8788_dvs_sel vsel; }; struct lp8788_buck2_dvs { int gpio[2]; enum lp8788_dvs_sel vsel; }; struct lp8788_chg_param { u8 addr; u8 val; }; struct lp8788; struct lp8788_charger_platform_data { const char *adc_vbatt; const char *adc_batt_temp; unsigned int max_vbatt_mv; struct lp8788_chg_param *chg_params; int num_chg_params; void (*charger_event)(struct lp8788 *, enum lp8788_charger_event); }; struct lp8788_platform_data; struct lp8788 { struct device *dev; struct regmap *regmap; struct irq_domain *irqdm; int irq; struct lp8788_platform_data *pdata; }; struct lp8788_backlight_platform_data { char *name; int initial_brightness; enum lp8788_bl_ctrl_mode bl_mode; enum lp8788_bl_dim_mode dim_mode; enum lp8788_bl_full_scale_current full_scale; enum lp8788_bl_ramp_step rise_time; enum lp8788_bl_ramp_step fall_time; enum pwm_polarity pwm_pol; unsigned int period_ns; }; struct lp8788_led_platform_data { char *name; enum lp8788_isink_scale scale; enum lp8788_isink_number num; int iout_code; }; struct lp8788_vib_platform_data { char *name; enum lp8788_isink_scale scale; enum lp8788_isink_number num; int iout_code; int pwm_code; }; struct iio_map; struct lp8788_platform_data { int (*init_func)(struct lp8788 *); struct regulator_init_data *buck_data[4]; struct regulator_init_data *dldo_data[12]; struct regulator_init_data *aldo_data[10]; struct lp8788_buck1_dvs *buck1_dvs; struct lp8788_buck2_dvs *buck2_dvs; struct lp8788_charger_platform_data *chg_pdata; enum lp8788_alarm_sel alarm_sel; struct lp8788_backlight_platform_data *bl_pdata; struct lp8788_led_platform_data *led_pdata; struct lp8788_vib_platform_data *vib_pdata; struct iio_map *adc_pdata; }; struct lp8788_irq_data { struct lp8788 *lp; struct mutex irq_lock; struct irq_domain *domain; int enabled[24]; }; struct da9055 { struct regmap *regmap; struct regmap_irq_chip_data *irq_data; struct device *dev; struct i2c_client *i2c_client; int irq_base; int chip_irq; }; enum gpio_select { NO_GPIO = 0, GPIO_1 = 1, GPIO_2 = 2, }; struct da9055_pdata { int (*init)(struct da9055 *); int irq_base; int gpio_base; struct regulator_init_data *regulators[8]; bool reset_enable; int *gpio_ren; int *gpio_rsel; enum gpio_select *reg_ren; enum gpio_select *reg_rsel; struct gpio_desc **ena_gpiods; }; enum max77693_types { TYPE_MAX77693_UNKNOWN = 0, TYPE_MAX77693 = 1, TYPE_MAX77843 = 2, TYPE_MAX77693_NUM = 3, }; struct max77693_dev { struct device *dev; struct i2c_client *i2c; struct i2c_client *i2c_muic; struct i2c_client *i2c_haptic; struct i2c_client *i2c_chg; enum max77693_types type; struct regmap *regmap; struct regmap *regmap_muic; struct regmap *regmap_haptic; struct regmap *regmap_chg; struct regmap_irq_chip_data *irq_data_led; struct regmap_irq_chip_data *irq_data_topsys; struct regmap_irq_chip_data *irq_data_chg; struct regmap_irq_chip_data *irq_data_muic; int irq; }; enum max77843_sys_reg { MAX77843_SYS_REG_PMICID = 0, MAX77843_SYS_REG_PMICREV = 1, MAX77843_SYS_REG_MAINCTRL1 = 2, MAX77843_SYS_REG_INTSRC = 34, MAX77843_SYS_REG_INTSRCMASK = 35, MAX77843_SYS_REG_SYSINTSRC = 36, MAX77843_SYS_REG_SYSINTMASK = 38, MAX77843_SYS_REG_TOPSYS_STAT = 40, MAX77843_SYS_REG_SAFEOUTCTRL = 198, MAX77843_SYS_REG_END = 199, }; enum max77843_charger_reg { MAX77843_CHG_REG_CHG_INT = 176, MAX77843_CHG_REG_CHG_INT_MASK = 177, MAX77843_CHG_REG_CHG_INT_OK = 178, MAX77843_CHG_REG_CHG_DTLS_00 = 179, MAX77843_CHG_REG_CHG_DTLS_01 = 180, MAX77843_CHG_REG_CHG_DTLS_02 = 181, MAX77843_CHG_REG_CHG_CNFG_00 = 183, MAX77843_CHG_REG_CHG_CNFG_01 = 184, MAX77843_CHG_REG_CHG_CNFG_02 = 185, MAX77843_CHG_REG_CHG_CNFG_03 = 186, MAX77843_CHG_REG_CHG_CNFG_04 = 187, MAX77843_CHG_REG_CHG_CNFG_06 = 189, MAX77843_CHG_REG_CHG_CNFG_07 = 190, MAX77843_CHG_REG_CHG_CNFG_09 = 192, MAX77843_CHG_REG_CHG_CNFG_10 = 193, MAX77843_CHG_REG_CHG_CNFG_11 = 194, MAX77843_CHG_REG_CHG_CNFG_12 = 195, MAX77843_CHG_REG_END = 196, }; enum { MAX8925_IRQ_VCHG_DC_OVP = 0, MAX8925_IRQ_VCHG_DC_F = 1, MAX8925_IRQ_VCHG_DC_R = 2, MAX8925_IRQ_VCHG_THM_OK_R = 3, MAX8925_IRQ_VCHG_THM_OK_F = 4, MAX8925_IRQ_VCHG_SYSLOW_F = 5, MAX8925_IRQ_VCHG_SYSLOW_R = 6, MAX8925_IRQ_VCHG_RST = 7, MAX8925_IRQ_VCHG_DONE = 8, MAX8925_IRQ_VCHG_TOPOFF = 9, MAX8925_IRQ_VCHG_TMR_FAULT = 10, MAX8925_IRQ_GPM_RSTIN = 11, MAX8925_IRQ_GPM_MPL = 12, MAX8925_IRQ_GPM_SW_3SEC = 13, MAX8925_IRQ_GPM_EXTON_F = 14, MAX8925_IRQ_GPM_EXTON_R = 15, MAX8925_IRQ_GPM_SW_1SEC = 16, MAX8925_IRQ_GPM_SW_F = 17, MAX8925_IRQ_GPM_SW_R = 18, MAX8925_IRQ_GPM_SYSCKEN_F = 19, MAX8925_IRQ_GPM_SYSCKEN_R = 20, MAX8925_IRQ_RTC_ALARM1 = 21, MAX8925_IRQ_RTC_ALARM0 = 22, MAX8925_IRQ_TSC_STICK = 23, MAX8925_IRQ_TSC_NSTICK = 24, MAX8925_NR_IRQS = 25, }; struct max8925_chip { struct device *dev; struct i2c_client *i2c; struct i2c_client *adc; struct i2c_client *rtc; struct mutex io_lock; struct mutex irq_lock; int irq_base; int core_irq; int tsc_irq; unsigned int wakeup_flag; }; struct max8925_backlight_pdata { int lxw_scl; int lxw_freq; int dual_string; }; struct max8925_touch_pdata { unsigned int flags; }; struct max8925_power_pdata { int (*set_charger)(int); unsigned int batt_detect: 1; unsigned int topoff_threshold: 2; unsigned int fast_charge: 3; unsigned int no_temp_support: 1; unsigned int no_insert_detect: 1; char **supplied_to; int num_supplicants; }; struct max8925_platform_data { struct max8925_backlight_pdata *backlight; struct max8925_touch_pdata *touch; struct max8925_power_pdata *power; struct regulator_init_data *sd1; struct regulator_init_data *sd2; struct regulator_init_data *sd3; struct regulator_init_data *ldo1; struct regulator_init_data *ldo2; struct regulator_init_data *ldo3; struct regulator_init_data *ldo4; struct regulator_init_data *ldo5; struct regulator_init_data *ldo6; struct regulator_init_data *ldo7; struct regulator_init_data *ldo8; struct regulator_init_data *ldo9; struct regulator_init_data *ldo10; struct regulator_init_data *ldo11; struct regulator_init_data *ldo12; struct regulator_init_data *ldo13; struct regulator_init_data *ldo14; struct regulator_init_data *ldo15; struct regulator_init_data *ldo16; struct regulator_init_data *ldo17; struct regulator_init_data *ldo18; struct regulator_init_data *ldo19; struct regulator_init_data *ldo20; int irq_base; int tsc_irq; }; enum { FLAGS_ADC = 1, FLAGS_RTC = 2, }; struct max8925_irq_data { int reg; int mask_reg; int enable; int offs; int flags; int tsc_irq; }; struct max8997_regulator_data { int id; struct regulator_init_data *initdata; struct device_node *reg_node; }; struct max8997_muic_reg_data { u8 addr; u8 data; }; struct max8997_muic_platform_data { struct max8997_muic_reg_data *init_data; int num_init_data; int detcable_delay_ms; int path_usb; int path_uart; }; enum max8997_haptic_motor_type { MAX8997_HAPTIC_ERM = 0, MAX8997_HAPTIC_LRA = 1, }; enum max8997_haptic_pulse_mode { MAX8997_EXTERNAL_MODE = 0, MAX8997_INTERNAL_MODE = 1, }; enum max8997_haptic_pwm_divisor { MAX8997_PWM_DIVISOR_32 = 0, MAX8997_PWM_DIVISOR_64 = 1, MAX8997_PWM_DIVISOR_128 = 2, MAX8997_PWM_DIVISOR_256 = 3, }; struct max8997_haptic_platform_data { unsigned int pwm_channel_id; unsigned int pwm_period; enum max8997_haptic_motor_type type; enum max8997_haptic_pulse_mode mode; enum max8997_haptic_pwm_divisor pwm_divisor; unsigned int internal_mode_pattern; unsigned int pattern_cycle; unsigned int pattern_signal_period; }; enum max8997_led_mode { MAX8997_NONE = 0, MAX8997_FLASH_MODE = 1, MAX8997_MOVIE_MODE = 2, MAX8997_FLASH_PIN_CONTROL_MODE = 3, MAX8997_MOVIE_PIN_CONTROL_MODE = 4, }; struct max8997_led_platform_data { enum max8997_led_mode mode[2]; u8 brightness[2]; }; struct max8997_platform_data { int ono; struct max8997_regulator_data *regulators; int num_regulators; bool ignore_gpiodvs_side_effect; int buck125_gpios[3]; int buck125_default_idx; unsigned int buck1_voltage[8]; bool buck1_gpiodvs; unsigned int buck2_voltage[8]; bool buck2_gpiodvs; unsigned int buck5_voltage[8]; bool buck5_gpiodvs; int eoc_mA; int timeout; struct max8997_muic_platform_data *muic_pdata; struct max8997_haptic_platform_data *haptic_pdata; struct max8997_led_platform_data *led_pdata; }; enum max8997_pmic_reg { MAX8997_REG_PMIC_ID0 = 0, MAX8997_REG_PMIC_ID1 = 1, MAX8997_REG_INTSRC = 2, MAX8997_REG_INT1 = 3, MAX8997_REG_INT2 = 4, MAX8997_REG_INT3 = 5, MAX8997_REG_INT4 = 6, MAX8997_REG_INT1MSK = 8, MAX8997_REG_INT2MSK = 9, MAX8997_REG_INT3MSK = 10, MAX8997_REG_INT4MSK = 11, MAX8997_REG_STATUS1 = 13, MAX8997_REG_STATUS2 = 14, MAX8997_REG_STATUS3 = 15, MAX8997_REG_STATUS4 = 16, MAX8997_REG_MAINCON1 = 19, MAX8997_REG_MAINCON2 = 20, MAX8997_REG_BUCKRAMP = 21, MAX8997_REG_BUCK1CTRL = 24, MAX8997_REG_BUCK1DVS1 = 25, MAX8997_REG_BUCK1DVS2 = 26, MAX8997_REG_BUCK1DVS3 = 27, MAX8997_REG_BUCK1DVS4 = 28, MAX8997_REG_BUCK1DVS5 = 29, MAX8997_REG_BUCK1DVS6 = 30, MAX8997_REG_BUCK1DVS7 = 31, MAX8997_REG_BUCK1DVS8 = 32, MAX8997_REG_BUCK2CTRL = 33, MAX8997_REG_BUCK2DVS1 = 34, MAX8997_REG_BUCK2DVS2 = 35, MAX8997_REG_BUCK2DVS3 = 36, MAX8997_REG_BUCK2DVS4 = 37, MAX8997_REG_BUCK2DVS5 = 38, MAX8997_REG_BUCK2DVS6 = 39, MAX8997_REG_BUCK2DVS7 = 40, MAX8997_REG_BUCK2DVS8 = 41, MAX8997_REG_BUCK3CTRL = 42, MAX8997_REG_BUCK3DVS = 43, MAX8997_REG_BUCK4CTRL = 44, MAX8997_REG_BUCK4DVS = 45, MAX8997_REG_BUCK5CTRL = 46, MAX8997_REG_BUCK5DVS1 = 47, MAX8997_REG_BUCK5DVS2 = 48, MAX8997_REG_BUCK5DVS3 = 49, MAX8997_REG_BUCK5DVS4 = 50, MAX8997_REG_BUCK5DVS5 = 51, MAX8997_REG_BUCK5DVS6 = 52, MAX8997_REG_BUCK5DVS7 = 53, MAX8997_REG_BUCK5DVS8 = 54, MAX8997_REG_BUCK6CTRL = 55, MAX8997_REG_BUCK6BPSKIPCTRL = 56, MAX8997_REG_BUCK7CTRL = 57, MAX8997_REG_BUCK7DVS = 58, MAX8997_REG_LDO1CTRL = 59, MAX8997_REG_LDO2CTRL = 60, MAX8997_REG_LDO3CTRL = 61, MAX8997_REG_LDO4CTRL = 62, MAX8997_REG_LDO5CTRL = 63, MAX8997_REG_LDO6CTRL = 64, MAX8997_REG_LDO7CTRL = 65, MAX8997_REG_LDO8CTRL = 66, MAX8997_REG_LDO9CTRL = 67, MAX8997_REG_LDO10CTRL = 68, MAX8997_REG_LDO11CTRL = 69, MAX8997_REG_LDO12CTRL = 70, MAX8997_REG_LDO13CTRL = 71, MAX8997_REG_LDO14CTRL = 72, MAX8997_REG_LDO15CTRL = 73, MAX8997_REG_LDO16CTRL = 74, MAX8997_REG_LDO17CTRL = 75, MAX8997_REG_LDO18CTRL = 76, MAX8997_REG_LDO21CTRL = 77, MAX8997_REG_MBCCTRL1 = 80, MAX8997_REG_MBCCTRL2 = 81, MAX8997_REG_MBCCTRL3 = 82, MAX8997_REG_MBCCTRL4 = 83, MAX8997_REG_MBCCTRL5 = 84, MAX8997_REG_MBCCTRL6 = 85, MAX8997_REG_OTPCGHCVS = 86, MAX8997_REG_SAFEOUTCTRL = 90, MAX8997_REG_LBCNFG1 = 94, MAX8997_REG_LBCNFG2 = 95, MAX8997_REG_BBCCTRL = 96, MAX8997_REG_FLASH1_CUR = 99, MAX8997_REG_FLASH2_CUR = 100, MAX8997_REG_MOVIE_CUR = 101, MAX8997_REG_GSMB_CUR = 102, MAX8997_REG_BOOST_CNTL = 103, MAX8997_REG_LEN_CNTL = 104, MAX8997_REG_FLASH_CNTL = 105, MAX8997_REG_WDT_CNTL = 106, MAX8997_REG_MAXFLASH1 = 107, MAX8997_REG_MAXFLASH2 = 108, MAX8997_REG_FLASHSTATUS = 109, MAX8997_REG_FLASHSTATUSMASK = 110, MAX8997_REG_GPIOCNTL1 = 112, MAX8997_REG_GPIOCNTL2 = 113, MAX8997_REG_GPIOCNTL3 = 114, MAX8997_REG_GPIOCNTL4 = 115, MAX8997_REG_GPIOCNTL5 = 116, MAX8997_REG_GPIOCNTL6 = 117, MAX8997_REG_GPIOCNTL7 = 118, MAX8997_REG_GPIOCNTL8 = 119, MAX8997_REG_GPIOCNTL9 = 120, MAX8997_REG_GPIOCNTL10 = 121, MAX8997_REG_GPIOCNTL11 = 122, MAX8997_REG_GPIOCNTL12 = 123, MAX8997_REG_LDO1CONFIG = 128, MAX8997_REG_LDO2CONFIG = 129, MAX8997_REG_LDO3CONFIG = 130, MAX8997_REG_LDO4CONFIG = 131, MAX8997_REG_LDO5CONFIG = 132, MAX8997_REG_LDO6CONFIG = 133, MAX8997_REG_LDO7CONFIG = 134, MAX8997_REG_LDO8CONFIG = 135, MAX8997_REG_LDO9CONFIG = 136, MAX8997_REG_LDO10CONFIG = 137, MAX8997_REG_LDO11CONFIG = 138, MAX8997_REG_LDO12CONFIG = 139, MAX8997_REG_LDO13CONFIG = 140, MAX8997_REG_LDO14CONFIG = 141, MAX8997_REG_LDO15CONFIG = 142, MAX8997_REG_LDO16CONFIG = 143, MAX8997_REG_LDO17CONFIG = 144, MAX8997_REG_LDO18CONFIG = 145, MAX8997_REG_LDO21CONFIG = 146, MAX8997_REG_DVSOKTIMER1 = 151, MAX8997_REG_DVSOKTIMER2 = 152, MAX8997_REG_DVSOKTIMER4 = 153, MAX8997_REG_DVSOKTIMER5 = 154, MAX8997_REG_PMIC_END = 155, }; enum max8997_muic_reg { MAX8997_MUIC_REG_ID = 0, MAX8997_MUIC_REG_INT1 = 1, MAX8997_MUIC_REG_INT2 = 2, MAX8997_MUIC_REG_INT3 = 3, MAX8997_MUIC_REG_STATUS1 = 4, MAX8997_MUIC_REG_STATUS2 = 5, MAX8997_MUIC_REG_STATUS3 = 6, MAX8997_MUIC_REG_INTMASK1 = 7, MAX8997_MUIC_REG_INTMASK2 = 8, MAX8997_MUIC_REG_INTMASK3 = 9, MAX8997_MUIC_REG_CDETCTRL = 10, MAX8997_MUIC_REG_CONTROL1 = 12, MAX8997_MUIC_REG_CONTROL2 = 13, MAX8997_MUIC_REG_CONTROL3 = 14, MAX8997_MUIC_REG_END = 15, }; enum max8997_haptic_reg { MAX8997_HAPTIC_REG_GENERAL = 0, MAX8997_HAPTIC_REG_CONF1 = 1, MAX8997_HAPTIC_REG_CONF2 = 2, MAX8997_HAPTIC_REG_DRVCONF = 3, MAX8997_HAPTIC_REG_CYCLECONF1 = 4, MAX8997_HAPTIC_REG_CYCLECONF2 = 5, MAX8997_HAPTIC_REG_SIGCONF1 = 6, MAX8997_HAPTIC_REG_SIGCONF2 = 7, MAX8997_HAPTIC_REG_SIGCONF3 = 8, MAX8997_HAPTIC_REG_SIGCONF4 = 9, MAX8997_HAPTIC_REG_SIGDC1 = 10, MAX8997_HAPTIC_REG_SIGDC2 = 11, MAX8997_HAPTIC_REG_SIGPWMDC1 = 12, MAX8997_HAPTIC_REG_SIGPWMDC2 = 13, MAX8997_HAPTIC_REG_SIGPWMDC3 = 14, MAX8997_HAPTIC_REG_SIGPWMDC4 = 15, MAX8997_HAPTIC_REG_MTR_REV = 16, MAX8997_HAPTIC_REG_END = 17, }; enum max8997_irq_source { PMIC_INT1 = 0, PMIC_INT2 = 1, PMIC_INT3 = 2, PMIC_INT4 = 3, FUEL_GAUGE = 4, MUIC_INT1 = 5, MUIC_INT2 = 6, MUIC_INT3 = 7, GPIO_LOW = 8, GPIO_HI = 9, FLASH_STATUS = 10, MAX8997_IRQ_GROUP_NR = 11, }; struct max8997_dev { struct device *dev; struct max8997_platform_data *pdata; struct i2c_client *i2c; struct i2c_client *rtc; struct i2c_client *haptic; struct i2c_client *muic; struct mutex iolock; long unsigned int type; struct platform_device *battery; int irq; int ono; struct irq_domain *irq_domain; struct mutex irqlock; int irq_masks_cur[11]; int irq_masks_cache[11]; u8 reg_dump[187]; bool gpio_status[12]; }; enum max8997_types { TYPE_MAX8997 = 0, TYPE_MAX8966 = 1, }; enum max8997_irq { MAX8997_PMICIRQ_PWRONR = 0, MAX8997_PMICIRQ_PWRONF = 1, MAX8997_PMICIRQ_PWRON1SEC = 2, MAX8997_PMICIRQ_JIGONR = 3, MAX8997_PMICIRQ_JIGONF = 4, MAX8997_PMICIRQ_LOWBAT2 = 5, MAX8997_PMICIRQ_LOWBAT1 = 6, MAX8997_PMICIRQ_JIGR = 7, MAX8997_PMICIRQ_JIGF = 8, MAX8997_PMICIRQ_MR = 9, MAX8997_PMICIRQ_DVS1OK = 10, MAX8997_PMICIRQ_DVS2OK = 11, MAX8997_PMICIRQ_DVS3OK = 12, MAX8997_PMICIRQ_DVS4OK = 13, MAX8997_PMICIRQ_CHGINS = 14, MAX8997_PMICIRQ_CHGRM = 15, MAX8997_PMICIRQ_DCINOVP = 16, MAX8997_PMICIRQ_TOPOFFR = 17, MAX8997_PMICIRQ_CHGRSTF = 18, MAX8997_PMICIRQ_MBCHGTMEXPD = 19, MAX8997_PMICIRQ_RTC60S = 20, MAX8997_PMICIRQ_RTCA1 = 21, MAX8997_PMICIRQ_RTCA2 = 22, MAX8997_PMICIRQ_SMPL_INT = 23, MAX8997_PMICIRQ_RTC1S = 24, MAX8997_PMICIRQ_WTSR = 25, MAX8997_MUICIRQ_ADCError = 26, MAX8997_MUICIRQ_ADCLow = 27, MAX8997_MUICIRQ_ADC = 28, MAX8997_MUICIRQ_VBVolt = 29, MAX8997_MUICIRQ_DBChg = 30, MAX8997_MUICIRQ_DCDTmr = 31, MAX8997_MUICIRQ_ChgDetRun = 32, MAX8997_MUICIRQ_ChgTyp = 33, MAX8997_MUICIRQ_OVP = 34, MAX8997_IRQ_NR = 35, }; struct max8997_irq_data { int mask; enum max8997_irq_source group; }; struct max8998_regulator_data { int id; struct regulator_init_data *initdata; struct device_node *reg_node; }; struct max8998_platform_data { struct max8998_regulator_data *regulators; int num_regulators; unsigned int irq_base; int ono; bool buck_voltage_lock; int buck1_voltage[4]; int buck2_voltage[2]; int buck1_set1; int buck1_set2; int buck1_default_idx; int buck2_set3; int buck2_default_idx; bool wakeup; bool rtc_delay; int eoc; int restart; int timeout; }; enum { MAX8998_REG_IRQ1 = 0, MAX8998_REG_IRQ2 = 1, MAX8998_REG_IRQ3 = 2, MAX8998_REG_IRQ4 = 3, MAX8998_REG_IRQM1 = 4, MAX8998_REG_IRQM2 = 5, MAX8998_REG_IRQM3 = 6, MAX8998_REG_IRQM4 = 7, MAX8998_REG_STATUS1 = 8, MAX8998_REG_STATUS2 = 9, MAX8998_REG_STATUSM1 = 10, MAX8998_REG_STATUSM2 = 11, MAX8998_REG_CHGR1 = 12, MAX8998_REG_CHGR2 = 13, MAX8998_REG_LDO_ACTIVE_DISCHARGE1 = 14, MAX8998_REG_LDO_ACTIVE_DISCHARGE2 = 15, MAX8998_REG_BUCK_ACTIVE_DISCHARGE3 = 16, MAX8998_REG_ONOFF1 = 17, MAX8998_REG_ONOFF2 = 18, MAX8998_REG_ONOFF3 = 19, MAX8998_REG_ONOFF4 = 20, MAX8998_REG_BUCK1_VOLTAGE1 = 21, MAX8998_REG_BUCK1_VOLTAGE2 = 22, MAX8998_REG_BUCK1_VOLTAGE3 = 23, MAX8998_REG_BUCK1_VOLTAGE4 = 24, MAX8998_REG_BUCK2_VOLTAGE1 = 25, MAX8998_REG_BUCK2_VOLTAGE2 = 26, MAX8998_REG_BUCK3 = 27, MAX8998_REG_BUCK4 = 28, MAX8998_REG_LDO2_LDO3 = 29, MAX8998_REG_LDO4 = 30, MAX8998_REG_LDO5 = 31, MAX8998_REG_LDO6 = 32, MAX8998_REG_LDO7 = 33, MAX8998_REG_LDO8_LDO9 = 34, MAX8998_REG_LDO10_LDO11 = 35, MAX8998_REG_LDO12 = 36, MAX8998_REG_LDO13 = 37, MAX8998_REG_LDO14 = 38, MAX8998_REG_LDO15 = 39, MAX8998_REG_LDO16 = 40, MAX8998_REG_LDO17 = 41, MAX8998_REG_BKCHR = 42, MAX8998_REG_LBCNFG1 = 43, MAX8998_REG_LBCNFG2 = 44, }; enum { TYPE_MAX8998 = 0, TYPE_LP3974 = 1, TYPE_LP3979 = 2, }; struct max8998_dev { struct device *dev; struct max8998_platform_data *pdata; struct i2c_client *i2c; struct i2c_client *rtc; struct mutex iolock; struct mutex irqlock; unsigned int irq_base; struct irq_domain *irq_domain; int irq; int ono; u8 irq_masks_cur[4]; u8 irq_masks_cache[4]; long unsigned int type; bool wakeup; }; struct max8998_reg_dump { u8 addr; u8 val; }; enum { MAX8998_IRQ_DCINF = 0, MAX8998_IRQ_DCINR = 1, MAX8998_IRQ_JIGF = 2, MAX8998_IRQ_JIGR = 3, MAX8998_IRQ_PWRONF = 4, MAX8998_IRQ_PWRONR = 5, MAX8998_IRQ_WTSREVNT = 6, MAX8998_IRQ_SMPLEVNT = 7, MAX8998_IRQ_ALARM1 = 8, MAX8998_IRQ_ALARM0 = 9, MAX8998_IRQ_ONKEY1S = 10, MAX8998_IRQ_TOPOFFR = 11, MAX8998_IRQ_DCINOVPR = 12, MAX8998_IRQ_CHGRSTF = 13, MAX8998_IRQ_DONER = 14, MAX8998_IRQ_CHGFAULT = 15, MAX8998_IRQ_LOBAT1 = 16, MAX8998_IRQ_LOBAT2 = 17, MAX8998_IRQ_NR = 18, }; struct max8998_irq_data { int reg; int mask; }; struct max8997_dev___2; struct adp5520_gpio_platform_data { unsigned int gpio_start; u8 gpio_en_mask; u8 gpio_pullup_mask; }; struct adp5520_keys_platform_data { int rows_en_mask; int cols_en_mask; const short unsigned int *keymap; short unsigned int keymapsize; unsigned int repeat: 1; }; struct led_info; struct adp5520_leds_platform_data { int num_leds; struct led_info *leds; u8 fade_in; u8 fade_out; u8 led_on_time; }; struct adp5520_backlight_platform_data { u8 fade_in; u8 fade_out; u8 fade_led_law; u8 en_ambl_sens; u8 abml_filt; u8 l1_daylight_max; u8 l1_daylight_dim; u8 l2_office_max; u8 l2_office_dim; u8 l3_dark_max; u8 l3_dark_dim; u8 l2_trip; u8 l2_hyst; u8 l3_trip; u8 l3_hyst; }; struct adp5520_platform_data { struct adp5520_keys_platform_data *keys; struct adp5520_gpio_platform_data *gpio; struct adp5520_leds_platform_data *leds; struct adp5520_backlight_platform_data *backlight; }; struct adp5520_chip { struct i2c_client *client; struct device *dev; struct mutex lock; struct blocking_notifier_head notifier_list; int irq; long unsigned int id; uint8_t mode; }; struct tps6586x_irq_data { u8 mask_reg; u8 mask_mask; }; struct tps6586x { struct device *dev; struct i2c_client *client; struct regmap *regmap; int version; int irq; struct irq_chip irq_chip; struct mutex irq_lock; int irq_base; u32 irq_en; u8 mask_reg[5]; struct irq_domain *irq_domain; }; enum { TPS65090_IRQ_INTERRUPT = 0, TPS65090_IRQ_VAC_STATUS_CHANGE = 1, TPS65090_IRQ_VSYS_STATUS_CHANGE = 2, TPS65090_IRQ_BAT_STATUS_CHANGE = 3, TPS65090_IRQ_CHARGING_STATUS_CHANGE = 4, TPS65090_IRQ_CHARGING_COMPLETE = 5, TPS65090_IRQ_OVERLOAD_DCDC1 = 6, TPS65090_IRQ_OVERLOAD_DCDC2 = 7, TPS65090_IRQ_OVERLOAD_DCDC3 = 8, TPS65090_IRQ_OVERLOAD_FET1 = 9, TPS65090_IRQ_OVERLOAD_FET2 = 10, TPS65090_IRQ_OVERLOAD_FET3 = 11, TPS65090_IRQ_OVERLOAD_FET4 = 12, TPS65090_IRQ_OVERLOAD_FET5 = 13, TPS65090_IRQ_OVERLOAD_FET6 = 14, TPS65090_IRQ_OVERLOAD_FET7 = 15, }; enum { TPS65090_REGULATOR_DCDC1 = 0, TPS65090_REGULATOR_DCDC2 = 1, TPS65090_REGULATOR_DCDC3 = 2, TPS65090_REGULATOR_FET1 = 3, TPS65090_REGULATOR_FET2 = 4, TPS65090_REGULATOR_FET3 = 5, TPS65090_REGULATOR_FET4 = 6, TPS65090_REGULATOR_FET5 = 7, TPS65090_REGULATOR_FET6 = 8, TPS65090_REGULATOR_FET7 = 9, TPS65090_REGULATOR_LDO1 = 10, TPS65090_REGULATOR_LDO2 = 11, TPS65090_REGULATOR_MAX = 12, }; struct tps65090 { struct device *dev; struct regmap *rmap; struct regmap_irq_chip_data *irq_data; }; struct tps65090_regulator_plat_data { struct regulator_init_data *reg_init_data; bool enable_ext_control; struct gpio_desc *gpiod; bool overcurrent_wait_valid; int overcurrent_wait; }; struct tps65090_platform_data { int irq_base; char **supplied_to; size_t num_supplicants; int enable_low_current_chrg; struct tps65090_regulator_plat_data *reg_pdata[12]; }; enum tps65090_cells { PMIC = 0, CHARGER = 1, }; enum aat2870_id { AAT2870_ID_BL = 0, AAT2870_ID_LDOA = 1, AAT2870_ID_LDOB = 2, AAT2870_ID_LDOC = 3, AAT2870_ID_LDOD = 4, }; struct aat2870_register { bool readable; bool writeable; u8 value; }; struct aat2870_data { struct device *dev; struct i2c_client *client; struct mutex io_lock; struct aat2870_register *reg_cache; int en_pin; bool is_enable; int (*init)(struct aat2870_data *); void (*uninit)(struct aat2870_data *); int (*read)(struct aat2870_data *, u8, u8 *); int (*write)(struct aat2870_data *, u8, u8); int (*update)(struct aat2870_data *, u8, u8, u8); struct dentry *dentry_root; }; struct aat2870_subdev_info { int id; const char *name; void *platform_data; }; struct aat2870_platform_data { int en_pin; struct aat2870_subdev_info *subdevs; int num_subdevs; int (*init)(struct aat2870_data *); void (*uninit)(struct aat2870_data *); }; enum { PALMAS_EXT_CONTROL_ENABLE1 = 1, PALMAS_EXT_CONTROL_ENABLE2 = 2, PALMAS_EXT_CONTROL_NSLEEP = 4, }; enum palmas_external_requestor_id { PALMAS_EXTERNAL_REQSTR_ID_REGEN1 = 0, PALMAS_EXTERNAL_REQSTR_ID_REGEN2 = 1, PALMAS_EXTERNAL_REQSTR_ID_SYSEN1 = 2, PALMAS_EXTERNAL_REQSTR_ID_SYSEN2 = 3, PALMAS_EXTERNAL_REQSTR_ID_CLK32KG = 4, PALMAS_EXTERNAL_REQSTR_ID_CLK32KGAUDIO = 5, PALMAS_EXTERNAL_REQSTR_ID_REGEN3 = 6, PALMAS_EXTERNAL_REQSTR_ID_SMPS12 = 7, PALMAS_EXTERNAL_REQSTR_ID_SMPS3 = 8, PALMAS_EXTERNAL_REQSTR_ID_SMPS45 = 9, PALMAS_EXTERNAL_REQSTR_ID_SMPS6 = 10, PALMAS_EXTERNAL_REQSTR_ID_SMPS7 = 11, PALMAS_EXTERNAL_REQSTR_ID_SMPS8 = 12, PALMAS_EXTERNAL_REQSTR_ID_SMPS9 = 13, PALMAS_EXTERNAL_REQSTR_ID_SMPS10 = 14, PALMAS_EXTERNAL_REQSTR_ID_LDO1 = 15, PALMAS_EXTERNAL_REQSTR_ID_LDO2 = 16, PALMAS_EXTERNAL_REQSTR_ID_LDO3 = 17, PALMAS_EXTERNAL_REQSTR_ID_LDO4 = 18, PALMAS_EXTERNAL_REQSTR_ID_LDO5 = 19, PALMAS_EXTERNAL_REQSTR_ID_LDO6 = 20, PALMAS_EXTERNAL_REQSTR_ID_LDO7 = 21, PALMAS_EXTERNAL_REQSTR_ID_LDO8 = 22, PALMAS_EXTERNAL_REQSTR_ID_LDO9 = 23, PALMAS_EXTERNAL_REQSTR_ID_LDOLN = 24, PALMAS_EXTERNAL_REQSTR_ID_LDOUSB = 25, PALMAS_EXTERNAL_REQSTR_ID_MAX = 26, }; enum tps65917_irqs { TPS65917_RESERVED1 = 0, TPS65917_PWRON_IRQ = 1, TPS65917_LONG_PRESS_KEY_IRQ = 2, TPS65917_RESERVED2 = 3, TPS65917_PWRDOWN_IRQ = 4, TPS65917_HOTDIE_IRQ = 5, TPS65917_VSYS_MON_IRQ = 6, TPS65917_RESERVED3 = 7, TPS65917_RESERVED4 = 8, TPS65917_OTP_ERROR_IRQ = 9, TPS65917_WDT_IRQ = 10, TPS65917_RESERVED5 = 11, TPS65917_RESET_IN_IRQ = 12, TPS65917_FSD_IRQ = 13, TPS65917_SHORT_IRQ = 14, TPS65917_RESERVED6 = 15, TPS65917_GPADC_AUTO_0_IRQ = 16, TPS65917_GPADC_AUTO_1_IRQ = 17, TPS65917_GPADC_EOC_SW_IRQ = 18, TPS65917_RESREVED6 = 19, TPS65917_RESERVED7 = 20, TPS65917_RESERVED8 = 21, TPS65917_RESERVED9 = 22, TPS65917_VBUS_IRQ = 23, TPS65917_GPIO_0_IRQ = 24, TPS65917_GPIO_1_IRQ = 25, TPS65917_GPIO_2_IRQ = 26, TPS65917_GPIO_3_IRQ = 27, TPS65917_GPIO_4_IRQ = 28, TPS65917_GPIO_5_IRQ = 29, TPS65917_GPIO_6_IRQ = 30, TPS65917_RESERVED10 = 31, TPS65917_NUM_IRQ = 32, }; struct palmas_driver_data { unsigned int *features; struct regmap_irq_chip *irq_chip; }; enum { RC5T583_DS_NONE = 0, RC5T583_DS_DC0 = 1, RC5T583_DS_DC1 = 2, RC5T583_DS_DC2 = 3, RC5T583_DS_DC3 = 4, RC5T583_DS_LDO0 = 5, RC5T583_DS_LDO1 = 6, RC5T583_DS_LDO2 = 7, RC5T583_DS_LDO3 = 8, RC5T583_DS_LDO4 = 9, RC5T583_DS_LDO5 = 10, RC5T583_DS_LDO6 = 11, RC5T583_DS_LDO7 = 12, RC5T583_DS_LDO8 = 13, RC5T583_DS_LDO9 = 14, RC5T583_DS_PSO0 = 15, RC5T583_DS_PSO1 = 16, RC5T583_DS_PSO2 = 17, RC5T583_DS_PSO3 = 18, RC5T583_DS_PSO4 = 19, RC5T583_DS_PSO5 = 20, RC5T583_DS_PSO6 = 21, RC5T583_DS_PSO7 = 22, RC5T583_DS_MAX = 23, }; enum { RC5T583_EXT_PWRREQ1_CONTROL = 1, RC5T583_EXT_PWRREQ2_CONTROL = 2, }; struct deepsleep_control_data { u8 reg_add; u8 ds_pos_bit; }; enum int_type { SYS_INT = 1, DCDC_INT = 2, RTC_INT = 4, ADC_INT = 8, GPIO_INT = 16, }; struct rc5t583_irq_data { u8 int_type; u8 master_bit; u8 int_en_bit; u8 mask_reg_index; int grp_index; }; struct syscon_platform_data { const char *label; }; struct syscon { struct device_node *np; struct regmap *regmap; struct list_head list; }; enum { AS3711_REGULATOR_SD_1 = 0, AS3711_REGULATOR_SD_2 = 1, AS3711_REGULATOR_SD_3 = 2, AS3711_REGULATOR_SD_4 = 3, AS3711_REGULATOR_LDO_1 = 4, AS3711_REGULATOR_LDO_2 = 5, AS3711_REGULATOR_LDO_3 = 6, AS3711_REGULATOR_LDO_4 = 7, AS3711_REGULATOR_LDO_5 = 8, AS3711_REGULATOR_LDO_6 = 9, AS3711_REGULATOR_LDO_7 = 10, AS3711_REGULATOR_LDO_8 = 11, AS3711_REGULATOR_MAX = 12, }; struct as3711 { struct device *dev; struct regmap *regmap; }; enum as3711_su2_feedback { AS3711_SU2_VOLTAGE = 0, AS3711_SU2_CURR1 = 1, AS3711_SU2_CURR2 = 2, AS3711_SU2_CURR3 = 3, AS3711_SU2_CURR_AUTO = 4, }; enum as3711_su2_fbprot { AS3711_SU2_LX_SD4 = 0, AS3711_SU2_GPIO2 = 1, AS3711_SU2_GPIO3 = 2, AS3711_SU2_GPIO4 = 3, }; struct as3711_regulator_pdata { struct regulator_init_data *init_data[12]; }; struct as3711_bl_pdata { bool su1_fb; int su1_max_uA; bool su2_fb; int su2_max_uA; enum as3711_su2_feedback su2_feedback; enum as3711_su2_fbprot su2_fbprot; bool su2_auto_curr1; bool su2_auto_curr2; bool su2_auto_curr3; }; struct as3711_platform_data { struct as3711_regulator_pdata regulator; struct as3711_bl_pdata backlight; }; enum { AS3711_REGULATOR = 0, AS3711_BACKLIGHT = 1, }; struct intel_soc_pmic_config { long unsigned int irq_flags; struct mfd_cell *cell_dev; int n_cell_devs; const struct regmap_config *regmap_config; const struct regmap_irq_chip *irq_chip; }; enum { CHT_WC_PWRSRC_IRQ = 0, CHT_WC_THRM_IRQ = 1, CHT_WC_BCU_IRQ = 2, CHT_WC_ADC_IRQ = 3, CHT_WC_EXT_CHGR_IRQ = 4, CHT_WC_GPIO_IRQ = 5, CHT_WC_CRIT_IRQ = 7, }; struct badrange { struct list_head list; spinlock_t lock; }; enum { NDD_ALIASING = 0, NDD_UNARMED = 1, NDD_LOCKED = 2, NDD_SECURITY_OVERWRITE = 3, NDD_WORK_PENDING = 4, NDD_NOBLK = 5, NDD_LABELING = 6, ND_IOCTL_MAX_BUFLEN = 4194304, ND_CMD_MAX_ELEM = 5, ND_CMD_MAX_ENVELOPE = 256, ND_MAX_MAPPINGS = 32, ND_REGION_PAGEMAP = 0, ND_REGION_PERSIST_CACHE = 1, ND_REGION_PERSIST_MEMCTRL = 2, ND_REGION_ASYNC = 3, DPA_RESOURCE_ADJUSTED = 1, }; struct nvdimm_bus_descriptor; struct nvdimm; typedef int (*ndctl_fn)(struct nvdimm_bus_descriptor *, struct nvdimm *, unsigned int, void *, unsigned int, int *); struct nvdimm_bus_fw_ops; struct nvdimm_bus_descriptor { const struct attribute_group **attr_groups; long unsigned int cmd_mask; long unsigned int dimm_family_mask; long unsigned int bus_family_mask; struct module *module; char *provider_name; struct device_node *of_node; ndctl_fn ndctl; int (*flush_probe)(struct nvdimm_bus_descriptor *); int (*clear_to_send)(struct nvdimm_bus_descriptor *, struct nvdimm *, unsigned int, void *); const struct nvdimm_bus_fw_ops *fw_ops; }; struct nvdimm_security_ops; struct nvdimm_fw_ops; struct nvdimm { long unsigned int flags; void *provider_data; long unsigned int cmd_mask; struct device dev; atomic_t busy; int id; int num_flush; struct resource *flush_wpq; const char *dimm_id; struct { const struct nvdimm_security_ops *ops; long unsigned int flags; long unsigned int ext_flags; unsigned int overwrite_tmo; struct kernfs_node *overwrite_state; } sec; struct delayed_work dwork; const struct nvdimm_fw_ops *fw_ops; }; enum nvdimm_fwa_state { NVDIMM_FWA_INVALID = 0, NVDIMM_FWA_IDLE = 1, NVDIMM_FWA_ARMED = 2, NVDIMM_FWA_BUSY = 3, NVDIMM_FWA_ARM_OVERFLOW = 4, }; enum nvdimm_fwa_capability { NVDIMM_FWA_CAP_INVALID = 0, NVDIMM_FWA_CAP_NONE = 1, NVDIMM_FWA_CAP_QUIESCE = 2, NVDIMM_FWA_CAP_LIVE = 3, }; struct nvdimm_bus_fw_ops { enum nvdimm_fwa_state (*activate_state)(struct nvdimm_bus_descriptor *); enum nvdimm_fwa_capability (*capability)(struct nvdimm_bus_descriptor *); int (*activate)(struct nvdimm_bus_descriptor *); }; struct nvdimm_bus { struct nvdimm_bus_descriptor *nd_desc; wait_queue_head_t wait; struct list_head list; struct device dev; int id; int probe_active; atomic_t ioctl_active; struct list_head mapping_list; struct mutex reconfig_mutex; struct badrange badrange; }; struct nvdimm_key_data { u8 data[32]; }; enum nvdimm_passphrase_type { NVDIMM_USER = 0, NVDIMM_MASTER = 1, }; struct nvdimm_security_ops { long unsigned int (*get_flags)(struct nvdimm *, enum nvdimm_passphrase_type); int (*freeze)(struct nvdimm *); int (*change_key)(struct nvdimm *, const struct nvdimm_key_data *, const struct nvdimm_key_data *, enum nvdimm_passphrase_type); int (*unlock)(struct nvdimm *, const struct nvdimm_key_data *); int (*disable)(struct nvdimm *, const struct nvdimm_key_data *); int (*erase)(struct nvdimm *, const struct nvdimm_key_data *, enum nvdimm_passphrase_type); int (*overwrite)(struct nvdimm *, const struct nvdimm_key_data *); int (*query_overwrite)(struct nvdimm *); }; enum nvdimm_fwa_trigger { NVDIMM_FWA_ARM = 0, NVDIMM_FWA_DISARM = 1, }; enum nvdimm_fwa_result { NVDIMM_FWA_RESULT_INVALID = 0, NVDIMM_FWA_RESULT_NONE = 1, NVDIMM_FWA_RESULT_SUCCESS = 2, NVDIMM_FWA_RESULT_NOTSTAGED = 3, NVDIMM_FWA_RESULT_NEEDRESET = 4, NVDIMM_FWA_RESULT_FAIL = 5, }; struct nvdimm_fw_ops { enum nvdimm_fwa_state (*activate_state)(struct nvdimm *); enum nvdimm_fwa_result (*activate_result)(struct nvdimm *); int (*arm)(struct nvdimm *, enum nvdimm_fwa_trigger); }; enum { ND_CMD_IMPLEMENTED = 0, ND_CMD_ARS_CAP = 1, ND_CMD_ARS_START = 2, ND_CMD_ARS_STATUS = 3, ND_CMD_CLEAR_ERROR = 4, ND_CMD_SMART = 1, ND_CMD_SMART_THRESHOLD = 2, ND_CMD_DIMM_FLAGS = 3, ND_CMD_GET_CONFIG_SIZE = 4, ND_CMD_GET_CONFIG_DATA = 5, ND_CMD_SET_CONFIG_DATA = 6, ND_CMD_VENDOR_EFFECT_LOG_SIZE = 7, ND_CMD_VENDOR_EFFECT_LOG = 8, ND_CMD_VENDOR = 9, ND_CMD_CALL = 10, }; enum { NSINDEX_SIG_LEN = 16, NSINDEX_ALIGN = 256, NSINDEX_SEQ_MASK = 3, NSLABEL_UUID_LEN = 16, NSLABEL_NAME_LEN = 64, NSLABEL_FLAG_ROLABEL = 1, NSLABEL_FLAG_LOCAL = 2, NSLABEL_FLAG_BTT = 4, NSLABEL_FLAG_UPDATING = 8, BTT_ALIGN = 4096, BTTINFO_SIG_LEN = 16, BTTINFO_UUID_LEN = 16, BTTINFO_FLAG_ERROR = 1, BTTINFO_MAJOR_VERSION = 1, ND_LABEL_MIN_SIZE = 1024, ND_LABEL_ID_SIZE = 50, ND_NSINDEX_INIT = 1, }; struct nvdimm_map { struct nvdimm_bus *nvdimm_bus; struct list_head list; resource_size_t offset; long unsigned int flags; size_t size; union { void *mem; void *iomem; }; struct kref kref; }; struct badrange_entry { u64 start; u64 length; struct list_head list; }; struct nd_cmd_desc { int in_num; int out_num; u32 in_sizes[5]; int out_sizes[5]; }; struct nd_interleave_set { u64 cookie1; u64 cookie2; u64 altcookie; guid_t type_guid; }; struct nvdimm_drvdata; struct nd_mapping { struct nvdimm *nvdimm; u64 start; u64 size; int position; struct list_head labels; struct mutex lock; struct nvdimm_drvdata *ndd; }; struct nd_percpu_lane; struct nd_region { struct device dev; struct ida ns_ida; struct ida btt_ida; struct ida pfn_ida; struct ida dax_ida; long unsigned int flags; struct device *ns_seed; struct device *btt_seed; struct device *pfn_seed; struct device *dax_seed; long unsigned int align; u16 ndr_mappings; u64 ndr_size; u64 ndr_start; int id; int num_lanes; int ro; int numa_node; int target_node; void *provider_data; struct kernfs_node *bb_state; struct badblocks bb; struct nd_interleave_set *nd_set; struct nd_percpu_lane *lane; int (*flush)(struct nd_region *, struct bio *); struct nd_mapping mapping[0]; }; struct nd_cmd_get_config_size { __u32 status; __u32 config_size; __u32 max_xfer; }; struct nd_cmd_set_config_hdr { __u32 in_offset; __u32 in_length; __u8 in_buf[0]; }; struct nd_cmd_vendor_hdr { __u32 opcode; __u32 in_length; __u8 in_buf[0]; }; struct nd_cmd_ars_cap { __u64 address; __u64 length; __u32 status; __u32 max_ars_out; __u32 clear_err_unit; __u16 flags; __u16 reserved; }; struct nd_cmd_clear_error { __u64 address; __u64 length; __u32 status; __u8 reserved[4]; __u64 cleared; }; struct nd_cmd_pkg { __u64 nd_family; __u64 nd_command; __u32 nd_size_in; __u32 nd_size_out; __u32 nd_reserved2[9]; __u32 nd_fw_size; unsigned char nd_payload[0]; }; enum nvdimm_event { NVDIMM_REVALIDATE_POISON = 0, NVDIMM_REVALIDATE_REGION = 1, }; enum nvdimm_claim_class { NVDIMM_CCLASS_NONE = 0, NVDIMM_CCLASS_BTT = 1, NVDIMM_CCLASS_BTT2 = 2, NVDIMM_CCLASS_PFN = 3, NVDIMM_CCLASS_DAX = 4, NVDIMM_CCLASS_UNKNOWN = 5, }; struct nd_device_driver { struct device_driver drv; long unsigned int type; int (*probe)(struct device *); void (*remove)(struct device *); void (*shutdown)(struct device *); void (*notify)(struct device *, enum nvdimm_event); }; struct nd_namespace_common { int force_raw; struct device dev; struct device *claim; enum nvdimm_claim_class claim_class; int (*rw_bytes)(struct nd_namespace_common *, resource_size_t, void *, size_t, int, long unsigned int); }; struct nd_namespace_io { struct nd_namespace_common common; struct resource res; resource_size_t size; void *addr; struct badblocks bb; }; struct nvdimm_drvdata { struct device *dev; int nslabel_size; struct nd_cmd_get_config_size nsarea; void *data; int ns_current; int ns_next; struct resource dpa; struct kref kref; }; struct nd_percpu_lane { int count; spinlock_t lock; }; struct btt; struct nd_btt { struct device dev; struct nd_namespace_common *ndns; struct btt *btt; long unsigned int lbasize; u64 size; u8 *uuid; int id; int initial_offset; u16 version_major; u16 version_minor; }; enum nd_pfn_mode { PFN_MODE_NONE = 0, PFN_MODE_RAM = 1, PFN_MODE_PMEM = 2, }; struct nd_pfn_sb; struct nd_pfn { int id; u8 *uuid; struct device dev; long unsigned int align; long unsigned int npfns; enum nd_pfn_mode mode; struct nd_pfn_sb *pfn_sb; struct nd_namespace_common *ndns; }; struct nd_pfn_sb { u8 signature[16]; u8 uuid[16]; u8 parent_uuid[16]; __le32 flags; __le16 version_major; __le16 version_minor; __le64 dataoff; __le64 npfns; __le32 mode; __le32 start_pad; __le32 end_trunc; __le32 align; __le32 page_size; __le16 page_struct_size; u8 padding[3994]; __le64 checksum; }; struct nd_dax { struct nd_pfn nd_pfn; }; enum nd_async_mode { ND_SYNC = 0, ND_ASYNC = 1, }; struct clear_badblocks_context { resource_size_t phys; resource_size_t cleared; }; enum nd_ioctl_mode { BUS_IOCTL = 0, DIMM_IOCTL = 1, }; struct nd_cmd_get_config_data_hdr { __u32 in_offset; __u32 in_length; __u32 status; __u8 out_buf[0]; }; struct nd_blk_region { int (*enable)(struct nvdimm_bus *, struct device *); int (*do_io)(struct nd_blk_region *, resource_size_t, void *, u64, int); void *blk_provider_data; struct nd_region nd_region; }; enum nvdimm_security_bits { NVDIMM_SECURITY_DISABLED = 0, NVDIMM_SECURITY_UNLOCKED = 1, NVDIMM_SECURITY_LOCKED = 2, NVDIMM_SECURITY_FROZEN = 3, NVDIMM_SECURITY_OVERWRITE = 4, }; struct nd_label_id { char id[50]; }; struct blk_alloc_info { struct nd_mapping *nd_mapping; resource_size_t available; resource_size_t busy; struct resource *res; }; enum nd_driver_flags { ND_DRIVER_DIMM = 2, ND_DRIVER_REGION_PMEM = 4, ND_DRIVER_REGION_BLK = 8, ND_DRIVER_NAMESPACE_IO = 16, ND_DRIVER_NAMESPACE_PMEM = 32, ND_DRIVER_NAMESPACE_BLK = 64, ND_DRIVER_DAX_PMEM = 128, }; struct nd_mapping_desc { struct nvdimm *nvdimm; u64 start; u64 size; int position; }; struct nd_region_desc { struct resource *res; struct nd_mapping_desc *mapping; u16 num_mappings; const struct attribute_group **attr_groups; struct nd_interleave_set *nd_set; void *provider_data; int num_lanes; int numa_node; int target_node; long unsigned int flags; struct device_node *of_node; int (*flush)(struct nd_region *, struct bio *); }; struct nd_blk_region_desc { int (*enable)(struct nvdimm_bus *, struct device *); int (*do_io)(struct nd_blk_region *, resource_size_t, void *, u64, int); struct nd_region_desc ndr_desc; }; struct nd_namespace_index { u8 sig[16]; u8 flags[3]; u8 labelsize; __le32 seq; __le64 myoff; __le64 mysize; __le64 otheroff; __le64 labeloff; __le32 nslot; __le16 major; __le16 minor; __le64 checksum; u8 free[0]; }; struct nd_namespace_label { u8 uuid[16]; u8 name[64]; __le32 flags; __le16 nlabel; __le16 position; __le64 isetcookie; __le64 lbasize; __le64 dpa; __le64 rawsize; __le32 slot; u8 align; u8 reserved[3]; guid_t type_guid; guid_t abstraction_guid; u8 reserved2[88]; __le64 checksum; }; enum { ND_MAX_LANES = 256, INT_LBASIZE_ALIGNMENT = 64, NVDIMM_IO_ATOMIC = 1, }; struct nd_region_data { int ns_count; int ns_active; unsigned int hints_shift; void *flush_wpq[0]; }; struct nd_label_ent { struct list_head list; long unsigned int flags; struct nd_namespace_label *label; }; struct conflict_context { struct nd_region *nd_region; resource_size_t start; resource_size_t size; }; enum { ND_MIN_NAMESPACE_SIZE = 4096, }; struct nd_namespace_pmem { struct nd_namespace_io nsio; long unsigned int lbasize; char *alt_name; u8 *uuid; int id; }; struct nd_namespace_blk { struct nd_namespace_common common; char *alt_name; u8 *uuid; int id; long unsigned int lbasize; resource_size_t size; int num_resources; struct resource **res; }; enum nd_label_flags { ND_LABEL_REAP = 0, }; enum alloc_loc { ALLOC_ERR = 0, ALLOC_BEFORE = 1, ALLOC_MID = 2, ALLOC_AFTER = 3, }; struct btt { struct gendisk *btt_disk; struct list_head arena_list; struct dentry *debugfs_dir; struct nd_btt *nd_btt; u64 nlba; long long unsigned int rawsize; u32 lbasize; u32 sector_size; struct nd_region *nd_region; struct mutex init_lock; int init_state; int num_arenas; struct badblocks *phys_bb; }; struct nd_gen_sb { char reserved[4088]; __le64 checksum; }; struct btt_sb { u8 signature[16]; u8 uuid[16]; u8 parent_uuid[16]; __le32 flags; __le16 version_major; __le16 version_minor; __le32 external_lbasize; __le32 external_nlba; __le32 internal_lbasize; __le32 internal_nlba; __le32 nfree; __le32 infosize; __le64 nextoff; __le64 dataoff; __le64 mapoff; __le64 logoff; __le64 info2off; u8 padding[3968]; __le64 checksum; }; struct dax_operations { long int (*direct_access)(struct dax_device *, long unsigned int, long int, void **, pfn_t *); bool (*dax_supported)(struct dax_device *, struct block_device *, int, sector_t, sector_t); size_t (*copy_from_iter)(struct dax_device *, long unsigned int, void *, size_t, struct iov_iter *); size_t (*copy_to_iter)(struct dax_device *, long unsigned int, void *, size_t, struct iov_iter *); int (*zero_page_range)(struct dax_device *, long unsigned int, size_t); }; struct dax_device { struct hlist_node list; struct inode inode; struct cdev cdev; const char *host; void *private; long unsigned int flags; const struct dax_operations *ops; }; enum dax_device_flags { DAXDEV_ALIVE = 0, DAXDEV_WRITE_CACHE = 1, DAXDEV_SYNC = 2, }; struct dax_region { int id; int target_node; struct kref kref; struct device *dev; unsigned int align; struct ida ida; struct resource res; struct device *seed; struct device *youngest; }; struct dax_mapping { struct device dev; int range_id; int id; }; struct dev_dax_range { long unsigned int pgoff; struct range range; struct dax_mapping *mapping; }; struct dev_dax { struct dax_region *region; struct dax_device *dax_dev; unsigned int align; int target_node; int id; struct ida ida; struct device dev; struct dev_pagemap *pgmap; int nr_range; struct dev_dax_range *ranges; }; enum dev_dax_subsys { DEV_DAX_BUS = 0, DEV_DAX_CLASS = 1, }; struct dev_dax_data { struct dax_region *dax_region; struct dev_pagemap *pgmap; enum dev_dax_subsys subsys; resource_size_t size; int id; }; struct dax_device_driver { struct device_driver drv; struct list_head ids; int match_always; int (*probe)(struct dev_dax *); void (*remove)(struct dev_dax *); }; struct dax_id { struct list_head list; char dev_name[30]; }; enum id_action { ID_REMOVE = 0, ID_ADD = 1, }; struct memregion_info { int target_node; }; struct seqcount_ww_mutex { seqcount_t seqcount; }; typedef struct seqcount_ww_mutex seqcount_ww_mutex_t; struct dma_buf_map { union { void *vaddr_iomem; void *vaddr; }; bool is_iomem; }; struct dma_fence_ops; struct dma_fence { spinlock_t *lock; const struct dma_fence_ops *ops; union { struct list_head cb_list; ktime_t timestamp; struct callback_head rcu; }; u64 context; u64 seqno; long unsigned int flags; struct kref refcount; int error; }; struct dma_fence_ops { bool use_64bit_seqno; const char * (*get_driver_name)(struct dma_fence *); const char * (*get_timeline_name)(struct dma_fence *); bool (*enable_signaling)(struct dma_fence *); bool (*signaled)(struct dma_fence *); long int (*wait)(struct dma_fence *, bool, long int); void (*release)(struct dma_fence *); void (*fence_value_str)(struct dma_fence *, char *, int); void (*timeline_value_str)(struct dma_fence *, char *, int); }; enum dma_fence_flag_bits { DMA_FENCE_FLAG_SIGNALED_BIT = 0, DMA_FENCE_FLAG_TIMESTAMP_BIT = 1, DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT = 2, DMA_FENCE_FLAG_USER_BITS = 3, }; struct dma_fence_cb; typedef void (*dma_fence_func_t)(struct dma_fence *, struct dma_fence_cb *); struct dma_fence_cb { struct list_head node; dma_fence_func_t func; }; struct dma_buf; struct dma_buf_attachment; struct dma_buf_ops { bool cache_sgt_mapping; int (*attach)(struct dma_buf *, struct dma_buf_attachment *); void (*detach)(struct dma_buf *, struct dma_buf_attachment *); int (*pin)(struct dma_buf_attachment *); void (*unpin)(struct dma_buf_attachment *); struct sg_table * (*map_dma_buf)(struct dma_buf_attachment *, enum dma_data_direction); void (*unmap_dma_buf)(struct dma_buf_attachment *, struct sg_table *, enum dma_data_direction); void (*release)(struct dma_buf *); int (*begin_cpu_access)(struct dma_buf *, enum dma_data_direction); int (*end_cpu_access)(struct dma_buf *, enum dma_data_direction); int (*mmap)(struct dma_buf *, struct vm_area_struct *); int (*vmap)(struct dma_buf *, struct dma_buf_map *); void (*vunmap)(struct dma_buf *, struct dma_buf_map *); }; struct dma_buf_poll_cb_t { struct dma_fence_cb cb; wait_queue_head_t *poll; __poll_t active; }; struct dma_resv; struct dma_buf { size_t size; struct file *file; struct list_head attachments; const struct dma_buf_ops *ops; struct mutex lock; unsigned int vmapping_counter; struct dma_buf_map vmap_ptr; const char *exp_name; const char *name; spinlock_t name_lock; struct module *owner; struct list_head list_node; void *priv; struct dma_resv *resv; wait_queue_head_t poll; struct dma_buf_poll_cb_t cb_excl; struct dma_buf_poll_cb_t cb_shared; }; struct dma_buf_attach_ops; struct dma_buf_attachment { struct dma_buf *dmabuf; struct device *dev; struct list_head node; struct sg_table *sgt; enum dma_data_direction dir; bool peer2peer; const struct dma_buf_attach_ops *importer_ops; void *importer_priv; void *priv; }; struct dma_resv_list; struct dma_resv { struct ww_mutex lock; seqcount_ww_mutex_t seq; struct dma_fence *fence_excl; struct dma_resv_list *fence; }; struct dma_buf_attach_ops { bool allow_peer2peer; void (*move_notify)(struct dma_buf_attachment *); }; struct dma_buf_export_info { const char *exp_name; struct module *owner; const struct dma_buf_ops *ops; size_t size; int flags; struct dma_resv *resv; void *priv; }; struct dma_resv_list { struct callback_head rcu; u32 shared_count; u32 shared_max; struct dma_fence *shared[0]; }; struct dma_buf_sync { __u64 flags; }; struct dma_buf_list { struct list_head head; struct mutex lock; }; struct trace_event_raw_dma_fence { struct trace_entry ent; u32 __data_loc_driver; u32 __data_loc_timeline; unsigned int context; unsigned int seqno; char __data[0]; }; struct trace_event_data_offsets_dma_fence { u32 driver; u32 timeline; }; typedef void (*btf_trace_dma_fence_emit)(void *, struct dma_fence *); typedef void (*btf_trace_dma_fence_init)(void *, struct dma_fence *); typedef void (*btf_trace_dma_fence_destroy)(void *, struct dma_fence *); typedef void (*btf_trace_dma_fence_enable_signal)(void *, struct dma_fence *); typedef void (*btf_trace_dma_fence_signaled)(void *, struct dma_fence *); typedef void (*btf_trace_dma_fence_wait_start)(void *, struct dma_fence *); typedef void (*btf_trace_dma_fence_wait_end)(void *, struct dma_fence *); struct default_wait_cb { struct dma_fence_cb base; struct task_struct *task; }; struct dma_fence_array; struct dma_fence_array_cb { struct dma_fence_cb cb; struct dma_fence_array *array; }; struct dma_fence_array { struct dma_fence base; spinlock_t lock; unsigned int num_fences; atomic_t num_pending; struct dma_fence **fences; struct irq_work work; }; struct dma_fence_chain { struct dma_fence base; spinlock_t lock; struct dma_fence *prev; u64 prev_seqno; struct dma_fence *fence; struct dma_fence_cb cb; struct irq_work work; }; enum seqno_fence_condition { SEQNO_FENCE_WAIT_GEQUAL = 0, SEQNO_FENCE_WAIT_NONZERO = 1, }; struct seqno_fence { struct dma_fence base; const struct dma_fence_ops *ops; struct dma_buf *sync_buf; uint32_t seqno_ofs; enum seqno_fence_condition condition; }; struct dma_heap; struct dma_heap_ops { struct dma_buf * (*allocate)(struct dma_heap *, long unsigned int, long unsigned int, long unsigned int); }; struct dma_heap { const char *name; const struct dma_heap_ops *ops; void *priv; dev_t heap_devt; struct list_head list; struct cdev heap_cdev; }; struct dma_heap_export_info { const char *name; const struct dma_heap_ops *ops; void *priv; }; struct dma_heap_allocation_data { __u64 len; __u32 fd; __u32 fd_flags; __u64 heap_flags; }; struct system_heap_buffer { struct dma_heap *heap; struct list_head attachments; struct mutex lock; long unsigned int len; struct sg_table sg_table; int vmap_cnt; void *vaddr; }; struct dma_heap_attachment { struct device *dev; struct sg_table *table; struct list_head list; bool mapped; }; struct cma_heap { struct dma_heap *heap; struct cma *cma; }; struct cma_heap_buffer { struct cma_heap *heap; struct list_head attachments; struct mutex lock; long unsigned int len; struct page *cma_pages; struct page **pages; long unsigned int pagecount; int vmap_cnt; void *vaddr; }; struct dma_heap_attachment___2 { struct device *dev; struct sg_table table; struct list_head list; bool mapped; }; struct sync_file { struct file *file; char user_name[32]; struct list_head sync_file_list; wait_queue_head_t wq; long unsigned int flags; struct dma_fence *fence; struct dma_fence_cb cb; }; struct sync_merge_data { char name[32]; __s32 fd2; __s32 fence; __u32 flags; __u32 pad; }; struct sync_fence_info { char obj_name[32]; char driver_name[32]; __s32 status; __u32 flags; __u64 timestamp_ns; }; struct sync_file_info { char name[32]; __s32 status; __u32 flags; __u32 num_fences; __u32 pad; __u64 sync_fence_info; }; struct udmabuf_create { __u32 memfd; __u32 flags; __u64 offset; __u64 size; }; struct udmabuf_create_item { __u32 memfd; __u32 __pad; __u64 offset; __u64 size; }; struct udmabuf_create_list { __u32 flags; __u32 count; struct udmabuf_create_item list[0]; }; struct udmabuf { long unsigned int pagecount; struct page **pages; struct sg_table *sg; struct miscdevice *device; }; enum scsi_host_status { DID_OK = 0, DID_NO_CONNECT = 1, DID_BUS_BUSY = 2, DID_TIME_OUT = 3, DID_BAD_TARGET = 4, DID_ABORT = 5, DID_PARITY = 6, DID_ERROR = 7, DID_RESET = 8, DID_BAD_INTR = 9, DID_PASSTHROUGH = 10, DID_SOFT_ERROR = 11, DID_IMM_RETRY = 12, DID_REQUEUE = 13, DID_TRANSPORT_DISRUPTED = 14, DID_TRANSPORT_FAILFAST = 15, DID_TARGET_FAILURE = 16, DID_NEXUS_FAILURE = 17, DID_ALLOC_FAILURE = 18, DID_MEDIUM_ERROR = 19, DID_TRANSPORT_MARGINAL = 20, }; enum scsi_disposition { NEEDS_RETRY = 8193, SUCCESS = 8194, FAILED = 8195, QUEUED = 8196, SOFT_ERROR = 8197, ADD_TO_MLQUEUE = 8198, TIMEOUT_ERROR = 8199, SCSI_RETURN_NOT_HANDLED = 8200, FAST_IO_FAIL = 8201, }; typedef __u64 blist_flags_t; enum scsi_device_state { SDEV_CREATED = 1, SDEV_RUNNING = 2, SDEV_CANCEL = 3, SDEV_DEL = 4, SDEV_QUIESCE = 5, SDEV_OFFLINE = 6, SDEV_TRANSPORT_OFFLINE = 7, SDEV_BLOCK = 8, SDEV_CREATED_BLOCK = 9, }; struct scsi_vpd { struct callback_head rcu; int len; unsigned char data[0]; }; struct Scsi_Host; struct scsi_target; struct scsi_device_handler; struct scsi_device { struct Scsi_Host *host; struct request_queue *request_queue; struct list_head siblings; struct list_head same_target_siblings; struct sbitmap budget_map; atomic_t device_blocked; atomic_t restarts; spinlock_t list_lock; struct list_head starved_entry; short unsigned int queue_depth; short unsigned int max_queue_depth; short unsigned int last_queue_full_depth; short unsigned int last_queue_full_count; long unsigned int last_queue_full_time; long unsigned int queue_ramp_up_period; long unsigned int last_queue_ramp_up; unsigned int id; unsigned int channel; u64 lun; unsigned int manufacturer; unsigned int sector_size; void *hostdata; unsigned char type; char scsi_level; char inq_periph_qual; struct mutex inquiry_mutex; unsigned char inquiry_len; unsigned char *inquiry; const char *vendor; const char *model; const char *rev; struct scsi_vpd *vpd_pg0; struct scsi_vpd *vpd_pg83; struct scsi_vpd *vpd_pg80; struct scsi_vpd *vpd_pg89; unsigned char current_tag; struct scsi_target *sdev_target; blist_flags_t sdev_bflags; unsigned int eh_timeout; unsigned int removable: 1; unsigned int changed: 1; unsigned int busy: 1; unsigned int lockable: 1; unsigned int locked: 1; unsigned int borken: 1; unsigned int disconnect: 1; unsigned int soft_reset: 1; unsigned int sdtr: 1; unsigned int wdtr: 1; unsigned int ppr: 1; unsigned int tagged_supported: 1; unsigned int simple_tags: 1; unsigned int was_reset: 1; unsigned int expecting_cc_ua: 1; unsigned int use_10_for_rw: 1; unsigned int use_10_for_ms: 1; unsigned int set_dbd_for_ms: 1; unsigned int no_report_opcodes: 1; unsigned int no_write_same: 1; unsigned int use_16_for_rw: 1; unsigned int skip_ms_page_8: 1; unsigned int skip_ms_page_3f: 1; unsigned int skip_vpd_pages: 1; unsigned int try_vpd_pages: 1; unsigned int use_192_bytes_for_3f: 1; unsigned int no_start_on_add: 1; unsigned int allow_restart: 1; unsigned int manage_start_stop: 1; unsigned int start_stop_pwr_cond: 1; unsigned int no_uld_attach: 1; unsigned int select_no_atn: 1; unsigned int fix_capacity: 1; unsigned int guess_capacity: 1; unsigned int retry_hwerror: 1; unsigned int last_sector_bug: 1; unsigned int no_read_disc_info: 1; unsigned int no_read_capacity_16: 1; unsigned int try_rc_10_first: 1; unsigned int security_supported: 1; unsigned int is_visible: 1; unsigned int wce_default_on: 1; unsigned int no_dif: 1; unsigned int broken_fua: 1; unsigned int lun_in_cdb: 1; unsigned int unmap_limit_for_ws: 1; unsigned int rpm_autosuspend: 1; bool offline_already; atomic_t disk_events_disable_depth; long unsigned int supported_events[1]; long unsigned int pending_events[1]; struct list_head event_list; struct work_struct event_work; unsigned int max_device_blocked; atomic_t iorequest_cnt; atomic_t iodone_cnt; atomic_t ioerr_cnt; struct device sdev_gendev; struct device sdev_dev; struct execute_work ew; struct work_struct requeue_work; struct scsi_device_handler *handler; void *handler_data; size_t dma_drain_len; void *dma_drain_buf; unsigned char access_state; struct mutex state_mutex; enum scsi_device_state sdev_state; struct task_struct *quiesced_by; long unsigned int sdev_data[0]; }; enum scsi_host_state { SHOST_CREATED = 1, SHOST_RUNNING = 2, SHOST_CANCEL = 3, SHOST_DEL = 4, SHOST_RECOVERY = 5, SHOST_CANCEL_RECOVERY = 6, SHOST_DEL_RECOVERY = 7, }; struct scsi_host_template; struct scsi_transport_template; struct Scsi_Host { struct list_head __devices; struct list_head __targets; struct list_head starved_list; spinlock_t default_lock; spinlock_t *host_lock; struct mutex scan_mutex; struct list_head eh_cmd_q; struct task_struct *ehandler; struct completion *eh_action; wait_queue_head_t host_wait; struct scsi_host_template *hostt; struct scsi_transport_template *transportt; struct blk_mq_tag_set tag_set; atomic_t host_blocked; unsigned int host_failed; unsigned int host_eh_scheduled; unsigned int host_no; int eh_deadline; long unsigned int last_reset; unsigned int max_channel; unsigned int max_id; u64 max_lun; unsigned int unique_id; short unsigned int max_cmd_len; int this_id; int can_queue; short int cmd_per_lun; short unsigned int sg_tablesize; short unsigned int sg_prot_tablesize; unsigned int max_sectors; unsigned int max_segment_size; long unsigned int dma_boundary; long unsigned int virt_boundary_mask; unsigned int nr_hw_queues; unsigned int nr_maps; unsigned int active_mode: 2; unsigned int host_self_blocked: 1; unsigned int reverse_ordering: 1; unsigned int tmf_in_progress: 1; unsigned int async_scan: 1; unsigned int eh_noresume: 1; unsigned int no_write_same: 1; unsigned int host_tagset: 1; unsigned int short_inquiry: 1; unsigned int no_scsi2_lun_in_cdb: 1; char work_q_name[20]; struct workqueue_struct *work_q; struct workqueue_struct *tmf_work_q; unsigned int max_host_blocked; unsigned int prot_capabilities; unsigned char prot_guard_type; long unsigned int base; long unsigned int io_port; unsigned char n_io_port; unsigned char dma_channel; unsigned int irq; enum scsi_host_state shost_state; struct device shost_gendev; struct device shost_dev; void *shost_data; struct device *dma_dev; long unsigned int hostdata[0]; }; enum scsi_target_state { STARGET_CREATED = 1, STARGET_RUNNING = 2, STARGET_REMOVE = 3, STARGET_CREATED_REMOVE = 4, STARGET_DEL = 5, }; struct scsi_target { struct scsi_device *starget_sdev_user; struct list_head siblings; struct list_head devices; struct device dev; struct kref reap_ref; unsigned int channel; unsigned int id; unsigned int create: 1; unsigned int single_lun: 1; unsigned int pdt_1f_for_no_lun: 1; unsigned int no_report_luns: 1; unsigned int expecting_lun_change: 1; atomic_t target_busy; atomic_t target_blocked; unsigned int can_queue; unsigned int max_target_blocked; char scsi_level; enum scsi_target_state state; void *hostdata; long unsigned int starget_data[0]; }; struct scsi_host_cmd_pool; struct scsi_cmnd; struct scsi_host_template { unsigned int cmd_size; int (*queuecommand)(struct Scsi_Host *, struct scsi_cmnd *); void (*commit_rqs)(struct Scsi_Host *, u16); struct module *module; const char *name; const char * (*info)(struct Scsi_Host *); int (*ioctl)(struct scsi_device *, unsigned int, void *); int (*compat_ioctl)(struct scsi_device *, unsigned int, void *); int (*init_cmd_priv)(struct Scsi_Host *, struct scsi_cmnd *); int (*exit_cmd_priv)(struct Scsi_Host *, struct scsi_cmnd *); int (*eh_abort_handler)(struct scsi_cmnd *); int (*eh_device_reset_handler)(struct scsi_cmnd *); int (*eh_target_reset_handler)(struct scsi_cmnd *); int (*eh_bus_reset_handler)(struct scsi_cmnd *); int (*eh_host_reset_handler)(struct scsi_cmnd *); int (*slave_alloc)(struct scsi_device *); int (*slave_configure)(struct scsi_device *); void (*slave_destroy)(struct scsi_device *); int (*target_alloc)(struct scsi_target *); void (*target_destroy)(struct scsi_target *); int (*scan_finished)(struct Scsi_Host *, long unsigned int); void (*scan_start)(struct Scsi_Host *); int (*change_queue_depth)(struct scsi_device *, int); int (*map_queues)(struct Scsi_Host *); int (*mq_poll)(struct Scsi_Host *, unsigned int); bool (*dma_need_drain)(struct request *); int (*bios_param)(struct scsi_device *, struct block_device *, sector_t, int *); void (*unlock_native_capacity)(struct scsi_device *); int (*show_info)(struct seq_file *, struct Scsi_Host *); int (*write_info)(struct Scsi_Host *, char *, int); enum blk_eh_timer_return (*eh_timed_out)(struct scsi_cmnd *); bool (*eh_should_retry_cmd)(struct scsi_cmnd *); int (*host_reset)(struct Scsi_Host *, int); const char *proc_name; struct proc_dir_entry *proc_dir; int can_queue; int this_id; short unsigned int sg_tablesize; short unsigned int sg_prot_tablesize; unsigned int max_sectors; unsigned int max_segment_size; long unsigned int dma_boundary; long unsigned int virt_boundary_mask; short int cmd_per_lun; unsigned char present; int tag_alloc_policy; unsigned int track_queue_depth: 1; unsigned int supported_mode: 2; unsigned int emulated: 1; unsigned int skip_settle_delay: 1; unsigned int no_write_same: 1; unsigned int host_tagset: 1; unsigned int max_host_blocked; struct device_attribute **shost_attrs; struct device_attribute **sdev_attrs; const struct attribute_group **sdev_groups; u64 vendor_id; struct scsi_host_cmd_pool *cmd_pool; int rpm_autosuspend_delay; }; struct scsi_data_buffer { struct sg_table table; unsigned int length; }; struct scsi_pointer { char *ptr; int this_residual; struct scatterlist *buffer; int buffers_residual; dma_addr_t dma_handle; volatile int Status; volatile int Message; volatile int have_data_in; volatile int sent_command; volatile int phase; }; struct scsi_cmnd { struct scsi_request req; struct scsi_device *device; struct list_head eh_entry; struct delayed_work abort_work; struct callback_head rcu; int eh_eflags; int budget_token; long unsigned int jiffies_at_alloc; int retries; int allowed; unsigned char prot_op; unsigned char prot_type; unsigned char prot_flags; short unsigned int cmd_len; enum dma_data_direction sc_data_direction; unsigned char *cmnd; struct scsi_data_buffer sdb; struct scsi_data_buffer *prot_sdb; unsigned int underflow; unsigned int transfersize; struct request *request; unsigned char *sense_buffer; void (*scsi_done)(struct scsi_cmnd *); struct scsi_pointer SCp; unsigned char *host_scribble; int result; int flags; long unsigned int state; unsigned char tag; unsigned int extra_len; }; enum scsi_prot_operations { SCSI_PROT_NORMAL = 0, SCSI_PROT_READ_INSERT = 1, SCSI_PROT_WRITE_STRIP = 2, SCSI_PROT_READ_STRIP = 3, SCSI_PROT_WRITE_INSERT = 4, SCSI_PROT_READ_PASS = 5, SCSI_PROT_WRITE_PASS = 6, }; struct scsi_driver { struct device_driver gendrv; void (*rescan)(struct device *); blk_status_t (*init_command)(struct scsi_cmnd *); void (*uninit_command)(struct scsi_cmnd *); int (*done)(struct scsi_cmnd *); int (*eh_action)(struct scsi_cmnd *, int); void (*eh_reset)(struct scsi_cmnd *); }; struct trace_event_raw_scsi_dispatch_cmd_start { struct trace_entry ent; unsigned int host_no; unsigned int channel; unsigned int id; unsigned int lun; unsigned int opcode; unsigned int cmd_len; unsigned int data_sglen; unsigned int prot_sglen; unsigned char prot_op; u32 __data_loc_cmnd; char __data[0]; }; struct trace_event_raw_scsi_dispatch_cmd_error { struct trace_entry ent; unsigned int host_no; unsigned int channel; unsigned int id; unsigned int lun; int rtn; unsigned int opcode; unsigned int cmd_len; unsigned int data_sglen; unsigned int prot_sglen; unsigned char prot_op; u32 __data_loc_cmnd; char __data[0]; }; struct trace_event_raw_scsi_cmd_done_timeout_template { struct trace_entry ent; unsigned int host_no; unsigned int channel; unsigned int id; unsigned int lun; int result; unsigned int opcode; unsigned int cmd_len; unsigned int data_sglen; unsigned int prot_sglen; unsigned char prot_op; u32 __data_loc_cmnd; char __data[0]; }; struct trace_event_raw_scsi_eh_wakeup { struct trace_entry ent; unsigned int host_no; char __data[0]; }; struct trace_event_data_offsets_scsi_dispatch_cmd_start { u32 cmnd; }; struct trace_event_data_offsets_scsi_dispatch_cmd_error { u32 cmnd; }; struct trace_event_data_offsets_scsi_cmd_done_timeout_template { u32 cmnd; }; struct trace_event_data_offsets_scsi_eh_wakeup {}; typedef void (*btf_trace_scsi_dispatch_cmd_start)(void *, struct scsi_cmnd *); typedef void (*btf_trace_scsi_dispatch_cmd_error)(void *, struct scsi_cmnd *, int); typedef void (*btf_trace_scsi_dispatch_cmd_done)(void *, struct scsi_cmnd *); typedef void (*btf_trace_scsi_dispatch_cmd_timeout)(void *, struct scsi_cmnd *); typedef void (*btf_trace_scsi_eh_wakeup)(void *, struct Scsi_Host *); struct scsi_transport_template { struct transport_container host_attrs; struct transport_container target_attrs; struct transport_container device_attrs; int (*user_scan)(struct Scsi_Host *, uint, uint, u64); int device_size; int device_private_offset; int target_size; int target_private_offset; int host_size; unsigned int create_work_queue: 1; void (*eh_strategy_handler)(struct Scsi_Host *); }; struct scsi_host_busy_iter_data { bool (*fn)(struct scsi_cmnd *, void *, bool); void *priv; }; struct scsi_idlun { __u32 dev_id; __u32 host_unique_id; }; typedef void (*activate_complete)(void *, int); struct scsi_device_handler { struct list_head list; struct module *module; const char *name; enum scsi_disposition (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *); int (*attach)(struct scsi_device *); void (*detach)(struct scsi_device *); int (*activate)(struct scsi_device *, activate_complete, void *); blk_status_t (*prep_fn)(struct scsi_device *, struct request *); int (*set_params)(struct scsi_device *, const char *); void (*rescan)(struct scsi_device *); }; struct scsi_eh_save { int result; unsigned int resid_len; int eh_eflags; enum dma_data_direction data_direction; unsigned int underflow; unsigned char cmd_len; unsigned char prot_op; unsigned char *cmnd; struct scsi_data_buffer sdb; unsigned char eh_cmnd[16]; struct scatterlist sense_sgl; }; struct scsi_varlen_cdb_hdr { __u8 opcode; __u8 control; __u8 misc[5]; __u8 additional_cdb_length; __be16 service_action; }; struct scsi_mode_data { __u32 length; __u16 block_descriptor_length; __u8 medium_type; __u8 device_specific; __u8 header_length; __u8 longlba: 1; }; struct scsi_event { enum scsi_device_event evt_type; struct list_head node; }; enum scsi_host_prot_capabilities { SHOST_DIF_TYPE1_PROTECTION = 1, SHOST_DIF_TYPE2_PROTECTION = 2, SHOST_DIF_TYPE3_PROTECTION = 4, SHOST_DIX_TYPE0_PROTECTION = 8, SHOST_DIX_TYPE1_PROTECTION = 16, SHOST_DIX_TYPE2_PROTECTION = 32, SHOST_DIX_TYPE3_PROTECTION = 64, }; enum { ACTION_FAIL = 0, ACTION_REPREP = 1, ACTION_RETRY = 2, ACTION_DELAYED_RETRY = 3, }; struct value_name_pair; struct sa_name_list { int opcode; const struct value_name_pair *arr; int arr_sz; }; struct value_name_pair { int value; const char *name; }; struct error_info { short unsigned int code12; short unsigned int size; }; struct error_info2 { unsigned char code1; unsigned char code2_min; unsigned char code2_max; const char *str; const char *fmt; }; struct scsi_lun { __u8 scsi_lun[8]; }; enum scsi_timeouts { SCSI_DEFAULT_EH_TIMEOUT = 3000, }; enum scsi_scan_mode { SCSI_SCAN_INITIAL = 0, SCSI_SCAN_RESCAN = 1, SCSI_SCAN_MANUAL = 2, }; struct async_scan_data { struct list_head list; struct Scsi_Host *shost; struct completion prev_finished; }; enum scsi_devinfo_key { SCSI_DEVINFO_GLOBAL = 0, SCSI_DEVINFO_SPI = 1, }; struct scsi_dev_info_list { struct list_head dev_info_list; char vendor[8]; char model[16]; blist_flags_t flags; unsigned int compatible; }; struct scsi_dev_info_list_table { struct list_head node; struct list_head scsi_dev_info_list; const char *name; int key; }; struct double_list { struct list_head *top; struct list_head *bottom; }; struct scsi_nl_hdr { __u8 version; __u8 transport; __u16 magic; __u16 msgtype; __u16 msglen; }; enum { SCSI_DH_OK = 0, SCSI_DH_DEV_FAILED = 1, SCSI_DH_DEV_TEMP_BUSY = 2, SCSI_DH_DEV_UNSUPP = 3, SCSI_DH_DEVICE_MAX = 4, SCSI_DH_NOTCONN = 5, SCSI_DH_CONN_FAILURE = 6, SCSI_DH_TRANSPORT_MAX = 7, SCSI_DH_IO = 8, SCSI_DH_INVALID_IO = 9, SCSI_DH_RETRY = 10, SCSI_DH_IMM_RETRY = 11, SCSI_DH_TIMED_OUT = 12, SCSI_DH_RES_TEMP_UNAVAIL = 13, SCSI_DH_DEV_OFFLINED = 14, SCSI_DH_NOMEM = 15, SCSI_DH_NOSYS = 16, SCSI_DH_DRIVER_MAX = 17, }; struct scsi_dh_blist { const char *vendor; const char *model; const char *driver; }; enum scsi_prot_flags { SCSI_PROT_TRANSFER_PI = 1, SCSI_PROT_GUARD_CHECK = 2, SCSI_PROT_REF_CHECK = 4, SCSI_PROT_REF_INCREMENT = 8, SCSI_PROT_IP_CHECKSUM = 16, }; enum { SD_EXT_CDB_SIZE = 32, SD_MEMPOOL_SIZE = 2, }; enum { SD_DEF_XFER_BLOCKS = 65535, SD_MAX_XFER_BLOCKS = 4294967295, SD_MAX_WS10_BLOCKS = 65535, SD_MAX_WS16_BLOCKS = 8388607, }; enum { SD_LBP_FULL = 0, SD_LBP_UNMAP = 1, SD_LBP_WS16 = 2, SD_LBP_WS10 = 3, SD_LBP_ZERO = 4, SD_LBP_DISABLE = 5, }; enum { SD_ZERO_WRITE = 0, SD_ZERO_WS = 1, SD_ZERO_WS16_UNMAP = 2, SD_ZERO_WS10_UNMAP = 3, }; struct opal_dev___2; struct scsi_disk { struct scsi_driver *driver; struct scsi_device *device; struct device dev; struct gendisk *disk; struct opal_dev___2 *opal_dev; u32 nr_zones; u32 rev_nr_zones; u32 zone_blocks; u32 rev_zone_blocks; u32 zones_optimal_open; u32 zones_optimal_nonseq; u32 zones_max_open; u32 *zones_wp_offset; spinlock_t zones_wp_offset_lock; u32 *rev_wp_offset; struct mutex rev_mutex; struct work_struct zone_wp_offset_work; char *zone_wp_update_buf; atomic_t openers; sector_t capacity; int max_retries; u32 max_xfer_blocks; u32 opt_xfer_blocks; u32 max_ws_blocks; u32 max_unmap_blocks; u32 unmap_granularity; u32 unmap_alignment; u32 index; unsigned int physical_block_size; unsigned int max_medium_access_timeouts; unsigned int medium_access_timed_out; u8 media_present; u8 write_prot; u8 protection_type; u8 provisioning_mode; u8 zeroing_mode; unsigned int ATO: 1; unsigned int cache_override: 1; unsigned int WCE: 1; unsigned int RCD: 1; unsigned int DPOFUA: 1; unsigned int first_scan: 1; unsigned int lbpme: 1; unsigned int lbprz: 1; unsigned int lbpu: 1; unsigned int lbpws: 1; unsigned int lbpws10: 1; unsigned int lbpvpd: 1; unsigned int ws10: 1; unsigned int ws16: 1; unsigned int rc_basis: 2; unsigned int zoned: 2; unsigned int urswrz: 1; unsigned int security: 1; unsigned int ignore_medium_access_errors: 1; }; enum scsi_host_guard_type { SHOST_DIX_GUARD_CRC = 1, SHOST_DIX_GUARD_IP = 2, }; enum zbc_zone_type { ZBC_ZONE_TYPE_CONV = 1, ZBC_ZONE_TYPE_SEQWRITE_REQ = 2, ZBC_ZONE_TYPE_SEQWRITE_PREF = 3, }; enum zbc_zone_cond { ZBC_ZONE_COND_NO_WP = 0, ZBC_ZONE_COND_EMPTY = 1, ZBC_ZONE_COND_IMP_OPEN = 2, ZBC_ZONE_COND_EXP_OPEN = 3, ZBC_ZONE_COND_CLOSED = 4, ZBC_ZONE_COND_READONLY = 13, ZBC_ZONE_COND_FULL = 14, ZBC_ZONE_COND_OFFLINE = 15, }; struct nvme_id_power_state { __le16 max_power; __u8 rsvd2; __u8 flags; __le32 entry_lat; __le32 exit_lat; __u8 read_tput; __u8 read_lat; __u8 write_tput; __u8 write_lat; __le16 idle_power; __u8 idle_scale; __u8 rsvd19; __le16 active_power; __u8 active_work_scale; __u8 rsvd23[9]; }; enum { NVME_PS_FLAGS_MAX_POWER_SCALE = 1, NVME_PS_FLAGS_NON_OP_STATE = 2, }; enum nvme_ctrl_attr { NVME_CTRL_ATTR_HID_128_BIT = 1, NVME_CTRL_ATTR_TBKAS = 64, }; struct nvme_id_ctrl { __le16 vid; __le16 ssvid; char sn[20]; char mn[40]; char fr[8]; __u8 rab; __u8 ieee[3]; __u8 cmic; __u8 mdts; __le16 cntlid; __le32 ver; __le32 rtd3r; __le32 rtd3e; __le32 oaes; __le32 ctratt; __u8 rsvd100[28]; __le16 crdt1; __le16 crdt2; __le16 crdt3; __u8 rsvd134[122]; __le16 oacs; __u8 acl; __u8 aerl; __u8 frmw; __u8 lpa; __u8 elpe; __u8 npss; __u8 avscc; __u8 apsta; __le16 wctemp; __le16 cctemp; __le16 mtfa; __le32 hmpre; __le32 hmmin; __u8 tnvmcap[16]; __u8 unvmcap[16]; __le32 rpmbs; __le16 edstt; __u8 dsto; __u8 fwug; __le16 kas; __le16 hctma; __le16 mntmt; __le16 mxtmt; __le32 sanicap; __le32 hmminds; __le16 hmmaxd; __u8 rsvd338[4]; __u8 anatt; __u8 anacap; __le32 anagrpmax; __le32 nanagrpid; __u8 rsvd352[160]; __u8 sqes; __u8 cqes; __le16 maxcmd; __le32 nn; __le16 oncs; __le16 fuses; __u8 fna; __u8 vwc; __le16 awun; __le16 awupf; __u8 nvscc; __u8 nwpc; __le16 acwu; __u8 rsvd534[2]; __le32 sgls; __le32 mnan; __u8 rsvd544[224]; char subnqn[256]; __u8 rsvd1024[768]; __le32 ioccsz; __le32 iorcsz; __le16 icdoff; __u8 ctrattr; __u8 msdbd; __u8 rsvd1804[244]; struct nvme_id_power_state psd[32]; __u8 vs[1024]; }; enum { NVME_CTRL_CMIC_MULTI_CTRL = 2, NVME_CTRL_CMIC_ANA = 8, NVME_CTRL_ONCS_COMPARE = 1, NVME_CTRL_ONCS_WRITE_UNCORRECTABLE = 2, NVME_CTRL_ONCS_DSM = 4, NVME_CTRL_ONCS_WRITE_ZEROES = 8, NVME_CTRL_ONCS_RESERVATIONS = 32, NVME_CTRL_ONCS_TIMESTAMP = 64, NVME_CTRL_VWC_PRESENT = 1, NVME_CTRL_OACS_SEC_SUPP = 1, NVME_CTRL_OACS_DIRECTIVES = 32, NVME_CTRL_OACS_DBBUF_SUPP = 256, NVME_CTRL_LPA_CMD_EFFECTS_LOG = 2, NVME_CTRL_CTRATT_128_ID = 1, NVME_CTRL_CTRATT_NON_OP_PSP = 2, NVME_CTRL_CTRATT_NVM_SETS = 4, NVME_CTRL_CTRATT_READ_RECV_LVLS = 8, NVME_CTRL_CTRATT_ENDURANCE_GROUPS = 16, NVME_CTRL_CTRATT_PREDICTABLE_LAT = 32, NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY = 128, NVME_CTRL_CTRATT_UUID_LIST = 512, }; struct nvme_lbaf { __le16 ms; __u8 ds; __u8 rp; }; struct nvme_id_ns { __le64 nsze; __le64 ncap; __le64 nuse; __u8 nsfeat; __u8 nlbaf; __u8 flbas; __u8 mc; __u8 dpc; __u8 dps; __u8 nmic; __u8 rescap; __u8 fpi; __u8 dlfeat; __le16 nawun; __le16 nawupf; __le16 nacwu; __le16 nabsn; __le16 nabo; __le16 nabspf; __le16 noiob; __u8 nvmcap[16]; __le16 npwg; __le16 npwa; __le16 npdg; __le16 npda; __le16 nows; __u8 rsvd74[18]; __le32 anagrpid; __u8 rsvd96[3]; __u8 nsattr; __le16 nvmsetid; __le16 endgid; __u8 nguid[16]; __u8 eui64[8]; struct nvme_lbaf lbaf[16]; __u8 rsvd192[192]; __u8 vs[3712]; }; struct nvme_id_ctrl_nvm { __u8 vsl; __u8 wzsl; __u8 wusl; __u8 dmrl; __le32 dmrsl; __le64 dmsl; __u8 rsvd16[4080]; }; enum { NVME_ID_CNS_NS = 0, NVME_ID_CNS_CTRL = 1, NVME_ID_CNS_NS_ACTIVE_LIST = 2, NVME_ID_CNS_NS_DESC_LIST = 3, NVME_ID_CNS_CS_NS = 5, NVME_ID_CNS_CS_CTRL = 6, NVME_ID_CNS_NS_PRESENT_LIST = 16, NVME_ID_CNS_NS_PRESENT = 17, NVME_ID_CNS_CTRL_NS_LIST = 18, NVME_ID_CNS_CTRL_LIST = 19, NVME_ID_CNS_SCNDRY_CTRL_LIST = 21, NVME_ID_CNS_NS_GRANULARITY = 22, NVME_ID_CNS_UUID_LIST = 23, }; enum { NVME_CSI_NVM = 0, NVME_CSI_ZNS = 2, }; enum { NVME_DIR_IDENTIFY = 0, NVME_DIR_STREAMS = 1, NVME_DIR_SND_ID_OP_ENABLE = 1, NVME_DIR_SND_ST_OP_REL_ID = 1, NVME_DIR_SND_ST_OP_REL_RSC = 2, NVME_DIR_RCV_ID_OP_PARAM = 1, NVME_DIR_RCV_ST_OP_PARAM = 1, NVME_DIR_RCV_ST_OP_STATUS = 2, NVME_DIR_RCV_ST_OP_RESOURCE = 3, NVME_DIR_ENDIR = 1, }; enum { NVME_NS_FEAT_THIN = 1, NVME_NS_FEAT_ATOMICS = 2, NVME_NS_FEAT_IO_OPT = 16, NVME_NS_ATTR_RO = 1, NVME_NS_FLBAS_LBA_MASK = 15, NVME_NS_FLBAS_META_EXT = 16, NVME_NS_NMIC_SHARED = 1, NVME_LBAF_RP_BEST = 0, NVME_LBAF_RP_BETTER = 1, NVME_LBAF_RP_GOOD = 2, NVME_LBAF_RP_DEGRADED = 3, NVME_NS_DPC_PI_LAST = 16, NVME_NS_DPC_PI_FIRST = 8, NVME_NS_DPC_PI_TYPE3 = 4, NVME_NS_DPC_PI_TYPE2 = 2, NVME_NS_DPC_PI_TYPE1 = 1, NVME_NS_DPS_PI_FIRST = 8, NVME_NS_DPS_PI_MASK = 7, NVME_NS_DPS_PI_TYPE1 = 1, NVME_NS_DPS_PI_TYPE2 = 2, NVME_NS_DPS_PI_TYPE3 = 3, }; struct nvme_ns_id_desc { __u8 nidt; __u8 nidl; __le16 reserved; }; enum { NVME_NIDT_EUI64 = 1, NVME_NIDT_NGUID = 2, NVME_NIDT_UUID = 3, NVME_NIDT_CSI = 4, }; struct nvme_fw_slot_info_log { __u8 afi; __u8 rsvd1[7]; __le64 frs[7]; __u8 rsvd64[448]; }; enum { NVME_CMD_EFFECTS_CSUPP = 1, NVME_CMD_EFFECTS_LBCC = 2, NVME_CMD_EFFECTS_NCC = 4, NVME_CMD_EFFECTS_NIC = 8, NVME_CMD_EFFECTS_CCC = 16, NVME_CMD_EFFECTS_CSE_MASK = 196608, NVME_CMD_EFFECTS_UUID_SEL = 524288, }; struct nvme_effects_log { __le32 acs[256]; __le32 iocs[256]; __u8 resv[2048]; }; enum nvme_ana_state { NVME_ANA_OPTIMIZED = 1, NVME_ANA_NONOPTIMIZED = 2, NVME_ANA_INACCESSIBLE = 3, NVME_ANA_PERSISTENT_LOSS = 4, NVME_ANA_CHANGE = 15, }; struct nvme_ana_rsp_hdr { __le64 chgcnt; __le16 ngrps; __le16 rsvd10[3]; }; enum { NVME_AER_ERROR = 0, NVME_AER_SMART = 1, NVME_AER_NOTICE = 2, NVME_AER_CSS = 6, NVME_AER_VS = 7, }; enum { NVME_AER_NOTICE_NS_CHANGED = 0, NVME_AER_NOTICE_FW_ACT_STARTING = 1, NVME_AER_NOTICE_ANA = 3, NVME_AER_NOTICE_DISC_CHANGED = 240, }; enum { NVME_AEN_CFG_NS_ATTR = 256, NVME_AEN_CFG_FW_ACT = 512, NVME_AEN_CFG_ANA_CHANGE = 2048, NVME_AEN_CFG_DISC_CHANGE = 2147483648, }; enum nvme_opcode { nvme_cmd_flush = 0, nvme_cmd_write = 1, nvme_cmd_read = 2, nvme_cmd_write_uncor = 4, nvme_cmd_compare = 5, nvme_cmd_write_zeroes = 8, nvme_cmd_dsm = 9, nvme_cmd_verify = 12, nvme_cmd_resv_register = 13, nvme_cmd_resv_report = 14, nvme_cmd_resv_acquire = 17, nvme_cmd_resv_release = 21, nvme_cmd_zone_mgmt_send = 121, nvme_cmd_zone_mgmt_recv = 122, nvme_cmd_zone_append = 125, }; struct nvme_sgl_desc { __le64 addr; __le32 length; __u8 rsvd[3]; __u8 type; }; struct nvme_keyed_sgl_desc { __le64 addr; __u8 length[3]; __u8 key[4]; __u8 type; }; union nvme_data_ptr { struct { __le64 prp1; __le64 prp2; }; struct nvme_sgl_desc sgl; struct nvme_keyed_sgl_desc ksgl; }; enum { NVME_CMD_FUSE_FIRST = 1, NVME_CMD_FUSE_SECOND = 2, NVME_CMD_SGL_METABUF = 64, NVME_CMD_SGL_METASEG = 128, NVME_CMD_SGL_ALL = 192, }; struct nvme_common_command { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __le32 cdw2[2]; __le64 metadata; union nvme_data_ptr dptr; __le32 cdw10; __le32 cdw11; __le32 cdw12; __le32 cdw13; __le32 cdw14; __le32 cdw15; }; struct nvme_rw_command { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __u64 rsvd2; __le64 metadata; union nvme_data_ptr dptr; __le64 slba; __le16 length; __le16 control; __le32 dsmgmt; __le32 reftag; __le16 apptag; __le16 appmask; }; enum { NVME_RW_LR = 32768, NVME_RW_FUA = 16384, NVME_RW_APPEND_PIREMAP = 512, NVME_RW_DSM_FREQ_UNSPEC = 0, NVME_RW_DSM_FREQ_TYPICAL = 1, NVME_RW_DSM_FREQ_RARE = 2, NVME_RW_DSM_FREQ_READS = 3, NVME_RW_DSM_FREQ_WRITES = 4, NVME_RW_DSM_FREQ_RW = 5, NVME_RW_DSM_FREQ_ONCE = 6, NVME_RW_DSM_FREQ_PREFETCH = 7, NVME_RW_DSM_FREQ_TEMP = 8, NVME_RW_DSM_LATENCY_NONE = 0, NVME_RW_DSM_LATENCY_IDLE = 16, NVME_RW_DSM_LATENCY_NORM = 32, NVME_RW_DSM_LATENCY_LOW = 48, NVME_RW_DSM_SEQ_REQ = 64, NVME_RW_DSM_COMPRESSED = 128, NVME_RW_PRINFO_PRCHK_REF = 1024, NVME_RW_PRINFO_PRCHK_APP = 2048, NVME_RW_PRINFO_PRCHK_GUARD = 4096, NVME_RW_PRINFO_PRACT = 8192, NVME_RW_DTYPE_STREAMS = 16, }; struct nvme_dsm_cmd { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __u64 rsvd2[2]; union nvme_data_ptr dptr; __le32 nr; __le32 attributes; __u32 rsvd12[4]; }; enum { NVME_DSMGMT_IDR = 1, NVME_DSMGMT_IDW = 2, NVME_DSMGMT_AD = 4, }; struct nvme_dsm_range { __le32 cattr; __le32 nlb; __le64 slba; }; struct nvme_write_zeroes_cmd { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __u64 rsvd2; __le64 metadata; union nvme_data_ptr dptr; __le64 slba; __le16 length; __le16 control; __le32 dsmgmt; __le32 reftag; __le16 apptag; __le16 appmask; }; enum nvme_zone_mgmt_action { NVME_ZONE_CLOSE = 1, NVME_ZONE_FINISH = 2, NVME_ZONE_OPEN = 3, NVME_ZONE_RESET = 4, NVME_ZONE_OFFLINE = 5, NVME_ZONE_SET_DESC_EXT = 16, }; struct nvme_zone_mgmt_send_cmd { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __le32 cdw2[2]; __le64 metadata; union nvme_data_ptr dptr; __le64 slba; __le32 cdw12; __u8 zsa; __u8 select_all; __u8 rsvd13[2]; __le32 cdw14[2]; }; struct nvme_zone_mgmt_recv_cmd { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __le64 rsvd2[2]; union nvme_data_ptr dptr; __le64 slba; __le32 numd; __u8 zra; __u8 zrasf; __u8 pr; __u8 rsvd13; __le32 cdw14[2]; }; struct nvme_feat_auto_pst { __le64 entries[32]; }; struct nvme_feat_host_behavior { __u8 acre; __u8 resv1[511]; }; enum { NVME_ENABLE_ACRE = 1, }; enum nvme_admin_opcode { nvme_admin_delete_sq = 0, nvme_admin_create_sq = 1, nvme_admin_get_log_page = 2, nvme_admin_delete_cq = 4, nvme_admin_create_cq = 5, nvme_admin_identify = 6, nvme_admin_abort_cmd = 8, nvme_admin_set_features = 9, nvme_admin_get_features = 10, nvme_admin_async_event = 12, nvme_admin_ns_mgmt = 13, nvme_admin_activate_fw = 16, nvme_admin_download_fw = 17, nvme_admin_dev_self_test = 20, nvme_admin_ns_attach = 21, nvme_admin_keep_alive = 24, nvme_admin_directive_send = 25, nvme_admin_directive_recv = 26, nvme_admin_virtual_mgmt = 28, nvme_admin_nvme_mi_send = 29, nvme_admin_nvme_mi_recv = 30, nvme_admin_dbbuf = 124, nvme_admin_format_nvm = 128, nvme_admin_security_send = 129, nvme_admin_security_recv = 130, nvme_admin_sanitize_nvm = 132, nvme_admin_get_lba_status = 134, nvme_admin_vendor_start = 192, }; enum { NVME_QUEUE_PHYS_CONTIG = 1, NVME_CQ_IRQ_ENABLED = 2, NVME_SQ_PRIO_URGENT = 0, NVME_SQ_PRIO_HIGH = 2, NVME_SQ_PRIO_MEDIUM = 4, NVME_SQ_PRIO_LOW = 6, NVME_FEAT_ARBITRATION = 1, NVME_FEAT_POWER_MGMT = 2, NVME_FEAT_LBA_RANGE = 3, NVME_FEAT_TEMP_THRESH = 4, NVME_FEAT_ERR_RECOVERY = 5, NVME_FEAT_VOLATILE_WC = 6, NVME_FEAT_NUM_QUEUES = 7, NVME_FEAT_IRQ_COALESCE = 8, NVME_FEAT_IRQ_CONFIG = 9, NVME_FEAT_WRITE_ATOMIC = 10, NVME_FEAT_ASYNC_EVENT = 11, NVME_FEAT_AUTO_PST = 12, NVME_FEAT_HOST_MEM_BUF = 13, NVME_FEAT_TIMESTAMP = 14, NVME_FEAT_KATO = 15, NVME_FEAT_HCTM = 16, NVME_FEAT_NOPSC = 17, NVME_FEAT_RRL = 18, NVME_FEAT_PLM_CONFIG = 19, NVME_FEAT_PLM_WINDOW = 20, NVME_FEAT_HOST_BEHAVIOR = 22, NVME_FEAT_SANITIZE = 23, NVME_FEAT_SW_PROGRESS = 128, NVME_FEAT_HOST_ID = 129, NVME_FEAT_RESV_MASK = 130, NVME_FEAT_RESV_PERSIST = 131, NVME_FEAT_WRITE_PROTECT = 132, NVME_FEAT_VENDOR_START = 192, NVME_FEAT_VENDOR_END = 255, NVME_LOG_ERROR = 1, NVME_LOG_SMART = 2, NVME_LOG_FW_SLOT = 3, NVME_LOG_CHANGED_NS = 4, NVME_LOG_CMD_EFFECTS = 5, NVME_LOG_DEVICE_SELF_TEST = 6, NVME_LOG_TELEMETRY_HOST = 7, NVME_LOG_TELEMETRY_CTRL = 8, NVME_LOG_ENDURANCE_GROUP = 9, NVME_LOG_ANA = 12, NVME_LOG_DISC = 112, NVME_LOG_RESERVATION = 128, NVME_FWACT_REPL = 0, NVME_FWACT_REPL_ACTV = 8, NVME_FWACT_ACTV = 16, }; struct nvme_identify { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __u64 rsvd2[2]; union nvme_data_ptr dptr; __u8 cns; __u8 rsvd3; __le16 ctrlid; __u8 rsvd11[3]; __u8 csi; __u32 rsvd12[4]; }; struct nvme_features { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __u64 rsvd2[2]; union nvme_data_ptr dptr; __le32 fid; __le32 dword11; __le32 dword12; __le32 dword13; __le32 dword14; __le32 dword15; }; struct nvme_create_cq { __u8 opcode; __u8 flags; __u16 command_id; __u32 rsvd1[5]; __le64 prp1; __u64 rsvd8; __le16 cqid; __le16 qsize; __le16 cq_flags; __le16 irq_vector; __u32 rsvd12[4]; }; struct nvme_create_sq { __u8 opcode; __u8 flags; __u16 command_id; __u32 rsvd1[5]; __le64 prp1; __u64 rsvd8; __le16 sqid; __le16 qsize; __le16 sq_flags; __le16 cqid; __u32 rsvd12[4]; }; struct nvme_delete_queue { __u8 opcode; __u8 flags; __u16 command_id; __u32 rsvd1[9]; __le16 qid; __u16 rsvd10; __u32 rsvd11[5]; }; struct nvme_abort_cmd { __u8 opcode; __u8 flags; __u16 command_id; __u32 rsvd1[9]; __le16 sqid; __u16 cid; __u32 rsvd11[5]; }; struct nvme_download_firmware { __u8 opcode; __u8 flags; __u16 command_id; __u32 rsvd1[5]; union nvme_data_ptr dptr; __le32 numd; __le32 offset; __u32 rsvd12[4]; }; struct nvme_format_cmd { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __u64 rsvd2[4]; __le32 cdw10; __u32 rsvd11[5]; }; struct nvme_get_log_page_command { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __u64 rsvd2[2]; union nvme_data_ptr dptr; __u8 lid; __u8 lsp; __le16 numdl; __le16 numdu; __u16 rsvd11; union { struct { __le32 lpol; __le32 lpou; }; __le64 lpo; }; __u8 rsvd14[3]; __u8 csi; __u32 rsvd15; }; struct nvme_directive_cmd { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __u64 rsvd2[2]; union nvme_data_ptr dptr; __le32 numd; __u8 doper; __u8 dtype; __le16 dspec; __u8 endir; __u8 tdtype; __u16 rsvd15; __u32 rsvd16[3]; }; enum nvmf_fabrics_opcode { nvme_fabrics_command = 127, }; enum nvmf_capsule_command { nvme_fabrics_type_property_set = 0, nvme_fabrics_type_connect = 1, nvme_fabrics_type_property_get = 4, }; struct nvmf_common_command { __u8 opcode; __u8 resv1; __u16 command_id; __u8 fctype; __u8 resv2[35]; __u8 ts[24]; }; struct nvmf_connect_command { __u8 opcode; __u8 resv1; __u16 command_id; __u8 fctype; __u8 resv2[19]; union nvme_data_ptr dptr; __le16 recfmt; __le16 qid; __le16 sqsize; __u8 cattr; __u8 resv3; __le32 kato; __u8 resv4[12]; }; struct nvmf_property_set_command { __u8 opcode; __u8 resv1; __u16 command_id; __u8 fctype; __u8 resv2[35]; __u8 attrib; __u8 resv3[3]; __le32 offset; __le64 value; __u8 resv4[8]; }; struct nvmf_property_get_command { __u8 opcode; __u8 resv1; __u16 command_id; __u8 fctype; __u8 resv2[35]; __u8 attrib; __u8 resv3[3]; __le32 offset; __u8 resv4[16]; }; struct nvme_dbbuf { __u8 opcode; __u8 flags; __u16 command_id; __u32 rsvd1[5]; __le64 prp1; __le64 prp2; __u32 rsvd12[6]; }; struct streams_directive_params { __le16 msl; __le16 nssa; __le16 nsso; __u8 rsvd[10]; __le32 sws; __le16 sgs; __le16 nsa; __le16 nso; __u8 rsvd2[6]; }; struct nvme_command { union { struct nvme_common_command common; struct nvme_rw_command rw; struct nvme_identify identify; struct nvme_features features; struct nvme_create_cq create_cq; struct nvme_create_sq create_sq; struct nvme_delete_queue delete_queue; struct nvme_download_firmware dlfw; struct nvme_format_cmd format; struct nvme_dsm_cmd dsm; struct nvme_write_zeroes_cmd write_zeroes; struct nvme_zone_mgmt_send_cmd zms; struct nvme_zone_mgmt_recv_cmd zmr; struct nvme_abort_cmd abort; struct nvme_get_log_page_command get_log_page; struct nvmf_common_command fabrics; struct nvmf_connect_command connect; struct nvmf_property_set_command prop_set; struct nvmf_property_get_command prop_get; struct nvme_dbbuf dbbuf; struct nvme_directive_cmd directive; }; }; enum { NVME_SC_SUCCESS = 0, NVME_SC_INVALID_OPCODE = 1, NVME_SC_INVALID_FIELD = 2, NVME_SC_CMDID_CONFLICT = 3, NVME_SC_DATA_XFER_ERROR = 4, NVME_SC_POWER_LOSS = 5, NVME_SC_INTERNAL = 6, NVME_SC_ABORT_REQ = 7, NVME_SC_ABORT_QUEUE = 8, NVME_SC_FUSED_FAIL = 9, NVME_SC_FUSED_MISSING = 10, NVME_SC_INVALID_NS = 11, NVME_SC_CMD_SEQ_ERROR = 12, NVME_SC_SGL_INVALID_LAST = 13, NVME_SC_SGL_INVALID_COUNT = 14, NVME_SC_SGL_INVALID_DATA = 15, NVME_SC_SGL_INVALID_METADATA = 16, NVME_SC_SGL_INVALID_TYPE = 17, NVME_SC_CMB_INVALID_USE = 18, NVME_SC_PRP_INVALID_OFFSET = 19, NVME_SC_ATOMIC_WU_EXCEEDED = 20, NVME_SC_OP_DENIED = 21, NVME_SC_SGL_INVALID_OFFSET = 22, NVME_SC_RESERVED = 23, NVME_SC_HOST_ID_INCONSIST = 24, NVME_SC_KA_TIMEOUT_EXPIRED = 25, NVME_SC_KA_TIMEOUT_INVALID = 26, NVME_SC_ABORTED_PREEMPT_ABORT = 27, NVME_SC_SANITIZE_FAILED = 28, NVME_SC_SANITIZE_IN_PROGRESS = 29, NVME_SC_SGL_INVALID_GRANULARITY = 30, NVME_SC_CMD_NOT_SUP_CMB_QUEUE = 31, NVME_SC_NS_WRITE_PROTECTED = 32, NVME_SC_CMD_INTERRUPTED = 33, NVME_SC_TRANSIENT_TR_ERR = 34, NVME_SC_INVALID_IO_CMD_SET = 44, NVME_SC_LBA_RANGE = 128, NVME_SC_CAP_EXCEEDED = 129, NVME_SC_NS_NOT_READY = 130, NVME_SC_RESERVATION_CONFLICT = 131, NVME_SC_FORMAT_IN_PROGRESS = 132, NVME_SC_CQ_INVALID = 256, NVME_SC_QID_INVALID = 257, NVME_SC_QUEUE_SIZE = 258, NVME_SC_ABORT_LIMIT = 259, NVME_SC_ABORT_MISSING = 260, NVME_SC_ASYNC_LIMIT = 261, NVME_SC_FIRMWARE_SLOT = 262, NVME_SC_FIRMWARE_IMAGE = 263, NVME_SC_INVALID_VECTOR = 264, NVME_SC_INVALID_LOG_PAGE = 265, NVME_SC_INVALID_FORMAT = 266, NVME_SC_FW_NEEDS_CONV_RESET = 267, NVME_SC_INVALID_QUEUE = 268, NVME_SC_FEATURE_NOT_SAVEABLE = 269, NVME_SC_FEATURE_NOT_CHANGEABLE = 270, NVME_SC_FEATURE_NOT_PER_NS = 271, NVME_SC_FW_NEEDS_SUBSYS_RESET = 272, NVME_SC_FW_NEEDS_RESET = 273, NVME_SC_FW_NEEDS_MAX_TIME = 274, NVME_SC_FW_ACTIVATE_PROHIBITED = 275, NVME_SC_OVERLAPPING_RANGE = 276, NVME_SC_NS_INSUFFICIENT_CAP = 277, NVME_SC_NS_ID_UNAVAILABLE = 278, NVME_SC_NS_ALREADY_ATTACHED = 280, NVME_SC_NS_IS_PRIVATE = 281, NVME_SC_NS_NOT_ATTACHED = 282, NVME_SC_THIN_PROV_NOT_SUPP = 283, NVME_SC_CTRL_LIST_INVALID = 284, NVME_SC_SELT_TEST_IN_PROGRESS = 285, NVME_SC_BP_WRITE_PROHIBITED = 286, NVME_SC_CTRL_ID_INVALID = 287, NVME_SC_SEC_CTRL_STATE_INVALID = 288, NVME_SC_CTRL_RES_NUM_INVALID = 289, NVME_SC_RES_ID_INVALID = 290, NVME_SC_PMR_SAN_PROHIBITED = 291, NVME_SC_ANA_GROUP_ID_INVALID = 292, NVME_SC_ANA_ATTACH_FAILED = 293, NVME_SC_BAD_ATTRIBUTES = 384, NVME_SC_INVALID_PI = 385, NVME_SC_READ_ONLY = 386, NVME_SC_ONCS_NOT_SUPPORTED = 387, NVME_SC_CONNECT_FORMAT = 384, NVME_SC_CONNECT_CTRL_BUSY = 385, NVME_SC_CONNECT_INVALID_PARAM = 386, NVME_SC_CONNECT_RESTART_DISC = 387, NVME_SC_CONNECT_INVALID_HOST = 388, NVME_SC_DISCOVERY_RESTART = 400, NVME_SC_AUTH_REQUIRED = 401, NVME_SC_ZONE_BOUNDARY_ERROR = 440, NVME_SC_ZONE_FULL = 441, NVME_SC_ZONE_READ_ONLY = 442, NVME_SC_ZONE_OFFLINE = 443, NVME_SC_ZONE_INVALID_WRITE = 444, NVME_SC_ZONE_TOO_MANY_ACTIVE = 445, NVME_SC_ZONE_TOO_MANY_OPEN = 446, NVME_SC_ZONE_INVALID_TRANSITION = 447, NVME_SC_WRITE_FAULT = 640, NVME_SC_READ_ERROR = 641, NVME_SC_GUARD_CHECK = 642, NVME_SC_APPTAG_CHECK = 643, NVME_SC_REFTAG_CHECK = 644, NVME_SC_COMPARE_FAILED = 645, NVME_SC_ACCESS_DENIED = 646, NVME_SC_UNWRITTEN_BLOCK = 647, NVME_SC_ANA_PERSISTENT_LOSS = 769, NVME_SC_ANA_INACCESSIBLE = 770, NVME_SC_ANA_TRANSITION = 771, NVME_SC_HOST_PATH_ERROR = 880, NVME_SC_HOST_ABORTED_CMD = 881, NVME_SC_CRD = 6144, NVME_SC_DNR = 16384, }; union nvme_result { __le16 u16; __le32 u32; __le64 u64; }; enum nvme_quirks { NVME_QUIRK_STRIPE_SIZE = 1, NVME_QUIRK_IDENTIFY_CNS = 2, NVME_QUIRK_DEALLOCATE_ZEROES = 4, NVME_QUIRK_DELAY_BEFORE_CHK_RDY = 8, NVME_QUIRK_NO_APST = 16, NVME_QUIRK_NO_DEEPEST_PS = 32, NVME_QUIRK_LIGHTNVM = 64, NVME_QUIRK_MEDIUM_PRIO_SQ = 128, NVME_QUIRK_IGNORE_DEV_SUBNQN = 256, NVME_QUIRK_DISABLE_WRITE_ZEROES = 512, NVME_QUIRK_SIMPLE_SUSPEND = 1024, NVME_QUIRK_SINGLE_VECTOR = 2048, NVME_QUIRK_128_BYTES_SQES = 4096, NVME_QUIRK_SHARED_TAGS = 8192, NVME_QUIRK_NO_TEMP_THRESH_CHANGE = 16384, NVME_QUIRK_NO_NS_DESC_LIST = 32768, NVME_QUIRK_DMA_ADDRESS_BITS_48 = 65536, }; struct nvme_ctrl; struct nvme_request { struct nvme_command *cmd; union nvme_result result; u8 retries; u8 flags; u16 status; struct nvme_ctrl *ctrl; }; enum nvme_ctrl_state { NVME_CTRL_NEW = 0, NVME_CTRL_LIVE = 1, NVME_CTRL_RESETTING = 2, NVME_CTRL_CONNECTING = 3, NVME_CTRL_DELETING = 4, NVME_CTRL_DELETING_NOIO = 5, NVME_CTRL_DEAD = 6, }; struct nvme_fault_inject {}; struct nvme_ctrl_ops; struct nvme_subsystem; struct nvmf_ctrl_options; struct nvme_ctrl { bool comp_seen; enum nvme_ctrl_state state; bool identified; spinlock_t lock; struct mutex scan_lock; const struct nvme_ctrl_ops *ops; struct request_queue *admin_q; struct request_queue *connect_q; struct request_queue *fabrics_q; struct device *dev; int instance; int numa_node; struct blk_mq_tag_set *tagset; struct blk_mq_tag_set *admin_tagset; struct list_head namespaces; struct rw_semaphore namespaces_rwsem; struct device ctrl_device; struct device *device; struct device *hwmon_device; struct cdev cdev; struct work_struct reset_work; struct work_struct delete_work; wait_queue_head_t state_wq; struct nvme_subsystem *subsys; struct list_head subsys_entry; struct opal_dev___2 *opal_dev; char name[12]; u16 cntlid; u32 ctrl_config; u16 mtfa; u32 queue_count; u64 cap; u32 max_hw_sectors; u32 max_segments; u32 max_integrity_segments; u32 max_discard_sectors; u32 max_discard_segments; u32 max_zeroes_sectors; u32 max_zone_append; u16 crdt[3]; u16 oncs; u16 oacs; u16 nssa; u16 nr_streams; u16 sqsize; u32 max_namespaces; atomic_t abort_limit; u8 vwc; u32 vs; u32 sgls; u16 kas; u8 npss; u8 apsta; u16 wctemp; u16 cctemp; u32 oaes; u32 aen_result; u32 ctratt; unsigned int shutdown_timeout; unsigned int kato; bool subsystem; long unsigned int quirks; struct nvme_id_power_state psd[32]; struct nvme_effects_log *effects; struct xarray cels; struct work_struct scan_work; struct work_struct async_event_work; struct delayed_work ka_work; struct delayed_work failfast_work; struct nvme_command ka_cmd; struct work_struct fw_act_work; long unsigned int events; u8 anacap; u8 anatt; u32 anagrpmax; u32 nanagrpid; struct mutex ana_lock; struct nvme_ana_rsp_hdr *ana_log_buf; size_t ana_log_size; struct timer_list anatt_timer; struct work_struct ana_work; u64 ps_max_latency_us; bool apst_enabled; u32 hmpre; u32 hmmin; u32 hmminds; u16 hmmaxd; u32 ioccsz; u32 iorcsz; u16 icdoff; u16 maxcmd; int nr_reconnects; long unsigned int flags; struct nvmf_ctrl_options *opts; struct page *discard_page; long unsigned int discard_page_busy; struct nvme_fault_inject fault_inject; }; enum { NVME_REQ_CANCELLED = 1, NVME_REQ_USERCMD = 2, }; struct nvme_ctrl_ops { const char *name; struct module *module; unsigned int flags; int (*reg_read32)(struct nvme_ctrl *, u32, u32 *); int (*reg_write32)(struct nvme_ctrl *, u32, u32); int (*reg_read64)(struct nvme_ctrl *, u32, u64 *); void (*free_ctrl)(struct nvme_ctrl *); void (*submit_async_event)(struct nvme_ctrl *); void (*delete_ctrl)(struct nvme_ctrl *); int (*get_address)(struct nvme_ctrl *, char *, int); }; enum nvme_iopolicy { NVME_IOPOLICY_NUMA = 0, NVME_IOPOLICY_RR = 1, }; struct nvme_subsystem { int instance; struct device dev; struct kref ref; struct list_head entry; struct mutex lock; struct list_head ctrls; struct list_head nsheads; char subnqn[223]; char serial[20]; char model[40]; char firmware_rev[8]; u8 cmic; u16 vendor_id; u16 awupf; struct ida ns_ida; enum nvme_iopolicy iopolicy; }; struct nvmf_host; struct nvmf_ctrl_options { unsigned int mask; char *transport; char *subsysnqn; char *traddr; char *trsvcid; char *host_traddr; char *host_iface; size_t queue_size; unsigned int nr_io_queues; unsigned int reconnect_delay; bool discovery_nqn; bool duplicate_connect; unsigned int kato; struct nvmf_host *host; int max_reconnects; bool disable_sqflow; bool hdr_digest; bool data_digest; unsigned int nr_write_queues; unsigned int nr_poll_queues; int tos; int fast_io_fail_tmo; }; struct nvme_ns_ids { u8 eui64[8]; u8 nguid[16]; uuid_t uuid; u8 csi; }; struct nvme_ns; struct nvme_ns_head { struct list_head list; struct srcu_struct srcu; struct nvme_subsystem *subsys; unsigned int ns_id; struct nvme_ns_ids ids; struct list_head entry; struct kref ref; bool shared; int instance; struct nvme_effects_log *effects; struct cdev cdev; struct device cdev_device; struct gendisk *disk; struct bio_list requeue_list; spinlock_t requeue_lock; struct work_struct requeue_work; struct mutex lock; long unsigned int flags; struct nvme_ns *current_path[0]; }; struct nvme_ns { struct list_head list; struct nvme_ctrl *ctrl; struct request_queue *queue; struct gendisk *disk; enum nvme_ana_state ana_state; u32 ana_grpid; struct list_head siblings; struct nvm_dev *ndev; struct kref kref; struct nvme_ns_head *head; int lba_shift; u16 ms; u16 sgs; u32 sws; u8 pi_type; u64 zsze; long unsigned int features; long unsigned int flags; struct cdev cdev; struct device cdev_device; struct nvme_fault_inject fault_inject; }; enum nvme_ns_features { NVME_NS_EXT_LBAS = 1, NVME_NS_METADATA_SUPPORTED = 2, }; struct nvmf_host { struct kref ref; struct list_head list; char nqn[223]; uuid_t id; }; struct trace_event_raw_nvme_setup_cmd { struct trace_entry ent; char disk[32]; int ctrl_id; int qid; u8 opcode; u8 flags; u8 fctype; u16 cid; u32 nsid; bool metadata; u8 cdw10[24]; char __data[0]; }; struct trace_event_raw_nvme_complete_rq { struct trace_entry ent; char disk[32]; int ctrl_id; int qid; int cid; u64 result; u8 retries; u8 flags; u16 status; char __data[0]; }; struct trace_event_raw_nvme_async_event { struct trace_entry ent; int ctrl_id; u32 result; char __data[0]; }; struct trace_event_raw_nvme_sq { struct trace_entry ent; int ctrl_id; char disk[32]; int qid; u16 sq_head; u16 sq_tail; char __data[0]; }; struct trace_event_data_offsets_nvme_setup_cmd {}; struct trace_event_data_offsets_nvme_complete_rq {}; struct trace_event_data_offsets_nvme_async_event {}; struct trace_event_data_offsets_nvme_sq {}; typedef void (*btf_trace_nvme_setup_cmd)(void *, struct request *, struct nvme_command *); typedef void (*btf_trace_nvme_complete_rq)(void *, struct request *); typedef void (*btf_trace_nvme_async_event)(void *, struct nvme_ctrl *, u32); typedef void (*btf_trace_nvme_sq)(void *, struct request *, __le16, int); enum nvme_disposition { COMPLETE = 0, RETRY = 1, FAILOVER = 2, }; struct nvme_core_quirk_entry { u16 vid; const char *mn; const char *fr; long unsigned int quirks; }; struct nvme_user_io { __u8 opcode; __u8 flags; __u16 control; __u16 nblocks; __u16 rsvd; __u64 metadata; __u64 addr; __u64 slba; __u32 dsmgmt; __u32 reftag; __u16 apptag; __u16 appmask; }; struct nvme_passthru_cmd { __u8 opcode; __u8 flags; __u16 rsvd1; __u32 nsid; __u32 cdw2; __u32 cdw3; __u64 metadata; __u64 addr; __u32 metadata_len; __u32 data_len; __u32 cdw10; __u32 cdw11; __u32 cdw12; __u32 cdw13; __u32 cdw14; __u32 cdw15; __u32 timeout_ms; __u32 result; }; struct nvme_passthru_cmd64 { __u8 opcode; __u8 flags; __u16 rsvd1; __u32 nsid; __u32 cdw2; __u32 cdw3; __u64 metadata; __u64 addr; __u32 metadata_len; __u32 data_len; __u32 cdw10; __u32 cdw11; __u32 cdw12; __u32 cdw13; __u32 cdw14; __u32 cdw15; __u32 timeout_ms; __u32 rsvd2; __u64 result; }; struct nvme_ana_group_desc { __le32 grpid; __le32 nnsids; __le64 chgcnt; __u8 state; __u8 rsvd17[15]; __le32 nsids[0]; }; struct nvm_user_vio { __u8 opcode; __u8 flags; __u16 control; __u16 nppas; __u16 rsvd; __u64 metadata; __u64 addr; __u64 ppa_list; __u32 metadata_len; __u32 data_len; __u64 status; __u32 result; __u32 rsvd3[3]; }; struct nvm_passthru_vio { __u8 opcode; __u8 flags; __u8 rsvd[2]; __u32 nsid; __u32 cdw2; __u32 cdw3; __u64 metadata; __u64 addr; __u32 metadata_len; __u32 data_len; __u64 ppa_list; __u16 nppas; __u16 control; __u32 cdw13; __u32 cdw14; __u32 cdw15; __u64 status; __u32 result; __u32 timeout_ms; }; enum nvme_nvm_admin_opcode { nvme_nvm_admin_identity = 226, nvme_nvm_admin_get_bb_tbl = 242, nvme_nvm_admin_set_bb_tbl = 241, }; enum nvme_nvm_log_page { NVME_NVM_LOG_REPORT_CHUNK = 202, }; struct nvme_nvm_ph_rw { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __u64 rsvd2; __le64 metadata; __le64 prp1; __le64 prp2; __le64 spba; __le16 length; __le16 control; __le32 dsmgmt; __le64 resv; }; struct nvme_nvm_erase_blk { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __u64 rsvd[2]; __le64 prp1; __le64 prp2; __le64 spba; __le16 length; __le16 control; __le32 dsmgmt; __le64 resv; }; struct nvme_nvm_identity { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __u64 rsvd[2]; __le64 prp1; __le64 prp2; __u32 rsvd11[6]; }; struct nvme_nvm_getbbtbl { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __u64 rsvd[2]; __le64 prp1; __le64 prp2; __le64 spba; __u32 rsvd4[4]; }; struct nvme_nvm_setbbtbl { __u8 opcode; __u8 flags; __u16 command_id; __le32 nsid; __le64 rsvd[2]; __le64 prp1; __le64 prp2; __le64 spba; __le16 nlb; __u8 value; __u8 rsvd3; __u32 rsvd4[3]; }; struct nvme_nvm_command { union { struct nvme_common_command common; struct nvme_nvm_ph_rw ph_rw; struct nvme_nvm_erase_blk erase; struct nvme_nvm_identity identity; struct nvme_nvm_getbbtbl get_bb; struct nvme_nvm_setbbtbl set_bb; }; }; struct nvme_nvm_id12_grp { __u8 mtype; __u8 fmtype; __le16 res16; __u8 num_ch; __u8 num_lun; __u8 num_pln; __u8 rsvd1; __le16 num_chk; __le16 num_pg; __le16 fpg_sz; __le16 csecs; __le16 sos; __le16 rsvd2; __le32 trdt; __le32 trdm; __le32 tprt; __le32 tprm; __le32 tbet; __le32 tbem; __le32 mpos; __le32 mccap; __le16 cpar; __u8 reserved[906]; }; struct nvme_nvm_id12_addrf { __u8 ch_offset; __u8 ch_len; __u8 lun_offset; __u8 lun_len; __u8 pln_offset; __u8 pln_len; __u8 blk_offset; __u8 blk_len; __u8 pg_offset; __u8 pg_len; __u8 sec_offset; __u8 sec_len; __u8 res[4]; }; struct nvme_nvm_id12 { __u8 ver_id; __u8 vmnt; __u8 cgrps; __u8 res; __le32 cap; __le32 dom; struct nvme_nvm_id12_addrf ppaf; __u8 resv[228]; struct nvme_nvm_id12_grp grp; __u8 resv2[2880]; }; struct nvme_nvm_bb_tbl { __u8 tblid[4]; __le16 verid; __le16 revid; __le32 rvsd1; __le32 tblks; __le32 tfact; __le32 tgrown; __le32 tdresv; __le32 thresv; __le32 rsvd2[8]; __u8 blk[0]; }; struct nvme_nvm_id20_addrf { __u8 grp_len; __u8 pu_len; __u8 chk_len; __u8 lba_len; __u8 resv[4]; }; struct nvme_nvm_id20 { __u8 mjr; __u8 mnr; __u8 resv[6]; struct nvme_nvm_id20_addrf lbaf; __le32 mccap; __u8 resv2[12]; __u8 wit; __u8 resv3[31]; __le16 num_grp; __le16 num_pu; __le32 num_chk; __le32 clba; __u8 resv4[52]; __le32 ws_min; __le32 ws_opt; __le32 mw_cunits; __le32 maxoc; __le32 maxocpu; __u8 resv5[44]; __le32 trdt; __le32 trdm; __le32 twrt; __le32 twrm; __le32 tcrst; __le32 tcrsm; __u8 resv6[40]; __u8 resv7[2816]; __u8 vs[1024]; }; struct nvme_nvm_chk_meta { __u8 state; __u8 type; __u8 wi; __u8 rsvd[5]; __le64 slba; __le64 cnlb; __le64 wp; }; struct nvme_zns_lbafe { __le64 zsze; __u8 zdes; __u8 rsvd9[7]; }; struct nvme_id_ns_zns { __le16 zoc; __le16 ozcs; __le32 mar; __le32 mor; __le32 rrl; __le32 frl; __u8 rsvd20[2796]; struct nvme_zns_lbafe lbafe[16]; __u8 rsvd3072[768]; __u8 vs[256]; }; struct nvme_id_ctrl_zns { __u8 zasl; __u8 rsvd1[4095]; }; struct nvme_zone_descriptor { __u8 zt; __u8 zs; __u8 za; __u8 rsvd3[5]; __le64 zcap; __le64 zslba; __le64 wp; __u8 rsvd32[32]; }; enum { NVME_ZONE_TYPE_SEQWRITE_REQ = 2, }; struct nvme_zone_report { __le64 nr_zones; __u8 resv8[56]; struct nvme_zone_descriptor entries[0]; }; enum { NVME_ZRA_ZONE_REPORT = 0, NVME_ZRASF_ZONE_REPORT_ALL = 0, NVME_ZRASF_ZONE_STATE_EMPTY = 1, NVME_ZRASF_ZONE_STATE_IMP_OPEN = 2, NVME_ZRASF_ZONE_STATE_EXP_OPEN = 3, NVME_ZRASF_ZONE_STATE_CLOSED = 4, NVME_ZRASF_ZONE_STATE_READONLY = 5, NVME_ZRASF_ZONE_STATE_FULL = 6, NVME_ZRASF_ZONE_STATE_OFFLINE = 7, NVME_REPORT_ZONE_PARTIAL = 1, }; enum hwmon_sensor_types { hwmon_chip = 0, hwmon_temp = 1, hwmon_in = 2, hwmon_curr = 3, hwmon_power = 4, hwmon_energy = 5, hwmon_humidity = 6, hwmon_fan = 7, hwmon_pwm = 8, hwmon_intrusion = 9, hwmon_max = 10, }; enum hwmon_chip_attributes { hwmon_chip_temp_reset_history = 0, hwmon_chip_in_reset_history = 1, hwmon_chip_curr_reset_history = 2, hwmon_chip_power_reset_history = 3, hwmon_chip_register_tz = 4, hwmon_chip_update_interval = 5, hwmon_chip_alarms = 6, hwmon_chip_samples = 7, hwmon_chip_curr_samples = 8, hwmon_chip_in_samples = 9, hwmon_chip_power_samples = 10, hwmon_chip_temp_samples = 11, }; enum hwmon_temp_attributes { hwmon_temp_enable = 0, hwmon_temp_input = 1, hwmon_temp_type = 2, hwmon_temp_lcrit = 3, hwmon_temp_lcrit_hyst = 4, hwmon_temp_min = 5, hwmon_temp_min_hyst = 6, hwmon_temp_max = 7, hwmon_temp_max_hyst = 8, hwmon_temp_crit = 9, hwmon_temp_crit_hyst = 10, hwmon_temp_emergency = 11, hwmon_temp_emergency_hyst = 12, hwmon_temp_alarm = 13, hwmon_temp_lcrit_alarm = 14, hwmon_temp_min_alarm = 15, hwmon_temp_max_alarm = 16, hwmon_temp_crit_alarm = 17, hwmon_temp_emergency_alarm = 18, hwmon_temp_fault = 19, hwmon_temp_offset = 20, hwmon_temp_label = 21, hwmon_temp_lowest = 22, hwmon_temp_highest = 23, hwmon_temp_reset_history = 24, hwmon_temp_rated_min = 25, hwmon_temp_rated_max = 26, }; struct hwmon_ops { umode_t (*is_visible)(const void *, enum hwmon_sensor_types, u32, int); int (*read)(struct device *, enum hwmon_sensor_types, u32, int, long int *); int (*read_string)(struct device *, enum hwmon_sensor_types, u32, int, const char **); int (*write)(struct device *, enum hwmon_sensor_types, u32, int, long int); }; struct hwmon_channel_info { enum hwmon_sensor_types type; const u32 *config; }; struct hwmon_chip_info { const struct hwmon_ops *ops; const struct hwmon_channel_info **info; }; struct nvme_smart_log { __u8 critical_warning; __u8 temperature[2]; __u8 avail_spare; __u8 spare_thresh; __u8 percent_used; __u8 endu_grp_crit_warn_sumry; __u8 rsvd7[25]; __u8 data_units_read[16]; __u8 data_units_written[16]; __u8 host_reads[16]; __u8 host_writes[16]; __u8 ctrl_busy_time[16]; __u8 power_cycles[16]; __u8 power_on_hours[16]; __u8 unsafe_shutdowns[16]; __u8 media_errors[16]; __u8 num_err_log_entries[16]; __le32 warning_temp_time; __le32 critical_comp_time; __le16 temp_sensor[8]; __le32 thm_temp1_trans_count; __le32 thm_temp2_trans_count; __le32 thm_temp1_total_time; __le32 thm_temp2_total_time; __u8 rsvd232[280]; }; enum { NVME_SMART_CRIT_SPARE = 1, NVME_SMART_CRIT_TEMPERATURE = 2, NVME_SMART_CRIT_RELIABILITY = 4, NVME_SMART_CRIT_MEDIA = 8, NVME_SMART_CRIT_VOLATILE_MEMORY = 16, }; enum { NVME_TEMP_THRESH_MASK = 65535, NVME_TEMP_THRESH_SELECT_SHIFT = 16, NVME_TEMP_THRESH_TYPE_UNDER = 1048576, }; struct nvme_hwmon_data { struct nvme_ctrl *ctrl; struct nvme_smart_log log; struct mutex read_lock; }; enum { NVME_CMBSZ_SQS = 1, NVME_CMBSZ_CQS = 2, NVME_CMBSZ_LISTS = 4, NVME_CMBSZ_RDS = 8, NVME_CMBSZ_WDS = 16, NVME_CMBSZ_SZ_SHIFT = 12, NVME_CMBSZ_SZ_MASK = 1048575, NVME_CMBSZ_SZU_SHIFT = 8, NVME_CMBSZ_SZU_MASK = 15, }; enum { NVME_SGL_FMT_DATA_DESC = 0, NVME_SGL_FMT_SEG_DESC = 2, NVME_SGL_FMT_LAST_SEG_DESC = 3, NVME_KEY_SGL_FMT_DATA_DESC = 4, NVME_TRANSPORT_SGL_DATA_DESC = 5, }; enum { NVME_HOST_MEM_ENABLE = 1, NVME_HOST_MEM_RETURN = 2, }; struct nvme_host_mem_buf_desc { __le64 addr; __le32 size; __u32 rsvd; }; struct nvme_completion { union nvme_result result; __le16 sq_head; __le16 sq_id; __u16 command_id; __le16 status; }; struct nvme_queue; struct nvme_dev { struct nvme_queue *queues; struct blk_mq_tag_set tagset; struct blk_mq_tag_set admin_tagset; u32 *dbs; struct device *dev; struct dma_pool___2 *prp_page_pool; struct dma_pool___2 *prp_small_pool; unsigned int online_queues; unsigned int max_qid; unsigned int io_queues[3]; unsigned int num_vecs; u32 q_depth; int io_sqes; u32 db_stride; void *bar; long unsigned int bar_mapped_size; struct work_struct remove_work; struct mutex shutdown_lock; bool subsystem; u64 cmb_size; bool cmb_use_sqes; u32 cmbsz; u32 cmbloc; struct nvme_ctrl ctrl; u32 last_ps; mempool_t *iod_mempool; u32 *dbbuf_dbs; dma_addr_t dbbuf_dbs_dma_addr; u32 *dbbuf_eis; dma_addr_t dbbuf_eis_dma_addr; u64 host_mem_size; u32 nr_host_mem_descs; dma_addr_t host_mem_descs_dma; struct nvme_host_mem_buf_desc *host_mem_descs; void **host_mem_desc_bufs; unsigned int nr_allocated_queues; unsigned int nr_write_queues; unsigned int nr_poll_queues; }; struct nvme_queue { struct nvme_dev *dev; spinlock_t sq_lock; void *sq_cmds; long: 64; long: 64; long: 64; long: 64; long: 64; spinlock_t cq_poll_lock; struct nvme_completion *cqes; dma_addr_t sq_dma_addr; dma_addr_t cq_dma_addr; u32 *q_db; u32 q_depth; u16 cq_vector; u16 sq_tail; u16 last_sq_tail; u16 cq_head; u16 qid; u8 cq_phase; u8 sqes; long unsigned int flags; u32 *dbbuf_sq_db; u32 *dbbuf_cq_db; u32 *dbbuf_sq_ei; u32 *dbbuf_cq_ei; struct completion delete_done; }; struct nvme_iod { struct nvme_request req; struct nvme_command cmd; struct nvme_queue *nvmeq; bool use_sgl; int aborted; int npages; int nents; dma_addr_t first_dma; unsigned int dma_len; dma_addr_t meta_dma; struct scatterlist *sg; }; struct pci_saved_state___2; enum { ATA_MAX_DEVICES = 2, ATA_MAX_PRD = 256, ATA_SECT_SIZE = 512, ATA_MAX_SECTORS_128 = 128, ATA_MAX_SECTORS = 256, ATA_MAX_SECTORS_1024 = 1024, ATA_MAX_SECTORS_LBA48 = 65535, ATA_MAX_SECTORS_TAPE = 65535, ATA_MAX_TRIM_RNUM = 64, ATA_ID_WORDS = 256, ATA_ID_CONFIG = 0, ATA_ID_CYLS = 1, ATA_ID_HEADS = 3, ATA_ID_SECTORS = 6, ATA_ID_SERNO = 10, ATA_ID_BUF_SIZE = 21, ATA_ID_FW_REV = 23, ATA_ID_PROD = 27, ATA_ID_MAX_MULTSECT = 47, ATA_ID_DWORD_IO = 48, ATA_ID_TRUSTED = 48, ATA_ID_CAPABILITY = 49, ATA_ID_OLD_PIO_MODES = 51, ATA_ID_OLD_DMA_MODES = 52, ATA_ID_FIELD_VALID = 53, ATA_ID_CUR_CYLS = 54, ATA_ID_CUR_HEADS = 55, ATA_ID_CUR_SECTORS = 56, ATA_ID_MULTSECT = 59, ATA_ID_LBA_CAPACITY = 60, ATA_ID_SWDMA_MODES = 62, ATA_ID_MWDMA_MODES = 63, ATA_ID_PIO_MODES = 64, ATA_ID_EIDE_DMA_MIN = 65, ATA_ID_EIDE_DMA_TIME = 66, ATA_ID_EIDE_PIO = 67, ATA_ID_EIDE_PIO_IORDY = 68, ATA_ID_ADDITIONAL_SUPP = 69, ATA_ID_QUEUE_DEPTH = 75, ATA_ID_SATA_CAPABILITY = 76, ATA_ID_SATA_CAPABILITY_2 = 77, ATA_ID_FEATURE_SUPP = 78, ATA_ID_MAJOR_VER = 80, ATA_ID_COMMAND_SET_1 = 82, ATA_ID_COMMAND_SET_2 = 83, ATA_ID_CFSSE = 84, ATA_ID_CFS_ENABLE_1 = 85, ATA_ID_CFS_ENABLE_2 = 86, ATA_ID_CSF_DEFAULT = 87, ATA_ID_UDMA_MODES = 88, ATA_ID_HW_CONFIG = 93, ATA_ID_SPG = 98, ATA_ID_LBA_CAPACITY_2 = 100, ATA_ID_SECTOR_SIZE = 106, ATA_ID_WWN = 108, ATA_ID_LOGICAL_SECTOR_SIZE = 117, ATA_ID_COMMAND_SET_3 = 119, ATA_ID_COMMAND_SET_4 = 120, ATA_ID_LAST_LUN = 126, ATA_ID_DLF = 128, ATA_ID_CSFO = 129, ATA_ID_CFA_POWER = 160, ATA_ID_CFA_KEY_MGMT = 162, ATA_ID_CFA_MODES = 163, ATA_ID_DATA_SET_MGMT = 169, ATA_ID_SCT_CMD_XPORT = 206, ATA_ID_ROT_SPEED = 217, ATA_ID_PIO4 = 2, ATA_ID_SERNO_LEN = 20, ATA_ID_FW_REV_LEN = 8, ATA_ID_PROD_LEN = 40, ATA_ID_WWN_LEN = 8, ATA_PCI_CTL_OFS = 2, ATA_PIO0 = 1, ATA_PIO1 = 3, ATA_PIO2 = 7, ATA_PIO3 = 15, ATA_PIO4 = 31, ATA_PIO5 = 63, ATA_PIO6 = 127, ATA_PIO4_ONLY = 16, ATA_SWDMA0 = 1, ATA_SWDMA1 = 3, ATA_SWDMA2 = 7, ATA_SWDMA2_ONLY = 4, ATA_MWDMA0 = 1, ATA_MWDMA1 = 3, ATA_MWDMA2 = 7, ATA_MWDMA3 = 15, ATA_MWDMA4 = 31, ATA_MWDMA12_ONLY = 6, ATA_MWDMA2_ONLY = 4, ATA_UDMA0 = 1, ATA_UDMA1 = 3, ATA_UDMA2 = 7, ATA_UDMA3 = 15, ATA_UDMA4 = 31, ATA_UDMA5 = 63, ATA_UDMA6 = 127, ATA_UDMA7 = 255, ATA_UDMA24_ONLY = 20, ATA_UDMA_MASK_40C = 7, ATA_PRD_SZ = 8, ATA_PRD_TBL_SZ = 2048, ATA_PRD_EOT = 2147483648, ATA_DMA_TABLE_OFS = 4, ATA_DMA_STATUS = 2, ATA_DMA_CMD = 0, ATA_DMA_WR = 8, ATA_DMA_START = 1, ATA_DMA_INTR = 4, ATA_DMA_ERR = 2, ATA_DMA_ACTIVE = 1, ATA_HOB = 128, ATA_NIEN = 2, ATA_LBA = 64, ATA_DEV1 = 16, ATA_DEVICE_OBS = 160, ATA_DEVCTL_OBS = 8, ATA_BUSY = 128, ATA_DRDY = 64, ATA_DF = 32, ATA_DSC = 16, ATA_DRQ = 8, ATA_CORR = 4, ATA_SENSE = 2, ATA_ERR = 1, ATA_SRST = 4, ATA_ICRC = 128, ATA_BBK = 128, ATA_UNC = 64, ATA_MC = 32, ATA_IDNF = 16, ATA_MCR = 8, ATA_ABORTED = 4, ATA_TRK0NF = 2, ATA_AMNF = 1, ATAPI_LFS = 240, ATAPI_EOM = 2, ATAPI_ILI = 1, ATAPI_IO = 2, ATAPI_COD = 1, ATA_REG_DATA = 0, ATA_REG_ERR = 1, ATA_REG_NSECT = 2, ATA_REG_LBAL = 3, ATA_REG_LBAM = 4, ATA_REG_LBAH = 5, ATA_REG_DEVICE = 6, ATA_REG_STATUS = 7, ATA_REG_FEATURE = 1, ATA_REG_CMD = 7, ATA_REG_BYTEL = 4, ATA_REG_BYTEH = 5, ATA_REG_DEVSEL = 6, ATA_REG_IRQ = 2, ATA_CMD_DEV_RESET = 8, ATA_CMD_CHK_POWER = 229, ATA_CMD_STANDBY = 226, ATA_CMD_IDLE = 227, ATA_CMD_EDD = 144, ATA_CMD_DOWNLOAD_MICRO = 146, ATA_CMD_DOWNLOAD_MICRO_DMA = 147, ATA_CMD_NOP = 0, ATA_CMD_FLUSH = 231, ATA_CMD_FLUSH_EXT = 234, ATA_CMD_ID_ATA = 236, ATA_CMD_ID_ATAPI = 161, ATA_CMD_SERVICE = 162, ATA_CMD_READ = 200, ATA_CMD_READ_EXT = 37, ATA_CMD_READ_QUEUED = 38, ATA_CMD_READ_STREAM_EXT = 43, ATA_CMD_READ_STREAM_DMA_EXT = 42, ATA_CMD_WRITE = 202, ATA_CMD_WRITE_EXT = 53, ATA_CMD_WRITE_QUEUED = 54, ATA_CMD_WRITE_STREAM_EXT = 59, ATA_CMD_WRITE_STREAM_DMA_EXT = 58, ATA_CMD_WRITE_FUA_EXT = 61, ATA_CMD_WRITE_QUEUED_FUA_EXT = 62, ATA_CMD_FPDMA_READ = 96, ATA_CMD_FPDMA_WRITE = 97, ATA_CMD_NCQ_NON_DATA = 99, ATA_CMD_FPDMA_SEND = 100, ATA_CMD_FPDMA_RECV = 101, ATA_CMD_PIO_READ = 32, ATA_CMD_PIO_READ_EXT = 36, ATA_CMD_PIO_WRITE = 48, ATA_CMD_PIO_WRITE_EXT = 52, ATA_CMD_READ_MULTI = 196, ATA_CMD_READ_MULTI_EXT = 41, ATA_CMD_WRITE_MULTI = 197, ATA_CMD_WRITE_MULTI_EXT = 57, ATA_CMD_WRITE_MULTI_FUA_EXT = 206, ATA_CMD_SET_FEATURES = 239, ATA_CMD_SET_MULTI = 198, ATA_CMD_PACKET = 160, ATA_CMD_VERIFY = 64, ATA_CMD_VERIFY_EXT = 66, ATA_CMD_WRITE_UNCORR_EXT = 69, ATA_CMD_STANDBYNOW1 = 224, ATA_CMD_IDLEIMMEDIATE = 225, ATA_CMD_SLEEP = 230, ATA_CMD_INIT_DEV_PARAMS = 145, ATA_CMD_READ_NATIVE_MAX = 248, ATA_CMD_READ_NATIVE_MAX_EXT = 39, ATA_CMD_SET_MAX = 249, ATA_CMD_SET_MAX_EXT = 55, ATA_CMD_READ_LOG_EXT = 47, ATA_CMD_WRITE_LOG_EXT = 63, ATA_CMD_READ_LOG_DMA_EXT = 71, ATA_CMD_WRITE_LOG_DMA_EXT = 87, ATA_CMD_TRUSTED_NONDATA = 91, ATA_CMD_TRUSTED_RCV = 92, ATA_CMD_TRUSTED_RCV_DMA = 93, ATA_CMD_TRUSTED_SND = 94, ATA_CMD_TRUSTED_SND_DMA = 95, ATA_CMD_PMP_READ = 228, ATA_CMD_PMP_READ_DMA = 233, ATA_CMD_PMP_WRITE = 232, ATA_CMD_PMP_WRITE_DMA = 235, ATA_CMD_CONF_OVERLAY = 177, ATA_CMD_SEC_SET_PASS = 241, ATA_CMD_SEC_UNLOCK = 242, ATA_CMD_SEC_ERASE_PREP = 243, ATA_CMD_SEC_ERASE_UNIT = 244, ATA_CMD_SEC_FREEZE_LOCK = 245, ATA_CMD_SEC_DISABLE_PASS = 246, ATA_CMD_CONFIG_STREAM = 81, ATA_CMD_SMART = 176, ATA_CMD_MEDIA_LOCK = 222, ATA_CMD_MEDIA_UNLOCK = 223, ATA_CMD_DSM = 6, ATA_CMD_CHK_MED_CRD_TYP = 209, ATA_CMD_CFA_REQ_EXT_ERR = 3, ATA_CMD_CFA_WRITE_NE = 56, ATA_CMD_CFA_TRANS_SECT = 135, ATA_CMD_CFA_ERASE = 192, ATA_CMD_CFA_WRITE_MULT_NE = 205, ATA_CMD_REQ_SENSE_DATA = 11, ATA_CMD_SANITIZE_DEVICE = 180, ATA_CMD_ZAC_MGMT_IN = 74, ATA_CMD_ZAC_MGMT_OUT = 159, ATA_CMD_RESTORE = 16, ATA_SUBCMD_FPDMA_RECV_RD_LOG_DMA_EXT = 1, ATA_SUBCMD_FPDMA_RECV_ZAC_MGMT_IN = 2, ATA_SUBCMD_FPDMA_SEND_DSM = 0, ATA_SUBCMD_FPDMA_SEND_WR_LOG_DMA_EXT = 2, ATA_SUBCMD_NCQ_NON_DATA_ABORT_QUEUE = 0, ATA_SUBCMD_NCQ_NON_DATA_SET_FEATURES = 5, ATA_SUBCMD_NCQ_NON_DATA_ZERO_EXT = 6, ATA_SUBCMD_NCQ_NON_DATA_ZAC_MGMT_OUT = 7, ATA_SUBCMD_ZAC_MGMT_IN_REPORT_ZONES = 0, ATA_SUBCMD_ZAC_MGMT_OUT_CLOSE_ZONE = 1, ATA_SUBCMD_ZAC_MGMT_OUT_FINISH_ZONE = 2, ATA_SUBCMD_ZAC_MGMT_OUT_OPEN_ZONE = 3, ATA_SUBCMD_ZAC_MGMT_OUT_RESET_WRITE_POINTER = 4, ATA_LOG_DIRECTORY = 0, ATA_LOG_SATA_NCQ = 16, ATA_LOG_NCQ_NON_DATA = 18, ATA_LOG_NCQ_SEND_RECV = 19, ATA_LOG_IDENTIFY_DEVICE = 48, ATA_LOG_SECURITY = 6, ATA_LOG_SATA_SETTINGS = 8, ATA_LOG_ZONED_INFORMATION = 9, ATA_LOG_DEVSLP_OFFSET = 48, ATA_LOG_DEVSLP_SIZE = 8, ATA_LOG_DEVSLP_MDAT = 0, ATA_LOG_DEVSLP_MDAT_MASK = 31, ATA_LOG_DEVSLP_DETO = 1, ATA_LOG_DEVSLP_VALID = 7, ATA_LOG_DEVSLP_VALID_MASK = 128, ATA_LOG_NCQ_PRIO_OFFSET = 9, ATA_LOG_NCQ_SEND_RECV_SUBCMDS_OFFSET = 0, ATA_LOG_NCQ_SEND_RECV_SUBCMDS_DSM = 1, ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET = 4, ATA_LOG_NCQ_SEND_RECV_DSM_TRIM = 1, ATA_LOG_NCQ_SEND_RECV_RD_LOG_OFFSET = 8, ATA_LOG_NCQ_SEND_RECV_RD_LOG_SUPPORTED = 1, ATA_LOG_NCQ_SEND_RECV_WR_LOG_OFFSET = 12, ATA_LOG_NCQ_SEND_RECV_WR_LOG_SUPPORTED = 1, ATA_LOG_NCQ_SEND_RECV_ZAC_MGMT_OFFSET = 16, ATA_LOG_NCQ_SEND_RECV_ZAC_MGMT_OUT_SUPPORTED = 1, ATA_LOG_NCQ_SEND_RECV_ZAC_MGMT_IN_SUPPORTED = 2, ATA_LOG_NCQ_SEND_RECV_SIZE = 20, ATA_LOG_NCQ_NON_DATA_SUBCMDS_OFFSET = 0, ATA_LOG_NCQ_NON_DATA_ABORT_OFFSET = 0, ATA_LOG_NCQ_NON_DATA_ABORT_NCQ = 1, ATA_LOG_NCQ_NON_DATA_ABORT_ALL = 2, ATA_LOG_NCQ_NON_DATA_ABORT_STREAMING = 4, ATA_LOG_NCQ_NON_DATA_ABORT_NON_STREAMING = 8, ATA_LOG_NCQ_NON_DATA_ABORT_SELECTED = 16, ATA_LOG_NCQ_NON_DATA_ZAC_MGMT_OFFSET = 28, ATA_LOG_NCQ_NON_DATA_ZAC_MGMT_OUT = 1, ATA_LOG_NCQ_NON_DATA_SIZE = 64, ATA_CMD_READ_LONG = 34, ATA_CMD_READ_LONG_ONCE = 35, ATA_CMD_WRITE_LONG = 50, ATA_CMD_WRITE_LONG_ONCE = 51, SETFEATURES_XFER = 3, XFER_UDMA_7 = 71, XFER_UDMA_6 = 70, XFER_UDMA_5 = 69, XFER_UDMA_4 = 68, XFER_UDMA_3 = 67, XFER_UDMA_2 = 66, XFER_UDMA_1 = 65, XFER_UDMA_0 = 64, XFER_MW_DMA_4 = 36, XFER_MW_DMA_3 = 35, XFER_MW_DMA_2 = 34, XFER_MW_DMA_1 = 33, XFER_MW_DMA_0 = 32, XFER_SW_DMA_2 = 18, XFER_SW_DMA_1 = 17, XFER_SW_DMA_0 = 16, XFER_PIO_6 = 14, XFER_PIO_5 = 13, XFER_PIO_4 = 12, XFER_PIO_3 = 11, XFER_PIO_2 = 10, XFER_PIO_1 = 9, XFER_PIO_0 = 8, XFER_PIO_SLOW = 0, SETFEATURES_WC_ON = 2, SETFEATURES_WC_OFF = 130, SETFEATURES_RA_ON = 170, SETFEATURES_RA_OFF = 85, SETFEATURES_AAM_ON = 66, SETFEATURES_AAM_OFF = 194, SETFEATURES_SPINUP = 7, SETFEATURES_SPINUP_TIMEOUT = 30000, SETFEATURES_SATA_ENABLE = 16, SETFEATURES_SATA_DISABLE = 144, SATA_FPDMA_OFFSET = 1, SATA_FPDMA_AA = 2, SATA_DIPM = 3, SATA_FPDMA_IN_ORDER = 4, SATA_AN = 5, SATA_SSP = 6, SATA_DEVSLP = 9, SETFEATURE_SENSE_DATA = 195, ATA_SET_MAX_ADDR = 0, ATA_SET_MAX_PASSWD = 1, ATA_SET_MAX_LOCK = 2, ATA_SET_MAX_UNLOCK = 3, ATA_SET_MAX_FREEZE_LOCK = 4, ATA_SET_MAX_PASSWD_DMA = 5, ATA_SET_MAX_UNLOCK_DMA = 6, ATA_DCO_RESTORE = 192, ATA_DCO_FREEZE_LOCK = 193, ATA_DCO_IDENTIFY = 194, ATA_DCO_SET = 195, ATA_SMART_ENABLE = 216, ATA_SMART_READ_VALUES = 208, ATA_SMART_READ_THRESHOLDS = 209, ATA_DSM_TRIM = 1, ATA_SMART_LBAM_PASS = 79, ATA_SMART_LBAH_PASS = 194, ATAPI_PKT_DMA = 1, ATAPI_DMADIR = 4, ATAPI_CDB_LEN = 16, SATA_PMP_MAX_PORTS = 15, SATA_PMP_CTRL_PORT = 15, SATA_PMP_GSCR_DWORDS = 128, SATA_PMP_GSCR_PROD_ID = 0, SATA_PMP_GSCR_REV = 1, SATA_PMP_GSCR_PORT_INFO = 2, SATA_PMP_GSCR_ERROR = 32, SATA_PMP_GSCR_ERROR_EN = 33, SATA_PMP_GSCR_FEAT = 64, SATA_PMP_GSCR_FEAT_EN = 96, SATA_PMP_PSCR_STATUS = 0, SATA_PMP_PSCR_ERROR = 1, SATA_PMP_PSCR_CONTROL = 2, SATA_PMP_FEAT_BIST = 1, SATA_PMP_FEAT_PMREQ = 2, SATA_PMP_FEAT_DYNSSC = 4, SATA_PMP_FEAT_NOTIFY = 8, ATA_CBL_NONE = 0, ATA_CBL_PATA40 = 1, ATA_CBL_PATA80 = 2, ATA_CBL_PATA40_SHORT = 3, ATA_CBL_PATA_UNK = 4, ATA_CBL_PATA_IGN = 5, ATA_CBL_SATA = 6, SCR_STATUS = 0, SCR_ERROR = 1, SCR_CONTROL = 2, SCR_ACTIVE = 3, SCR_NOTIFICATION = 4, SERR_DATA_RECOVERED = 1, SERR_COMM_RECOVERED = 2, SERR_DATA = 256, SERR_PERSISTENT = 512, SERR_PROTOCOL = 1024, SERR_INTERNAL = 2048, SERR_PHYRDY_CHG = 65536, SERR_PHY_INT_ERR = 131072, SERR_COMM_WAKE = 262144, SERR_10B_8B_ERR = 524288, SERR_DISPARITY = 1048576, SERR_CRC = 2097152, SERR_HANDSHAKE = 4194304, SERR_LINK_SEQ_ERR = 8388608, SERR_TRANS_ST_ERROR = 16777216, SERR_UNRECOG_FIS = 33554432, SERR_DEV_XCHG = 67108864, }; enum ata_prot_flags { ATA_PROT_FLAG_PIO = 1, ATA_PROT_FLAG_DMA = 2, ATA_PROT_FLAG_NCQ = 4, ATA_PROT_FLAG_ATAPI = 8, ATA_PROT_UNKNOWN = 255, ATA_PROT_NODATA = 0, ATA_PROT_PIO = 1, ATA_PROT_DMA = 2, ATA_PROT_NCQ_NODATA = 4, ATA_PROT_NCQ = 6, ATAPI_PROT_NODATA = 8, ATAPI_PROT_PIO = 9, ATAPI_PROT_DMA = 10, }; struct ata_bmdma_prd { __le32 addr; __le32 flags_len; }; enum { ATA_MSG_DRV = 1, ATA_MSG_INFO = 2, ATA_MSG_PROBE = 4, ATA_MSG_WARN = 8, ATA_MSG_MALLOC = 16, ATA_MSG_CTL = 32, ATA_MSG_INTR = 64, ATA_MSG_ERR = 128, }; enum { LIBATA_MAX_PRD = 128, LIBATA_DUMB_MAX_PRD = 64, ATA_DEF_QUEUE = 1, ATA_MAX_QUEUE = 32, ATA_TAG_INTERNAL = 32, ATA_SHORT_PAUSE = 16, ATAPI_MAX_DRAIN = 16384, ATA_ALL_DEVICES = 3, ATA_SHT_EMULATED = 1, ATA_SHT_THIS_ID = 4294967295, ATA_TFLAG_LBA48 = 1, ATA_TFLAG_ISADDR = 2, ATA_TFLAG_DEVICE = 4, ATA_TFLAG_WRITE = 8, ATA_TFLAG_LBA = 16, ATA_TFLAG_FUA = 32, ATA_TFLAG_POLLING = 64, ATA_DFLAG_LBA = 1, ATA_DFLAG_LBA48 = 2, ATA_DFLAG_CDB_INTR = 4, ATA_DFLAG_NCQ = 8, ATA_DFLAG_FLUSH_EXT = 16, ATA_DFLAG_ACPI_PENDING = 32, ATA_DFLAG_ACPI_FAILED = 64, ATA_DFLAG_AN = 128, ATA_DFLAG_TRUSTED = 256, ATA_DFLAG_DMADIR = 1024, ATA_DFLAG_CFG_MASK = 4095, ATA_DFLAG_PIO = 4096, ATA_DFLAG_NCQ_OFF = 8192, ATA_DFLAG_SLEEPING = 32768, ATA_DFLAG_DUBIOUS_XFER = 65536, ATA_DFLAG_NO_UNLOAD = 131072, ATA_DFLAG_UNLOCK_HPA = 262144, ATA_DFLAG_NCQ_SEND_RECV = 524288, ATA_DFLAG_NCQ_PRIO = 1048576, ATA_DFLAG_NCQ_PRIO_ENABLE = 2097152, ATA_DFLAG_INIT_MASK = 16777215, ATA_DFLAG_DETACH = 16777216, ATA_DFLAG_DETACHED = 33554432, ATA_DFLAG_DA = 67108864, ATA_DFLAG_DEVSLP = 134217728, ATA_DFLAG_ACPI_DISABLED = 268435456, ATA_DFLAG_D_SENSE = 536870912, ATA_DFLAG_ZAC = 1073741824, ATA_DEV_UNKNOWN = 0, ATA_DEV_ATA = 1, ATA_DEV_ATA_UNSUP = 2, ATA_DEV_ATAPI = 3, ATA_DEV_ATAPI_UNSUP = 4, ATA_DEV_PMP = 5, ATA_DEV_PMP_UNSUP = 6, ATA_DEV_SEMB = 7, ATA_DEV_SEMB_UNSUP = 8, ATA_DEV_ZAC = 9, ATA_DEV_ZAC_UNSUP = 10, ATA_DEV_NONE = 11, ATA_LFLAG_NO_HRST = 2, ATA_LFLAG_NO_SRST = 4, ATA_LFLAG_ASSUME_ATA = 8, ATA_LFLAG_ASSUME_SEMB = 16, ATA_LFLAG_ASSUME_CLASS = 24, ATA_LFLAG_NO_RETRY = 32, ATA_LFLAG_DISABLED = 64, ATA_LFLAG_SW_ACTIVITY = 128, ATA_LFLAG_NO_LPM = 256, ATA_LFLAG_RST_ONCE = 512, ATA_LFLAG_CHANGED = 1024, ATA_LFLAG_NO_DB_DELAY = 2048, ATA_FLAG_SLAVE_POSS = 1, ATA_FLAG_SATA = 2, ATA_FLAG_NO_LPM = 4, ATA_FLAG_NO_LOG_PAGE = 32, ATA_FLAG_NO_ATAPI = 64, ATA_FLAG_PIO_DMA = 128, ATA_FLAG_PIO_LBA48 = 256, ATA_FLAG_PIO_POLLING = 512, ATA_FLAG_NCQ = 1024, ATA_FLAG_NO_POWEROFF_SPINDOWN = 2048, ATA_FLAG_NO_HIBERNATE_SPINDOWN = 4096, ATA_FLAG_DEBUGMSG = 8192, ATA_FLAG_FPDMA_AA = 16384, ATA_FLAG_IGN_SIMPLEX = 32768, ATA_FLAG_NO_IORDY = 65536, ATA_FLAG_ACPI_SATA = 131072, ATA_FLAG_AN = 262144, ATA_FLAG_PMP = 524288, ATA_FLAG_FPDMA_AUX = 1048576, ATA_FLAG_EM = 2097152, ATA_FLAG_SW_ACTIVITY = 4194304, ATA_FLAG_NO_DIPM = 8388608, ATA_FLAG_SAS_HOST = 16777216, ATA_PFLAG_EH_PENDING = 1, ATA_PFLAG_EH_IN_PROGRESS = 2, ATA_PFLAG_FROZEN = 4, ATA_PFLAG_RECOVERED = 8, ATA_PFLAG_LOADING = 16, ATA_PFLAG_SCSI_HOTPLUG = 64, ATA_PFLAG_INITIALIZING = 128, ATA_PFLAG_RESETTING = 256, ATA_PFLAG_UNLOADING = 512, ATA_PFLAG_UNLOADED = 1024, ATA_PFLAG_SUSPENDED = 131072, ATA_PFLAG_PM_PENDING = 262144, ATA_PFLAG_INIT_GTM_VALID = 524288, ATA_PFLAG_PIO32 = 1048576, ATA_PFLAG_PIO32CHANGE = 2097152, ATA_PFLAG_EXTERNAL = 4194304, ATA_QCFLAG_ACTIVE = 1, ATA_QCFLAG_DMAMAP = 2, ATA_QCFLAG_IO = 8, ATA_QCFLAG_RESULT_TF = 16, ATA_QCFLAG_CLEAR_EXCL = 32, ATA_QCFLAG_QUIET = 64, ATA_QCFLAG_RETRY = 128, ATA_QCFLAG_FAILED = 65536, ATA_QCFLAG_SENSE_VALID = 131072, ATA_QCFLAG_EH_SCHEDULED = 262144, ATA_HOST_SIMPLEX = 1, ATA_HOST_STARTED = 2, ATA_HOST_PARALLEL_SCAN = 4, ATA_HOST_IGNORE_ATA = 8, ATA_TMOUT_BOOT = 30000, ATA_TMOUT_BOOT_QUICK = 7000, ATA_TMOUT_INTERNAL_QUICK = 5000, ATA_TMOUT_MAX_PARK = 30000, ATA_TMOUT_FF_WAIT_LONG = 2000, ATA_TMOUT_FF_WAIT = 800, ATA_WAIT_AFTER_RESET = 150, ATA_TMOUT_PMP_SRST_WAIT = 5000, ATA_TMOUT_SPURIOUS_PHY = 10000, BUS_UNKNOWN = 0, BUS_DMA = 1, BUS_IDLE = 2, BUS_NOINTR = 3, BUS_NODATA = 4, BUS_TIMER = 5, BUS_PIO = 6, BUS_EDD = 7, BUS_IDENTIFY = 8, BUS_PACKET = 9, PORT_UNKNOWN = 0, PORT_ENABLED = 1, PORT_DISABLED = 2, ATA_NR_PIO_MODES = 7, ATA_NR_MWDMA_MODES = 5, ATA_NR_UDMA_MODES = 8, ATA_SHIFT_PIO = 0, ATA_SHIFT_MWDMA = 7, ATA_SHIFT_UDMA = 12, ATA_SHIFT_PRIO = 6, ATA_PRIO_HIGH = 2, ATA_DMA_PAD_SZ = 4, ATA_ERING_SIZE = 32, ATA_DEFER_LINK = 1, ATA_DEFER_PORT = 2, ATA_EH_DESC_LEN = 80, ATA_EH_REVALIDATE = 1, ATA_EH_SOFTRESET = 2, ATA_EH_HARDRESET = 4, ATA_EH_RESET = 6, ATA_EH_ENABLE_LINK = 8, ATA_EH_PARK = 32, ATA_EH_PERDEV_MASK = 33, ATA_EH_ALL_ACTIONS = 15, ATA_EHI_HOTPLUGGED = 1, ATA_EHI_NO_AUTOPSY = 4, ATA_EHI_QUIET = 8, ATA_EHI_NO_RECOVERY = 16, ATA_EHI_DID_SOFTRESET = 65536, ATA_EHI_DID_HARDRESET = 131072, ATA_EHI_PRINTINFO = 262144, ATA_EHI_SETMODE = 524288, ATA_EHI_POST_SETMODE = 1048576, ATA_EHI_DID_RESET = 196608, ATA_EHI_TO_SLAVE_MASK = 12, ATA_EH_MAX_TRIES = 5, ATA_LINK_RESUME_TRIES = 5, ATA_PROBE_MAX_TRIES = 3, ATA_EH_DEV_TRIES = 3, ATA_EH_PMP_TRIES = 5, ATA_EH_PMP_LINK_TRIES = 3, SATA_PMP_RW_TIMEOUT = 3000, ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 6, ATA_HORKAGE_DIAGNOSTIC = 1, ATA_HORKAGE_NODMA = 2, ATA_HORKAGE_NONCQ = 4, ATA_HORKAGE_MAX_SEC_128 = 8, ATA_HORKAGE_BROKEN_HPA = 16, ATA_HORKAGE_DISABLE = 32, ATA_HORKAGE_HPA_SIZE = 64, ATA_HORKAGE_IVB = 256, ATA_HORKAGE_STUCK_ERR = 512, ATA_HORKAGE_BRIDGE_OK = 1024, ATA_HORKAGE_ATAPI_MOD16_DMA = 2048, ATA_HORKAGE_FIRMWARE_WARN = 4096, ATA_HORKAGE_1_5_GBPS = 8192, ATA_HORKAGE_NOSETXFER = 16384, ATA_HORKAGE_BROKEN_FPDMA_AA = 32768, ATA_HORKAGE_DUMP_ID = 65536, ATA_HORKAGE_MAX_SEC_LBA48 = 131072, ATA_HORKAGE_ATAPI_DMADIR = 262144, ATA_HORKAGE_NO_NCQ_TRIM = 524288, ATA_HORKAGE_NOLPM = 1048576, ATA_HORKAGE_WD_BROKEN_LPM = 2097152, ATA_HORKAGE_ZERO_AFTER_TRIM = 4194304, ATA_HORKAGE_NO_DMA_LOG = 8388608, ATA_HORKAGE_NOTRIM = 16777216, ATA_HORKAGE_MAX_SEC_1024 = 33554432, ATA_HORKAGE_MAX_TRIM_128M = 67108864, ATA_DMA_MASK_ATA = 1, ATA_DMA_MASK_ATAPI = 2, ATA_DMA_MASK_CFA = 4, ATAPI_READ = 0, ATAPI_WRITE = 1, ATAPI_READ_CD = 2, ATAPI_PASS_THRU = 3, ATAPI_MISC = 4, ATA_TIMING_SETUP = 1, ATA_TIMING_ACT8B = 2, ATA_TIMING_REC8B = 4, ATA_TIMING_CYC8B = 8, ATA_TIMING_8BIT = 14, ATA_TIMING_ACTIVE = 16, ATA_TIMING_RECOVER = 32, ATA_TIMING_DMACK_HOLD = 64, ATA_TIMING_CYCLE = 128, ATA_TIMING_UDMA = 256, ATA_TIMING_ALL = 511, ATA_ACPI_FILTER_SETXFER = 1, ATA_ACPI_FILTER_LOCK = 2, ATA_ACPI_FILTER_DIPM = 4, ATA_ACPI_FILTER_FPDMA_OFFSET = 8, ATA_ACPI_FILTER_FPDMA_AA = 16, ATA_ACPI_FILTER_DEFAULT = 7, }; enum ata_xfer_mask { ATA_MASK_PIO = 127, ATA_MASK_MWDMA = 3968, ATA_MASK_UDMA = 1044480, }; enum ata_completion_errors { AC_ERR_OK = 0, AC_ERR_DEV = 1, AC_ERR_HSM = 2, AC_ERR_TIMEOUT = 4, AC_ERR_MEDIA = 8, AC_ERR_ATA_BUS = 16, AC_ERR_HOST_BUS = 32, AC_ERR_SYSTEM = 64, AC_ERR_INVALID = 128, AC_ERR_OTHER = 256, AC_ERR_NODEV_HINT = 512, AC_ERR_NCQ = 1024, }; enum ata_lpm_policy { ATA_LPM_UNKNOWN = 0, ATA_LPM_MAX_POWER = 1, ATA_LPM_MED_POWER = 2, ATA_LPM_MED_POWER_WITH_DIPM = 3, ATA_LPM_MIN_POWER_WITH_PARTIAL = 4, ATA_LPM_MIN_POWER = 5, }; struct ata_queued_cmd; typedef void (*ata_qc_cb_t)(struct ata_queued_cmd *); struct ata_taskfile { long unsigned int flags; u8 protocol; u8 ctl; u8 hob_feature; u8 hob_nsect; u8 hob_lbal; u8 hob_lbam; u8 hob_lbah; u8 feature; u8 nsect; u8 lbal; u8 lbam; u8 lbah; u8 device; u8 command; u32 auxiliary; }; struct ata_port; struct ata_device; struct ata_queued_cmd { struct ata_port *ap; struct ata_device *dev; struct scsi_cmnd *scsicmd; void (*scsidone)(struct scsi_cmnd *); struct ata_taskfile tf; u8 cdb[16]; long unsigned int flags; unsigned int tag; unsigned int hw_tag; unsigned int n_elem; unsigned int orig_n_elem; int dma_dir; unsigned int sect_size; unsigned int nbytes; unsigned int extrabytes; unsigned int curbytes; struct scatterlist sgent; struct scatterlist *sg; struct scatterlist *cursg; unsigned int cursg_ofs; unsigned int err_mask; struct ata_taskfile result_tf; ata_qc_cb_t complete_fn; void *private_data; void *lldd_task; }; struct ata_link; typedef int (*ata_prereset_fn_t)(struct ata_link *, long unsigned int); struct ata_eh_info { struct ata_device *dev; u32 serror; unsigned int err_mask; unsigned int action; unsigned int dev_action[2]; unsigned int flags; unsigned int probe_mask; char desc[80]; int desc_len; }; struct ata_eh_context { struct ata_eh_info i; int tries[2]; int cmd_timeout_idx[12]; unsigned int classes[2]; unsigned int did_probe_mask; unsigned int unloaded_mask; unsigned int saved_ncq_enabled; u8 saved_xfer_mode[2]; long unsigned int last_reset; }; struct ata_ering_entry { unsigned int eflags; unsigned int err_mask; u64 timestamp; }; struct ata_ering { int cursor; struct ata_ering_entry ring[32]; }; struct ata_device { struct ata_link *link; unsigned int devno; unsigned int horkage; long unsigned int flags; struct scsi_device *sdev; void *private_data; union acpi_object *gtf_cache; unsigned int gtf_filter; void *zpodd; struct device tdev; u64 n_sectors; u64 n_native_sectors; unsigned int class; long unsigned int unpark_deadline; u8 pio_mode; u8 dma_mode; u8 xfer_mode; unsigned int xfer_shift; unsigned int multi_count; unsigned int max_sectors; unsigned int cdb_len; long unsigned int pio_mask; long unsigned int mwdma_mask; long unsigned int udma_mask; u16 cylinders; u16 heads; u16 sectors; union { u16 id[256]; u32 gscr[128]; }; u8 devslp_timing[8]; u8 ncq_send_recv_cmds[20]; u8 ncq_non_data_cmds[64]; u32 zac_zoned_cap; u32 zac_zones_optimal_open; u32 zac_zones_optimal_nonseq; u32 zac_zones_max_open; int spdn_cnt; struct ata_ering ering; long: 64; }; struct ata_link { struct ata_port *ap; int pmp; struct device tdev; unsigned int active_tag; u32 sactive; unsigned int flags; u32 saved_scontrol; unsigned int hw_sata_spd_limit; unsigned int sata_spd_limit; unsigned int sata_spd; enum ata_lpm_policy lpm_policy; struct ata_eh_info eh_info; struct ata_eh_context eh_context; long: 64; long: 64; long: 64; long: 64; struct ata_device device[2]; long unsigned int last_lpm_change; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; typedef int (*ata_reset_fn_t)(struct ata_link *, unsigned int *, long unsigned int); typedef void (*ata_postreset_fn_t)(struct ata_link *, unsigned int *); enum sw_activity { OFF = 0, BLINK_ON = 1, BLINK_OFF = 2, }; struct ata_ioports { void *cmd_addr; void *data_addr; void *error_addr; void *feature_addr; void *nsect_addr; void *lbal_addr; void *lbam_addr; void *lbah_addr; void *device_addr; void *status_addr; void *command_addr; void *altstatus_addr; void *ctl_addr; void *bmdma_addr; void *scr_addr; }; struct ata_port_operations; struct ata_host { spinlock_t lock; struct device *dev; void * const *iomap; unsigned int n_ports; unsigned int n_tags; void *private_data; struct ata_port_operations *ops; long unsigned int flags; struct kref kref; struct mutex eh_mutex; struct task_struct *eh_owner; struct ata_port *simplex_claimed; struct ata_port *ports[0]; }; struct ata_port_operations { int (*qc_defer)(struct ata_queued_cmd *); int (*check_atapi_dma)(struct ata_queued_cmd *); enum ata_completion_errors (*qc_prep)(struct ata_queued_cmd *); unsigned int (*qc_issue)(struct ata_queued_cmd *); bool (*qc_fill_rtf)(struct ata_queued_cmd *); int (*cable_detect)(struct ata_port *); long unsigned int (*mode_filter)(struct ata_device *, long unsigned int); void (*set_piomode)(struct ata_port *, struct ata_device *); void (*set_dmamode)(struct ata_port *, struct ata_device *); int (*set_mode)(struct ata_link *, struct ata_device **); unsigned int (*read_id)(struct ata_device *, struct ata_taskfile *, u16 *); void (*dev_config)(struct ata_device *); void (*freeze)(struct ata_port *); void (*thaw)(struct ata_port *); ata_prereset_fn_t prereset; ata_reset_fn_t softreset; ata_reset_fn_t hardreset; ata_postreset_fn_t postreset; ata_prereset_fn_t pmp_prereset; ata_reset_fn_t pmp_softreset; ata_reset_fn_t pmp_hardreset; ata_postreset_fn_t pmp_postreset; void (*error_handler)(struct ata_port *); void (*lost_interrupt)(struct ata_port *); void (*post_internal_cmd)(struct ata_queued_cmd *); void (*sched_eh)(struct ata_port *); void (*end_eh)(struct ata_port *); int (*scr_read)(struct ata_link *, unsigned int, u32 *); int (*scr_write)(struct ata_link *, unsigned int, u32); void (*pmp_attach)(struct ata_port *); void (*pmp_detach)(struct ata_port *); int (*set_lpm)(struct ata_link *, enum ata_lpm_policy, unsigned int); int (*port_suspend)(struct ata_port *, pm_message_t); int (*port_resume)(struct ata_port *); int (*port_start)(struct ata_port *); void (*port_stop)(struct ata_port *); void (*host_stop)(struct ata_host *); void (*sff_dev_select)(struct ata_port *, unsigned int); void (*sff_set_devctl)(struct ata_port *, u8); u8 (*sff_check_status)(struct ata_port *); u8 (*sff_check_altstatus)(struct ata_port *); void (*sff_tf_load)(struct ata_port *, const struct ata_taskfile *); void (*sff_tf_read)(struct ata_port *, struct ata_taskfile *); void (*sff_exec_command)(struct ata_port *, const struct ata_taskfile *); unsigned int (*sff_data_xfer)(struct ata_queued_cmd *, unsigned char *, unsigned int, int); void (*sff_irq_on)(struct ata_port *); bool (*sff_irq_check)(struct ata_port *); void (*sff_irq_clear)(struct ata_port *); void (*sff_drain_fifo)(struct ata_queued_cmd *); void (*bmdma_setup)(struct ata_queued_cmd *); void (*bmdma_start)(struct ata_queued_cmd *); void (*bmdma_stop)(struct ata_queued_cmd *); u8 (*bmdma_status)(struct ata_port *); ssize_t (*em_show)(struct ata_port *, char *); ssize_t (*em_store)(struct ata_port *, const char *, size_t); ssize_t (*sw_activity_show)(struct ata_device *, char *); ssize_t (*sw_activity_store)(struct ata_device *, enum sw_activity); ssize_t (*transmit_led_message)(struct ata_port *, u32, ssize_t); void (*phy_reset)(struct ata_port *); void (*eng_timeout)(struct ata_port *); const struct ata_port_operations *inherits; }; struct ata_port_stats { long unsigned int unhandled_irq; long unsigned int idle_irq; long unsigned int rw_reqbuf; }; struct ata_acpi_drive { u32 pio; u32 dma; }; struct ata_acpi_gtm { struct ata_acpi_drive drive[2]; u32 flags; }; struct ata_port { struct Scsi_Host *scsi_host; struct ata_port_operations *ops; spinlock_t *lock; long unsigned int flags; unsigned int pflags; unsigned int print_id; unsigned int local_port_no; unsigned int port_no; struct ata_ioports ioaddr; u8 ctl; u8 last_ctl; struct ata_link *sff_pio_task_link; struct delayed_work sff_pio_task; struct ata_bmdma_prd *bmdma_prd; dma_addr_t bmdma_prd_dma; unsigned int pio_mask; unsigned int mwdma_mask; unsigned int udma_mask; unsigned int cbl; struct ata_queued_cmd qcmd[33]; long unsigned int sas_tag_allocated; u64 qc_active; int nr_active_links; unsigned int sas_last_tag; long: 64; struct ata_link link; struct ata_link *slave_link; int nr_pmp_links; struct ata_link *pmp_link; struct ata_link *excl_link; struct ata_port_stats stats; struct ata_host *host; struct device *dev; struct device tdev; struct mutex scsi_scan_mutex; struct delayed_work hotplug_task; struct work_struct scsi_rescan_task; unsigned int hsm_task_state; u32 msg_enable; struct list_head eh_done_q; wait_queue_head_t eh_wait_q; int eh_tries; struct completion park_req_pending; pm_message_t pm_mesg; enum ata_lpm_policy target_lpm_policy; struct timer_list fastdrain_timer; long unsigned int fastdrain_cnt; async_cookie_t cookie; int em_message_type; void *private_data; struct ata_acpi_gtm __acpi_init_gtm; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; u8 sector_buf[512]; }; struct ata_port_info { long unsigned int flags; long unsigned int link_flags; long unsigned int pio_mask; long unsigned int mwdma_mask; long unsigned int udma_mask; struct ata_port_operations *port_ops; void *private_data; }; struct ata_timing { short unsigned int mode; short unsigned int setup; short unsigned int act8b; short unsigned int rec8b; short unsigned int cyc8b; short unsigned int active; short unsigned int recover; short unsigned int dmack_hold; short unsigned int cycle; short unsigned int udma; }; struct pci_bits { unsigned int reg; unsigned int width; long unsigned int mask; long unsigned int val; }; enum ata_link_iter_mode { ATA_LITER_EDGE = 0, ATA_LITER_HOST_FIRST = 1, ATA_LITER_PMP_FIRST = 2, }; enum ata_dev_iter_mode { ATA_DITER_ENABLED = 0, ATA_DITER_ENABLED_REVERSE = 1, ATA_DITER_ALL = 2, ATA_DITER_ALL_REVERSE = 3, }; struct trace_event_raw_ata_qc_issue { struct trace_entry ent; unsigned int ata_port; unsigned int ata_dev; unsigned int tag; unsigned char cmd; unsigned char dev; unsigned char lbal; unsigned char lbam; unsigned char lbah; unsigned char nsect; unsigned char feature; unsigned char hob_lbal; unsigned char hob_lbam; unsigned char hob_lbah; unsigned char hob_nsect; unsigned char hob_feature; unsigned char ctl; unsigned char proto; long unsigned int flags; char __data[0]; }; struct trace_event_raw_ata_qc_complete_template { struct trace_entry ent; unsigned int ata_port; unsigned int ata_dev; unsigned int tag; unsigned char status; unsigned char dev; unsigned char lbal; unsigned char lbam; unsigned char lbah; unsigned char nsect; unsigned char error; unsigned char hob_lbal; unsigned char hob_lbam; unsigned char hob_lbah; unsigned char hob_nsect; unsigned char hob_feature; unsigned char ctl; long unsigned int flags; char __data[0]; }; struct trace_event_raw_ata_eh_link_autopsy { struct trace_entry ent; unsigned int ata_port; unsigned int ata_dev; unsigned int eh_action; unsigned int eh_err_mask; char __data[0]; }; struct trace_event_raw_ata_eh_link_autopsy_qc { struct trace_entry ent; unsigned int ata_port; unsigned int ata_dev; unsigned int tag; unsigned int qc_flags; unsigned int eh_err_mask; char __data[0]; }; struct trace_event_data_offsets_ata_qc_issue {}; struct trace_event_data_offsets_ata_qc_complete_template {}; struct trace_event_data_offsets_ata_eh_link_autopsy {}; struct trace_event_data_offsets_ata_eh_link_autopsy_qc {}; typedef void (*btf_trace_ata_qc_issue)(void *, struct ata_queued_cmd *); typedef void (*btf_trace_ata_qc_complete_internal)(void *, struct ata_queued_cmd *); typedef void (*btf_trace_ata_qc_complete_failed)(void *, struct ata_queued_cmd *); typedef void (*btf_trace_ata_qc_complete_done)(void *, struct ata_queued_cmd *); typedef void (*btf_trace_ata_eh_link_autopsy)(void *, struct ata_device *, unsigned int, unsigned int); typedef void (*btf_trace_ata_eh_link_autopsy_qc)(void *, struct ata_queued_cmd *); enum { ATA_READID_POSTRESET = 1, ATA_DNXFER_PIO = 0, ATA_DNXFER_DMA = 1, ATA_DNXFER_40C = 2, ATA_DNXFER_FORCE_PIO = 3, ATA_DNXFER_FORCE_PIO0 = 4, ATA_DNXFER_QUIET = 2147483648, }; struct ata_force_param { const char *name; u8 cbl; u8 spd_limit; long unsigned int xfer_mask; unsigned int horkage_on; unsigned int horkage_off; u16 lflags; }; struct ata_force_ent { int port; int device; struct ata_force_param param; }; struct ata_xfer_ent { int shift; int bits; u8 base; }; struct ata_blacklist_entry { const char *model_num; const char *model_rev; long unsigned int horkage; }; typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *); struct ata_scsi_args { struct ata_device *dev; u16 *id; struct scsi_cmnd *cmd; }; enum ata_lpm_hints { ATA_LPM_EMPTY = 1, ATA_LPM_HIPM = 2, ATA_LPM_WAKE_ONLY = 4, }; enum { ATA_EH_SPDN_NCQ_OFF = 1, ATA_EH_SPDN_SPEED_DOWN = 2, ATA_EH_SPDN_FALLBACK_TO_PIO = 4, ATA_EH_SPDN_KEEP_ERRORS = 8, ATA_EFLAG_IS_IO = 1, ATA_EFLAG_DUBIOUS_XFER = 2, ATA_EFLAG_OLD_ER = 2147483648, ATA_ECAT_NONE = 0, ATA_ECAT_ATA_BUS = 1, ATA_ECAT_TOUT_HSM = 2, ATA_ECAT_UNK_DEV = 3, ATA_ECAT_DUBIOUS_NONE = 4, ATA_ECAT_DUBIOUS_ATA_BUS = 5, ATA_ECAT_DUBIOUS_TOUT_HSM = 6, ATA_ECAT_DUBIOUS_UNK_DEV = 7, ATA_ECAT_NR = 8, ATA_EH_CMD_DFL_TIMEOUT = 5000, ATA_EH_RESET_COOL_DOWN = 5000, ATA_EH_PRERESET_TIMEOUT = 10000, ATA_EH_FASTDRAIN_INTERVAL = 3000, ATA_EH_UA_TRIES = 5, ATA_EH_PROBE_TRIAL_INTERVAL = 60000, ATA_EH_PROBE_TRIALS = 2, }; struct ata_eh_cmd_timeout_ent { const u8 *commands; const long unsigned int *timeouts; }; struct speed_down_verdict_arg { u64 since; int xfer_ok; int nr_errors[8]; }; struct ata_internal { struct scsi_transport_template t; struct device_attribute private_port_attrs[3]; struct device_attribute private_link_attrs[3]; struct device_attribute private_dev_attrs[9]; struct transport_container link_attr_cont; struct transport_container dev_attr_cont; struct device_attribute *link_attrs[4]; struct device_attribute *port_attrs[4]; struct device_attribute *dev_attrs[10]; }; struct ata_show_ering_arg { char *buf; int written; }; enum hsm_task_states { HSM_ST_IDLE = 0, HSM_ST_FIRST = 1, HSM_ST = 2, HSM_ST_LAST = 3, HSM_ST_ERR = 4, }; struct ata_acpi_gtf { u8 tf[7]; }; struct ata_acpi_hotplug_context { struct acpi_hotplug_context hp; union { struct ata_port *ap; struct ata_device *dev; } data; }; struct rm_feature_desc { __be16 feature_code; __u8 curr: 1; __u8 persistent: 1; __u8 feature_version: 4; __u8 reserved1: 2; __u8 add_len; __u8 lock: 1; __u8 dbml: 1; __u8 pvnt_jmpr: 1; __u8 eject: 1; __u8 load: 1; __u8 mech_type: 3; __u8 reserved2; __u8 reserved3; __u8 reserved4; }; enum odd_mech_type { ODD_MECH_TYPE_SLOT = 0, ODD_MECH_TYPE_DRAWER = 1, ODD_MECH_TYPE_UNSUPPORTED = 2, }; struct zpodd { enum odd_mech_type mech_type; struct ata_device *dev; bool from_notify; bool zp_ready; long unsigned int last_ready; bool zp_sampled; bool powered_off; }; enum { AHCI_MAX_PORTS = 32, AHCI_MAX_CLKS = 5, AHCI_MAX_SG = 168, AHCI_DMA_BOUNDARY = 4294967295, AHCI_MAX_CMDS = 32, AHCI_CMD_SZ = 32, AHCI_CMD_SLOT_SZ = 1024, AHCI_RX_FIS_SZ = 256, AHCI_CMD_TBL_CDB = 64, AHCI_CMD_TBL_HDR_SZ = 128, AHCI_CMD_TBL_SZ = 2816, AHCI_CMD_TBL_AR_SZ = 90112, AHCI_PORT_PRIV_DMA_SZ = 91392, AHCI_PORT_PRIV_FBS_DMA_SZ = 95232, AHCI_IRQ_ON_SG = 2147483648, AHCI_CMD_ATAPI = 32, AHCI_CMD_WRITE = 64, AHCI_CMD_PREFETCH = 128, AHCI_CMD_RESET = 256, AHCI_CMD_CLR_BUSY = 1024, RX_FIS_PIO_SETUP = 32, RX_FIS_D2H_REG = 64, RX_FIS_SDB = 88, RX_FIS_UNK = 96, HOST_CAP = 0, HOST_CTL = 4, HOST_IRQ_STAT = 8, HOST_PORTS_IMPL = 12, HOST_VERSION = 16, HOST_EM_LOC = 28, HOST_EM_CTL = 32, HOST_CAP2 = 36, HOST_RESET = 1, HOST_IRQ_EN = 2, HOST_MRSM = 4, HOST_AHCI_EN = 2147483648, HOST_CAP_SXS = 32, HOST_CAP_EMS = 64, HOST_CAP_CCC = 128, HOST_CAP_PART = 8192, HOST_CAP_SSC = 16384, HOST_CAP_PIO_MULTI = 32768, HOST_CAP_FBS = 65536, HOST_CAP_PMP = 131072, HOST_CAP_ONLY = 262144, HOST_CAP_CLO = 16777216, HOST_CAP_LED = 33554432, HOST_CAP_ALPM = 67108864, HOST_CAP_SSS = 134217728, HOST_CAP_MPS = 268435456, HOST_CAP_SNTF = 536870912, HOST_CAP_NCQ = 1073741824, HOST_CAP_64 = 2147483648, HOST_CAP2_BOH = 1, HOST_CAP2_NVMHCI = 2, HOST_CAP2_APST = 4, HOST_CAP2_SDS = 8, HOST_CAP2_SADM = 16, HOST_CAP2_DESO = 32, PORT_LST_ADDR = 0, PORT_LST_ADDR_HI = 4, PORT_FIS_ADDR = 8, PORT_FIS_ADDR_HI = 12, PORT_IRQ_STAT = 16, PORT_IRQ_MASK = 20, PORT_CMD = 24, PORT_TFDATA = 32, PORT_SIG = 36, PORT_CMD_ISSUE = 56, PORT_SCR_STAT = 40, PORT_SCR_CTL = 44, PORT_SCR_ERR = 48, PORT_SCR_ACT = 52, PORT_SCR_NTF = 60, PORT_FBS = 64, PORT_DEVSLP = 68, PORT_IRQ_COLD_PRES = 2147483648, PORT_IRQ_TF_ERR = 1073741824, PORT_IRQ_HBUS_ERR = 536870912, PORT_IRQ_HBUS_DATA_ERR = 268435456, PORT_IRQ_IF_ERR = 134217728, PORT_IRQ_IF_NONFATAL = 67108864, PORT_IRQ_OVERFLOW = 16777216, PORT_IRQ_BAD_PMP = 8388608, PORT_IRQ_PHYRDY = 4194304, PORT_IRQ_DEV_ILCK = 128, PORT_IRQ_CONNECT = 64, PORT_IRQ_SG_DONE = 32, PORT_IRQ_UNK_FIS = 16, PORT_IRQ_SDB_FIS = 8, PORT_IRQ_DMAS_FIS = 4, PORT_IRQ_PIOS_FIS = 2, PORT_IRQ_D2H_REG_FIS = 1, PORT_IRQ_FREEZE = 683671632, PORT_IRQ_ERROR = 2025848912, DEF_PORT_IRQ = 2025848959, PORT_CMD_ASP = 134217728, PORT_CMD_ALPE = 67108864, PORT_CMD_ATAPI = 16777216, PORT_CMD_FBSCP = 4194304, PORT_CMD_ESP = 2097152, PORT_CMD_HPCP = 262144, PORT_CMD_PMP = 131072, PORT_CMD_LIST_ON = 32768, PORT_CMD_FIS_ON = 16384, PORT_CMD_FIS_RX = 16, PORT_CMD_CLO = 8, PORT_CMD_POWER_ON = 4, PORT_CMD_SPIN_UP = 2, PORT_CMD_START = 1, PORT_CMD_ICC_MASK = 4026531840, PORT_CMD_ICC_ACTIVE = 268435456, PORT_CMD_ICC_PARTIAL = 536870912, PORT_CMD_ICC_SLUMBER = 1610612736, PORT_FBS_DWE_OFFSET = 16, PORT_FBS_ADO_OFFSET = 12, PORT_FBS_DEV_OFFSET = 8, PORT_FBS_DEV_MASK = 3840, PORT_FBS_SDE = 4, PORT_FBS_DEC = 2, PORT_FBS_EN = 1, PORT_DEVSLP_DM_OFFSET = 25, PORT_DEVSLP_DM_MASK = 503316480, PORT_DEVSLP_DITO_OFFSET = 15, PORT_DEVSLP_MDAT_OFFSET = 10, PORT_DEVSLP_DETO_OFFSET = 2, PORT_DEVSLP_DSP = 2, PORT_DEVSLP_ADSE = 1, AHCI_HFLAG_NO_NCQ = 1, AHCI_HFLAG_IGN_IRQ_IF_ERR = 2, AHCI_HFLAG_IGN_SERR_INTERNAL = 4, AHCI_HFLAG_32BIT_ONLY = 8, AHCI_HFLAG_MV_PATA = 16, AHCI_HFLAG_NO_MSI = 32, AHCI_HFLAG_NO_PMP = 64, AHCI_HFLAG_SECT255 = 256, AHCI_HFLAG_YES_NCQ = 512, AHCI_HFLAG_NO_SUSPEND = 1024, AHCI_HFLAG_SRST_TOUT_IS_OFFLINE = 2048, AHCI_HFLAG_NO_SNTF = 4096, AHCI_HFLAG_NO_FPDMA_AA = 8192, AHCI_HFLAG_YES_FBS = 16384, AHCI_HFLAG_DELAY_ENGINE = 32768, AHCI_HFLAG_NO_DEVSLP = 131072, AHCI_HFLAG_NO_FBS = 262144, AHCI_HFLAG_MULTI_MSI = 1048576, AHCI_HFLAG_WAKE_BEFORE_STOP = 4194304, AHCI_HFLAG_YES_ALPM = 8388608, AHCI_HFLAG_NO_WRITE_TO_RO = 16777216, AHCI_HFLAG_IS_MOBILE = 33554432, AHCI_HFLAG_SUSPEND_PHYS = 67108864, AHCI_HFLAG_IGN_NOTSUPP_POWER_ON = 134217728, AHCI_HFLAG_NO_SXS = 268435456, AHCI_FLAG_COMMON = 393346, ICH_MAP = 144, PCS_6 = 146, PCS_7 = 148, EM_MAX_SLOTS = 8, EM_MAX_RETRY = 5, EM_CTL_RST = 512, EM_CTL_TM = 256, EM_CTL_MR = 1, EM_CTL_ALHD = 67108864, EM_CTL_XMT = 33554432, EM_CTL_SMB = 16777216, EM_CTL_SGPIO = 524288, EM_CTL_SES = 262144, EM_CTL_SAFTE = 131072, EM_CTL_LED = 65536, EM_MSG_TYPE_LED = 1, EM_MSG_TYPE_SAFTE = 2, EM_MSG_TYPE_SES2 = 4, EM_MSG_TYPE_SGPIO = 8, }; struct ahci_cmd_hdr { __le32 opts; __le32 status; __le32 tbl_addr; __le32 tbl_addr_hi; __le32 reserved[4]; }; struct ahci_em_priv { enum sw_activity blink_policy; struct timer_list timer; long unsigned int saved_activity; long unsigned int activity; long unsigned int led_state; struct ata_link *link; }; struct ahci_port_priv { struct ata_link *active_link; struct ahci_cmd_hdr *cmd_slot; dma_addr_t cmd_slot_dma; void *cmd_tbl; dma_addr_t cmd_tbl_dma; void *rx_fis; dma_addr_t rx_fis_dma; unsigned int ncq_saw_d2h: 1; unsigned int ncq_saw_dmas: 1; unsigned int ncq_saw_sdb: 1; spinlock_t lock; u32 intr_mask; bool fbs_supported; bool fbs_enabled; int fbs_last_dev; struct ahci_em_priv em_priv[8]; char *irq_desc; }; struct ahci_host_priv { unsigned int flags; u32 force_port_map; u32 mask_port_map; void *mmio; u32 cap; u32 cap2; u32 version; u32 port_map; u32 saved_cap; u32 saved_cap2; u32 saved_port_map; u32 em_loc; u32 em_buf_sz; u32 em_msg_type; u32 remapped_nvme; bool got_runtime_pm; struct clk *clks[5]; struct reset_control *rsts; struct regulator **target_pwrs; struct regulator *ahci_regulator; struct regulator *phy_regulator; struct phy **phys; unsigned int nports; void *plat_data; unsigned int irq; void (*start_engine)(struct ata_port *); int (*stop_engine)(struct ata_port *); irqreturn_t (*irq_handler)(int, void *); int (*get_irq_vector)(struct ata_host *, int); }; enum { AHCI_PCI_BAR_STA2X11 = 0, AHCI_PCI_BAR_CAVIUM = 0, AHCI_PCI_BAR_LOONGSON = 0, AHCI_PCI_BAR_ENMOTUS = 2, AHCI_PCI_BAR_CAVIUM_GEN5 = 4, AHCI_PCI_BAR_STANDARD = 5, }; enum board_ids { board_ahci = 0, board_ahci_ign_iferr = 1, board_ahci_mobile = 2, board_ahci_nomsi = 3, board_ahci_noncq = 4, board_ahci_nosntf = 5, board_ahci_yes_fbs = 6, board_ahci_al = 7, board_ahci_avn = 8, board_ahci_mcp65 = 9, board_ahci_mcp77 = 10, board_ahci_mcp89 = 11, board_ahci_mv = 12, board_ahci_sb600 = 13, board_ahci_sb700 = 14, board_ahci_vt8251 = 15, board_ahci_pcs7 = 16, board_ahci_mcp_linux = 9, board_ahci_mcp67 = 9, board_ahci_mcp73 = 9, board_ahci_mcp79 = 10, }; struct ahci_sg { __le32 addr; __le32 addr_hi; __le32 reserved; __le32 flags_size; }; typedef void (*spi_res_release_t)(struct spi_controller *, struct spi_message *, void *); struct spi_res { struct list_head entry; spi_res_release_t release; long long unsigned int data[0]; }; struct spi_replaced_transfers; typedef void (*spi_replaced_release_t)(struct spi_controller *, struct spi_message *, struct spi_replaced_transfers *); struct spi_replaced_transfers { spi_replaced_release_t release; void *extradata; struct list_head replaced_transfers; struct list_head *replaced_after; size_t inserted; struct spi_transfer inserted_transfers[0]; }; struct spi_board_info { char modalias[32]; const void *platform_data; const struct software_node *swnode; void *controller_data; int irq; u32 max_speed_hz; u16 bus_num; u16 chip_select; u32 mode; }; enum spi_mem_data_dir { SPI_MEM_NO_DATA = 0, SPI_MEM_DATA_IN = 1, SPI_MEM_DATA_OUT = 2, }; struct spi_mem_op { struct { u8 nbytes; u8 buswidth; u8 dtr: 1; u16 opcode; } cmd; struct { u8 nbytes; u8 buswidth; u8 dtr: 1; u64 val; } addr; struct { u8 nbytes; u8 buswidth; u8 dtr: 1; } dummy; struct { u8 buswidth; u8 dtr: 1; enum spi_mem_data_dir dir; unsigned int nbytes; union { void *in; const void *out; } buf; } data; }; struct spi_mem_dirmap_info { struct spi_mem_op op_tmpl; u64 offset; u64 length; }; struct spi_mem_dirmap_desc { struct spi_mem *mem; struct spi_mem_dirmap_info info; unsigned int nodirmap; void *priv; }; struct spi_mem { struct spi_device *spi; void *drvpriv; const char *name; }; struct trace_event_raw_spi_controller { struct trace_entry ent; int bus_num; char __data[0]; }; struct trace_event_raw_spi_setup { struct trace_entry ent; int bus_num; int chip_select; long unsigned int mode; unsigned int bits_per_word; unsigned int max_speed_hz; int status; char __data[0]; }; struct trace_event_raw_spi_set_cs { struct trace_entry ent; int bus_num; int chip_select; long unsigned int mode; bool enable; char __data[0]; }; struct trace_event_raw_spi_message { struct trace_entry ent; int bus_num; int chip_select; struct spi_message *msg; char __data[0]; }; struct trace_event_raw_spi_message_done { struct trace_entry ent; int bus_num; int chip_select; struct spi_message *msg; unsigned int frame; unsigned int actual; char __data[0]; }; struct trace_event_raw_spi_transfer { struct trace_entry ent; int bus_num; int chip_select; struct spi_transfer *xfer; int len; u32 __data_loc_rx_buf; u32 __data_loc_tx_buf; char __data[0]; }; struct trace_event_data_offsets_spi_controller {}; struct trace_event_data_offsets_spi_setup {}; struct trace_event_data_offsets_spi_set_cs {}; struct trace_event_data_offsets_spi_message {}; struct trace_event_data_offsets_spi_message_done {}; struct trace_event_data_offsets_spi_transfer { u32 rx_buf; u32 tx_buf; }; typedef void (*btf_trace_spi_controller_idle)(void *, struct spi_controller *); typedef void (*btf_trace_spi_controller_busy)(void *, struct spi_controller *); typedef void (*btf_trace_spi_setup)(void *, struct spi_device *, int); typedef void (*btf_trace_spi_set_cs)(void *, struct spi_device *, bool); typedef void (*btf_trace_spi_message_submit)(void *, struct spi_message *); typedef void (*btf_trace_spi_message_start)(void *, struct spi_message *); typedef void (*btf_trace_spi_message_done)(void *, struct spi_message *); typedef void (*btf_trace_spi_transfer_start)(void *, struct spi_message *, struct spi_transfer *); typedef void (*btf_trace_spi_transfer_stop)(void *, struct spi_message *, struct spi_transfer *); struct boardinfo { struct list_head list; struct spi_board_info board_info; }; struct acpi_spi_lookup { struct spi_controller *ctlr; u32 max_speed_hz; u32 mode; int irq; u8 bits_per_word; u8 chip_select; }; struct spi_mem_driver { struct spi_driver spidrv; int (*probe)(struct spi_mem *); int (*remove)(struct spi_mem *); void (*shutdown)(struct spi_mem *); }; struct devprobe2 { struct net_device * (*probe)(int); int status; }; enum { NETIF_F_SG_BIT = 0, NETIF_F_IP_CSUM_BIT = 1, __UNUSED_NETIF_F_1 = 2, NETIF_F_HW_CSUM_BIT = 3, NETIF_F_IPV6_CSUM_BIT = 4, NETIF_F_HIGHDMA_BIT = 5, NETIF_F_FRAGLIST_BIT = 6, NETIF_F_HW_VLAN_CTAG_TX_BIT = 7, NETIF_F_HW_VLAN_CTAG_RX_BIT = 8, NETIF_F_HW_VLAN_CTAG_FILTER_BIT = 9, NETIF_F_VLAN_CHALLENGED_BIT = 10, NETIF_F_GSO_BIT = 11, NETIF_F_LLTX_BIT = 12, NETIF_F_NETNS_LOCAL_BIT = 13, NETIF_F_GRO_BIT = 14, NETIF_F_LRO_BIT = 15, NETIF_F_GSO_SHIFT = 16, NETIF_F_TSO_BIT = 16, NETIF_F_GSO_ROBUST_BIT = 17, NETIF_F_TSO_ECN_BIT = 18, NETIF_F_TSO_MANGLEID_BIT = 19, NETIF_F_TSO6_BIT = 20, NETIF_F_FSO_BIT = 21, NETIF_F_GSO_GRE_BIT = 22, NETIF_F_GSO_GRE_CSUM_BIT = 23, NETIF_F_GSO_IPXIP4_BIT = 24, NETIF_F_GSO_IPXIP6_BIT = 25, NETIF_F_GSO_UDP_TUNNEL_BIT = 26, NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT = 27, NETIF_F_GSO_PARTIAL_BIT = 28, NETIF_F_GSO_TUNNEL_REMCSUM_BIT = 29, NETIF_F_GSO_SCTP_BIT = 30, NETIF_F_GSO_ESP_BIT = 31, NETIF_F_GSO_UDP_BIT = 32, NETIF_F_GSO_UDP_L4_BIT = 33, NETIF_F_GSO_FRAGLIST_BIT = 34, NETIF_F_GSO_LAST = 34, NETIF_F_FCOE_CRC_BIT = 35, NETIF_F_SCTP_CRC_BIT = 36, NETIF_F_FCOE_MTU_BIT = 37, NETIF_F_NTUPLE_BIT = 38, NETIF_F_RXHASH_BIT = 39, NETIF_F_RXCSUM_BIT = 40, NETIF_F_NOCACHE_COPY_BIT = 41, NETIF_F_LOOPBACK_BIT = 42, NETIF_F_RXFCS_BIT = 43, NETIF_F_RXALL_BIT = 44, NETIF_F_HW_VLAN_STAG_TX_BIT = 45, NETIF_F_HW_VLAN_STAG_RX_BIT = 46, NETIF_F_HW_VLAN_STAG_FILTER_BIT = 47, NETIF_F_HW_L2FW_DOFFLOAD_BIT = 48, NETIF_F_HW_TC_BIT = 49, NETIF_F_HW_ESP_BIT = 50, NETIF_F_HW_ESP_TX_CSUM_BIT = 51, NETIF_F_RX_UDP_TUNNEL_PORT_BIT = 52, NETIF_F_HW_TLS_TX_BIT = 53, NETIF_F_HW_TLS_RX_BIT = 54, NETIF_F_GRO_HW_BIT = 55, NETIF_F_HW_TLS_RECORD_BIT = 56, NETIF_F_GRO_FRAGLIST_BIT = 57, NETIF_F_HW_MACSEC_BIT = 58, NETIF_F_GRO_UDP_FWD_BIT = 59, NETIF_F_HW_HSR_TAG_INS_BIT = 60, NETIF_F_HW_HSR_TAG_RM_BIT = 61, NETIF_F_HW_HSR_FWD_BIT = 62, NETIF_F_HW_HSR_DUP_BIT = 63, NETDEV_FEATURE_COUNT = 64, }; typedef struct bio_vec skb_frag_t; struct skb_shared_hwtstamps { ktime_t hwtstamp; }; enum { SKBTX_HW_TSTAMP = 1, SKBTX_SW_TSTAMP = 2, SKBTX_IN_PROGRESS = 4, SKBTX_WIFI_STATUS = 16, SKBTX_SCHED_TSTAMP = 64, }; struct skb_shared_info { __u8 flags; __u8 meta_len; __u8 nr_frags; __u8 tx_flags; short unsigned int gso_size; short unsigned int gso_segs; struct sk_buff *frag_list; struct skb_shared_hwtstamps hwtstamps; unsigned int gso_type; u32 tskey; atomic_t dataref; void *destructor_arg; skb_frag_t frags[17]; }; enum netdev_priv_flags { IFF_802_1Q_VLAN = 1, IFF_EBRIDGE = 2, IFF_BONDING = 4, IFF_ISATAP = 8, IFF_WAN_HDLC = 16, IFF_XMIT_DST_RELEASE = 32, IFF_DONT_BRIDGE = 64, IFF_DISABLE_NETPOLL = 128, IFF_MACVLAN_PORT = 256, IFF_BRIDGE_PORT = 512, IFF_OVS_DATAPATH = 1024, IFF_TX_SKB_SHARING = 2048, IFF_UNICAST_FLT = 4096, IFF_TEAM_PORT = 8192, IFF_SUPP_NOFCS = 16384, IFF_LIVE_ADDR_CHANGE = 32768, IFF_MACVLAN = 65536, IFF_XMIT_DST_RELEASE_PERM = 131072, IFF_L3MDEV_MASTER = 262144, IFF_NO_QUEUE = 524288, IFF_OPENVSWITCH = 1048576, IFF_L3MDEV_SLAVE = 2097152, IFF_TEAM = 4194304, IFF_RXFH_CONFIGURED = 8388608, IFF_PHONY_HEADROOM = 16777216, IFF_MACSEC = 33554432, IFF_NO_RX_HANDLER = 67108864, IFF_FAILOVER = 134217728, IFF_FAILOVER_SLAVE = 268435456, IFF_L3MDEV_RX_HANDLER = 536870912, IFF_LIVE_RENAME_OK = 1073741824, IFF_TX_SKB_NO_LINEAR = 2147483648, }; struct mdio_board_info { const char *bus_id; char modalias[32]; int mdio_addr; const void *platform_data; }; struct mdio_board_entry { struct list_head list; struct mdio_board_info board_info; }; struct mii_timestamping_ctrl { struct mii_timestamper * (*probe_channel)(struct device *, unsigned int); void (*release_channel)(struct device *, struct mii_timestamper *); }; struct mii_timestamping_desc { struct list_head list; struct mii_timestamping_ctrl *ctrl; struct device *device; }; struct sfp; struct sfp_socket_ops; struct sfp_quirk; struct sfp_upstream_ops; struct sfp_bus { struct kref kref; struct list_head node; struct fwnode_handle *fwnode; const struct sfp_socket_ops *socket_ops; struct device *sfp_dev; struct sfp *sfp; const struct sfp_quirk *sfp_quirk; const struct sfp_upstream_ops *upstream_ops; void *upstream; struct phy_device *phydev; bool registered; bool started; }; struct sfp_eeprom_base { u8 phys_id; u8 phys_ext_id; u8 connector; u8 if_1x_copper_passive: 1; u8 if_1x_copper_active: 1; u8 if_1x_lx: 1; u8 if_1x_sx: 1; u8 e10g_base_sr: 1; u8 e10g_base_lr: 1; u8 e10g_base_lrm: 1; u8 e10g_base_er: 1; u8 sonet_oc3_short_reach: 1; u8 sonet_oc3_smf_intermediate_reach: 1; u8 sonet_oc3_smf_long_reach: 1; u8 unallocated_5_3: 1; u8 sonet_oc12_short_reach: 1; u8 sonet_oc12_smf_intermediate_reach: 1; u8 sonet_oc12_smf_long_reach: 1; u8 unallocated_5_7: 1; u8 sonet_oc48_short_reach: 1; u8 sonet_oc48_intermediate_reach: 1; u8 sonet_oc48_long_reach: 1; u8 sonet_reach_bit2: 1; u8 sonet_reach_bit1: 1; u8 sonet_oc192_short_reach: 1; u8 escon_smf_1310_laser: 1; u8 escon_mmf_1310_led: 1; u8 e1000_base_sx: 1; u8 e1000_base_lx: 1; u8 e1000_base_cx: 1; u8 e1000_base_t: 1; u8 e100_base_lx: 1; u8 e100_base_fx: 1; u8 e_base_bx10: 1; u8 e_base_px: 1; u8 fc_tech_electrical_inter_enclosure: 1; u8 fc_tech_lc: 1; u8 fc_tech_sa: 1; u8 fc_ll_m: 1; u8 fc_ll_l: 1; u8 fc_ll_i: 1; u8 fc_ll_s: 1; u8 fc_ll_v: 1; u8 unallocated_8_0: 1; u8 unallocated_8_1: 1; u8 sfp_ct_passive: 1; u8 sfp_ct_active: 1; u8 fc_tech_ll: 1; u8 fc_tech_sl: 1; u8 fc_tech_sn: 1; u8 fc_tech_electrical_intra_enclosure: 1; u8 fc_media_sm: 1; u8 unallocated_9_1: 1; u8 fc_media_m5: 1; u8 fc_media_m6: 1; u8 fc_media_tv: 1; u8 fc_media_mi: 1; u8 fc_media_tp: 1; u8 fc_media_tw: 1; u8 fc_speed_100: 1; u8 unallocated_10_1: 1; u8 fc_speed_200: 1; u8 fc_speed_3200: 1; u8 fc_speed_400: 1; u8 fc_speed_1600: 1; u8 fc_speed_800: 1; u8 fc_speed_1200: 1; u8 encoding; u8 br_nominal; u8 rate_id; u8 link_len[6]; char vendor_name[16]; u8 extended_cc; char vendor_oui[3]; char vendor_pn[16]; char vendor_rev[4]; union { __be16 optical_wavelength; __be16 cable_compliance; struct { u8 sff8431_app_e: 1; u8 fc_pi_4_app_h: 1; u8 reserved60_2: 6; u8 reserved61: 8; } passive; struct { u8 sff8431_app_e: 1; u8 fc_pi_4_app_h: 1; u8 sff8431_lim: 1; u8 fc_pi_4_lim: 1; u8 reserved60_4: 4; u8 reserved61: 8; } active; }; u8 reserved62; u8 cc_base; }; struct sfp_eeprom_ext { __be16 options; u8 br_max; u8 br_min; char vendor_sn[16]; char datecode[8]; u8 diagmon; u8 enhopts; u8 sff8472_compliance; u8 cc_ext; }; struct sfp_eeprom_id { struct sfp_eeprom_base base; struct sfp_eeprom_ext ext; }; enum { SFF8024_ID_UNK = 0, SFF8024_ID_SFF_8472 = 2, SFF8024_ID_SFP = 3, SFF8024_ID_DWDM_SFP = 11, SFF8024_ID_QSFP_8438 = 12, SFF8024_ID_QSFP_8436_8636 = 13, SFF8024_ID_QSFP28_8636 = 17, SFF8024_ENCODING_UNSPEC = 0, SFF8024_ENCODING_8B10B = 1, SFF8024_ENCODING_4B5B = 2, SFF8024_ENCODING_NRZ = 3, SFF8024_ENCODING_8472_MANCHESTER = 4, SFF8024_ENCODING_8472_SONET = 5, SFF8024_ENCODING_8472_64B66B = 6, SFF8024_ENCODING_8436_MANCHESTER = 6, SFF8024_ENCODING_8436_SONET = 4, SFF8024_ENCODING_8436_64B66B = 5, SFF8024_ENCODING_256B257B = 7, SFF8024_ENCODING_PAM4 = 8, SFF8024_CONNECTOR_UNSPEC = 0, SFF8024_CONNECTOR_SC = 1, SFF8024_CONNECTOR_FIBERJACK = 6, SFF8024_CONNECTOR_LC = 7, SFF8024_CONNECTOR_MT_RJ = 8, SFF8024_CONNECTOR_MU = 9, SFF8024_CONNECTOR_SG = 10, SFF8024_CONNECTOR_OPTICAL_PIGTAIL = 11, SFF8024_CONNECTOR_MPO_1X12 = 12, SFF8024_CONNECTOR_MPO_2X16 = 13, SFF8024_CONNECTOR_HSSDC_II = 32, SFF8024_CONNECTOR_COPPER_PIGTAIL = 33, SFF8024_CONNECTOR_RJ45 = 34, SFF8024_CONNECTOR_NOSEPARATE = 35, SFF8024_CONNECTOR_MXC_2X16 = 36, SFF8024_ECC_UNSPEC = 0, SFF8024_ECC_100G_25GAUI_C2M_AOC = 1, SFF8024_ECC_100GBASE_SR4_25GBASE_SR = 2, SFF8024_ECC_100GBASE_LR4_25GBASE_LR = 3, SFF8024_ECC_100GBASE_ER4_25GBASE_ER = 4, SFF8024_ECC_100GBASE_SR10 = 5, SFF8024_ECC_100GBASE_CR4 = 11, SFF8024_ECC_25GBASE_CR_S = 12, SFF8024_ECC_25GBASE_CR_N = 13, SFF8024_ECC_10GBASE_T_SFI = 22, SFF8024_ECC_10GBASE_T_SR = 28, SFF8024_ECC_5GBASE_T = 29, SFF8024_ECC_2_5GBASE_T = 30, }; struct sfp_upstream_ops { void (*attach)(void *, struct sfp_bus *); void (*detach)(void *, struct sfp_bus *); int (*module_insert)(void *, const struct sfp_eeprom_id *); void (*module_remove)(void *); int (*module_start)(void *); void (*module_stop)(void *); void (*link_down)(void *); void (*link_up)(void *); int (*connect_phy)(void *, struct phy_device *); void (*disconnect_phy)(void *); }; struct sfp_socket_ops { void (*attach)(struct sfp *); void (*detach)(struct sfp *); void (*start)(struct sfp *); void (*stop)(struct sfp *); int (*module_info)(struct sfp *, struct ethtool_modinfo *); int (*module_eeprom)(struct sfp *, struct ethtool_eeprom *, u8 *); int (*module_eeprom_by_page)(struct sfp *, const struct ethtool_module_eeprom *, struct netlink_ext_ack *); }; struct sfp_quirk { const char *vendor; const char *part; void (*modes)(const struct sfp_eeprom_id *, long unsigned int *); }; struct wl1251_platform_data { int power_gpio; int irq; bool use_eeprom; }; enum { IFLA_UNSPEC = 0, IFLA_ADDRESS = 1, IFLA_BROADCAST = 2, IFLA_IFNAME = 3, IFLA_MTU = 4, IFLA_LINK = 5, IFLA_QDISC = 6, IFLA_STATS = 7, IFLA_COST = 8, IFLA_PRIORITY = 9, IFLA_MASTER = 10, IFLA_WIRELESS = 11, IFLA_PROTINFO = 12, IFLA_TXQLEN = 13, IFLA_MAP = 14, IFLA_WEIGHT = 15, IFLA_OPERSTATE = 16, IFLA_LINKMODE = 17, IFLA_LINKINFO = 18, IFLA_NET_NS_PID = 19, IFLA_IFALIAS = 20, IFLA_NUM_VF = 21, IFLA_VFINFO_LIST = 22, IFLA_STATS64 = 23, IFLA_VF_PORTS = 24, IFLA_PORT_SELF = 25, IFLA_AF_SPEC = 26, IFLA_GROUP = 27, IFLA_NET_NS_FD = 28, IFLA_EXT_MASK = 29, IFLA_PROMISCUITY = 30, IFLA_NUM_TX_QUEUES = 31, IFLA_NUM_RX_QUEUES = 32, IFLA_CARRIER = 33, IFLA_PHYS_PORT_ID = 34, IFLA_CARRIER_CHANGES = 35, IFLA_PHYS_SWITCH_ID = 36, IFLA_LINK_NETNSID = 37, IFLA_PHYS_PORT_NAME = 38, IFLA_PROTO_DOWN = 39, IFLA_GSO_MAX_SEGS = 40, IFLA_GSO_MAX_SIZE = 41, IFLA_PAD = 42, IFLA_XDP = 43, IFLA_EVENT = 44, IFLA_NEW_NETNSID = 45, IFLA_IF_NETNSID = 46, IFLA_TARGET_NETNSID = 46, IFLA_CARRIER_UP_COUNT = 47, IFLA_CARRIER_DOWN_COUNT = 48, IFLA_NEW_IFINDEX = 49, IFLA_MIN_MTU = 50, IFLA_MAX_MTU = 51, IFLA_PROP_LIST = 52, IFLA_ALT_IFNAME = 53, IFLA_PERM_ADDRESS = 54, IFLA_PROTO_DOWN_REASON = 55, IFLA_PARENT_DEV_NAME = 56, IFLA_PARENT_DEV_BUS_NAME = 57, __IFLA_MAX = 58, }; enum { IFLA_INFO_UNSPEC = 0, IFLA_INFO_KIND = 1, IFLA_INFO_DATA = 2, IFLA_INFO_XSTATS = 3, IFLA_INFO_SLAVE_KIND = 4, IFLA_INFO_SLAVE_DATA = 5, __IFLA_INFO_MAX = 6, }; enum wwan_port_type { WWAN_PORT_AT = 0, WWAN_PORT_MBIM = 1, WWAN_PORT_QMI = 2, WWAN_PORT_QCDM = 3, WWAN_PORT_FIREHOSE = 4, __WWAN_PORT_MAX = 5, WWAN_PORT_MAX = 4, WWAN_PORT_UNKNOWN = 5, }; struct wwan_port; struct wwan_port_ops { int (*start)(struct wwan_port *); void (*stop)(struct wwan_port *); int (*tx)(struct wwan_port *, struct sk_buff *); int (*tx_blocking)(struct wwan_port *, struct sk_buff *); __poll_t (*tx_poll)(struct wwan_port *, struct file *, poll_table *); }; struct wwan_port { enum wwan_port_type type; unsigned int start_count; long unsigned int flags; const struct wwan_port_ops *ops; struct mutex ops_lock; struct device dev; struct sk_buff_head rxq; wait_queue_head_t waitqueue; struct mutex data_lock; union { struct { struct ktermios termios; int mdmbits; } at_data; }; }; struct wwan_netdev_priv { u32 link_id; int: 32; u8 drv_priv[0]; }; struct wwan_ops { unsigned int priv_size; void (*setup)(struct net_device *); int (*newlink)(void *, struct net_device *, u32, struct netlink_ext_ack *); void (*dellink)(void *, struct net_device *, struct list_head *); }; struct ifinfomsg { unsigned char ifi_family; unsigned char __ifi_pad; short unsigned int ifi_type; int ifi_index; unsigned int ifi_flags; unsigned int ifi_change; }; enum { IFLA_WWAN_UNSPEC = 0, IFLA_WWAN_LINK_ID = 1, __IFLA_WWAN_MAX = 2, }; struct wwan_device { unsigned int id; struct device dev; atomic_t port_id; const struct wwan_ops *ops; void *ops_ctxt; }; enum usb_otg_state { OTG_STATE_UNDEFINED = 0, OTG_STATE_B_IDLE = 1, OTG_STATE_B_SRP_INIT = 2, OTG_STATE_B_PERIPHERAL = 3, OTG_STATE_B_WAIT_ACON = 4, OTG_STATE_B_HOST = 5, OTG_STATE_A_IDLE = 6, OTG_STATE_A_WAIT_VRISE = 7, OTG_STATE_A_WAIT_BCON = 8, OTG_STATE_A_HOST = 9, OTG_STATE_A_SUSPEND = 10, OTG_STATE_A_PERIPHERAL = 11, OTG_STATE_A_WAIT_VFALL = 12, OTG_STATE_A_VBUS_ERR = 13, }; enum usb_dr_mode { USB_DR_MODE_UNKNOWN = 0, USB_DR_MODE_HOST = 1, USB_DR_MODE_PERIPHERAL = 2, USB_DR_MODE_OTG = 3, }; enum usb_led_event { USB_LED_EVENT_HOST = 0, USB_LED_EVENT_GADGET = 1, }; struct usb_device_id { __u16 match_flags; __u16 idVendor; __u16 idProduct; __u16 bcdDevice_lo; __u16 bcdDevice_hi; __u8 bDeviceClass; __u8 bDeviceSubClass; __u8 bDeviceProtocol; __u8 bInterfaceClass; __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; __u8 bInterfaceNumber; kernel_ulong_t driver_info; }; struct usb_descriptor_header { __u8 bLength; __u8 bDescriptorType; }; enum usb_port_connect_type { USB_PORT_CONNECT_TYPE_UNKNOWN = 0, USB_PORT_CONNECT_TYPE_HOT_PLUG = 1, USB_PORT_CONNECT_TYPE_HARD_WIRED = 2, USB_PORT_NOT_USED = 3, }; struct usb_dynids { spinlock_t lock; struct list_head list; }; struct usbdrv_wrap { struct device_driver driver; int for_devices; }; struct usb_driver { const char *name; int (*probe)(struct usb_interface *, const struct usb_device_id *); void (*disconnect)(struct usb_interface *); int (*unlocked_ioctl)(struct usb_interface *, unsigned int, void *); int (*suspend)(struct usb_interface *, pm_message_t); int (*resume)(struct usb_interface *); int (*reset_resume)(struct usb_interface *); int (*pre_reset)(struct usb_interface *); int (*post_reset)(struct usb_interface *); const struct usb_device_id *id_table; const struct attribute_group **dev_groups; struct usb_dynids dynids; struct usbdrv_wrap drvwrap; unsigned int no_dynamic_id: 1; unsigned int supports_autosuspend: 1; unsigned int disable_hub_initiated_lpm: 1; unsigned int soft_unbind: 1; }; struct usb_device_driver { const char *name; bool (*match)(struct usb_device *); int (*probe)(struct usb_device *); void (*disconnect)(struct usb_device *); int (*suspend)(struct usb_device *, pm_message_t); int (*resume)(struct usb_device *, pm_message_t); const struct attribute_group **dev_groups; struct usbdrv_wrap drvwrap; const struct usb_device_id *id_table; unsigned int supports_autosuspend: 1; unsigned int generic_subclass: 1; }; enum usb_phy_type { USB_PHY_TYPE_UNDEFINED = 0, USB_PHY_TYPE_USB2 = 1, USB_PHY_TYPE_USB3 = 2, }; enum usb_phy_events { USB_EVENT_NONE = 0, USB_EVENT_VBUS = 1, USB_EVENT_ID = 2, USB_EVENT_CHARGER = 3, USB_EVENT_ENUMERATED = 4, }; enum usb_charger_type { UNKNOWN_TYPE = 0, SDP_TYPE = 1, DCP_TYPE = 2, CDP_TYPE = 3, ACA_TYPE = 4, }; enum usb_charger_state { USB_CHARGER_DEFAULT = 0, USB_CHARGER_PRESENT = 1, USB_CHARGER_ABSENT = 2, }; struct usb_charger_current { unsigned int sdp_min; unsigned int sdp_max; unsigned int dcp_min; unsigned int dcp_max; unsigned int cdp_min; unsigned int cdp_max; unsigned int aca_min; unsigned int aca_max; }; struct usb_otg; struct usb_phy_io_ops; struct usb_phy { struct device *dev; const char *label; unsigned int flags; enum usb_phy_type type; enum usb_phy_events last_event; struct usb_otg *otg; struct device *io_dev; struct usb_phy_io_ops *io_ops; void *io_priv; struct extcon_dev *edev; struct extcon_dev *id_edev; struct notifier_block vbus_nb; struct notifier_block id_nb; struct notifier_block type_nb; enum usb_charger_type chg_type; enum usb_charger_state chg_state; struct usb_charger_current chg_cur; struct work_struct chg_work; struct atomic_notifier_head notifier; u16 port_status; u16 port_change; struct list_head head; int (*init)(struct usb_phy *); void (*shutdown)(struct usb_phy *); int (*set_vbus)(struct usb_phy *, int); int (*set_power)(struct usb_phy *, unsigned int); int (*set_suspend)(struct usb_phy *, int); int (*set_wakeup)(struct usb_phy *, bool); int (*notify_connect)(struct usb_phy *, enum usb_device_speed); int (*notify_disconnect)(struct usb_phy *, enum usb_device_speed); enum usb_charger_type (*charger_detect)(struct usb_phy *); }; struct usb_port_status { __le16 wPortStatus; __le16 wPortChange; __le32 dwExtPortStatus; }; struct usb_hub_status { __le16 wHubStatus; __le16 wHubChange; }; struct usb_hub_descriptor { __u8 bDescLength; __u8 bDescriptorType; __u8 bNbrPorts; __le16 wHubCharacteristics; __u8 bPwrOn2PwrGood; __u8 bHubContrCurrent; union { struct { __u8 DeviceRemovable[4]; __u8 PortPwrCtrlMask[4]; } hs; struct { __u8 bHubHdrDecLat; __le16 wHubDelay; __le16 DeviceRemovable; } __attribute__((packed)) ss; } u; } __attribute__((packed)); struct usb_phy_io_ops { int (*read)(struct usb_phy *, u32); int (*write)(struct usb_phy *, u32, u32); }; struct usb_gadget; struct usb_otg { u8 default_a; struct phy *phy; struct usb_phy *usb_phy; struct usb_bus *host; struct usb_gadget *gadget; enum usb_otg_state state; int (*set_host)(struct usb_otg *, struct usb_bus *); int (*set_peripheral)(struct usb_otg *, struct usb_gadget *); int (*set_vbus)(struct usb_otg *, bool); int (*start_srp)(struct usb_otg *); int (*start_hnp)(struct usb_otg *); }; typedef u32 usb_port_location_t; struct usb_port; struct usb_hub { struct device *intfdev; struct usb_device *hdev; struct kref kref; struct urb *urb; u8 (*buffer)[8]; union { struct usb_hub_status hub; struct usb_port_status port; } *status; struct mutex status_mutex; int error; int nerrors; long unsigned int event_bits[1]; long unsigned int change_bits[1]; long unsigned int removed_bits[1]; long unsigned int wakeup_bits[1]; long unsigned int power_bits[1]; long unsigned int child_usage_bits[1]; long unsigned int warm_reset_bits[1]; struct usb_hub_descriptor *descriptor; struct usb_tt tt; unsigned int mA_per_port; unsigned int wakeup_enabled_descendants; unsigned int limited_power: 1; unsigned int quiescing: 1; unsigned int disconnected: 1; unsigned int in_reset: 1; unsigned int quirk_disable_autosuspend: 1; unsigned int quirk_check_port_auto_suspend: 1; unsigned int has_indicators: 1; u8 indicator[31]; struct delayed_work leds; struct delayed_work init_work; struct work_struct events; spinlock_t irq_urb_lock; struct timer_list irq_urb_retry; struct usb_port **ports; }; struct usb_dev_state; struct usb_port { struct usb_device *child; struct device dev; struct usb_dev_state *port_owner; struct usb_port *peer; struct dev_pm_qos_request *req; enum usb_port_connect_type connect_type; usb_port_location_t location; struct mutex status_lock; u32 over_current_count; u8 portnum; u32 quirks; unsigned int is_superspeed: 1; unsigned int usb3_lpm_u1_permit: 1; unsigned int usb3_lpm_u2_permit: 1; }; struct find_interface_arg { int minor; struct device_driver *drv; }; struct each_dev_arg { void *data; int (*fn)(struct usb_device *, void *); }; struct each_hub_arg { void *data; int (*fn)(struct device *, void *); }; struct usb_qualifier_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 bcdUSB; __u8 bDeviceClass; __u8 bDeviceSubClass; __u8 bDeviceProtocol; __u8 bMaxPacketSize0; __u8 bNumConfigurations; __u8 bRESERVED; }; struct usb_set_sel_req { __u8 u1_sel; __u8 u1_pel; __le16 u2_sel; __le16 u2_pel; }; struct usbdevfs_hub_portinfo { char nports; char port[127]; }; enum hub_led_mode { INDICATOR_AUTO = 0, INDICATOR_CYCLE = 1, INDICATOR_GREEN_BLINK = 2, INDICATOR_GREEN_BLINK_OFF = 3, INDICATOR_AMBER_BLINK = 4, INDICATOR_AMBER_BLINK_OFF = 5, INDICATOR_ALT_BLINK = 6, INDICATOR_ALT_BLINK_OFF = 7, }; struct usb_tt_clear { struct list_head clear_list; unsigned int tt; u16 devinfo; struct usb_hcd *hcd; struct usb_host_endpoint *ep; }; enum hub_activation_type { HUB_INIT = 0, HUB_INIT2 = 1, HUB_INIT3 = 2, HUB_POST_RESET = 3, HUB_RESUME = 4, HUB_RESET_RESUME = 5, }; enum hub_quiescing_type { HUB_DISCONNECT = 0, HUB_PRE_RESET = 1, HUB_SUSPEND = 2, }; struct usb_ctrlrequest { __u8 bRequestType; __u8 bRequest; __le16 wValue; __le16 wIndex; __le16 wLength; }; struct usb_mon_operations { void (*urb_submit)(struct usb_bus *, struct urb *); void (*urb_submit_error)(struct usb_bus *, struct urb *, int); void (*urb_complete)(struct usb_bus *, struct urb *, int); }; struct usb_sg_request { int status; size_t bytes; spinlock_t lock; struct usb_device *dev; int pipe; int entries; struct urb **urbs; int count; struct completion complete; }; struct usb_cdc_header_desc { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __le16 bcdCDC; } __attribute__((packed)); struct usb_cdc_call_mgmt_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __u8 bmCapabilities; __u8 bDataInterface; }; struct usb_cdc_acm_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __u8 bmCapabilities; }; struct usb_cdc_union_desc { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __u8 bMasterInterface0; __u8 bSlaveInterface0; }; struct usb_cdc_country_functional_desc { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __u8 iCountryCodeRelDate; __le16 wCountyCode0; }; struct usb_cdc_network_terminal_desc { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __u8 bEntityId; __u8 iName; __u8 bChannelIndex; __u8 bPhysicalInterface; }; struct usb_cdc_ether_desc { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __u8 iMACAddress; __le32 bmEthernetStatistics; __le16 wMaxSegmentSize; __le16 wNumberMCFilters; __u8 bNumberPowerFilters; } __attribute__((packed)); struct usb_cdc_dmm_desc { __u8 bFunctionLength; __u8 bDescriptorType; __u8 bDescriptorSubtype; __u16 bcdVersion; __le16 wMaxCommand; } __attribute__((packed)); struct usb_cdc_mdlm_desc { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __le16 bcdVersion; __u8 bGUID[16]; } __attribute__((packed)); struct usb_cdc_mdlm_detail_desc { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __u8 bGuidDescriptorType; __u8 bDetailData[0]; }; struct usb_cdc_obex_desc { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __le16 bcdVersion; } __attribute__((packed)); struct usb_cdc_ncm_desc { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __le16 bcdNcmVersion; __u8 bmNetworkCapabilities; } __attribute__((packed)); struct usb_cdc_mbim_desc { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __le16 bcdMBIMVersion; __le16 wMaxControlMessage; __u8 bNumberFilters; __u8 bMaxFilterSize; __le16 wMaxSegmentSize; __u8 bmNetworkCapabilities; } __attribute__((packed)); struct usb_cdc_mbim_extended_desc { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubType; __le16 bcdMBIMExtendedVersion; __u8 bMaxOutstandingCommandMessages; __le16 wMTU; } __attribute__((packed)); struct usb_cdc_parsed_header { struct usb_cdc_union_desc *usb_cdc_union_desc; struct usb_cdc_header_desc *usb_cdc_header_desc; struct usb_cdc_call_mgmt_descriptor *usb_cdc_call_mgmt_descriptor; struct usb_cdc_acm_descriptor *usb_cdc_acm_descriptor; struct usb_cdc_country_functional_desc *usb_cdc_country_functional_desc; struct usb_cdc_network_terminal_desc *usb_cdc_network_terminal_desc; struct usb_cdc_ether_desc *usb_cdc_ether_desc; struct usb_cdc_dmm_desc *usb_cdc_dmm_desc; struct usb_cdc_mdlm_desc *usb_cdc_mdlm_desc; struct usb_cdc_mdlm_detail_desc *usb_cdc_mdlm_detail_desc; struct usb_cdc_obex_desc *usb_cdc_obex_desc; struct usb_cdc_ncm_desc *usb_cdc_ncm_desc; struct usb_cdc_mbim_desc *usb_cdc_mbim_desc; struct usb_cdc_mbim_extended_desc *usb_cdc_mbim_extended_desc; bool phonet_magic_present; }; struct api_context { struct completion done; int status; }; struct set_config_request { struct usb_device *udev; int config; struct work_struct work; struct list_head node; }; struct usb_dynid { struct list_head node; struct usb_device_id id; }; struct usb_dev_cap_header { __u8 bLength; __u8 bDescriptorType; __u8 bDevCapabilityType; }; struct usb_class_driver { char *name; char * (*devnode)(struct device *, umode_t *); const struct file_operations *fops; int minor_base; }; struct usb_class { struct kref kref; struct class *class; }; struct ep_device { struct usb_endpoint_descriptor *desc; struct usb_device *udev; struct device dev; }; struct usbdevfs_ctrltransfer { __u8 bRequestType; __u8 bRequest; __u16 wValue; __u16 wIndex; __u16 wLength; __u32 timeout; void *data; }; struct usbdevfs_bulktransfer { unsigned int ep; unsigned int len; unsigned int timeout; void *data; }; struct usbdevfs_setinterface { unsigned int interface; unsigned int altsetting; }; struct usbdevfs_disconnectsignal { unsigned int signr; void *context; }; struct usbdevfs_getdriver { unsigned int interface; char driver[256]; }; struct usbdevfs_connectinfo { unsigned int devnum; unsigned char slow; }; struct usbdevfs_conninfo_ex { __u32 size; __u32 busnum; __u32 devnum; __u32 speed; __u8 num_ports; __u8 ports[7]; }; struct usbdevfs_iso_packet_desc { unsigned int length; unsigned int actual_length; unsigned int status; }; struct usbdevfs_urb { unsigned char type; unsigned char endpoint; int status; unsigned int flags; void *buffer; int buffer_length; int actual_length; int start_frame; union { int number_of_packets; unsigned int stream_id; }; int error_count; unsigned int signr; void *usercontext; struct usbdevfs_iso_packet_desc iso_frame_desc[0]; }; struct usbdevfs_ioctl { int ifno; int ioctl_code; void *data; }; struct usbdevfs_disconnect_claim { unsigned int interface; unsigned int flags; char driver[256]; }; struct usbdevfs_streams { unsigned int num_streams; unsigned int num_eps; unsigned char eps[0]; }; struct usbdevfs_ctrltransfer32 { u8 bRequestType; u8 bRequest; u16 wValue; u16 wIndex; u16 wLength; u32 timeout; compat_caddr_t data; }; struct usbdevfs_bulktransfer32 { compat_uint_t ep; compat_uint_t len; compat_uint_t timeout; compat_caddr_t data; }; struct usbdevfs_disconnectsignal32 { compat_int_t signr; compat_caddr_t context; }; struct usbdevfs_urb32 { unsigned char type; unsigned char endpoint; compat_int_t status; compat_uint_t flags; compat_caddr_t buffer; compat_int_t buffer_length; compat_int_t actual_length; compat_int_t start_frame; compat_int_t number_of_packets; compat_int_t error_count; compat_uint_t signr; compat_caddr_t usercontext; struct usbdevfs_iso_packet_desc iso_frame_desc[0]; }; struct usbdevfs_ioctl32 { s32 ifno; s32 ioctl_code; compat_caddr_t data; }; struct usb_dev_state___2 { struct list_head list; struct usb_device *dev; struct file *file; spinlock_t lock; struct list_head async_pending; struct list_head async_completed; struct list_head memory_list; wait_queue_head_t wait; wait_queue_head_t wait_for_resume; unsigned int discsignr; struct pid *disc_pid; const struct cred *cred; sigval_t disccontext; long unsigned int ifclaimed; u32 disabled_bulk_eps; long unsigned int interface_allowed_mask; int not_yet_resumed; bool suspend_allowed; bool privileges_dropped; }; struct usb_memory { struct list_head memlist; int vma_use_count; int urb_use_count; u32 size; void *mem; dma_addr_t dma_handle; long unsigned int vm_start; struct usb_dev_state___2 *ps; }; struct async { struct list_head asynclist; struct usb_dev_state___2 *ps; struct pid *pid; const struct cred *cred; unsigned int signr; unsigned int ifnum; void *userbuffer; void *userurb; sigval_t userurb_sigval; struct urb *urb; struct usb_memory *usbm; unsigned int mem_usage; int status; u8 bulk_addr; u8 bulk_status; }; enum snoop_when { SUBMIT = 0, COMPLETE___2 = 1, }; struct quirk_entry { u16 vid; u16 pid; u32 flags; }; struct class_info { int class; char *class_name; }; struct usb_phy_roothub___2 { struct phy *phy; struct list_head list; }; typedef void (*companion_fn)(struct pci_dev *, struct usb_hcd *, struct pci_dev *, struct usb_hcd *); struct phy_devm { struct usb_phy *phy; struct notifier_block *nb; }; enum amd_chipset_gen { NOT_AMD_CHIPSET = 0, AMD_CHIPSET_SB600 = 1, AMD_CHIPSET_SB700 = 2, AMD_CHIPSET_SB800 = 3, AMD_CHIPSET_HUDSON2 = 4, AMD_CHIPSET_BOLTON = 5, AMD_CHIPSET_YANGTZE = 6, AMD_CHIPSET_TAISHAN = 7, AMD_CHIPSET_UNKNOWN = 8, }; struct amd_chipset_type { enum amd_chipset_gen gen; u8 rev; }; struct amd_chipset_info { struct pci_dev *nb_dev; struct pci_dev *smbus_dev; int nb_type; struct amd_chipset_type sb_type; int isoc_reqs; int probe_count; bool need_pll_quirk; }; struct ehci_stats { long unsigned int normal; long unsigned int error; long unsigned int iaa; long unsigned int lost_iaa; long unsigned int complete; long unsigned int unlink; }; struct ehci_per_sched { struct usb_device *udev; struct usb_host_endpoint *ep; struct list_head ps_list; u16 tt_usecs; u16 cs_mask; u16 period; u16 phase; u8 bw_phase; u8 phase_uf; u8 usecs; u8 c_usecs; u8 bw_uperiod; u8 bw_period; }; enum ehci_rh_state { EHCI_RH_HALTED = 0, EHCI_RH_SUSPENDED = 1, EHCI_RH_RUNNING = 2, EHCI_RH_STOPPING = 3, }; enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_ASS = 0, EHCI_HRTIMER_POLL_PSS = 1, EHCI_HRTIMER_POLL_DEAD = 2, EHCI_HRTIMER_UNLINK_INTR = 3, EHCI_HRTIMER_FREE_ITDS = 4, EHCI_HRTIMER_ACTIVE_UNLINK = 5, EHCI_HRTIMER_START_UNLINK_INTR = 6, EHCI_HRTIMER_ASYNC_UNLINKS = 7, EHCI_HRTIMER_IAA_WATCHDOG = 8, EHCI_HRTIMER_DISABLE_PERIODIC = 9, EHCI_HRTIMER_DISABLE_ASYNC = 10, EHCI_HRTIMER_IO_WATCHDOG = 11, EHCI_HRTIMER_NUM_EVENTS = 12, }; struct ehci_caps; struct ehci_regs; struct ehci_dbg_port; struct ehci_qh; union ehci_shadow; struct ehci_itd; struct ehci_sitd; struct ehci_hcd { enum ehci_hrtimer_event next_hrtimer_event; unsigned int enabled_hrtimer_events; ktime_t hr_timeouts[12]; struct hrtimer hrtimer; int PSS_poll_count; int ASS_poll_count; int died_poll_count; struct ehci_caps *caps; struct ehci_regs *regs; struct ehci_dbg_port *debug; __u32 hcs_params; spinlock_t lock; enum ehci_rh_state rh_state; bool scanning: 1; bool need_rescan: 1; bool intr_unlinking: 1; bool iaa_in_progress: 1; bool async_unlinking: 1; bool shutdown: 1; struct ehci_qh *qh_scan_next; struct ehci_qh *async; struct ehci_qh *dummy; struct list_head async_unlink; struct list_head async_idle; unsigned int async_unlink_cycle; unsigned int async_count; __le32 old_current; __le32 old_token; unsigned int periodic_size; __le32 *periodic; dma_addr_t periodic_dma; struct list_head intr_qh_list; unsigned int i_thresh; union ehci_shadow *pshadow; struct list_head intr_unlink_wait; struct list_head intr_unlink; unsigned int intr_unlink_wait_cycle; unsigned int intr_unlink_cycle; unsigned int now_frame; unsigned int last_iso_frame; unsigned int intr_count; unsigned int isoc_count; unsigned int periodic_count; unsigned int uframe_periodic_max; struct list_head cached_itd_list; struct ehci_itd *last_itd_to_free; struct list_head cached_sitd_list; struct ehci_sitd *last_sitd_to_free; long unsigned int reset_done[15]; long unsigned int bus_suspended; long unsigned int companion_ports; long unsigned int owned_ports; long unsigned int port_c_suspend; long unsigned int suspended_ports; long unsigned int resuming_ports; struct dma_pool___2 *qh_pool; struct dma_pool___2 *qtd_pool; struct dma_pool___2 *itd_pool; struct dma_pool___2 *sitd_pool; unsigned int random_frame; long unsigned int next_statechange; ktime_t last_periodic_enable; u32 command; unsigned int no_selective_suspend: 1; unsigned int has_fsl_port_bug: 1; unsigned int has_fsl_hs_errata: 1; unsigned int has_fsl_susp_errata: 1; unsigned int big_endian_mmio: 1; unsigned int big_endian_desc: 1; unsigned int big_endian_capbase: 1; unsigned int has_amcc_usb23: 1; unsigned int need_io_watchdog: 1; unsigned int amd_pll_fix: 1; unsigned int use_dummy_qh: 1; unsigned int has_synopsys_hc_bug: 1; unsigned int frame_index_bug: 1; unsigned int need_oc_pp_cycle: 1; unsigned int imx28_write_fix: 1; unsigned int spurious_oc: 1; __le32 *ohci_hcctrl_reg; unsigned int has_hostpc: 1; unsigned int has_tdi_phy_lpm: 1; unsigned int has_ppcd: 1; u8 sbrn; struct ehci_stats stats; struct dentry *debug_dir; u8 bandwidth[64]; u8 tt_budget[64]; struct list_head tt_list; long unsigned int priv[0]; }; struct ehci_caps { u32 hc_capbase; u32 hcs_params; u32 hcc_params; u8 portroute[8]; }; struct ehci_regs { u32 command; u32 status; u32 intr_enable; u32 frame_index; u32 segment; u32 frame_list; u32 async_next; u32 reserved1[2]; u32 txfill_tuning; u32 reserved2[6]; u32 configured_flag; u32 port_status[0]; u32 reserved3[9]; u32 usbmode; u32 reserved4[6]; u32 hostpc[0]; u32 reserved5[17]; u32 usbmode_ex; }; struct ehci_dbg_port { u32 control; u32 pids; u32 data03; u32 data47; u32 address; }; struct ehci_fstn; union ehci_shadow { struct ehci_qh *qh; struct ehci_itd *itd; struct ehci_sitd *sitd; struct ehci_fstn *fstn; __le32 *hw_next; void *ptr; }; struct ehci_qh_hw; struct ehci_qtd; struct ehci_qh { struct ehci_qh_hw *hw; dma_addr_t qh_dma; union ehci_shadow qh_next; struct list_head qtd_list; struct list_head intr_node; struct ehci_qtd *dummy; struct list_head unlink_node; struct ehci_per_sched ps; unsigned int unlink_cycle; u8 qh_state; u8 xacterrs; u8 unlink_reason; u8 gap_uf; unsigned int is_out: 1; unsigned int clearing_tt: 1; unsigned int dequeue_during_giveback: 1; unsigned int should_be_inactive: 1; }; struct ehci_iso_stream; struct ehci_itd { __le32 hw_next; __le32 hw_transaction[8]; __le32 hw_bufp[7]; __le32 hw_bufp_hi[7]; dma_addr_t itd_dma; union ehci_shadow itd_next; struct urb *urb; struct ehci_iso_stream *stream; struct list_head itd_list; unsigned int frame; unsigned int pg; unsigned int index[8]; long: 64; }; struct ehci_sitd { __le32 hw_next; __le32 hw_fullspeed_ep; __le32 hw_uframe; __le32 hw_results; __le32 hw_buf[2]; __le32 hw_backpointer; __le32 hw_buf_hi[2]; dma_addr_t sitd_dma; union ehci_shadow sitd_next; struct urb *urb; struct ehci_iso_stream *stream; struct list_head sitd_list; unsigned int frame; unsigned int index; }; struct ehci_qtd { __le32 hw_next; __le32 hw_alt_next; __le32 hw_token; __le32 hw_buf[5]; __le32 hw_buf_hi[5]; dma_addr_t qtd_dma; struct list_head qtd_list; struct urb *urb; size_t length; }; struct ehci_fstn { __le32 hw_next; __le32 hw_prev; dma_addr_t fstn_dma; union ehci_shadow fstn_next; long: 64; }; struct ehci_qh_hw { __le32 hw_next; __le32 hw_info1; __le32 hw_info2; __le32 hw_current; __le32 hw_qtd_next; __le32 hw_alt_next; __le32 hw_token; __le32 hw_buf[5]; __le32 hw_buf_hi[5]; long: 32; long: 64; long: 64; long: 64; }; struct ehci_iso_packet { u64 bufp; __le32 transaction; u8 cross; u32 buf1; }; struct ehci_iso_sched { struct list_head td_list; unsigned int span; unsigned int first_packet; struct ehci_iso_packet packet[0]; }; struct ehci_iso_stream { struct ehci_qh_hw *hw; u8 bEndpointAddress; u8 highspeed; struct list_head td_list; struct list_head free_list; struct ehci_per_sched ps; unsigned int next_uframe; __le32 splits; u16 uperiod; u16 maxp; unsigned int bandwidth; __le32 buf0; __le32 buf1; __le32 buf2; __le32 address; }; struct ehci_tt { u16 bandwidth[8]; struct list_head tt_list; struct list_head ps_list; struct usb_tt *usb_tt; int tt_port; }; struct ehci_driver_overrides { size_t extra_priv_size; int (*reset)(struct usb_hcd *); int (*port_power)(struct usb_hcd *, int, bool); }; struct debug_buffer { ssize_t (*fill_func)(struct debug_buffer *); struct usb_bus *bus; struct mutex mutex; size_t count; char *output_buf; size_t alloc_size; }; typedef __u32 __hc32; typedef __u16 __hc16; struct td; struct ed { __hc32 hwINFO; __hc32 hwTailP; __hc32 hwHeadP; __hc32 hwNextED; dma_addr_t dma; struct td *dummy; struct ed *ed_next; struct ed *ed_prev; struct list_head td_list; struct list_head in_use_list; u8 state; u8 type; u8 branch; u16 interval; u16 load; u16 last_iso; u16 tick; unsigned int takeback_wdh_cnt; struct td *pending_td; long: 64; }; struct td { __hc32 hwINFO; __hc32 hwCBP; __hc32 hwNextTD; __hc32 hwBE; __hc16 hwPSW[2]; __u8 index; struct ed *ed; struct td *td_hash; struct td *next_dl_td; struct urb *urb; dma_addr_t td_dma; dma_addr_t data_dma; struct list_head td_list; long: 64; }; struct ohci_hcca { __hc32 int_table[32]; __hc32 frame_no; __hc32 done_head; u8 reserved_for_hc[116]; u8 what[4]; }; struct ohci_roothub_regs { __hc32 a; __hc32 b; __hc32 status; __hc32 portstatus[15]; }; struct ohci_regs { __hc32 revision; __hc32 control; __hc32 cmdstatus; __hc32 intrstatus; __hc32 intrenable; __hc32 intrdisable; __hc32 hcca; __hc32 ed_periodcurrent; __hc32 ed_controlhead; __hc32 ed_controlcurrent; __hc32 ed_bulkhead; __hc32 ed_bulkcurrent; __hc32 donehead; __hc32 fminterval; __hc32 fmremaining; __hc32 fmnumber; __hc32 periodicstart; __hc32 lsthresh; struct ohci_roothub_regs roothub; long: 64; long: 64; }; struct urb_priv { struct ed *ed; u16 length; u16 td_cnt; struct list_head pending; struct td *td[0]; }; typedef struct urb_priv urb_priv_t; enum ohci_rh_state { OHCI_RH_HALTED = 0, OHCI_RH_SUSPENDED = 1, OHCI_RH_RUNNING = 2, }; struct ohci_hcd { spinlock_t lock; struct ohci_regs *regs; struct ohci_hcca *hcca; dma_addr_t hcca_dma; struct ed *ed_rm_list; struct ed *ed_bulktail; struct ed *ed_controltail; struct ed *periodic[32]; void (*start_hnp)(struct ohci_hcd *); struct dma_pool___2 *td_cache; struct dma_pool___2 *ed_cache; struct td *td_hash[64]; struct td *dl_start; struct td *dl_end; struct list_head pending; struct list_head eds_in_use; enum ohci_rh_state rh_state; int num_ports; int load[32]; u32 hc_control; long unsigned int next_statechange; u32 fminterval; unsigned int autostop: 1; unsigned int working: 1; unsigned int restart_work: 1; long unsigned int flags; unsigned int prev_frame_no; unsigned int wdh_cnt; unsigned int prev_wdh_cnt; u32 prev_donehead; struct timer_list io_watchdog; struct work_struct nec_work; struct dentry *debug_dir; long unsigned int priv[0]; }; struct ohci_driver_overrides { const char *product_desc; size_t extra_priv_size; int (*reset)(struct usb_hcd *); }; struct debug_buffer___2 { ssize_t (*fill_func)(struct debug_buffer___2 *); struct ohci_hcd *ohci; struct mutex mutex; size_t count; char *page; }; struct uhci_td; struct uhci_qh { __le32 link; __le32 element; dma_addr_t dma_handle; struct list_head node; struct usb_host_endpoint *hep; struct usb_device *udev; struct list_head queue; struct uhci_td *dummy_td; struct uhci_td *post_td; struct usb_iso_packet_descriptor *iso_packet_desc; long unsigned int advance_jiffies; unsigned int unlink_frame; unsigned int period; short int phase; short int load; unsigned int iso_frame; int state; int type; int skel; unsigned int initial_toggle: 1; unsigned int needs_fixup: 1; unsigned int is_stopped: 1; unsigned int wait_expired: 1; unsigned int bandwidth_reserved: 1; }; struct uhci_td { __le32 link; __le32 status; __le32 token; __le32 buffer; dma_addr_t dma_handle; struct list_head list; int frame; struct list_head fl_list; }; enum uhci_rh_state { UHCI_RH_RESET = 0, UHCI_RH_SUSPENDED = 1, UHCI_RH_AUTO_STOPPED = 2, UHCI_RH_RESUMING = 3, UHCI_RH_SUSPENDING = 4, UHCI_RH_RUNNING = 5, UHCI_RH_RUNNING_NODEVS = 6, }; struct uhci_hcd { long unsigned int io_addr; void *regs; struct dma_pool___2 *qh_pool; struct dma_pool___2 *td_pool; struct uhci_td *term_td; struct uhci_qh *skelqh[11]; struct uhci_qh *next_qh; spinlock_t lock; dma_addr_t frame_dma_handle; __le32 *frame; void **frame_cpu; enum uhci_rh_state rh_state; long unsigned int auto_stop_time; unsigned int frame_number; unsigned int is_stopped; unsigned int last_iso_frame; unsigned int cur_iso_frame; unsigned int scan_in_progress: 1; unsigned int need_rescan: 1; unsigned int dead: 1; unsigned int RD_enable: 1; unsigned int is_initialized: 1; unsigned int fsbr_is_on: 1; unsigned int fsbr_is_wanted: 1; unsigned int fsbr_expiring: 1; struct timer_list fsbr_timer; unsigned int oc_low: 1; unsigned int wait_for_hp: 1; unsigned int big_endian_mmio: 1; unsigned int big_endian_desc: 1; unsigned int is_aspeed: 1; long unsigned int port_c_suspend; long unsigned int resuming_ports; long unsigned int ports_timeout; struct list_head idle_qh_list; int rh_numports; wait_queue_head_t waitqh; int num_waiting; int total_load; short int load[32]; struct clk *clk; void (*reset_hc)(struct uhci_hcd *); int (*check_and_reset_hc)(struct uhci_hcd *); void (*configure_hc)(struct uhci_hcd *); int (*resume_detect_interrupts_are_broken)(struct uhci_hcd *); int (*global_suspend_mode_is_broken)(struct uhci_hcd *); }; struct urb_priv___2 { struct list_head node; struct urb *urb; struct uhci_qh *qh; struct list_head td_list; unsigned int fsbr: 1; }; struct uhci_debug { int size; char *data; }; struct xhci_cap_regs { __le32 hc_capbase; __le32 hcs_params1; __le32 hcs_params2; __le32 hcs_params3; __le32 hcc_params; __le32 db_off; __le32 run_regs_off; __le32 hcc_params2; }; struct xhci_op_regs { __le32 command; __le32 status; __le32 page_size; __le32 reserved1; __le32 reserved2; __le32 dev_notification; __le64 cmd_ring; __le32 reserved3[4]; __le64 dcbaa_ptr; __le32 config_reg; __le32 reserved4[241]; __le32 port_status_base; __le32 port_power_base; __le32 port_link_base; __le32 reserved5; __le32 reserved6[1016]; }; struct xhci_intr_reg { __le32 irq_pending; __le32 irq_control; __le32 erst_size; __le32 rsvd; __le64 erst_base; __le64 erst_dequeue; }; struct xhci_run_regs { __le32 microframe_index; __le32 rsvd[7]; struct xhci_intr_reg ir_set[128]; }; struct xhci_doorbell_array { __le32 doorbell[256]; }; struct xhci_container_ctx { unsigned int type; int size; u8 *bytes; dma_addr_t dma; }; struct xhci_slot_ctx { __le32 dev_info; __le32 dev_info2; __le32 tt_info; __le32 dev_state; __le32 reserved[4]; }; struct xhci_ep_ctx { __le32 ep_info; __le32 ep_info2; __le64 deq; __le32 tx_info; __le32 reserved[3]; }; struct xhci_input_control_ctx { __le32 drop_flags; __le32 add_flags; __le32 rsvd2[6]; }; union xhci_trb; struct xhci_command { struct xhci_container_ctx *in_ctx; u32 status; int slot_id; struct completion *completion; union xhci_trb *command_trb; struct list_head cmd_list; }; struct xhci_link_trb { __le64 segment_ptr; __le32 intr_target; __le32 control; }; struct xhci_transfer_event { __le64 buffer; __le32 transfer_len; __le32 flags; }; struct xhci_event_cmd { __le64 cmd_trb; __le32 status; __le32 flags; }; struct xhci_generic_trb { __le32 field[4]; }; union xhci_trb { struct xhci_link_trb link; struct xhci_transfer_event trans_event; struct xhci_event_cmd event_cmd; struct xhci_generic_trb generic; }; struct xhci_stream_ctx { __le64 stream_ring; __le32 reserved[2]; }; struct xhci_ring; struct xhci_stream_info { struct xhci_ring **stream_rings; unsigned int num_streams; struct xhci_stream_ctx *stream_ctx_array; unsigned int num_stream_ctxs; dma_addr_t ctx_array_dma; struct xarray trb_address_map; struct xhci_command *free_streams_command; }; enum xhci_ring_type { TYPE_CTRL = 0, TYPE_ISOC = 1, TYPE_BULK = 2, TYPE_INTR = 3, TYPE_STREAM = 4, TYPE_COMMAND = 5, TYPE_EVENT = 6, }; struct xhci_segment; struct xhci_ring { struct xhci_segment *first_seg; struct xhci_segment *last_seg; union xhci_trb *enqueue; struct xhci_segment *enq_seg; union xhci_trb *dequeue; struct xhci_segment *deq_seg; struct list_head td_list; u32 cycle_state; unsigned int err_count; unsigned int stream_id; unsigned int num_segs; unsigned int num_trbs_free; unsigned int num_trbs_free_temp; unsigned int bounce_buf_len; enum xhci_ring_type type; bool last_td_was_short; struct xarray *trb_address_map; }; struct xhci_bw_info { unsigned int ep_interval; unsigned int mult; unsigned int num_packets; unsigned int max_packet_size; unsigned int max_esit_payload; unsigned int type; }; struct xhci_virt_device; struct xhci_hcd; struct xhci_virt_ep { struct xhci_virt_device *vdev; unsigned int ep_index; struct xhci_ring *ring; struct xhci_stream_info *stream_info; struct xhci_ring *new_ring; unsigned int ep_state; struct list_head cancelled_td_list; struct timer_list stop_cmd_timer; struct xhci_hcd *xhci; struct xhci_segment *queued_deq_seg; union xhci_trb *queued_deq_ptr; bool skip; struct xhci_bw_info bw_info; struct list_head bw_endpoint_list; int next_frame_id; bool use_extended_tbc; }; struct xhci_interval_bw_table; struct xhci_tt_bw_info; struct xhci_virt_device { int slot_id; struct usb_device *udev; struct xhci_container_ctx *out_ctx; struct xhci_container_ctx *in_ctx; struct xhci_virt_ep eps[31]; u8 fake_port; u8 real_port; struct xhci_interval_bw_table *bw_table; struct xhci_tt_bw_info *tt_info; long unsigned int flags; u16 current_mel; void *debugfs_private; }; struct xhci_erst_entry; struct xhci_erst { struct xhci_erst_entry *entries; unsigned int num_entries; dma_addr_t erst_dma_addr; unsigned int erst_size; }; struct s3_save { u32 command; u32 dev_nt; u64 dcbaa_ptr; u32 config_reg; u32 irq_pending; u32 irq_control; u32 erst_size; u64 erst_base; u64 erst_dequeue; }; struct xhci_bus_state { long unsigned int bus_suspended; long unsigned int next_statechange; u32 port_c_suspend; u32 suspended_ports; u32 port_remote_wakeup; long unsigned int resume_done[31]; long unsigned int resuming_ports; long unsigned int rexit_ports; struct completion rexit_done[31]; struct completion u3exit_done[31]; }; struct xhci_port; struct xhci_hub { struct xhci_port **ports; unsigned int num_ports; struct usb_hcd *hcd; struct xhci_bus_state bus_state; u8 maj_rev; u8 min_rev; }; struct xhci_device_context_array; struct xhci_scratchpad; struct xhci_root_port_bw_info; struct xhci_port_cap; struct xhci_hcd { struct usb_hcd *main_hcd; struct usb_hcd *shared_hcd; struct xhci_cap_regs *cap_regs; struct xhci_op_regs *op_regs; struct xhci_run_regs *run_regs; struct xhci_doorbell_array *dba; struct xhci_intr_reg *ir_set; __u32 hcs_params1; __u32 hcs_params2; __u32 hcs_params3; __u32 hcc_params; __u32 hcc_params2; spinlock_t lock; u8 sbrn; u16 hci_version; u8 max_slots; u8 max_interrupters; u8 max_ports; u8 isoc_threshold; u32 imod_interval; u32 isoc_bei_interval; int event_ring_max; int page_size; int page_shift; int msix_count; struct clk *clk; struct clk *reg_clk; struct reset_control *reset; struct xhci_device_context_array *dcbaa; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; struct list_head cmd_list; unsigned int cmd_ring_reserved_trbs; struct delayed_work cmd_timer; struct completion cmd_ring_stop_completion; struct xhci_command *current_cmd; struct xhci_ring *event_ring; struct xhci_erst erst; struct xhci_scratchpad *scratchpad; struct list_head lpm_failed_devs; struct mutex mutex; struct xhci_command *lpm_command; struct xhci_virt_device *devs[256]; struct xhci_root_port_bw_info *rh_bw; struct dma_pool___2 *device_pool; struct dma_pool___2 *segment_pool; struct dma_pool___2 *small_streams_pool; struct dma_pool___2 *medium_streams_pool; unsigned int xhc_state; u32 command; struct s3_save s3; long long unsigned int quirks; unsigned int num_active_eps; unsigned int limit_active_eps; struct xhci_port *hw_ports; struct xhci_hub usb2_rhub; struct xhci_hub usb3_rhub; unsigned int hw_lpm_support: 1; unsigned int broken_suspend: 1; u32 *ext_caps; unsigned int num_ext_caps; struct xhci_port_cap *port_caps; unsigned int num_port_caps; struct timer_list comp_mode_recovery_timer; u32 port_status_u0; u16 test_mode; struct dentry *debugfs_root; struct dentry *debugfs_slots; struct list_head regset_list; void *dbc; long unsigned int priv[0]; }; struct xhci_segment { union xhci_trb *trbs; struct xhci_segment *next; dma_addr_t dma; dma_addr_t bounce_dma; void *bounce_buf; unsigned int bounce_offs; unsigned int bounce_len; }; enum xhci_overhead_type { LS_OVERHEAD_TYPE = 0, FS_OVERHEAD_TYPE = 1, HS_OVERHEAD_TYPE = 2, }; struct xhci_interval_bw { unsigned int num_packets; struct list_head endpoints; unsigned int overhead[3]; }; struct xhci_interval_bw_table { unsigned int interval0_esit_payload; struct xhci_interval_bw interval_bw[16]; unsigned int bw_used; unsigned int ss_bw_in; unsigned int ss_bw_out; }; struct xhci_tt_bw_info { struct list_head tt_list; int slot_id; int ttport; struct xhci_interval_bw_table bw_table; int active_eps; }; struct xhci_root_port_bw_info { struct list_head tts; unsigned int num_active_tts; struct xhci_interval_bw_table bw_table; }; struct xhci_device_context_array { __le64 dev_context_ptrs[256]; dma_addr_t dma; }; enum xhci_setup_dev { SETUP_CONTEXT_ONLY = 0, SETUP_CONTEXT_ADDRESS = 1, }; enum xhci_cancelled_td_status { TD_DIRTY = 0, TD_HALTED = 1, TD_CLEARING_CACHE = 2, TD_CLEARED = 3, }; struct xhci_td { struct list_head td_list; struct list_head cancelled_td_list; int status; enum xhci_cancelled_td_status cancel_status; struct urb *urb; struct xhci_segment *start_seg; union xhci_trb *first_trb; union xhci_trb *last_trb; struct xhci_segment *last_trb_seg; struct xhci_segment *bounce_seg; bool urb_length_set; unsigned int num_trbs; }; struct xhci_erst_entry { __le64 seg_addr; __le32 seg_size; __le32 rsvd; }; struct xhci_scratchpad { u64 *sp_array; dma_addr_t sp_dma; void **sp_buffers; }; struct urb_priv___3 { int num_tds; int num_tds_done; struct xhci_td td[0]; }; struct xhci_port_cap { u32 *psi; u8 psi_count; u8 psi_uid_count; u8 maj_rev; u8 min_rev; }; struct xhci_port { __le32 *addr; int hw_portnum; int hcd_portnum; struct xhci_hub *rhub; struct xhci_port_cap *port_cap; }; struct xhci_driver_overrides { size_t extra_priv_size; int (*reset)(struct usb_hcd *); int (*start)(struct usb_hcd *); int (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *); int (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *); int (*check_bandwidth)(struct usb_hcd *, struct usb_device *); void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *); }; typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); enum xhci_ep_reset_type { EP_HARD_RESET = 0, EP_SOFT_RESET = 1, }; struct dbc_regs { __le32 capability; __le32 doorbell; __le32 ersts; __le32 __reserved_0; __le64 erstba; __le64 erdp; __le32 control; __le32 status; __le32 portsc; __le32 __reserved_1; __le64 dccp; __le32 devinfo1; __le32 devinfo2; }; struct dbc_str_descs { char string0[64]; char manufacturer[64]; char product[64]; char serial[64]; }; enum dbc_state { DS_DISABLED = 0, DS_INITIALIZED = 1, DS_ENABLED = 2, DS_CONNECTED = 3, DS_CONFIGURED = 4, DS_STALLED = 5, }; struct xhci_dbc; struct dbc_ep { struct xhci_dbc *dbc; struct list_head list_pending; struct xhci_ring *ring; unsigned int direction: 1; }; struct dbc_driver; struct xhci_dbc { spinlock_t lock; struct device *dev; struct xhci_hcd *xhci; struct dbc_regs *regs; struct xhci_ring *ring_evt; struct xhci_ring *ring_in; struct xhci_ring *ring_out; struct xhci_erst erst; struct xhci_container_ctx *ctx; struct dbc_str_descs *string; dma_addr_t string_dma; size_t string_size; enum dbc_state state; struct delayed_work event_work; unsigned int resume_required: 1; struct dbc_ep eps[2]; const struct dbc_driver *driver; void *priv; }; struct dbc_driver { int (*configure)(struct xhci_dbc *); void (*disconnect)(struct xhci_dbc *); }; struct dbc_request { void *buf; unsigned int length; dma_addr_t dma; void (*complete)(struct xhci_dbc *, struct dbc_request *); struct list_head list_pool; int status; unsigned int actual; struct xhci_dbc *dbc; struct list_head list_pending; dma_addr_t trb_dma; union xhci_trb *trb; unsigned int direction: 1; }; struct trace_event_raw_xhci_log_msg { struct trace_entry ent; u32 __data_loc_msg; char __data[0]; }; struct trace_event_raw_xhci_log_ctx { struct trace_entry ent; int ctx_64; unsigned int ctx_type; dma_addr_t ctx_dma; u8 *ctx_va; unsigned int ctx_ep_num; int slot_id; u32 __data_loc_ctx_data; char __data[0]; }; struct trace_event_raw_xhci_log_trb { struct trace_entry ent; u32 type; u32 field0; u32 field1; u32 field2; u32 field3; u32 __data_loc_str; char __data[0]; }; struct trace_event_raw_xhci_log_free_virt_dev { struct trace_entry ent; void *vdev; long long unsigned int out_ctx; long long unsigned int in_ctx; u8 fake_port; u8 real_port; u16 current_mel; char __data[0]; }; struct trace_event_raw_xhci_log_virt_dev { struct trace_entry ent; void *vdev; long long unsigned int out_ctx; long long unsigned int in_ctx; int devnum; int state; int speed; u8 portnum; u8 level; int slot_id; char __data[0]; }; struct trace_event_raw_xhci_log_urb { struct trace_entry ent; void *urb; unsigned int pipe; unsigned int stream; int status; unsigned int flags; int num_mapped_sgs; int num_sgs; int length; int actual; int epnum; int dir_in; int type; int slot_id; char __data[0]; }; struct trace_event_raw_xhci_log_ep_ctx { struct trace_entry ent; u32 info; u32 info2; u64 deq; u32 tx_info; u32 __data_loc_str; char __data[0]; }; struct trace_event_raw_xhci_log_slot_ctx { struct trace_entry ent; u32 info; u32 info2; u32 tt_info; u32 state; u32 __data_loc_str; char __data[0]; }; struct trace_event_raw_xhci_log_ctrl_ctx { struct trace_entry ent; u32 drop; u32 add; u32 __data_loc_str; char __data[0]; }; struct trace_event_raw_xhci_log_ring { struct trace_entry ent; u32 type; void *ring; dma_addr_t enq; dma_addr_t deq; dma_addr_t enq_seg; dma_addr_t deq_seg; unsigned int num_segs; unsigned int stream_id; unsigned int cycle_state; unsigned int num_trbs_free; unsigned int bounce_buf_len; char __data[0]; }; struct trace_event_raw_xhci_log_portsc { struct trace_entry ent; u32 portnum; u32 portsc; u32 __data_loc_str; char __data[0]; }; struct trace_event_raw_xhci_log_doorbell { struct trace_entry ent; u32 slot; u32 doorbell; u32 __data_loc_str; char __data[0]; }; struct trace_event_raw_xhci_dbc_log_request { struct trace_entry ent; struct dbc_request *req; bool dir; unsigned int actual; unsigned int length; int status; char __data[0]; }; struct trace_event_data_offsets_xhci_log_msg { u32 msg; }; struct trace_event_data_offsets_xhci_log_ctx { u32 ctx_data; }; struct trace_event_data_offsets_xhci_log_trb { u32 str; }; struct trace_event_data_offsets_xhci_log_free_virt_dev {}; struct trace_event_data_offsets_xhci_log_virt_dev {}; struct trace_event_data_offsets_xhci_log_urb {}; struct trace_event_data_offsets_xhci_log_ep_ctx { u32 str; }; struct trace_event_data_offsets_xhci_log_slot_ctx { u32 str; }; struct trace_event_data_offsets_xhci_log_ctrl_ctx { u32 str; }; struct trace_event_data_offsets_xhci_log_ring {}; struct trace_event_data_offsets_xhci_log_portsc { u32 str; }; struct trace_event_data_offsets_xhci_log_doorbell { u32 str; }; struct trace_event_data_offsets_xhci_dbc_log_request {}; typedef void (*btf_trace_xhci_dbg_address)(void *, struct va_format *); typedef void (*btf_trace_xhci_dbg_context_change)(void *, struct va_format *); typedef void (*btf_trace_xhci_dbg_quirks)(void *, struct va_format *); typedef void (*btf_trace_xhci_dbg_reset_ep)(void *, struct va_format *); typedef void (*btf_trace_xhci_dbg_cancel_urb)(void *, struct va_format *); typedef void (*btf_trace_xhci_dbg_init)(void *, struct va_format *); typedef void (*btf_trace_xhci_dbg_ring_expansion)(void *, struct va_format *); typedef void (*btf_trace_xhci_address_ctx)(void *, struct xhci_hcd *, struct xhci_container_ctx *, unsigned int); typedef void (*btf_trace_xhci_handle_event)(void *, struct xhci_ring *, struct xhci_generic_trb *); typedef void (*btf_trace_xhci_handle_command)(void *, struct xhci_ring *, struct xhci_generic_trb *); typedef void (*btf_trace_xhci_handle_transfer)(void *, struct xhci_ring *, struct xhci_generic_trb *); typedef void (*btf_trace_xhci_queue_trb)(void *, struct xhci_ring *, struct xhci_generic_trb *); typedef void (*btf_trace_xhci_dbc_handle_event)(void *, struct xhci_ring *, struct xhci_generic_trb *); typedef void (*btf_trace_xhci_dbc_handle_transfer)(void *, struct xhci_ring *, struct xhci_generic_trb *); typedef void (*btf_trace_xhci_dbc_gadget_ep_queue)(void *, struct xhci_ring *, struct xhci_generic_trb *); typedef void (*btf_trace_xhci_free_virt_device)(void *, struct xhci_virt_device *); typedef void (*btf_trace_xhci_alloc_virt_device)(void *, struct xhci_virt_device *); typedef void (*btf_trace_xhci_setup_device)(void *, struct xhci_virt_device *); typedef void (*btf_trace_xhci_setup_addressable_virt_device)(void *, struct xhci_virt_device *); typedef void (*btf_trace_xhci_stop_device)(void *, struct xhci_virt_device *); typedef void (*btf_trace_xhci_urb_enqueue)(void *, struct urb *); typedef void (*btf_trace_xhci_urb_giveback)(void *, struct urb *); typedef void (*btf_trace_xhci_urb_dequeue)(void *, struct urb *); typedef void (*btf_trace_xhci_handle_cmd_stop_ep)(void *, struct xhci_ep_ctx *); typedef void (*btf_trace_xhci_handle_cmd_set_deq_ep)(void *, struct xhci_ep_ctx *); typedef void (*btf_trace_xhci_handle_cmd_reset_ep)(void *, struct xhci_ep_ctx *); typedef void (*btf_trace_xhci_handle_cmd_config_ep)(void *, struct xhci_ep_ctx *); typedef void (*btf_trace_xhci_add_endpoint)(void *, struct xhci_ep_ctx *); typedef void (*btf_trace_xhci_alloc_dev)(void *, struct xhci_slot_ctx *); typedef void (*btf_trace_xhci_free_dev)(void *, struct xhci_slot_ctx *); typedef void (*btf_trace_xhci_handle_cmd_disable_slot)(void *, struct xhci_slot_ctx *); typedef void (*btf_trace_xhci_discover_or_reset_device)(void *, struct xhci_slot_ctx *); typedef void (*btf_trace_xhci_setup_device_slot)(void *, struct xhci_slot_ctx *); typedef void (*btf_trace_xhci_handle_cmd_addr_dev)(void *, struct xhci_slot_ctx *); typedef void (*btf_trace_xhci_handle_cmd_reset_dev)(void *, struct xhci_slot_ctx *); typedef void (*btf_trace_xhci_handle_cmd_set_deq)(void *, struct xhci_slot_ctx *); typedef void (*btf_trace_xhci_configure_endpoint)(void *, struct xhci_slot_ctx *); typedef void (*btf_trace_xhci_address_ctrl_ctx)(void *, struct xhci_input_control_ctx *); typedef void (*btf_trace_xhci_configure_endpoint_ctrl_ctx)(void *, struct xhci_input_control_ctx *); typedef void (*btf_trace_xhci_ring_alloc)(void *, struct xhci_ring *); typedef void (*btf_trace_xhci_ring_free)(void *, struct xhci_ring *); typedef void (*btf_trace_xhci_ring_expansion)(void *, struct xhci_ring *); typedef void (*btf_trace_xhci_inc_enq)(void *, struct xhci_ring *); typedef void (*btf_trace_xhci_inc_deq)(void *, struct xhci_ring *); typedef void (*btf_trace_xhci_handle_port_status)(void *, u32, u32); typedef void (*btf_trace_xhci_get_port_status)(void *, u32, u32); typedef void (*btf_trace_xhci_hub_status_data)(void *, u32, u32); typedef void (*btf_trace_xhci_ring_ep_doorbell)(void *, u32, u32); typedef void (*btf_trace_xhci_ring_host_doorbell)(void *, u32, u32); typedef void (*btf_trace_xhci_dbc_alloc_request)(void *, struct dbc_request *); typedef void (*btf_trace_xhci_dbc_free_request)(void *, struct dbc_request *); typedef void (*btf_trace_xhci_dbc_queue_request)(void *, struct dbc_request *); typedef void (*btf_trace_xhci_dbc_giveback_request)(void *, struct dbc_request *); struct xhci_regset { char name[32]; struct debugfs_regset32 regset; size_t nregs; struct list_head list; }; struct xhci_file_map { const char *name; int (*show)(struct seq_file *, void *); }; struct xhci_ep_priv { char name[32]; struct dentry *root; struct xhci_stream_info *stream_info; struct xhci_ring *show_ring; unsigned int stream_id; }; struct xhci_slot_priv { char name[32]; struct dentry *root; struct xhci_ep_priv *eps[31]; struct xhci_virt_device *dev; }; struct async_icount { __u32 cts; __u32 dsr; __u32 rng; __u32 dcd; __u32 tx; __u32 rx; __u32 frame; __u32 parity; __u32 overrun; __u32 brk; __u32 buf_overrun; }; struct kfifo { union { struct __kfifo kfifo; unsigned char *type; const unsigned char *const_type; char (*rectype)[0]; void *ptr; const void *ptr_const; }; unsigned char buf[0]; }; struct usb_serial; struct usb_serial_port { struct usb_serial *serial; struct tty_port port; spinlock_t lock; u32 minor; u8 port_number; unsigned char *interrupt_in_buffer; struct urb *interrupt_in_urb; __u8 interrupt_in_endpointAddress; unsigned char *interrupt_out_buffer; int interrupt_out_size; struct urb *interrupt_out_urb; __u8 interrupt_out_endpointAddress; unsigned char *bulk_in_buffer; int bulk_in_size; struct urb *read_urb; __u8 bulk_in_endpointAddress; unsigned char *bulk_in_buffers[2]; struct urb *read_urbs[2]; long unsigned int read_urbs_free; unsigned char *bulk_out_buffer; int bulk_out_size; struct urb *write_urb; struct kfifo write_fifo; unsigned char *bulk_out_buffers[2]; struct urb *write_urbs[2]; long unsigned int write_urbs_free; __u8 bulk_out_endpointAddress; struct async_icount icount; int tx_bytes; long unsigned int flags; struct work_struct work; long unsigned int sysrq; struct device dev; }; struct usb_serial_driver; struct usb_serial { struct usb_device *dev; struct usb_serial_driver *type; struct usb_interface *interface; struct usb_interface *sibling; unsigned int suspend_count; unsigned char disconnected: 1; unsigned char attached: 1; unsigned char minors_reserved: 1; unsigned char num_ports; unsigned char num_port_pointers; unsigned char num_interrupt_in; unsigned char num_interrupt_out; unsigned char num_bulk_in; unsigned char num_bulk_out; struct usb_serial_port *port[16]; struct kref kref; struct mutex disc_mutex; void *private; }; struct usb_serial_endpoints; struct usb_serial_driver { const char *description; const struct usb_device_id *id_table; struct list_head driver_list; struct device_driver driver; struct usb_driver *usb_driver; struct usb_dynids dynids; unsigned char num_ports; unsigned char num_bulk_in; unsigned char num_bulk_out; unsigned char num_interrupt_in; unsigned char num_interrupt_out; size_t bulk_in_size; size_t bulk_out_size; int (*probe)(struct usb_serial *, const struct usb_device_id *); int (*attach)(struct usb_serial *); int (*calc_num_ports)(struct usb_serial *, struct usb_serial_endpoints *); void (*disconnect)(struct usb_serial *); void (*release)(struct usb_serial *); int (*port_probe)(struct usb_serial_port *); void (*port_remove)(struct usb_serial_port *); int (*suspend)(struct usb_serial *, pm_message_t); int (*resume)(struct usb_serial *); int (*reset_resume)(struct usb_serial *); int (*open)(struct tty_struct *, struct usb_serial_port *); void (*close)(struct usb_serial_port *); int (*write)(struct tty_struct *, struct usb_serial_port *, const unsigned char *, int); unsigned int (*write_room)(struct tty_struct *); int (*ioctl)(struct tty_struct *, unsigned int, long unsigned int); void (*get_serial)(struct tty_struct *, struct serial_struct *); int (*set_serial)(struct tty_struct *, struct serial_struct *); void (*set_termios)(struct tty_struct *, struct usb_serial_port *, struct ktermios *); void (*break_ctl)(struct tty_struct *, int); unsigned int (*chars_in_buffer)(struct tty_struct *); void (*wait_until_sent)(struct tty_struct *, long int); bool (*tx_empty)(struct usb_serial_port *); void (*throttle)(struct tty_struct *); void (*unthrottle)(struct tty_struct *); int (*tiocmget)(struct tty_struct *); int (*tiocmset)(struct tty_struct *, unsigned int, unsigned int); int (*tiocmiwait)(struct tty_struct *, long unsigned int); int (*get_icount)(struct tty_struct *, struct serial_icounter_struct *); void (*dtr_rts)(struct usb_serial_port *, int); int (*carrier_raised)(struct usb_serial_port *); void (*init_termios)(struct tty_struct *); void (*read_int_callback)(struct urb *); void (*write_int_callback)(struct urb *); void (*read_bulk_callback)(struct urb *); void (*write_bulk_callback)(struct urb *); void (*process_read_urb)(struct urb *); int (*prepare_write_buffer)(struct usb_serial_port *, void *, size_t); }; struct usb_serial_endpoints { unsigned char num_bulk_in; unsigned char num_bulk_out; unsigned char num_interrupt_in; unsigned char num_interrupt_out; struct usb_endpoint_descriptor *bulk_in[16]; struct usb_endpoint_descriptor *bulk_out[16]; struct usb_endpoint_descriptor *interrupt_in[16]; struct usb_endpoint_descriptor *interrupt_out[16]; }; struct usbcons_info { int magic; int break_flag; struct usb_serial_port *port; }; struct usb_debug_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bDebugInEndpoint; __u8 bDebugOutEndpoint; }; struct ehci_dev { u32 bus; u32 slot; u32 func; }; typedef void (*set_debug_port_t)(int); struct usb_hcd___2; struct usb_string_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 wData[1]; }; struct xdbc_regs { __le32 capability; __le32 doorbell; __le32 ersts; __le32 __reserved_0; __le64 erstba; __le64 erdp; __le32 control; __le32 status; __le32 portsc; __le32 __reserved_1; __le64 dccp; __le32 devinfo1; __le32 devinfo2; }; struct xdbc_trb { __le32 field[4]; }; struct xdbc_erst_entry { __le64 seg_addr; __le32 seg_size; __le32 __reserved_0; }; struct xdbc_info_context { __le64 string0; __le64 manufacturer; __le64 product; __le64 serial; __le32 length; __le32 __reserved_0[7]; }; struct xdbc_ep_context { __le32 ep_info1; __le32 ep_info2; __le64 deq; __le32 tx_info; __le32 __reserved_0[11]; }; struct xdbc_context { struct xdbc_info_context info; struct xdbc_ep_context out; struct xdbc_ep_context in; }; struct xdbc_strings { char string0[64]; char manufacturer[64]; char product[64]; char serial[64]; }; struct xdbc_segment { struct xdbc_trb *trbs; dma_addr_t dma; }; struct xdbc_ring { struct xdbc_segment *segment; struct xdbc_trb *enqueue; struct xdbc_trb *dequeue; u32 cycle_state; }; struct xdbc_state { u16 vendor; u16 device; u32 bus; u32 dev; u32 func; void *xhci_base; u64 xhci_start; size_t xhci_length; int port_number; struct xdbc_regs *xdbc_reg; dma_addr_t table_dma; void *table_base; dma_addr_t erst_dma; size_t erst_size; void *erst_base; struct xdbc_ring evt_ring; struct xdbc_segment evt_seg; dma_addr_t dbcc_dma; size_t dbcc_size; void *dbcc_base; dma_addr_t string_dma; size_t string_size; void *string_base; struct xdbc_ring out_ring; struct xdbc_segment out_seg; void *out_buf; dma_addr_t out_dma; struct xdbc_ring in_ring; struct xdbc_segment in_seg; void *in_buf; dma_addr_t in_dma; u32 flags; raw_spinlock_t lock; }; struct input_mt_slot { int abs[14]; unsigned int frame; unsigned int key; }; struct input_mt { int trkid; int num_slots; int slot; unsigned int flags; unsigned int frame; int *red; struct input_mt_slot slots[0]; }; union input_seq_state { struct { short unsigned int pos; bool mutex_acquired; }; void *p; }; struct input_devres { struct input_dev *input; }; struct input_event { __kernel_ulong_t __sec; __kernel_ulong_t __usec; __u16 type; __u16 code; __s32 value; }; struct input_event_compat { compat_ulong_t sec; compat_ulong_t usec; __u16 type; __u16 code; __s32 value; }; struct ff_periodic_effect_compat { __u16 waveform; __u16 period; __s16 magnitude; __s16 offset; __u16 phase; struct ff_envelope envelope; __u32 custom_len; compat_uptr_t custom_data; }; struct ff_effect_compat { __u16 type; __s16 id; __u16 direction; struct ff_trigger trigger; struct ff_replay replay; union { struct ff_constant_effect constant; struct ff_ramp_effect ramp; struct ff_periodic_effect_compat periodic; struct ff_condition_effect condition[2]; struct ff_rumble_effect rumble; } u; }; struct input_mt_pos { s16 x; s16 y; }; struct input_dev_poller { void (*poll)(struct input_dev *); unsigned int poll_interval; unsigned int poll_interval_max; unsigned int poll_interval_min; struct input_dev *input; struct delayed_work work; }; struct touchscreen_properties { unsigned int max_x; unsigned int max_y; bool invert_x; bool invert_y; bool swap_x_y; }; struct led_init_data { struct fwnode_handle *fwnode; const char *default_label; const char *devicename; bool devname_mandatory; }; struct input_led { struct led_classdev cdev; struct input_handle *handle; unsigned int code; }; struct input_leds { struct input_handle handle; unsigned int num_leds; struct input_led leds[0]; }; struct input_mask { __u32 type; __u32 codes_size; __u64 codes_ptr; }; struct evdev_client; struct evdev { int open; struct input_handle handle; struct evdev_client *grab; struct list_head client_list; spinlock_t client_lock; struct mutex mutex; struct device dev; struct cdev cdev; bool exist; }; struct evdev_client { unsigned int head; unsigned int tail; unsigned int packet_head; spinlock_t buffer_lock; wait_queue_head_t wait; struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; enum input_clock_type clk_type; bool revoked; long unsigned int *evmasks[32]; unsigned int bufsize; struct input_event buffer[0]; }; struct trace_event_raw_rtc_time_alarm_class { struct trace_entry ent; time64_t secs; int err; char __data[0]; }; struct trace_event_raw_rtc_irq_set_freq { struct trace_entry ent; int freq; int err; char __data[0]; }; struct trace_event_raw_rtc_irq_set_state { struct trace_entry ent; int enabled; int err; char __data[0]; }; struct trace_event_raw_rtc_alarm_irq_enable { struct trace_entry ent; unsigned int enabled; int err; char __data[0]; }; struct trace_event_raw_rtc_offset_class { struct trace_entry ent; long int offset; int err; char __data[0]; }; struct trace_event_raw_rtc_timer_class { struct trace_entry ent; struct rtc_timer *timer; ktime_t expires; ktime_t period; char __data[0]; }; struct trace_event_data_offsets_rtc_time_alarm_class {}; struct trace_event_data_offsets_rtc_irq_set_freq {}; struct trace_event_data_offsets_rtc_irq_set_state {}; struct trace_event_data_offsets_rtc_alarm_irq_enable {}; struct trace_event_data_offsets_rtc_offset_class {}; struct trace_event_data_offsets_rtc_timer_class {}; typedef void (*btf_trace_rtc_set_time)(void *, time64_t, int); typedef void (*btf_trace_rtc_read_time)(void *, time64_t, int); typedef void (*btf_trace_rtc_set_alarm)(void *, time64_t, int); typedef void (*btf_trace_rtc_read_alarm)(void *, time64_t, int); typedef void (*btf_trace_rtc_irq_set_freq)(void *, int, int); typedef void (*btf_trace_rtc_irq_set_state)(void *, int, int); typedef void (*btf_trace_rtc_alarm_irq_enable)(void *, unsigned int, int); typedef void (*btf_trace_rtc_set_offset)(void *, long int, int); typedef void (*btf_trace_rtc_read_offset)(void *, long int, int); typedef void (*btf_trace_rtc_timer_enqueue)(void *, struct rtc_timer *); typedef void (*btf_trace_rtc_timer_dequeue)(void *, struct rtc_timer *); typedef void (*btf_trace_rtc_timer_fired)(void *, struct rtc_timer *); enum { none = 0, day = 1, month = 2, year = 3, }; struct nvmem_cell_info { const char *name; unsigned int offset; unsigned int bytes; unsigned int bit_offset; unsigned int nbits; }; typedef int (*nvmem_reg_read_t)(void *, unsigned int, void *, size_t); typedef int (*nvmem_reg_write_t)(void *, unsigned int, void *, size_t); enum nvmem_type { NVMEM_TYPE_UNKNOWN = 0, NVMEM_TYPE_EEPROM = 1, NVMEM_TYPE_OTP = 2, NVMEM_TYPE_BATTERY_BACKED = 3, NVMEM_TYPE_FRAM = 4, }; struct nvmem_keepout { unsigned int start; unsigned int end; unsigned char value; }; struct nvmem_config { struct device *dev; const char *name; int id; struct module *owner; struct gpio_desc *wp_gpio; const struct nvmem_cell_info *cells; int ncells; const struct nvmem_keepout *keepout; unsigned int nkeepout; enum nvmem_type type; bool read_only; bool root_only; struct device_node *of_node; bool no_of_node; nvmem_reg_read_t reg_read; nvmem_reg_write_t reg_write; int size; int word_size; int stride; void *priv; bool compat; struct device *base_dev; }; struct nvmem_device; struct cmos_rtc_board_info { void (*wake_on)(struct device *); void (*wake_off)(struct device *); u32 flags; int address_space; u8 rtc_day_alarm; u8 rtc_mon_alarm; u8 rtc_century; }; struct cmos_rtc { struct rtc_device *rtc; struct device *dev; int irq; struct resource *iomem; time64_t alarm_expires; void (*wake_on)(struct device *); void (*wake_off)(struct device *); u8 enabled_wake; u8 suspend_ctrl; u8 day_alrm; u8 mon_alrm; u8 century; struct rtc_wkalrm saved_wkalrm; }; struct i2c_devinfo { struct list_head list; int busnum; struct i2c_board_info board_info; }; struct i2c_device_identity { u16 manufacturer_id; u16 part_id; u8 die_revision; }; struct i2c_timings { u32 bus_freq_hz; u32 scl_rise_ns; u32 scl_fall_ns; u32 scl_int_delay_ns; u32 sda_fall_ns; u32 sda_hold_ns; u32 digital_filter_width_ns; u32 analog_filter_cutoff_freq_hz; }; struct trace_event_raw_i2c_write { struct trace_entry ent; int adapter_nr; __u16 msg_nr; __u16 addr; __u16 flags; __u16 len; u32 __data_loc_buf; char __data[0]; }; struct trace_event_raw_i2c_read { struct trace_entry ent; int adapter_nr; __u16 msg_nr; __u16 addr; __u16 flags; __u16 len; char __data[0]; }; struct trace_event_raw_i2c_reply { struct trace_entry ent; int adapter_nr; __u16 msg_nr; __u16 addr; __u16 flags; __u16 len; u32 __data_loc_buf; char __data[0]; }; struct trace_event_raw_i2c_result { struct trace_entry ent; int adapter_nr; __u16 nr_msgs; __s16 ret; char __data[0]; }; struct trace_event_data_offsets_i2c_write { u32 buf; }; struct trace_event_data_offsets_i2c_read {}; struct trace_event_data_offsets_i2c_reply { u32 buf; }; struct trace_event_data_offsets_i2c_result {}; typedef void (*btf_trace_i2c_write)(void *, const struct i2c_adapter *, const struct i2c_msg *, int); typedef void (*btf_trace_i2c_read)(void *, const struct i2c_adapter *, const struct i2c_msg *, int); typedef void (*btf_trace_i2c_reply)(void *, const struct i2c_adapter *, const struct i2c_msg *, int); typedef void (*btf_trace_i2c_result)(void *, const struct i2c_adapter *, int, int); struct class_compat___2; struct i2c_cmd_arg { unsigned int cmd; void *arg; }; struct i2c_smbus_alert_setup { int irq; }; struct trace_event_raw_smbus_write { struct trace_entry ent; int adapter_nr; __u16 addr; __u16 flags; __u8 command; __u8 len; __u32 protocol; __u8 buf[34]; char __data[0]; }; struct trace_event_raw_smbus_read { struct trace_entry ent; int adapter_nr; __u16 flags; __u16 addr; __u8 command; __u32 protocol; __u8 buf[34]; char __data[0]; }; struct trace_event_raw_smbus_reply { struct trace_entry ent; int adapter_nr; __u16 addr; __u16 flags; __u8 command; __u8 len; __u32 protocol; __u8 buf[34]; char __data[0]; }; struct trace_event_raw_smbus_result { struct trace_entry ent; int adapter_nr; __u16 addr; __u16 flags; __u8 read_write; __u8 command; __s16 res; __u32 protocol; char __data[0]; }; struct trace_event_data_offsets_smbus_write {}; struct trace_event_data_offsets_smbus_read {}; struct trace_event_data_offsets_smbus_reply {}; struct trace_event_data_offsets_smbus_result {}; typedef void (*btf_trace_smbus_write)(void *, const struct i2c_adapter *, u16, short unsigned int, char, u8, int, const union i2c_smbus_data *); typedef void (*btf_trace_smbus_read)(void *, const struct i2c_adapter *, u16, short unsigned int, char, u8, int); typedef void (*btf_trace_smbus_reply)(void *, const struct i2c_adapter *, u16, short unsigned int, char, u8, int, const union i2c_smbus_data *, int); typedef void (*btf_trace_smbus_result)(void *, const struct i2c_adapter *, u16, short unsigned int, char, u8, int, int); struct i2c_acpi_handler_data { struct acpi_connection_info info; struct i2c_adapter *adapter; }; struct gsb_buffer { u8 status; u8 len; union { u16 wdata; u8 bdata; u8 data[0]; }; }; struct i2c_acpi_lookup { struct i2c_board_info *info; acpi_handle adapter_handle; acpi_handle device_handle; acpi_handle search_handle; int n; int index; u32 speed; u32 min_speed; u32 force_speed; }; struct dw_i2c_dev { struct device *dev; struct regmap *map; struct regmap *sysmap; void *base; void *ext; struct completion cmd_complete; struct clk *clk; struct clk *pclk; struct reset_control *rst; struct i2c_client *slave; u32 (*get_clk_rate_khz)(struct dw_i2c_dev *); int cmd_err; struct i2c_msg *msgs; int msgs_num; int msg_write_idx; u32 tx_buf_len; u8 *tx_buf; int msg_read_idx; u32 rx_buf_len; u8 *rx_buf; int msg_err; unsigned int status; u32 abort_source; int irq; u32 flags; struct i2c_adapter adapter; u32 functionality; u32 master_cfg; u32 slave_cfg; unsigned int tx_fifo_depth; unsigned int rx_fifo_depth; int rx_outstanding; struct i2c_timings timings; u32 sda_hold_time; u16 ss_hcnt; u16 ss_lcnt; u16 fs_hcnt; u16 fs_lcnt; u16 fp_hcnt; u16 fp_lcnt; u16 hs_hcnt; u16 hs_lcnt; int (*acquire_lock)(); void (*release_lock)(); bool shared_with_punit; void (*disable)(struct dw_i2c_dev *); void (*disable_int)(struct dw_i2c_dev *); int (*init)(struct dw_i2c_dev *); int (*set_sda_hold_time)(struct dw_i2c_dev *); int mode; struct i2c_bus_recovery_info rinfo; bool suspended; }; enum dw_pci_ctl_id_t { medfield = 0, merrifield = 1, baytrail = 2, cherrytrail = 3, haswell = 4, elkhartlake = 5, navi_amd = 6, }; struct dw_scl_sda_cfg { u32 ss_hcnt; u32 fs_hcnt; u32 ss_lcnt; u32 fs_lcnt; u32 sda_hold; }; struct dw_pci_controller { u32 bus_num; u32 flags; struct dw_scl_sda_cfg *scl_sda_cfg; int (*setup)(struct pci_dev *, struct dw_pci_controller *); u32 (*get_clk_rate_khz)(struct dw_i2c_dev *); }; struct lirc_scancode { __u64 timestamp; __u16 flags; __u16 rc_proto; __u32 keycode; __u64 scancode; }; enum rc_proto { RC_PROTO_UNKNOWN = 0, RC_PROTO_OTHER = 1, RC_PROTO_RC5 = 2, RC_PROTO_RC5X_20 = 3, RC_PROTO_RC5_SZ = 4, RC_PROTO_JVC = 5, RC_PROTO_SONY12 = 6, RC_PROTO_SONY15 = 7, RC_PROTO_SONY20 = 8, RC_PROTO_NEC = 9, RC_PROTO_NECX = 10, RC_PROTO_NEC32 = 11, RC_PROTO_SANYO = 12, RC_PROTO_MCIR2_KBD = 13, RC_PROTO_MCIR2_MSE = 14, RC_PROTO_RC6_0 = 15, RC_PROTO_RC6_6A_20 = 16, RC_PROTO_RC6_6A_24 = 17, RC_PROTO_RC6_6A_32 = 18, RC_PROTO_RC6_MCE = 19, RC_PROTO_SHARP = 20, RC_PROTO_XMP = 21, RC_PROTO_CEC = 22, RC_PROTO_IMON = 23, RC_PROTO_RCMM12 = 24, RC_PROTO_RCMM24 = 25, RC_PROTO_RCMM32 = 26, RC_PROTO_XBOX_DVD = 27, RC_PROTO_MAX = 27, }; struct rc_map_table { u64 scancode; u32 keycode; }; struct rc_map { struct rc_map_table *scan; unsigned int size; unsigned int len; unsigned int alloc; enum rc_proto rc_proto; const char *name; spinlock_t lock; }; struct rc_map_list { struct list_head list; struct rc_map map; }; enum rc_driver_type { RC_DRIVER_SCANCODE = 0, RC_DRIVER_IR_RAW = 1, RC_DRIVER_IR_RAW_TX = 2, }; struct rc_scancode_filter { u32 data; u32 mask; }; enum rc_filter_type { RC_FILTER_NORMAL = 0, RC_FILTER_WAKEUP = 1, RC_FILTER_MAX = 2, }; struct ir_raw_event_ctrl; struct rc_dev { struct device dev; bool managed_alloc; const struct attribute_group *sysfs_groups[5]; const char *device_name; const char *input_phys; struct input_id input_id; const char *driver_name; const char *map_name; struct rc_map rc_map; struct mutex lock; unsigned int minor; struct ir_raw_event_ctrl *raw; struct input_dev *input_dev; enum rc_driver_type driver_type; bool idle; bool encode_wakeup; u64 allowed_protocols; u64 enabled_protocols; u64 allowed_wakeup_protocols; enum rc_proto wakeup_protocol; struct rc_scancode_filter scancode_filter; struct rc_scancode_filter scancode_wakeup_filter; u32 scancode_mask; u32 users; void *priv; spinlock_t keylock; bool keypressed; long unsigned int keyup_jiffies; struct timer_list timer_keyup; struct timer_list timer_repeat; u32 last_keycode; enum rc_proto last_protocol; u64 last_scancode; u8 last_toggle; u32 timeout; u32 min_timeout; u32 max_timeout; u32 rx_resolution; u32 tx_resolution; struct device lirc_dev; struct cdev lirc_cdev; ktime_t gap_start; u64 gap_duration; bool gap; spinlock_t lirc_fh_lock; struct list_head lirc_fh; bool registered; int (*change_protocol)(struct rc_dev *, u64 *); int (*open)(struct rc_dev *); void (*close)(struct rc_dev *); int (*s_tx_mask)(struct rc_dev *, u32); int (*s_tx_carrier)(struct rc_dev *, u32); int (*s_tx_duty_cycle)(struct rc_dev *, u32); int (*s_rx_carrier_range)(struct rc_dev *, u32, u32); int (*tx_ir)(struct rc_dev *, unsigned int *, unsigned int); void (*s_idle)(struct rc_dev *, bool); int (*s_learning_mode)(struct rc_dev *, int); int (*s_carrier_report)(struct rc_dev *, int); int (*s_filter)(struct rc_dev *, struct rc_scancode_filter *); int (*s_wakeup_filter)(struct rc_dev *, struct rc_scancode_filter *); int (*s_timeout)(struct rc_dev *, unsigned int); }; struct ir_raw_event { union { u32 duration; u32 carrier; }; u8 duty_cycle; unsigned int pulse: 1; unsigned int reset: 1; unsigned int timeout: 1; unsigned int carrier_report: 1; }; struct nec_dec { int state; unsigned int count; u32 bits; bool is_nec_x; bool necx_repeat; }; struct rc5_dec { int state; u32 bits; unsigned int count; bool is_rc5x; }; struct rc6_dec { int state; u8 header; u32 body; bool toggle; unsigned int count; unsigned int wanted_bits; }; struct sony_dec { int state; u32 bits; unsigned int count; }; struct jvc_dec { int state; u16 bits; u16 old_bits; unsigned int count; bool first; bool toggle; }; struct sanyo_dec { int state; unsigned int count; u64 bits; }; struct sharp_dec { int state; unsigned int count; u32 bits; unsigned int pulse_len; }; struct mce_kbd_dec { spinlock_t keylock; struct timer_list rx_timeout; int state; u8 header; u32 body; unsigned int count; unsigned int wanted_bits; }; struct xmp_dec { int state; unsigned int count; u32 durations[16]; }; struct imon_dec { int state; int count; int last_chk; unsigned int bits; bool stick_keyboard; }; struct rcmm_dec { int state; unsigned int count; u32 bits; }; struct ir_raw_event_ctrl { struct list_head list; struct task_struct *thread; struct { union { struct __kfifo kfifo; struct ir_raw_event *type; const struct ir_raw_event *const_type; char (*rectype)[0]; struct ir_raw_event *ptr; const struct ir_raw_event *ptr_const; }; struct ir_raw_event buf[512]; } kfifo; ktime_t last_event; struct rc_dev *dev; spinlock_t edge_spinlock; struct timer_list edge_handle; struct ir_raw_event prev_ev; struct ir_raw_event this_ev; u32 bpf_sample; struct bpf_prog_array *progs; struct nec_dec nec; struct rc5_dec rc5; struct rc6_dec rc6; struct sony_dec sony; struct jvc_dec jvc; struct sanyo_dec sanyo; struct sharp_dec sharp; struct mce_kbd_dec mce_kbd; struct xmp_dec xmp; struct imon_dec imon; struct rcmm_dec rcmm; }; struct rc_filter_attribute { struct device_attribute attr; enum rc_filter_type type; bool mask; }; struct ir_raw_handler { struct list_head list; u64 protocols; int (*decode)(struct rc_dev *, struct ir_raw_event); int (*encode)(enum rc_proto, u32, struct ir_raw_event *, unsigned int); u32 carrier; u32 min_timeout; int (*raw_register)(struct rc_dev *); int (*raw_unregister)(struct rc_dev *); }; struct ir_raw_timings_manchester { unsigned int leader_pulse; unsigned int leader_space; unsigned int clock; unsigned int invert: 1; unsigned int trailer_space; }; struct ir_raw_timings_pd { unsigned int header_pulse; unsigned int header_space; unsigned int bit_pulse; unsigned int bit_space[2]; unsigned int trailer_pulse; unsigned int trailer_space; unsigned int msb_first: 1; }; struct ir_raw_timings_pl { unsigned int header_pulse; unsigned int bit_space; unsigned int bit_pulse[2]; unsigned int trailer_space; unsigned int msb_first: 1; }; struct lirc_fh { struct list_head list; struct rc_dev *rc; int carrier_low; bool send_timeout_reports; struct { union { struct __kfifo kfifo; unsigned int *type; const unsigned int *const_type; char (*rectype)[0]; unsigned int *ptr; const unsigned int *ptr_const; }; unsigned int buf[0]; } rawir; struct { union { struct __kfifo kfifo; struct lirc_scancode *type; const struct lirc_scancode *const_type; char (*rectype)[0]; struct lirc_scancode *ptr; const struct lirc_scancode *ptr_const; }; struct lirc_scancode buf[0]; } scancodes; wait_queue_head_t wait_poll; u8 send_mode; u8 rec_mode; }; typedef u64 (*btf_bpf_rc_repeat)(u32 *); typedef u64 (*btf_bpf_rc_keydown)(u32 *, u32, u64, u32); typedef u64 (*btf_bpf_rc_pointer_rel)(u32 *, s32, s32); struct pps_ktime { __s64 sec; __s32 nsec; __u32 flags; }; struct pps_ktime_compat { __s64 sec; __s32 nsec; __u32 flags; }; struct pps_kinfo { __u32 assert_sequence; __u32 clear_sequence; struct pps_ktime assert_tu; struct pps_ktime clear_tu; int current_mode; }; struct pps_kinfo_compat { __u32 assert_sequence; __u32 clear_sequence; struct pps_ktime_compat assert_tu; struct pps_ktime_compat clear_tu; int current_mode; } __attribute__((packed)); struct pps_kparams { int api_version; int mode; struct pps_ktime assert_off_tu; struct pps_ktime clear_off_tu; }; struct pps_fdata { struct pps_kinfo info; struct pps_ktime timeout; }; struct pps_fdata_compat { struct pps_kinfo_compat info; struct pps_ktime_compat timeout; } __attribute__((packed)); struct pps_bind_args { int tsformat; int edge; int consumer; }; struct pps_device; struct pps_source_info { char name[32]; char path[32]; int mode; void (*echo)(struct pps_device *, int, void *); struct module *owner; struct device *dev; }; struct pps_device { struct pps_source_info info; struct pps_kparams params; __u32 assert_sequence; __u32 clear_sequence; struct pps_ktime assert_tu; struct pps_ktime clear_tu; int current_mode; unsigned int last_ev; wait_queue_head_t queue; unsigned int id; const void *lookup_cookie; struct cdev cdev; struct device *dev; struct fasync_struct *async_queue; spinlock_t lock; }; struct pps_event_time { struct timespec64 ts_real; }; struct ptp_clock_time { __s64 sec; __u32 nsec; __u32 reserved; }; struct ptp_extts_request { unsigned int index; unsigned int flags; unsigned int rsv[2]; }; struct ptp_perout_request { union { struct ptp_clock_time start; struct ptp_clock_time phase; }; struct ptp_clock_time period; unsigned int index; unsigned int flags; union { struct ptp_clock_time on; unsigned int rsv[4]; }; }; enum ptp_pin_function { PTP_PF_NONE = 0, PTP_PF_EXTTS = 1, PTP_PF_PEROUT = 2, PTP_PF_PHYSYNC = 3, }; struct ptp_pin_desc { char name[64]; unsigned int index; unsigned int func; unsigned int chan; unsigned int rsv[5]; }; struct ptp_extts_event { struct ptp_clock_time t; unsigned int index; unsigned int flags; unsigned int rsv[2]; }; struct ptp_clock_request { enum { PTP_CLK_REQ_EXTTS = 0, PTP_CLK_REQ_PEROUT = 1, PTP_CLK_REQ_PPS = 2, } type; union { struct ptp_extts_request extts; struct ptp_perout_request perout; }; }; struct ptp_clock_info { struct module *owner; char name[32]; s32 max_adj; int n_alarm; int n_ext_ts; int n_per_out; int n_pins; int pps; struct ptp_pin_desc *pin_config; int (*adjfine)(struct ptp_clock_info *, long int); int (*adjfreq)(struct ptp_clock_info *, s32); int (*adjphase)(struct ptp_clock_info *, s32); int (*adjtime)(struct ptp_clock_info *, s64); int (*gettime64)(struct ptp_clock_info *, struct timespec64 *); int (*gettimex64)(struct ptp_clock_info *, struct timespec64 *, struct ptp_system_timestamp *); int (*getcrosststamp)(struct ptp_clock_info *, struct system_device_crosststamp *); int (*settime64)(struct ptp_clock_info *, const struct timespec64 *); int (*enable)(struct ptp_clock_info *, struct ptp_clock_request *, int); int (*verify)(struct ptp_clock_info *, unsigned int, enum ptp_pin_function, unsigned int); long int (*do_aux_work)(struct ptp_clock_info *); }; enum ptp_clock_events { PTP_CLOCK_ALARM = 0, PTP_CLOCK_EXTTS = 1, PTP_CLOCK_PPS = 2, PTP_CLOCK_PPSUSR = 3, }; struct ptp_clock_event { int type; int index; union { u64 timestamp; struct pps_event_time pps_times; }; }; struct timestamp_event_queue { struct ptp_extts_event buf[128]; int head; int tail; spinlock_t lock; }; struct ptp_clock { struct posix_clock clock; struct device dev; struct ptp_clock_info *info; dev_t devid; int index; struct pps_device *pps_source; long int dialed_frequency; struct timestamp_event_queue tsevq; struct mutex tsevq_mux; struct mutex pincfg_mux; wait_queue_head_t tsev_wq; int defunct; struct device_attribute *pin_dev_attr; struct attribute **pin_attr; struct attribute_group pin_attr_group; const struct attribute_group *pin_attr_groups[2]; struct kthread_worker *kworker; struct kthread_delayed_work aux_work; unsigned int max_vclocks; unsigned int n_vclocks; int *vclock_index; struct mutex n_vclocks_mux; bool is_virtual_clock; }; struct ptp_clock_caps { int max_adj; int n_alarm; int n_ext_ts; int n_per_out; int pps; int n_pins; int cross_timestamping; int adjust_phase; int rsv[12]; }; struct ptp_sys_offset { unsigned int n_samples; unsigned int rsv[3]; struct ptp_clock_time ts[51]; }; struct ptp_sys_offset_extended { unsigned int n_samples; unsigned int rsv[3]; struct ptp_clock_time ts[75]; }; struct ptp_sys_offset_precise { struct ptp_clock_time device; struct ptp_clock_time sys_realtime; struct ptp_clock_time sys_monoraw; unsigned int rsv[4]; }; struct ptp_vclock { struct ptp_clock *pclock; struct ptp_clock_info info; struct ptp_clock *clock; struct cyclecounter cc; struct timecounter tc; spinlock_t lock; }; struct mt6397_chip { struct device *dev; struct regmap *regmap; struct notifier_block pm_nb; int irq; struct irq_domain *irq_domain; struct mutex irqlock; u16 wake_mask[2]; u16 irq_masks_cur[2]; u16 irq_masks_cache[2]; u16 int_con[2]; u16 int_status[2]; u16 chip_id; void *irq_data; }; struct mt6323_pwrc { struct device *dev; struct regmap *regmap; u32 base; }; enum power_supply_notifier_events { PSY_EVENT_PROP_CHANGED = 0, }; struct power_supply_battery_ocv_table { int ocv; int capacity; }; struct power_supply_resistance_temp_table { int temp; int resistance; }; struct power_supply_battery_info { int energy_full_design_uwh; int charge_full_design_uah; int voltage_min_design_uv; int voltage_max_design_uv; int tricklecharge_current_ua; int precharge_current_ua; int precharge_voltage_max_uv; int charge_term_current_ua; int charge_restart_voltage_uv; int overvoltage_limit_uv; int constant_charge_current_max_ua; int constant_charge_voltage_max_uv; int factory_internal_resistance_uohm; int ocv_temp[20]; int temp_ambient_alert_min; int temp_ambient_alert_max; int temp_alert_min; int temp_alert_max; int temp_min; int temp_max; struct power_supply_battery_ocv_table *ocv_table[20]; int ocv_table_size[20]; struct power_supply_resistance_temp_table *resist_table; int resist_table_size; }; struct psy_am_i_supplied_data { struct power_supply *psy; unsigned int count; }; enum { POWER_SUPPLY_CHARGE_TYPE_UNKNOWN = 0, POWER_SUPPLY_CHARGE_TYPE_NONE = 1, POWER_SUPPLY_CHARGE_TYPE_TRICKLE = 2, POWER_SUPPLY_CHARGE_TYPE_FAST = 3, POWER_SUPPLY_CHARGE_TYPE_STANDARD = 4, POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE = 5, POWER_SUPPLY_CHARGE_TYPE_CUSTOM = 6, POWER_SUPPLY_CHARGE_TYPE_LONGLIFE = 7, }; enum { POWER_SUPPLY_HEALTH_UNKNOWN = 0, POWER_SUPPLY_HEALTH_GOOD = 1, POWER_SUPPLY_HEALTH_OVERHEAT = 2, POWER_SUPPLY_HEALTH_DEAD = 3, POWER_SUPPLY_HEALTH_OVERVOLTAGE = 4, POWER_SUPPLY_HEALTH_UNSPEC_FAILURE = 5, POWER_SUPPLY_HEALTH_COLD = 6, POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE = 7, POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE = 8, POWER_SUPPLY_HEALTH_OVERCURRENT = 9, POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED = 10, POWER_SUPPLY_HEALTH_WARM = 11, POWER_SUPPLY_HEALTH_COOL = 12, POWER_SUPPLY_HEALTH_HOT = 13, }; enum { POWER_SUPPLY_SCOPE_UNKNOWN = 0, POWER_SUPPLY_SCOPE_SYSTEM = 1, POWER_SUPPLY_SCOPE_DEVICE = 2, }; struct power_supply_attr { const char *prop_name; char attr_name[31]; struct device_attribute dev_attr; const char * const *text_values; int text_values_len; }; enum hwmon_in_attributes { hwmon_in_enable = 0, hwmon_in_input = 1, hwmon_in_min = 2, hwmon_in_max = 3, hwmon_in_lcrit = 4, hwmon_in_crit = 5, hwmon_in_average = 6, hwmon_in_lowest = 7, hwmon_in_highest = 8, hwmon_in_reset_history = 9, hwmon_in_label = 10, hwmon_in_alarm = 11, hwmon_in_min_alarm = 12, hwmon_in_max_alarm = 13, hwmon_in_lcrit_alarm = 14, hwmon_in_crit_alarm = 15, hwmon_in_rated_min = 16, hwmon_in_rated_max = 17, }; enum hwmon_curr_attributes { hwmon_curr_enable = 0, hwmon_curr_input = 1, hwmon_curr_min = 2, hwmon_curr_max = 3, hwmon_curr_lcrit = 4, hwmon_curr_crit = 5, hwmon_curr_average = 6, hwmon_curr_lowest = 7, hwmon_curr_highest = 8, hwmon_curr_reset_history = 9, hwmon_curr_label = 10, hwmon_curr_alarm = 11, hwmon_curr_min_alarm = 12, hwmon_curr_max_alarm = 13, hwmon_curr_lcrit_alarm = 14, hwmon_curr_crit_alarm = 15, hwmon_curr_rated_min = 16, hwmon_curr_rated_max = 17, }; struct power_supply_hwmon { struct power_supply *psy; long unsigned int *props; }; struct hwmon_type_attr_list { const u32 *attrs; size_t n_attrs; }; enum cm_batt_temp { CM_BATT_OK = 0, CM_BATT_OVERHEAT = 1, CM_BATT_COLD = 2, }; enum hwmon_power_attributes { hwmon_power_enable = 0, hwmon_power_average = 1, hwmon_power_average_interval = 2, hwmon_power_average_interval_max = 3, hwmon_power_average_interval_min = 4, hwmon_power_average_highest = 5, hwmon_power_average_lowest = 6, hwmon_power_average_max = 7, hwmon_power_average_min = 8, hwmon_power_input = 9, hwmon_power_input_highest = 10, hwmon_power_input_lowest = 11, hwmon_power_reset_history = 12, hwmon_power_accuracy = 13, hwmon_power_cap = 14, hwmon_power_cap_hyst = 15, hwmon_power_cap_max = 16, hwmon_power_cap_min = 17, hwmon_power_min = 18, hwmon_power_max = 19, hwmon_power_crit = 20, hwmon_power_lcrit = 21, hwmon_power_label = 22, hwmon_power_alarm = 23, hwmon_power_cap_alarm = 24, hwmon_power_min_alarm = 25, hwmon_power_max_alarm = 26, hwmon_power_lcrit_alarm = 27, hwmon_power_crit_alarm = 28, hwmon_power_rated_min = 29, hwmon_power_rated_max = 30, }; enum hwmon_energy_attributes { hwmon_energy_enable = 0, hwmon_energy_input = 1, hwmon_energy_label = 2, }; enum hwmon_humidity_attributes { hwmon_humidity_enable = 0, hwmon_humidity_input = 1, hwmon_humidity_label = 2, hwmon_humidity_min = 3, hwmon_humidity_min_hyst = 4, hwmon_humidity_max = 5, hwmon_humidity_max_hyst = 6, hwmon_humidity_alarm = 7, hwmon_humidity_fault = 8, hwmon_humidity_rated_min = 9, hwmon_humidity_rated_max = 10, }; enum hwmon_fan_attributes { hwmon_fan_enable = 0, hwmon_fan_input = 1, hwmon_fan_label = 2, hwmon_fan_min = 3, hwmon_fan_max = 4, hwmon_fan_div = 5, hwmon_fan_pulses = 6, hwmon_fan_target = 7, hwmon_fan_alarm = 8, hwmon_fan_min_alarm = 9, hwmon_fan_max_alarm = 10, hwmon_fan_fault = 11, }; enum hwmon_pwm_attributes { hwmon_pwm_input = 0, hwmon_pwm_enable = 1, hwmon_pwm_mode = 2, hwmon_pwm_freq = 3, }; enum hwmon_intrusion_attributes { hwmon_intrusion_alarm = 0, hwmon_intrusion_beep = 1, }; struct trace_event_raw_hwmon_attr_class { struct trace_entry ent; int index; u32 __data_loc_attr_name; long int val; char __data[0]; }; struct trace_event_raw_hwmon_attr_show_string { struct trace_entry ent; int index; u32 __data_loc_attr_name; u32 __data_loc_label; char __data[0]; }; struct trace_event_data_offsets_hwmon_attr_class { u32 attr_name; }; struct trace_event_data_offsets_hwmon_attr_show_string { u32 attr_name; u32 label; }; typedef void (*btf_trace_hwmon_attr_show)(void *, int, const char *, long int); typedef void (*btf_trace_hwmon_attr_store)(void *, int, const char *, long int); typedef void (*btf_trace_hwmon_attr_show_string)(void *, int, const char *, const char *); struct hwmon_device { const char *name; struct device dev; const struct hwmon_chip_info *chip; struct list_head tzdata; struct attribute_group group; const struct attribute_group **groups; }; struct hwmon_device_attribute { struct device_attribute dev_attr; const struct hwmon_ops *ops; enum hwmon_sensor_types type; u32 attr; int index; char name[32]; }; struct thermal_attr { struct device_attribute attr; char name[20]; }; struct devfreq_dev_status { long unsigned int total_time; long unsigned int busy_time; long unsigned int current_frequency; void *private_data; }; struct trace_event_raw_thermal_temperature { struct trace_entry ent; u32 __data_loc_thermal_zone; int id; int temp_prev; int temp; char __data[0]; }; struct trace_event_raw_cdev_update { struct trace_entry ent; u32 __data_loc_type; long unsigned int target; char __data[0]; }; struct trace_event_raw_thermal_zone_trip { struct trace_entry ent; u32 __data_loc_thermal_zone; int id; int trip; enum thermal_trip_type trip_type; char __data[0]; }; struct trace_event_raw_thermal_power_devfreq_get_power { struct trace_entry ent; u32 __data_loc_type; long unsigned int freq; u32 busy_time; u32 total_time; u32 power; char __data[0]; }; struct trace_event_raw_thermal_power_devfreq_limit { struct trace_entry ent; u32 __data_loc_type; unsigned int freq; long unsigned int cdev_state; u32 power; char __data[0]; }; struct trace_event_data_offsets_thermal_temperature { u32 thermal_zone; }; struct trace_event_data_offsets_cdev_update { u32 type; }; struct trace_event_data_offsets_thermal_zone_trip { u32 thermal_zone; }; struct trace_event_data_offsets_thermal_power_devfreq_get_power { u32 type; }; struct trace_event_data_offsets_thermal_power_devfreq_limit { u32 type; }; typedef void (*btf_trace_thermal_temperature)(void *, struct thermal_zone_device *); typedef void (*btf_trace_cdev_update)(void *, struct thermal_cooling_device *, long unsigned int); typedef void (*btf_trace_thermal_zone_trip)(void *, struct thermal_zone_device *, int, enum thermal_trip_type); typedef void (*btf_trace_thermal_power_devfreq_get_power)(void *, struct thermal_cooling_device *, struct devfreq_dev_status *, long unsigned int, u32); typedef void (*btf_trace_thermal_power_devfreq_limit)(void *, struct thermal_cooling_device *, long unsigned int, long unsigned int, u32); struct thermal_instance { int id; char name[20]; struct thermal_zone_device *tz; struct thermal_cooling_device *cdev; int trip; bool initialized; long unsigned int upper; long unsigned int lower; long unsigned int target; char attr_name[20]; struct device_attribute attr; char weight_attr_name[20]; struct device_attribute weight_attr; struct list_head tz_node; struct list_head cdev_node; unsigned int weight; }; struct genl_dumpit_info { const struct genl_family *family; struct genl_ops op; struct nlattr **attrs; }; enum thermal_genl_attr { THERMAL_GENL_ATTR_UNSPEC = 0, THERMAL_GENL_ATTR_TZ = 1, THERMAL_GENL_ATTR_TZ_ID = 2, THERMAL_GENL_ATTR_TZ_TEMP = 3, THERMAL_GENL_ATTR_TZ_TRIP = 4, THERMAL_GENL_ATTR_TZ_TRIP_ID = 5, THERMAL_GENL_ATTR_TZ_TRIP_TYPE = 6, THERMAL_GENL_ATTR_TZ_TRIP_TEMP = 7, THERMAL_GENL_ATTR_TZ_TRIP_HYST = 8, THERMAL_GENL_ATTR_TZ_MODE = 9, THERMAL_GENL_ATTR_TZ_NAME = 10, THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT = 11, THERMAL_GENL_ATTR_TZ_GOV = 12, THERMAL_GENL_ATTR_TZ_GOV_NAME = 13, THERMAL_GENL_ATTR_CDEV = 14, THERMAL_GENL_ATTR_CDEV_ID = 15, THERMAL_GENL_ATTR_CDEV_CUR_STATE = 16, THERMAL_GENL_ATTR_CDEV_MAX_STATE = 17, THERMAL_GENL_ATTR_CDEV_NAME = 18, THERMAL_GENL_ATTR_GOV_NAME = 19, __THERMAL_GENL_ATTR_MAX = 20, }; enum thermal_genl_sampling { THERMAL_GENL_SAMPLING_TEMP = 0, __THERMAL_GENL_SAMPLING_MAX = 1, }; enum thermal_genl_event { THERMAL_GENL_EVENT_UNSPEC = 0, THERMAL_GENL_EVENT_TZ_CREATE = 1, THERMAL_GENL_EVENT_TZ_DELETE = 2, THERMAL_GENL_EVENT_TZ_DISABLE = 3, THERMAL_GENL_EVENT_TZ_ENABLE = 4, THERMAL_GENL_EVENT_TZ_TRIP_UP = 5, THERMAL_GENL_EVENT_TZ_TRIP_DOWN = 6, THERMAL_GENL_EVENT_TZ_TRIP_CHANGE = 7, THERMAL_GENL_EVENT_TZ_TRIP_ADD = 8, THERMAL_GENL_EVENT_TZ_TRIP_DELETE = 9, THERMAL_GENL_EVENT_CDEV_ADD = 10, THERMAL_GENL_EVENT_CDEV_DELETE = 11, THERMAL_GENL_EVENT_CDEV_STATE_UPDATE = 12, THERMAL_GENL_EVENT_TZ_GOV_CHANGE = 13, __THERMAL_GENL_EVENT_MAX = 14, }; enum thermal_genl_cmd { THERMAL_GENL_CMD_UNSPEC = 0, THERMAL_GENL_CMD_TZ_GET_ID = 1, THERMAL_GENL_CMD_TZ_GET_TRIP = 2, THERMAL_GENL_CMD_TZ_GET_TEMP = 3, THERMAL_GENL_CMD_TZ_GET_GOV = 4, THERMAL_GENL_CMD_TZ_GET_MODE = 5, THERMAL_GENL_CMD_CDEV_GET = 6, __THERMAL_GENL_CMD_MAX = 7, }; struct param { struct nlattr **attrs; struct sk_buff *msg; const char *name; int tz_id; int cdev_id; int trip_id; int trip_temp; int trip_type; int trip_hyst; int temp; int cdev_state; int cdev_max_state; }; typedef int (*cb_t)(struct param *); struct thermal_hwmon_device { char type[20]; struct device *device; int count; struct list_head tz_list; struct list_head node; }; struct thermal_hwmon_attr { struct device_attribute attr; char name[16]; }; struct thermal_hwmon_temp { struct list_head hwmon_node; struct thermal_zone_device *tz; struct thermal_hwmon_attr temp_input; struct thermal_hwmon_attr temp_crit; }; struct trace_event_raw_thermal_power_allocator { struct trace_entry ent; int tz_id; u32 __data_loc_req_power; u32 total_req_power; u32 __data_loc_granted_power; u32 total_granted_power; size_t num_actors; u32 power_range; u32 max_allocatable_power; int current_temp; s32 delta_temp; char __data[0]; }; struct trace_event_raw_thermal_power_allocator_pid { struct trace_entry ent; int tz_id; s32 err; s32 err_integral; s64 p; s64 i; s64 d; s32 output; char __data[0]; }; struct trace_event_data_offsets_thermal_power_allocator { u32 req_power; u32 granted_power; }; struct trace_event_data_offsets_thermal_power_allocator_pid {}; typedef void (*btf_trace_thermal_power_allocator)(void *, struct thermal_zone_device *, u32 *, u32, u32 *, u32, size_t, u32, u32, int, s32); typedef void (*btf_trace_thermal_power_allocator_pid)(void *, struct thermal_zone_device *, s32, s32, s64, s64, s64, s32); struct power_allocator_params { bool allocated_tzp; s64 err_integral; s32 prev_err; int trip_switch_on; int trip_max_desired_temperature; u32 sustainable_power; }; enum devfreq_timer { DEVFREQ_TIMER_DEFERRABLE = 0, DEVFREQ_TIMER_DELAYED = 1, DEVFREQ_TIMER_NUM = 2, }; struct devfreq_dev_profile { long unsigned int initial_freq; unsigned int polling_ms; enum devfreq_timer timer; bool is_cooling_device; int (*target)(struct device *, long unsigned int *, u32); int (*get_dev_status)(struct device *, struct devfreq_dev_status *); int (*get_cur_freq)(struct device *, long unsigned int *); void (*exit)(struct device *); long unsigned int *freq_table; unsigned int max_state; }; struct devfreq_stats { unsigned int total_trans; unsigned int *trans_table; u64 *time_in_state; u64 last_update; }; struct devfreq_governor; struct devfreq { struct list_head node; struct mutex lock; struct device dev; struct devfreq_dev_profile *profile; const struct devfreq_governor *governor; struct opp_table *opp_table; struct notifier_block nb; struct delayed_work work; long unsigned int previous_freq; struct devfreq_dev_status last_status; void *data; struct dev_pm_qos_request user_min_freq_req; struct dev_pm_qos_request user_max_freq_req; long unsigned int scaling_min_freq; long unsigned int scaling_max_freq; bool stop_polling; long unsigned int suspend_freq; long unsigned int resume_freq; atomic_t suspend_count; struct devfreq_stats stats; struct srcu_notifier_head transition_notifier_list; struct thermal_cooling_device *cdev; struct notifier_block nb_min; struct notifier_block nb_max; }; struct devfreq_governor { struct list_head node; const char name[16]; const u64 attrs; const u64 flags; int (*get_target_freq)(struct devfreq *, long unsigned int *); int (*event_handler)(struct devfreq *, unsigned int, void *); }; struct devfreq_cooling_power { int (*get_real_power)(struct devfreq *, u32 *, long unsigned int, long unsigned int); }; struct devfreq_cooling_device { struct thermal_cooling_device *cdev; struct devfreq *devfreq; long unsigned int cooling_state; u32 *freq_table; size_t max_state; struct devfreq_cooling_power *power_ops; u32 res_util; int capped_state; struct dev_pm_qos_request req_max_freq; struct em_perf_domain *em_pd; }; struct _thermal_state { u64 next_check; u64 last_interrupt_time; struct delayed_work therm_work; long unsigned int count; long unsigned int last_count; long unsigned int max_time_ms; long unsigned int total_time_ms; bool rate_control_active; bool new_event; u8 level; u8 sample_index; u8 sample_count; u8 average; u8 baseline_temp; u8 temp_samples[3]; }; struct thermal_state { struct _thermal_state core_throttle; struct _thermal_state core_power_limit; struct _thermal_state package_throttle; struct _thermal_state package_power_limit; struct _thermal_state core_thresh0; struct _thermal_state core_thresh1; struct _thermal_state pkg_thresh0; struct _thermal_state pkg_thresh1; }; struct watchdog_info { __u32 options; __u32 firmware_version; __u8 identity[32]; }; struct watchdog_device; struct watchdog_ops { struct module *owner; int (*start)(struct watchdog_device *); int (*stop)(struct watchdog_device *); int (*ping)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *); int (*set_timeout)(struct watchdog_device *, unsigned int); int (*set_pretimeout)(struct watchdog_device *, unsigned int); unsigned int (*get_timeleft)(struct watchdog_device *); int (*restart)(struct watchdog_device *, long unsigned int, void *); long int (*ioctl)(struct watchdog_device *, unsigned int, long unsigned int); }; struct watchdog_governor; struct watchdog_core_data; struct watchdog_device { int id; struct device *parent; const struct attribute_group **groups; const struct watchdog_info *info; const struct watchdog_ops *ops; const struct watchdog_governor *gov; unsigned int bootstatus; unsigned int timeout; unsigned int pretimeout; unsigned int min_timeout; unsigned int max_timeout; unsigned int min_hw_heartbeat_ms; unsigned int max_hw_heartbeat_ms; struct notifier_block reboot_nb; struct notifier_block restart_nb; void *driver_data; struct watchdog_core_data *wd_data; long unsigned int status; struct list_head deferred; }; struct watchdog_governor { const char name[20]; void (*pretimeout)(struct watchdog_device *); }; struct watchdog_core_data { struct device dev; struct cdev cdev; struct watchdog_device *wdd; struct mutex lock; ktime_t last_keepalive; ktime_t last_hw_keepalive; ktime_t open_deadline; struct hrtimer timer; struct kthread_work work; long unsigned int status; }; struct watchdog_pretimeout { struct watchdog_device *wdd; struct list_head entry; }; struct governor_priv { struct watchdog_governor *gov; struct list_head entry; }; struct dm_kobject_holder { struct kobject kobj; struct completion completion; }; enum dev_type { DEV_UNKNOWN = 0, DEV_X1 = 1, DEV_X2 = 2, DEV_X4 = 3, DEV_X8 = 4, DEV_X16 = 5, DEV_X32 = 6, DEV_X64 = 7, }; enum hw_event_mc_err_type { HW_EVENT_ERR_CORRECTED = 0, HW_EVENT_ERR_UNCORRECTED = 1, HW_EVENT_ERR_DEFERRED = 2, HW_EVENT_ERR_FATAL = 3, HW_EVENT_ERR_INFO = 4, }; enum mem_type { MEM_EMPTY = 0, MEM_RESERVED = 1, MEM_UNKNOWN = 2, MEM_FPM = 3, MEM_EDO = 4, MEM_BEDO = 5, MEM_SDR = 6, MEM_RDR = 7, MEM_DDR = 8, MEM_RDDR = 9, MEM_RMBS = 10, MEM_DDR2 = 11, MEM_FB_DDR2 = 12, MEM_RDDR2 = 13, MEM_XDR = 14, MEM_DDR3 = 15, MEM_RDDR3 = 16, MEM_LRDDR3 = 17, MEM_LPDDR3 = 18, MEM_DDR4 = 19, MEM_RDDR4 = 20, MEM_LRDDR4 = 21, MEM_LPDDR4 = 22, MEM_DDR5 = 23, MEM_NVDIMM = 24, MEM_WIO2 = 25, }; enum edac_type { EDAC_UNKNOWN = 0, EDAC_NONE = 1, EDAC_RESERVED = 2, EDAC_PARITY = 3, EDAC_EC = 4, EDAC_SECDED = 5, EDAC_S2ECD2ED = 6, EDAC_S4ECD4ED = 7, EDAC_S8ECD8ED = 8, EDAC_S16ECD16ED = 9, }; enum scrub_type { SCRUB_UNKNOWN = 0, SCRUB_NONE = 1, SCRUB_SW_PROG = 2, SCRUB_SW_SRC = 3, SCRUB_SW_PROG_SRC = 4, SCRUB_SW_TUNABLE = 5, SCRUB_HW_PROG = 6, SCRUB_HW_SRC = 7, SCRUB_HW_PROG_SRC = 8, SCRUB_HW_TUNABLE = 9, }; enum edac_mc_layer_type { EDAC_MC_LAYER_BRANCH = 0, EDAC_MC_LAYER_CHANNEL = 1, EDAC_MC_LAYER_SLOT = 2, EDAC_MC_LAYER_CHIP_SELECT = 3, EDAC_MC_LAYER_ALL_MEM = 4, }; struct edac_mc_layer { enum edac_mc_layer_type type; unsigned int size; bool is_virt_csrow; }; struct mem_ctl_info; struct dimm_info { struct device dev; char label[32]; unsigned int location[3]; struct mem_ctl_info *mci; unsigned int idx; u32 grain; enum dev_type dtype; enum mem_type mtype; enum edac_type edac_mode; u32 nr_pages; unsigned int csrow; unsigned int cschannel; u16 smbios_handle; u32 ce_count; u32 ue_count; }; struct mcidev_sysfs_attribute; struct edac_raw_error_desc { char location[256]; char label[296]; long int grain; u16 error_count; enum hw_event_mc_err_type type; int top_layer; int mid_layer; int low_layer; long unsigned int page_frame_number; long unsigned int offset_in_page; long unsigned int syndrome; const char *msg; const char *other_detail; }; struct csrow_info; struct mem_ctl_info { struct device dev; struct bus_type *bus; struct list_head link; struct module *owner; long unsigned int mtype_cap; long unsigned int edac_ctl_cap; long unsigned int edac_cap; long unsigned int scrub_cap; enum scrub_type scrub_mode; int (*set_sdram_scrub_rate)(struct mem_ctl_info *, u32); int (*get_sdram_scrub_rate)(struct mem_ctl_info *); void (*edac_check)(struct mem_ctl_info *); long unsigned int (*ctl_page_to_phys)(struct mem_ctl_info *, long unsigned int); int mc_idx; struct csrow_info **csrows; unsigned int nr_csrows; unsigned int num_cschannel; unsigned int n_layers; struct edac_mc_layer *layers; bool csbased; unsigned int tot_dimms; struct dimm_info **dimms; struct device *pdev; const char *mod_name; const char *ctl_name; const char *dev_name; void *pvt_info; long unsigned int start_time; u32 ce_noinfo_count; u32 ue_noinfo_count; u32 ue_mc; u32 ce_mc; struct completion complete; const struct mcidev_sysfs_attribute *mc_driver_sysfs_attributes; struct delayed_work work; struct edac_raw_error_desc error_desc; int op_state; struct dentry *debugfs; u8 fake_inject_layer[3]; bool fake_inject_ue; u16 fake_inject_count; }; struct rank_info { int chan_idx; struct csrow_info *csrow; struct dimm_info *dimm; u32 ce_count; }; struct csrow_info { struct device dev; long unsigned int first_page; long unsigned int last_page; long unsigned int page_mask; int csrow_idx; u32 ue_count; u32 ce_count; struct mem_ctl_info *mci; u32 nr_channels; struct rank_info **channels; }; struct edac_device_counter { u32 ue_count; u32 ce_count; }; struct edac_device_ctl_info; struct edac_dev_sysfs_attribute { struct attribute attr; ssize_t (*show)(struct edac_device_ctl_info *, char *); ssize_t (*store)(struct edac_device_ctl_info *, const char *, size_t); }; struct edac_device_instance; struct edac_device_ctl_info { struct list_head link; struct module *owner; int dev_idx; int log_ue; int log_ce; int panic_on_ue; unsigned int poll_msec; long unsigned int delay; struct edac_dev_sysfs_attribute *sysfs_attributes; struct bus_type *edac_subsys; int op_state; struct delayed_work work; void (*edac_check)(struct edac_device_ctl_info *); struct device *dev; const char *mod_name; const char *ctl_name; const char *dev_name; void *pvt_info; long unsigned int start_time; struct completion removal_complete; char name[32]; u32 nr_instances; struct edac_device_instance *instances; struct edac_device_counter counters; struct kobject kobj; }; struct edac_device_block; struct edac_dev_sysfs_block_attribute { struct attribute attr; ssize_t (*show)(struct kobject *, struct attribute *, char *); ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); struct edac_device_block *block; unsigned int value; }; struct edac_device_block { struct edac_device_instance *instance; char name[32]; struct edac_device_counter counters; int nr_attribs; struct edac_dev_sysfs_block_attribute *block_attributes; struct kobject kobj; }; struct edac_device_instance { struct edac_device_ctl_info *ctl; char name[35]; struct edac_device_counter counters; u32 nr_blocks; struct edac_device_block *blocks; struct kobject kobj; }; struct dev_ch_attribute { struct device_attribute attr; unsigned int channel; }; struct ctl_info_attribute { struct attribute attr; ssize_t (*show)(struct edac_device_ctl_info *, char *); ssize_t (*store)(struct edac_device_ctl_info *, const char *, size_t); }; struct instance_attribute { struct attribute attr; ssize_t (*show)(struct edac_device_instance *, char *); ssize_t (*store)(struct edac_device_instance *, const char *, size_t); }; struct edac_pci_counter { atomic_t pe_count; atomic_t npe_count; }; struct edac_pci_ctl_info { struct list_head link; int pci_idx; struct bus_type *edac_subsys; int op_state; struct delayed_work work; void (*edac_check)(struct edac_pci_ctl_info *); struct device *dev; const char *mod_name; const char *ctl_name; const char *dev_name; void *pvt_info; long unsigned int start_time; struct completion complete; char name[32]; struct edac_pci_counter counters; struct kobject kobj; }; struct edac_pci_gen_data { int edac_idx; }; struct instance_attribute___2 { struct attribute attr; ssize_t (*show)(struct edac_pci_ctl_info *, char *); ssize_t (*store)(struct edac_pci_ctl_info *, const char *, size_t); }; struct edac_pci_dev_attribute { struct attribute attr; void *value; ssize_t (*show)(void *, char *); ssize_t (*store)(void *, const char *, size_t); }; typedef void (*pci_parity_check_fn_t)(struct pci_dev *); struct ghes_pvt { struct mem_ctl_info *mci; char other_detail[400]; char msg[80]; }; struct ghes_hw_desc { int num_dimms; struct dimm_info *dimms; }; struct memdev_dmi_entry { u8 type; u8 length; u16 handle; u16 phys_mem_array_handle; u16 mem_err_info_handle; u16 total_width; u16 data_width; u16 size; u8 form_factor; u8 device_set; u8 device_locator; u8 bank_locator; u8 memory_type; u16 type_detail; u16 speed; u8 manufacturer; u8 serial_number; u8 asset_tag; u8 part_number; u8 attributes; u32 extended_size; u16 conf_mem_clk_speed; } __attribute__((packed)); enum opp_table_access { OPP_TABLE_ACCESS_UNKNOWN = 0, OPP_TABLE_ACCESS_EXCLUSIVE = 1, OPP_TABLE_ACCESS_SHARED = 2, }; struct icc_path; struct dev_pm_opp___2; struct dev_pm_set_opp_data; struct dev_pm_opp_supply; struct opp_table___2 { struct list_head node; struct list_head lazy; struct blocking_notifier_head head; struct list_head dev_list; struct list_head opp_list; struct kref kref; struct mutex lock; struct device_node *np; long unsigned int clock_latency_ns_max; unsigned int voltage_tolerance_v1; unsigned int parsed_static_opps; enum opp_table_access shared_opp; long unsigned int current_rate; struct dev_pm_opp___2 *current_opp; struct dev_pm_opp___2 *suspend_opp; struct mutex genpd_virt_dev_lock; struct device **genpd_virt_devs; struct opp_table___2 **required_opp_tables; unsigned int required_opp_count; unsigned int *supported_hw; unsigned int supported_hw_count; const char *prop_name; struct clk *clk; struct regulator **regulators; int regulator_count; struct icc_path **paths; unsigned int path_count; bool enabled; bool genpd_performance_state; bool is_genpd; int (*set_opp)(struct dev_pm_set_opp_data *); struct dev_pm_opp_supply *sod_supplies; struct dev_pm_set_opp_data *set_opp_data; struct dentry *dentry; char dentry_name[255]; }; struct dev_pm_opp_icc_bw; struct dev_pm_opp___2 { struct list_head node; struct kref kref; bool available; bool dynamic; bool turbo; bool suspend; bool removed; unsigned int pstate; long unsigned int rate; unsigned int level; struct dev_pm_opp_supply *supplies; struct dev_pm_opp_icc_bw *bandwidth; long unsigned int clock_latency_ns; struct dev_pm_opp___2 **required_opps; struct opp_table___2 *opp_table; struct device_node *np; struct dentry *dentry; }; enum dev_pm_opp_event { OPP_EVENT_ADD = 0, OPP_EVENT_REMOVE = 1, OPP_EVENT_ENABLE = 2, OPP_EVENT_DISABLE = 3, OPP_EVENT_ADJUST_VOLTAGE = 4, }; struct dev_pm_opp_supply { long unsigned int u_volt; long unsigned int u_volt_min; long unsigned int u_volt_max; long unsigned int u_amp; }; struct dev_pm_opp_icc_bw { u32 avg; u32 peak; }; struct dev_pm_opp_info { long unsigned int rate; struct dev_pm_opp_supply *supplies; }; struct dev_pm_set_opp_data { struct dev_pm_opp_info old_opp; struct dev_pm_opp_info new_opp; struct regulator **regulators; unsigned int regulator_count; struct clk *clk; struct device *dev; }; struct opp_device { struct list_head node; const struct device *dev; struct dentry *dentry; }; struct cpufreq_policy_data { struct cpufreq_cpuinfo cpuinfo; struct cpufreq_frequency_table *freq_table; unsigned int cpu; unsigned int min; unsigned int max; }; struct freq_attr { struct attribute attr; ssize_t (*show)(struct cpufreq_policy *, char *); ssize_t (*store)(struct cpufreq_policy *, const char *, size_t); }; struct cpufreq_driver { char name[16]; u16 flags; void *driver_data; int (*init)(struct cpufreq_policy *); int (*verify)(struct cpufreq_policy_data *); int (*setpolicy)(struct cpufreq_policy *); int (*target)(struct cpufreq_policy *, unsigned int, unsigned int); int (*target_index)(struct cpufreq_policy *, unsigned int); unsigned int (*fast_switch)(struct cpufreq_policy *, unsigned int); void (*adjust_perf)(unsigned int, long unsigned int, long unsigned int, long unsigned int); unsigned int (*get_intermediate)(struct cpufreq_policy *, unsigned int); int (*target_intermediate)(struct cpufreq_policy *, unsigned int); unsigned int (*get)(unsigned int); void (*update_limits)(unsigned int); int (*bios_limit)(int, unsigned int *); int (*online)(struct cpufreq_policy *); int (*offline)(struct cpufreq_policy *); int (*exit)(struct cpufreq_policy *); int (*suspend)(struct cpufreq_policy *); int (*resume)(struct cpufreq_policy *); void (*ready)(struct cpufreq_policy *); struct freq_attr **attr; bool boost_enabled; int (*set_boost)(struct cpufreq_policy *, int); }; struct cpufreq_stats { unsigned int total_trans; long long unsigned int last_time; unsigned int max_state; unsigned int state_num; unsigned int last_index; u64 *time_in_state; unsigned int *freq_table; unsigned int *trans_table; unsigned int reset_pending; long long unsigned int reset_time; }; enum { OD_NORMAL_SAMPLE = 0, OD_SUB_SAMPLE = 1, }; struct dbs_data { struct gov_attr_set attr_set; void *tuners; unsigned int ignore_nice_load; unsigned int sampling_rate; unsigned int sampling_down_factor; unsigned int up_threshold; unsigned int io_is_busy; }; struct policy_dbs_info { struct cpufreq_policy *policy; struct mutex update_mutex; u64 last_sample_time; s64 sample_delay_ns; atomic_t work_count; struct irq_work irq_work; struct work_struct work; struct dbs_data *dbs_data; struct list_head list; unsigned int rate_mult; unsigned int idle_periods; bool is_shared; bool work_in_progress; }; struct dbs_governor { struct cpufreq_governor gov; struct kobj_type kobj_type; struct dbs_data *gdbs_data; unsigned int (*gov_dbs_update)(struct cpufreq_policy *); struct policy_dbs_info * (*alloc)(); void (*free)(struct policy_dbs_info *); int (*init)(struct dbs_data *); void (*exit)(struct dbs_data *); void (*start)(struct cpufreq_policy *); }; struct od_ops { unsigned int (*powersave_bias_target)(struct cpufreq_policy *, unsigned int, unsigned int); }; struct od_policy_dbs_info { struct policy_dbs_info policy_dbs; unsigned int freq_lo; unsigned int freq_lo_delay_us; unsigned int freq_hi_delay_us; unsigned int sample_type: 1; }; struct od_dbs_tuners { unsigned int powersave_bias; }; struct cs_policy_dbs_info { struct policy_dbs_info policy_dbs; unsigned int down_skip; unsigned int requested_freq; }; struct cs_dbs_tuners { unsigned int down_threshold; unsigned int freq_step; }; struct cpu_dbs_info { u64 prev_cpu_idle; u64 prev_update_time; u64 prev_cpu_nice; unsigned int prev_load; struct update_util_data update_util; struct policy_dbs_info *policy_dbs; }; enum acpi_preferred_pm_profiles { PM_UNSPECIFIED = 0, PM_DESKTOP = 1, PM_MOBILE = 2, PM_WORKSTATION = 3, PM_ENTERPRISE_SERVER = 4, PM_SOHO_SERVER = 5, PM_APPLIANCE_PC = 6, PM_PERFORMANCE_SERVER = 7, PM_TABLET = 8, }; struct sample { int32_t core_avg_perf; int32_t busy_scaled; u64 aperf; u64 mperf; u64 tsc; u64 time; }; struct pstate_data { int current_pstate; int min_pstate; int max_pstate; int max_pstate_physical; int perf_ctl_scaling; int scaling; int turbo_pstate; unsigned int min_freq; unsigned int max_freq; unsigned int turbo_freq; }; struct vid_data { int min; int max; int turbo; int32_t ratio; }; struct global_params { bool no_turbo; bool turbo_disabled; bool turbo_disabled_mf; int max_perf_pct; int min_perf_pct; }; struct cpudata { int cpu; unsigned int policy; struct update_util_data update_util; bool update_util_set; struct pstate_data pstate; struct vid_data vid; u64 last_update; u64 last_sample_time; u64 aperf_mperf_shift; u64 prev_aperf; u64 prev_mperf; u64 prev_tsc; u64 prev_cummulative_iowait; struct sample sample; int32_t min_perf_ratio; int32_t max_perf_ratio; struct acpi_processor_performance acpi_perf_data; bool valid_pss_table; unsigned int iowait_boost; s16 epp_powersave; s16 epp_policy; s16 epp_default; s16 epp_cached; u64 hwp_req_cached; u64 hwp_cap_cached; u64 last_io_update; unsigned int sched_flags; u32 hwp_boost_min; bool suspended; }; struct pstate_funcs { int (*get_max)(); int (*get_max_physical)(); int (*get_min)(); int (*get_turbo)(); int (*get_scaling)(); int (*get_aperf_mperf_shift)(); u64 (*get_val)(struct cpudata *, int); void (*get_vid)(struct cpudata *); }; enum { PSS = 0, PPC = 1, }; struct cpuidle_governor { char name[16]; struct list_head governor_list; unsigned int rating; int (*enable)(struct cpuidle_driver *, struct cpuidle_device *); void (*disable)(struct cpuidle_driver *, struct cpuidle_device *); int (*select)(struct cpuidle_driver *, struct cpuidle_device *, bool *); void (*reflect)(struct cpuidle_device *, int); }; struct cpuidle_state_kobj { struct cpuidle_state *state; struct cpuidle_state_usage *state_usage; struct completion kobj_unregister; struct kobject kobj; struct cpuidle_device *device; }; struct cpuidle_device_kobj { struct cpuidle_device *dev; struct completion kobj_unregister; struct kobject kobj; }; struct cpuidle_attr { struct attribute attr; ssize_t (*show)(struct cpuidle_device *, char *); ssize_t (*store)(struct cpuidle_device *, const char *, size_t); }; struct cpuidle_state_attr { struct attribute attr; ssize_t (*show)(struct cpuidle_state *, struct cpuidle_state_usage *, char *); ssize_t (*store)(struct cpuidle_state *, struct cpuidle_state_usage *, const char *, size_t); }; struct ladder_device_state { struct { u32 promotion_count; u32 demotion_count; u64 promotion_time_ns; u64 demotion_time_ns; } threshold; struct { int promotion_count; int demotion_count; } stats; }; struct ladder_device { struct ladder_device_state states[10]; }; struct menu_device { int needs_update; int tick_wakeup; u64 next_timer_ns; unsigned int bucket; unsigned int correction_factor[12]; unsigned int intervals[8]; int interval_ptr; }; struct teo_bin { unsigned int intercepts; unsigned int hits; unsigned int recent; }; struct teo_cpu { s64 time_span_ns; s64 sleep_length_ns; struct teo_bin state_bins[10]; unsigned int total; int next_recent_idx; int recent_idx[9]; }; struct pci_dev___2; struct sdhci_pci_data { struct pci_dev___2 *pdev; int slotno; int rst_n_gpio; int cd_gpio; int (*setup)(struct sdhci_pci_data *); void (*cleanup)(struct sdhci_pci_data *); }; struct led_properties { u32 color; bool color_present; const char *function; u32 func_enum; bool func_enum_present; const char *label; }; enum cpu_led_event { CPU_LED_IDLE_START = 0, CPU_LED_IDLE_END = 1, CPU_LED_START = 2, CPU_LED_STOP = 3, CPU_LED_HALTED = 4, }; struct led_trigger_cpu { bool is_active; char name[8]; struct led_trigger *_trig; }; struct dmi_memdev_info { const char *device; const char *bank; u64 size; u16 handle; u8 type; }; struct dmi_sysfs_entry { struct dmi_header dh; struct kobject kobj; int instance; int position; struct list_head list; struct kobject *child; }; struct dmi_sysfs_attribute { struct attribute attr; ssize_t (*show)(struct dmi_sysfs_entry *, char *); }; struct dmi_sysfs_mapped_attribute { struct attribute attr; ssize_t (*show)(struct dmi_sysfs_entry *, const struct dmi_header *, char *); }; typedef ssize_t (*dmi_callback)(struct dmi_sysfs_entry *, const struct dmi_header *, void *); struct find_dmi_data { struct dmi_sysfs_entry *entry; dmi_callback callback; void *private; int instance_countdown; ssize_t ret; }; struct dmi_read_state { char *buf; loff_t pos; size_t count; }; struct dmi_entry_attr_show_data { struct attribute *attr; char *buf; }; struct dmi_system_event_log { struct dmi_header header; u16 area_length; u16 header_start_offset; u16 data_start_offset; u8 access_method; u8 status; u32 change_token; union { struct { u16 index_addr; u16 data_addr; } io; u32 phys_addr32; u16 gpnv_handle; u32 access_method_address; }; u8 header_format; u8 type_descriptors_supported_count; u8 per_log_type_descriptor_length; u8 supported_log_type_descriptos[0]; } __attribute__((packed)); typedef u8 (*sel_io_reader)(const struct dmi_system_event_log *, loff_t); struct dmi_device_attribute { struct device_attribute dev_attr; int field; }; struct mafield { const char *prefix; int field; }; struct acpi_table_ibft { struct acpi_table_header header; u8 reserved[12]; }; struct firmware_map_entry { u64 start; u64 end; const char *type; struct list_head list; struct kobject kobj; }; struct memmap_attribute { struct attribute attr; ssize_t (*show)(struct firmware_map_entry *, char *); }; struct bmp_header { u16 id; u32 size; } __attribute__((packed)); typedef efi_status_t efi_query_variable_store_t(u32, long unsigned int, bool); typedef struct { u16 version; u16 length; u32 runtime_services_supported; } efi_rt_properties_table_t; struct efivar_operations { efi_get_variable_t *get_variable; efi_get_next_variable_t *get_next_variable; efi_set_variable_t *set_variable; efi_set_variable_t *set_variable_nonblocking; efi_query_variable_store_t *query_variable_store; }; struct efivars { struct kset *kset; struct kobject *kobject; const struct efivar_operations *ops; }; struct linux_efi_random_seed { u32 size; u8 bits[0]; }; struct linux_efi_memreserve { int size; atomic_t count; phys_addr_t next; struct { phys_addr_t base; phys_addr_t size; } entry[0]; }; struct efi_generic_dev_path { u8 type; u8 sub_type; u16 length; }; struct variable_validate { efi_guid_t vendor; char *name; bool (*validate)(efi_char16_t *, int, u8 *, long unsigned int); }; typedef struct { u32 version; u32 num_entries; u32 desc_size; u32 reserved; efi_memory_desc_t entry[0]; } efi_memory_attributes_table_t; struct linux_efi_tpm_eventlog { u32 size; u32 final_events_preboot_size; u8 version; u8 log[0]; }; struct efi_tcg2_final_events_table { u64 version; u64 nr_events; u8 events[0]; }; struct tpm_digest { u16 alg_id; u8 digest[64]; }; enum tpm_duration { TPM_SHORT = 0, TPM_MEDIUM = 1, TPM_LONG = 2, TPM_LONG_LONG = 3, TPM_UNDEFINED = 4, TPM_NUM_DURATIONS = 4, }; enum tcpa_event_types { PREBOOT = 0, POST_CODE = 1, UNUSED = 2, NO_ACTION = 3, SEPARATOR = 4, ACTION = 5, EVENT_TAG = 6, SCRTM_CONTENTS = 7, SCRTM_VERSION = 8, CPU_MICROCODE = 9, PLATFORM_CONFIG_FLAGS = 10, TABLE_OF_DEVICES = 11, COMPACT_HASH = 12, IPL = 13, IPL_PARTITION_DATA = 14, NONHOST_CODE = 15, NONHOST_CONFIG = 16, NONHOST_INFO = 17, }; struct tcg_efi_specid_event_algs { u16 alg_id; u16 digest_size; }; struct tcg_efi_specid_event_head { u8 signature[16]; u32 platform_class; u8 spec_version_minor; u8 spec_version_major; u8 spec_errata; u8 uintnsize; u32 num_algs; struct tcg_efi_specid_event_algs digest_sizes[0]; }; struct tcg_pcr_event { u32 pcr_idx; u32 event_type; u8 digest[20]; u32 event_size; u8 event[0]; }; struct tcg_event_field { u32 event_size; u8 event[0]; }; struct tcg_pcr_event2_head { u32 pcr_idx; u32 event_type; u32 count; struct tpm_digest digests[0]; }; typedef u64 efi_physical_addr_t; typedef struct { u64 length; u64 data; } efi_capsule_block_desc_t; struct efi_system_resource_entry_v1 { efi_guid_t fw_class; u32 fw_type; u32 fw_version; u32 lowest_supported_fw_version; u32 capsule_flags; u32 last_attempt_version; u32 last_attempt_status; }; struct efi_system_resource_table { u32 fw_resource_count; u32 fw_resource_count_max; u64 fw_resource_version; u8 entries[0]; }; struct esre_entry { union { struct efi_system_resource_entry_v1 *esre1; } esre; struct kobject kobj; struct list_head list; }; struct esre_attribute { struct attribute attr; ssize_t (*show)(struct esre_entry *, char *); ssize_t (*store)(struct esre_entry *, const char *, size_t); }; struct cper_sec_proc_generic { u64 validation_bits; u8 proc_type; u8 proc_isa; u8 proc_error_type; u8 operation; u8 flags; u8 level; u16 reserved; u64 cpu_version; char cpu_brand[128]; u64 proc_id; u64 target_addr; u64 requestor_id; u64 responder_id; u64 ip; }; struct cper_sec_proc_ia { u64 validation_bits; u64 lapic_id; u8 cpuid[48]; }; struct cper_mem_err_compact { u64 validation_bits; u16 node; u16 card; u16 module; u16 bank; u16 device; u16 row; u16 column; u16 bit_pos; u64 requestor_id; u64 responder_id; u64 target_id; u16 rank; u16 mem_array_handle; u16 mem_dev_handle; u8 extended; } __attribute__((packed)); struct cper_sec_fw_err_rec_ref { u8 record_type; u8 revision; u8 reserved[6]; u64 record_identifier; guid_t record_identifier_guid; }; struct efi_runtime_map_entry { efi_memory_desc_t md; struct kobject kobj; }; struct map_attribute { struct attribute attr; ssize_t (*show)(struct efi_runtime_map_entry *, char *); }; struct efi_acpi_dev_path { struct efi_generic_dev_path header; u32 hid; u32 uid; }; struct efi_pci_dev_path { struct efi_generic_dev_path header; u8 fn; u8 dev; }; struct efi_vendor_dev_path { struct efi_generic_dev_path header; efi_guid_t vendorguid; u8 vendordata[0]; }; struct efi_dev_path { union { struct efi_generic_dev_path header; struct efi_acpi_dev_path acpi; struct efi_pci_dev_path pci; struct efi_vendor_dev_path vendor; }; }; struct dev_header { u32 len; u32 prop_count; struct efi_dev_path path[0]; }; struct properties_header { u32 len; u32 version; u32 dev_count; struct dev_header dev_header[0]; }; struct efi_embedded_fw { struct list_head list; const char *name; const u8 *data; size_t length; }; struct efi_embedded_fw_desc { const char *name; u8 prefix[8]; u32 length; u8 sha256[32]; }; struct cper_ia_err_info { guid_t err_type; u64 validation_bits; u64 check_info; u64 target_id; u64 requestor_id; u64 responder_id; u64 ip; }; enum err_types { ERR_TYPE_CACHE = 0, ERR_TYPE_TLB = 1, ERR_TYPE_BUS = 2, ERR_TYPE_MS = 3, N_ERR_TYPES = 4, }; struct hid_device_id { __u16 bus; __u16 group; __u32 vendor; __u32 product; kernel_ulong_t driver_data; }; struct hid_item { unsigned int format; __u8 size; __u8 type; __u8 tag; union { __u8 u8; __s8 s8; __u16 u16; __s16 s16; __u32 u32; __s32 s32; __u8 *longdata; } data; }; struct hid_global { unsigned int usage_page; __s32 logical_minimum; __s32 logical_maximum; __s32 physical_minimum; __s32 physical_maximum; __s32 unit_exponent; unsigned int unit; unsigned int report_id; unsigned int report_size; unsigned int report_count; }; struct hid_local { unsigned int usage[12288]; u8 usage_size[12288]; unsigned int collection_index[12288]; unsigned int usage_index; unsigned int usage_minimum; unsigned int delimiter_depth; unsigned int delimiter_branch; }; struct hid_collection { int parent_idx; unsigned int type; unsigned int usage; unsigned int level; }; struct hid_usage { unsigned int hid; unsigned int collection_index; unsigned int usage_index; __s8 resolution_multiplier; __s8 wheel_factor; __u16 code; __u8 type; __s8 hat_min; __s8 hat_max; __s8 hat_dir; __s16 wheel_accumulated; }; struct hid_report; struct hid_input; struct hid_field { unsigned int physical; unsigned int logical; unsigned int application; struct hid_usage *usage; unsigned int maxusage; unsigned int flags; unsigned int report_offset; unsigned int report_size; unsigned int report_count; unsigned int report_type; __s32 *value; __s32 logical_minimum; __s32 logical_maximum; __s32 physical_minimum; __s32 physical_maximum; __s32 unit_exponent; unsigned int unit; struct hid_report *report; unsigned int index; struct hid_input *hidinput; __u16 dpad; }; struct hid_device; struct hid_report { struct list_head list; struct list_head hidinput_list; unsigned int id; unsigned int type; unsigned int application; struct hid_field *field[256]; unsigned int maxfield; unsigned int size; struct hid_device *device; }; struct hid_input { struct list_head list; struct hid_report *report; struct input_dev *input; const char *name; bool registered; struct list_head reports; unsigned int application; }; enum hid_type { HID_TYPE_OTHER = 0, HID_TYPE_USBMOUSE = 1, HID_TYPE_USBNONE = 2, }; struct hid_report_enum { unsigned int numbered; struct list_head report_list; struct hid_report *report_id_hash[256]; }; enum hid_battery_status { HID_BATTERY_UNKNOWN = 0, HID_BATTERY_QUERIED = 1, HID_BATTERY_REPORTED = 2, }; struct hid_driver; struct hid_ll_driver; struct hid_device { __u8 *dev_rdesc; unsigned int dev_rsize; __u8 *rdesc; unsigned int rsize; struct hid_collection *collection; unsigned int collection_size; unsigned int maxcollection; unsigned int maxapplication; __u16 bus; __u16 group; __u32 vendor; __u32 product; __u32 version; enum hid_type type; unsigned int country; struct hid_report_enum report_enum[3]; struct work_struct led_work; struct semaphore driver_input_lock; struct device dev; struct hid_driver *driver; struct hid_ll_driver *ll_driver; struct mutex ll_open_lock; unsigned int ll_open_count; struct power_supply *battery; __s32 battery_capacity; __s32 battery_min; __s32 battery_max; __s32 battery_report_type; __s32 battery_report_id; enum hid_battery_status battery_status; bool battery_avoid_query; ktime_t battery_ratelimit_time; long unsigned int status; unsigned int claimed; unsigned int quirks; bool io_started; struct list_head inputs; void *hiddev; void *hidraw; char name[128]; char phys[64]; char uniq[64]; void *driver_data; int (*ff_init)(struct hid_device *); int (*hiddev_connect)(struct hid_device *, unsigned int); void (*hiddev_disconnect)(struct hid_device *); void (*hiddev_hid_event)(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); void (*hiddev_report_event)(struct hid_device *, struct hid_report *); short unsigned int debug; struct dentry *debug_dir; struct dentry *debug_rdesc; struct dentry *debug_events; struct list_head debug_list; spinlock_t debug_list_lock; wait_queue_head_t debug_wait; }; struct hid_report_id; struct hid_usage_id; struct hid_driver { char *name; const struct hid_device_id *id_table; struct list_head dyn_list; spinlock_t dyn_lock; bool (*match)(struct hid_device *, bool); int (*probe)(struct hid_device *, const struct hid_device_id *); void (*remove)(struct hid_device *); const struct hid_report_id *report_table; int (*raw_event)(struct hid_device *, struct hid_report *, u8 *, int); const struct hid_usage_id *usage_table; int (*event)(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); void (*report)(struct hid_device *, struct hid_report *); __u8 * (*report_fixup)(struct hid_device *, __u8 *, unsigned int *); int (*input_mapping)(struct hid_device *, struct hid_input *, struct hid_field *, struct hid_usage *, long unsigned int **, int *); int (*input_mapped)(struct hid_device *, struct hid_input *, struct hid_field *, struct hid_usage *, long unsigned int **, int *); int (*input_configured)(struct hid_device *, struct hid_input *); void (*feature_mapping)(struct hid_device *, struct hid_field *, struct hid_usage *); int (*suspend)(struct hid_device *, pm_message_t); int (*resume)(struct hid_device *); int (*reset_resume)(struct hid_device *); struct device_driver driver; }; struct hid_ll_driver { int (*start)(struct hid_device *); void (*stop)(struct hid_device *); int (*open)(struct hid_device *); void (*close)(struct hid_device *); int (*power)(struct hid_device *, int); int (*parse)(struct hid_device *); void (*request)(struct hid_device *, struct hid_report *, int); int (*wait)(struct hid_device *); int (*raw_request)(struct hid_device *, unsigned char, __u8 *, size_t, unsigned char, int); int (*output_report)(struct hid_device *, __u8 *, size_t); int (*idle)(struct hid_device *, int, int, int); bool (*may_wakeup)(struct hid_device *); }; struct hid_parser { struct hid_global global; struct hid_global global_stack[4]; unsigned int global_stack_ptr; struct hid_local local; unsigned int *collection_stack; unsigned int collection_stack_ptr; unsigned int collection_stack_size; struct hid_device *device; unsigned int scan_flags; }; struct hid_report_id { __u32 report_type; }; struct hid_usage_id { __u32 usage_hid; __u32 usage_type; __u32 usage_code; }; struct hiddev { int minor; int exist; int open; struct mutex existancelock; wait_queue_head_t wait; struct hid_device *hid; struct list_head list; spinlock_t list_lock; bool initialized; }; struct hidraw { unsigned int minor; int exist; int open; wait_queue_head_t wait; struct hid_device *hid; struct device *dev; spinlock_t list_lock; struct list_head list; }; struct hid_dynid { struct list_head list; struct hid_device_id id; }; typedef bool (*hid_usage_cmp_t)(struct hid_usage *, unsigned int, unsigned int); struct quirks_list_struct { struct hid_device_id hid_bl_item; struct list_head node; }; struct hid_debug_list { struct { union { struct __kfifo kfifo; char *type; const char *const_type; char (*rectype)[0]; char *ptr; const char *ptr_const; }; char buf[0]; } hid_debug_fifo; struct fasync_struct *fasync; struct hid_device *hdev; struct list_head node; struct mutex read_mutex; }; struct hid_usage_entry { unsigned int page; unsigned int usage; const char *description; }; struct hidraw_devinfo { __u32 bustype; __s16 vendor; __s16 product; }; struct hidraw_report { __u8 *value; int len; }; struct hidraw_list { struct hidraw_report buffer[64]; int head; int tail; struct fasync_struct *fasync; struct hidraw *hidraw; struct list_head node; struct mutex read_mutex; }; struct ts_dmi_data { struct efi_embedded_fw_desc embedded_fw; const char *acpi_name; const struct property_entry *properties; }; enum ppfear_regs { SPT_PMC_XRAM_PPFEAR0A = 1424, SPT_PMC_XRAM_PPFEAR0B = 1425, SPT_PMC_XRAM_PPFEAR0C = 1426, SPT_PMC_XRAM_PPFEAR0D = 1427, SPT_PMC_XRAM_PPFEAR1A = 1428, }; struct pmc_bit_map { const char *name; u32 bit_mask; }; struct pmc_reg_map { const struct pmc_bit_map **pfear_sts; const struct pmc_bit_map *mphy_sts; const struct pmc_bit_map *pll_sts; const struct pmc_bit_map **slps0_dbg_maps; const struct pmc_bit_map *ltr_show_sts; const struct pmc_bit_map *msr_sts; const struct pmc_bit_map **lpm_sts; const u32 slp_s0_offset; const int slp_s0_res_counter_step; const u32 ltr_ignore_offset; const int regmap_length; const u32 ppfear0_offset; const int ppfear_buckets; const u32 pm_cfg_offset; const int pm_read_disable_bit; const u32 slps0_dbg_offset; const u32 ltr_ignore_max; const u32 pm_vric1_offset; const int lpm_num_maps; const int lpm_res_counter_step_x2; const u32 lpm_sts_latch_en_offset; const u32 lpm_en_offset; const u32 lpm_priority_offset; const u32 lpm_residency_offset; const u32 lpm_status_offset; const u32 lpm_live_status_offset; const u32 etr3_offset; }; struct pmc_dev { u32 base_addr; void *regbase; const struct pmc_reg_map *map; struct dentry *dbgfs_dir; int pmc_xram_read_bit; struct mutex lock; bool check_counters; u64 pc10_counter; u64 s0ix_counter; int num_lpm_modes; int lpm_en_modes[8]; u32 *lpm_req_regs; }; struct intel_scu_ipc_data { struct resource mem; int irq; }; struct intel_scu_ipc_dev___2 { struct device dev; struct resource mem; struct module *owner; int irq; void *ipc_base; struct completion cmd_complete; }; struct intel_scu_ipc_devres { struct intel_scu_ipc_dev___2 *scu; }; struct pmc_reg_map___2 { const struct pmc_bit_map *d3_sts_0; const struct pmc_bit_map *d3_sts_1; const struct pmc_bit_map *func_dis; const struct pmc_bit_map *func_dis_2; const struct pmc_bit_map *pss; }; struct pmc_data { const struct pmc_reg_map___2 *map; const struct pmc_clk *clks; }; struct pmc_dev___2 { u32 base_addr; void *regmap; const struct pmc_reg_map___2 *map; struct dentry *dbgfs_dir; bool init; }; enum ec_status { EC_RES_SUCCESS = 0, EC_RES_INVALID_COMMAND = 1, EC_RES_ERROR = 2, EC_RES_INVALID_PARAM = 3, EC_RES_ACCESS_DENIED = 4, EC_RES_INVALID_RESPONSE = 5, EC_RES_INVALID_VERSION = 6, EC_RES_INVALID_CHECKSUM = 7, EC_RES_IN_PROGRESS = 8, EC_RES_UNAVAILABLE = 9, EC_RES_TIMEOUT = 10, EC_RES_OVERFLOW = 11, EC_RES_INVALID_HEADER = 12, EC_RES_REQUEST_TRUNCATED = 13, EC_RES_RESPONSE_TOO_BIG = 14, EC_RES_BUS_ERROR = 15, EC_RES_BUSY = 16, EC_RES_INVALID_HEADER_VERSION = 17, EC_RES_INVALID_HEADER_CRC = 18, EC_RES_INVALID_DATA_CRC = 19, EC_RES_DUP_UNAVAILABLE = 20, }; enum host_event_code { EC_HOST_EVENT_LID_CLOSED = 1, EC_HOST_EVENT_LID_OPEN = 2, EC_HOST_EVENT_POWER_BUTTON = 3, EC_HOST_EVENT_AC_CONNECTED = 4, EC_HOST_EVENT_AC_DISCONNECTED = 5, EC_HOST_EVENT_BATTERY_LOW = 6, EC_HOST_EVENT_BATTERY_CRITICAL = 7, EC_HOST_EVENT_BATTERY = 8, EC_HOST_EVENT_THERMAL_THRESHOLD = 9, EC_HOST_EVENT_DEVICE = 10, EC_HOST_EVENT_THERMAL = 11, EC_HOST_EVENT_USB_CHARGER = 12, EC_HOST_EVENT_KEY_PRESSED = 13, EC_HOST_EVENT_INTERFACE_READY = 14, EC_HOST_EVENT_KEYBOARD_RECOVERY = 15, EC_HOST_EVENT_THERMAL_SHUTDOWN = 16, EC_HOST_EVENT_BATTERY_SHUTDOWN = 17, EC_HOST_EVENT_THROTTLE_START = 18, EC_HOST_EVENT_THROTTLE_STOP = 19, EC_HOST_EVENT_HANG_DETECT = 20, EC_HOST_EVENT_HANG_REBOOT = 21, EC_HOST_EVENT_PD_MCU = 22, EC_HOST_EVENT_BATTERY_STATUS = 23, EC_HOST_EVENT_PANIC = 24, EC_HOST_EVENT_KEYBOARD_FASTBOOT = 25, EC_HOST_EVENT_RTC = 26, EC_HOST_EVENT_MKBP = 27, EC_HOST_EVENT_USB_MUX = 28, EC_HOST_EVENT_MODE_CHANGE = 29, EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT = 30, EC_HOST_EVENT_WOV = 31, EC_HOST_EVENT_INVALID = 32, }; struct ec_host_request { uint8_t struct_version; uint8_t checksum; uint16_t command; uint8_t command_version; uint8_t reserved; uint16_t data_len; }; struct ec_params_hello { uint32_t in_data; }; struct ec_response_hello { uint32_t out_data; }; struct ec_params_get_cmd_versions { uint8_t cmd; }; struct ec_response_get_cmd_versions { uint32_t version_mask; }; enum ec_comms_status { EC_COMMS_STATUS_PROCESSING = 1, }; struct ec_response_get_comms_status { uint32_t flags; }; struct ec_response_get_protocol_info { uint32_t protocol_versions; uint16_t max_request_packet_size; uint16_t max_response_packet_size; uint32_t flags; }; enum ec_led_colors { EC_LED_COLOR_RED = 0, EC_LED_COLOR_GREEN = 1, EC_LED_COLOR_BLUE = 2, EC_LED_COLOR_YELLOW = 3, EC_LED_COLOR_WHITE = 4, EC_LED_COLOR_AMBER = 5, EC_LED_COLOR_COUNT = 6, }; enum motionsense_command { MOTIONSENSE_CMD_DUMP = 0, MOTIONSENSE_CMD_INFO = 1, MOTIONSENSE_CMD_EC_RATE = 2, MOTIONSENSE_CMD_SENSOR_ODR = 3, MOTIONSENSE_CMD_SENSOR_RANGE = 4, MOTIONSENSE_CMD_KB_WAKE_ANGLE = 5, MOTIONSENSE_CMD_DATA = 6, MOTIONSENSE_CMD_FIFO_INFO = 7, MOTIONSENSE_CMD_FIFO_FLUSH = 8, MOTIONSENSE_CMD_FIFO_READ = 9, MOTIONSENSE_CMD_PERFORM_CALIB = 10, MOTIONSENSE_CMD_SENSOR_OFFSET = 11, MOTIONSENSE_CMD_LIST_ACTIVITIES = 12, MOTIONSENSE_CMD_SET_ACTIVITY = 13, MOTIONSENSE_CMD_LID_ANGLE = 14, MOTIONSENSE_CMD_FIFO_INT_ENABLE = 15, MOTIONSENSE_CMD_SPOOF = 16, MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE = 17, MOTIONSENSE_CMD_SENSOR_SCALE = 18, MOTIONSENSE_NUM_CMDS = 19, }; struct ec_response_motion_sensor_data { uint8_t flags; uint8_t sensor_num; union { int16_t data[3]; struct { uint16_t reserved; uint32_t timestamp; } __attribute__((packed)); struct { uint8_t activity; uint8_t state; int16_t add_info[2]; }; }; } __attribute__((packed)); struct ec_response_motion_sense_fifo_info { uint16_t size; uint16_t count; uint32_t timestamp; uint16_t total_lost; uint16_t lost[0]; } __attribute__((packed)); struct ec_response_motion_sense_fifo_data { uint32_t number_data; struct ec_response_motion_sensor_data data[0]; }; struct ec_motion_sense_activity { uint8_t sensor_num; uint8_t activity; uint8_t enable; uint8_t reserved; uint16_t parameters[3]; }; struct ec_params_motion_sense { uint8_t cmd; union { struct { uint8_t max_sensor_count; } dump; struct { int16_t data; } kb_wake_angle; struct { uint8_t sensor_num; } info; struct { uint8_t sensor_num; } info_3; struct { uint8_t sensor_num; } data; struct { uint8_t sensor_num; } fifo_flush; struct { uint8_t sensor_num; } perform_calib; struct { uint8_t sensor_num; } list_activities; struct { uint8_t sensor_num; uint8_t roundup; uint16_t reserved; int32_t data; } ec_rate; struct { uint8_t sensor_num; uint8_t roundup; uint16_t reserved; int32_t data; } sensor_odr; struct { uint8_t sensor_num; uint8_t roundup; uint16_t reserved; int32_t data; } sensor_range; struct { uint8_t sensor_num; uint16_t flags; int16_t temp; int16_t offset[3]; } __attribute__((packed)) sensor_offset; struct { uint8_t sensor_num; uint16_t flags; int16_t temp; uint16_t scale[3]; } __attribute__((packed)) sensor_scale; struct { uint32_t max_data_vector; } fifo_read; struct ec_motion_sense_activity set_activity; struct { int8_t enable; } fifo_int_enable; struct { uint8_t sensor_id; uint8_t spoof_enable; uint8_t reserved; int16_t components[3]; } __attribute__((packed)) spoof; struct { int16_t lid_angle; int16_t hys_degree; } tablet_mode_threshold; }; } __attribute__((packed)); struct ec_response_motion_sense { union { struct { uint8_t module_flags; uint8_t sensor_count; struct ec_response_motion_sensor_data sensor[0]; } __attribute__((packed)) dump; struct { uint8_t type; uint8_t location; uint8_t chip; } info; struct { uint8_t type; uint8_t location; uint8_t chip; uint32_t min_frequency; uint32_t max_frequency; uint32_t fifo_max_event_count; } info_3; struct ec_response_motion_sensor_data data; struct { int32_t ret; } ec_rate; struct { int32_t ret; } sensor_odr; struct { int32_t ret; } sensor_range; struct { int32_t ret; } kb_wake_angle; struct { int32_t ret; } fifo_int_enable; struct { int32_t ret; } spoof; struct { int16_t temp; int16_t offset[3]; } sensor_offset; struct { int16_t temp; int16_t offset[3]; } perform_calib; struct { int16_t temp; uint16_t scale[3]; } sensor_scale; struct ec_response_motion_sense_fifo_info fifo_info; struct ec_response_motion_sense_fifo_info fifo_flush; struct ec_response_motion_sense_fifo_data fifo_read; struct { uint16_t reserved; uint32_t enabled; uint32_t disabled; } __attribute__((packed)) list_activities; struct { uint16_t value; } lid_angle; struct { uint16_t lid_angle; uint16_t hys_degree; } tablet_mode_threshold; }; }; enum ec_temp_thresholds { EC_TEMP_THRESH_WARN = 0, EC_TEMP_THRESH_HIGH = 1, EC_TEMP_THRESH_HALT = 2, EC_TEMP_THRESH_COUNT = 3, }; enum ec_mkbp_event { EC_MKBP_EVENT_KEY_MATRIX = 0, EC_MKBP_EVENT_HOST_EVENT = 1, EC_MKBP_EVENT_SENSOR_FIFO = 2, EC_MKBP_EVENT_BUTTON = 3, EC_MKBP_EVENT_SWITCH = 4, EC_MKBP_EVENT_FINGERPRINT = 5, EC_MKBP_EVENT_SYSRQ = 6, EC_MKBP_EVENT_HOST_EVENT64 = 7, EC_MKBP_EVENT_CEC_EVENT = 8, EC_MKBP_EVENT_CEC_MESSAGE = 9, EC_MKBP_EVENT_COUNT = 10, }; union ec_response_get_next_data_v1 { uint8_t key_matrix[16]; uint32_t host_event; uint64_t host_event64; struct { uint8_t reserved[3]; struct ec_response_motion_sense_fifo_info info; } __attribute__((packed)) sensor_fifo; uint32_t buttons; uint32_t switches; uint32_t fp_events; uint32_t sysrq; uint32_t cec_events; uint8_t cec_message[16]; }; struct ec_response_get_next_event_v1 { uint8_t event_type; union ec_response_get_next_data_v1 data; } __attribute__((packed)); struct ec_response_host_event_mask { uint32_t mask; }; enum { EC_MSG_TX_HEADER_BYTES = 3, EC_MSG_TX_TRAILER_BYTES = 1, EC_MSG_TX_PROTO_BYTES = 4, EC_MSG_RX_PROTO_BYTES = 3, EC_PROTO2_MSG_BYTES = 256, EC_MAX_MSG_BYTES = 65536, }; struct cros_ec_command { uint32_t version; uint32_t command; uint32_t outsize; uint32_t insize; uint32_t result; uint8_t data[0]; }; struct cros_ec_device { const char *phys_name; struct device *dev; bool was_wake_device; struct class *cros_class; int (*cmd_readmem)(struct cros_ec_device *, unsigned int, unsigned int, void *); u16 max_request; u16 max_response; u16 max_passthru; u16 proto_version; void *priv; int irq; u8 *din; u8 *dout; int din_size; int dout_size; bool wake_enabled; bool suspended; int (*cmd_xfer)(struct cros_ec_device *, struct cros_ec_command *); int (*pkt_xfer)(struct cros_ec_device *, struct cros_ec_command *); struct mutex lock; u8 mkbp_event_supported; bool host_sleep_v1; struct blocking_notifier_head event_notifier; struct ec_response_get_next_event_v1 event_data; int event_size; u32 host_event_wake_mask; u32 last_resume_result; ktime_t last_event_time; struct notifier_block notifier_ready; struct platform_device *ec; struct platform_device *pd; }; struct cros_ec_debugfs; struct cros_ec_dev { struct device class_dev; struct cros_ec_device *ec_dev; struct device *dev; struct cros_ec_debugfs *debug_info; bool has_kb_wake_angle; u16 cmd_offset; u32 features[2]; }; struct trace_event_raw_cros_ec_request_start { struct trace_entry ent; uint32_t version; uint32_t offset; uint32_t command; uint32_t outsize; uint32_t insize; char __data[0]; }; struct trace_event_raw_cros_ec_request_done { struct trace_entry ent; uint32_t version; uint32_t offset; uint32_t command; uint32_t outsize; uint32_t insize; uint32_t result; int retval; char __data[0]; }; struct trace_event_data_offsets_cros_ec_request_start {}; struct trace_event_data_offsets_cros_ec_request_done {}; typedef void (*btf_trace_cros_ec_request_start)(void *, struct cros_ec_command *); typedef void (*btf_trace_cros_ec_request_done)(void *, struct cros_ec_command *, int); struct acpi_table_pcct { struct acpi_table_header header; u32 flags; u64 reserved; }; enum acpi_pcct_type { ACPI_PCCT_TYPE_GENERIC_SUBSPACE = 0, ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE = 1, ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2 = 2, ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE = 3, ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE = 4, ACPI_PCCT_TYPE_HW_REG_COMM_SUBSPACE = 5, ACPI_PCCT_TYPE_RESERVED = 6, }; struct acpi_pcct_subspace { struct acpi_subtable_header header; u8 reserved[6]; u64 base_address; u64 length; struct acpi_generic_address doorbell_register; u64 preserve_mask; u64 write_mask; u32 latency; u32 max_access_rate; u16 min_turnaround_time; } __attribute__((packed)); struct acpi_pcct_hw_reduced_type2 { struct acpi_subtable_header header; u32 platform_interrupt; u8 flags; u8 reserved; u64 base_address; u64 length; struct acpi_generic_address doorbell_register; u64 preserve_mask; u64 write_mask; u32 latency; u32 max_access_rate; u16 min_turnaround_time; struct acpi_generic_address platform_ack_register; u64 ack_preserve_mask; u64 ack_write_mask; } __attribute__((packed)); struct hwspinlock___2; struct hwspinlock_ops { int (*trylock)(struct hwspinlock___2 *); void (*unlock)(struct hwspinlock___2 *); void (*relax)(struct hwspinlock___2 *); }; struct hwspinlock_device; struct hwspinlock___2 { struct hwspinlock_device *bank; spinlock_t lock; void *priv; }; struct hwspinlock_device { struct device *dev; const struct hwspinlock_ops *ops; int base_id; int num_locks; struct hwspinlock___2 lock[0]; }; struct resource_table { u32 ver; u32 num; u32 reserved[2]; u32 offset[0]; }; struct fw_rsc_hdr { u32 type; u8 data[0]; }; enum fw_resource_type { RSC_CARVEOUT = 0, RSC_DEVMEM = 1, RSC_TRACE = 2, RSC_VDEV = 3, RSC_LAST = 4, RSC_VENDOR_START = 128, RSC_VENDOR_END = 512, }; struct fw_rsc_carveout { u32 da; u32 pa; u32 len; u32 flags; u32 reserved; u8 name[32]; }; struct fw_rsc_devmem { u32 da; u32 pa; u32 len; u32 flags; u32 reserved; u8 name[32]; }; struct fw_rsc_trace { u32 da; u32 len; u32 reserved; u8 name[32]; }; struct fw_rsc_vdev_vring { u32 da; u32 align; u32 num; u32 notifyid; u32 pa; }; struct fw_rsc_vdev { u32 id; u32 notifyid; u32 dfeatures; u32 gfeatures; u32 config_len; u8 status; u8 num_of_vrings; u8 reserved[2]; struct fw_rsc_vdev_vring vring[0]; }; struct rproc; struct rproc_mem_entry { void *va; bool is_iomem; dma_addr_t dma; size_t len; u32 da; void *priv; char name[32]; struct list_head node; u32 rsc_offset; u32 flags; u32 of_resm_idx; int (*alloc)(struct rproc *, struct rproc_mem_entry *); int (*release)(struct rproc *, struct rproc_mem_entry *); }; enum rproc_dump_mechanism { RPROC_COREDUMP_DISABLED = 0, RPROC_COREDUMP_ENABLED = 1, RPROC_COREDUMP_INLINE = 2, }; struct rproc_ops; struct rproc { struct list_head node; struct iommu_domain *domain; const char *name; const char *firmware; void *priv; struct rproc_ops *ops; struct device dev; atomic_t power; unsigned int state; enum rproc_dump_mechanism dump_conf; struct mutex lock; struct dentry *dbg_dir; struct list_head traces; int num_traces; struct list_head carveouts; struct list_head mappings; u64 bootaddr; struct list_head rvdevs; struct list_head subdevs; struct idr notifyids; int index; struct work_struct crash_handler; unsigned int crash_cnt; bool recovery_disabled; int max_notifyid; struct resource_table *table_ptr; struct resource_table *clean_table; struct resource_table *cached_table; size_t table_sz; bool has_iommu; bool auto_boot; struct list_head dump_segments; int nb_vdev; u8 elf_class; u16 elf_machine; struct cdev cdev; bool cdev_put_on_release; }; enum rsc_handling_status { RSC_HANDLED = 0, RSC_IGNORED = 1, }; struct rproc_ops { int (*prepare)(struct rproc *); int (*unprepare)(struct rproc *); int (*start)(struct rproc *); int (*stop)(struct rproc *); int (*attach)(struct rproc *); int (*detach)(struct rproc *); void (*kick)(struct rproc *, int); void * (*da_to_va)(struct rproc *, u64, size_t, bool *); int (*parse_fw)(struct rproc *, const struct firmware *); int (*handle_rsc)(struct rproc *, u32, void *, int, int); struct resource_table * (*find_loaded_rsc_table)(struct rproc *, const struct firmware *); struct resource_table * (*get_loaded_rsc_table)(struct rproc *, size_t *); int (*load)(struct rproc *, const struct firmware *); int (*sanity_check)(struct rproc *, const struct firmware *); u64 (*get_boot_addr)(struct rproc *, const struct firmware *); long unsigned int (*panic)(struct rproc *); void (*coredump)(struct rproc *); }; enum rproc_state { RPROC_OFFLINE = 0, RPROC_SUSPENDED = 1, RPROC_RUNNING = 2, RPROC_CRASHED = 3, RPROC_DELETED = 4, RPROC_ATTACHED = 5, RPROC_DETACHED = 6, RPROC_LAST = 7, }; enum rproc_crash_type { RPROC_MMUFAULT = 0, RPROC_WATCHDOG = 1, RPROC_FATAL_ERROR = 2, }; struct rproc_subdev { struct list_head node; int (*prepare)(struct rproc_subdev *); int (*start)(struct rproc_subdev *); void (*stop)(struct rproc_subdev *, bool); void (*unprepare)(struct rproc_subdev *); }; struct rproc_vdev; struct rproc_vring { void *va; int len; u32 da; u32 align; int notifyid; struct rproc_vdev *rvdev; struct virtqueue *vq; }; struct rproc_vdev { struct kref refcount; struct rproc_subdev subdev; struct device dev; unsigned int id; struct list_head node; struct rproc *rproc; struct rproc_vring vring[2]; u32 rsc_offset; u32 index; }; struct rproc_debug_trace { struct rproc *rproc; struct dentry *tfile; struct list_head node; struct rproc_mem_entry trace_mem; }; typedef int (*rproc_handle_resource_t)(struct rproc *, void *, int, int); struct rproc_dump_segment { struct list_head node; dma_addr_t da; size_t size; void *priv; void (*dump)(struct rproc *, struct rproc_dump_segment *, void *, size_t, size_t); loff_t offset; }; struct rproc_coredump_state { struct rproc *rproc; void *header; struct completion dump_done; }; struct devfreq_freqs { long unsigned int old; long unsigned int new; }; struct devfreq_passive_data { struct devfreq *parent; int (*get_target_freq)(struct devfreq *, long unsigned int *); struct devfreq *this; struct notifier_block nb; }; struct trace_event_raw_devfreq_frequency { struct trace_entry ent; u32 __data_loc_dev_name; long unsigned int freq; long unsigned int prev_freq; long unsigned int busy_time; long unsigned int total_time; char __data[0]; }; struct trace_event_raw_devfreq_monitor { struct trace_entry ent; long unsigned int freq; long unsigned int busy_time; long unsigned int total_time; unsigned int polling_ms; u32 __data_loc_dev_name; char __data[0]; }; struct trace_event_data_offsets_devfreq_frequency { u32 dev_name; }; struct trace_event_data_offsets_devfreq_monitor { u32 dev_name; }; typedef void (*btf_trace_devfreq_frequency)(void *, struct devfreq *, long unsigned int, long unsigned int); typedef void (*btf_trace_devfreq_monitor)(void *, struct devfreq *); struct devfreq_notifier_devres { struct devfreq *devfreq; struct notifier_block *nb; unsigned int list; }; struct devfreq_event_desc; struct devfreq_event_dev { struct list_head node; struct device dev; struct mutex lock; u32 enable_count; const struct devfreq_event_desc *desc; }; struct devfreq_event_ops; struct devfreq_event_desc { const char *name; u32 event_type; void *driver_data; const struct devfreq_event_ops *ops; }; struct devfreq_event_data { long unsigned int load_count; long unsigned int total_count; }; struct devfreq_event_ops { int (*enable)(struct devfreq_event_dev *); int (*disable)(struct devfreq_event_dev *); int (*reset)(struct devfreq_event_dev *); int (*set_event)(struct devfreq_event_dev *); int (*get_event)(struct devfreq_event_dev *, struct devfreq_event_data *); }; union extcon_property_value { int intval; }; struct extcon_cable; struct extcon_dev___2 { const char *name; const unsigned int *supported_cable; const u32 *mutually_exclusive; struct device dev; struct raw_notifier_head nh_all; struct raw_notifier_head *nh; struct list_head entry; int max_supported; spinlock_t lock; u32 state; struct device_type extcon_dev_type; struct extcon_cable *cables; struct attribute_group attr_g_muex; struct attribute **attrs_muex; struct device_attribute *d_attrs_muex; }; struct extcon_cable { struct extcon_dev___2 *edev; int cable_index; struct attribute_group attr_g; struct device_attribute attr_name; struct device_attribute attr_state; struct attribute *attrs[3]; union extcon_property_value usb_propval[3]; union extcon_property_value chg_propval[1]; union extcon_property_value jack_propval[1]; union extcon_property_value disp_propval[2]; long unsigned int usb_bits[1]; long unsigned int chg_bits[1]; long unsigned int jack_bits[1]; long unsigned int disp_bits[1]; }; struct __extcon_info { unsigned int type; unsigned int id; const char *name; }; struct extcon_dev_notifier_devres { struct extcon_dev___2 *edev; unsigned int id; struct notifier_block *nb; }; struct powercap_control_type; struct powercap_control_type_ops { int (*set_enable)(struct powercap_control_type *, bool); int (*get_enable)(struct powercap_control_type *, bool *); int (*release)(struct powercap_control_type *); }; struct powercap_control_type { struct device dev; struct idr idr; int nr_zones; const struct powercap_control_type_ops *ops; struct mutex lock; bool allocated; struct list_head node; }; struct powercap_zone; struct powercap_zone_ops { int (*get_max_energy_range_uj)(struct powercap_zone *, u64 *); int (*get_energy_uj)(struct powercap_zone *, u64 *); int (*reset_energy_uj)(struct powercap_zone *); int (*get_max_power_range_uw)(struct powercap_zone *, u64 *); int (*get_power_uw)(struct powercap_zone *, u64 *); int (*set_enable)(struct powercap_zone *, bool); int (*get_enable)(struct powercap_zone *, bool *); int (*release)(struct powercap_zone *); }; struct powercap_zone_constraint; struct powercap_zone { int id; char *name; void *control_type_inst; const struct powercap_zone_ops *ops; struct device dev; int const_id_cnt; struct idr idr; struct idr *parent_idr; void *private_data; struct attribute **zone_dev_attrs; int zone_attr_count; struct attribute_group dev_zone_attr_group; const struct attribute_group *dev_attr_groups[2]; bool allocated; struct powercap_zone_constraint *constraints; }; struct powercap_zone_constraint_ops; struct powercap_zone_constraint { int id; struct powercap_zone *power_zone; const struct powercap_zone_constraint_ops *ops; }; struct powercap_zone_constraint_ops { int (*set_power_limit_uw)(struct powercap_zone *, int, u64); int (*get_power_limit_uw)(struct powercap_zone *, int, u64 *); int (*set_time_window_us)(struct powercap_zone *, int, u64); int (*get_time_window_us)(struct powercap_zone *, int, u64 *); int (*get_max_power_uw)(struct powercap_zone *, int, u64 *); int (*get_min_power_uw)(struct powercap_zone *, int, u64 *); int (*get_max_time_window_us)(struct powercap_zone *, int, u64 *); int (*get_min_time_window_us)(struct powercap_zone *, int, u64 *); const char * (*get_name)(struct powercap_zone *, int); }; struct dtpm_ops; struct dtpm { struct powercap_zone zone; struct dtpm *parent; struct list_head sibling; struct list_head children; struct dtpm_ops *ops; long unsigned int flags; u64 power_limit; u64 power_max; u64 power_min; int weight; void *private; }; struct dtpm_ops { u64 (*set_power_uw)(struct dtpm *, u64); u64 (*get_power_uw)(struct dtpm *); void (*release)(struct dtpm *); }; struct dtpm_descr; typedef int (*dtpm_init_t)(struct dtpm_descr *); struct dtpm_descr { struct dtpm *parent; const char *name; dtpm_init_t init; }; struct dtpm_cpu { struct freq_qos_request qos_req; int cpu; }; struct powercap_constraint_attr { struct device_attribute power_limit_attr; struct device_attribute time_window_attr; struct device_attribute max_power_attr; struct device_attribute min_power_attr; struct device_attribute max_time_window_attr; struct device_attribute min_time_window_attr; struct device_attribute name_attr; }; struct idle_inject_thread { struct task_struct *tsk; int should_run; }; struct idle_inject_device { struct hrtimer timer; unsigned int idle_duration_us; unsigned int run_duration_us; unsigned int latency_us; long unsigned int cpumask[0]; }; struct trace_event_raw_extlog_mem_event { struct trace_entry ent; u32 err_seq; u8 etype; u8 sev; u64 pa; u8 pa_mask_lsb; guid_t fru_id; u32 __data_loc_fru_text; struct cper_mem_err_compact data; char __data[0]; }; struct trace_event_raw_mc_event { struct trace_entry ent; unsigned int error_type; u32 __data_loc_msg; u32 __data_loc_label; u16 error_count; u8 mc_index; s8 top_layer; s8 middle_layer; s8 lower_layer; long int address; u8 grain_bits; long int syndrome; u32 __data_loc_driver_detail; char __data[0]; }; struct trace_event_raw_arm_event { struct trace_entry ent; u64 mpidr; u64 midr; u32 running_state; u32 psci_state; u8 affinity; char __data[0]; }; struct trace_event_raw_non_standard_event { struct trace_entry ent; char sec_type[16]; char fru_id[16]; u32 __data_loc_fru_text; u8 sev; u32 len; u32 __data_loc_buf; char __data[0]; }; struct trace_event_raw_aer_event { struct trace_entry ent; u32 __data_loc_dev_name; u32 status; u8 severity; u8 tlp_header_valid; u32 tlp_header[4]; char __data[0]; }; struct trace_event_raw_memory_failure_event { struct trace_entry ent; long unsigned int pfn; int type; int result; char __data[0]; }; struct trace_event_data_offsets_extlog_mem_event { u32 fru_text; }; struct trace_event_data_offsets_mc_event { u32 msg; u32 label; u32 driver_detail; }; struct trace_event_data_offsets_arm_event {}; struct trace_event_data_offsets_non_standard_event { u32 fru_text; u32 buf; }; struct trace_event_data_offsets_aer_event { u32 dev_name; }; struct trace_event_data_offsets_memory_failure_event {}; typedef void (*btf_trace_extlog_mem_event)(void *, struct cper_sec_mem_err *, u32, const guid_t *, const char *, u8); typedef void (*btf_trace_mc_event)(void *, const unsigned int, const char *, const char *, const int, const u8, const s8, const s8, const s8, long unsigned int, const u8, long unsigned int, const char *); typedef void (*btf_trace_arm_event)(void *, const struct cper_sec_proc_arm *); typedef void (*btf_trace_non_standard_event)(void *, const guid_t *, const guid_t *, const char *, const u8, const u8 *, const u32); typedef void (*btf_trace_aer_event)(void *, const char *, const u32, const u8, const u8, struct aer_header_log_regs *); typedef void (*btf_trace_memory_failure_event)(void *, long unsigned int, int, int); struct ce_array { u64 *array; unsigned int n; unsigned int decay_count; u64 pfns_poisoned; u64 ces_entered; u64 decays_done; union { struct { __u32 disabled: 1; __u32 __resv: 31; }; __u32 flags; }; }; struct nvmem_cell_lookup { const char *nvmem_name; const char *cell_name; const char *dev_id; const char *con_id; struct list_head node; }; enum { NVMEM_ADD = 1, NVMEM_REMOVE = 2, NVMEM_CELL_ADD = 3, NVMEM_CELL_REMOVE = 4, }; struct nvmem_cell_table { const char *nvmem_name; const struct nvmem_cell_info *cells; size_t ncells; struct list_head node; }; struct nvmem_device___2 { struct module *owner; struct device dev; int stride; int word_size; int id; struct kref refcnt; size_t size; bool read_only; bool root_only; int flags; enum nvmem_type type; struct bin_attribute eeprom; struct device *base_dev; struct list_head cells; const struct nvmem_keepout *keepout; unsigned int nkeepout; nvmem_reg_read_t reg_read; nvmem_reg_write_t reg_write; struct gpio_desc *wp_gpio; void *priv; }; struct nvmem_cell { const char *name; int offset; int bytes; int bit_offset; int nbits; struct device_node *np; struct nvmem_device___2 *nvmem; struct list_head node; }; struct icc_node; struct icc_req { struct hlist_node req_node; struct icc_node *node; struct device *dev; bool enabled; u32 tag; u32 avg_bw; u32 peak_bw; }; struct icc_path___2 { const char *name; size_t num_nodes; struct icc_req reqs[0]; }; struct icc_node_data { struct icc_node *node; u32 tag; }; struct icc_provider; struct icc_node { int id; const char *name; struct icc_node **links; size_t num_links; struct icc_provider *provider; struct list_head node_list; struct list_head search_list; struct icc_node *reverse; u8 is_traversed: 1; struct hlist_head req_list; u32 avg_bw; u32 peak_bw; u32 init_avg; u32 init_peak; void *data; }; struct icc_onecell_data { unsigned int num_nodes; struct icc_node *nodes[0]; }; struct icc_provider { struct list_head provider_list; struct list_head nodes; int (*set)(struct icc_node *, struct icc_node *); int (*aggregate)(struct icc_node *, u32, u32, u32, u32 *, u32 *); void (*pre_aggregate)(struct icc_node *); int (*get_bw)(struct icc_node *, u32 *, u32 *); struct icc_node * (*xlate)(struct of_phandle_args *, void *); struct icc_node_data * (*xlate_extended)(struct of_phandle_args *, void *); struct device *dev; int users; bool inter_set; void *data; }; struct trace_event_raw_icc_set_bw { struct trace_entry ent; u32 __data_loc_path_name; u32 __data_loc_dev; u32 __data_loc_node_name; u32 avg_bw; u32 peak_bw; u32 node_avg_bw; u32 node_peak_bw; char __data[0]; }; struct trace_event_raw_icc_set_bw_end { struct trace_entry ent; u32 __data_loc_path_name; u32 __data_loc_dev; int ret; char __data[0]; }; struct trace_event_data_offsets_icc_set_bw { u32 path_name; u32 dev; u32 node_name; }; struct trace_event_data_offsets_icc_set_bw_end { u32 path_name; u32 dev; }; typedef void (*btf_trace_icc_set_bw)(void *, struct icc_path___2 *, struct icc_node *, int, u32, u32); typedef void (*btf_trace_icc_set_bw_end)(void *, struct icc_path___2 *, int); struct icc_bulk_data { struct icc_path *path; const char *name; u32 avg_bw; u32 peak_bw; }; struct net_device_devres { struct net_device *ndev; }; struct __kernel_old_timespec { __kernel_old_time_t tv_sec; long int tv_nsec; }; struct __kernel_sock_timeval { __s64 tv_sec; __s64 tv_usec; }; struct mmsghdr { struct user_msghdr msg_hdr; unsigned int msg_len; }; struct scm_timestamping_internal { struct timespec64 ts[3]; }; struct ifconf { int ifc_len; union { char *ifcu_buf; struct ifreq *ifcu_req; } ifc_ifcu; }; struct compat_ifmap { compat_ulong_t mem_start; compat_ulong_t mem_end; short unsigned int base_addr; unsigned char irq; unsigned char dma; unsigned char port; }; struct compat_if_settings { unsigned int type; unsigned int size; compat_uptr_t ifs_ifsu; }; struct compat_ifreq { union { char ifrn_name[16]; } ifr_ifrn; union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; struct sockaddr ifru_netmask; struct sockaddr ifru_hwaddr; short int ifru_flags; compat_int_t ifru_ivalue; compat_int_t ifru_mtu; struct compat_ifmap ifru_map; char ifru_slave[16]; char ifru_newname[16]; compat_caddr_t ifru_data; struct compat_if_settings ifru_settings; } ifr_ifru; }; struct compat_ifconf { compat_int_t ifc_len; compat_caddr_t ifcbuf; }; enum sock_shutdown_cmd { SHUT_RD = 0, SHUT_WR = 1, SHUT_RDWR = 2, }; struct net_proto_family { int family; int (*create)(struct net *, struct socket *, int, int); struct module *owner; }; enum { SOCK_WAKE_IO = 0, SOCK_WAKE_WAITD = 1, SOCK_WAKE_SPACE = 2, SOCK_WAKE_URG = 3, }; struct compat_ethtool_rx_flow_spec { u32 flow_type; union ethtool_flow_union h_u; struct ethtool_flow_ext h_ext; union ethtool_flow_union m_u; struct ethtool_flow_ext m_ext; compat_u64 ring_cookie; u32 location; } __attribute__((packed)); struct compat_ethtool_rxnfc { u32 cmd; u32 flow_type; compat_u64 data; struct compat_ethtool_rx_flow_spec fs; u32 rule_cnt; u32 rule_locs[0]; } __attribute__((packed)); struct libipw_device; struct iw_spy_data; struct iw_public_data { struct iw_spy_data *spy_data; struct libipw_device *libipw; }; struct iw_param { __s32 value; __u8 fixed; __u8 disabled; __u16 flags; }; struct iw_point { void *pointer; __u16 length; __u16 flags; }; struct iw_freq { __s32 m; __s16 e; __u8 i; __u8 flags; }; struct iw_quality { __u8 qual; __u8 level; __u8 noise; __u8 updated; }; struct iw_discarded { __u32 nwid; __u32 code; __u32 fragment; __u32 retries; __u32 misc; }; struct iw_missed { __u32 beacon; }; struct iw_statistics { __u16 status; struct iw_quality qual; struct iw_discarded discard; struct iw_missed miss; }; union iwreq_data { char name[16]; struct iw_point essid; struct iw_param nwid; struct iw_freq freq; struct iw_param sens; struct iw_param bitrate; struct iw_param txpower; struct iw_param rts; struct iw_param frag; __u32 mode; struct iw_param retry; struct iw_point encoding; struct iw_param power; struct iw_quality qual; struct sockaddr ap_addr; struct sockaddr addr; struct iw_param param; struct iw_point data; }; struct iw_priv_args { __u32 cmd; __u16 set_args; __u16 get_args; char name[16]; }; struct compat_mmsghdr { struct compat_msghdr msg_hdr; compat_uint_t msg_len; }; struct iw_request_info { __u16 cmd; __u16 flags; }; struct iw_spy_data { int spy_number; u_char spy_address[48]; struct iw_quality spy_stat[8]; struct iw_quality spy_thr_low; struct iw_quality spy_thr_high; u_char spy_thr_under[8]; }; enum { SOF_TIMESTAMPING_TX_HARDWARE = 1, SOF_TIMESTAMPING_TX_SOFTWARE = 2, SOF_TIMESTAMPING_RX_HARDWARE = 4, SOF_TIMESTAMPING_RX_SOFTWARE = 8, SOF_TIMESTAMPING_SOFTWARE = 16, SOF_TIMESTAMPING_SYS_HARDWARE = 32, SOF_TIMESTAMPING_RAW_HARDWARE = 64, SOF_TIMESTAMPING_OPT_ID = 128, SOF_TIMESTAMPING_TX_SCHED = 256, SOF_TIMESTAMPING_TX_ACK = 512, SOF_TIMESTAMPING_OPT_CMSG = 1024, SOF_TIMESTAMPING_OPT_TSONLY = 2048, SOF_TIMESTAMPING_OPT_STATS = 4096, SOF_TIMESTAMPING_OPT_PKTINFO = 8192, SOF_TIMESTAMPING_OPT_TX_SWHW = 16384, SOF_TIMESTAMPING_BIND_PHC = 32768, SOF_TIMESTAMPING_LAST = 32768, SOF_TIMESTAMPING_MASK = 65535, }; struct scm_ts_pktinfo { __u32 if_index; __u32 pkt_length; __u32 reserved[2]; }; struct sock_skb_cb { u32 dropcount; }; struct sock_ee_data_rfc4884 { __u16 len; __u8 flags; __u8 reserved; }; struct sock_extended_err { __u32 ee_errno; __u8 ee_origin; __u8 ee_type; __u8 ee_code; __u8 ee_pad; __u32 ee_info; union { __u32 ee_data; struct sock_ee_data_rfc4884 ee_rfc4884; }; }; struct sock_exterr_skb { union { struct inet_skb_parm h4; struct inet6_skb_parm h6; } header; struct sock_extended_err ee; u16 addr_offset; __be16 port; u8 opt_stats: 1; u8 unused: 7; }; struct used_address { struct __kernel_sockaddr_storage name; unsigned int name_len; }; struct linger { int l_onoff; int l_linger; }; struct cmsghdr { __kernel_size_t cmsg_len; int cmsg_level; int cmsg_type; }; struct ucred { __u32 pid; __u32 uid; __u32 gid; }; struct mmpin { struct user_struct *user; unsigned int num_pg; }; struct ubuf_info { void (*callback)(struct sk_buff *, struct ubuf_info *, bool); union { struct { long unsigned int desc; void *ctx; }; struct { u32 id; u16 len; u16 zerocopy: 1; u32 bytelen; }; }; refcount_t refcnt; u8 flags; struct mmpin mmp; }; enum { SKB_GSO_TCPV4 = 1, SKB_GSO_DODGY = 2, SKB_GSO_TCP_ECN = 4, SKB_GSO_TCP_FIXEDID = 8, SKB_GSO_TCPV6 = 16, SKB_GSO_FCOE = 32, SKB_GSO_GRE = 64, SKB_GSO_GRE_CSUM = 128, SKB_GSO_IPXIP4 = 256, SKB_GSO_IPXIP6 = 512, SKB_GSO_UDP_TUNNEL = 1024, SKB_GSO_UDP_TUNNEL_CSUM = 2048, SKB_GSO_PARTIAL = 4096, SKB_GSO_TUNNEL_REMCSUM = 8192, SKB_GSO_SCTP = 16384, SKB_GSO_ESP = 32768, SKB_GSO_UDP = 65536, SKB_GSO_UDP_L4 = 131072, SKB_GSO_FRAGLIST = 262144, }; struct prot_inuse { int val[64]; }; struct gro_list { struct list_head list; int count; }; struct napi_struct { struct list_head poll_list; long unsigned int state; int weight; int defer_hard_irqs_count; long unsigned int gro_bitmask; int (*poll)(struct napi_struct *, int); int poll_owner; struct net_device *dev; struct gro_list gro_hash[8]; struct sk_buff *skb; struct list_head rx_list; int rx_count; struct hrtimer timer; struct list_head dev_list; struct hlist_node napi_hash_node; unsigned int napi_id; struct task_struct *thread; }; struct sd_flow_limit { u64 count; unsigned int num_buckets; unsigned int history_head; u16 history[128]; u8 buckets[0]; }; struct softnet_data { struct list_head poll_list; struct sk_buff_head process_queue; unsigned int processed; unsigned int time_squeeze; unsigned int received_rps; struct softnet_data *rps_ipi_list; struct sd_flow_limit *flow_limit; struct Qdisc *output_queue; struct Qdisc **output_queue_tailp; struct sk_buff *completion_queue; struct sk_buff_head xfrm_backlog; struct { u16 recursion; u8 more; } xmit; int: 32; unsigned int input_queue_head; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; call_single_data_t csd; struct softnet_data *rps_ipi_next; unsigned int cpu; unsigned int input_queue_tail; unsigned int dropped; struct sk_buff_head input_pkt_queue; struct napi_struct backlog; long: 64; long: 64; long: 64; long: 64; }; struct so_timestamping { int flags; int bind_phc; }; enum txtime_flags { SOF_TXTIME_DEADLINE_MODE = 1, SOF_TXTIME_REPORT_ERRORS = 2, SOF_TXTIME_FLAGS_LAST = 2, SOF_TXTIME_FLAGS_MASK = 3, }; struct sock_txtime { __kernel_clockid_t clockid; __u32 flags; }; enum sk_pacing { SK_PACING_NONE = 0, SK_PACING_NEEDED = 1, SK_PACING_FQ = 2, }; struct sockcm_cookie { u64 transmit_time; u32 mark; u16 tsflags; }; struct inet_bind_bucket { possible_net_t ib_net; int l3mdev; short unsigned int port; signed char fastreuse; signed char fastreuseport; kuid_t fastuid; struct in6_addr fast_v6_rcv_saddr; __be32 fast_rcv_saddr; short unsigned int fast_sk_family; bool fast_ipv6_only; struct hlist_node node; struct hlist_head owners; }; struct tcp_fastopen_cookie { __le64 val[2]; s8 len; bool exp; }; struct tcp_sack_block { u32 start_seq; u32 end_seq; }; struct tcp_options_received { int ts_recent_stamp; u32 ts_recent; u32 rcv_tsval; u32 rcv_tsecr; u16 saw_tstamp: 1; u16 tstamp_ok: 1; u16 dsack: 1; u16 wscale_ok: 1; u16 sack_ok: 3; u16 smc_ok: 1; u16 snd_wscale: 4; u16 rcv_wscale: 4; u8 saw_unknown: 1; u8 unused: 7; u8 num_sacks; u16 user_mss; u16 mss_clamp; }; struct tcp_rack { u64 mstamp; u32 rtt_us; u32 end_seq; u32 last_delivered; u8 reo_wnd_steps; u8 reo_wnd_persist: 5; u8 dsack_seen: 1; u8 advanced: 1; }; struct tcp_sock_af_ops; struct tcp_md5sig_info; struct tcp_fastopen_request; struct tcp_sock { struct inet_connection_sock inet_conn; u16 tcp_header_len; u16 gso_segs; __be32 pred_flags; u64 bytes_received; u32 segs_in; u32 data_segs_in; u32 rcv_nxt; u32 copied_seq; u32 rcv_wup; u32 snd_nxt; u32 segs_out; u32 data_segs_out; u64 bytes_sent; u64 bytes_acked; u32 dsack_dups; u32 snd_una; u32 snd_sml; u32 rcv_tstamp; u32 lsndtime; u32 last_oow_ack_time; u32 compressed_ack_rcv_nxt; u32 tsoffset; struct list_head tsq_node; struct list_head tsorted_sent_queue; u32 snd_wl1; u32 snd_wnd; u32 max_window; u32 mss_cache; u32 window_clamp; u32 rcv_ssthresh; struct tcp_rack rack; u16 advmss; u8 compressed_ack; u8 dup_ack_counter: 2; u8 tlp_retrans: 1; u8 unused: 5; u32 chrono_start; u32 chrono_stat[3]; u8 chrono_type: 2; u8 rate_app_limited: 1; u8 fastopen_connect: 1; u8 fastopen_no_cookie: 1; u8 is_sack_reneg: 1; u8 fastopen_client_fail: 2; u8 nonagle: 4; u8 thin_lto: 1; u8 recvmsg_inq: 1; u8 repair: 1; u8 frto: 1; u8 repair_queue; u8 save_syn: 2; u8 syn_data: 1; u8 syn_fastopen: 1; u8 syn_fastopen_exp: 1; u8 syn_fastopen_ch: 1; u8 syn_data_acked: 1; u8 is_cwnd_limited: 1; u32 tlp_high_seq; u32 tcp_tx_delay; u64 tcp_wstamp_ns; u64 tcp_clock_cache; u64 tcp_mstamp; u32 srtt_us; u32 mdev_us; u32 mdev_max_us; u32 rttvar_us; u32 rtt_seq; struct minmax rtt_min; u32 packets_out; u32 retrans_out; u32 max_packets_out; u32 max_packets_seq; u16 urg_data; u8 ecn_flags; u8 keepalive_probes; u32 reordering; u32 reord_seen; u32 snd_up; struct tcp_options_received rx_opt; u32 snd_ssthresh; u32 snd_cwnd; u32 snd_cwnd_cnt; u32 snd_cwnd_clamp; u32 snd_cwnd_used; u32 snd_cwnd_stamp; u32 prior_cwnd; u32 prr_delivered; u32 prr_out; u32 delivered; u32 delivered_ce; u32 lost; u32 app_limited; u64 first_tx_mstamp; u64 delivered_mstamp; u32 rate_delivered; u32 rate_interval_us; u32 rcv_wnd; u32 write_seq; u32 notsent_lowat; u32 pushed_seq; u32 lost_out; u32 sacked_out; struct hrtimer pacing_timer; struct hrtimer compressed_ack_timer; struct sk_buff *lost_skb_hint; struct sk_buff *retransmit_skb_hint; struct rb_root out_of_order_queue; struct sk_buff *ooo_last_skb; struct tcp_sack_block duplicate_sack[1]; struct tcp_sack_block selective_acks[4]; struct tcp_sack_block recv_sack_cache[4]; struct sk_buff *highest_sack; int lost_cnt_hint; u32 prior_ssthresh; u32 high_seq; u32 retrans_stamp; u32 undo_marker; int undo_retrans; u64 bytes_retrans; u32 total_retrans; u32 urg_seq; unsigned int keepalive_time; unsigned int keepalive_intvl; int linger2; u8 bpf_sock_ops_cb_flags; u16 timeout_rehash; u32 rcv_ooopack; u32 rcv_rtt_last_tsecr; struct { u32 rtt_us; u32 seq; u64 time; } rcv_rtt_est; struct { u32 space; u32 seq; u64 time; } rcvq_space; struct { u32 probe_seq_start; u32 probe_seq_end; } mtu_probe; u32 mtu_info; bool is_mptcp; bool syn_smc; const struct tcp_sock_af_ops *af_specific; struct tcp_md5sig_info *md5sig_info; struct tcp_fastopen_request *fastopen_req; struct request_sock *fastopen_rsk; struct saved_syn *saved_syn; }; struct tcp_md5sig_key; struct tcp_sock_af_ops { struct tcp_md5sig_key * (*md5_lookup)(const struct sock *, const struct sock *); int (*calc_md5_hash)(char *, const struct tcp_md5sig_key *, const struct sock *, const struct sk_buff *); int (*md5_parse)(struct sock *, int, sockptr_t, int); }; struct tcp_md5sig_info { struct hlist_head head; struct callback_head rcu; }; struct tcp_fastopen_request { struct tcp_fastopen_cookie cookie; struct msghdr *data; size_t size; int copied; struct ubuf_info *uarg; }; union tcp_md5_addr { struct in_addr a4; struct in6_addr a6; }; struct tcp_md5sig_key { struct hlist_node node; u8 keylen; u8 family; u8 prefixlen; union tcp_md5_addr addr; int l3index; u8 key[80]; struct callback_head rcu; }; struct net_protocol { int (*early_demux)(struct sk_buff *); int (*early_demux_handler)(struct sk_buff *); int (*handler)(struct sk_buff *); int (*err_handler)(struct sk_buff *, u32); unsigned int no_policy: 1; unsigned int icmp_strict_tag_validation: 1; }; struct cgroup_cls_state { struct cgroup_subsys_state css; u32 classid; }; enum { SK_MEMINFO_RMEM_ALLOC = 0, SK_MEMINFO_RCVBUF = 1, SK_MEMINFO_WMEM_ALLOC = 2, SK_MEMINFO_SNDBUF = 3, SK_MEMINFO_FWD_ALLOC = 4, SK_MEMINFO_WMEM_QUEUED = 5, SK_MEMINFO_OPTMEM = 6, SK_MEMINFO_BACKLOG = 7, SK_MEMINFO_DROPS = 8, SK_MEMINFO_VARS = 9, }; enum sknetlink_groups { SKNLGRP_NONE = 0, SKNLGRP_INET_TCP_DESTROY = 1, SKNLGRP_INET_UDP_DESTROY = 2, SKNLGRP_INET6_TCP_DESTROY = 3, SKNLGRP_INET6_UDP_DESTROY = 4, __SKNLGRP_MAX = 5, }; struct inet_request_sock { struct request_sock req; u16 snd_wscale: 4; u16 rcv_wscale: 4; u16 tstamp_ok: 1; u16 sack_ok: 1; u16 wscale_ok: 1; u16 ecn_ok: 1; u16 acked: 1; u16 no_srccheck: 1; u16 smc_ok: 1; u32 ir_mark; union { struct ip_options_rcu *ireq_opt; struct { struct ipv6_txoptions *ipv6_opt; struct sk_buff *pktopts; }; }; }; struct tcp_request_sock_ops; struct tcp_request_sock { struct inet_request_sock req; const struct tcp_request_sock_ops *af_specific; u64 snt_synack; bool tfo_listener; bool is_mptcp; bool drop_req; u32 txhash; u32 rcv_isn; u32 snt_isn; u32 ts_off; u32 last_oow_ack_time; u32 rcv_nxt; u8 syn_tos; }; enum tcp_synack_type { TCP_SYNACK_NORMAL = 0, TCP_SYNACK_FASTOPEN = 1, TCP_SYNACK_COOKIE = 2, }; struct tcp_request_sock_ops { u16 mss_clamp; struct tcp_md5sig_key * (*req_md5_lookup)(const struct sock *, const struct sock *); int (*calc_md5_hash)(char *, const struct tcp_md5sig_key *, const struct sock *, const struct sk_buff *); __u32 (*cookie_init_seq)(const struct sk_buff *, __u16 *); struct dst_entry * (*route_req)(const struct sock *, struct sk_buff *, struct flowi *, struct request_sock *); u32 (*init_seq)(const struct sk_buff *); u32 (*init_ts_off)(const struct net *, const struct sk_buff *); int (*send_synack)(const struct sock *, struct dst_entry *, struct flowi *, struct request_sock *, struct tcp_fastopen_cookie *, enum tcp_synack_type, struct sk_buff *); }; struct nf_conntrack { atomic_t use; }; enum { SKBFL_ZEROCOPY_ENABLE = 1, SKBFL_SHARED_FRAG = 2, }; enum { SKB_FCLONE_UNAVAILABLE = 0, SKB_FCLONE_ORIG = 1, SKB_FCLONE_CLONE = 2, }; struct sk_buff_fclones { struct sk_buff skb1; struct sk_buff skb2; refcount_t fclone_ref; }; struct skb_seq_state { __u32 lower_offset; __u32 upper_offset; __u32 frag_idx; __u32 stepped_offset; struct sk_buff *root_skb; struct sk_buff *cur_skb; __u8 *frag_data; __u32 frag_off; }; struct skb_checksum_ops { __wsum (*update)(const void *, int, __wsum); __wsum (*combine)(__wsum, __wsum, int, int); }; struct skb_gso_cb { union { int mac_offset; int data_offset; }; int encap_level; __wsum csum; __u16 csum_start; }; struct napi_gro_cb { void *frag0; unsigned int frag0_len; int data_offset; u16 flush; u16 flush_id; u16 count; u16 gro_remcsum_start; long unsigned int age; u16 proto; u8 same_flow: 1; u8 encap_mark: 1; u8 csum_valid: 1; u8 csum_cnt: 3; u8 free: 2; u8 is_ipv6: 1; u8 is_fou: 1; u8 is_atomic: 1; u8 recursion_counter: 4; u8 is_flist: 1; __wsum csum; struct sk_buff *last; }; enum skb_free_reason { SKB_REASON_CONSUMED = 0, SKB_REASON_DROPPED = 1, }; struct vlan_hdr { __be16 h_vlan_TCI; __be16 h_vlan_encapsulated_proto; }; struct vlan_ethhdr { unsigned char h_dest[6]; unsigned char h_source[6]; __be16 h_vlan_proto; __be16 h_vlan_TCI; __be16 h_vlan_encapsulated_proto; }; struct qdisc_walker { int stop; int skip; int count; int (*fn)(struct Qdisc *, long unsigned int, struct qdisc_walker *); }; struct ip_auth_hdr { __u8 nexthdr; __u8 hdrlen; __be16 reserved; __be32 spi; __be32 seq_no; __u8 auth_data[0]; }; struct frag_hdr { __u8 nexthdr; __u8 reserved; __be16 frag_off; __be32 identification; }; enum { SCM_TSTAMP_SND = 0, SCM_TSTAMP_SCHED = 1, SCM_TSTAMP_ACK = 2, }; struct mpls_shim_hdr { __be32 label_stack_entry; }; struct napi_alloc_cache { struct page_frag_cache page; unsigned int skb_count; void *skb_cache[64]; }; typedef int (*sendmsg_func)(struct sock *, struct msghdr *, struct kvec *, size_t, size_t); typedef int (*sendpage_func)(struct sock *, struct page *, int, size_t, int); struct ahash_request___2; struct scm_cookie { struct pid *pid; struct scm_fp_list *fp; struct scm_creds creds; u32 secid; }; struct scm_timestamping { struct __kernel_old_timespec ts[3]; }; struct scm_timestamping64 { struct __kernel_timespec ts[3]; }; enum { TCA_STATS_UNSPEC = 0, TCA_STATS_BASIC = 1, TCA_STATS_RATE_EST = 2, TCA_STATS_QUEUE = 3, TCA_STATS_APP = 4, TCA_STATS_RATE_EST64 = 5, TCA_STATS_PAD = 6, TCA_STATS_BASIC_HW = 7, TCA_STATS_PKT64 = 8, __TCA_STATS_MAX = 9, }; struct gnet_stats_basic { __u64 bytes; __u32 packets; }; struct gnet_stats_rate_est { __u32 bps; __u32 pps; }; struct gnet_stats_rate_est64 { __u64 bps; __u64 pps; }; struct gnet_estimator { signed char interval; unsigned char ewma_log; }; struct net_rate_estimator___2 { struct gnet_stats_basic_packed *bstats; spinlock_t *stats_lock; seqcount_t *running; struct gnet_stats_basic_cpu *cpu_bstats; u8 ewma_log; u8 intvl_log; seqcount_t seq; u64 last_packets; u64 last_bytes; u64 avpps; u64 avbps; long unsigned int next_jiffies; struct timer_list timer; struct callback_head rcu; }; struct rtgenmsg { unsigned char rtgen_family; }; enum rtnetlink_groups { RTNLGRP_NONE = 0, RTNLGRP_LINK = 1, RTNLGRP_NOTIFY = 2, RTNLGRP_NEIGH = 3, RTNLGRP_TC = 4, RTNLGRP_IPV4_IFADDR = 5, RTNLGRP_IPV4_MROUTE = 6, RTNLGRP_IPV4_ROUTE = 7, RTNLGRP_IPV4_RULE = 8, RTNLGRP_IPV6_IFADDR = 9, RTNLGRP_IPV6_MROUTE = 10, RTNLGRP_IPV6_ROUTE = 11, RTNLGRP_IPV6_IFINFO = 12, RTNLGRP_DECnet_IFADDR = 13, RTNLGRP_NOP2 = 14, RTNLGRP_DECnet_ROUTE = 15, RTNLGRP_DECnet_RULE = 16, RTNLGRP_NOP4 = 17, RTNLGRP_IPV6_PREFIX = 18, RTNLGRP_IPV6_RULE = 19, RTNLGRP_ND_USEROPT = 20, RTNLGRP_PHONET_IFADDR = 21, RTNLGRP_PHONET_ROUTE = 22, RTNLGRP_DCB = 23, RTNLGRP_IPV4_NETCONF = 24, RTNLGRP_IPV6_NETCONF = 25, RTNLGRP_MDB = 26, RTNLGRP_MPLS_ROUTE = 27, RTNLGRP_NSID = 28, RTNLGRP_MPLS_NETCONF = 29, RTNLGRP_IPV4_MROUTE_R = 30, RTNLGRP_IPV6_MROUTE_R = 31, RTNLGRP_NEXTHOP = 32, RTNLGRP_BRVLAN = 33, __RTNLGRP_MAX = 34, }; enum { NETNSA_NONE = 0, NETNSA_NSID = 1, NETNSA_PID = 2, NETNSA_FD = 3, NETNSA_TARGET_NSID = 4, NETNSA_CURRENT_NSID = 5, __NETNSA_MAX = 6, }; struct pcpu_gen_cookie { local_t nesting; u64 last; }; struct gen_cookie { struct pcpu_gen_cookie *local; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; atomic64_t forward_last; atomic64_t reverse_last; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *, struct netlink_ext_ack *); typedef int (*rtnl_dumpit_func)(struct sk_buff *, struct netlink_callback *); enum rtnl_link_flags { RTNL_FLAG_DOIT_UNLOCKED = 1, }; struct net_fill_args { u32 portid; u32 seq; int flags; int cmd; int nsid; bool add_ref; int ref_nsid; }; struct rtnl_net_dump_cb { struct net *tgt_net; struct net *ref_net; struct sk_buff *skb; struct net_fill_args fillargs; int idx; int s_idx; }; typedef u16 u_int16_t; typedef u32 u_int32_t; typedef u64 u_int64_t; struct flow_dissector_key_control { u16 thoff; u16 addr_type; u32 flags; }; enum flow_dissect_ret { FLOW_DISSECT_RET_OUT_GOOD = 0, FLOW_DISSECT_RET_OUT_BAD = 1, FLOW_DISSECT_RET_PROTO_AGAIN = 2, FLOW_DISSECT_RET_IPPROTO_AGAIN = 3, FLOW_DISSECT_RET_CONTINUE = 4, }; struct flow_dissector_key_basic { __be16 n_proto; u8 ip_proto; u8 padding; }; struct flow_dissector_key_tags { u32 flow_label; }; struct flow_dissector_key_vlan { union { struct { u16 vlan_id: 12; u16 vlan_dei: 1; u16 vlan_priority: 3; }; __be16 vlan_tci; }; __be16 vlan_tpid; }; struct flow_dissector_mpls_lse { u32 mpls_ttl: 8; u32 mpls_bos: 1; u32 mpls_tc: 3; u32 mpls_label: 20; }; struct flow_dissector_key_mpls { struct flow_dissector_mpls_lse ls[7]; u8 used_lses; }; struct flow_dissector_key_enc_opts { u8 data[255]; u8 len; __be16 dst_opt_type; }; struct flow_dissector_key_keyid { __be32 keyid; }; struct flow_dissector_key_ipv4_addrs { __be32 src; __be32 dst; }; struct flow_dissector_key_ipv6_addrs { struct in6_addr src; struct in6_addr dst; }; struct flow_dissector_key_tipc { __be32 key; }; struct flow_dissector_key_addrs { union { struct flow_dissector_key_ipv4_addrs v4addrs; struct flow_dissector_key_ipv6_addrs v6addrs; struct flow_dissector_key_tipc tipckey; }; }; struct flow_dissector_key_arp { __u32 sip; __u32 tip; __u8 op; unsigned char sha[6]; unsigned char tha[6]; }; struct flow_dissector_key_ports { union { __be32 ports; struct { __be16 src; __be16 dst; }; }; }; struct flow_dissector_key_icmp { struct { u8 type; u8 code; }; u16 id; }; struct flow_dissector_key_eth_addrs { unsigned char dst[6]; unsigned char src[6]; }; struct flow_dissector_key_tcp { __be16 flags; }; struct flow_dissector_key_ip { __u8 tos; __u8 ttl; }; struct flow_dissector_key_meta { int ingress_ifindex; u16 ingress_iftype; }; struct flow_dissector_key_ct { u16 ct_state; u16 ct_zone; u32 ct_mark; u32 ct_labels[4]; }; struct flow_dissector_key_hash { u32 hash; }; struct flow_dissector_key { enum flow_dissector_key_id key_id; size_t offset; }; struct flow_dissector { unsigned int used_keys; short unsigned int offset[28]; }; struct flow_keys_basic { struct flow_dissector_key_control control; struct flow_dissector_key_basic basic; }; struct flow_keys { struct flow_dissector_key_control control; struct flow_dissector_key_basic basic; struct flow_dissector_key_tags tags; struct flow_dissector_key_vlan vlan; struct flow_dissector_key_vlan cvlan; struct flow_dissector_key_keyid keyid; struct flow_dissector_key_ports ports; struct flow_dissector_key_icmp icmp; struct flow_dissector_key_addrs addrs; int: 32; }; struct flow_keys_digest { u8 data[16]; }; enum ip_conntrack_info { IP_CT_ESTABLISHED = 0, IP_CT_RELATED = 1, IP_CT_NEW = 2, IP_CT_IS_REPLY = 3, IP_CT_ESTABLISHED_REPLY = 3, IP_CT_RELATED_REPLY = 4, IP_CT_NUMBER = 5, IP_CT_UNTRACKED = 7, }; union nf_inet_addr { __u32 all[4]; __be32 ip; __be32 ip6[4]; struct in_addr in; struct in6_addr in6; }; struct ip_ct_tcp_state { u_int32_t td_end; u_int32_t td_maxend; u_int32_t td_maxwin; u_int32_t td_maxack; u_int8_t td_scale; u_int8_t flags; }; struct ip_ct_tcp { struct ip_ct_tcp_state seen[2]; u_int8_t state; u_int8_t last_dir; u_int8_t retrans; u_int8_t last_index; u_int32_t last_seq; u_int32_t last_ack; u_int32_t last_end; u_int16_t last_win; u_int8_t last_wscale; u_int8_t last_flags; }; union nf_conntrack_man_proto { __be16 all; struct { __be16 port; } tcp; struct { __be16 port; } udp; struct { __be16 id; } icmp; struct { __be16 port; } dccp; struct { __be16 port; } sctp; struct { __be16 key; } gre; }; struct nf_ct_dccp { u_int8_t role[2]; u_int8_t state; u_int8_t last_pkt; u_int8_t last_dir; u_int64_t handshake_seq; }; struct ip_ct_sctp { enum sctp_conntrack state; __be32 vtag[2]; u8 last_dir; u8 flags; }; struct nf_ct_event; struct nf_ct_event_notifier { int (*fcn)(unsigned int, struct nf_ct_event *); }; struct nf_exp_event; struct nf_exp_event_notifier { int (*fcn)(unsigned int, struct nf_exp_event *); }; enum bpf_ret_code { BPF_OK = 0, BPF_DROP = 2, BPF_REDIRECT = 7, BPF_LWT_REROUTE = 128, }; enum { BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG = 1, BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL = 2, BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP = 4, }; enum { TCA_FLOWER_KEY_CT_FLAGS_NEW = 1, TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED = 2, TCA_FLOWER_KEY_CT_FLAGS_RELATED = 4, TCA_FLOWER_KEY_CT_FLAGS_TRACKED = 8, TCA_FLOWER_KEY_CT_FLAGS_INVALID = 16, TCA_FLOWER_KEY_CT_FLAGS_REPLY = 32, __TCA_FLOWER_KEY_CT_FLAGS_MAX = 33, }; enum devlink_port_type { DEVLINK_PORT_TYPE_NOTSET = 0, DEVLINK_PORT_TYPE_AUTO = 1, DEVLINK_PORT_TYPE_ETH = 2, DEVLINK_PORT_TYPE_IB = 3, }; enum devlink_port_flavour { DEVLINK_PORT_FLAVOUR_PHYSICAL = 0, DEVLINK_PORT_FLAVOUR_CPU = 1, DEVLINK_PORT_FLAVOUR_DSA = 2, DEVLINK_PORT_FLAVOUR_PCI_PF = 3, DEVLINK_PORT_FLAVOUR_PCI_VF = 4, DEVLINK_PORT_FLAVOUR_VIRTUAL = 5, DEVLINK_PORT_FLAVOUR_UNUSED = 6, DEVLINK_PORT_FLAVOUR_PCI_SF = 7, }; struct devlink_port_phys_attrs { u32 port_number; u32 split_subport_number; }; struct devlink_port_pci_pf_attrs { u32 controller; u16 pf; u8 external: 1; }; struct devlink_port_pci_vf_attrs { u32 controller; u16 pf; u16 vf; u8 external: 1; }; struct devlink_port_pci_sf_attrs { u32 controller; u32 sf; u16 pf; u8 external: 1; }; struct devlink_port_attrs { u8 split: 1; u8 splittable: 1; u32 lanes; enum devlink_port_flavour flavour; struct netdev_phys_item_id switch_id; union { struct devlink_port_phys_attrs phys; struct devlink_port_pci_pf_attrs pci_pf; struct devlink_port_pci_vf_attrs pci_vf; struct devlink_port_pci_sf_attrs pci_sf; }; }; struct devlink; struct devlink_rate; struct devlink_port { struct list_head list; struct list_head param_list; struct list_head region_list; struct devlink *devlink; unsigned int index; bool registered; spinlock_t type_lock; enum devlink_port_type type; enum devlink_port_type desired_type; void *type_dev; struct devlink_port_attrs attrs; u8 attrs_set: 1; u8 switch_port: 1; struct delayed_work type_warn_dw; struct list_head reporter_list; struct mutex reporters_lock; struct devlink_rate *devlink_rate; }; struct ip_tunnel_parm { char name[16]; int link; __be16 i_flags; __be16 o_flags; __be32 i_key; __be32 o_key; struct iphdr iph; }; enum phylink_op_type { PHYLINK_NETDEV = 0, PHYLINK_DEV = 1, }; struct phylink_link_state; struct phylink_config { struct device *dev; enum phylink_op_type type; bool pcs_poll; bool poll_fixed_state; bool ovr_an_inband; void (*get_fixed_state)(struct phylink_config *, struct phylink_link_state *); }; struct dsa_device_ops; struct dsa_switch_tree; struct packet_type; struct dsa_switch; struct dsa_netdevice_ops; struct dsa_port { union { struct net_device *master; struct net_device *slave; }; const struct dsa_device_ops *tag_ops; struct dsa_switch_tree *dst; struct sk_buff * (*rcv)(struct sk_buff *, struct net_device *, struct packet_type *); bool (*filter)(const struct sk_buff *, struct net_device *); enum { DSA_PORT_TYPE_UNUSED = 0, DSA_PORT_TYPE_CPU = 1, DSA_PORT_TYPE_DSA = 2, DSA_PORT_TYPE_USER = 3, } type; struct dsa_switch *ds; unsigned int index; const char *name; struct dsa_port *cpu_dp; u8 mac[6]; struct device_node *dn; unsigned int ageing_time; bool vlan_filtering; u8 stp_state; struct net_device *bridge_dev; struct devlink_port devlink_port; bool devlink_port_setup; struct phylink *pl; struct phylink_config pl_config; struct net_device *lag_dev; bool lag_tx_enabled; struct net_device *hsr_dev; struct list_head list; void *priv; const struct ethtool_ops *orig_ethtool_ops; const struct dsa_netdevice_ops *netdev_ops; struct list_head fdbs; struct list_head mdbs; bool setup; }; struct packet_type { __be16 type; bool ignore_outgoing; struct net_device *dev; int (*func)(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); void (*list_func)(struct list_head *, struct packet_type *, struct net_device *); bool (*id_match)(struct packet_type *, struct sock *); void *af_packet_priv; struct list_head list; }; enum netdev_lag_tx_type { NETDEV_LAG_TX_TYPE_UNKNOWN = 0, NETDEV_LAG_TX_TYPE_RANDOM = 1, NETDEV_LAG_TX_TYPE_BROADCAST = 2, NETDEV_LAG_TX_TYPE_ROUNDROBIN = 3, NETDEV_LAG_TX_TYPE_ACTIVEBACKUP = 4, NETDEV_LAG_TX_TYPE_HASH = 5, }; enum netdev_lag_hash { NETDEV_LAG_HASH_NONE = 0, NETDEV_LAG_HASH_L2 = 1, NETDEV_LAG_HASH_L34 = 2, NETDEV_LAG_HASH_L23 = 3, NETDEV_LAG_HASH_E23 = 4, NETDEV_LAG_HASH_E34 = 5, NETDEV_LAG_HASH_VLAN_SRCMAC = 6, NETDEV_LAG_HASH_UNKNOWN = 7, }; struct netdev_lag_upper_info { enum netdev_lag_tx_type tx_type; enum netdev_lag_hash hash_type; }; struct netdev_notifier_changeupper_info { struct netdev_notifier_info info; struct net_device *upper_dev; bool master; bool linking; void *upper_info; }; struct flow_match { struct flow_dissector *dissector; void *mask; void *key; }; enum flow_action_id { FLOW_ACTION_ACCEPT = 0, FLOW_ACTION_DROP = 1, FLOW_ACTION_TRAP = 2, FLOW_ACTION_GOTO = 3, FLOW_ACTION_REDIRECT = 4, FLOW_ACTION_MIRRED = 5, FLOW_ACTION_REDIRECT_INGRESS = 6, FLOW_ACTION_MIRRED_INGRESS = 7, FLOW_ACTION_VLAN_PUSH = 8, FLOW_ACTION_VLAN_POP = 9, FLOW_ACTION_VLAN_MANGLE = 10, FLOW_ACTION_TUNNEL_ENCAP = 11, FLOW_ACTION_TUNNEL_DECAP = 12, FLOW_ACTION_MANGLE = 13, FLOW_ACTION_ADD = 14, FLOW_ACTION_CSUM = 15, FLOW_ACTION_MARK = 16, FLOW_ACTION_PTYPE = 17, FLOW_ACTION_PRIORITY = 18, FLOW_ACTION_WAKE = 19, FLOW_ACTION_QUEUE = 20, FLOW_ACTION_SAMPLE = 21, FLOW_ACTION_POLICE = 22, FLOW_ACTION_CT = 23, FLOW_ACTION_CT_METADATA = 24, FLOW_ACTION_MPLS_PUSH = 25, FLOW_ACTION_MPLS_POP = 26, FLOW_ACTION_MPLS_MANGLE = 27, FLOW_ACTION_GATE = 28, FLOW_ACTION_PPPOE_PUSH = 29, NUM_FLOW_ACTIONS = 30, }; enum flow_action_mangle_base { FLOW_ACT_MANGLE_UNSPEC = 0, FLOW_ACT_MANGLE_HDR_TYPE_ETH = 1, FLOW_ACT_MANGLE_HDR_TYPE_IP4 = 2, FLOW_ACT_MANGLE_HDR_TYPE_IP6 = 3, FLOW_ACT_MANGLE_HDR_TYPE_TCP = 4, FLOW_ACT_MANGLE_HDR_TYPE_UDP = 5, }; enum flow_action_hw_stats { FLOW_ACTION_HW_STATS_IMMEDIATE = 1, FLOW_ACTION_HW_STATS_DELAYED = 2, FLOW_ACTION_HW_STATS_ANY = 3, FLOW_ACTION_HW_STATS_DISABLED = 4, FLOW_ACTION_HW_STATS_DONT_CARE = 7, }; typedef void (*action_destr)(void *); struct flow_action_cookie { u32 cookie_len; u8 cookie[0]; }; struct nf_flowtable; struct ip_tunnel_key { __be64 tun_id; union { struct { __be32 src; __be32 dst; } ipv4; struct { struct in6_addr src; struct in6_addr dst; } ipv6; } u; __be16 tun_flags; u8 tos; u8 ttl; __be32 label; __be16 tp_src; __be16 tp_dst; }; struct dst_cache_pcpu; struct dst_cache { struct dst_cache_pcpu *cache; long unsigned int reset_ts; }; struct ip_tunnel_info { struct ip_tunnel_key key; struct dst_cache dst_cache; u8 options_len; u8 mode; }; struct psample_group; struct action_gate_entry; struct flow_action_entry { enum flow_action_id id; enum flow_action_hw_stats hw_stats; action_destr destructor; void *destructor_priv; union { u32 chain_index; struct net_device *dev; struct { u16 vid; __be16 proto; u8 prio; } vlan; struct { enum flow_action_mangle_base htype; u32 offset; u32 mask; u32 val; } mangle; struct ip_tunnel_info *tunnel; u32 csum_flags; u32 mark; u16 ptype; u32 priority; struct { u32 ctx; u32 index; u8 vf; } queue; struct { struct psample_group *psample_group; u32 rate; u32 trunc_size; bool truncate; } sample; struct { u32 index; u32 burst; u64 rate_bytes_ps; u64 burst_pkt; u64 rate_pkt_ps; u32 mtu; } police; struct { int action; u16 zone; struct nf_flowtable *flow_table; } ct; struct { long unsigned int cookie; u32 mark; u32 labels[4]; bool orig_dir; } ct_metadata; struct { u32 label; __be16 proto; u8 tc; u8 bos; u8 ttl; } mpls_push; struct { __be16 proto; } mpls_pop; struct { u32 label; u8 tc; u8 bos; u8 ttl; } mpls_mangle; struct { u32 index; s32 prio; u64 basetime; u64 cycletime; u64 cycletimeext; u32 num_entries; struct action_gate_entry *entries; } gate; struct { u16 sid; } pppoe; }; struct flow_action_cookie *cookie; }; struct flow_action { unsigned int num_entries; struct flow_action_entry entries[0]; }; struct flow_rule { struct flow_match match; struct flow_action action; }; struct flow_stats { u64 pkts; u64 bytes; u64 drops; u64 lastused; enum flow_action_hw_stats used_hw_stats; bool used_hw_stats_valid; }; enum flow_cls_command { FLOW_CLS_REPLACE = 0, FLOW_CLS_DESTROY = 1, FLOW_CLS_STATS = 2, FLOW_CLS_TMPLT_CREATE = 3, FLOW_CLS_TMPLT_DESTROY = 4, }; struct flow_cls_common_offload { u32 chain_index; __be16 protocol; u32 prio; struct netlink_ext_ack *extack; }; struct flow_cls_offload { struct flow_cls_common_offload common; enum flow_cls_command command; long unsigned int cookie; struct flow_rule *rule; struct flow_stats stats; u32 classid; }; union tcp_word_hdr { struct tcphdr hdr; __be32 words[5]; }; struct dsa_chip_data { struct device *host_dev; int sw_addr; struct device *netdev[12]; int eeprom_len; struct device_node *of_node; char *port_names[12]; struct device_node *port_dn[12]; s8 rtable[4]; }; struct dsa_platform_data { struct device *netdev; struct net_device *of_netdev; int nr_chips; struct dsa_chip_data *chip; }; struct phylink_link_state { long unsigned int advertising[2]; long unsigned int lp_advertising[2]; phy_interface_t interface; int speed; int duplex; int pause; unsigned int link: 1; unsigned int an_enabled: 1; unsigned int an_complete: 1; }; enum devlink_sb_pool_type { DEVLINK_SB_POOL_TYPE_INGRESS = 0, DEVLINK_SB_POOL_TYPE_EGRESS = 1, }; enum devlink_sb_threshold_type { DEVLINK_SB_THRESHOLD_TYPE_STATIC = 0, DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC = 1, }; enum devlink_eswitch_encap_mode { DEVLINK_ESWITCH_ENCAP_MODE_NONE = 0, DEVLINK_ESWITCH_ENCAP_MODE_BASIC = 1, }; enum devlink_rate_type { DEVLINK_RATE_TYPE_LEAF = 0, DEVLINK_RATE_TYPE_NODE = 1, }; enum devlink_param_cmode { DEVLINK_PARAM_CMODE_RUNTIME = 0, DEVLINK_PARAM_CMODE_DRIVERINIT = 1, DEVLINK_PARAM_CMODE_PERMANENT = 2, __DEVLINK_PARAM_CMODE_MAX = 3, DEVLINK_PARAM_CMODE_MAX = 2, }; enum devlink_trap_action { DEVLINK_TRAP_ACTION_DROP = 0, DEVLINK_TRAP_ACTION_TRAP = 1, DEVLINK_TRAP_ACTION_MIRROR = 2, }; enum devlink_trap_type { DEVLINK_TRAP_TYPE_DROP = 0, DEVLINK_TRAP_TYPE_EXCEPTION = 1, DEVLINK_TRAP_TYPE_CONTROL = 2, }; enum devlink_reload_action { DEVLINK_RELOAD_ACTION_UNSPEC = 0, DEVLINK_RELOAD_ACTION_DRIVER_REINIT = 1, DEVLINK_RELOAD_ACTION_FW_ACTIVATE = 2, __DEVLINK_RELOAD_ACTION_MAX = 3, DEVLINK_RELOAD_ACTION_MAX = 2, }; enum devlink_reload_limit { DEVLINK_RELOAD_LIMIT_UNSPEC = 0, DEVLINK_RELOAD_LIMIT_NO_RESET = 1, __DEVLINK_RELOAD_LIMIT_MAX = 2, DEVLINK_RELOAD_LIMIT_MAX = 1, }; enum devlink_dpipe_field_mapping_type { DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE = 0, DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX = 1, }; enum devlink_port_fn_state { DEVLINK_PORT_FN_STATE_INACTIVE = 0, DEVLINK_PORT_FN_STATE_ACTIVE = 1, }; enum devlink_port_fn_opstate { DEVLINK_PORT_FN_OPSTATE_DETACHED = 0, DEVLINK_PORT_FN_OPSTATE_ATTACHED = 1, }; struct devlink_dev_stats { u32 reload_stats[6]; u32 remote_reload_stats[6]; }; struct devlink_dpipe_headers; struct devlink_ops; struct devlink { struct list_head list; struct list_head port_list; struct list_head rate_list; struct list_head sb_list; struct list_head dpipe_table_list; struct list_head resource_list; struct list_head param_list; struct list_head region_list; struct list_head reporter_list; struct mutex reporters_lock; struct devlink_dpipe_headers *dpipe_headers; struct list_head trap_list; struct list_head trap_group_list; struct list_head trap_policer_list; const struct devlink_ops *ops; struct xarray snapshot_ids; struct devlink_dev_stats stats; struct device *dev; possible_net_t _net; struct mutex lock; u8 reload_failed: 1; u8 reload_enabled: 1; u8 registered: 1; long: 61; long: 64; long: 64; long: 64; char priv[0]; }; struct devlink_dpipe_header; struct devlink_dpipe_headers { struct devlink_dpipe_header **headers; unsigned int headers_count; }; struct devlink_sb_pool_info; struct devlink_info_req; struct devlink_flash_update_params; struct devlink_trap; struct devlink_trap_group; struct devlink_trap_policer; struct devlink_port_new_attrs; struct devlink_ops { u32 supported_flash_update_params; long unsigned int reload_actions; long unsigned int reload_limits; int (*reload_down)(struct devlink *, bool, enum devlink_reload_action, enum devlink_reload_limit, struct netlink_ext_ack *); int (*reload_up)(struct devlink *, enum devlink_reload_action, enum devlink_reload_limit, u32 *, struct netlink_ext_ack *); int (*port_type_set)(struct devlink_port *, enum devlink_port_type); int (*port_split)(struct devlink *, unsigned int, unsigned int, struct netlink_ext_ack *); int (*port_unsplit)(struct devlink *, unsigned int, struct netlink_ext_ack *); int (*sb_pool_get)(struct devlink *, unsigned int, u16, struct devlink_sb_pool_info *); int (*sb_pool_set)(struct devlink *, unsigned int, u16, u32, enum devlink_sb_threshold_type, struct netlink_ext_ack *); int (*sb_port_pool_get)(struct devlink_port *, unsigned int, u16, u32 *); int (*sb_port_pool_set)(struct devlink_port *, unsigned int, u16, u32, struct netlink_ext_ack *); int (*sb_tc_pool_bind_get)(struct devlink_port *, unsigned int, u16, enum devlink_sb_pool_type, u16 *, u32 *); int (*sb_tc_pool_bind_set)(struct devlink_port *, unsigned int, u16, enum devlink_sb_pool_type, u16, u32, struct netlink_ext_ack *); int (*sb_occ_snapshot)(struct devlink *, unsigned int); int (*sb_occ_max_clear)(struct devlink *, unsigned int); int (*sb_occ_port_pool_get)(struct devlink_port *, unsigned int, u16, u32 *, u32 *); int (*sb_occ_tc_port_bind_get)(struct devlink_port *, unsigned int, u16, enum devlink_sb_pool_type, u32 *, u32 *); int (*eswitch_mode_get)(struct devlink *, u16 *); int (*eswitch_mode_set)(struct devlink *, u16, struct netlink_ext_ack *); int (*eswitch_inline_mode_get)(struct devlink *, u8 *); int (*eswitch_inline_mode_set)(struct devlink *, u8, struct netlink_ext_ack *); int (*eswitch_encap_mode_get)(struct devlink *, enum devlink_eswitch_encap_mode *); int (*eswitch_encap_mode_set)(struct devlink *, enum devlink_eswitch_encap_mode, struct netlink_ext_ack *); int (*info_get)(struct devlink *, struct devlink_info_req *, struct netlink_ext_ack *); int (*flash_update)(struct devlink *, struct devlink_flash_update_params *, struct netlink_ext_ack *); int (*trap_init)(struct devlink *, const struct devlink_trap *, void *); void (*trap_fini)(struct devlink *, const struct devlink_trap *, void *); int (*trap_action_set)(struct devlink *, const struct devlink_trap *, enum devlink_trap_action, struct netlink_ext_ack *); int (*trap_group_init)(struct devlink *, const struct devlink_trap_group *); int (*trap_group_set)(struct devlink *, const struct devlink_trap_group *, const struct devlink_trap_policer *, struct netlink_ext_ack *); int (*trap_group_action_set)(struct devlink *, const struct devlink_trap_group *, enum devlink_trap_action, struct netlink_ext_ack *); int (*trap_drop_counter_get)(struct devlink *, const struct devlink_trap *, u64 *); int (*trap_policer_init)(struct devlink *, const struct devlink_trap_policer *); void (*trap_policer_fini)(struct devlink *, const struct devlink_trap_policer *); int (*trap_policer_set)(struct devlink *, const struct devlink_trap_policer *, u64, u64, struct netlink_ext_ack *); int (*trap_policer_counter_get)(struct devlink *, const struct devlink_trap_policer *, u64 *); int (*port_function_hw_addr_get)(struct devlink *, struct devlink_port *, u8 *, int *, struct netlink_ext_ack *); int (*port_function_hw_addr_set)(struct devlink *, struct devlink_port *, const u8 *, int, struct netlink_ext_ack *); int (*port_new)(struct devlink *, const struct devlink_port_new_attrs *, struct netlink_ext_ack *, unsigned int *); int (*port_del)(struct devlink *, unsigned int, struct netlink_ext_ack *); int (*port_fn_state_get)(struct devlink *, struct devlink_port *, enum devlink_port_fn_state *, enum devlink_port_fn_opstate *, struct netlink_ext_ack *); int (*port_fn_state_set)(struct devlink *, struct devlink_port *, enum devlink_port_fn_state, struct netlink_ext_ack *); int (*rate_leaf_tx_share_set)(struct devlink_rate *, void *, u64, struct netlink_ext_ack *); int (*rate_leaf_tx_max_set)(struct devlink_rate *, void *, u64, struct netlink_ext_ack *); int (*rate_node_tx_share_set)(struct devlink_rate *, void *, u64, struct netlink_ext_ack *); int (*rate_node_tx_max_set)(struct devlink_rate *, void *, u64, struct netlink_ext_ack *); int (*rate_node_new)(struct devlink_rate *, void **, struct netlink_ext_ack *); int (*rate_node_del)(struct devlink_rate *, void *, struct netlink_ext_ack *); int (*rate_leaf_parent_set)(struct devlink_rate *, struct devlink_rate *, void *, void *, struct netlink_ext_ack *); int (*rate_node_parent_set)(struct devlink_rate *, struct devlink_rate *, void *, void *, struct netlink_ext_ack *); }; struct devlink_rate { struct list_head list; enum devlink_rate_type type; struct devlink *devlink; void *priv; u64 tx_share; u64 tx_max; struct devlink_rate *parent; union { struct devlink_port *devlink_port; struct { char *name; refcount_t refcnt; }; }; }; struct devlink_port_new_attrs { enum devlink_port_flavour flavour; unsigned int port_index; u32 controller; u32 sfnum; u16 pfnum; u8 port_index_valid: 1; u8 controller_valid: 1; u8 sfnum_valid: 1; }; struct devlink_sb_pool_info { enum devlink_sb_pool_type pool_type; u32 size; enum devlink_sb_threshold_type threshold_type; u32 cell_size; }; struct devlink_dpipe_field { const char *name; unsigned int id; unsigned int bitwidth; enum devlink_dpipe_field_mapping_type mapping_type; }; struct devlink_dpipe_header { const char *name; unsigned int id; struct devlink_dpipe_field *fields; unsigned int fields_count; bool global; }; union devlink_param_value { u8 vu8; u16 vu16; u32 vu32; char vstr[32]; bool vbool; }; struct devlink_param_gset_ctx { union devlink_param_value val; enum devlink_param_cmode cmode; }; struct devlink_flash_update_params { const struct firmware *fw; const char *component; u32 overwrite_mask; }; struct devlink_trap_policer { u32 id; u64 init_rate; u64 init_burst; u64 max_rate; u64 min_rate; u64 max_burst; u64 min_burst; }; struct devlink_trap_group { const char *name; u16 id; bool generic; u32 init_policer_id; }; struct devlink_trap { enum devlink_trap_type type; enum devlink_trap_action init_action; bool generic; u16 id; const char *name; u16 init_group_id; u32 metadata_cap; }; struct arphdr { __be16 ar_hrd; __be16 ar_pro; unsigned char ar_hln; unsigned char ar_pln; __be16 ar_op; }; struct fib_info; struct fib_nh { struct fib_nh_common nh_common; struct hlist_node nh_hash; struct fib_info *nh_parent; __u32 nh_tclassid; __be32 nh_saddr; int nh_saddr_genid; }; struct fib_info { struct hlist_node fib_hash; struct hlist_node fib_lhash; struct list_head nh_list; struct net *fib_net; int fib_treeref; refcount_t fib_clntref; unsigned int fib_flags; unsigned char fib_dead; unsigned char fib_protocol; unsigned char fib_scope; unsigned char fib_type; __be32 fib_prefsrc; u32 fib_tb_id; u32 fib_priority; struct dst_metrics *fib_metrics; int fib_nhs; bool fib_nh_is_v6; bool nh_updated; struct nexthop *nh; struct callback_head rcu; struct fib_nh fib_nh[0]; }; struct nh_info; struct nh_group; struct nexthop { struct rb_node rb_node; struct list_head fi_list; struct list_head f6i_list; struct list_head fdb_list; struct list_head grp_list; struct net *net; u32 id; u8 protocol; u8 nh_flags; bool is_group; refcount_t refcnt; struct callback_head rcu; union { struct nh_info *nh_info; struct nh_group *nh_grp; }; }; struct switchdev_brport_flags { long unsigned int val; long unsigned int mask; }; enum switchdev_obj_id { SWITCHDEV_OBJ_ID_UNDEFINED = 0, SWITCHDEV_OBJ_ID_PORT_VLAN = 1, SWITCHDEV_OBJ_ID_PORT_MDB = 2, SWITCHDEV_OBJ_ID_HOST_MDB = 3, SWITCHDEV_OBJ_ID_MRP = 4, SWITCHDEV_OBJ_ID_RING_TEST_MRP = 5, SWITCHDEV_OBJ_ID_RING_ROLE_MRP = 6, SWITCHDEV_OBJ_ID_RING_STATE_MRP = 7, SWITCHDEV_OBJ_ID_IN_TEST_MRP = 8, SWITCHDEV_OBJ_ID_IN_ROLE_MRP = 9, SWITCHDEV_OBJ_ID_IN_STATE_MRP = 10, }; struct switchdev_obj { struct list_head list; struct net_device *orig_dev; enum switchdev_obj_id id; u32 flags; void *complete_priv; void (*complete)(struct net_device *, int, void *); }; struct switchdev_obj_port_vlan { struct switchdev_obj obj; u16 flags; u16 vid; }; struct switchdev_obj_port_mdb { struct switchdev_obj obj; unsigned char addr[6]; u16 vid; }; struct switchdev_obj_mrp { struct switchdev_obj obj; struct net_device *p_port; struct net_device *s_port; u32 ring_id; u16 prio; }; struct switchdev_obj_ring_role_mrp { struct switchdev_obj obj; u8 ring_role; u32 ring_id; u8 sw_backup; }; enum dsa_tag_protocol { DSA_TAG_PROTO_NONE = 0, DSA_TAG_PROTO_BRCM = 1, DSA_TAG_PROTO_BRCM_LEGACY = 22, DSA_TAG_PROTO_BRCM_PREPEND = 2, DSA_TAG_PROTO_DSA = 3, DSA_TAG_PROTO_EDSA = 4, DSA_TAG_PROTO_GSWIP = 5, DSA_TAG_PROTO_KSZ9477 = 6, DSA_TAG_PROTO_KSZ9893 = 7, DSA_TAG_PROTO_LAN9303 = 8, DSA_TAG_PROTO_MTK = 9, DSA_TAG_PROTO_QCA = 10, DSA_TAG_PROTO_TRAILER = 11, DSA_TAG_PROTO_8021Q = 12, DSA_TAG_PROTO_SJA1105 = 13, DSA_TAG_PROTO_KSZ8795 = 14, DSA_TAG_PROTO_OCELOT = 15, DSA_TAG_PROTO_AR9331 = 16, DSA_TAG_PROTO_RTL4_A = 17, DSA_TAG_PROTO_HELLCREEK = 18, DSA_TAG_PROTO_XRS700X = 19, DSA_TAG_PROTO_OCELOT_8021Q = 20, DSA_TAG_PROTO_SEVILLE = 21, DSA_TAG_PROTO_SJA1110 = 23, }; struct dsa_device_ops { struct sk_buff * (*xmit)(struct sk_buff *, struct net_device *); struct sk_buff * (*rcv)(struct sk_buff *, struct net_device *, struct packet_type *); void (*flow_dissect)(const struct sk_buff *, __be16 *, int *); bool (*filter)(const struct sk_buff *, struct net_device *); unsigned int needed_headroom; unsigned int needed_tailroom; const char *name; enum dsa_tag_protocol proto; bool promisc_on_master; }; struct dsa_netdevice_ops { int (*ndo_do_ioctl)(struct net_device *, struct ifreq *, int); }; struct dsa_switch_tree { struct list_head list; struct raw_notifier_head nh; unsigned int index; struct kref refcount; bool setup; const struct dsa_device_ops *tag_ops; enum dsa_tag_protocol default_proto; struct dsa_platform_data *pd; struct list_head ports; struct list_head rtable; struct net_device **lags; unsigned int lags_len; }; struct dsa_mall_mirror_tc_entry { u8 to_local_port; bool ingress; }; struct dsa_mall_policer_tc_entry { u32 burst; u64 rate_bytes_per_sec; }; struct dsa_switch_ops; struct dsa_switch { bool setup; struct device *dev; struct dsa_switch_tree *dst; unsigned int index; struct notifier_block nb; void *priv; struct dsa_chip_data *cd; const struct dsa_switch_ops *ops; u32 phys_mii_mask; struct mii_bus *slave_mii_bus; unsigned int ageing_time_min; unsigned int ageing_time_max; struct devlink *devlink; unsigned int num_tx_queues; bool vlan_filtering_is_global; bool configure_vlan_while_not_filtering; bool untag_bridge_pvid; bool assisted_learning_on_cpu_port; bool vlan_filtering; bool pcs_poll; bool mtu_enforcement_ingress; unsigned int num_lag_ids; size_t num_ports; }; struct fixed_phy_status; typedef int dsa_fdb_dump_cb_t(const unsigned char *, u16, bool, void *); struct dsa_switch_ops { enum dsa_tag_protocol (*get_tag_protocol)(struct dsa_switch *, int, enum dsa_tag_protocol); int (*change_tag_protocol)(struct dsa_switch *, int, enum dsa_tag_protocol); int (*setup)(struct dsa_switch *); void (*teardown)(struct dsa_switch *); u32 (*get_phy_flags)(struct dsa_switch *, int); int (*phy_read)(struct dsa_switch *, int, int); int (*phy_write)(struct dsa_switch *, int, int, u16); void (*adjust_link)(struct dsa_switch *, int, struct phy_device *); void (*fixed_link_update)(struct dsa_switch *, int, struct fixed_phy_status *); void (*phylink_validate)(struct dsa_switch *, int, long unsigned int *, struct phylink_link_state *); int (*phylink_mac_link_state)(struct dsa_switch *, int, struct phylink_link_state *); void (*phylink_mac_config)(struct dsa_switch *, int, unsigned int, const struct phylink_link_state *); void (*phylink_mac_an_restart)(struct dsa_switch *, int); void (*phylink_mac_link_down)(struct dsa_switch *, int, unsigned int, phy_interface_t); void (*phylink_mac_link_up)(struct dsa_switch *, int, unsigned int, phy_interface_t, struct phy_device *, int, int, bool, bool); void (*phylink_fixed_state)(struct dsa_switch *, int, struct phylink_link_state *); void (*get_strings)(struct dsa_switch *, int, u32, uint8_t *); void (*get_ethtool_stats)(struct dsa_switch *, int, uint64_t *); int (*get_sset_count)(struct dsa_switch *, int, int); void (*get_ethtool_phy_stats)(struct dsa_switch *, int, uint64_t *); void (*get_stats64)(struct dsa_switch *, int, struct rtnl_link_stats64 *); void (*self_test)(struct dsa_switch *, int, struct ethtool_test *, u64 *); void (*get_wol)(struct dsa_switch *, int, struct ethtool_wolinfo *); int (*set_wol)(struct dsa_switch *, int, struct ethtool_wolinfo *); int (*get_ts_info)(struct dsa_switch *, int, struct ethtool_ts_info *); int (*suspend)(struct dsa_switch *); int (*resume)(struct dsa_switch *); int (*port_enable)(struct dsa_switch *, int, struct phy_device *); void (*port_disable)(struct dsa_switch *, int); int (*set_mac_eee)(struct dsa_switch *, int, struct ethtool_eee *); int (*get_mac_eee)(struct dsa_switch *, int, struct ethtool_eee *); int (*get_eeprom_len)(struct dsa_switch *); int (*get_eeprom)(struct dsa_switch *, struct ethtool_eeprom *, u8 *); int (*set_eeprom)(struct dsa_switch *, struct ethtool_eeprom *, u8 *); int (*get_regs_len)(struct dsa_switch *, int); void (*get_regs)(struct dsa_switch *, int, struct ethtool_regs *, void *); int (*port_prechangeupper)(struct dsa_switch *, int, struct netdev_notifier_changeupper_info *); int (*set_ageing_time)(struct dsa_switch *, unsigned int); int (*port_bridge_join)(struct dsa_switch *, int, struct net_device *); void (*port_bridge_leave)(struct dsa_switch *, int, struct net_device *); void (*port_stp_state_set)(struct dsa_switch *, int, u8); void (*port_fast_age)(struct dsa_switch *, int); int (*port_pre_bridge_flags)(struct dsa_switch *, int, struct switchdev_brport_flags, struct netlink_ext_ack *); int (*port_bridge_flags)(struct dsa_switch *, int, struct switchdev_brport_flags, struct netlink_ext_ack *); int (*port_set_mrouter)(struct dsa_switch *, int, bool, struct netlink_ext_ack *); int (*port_vlan_filtering)(struct dsa_switch *, int, bool, struct netlink_ext_ack *); int (*port_vlan_add)(struct dsa_switch *, int, const struct switchdev_obj_port_vlan *, struct netlink_ext_ack *); int (*port_vlan_del)(struct dsa_switch *, int, const struct switchdev_obj_port_vlan *); int (*port_fdb_add)(struct dsa_switch *, int, const unsigned char *, u16); int (*port_fdb_del)(struct dsa_switch *, int, const unsigned char *, u16); int (*port_fdb_dump)(struct dsa_switch *, int, dsa_fdb_dump_cb_t *, void *); int (*port_mdb_add)(struct dsa_switch *, int, const struct switchdev_obj_port_mdb *); int (*port_mdb_del)(struct dsa_switch *, int, const struct switchdev_obj_port_mdb *); int (*get_rxnfc)(struct dsa_switch *, int, struct ethtool_rxnfc *, u32 *); int (*set_rxnfc)(struct dsa_switch *, int, struct ethtool_rxnfc *); int (*cls_flower_add)(struct dsa_switch *, int, struct flow_cls_offload *, bool); int (*cls_flower_del)(struct dsa_switch *, int, struct flow_cls_offload *, bool); int (*cls_flower_stats)(struct dsa_switch *, int, struct flow_cls_offload *, bool); int (*port_mirror_add)(struct dsa_switch *, int, struct dsa_mall_mirror_tc_entry *, bool); void (*port_mirror_del)(struct dsa_switch *, int, struct dsa_mall_mirror_tc_entry *); int (*port_policer_add)(struct dsa_switch *, int, struct dsa_mall_policer_tc_entry *); void (*port_policer_del)(struct dsa_switch *, int); int (*port_setup_tc)(struct dsa_switch *, int, enum tc_setup_type, void *); int (*crosschip_bridge_join)(struct dsa_switch *, int, int, int, struct net_device *); void (*crosschip_bridge_leave)(struct dsa_switch *, int, int, int, struct net_device *); int (*crosschip_lag_change)(struct dsa_switch *, int, int); int (*crosschip_lag_join)(struct dsa_switch *, int, int, struct net_device *, struct netdev_lag_upper_info *); int (*crosschip_lag_leave)(struct dsa_switch *, int, int, struct net_device *); int (*port_hwtstamp_get)(struct dsa_switch *, int, struct ifreq *); int (*port_hwtstamp_set)(struct dsa_switch *, int, struct ifreq *); void (*port_txtstamp)(struct dsa_switch *, int, struct sk_buff *); bool (*port_rxtstamp)(struct dsa_switch *, int, struct sk_buff *, unsigned int); int (*devlink_param_get)(struct dsa_switch *, u32, struct devlink_param_gset_ctx *); int (*devlink_param_set)(struct dsa_switch *, u32, struct devlink_param_gset_ctx *); int (*devlink_info_get)(struct dsa_switch *, struct devlink_info_req *, struct netlink_ext_ack *); int (*devlink_sb_pool_get)(struct dsa_switch *, unsigned int, u16, struct devlink_sb_pool_info *); int (*devlink_sb_pool_set)(struct dsa_switch *, unsigned int, u16, u32, enum devlink_sb_threshold_type, struct netlink_ext_ack *); int (*devlink_sb_port_pool_get)(struct dsa_switch *, int, unsigned int, u16, u32 *); int (*devlink_sb_port_pool_set)(struct dsa_switch *, int, unsigned int, u16, u32, struct netlink_ext_ack *); int (*devlink_sb_tc_pool_bind_get)(struct dsa_switch *, int, unsigned int, u16, enum devlink_sb_pool_type, u16 *, u32 *); int (*devlink_sb_tc_pool_bind_set)(struct dsa_switch *, int, unsigned int, u16, enum devlink_sb_pool_type, u16, u32, struct netlink_ext_ack *); int (*devlink_sb_occ_snapshot)(struct dsa_switch *, unsigned int); int (*devlink_sb_occ_max_clear)(struct dsa_switch *, unsigned int); int (*devlink_sb_occ_port_pool_get)(struct dsa_switch *, int, unsigned int, u16, u32 *, u32 *); int (*devlink_sb_occ_tc_port_bind_get)(struct dsa_switch *, int, unsigned int, u16, enum devlink_sb_pool_type, u32 *, u32 *); int (*port_change_mtu)(struct dsa_switch *, int, int); int (*port_max_mtu)(struct dsa_switch *, int); int (*port_lag_change)(struct dsa_switch *, int); int (*port_lag_join)(struct dsa_switch *, int, struct net_device *, struct netdev_lag_upper_info *); int (*port_lag_leave)(struct dsa_switch *, int, struct net_device *); int (*port_hsr_join)(struct dsa_switch *, int, struct net_device *); int (*port_hsr_leave)(struct dsa_switch *, int, struct net_device *); int (*port_mrp_add)(struct dsa_switch *, int, const struct switchdev_obj_mrp *); int (*port_mrp_del)(struct dsa_switch *, int, const struct switchdev_obj_mrp *); int (*port_mrp_add_ring_role)(struct dsa_switch *, int, const struct switchdev_obj_ring_role_mrp *); int (*port_mrp_del_ring_role)(struct dsa_switch *, int, const struct switchdev_obj_ring_role_mrp *); }; enum lwtunnel_encap_types { LWTUNNEL_ENCAP_NONE = 0, LWTUNNEL_ENCAP_MPLS = 1, LWTUNNEL_ENCAP_IP = 2, LWTUNNEL_ENCAP_ILA = 3, LWTUNNEL_ENCAP_IP6 = 4, LWTUNNEL_ENCAP_SEG6 = 5, LWTUNNEL_ENCAP_BPF = 6, LWTUNNEL_ENCAP_SEG6_LOCAL = 7, LWTUNNEL_ENCAP_RPL = 8, __LWTUNNEL_ENCAP_MAX = 9, }; struct nh_info { struct hlist_node dev_hash; struct nexthop *nh_parent; u8 family; bool reject_nh; bool fdb_nh; union { struct fib_nh_common fib_nhc; struct fib_nh fib_nh; struct fib6_nh fib6_nh; }; }; struct nh_grp_entry; struct nh_res_bucket { struct nh_grp_entry *nh_entry; atomic_long_t used_time; long unsigned int migrated_time; bool occupied; u8 nh_flags; }; struct nh_grp_entry { struct nexthop *nh; u8 weight; union { struct { atomic_t upper_bound; } hthr; struct { struct list_head uw_nh_entry; u16 count_buckets; u16 wants_buckets; } res; }; struct list_head nh_list; struct nexthop *nh_parent; }; struct nh_res_table { struct net *net; u32 nhg_id; struct delayed_work upkeep_dw; struct list_head uw_nh_entries; long unsigned int unbalanced_since; u32 idle_timer; u32 unbalanced_timer; u16 num_nh_buckets; struct nh_res_bucket nh_buckets[0]; }; struct nh_group { struct nh_group *spare; u16 num_nh; bool is_multipath; bool hash_threshold; bool resilient; bool fdb_nh; bool has_v4; struct nh_res_table *res_table; struct nh_grp_entry nh_entries[0]; }; enum metadata_type { METADATA_IP_TUNNEL = 0, METADATA_HW_PORT_MUX = 1, }; struct hw_port_info { struct net_device *lower_dev; u32 port_id; }; struct metadata_dst { struct dst_entry dst; enum metadata_type type; union { struct ip_tunnel_info tun_info; struct hw_port_info port_info; } u; }; struct gre_base_hdr { __be16 flags; __be16 protocol; }; struct gre_full_hdr { struct gre_base_hdr fixed_header; __be16 csum; __be16 reserved1; __be32 key; __be32 seq; }; struct pptp_gre_header { struct gre_base_hdr gre_hd; __be16 payload_len; __be16 call_id; __be32 seq; __be32 ack; }; struct tipc_basic_hdr { __be32 w[4]; }; struct icmphdr { __u8 type; __u8 code; __sum16 checksum; union { struct { __be16 id; __be16 sequence; } echo; __be32 gateway; struct { __be16 __unused; __be16 mtu; } frag; __u8 reserved[4]; } un; }; enum l2tp_debug_flags { L2TP_MSG_DEBUG = 1, L2TP_MSG_CONTROL = 2, L2TP_MSG_SEQ = 4, L2TP_MSG_DATA = 8, }; struct pppoe_tag { __be16 tag_type; __be16 tag_len; char tag_data[0]; }; struct pppoe_hdr { __u8 type: 4; __u8 ver: 4; __u8 code; __be16 sid; __be16 length; struct pppoe_tag tag[0]; }; struct mpls_label { __be32 entry; }; struct clock_identity { u8 id[8]; }; struct port_identity { struct clock_identity clock_identity; __be16 port_number; }; struct ptp_header { u8 tsmt; u8 ver; __be16 message_length; u8 domain_number; u8 reserved1; u8 flag_field[2]; __be64 correction; __be32 reserved2; struct port_identity source_port_identity; __be16 sequence_id; u8 control; u8 log_message_interval; } __attribute__((packed)); enum batadv_packettype { BATADV_IV_OGM = 0, BATADV_BCAST = 1, BATADV_CODED = 2, BATADV_ELP = 3, BATADV_OGM2 = 4, BATADV_UNICAST = 64, BATADV_UNICAST_FRAG = 65, BATADV_UNICAST_4ADDR = 66, BATADV_ICMP = 67, BATADV_UNICAST_TVLV = 68, }; struct batadv_unicast_packet { __u8 packet_type; __u8 version; __u8 ttl; __u8 ttvn; __u8 dest[6]; }; struct nf_conntrack_zone { u16 id; u8 flags; u8 dir; }; struct nf_conntrack_man { union nf_inet_addr u3; union nf_conntrack_man_proto u; u_int16_t l3num; }; struct nf_conntrack_tuple { struct nf_conntrack_man src; struct { union nf_inet_addr u3; union { __be16 all; struct { __be16 port; } tcp; struct { __be16 port; } udp; struct { u_int8_t type; u_int8_t code; } icmp; struct { __be16 port; } dccp; struct { __be16 port; } sctp; struct { __be16 key; } gre; } u; u_int8_t protonum; u_int8_t dir; } dst; }; struct nf_conntrack_tuple_hash { struct hlist_nulls_node hnnode; struct nf_conntrack_tuple tuple; }; struct nf_ct_udp { long unsigned int stream_ts; }; struct nf_ct_gre { unsigned int stream_timeout; unsigned int timeout; }; union nf_conntrack_proto { struct nf_ct_dccp dccp; struct ip_ct_sctp sctp; struct ip_ct_tcp tcp; struct nf_ct_udp udp; struct nf_ct_gre gre; unsigned int tmpl_padto; }; struct nf_ct_ext; struct nf_conn { struct nf_conntrack ct_general; spinlock_t lock; u32 timeout; struct nf_conntrack_zone zone; struct nf_conntrack_tuple_hash tuplehash[2]; long unsigned int status; u16 cpu; possible_net_t ct_net; struct hlist_node nat_bysource; struct { } __nfct_init_offset; struct nf_conn *master; u_int32_t mark; u_int32_t secmark; struct nf_ct_ext *ext; union nf_conntrack_proto proto; }; struct nf_conntrack_tuple_mask { struct { union nf_inet_addr u3; union nf_conntrack_man_proto u; } src; }; struct nf_ct_ext { u8 offset[9]; u8 len; char data[0]; }; struct nf_conntrack_helper; struct nf_conntrack_expect { struct hlist_node lnode; struct hlist_node hnode; struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple_mask mask; void (*expectfn)(struct nf_conn *, struct nf_conntrack_expect *); struct nf_conntrack_helper *helper; struct nf_conn *master; struct timer_list timeout; refcount_t use; unsigned int flags; unsigned int class; union nf_inet_addr saved_addr; union nf_conntrack_man_proto saved_proto; enum ip_conntrack_dir dir; struct callback_head rcu; }; enum nf_ct_ext_id { NF_CT_EXT_HELPER = 0, NF_CT_EXT_NAT = 1, NF_CT_EXT_SEQADJ = 2, NF_CT_EXT_ACCT = 3, NF_CT_EXT_ECACHE = 4, NF_CT_EXT_TSTAMP = 5, NF_CT_EXT_TIMEOUT = 6, NF_CT_EXT_LABELS = 7, NF_CT_EXT_SYNPROXY = 8, NF_CT_EXT_NUM = 9, }; struct nf_ct_event { struct nf_conn *ct; u32 portid; int report; }; struct nf_exp_event { struct nf_conntrack_expect *exp; u32 portid; int report; }; struct nf_conn_labels { long unsigned int bits[2]; }; struct _flow_keys_digest_data { __be16 n_proto; u8 ip_proto; u8 padding; __be32 ports; __be32 src; __be32 dst; }; struct rps_sock_flow_table { u32 mask; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; u32 ents[0]; }; struct tc_skb_ext { __u32 chain; __u16 mru; bool post_ct; }; struct ipv4_devconf { void *sysctl; int data[32]; long unsigned int state[1]; }; enum nf_dev_hooks { NF_NETDEV_INGRESS = 0, NF_NETDEV_NUMHOOKS = 1, }; enum { IF_OPER_UNKNOWN = 0, IF_OPER_NOTPRESENT = 1, IF_OPER_DOWN = 2, IF_OPER_LOWERLAYERDOWN = 3, IF_OPER_TESTING = 4, IF_OPER_DORMANT = 5, IF_OPER_UP = 6, }; struct ifbond { __s32 bond_mode; __s32 num_slaves; __s32 miimon; }; typedef struct ifbond ifbond; struct ifslave { __s32 slave_id; char slave_name[16]; __s8 link; __s8 state; __u32 link_failure_count; }; typedef struct ifslave ifslave; enum netdev_state_t { __LINK_STATE_START = 0, __LINK_STATE_PRESENT = 1, __LINK_STATE_NOCARRIER = 2, __LINK_STATE_LINKWATCH_PENDING = 3, __LINK_STATE_DORMANT = 4, __LINK_STATE_TESTING = 5, }; struct netdev_boot_setup { char name[16]; struct ifmap map; }; enum { NAPIF_STATE_SCHED = 1, NAPIF_STATE_MISSED = 2, NAPIF_STATE_DISABLE = 4, NAPIF_STATE_NPSVC = 8, NAPIF_STATE_LISTED = 16, NAPIF_STATE_NO_BUSY_POLL = 32, NAPIF_STATE_IN_BUSY_POLL = 64, NAPIF_STATE_PREFER_BUSY_POLL = 128, NAPIF_STATE_THREADED = 256, NAPIF_STATE_SCHED_THREADED = 512, }; enum gro_result { GRO_MERGED = 0, GRO_MERGED_FREE = 1, GRO_HELD = 2, GRO_NORMAL = 3, GRO_CONSUMED = 4, }; typedef enum gro_result gro_result_t; enum netdev_queue_state_t { __QUEUE_STATE_DRV_XOFF = 0, __QUEUE_STATE_STACK_XOFF = 1, __QUEUE_STATE_FROZEN = 2, }; struct net_device_path_stack { int num_paths; struct net_device_path path[5]; }; struct bpf_xdp_link { struct bpf_link link; struct net_device *dev; int flags; }; struct netdev_net_notifier { struct list_head list; struct notifier_block *nb; }; struct netpoll; struct netpoll_info { refcount_t refcnt; struct semaphore dev_lock; struct sk_buff_head txq; struct delayed_work tx_work; struct netpoll *netpoll; struct callback_head rcu; }; struct in_ifaddr; struct ip_mc_list; struct in_device { struct net_device *dev; refcount_t refcnt; int dead; struct in_ifaddr *ifa_list; struct ip_mc_list *mc_list; struct ip_mc_list **mc_hash; int mc_count; spinlock_t mc_tomb_lock; struct ip_mc_list *mc_tomb; long unsigned int mr_v1_seen; long unsigned int mr_v2_seen; long unsigned int mr_maxdelay; long unsigned int mr_qi; long unsigned int mr_qri; unsigned char mr_qrv; unsigned char mr_gq_running; u32 mr_ifc_count; struct timer_list mr_gq_timer; struct timer_list mr_ifc_timer; struct neigh_parms *arp_parms; struct ipv4_devconf cnf; struct callback_head callback_head; }; struct offload_callbacks { struct sk_buff * (*gso_segment)(struct sk_buff *, netdev_features_t); struct sk_buff * (*gro_receive)(struct list_head *, struct sk_buff *); int (*gro_complete)(struct sk_buff *, int); }; struct packet_offload { __be16 type; u16 priority; struct offload_callbacks callbacks; struct list_head list; }; struct netdev_notifier_info_ext { struct netdev_notifier_info info; union { u32 mtu; } ext; }; struct netdev_notifier_change_info { struct netdev_notifier_info info; unsigned int flags_changed; }; struct netdev_notifier_changelowerstate_info { struct netdev_notifier_info info; void *lower_state_info; }; struct netdev_notifier_pre_changeaddr_info { struct netdev_notifier_info info; const unsigned char *dev_addr; }; typedef int (*bpf_op_t)(struct net_device *, struct netdev_bpf *); enum { NESTED_SYNC_IMM_BIT = 0, NESTED_SYNC_TODO_BIT = 1, }; struct netdev_nested_priv { unsigned char flags; void *data; }; struct netdev_bonding_info { ifslave slave; ifbond master; }; struct netdev_notifier_bonding_info { struct netdev_notifier_info info; struct netdev_bonding_info bonding_info; }; union inet_addr { __u32 all[4]; __be32 ip; __be32 ip6[4]; struct in_addr in; struct in6_addr in6; }; struct netpoll { struct net_device *dev; char dev_name[16]; const char *name; union inet_addr local_ip; union inet_addr remote_ip; bool ipv6; u16 local_port; u16 remote_port; u8 remote_mac[6]; }; enum qdisc_state_t { __QDISC_STATE_SCHED = 0, __QDISC_STATE_DEACTIVATED = 1, __QDISC_STATE_MISSED = 2, __QDISC_STATE_DRAINING = 3, }; struct tcf_walker { int stop; int skip; int count; bool nonempty; long unsigned int cookie; int (*fn)(struct tcf_proto *, void *, struct tcf_walker *); }; enum { IPV4_DEVCONF_FORWARDING = 1, IPV4_DEVCONF_MC_FORWARDING = 2, IPV4_DEVCONF_PROXY_ARP = 3, IPV4_DEVCONF_ACCEPT_REDIRECTS = 4, IPV4_DEVCONF_SECURE_REDIRECTS = 5, IPV4_DEVCONF_SEND_REDIRECTS = 6, IPV4_DEVCONF_SHARED_MEDIA = 7, IPV4_DEVCONF_RP_FILTER = 8, IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE = 9, IPV4_DEVCONF_BOOTP_RELAY = 10, IPV4_DEVCONF_LOG_MARTIANS = 11, IPV4_DEVCONF_TAG = 12, IPV4_DEVCONF_ARPFILTER = 13, IPV4_DEVCONF_MEDIUM_ID = 14, IPV4_DEVCONF_NOXFRM = 15, IPV4_DEVCONF_NOPOLICY = 16, IPV4_DEVCONF_FORCE_IGMP_VERSION = 17, IPV4_DEVCONF_ARP_ANNOUNCE = 18, IPV4_DEVCONF_ARP_IGNORE = 19, IPV4_DEVCONF_PROMOTE_SECONDARIES = 20, IPV4_DEVCONF_ARP_ACCEPT = 21, IPV4_DEVCONF_ARP_NOTIFY = 22, IPV4_DEVCONF_ACCEPT_LOCAL = 23, IPV4_DEVCONF_SRC_VMARK = 24, IPV4_DEVCONF_PROXY_ARP_PVLAN = 25, IPV4_DEVCONF_ROUTE_LOCALNET = 26, IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL = 27, IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL = 28, IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN = 29, IPV4_DEVCONF_DROP_UNICAST_IN_L2_MULTICAST = 30, IPV4_DEVCONF_DROP_GRATUITOUS_ARP = 31, IPV4_DEVCONF_BC_FORWARDING = 32, __IPV4_DEVCONF_MAX = 33, }; struct in_ifaddr { struct hlist_node hash; struct in_ifaddr *ifa_next; struct in_device *ifa_dev; struct callback_head callback_head; __be32 ifa_local; __be32 ifa_address; __be32 ifa_mask; __u32 ifa_rt_priority; __be32 ifa_broadcast; unsigned char ifa_scope; unsigned char ifa_prefixlen; __u32 ifa_flags; char ifa_label[16]; __u32 ifa_valid_lft; __u32 ifa_preferred_lft; long unsigned int ifa_cstamp; long unsigned int ifa_tstamp; }; struct udp_tunnel_info { short unsigned int type; sa_family_t sa_family; __be16 port; u8 hw_priv; }; struct udp_tunnel_nic_shared { struct udp_tunnel_nic *udp_tunnel_nic_info; struct list_head devices; }; struct dev_kfree_skb_cb { enum skb_free_reason reason; }; struct netdev_adjacent { struct net_device *dev; bool master; bool ignore; u16 ref_nr; void *private; struct list_head list; struct callback_head rcu; }; struct netdev_hw_addr { struct list_head list; unsigned char addr[32]; unsigned char type; bool global_use; int sync_cnt; int refcount; int synced; struct callback_head callback_head; }; enum { NDA_UNSPEC = 0, NDA_DST = 1, NDA_LLADDR = 2, NDA_CACHEINFO = 3, NDA_PROBES = 4, NDA_VLAN = 5, NDA_PORT = 6, NDA_VNI = 7, NDA_IFINDEX = 8, NDA_MASTER = 9, NDA_LINK_NETNSID = 10, NDA_SRC_VNI = 11, NDA_PROTOCOL = 12, NDA_NH_ID = 13, NDA_FDB_EXT_ATTRS = 14, __NDA_MAX = 15, }; struct nda_cacheinfo { __u32 ndm_confirmed; __u32 ndm_used; __u32 ndm_updated; __u32 ndm_refcnt; }; struct ndt_stats { __u64 ndts_allocs; __u64 ndts_destroys; __u64 ndts_hash_grows; __u64 ndts_res_failed; __u64 ndts_lookups; __u64 ndts_hits; __u64 ndts_rcv_probes_mcast; __u64 ndts_rcv_probes_ucast; __u64 ndts_periodic_gc_runs; __u64 ndts_forced_gc_runs; __u64 ndts_table_fulls; }; enum { NDTPA_UNSPEC = 0, NDTPA_IFINDEX = 1, NDTPA_REFCNT = 2, NDTPA_REACHABLE_TIME = 3, NDTPA_BASE_REACHABLE_TIME = 4, NDTPA_RETRANS_TIME = 5, NDTPA_GC_STALETIME = 6, NDTPA_DELAY_PROBE_TIME = 7, NDTPA_QUEUE_LEN = 8, NDTPA_APP_PROBES = 9, NDTPA_UCAST_PROBES = 10, NDTPA_MCAST_PROBES = 11, NDTPA_ANYCAST_DELAY = 12, NDTPA_PROXY_DELAY = 13, NDTPA_PROXY_QLEN = 14, NDTPA_LOCKTIME = 15, NDTPA_QUEUE_LENBYTES = 16, NDTPA_MCAST_REPROBES = 17, NDTPA_PAD = 18, __NDTPA_MAX = 19, }; struct ndtmsg { __u8 ndtm_family; __u8 ndtm_pad1; __u16 ndtm_pad2; }; struct ndt_config { __u16 ndtc_key_len; __u16 ndtc_entry_size; __u32 ndtc_entries; __u32 ndtc_last_flush; __u32 ndtc_last_rand; __u32 ndtc_hash_rnd; __u32 ndtc_hash_mask; __u32 ndtc_hash_chain_gc; __u32 ndtc_proxy_qlen; }; enum { NDTA_UNSPEC = 0, NDTA_NAME = 1, NDTA_THRESH1 = 2, NDTA_THRESH2 = 3, NDTA_THRESH3 = 4, NDTA_CONFIG = 5, NDTA_PARMS = 6, NDTA_STATS = 7, NDTA_GC_INTERVAL = 8, NDTA_PAD = 9, __NDTA_MAX = 10, }; enum { RTN_UNSPEC = 0, RTN_UNICAST = 1, RTN_LOCAL = 2, RTN_BROADCAST = 3, RTN_ANYCAST = 4, RTN_MULTICAST = 5, RTN_BLACKHOLE = 6, RTN_UNREACHABLE = 7, RTN_PROHIBIT = 8, RTN_THROW = 9, RTN_NAT = 10, RTN_XRESOLVE = 11, __RTN_MAX = 12, }; enum { NEIGH_ARP_TABLE = 0, NEIGH_ND_TABLE = 1, NEIGH_DN_TABLE = 2, NEIGH_NR_TABLES = 3, NEIGH_LINK_TABLE = 3, }; struct neigh_seq_state { struct seq_net_private p; struct neigh_table *tbl; struct neigh_hash_table *nht; void * (*neigh_sub_iter)(struct neigh_seq_state *, struct neighbour *, loff_t *); unsigned int bucket; unsigned int flags; }; struct neighbour_cb { long unsigned int sched_next; unsigned int flags; }; enum netevent_notif_type { NETEVENT_NEIGH_UPDATE = 1, NETEVENT_REDIRECT = 2, NETEVENT_DELAY_PROBE_TIME_UPDATE = 3, NETEVENT_IPV4_MPATH_HASH_UPDATE = 4, NETEVENT_IPV6_MPATH_HASH_UPDATE = 5, NETEVENT_IPV4_FWD_UPDATE_PRIORITY_UPDATE = 6, }; struct neigh_dump_filter { int master_idx; int dev_idx; }; struct neigh_sysctl_table { struct ctl_table_header *sysctl_header; struct ctl_table neigh_vars[21]; }; struct netlink_dump_control { int (*start)(struct netlink_callback *); int (*dump)(struct sk_buff *, struct netlink_callback *); int (*done)(struct netlink_callback *); void *data; struct module *module; u32 min_dump_alloc; }; struct rtnl_link_stats { __u32 rx_packets; __u32 tx_packets; __u32 rx_bytes; __u32 tx_bytes; __u32 rx_errors; __u32 tx_errors; __u32 rx_dropped; __u32 tx_dropped; __u32 multicast; __u32 collisions; __u32 rx_length_errors; __u32 rx_over_errors; __u32 rx_crc_errors; __u32 rx_frame_errors; __u32 rx_fifo_errors; __u32 rx_missed_errors; __u32 tx_aborted_errors; __u32 tx_carrier_errors; __u32 tx_fifo_errors; __u32 tx_heartbeat_errors; __u32 tx_window_errors; __u32 rx_compressed; __u32 tx_compressed; __u32 rx_nohandler; }; struct rtnl_link_ifmap { __u64 mem_start; __u64 mem_end; __u64 base_addr; __u16 irq; __u8 dma; __u8 port; }; enum { IFLA_PROTO_DOWN_REASON_UNSPEC = 0, IFLA_PROTO_DOWN_REASON_MASK = 1, IFLA_PROTO_DOWN_REASON_VALUE = 2, __IFLA_PROTO_DOWN_REASON_CNT = 3, IFLA_PROTO_DOWN_REASON_MAX = 2, }; enum { IFLA_BRPORT_UNSPEC = 0, IFLA_BRPORT_STATE = 1, IFLA_BRPORT_PRIORITY = 2, IFLA_BRPORT_COST = 3, IFLA_BRPORT_MODE = 4, IFLA_BRPORT_GUARD = 5, IFLA_BRPORT_PROTECT = 6, IFLA_BRPORT_FAST_LEAVE = 7, IFLA_BRPORT_LEARNING = 8, IFLA_BRPORT_UNICAST_FLOOD = 9, IFLA_BRPORT_PROXYARP = 10, IFLA_BRPORT_LEARNING_SYNC = 11, IFLA_BRPORT_PROXYARP_WIFI = 12, IFLA_BRPORT_ROOT_ID = 13, IFLA_BRPORT_BRIDGE_ID = 14, IFLA_BRPORT_DESIGNATED_PORT = 15, IFLA_BRPORT_DESIGNATED_COST = 16, IFLA_BRPORT_ID = 17, IFLA_BRPORT_NO = 18, IFLA_BRPORT_TOPOLOGY_CHANGE_ACK = 19, IFLA_BRPORT_CONFIG_PENDING = 20, IFLA_BRPORT_MESSAGE_AGE_TIMER = 21, IFLA_BRPORT_FORWARD_DELAY_TIMER = 22, IFLA_BRPORT_HOLD_TIMER = 23, IFLA_BRPORT_FLUSH = 24, IFLA_BRPORT_MULTICAST_ROUTER = 25, IFLA_BRPORT_PAD = 26, IFLA_BRPORT_MCAST_FLOOD = 27, IFLA_BRPORT_MCAST_TO_UCAST = 28, IFLA_BRPORT_VLAN_TUNNEL = 29, IFLA_BRPORT_BCAST_FLOOD = 30, IFLA_BRPORT_GROUP_FWD_MASK = 31, IFLA_BRPORT_NEIGH_SUPPRESS = 32, IFLA_BRPORT_ISOLATED = 33, IFLA_BRPORT_BACKUP_PORT = 34, IFLA_BRPORT_MRP_RING_OPEN = 35, IFLA_BRPORT_MRP_IN_OPEN = 36, IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT = 37, IFLA_BRPORT_MCAST_EHT_HOSTS_CNT = 38, __IFLA_BRPORT_MAX = 39, }; enum { IFLA_VF_INFO_UNSPEC = 0, IFLA_VF_INFO = 1, __IFLA_VF_INFO_MAX = 2, }; enum { IFLA_VF_UNSPEC = 0, IFLA_VF_MAC = 1, IFLA_VF_VLAN = 2, IFLA_VF_TX_RATE = 3, IFLA_VF_SPOOFCHK = 4, IFLA_VF_LINK_STATE = 5, IFLA_VF_RATE = 6, IFLA_VF_RSS_QUERY_EN = 7, IFLA_VF_STATS = 8, IFLA_VF_TRUST = 9, IFLA_VF_IB_NODE_GUID = 10, IFLA_VF_IB_PORT_GUID = 11, IFLA_VF_VLAN_LIST = 12, IFLA_VF_BROADCAST = 13, __IFLA_VF_MAX = 14, }; struct ifla_vf_mac { __u32 vf; __u8 mac[32]; }; struct ifla_vf_broadcast { __u8 broadcast[32]; }; struct ifla_vf_vlan { __u32 vf; __u32 vlan; __u32 qos; }; enum { IFLA_VF_VLAN_INFO_UNSPEC = 0, IFLA_VF_VLAN_INFO = 1, __IFLA_VF_VLAN_INFO_MAX = 2, }; struct ifla_vf_vlan_info { __u32 vf; __u32 vlan; __u32 qos; __be16 vlan_proto; }; struct ifla_vf_tx_rate { __u32 vf; __u32 rate; }; struct ifla_vf_rate { __u32 vf; __u32 min_tx_rate; __u32 max_tx_rate; }; struct ifla_vf_spoofchk { __u32 vf; __u32 setting; }; struct ifla_vf_link_state { __u32 vf; __u32 link_state; }; struct ifla_vf_rss_query_en { __u32 vf; __u32 setting; }; enum { IFLA_VF_STATS_RX_PACKETS = 0, IFLA_VF_STATS_TX_PACKETS = 1, IFLA_VF_STATS_RX_BYTES = 2, IFLA_VF_STATS_TX_BYTES = 3, IFLA_VF_STATS_BROADCAST = 4, IFLA_VF_STATS_MULTICAST = 5, IFLA_VF_STATS_PAD = 6, IFLA_VF_STATS_RX_DROPPED = 7, IFLA_VF_STATS_TX_DROPPED = 8, __IFLA_VF_STATS_MAX = 9, }; struct ifla_vf_trust { __u32 vf; __u32 setting; }; enum { IFLA_VF_PORT_UNSPEC = 0, IFLA_VF_PORT = 1, __IFLA_VF_PORT_MAX = 2, }; enum { IFLA_PORT_UNSPEC = 0, IFLA_PORT_VF = 1, IFLA_PORT_PROFILE = 2, IFLA_PORT_VSI_TYPE = 3, IFLA_PORT_INSTANCE_UUID = 4, IFLA_PORT_HOST_UUID = 5, IFLA_PORT_REQUEST = 6, IFLA_PORT_RESPONSE = 7, __IFLA_PORT_MAX = 8, }; struct if_stats_msg { __u8 family; __u8 pad1; __u16 pad2; __u32 ifindex; __u32 filter_mask; }; enum { IFLA_STATS_UNSPEC = 0, IFLA_STATS_LINK_64 = 1, IFLA_STATS_LINK_XSTATS = 2, IFLA_STATS_LINK_XSTATS_SLAVE = 3, IFLA_STATS_LINK_OFFLOAD_XSTATS = 4, IFLA_STATS_AF_SPEC = 5, __IFLA_STATS_MAX = 6, }; enum { IFLA_OFFLOAD_XSTATS_UNSPEC = 0, IFLA_OFFLOAD_XSTATS_CPU_HIT = 1, __IFLA_OFFLOAD_XSTATS_MAX = 2, }; enum { XDP_ATTACHED_NONE = 0, XDP_ATTACHED_DRV = 1, XDP_ATTACHED_SKB = 2, XDP_ATTACHED_HW = 3, XDP_ATTACHED_MULTI = 4, }; enum { IFLA_XDP_UNSPEC = 0, IFLA_XDP_FD = 1, IFLA_XDP_ATTACHED = 2, IFLA_XDP_FLAGS = 3, IFLA_XDP_PROG_ID = 4, IFLA_XDP_DRV_PROG_ID = 5, IFLA_XDP_SKB_PROG_ID = 6, IFLA_XDP_HW_PROG_ID = 7, IFLA_XDP_EXPECTED_FD = 8, __IFLA_XDP_MAX = 9, }; enum { IFLA_EVENT_NONE = 0, IFLA_EVENT_REBOOT = 1, IFLA_EVENT_FEATURES = 2, IFLA_EVENT_BONDING_FAILOVER = 3, IFLA_EVENT_NOTIFY_PEERS = 4, IFLA_EVENT_IGMP_RESEND = 5, IFLA_EVENT_BONDING_OPTIONS = 6, }; enum { IFLA_BRIDGE_FLAGS = 0, IFLA_BRIDGE_MODE = 1, IFLA_BRIDGE_VLAN_INFO = 2, IFLA_BRIDGE_VLAN_TUNNEL_INFO = 3, IFLA_BRIDGE_MRP = 4, IFLA_BRIDGE_CFM = 5, __IFLA_BRIDGE_MAX = 6, }; enum { BR_MCAST_DIR_RX = 0, BR_MCAST_DIR_TX = 1, BR_MCAST_DIR_SIZE = 2, }; enum rtattr_type_t { RTA_UNSPEC = 0, RTA_DST = 1, RTA_SRC = 2, RTA_IIF = 3, RTA_OIF = 4, RTA_GATEWAY = 5, RTA_PRIORITY = 6, RTA_PREFSRC = 7, RTA_METRICS = 8, RTA_MULTIPATH = 9, RTA_PROTOINFO = 10, RTA_FLOW = 11, RTA_CACHEINFO = 12, RTA_SESSION = 13, RTA_MP_ALGO = 14, RTA_TABLE = 15, RTA_MARK = 16, RTA_MFC_STATS = 17, RTA_VIA = 18, RTA_NEWDST = 19, RTA_PREF = 20, RTA_ENCAP_TYPE = 21, RTA_ENCAP = 22, RTA_EXPIRES = 23, RTA_PAD = 24, RTA_UID = 25, RTA_TTL_PROPAGATE = 26, RTA_IP_PROTO = 27, RTA_SPORT = 28, RTA_DPORT = 29, RTA_NH_ID = 30, __RTA_MAX = 31, }; struct rta_cacheinfo { __u32 rta_clntref; __u32 rta_lastuse; __s32 rta_expires; __u32 rta_error; __u32 rta_used; __u32 rta_id; __u32 rta_ts; __u32 rta_tsage; }; struct rtnl_af_ops { struct list_head list; int family; int (*fill_link_af)(struct sk_buff *, const struct net_device *, u32); size_t (*get_link_af_size)(const struct net_device *, u32); int (*validate_link_af)(const struct net_device *, const struct nlattr *); int (*set_link_af)(struct net_device *, const struct nlattr *, struct netlink_ext_ack *); int (*fill_stats_af)(struct sk_buff *, const struct net_device *); size_t (*get_stats_af_size)(const struct net_device *); }; struct rtnl_link { rtnl_doit_func doit; rtnl_dumpit_func dumpit; struct module *owner; unsigned int flags; struct callback_head rcu; }; enum { IF_LINK_MODE_DEFAULT = 0, IF_LINK_MODE_DORMANT = 1, IF_LINK_MODE_TESTING = 2, }; enum lw_bits { LW_URGENT = 0, }; struct seg6_pernet_data { struct mutex lock; struct in6_addr *tun_src; struct rhashtable hmac_infos; }; enum { BPF_F_RECOMPUTE_CSUM = 1, BPF_F_INVALIDATE_HASH = 2, }; enum { BPF_F_HDR_FIELD_MASK = 15, }; enum { BPF_F_PSEUDO_HDR = 16, BPF_F_MARK_MANGLED_0 = 32, BPF_F_MARK_ENFORCE = 64, }; enum { BPF_F_INGRESS = 1, }; enum { BPF_F_TUNINFO_IPV6 = 1, }; enum { BPF_F_ZERO_CSUM_TX = 2, BPF_F_DONT_FRAGMENT = 4, BPF_F_SEQ_NUMBER = 8, }; enum { BPF_CSUM_LEVEL_QUERY = 0, BPF_CSUM_LEVEL_INC = 1, BPF_CSUM_LEVEL_DEC = 2, BPF_CSUM_LEVEL_RESET = 3, }; enum { BPF_F_ADJ_ROOM_FIXED_GSO = 1, BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 = 2, BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 = 4, BPF_F_ADJ_ROOM_ENCAP_L4_GRE = 8, BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 16, BPF_F_ADJ_ROOM_NO_CSUM_RESET = 32, BPF_F_ADJ_ROOM_ENCAP_L2_ETH = 64, }; enum { BPF_ADJ_ROOM_ENCAP_L2_MASK = 255, BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 56, }; enum { BPF_SK_LOOKUP_F_REPLACE = 1, BPF_SK_LOOKUP_F_NO_REUSEPORT = 2, }; enum bpf_adj_room_mode { BPF_ADJ_ROOM_NET = 0, BPF_ADJ_ROOM_MAC = 1, }; enum bpf_hdr_start_off { BPF_HDR_START_MAC = 0, BPF_HDR_START_NET = 1, }; enum bpf_lwt_encap_mode { BPF_LWT_ENCAP_SEG6 = 0, BPF_LWT_ENCAP_SEG6_INLINE = 1, BPF_LWT_ENCAP_IP = 2, }; struct bpf_tunnel_key { __u32 tunnel_id; union { __u32 remote_ipv4; __u32 remote_ipv6[4]; }; __u8 tunnel_tos; __u8 tunnel_ttl; __u16 tunnel_ext; __u32 tunnel_label; }; struct bpf_xfrm_state { __u32 reqid; __u32 spi; __u16 family; __u16 ext; union { __u32 remote_ipv4; __u32 remote_ipv6[4]; }; }; struct bpf_tcp_sock { __u32 snd_cwnd; __u32 srtt_us; __u32 rtt_min; __u32 snd_ssthresh; __u32 rcv_nxt; __u32 snd_nxt; __u32 snd_una; __u32 mss_cache; __u32 ecn_flags; __u32 rate_delivered; __u32 rate_interval_us; __u32 packets_out; __u32 retrans_out; __u32 total_retrans; __u32 segs_in; __u32 data_segs_in; __u32 segs_out; __u32 data_segs_out; __u32 lost_out; __u32 sacked_out; __u64 bytes_received; __u64 bytes_acked; __u32 dsack_dups; __u32 delivered; __u32 delivered_ce; __u32 icsk_retransmits; }; struct bpf_sock_tuple { union { struct { __be32 saddr; __be32 daddr; __be16 sport; __be16 dport; } ipv4; struct { __be32 saddr[4]; __be32 daddr[4]; __be16 sport; __be16 dport; } ipv6; }; }; struct bpf_xdp_sock { __u32 queue_id; }; enum { BPF_SOCK_OPS_RTO_CB_FLAG = 1, BPF_SOCK_OPS_RETRANS_CB_FLAG = 2, BPF_SOCK_OPS_STATE_CB_FLAG = 4, BPF_SOCK_OPS_RTT_CB_FLAG = 8, BPF_SOCK_OPS_PARSE_ALL_HDR_OPT_CB_FLAG = 16, BPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG = 32, BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG = 64, BPF_SOCK_OPS_ALL_CB_FLAGS = 127, }; enum { BPF_SOCK_OPS_VOID = 0, BPF_SOCK_OPS_TIMEOUT_INIT = 1, BPF_SOCK_OPS_RWND_INIT = 2, BPF_SOCK_OPS_TCP_CONNECT_CB = 3, BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB = 4, BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB = 5, BPF_SOCK_OPS_NEEDS_ECN = 6, BPF_SOCK_OPS_BASE_RTT = 7, BPF_SOCK_OPS_RTO_CB = 8, BPF_SOCK_OPS_RETRANS_CB = 9, BPF_SOCK_OPS_STATE_CB = 10, BPF_SOCK_OPS_TCP_LISTEN_CB = 11, BPF_SOCK_OPS_RTT_CB = 12, BPF_SOCK_OPS_PARSE_HDR_OPT_CB = 13, BPF_SOCK_OPS_HDR_OPT_LEN_CB = 14, BPF_SOCK_OPS_WRITE_HDR_OPT_CB = 15, }; enum { TCP_BPF_IW = 1001, TCP_BPF_SNDCWND_CLAMP = 1002, TCP_BPF_DELACK_MAX = 1003, TCP_BPF_RTO_MIN = 1004, TCP_BPF_SYN = 1005, TCP_BPF_SYN_IP = 1006, TCP_BPF_SYN_MAC = 1007, }; enum { BPF_LOAD_HDR_OPT_TCP_SYN = 1, }; enum { BPF_FIB_LOOKUP_DIRECT = 1, BPF_FIB_LOOKUP_OUTPUT = 2, }; enum { BPF_FIB_LKUP_RET_SUCCESS = 0, BPF_FIB_LKUP_RET_BLACKHOLE = 1, BPF_FIB_LKUP_RET_UNREACHABLE = 2, BPF_FIB_LKUP_RET_PROHIBIT = 3, BPF_FIB_LKUP_RET_NOT_FWDED = 4, BPF_FIB_LKUP_RET_FWD_DISABLED = 5, BPF_FIB_LKUP_RET_UNSUPP_LWT = 6, BPF_FIB_LKUP_RET_NO_NEIGH = 7, BPF_FIB_LKUP_RET_FRAG_NEEDED = 8, }; struct bpf_fib_lookup { __u8 family; __u8 l4_protocol; __be16 sport; __be16 dport; union { __u16 tot_len; __u16 mtu_result; }; __u32 ifindex; union { __u8 tos; __be32 flowinfo; __u32 rt_metric; }; union { __be32 ipv4_src; __u32 ipv6_src[4]; }; union { __be32 ipv4_dst; __u32 ipv6_dst[4]; }; __be16 h_vlan_proto; __be16 h_vlan_TCI; __u8 smac[6]; __u8 dmac[6]; }; struct bpf_redir_neigh { __u32 nh_family; union { __be32 ipv4_nh; __u32 ipv6_nh[4]; }; }; enum bpf_check_mtu_flags { BPF_MTU_CHK_SEGS = 1, }; enum bpf_check_mtu_ret { BPF_MTU_CHK_RET_SUCCESS = 0, BPF_MTU_CHK_RET_FRAG_NEEDED = 1, BPF_MTU_CHK_RET_SEGS_TOOBIG = 2, }; enum rt_scope_t { RT_SCOPE_UNIVERSE = 0, RT_SCOPE_SITE = 200, RT_SCOPE_LINK = 253, RT_SCOPE_HOST = 254, RT_SCOPE_NOWHERE = 255, }; enum rt_class_t { RT_TABLE_UNSPEC = 0, RT_TABLE_COMPAT = 252, RT_TABLE_DEFAULT = 253, RT_TABLE_MAIN = 254, RT_TABLE_LOCAL = 255, RT_TABLE_MAX = 4294967295, }; struct nl_info { struct nlmsghdr *nlh; struct net *nl_net; u32 portid; u8 skip_notify: 1; u8 skip_notify_kernel: 1; }; struct inet_timewait_sock { struct sock_common __tw_common; __u32 tw_mark; volatile unsigned char tw_substate; unsigned char tw_rcv_wscale; __be16 tw_sport; unsigned int tw_kill: 1; unsigned int tw_transparent: 1; unsigned int tw_flowlabel: 20; unsigned int tw_pad: 2; unsigned int tw_tos: 8; u32 tw_txhash; u32 tw_priority; struct timer_list tw_timer; struct inet_bind_bucket *tw_tb; }; struct tcp_timewait_sock { struct inet_timewait_sock tw_sk; u32 tw_rcv_wnd; u32 tw_ts_offset; u32 tw_ts_recent; u32 tw_last_oow_ack_time; int tw_ts_recent_stamp; u32 tw_tx_delay; struct tcp_md5sig_key *tw_md5_key; }; struct udp_sock { struct inet_sock inet; int pending; unsigned int corkflag; __u8 encap_type; unsigned char no_check6_tx: 1; unsigned char no_check6_rx: 1; unsigned char encap_enabled: 1; unsigned char gro_enabled: 1; unsigned char accept_udp_l4: 1; unsigned char accept_udp_fraglist: 1; __u16 len; __u16 gso_size; __u16 pcslen; __u16 pcrlen; __u8 pcflag; __u8 unused[3]; int (*encap_rcv)(struct sock *, struct sk_buff *); int (*encap_err_lookup)(struct sock *, struct sk_buff *); void (*encap_destroy)(struct sock *); struct sk_buff * (*gro_receive)(struct sock *, struct list_head *, struct sk_buff *); int (*gro_complete)(struct sock *, struct sk_buff *, int); long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; struct sk_buff_head reader_queue; int forward_deficit; long: 32; long: 64; long: 64; long: 64; long: 64; }; struct udp6_sock { struct udp_sock udp; struct ipv6_pinfo inet6; long: 64; long: 64; long: 64; long: 64; }; struct tcp6_sock { struct tcp_sock tcp; struct ipv6_pinfo inet6; }; struct fib6_result; struct fib6_config; struct ipv6_stub { int (*ipv6_sock_mc_join)(struct sock *, int, const struct in6_addr *); int (*ipv6_sock_mc_drop)(struct sock *, int, const struct in6_addr *); struct dst_entry * (*ipv6_dst_lookup_flow)(struct net *, const struct sock *, struct flowi6 *, const struct in6_addr *); int (*ipv6_route_input)(struct sk_buff *); struct fib6_table * (*fib6_get_table)(struct net *, u32); int (*fib6_lookup)(struct net *, int, struct flowi6 *, struct fib6_result *, int); int (*fib6_table_lookup)(struct net *, struct fib6_table *, int, struct flowi6 *, struct fib6_result *, int); void (*fib6_select_path)(const struct net *, struct fib6_result *, struct flowi6 *, int, bool, const struct sk_buff *, int); u32 (*ip6_mtu_from_fib6)(const struct fib6_result *, const struct in6_addr *, const struct in6_addr *); int (*fib6_nh_init)(struct net *, struct fib6_nh *, struct fib6_config *, gfp_t, struct netlink_ext_ack *); void (*fib6_nh_release)(struct fib6_nh *); void (*fib6_update_sernum)(struct net *, struct fib6_info *); int (*ip6_del_rt)(struct net *, struct fib6_info *, bool); void (*fib6_rt_update)(struct net *, struct fib6_info *, struct nl_info *); void (*udpv6_encap_enable)(); void (*ndisc_send_na)(struct net_device *, const struct in6_addr *, const struct in6_addr *, bool, bool, bool, bool); void (*xfrm6_local_rxpmtu)(struct sk_buff *, u32); int (*xfrm6_udp_encap_rcv)(struct sock *, struct sk_buff *); int (*xfrm6_rcv_encap)(struct sk_buff *, int, __be32, int); struct neigh_table *nd_tbl; int (*ipv6_fragment)(struct net *, struct sock *, struct sk_buff *, int (*)(struct net *, struct sock *, struct sk_buff *)); struct net_device * (*ipv6_dev_find)(struct net *, const struct in6_addr *, struct net_device *); }; struct fib6_result { struct fib6_nh *nh; struct fib6_info *f6i; u32 fib6_flags; u8 fib6_type; struct rt6_info *rt6; }; struct fib6_config { u32 fc_table; u32 fc_metric; int fc_dst_len; int fc_src_len; int fc_ifindex; u32 fc_flags; u32 fc_protocol; u16 fc_type; u16 fc_delete_all_nh: 1; u16 fc_ignore_dev_down: 1; u16 __unused: 14; u32 fc_nh_id; struct in6_addr fc_dst; struct in6_addr fc_src; struct in6_addr fc_prefsrc; struct in6_addr fc_gateway; long unsigned int fc_expires; struct nlattr *fc_mx; int fc_mx_len; int fc_mp_len; struct nlattr *fc_mp; struct nl_info fc_nlinfo; struct nlattr *fc_encap; u16 fc_encap_type; bool fc_is_fdb; }; struct ipv6_bpf_stub { int (*inet6_bind)(struct sock *, struct sockaddr *, int, u32); struct sock * (*udp6_lib_lookup)(struct net *, const struct in6_addr *, __be16, const struct in6_addr *, __be16, int, int, struct udp_table *, struct sk_buff *); }; struct fib_result { __be32 prefix; unsigned char prefixlen; unsigned char nh_sel; unsigned char type; unsigned char scope; u32 tclassid; struct fib_nh_common *nhc; struct fib_info *fi; struct fib_table *table; struct hlist_head *fa_head; }; enum { INET_ECN_NOT_ECT = 0, INET_ECN_ECT_1 = 1, INET_ECN_ECT_0 = 2, INET_ECN_CE = 3, INET_ECN_MASK = 3, }; struct tcp_skb_cb { __u32 seq; __u32 end_seq; union { __u32 tcp_tw_isn; struct { u16 tcp_gso_segs; u16 tcp_gso_size; }; }; __u8 tcp_flags; __u8 sacked; __u8 ip_dsfield; __u8 txstamp_ack: 1; __u8 eor: 1; __u8 has_rxtstamp: 1; __u8 unused: 5; __u32 ack_seq; union { struct { __u32 in_flight: 30; __u32 is_app_limited: 1; __u32 unused: 1; __u32 delivered; u64 first_tx_mstamp; u64 delivered_mstamp; } tx; union { struct inet_skb_parm h4; struct inet6_skb_parm h6; } header; }; }; struct strp_msg { int full_len; int offset; }; struct xdp_umem { void *addrs; u64 size; u32 headroom; u32 chunk_size; u32 chunks; u32 npgs; struct user_struct *user; refcount_t users; u8 flags; bool zc; struct page **pgs; int id; struct list_head xsk_dma_list; struct work_struct work; }; struct xsk_queue; struct xdp_sock { struct sock sk; struct xsk_queue *rx; struct net_device *dev; struct xdp_umem *umem; struct list_head flush_node; struct xsk_buff_pool *pool; u16 queue_id; bool zc; enum { XSK_READY = 0, XSK_BOUND = 1, XSK_UNBOUND = 2, } state; long: 64; struct xsk_queue *tx; struct list_head tx_list; spinlock_t rx_lock; u64 rx_dropped; u64 rx_queue_full; struct list_head map_list; spinlock_t map_list_lock; struct mutex mutex; struct xsk_queue *fq_tmp; struct xsk_queue *cq_tmp; long: 64; }; struct ipv6_sr_hdr { __u8 nexthdr; __u8 hdrlen; __u8 type; __u8 segments_left; __u8 first_segment; __u8 flags; __u16 tag; struct in6_addr segments[0]; }; enum { SEG6_LOCAL_ACTION_UNSPEC = 0, SEG6_LOCAL_ACTION_END = 1, SEG6_LOCAL_ACTION_END_X = 2, SEG6_LOCAL_ACTION_END_T = 3, SEG6_LOCAL_ACTION_END_DX2 = 4, SEG6_LOCAL_ACTION_END_DX6 = 5, SEG6_LOCAL_ACTION_END_DX4 = 6, SEG6_LOCAL_ACTION_END_DT6 = 7, SEG6_LOCAL_ACTION_END_DT4 = 8, SEG6_LOCAL_ACTION_END_B6 = 9, SEG6_LOCAL_ACTION_END_B6_ENCAP = 10, SEG6_LOCAL_ACTION_END_BM = 11, SEG6_LOCAL_ACTION_END_S = 12, SEG6_LOCAL_ACTION_END_AS = 13, SEG6_LOCAL_ACTION_END_AM = 14, SEG6_LOCAL_ACTION_END_BPF = 15, SEG6_LOCAL_ACTION_END_DT46 = 16, __SEG6_LOCAL_ACTION_MAX = 17, }; struct seg6_bpf_srh_state { struct ipv6_sr_hdr *srh; u16 hdrlen; bool valid; }; struct tls_crypto_info { __u16 version; __u16 cipher_type; }; struct tls12_crypto_info_aes_gcm_128 { struct tls_crypto_info info; unsigned char iv[8]; unsigned char key[16]; unsigned char salt[4]; unsigned char rec_seq[8]; }; struct tls12_crypto_info_aes_gcm_256 { struct tls_crypto_info info; unsigned char iv[8]; unsigned char key[32]; unsigned char salt[4]; unsigned char rec_seq[8]; }; struct tls12_crypto_info_chacha20_poly1305 { struct tls_crypto_info info; unsigned char iv[12]; unsigned char key[32]; unsigned char salt[0]; unsigned char rec_seq[8]; }; struct tls_sw_context_rx { struct crypto_aead *aead_recv; struct crypto_wait async_wait; struct strparser strp; struct sk_buff_head rx_list; void (*saved_data_ready)(struct sock *); struct sk_buff *recv_pkt; u8 control; u8 async_capable: 1; u8 decrypted: 1; atomic_t decrypt_pending; spinlock_t decrypt_compl_lock; bool async_notify; }; struct cipher_context { char *iv; char *rec_seq; }; union tls_crypto_context { struct tls_crypto_info info; union { struct tls12_crypto_info_aes_gcm_128 aes_gcm_128; struct tls12_crypto_info_aes_gcm_256 aes_gcm_256; struct tls12_crypto_info_chacha20_poly1305 chacha20_poly1305; }; }; struct tls_prot_info { u16 version; u16 cipher_type; u16 prepend_size; u16 tag_size; u16 overhead_size; u16 iv_size; u16 salt_size; u16 rec_seq_size; u16 aad_size; u16 tail_size; }; struct tls_context { struct tls_prot_info prot_info; u8 tx_conf: 3; u8 rx_conf: 3; int (*push_pending_record)(struct sock *, int); void (*sk_write_space)(struct sock *); void *priv_ctx_tx; void *priv_ctx_rx; struct net_device *netdev; struct cipher_context tx; struct cipher_context rx; struct scatterlist *partially_sent_record; u16 partially_sent_offset; bool in_tcp_sendpages; bool pending_open_record_frags; struct mutex tx_lock; long unsigned int flags; struct proto *sk_proto; struct sock *sk; void (*sk_destruct)(struct sock *); union tls_crypto_context crypto_send; union tls_crypto_context crypto_recv; struct list_head list; refcount_t refcount; struct callback_head rcu; }; typedef u64 (*btf_bpf_skb_get_pay_offset)(struct sk_buff *); typedef u64 (*btf_bpf_skb_get_nlattr)(struct sk_buff *, u32, u32); typedef u64 (*btf_bpf_skb_get_nlattr_nest)(struct sk_buff *, u32, u32); typedef u64 (*btf_bpf_skb_load_helper_8)(const struct sk_buff *, const void *, int, int); typedef u64 (*btf_bpf_skb_load_helper_8_no_cache)(const struct sk_buff *, int); typedef u64 (*btf_bpf_skb_load_helper_16)(const struct sk_buff *, const void *, int, int); typedef u64 (*btf_bpf_skb_load_helper_16_no_cache)(const struct sk_buff *, int); typedef u64 (*btf_bpf_skb_load_helper_32)(const struct sk_buff *, const void *, int, int); typedef u64 (*btf_bpf_skb_load_helper_32_no_cache)(const struct sk_buff *, int); struct bpf_scratchpad { union { __be32 diff[128]; u8 buff[512]; }; }; typedef u64 (*btf_bpf_skb_store_bytes)(struct sk_buff *, u32, const void *, u32, u64); typedef u64 (*btf_bpf_skb_load_bytes)(const struct sk_buff *, u32, void *, u32); typedef u64 (*btf_bpf_flow_dissector_load_bytes)(const struct bpf_flow_dissector *, u32, void *, u32); typedef u64 (*btf_bpf_skb_load_bytes_relative)(const struct sk_buff *, u32, void *, u32, u32); typedef u64 (*btf_bpf_skb_pull_data)(struct sk_buff *, u32); typedef u64 (*btf_bpf_sk_fullsock)(struct sock *); typedef u64 (*btf_sk_skb_pull_data)(struct sk_buff *, u32); typedef u64 (*btf_bpf_l3_csum_replace)(struct sk_buff *, u32, u64, u64, u64); typedef u64 (*btf_bpf_l4_csum_replace)(struct sk_buff *, u32, u64, u64, u64); typedef u64 (*btf_bpf_csum_diff)(__be32 *, u32, __be32 *, u32, __wsum); typedef u64 (*btf_bpf_csum_update)(struct sk_buff *, __wsum); typedef u64 (*btf_bpf_csum_level)(struct sk_buff *, u64); enum { BPF_F_NEIGH = 2, BPF_F_PEER = 4, BPF_F_NEXTHOP = 8, }; typedef u64 (*btf_bpf_clone_redirect)(struct sk_buff *, u32, u64); typedef u64 (*btf_bpf_redirect)(u32, u64); typedef u64 (*btf_bpf_redirect_peer)(u32, u64); typedef u64 (*btf_bpf_redirect_neigh)(u32, struct bpf_redir_neigh *, int, u64); typedef u64 (*btf_bpf_msg_apply_bytes)(struct sk_msg *, u32); typedef u64 (*btf_bpf_msg_cork_bytes)(struct sk_msg *, u32); typedef u64 (*btf_bpf_msg_pull_data)(struct sk_msg *, u32, u32, u64); typedef u64 (*btf_bpf_msg_push_data)(struct sk_msg *, u32, u32, u64); typedef u64 (*btf_bpf_msg_pop_data)(struct sk_msg *, u32, u32, u64); typedef u64 (*btf_bpf_get_cgroup_classid_curr)(); typedef u64 (*btf_bpf_skb_cgroup_classid)(const struct sk_buff *); typedef u64 (*btf_bpf_get_cgroup_classid)(const struct sk_buff *); typedef u64 (*btf_bpf_get_route_realm)(const struct sk_buff *); typedef u64 (*btf_bpf_get_hash_recalc)(struct sk_buff *); typedef u64 (*btf_bpf_set_hash_invalid)(struct sk_buff *); typedef u64 (*btf_bpf_set_hash)(struct sk_buff *, u32); typedef u64 (*btf_bpf_skb_vlan_push)(struct sk_buff *, __be16, u16); typedef u64 (*btf_bpf_skb_vlan_pop)(struct sk_buff *); typedef u64 (*btf_bpf_skb_change_proto)(struct sk_buff *, __be16, u64); typedef u64 (*btf_bpf_skb_change_type)(struct sk_buff *, u32); typedef u64 (*btf_sk_skb_adjust_room)(struct sk_buff *, s32, u32, u64); typedef u64 (*btf_bpf_skb_adjust_room)(struct sk_buff *, s32, u32, u64); typedef u64 (*btf_bpf_skb_change_tail)(struct sk_buff *, u32, u64); typedef u64 (*btf_sk_skb_change_tail)(struct sk_buff *, u32, u64); typedef u64 (*btf_bpf_skb_change_head)(struct sk_buff *, u32, u64); typedef u64 (*btf_sk_skb_change_head)(struct sk_buff *, u32, u64); typedef u64 (*btf_bpf_xdp_adjust_head)(struct xdp_buff *, int); typedef u64 (*btf_bpf_xdp_adjust_tail)(struct xdp_buff *, int); typedef u64 (*btf_bpf_xdp_adjust_meta)(struct xdp_buff *, int); typedef u64 (*btf_bpf_xdp_redirect)(u32, u64); typedef u64 (*btf_bpf_xdp_redirect_map)(struct bpf_map *, u32, u64); typedef u64 (*btf_bpf_skb_event_output)(struct sk_buff *, struct bpf_map *, u64, void *, u64); typedef u64 (*btf_bpf_skb_get_tunnel_key)(struct sk_buff *, struct bpf_tunnel_key *, u32, u64); typedef u64 (*btf_bpf_skb_get_tunnel_opt)(struct sk_buff *, u8 *, u32); typedef u64 (*btf_bpf_skb_set_tunnel_key)(struct sk_buff *, const struct bpf_tunnel_key *, u32, u64); typedef u64 (*btf_bpf_skb_set_tunnel_opt)(struct sk_buff *, const u8 *, u32); typedef u64 (*btf_bpf_skb_under_cgroup)(struct sk_buff *, struct bpf_map *, u32); typedef u64 (*btf_bpf_skb_cgroup_id)(const struct sk_buff *); typedef u64 (*btf_bpf_skb_ancestor_cgroup_id)(const struct sk_buff *, int); typedef u64 (*btf_bpf_sk_cgroup_id)(struct sock *); typedef u64 (*btf_bpf_sk_ancestor_cgroup_id)(struct sock *, int); typedef u64 (*btf_bpf_xdp_event_output)(struct xdp_buff *, struct bpf_map *, u64, void *, u64); typedef u64 (*btf_bpf_get_socket_cookie)(struct sk_buff *); typedef u64 (*btf_bpf_get_socket_cookie_sock_addr)(struct bpf_sock_addr_kern *); typedef u64 (*btf_bpf_get_socket_cookie_sock)(struct sock *); typedef u64 (*btf_bpf_get_socket_ptr_cookie)(struct sock *); typedef u64 (*btf_bpf_get_socket_cookie_sock_ops)(struct bpf_sock_ops_kern *); typedef u64 (*btf_bpf_get_netns_cookie_sock)(struct sock *); typedef u64 (*btf_bpf_get_netns_cookie_sock_addr)(struct bpf_sock_addr_kern *); typedef u64 (*btf_bpf_get_socket_uid)(struct sk_buff *); typedef u64 (*btf_bpf_sock_addr_setsockopt)(struct bpf_sock_addr_kern *, int, int, char *, int); typedef u64 (*btf_bpf_sock_addr_getsockopt)(struct bpf_sock_addr_kern *, int, int, char *, int); typedef u64 (*btf_bpf_sock_ops_setsockopt)(struct bpf_sock_ops_kern *, int, int, char *, int); typedef u64 (*btf_bpf_sock_ops_getsockopt)(struct bpf_sock_ops_kern *, int, int, char *, int); typedef u64 (*btf_bpf_sock_ops_cb_flags_set)(struct bpf_sock_ops_kern *, int); typedef u64 (*btf_bpf_bind)(struct bpf_sock_addr_kern *, struct sockaddr *, int); typedef u64 (*btf_bpf_skb_get_xfrm_state)(struct sk_buff *, u32, struct bpf_xfrm_state *, u32, u64); typedef u64 (*btf_bpf_xdp_fib_lookup)(struct xdp_buff *, struct bpf_fib_lookup *, int, u32); typedef u64 (*btf_bpf_skb_fib_lookup)(struct sk_buff *, struct bpf_fib_lookup *, int, u32); typedef u64 (*btf_bpf_skb_check_mtu)(struct sk_buff *, u32, u32 *, s32, u64); typedef u64 (*btf_bpf_xdp_check_mtu)(struct xdp_buff *, u32, u32 *, s32, u64); typedef u64 (*btf_bpf_lwt_in_push_encap)(struct sk_buff *, u32, void *, u32); typedef u64 (*btf_bpf_lwt_xmit_push_encap)(struct sk_buff *, u32, void *, u32); typedef u64 (*btf_bpf_lwt_seg6_store_bytes)(struct sk_buff *, u32, const void *, u32); typedef u64 (*btf_bpf_lwt_seg6_action)(struct sk_buff *, u32, void *, u32); typedef u64 (*btf_bpf_lwt_seg6_adjust_srh)(struct sk_buff *, u32, s32); typedef u64 (*btf_bpf_skc_lookup_tcp)(struct sk_buff *, struct bpf_sock_tuple *, u32, u64, u64); typedef u64 (*btf_bpf_sk_lookup_tcp)(struct sk_buff *, struct bpf_sock_tuple *, u32, u64, u64); typedef u64 (*btf_bpf_sk_lookup_udp)(struct sk_buff *, struct bpf_sock_tuple *, u32, u64, u64); typedef u64 (*btf_bpf_sk_release)(struct sock *); typedef u64 (*btf_bpf_xdp_sk_lookup_udp)(struct xdp_buff *, struct bpf_sock_tuple *, u32, u32, u64); typedef u64 (*btf_bpf_xdp_skc_lookup_tcp)(struct xdp_buff *, struct bpf_sock_tuple *, u32, u32, u64); typedef u64 (*btf_bpf_xdp_sk_lookup_tcp)(struct xdp_buff *, struct bpf_sock_tuple *, u32, u32, u64); typedef u64 (*btf_bpf_sock_addr_skc_lookup_tcp)(struct bpf_sock_addr_kern *, struct bpf_sock_tuple *, u32, u64, u64); typedef u64 (*btf_bpf_sock_addr_sk_lookup_tcp)(struct bpf_sock_addr_kern *, struct bpf_sock_tuple *, u32, u64, u64); typedef u64 (*btf_bpf_sock_addr_sk_lookup_udp)(struct bpf_sock_addr_kern *, struct bpf_sock_tuple *, u32, u64, u64); typedef u64 (*btf_bpf_tcp_sock)(struct sock *); typedef u64 (*btf_bpf_get_listener_sock)(struct sock *); typedef u64 (*btf_bpf_skb_ecn_set_ce)(struct sk_buff *); typedef u64 (*btf_bpf_tcp_check_syncookie)(struct sock *, void *, u32, struct tcphdr *, u32); typedef u64 (*btf_bpf_tcp_gen_syncookie)(struct sock *, void *, u32, struct tcphdr *, u32); typedef u64 (*btf_bpf_sk_assign)(struct sk_buff *, struct sock *, u64); typedef u64 (*btf_bpf_sock_ops_load_hdr_opt)(struct bpf_sock_ops_kern *, void *, u32, u64); typedef u64 (*btf_bpf_sock_ops_store_hdr_opt)(struct bpf_sock_ops_kern *, const void *, u32, u64); typedef u64 (*btf_bpf_sock_ops_reserve_hdr_opt)(struct bpf_sock_ops_kern *, u32, u64); typedef u64 (*btf_sk_select_reuseport)(struct sk_reuseport_kern *, struct bpf_map *, void *, u32); typedef u64 (*btf_sk_reuseport_load_bytes)(const struct sk_reuseport_kern *, u32, void *, u32); typedef u64 (*btf_sk_reuseport_load_bytes_relative)(const struct sk_reuseport_kern *, u32, void *, u32, u32); typedef u64 (*btf_bpf_sk_lookup_assign)(struct bpf_sk_lookup_kern *, struct sock *, u64); typedef u64 (*btf_bpf_skc_to_tcp6_sock)(struct sock *); typedef u64 (*btf_bpf_skc_to_tcp_sock)(struct sock *); typedef u64 (*btf_bpf_skc_to_tcp_timewait_sock)(struct sock *); typedef u64 (*btf_bpf_skc_to_tcp_request_sock)(struct sock *); typedef u64 (*btf_bpf_skc_to_udp6_sock)(struct sock *); typedef u64 (*btf_bpf_sock_from_file)(struct file *); struct bpf_dtab_netdev___2; struct bpf_cpu_map_entry___2; enum { INET_DIAG_REQ_NONE = 0, INET_DIAG_REQ_BYTECODE = 1, INET_DIAG_REQ_SK_BPF_STORAGES = 2, INET_DIAG_REQ_PROTOCOL = 3, __INET_DIAG_REQ_MAX = 4, }; struct sock_diag_req { __u8 sdiag_family; __u8 sdiag_protocol; }; struct sock_diag_handler { __u8 family; int (*dump)(struct sk_buff *, struct nlmsghdr *); int (*get_info)(struct sk_buff *, struct sock *); int (*destroy)(struct sk_buff *, struct nlmsghdr *); }; struct broadcast_sk { struct sock *sk; struct work_struct work; }; typedef int gifconf_func_t(struct net_device *, char *, int, int); struct hwtstamp_config { int flags; int tx_type; int rx_filter; }; enum hwtstamp_tx_types { HWTSTAMP_TX_OFF = 0, HWTSTAMP_TX_ON = 1, HWTSTAMP_TX_ONESTEP_SYNC = 2, HWTSTAMP_TX_ONESTEP_P2P = 3, __HWTSTAMP_TX_CNT = 4, }; enum hwtstamp_rx_filters { HWTSTAMP_FILTER_NONE = 0, HWTSTAMP_FILTER_ALL = 1, HWTSTAMP_FILTER_SOME = 2, HWTSTAMP_FILTER_PTP_V1_L4_EVENT = 3, HWTSTAMP_FILTER_PTP_V1_L4_SYNC = 4, HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ = 5, HWTSTAMP_FILTER_PTP_V2_L4_EVENT = 6, HWTSTAMP_FILTER_PTP_V2_L4_SYNC = 7, HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ = 8, HWTSTAMP_FILTER_PTP_V2_L2_EVENT = 9, HWTSTAMP_FILTER_PTP_V2_L2_SYNC = 10, HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ = 11, HWTSTAMP_FILTER_PTP_V2_EVENT = 12, HWTSTAMP_FILTER_PTP_V2_SYNC = 13, HWTSTAMP_FILTER_PTP_V2_DELAY_REQ = 14, HWTSTAMP_FILTER_NTP_ALL = 15, __HWTSTAMP_FILTER_CNT = 16, }; struct tso_t { int next_frag_idx; int size; void *data; u16 ip_id; u8 tlen; bool ipv6; u32 tcp_seq; }; struct fib_notifier_info { int family; struct netlink_ext_ack *extack; }; enum fib_event_type { FIB_EVENT_ENTRY_REPLACE = 0, FIB_EVENT_ENTRY_APPEND = 1, FIB_EVENT_ENTRY_ADD = 2, FIB_EVENT_ENTRY_DEL = 3, FIB_EVENT_RULE_ADD = 4, FIB_EVENT_RULE_DEL = 5, FIB_EVENT_NH_ADD = 6, FIB_EVENT_NH_DEL = 7, FIB_EVENT_VIF_ADD = 8, FIB_EVENT_VIF_DEL = 9, }; struct fib_notifier_net { struct list_head fib_notifier_ops; struct atomic_notifier_head fib_chain; }; struct xdp_frame_bulk { int count; void *xa; void *q[16]; }; struct xdp_attachment_info { struct bpf_prog *prog; u32 flags; }; struct xdp_buff_xsk; struct xsk_buff_pool { struct device *dev; struct net_device *netdev; struct list_head xsk_tx_list; spinlock_t xsk_tx_list_lock; refcount_t users; struct xdp_umem *umem; struct work_struct work; struct list_head free_list; u32 heads_cnt; u16 queue_id; long: 16; long: 64; long: 64; long: 64; struct xsk_queue *fq; struct xsk_queue *cq; dma_addr_t *dma_pages; struct xdp_buff_xsk *heads; u64 chunk_mask; u64 addrs_cnt; u32 free_list_cnt; u32 dma_pages_cnt; u32 free_heads_cnt; u32 headroom; u32 chunk_size; u32 frame_len; u8 cached_need_wakeup; bool uses_need_wakeup; bool dma_need_sync; bool unaligned; void *addrs; spinlock_t cq_lock; struct xdp_buff_xsk *free_heads[0]; long: 64; long: 64; long: 64; long: 64; }; struct xdp_buff_xsk { struct xdp_buff xdp; dma_addr_t dma; dma_addr_t frame_dma; struct xsk_buff_pool *pool; bool unaligned; u64 orig_addr; struct list_head free_list_node; }; struct flow_match_meta { struct flow_dissector_key_meta *key; struct flow_dissector_key_meta *mask; }; struct flow_match_basic { struct flow_dissector_key_basic *key; struct flow_dissector_key_basic *mask; }; struct flow_match_control { struct flow_dissector_key_control *key; struct flow_dissector_key_control *mask; }; struct flow_match_eth_addrs { struct flow_dissector_key_eth_addrs *key; struct flow_dissector_key_eth_addrs *mask; }; struct flow_match_vlan { struct flow_dissector_key_vlan *key; struct flow_dissector_key_vlan *mask; }; struct flow_match_ipv4_addrs { struct flow_dissector_key_ipv4_addrs *key; struct flow_dissector_key_ipv4_addrs *mask; }; struct flow_match_ipv6_addrs { struct flow_dissector_key_ipv6_addrs *key; struct flow_dissector_key_ipv6_addrs *mask; }; struct flow_match_ip { struct flow_dissector_key_ip *key; struct flow_dissector_key_ip *mask; }; struct flow_match_ports { struct flow_dissector_key_ports *key; struct flow_dissector_key_ports *mask; }; struct flow_match_icmp { struct flow_dissector_key_icmp *key; struct flow_dissector_key_icmp *mask; }; struct flow_match_tcp { struct flow_dissector_key_tcp *key; struct flow_dissector_key_tcp *mask; }; struct flow_match_mpls { struct flow_dissector_key_mpls *key; struct flow_dissector_key_mpls *mask; }; struct flow_match_enc_keyid { struct flow_dissector_key_keyid *key; struct flow_dissector_key_keyid *mask; }; struct flow_match_enc_opts { struct flow_dissector_key_enc_opts *key; struct flow_dissector_key_enc_opts *mask; }; struct flow_match_ct { struct flow_dissector_key_ct *key; struct flow_dissector_key_ct *mask; }; enum flow_block_command { FLOW_BLOCK_BIND = 0, FLOW_BLOCK_UNBIND = 1, }; enum flow_block_binder_type { FLOW_BLOCK_BINDER_TYPE_UNSPEC = 0, FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS = 1, FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS = 2, FLOW_BLOCK_BINDER_TYPE_RED_EARLY_DROP = 3, FLOW_BLOCK_BINDER_TYPE_RED_MARK = 4, }; struct flow_block_offload { enum flow_block_command command; enum flow_block_binder_type binder_type; bool block_shared; bool unlocked_driver_cb; struct net *net; struct flow_block *block; struct list_head cb_list; struct list_head *driver_block_list; struct netlink_ext_ack *extack; struct Qdisc *sch; }; struct flow_block_cb; struct flow_block_indr { struct list_head list; struct net_device *dev; struct Qdisc *sch; enum flow_block_binder_type binder_type; void *data; void *cb_priv; void (*cleanup)(struct flow_block_cb *); }; struct flow_block_cb { struct list_head driver_list; struct list_head list; flow_setup_cb_t *cb; void *cb_ident; void *cb_priv; void (*release)(void *); struct flow_block_indr indr; unsigned int refcnt; }; typedef int flow_indr_block_bind_cb_t(struct net_device *, struct Qdisc *, void *, enum tc_setup_type, void *, void *, void (*)(struct flow_block_cb *)); struct flow_indr_dev { struct list_head list; flow_indr_block_bind_cb_t *cb; void *cb_priv; refcount_t refcnt; struct callback_head rcu; }; struct rx_queue_attribute { struct attribute attr; ssize_t (*show)(struct netdev_rx_queue *, char *); ssize_t (*store)(struct netdev_rx_queue *, const char *, size_t); }; struct netdev_queue_attribute { struct attribute attr; ssize_t (*show)(struct netdev_queue *, char *); ssize_t (*store)(struct netdev_queue *, const char *, size_t); }; struct inet6_ifaddr { struct in6_addr addr; __u32 prefix_len; __u32 rt_priority; __u32 valid_lft; __u32 prefered_lft; refcount_t refcnt; spinlock_t lock; int state; __u32 flags; __u8 dad_probes; __u8 stable_privacy_retry; __u16 scope; __u64 dad_nonce; long unsigned int cstamp; long unsigned int tstamp; struct delayed_work dad_work; struct inet6_dev *idev; struct fib6_info *rt; struct hlist_node addr_lst; struct list_head if_list; struct list_head tmp_list; struct inet6_ifaddr *ifpub; int regen_count; bool tokenized; struct callback_head rcu; struct in6_addr peer_addr; }; struct fib_rule_uid_range { __u32 start; __u32 end; }; enum { FRA_UNSPEC = 0, FRA_DST = 1, FRA_SRC = 2, FRA_IIFNAME = 3, FRA_GOTO = 4, FRA_UNUSED2 = 5, FRA_PRIORITY = 6, FRA_UNUSED3 = 7, FRA_UNUSED4 = 8, FRA_UNUSED5 = 9, FRA_FWMARK = 10, FRA_FLOW = 11, FRA_TUN_ID = 12, FRA_SUPPRESS_IFGROUP = 13, FRA_SUPPRESS_PREFIXLEN = 14, FRA_TABLE = 15, FRA_FWMASK = 16, FRA_OIFNAME = 17, FRA_PAD = 18, FRA_L3MDEV = 19, FRA_UID_RANGE = 20, FRA_PROTOCOL = 21, FRA_IP_PROTO = 22, FRA_SPORT_RANGE = 23, FRA_DPORT_RANGE = 24, __FRA_MAX = 25, }; enum { FR_ACT_UNSPEC = 0, FR_ACT_TO_TBL = 1, FR_ACT_GOTO = 2, FR_ACT_NOP = 3, FR_ACT_RES3 = 4, FR_ACT_RES4 = 5, FR_ACT_BLACKHOLE = 6, FR_ACT_UNREACHABLE = 7, FR_ACT_PROHIBIT = 8, __FR_ACT_MAX = 9, }; struct fib_rule_notifier_info { struct fib_notifier_info info; struct fib_rule *rule; }; struct trace_event_raw_kfree_skb { struct trace_entry ent; void *skbaddr; void *location; short unsigned int protocol; char __data[0]; }; struct trace_event_raw_consume_skb { struct trace_entry ent; void *skbaddr; char __data[0]; }; struct trace_event_raw_skb_copy_datagram_iovec { struct trace_entry ent; const void *skbaddr; int len; char __data[0]; }; struct trace_event_data_offsets_kfree_skb {}; struct trace_event_data_offsets_consume_skb {}; struct trace_event_data_offsets_skb_copy_datagram_iovec {}; typedef void (*btf_trace_kfree_skb)(void *, struct sk_buff *, void *); typedef void (*btf_trace_consume_skb)(void *, struct sk_buff *); typedef void (*btf_trace_skb_copy_datagram_iovec)(void *, const struct sk_buff *, int); struct trace_event_raw_net_dev_start_xmit { struct trace_entry ent; u32 __data_loc_name; u16 queue_mapping; const void *skbaddr; bool vlan_tagged; u16 vlan_proto; u16 vlan_tci; u16 protocol; u8 ip_summed; unsigned int len; unsigned int data_len; int network_offset; bool transport_offset_valid; int transport_offset; u8 tx_flags; u16 gso_size; u16 gso_segs; u16 gso_type; char __data[0]; }; struct trace_event_raw_net_dev_xmit { struct trace_entry ent; void *skbaddr; unsigned int len; int rc; u32 __data_loc_name; char __data[0]; }; struct trace_event_raw_net_dev_xmit_timeout { struct trace_entry ent; u32 __data_loc_name; u32 __data_loc_driver; int queue_index; char __data[0]; }; struct trace_event_raw_net_dev_template { struct trace_entry ent; void *skbaddr; unsigned int len; u32 __data_loc_name; char __data[0]; }; struct trace_event_raw_net_dev_rx_verbose_template { struct trace_entry ent; u32 __data_loc_name; unsigned int napi_id; u16 queue_mapping; const void *skbaddr; bool vlan_tagged; u16 vlan_proto; u16 vlan_tci; u16 protocol; u8 ip_summed; u32 hash; bool l4_hash; unsigned int len; unsigned int data_len; unsigned int truesize; bool mac_header_valid; int mac_header; unsigned char nr_frags; u16 gso_size; u16 gso_type; char __data[0]; }; struct trace_event_raw_net_dev_rx_exit_template { struct trace_entry ent; int ret; char __data[0]; }; struct trace_event_data_offsets_net_dev_start_xmit { u32 name; }; struct trace_event_data_offsets_net_dev_xmit { u32 name; }; struct trace_event_data_offsets_net_dev_xmit_timeout { u32 name; u32 driver; }; struct trace_event_data_offsets_net_dev_template { u32 name; }; struct trace_event_data_offsets_net_dev_rx_verbose_template { u32 name; }; struct trace_event_data_offsets_net_dev_rx_exit_template {}; typedef void (*btf_trace_net_dev_start_xmit)(void *, const struct sk_buff *, const struct net_device *); typedef void (*btf_trace_net_dev_xmit)(void *, struct sk_buff *, int, struct net_device *, unsigned int); typedef void (*btf_trace_net_dev_xmit_timeout)(void *, struct net_device *, int); typedef void (*btf_trace_net_dev_queue)(void *, struct sk_buff *); typedef void (*btf_trace_netif_receive_skb)(void *, struct sk_buff *); typedef void (*btf_trace_netif_rx)(void *, struct sk_buff *); typedef void (*btf_trace_napi_gro_frags_entry)(void *, const struct sk_buff *); typedef void (*btf_trace_napi_gro_receive_entry)(void *, const struct sk_buff *); typedef void (*btf_trace_netif_receive_skb_entry)(void *, const struct sk_buff *); typedef void (*btf_trace_netif_receive_skb_list_entry)(void *, const struct sk_buff *); typedef void (*btf_trace_netif_rx_entry)(void *, const struct sk_buff *); typedef void (*btf_trace_netif_rx_ni_entry)(void *, const struct sk_buff *); typedef void (*btf_trace_napi_gro_frags_exit)(void *, int); typedef void (*btf_trace_napi_gro_receive_exit)(void *, int); typedef void (*btf_trace_netif_receive_skb_exit)(void *, int); typedef void (*btf_trace_netif_rx_exit)(void *, int); typedef void (*btf_trace_netif_rx_ni_exit)(void *, int); typedef void (*btf_trace_netif_receive_skb_list_exit)(void *, int); struct trace_event_raw_napi_poll { struct trace_entry ent; struct napi_struct *napi; u32 __data_loc_dev_name; int work; int budget; char __data[0]; }; struct trace_event_data_offsets_napi_poll { u32 dev_name; }; typedef void (*btf_trace_napi_poll)(void *, struct napi_struct *, int, int); enum tcp_ca_state { TCP_CA_Open = 0, TCP_CA_Disorder = 1, TCP_CA_CWR = 2, TCP_CA_Recovery = 3, TCP_CA_Loss = 4, }; struct trace_event_raw_sock_rcvqueue_full { struct trace_entry ent; int rmem_alloc; unsigned int truesize; int sk_rcvbuf; char __data[0]; }; struct trace_event_raw_sock_exceed_buf_limit { struct trace_entry ent; char name[32]; long int *sysctl_mem; long int allocated; int sysctl_rmem; int rmem_alloc; int sysctl_wmem; int wmem_alloc; int wmem_queued; int kind; char __data[0]; }; struct trace_event_raw_inet_sock_set_state { struct trace_entry ent; const void *skaddr; int oldstate; int newstate; __u16 sport; __u16 dport; __u16 family; __u16 protocol; __u8 saddr[4]; __u8 daddr[4]; __u8 saddr_v6[16]; __u8 daddr_v6[16]; char __data[0]; }; struct trace_event_raw_inet_sk_error_report { struct trace_entry ent; int error; __u16 sport; __u16 dport; __u16 family; __u16 protocol; __u8 saddr[4]; __u8 daddr[4]; __u8 saddr_v6[16]; __u8 daddr_v6[16]; char __data[0]; }; struct trace_event_data_offsets_sock_rcvqueue_full {}; struct trace_event_data_offsets_sock_exceed_buf_limit {}; struct trace_event_data_offsets_inet_sock_set_state {}; struct trace_event_data_offsets_inet_sk_error_report {}; typedef void (*btf_trace_sock_rcvqueue_full)(void *, struct sock *, struct sk_buff *); typedef void (*btf_trace_sock_exceed_buf_limit)(void *, struct sock *, struct proto *, long int, int); typedef void (*btf_trace_inet_sock_set_state)(void *, const struct sock *, const int, const int); typedef void (*btf_trace_inet_sk_error_report)(void *, const struct sock *); struct trace_event_raw_udp_fail_queue_rcv_skb { struct trace_entry ent; int rc; __u16 lport; char __data[0]; }; struct trace_event_data_offsets_udp_fail_queue_rcv_skb {}; typedef void (*btf_trace_udp_fail_queue_rcv_skb)(void *, int, struct sock *); struct trace_event_raw_tcp_event_sk_skb { struct trace_entry ent; const void *skbaddr; const void *skaddr; int state; __u16 sport; __u16 dport; __u16 family; __u8 saddr[4]; __u8 daddr[4]; __u8 saddr_v6[16]; __u8 daddr_v6[16]; char __data[0]; }; struct trace_event_raw_tcp_event_sk { struct trace_entry ent; const void *skaddr; __u16 sport; __u16 dport; __u16 family; __u8 saddr[4]; __u8 daddr[4]; __u8 saddr_v6[16]; __u8 daddr_v6[16]; __u64 sock_cookie; char __data[0]; }; struct trace_event_raw_tcp_retransmit_synack { struct trace_entry ent; const void *skaddr; const void *req; __u16 sport; __u16 dport; __u16 family; __u8 saddr[4]; __u8 daddr[4]; __u8 saddr_v6[16]; __u8 daddr_v6[16]; char __data[0]; }; struct trace_event_raw_tcp_probe { struct trace_entry ent; __u8 saddr[28]; __u8 daddr[28]; __u16 sport; __u16 dport; __u16 family; __u32 mark; __u16 data_len; __u32 snd_nxt; __u32 snd_una; __u32 snd_cwnd; __u32 ssthresh; __u32 snd_wnd; __u32 srtt; __u32 rcv_wnd; __u64 sock_cookie; char __data[0]; }; struct trace_event_raw_tcp_event_skb { struct trace_entry ent; const void *skbaddr; __u8 saddr[28]; __u8 daddr[28]; char __data[0]; }; struct trace_event_data_offsets_tcp_event_sk_skb {}; struct trace_event_data_offsets_tcp_event_sk {}; struct trace_event_data_offsets_tcp_retransmit_synack {}; struct trace_event_data_offsets_tcp_probe {}; struct trace_event_data_offsets_tcp_event_skb {}; typedef void (*btf_trace_tcp_retransmit_skb)(void *, const struct sock *, const struct sk_buff *); typedef void (*btf_trace_tcp_send_reset)(void *, const struct sock *, const struct sk_buff *); typedef void (*btf_trace_tcp_receive_reset)(void *, struct sock *); typedef void (*btf_trace_tcp_destroy_sock)(void *, struct sock *); typedef void (*btf_trace_tcp_rcv_space_adjust)(void *, struct sock *); typedef void (*btf_trace_tcp_retransmit_synack)(void *, const struct sock *, const struct request_sock *); typedef void (*btf_trace_tcp_probe)(void *, struct sock *, struct sk_buff *); typedef void (*btf_trace_tcp_bad_csum)(void *, const struct sk_buff *); struct trace_event_raw_fib_table_lookup { struct trace_entry ent; u32 tb_id; int err; int oif; int iif; u8 proto; __u8 tos; __u8 scope; __u8 flags; __u8 src[4]; __u8 dst[4]; __u8 gw4[4]; __u8 gw6[16]; u16 sport; u16 dport; u32 __data_loc_name; char __data[0]; }; struct trace_event_data_offsets_fib_table_lookup { u32 name; }; typedef void (*btf_trace_fib_table_lookup)(void *, u32, const struct flowi4 *, const struct fib_nh_common *, int); struct trace_event_raw_qdisc_dequeue { struct trace_entry ent; struct Qdisc *qdisc; const struct netdev_queue *txq; int packets; void *skbaddr; int ifindex; u32 handle; u32 parent; long unsigned int txq_state; char __data[0]; }; struct trace_event_raw_qdisc_enqueue { struct trace_entry ent; struct Qdisc *qdisc; void *skbaddr; int ifindex; u32 handle; u32 parent; char __data[0]; }; struct trace_event_raw_qdisc_reset { struct trace_entry ent; u32 __data_loc_dev; u32 __data_loc_kind; u32 parent; u32 handle; char __data[0]; }; struct trace_event_raw_qdisc_destroy { struct trace_entry ent; u32 __data_loc_dev; u32 __data_loc_kind; u32 parent; u32 handle; char __data[0]; }; struct trace_event_raw_qdisc_create { struct trace_entry ent; u32 __data_loc_dev; u32 __data_loc_kind; u32 parent; char __data[0]; }; struct trace_event_data_offsets_qdisc_dequeue {}; struct trace_event_data_offsets_qdisc_enqueue {}; struct trace_event_data_offsets_qdisc_reset { u32 dev; u32 kind; }; struct trace_event_data_offsets_qdisc_destroy { u32 dev; u32 kind; }; struct trace_event_data_offsets_qdisc_create { u32 dev; u32 kind; }; typedef void (*btf_trace_qdisc_dequeue)(void *, struct Qdisc *, const struct netdev_queue *, int, struct sk_buff *); typedef void (*btf_trace_qdisc_enqueue)(void *, struct Qdisc *, const struct netdev_queue *, struct sk_buff *); typedef void (*btf_trace_qdisc_reset)(void *, struct Qdisc *); typedef void (*btf_trace_qdisc_destroy)(void *, struct Qdisc *); typedef void (*btf_trace_qdisc_create)(void *, const struct Qdisc_ops *, struct net_device *, u32); struct bridge_stp_xstats { __u64 transition_blk; __u64 transition_fwd; __u64 rx_bpdu; __u64 tx_bpdu; __u64 rx_tcn; __u64 tx_tcn; }; struct br_mcast_stats { __u64 igmp_v1queries[2]; __u64 igmp_v2queries[2]; __u64 igmp_v3queries[2]; __u64 igmp_leaves[2]; __u64 igmp_v1reports[2]; __u64 igmp_v2reports[2]; __u64 igmp_v3reports[2]; __u64 igmp_parse_errors; __u64 mld_v1queries[2]; __u64 mld_v2queries[2]; __u64 mld_leaves[2]; __u64 mld_v1reports[2]; __u64 mld_v2reports[2]; __u64 mld_parse_errors; __u64 mcast_bytes[2]; __u64 mcast_packets[2]; }; struct br_ip { union { __be32 ip4; struct in6_addr ip6; } src; union { __be32 ip4; struct in6_addr ip6; unsigned char mac_addr[6]; } dst; __be16 proto; __u16 vid; }; struct bridge_id { unsigned char prio[2]; unsigned char addr[6]; }; typedef struct bridge_id bridge_id; struct mac_addr { unsigned char addr[6]; }; typedef struct mac_addr mac_addr; typedef __u16 port_id; struct bridge_mcast_own_query { struct timer_list timer; u32 startup_sent; }; struct bridge_mcast_other_query { struct timer_list timer; long unsigned int delay_time; }; struct net_bridge_port; struct bridge_mcast_querier { struct br_ip addr; struct net_bridge_port *port; }; struct net_bridge; struct net_bridge_vlan_group; struct bridge_mcast_stats; struct net_bridge_port { struct net_bridge *br; struct net_device *dev; struct list_head list; long unsigned int flags; struct net_bridge_vlan_group *vlgrp; struct net_bridge_port *backup_port; u8 priority; u8 state; u16 port_no; unsigned char topology_change_ack; unsigned char config_pending; port_id port_id; port_id designated_port; bridge_id designated_root; bridge_id designated_bridge; u32 path_cost; u32 designated_cost; long unsigned int designated_age; struct timer_list forward_delay_timer; struct timer_list hold_timer; struct timer_list message_age_timer; struct kobject kobj; struct callback_head rcu; struct bridge_mcast_own_query ip4_own_query; struct timer_list ip4_mc_router_timer; struct hlist_node ip4_rlist; struct bridge_mcast_own_query ip6_own_query; struct timer_list ip6_mc_router_timer; struct hlist_node ip6_rlist; u32 multicast_eht_hosts_limit; u32 multicast_eht_hosts_cnt; unsigned char multicast_router; struct bridge_mcast_stats *mcast_stats; struct hlist_head mglist; char sysfs_name[16]; struct netpoll *np; int offload_fwd_mark; u16 group_fwd_mask; u16 backup_redirected_cnt; struct bridge_stp_xstats stp_xstats; }; struct bridge_mcast_stats { struct br_mcast_stats mstats; struct u64_stats_sync syncp; }; struct net_bridge { spinlock_t lock; spinlock_t hash_lock; struct hlist_head frame_type_list; struct net_device *dev; long unsigned int options; __be16 vlan_proto; u16 default_pvid; struct net_bridge_vlan_group *vlgrp; struct rhashtable fdb_hash_tbl; struct list_head port_list; union { struct rtable fake_rtable; struct rt6_info fake_rt6_info; }; u16 group_fwd_mask; u16 group_fwd_mask_required; bridge_id designated_root; bridge_id bridge_id; unsigned char topology_change; unsigned char topology_change_detected; u16 root_port; long unsigned int max_age; long unsigned int hello_time; long unsigned int forward_delay; long unsigned int ageing_time; long unsigned int bridge_max_age; long unsigned int bridge_hello_time; long unsigned int bridge_forward_delay; long unsigned int bridge_ageing_time; u32 root_path_cost; u8 group_addr[6]; enum { BR_NO_STP = 0, BR_KERNEL_STP = 1, BR_USER_STP = 2, } stp_enabled; u32 hash_max; u32 multicast_last_member_count; u32 multicast_startup_query_count; u8 multicast_igmp_version; u8 multicast_router; u8 multicast_mld_version; spinlock_t multicast_lock; long unsigned int multicast_last_member_interval; long unsigned int multicast_membership_interval; long unsigned int multicast_querier_interval; long unsigned int multicast_query_interval; long unsigned int multicast_query_response_interval; long unsigned int multicast_startup_query_interval; struct rhashtable mdb_hash_tbl; struct rhashtable sg_port_tbl; struct hlist_head mcast_gc_list; struct hlist_head mdb_list; struct hlist_head ip4_mc_router_list; struct timer_list ip4_mc_router_timer; struct bridge_mcast_other_query ip4_other_query; struct bridge_mcast_own_query ip4_own_query; struct bridge_mcast_querier ip4_querier; struct bridge_mcast_stats *mcast_stats; struct hlist_head ip6_mc_router_list; struct timer_list ip6_mc_router_timer; struct bridge_mcast_other_query ip6_other_query; struct bridge_mcast_own_query ip6_own_query; struct bridge_mcast_querier ip6_querier; struct work_struct mcast_gc_work; struct timer_list hello_timer; struct timer_list tcn_timer; struct timer_list topology_change_timer; struct delayed_work gc_work; struct kobject *ifobj; u32 auto_cnt; int offload_fwd_mark; struct hlist_head fdb_list; struct hlist_head mrp_list; struct hlist_head mep_list; }; struct net_bridge_vlan_group { struct rhashtable vlan_hash; struct rhashtable tunnel_hash; struct list_head vlan_list; u16 num_vlans; u16 pvid; u8 pvid_state; }; struct net_bridge_fdb_key { mac_addr addr; u16 vlan_id; }; struct net_bridge_fdb_entry { struct rhash_head rhnode; struct net_bridge_port *dst; struct net_bridge_fdb_key key; struct hlist_node fdb_node; long unsigned int flags; long: 64; long: 64; long unsigned int updated; long unsigned int used; struct callback_head rcu; long: 64; long: 64; long: 64; long: 64; }; struct trace_event_raw_br_fdb_add { struct trace_entry ent; u8 ndm_flags; u32 __data_loc_dev; unsigned char addr[6]; u16 vid; u16 nlh_flags; char __data[0]; }; struct trace_event_raw_br_fdb_external_learn_add { struct trace_entry ent; u32 __data_loc_br_dev; u32 __data_loc_dev; unsigned char addr[6]; u16 vid; char __data[0]; }; struct trace_event_raw_fdb_delete { struct trace_entry ent; u32 __data_loc_br_dev; u32 __data_loc_dev; unsigned char addr[6]; u16 vid; char __data[0]; }; struct trace_event_raw_br_fdb_update { struct trace_entry ent; u32 __data_loc_br_dev; u32 __data_loc_dev; unsigned char addr[6]; u16 vid; long unsigned int flags; char __data[0]; }; struct trace_event_data_offsets_br_fdb_add { u32 dev; }; struct trace_event_data_offsets_br_fdb_external_learn_add { u32 br_dev; u32 dev; }; struct trace_event_data_offsets_fdb_delete { u32 br_dev; u32 dev; }; struct trace_event_data_offsets_br_fdb_update { u32 br_dev; u32 dev; }; typedef void (*btf_trace_br_fdb_add)(void *, struct ndmsg *, struct net_device *, const unsigned char *, u16, u16); typedef void (*btf_trace_br_fdb_external_learn_add)(void *, struct net_bridge *, struct net_bridge_port *, const unsigned char *, u16); typedef void (*btf_trace_fdb_delete)(void *, struct net_bridge *, struct net_bridge_fdb_entry *); typedef void (*btf_trace_br_fdb_update)(void *, struct net_bridge *, struct net_bridge_port *, const unsigned char *, u16, long unsigned int); struct trace_event_raw_page_pool_release { struct trace_entry ent; const struct page_pool *pool; s32 inflight; u32 hold; u32 release; u64 cnt; char __data[0]; }; struct trace_event_raw_page_pool_state_release { struct trace_entry ent; const struct page_pool *pool; const struct page *page; u32 release; long unsigned int pfn; char __data[0]; }; struct trace_event_raw_page_pool_state_hold { struct trace_entry ent; const struct page_pool *pool; const struct page *page; u32 hold; long unsigned int pfn; char __data[0]; }; struct trace_event_raw_page_pool_update_nid { struct trace_entry ent; const struct page_pool *pool; int pool_nid; int new_nid; char __data[0]; }; struct trace_event_data_offsets_page_pool_release {}; struct trace_event_data_offsets_page_pool_state_release {}; struct trace_event_data_offsets_page_pool_state_hold {}; struct trace_event_data_offsets_page_pool_update_nid {}; typedef void (*btf_trace_page_pool_release)(void *, const struct page_pool *, s32, u32, u32); typedef void (*btf_trace_page_pool_state_release)(void *, const struct page_pool *, const struct page *, u32); typedef void (*btf_trace_page_pool_state_hold)(void *, const struct page_pool *, const struct page *, u32); typedef void (*btf_trace_page_pool_update_nid)(void *, const struct page_pool *, int); struct trace_event_raw_neigh_create { struct trace_entry ent; u32 family; u32 __data_loc_dev; int entries; u8 created; u8 gc_exempt; u8 primary_key4[4]; u8 primary_key6[16]; char __data[0]; }; struct trace_event_raw_neigh_update { struct trace_entry ent; u32 family; u32 __data_loc_dev; u8 lladdr[32]; u8 lladdr_len; u8 flags; u8 nud_state; u8 type; u8 dead; int refcnt; __u8 primary_key4[4]; __u8 primary_key6[16]; long unsigned int confirmed; long unsigned int updated; long unsigned int used; u8 new_lladdr[32]; u8 new_state; u32 update_flags; u32 pid; char __data[0]; }; struct trace_event_raw_neigh__update { struct trace_entry ent; u32 family; u32 __data_loc_dev; u8 lladdr[32]; u8 lladdr_len; u8 flags; u8 nud_state; u8 type; u8 dead; int refcnt; __u8 primary_key4[4]; __u8 primary_key6[16]; long unsigned int confirmed; long unsigned int updated; long unsigned int used; u32 err; char __data[0]; }; struct trace_event_data_offsets_neigh_create { u32 dev; }; struct trace_event_data_offsets_neigh_update { u32 dev; }; struct trace_event_data_offsets_neigh__update { u32 dev; }; typedef void (*btf_trace_neigh_create)(void *, struct neigh_table *, struct net_device *, const void *, const struct neighbour *, bool); typedef void (*btf_trace_neigh_update)(void *, struct neighbour *, const u8 *, u8, u32, u32); typedef void (*btf_trace_neigh_update_done)(void *, struct neighbour *, int); typedef void (*btf_trace_neigh_timer_handler)(void *, struct neighbour *, int); typedef void (*btf_trace_neigh_event_send_done)(void *, struct neighbour *, int); typedef void (*btf_trace_neigh_event_send_dead)(void *, struct neighbour *, int); typedef void (*btf_trace_neigh_cleanup_and_release)(void *, struct neighbour *, int); struct net_dm_drop_point { __u8 pc[8]; __u32 count; }; struct net_dm_alert_msg { __u32 entries; struct net_dm_drop_point points[0]; }; enum { NET_DM_CMD_UNSPEC = 0, NET_DM_CMD_ALERT = 1, NET_DM_CMD_CONFIG = 2, NET_DM_CMD_START = 3, NET_DM_CMD_STOP = 4, NET_DM_CMD_PACKET_ALERT = 5, NET_DM_CMD_CONFIG_GET = 6, NET_DM_CMD_CONFIG_NEW = 7, NET_DM_CMD_STATS_GET = 8, NET_DM_CMD_STATS_NEW = 9, _NET_DM_CMD_MAX = 10, }; enum net_dm_attr { NET_DM_ATTR_UNSPEC = 0, NET_DM_ATTR_ALERT_MODE = 1, NET_DM_ATTR_PC = 2, NET_DM_ATTR_SYMBOL = 3, NET_DM_ATTR_IN_PORT = 4, NET_DM_ATTR_TIMESTAMP = 5, NET_DM_ATTR_PROTO = 6, NET_DM_ATTR_PAYLOAD = 7, NET_DM_ATTR_PAD = 8, NET_DM_ATTR_TRUNC_LEN = 9, NET_DM_ATTR_ORIG_LEN = 10, NET_DM_ATTR_QUEUE_LEN = 11, NET_DM_ATTR_STATS = 12, NET_DM_ATTR_HW_STATS = 13, NET_DM_ATTR_ORIGIN = 14, NET_DM_ATTR_HW_TRAP_GROUP_NAME = 15, NET_DM_ATTR_HW_TRAP_NAME = 16, NET_DM_ATTR_HW_ENTRIES = 17, NET_DM_ATTR_HW_ENTRY = 18, NET_DM_ATTR_HW_TRAP_COUNT = 19, NET_DM_ATTR_SW_DROPS = 20, NET_DM_ATTR_HW_DROPS = 21, NET_DM_ATTR_FLOW_ACTION_COOKIE = 22, __NET_DM_ATTR_MAX = 23, NET_DM_ATTR_MAX = 22, }; enum net_dm_alert_mode { NET_DM_ALERT_MODE_SUMMARY = 0, NET_DM_ALERT_MODE_PACKET = 1, }; enum { NET_DM_ATTR_PORT_NETDEV_IFINDEX = 0, NET_DM_ATTR_PORT_NETDEV_NAME = 1, __NET_DM_ATTR_PORT_MAX = 2, NET_DM_ATTR_PORT_MAX = 1, }; enum { NET_DM_ATTR_STATS_DROPPED = 0, __NET_DM_ATTR_STATS_MAX = 1, NET_DM_ATTR_STATS_MAX = 0, }; enum net_dm_origin { NET_DM_ORIGIN_SW = 0, NET_DM_ORIGIN_HW = 1, }; struct devlink_trap_metadata { const char *trap_name; const char *trap_group_name; struct net_device *input_dev; const struct flow_action_cookie *fa_cookie; enum devlink_trap_type trap_type; }; struct net_dm_stats { u64 dropped; struct u64_stats_sync syncp; }; struct net_dm_hw_entry { char trap_name[40]; u32 count; }; struct net_dm_hw_entries { u32 num_entries; struct net_dm_hw_entry entries[0]; }; struct per_cpu_dm_data { spinlock_t lock; union { struct sk_buff *skb; struct net_dm_hw_entries *hw_entries; }; struct sk_buff_head drop_queue; struct work_struct dm_alert_work; struct timer_list send_timer; struct net_dm_stats stats; }; struct dm_hw_stat_delta { struct net_device *dev; long unsigned int last_rx; struct list_head list; struct callback_head rcu; long unsigned int last_drop_val; }; struct net_dm_alert_ops { void (*kfree_skb_probe)(void *, struct sk_buff *, void *); void (*napi_poll_probe)(void *, struct napi_struct *, int, int); void (*work_item_func)(struct work_struct *); void (*hw_work_item_func)(struct work_struct *); void (*hw_trap_probe)(void *, const struct devlink *, struct sk_buff *, const struct devlink_trap_metadata *); }; struct net_dm_skb_cb { union { struct devlink_trap_metadata *hw_metadata; void *pc; }; }; struct update_classid_context { u32 classid; unsigned int batch; }; struct rtnexthop { short unsigned int rtnh_len; unsigned char rtnh_flags; unsigned char rtnh_hops; int rtnh_ifindex; }; struct lwtunnel_encap_ops { int (*build_state)(struct net *, struct nlattr *, unsigned int, const void *, struct lwtunnel_state **, struct netlink_ext_ack *); void (*destroy_state)(struct lwtunnel_state *); int (*output)(struct net *, struct sock *, struct sk_buff *); int (*input)(struct sk_buff *); int (*fill_encap)(struct sk_buff *, struct lwtunnel_state *); int (*get_encap_size)(struct lwtunnel_state *); int (*cmp_encap)(struct lwtunnel_state *, struct lwtunnel_state *); int (*xmit)(struct sk_buff *); struct module *owner; }; enum { LWT_BPF_PROG_UNSPEC = 0, LWT_BPF_PROG_FD = 1, LWT_BPF_PROG_NAME = 2, __LWT_BPF_PROG_MAX = 3, }; enum { LWT_BPF_UNSPEC = 0, LWT_BPF_IN = 1, LWT_BPF_OUT = 2, LWT_BPF_XMIT = 3, LWT_BPF_XMIT_HEADROOM = 4, __LWT_BPF_MAX = 5, }; enum { LWTUNNEL_XMIT_DONE = 0, LWTUNNEL_XMIT_CONTINUE = 1, }; struct bpf_lwt_prog { struct bpf_prog *prog; char *name; }; struct bpf_lwt { struct bpf_lwt_prog in; struct bpf_lwt_prog out; struct bpf_lwt_prog xmit; int family; }; struct dst_cache_pcpu { long unsigned int refresh_ts; struct dst_entry *dst; u32 cookie; union { struct in_addr in_saddr; struct in6_addr in6_saddr; }; }; enum devlink_command { DEVLINK_CMD_UNSPEC = 0, DEVLINK_CMD_GET = 1, DEVLINK_CMD_SET = 2, DEVLINK_CMD_NEW = 3, DEVLINK_CMD_DEL = 4, DEVLINK_CMD_PORT_GET = 5, DEVLINK_CMD_PORT_SET = 6, DEVLINK_CMD_PORT_NEW = 7, DEVLINK_CMD_PORT_DEL = 8, DEVLINK_CMD_PORT_SPLIT = 9, DEVLINK_CMD_PORT_UNSPLIT = 10, DEVLINK_CMD_SB_GET = 11, DEVLINK_CMD_SB_SET = 12, DEVLINK_CMD_SB_NEW = 13, DEVLINK_CMD_SB_DEL = 14, DEVLINK_CMD_SB_POOL_GET = 15, DEVLINK_CMD_SB_POOL_SET = 16, DEVLINK_CMD_SB_POOL_NEW = 17, DEVLINK_CMD_SB_POOL_DEL = 18, DEVLINK_CMD_SB_PORT_POOL_GET = 19, DEVLINK_CMD_SB_PORT_POOL_SET = 20, DEVLINK_CMD_SB_PORT_POOL_NEW = 21, DEVLINK_CMD_SB_PORT_POOL_DEL = 22, DEVLINK_CMD_SB_TC_POOL_BIND_GET = 23, DEVLINK_CMD_SB_TC_POOL_BIND_SET = 24, DEVLINK_CMD_SB_TC_POOL_BIND_NEW = 25, DEVLINK_CMD_SB_TC_POOL_BIND_DEL = 26, DEVLINK_CMD_SB_OCC_SNAPSHOT = 27, DEVLINK_CMD_SB_OCC_MAX_CLEAR = 28, DEVLINK_CMD_ESWITCH_GET = 29, DEVLINK_CMD_ESWITCH_SET = 30, DEVLINK_CMD_DPIPE_TABLE_GET = 31, DEVLINK_CMD_DPIPE_ENTRIES_GET = 32, DEVLINK_CMD_DPIPE_HEADERS_GET = 33, DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET = 34, DEVLINK_CMD_RESOURCE_SET = 35, DEVLINK_CMD_RESOURCE_DUMP = 36, DEVLINK_CMD_RELOAD = 37, DEVLINK_CMD_PARAM_GET = 38, DEVLINK_CMD_PARAM_SET = 39, DEVLINK_CMD_PARAM_NEW = 40, DEVLINK_CMD_PARAM_DEL = 41, DEVLINK_CMD_REGION_GET = 42, DEVLINK_CMD_REGION_SET = 43, DEVLINK_CMD_REGION_NEW = 44, DEVLINK_CMD_REGION_DEL = 45, DEVLINK_CMD_REGION_READ = 46, DEVLINK_CMD_PORT_PARAM_GET = 47, DEVLINK_CMD_PORT_PARAM_SET = 48, DEVLINK_CMD_PORT_PARAM_NEW = 49, DEVLINK_CMD_PORT_PARAM_DEL = 50, DEVLINK_CMD_INFO_GET = 51, DEVLINK_CMD_HEALTH_REPORTER_GET = 52, DEVLINK_CMD_HEALTH_REPORTER_SET = 53, DEVLINK_CMD_HEALTH_REPORTER_RECOVER = 54, DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE = 55, DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET = 56, DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR = 57, DEVLINK_CMD_FLASH_UPDATE = 58, DEVLINK_CMD_FLASH_UPDATE_END = 59, DEVLINK_CMD_FLASH_UPDATE_STATUS = 60, DEVLINK_CMD_TRAP_GET = 61, DEVLINK_CMD_TRAP_SET = 62, DEVLINK_CMD_TRAP_NEW = 63, DEVLINK_CMD_TRAP_DEL = 64, DEVLINK_CMD_TRAP_GROUP_GET = 65, DEVLINK_CMD_TRAP_GROUP_SET = 66, DEVLINK_CMD_TRAP_GROUP_NEW = 67, DEVLINK_CMD_TRAP_GROUP_DEL = 68, DEVLINK_CMD_TRAP_POLICER_GET = 69, DEVLINK_CMD_TRAP_POLICER_SET = 70, DEVLINK_CMD_TRAP_POLICER_NEW = 71, DEVLINK_CMD_TRAP_POLICER_DEL = 72, DEVLINK_CMD_HEALTH_REPORTER_TEST = 73, DEVLINK_CMD_RATE_GET = 74, DEVLINK_CMD_RATE_SET = 75, DEVLINK_CMD_RATE_NEW = 76, DEVLINK_CMD_RATE_DEL = 77, __DEVLINK_CMD_MAX = 78, DEVLINK_CMD_MAX = 77, }; enum devlink_eswitch_mode { DEVLINK_ESWITCH_MODE_LEGACY = 0, DEVLINK_ESWITCH_MODE_SWITCHDEV = 1, }; enum { DEVLINK_ATTR_STATS_RX_PACKETS = 0, DEVLINK_ATTR_STATS_RX_BYTES = 1, DEVLINK_ATTR_STATS_RX_DROPPED = 2, __DEVLINK_ATTR_STATS_MAX = 3, DEVLINK_ATTR_STATS_MAX = 2, }; enum { DEVLINK_FLASH_OVERWRITE_SETTINGS_BIT = 0, DEVLINK_FLASH_OVERWRITE_IDENTIFIERS_BIT = 1, __DEVLINK_FLASH_OVERWRITE_MAX_BIT = 2, DEVLINK_FLASH_OVERWRITE_MAX_BIT = 1, }; enum { DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT = 0, DEVLINK_ATTR_TRAP_METADATA_TYPE_FA_COOKIE = 1, }; enum devlink_attr { DEVLINK_ATTR_UNSPEC = 0, DEVLINK_ATTR_BUS_NAME = 1, DEVLINK_ATTR_DEV_NAME = 2, DEVLINK_ATTR_PORT_INDEX = 3, DEVLINK_ATTR_PORT_TYPE = 4, DEVLINK_ATTR_PORT_DESIRED_TYPE = 5, DEVLINK_ATTR_PORT_NETDEV_IFINDEX = 6, DEVLINK_ATTR_PORT_NETDEV_NAME = 7, DEVLINK_ATTR_PORT_IBDEV_NAME = 8, DEVLINK_ATTR_PORT_SPLIT_COUNT = 9, DEVLINK_ATTR_PORT_SPLIT_GROUP = 10, DEVLINK_ATTR_SB_INDEX = 11, DEVLINK_ATTR_SB_SIZE = 12, DEVLINK_ATTR_SB_INGRESS_POOL_COUNT = 13, DEVLINK_ATTR_SB_EGRESS_POOL_COUNT = 14, DEVLINK_ATTR_SB_INGRESS_TC_COUNT = 15, DEVLINK_ATTR_SB_EGRESS_TC_COUNT = 16, DEVLINK_ATTR_SB_POOL_INDEX = 17, DEVLINK_ATTR_SB_POOL_TYPE = 18, DEVLINK_ATTR_SB_POOL_SIZE = 19, DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE = 20, DEVLINK_ATTR_SB_THRESHOLD = 21, DEVLINK_ATTR_SB_TC_INDEX = 22, DEVLINK_ATTR_SB_OCC_CUR = 23, DEVLINK_ATTR_SB_OCC_MAX = 24, DEVLINK_ATTR_ESWITCH_MODE = 25, DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26, DEVLINK_ATTR_DPIPE_TABLES = 27, DEVLINK_ATTR_DPIPE_TABLE = 28, DEVLINK_ATTR_DPIPE_TABLE_NAME = 29, DEVLINK_ATTR_DPIPE_TABLE_SIZE = 30, DEVLINK_ATTR_DPIPE_TABLE_MATCHES = 31, DEVLINK_ATTR_DPIPE_TABLE_ACTIONS = 32, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED = 33, DEVLINK_ATTR_DPIPE_ENTRIES = 34, DEVLINK_ATTR_DPIPE_ENTRY = 35, DEVLINK_ATTR_DPIPE_ENTRY_INDEX = 36, DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES = 37, DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES = 38, DEVLINK_ATTR_DPIPE_ENTRY_COUNTER = 39, DEVLINK_ATTR_DPIPE_MATCH = 40, DEVLINK_ATTR_DPIPE_MATCH_VALUE = 41, DEVLINK_ATTR_DPIPE_MATCH_TYPE = 42, DEVLINK_ATTR_DPIPE_ACTION = 43, DEVLINK_ATTR_DPIPE_ACTION_VALUE = 44, DEVLINK_ATTR_DPIPE_ACTION_TYPE = 45, DEVLINK_ATTR_DPIPE_VALUE = 46, DEVLINK_ATTR_DPIPE_VALUE_MASK = 47, DEVLINK_ATTR_DPIPE_VALUE_MAPPING = 48, DEVLINK_ATTR_DPIPE_HEADERS = 49, DEVLINK_ATTR_DPIPE_HEADER = 50, DEVLINK_ATTR_DPIPE_HEADER_NAME = 51, DEVLINK_ATTR_DPIPE_HEADER_ID = 52, DEVLINK_ATTR_DPIPE_HEADER_FIELDS = 53, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL = 54, DEVLINK_ATTR_DPIPE_HEADER_INDEX = 55, DEVLINK_ATTR_DPIPE_FIELD = 56, DEVLINK_ATTR_DPIPE_FIELD_NAME = 57, DEVLINK_ATTR_DPIPE_FIELD_ID = 58, DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH = 59, DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE = 60, DEVLINK_ATTR_PAD = 61, DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62, DEVLINK_ATTR_RESOURCE_LIST = 63, DEVLINK_ATTR_RESOURCE = 64, DEVLINK_ATTR_RESOURCE_NAME = 65, DEVLINK_ATTR_RESOURCE_ID = 66, DEVLINK_ATTR_RESOURCE_SIZE = 67, DEVLINK_ATTR_RESOURCE_SIZE_NEW = 68, DEVLINK_ATTR_RESOURCE_SIZE_VALID = 69, DEVLINK_ATTR_RESOURCE_SIZE_MIN = 70, DEVLINK_ATTR_RESOURCE_SIZE_MAX = 71, DEVLINK_ATTR_RESOURCE_SIZE_GRAN = 72, DEVLINK_ATTR_RESOURCE_UNIT = 73, DEVLINK_ATTR_RESOURCE_OCC = 74, DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID = 75, DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS = 76, DEVLINK_ATTR_PORT_FLAVOUR = 77, DEVLINK_ATTR_PORT_NUMBER = 78, DEVLINK_ATTR_PORT_SPLIT_SUBPORT_NUMBER = 79, DEVLINK_ATTR_PARAM = 80, DEVLINK_ATTR_PARAM_NAME = 81, DEVLINK_ATTR_PARAM_GENERIC = 82, DEVLINK_ATTR_PARAM_TYPE = 83, DEVLINK_ATTR_PARAM_VALUES_LIST = 84, DEVLINK_ATTR_PARAM_VALUE = 85, DEVLINK_ATTR_PARAM_VALUE_DATA = 86, DEVLINK_ATTR_PARAM_VALUE_CMODE = 87, DEVLINK_ATTR_REGION_NAME = 88, DEVLINK_ATTR_REGION_SIZE = 89, DEVLINK_ATTR_REGION_SNAPSHOTS = 90, DEVLINK_ATTR_REGION_SNAPSHOT = 91, DEVLINK_ATTR_REGION_SNAPSHOT_ID = 92, DEVLINK_ATTR_REGION_CHUNKS = 93, DEVLINK_ATTR_REGION_CHUNK = 94, DEVLINK_ATTR_REGION_CHUNK_DATA = 95, DEVLINK_ATTR_REGION_CHUNK_ADDR = 96, DEVLINK_ATTR_REGION_CHUNK_LEN = 97, DEVLINK_ATTR_INFO_DRIVER_NAME = 98, DEVLINK_ATTR_INFO_SERIAL_NUMBER = 99, DEVLINK_ATTR_INFO_VERSION_FIXED = 100, DEVLINK_ATTR_INFO_VERSION_RUNNING = 101, DEVLINK_ATTR_INFO_VERSION_STORED = 102, DEVLINK_ATTR_INFO_VERSION_NAME = 103, DEVLINK_ATTR_INFO_VERSION_VALUE = 104, DEVLINK_ATTR_SB_POOL_CELL_SIZE = 105, DEVLINK_ATTR_FMSG = 106, DEVLINK_ATTR_FMSG_OBJ_NEST_START = 107, DEVLINK_ATTR_FMSG_PAIR_NEST_START = 108, DEVLINK_ATTR_FMSG_ARR_NEST_START = 109, DEVLINK_ATTR_FMSG_NEST_END = 110, DEVLINK_ATTR_FMSG_OBJ_NAME = 111, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE = 112, DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA = 113, DEVLINK_ATTR_HEALTH_REPORTER = 114, DEVLINK_ATTR_HEALTH_REPORTER_NAME = 115, DEVLINK_ATTR_HEALTH_REPORTER_STATE = 116, DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT = 117, DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT = 118, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS = 119, DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD = 120, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER = 121, DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME = 122, DEVLINK_ATTR_FLASH_UPDATE_COMPONENT = 123, DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG = 124, DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE = 125, DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL = 126, DEVLINK_ATTR_PORT_PCI_PF_NUMBER = 127, DEVLINK_ATTR_PORT_PCI_VF_NUMBER = 128, DEVLINK_ATTR_STATS = 129, DEVLINK_ATTR_TRAP_NAME = 130, DEVLINK_ATTR_TRAP_ACTION = 131, DEVLINK_ATTR_TRAP_TYPE = 132, DEVLINK_ATTR_TRAP_GENERIC = 133, DEVLINK_ATTR_TRAP_METADATA = 134, DEVLINK_ATTR_TRAP_GROUP_NAME = 135, DEVLINK_ATTR_RELOAD_FAILED = 136, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS = 137, DEVLINK_ATTR_NETNS_FD = 138, DEVLINK_ATTR_NETNS_PID = 139, DEVLINK_ATTR_NETNS_ID = 140, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP = 141, DEVLINK_ATTR_TRAP_POLICER_ID = 142, DEVLINK_ATTR_TRAP_POLICER_RATE = 143, DEVLINK_ATTR_TRAP_POLICER_BURST = 144, DEVLINK_ATTR_PORT_FUNCTION = 145, DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER = 146, DEVLINK_ATTR_PORT_LANES = 147, DEVLINK_ATTR_PORT_SPLITTABLE = 148, DEVLINK_ATTR_PORT_EXTERNAL = 149, DEVLINK_ATTR_PORT_CONTROLLER_NUMBER = 150, DEVLINK_ATTR_FLASH_UPDATE_STATUS_TIMEOUT = 151, DEVLINK_ATTR_FLASH_UPDATE_OVERWRITE_MASK = 152, DEVLINK_ATTR_RELOAD_ACTION = 153, DEVLINK_ATTR_RELOAD_ACTIONS_PERFORMED = 154, DEVLINK_ATTR_RELOAD_LIMITS = 155, DEVLINK_ATTR_DEV_STATS = 156, DEVLINK_ATTR_RELOAD_STATS = 157, DEVLINK_ATTR_RELOAD_STATS_ENTRY = 158, DEVLINK_ATTR_RELOAD_STATS_LIMIT = 159, DEVLINK_ATTR_RELOAD_STATS_VALUE = 160, DEVLINK_ATTR_REMOTE_RELOAD_STATS = 161, DEVLINK_ATTR_RELOAD_ACTION_INFO = 162, DEVLINK_ATTR_RELOAD_ACTION_STATS = 163, DEVLINK_ATTR_PORT_PCI_SF_NUMBER = 164, DEVLINK_ATTR_RATE_TYPE = 165, DEVLINK_ATTR_RATE_TX_SHARE = 166, DEVLINK_ATTR_RATE_TX_MAX = 167, DEVLINK_ATTR_RATE_NODE_NAME = 168, DEVLINK_ATTR_RATE_PARENT_NODE_NAME = 169, __DEVLINK_ATTR_MAX = 170, DEVLINK_ATTR_MAX = 169, }; enum devlink_dpipe_match_type { DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT = 0, }; enum devlink_dpipe_action_type { DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY = 0, }; enum devlink_dpipe_field_ethernet_id { DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC = 0, }; enum devlink_dpipe_field_ipv4_id { DEVLINK_DPIPE_FIELD_IPV4_DST_IP = 0, }; enum devlink_dpipe_field_ipv6_id { DEVLINK_DPIPE_FIELD_IPV6_DST_IP = 0, }; enum devlink_dpipe_header_id { DEVLINK_DPIPE_HEADER_ETHERNET = 0, DEVLINK_DPIPE_HEADER_IPV4 = 1, DEVLINK_DPIPE_HEADER_IPV6 = 2, }; enum devlink_resource_unit { DEVLINK_RESOURCE_UNIT_ENTRY = 0, }; enum devlink_port_function_attr { DEVLINK_PORT_FUNCTION_ATTR_UNSPEC = 0, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR = 1, DEVLINK_PORT_FN_ATTR_STATE = 2, DEVLINK_PORT_FN_ATTR_OPSTATE = 3, __DEVLINK_PORT_FUNCTION_ATTR_MAX = 4, DEVLINK_PORT_FUNCTION_ATTR_MAX = 3, }; struct devlink_dpipe_match { enum devlink_dpipe_match_type type; unsigned int header_index; struct devlink_dpipe_header *header; unsigned int field_id; }; struct devlink_dpipe_action { enum devlink_dpipe_action_type type; unsigned int header_index; struct devlink_dpipe_header *header; unsigned int field_id; }; struct devlink_dpipe_value { union { struct devlink_dpipe_action *action; struct devlink_dpipe_match *match; }; unsigned int mapping_value; bool mapping_valid; unsigned int value_size; void *value; void *mask; }; struct devlink_dpipe_entry { u64 index; struct devlink_dpipe_value *match_values; unsigned int match_values_count; struct devlink_dpipe_value *action_values; unsigned int action_values_count; u64 counter; bool counter_valid; }; struct devlink_dpipe_dump_ctx { struct genl_info *info; enum devlink_command cmd; struct sk_buff *skb; struct nlattr *nest; void *hdr; }; struct devlink_dpipe_table_ops; struct devlink_dpipe_table { void *priv; struct list_head list; const char *name; bool counters_enabled; bool counter_control_extern; bool resource_valid; u64 resource_id; u64 resource_units; struct devlink_dpipe_table_ops *table_ops; struct callback_head rcu; }; struct devlink_dpipe_table_ops { int (*actions_dump)(void *, struct sk_buff *); int (*matches_dump)(void *, struct sk_buff *); int (*entries_dump)(void *, bool, struct devlink_dpipe_dump_ctx *); int (*counters_set_update)(void *, bool); u64 (*size_get)(void *); }; struct devlink_resource_size_params { u64 size_min; u64 size_max; u64 size_granularity; enum devlink_resource_unit unit; }; typedef u64 devlink_resource_occ_get_t(void *); struct devlink_resource { const char *name; u64 id; u64 size; u64 size_new; bool size_valid; struct devlink_resource *parent; struct devlink_resource_size_params size_params; struct list_head list; struct list_head resource_list; devlink_resource_occ_get_t *occ_get; void *occ_get_priv; }; enum devlink_param_type { DEVLINK_PARAM_TYPE_U8 = 0, DEVLINK_PARAM_TYPE_U16 = 1, DEVLINK_PARAM_TYPE_U32 = 2, DEVLINK_PARAM_TYPE_STRING = 3, DEVLINK_PARAM_TYPE_BOOL = 4, }; struct devlink_flash_notify { const char *status_msg; const char *component; long unsigned int done; long unsigned int total; long unsigned int timeout; }; struct devlink_param { u32 id; const char *name; bool generic; enum devlink_param_type type; long unsigned int supported_cmodes; int (*get)(struct devlink *, u32, struct devlink_param_gset_ctx *); int (*set)(struct devlink *, u32, struct devlink_param_gset_ctx *); int (*validate)(struct devlink *, u32, union devlink_param_value, struct netlink_ext_ack *); }; struct devlink_param_item { struct list_head list; const struct devlink_param *param; union devlink_param_value driverinit_value; bool driverinit_value_valid; bool published; }; enum devlink_param_generic_id { DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET = 0, DEVLINK_PARAM_GENERIC_ID_MAX_MACS = 1, DEVLINK_PARAM_GENERIC_ID_ENABLE_SRIOV = 2, DEVLINK_PARAM_GENERIC_ID_REGION_SNAPSHOT = 3, DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI = 4, DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX = 5, DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN = 6, DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY = 7, DEVLINK_PARAM_GENERIC_ID_RESET_DEV_ON_DRV_PROBE = 8, DEVLINK_PARAM_GENERIC_ID_ENABLE_ROCE = 9, DEVLINK_PARAM_GENERIC_ID_ENABLE_REMOTE_DEV_RESET = 10, __DEVLINK_PARAM_GENERIC_ID_MAX = 11, DEVLINK_PARAM_GENERIC_ID_MAX = 10, }; struct devlink_region_ops { const char *name; void (*destructor)(const void *); int (*snapshot)(struct devlink *, const struct devlink_region_ops *, struct netlink_ext_ack *, u8 **); void *priv; }; struct devlink_port_region_ops { const char *name; void (*destructor)(const void *); int (*snapshot)(struct devlink_port *, const struct devlink_port_region_ops *, struct netlink_ext_ack *, u8 **); void *priv; }; enum devlink_health_reporter_state { DEVLINK_HEALTH_REPORTER_STATE_HEALTHY = 0, DEVLINK_HEALTH_REPORTER_STATE_ERROR = 1, }; struct devlink_health_reporter; struct devlink_fmsg; struct devlink_health_reporter_ops { char *name; int (*recover)(struct devlink_health_reporter *, void *, struct netlink_ext_ack *); int (*dump)(struct devlink_health_reporter *, struct devlink_fmsg *, void *, struct netlink_ext_ack *); int (*diagnose)(struct devlink_health_reporter *, struct devlink_fmsg *, struct netlink_ext_ack *); int (*test)(struct devlink_health_reporter *, struct netlink_ext_ack *); }; struct devlink_health_reporter { struct list_head list; void *priv; const struct devlink_health_reporter_ops *ops; struct devlink *devlink; struct devlink_port *devlink_port; struct devlink_fmsg *dump_fmsg; struct mutex dump_lock; u64 graceful_period; bool auto_recover; bool auto_dump; u8 health_state; u64 dump_ts; u64 dump_real_ts; u64 error_count; u64 recovery_count; u64 last_recovery_ts; refcount_t refcount; }; struct devlink_fmsg { struct list_head item_list; bool putting_binary; }; enum devlink_trap_generic_id { DEVLINK_TRAP_GENERIC_ID_SMAC_MC = 0, DEVLINK_TRAP_GENERIC_ID_VLAN_TAG_MISMATCH = 1, DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER = 2, DEVLINK_TRAP_GENERIC_ID_INGRESS_STP_FILTER = 3, DEVLINK_TRAP_GENERIC_ID_EMPTY_TX_LIST = 4, DEVLINK_TRAP_GENERIC_ID_PORT_LOOPBACK_FILTER = 5, DEVLINK_TRAP_GENERIC_ID_BLACKHOLE_ROUTE = 6, DEVLINK_TRAP_GENERIC_ID_TTL_ERROR = 7, DEVLINK_TRAP_GENERIC_ID_TAIL_DROP = 8, DEVLINK_TRAP_GENERIC_ID_NON_IP_PACKET = 9, DEVLINK_TRAP_GENERIC_ID_UC_DIP_MC_DMAC = 10, DEVLINK_TRAP_GENERIC_ID_DIP_LB = 11, DEVLINK_TRAP_GENERIC_ID_SIP_MC = 12, DEVLINK_TRAP_GENERIC_ID_SIP_LB = 13, DEVLINK_TRAP_GENERIC_ID_CORRUPTED_IP_HDR = 14, DEVLINK_TRAP_GENERIC_ID_IPV4_SIP_BC = 15, DEVLINK_TRAP_GENERIC_ID_IPV6_MC_DIP_RESERVED_SCOPE = 16, DEVLINK_TRAP_GENERIC_ID_IPV6_MC_DIP_INTERFACE_LOCAL_SCOPE = 17, DEVLINK_TRAP_GENERIC_ID_MTU_ERROR = 18, DEVLINK_TRAP_GENERIC_ID_UNRESOLVED_NEIGH = 19, DEVLINK_TRAP_GENERIC_ID_RPF = 20, DEVLINK_TRAP_GENERIC_ID_REJECT_ROUTE = 21, DEVLINK_TRAP_GENERIC_ID_IPV4_LPM_UNICAST_MISS = 22, DEVLINK_TRAP_GENERIC_ID_IPV6_LPM_UNICAST_MISS = 23, DEVLINK_TRAP_GENERIC_ID_NON_ROUTABLE = 24, DEVLINK_TRAP_GENERIC_ID_DECAP_ERROR = 25, DEVLINK_TRAP_GENERIC_ID_OVERLAY_SMAC_MC = 26, DEVLINK_TRAP_GENERIC_ID_INGRESS_FLOW_ACTION_DROP = 27, DEVLINK_TRAP_GENERIC_ID_EGRESS_FLOW_ACTION_DROP = 28, DEVLINK_TRAP_GENERIC_ID_STP = 29, DEVLINK_TRAP_GENERIC_ID_LACP = 30, DEVLINK_TRAP_GENERIC_ID_LLDP = 31, DEVLINK_TRAP_GENERIC_ID_IGMP_QUERY = 32, DEVLINK_TRAP_GENERIC_ID_IGMP_V1_REPORT = 33, DEVLINK_TRAP_GENERIC_ID_IGMP_V2_REPORT = 34, DEVLINK_TRAP_GENERIC_ID_IGMP_V3_REPORT = 35, DEVLINK_TRAP_GENERIC_ID_IGMP_V2_LEAVE = 36, DEVLINK_TRAP_GENERIC_ID_MLD_QUERY = 37, DEVLINK_TRAP_GENERIC_ID_MLD_V1_REPORT = 38, DEVLINK_TRAP_GENERIC_ID_MLD_V2_REPORT = 39, DEVLINK_TRAP_GENERIC_ID_MLD_V1_DONE = 40, DEVLINK_TRAP_GENERIC_ID_IPV4_DHCP = 41, DEVLINK_TRAP_GENERIC_ID_IPV6_DHCP = 42, DEVLINK_TRAP_GENERIC_ID_ARP_REQUEST = 43, DEVLINK_TRAP_GENERIC_ID_ARP_RESPONSE = 44, DEVLINK_TRAP_GENERIC_ID_ARP_OVERLAY = 45, DEVLINK_TRAP_GENERIC_ID_IPV6_NEIGH_SOLICIT = 46, DEVLINK_TRAP_GENERIC_ID_IPV6_NEIGH_ADVERT = 47, DEVLINK_TRAP_GENERIC_ID_IPV4_BFD = 48, DEVLINK_TRAP_GENERIC_ID_IPV6_BFD = 49, DEVLINK_TRAP_GENERIC_ID_IPV4_OSPF = 50, DEVLINK_TRAP_GENERIC_ID_IPV6_OSPF = 51, DEVLINK_TRAP_GENERIC_ID_IPV4_BGP = 52, DEVLINK_TRAP_GENERIC_ID_IPV6_BGP = 53, DEVLINK_TRAP_GENERIC_ID_IPV4_VRRP = 54, DEVLINK_TRAP_GENERIC_ID_IPV6_VRRP = 55, DEVLINK_TRAP_GENERIC_ID_IPV4_PIM = 56, DEVLINK_TRAP_GENERIC_ID_IPV6_PIM = 57, DEVLINK_TRAP_GENERIC_ID_UC_LB = 58, DEVLINK_TRAP_GENERIC_ID_LOCAL_ROUTE = 59, DEVLINK_TRAP_GENERIC_ID_EXTERNAL_ROUTE = 60, DEVLINK_TRAP_GENERIC_ID_IPV6_UC_DIP_LINK_LOCAL_SCOPE = 61, DEVLINK_TRAP_GENERIC_ID_IPV6_DIP_ALL_NODES = 62, DEVLINK_TRAP_GENERIC_ID_IPV6_DIP_ALL_ROUTERS = 63, DEVLINK_TRAP_GENERIC_ID_IPV6_ROUTER_SOLICIT = 64, DEVLINK_TRAP_GENERIC_ID_IPV6_ROUTER_ADVERT = 65, DEVLINK_TRAP_GENERIC_ID_IPV6_REDIRECT = 66, DEVLINK_TRAP_GENERIC_ID_IPV4_ROUTER_ALERT = 67, DEVLINK_TRAP_GENERIC_ID_IPV6_ROUTER_ALERT = 68, DEVLINK_TRAP_GENERIC_ID_PTP_EVENT = 69, DEVLINK_TRAP_GENERIC_ID_PTP_GENERAL = 70, DEVLINK_TRAP_GENERIC_ID_FLOW_ACTION_SAMPLE = 71, DEVLINK_TRAP_GENERIC_ID_FLOW_ACTION_TRAP = 72, DEVLINK_TRAP_GENERIC_ID_EARLY_DROP = 73, DEVLINK_TRAP_GENERIC_ID_VXLAN_PARSING = 74, DEVLINK_TRAP_GENERIC_ID_LLC_SNAP_PARSING = 75, DEVLINK_TRAP_GENERIC_ID_VLAN_PARSING = 76, DEVLINK_TRAP_GENERIC_ID_PPPOE_PPP_PARSING = 77, DEVLINK_TRAP_GENERIC_ID_MPLS_PARSING = 78, DEVLINK_TRAP_GENERIC_ID_ARP_PARSING = 79, DEVLINK_TRAP_GENERIC_ID_IP_1_PARSING = 80, DEVLINK_TRAP_GENERIC_ID_IP_N_PARSING = 81, DEVLINK_TRAP_GENERIC_ID_GRE_PARSING = 82, DEVLINK_TRAP_GENERIC_ID_UDP_PARSING = 83, DEVLINK_TRAP_GENERIC_ID_TCP_PARSING = 84, DEVLINK_TRAP_GENERIC_ID_IPSEC_PARSING = 85, DEVLINK_TRAP_GENERIC_ID_SCTP_PARSING = 86, DEVLINK_TRAP_GENERIC_ID_DCCP_PARSING = 87, DEVLINK_TRAP_GENERIC_ID_GTP_PARSING = 88, DEVLINK_TRAP_GENERIC_ID_ESP_PARSING = 89, DEVLINK_TRAP_GENERIC_ID_BLACKHOLE_NEXTHOP = 90, DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER = 91, __DEVLINK_TRAP_GENERIC_ID_MAX = 92, DEVLINK_TRAP_GENERIC_ID_MAX = 91, }; enum devlink_trap_group_generic_id { DEVLINK_TRAP_GROUP_GENERIC_ID_L2_DROPS = 0, DEVLINK_TRAP_GROUP_GENERIC_ID_L3_DROPS = 1, DEVLINK_TRAP_GROUP_GENERIC_ID_L3_EXCEPTIONS = 2, DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS = 3, DEVLINK_TRAP_GROUP_GENERIC_ID_TUNNEL_DROPS = 4, DEVLINK_TRAP_GROUP_GENERIC_ID_ACL_DROPS = 5, DEVLINK_TRAP_GROUP_GENERIC_ID_STP = 6, DEVLINK_TRAP_GROUP_GENERIC_ID_LACP = 7, DEVLINK_TRAP_GROUP_GENERIC_ID_LLDP = 8, DEVLINK_TRAP_GROUP_GENERIC_ID_MC_SNOOPING = 9, DEVLINK_TRAP_GROUP_GENERIC_ID_DHCP = 10, DEVLINK_TRAP_GROUP_GENERIC_ID_NEIGH_DISCOVERY = 11, DEVLINK_TRAP_GROUP_GENERIC_ID_BFD = 12, DEVLINK_TRAP_GROUP_GENERIC_ID_OSPF = 13, DEVLINK_TRAP_GROUP_GENERIC_ID_BGP = 14, DEVLINK_TRAP_GROUP_GENERIC_ID_VRRP = 15, DEVLINK_TRAP_GROUP_GENERIC_ID_PIM = 16, DEVLINK_TRAP_GROUP_GENERIC_ID_UC_LB = 17, DEVLINK_TRAP_GROUP_GENERIC_ID_LOCAL_DELIVERY = 18, DEVLINK_TRAP_GROUP_GENERIC_ID_EXTERNAL_DELIVERY = 19, DEVLINK_TRAP_GROUP_GENERIC_ID_IPV6 = 20, DEVLINK_TRAP_GROUP_GENERIC_ID_PTP_EVENT = 21, DEVLINK_TRAP_GROUP_GENERIC_ID_PTP_GENERAL = 22, DEVLINK_TRAP_GROUP_GENERIC_ID_ACL_SAMPLE = 23, DEVLINK_TRAP_GROUP_GENERIC_ID_ACL_TRAP = 24, DEVLINK_TRAP_GROUP_GENERIC_ID_PARSER_ERROR_DROPS = 25, __DEVLINK_TRAP_GROUP_GENERIC_ID_MAX = 26, DEVLINK_TRAP_GROUP_GENERIC_ID_MAX = 25, }; struct devlink_info_req { struct sk_buff *msg; }; struct trace_event_raw_devlink_hwmsg { struct trace_entry ent; u32 __data_loc_bus_name; u32 __data_loc_dev_name; u32 __data_loc_driver_name; bool incoming; long unsigned int type; u32 __data_loc_buf; size_t len; char __data[0]; }; struct trace_event_raw_devlink_hwerr { struct trace_entry ent; u32 __data_loc_bus_name; u32 __data_loc_dev_name; u32 __data_loc_driver_name; int err; u32 __data_loc_msg; char __data[0]; }; struct trace_event_raw_devlink_health_report { struct trace_entry ent; u32 __data_loc_bus_name; u32 __data_loc_dev_name; u32 __data_loc_driver_name; u32 __data_loc_reporter_name; u32 __data_loc_msg; char __data[0]; }; struct trace_event_raw_devlink_health_recover_aborted { struct trace_entry ent; u32 __data_loc_bus_name; u32 __data_loc_dev_name; u32 __data_loc_driver_name; u32 __data_loc_reporter_name; bool health_state; u64 time_since_last_recover; char __data[0]; }; struct trace_event_raw_devlink_health_reporter_state_update { struct trace_entry ent; u32 __data_loc_bus_name; u32 __data_loc_dev_name; u32 __data_loc_driver_name; u32 __data_loc_reporter_name; u8 new_state; char __data[0]; }; struct trace_event_raw_devlink_trap_report { struct trace_entry ent; u32 __data_loc_bus_name; u32 __data_loc_dev_name; u32 __data_loc_driver_name; u32 __data_loc_trap_name; u32 __data_loc_trap_group_name; u32 __data_loc_input_dev_name; char __data[0]; }; struct trace_event_data_offsets_devlink_hwmsg { u32 bus_name; u32 dev_name; u32 driver_name; u32 buf; }; struct trace_event_data_offsets_devlink_hwerr { u32 bus_name; u32 dev_name; u32 driver_name; u32 msg; }; struct trace_event_data_offsets_devlink_health_report { u32 bus_name; u32 dev_name; u32 driver_name; u32 reporter_name; u32 msg; }; struct trace_event_data_offsets_devlink_health_recover_aborted { u32 bus_name; u32 dev_name; u32 driver_name; u32 reporter_name; }; struct trace_event_data_offsets_devlink_health_reporter_state_update { u32 bus_name; u32 dev_name; u32 driver_name; u32 reporter_name; }; struct trace_event_data_offsets_devlink_trap_report { u32 bus_name; u32 dev_name; u32 driver_name; u32 trap_name; u32 trap_group_name; u32 input_dev_name; }; typedef void (*btf_trace_devlink_hwmsg)(void *, const struct devlink *, bool, long unsigned int, const u8 *, size_t); typedef void (*btf_trace_devlink_hwerr)(void *, const struct devlink *, int, const char *); typedef void (*btf_trace_devlink_health_report)(void *, const struct devlink *, const char *, const char *); typedef void (*btf_trace_devlink_health_recover_aborted)(void *, const struct devlink *, const char *, bool, u64); typedef void (*btf_trace_devlink_health_reporter_state_update)(void *, const struct devlink *, const char *, bool); typedef void (*btf_trace_devlink_trap_report)(void *, const struct devlink *, struct sk_buff *, const struct devlink_trap_metadata *); struct devlink_sb { struct list_head list; unsigned int index; u32 size; u16 ingress_pools_count; u16 egress_pools_count; u16 ingress_tc_count; u16 egress_tc_count; }; struct devlink_region { struct devlink *devlink; struct devlink_port *port; struct list_head list; union { const struct devlink_region_ops *ops; const struct devlink_port_region_ops *port_ops; }; struct list_head snapshot_list; u32 max_snapshots; u32 cur_snapshots; u64 size; }; struct devlink_snapshot { struct list_head list; struct devlink_region *region; u8 *data; u32 id; }; enum devlink_multicast_groups { DEVLINK_MCGRP_CONFIG = 0, }; struct devlink_reload_combination { enum devlink_reload_action action; enum devlink_reload_limit limit; }; struct devlink_fmsg_item { struct list_head list; int attrtype; u8 nla_type; u16 len; int value[0]; }; struct devlink_stats { u64 rx_bytes; u64 rx_packets; struct u64_stats_sync syncp; }; struct devlink_trap_policer_item { const struct devlink_trap_policer *policer; u64 rate; u64 burst; struct list_head list; }; struct devlink_trap_group_item { const struct devlink_trap_group *group; struct devlink_trap_policer_item *policer_item; struct list_head list; struct devlink_stats *stats; }; struct devlink_trap_item { const struct devlink_trap *trap; struct devlink_trap_group_item *group_item; struct list_head list; enum devlink_trap_action action; struct devlink_stats *stats; void *priv; }; struct gro_cell; struct gro_cells { struct gro_cell *cells; }; struct gro_cell { struct sk_buff_head napi_skbs; struct napi_struct napi; }; enum __sk_action { __SK_DROP = 0, __SK_PASS = 1, __SK_REDIRECT = 2, __SK_NONE = 3, }; enum sk_psock_state_bits { SK_PSOCK_TX_ENABLED = 0, }; struct sk_psock_link { struct list_head list; struct bpf_map *map; void *link_raw; }; struct bpf_stab { struct bpf_map map; struct sock **sks; struct sk_psock_progs progs; raw_spinlock_t lock; long: 32; long: 64; long: 64; }; typedef u64 (*btf_bpf_sock_map_update)(struct bpf_sock_ops_kern *, struct bpf_map *, void *, u64); typedef u64 (*btf_bpf_sk_redirect_map)(struct sk_buff *, struct bpf_map *, u32, u64); typedef u64 (*btf_bpf_msg_redirect_map)(struct sk_msg *, struct bpf_map *, u32, u64); struct sock_map_seq_info { struct bpf_map *map; struct sock *sk; u32 index; }; struct bpf_iter__sockmap { union { struct bpf_iter_meta *meta; }; union { struct bpf_map *map; }; union { void *key; }; union { struct sock *sk; }; }; struct bpf_shtab_elem { struct callback_head rcu; u32 hash; struct sock *sk; struct hlist_node node; u8 key[0]; }; struct bpf_shtab_bucket { struct hlist_head head; raw_spinlock_t lock; }; struct bpf_shtab { struct bpf_map map; struct bpf_shtab_bucket *buckets; u32 buckets_num; u32 elem_size; struct sk_psock_progs progs; atomic_t count; long: 32; long: 64; }; typedef u64 (*btf_bpf_sock_hash_update)(struct bpf_sock_ops_kern *, struct bpf_map *, void *, u64); typedef u64 (*btf_bpf_sk_redirect_hash)(struct sk_buff *, struct bpf_map *, void *, u64); typedef u64 (*btf_bpf_msg_redirect_hash)(struct sk_msg *, struct bpf_map *, void *, u64); struct sock_hash_seq_info { struct bpf_map *map; struct bpf_shtab *htab; u32 bucket_id; }; enum { SK_DIAG_BPF_STORAGE_REQ_NONE = 0, SK_DIAG_BPF_STORAGE_REQ_MAP_FD = 1, __SK_DIAG_BPF_STORAGE_REQ_MAX = 2, }; enum { SK_DIAG_BPF_STORAGE_REP_NONE = 0, SK_DIAG_BPF_STORAGE = 1, __SK_DIAG_BPF_STORAGE_REP_MAX = 2, }; enum { SK_DIAG_BPF_STORAGE_NONE = 0, SK_DIAG_BPF_STORAGE_PAD = 1, SK_DIAG_BPF_STORAGE_MAP_ID = 2, SK_DIAG_BPF_STORAGE_MAP_VALUE = 3, __SK_DIAG_BPF_STORAGE_MAX = 4, }; typedef u64 (*btf_bpf_sk_storage_get)(struct bpf_map *, struct sock *, void *, u64); typedef u64 (*btf_bpf_sk_storage_delete)(struct bpf_map *, struct sock *); typedef u64 (*btf_bpf_sk_storage_get_tracing)(struct bpf_map *, struct sock *, void *, u64); typedef u64 (*btf_bpf_sk_storage_delete_tracing)(struct bpf_map *, struct sock *); struct bpf_sk_storage_diag { u32 nr_maps; struct bpf_map *maps[0]; }; struct bpf_iter_seq_sk_storage_map_info { struct bpf_map *map; unsigned int bucket_id; unsigned int skip_elems; }; struct bpf_iter__bpf_sk_storage_map { union { struct bpf_iter_meta *meta; }; union { struct bpf_map *map; }; union { struct sock *sk; }; union { void *value; }; }; struct compat_cmsghdr { compat_size_t cmsg_len; compat_int_t cmsg_level; compat_int_t cmsg_type; }; struct nvmem_cell___2; struct fch_hdr { __u8 daddr[6]; __u8 saddr[6]; }; struct fcllc { __u8 dsap; __u8 ssap; __u8 llc; __u8 protid[3]; __be16 ethertype; }; enum macvlan_mode { MACVLAN_MODE_PRIVATE = 1, MACVLAN_MODE_VEPA = 2, MACVLAN_MODE_BRIDGE = 4, MACVLAN_MODE_PASSTHRU = 8, MACVLAN_MODE_SOURCE = 16, }; struct tc_ratespec { unsigned char cell_log; __u8 linklayer; short unsigned int overhead; short int cell_align; short unsigned int mpu; __u32 rate; }; struct tc_prio_qopt { int bands; __u8 priomap[16]; }; enum { TCA_UNSPEC = 0, TCA_KIND = 1, TCA_OPTIONS = 2, TCA_STATS = 3, TCA_XSTATS = 4, TCA_RATE = 5, TCA_FCNT = 6, TCA_STATS2 = 7, TCA_STAB = 8, TCA_PAD = 9, TCA_DUMP_INVISIBLE = 10, TCA_CHAIN = 11, TCA_HW_OFFLOAD = 12, TCA_INGRESS_BLOCK = 13, TCA_EGRESS_BLOCK = 14, TCA_DUMP_FLAGS = 15, __TCA_MAX = 16, }; struct vlan_pcpu_stats { u64 rx_packets; u64 rx_bytes; u64 rx_multicast; u64 tx_packets; u64 tx_bytes; struct u64_stats_sync syncp; u32 rx_errors; u32 tx_dropped; }; struct netpoll___2; struct skb_array { struct ptr_ring ring; }; struct macvlan_port; struct macvlan_dev { struct net_device *dev; struct list_head list; struct hlist_node hlist; struct macvlan_port *port; struct net_device *lowerdev; void *accel_priv; struct vlan_pcpu_stats *pcpu_stats; long unsigned int mc_filter[4]; netdev_features_t set_features; enum macvlan_mode mode; u16 flags; unsigned int macaddr_count; u32 bc_queue_len_req; struct netpoll___2 *netpoll; }; struct psched_ratecfg { u64 rate_bytes_ps; u32 mult; u16 overhead; u8 linklayer; u8 shift; }; struct psched_pktrate { u64 rate_pkts_ps; u32 mult; u8 shift; }; struct mini_Qdisc_pair { struct mini_Qdisc miniq1; struct mini_Qdisc miniq2; struct mini_Qdisc **p_miniq; }; struct pfifo_fast_priv { struct skb_array q[3]; }; struct tc_qopt_offload_stats { struct gnet_stats_basic_packed *bstats; struct gnet_stats_queue *qstats; }; enum tc_mq_command { TC_MQ_CREATE = 0, TC_MQ_DESTROY = 1, TC_MQ_STATS = 2, TC_MQ_GRAFT = 3, }; struct tc_mq_opt_offload_graft_params { long unsigned int queue; u32 child_handle; }; struct tc_mq_qopt_offload { enum tc_mq_command command; u32 handle; union { struct tc_qopt_offload_stats stats; struct tc_mq_opt_offload_graft_params graft_params; }; }; struct mq_sched { struct Qdisc **qdiscs; }; struct sch_frag_data { long unsigned int dst; struct qdisc_skb_cb cb; __be16 inner_protocol; u16 vlan_tci; __be16 vlan_proto; unsigned int l2_len; u8 l2_data[18]; int (*xmit)(struct sk_buff *); }; enum tc_link_layer { TC_LINKLAYER_UNAWARE = 0, TC_LINKLAYER_ETHERNET = 1, TC_LINKLAYER_ATM = 2, }; enum { TCA_STAB_UNSPEC = 0, TCA_STAB_BASE = 1, TCA_STAB_DATA = 2, __TCA_STAB_MAX = 3, }; struct qdisc_rate_table { struct tc_ratespec rate; u32 data[256]; struct qdisc_rate_table *next; int refcnt; }; struct Qdisc_class_common { u32 classid; struct hlist_node hnode; }; struct Qdisc_class_hash { struct hlist_head *hash; unsigned int hashsize; unsigned int hashmask; unsigned int hashelems; }; struct qdisc_watchdog { u64 last_expires; struct hrtimer timer; struct Qdisc *qdisc; }; enum tc_root_command { TC_ROOT_GRAFT = 0, }; struct tc_root_qopt_offload { enum tc_root_command command; u32 handle; bool ingress; }; struct check_loop_arg { struct qdisc_walker w; struct Qdisc *p; int depth; }; struct tcf_bind_args { struct tcf_walker w; long unsigned int base; long unsigned int cl; u32 classid; }; struct tc_bind_class_args { struct qdisc_walker w; long unsigned int new_cl; u32 portid; u32 clid; }; struct qdisc_dump_args { struct qdisc_walker w; struct sk_buff *skb; struct netlink_callback *cb; }; enum net_xmit_qdisc_t { __NET_XMIT_STOLEN = 65536, __NET_XMIT_BYPASS = 131072, }; enum { TCA_ACT_UNSPEC = 0, TCA_ACT_KIND = 1, TCA_ACT_OPTIONS = 2, TCA_ACT_INDEX = 3, TCA_ACT_STATS = 4, TCA_ACT_PAD = 5, TCA_ACT_COOKIE = 6, TCA_ACT_FLAGS = 7, TCA_ACT_HW_STATS = 8, TCA_ACT_USED_HW_STATS = 9, __TCA_ACT_MAX = 10, }; enum tca_id { TCA_ID_UNSPEC = 0, TCA_ID_POLICE = 1, TCA_ID_GACT = 5, TCA_ID_IPT = 6, TCA_ID_PEDIT = 7, TCA_ID_MIRRED = 8, TCA_ID_NAT = 9, TCA_ID_XT = 10, TCA_ID_SKBEDIT = 11, TCA_ID_VLAN = 12, TCA_ID_BPF = 13, TCA_ID_CONNMARK = 14, TCA_ID_SKBMOD = 15, TCA_ID_CSUM = 16, TCA_ID_TUNNEL_KEY = 17, TCA_ID_SIMP = 22, TCA_ID_IFE = 25, TCA_ID_SAMPLE = 26, TCA_ID_CTINFO = 27, TCA_ID_MPLS = 28, TCA_ID_CT = 29, TCA_ID_GATE = 30, __TCA_ID_MAX = 255, }; struct tcf_t { __u64 install; __u64 lastuse; __u64 expires; __u64 firstuse; }; struct psample_group { struct list_head list; struct net *net; u32 group_num; u32 refcount; u32 seq; struct callback_head rcu; }; struct action_gate_entry { u8 gate_state; u32 interval; s32 ipv; s32 maxoctets; }; enum qdisc_class_ops_flags { QDISC_CLASS_OPS_DOIT_UNLOCKED = 1, }; enum tcf_proto_ops_flags { TCF_PROTO_OPS_DOIT_UNLOCKED = 1, }; typedef void tcf_chain_head_change_t(struct tcf_proto *, void *); struct tcf_idrinfo { struct mutex lock; struct idr action_idr; struct net *net; }; struct tc_action_ops; struct tc_cookie; struct tc_action { const struct tc_action_ops *ops; __u32 type; struct tcf_idrinfo *idrinfo; u32 tcfa_index; refcount_t tcfa_refcnt; atomic_t tcfa_bindcnt; int tcfa_action; struct tcf_t tcfa_tm; struct gnet_stats_basic_packed tcfa_bstats; struct gnet_stats_basic_packed tcfa_bstats_hw; struct gnet_stats_queue tcfa_qstats; struct net_rate_estimator *tcfa_rate_est; spinlock_t tcfa_lock; struct gnet_stats_basic_cpu *cpu_bstats; struct gnet_stats_basic_cpu *cpu_bstats_hw; struct gnet_stats_queue *cpu_qstats; struct tc_cookie *act_cookie; struct tcf_chain *goto_chain; u32 tcfa_flags; u8 hw_stats; u8 used_hw_stats; bool used_hw_stats_valid; }; typedef void (*tc_action_priv_destructor)(void *); struct tc_action_ops { struct list_head head; char kind[16]; enum tca_id id; size_t size; struct module *owner; int (*act)(struct sk_buff *, const struct tc_action *, struct tcf_result *); int (*dump)(struct sk_buff *, struct tc_action *, int, int); void (*cleanup)(struct tc_action *); int (*lookup)(struct net *, struct tc_action **, u32); int (*init)(struct net *, struct nlattr *, struct nlattr *, struct tc_action **, int, int, bool, struct tcf_proto *, u32, struct netlink_ext_ack *); int (*walk)(struct net *, struct sk_buff *, struct netlink_callback *, int, const struct tc_action_ops *, struct netlink_ext_ack *); void (*stats_update)(struct tc_action *, u64, u64, u64, u64, bool); size_t (*get_fill_size)(const struct tc_action *); struct net_device * (*get_dev)(const struct tc_action *, tc_action_priv_destructor *); struct psample_group * (*get_psample_group)(const struct tc_action *, tc_action_priv_destructor *); }; struct tc_cookie { u8 *data; u32 len; struct callback_head rcu; }; struct tcf_block_ext_info { enum flow_block_binder_type binder_type; tcf_chain_head_change_t *chain_head_change; void *chain_head_change_priv; u32 block_index; }; struct tcf_qevent { struct tcf_block *block; struct tcf_block_ext_info info; struct tcf_proto *filter_chain; }; struct tcf_exts { __u32 type; int nr_actions; struct tc_action **actions; struct net *net; int action; int police; }; enum pedit_header_type { TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK = 0, TCA_PEDIT_KEY_EX_HDR_TYPE_ETH = 1, TCA_PEDIT_KEY_EX_HDR_TYPE_IP4 = 2, TCA_PEDIT_KEY_EX_HDR_TYPE_IP6 = 3, TCA_PEDIT_KEY_EX_HDR_TYPE_TCP = 4, TCA_PEDIT_KEY_EX_HDR_TYPE_UDP = 5, __PEDIT_HDR_TYPE_MAX = 6, }; enum pedit_cmd { TCA_PEDIT_KEY_EX_CMD_SET = 0, TCA_PEDIT_KEY_EX_CMD_ADD = 1, __PEDIT_CMD_MAX = 2, }; struct tc_pedit_key { __u32 mask; __u32 val; __u32 off; __u32 at; __u32 offmask; __u32 shift; }; struct tcf_pedit_key_ex { enum pedit_header_type htype; enum pedit_cmd cmd; }; struct tcf_pedit { struct tc_action common; unsigned char tcfp_nkeys; unsigned char tcfp_flags; struct tc_pedit_key *tcfp_keys; struct tcf_pedit_key_ex *tcfp_keys_ex; }; struct tcf_mirred { struct tc_action common; int tcfm_eaction; bool tcfm_mac_header_xmit; struct net_device *tcfm_dev; struct list_head tcfm_list; }; struct tcf_vlan_params { int tcfv_action; unsigned char tcfv_push_dst[6]; unsigned char tcfv_push_src[6]; u16 tcfv_push_vid; __be16 tcfv_push_proto; u8 tcfv_push_prio; bool tcfv_push_prio_exists; struct callback_head rcu; }; struct tcf_vlan { struct tc_action common; struct tcf_vlan_params *vlan_p; }; struct tcf_tunnel_key_params { struct callback_head rcu; int tcft_action; struct metadata_dst *tcft_enc_metadata; }; struct tcf_tunnel_key { struct tc_action common; struct tcf_tunnel_key_params *params; }; struct tcf_csum_params { u32 update_flags; struct callback_head rcu; }; struct tcf_csum { struct tc_action common; struct tcf_csum_params *params; }; struct tcf_gact { struct tc_action common; u16 tcfg_ptype; u16 tcfg_pval; int tcfg_paction; atomic_t packets; }; struct tcf_police_params { int tcfp_result; u32 tcfp_ewma_rate; s64 tcfp_burst; u32 tcfp_mtu; s64 tcfp_mtu_ptoks; s64 tcfp_pkt_burst; struct psched_ratecfg rate; bool rate_present; struct psched_ratecfg peak; bool peak_present; struct psched_pktrate ppsrate; bool pps_present; struct callback_head rcu; }; struct tcf_police { struct tc_action common; struct tcf_police_params *params; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; spinlock_t tcfp_lock; s64 tcfp_toks; s64 tcfp_ptoks; s64 tcfp_pkttoks; s64 tcfp_t_c; long: 64; long: 64; long: 64; }; struct tcf_sample { struct tc_action common; u32 rate; bool truncate; u32 trunc_size; struct psample_group *psample_group; u32 psample_group_num; struct list_head tcfm_list; }; struct tcf_skbedit_params { u32 flags; u32 priority; u32 mark; u32 mask; u16 queue_mapping; u16 ptype; struct callback_head rcu; }; struct tcf_skbedit { struct tc_action common; struct tcf_skbedit_params *params; }; struct nf_nat_range2 { unsigned int flags; union nf_inet_addr min_addr; union nf_inet_addr max_addr; union nf_conntrack_man_proto min_proto; union nf_conntrack_man_proto max_proto; union nf_conntrack_man_proto base_proto; }; struct tcf_ct_flow_table; struct tcf_ct_params { struct nf_conn *tmpl; u16 zone; u32 mark; u32 mark_mask; u32 labels[4]; u32 labels_mask[4]; struct nf_nat_range2 range; bool ipv4_range; u16 ct_action; struct callback_head rcu; struct tcf_ct_flow_table *ct_ft; struct nf_flowtable *nf_ft; }; struct tcf_ct { struct tc_action common; struct tcf_ct_params *params; }; struct tcf_mpls_params { int tcfm_action; u32 tcfm_label; u8 tcfm_tc; u8 tcfm_ttl; u8 tcfm_bos; __be16 tcfm_proto; struct callback_head rcu; }; struct tcf_mpls { struct tc_action common; struct tcf_mpls_params *mpls_p; }; struct tcfg_gate_entry { int index; u8 gate_state; u32 interval; s32 ipv; s32 maxoctets; struct list_head list; }; struct tcf_gate_params { s32 tcfg_priority; u64 tcfg_basetime; u64 tcfg_cycletime; u64 tcfg_cycletime_ext; u32 tcfg_flags; s32 tcfg_clockid; size_t num_entries; struct list_head entries; }; struct tcf_gate { struct tc_action common; struct tcf_gate_params param; u8 current_gate_status; ktime_t current_close_time; u32 current_entry_octets; s32 current_max_octets; struct tcfg_gate_entry *next_entry; struct hrtimer hitimer; enum tk_offsets tk_offset; }; struct tcf_filter_chain_list_item { struct list_head list; tcf_chain_head_change_t *chain_head_change; void *chain_head_change_priv; }; struct tcf_net { spinlock_t idr_lock; struct idr idr; }; struct tcf_block_owner_item { struct list_head list; struct Qdisc *q; enum flow_block_binder_type binder_type; }; struct tcf_chain_info { struct tcf_proto **pprev; struct tcf_proto *next; }; struct tcf_dump_args { struct tcf_walker w; struct sk_buff *skb; struct netlink_callback *cb; struct tcf_block *block; struct Qdisc *q; u32 parent; bool terse_dump; }; struct tcamsg { unsigned char tca_family; unsigned char tca__pad1; short unsigned int tca__pad2; }; enum { TCA_ROOT_UNSPEC = 0, TCA_ROOT_TAB = 1, TCA_ROOT_FLAGS = 2, TCA_ROOT_COUNT = 3, TCA_ROOT_TIME_DELTA = 4, __TCA_ROOT_MAX = 5, }; struct tc_action_net { struct tcf_idrinfo *idrinfo; const struct tc_action_ops *ops; }; struct tc_fifo_qopt { __u32 limit; }; enum tc_fifo_command { TC_FIFO_REPLACE = 0, TC_FIFO_DESTROY = 1, TC_FIFO_STATS = 2, }; struct tc_fifo_qopt_offload { enum tc_fifo_command command; u32 handle; u32 parent; union { struct tc_qopt_offload_stats stats; }; }; enum { TCA_FQ_CODEL_UNSPEC = 0, TCA_FQ_CODEL_TARGET = 1, TCA_FQ_CODEL_LIMIT = 2, TCA_FQ_CODEL_INTERVAL = 3, TCA_FQ_CODEL_ECN = 4, TCA_FQ_CODEL_FLOWS = 5, TCA_FQ_CODEL_QUANTUM = 6, TCA_FQ_CODEL_CE_THRESHOLD = 7, TCA_FQ_CODEL_DROP_BATCH_SIZE = 8, TCA_FQ_CODEL_MEMORY_LIMIT = 9, __TCA_FQ_CODEL_MAX = 10, }; enum { TCA_FQ_CODEL_XSTATS_QDISC = 0, TCA_FQ_CODEL_XSTATS_CLASS = 1, }; struct tc_fq_codel_qd_stats { __u32 maxpacket; __u32 drop_overlimit; __u32 ecn_mark; __u32 new_flow_count; __u32 new_flows_len; __u32 old_flows_len; __u32 ce_mark; __u32 memory_usage; __u32 drop_overmemory; }; struct tc_fq_codel_cl_stats { __s32 deficit; __u32 ldelay; __u32 count; __u32 lastcount; __u32 dropping; __s32 drop_next; }; struct tc_fq_codel_xstats { __u32 type; union { struct tc_fq_codel_qd_stats qdisc_stats; struct tc_fq_codel_cl_stats class_stats; }; }; typedef u32 codel_time_t; typedef s32 codel_tdiff_t; struct codel_params { codel_time_t target; codel_time_t ce_threshold; codel_time_t interval; u32 mtu; bool ecn; }; struct codel_vars { u32 count; u32 lastcount; bool dropping; u16 rec_inv_sqrt; codel_time_t first_above_time; codel_time_t drop_next; codel_time_t ldelay; }; struct codel_stats { u32 maxpacket; u32 drop_count; u32 drop_len; u32 ecn_mark; u32 ce_mark; }; typedef u32 (*codel_skb_len_t)(const struct sk_buff *); typedef codel_time_t (*codel_skb_time_t)(const struct sk_buff *); typedef void (*codel_skb_drop_t)(struct sk_buff *, void *); typedef struct sk_buff * (*codel_skb_dequeue_t)(struct codel_vars *, void *); struct codel_skb_cb { codel_time_t enqueue_time; unsigned int mem_usage; }; struct fq_codel_flow { struct sk_buff *head; struct sk_buff *tail; struct list_head flowchain; int deficit; struct codel_vars cvars; }; struct fq_codel_sched_data { struct tcf_proto *filter_list; struct tcf_block *block; struct fq_codel_flow *flows; u32 *backlogs; u32 flows_cnt; u32 quantum; u32 drop_batch_size; u32 memory_limit; struct codel_params cparams; struct codel_stats cstats; u32 memory_usage; u32 drop_overmemory; u32 drop_overlimit; u32 new_flow_count; struct list_head new_flows; struct list_head old_flows; }; struct tcf_ematch_tree_hdr { __u16 nmatches; __u16 progid; }; enum { TCA_EMATCH_TREE_UNSPEC = 0, TCA_EMATCH_TREE_HDR = 1, TCA_EMATCH_TREE_LIST = 2, __TCA_EMATCH_TREE_MAX = 3, }; struct tcf_ematch_hdr { __u16 matchid; __u16 kind; __u16 flags; __u16 pad; }; struct tcf_pkt_info { unsigned char *ptr; int nexthdr; }; struct tcf_ematch_ops; struct tcf_ematch { struct tcf_ematch_ops *ops; long unsigned int data; unsigned int datalen; u16 matchid; u16 flags; struct net *net; }; struct tcf_ematch_ops { int kind; int datalen; int (*change)(struct net *, void *, int, struct tcf_ematch *); int (*match)(struct sk_buff *, struct tcf_ematch *, struct tcf_pkt_info *); void (*destroy)(struct tcf_ematch *); int (*dump)(struct sk_buff *, struct tcf_ematch *); struct module *owner; struct list_head link; }; struct tcf_ematch_tree { struct tcf_ematch_tree_hdr hdr; struct tcf_ematch *matches; }; struct sockaddr_nl { __kernel_sa_family_t nl_family; short unsigned int nl_pad; __u32 nl_pid; __u32 nl_groups; }; struct nlmsgerr { int error; struct nlmsghdr msg; }; enum nlmsgerr_attrs { NLMSGERR_ATTR_UNUSED = 0, NLMSGERR_ATTR_MSG = 1, NLMSGERR_ATTR_OFFS = 2, NLMSGERR_ATTR_COOKIE = 3, NLMSGERR_ATTR_POLICY = 4, __NLMSGERR_ATTR_MAX = 5, NLMSGERR_ATTR_MAX = 4, }; struct nl_pktinfo { __u32 group; }; enum { NETLINK_UNCONNECTED = 0, NETLINK_CONNECTED = 1, }; enum netlink_skb_flags { NETLINK_SKB_DST = 8, }; struct netlink_notify { struct net *net; u32 portid; int protocol; }; struct netlink_tap { struct net_device *dev; struct module *module; struct list_head list; }; struct trace_event_raw_netlink_extack { struct trace_entry ent; u32 __data_loc_msg; char __data[0]; }; struct trace_event_data_offsets_netlink_extack { u32 msg; }; typedef void (*btf_trace_netlink_extack)(void *, const char *); struct netlink_sock { struct sock sk; u32 portid; u32 dst_portid; u32 dst_group; u32 flags; u32 subscriptions; u32 ngroups; long unsigned int *groups; long unsigned int state; size_t max_recvmsg_len; wait_queue_head_t wait; bool bound; bool cb_running; int dump_done_errno; struct netlink_callback cb; struct mutex *cb_mutex; struct mutex cb_def_mutex; void (*netlink_rcv)(struct sk_buff *); int (*netlink_bind)(struct net *, int); void (*netlink_unbind)(struct net *, int); struct module *module; struct rhash_head node; struct callback_head rcu; struct work_struct work; }; struct listeners; struct netlink_table { struct rhashtable hash; struct hlist_head mc_list; struct listeners *listeners; unsigned int flags; unsigned int groups; struct mutex *cb_mutex; struct module *module; int (*bind)(struct net *, int); void (*unbind)(struct net *, int); bool (*compare)(struct net *, struct sock *); int registered; }; struct listeners { struct callback_head rcu; long unsigned int masks[0]; }; struct netlink_tap_net { struct list_head netlink_tap_all; struct mutex netlink_tap_lock; }; struct netlink_compare_arg { possible_net_t pnet; u32 portid; }; struct netlink_broadcast_data { struct sock *exclude_sk; struct net *net; u32 portid; u32 group; int failure; int delivery_failure; int congested; int delivered; gfp_t allocation; struct sk_buff *skb; struct sk_buff *skb2; int (*tx_filter)(struct sock *, struct sk_buff *, void *); void *tx_data; }; struct netlink_set_err_data { struct sock *exclude_sk; u32 portid; u32 group; int code; }; struct nl_seq_iter { struct seq_net_private p; struct rhashtable_iter hti; int link; }; struct bpf_iter__netlink { union { struct bpf_iter_meta *meta; }; union { struct netlink_sock *sk; }; }; enum { CTRL_CMD_UNSPEC = 0, CTRL_CMD_NEWFAMILY = 1, CTRL_CMD_DELFAMILY = 2, CTRL_CMD_GETFAMILY = 3, CTRL_CMD_NEWOPS = 4, CTRL_CMD_DELOPS = 5, CTRL_CMD_GETOPS = 6, CTRL_CMD_NEWMCAST_GRP = 7, CTRL_CMD_DELMCAST_GRP = 8, CTRL_CMD_GETMCAST_GRP = 9, CTRL_CMD_GETPOLICY = 10, __CTRL_CMD_MAX = 11, }; enum { CTRL_ATTR_UNSPEC = 0, CTRL_ATTR_FAMILY_ID = 1, CTRL_ATTR_FAMILY_NAME = 2, CTRL_ATTR_VERSION = 3, CTRL_ATTR_HDRSIZE = 4, CTRL_ATTR_MAXATTR = 5, CTRL_ATTR_OPS = 6, CTRL_ATTR_MCAST_GROUPS = 7, CTRL_ATTR_POLICY = 8, CTRL_ATTR_OP_POLICY = 9, CTRL_ATTR_OP = 10, __CTRL_ATTR_MAX = 11, }; enum { CTRL_ATTR_OP_UNSPEC = 0, CTRL_ATTR_OP_ID = 1, CTRL_ATTR_OP_FLAGS = 2, __CTRL_ATTR_OP_MAX = 3, }; enum { CTRL_ATTR_MCAST_GRP_UNSPEC = 0, CTRL_ATTR_MCAST_GRP_NAME = 1, CTRL_ATTR_MCAST_GRP_ID = 2, __CTRL_ATTR_MCAST_GRP_MAX = 3, }; enum { CTRL_ATTR_POLICY_UNSPEC = 0, CTRL_ATTR_POLICY_DO = 1, CTRL_ATTR_POLICY_DUMP = 2, __CTRL_ATTR_POLICY_DUMP_MAX = 3, CTRL_ATTR_POLICY_DUMP_MAX = 2, }; struct genl_start_context { const struct genl_family *family; struct nlmsghdr *nlh; struct netlink_ext_ack *extack; const struct genl_ops *ops; int hdrlen; }; struct netlink_policy_dump_state; struct ctrl_dump_policy_ctx { struct netlink_policy_dump_state *state; const struct genl_family *rt; unsigned int opidx; u32 op; u16 fam_id; u8 policies: 1; u8 single_op: 1; }; enum netlink_attribute_type { NL_ATTR_TYPE_INVALID = 0, NL_ATTR_TYPE_FLAG = 1, NL_ATTR_TYPE_U8 = 2, NL_ATTR_TYPE_U16 = 3, NL_ATTR_TYPE_U32 = 4, NL_ATTR_TYPE_U64 = 5, NL_ATTR_TYPE_S8 = 6, NL_ATTR_TYPE_S16 = 7, NL_ATTR_TYPE_S32 = 8, NL_ATTR_TYPE_S64 = 9, NL_ATTR_TYPE_BINARY = 10, NL_ATTR_TYPE_STRING = 11, NL_ATTR_TYPE_NUL_STRING = 12, NL_ATTR_TYPE_NESTED = 13, NL_ATTR_TYPE_NESTED_ARRAY = 14, NL_ATTR_TYPE_BITFIELD32 = 15, }; enum netlink_policy_type_attr { NL_POLICY_TYPE_ATTR_UNSPEC = 0, NL_POLICY_TYPE_ATTR_TYPE = 1, NL_POLICY_TYPE_ATTR_MIN_VALUE_S = 2, NL_POLICY_TYPE_ATTR_MAX_VALUE_S = 3, NL_POLICY_TYPE_ATTR_MIN_VALUE_U = 4, NL_POLICY_TYPE_ATTR_MAX_VALUE_U = 5, NL_POLICY_TYPE_ATTR_MIN_LENGTH = 6, NL_POLICY_TYPE_ATTR_MAX_LENGTH = 7, NL_POLICY_TYPE_ATTR_POLICY_IDX = 8, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE = 9, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK = 10, NL_POLICY_TYPE_ATTR_PAD = 11, NL_POLICY_TYPE_ATTR_MASK = 12, __NL_POLICY_TYPE_ATTR_MAX = 13, NL_POLICY_TYPE_ATTR_MAX = 12, }; struct netlink_policy_dump_state___2 { unsigned int policy_idx; unsigned int attr_idx; unsigned int n_alloc; struct { const struct nla_policy *policy; unsigned int maxtype; } policies[0]; }; struct trace_event_raw_bpf_test_finish { struct trace_entry ent; int err; char __data[0]; }; struct trace_event_data_offsets_bpf_test_finish {}; typedef void (*btf_trace_bpf_test_finish)(void *, int *); struct bpf_test_timer { enum { NO_PREEMPT = 0, NO_MIGRATE = 1, } mode; u32 i; u64 time_start; u64 time_spent; }; struct bpf_fentry_test_t { struct bpf_fentry_test_t *a; }; struct bpf_raw_tp_test_run_info { struct bpf_prog *prog; void *ctx; u32 retval; }; struct ethtool_cmd { __u32 cmd; __u32 supported; __u32 advertising; __u16 speed; __u8 duplex; __u8 port; __u8 phy_address; __u8 transceiver; __u8 autoneg; __u8 mdio_support; __u32 maxtxpkt; __u32 maxrxpkt; __u16 speed_hi; __u8 eth_tp_mdix; __u8 eth_tp_mdix_ctrl; __u32 lp_advertising; __u32 reserved[2]; }; struct ethtool_value { __u32 cmd; __u32 data; }; enum tunable_id { ETHTOOL_ID_UNSPEC = 0, ETHTOOL_RX_COPYBREAK = 1, ETHTOOL_TX_COPYBREAK = 2, ETHTOOL_PFC_PREVENTION_TOUT = 3, __ETHTOOL_TUNABLE_COUNT = 4, }; enum tunable_type_id { ETHTOOL_TUNABLE_UNSPEC = 0, ETHTOOL_TUNABLE_U8 = 1, ETHTOOL_TUNABLE_U16 = 2, ETHTOOL_TUNABLE_U32 = 3, ETHTOOL_TUNABLE_U64 = 4, ETHTOOL_TUNABLE_STRING = 5, ETHTOOL_TUNABLE_S8 = 6, ETHTOOL_TUNABLE_S16 = 7, ETHTOOL_TUNABLE_S32 = 8, ETHTOOL_TUNABLE_S64 = 9, }; enum phy_tunable_id { ETHTOOL_PHY_ID_UNSPEC = 0, ETHTOOL_PHY_DOWNSHIFT = 1, ETHTOOL_PHY_FAST_LINK_DOWN = 2, ETHTOOL_PHY_EDPD = 3, __ETHTOOL_PHY_TUNABLE_COUNT = 4, }; enum ethtool_stringset { ETH_SS_TEST = 0, ETH_SS_STATS = 1, ETH_SS_PRIV_FLAGS = 2, ETH_SS_NTUPLE_FILTERS = 3, ETH_SS_FEATURES = 4, ETH_SS_RSS_HASH_FUNCS = 5, ETH_SS_TUNABLES = 6, ETH_SS_PHY_STATS = 7, ETH_SS_PHY_TUNABLES = 8, ETH_SS_LINK_MODES = 9, ETH_SS_MSG_CLASSES = 10, ETH_SS_WOL_MODES = 11, ETH_SS_SOF_TIMESTAMPING = 12, ETH_SS_TS_TX_TYPES = 13, ETH_SS_TS_RX_FILTERS = 14, ETH_SS_UDP_TUNNEL_TYPES = 15, ETH_SS_STATS_STD = 16, ETH_SS_STATS_ETH_PHY = 17, ETH_SS_STATS_ETH_MAC = 18, ETH_SS_STATS_ETH_CTRL = 19, ETH_SS_STATS_RMON = 20, ETH_SS_COUNT = 21, }; struct ethtool_gstrings { __u32 cmd; __u32 string_set; __u32 len; __u8 data[0]; }; struct ethtool_sset_info { __u32 cmd; __u32 reserved; __u64 sset_mask; __u32 data[0]; }; struct ethtool_perm_addr { __u32 cmd; __u32 size; __u8 data[0]; }; enum ethtool_flags { ETH_FLAG_TXVLAN = 128, ETH_FLAG_RXVLAN = 256, ETH_FLAG_LRO = 32768, ETH_FLAG_NTUPLE = 134217728, ETH_FLAG_RXHASH = 268435456, }; struct ethtool_rxfh { __u32 cmd; __u32 rss_context; __u32 indir_size; __u32 key_size; __u8 hfunc; __u8 rsvd8[3]; __u32 rsvd32; __u32 rss_config[0]; }; struct ethtool_get_features_block { __u32 available; __u32 requested; __u32 active; __u32 never_changed; }; struct ethtool_gfeatures { __u32 cmd; __u32 size; struct ethtool_get_features_block features[0]; }; struct ethtool_set_features_block { __u32 valid; __u32 requested; }; struct ethtool_sfeatures { __u32 cmd; __u32 size; struct ethtool_set_features_block features[0]; }; enum ethtool_sfeatures_retval_bits { ETHTOOL_F_UNSUPPORTED__BIT = 0, ETHTOOL_F_WISH__BIT = 1, ETHTOOL_F_COMPAT__BIT = 2, }; struct ethtool_per_queue_op { __u32 cmd; __u32 sub_command; __u32 queue_mask[128]; char data[0]; }; enum ethtool_fec_config_bits { ETHTOOL_FEC_NONE_BIT = 0, ETHTOOL_FEC_AUTO_BIT = 1, ETHTOOL_FEC_OFF_BIT = 2, ETHTOOL_FEC_RS_BIT = 3, ETHTOOL_FEC_BASER_BIT = 4, ETHTOOL_FEC_LLRS_BIT = 5, }; enum { ETH_RSS_HASH_TOP_BIT = 0, ETH_RSS_HASH_XOR_BIT = 1, ETH_RSS_HASH_CRC32_BIT = 2, ETH_RSS_HASH_FUNCS_COUNT = 3, }; struct ethtool_rx_flow_rule { struct flow_rule *rule; long unsigned int priv[0]; }; struct ethtool_rx_flow_spec_input { const struct ethtool_rx_flow_spec *fs; u32 rss_ctx; }; struct ethtool_phy_ops { int (*get_sset_count)(struct phy_device *); int (*get_strings)(struct phy_device *, u8 *); int (*get_stats)(struct phy_device *, struct ethtool_stats *, u64 *); int (*start_cable_test)(struct phy_device *, struct netlink_ext_ack *); int (*start_cable_test_tdr)(struct phy_device *, struct netlink_ext_ack *, const struct phy_tdr_config *); }; enum { ETHTOOL_MSG_KERNEL_NONE = 0, ETHTOOL_MSG_STRSET_GET_REPLY = 1, ETHTOOL_MSG_LINKINFO_GET_REPLY = 2, ETHTOOL_MSG_LINKINFO_NTF = 3, ETHTOOL_MSG_LINKMODES_GET_REPLY = 4, ETHTOOL_MSG_LINKMODES_NTF = 5, ETHTOOL_MSG_LINKSTATE_GET_REPLY = 6, ETHTOOL_MSG_DEBUG_GET_REPLY = 7, ETHTOOL_MSG_DEBUG_NTF = 8, ETHTOOL_MSG_WOL_GET_REPLY = 9, ETHTOOL_MSG_WOL_NTF = 10, ETHTOOL_MSG_FEATURES_GET_REPLY = 11, ETHTOOL_MSG_FEATURES_SET_REPLY = 12, ETHTOOL_MSG_FEATURES_NTF = 13, ETHTOOL_MSG_PRIVFLAGS_GET_REPLY = 14, ETHTOOL_MSG_PRIVFLAGS_NTF = 15, ETHTOOL_MSG_RINGS_GET_REPLY = 16, ETHTOOL_MSG_RINGS_NTF = 17, ETHTOOL_MSG_CHANNELS_GET_REPLY = 18, ETHTOOL_MSG_CHANNELS_NTF = 19, ETHTOOL_MSG_COALESCE_GET_REPLY = 20, ETHTOOL_MSG_COALESCE_NTF = 21, ETHTOOL_MSG_PAUSE_GET_REPLY = 22, ETHTOOL_MSG_PAUSE_NTF = 23, ETHTOOL_MSG_EEE_GET_REPLY = 24, ETHTOOL_MSG_EEE_NTF = 25, ETHTOOL_MSG_TSINFO_GET_REPLY = 26, ETHTOOL_MSG_CABLE_TEST_NTF = 27, ETHTOOL_MSG_CABLE_TEST_TDR_NTF = 28, ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY = 29, ETHTOOL_MSG_FEC_GET_REPLY = 30, ETHTOOL_MSG_FEC_NTF = 31, ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY = 32, ETHTOOL_MSG_STATS_GET_REPLY = 33, ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY = 34, __ETHTOOL_MSG_KERNEL_CNT = 35, ETHTOOL_MSG_KERNEL_MAX = 34, }; enum { ETHTOOL_A_STATS_UNSPEC = 0, ETHTOOL_A_STATS_PAD = 1, ETHTOOL_A_STATS_HEADER = 2, ETHTOOL_A_STATS_GROUPS = 3, ETHTOOL_A_STATS_GRP = 4, __ETHTOOL_A_STATS_CNT = 5, ETHTOOL_A_STATS_MAX = 4, }; struct ethtool_link_usettings { struct ethtool_link_settings base; struct { __u32 supported[3]; __u32 advertising[3]; __u32 lp_advertising[3]; } link_modes; }; struct ethtool_rx_flow_key { struct flow_dissector_key_basic basic; union { struct flow_dissector_key_ipv4_addrs ipv4; struct flow_dissector_key_ipv6_addrs ipv6; }; struct flow_dissector_key_ports tp; struct flow_dissector_key_ip ip; struct flow_dissector_key_vlan vlan; struct flow_dissector_key_eth_addrs eth_addrs; long: 48; }; struct ethtool_rx_flow_match { struct flow_dissector dissector; int: 32; struct ethtool_rx_flow_key key; struct ethtool_rx_flow_key mask; }; enum { ETHTOOL_UDP_TUNNEL_TYPE_VXLAN = 0, ETHTOOL_UDP_TUNNEL_TYPE_GENEVE = 1, ETHTOOL_UDP_TUNNEL_TYPE_VXLAN_GPE = 2, __ETHTOOL_UDP_TUNNEL_TYPE_CNT = 3, }; struct link_mode_info { int speed; u8 lanes; u8 duplex; }; enum { ETHTOOL_MSG_USER_NONE = 0, ETHTOOL_MSG_STRSET_GET = 1, ETHTOOL_MSG_LINKINFO_GET = 2, ETHTOOL_MSG_LINKINFO_SET = 3, ETHTOOL_MSG_LINKMODES_GET = 4, ETHTOOL_MSG_LINKMODES_SET = 5, ETHTOOL_MSG_LINKSTATE_GET = 6, ETHTOOL_MSG_DEBUG_GET = 7, ETHTOOL_MSG_DEBUG_SET = 8, ETHTOOL_MSG_WOL_GET = 9, ETHTOOL_MSG_WOL_SET = 10, ETHTOOL_MSG_FEATURES_GET = 11, ETHTOOL_MSG_FEATURES_SET = 12, ETHTOOL_MSG_PRIVFLAGS_GET = 13, ETHTOOL_MSG_PRIVFLAGS_SET = 14, ETHTOOL_MSG_RINGS_GET = 15, ETHTOOL_MSG_RINGS_SET = 16, ETHTOOL_MSG_CHANNELS_GET = 17, ETHTOOL_MSG_CHANNELS_SET = 18, ETHTOOL_MSG_COALESCE_GET = 19, ETHTOOL_MSG_COALESCE_SET = 20, ETHTOOL_MSG_PAUSE_GET = 21, ETHTOOL_MSG_PAUSE_SET = 22, ETHTOOL_MSG_EEE_GET = 23, ETHTOOL_MSG_EEE_SET = 24, ETHTOOL_MSG_TSINFO_GET = 25, ETHTOOL_MSG_CABLE_TEST_ACT = 26, ETHTOOL_MSG_CABLE_TEST_TDR_ACT = 27, ETHTOOL_MSG_TUNNEL_INFO_GET = 28, ETHTOOL_MSG_FEC_GET = 29, ETHTOOL_MSG_FEC_SET = 30, ETHTOOL_MSG_MODULE_EEPROM_GET = 31, ETHTOOL_MSG_STATS_GET = 32, ETHTOOL_MSG_PHC_VCLOCKS_GET = 33, __ETHTOOL_MSG_USER_CNT = 34, ETHTOOL_MSG_USER_MAX = 33, }; enum { ETHTOOL_A_HEADER_UNSPEC = 0, ETHTOOL_A_HEADER_DEV_INDEX = 1, ETHTOOL_A_HEADER_DEV_NAME = 2, ETHTOOL_A_HEADER_FLAGS = 3, __ETHTOOL_A_HEADER_CNT = 4, ETHTOOL_A_HEADER_MAX = 3, }; enum { ETHTOOL_A_STRSET_UNSPEC = 0, ETHTOOL_A_STRSET_HEADER = 1, ETHTOOL_A_STRSET_STRINGSETS = 2, ETHTOOL_A_STRSET_COUNTS_ONLY = 3, __ETHTOOL_A_STRSET_CNT = 4, ETHTOOL_A_STRSET_MAX = 3, }; enum { ETHTOOL_A_LINKINFO_UNSPEC = 0, ETHTOOL_A_LINKINFO_HEADER = 1, ETHTOOL_A_LINKINFO_PORT = 2, ETHTOOL_A_LINKINFO_PHYADDR = 3, ETHTOOL_A_LINKINFO_TP_MDIX = 4, ETHTOOL_A_LINKINFO_TP_MDIX_CTRL = 5, ETHTOOL_A_LINKINFO_TRANSCEIVER = 6, __ETHTOOL_A_LINKINFO_CNT = 7, ETHTOOL_A_LINKINFO_MAX = 6, }; enum { ETHTOOL_A_LINKMODES_UNSPEC = 0, ETHTOOL_A_LINKMODES_HEADER = 1, ETHTOOL_A_LINKMODES_AUTONEG = 2, ETHTOOL_A_LINKMODES_OURS = 3, ETHTOOL_A_LINKMODES_PEER = 4, ETHTOOL_A_LINKMODES_SPEED = 5, ETHTOOL_A_LINKMODES_DUPLEX = 6, ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG = 7, ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE = 8, ETHTOOL_A_LINKMODES_LANES = 9, __ETHTOOL_A_LINKMODES_CNT = 10, ETHTOOL_A_LINKMODES_MAX = 9, }; enum { ETHTOOL_A_LINKSTATE_UNSPEC = 0, ETHTOOL_A_LINKSTATE_HEADER = 1, ETHTOOL_A_LINKSTATE_LINK = 2, ETHTOOL_A_LINKSTATE_SQI = 3, ETHTOOL_A_LINKSTATE_SQI_MAX = 4, ETHTOOL_A_LINKSTATE_EXT_STATE = 5, ETHTOOL_A_LINKSTATE_EXT_SUBSTATE = 6, __ETHTOOL_A_LINKSTATE_CNT = 7, ETHTOOL_A_LINKSTATE_MAX = 6, }; enum { ETHTOOL_A_DEBUG_UNSPEC = 0, ETHTOOL_A_DEBUG_HEADER = 1, ETHTOOL_A_DEBUG_MSGMASK = 2, __ETHTOOL_A_DEBUG_CNT = 3, ETHTOOL_A_DEBUG_MAX = 2, }; enum { ETHTOOL_A_WOL_UNSPEC = 0, ETHTOOL_A_WOL_HEADER = 1, ETHTOOL_A_WOL_MODES = 2, ETHTOOL_A_WOL_SOPASS = 3, __ETHTOOL_A_WOL_CNT = 4, ETHTOOL_A_WOL_MAX = 3, }; enum { ETHTOOL_A_FEATURES_UNSPEC = 0, ETHTOOL_A_FEATURES_HEADER = 1, ETHTOOL_A_FEATURES_HW = 2, ETHTOOL_A_FEATURES_WANTED = 3, ETHTOOL_A_FEATURES_ACTIVE = 4, ETHTOOL_A_FEATURES_NOCHANGE = 5, __ETHTOOL_A_FEATURES_CNT = 6, ETHTOOL_A_FEATURES_MAX = 5, }; enum { ETHTOOL_A_PRIVFLAGS_UNSPEC = 0, ETHTOOL_A_PRIVFLAGS_HEADER = 1, ETHTOOL_A_PRIVFLAGS_FLAGS = 2, __ETHTOOL_A_PRIVFLAGS_CNT = 3, ETHTOOL_A_PRIVFLAGS_MAX = 2, }; enum { ETHTOOL_A_RINGS_UNSPEC = 0, ETHTOOL_A_RINGS_HEADER = 1, ETHTOOL_A_RINGS_RX_MAX = 2, ETHTOOL_A_RINGS_RX_MINI_MAX = 3, ETHTOOL_A_RINGS_RX_JUMBO_MAX = 4, ETHTOOL_A_RINGS_TX_MAX = 5, ETHTOOL_A_RINGS_RX = 6, ETHTOOL_A_RINGS_RX_MINI = 7, ETHTOOL_A_RINGS_RX_JUMBO = 8, ETHTOOL_A_RINGS_TX = 9, __ETHTOOL_A_RINGS_CNT = 10, ETHTOOL_A_RINGS_MAX = 9, }; enum { ETHTOOL_A_CHANNELS_UNSPEC = 0, ETHTOOL_A_CHANNELS_HEADER = 1, ETHTOOL_A_CHANNELS_RX_MAX = 2, ETHTOOL_A_CHANNELS_TX_MAX = 3, ETHTOOL_A_CHANNELS_OTHER_MAX = 4, ETHTOOL_A_CHANNELS_COMBINED_MAX = 5, ETHTOOL_A_CHANNELS_RX_COUNT = 6, ETHTOOL_A_CHANNELS_TX_COUNT = 7, ETHTOOL_A_CHANNELS_OTHER_COUNT = 8, ETHTOOL_A_CHANNELS_COMBINED_COUNT = 9, __ETHTOOL_A_CHANNELS_CNT = 10, ETHTOOL_A_CHANNELS_MAX = 9, }; enum { ETHTOOL_A_COALESCE_UNSPEC = 0, ETHTOOL_A_COALESCE_HEADER = 1, ETHTOOL_A_COALESCE_RX_USECS = 2, ETHTOOL_A_COALESCE_RX_MAX_FRAMES = 3, ETHTOOL_A_COALESCE_RX_USECS_IRQ = 4, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ = 5, ETHTOOL_A_COALESCE_TX_USECS = 6, ETHTOOL_A_COALESCE_TX_MAX_FRAMES = 7, ETHTOOL_A_COALESCE_TX_USECS_IRQ = 8, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ = 9, ETHTOOL_A_COALESCE_STATS_BLOCK_USECS = 10, ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX = 11, ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX = 12, ETHTOOL_A_COALESCE_PKT_RATE_LOW = 13, ETHTOOL_A_COALESCE_RX_USECS_LOW = 14, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW = 15, ETHTOOL_A_COALESCE_TX_USECS_LOW = 16, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW = 17, ETHTOOL_A_COALESCE_PKT_RATE_HIGH = 18, ETHTOOL_A_COALESCE_RX_USECS_HIGH = 19, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH = 20, ETHTOOL_A_COALESCE_TX_USECS_HIGH = 21, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH = 22, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL = 23, __ETHTOOL_A_COALESCE_CNT = 24, ETHTOOL_A_COALESCE_MAX = 23, }; enum { ETHTOOL_A_PAUSE_UNSPEC = 0, ETHTOOL_A_PAUSE_HEADER = 1, ETHTOOL_A_PAUSE_AUTONEG = 2, ETHTOOL_A_PAUSE_RX = 3, ETHTOOL_A_PAUSE_TX = 4, ETHTOOL_A_PAUSE_STATS = 5, __ETHTOOL_A_PAUSE_CNT = 6, ETHTOOL_A_PAUSE_MAX = 5, }; enum { ETHTOOL_A_EEE_UNSPEC = 0, ETHTOOL_A_EEE_HEADER = 1, ETHTOOL_A_EEE_MODES_OURS = 2, ETHTOOL_A_EEE_MODES_PEER = 3, ETHTOOL_A_EEE_ACTIVE = 4, ETHTOOL_A_EEE_ENABLED = 5, ETHTOOL_A_EEE_TX_LPI_ENABLED = 6, ETHTOOL_A_EEE_TX_LPI_TIMER = 7, __ETHTOOL_A_EEE_CNT = 8, ETHTOOL_A_EEE_MAX = 7, }; enum { ETHTOOL_A_TSINFO_UNSPEC = 0, ETHTOOL_A_TSINFO_HEADER = 1, ETHTOOL_A_TSINFO_TIMESTAMPING = 2, ETHTOOL_A_TSINFO_TX_TYPES = 3, ETHTOOL_A_TSINFO_RX_FILTERS = 4, ETHTOOL_A_TSINFO_PHC_INDEX = 5, __ETHTOOL_A_TSINFO_CNT = 6, ETHTOOL_A_TSINFO_MAX = 5, }; enum { ETHTOOL_A_PHC_VCLOCKS_UNSPEC = 0, ETHTOOL_A_PHC_VCLOCKS_HEADER = 1, ETHTOOL_A_PHC_VCLOCKS_NUM = 2, ETHTOOL_A_PHC_VCLOCKS_INDEX = 3, __ETHTOOL_A_PHC_VCLOCKS_CNT = 4, ETHTOOL_A_PHC_VCLOCKS_MAX = 3, }; enum { ETHTOOL_A_CABLE_TEST_UNSPEC = 0, ETHTOOL_A_CABLE_TEST_HEADER = 1, __ETHTOOL_A_CABLE_TEST_CNT = 2, ETHTOOL_A_CABLE_TEST_MAX = 1, }; enum { ETHTOOL_A_CABLE_TEST_TDR_UNSPEC = 0, ETHTOOL_A_CABLE_TEST_TDR_HEADER = 1, ETHTOOL_A_CABLE_TEST_TDR_CFG = 2, __ETHTOOL_A_CABLE_TEST_TDR_CNT = 3, ETHTOOL_A_CABLE_TEST_TDR_MAX = 2, }; enum { ETHTOOL_A_TUNNEL_INFO_UNSPEC = 0, ETHTOOL_A_TUNNEL_INFO_HEADER = 1, ETHTOOL_A_TUNNEL_INFO_UDP_PORTS = 2, __ETHTOOL_A_TUNNEL_INFO_CNT = 3, ETHTOOL_A_TUNNEL_INFO_MAX = 2, }; enum { ETHTOOL_A_FEC_UNSPEC = 0, ETHTOOL_A_FEC_HEADER = 1, ETHTOOL_A_FEC_MODES = 2, ETHTOOL_A_FEC_AUTO = 3, ETHTOOL_A_FEC_ACTIVE = 4, ETHTOOL_A_FEC_STATS = 5, __ETHTOOL_A_FEC_CNT = 6, ETHTOOL_A_FEC_MAX = 5, }; enum { ETHTOOL_A_MODULE_EEPROM_UNSPEC = 0, ETHTOOL_A_MODULE_EEPROM_HEADER = 1, ETHTOOL_A_MODULE_EEPROM_OFFSET = 2, ETHTOOL_A_MODULE_EEPROM_LENGTH = 3, ETHTOOL_A_MODULE_EEPROM_PAGE = 4, ETHTOOL_A_MODULE_EEPROM_BANK = 5, ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS = 6, ETHTOOL_A_MODULE_EEPROM_DATA = 7, __ETHTOOL_A_MODULE_EEPROM_CNT = 8, ETHTOOL_A_MODULE_EEPROM_MAX = 7, }; enum { ETHTOOL_STATS_ETH_PHY = 0, ETHTOOL_STATS_ETH_MAC = 1, ETHTOOL_STATS_ETH_CTRL = 2, ETHTOOL_STATS_RMON = 3, __ETHTOOL_STATS_CNT = 4, }; enum { ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR = 0, __ETHTOOL_A_STATS_ETH_PHY_CNT = 1, ETHTOOL_A_STATS_ETH_PHY_MAX = 0, }; enum { ETHTOOL_A_STATS_ETH_MAC_2_TX_PKT = 0, ETHTOOL_A_STATS_ETH_MAC_3_SINGLE_COL = 1, ETHTOOL_A_STATS_ETH_MAC_4_MULTI_COL = 2, ETHTOOL_A_STATS_ETH_MAC_5_RX_PKT = 3, ETHTOOL_A_STATS_ETH_MAC_6_FCS_ERR = 4, ETHTOOL_A_STATS_ETH_MAC_7_ALIGN_ERR = 5, ETHTOOL_A_STATS_ETH_MAC_8_TX_BYTES = 6, ETHTOOL_A_STATS_ETH_MAC_9_TX_DEFER = 7, ETHTOOL_A_STATS_ETH_MAC_10_LATE_COL = 8, ETHTOOL_A_STATS_ETH_MAC_11_XS_COL = 9, ETHTOOL_A_STATS_ETH_MAC_12_TX_INT_ERR = 10, ETHTOOL_A_STATS_ETH_MAC_13_CS_ERR = 11, ETHTOOL_A_STATS_ETH_MAC_14_RX_BYTES = 12, ETHTOOL_A_STATS_ETH_MAC_15_RX_INT_ERR = 13, ETHTOOL_A_STATS_ETH_MAC_18_TX_MCAST = 14, ETHTOOL_A_STATS_ETH_MAC_19_TX_BCAST = 15, ETHTOOL_A_STATS_ETH_MAC_20_XS_DEFER = 16, ETHTOOL_A_STATS_ETH_MAC_21_RX_MCAST = 17, ETHTOOL_A_STATS_ETH_MAC_22_RX_BCAST = 18, ETHTOOL_A_STATS_ETH_MAC_23_IR_LEN_ERR = 19, ETHTOOL_A_STATS_ETH_MAC_24_OOR_LEN = 20, ETHTOOL_A_STATS_ETH_MAC_25_TOO_LONG_ERR = 21, __ETHTOOL_A_STATS_ETH_MAC_CNT = 22, ETHTOOL_A_STATS_ETH_MAC_MAX = 21, }; enum { ETHTOOL_A_STATS_ETH_CTRL_3_TX = 0, ETHTOOL_A_STATS_ETH_CTRL_4_RX = 1, ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP = 2, __ETHTOOL_A_STATS_ETH_CTRL_CNT = 3, ETHTOOL_A_STATS_ETH_CTRL_MAX = 2, }; enum { ETHTOOL_A_STATS_RMON_UNDERSIZE = 0, ETHTOOL_A_STATS_RMON_OVERSIZE = 1, ETHTOOL_A_STATS_RMON_FRAG = 2, ETHTOOL_A_STATS_RMON_JABBER = 3, __ETHTOOL_A_STATS_RMON_CNT = 4, ETHTOOL_A_STATS_RMON_MAX = 3, }; enum ethtool_multicast_groups { ETHNL_MCGRP_MONITOR = 0, }; struct ethnl_req_info { struct net_device *dev; u32 flags; }; struct ethnl_reply_data { struct net_device *dev; }; struct ethnl_request_ops { u8 request_cmd; u8 reply_cmd; u16 hdr_attr; unsigned int req_info_size; unsigned int reply_data_size; bool allow_nodev_do; int (*parse_request)(struct ethnl_req_info *, struct nlattr **, struct netlink_ext_ack *); int (*prepare_data)(const struct ethnl_req_info *, struct ethnl_reply_data *, struct genl_info *); int (*reply_size)(const struct ethnl_req_info *, const struct ethnl_reply_data *); int (*fill_reply)(struct sk_buff *, const struct ethnl_req_info *, const struct ethnl_reply_data *); void (*cleanup_data)(struct ethnl_reply_data *); }; struct ethnl_dump_ctx { const struct ethnl_request_ops *ops; struct ethnl_req_info *req_info; struct ethnl_reply_data *reply_data; int pos_hash; int pos_idx; }; typedef void (*ethnl_notify_handler_t)(struct net_device *, unsigned int, const void *); enum { ETHTOOL_A_BITSET_BIT_UNSPEC = 0, ETHTOOL_A_BITSET_BIT_INDEX = 1, ETHTOOL_A_BITSET_BIT_NAME = 2, ETHTOOL_A_BITSET_BIT_VALUE = 3, __ETHTOOL_A_BITSET_BIT_CNT = 4, ETHTOOL_A_BITSET_BIT_MAX = 3, }; enum { ETHTOOL_A_BITSET_BITS_UNSPEC = 0, ETHTOOL_A_BITSET_BITS_BIT = 1, __ETHTOOL_A_BITSET_BITS_CNT = 2, ETHTOOL_A_BITSET_BITS_MAX = 1, }; enum { ETHTOOL_A_BITSET_UNSPEC = 0, ETHTOOL_A_BITSET_NOMASK = 1, ETHTOOL_A_BITSET_SIZE = 2, ETHTOOL_A_BITSET_BITS = 3, ETHTOOL_A_BITSET_VALUE = 4, ETHTOOL_A_BITSET_MASK = 5, __ETHTOOL_A_BITSET_CNT = 6, ETHTOOL_A_BITSET_MAX = 5, }; typedef const char (* const ethnl_string_array_t)[32]; enum { ETHTOOL_A_STRING_UNSPEC = 0, ETHTOOL_A_STRING_INDEX = 1, ETHTOOL_A_STRING_VALUE = 2, __ETHTOOL_A_STRING_CNT = 3, ETHTOOL_A_STRING_MAX = 2, }; enum { ETHTOOL_A_STRINGS_UNSPEC = 0, ETHTOOL_A_STRINGS_STRING = 1, __ETHTOOL_A_STRINGS_CNT = 2, ETHTOOL_A_STRINGS_MAX = 1, }; enum { ETHTOOL_A_STRINGSET_UNSPEC = 0, ETHTOOL_A_STRINGSET_ID = 1, ETHTOOL_A_STRINGSET_COUNT = 2, ETHTOOL_A_STRINGSET_STRINGS = 3, __ETHTOOL_A_STRINGSET_CNT = 4, ETHTOOL_A_STRINGSET_MAX = 3, }; enum { ETHTOOL_A_STRINGSETS_UNSPEC = 0, ETHTOOL_A_STRINGSETS_STRINGSET = 1, __ETHTOOL_A_STRINGSETS_CNT = 2, ETHTOOL_A_STRINGSETS_MAX = 1, }; struct strset_info { bool per_dev; bool free_strings; unsigned int count; const char (*strings)[32]; }; struct strset_req_info { struct ethnl_req_info base; u32 req_ids; bool counts_only; }; struct strset_reply_data { struct ethnl_reply_data base; struct strset_info sets[21]; }; struct linkinfo_reply_data { struct ethnl_reply_data base; struct ethtool_link_ksettings ksettings; struct ethtool_link_settings *lsettings; }; struct linkmodes_reply_data { struct ethnl_reply_data base; struct ethtool_link_ksettings ksettings; struct ethtool_link_settings *lsettings; bool peer_empty; }; struct linkstate_reply_data { struct ethnl_reply_data base; int link; int sqi; int sqi_max; bool link_ext_state_provided; struct ethtool_link_ext_state_info ethtool_link_ext_state_info; }; struct debug_reply_data { struct ethnl_reply_data base; u32 msg_mask; }; struct wol_reply_data { struct ethnl_reply_data base; struct ethtool_wolinfo wol; bool show_sopass; }; struct features_reply_data { struct ethnl_reply_data base; u32 hw[2]; u32 wanted[2]; u32 active[2]; u32 nochange[2]; u32 all[2]; }; struct privflags_reply_data { struct ethnl_reply_data base; const char (*priv_flag_names)[32]; unsigned int n_priv_flags; u32 priv_flags; }; struct rings_reply_data { struct ethnl_reply_data base; struct ethtool_ringparam ringparam; }; struct channels_reply_data { struct ethnl_reply_data base; struct ethtool_channels channels; }; struct coalesce_reply_data { struct ethnl_reply_data base; struct ethtool_coalesce coalesce; u32 supported_params; }; enum { ETHTOOL_A_PAUSE_STAT_UNSPEC = 0, ETHTOOL_A_PAUSE_STAT_PAD = 1, ETHTOOL_A_PAUSE_STAT_TX_FRAMES = 2, ETHTOOL_A_PAUSE_STAT_RX_FRAMES = 3, __ETHTOOL_A_PAUSE_STAT_CNT = 4, ETHTOOL_A_PAUSE_STAT_MAX = 3, }; struct pause_reply_data { struct ethnl_reply_data base; struct ethtool_pauseparam pauseparam; struct ethtool_pause_stats pausestat; }; struct eee_reply_data { struct ethnl_reply_data base; struct ethtool_eee eee; }; struct tsinfo_reply_data { struct ethnl_reply_data base; struct ethtool_ts_info ts_info; }; enum { ETHTOOL_A_CABLE_PAIR_A = 0, ETHTOOL_A_CABLE_PAIR_B = 1, ETHTOOL_A_CABLE_PAIR_C = 2, ETHTOOL_A_CABLE_PAIR_D = 3, }; enum { ETHTOOL_A_CABLE_RESULT_UNSPEC = 0, ETHTOOL_A_CABLE_RESULT_PAIR = 1, ETHTOOL_A_CABLE_RESULT_CODE = 2, __ETHTOOL_A_CABLE_RESULT_CNT = 3, ETHTOOL_A_CABLE_RESULT_MAX = 2, }; enum { ETHTOOL_A_CABLE_FAULT_LENGTH_UNSPEC = 0, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR = 1, ETHTOOL_A_CABLE_FAULT_LENGTH_CM = 2, __ETHTOOL_A_CABLE_FAULT_LENGTH_CNT = 3, ETHTOOL_A_CABLE_FAULT_LENGTH_MAX = 2, }; enum { ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC = 0, ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED = 1, ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED = 2, }; enum { ETHTOOL_A_CABLE_NEST_UNSPEC = 0, ETHTOOL_A_CABLE_NEST_RESULT = 1, ETHTOOL_A_CABLE_NEST_FAULT_LENGTH = 2, __ETHTOOL_A_CABLE_NEST_CNT = 3, ETHTOOL_A_CABLE_NEST_MAX = 2, }; enum { ETHTOOL_A_CABLE_TEST_NTF_UNSPEC = 0, ETHTOOL_A_CABLE_TEST_NTF_HEADER = 1, ETHTOOL_A_CABLE_TEST_NTF_STATUS = 2, ETHTOOL_A_CABLE_TEST_NTF_NEST = 3, __ETHTOOL_A_CABLE_TEST_NTF_CNT = 4, ETHTOOL_A_CABLE_TEST_NTF_MAX = 3, }; enum { ETHTOOL_A_CABLE_TEST_TDR_CFG_UNSPEC = 0, ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST = 1, ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST = 2, ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP = 3, ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR = 4, __ETHTOOL_A_CABLE_TEST_TDR_CFG_CNT = 5, ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX = 4, }; enum { ETHTOOL_A_CABLE_AMPLITUDE_UNSPEC = 0, ETHTOOL_A_CABLE_AMPLITUDE_PAIR = 1, ETHTOOL_A_CABLE_AMPLITUDE_mV = 2, __ETHTOOL_A_CABLE_AMPLITUDE_CNT = 3, ETHTOOL_A_CABLE_AMPLITUDE_MAX = 2, }; enum { ETHTOOL_A_CABLE_PULSE_UNSPEC = 0, ETHTOOL_A_CABLE_PULSE_mV = 1, __ETHTOOL_A_CABLE_PULSE_CNT = 2, ETHTOOL_A_CABLE_PULSE_MAX = 1, }; enum { ETHTOOL_A_CABLE_STEP_UNSPEC = 0, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE = 1, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE = 2, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE = 3, __ETHTOOL_A_CABLE_STEP_CNT = 4, ETHTOOL_A_CABLE_STEP_MAX = 3, }; enum { ETHTOOL_A_CABLE_TDR_NEST_UNSPEC = 0, ETHTOOL_A_CABLE_TDR_NEST_STEP = 1, ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE = 2, ETHTOOL_A_CABLE_TDR_NEST_PULSE = 3, __ETHTOOL_A_CABLE_TDR_NEST_CNT = 4, ETHTOOL_A_CABLE_TDR_NEST_MAX = 3, }; enum { ETHTOOL_A_TUNNEL_UDP_ENTRY_UNSPEC = 0, ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT = 1, ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE = 2, __ETHTOOL_A_TUNNEL_UDP_ENTRY_CNT = 3, ETHTOOL_A_TUNNEL_UDP_ENTRY_MAX = 2, }; enum { ETHTOOL_A_TUNNEL_UDP_TABLE_UNSPEC = 0, ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE = 1, ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES = 2, ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY = 3, __ETHTOOL_A_TUNNEL_UDP_TABLE_CNT = 4, ETHTOOL_A_TUNNEL_UDP_TABLE_MAX = 3, }; enum { ETHTOOL_A_TUNNEL_UDP_UNSPEC = 0, ETHTOOL_A_TUNNEL_UDP_TABLE = 1, __ETHTOOL_A_TUNNEL_UDP_CNT = 2, ETHTOOL_A_TUNNEL_UDP_MAX = 1, }; enum udp_parsable_tunnel_type { UDP_TUNNEL_TYPE_VXLAN = 1, UDP_TUNNEL_TYPE_GENEVE = 2, UDP_TUNNEL_TYPE_VXLAN_GPE = 4, }; enum udp_tunnel_nic_info_flags { UDP_TUNNEL_NIC_INFO_MAY_SLEEP = 1, UDP_TUNNEL_NIC_INFO_OPEN_ONLY = 2, UDP_TUNNEL_NIC_INFO_IPV4_ONLY = 4, UDP_TUNNEL_NIC_INFO_STATIC_IANA_VXLAN = 8, }; struct udp_tunnel_nic_ops { void (*get_port)(struct net_device *, unsigned int, unsigned int, struct udp_tunnel_info *); void (*set_port_priv)(struct net_device *, unsigned int, unsigned int, u8); void (*add_port)(struct net_device *, struct udp_tunnel_info *); void (*del_port)(struct net_device *, struct udp_tunnel_info *); void (*reset_ntf)(struct net_device *); size_t (*dump_size)(struct net_device *, unsigned int); int (*dump_write)(struct net_device *, unsigned int, struct sk_buff *); }; struct ethnl_tunnel_info_dump_ctx { struct ethnl_req_info req_info; int pos_hash; int pos_idx; }; enum { ETHTOOL_A_FEC_STAT_UNSPEC = 0, ETHTOOL_A_FEC_STAT_PAD = 1, ETHTOOL_A_FEC_STAT_CORRECTED = 2, ETHTOOL_A_FEC_STAT_UNCORR = 3, ETHTOOL_A_FEC_STAT_CORR_BITS = 4, __ETHTOOL_A_FEC_STAT_CNT = 5, ETHTOOL_A_FEC_STAT_MAX = 4, }; struct fec_stat_grp { u64 stats[9]; u8 cnt; }; struct fec_reply_data { struct ethnl_reply_data base; long unsigned int fec_link_modes[2]; u32 active_fec; u8 fec_auto; struct fec_stat_grp corr; struct fec_stat_grp uncorr; struct fec_stat_grp corr_bits; }; struct eeprom_req_info { struct ethnl_req_info base; u32 offset; u32 length; u8 page; u8 bank; u8 i2c_address; }; struct eeprom_reply_data { struct ethnl_reply_data base; u32 length; u8 *data; }; enum { ETHTOOL_A_STATS_GRP_UNSPEC = 0, ETHTOOL_A_STATS_GRP_PAD = 1, ETHTOOL_A_STATS_GRP_ID = 2, ETHTOOL_A_STATS_GRP_SS_ID = 3, ETHTOOL_A_STATS_GRP_STAT = 4, ETHTOOL_A_STATS_GRP_HIST_RX = 5, ETHTOOL_A_STATS_GRP_HIST_TX = 6, ETHTOOL_A_STATS_GRP_HIST_BKT_LOW = 7, ETHTOOL_A_STATS_GRP_HIST_BKT_HI = 8, ETHTOOL_A_STATS_GRP_HIST_VAL = 9, __ETHTOOL_A_STATS_GRP_CNT = 10, ETHTOOL_A_STATS_GRP_MAX = 4, }; struct stats_req_info { struct ethnl_req_info base; long unsigned int stat_mask[1]; }; struct stats_reply_data { struct ethnl_reply_data base; struct ethtool_eth_phy_stats phy_stats; struct ethtool_eth_mac_stats mac_stats; struct ethtool_eth_ctrl_stats ctrl_stats; struct ethtool_rmon_stats rmon_stats; const struct ethtool_rmon_hist_range *rmon_ranges; }; struct phc_vclocks_reply_data { struct ethnl_reply_data base; int num; int *index; }; struct nf_hook_entries_rcu_head { struct callback_head head; void *allocation; }; struct nf_conn___2; enum nf_nat_manip_type; struct nf_nat_hook { int (*parse_nat_setup)(struct nf_conn___2 *, enum nf_nat_manip_type, const struct nlattr *); void (*decode_session)(struct sk_buff *, struct flowi *); unsigned int (*manip_pkt)(struct sk_buff *, struct nf_conn___2 *, enum nf_nat_manip_type, enum ip_conntrack_dir); }; struct nf_conntrack_tuple___2; struct nf_ct_hook { int (*update)(struct net *, struct sk_buff *); void (*destroy)(struct nf_conntrack *); bool (*get_tuple_skb)(struct nf_conntrack_tuple___2 *, const struct sk_buff *); }; struct nfnl_ct_hook { size_t (*build_size)(const struct nf_conn___2 *); int (*build)(struct sk_buff *, struct nf_conn___2 *, enum ip_conntrack_info, u_int16_t, u_int16_t); int (*parse)(const struct nlattr *, struct nf_conn___2 *); int (*attach_expect)(const struct nlattr *, struct nf_conn___2 *, u32, u32); void (*seq_adjust)(struct sk_buff *, struct nf_conn___2 *, enum ip_conntrack_info, s32); }; struct nf_ipv6_ops { void (*route_input)(struct sk_buff *); int (*fragment)(struct net *, struct sock *, struct sk_buff *, int (*)(struct net *, struct sock *, struct sk_buff *)); int (*reroute)(struct sk_buff *, const struct nf_queue_entry *); }; struct nf_queue_entry { struct list_head list; struct sk_buff *skb; unsigned int id; unsigned int hook_index; struct net_device *physin; struct net_device *physout; struct nf_hook_state state; u16 size; }; struct nf_loginfo { u_int8_t type; union { struct { u_int32_t copy_len; u_int16_t group; u_int16_t qthreshold; u_int16_t flags; } ulog; struct { u_int8_t level; u_int8_t logflags; } log; } u; }; struct nf_log_buf { unsigned int count; char buf[1020]; }; struct nf_bridge_info { enum { BRNF_PROTO_UNCHANGED = 0, BRNF_PROTO_8021Q = 1, BRNF_PROTO_PPPOE = 2, } orig_proto: 8; u8 pkt_otherhost: 1; u8 in_prerouting: 1; u8 bridged_dnat: 1; __u16 frag_max_size; struct net_device *physindev; struct net_device *physoutdev; union { __be32 ipv4_daddr; struct in6_addr ipv6_daddr; char neigh_header[8]; }; }; struct ip_rt_info { __be32 daddr; __be32 saddr; u_int8_t tos; u_int32_t mark; }; struct ip6_rt_info { struct in6_addr daddr; struct in6_addr saddr; u_int32_t mark; }; struct nf_sockopt_ops { struct list_head list; u_int8_t pf; int set_optmin; int set_optmax; int (*set)(struct sock *, int, sockptr_t, unsigned int); int get_optmin; int get_optmax; int (*get)(struct sock *, int, void *, int *); struct module *owner; }; struct ip_mreqn { struct in_addr imr_multiaddr; struct in_addr imr_address; int imr_ifindex; }; struct rtmsg { unsigned char rtm_family; unsigned char rtm_dst_len; unsigned char rtm_src_len; unsigned char rtm_tos; unsigned char rtm_table; unsigned char rtm_protocol; unsigned char rtm_scope; unsigned char rtm_type; unsigned int rtm_flags; }; struct rtvia { __kernel_sa_family_t rtvia_family; __u8 rtvia_addr[0]; }; struct ip_sf_list; struct ip_mc_list { struct in_device *interface; __be32 multiaddr; unsigned int sfmode; struct ip_sf_list *sources; struct ip_sf_list *tomb; long unsigned int sfcount[2]; union { struct ip_mc_list *next; struct ip_mc_list *next_rcu; }; struct ip_mc_list *next_hash; struct timer_list timer; int users; refcount_t refcnt; spinlock_t lock; char tm_running; char reporter; char unsolicit_count; char loaded; unsigned char gsquery; unsigned char crcount; struct callback_head rcu; }; struct ip_sf_socklist { unsigned int sl_max; unsigned int sl_count; struct callback_head rcu; __be32 sl_addr[0]; }; struct ip_mc_socklist { struct ip_mc_socklist *next_rcu; struct ip_mreqn multi; unsigned int sfmode; struct ip_sf_socklist *sflist; struct callback_head rcu; }; struct ip_sf_list { struct ip_sf_list *sf_next; long unsigned int sf_count[2]; __be32 sf_inaddr; unsigned char sf_gsresp; unsigned char sf_oldin; unsigned char sf_crcount; }; struct ipv4_addr_key { __be32 addr; int vif; }; struct inetpeer_addr { union { struct ipv4_addr_key a4; struct in6_addr a6; u32 key[4]; }; __u16 family; }; struct inet_peer { struct rb_node rb_node; struct inetpeer_addr daddr; u32 metrics[17]; u32 rate_tokens; u32 n_redirects; long unsigned int rate_last; union { struct { atomic_t rid; }; struct callback_head rcu; }; __u32 dtime; refcount_t refcnt; }; struct fib_rt_info { struct fib_info *fi; u32 tb_id; __be32 dst; int dst_len; u8 tos; u8 type; u8 offload: 1; u8 trap: 1; u8 offload_failed: 1; u8 unused: 5; }; struct uncached_list { spinlock_t lock; struct list_head head; }; struct ip_rt_acct { __u32 o_bytes; __u32 o_packets; __u32 i_bytes; __u32 i_packets; }; struct rt_cache_stat { unsigned int in_slow_tot; unsigned int in_slow_mc; unsigned int in_no_route; unsigned int in_brd; unsigned int in_martian_dst; unsigned int in_martian_src; unsigned int out_slow_tot; unsigned int out_slow_mc; }; struct fib_alias { struct hlist_node fa_list; struct fib_info *fa_info; u8 fa_tos; u8 fa_type; u8 fa_state; u8 fa_slen; u32 tb_id; s16 fa_default; u8 offload: 1; u8 trap: 1; u8 offload_failed: 1; u8 unused: 5; struct callback_head rcu; }; struct fib_prop { int error; u8 scope; }; struct net_offload { struct offload_callbacks callbacks; unsigned int flags; }; struct raw_hashinfo { rwlock_t lock; struct hlist_head ht[256]; }; enum ip_defrag_users { IP_DEFRAG_LOCAL_DELIVER = 0, IP_DEFRAG_CALL_RA_CHAIN = 1, IP_DEFRAG_CONNTRACK_IN = 2, __IP_DEFRAG_CONNTRACK_IN_END = 65537, IP_DEFRAG_CONNTRACK_OUT = 65538, __IP_DEFRAG_CONNTRACK_OUT_END = 131073, IP_DEFRAG_CONNTRACK_BRIDGE_IN = 131074, __IP_DEFRAG_CONNTRACK_BRIDGE_IN = 196609, IP_DEFRAG_VS_IN = 196610, IP_DEFRAG_VS_OUT = 196611, IP_DEFRAG_VS_FWD = 196612, IP_DEFRAG_AF_PACKET = 196613, IP_DEFRAG_MACVLAN = 196614, }; enum { INET_FRAG_FIRST_IN = 1, INET_FRAG_LAST_IN = 2, INET_FRAG_COMPLETE = 4, INET_FRAG_HASH_DEAD = 8, }; struct ipq { struct inet_frag_queue q; u8 ecn; u16 max_df_size; int iif; unsigned int rid; struct inet_peer *peer; }; struct ip_options_data { struct ip_options_rcu opt; char data[40]; }; struct ipcm_cookie { struct sockcm_cookie sockc; __be32 addr; int oif; struct ip_options_rcu *opt; __u8 ttl; __s16 tos; char priority; __u16 gso_size; }; struct ip_fraglist_iter { struct sk_buff *frag; struct iphdr *iph; int offset; unsigned int hlen; }; struct ip_frag_state { bool DF; unsigned int hlen; unsigned int ll_rs; unsigned int mtu; unsigned int left; int offset; int ptr; __be16 not_last_frag; }; struct ip_reply_arg { struct kvec iov[1]; int flags; __wsum csum; int csumoffset; int bound_dev_if; u8 tos; kuid_t uid; }; struct ip_mreq_source { __be32 imr_multiaddr; __be32 imr_interface; __be32 imr_sourceaddr; }; struct ip_msfilter { __be32 imsf_multiaddr; __be32 imsf_interface; __u32 imsf_fmode; __u32 imsf_numsrc; __be32 imsf_slist[1]; }; struct group_req { __u32 gr_interface; struct __kernel_sockaddr_storage gr_group; }; struct group_source_req { __u32 gsr_interface; struct __kernel_sockaddr_storage gsr_group; struct __kernel_sockaddr_storage gsr_source; }; struct group_filter { __u32 gf_interface; struct __kernel_sockaddr_storage gf_group; __u32 gf_fmode; __u32 gf_numsrc; struct __kernel_sockaddr_storage gf_slist[1]; }; struct in_pktinfo { int ipi_ifindex; struct in_addr ipi_spec_dst; struct in_addr ipi_addr; }; struct compat_group_req { __u32 gr_interface; struct __kernel_sockaddr_storage gr_group; } __attribute__((packed)); struct compat_group_source_req { __u32 gsr_interface; struct __kernel_sockaddr_storage gsr_group; struct __kernel_sockaddr_storage gsr_source; } __attribute__((packed)); struct compat_group_filter { __u32 gf_interface; struct __kernel_sockaddr_storage gf_group; __u32 gf_fmode; __u32 gf_numsrc; struct __kernel_sockaddr_storage gf_slist[1]; } __attribute__((packed)); struct tcpvegas_info { __u32 tcpv_enabled; __u32 tcpv_rttcnt; __u32 tcpv_rtt; __u32 tcpv_minrtt; }; struct tcp_dctcp_info { __u16 dctcp_enabled; __u16 dctcp_ce_state; __u32 dctcp_alpha; __u32 dctcp_ab_ecn; __u32 dctcp_ab_tot; }; struct tcp_bbr_info { __u32 bbr_bw_lo; __u32 bbr_bw_hi; __u32 bbr_min_rtt; __u32 bbr_pacing_gain; __u32 bbr_cwnd_gain; }; union tcp_cc_info { struct tcpvegas_info vegas; struct tcp_dctcp_info dctcp; struct tcp_bbr_info bbr; }; enum { BPF_TCP_ESTABLISHED = 1, BPF_TCP_SYN_SENT = 2, BPF_TCP_SYN_RECV = 3, BPF_TCP_FIN_WAIT1 = 4, BPF_TCP_FIN_WAIT2 = 5, BPF_TCP_TIME_WAIT = 6, BPF_TCP_CLOSE = 7, BPF_TCP_CLOSE_WAIT = 8, BPF_TCP_LAST_ACK = 9, BPF_TCP_LISTEN = 10, BPF_TCP_CLOSING = 11, BPF_TCP_NEW_SYN_RECV = 12, BPF_TCP_MAX_STATES = 13, }; enum inet_csk_ack_state_t { ICSK_ACK_SCHED = 1, ICSK_ACK_TIMER = 2, ICSK_ACK_PUSHED = 4, ICSK_ACK_PUSHED2 = 8, ICSK_ACK_NOW = 16, }; enum { TCP_FLAG_CWR = 32768, TCP_FLAG_ECE = 16384, TCP_FLAG_URG = 8192, TCP_FLAG_ACK = 4096, TCP_FLAG_PSH = 2048, TCP_FLAG_RST = 1024, TCP_FLAG_SYN = 512, TCP_FLAG_FIN = 256, TCP_RESERVED_BITS = 15, TCP_DATA_OFFSET = 240, }; struct tcp_repair_opt { __u32 opt_code; __u32 opt_val; }; struct tcp_repair_window { __u32 snd_wl1; __u32 snd_wnd; __u32 max_window; __u32 rcv_wnd; __u32 rcv_wup; }; enum { TCP_NO_QUEUE = 0, TCP_RECV_QUEUE = 1, TCP_SEND_QUEUE = 2, TCP_QUEUES_NR = 3, }; struct tcp_info { __u8 tcpi_state; __u8 tcpi_ca_state; __u8 tcpi_retransmits; __u8 tcpi_probes; __u8 tcpi_backoff; __u8 tcpi_options; __u8 tcpi_snd_wscale: 4; __u8 tcpi_rcv_wscale: 4; __u8 tcpi_delivery_rate_app_limited: 1; __u8 tcpi_fastopen_client_fail: 2; __u32 tcpi_rto; __u32 tcpi_ato; __u32 tcpi_snd_mss; __u32 tcpi_rcv_mss; __u32 tcpi_unacked; __u32 tcpi_sacked; __u32 tcpi_lost; __u32 tcpi_retrans; __u32 tcpi_fackets; __u32 tcpi_last_data_sent; __u32 tcpi_last_ack_sent; __u32 tcpi_last_data_recv; __u32 tcpi_last_ack_recv; __u32 tcpi_pmtu; __u32 tcpi_rcv_ssthresh; __u32 tcpi_rtt; __u32 tcpi_rttvar; __u32 tcpi_snd_ssthresh; __u32 tcpi_snd_cwnd; __u32 tcpi_advmss; __u32 tcpi_reordering; __u32 tcpi_rcv_rtt; __u32 tcpi_rcv_space; __u32 tcpi_total_retrans; __u64 tcpi_pacing_rate; __u64 tcpi_max_pacing_rate; __u64 tcpi_bytes_acked; __u64 tcpi_bytes_received; __u32 tcpi_segs_out; __u32 tcpi_segs_in; __u32 tcpi_notsent_bytes; __u32 tcpi_min_rtt; __u32 tcpi_data_segs_in; __u32 tcpi_data_segs_out; __u64 tcpi_delivery_rate; __u64 tcpi_busy_time; __u64 tcpi_rwnd_limited; __u64 tcpi_sndbuf_limited; __u32 tcpi_delivered; __u32 tcpi_delivered_ce; __u64 tcpi_bytes_sent; __u64 tcpi_bytes_retrans; __u32 tcpi_dsack_dups; __u32 tcpi_reord_seen; __u32 tcpi_rcv_ooopack; __u32 tcpi_snd_wnd; }; enum { TCP_NLA_PAD = 0, TCP_NLA_BUSY = 1, TCP_NLA_RWND_LIMITED = 2, TCP_NLA_SNDBUF_LIMITED = 3, TCP_NLA_DATA_SEGS_OUT = 4, TCP_NLA_TOTAL_RETRANS = 5, TCP_NLA_PACING_RATE = 6, TCP_NLA_DELIVERY_RATE = 7, TCP_NLA_SND_CWND = 8, TCP_NLA_REORDERING = 9, TCP_NLA_MIN_RTT = 10, TCP_NLA_RECUR_RETRANS = 11, TCP_NLA_DELIVERY_RATE_APP_LMT = 12, TCP_NLA_SNDQ_SIZE = 13, TCP_NLA_CA_STATE = 14, TCP_NLA_SND_SSTHRESH = 15, TCP_NLA_DELIVERED = 16, TCP_NLA_DELIVERED_CE = 17, TCP_NLA_BYTES_SENT = 18, TCP_NLA_BYTES_RETRANS = 19, TCP_NLA_DSACK_DUPS = 20, TCP_NLA_REORD_SEEN = 21, TCP_NLA_SRTT = 22, TCP_NLA_TIMEOUT_REHASH = 23, TCP_NLA_BYTES_NOTSENT = 24, TCP_NLA_EDT = 25, TCP_NLA_TTL = 26, }; struct tcp_zerocopy_receive { __u64 address; __u32 length; __u32 recv_skip_hint; __u32 inq; __s32 err; __u64 copybuf_address; __s32 copybuf_len; __u32 flags; __u64 msg_control; __u64 msg_controllen; __u32 msg_flags; __u32 reserved; }; struct tcp_md5sig_pool { struct ahash_request *md5_req; void *scratch; }; enum tcp_chrono { TCP_CHRONO_UNSPEC = 0, TCP_CHRONO_BUSY = 1, TCP_CHRONO_RWND_LIMITED = 2, TCP_CHRONO_SNDBUF_LIMITED = 3, __TCP_CHRONO_MAX = 4, }; enum { TCP_CMSG_INQ = 1, TCP_CMSG_TS = 2, }; struct tcp_splice_state { struct pipe_inode_info *pipe; size_t len; unsigned int flags; }; enum tcp_fastopen_client_fail { TFO_STATUS_UNSPEC = 0, TFO_COOKIE_UNAVAILABLE = 1, TFO_DATA_NOT_ACKED = 2, TFO_SYN_RETRANSMITTED = 3, }; struct tcp_sack_block_wire { __be32 start_seq; __be32 end_seq; }; struct static_key_false_deferred { struct static_key_false key; long unsigned int timeout; struct delayed_work work; }; struct mptcp_ext { union { u64 data_ack; u32 data_ack32; }; u64 data_seq; u32 subflow_seq; u16 data_len; __sum16 csum; u8 use_map: 1; u8 dsn64: 1; u8 data_fin: 1; u8 use_ack: 1; u8 ack64: 1; u8 mpc_map: 1; u8 frozen: 1; u8 reset_transient: 1; u8 reset_reason: 4; u8 csum_reqd: 1; }; enum tcp_queue { TCP_FRAG_IN_WRITE_QUEUE = 0, TCP_FRAG_IN_RTX_QUEUE = 1, }; enum tcp_ca_ack_event_flags { CA_ACK_SLOWPATH = 1, CA_ACK_WIN_UPDATE = 2, CA_ACK_ECE = 4, }; struct tcp_sacktag_state { u64 first_sackt; u64 last_sackt; u32 reord; u32 sack_delivered; int flag; unsigned int mss_now; struct rate_sample *rate; }; enum pkt_hash_types { PKT_HASH_TYPE_NONE = 0, PKT_HASH_TYPE_L2 = 1, PKT_HASH_TYPE_L3 = 2, PKT_HASH_TYPE_L4 = 3, }; enum { BPF_WRITE_HDR_TCP_CURRENT_MSS = 1, BPF_WRITE_HDR_TCP_SYNACK_COOKIE = 2, }; enum tsq_flags { TSQF_THROTTLED = 1, TSQF_QUEUED = 2, TCPF_TSQ_DEFERRED = 4, TCPF_WRITE_TIMER_DEFERRED = 8, TCPF_DELACK_TIMER_DEFERRED = 16, TCPF_MTU_REDUCED_DEFERRED = 32, }; struct mptcp_rm_list { u8 ids[8]; u8 nr; }; struct mptcp_addr_info { u8 id; sa_family_t family; __be16 port; union { struct in_addr addr; struct in6_addr addr6; }; }; struct mptcp_out_options { u16 suboptions; u64 sndr_key; u64 rcvr_key; u64 ahmac; struct mptcp_addr_info addr; struct mptcp_rm_list rm_list; u8 join_id; u8 backup; u8 reset_reason: 4; u8 reset_transient: 1; u8 csum_reqd: 1; u8 allow_join_id0: 1; u32 nonce; u64 thmac; u32 token; u8 hmac[20]; struct mptcp_ext ext_copy; }; struct tcp_out_options { u16 options; u16 mss; u8 ws; u8 num_sack_blocks; u8 hash_size; u8 bpf_opt_len; __u8 *hash_location; __u32 tsval; __u32 tsecr; struct tcp_fastopen_cookie *fastopen_cookie; struct mptcp_out_options mptcp; }; struct tsq_tasklet { struct tasklet_struct tasklet; struct list_head head; }; struct tcp_md5sig { struct __kernel_sockaddr_storage tcpm_addr; __u8 tcpm_flags; __u8 tcpm_prefixlen; __u16 tcpm_keylen; int tcpm_ifindex; __u8 tcpm_key[80]; }; struct icmp_err { int errno; unsigned int fatal: 1; }; enum tcp_tw_status { TCP_TW_SUCCESS = 0, TCP_TW_RST = 1, TCP_TW_ACK = 2, TCP_TW_SYN = 3, }; struct tcp4_pseudohdr { __be32 saddr; __be32 daddr; __u8 pad; __u8 protocol; __be16 len; }; enum tcp_seq_states { TCP_SEQ_STATE_LISTENING = 0, TCP_SEQ_STATE_ESTABLISHED = 1, }; struct tcp_seq_afinfo { sa_family_t family; }; struct tcp_iter_state { struct seq_net_private p; enum tcp_seq_states state; struct sock *syn_wait_sk; struct tcp_seq_afinfo *bpf_seq_afinfo; int bucket; int offset; int sbucket; int num; loff_t last_pos; }; struct bpf_iter__tcp { union { struct bpf_iter_meta *meta; }; union { struct sock_common *sk_common; }; uid_t uid; }; enum tcp_metric_index { TCP_METRIC_RTT = 0, TCP_METRIC_RTTVAR = 1, TCP_METRIC_SSTHRESH = 2, TCP_METRIC_CWND = 3, TCP_METRIC_REORDERING = 4, TCP_METRIC_RTT_US = 5, TCP_METRIC_RTTVAR_US = 6, __TCP_METRIC_MAX = 7, }; enum { TCP_METRICS_ATTR_UNSPEC = 0, TCP_METRICS_ATTR_ADDR_IPV4 = 1, TCP_METRICS_ATTR_ADDR_IPV6 = 2, TCP_METRICS_ATTR_AGE = 3, TCP_METRICS_ATTR_TW_TSVAL = 4, TCP_METRICS_ATTR_TW_TS_STAMP = 5, TCP_METRICS_ATTR_VALS = 6, TCP_METRICS_ATTR_FOPEN_MSS = 7, TCP_METRICS_ATTR_FOPEN_SYN_DROPS = 8, TCP_METRICS_ATTR_FOPEN_SYN_DROP_TS = 9, TCP_METRICS_ATTR_FOPEN_COOKIE = 10, TCP_METRICS_ATTR_SADDR_IPV4 = 11, TCP_METRICS_ATTR_SADDR_IPV6 = 12, TCP_METRICS_ATTR_PAD = 13, __TCP_METRICS_ATTR_MAX = 14, }; enum { TCP_METRICS_CMD_UNSPEC = 0, TCP_METRICS_CMD_GET = 1, TCP_METRICS_CMD_DEL = 2, __TCP_METRICS_CMD_MAX = 3, }; struct tcp_fastopen_metrics { u16 mss; u16 syn_loss: 10; u16 try_exp: 2; long unsigned int last_syn_loss; struct tcp_fastopen_cookie cookie; }; struct tcp_metrics_block { struct tcp_metrics_block *tcpm_next; possible_net_t tcpm_net; struct inetpeer_addr tcpm_saddr; struct inetpeer_addr tcpm_daddr; long unsigned int tcpm_stamp; u32 tcpm_lock; u32 tcpm_vals[5]; struct tcp_fastopen_metrics tcpm_fastopen; struct callback_head callback_head; }; struct tcpm_hash_bucket { struct tcp_metrics_block *chain; }; struct icmp_filter { __u32 data; }; struct raw_iter_state { struct seq_net_private p; int bucket; }; struct raw_sock { struct inet_sock inet; struct icmp_filter filter; u32 ipmr_table; }; struct raw_frag_vec { struct msghdr *msg; union { struct icmphdr icmph; char c[1]; } hdr; int hlen; }; struct ip_tunnel_encap { u16 type; u16 flags; __be16 sport; __be16 dport; }; struct ip_tunnel_encap_ops { size_t (*encap_hlen)(struct ip_tunnel_encap *); int (*build_header)(struct sk_buff *, struct ip_tunnel_encap *, u8 *, struct flowi4 *); int (*err_handler)(struct sk_buff *, u32); }; struct udp_skb_cb { union { struct inet_skb_parm h4; struct inet6_skb_parm h6; } header; __u16 cscov; __u8 partial_cov; }; struct udp_dev_scratch { u32 _tsize_state; u16 len; bool is_linear; bool csum_unnecessary; }; struct udp_seq_afinfo { sa_family_t family; struct udp_table *udp_table; }; struct udp_iter_state { struct seq_net_private p; int bucket; struct udp_seq_afinfo *bpf_seq_afinfo; }; struct bpf_iter__udp { union { struct bpf_iter_meta *meta; }; union { struct udp_sock *udp_sk; }; uid_t uid; int: 32; int bucket; }; struct inet_protosw { struct list_head list; short unsigned int type; short unsigned int protocol; struct proto *prot; const struct proto_ops *ops; unsigned char flags; }; typedef struct sk_buff * (*gro_receive_t)(struct list_head *, struct sk_buff *); typedef struct sk_buff * (*gro_receive_sk_t)(struct sock *, struct list_head *, struct sk_buff *); typedef struct sock * (*udp_lookup_t)(const struct sk_buff *, __be16, __be16); struct arpreq { struct sockaddr arp_pa; struct sockaddr arp_ha; int arp_flags; struct sockaddr arp_netmask; char arp_dev[16]; }; typedef struct { char ax25_call[7]; } ax25_address; enum { AX25_VALUES_IPDEFMODE = 0, AX25_VALUES_AXDEFMODE = 1, AX25_VALUES_BACKOFF = 2, AX25_VALUES_CONMODE = 3, AX25_VALUES_WINDOW = 4, AX25_VALUES_EWINDOW = 5, AX25_VALUES_T1 = 6, AX25_VALUES_T2 = 7, AX25_VALUES_T3 = 8, AX25_VALUES_IDLE = 9, AX25_VALUES_N2 = 10, AX25_VALUES_PACLEN = 11, AX25_VALUES_PROTOCOL = 12, AX25_VALUES_DS_TIMEOUT = 13, AX25_MAX_VALUES = 14, }; enum ip_conntrack_status { IPS_EXPECTED_BIT = 0, IPS_EXPECTED = 1, IPS_SEEN_REPLY_BIT = 1, IPS_SEEN_REPLY = 2, IPS_ASSURED_BIT = 2, IPS_ASSURED = 4, IPS_CONFIRMED_BIT = 3, IPS_CONFIRMED = 8, IPS_SRC_NAT_BIT = 4, IPS_SRC_NAT = 16, IPS_DST_NAT_BIT = 5, IPS_DST_NAT = 32, IPS_NAT_MASK = 48, IPS_SEQ_ADJUST_BIT = 6, IPS_SEQ_ADJUST = 64, IPS_SRC_NAT_DONE_BIT = 7, IPS_SRC_NAT_DONE = 128, IPS_DST_NAT_DONE_BIT = 8, IPS_DST_NAT_DONE = 256, IPS_NAT_DONE_MASK = 384, IPS_DYING_BIT = 9, IPS_DYING = 512, IPS_FIXED_TIMEOUT_BIT = 10, IPS_FIXED_TIMEOUT = 1024, IPS_TEMPLATE_BIT = 11, IPS_TEMPLATE = 2048, IPS_UNTRACKED_BIT = 12, IPS_UNTRACKED = 4096, IPS_NAT_CLASH_BIT = 12, IPS_NAT_CLASH = 4096, IPS_HELPER_BIT = 13, IPS_HELPER = 8192, IPS_OFFLOAD_BIT = 14, IPS_OFFLOAD = 16384, IPS_HW_OFFLOAD_BIT = 15, IPS_HW_OFFLOAD = 32768, IPS_UNCHANGEABLE_MASK = 56313, __IPS_MAX_BIT = 16, }; enum { XFRM_LOOKUP_ICMP = 1, XFRM_LOOKUP_QUEUE = 2, XFRM_LOOKUP_KEEP_DST_REF = 4, }; struct icmp_ext_hdr { __u8 reserved1: 4; __u8 version: 4; __u8 reserved2; __sum16 checksum; }; struct icmp_extobj_hdr { __be16 length; __u8 class_num; __u8 class_type; }; struct icmp_ext_echo_ctype3_hdr { __be16 afi; __u8 addrlen; __u8 reserved; }; struct icmp_ext_echo_iio { struct icmp_extobj_hdr extobj_hdr; union { char name[16]; __be32 ifindex; struct { struct icmp_ext_echo_ctype3_hdr ctype3_hdr; union { __be32 ipv4_addr; struct in6_addr ipv6_addr; } ip_addr; } addr; } ident; }; struct icmp_bxm { struct sk_buff *skb; int offset; int data_len; struct { struct icmphdr icmph; __be32 times[3]; } data; int head_len; struct ip_options_data replyopts; }; struct icmp_control { bool (*handler)(struct sk_buff *); short int error; }; struct ifaddrmsg { __u8 ifa_family; __u8 ifa_prefixlen; __u8 ifa_flags; __u8 ifa_scope; __u32 ifa_index; }; enum { IFA_UNSPEC = 0, IFA_ADDRESS = 1, IFA_LOCAL = 2, IFA_LABEL = 3, IFA_BROADCAST = 4, IFA_ANYCAST = 5, IFA_CACHEINFO = 6, IFA_MULTICAST = 7, IFA_FLAGS = 8, IFA_RT_PRIORITY = 9, IFA_TARGET_NETNSID = 10, __IFA_MAX = 11, }; struct ifa_cacheinfo { __u32 ifa_prefered; __u32 ifa_valid; __u32 cstamp; __u32 tstamp; }; enum { IFLA_INET_UNSPEC = 0, IFLA_INET_CONF = 1, __IFLA_INET_MAX = 2, }; struct in_validator_info { __be32 ivi_addr; struct in_device *ivi_dev; struct netlink_ext_ack *extack; }; struct netconfmsg { __u8 ncm_family; }; enum { NETCONFA_UNSPEC = 0, NETCONFA_IFINDEX = 1, NETCONFA_FORWARDING = 2, NETCONFA_RP_FILTER = 3, NETCONFA_MC_FORWARDING = 4, NETCONFA_PROXY_NEIGH = 5, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN = 6, NETCONFA_INPUT = 7, NETCONFA_BC_FORWARDING = 8, __NETCONFA_MAX = 9, }; struct inet_fill_args { u32 portid; u32 seq; int event; unsigned int flags; int netnsid; int ifindex; }; struct devinet_sysctl_table { struct ctl_table_header *sysctl_header; struct ctl_table devinet_vars[33]; }; struct rtentry { long unsigned int rt_pad1; struct sockaddr rt_dst; struct sockaddr rt_gateway; struct sockaddr rt_genmask; short unsigned int rt_flags; short int rt_pad2; long unsigned int rt_pad3; void *rt_pad4; short int rt_metric; char *rt_dev; long unsigned int rt_mtu; long unsigned int rt_window; short unsigned int rt_irtt; }; struct pingv6_ops { int (*ipv6_recv_error)(struct sock *, struct msghdr *, int, int *); void (*ip6_datagram_recv_common_ctl)(struct sock *, struct msghdr *, struct sk_buff *); void (*ip6_datagram_recv_specific_ctl)(struct sock *, struct msghdr *, struct sk_buff *); int (*icmpv6_err_convert)(u8, u8, int *); void (*ipv6_icmp_error)(struct sock *, struct sk_buff *, int, __be16, u32, u8 *); int (*ipv6_chk_addr)(struct net *, const struct in6_addr *, const struct net_device *, int); }; struct compat_rtentry { u32 rt_pad1; struct sockaddr rt_dst; struct sockaddr rt_gateway; struct sockaddr rt_genmask; short unsigned int rt_flags; short int rt_pad2; u32 rt_pad3; unsigned char rt_tos; unsigned char rt_class; short int rt_pad4; short int rt_metric; compat_uptr_t rt_dev; u32 rt_mtu; u32 rt_window; short unsigned int rt_irtt; }; struct igmphdr { __u8 type; __u8 code; __sum16 csum; __be32 group; }; struct igmpv3_grec { __u8 grec_type; __u8 grec_auxwords; __be16 grec_nsrcs; __be32 grec_mca; __be32 grec_src[0]; }; struct igmpv3_report { __u8 type; __u8 resv1; __sum16 csum; __be16 resv2; __be16 ngrec; struct igmpv3_grec grec[0]; }; struct igmpv3_query { __u8 type; __u8 code; __sum16 csum; __be32 group; __u8 qrv: 3; __u8 suppress: 1; __u8 resv: 4; __u8 qqic; __be16 nsrcs; __be32 srcs[0]; }; struct igmp_mc_iter_state { struct seq_net_private p; struct net_device *dev; struct in_device *in_dev; }; struct igmp_mcf_iter_state { struct seq_net_private p; struct net_device *dev; struct in_device *idev; struct ip_mc_list *im; }; struct fib_config { u8 fc_dst_len; u8 fc_tos; u8 fc_protocol; u8 fc_scope; u8 fc_type; u8 fc_gw_family; u32 fc_table; __be32 fc_dst; union { __be32 fc_gw4; struct in6_addr fc_gw6; }; int fc_oif; u32 fc_flags; u32 fc_priority; __be32 fc_prefsrc; u32 fc_nh_id; struct nlattr *fc_mx; struct rtnexthop *fc_mp; int fc_mx_len; int fc_mp_len; u32 fc_flow; u32 fc_nlflags; struct nl_info fc_nlinfo; struct nlattr *fc_encap; u16 fc_encap_type; }; struct fib_result_nl { __be32 fl_addr; u32 fl_mark; unsigned char fl_tos; unsigned char fl_scope; unsigned char tb_id_in; unsigned char tb_id; unsigned char prefixlen; unsigned char nh_sel; unsigned char type; unsigned char scope; int err; }; struct fib_dump_filter { u32 table_id; bool filter_set; bool dump_routes; bool dump_exceptions; unsigned char protocol; unsigned char rt_type; unsigned int flags; struct net_device *dev; }; struct fib_nh_notifier_info { struct fib_notifier_info info; struct fib_nh *fib_nh; }; struct fib_entry_notifier_info { struct fib_notifier_info info; u32 dst; int dst_len; struct fib_info *fi; u8 tos; u8 type; u32 tb_id; }; typedef unsigned int t_key; struct key_vector { t_key key; unsigned char pos; unsigned char bits; unsigned char slen; union { struct hlist_head leaf; struct key_vector *tnode[0]; }; }; struct tnode { struct callback_head rcu; t_key empty_children; t_key full_children; struct key_vector *parent; struct key_vector kv[1]; }; struct trie_use_stats { unsigned int gets; unsigned int backtrack; unsigned int semantic_match_passed; unsigned int semantic_match_miss; unsigned int null_node_hit; unsigned int resize_node_skipped; }; struct trie_stat { unsigned int totdepth; unsigned int maxdepth; unsigned int tnodes; unsigned int leaves; unsigned int nullpointers; unsigned int prefixes; unsigned int nodesizes[32]; }; struct trie { struct key_vector kv[1]; struct trie_use_stats *stats; }; struct fib_trie_iter { struct seq_net_private p; struct fib_table *tb; struct key_vector *tnode; unsigned int index; unsigned int depth; }; struct fib_route_iter { struct seq_net_private p; struct fib_table *main_tb; struct key_vector *tnode; loff_t pos; t_key key; }; struct ipfrag_skb_cb { union { struct inet_skb_parm h4; struct inet6_skb_parm h6; }; struct sk_buff *next_frag; int frag_run_len; }; struct icmpv6_echo { __be16 identifier; __be16 sequence; }; struct icmpv6_nd_advt { __u32 reserved: 5; __u32 override: 1; __u32 solicited: 1; __u32 router: 1; __u32 reserved2: 24; }; struct icmpv6_nd_ra { __u8 hop_limit; __u8 reserved: 3; __u8 router_pref: 2; __u8 home_agent: 1; __u8 other: 1; __u8 managed: 1; __be16 rt_lifetime; }; struct icmp6hdr { __u8 icmp6_type; __u8 icmp6_code; __sum16 icmp6_cksum; union { __be32 un_data32[1]; __be16 un_data16[2]; __u8 un_data8[4]; struct icmpv6_echo u_echo; struct icmpv6_nd_advt u_nd_advt; struct icmpv6_nd_ra u_nd_ra; } icmp6_dataun; }; struct ping_iter_state { struct seq_net_private p; int bucket; sa_family_t family; }; struct pingfakehdr { struct icmphdr icmph; struct msghdr *msg; sa_family_t family; __wsum wcheck; }; struct ping_table { struct hlist_nulls_head hash[64]; rwlock_t lock; }; enum lwtunnel_ip_t { LWTUNNEL_IP_UNSPEC = 0, LWTUNNEL_IP_ID = 1, LWTUNNEL_IP_DST = 2, LWTUNNEL_IP_SRC = 3, LWTUNNEL_IP_TTL = 4, LWTUNNEL_IP_TOS = 5, LWTUNNEL_IP_FLAGS = 6, LWTUNNEL_IP_PAD = 7, LWTUNNEL_IP_OPTS = 8, __LWTUNNEL_IP_MAX = 9, }; enum lwtunnel_ip6_t { LWTUNNEL_IP6_UNSPEC = 0, LWTUNNEL_IP6_ID = 1, LWTUNNEL_IP6_DST = 2, LWTUNNEL_IP6_SRC = 3, LWTUNNEL_IP6_HOPLIMIT = 4, LWTUNNEL_IP6_TC = 5, LWTUNNEL_IP6_FLAGS = 6, LWTUNNEL_IP6_PAD = 7, LWTUNNEL_IP6_OPTS = 8, __LWTUNNEL_IP6_MAX = 9, }; enum { LWTUNNEL_IP_OPTS_UNSPEC = 0, LWTUNNEL_IP_OPTS_GENEVE = 1, LWTUNNEL_IP_OPTS_VXLAN = 2, LWTUNNEL_IP_OPTS_ERSPAN = 3, __LWTUNNEL_IP_OPTS_MAX = 4, }; enum { LWTUNNEL_IP_OPT_GENEVE_UNSPEC = 0, LWTUNNEL_IP_OPT_GENEVE_CLASS = 1, LWTUNNEL_IP_OPT_GENEVE_TYPE = 2, LWTUNNEL_IP_OPT_GENEVE_DATA = 3, __LWTUNNEL_IP_OPT_GENEVE_MAX = 4, }; enum { LWTUNNEL_IP_OPT_VXLAN_UNSPEC = 0, LWTUNNEL_IP_OPT_VXLAN_GBP = 1, __LWTUNNEL_IP_OPT_VXLAN_MAX = 2, }; enum { LWTUNNEL_IP_OPT_ERSPAN_UNSPEC = 0, LWTUNNEL_IP_OPT_ERSPAN_VER = 1, LWTUNNEL_IP_OPT_ERSPAN_INDEX = 2, LWTUNNEL_IP_OPT_ERSPAN_DIR = 3, LWTUNNEL_IP_OPT_ERSPAN_HWID = 4, __LWTUNNEL_IP_OPT_ERSPAN_MAX = 5, }; struct ip6_tnl_encap_ops { size_t (*encap_hlen)(struct ip_tunnel_encap *); int (*build_header)(struct sk_buff *, struct ip_tunnel_encap *, u8 *, struct flowi6 *); int (*err_handler)(struct sk_buff *, struct inet6_skb_parm *, u8, u8, int, __be32); }; struct geneve_opt { __be16 opt_class; u8 type; u8 length: 5; u8 r3: 1; u8 r2: 1; u8 r1: 1; u8 opt_data[0]; }; struct vxlan_metadata { u32 gbp; }; struct erspan_md2 { __be32 timestamp; __be16 sgt; __u8 hwid_upper: 2; __u8 ft: 5; __u8 p: 1; __u8 o: 1; __u8 gra: 2; __u8 dir: 1; __u8 hwid: 4; }; struct erspan_metadata { int version; union { __be32 index; struct erspan_md2 md2; } u; }; struct nhmsg { unsigned char nh_family; unsigned char nh_scope; unsigned char nh_protocol; unsigned char resvd; unsigned int nh_flags; }; struct nexthop_grp { __u32 id; __u8 weight; __u8 resvd1; __u16 resvd2; }; enum { NEXTHOP_GRP_TYPE_MPATH = 0, NEXTHOP_GRP_TYPE_RES = 1, __NEXTHOP_GRP_TYPE_MAX = 2, }; enum { NHA_UNSPEC = 0, NHA_ID = 1, NHA_GROUP = 2, NHA_GROUP_TYPE = 3, NHA_BLACKHOLE = 4, NHA_OIF = 5, NHA_GATEWAY = 6, NHA_ENCAP_TYPE = 7, NHA_ENCAP = 8, NHA_GROUPS = 9, NHA_MASTER = 10, NHA_FDB = 11, NHA_RES_GROUP = 12, NHA_RES_BUCKET = 13, __NHA_MAX = 14, }; enum { NHA_RES_GROUP_UNSPEC = 0, NHA_RES_GROUP_PAD = 0, NHA_RES_GROUP_BUCKETS = 1, NHA_RES_GROUP_IDLE_TIMER = 2, NHA_RES_GROUP_UNBALANCED_TIMER = 3, NHA_RES_GROUP_UNBALANCED_TIME = 4, __NHA_RES_GROUP_MAX = 5, }; enum { NHA_RES_BUCKET_UNSPEC = 0, NHA_RES_BUCKET_PAD = 0, NHA_RES_BUCKET_INDEX = 1, NHA_RES_BUCKET_IDLE_TIME = 2, NHA_RES_BUCKET_NH_ID = 3, __NHA_RES_BUCKET_MAX = 4, }; struct nh_config { u32 nh_id; u8 nh_family; u8 nh_protocol; u8 nh_blackhole; u8 nh_fdb; u32 nh_flags; int nh_ifindex; struct net_device *dev; union { __be32 ipv4; struct in6_addr ipv6; } gw; struct nlattr *nh_grp; u16 nh_grp_type; u16 nh_grp_res_num_buckets; long unsigned int nh_grp_res_idle_timer; long unsigned int nh_grp_res_unbalanced_timer; bool nh_grp_res_has_num_buckets; bool nh_grp_res_has_idle_timer; bool nh_grp_res_has_unbalanced_timer; struct nlattr *nh_encap; u16 nh_encap_type; u32 nlflags; struct nl_info nlinfo; }; enum nexthop_event_type { NEXTHOP_EVENT_DEL = 0, NEXTHOP_EVENT_REPLACE = 1, NEXTHOP_EVENT_RES_TABLE_PRE_REPLACE = 2, NEXTHOP_EVENT_BUCKET_REPLACE = 3, }; enum nh_notifier_info_type { NH_NOTIFIER_INFO_TYPE_SINGLE = 0, NH_NOTIFIER_INFO_TYPE_GRP = 1, NH_NOTIFIER_INFO_TYPE_RES_TABLE = 2, NH_NOTIFIER_INFO_TYPE_RES_BUCKET = 3, }; struct nh_notifier_single_info { struct net_device *dev; u8 gw_family; union { __be32 ipv4; struct in6_addr ipv6; }; u8 is_reject: 1; u8 is_fdb: 1; u8 has_encap: 1; }; struct nh_notifier_grp_entry_info { u8 weight; u32 id; struct nh_notifier_single_info nh; }; struct nh_notifier_grp_info { u16 num_nh; bool is_fdb; struct nh_notifier_grp_entry_info nh_entries[0]; }; struct nh_notifier_res_bucket_info { u16 bucket_index; unsigned int idle_timer_ms; bool force; struct nh_notifier_single_info old_nh; struct nh_notifier_single_info new_nh; }; struct nh_notifier_res_table_info { u16 num_nh_buckets; struct nh_notifier_single_info nhs[0]; }; struct nh_notifier_info { struct net *net; struct netlink_ext_ack *extack; u32 id; enum nh_notifier_info_type type; union { struct nh_notifier_single_info *nh; struct nh_notifier_grp_info *nh_grp; struct nh_notifier_res_table_info *nh_res_table; struct nh_notifier_res_bucket_info *nh_res_bucket; }; }; struct nh_dump_filter { u32 nh_id; int dev_idx; int master_idx; bool group_filter; bool fdb_filter; u32 res_bucket_nh_id; }; struct rtm_dump_nh_ctx { u32 idx; }; struct rtm_dump_res_bucket_ctx { struct rtm_dump_nh_ctx nh; u16 bucket_index; u32 done_nh_idx; }; struct rtm_dump_nexthop_bucket_data { struct rtm_dump_res_bucket_ctx *ctx; struct nh_dump_filter filter; }; struct inet6_protocol { void (*early_demux)(struct sk_buff *); void (*early_demux_handler)(struct sk_buff *); int (*handler)(struct sk_buff *); int (*err_handler)(struct sk_buff *, struct inet6_skb_parm *, u8, u8, int, __be32); unsigned int flags; }; struct snmp_mib { const char *name; int entry; }; struct fib4_rule { struct fib_rule common; u8 dst_len; u8 src_len; u8 tos; __be32 src; __be32 srcmask; __be32 dst; __be32 dstmask; u32 tclassid; }; enum { PIM_TYPE_HELLO = 0, PIM_TYPE_REGISTER = 1, PIM_TYPE_REGISTER_STOP = 2, PIM_TYPE_JOIN_PRUNE = 3, PIM_TYPE_BOOTSTRAP = 4, PIM_TYPE_ASSERT = 5, PIM_TYPE_GRAFT = 6, PIM_TYPE_GRAFT_ACK = 7, PIM_TYPE_CANDIDATE_RP_ADV = 8, }; struct pimreghdr { __u8 type; __u8 reserved; __be16 csum; __be32 flags; }; typedef short unsigned int vifi_t; struct vifctl { vifi_t vifc_vifi; unsigned char vifc_flags; unsigned char vifc_threshold; unsigned int vifc_rate_limit; union { struct in_addr vifc_lcl_addr; int vifc_lcl_ifindex; }; struct in_addr vifc_rmt_addr; }; struct mfcctl { struct in_addr mfcc_origin; struct in_addr mfcc_mcastgrp; vifi_t mfcc_parent; unsigned char mfcc_ttls[32]; unsigned int mfcc_pkt_cnt; unsigned int mfcc_byte_cnt; unsigned int mfcc_wrong_if; int mfcc_expire; }; struct sioc_sg_req { struct in_addr src; struct in_addr grp; long unsigned int pktcnt; long unsigned int bytecnt; long unsigned int wrong_if; }; struct sioc_vif_req { vifi_t vifi; long unsigned int icount; long unsigned int ocount; long unsigned int ibytes; long unsigned int obytes; }; struct igmpmsg { __u32 unused1; __u32 unused2; unsigned char im_msgtype; unsigned char im_mbz; unsigned char im_vif; unsigned char im_vif_hi; struct in_addr im_src; struct in_addr im_dst; }; enum { IPMRA_TABLE_UNSPEC = 0, IPMRA_TABLE_ID = 1, IPMRA_TABLE_CACHE_RES_QUEUE_LEN = 2, IPMRA_TABLE_MROUTE_REG_VIF_NUM = 3, IPMRA_TABLE_MROUTE_DO_ASSERT = 4, IPMRA_TABLE_MROUTE_DO_PIM = 5, IPMRA_TABLE_VIFS = 6, IPMRA_TABLE_MROUTE_DO_WRVIFWHOLE = 7, __IPMRA_TABLE_MAX = 8, }; enum { IPMRA_VIF_UNSPEC = 0, IPMRA_VIF = 1, __IPMRA_VIF_MAX = 2, }; enum { IPMRA_VIFA_UNSPEC = 0, IPMRA_VIFA_IFINDEX = 1, IPMRA_VIFA_VIF_ID = 2, IPMRA_VIFA_FLAGS = 3, IPMRA_VIFA_BYTES_IN = 4, IPMRA_VIFA_BYTES_OUT = 5, IPMRA_VIFA_PACKETS_IN = 6, IPMRA_VIFA_PACKETS_OUT = 7, IPMRA_VIFA_LOCAL_ADDR = 8, IPMRA_VIFA_REMOTE_ADDR = 9, IPMRA_VIFA_PAD = 10, __IPMRA_VIFA_MAX = 11, }; enum { IPMRA_CREPORT_UNSPEC = 0, IPMRA_CREPORT_MSGTYPE = 1, IPMRA_CREPORT_VIF_ID = 2, IPMRA_CREPORT_SRC_ADDR = 3, IPMRA_CREPORT_DST_ADDR = 4, IPMRA_CREPORT_PKT = 5, IPMRA_CREPORT_TABLE = 6, __IPMRA_CREPORT_MAX = 7, }; struct vif_device { struct net_device *dev; long unsigned int bytes_in; long unsigned int bytes_out; long unsigned int pkt_in; long unsigned int pkt_out; long unsigned int rate_limit; unsigned char threshold; short unsigned int flags; int link; struct netdev_phys_item_id dev_parent_id; __be32 local; __be32 remote; }; struct vif_entry_notifier_info { struct fib_notifier_info info; struct net_device *dev; short unsigned int vif_index; short unsigned int vif_flags; u32 tb_id; }; enum { MFC_STATIC = 1, MFC_OFFLOAD = 2, }; struct mr_mfc { struct rhlist_head mnode; short unsigned int mfc_parent; int mfc_flags; union { struct { long unsigned int expires; struct sk_buff_head unresolved; } unres; struct { long unsigned int last_assert; int minvif; int maxvif; long unsigned int bytes; long unsigned int pkt; long unsigned int wrong_if; long unsigned int lastuse; unsigned char ttls[32]; refcount_t refcount; } res; } mfc_un; struct list_head list; struct callback_head rcu; void (*free)(struct callback_head *); }; struct mfc_entry_notifier_info { struct fib_notifier_info info; struct mr_mfc *mfc; u32 tb_id; }; struct mr_table_ops { const struct rhashtable_params *rht_params; void *cmparg_any; }; struct mr_table { struct list_head list; possible_net_t net; struct mr_table_ops ops; u32 id; struct sock *mroute_sk; struct timer_list ipmr_expire_timer; struct list_head mfc_unres_queue; struct vif_device vif_table[32]; struct rhltable mfc_hash; struct list_head mfc_cache_list; int maxvif; atomic_t cache_resolve_queue_len; bool mroute_do_assert; bool mroute_do_pim; bool mroute_do_wrvifwhole; int mroute_reg_vif_num; }; struct mr_vif_iter { struct seq_net_private p; struct mr_table *mrt; int ct; }; struct mr_mfc_iter { struct seq_net_private p; struct mr_table *mrt; struct list_head *cache; spinlock_t *lock; }; struct mfc_cache_cmp_arg { __be32 mfc_mcastgrp; __be32 mfc_origin; }; struct mfc_cache { struct mr_mfc _c; union { struct { __be32 mfc_mcastgrp; __be32 mfc_origin; }; struct mfc_cache_cmp_arg cmparg; }; }; struct ipmr_result { struct mr_table *mrt; }; struct compat_sioc_sg_req { struct in_addr src; struct in_addr grp; compat_ulong_t pktcnt; compat_ulong_t bytecnt; compat_ulong_t wrong_if; }; struct compat_sioc_vif_req { vifi_t vifi; compat_ulong_t icount; compat_ulong_t ocount; compat_ulong_t ibytes; compat_ulong_t obytes; }; struct rta_mfc_stats { __u64 mfcs_packets; __u64 mfcs_bytes; __u64 mfcs_wrong_if; }; struct bictcp { u32 cnt; u32 last_max_cwnd; u32 last_cwnd; u32 last_time; u32 bic_origin_point; u32 bic_K; u32 delay_min; u32 epoch_start; u32 ack_cnt; u32 tcp_cwnd; u16 unused; u8 sample_cnt; u8 found; u32 round_start; u32 end_seq; u32 last_ack; u32 curr_rtt; }; struct tls_rec { struct list_head list; int tx_ready; int tx_flags; struct sk_msg msg_plaintext; struct sk_msg msg_encrypted; struct scatterlist sg_aead_in[2]; struct scatterlist sg_aead_out[2]; char content_type; struct scatterlist sg_content_type; char aad_space[13]; u8 iv_data[16]; struct aead_request aead_req; u8 aead_req_ctx[0]; }; struct tx_work { struct delayed_work work; struct sock *sk; }; struct tls_sw_context_tx { struct crypto_aead *aead_send; struct crypto_wait async_wait; struct tx_work tx_work; struct tls_rec *open_rec; struct list_head tx_list; atomic_t encrypt_pending; spinlock_t encrypt_compl_lock; int async_notify; u8 async_capable: 1; long unsigned int tx_bitmask; }; enum { TCP_BPF_IPV4 = 0, TCP_BPF_IPV6 = 1, TCP_BPF_NUM_PROTS = 2, }; enum { TCP_BPF_BASE = 0, TCP_BPF_TX = 1, TCP_BPF_NUM_CFGS = 2, }; enum { UDP_BPF_IPV4 = 0, UDP_BPF_IPV6 = 1, UDP_BPF_NUM_PROTS = 2, }; struct cipso_v4_map_cache_bkt { spinlock_t lock; u32 size; struct list_head list; }; struct cipso_v4_map_cache_entry { u32 hash; unsigned char *key; size_t key_len; struct netlbl_lsm_cache *lsm_data; u32 activity; struct list_head list; }; struct xfrm_policy_afinfo { struct dst_ops *dst_ops; struct dst_entry * (*dst_lookup)(struct net *, int, int, const xfrm_address_t *, const xfrm_address_t *, u32); int (*get_saddr)(struct net *, int, xfrm_address_t *, xfrm_address_t *, u32); int (*fill_dst)(struct xfrm_dst *, struct net_device *, const struct flowi *); struct dst_entry * (*blackhole_route)(struct net *, struct dst_entry *); }; struct xfrm_state_afinfo { u8 family; u8 proto; const struct xfrm_type_offload *type_offload_esp; const struct xfrm_type *type_esp; const struct xfrm_type *type_ipip; const struct xfrm_type *type_ipip6; const struct xfrm_type *type_comp; const struct xfrm_type *type_ah; const struct xfrm_type *type_routing; const struct xfrm_type *type_dstopts; int (*output)(struct net *, struct sock *, struct sk_buff *); int (*transport_finish)(struct sk_buff *, int); void (*local_error)(struct sk_buff *, u32); }; struct ip_tunnel; struct ip6_tnl; struct xfrm_tunnel_skb_cb { union { struct inet_skb_parm h4; struct inet6_skb_parm h6; } header; union { struct ip_tunnel *ip4; struct ip6_tnl *ip6; } tunnel; }; struct xfrm_mode_skb_cb { struct xfrm_tunnel_skb_cb header; __be16 id; __be16 frag_off; u8 ihl; u8 tos; u8 ttl; u8 protocol; u8 optlen; u8 flow_lbl[3]; }; struct xfrm_spi_skb_cb { struct xfrm_tunnel_skb_cb header; unsigned int daddroff; unsigned int family; __be32 seq; }; struct xfrm_input_afinfo { u8 family; bool is_ipip; int (*callback)(struct sk_buff *, u8, int); }; struct xfrm4_protocol { int (*handler)(struct sk_buff *); int (*input_handler)(struct sk_buff *, int, __be32, int); int (*cb_handler)(struct sk_buff *, int); int (*err_handler)(struct sk_buff *, u32); struct xfrm4_protocol *next; int priority; }; typedef u64 (*btf_bpf_tcp_send_ack)(struct tcp_sock *, u32); enum { XFRM_STATE_VOID = 0, XFRM_STATE_ACQ = 1, XFRM_STATE_VALID = 2, XFRM_STATE_ERROR = 3, XFRM_STATE_EXPIRED = 4, XFRM_STATE_DEAD = 5, }; struct xfrm_if; struct xfrm_if_cb { struct xfrm_if * (*decode_session)(struct sk_buff *, short unsigned int); }; struct xfrm_if_parms { int link; u32 if_id; }; struct xfrm_if { struct xfrm_if *next; struct net_device *dev; struct net *net; struct xfrm_if_parms p; struct gro_cells gro_cells; }; struct xfrm_policy_walk { struct xfrm_policy_walk_entry walk; u8 type; u32 seq; }; struct xfrm_kmaddress { xfrm_address_t local; xfrm_address_t remote; u32 reserved; u16 family; }; struct xfrm_migrate { xfrm_address_t old_daddr; xfrm_address_t old_saddr; xfrm_address_t new_daddr; xfrm_address_t new_saddr; u8 proto; u8 mode; u16 reserved; u32 reqid; u16 old_family; u16 new_family; }; struct xfrmk_spdinfo { u32 incnt; u32 outcnt; u32 fwdcnt; u32 inscnt; u32 outscnt; u32 fwdscnt; u32 spdhcnt; u32 spdhmcnt; }; struct ip6_mh { __u8 ip6mh_proto; __u8 ip6mh_hdrlen; __u8 ip6mh_type; __u8 ip6mh_reserved; __u16 ip6mh_cksum; __u8 data[0]; }; struct xfrm_flo { struct dst_entry *dst_orig; u8 flags; }; struct xfrm_pol_inexact_node { struct rb_node node; union { xfrm_address_t addr; struct callback_head rcu; }; u8 prefixlen; struct rb_root root; struct hlist_head hhead; }; struct xfrm_pol_inexact_key { possible_net_t net; u32 if_id; u16 family; u8 dir; u8 type; }; struct xfrm_pol_inexact_bin { struct xfrm_pol_inexact_key k; struct rhash_head head; struct hlist_head hhead; seqcount_spinlock_t count; struct rb_root root_d; struct rb_root root_s; struct list_head inexact_bins; struct callback_head rcu; }; enum xfrm_pol_inexact_candidate_type { XFRM_POL_CAND_BOTH = 0, XFRM_POL_CAND_SADDR = 1, XFRM_POL_CAND_DADDR = 2, XFRM_POL_CAND_ANY = 3, XFRM_POL_CAND_MAX = 4, }; struct xfrm_pol_inexact_candidates { struct hlist_head *res[4]; }; enum xfrm_ae_ftype_t { XFRM_AE_UNSPEC = 0, XFRM_AE_RTHR = 1, XFRM_AE_RVAL = 2, XFRM_AE_LVAL = 4, XFRM_AE_ETHR = 8, XFRM_AE_CR = 16, XFRM_AE_CE = 32, XFRM_AE_CU = 64, __XFRM_AE_MAX = 65, }; enum xfrm_nlgroups { XFRMNLGRP_NONE = 0, XFRMNLGRP_ACQUIRE = 1, XFRMNLGRP_EXPIRE = 2, XFRMNLGRP_SA = 3, XFRMNLGRP_POLICY = 4, XFRMNLGRP_AEVENTS = 5, XFRMNLGRP_REPORT = 6, XFRMNLGRP_MIGRATE = 7, XFRMNLGRP_MAPPING = 8, __XFRMNLGRP_MAX = 9, }; enum { XFRM_MODE_FLAG_TUNNEL = 1, }; struct km_event { union { u32 hard; u32 proto; u32 byid; u32 aevent; u32 type; } data; u32 seq; u32 portid; u32 event; struct net *net; }; struct xfrm_mgr { struct list_head list; int (*notify)(struct xfrm_state *, const struct km_event *); int (*acquire)(struct xfrm_state *, struct xfrm_tmpl *, struct xfrm_policy *); struct xfrm_policy * (*compile_policy)(struct sock *, int, u8 *, int, int *); int (*new_mapping)(struct xfrm_state *, xfrm_address_t *, __be16); int (*notify_policy)(struct xfrm_policy *, int, const struct km_event *); int (*report)(struct net *, u8, struct xfrm_selector *, xfrm_address_t *); int (*migrate)(const struct xfrm_selector *, u8, u8, const struct xfrm_migrate *, int, const struct xfrm_kmaddress *, const struct xfrm_encap_tmpl *); bool (*is_alive)(const struct km_event *); }; struct xfrmk_sadinfo { u32 sadhcnt; u32 sadhmcnt; u32 sadcnt; }; struct xfrm_translator { int (*alloc_compat)(struct sk_buff *, const struct nlmsghdr *); struct nlmsghdr * (*rcv_msg_compat)(const struct nlmsghdr *, int, const struct nla_policy *, struct netlink_ext_ack *); int (*xlate_user_policy_sockptr)(u8 **, int); struct module *owner; }; struct ip_beet_phdr { __u8 nexthdr; __u8 hdrlen; __u8 padlen; __u8 reserved; }; struct ip_tunnel_6rd_parm { struct in6_addr prefix; __be32 relay_prefix; u16 prefixlen; u16 relay_prefixlen; }; struct ip_tunnel_prl_entry; struct ip_tunnel { struct ip_tunnel *next; struct hlist_node hash_node; struct net_device *dev; struct net *net; long unsigned int err_time; int err_count; u32 i_seqno; u32 o_seqno; int tun_hlen; u32 index; u8 erspan_ver; u8 dir; u16 hwid; struct dst_cache dst_cache; struct ip_tunnel_parm parms; int mlink; int encap_hlen; int hlen; struct ip_tunnel_encap encap; struct ip_tunnel_6rd_parm ip6rd; struct ip_tunnel_prl_entry *prl; unsigned int prl_count; unsigned int ip_tnl_net_id; struct gro_cells gro_cells; __u32 fwmark; bool collect_md; bool ignore_df; }; struct __ip6_tnl_parm { char name[16]; int link; __u8 proto; __u8 encap_limit; __u8 hop_limit; bool collect_md; __be32 flowinfo; __u32 flags; struct in6_addr laddr; struct in6_addr raddr; __be16 i_flags; __be16 o_flags; __be32 i_key; __be32 o_key; __u32 fwmark; __u32 index; __u8 erspan_ver; __u8 dir; __u16 hwid; }; struct ip6_tnl { struct ip6_tnl *next; struct net_device *dev; struct net *net; struct __ip6_tnl_parm parms; struct flowi fl; struct dst_cache dst_cache; struct gro_cells gro_cells; int err_count; long unsigned int err_time; __u32 i_seqno; __u32 o_seqno; int hlen; int tun_hlen; int encap_hlen; struct ip_tunnel_encap encap; int mlink; }; struct xfrm_skb_cb { struct xfrm_tunnel_skb_cb header; union { struct { __u32 low; __u32 hi; } output; struct { __be32 low; __be32 hi; } input; } seq; }; struct ip_tunnel_prl_entry { struct ip_tunnel_prl_entry *next; __be32 addr; u16 flags; struct callback_head callback_head; }; struct xfrm_trans_tasklet { struct tasklet_struct tasklet; struct sk_buff_head queue; }; struct xfrm_trans_cb { union { struct inet_skb_parm h4; struct inet6_skb_parm h6; } header; int (*finish)(struct net *, struct sock *, struct sk_buff *); struct net *net; }; struct xfrm_user_offload { int ifindex; __u8 flags; }; struct sadb_alg { __u8 sadb_alg_id; __u8 sadb_alg_ivlen; __u16 sadb_alg_minbits; __u16 sadb_alg_maxbits; __u16 sadb_alg_reserved; }; struct xfrm_algo_aead_info { char *geniv; u16 icv_truncbits; }; struct xfrm_algo_auth_info { u16 icv_truncbits; u16 icv_fullbits; }; struct xfrm_algo_encr_info { char *geniv; u16 blockbits; u16 defkeybits; }; struct xfrm_algo_comp_info { u16 threshold; }; struct xfrm_algo_desc { char *name; char *compat; u8 available: 1; u8 pfkey_supported: 1; union { struct xfrm_algo_aead_info aead; struct xfrm_algo_auth_info auth; struct xfrm_algo_encr_info encr; struct xfrm_algo_comp_info comp; } uinfo; struct sadb_alg desc; }; struct xfrm_algo_list { struct xfrm_algo_desc *algs; int entries; u32 type; u32 mask; }; struct xfrm_aead_name { const char *name; int icvbits; }; enum { XFRM_SHARE_ANY = 0, XFRM_SHARE_SESSION = 1, XFRM_SHARE_USER = 2, XFRM_SHARE_UNIQUE = 3, }; struct xfrm_user_tmpl { struct xfrm_id id; __u16 family; xfrm_address_t saddr; __u32 reqid; __u8 mode; __u8 share; __u8 optional; __u32 aalgos; __u32 ealgos; __u32 calgos; }; struct xfrm_userpolicy_type { __u8 type; __u16 reserved1; __u8 reserved2; }; enum xfrm_sadattr_type_t { XFRMA_SAD_UNSPEC = 0, XFRMA_SAD_CNT = 1, XFRMA_SAD_HINFO = 2, __XFRMA_SAD_MAX = 3, }; struct xfrmu_sadhinfo { __u32 sadhcnt; __u32 sadhmcnt; }; enum xfrm_spdattr_type_t { XFRMA_SPD_UNSPEC = 0, XFRMA_SPD_INFO = 1, XFRMA_SPD_HINFO = 2, XFRMA_SPD_IPV4_HTHRESH = 3, XFRMA_SPD_IPV6_HTHRESH = 4, __XFRMA_SPD_MAX = 5, }; struct xfrmu_spdinfo { __u32 incnt; __u32 outcnt; __u32 fwdcnt; __u32 inscnt; __u32 outscnt; __u32 fwdscnt; }; struct xfrmu_spdhinfo { __u32 spdhcnt; __u32 spdhmcnt; }; struct xfrmu_spdhthresh { __u8 lbits; __u8 rbits; }; struct xfrm_usersa_info { struct xfrm_selector sel; struct xfrm_id id; xfrm_address_t saddr; struct xfrm_lifetime_cfg lft; struct xfrm_lifetime_cur curlft; struct xfrm_stats stats; __u32 seq; __u32 reqid; __u16 family; __u8 mode; __u8 replay_window; __u8 flags; }; struct xfrm_usersa_id { xfrm_address_t daddr; __be32 spi; __u16 family; __u8 proto; }; struct xfrm_aevent_id { struct xfrm_usersa_id sa_id; xfrm_address_t saddr; __u32 flags; __u32 reqid; }; struct xfrm_userspi_info { struct xfrm_usersa_info info; __u32 min; __u32 max; }; struct xfrm_userpolicy_info { struct xfrm_selector sel; struct xfrm_lifetime_cfg lft; struct xfrm_lifetime_cur curlft; __u32 priority; __u32 index; __u8 dir; __u8 action; __u8 flags; __u8 share; }; struct xfrm_userpolicy_id { struct xfrm_selector sel; __u32 index; __u8 dir; }; struct xfrm_user_acquire { struct xfrm_id id; xfrm_address_t saddr; struct xfrm_selector sel; struct xfrm_userpolicy_info policy; __u32 aalgos; __u32 ealgos; __u32 calgos; __u32 seq; }; struct xfrm_user_expire { struct xfrm_usersa_info state; __u8 hard; }; struct xfrm_user_polexpire { struct xfrm_userpolicy_info pol; __u8 hard; }; struct xfrm_usersa_flush { __u8 proto; }; struct xfrm_user_report { __u8 proto; struct xfrm_selector sel; }; struct xfrm_user_kmaddress { xfrm_address_t local; xfrm_address_t remote; __u32 reserved; __u16 family; }; struct xfrm_user_migrate { xfrm_address_t old_daddr; xfrm_address_t old_saddr; xfrm_address_t new_daddr; xfrm_address_t new_saddr; __u8 proto; __u8 mode; __u16 reserved; __u32 reqid; __u16 old_family; __u16 new_family; }; struct xfrm_user_mapping { struct xfrm_usersa_id id; __u32 reqid; xfrm_address_t old_saddr; xfrm_address_t new_saddr; __be16 old_sport; __be16 new_sport; }; struct xfrm_dump_info { struct sk_buff *in_skb; struct sk_buff *out_skb; u32 nlmsg_seq; u16 nlmsg_flags; }; struct xfrm_link { int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); int (*start)(struct netlink_callback *); int (*dump)(struct sk_buff *, struct netlink_callback *); int (*done)(struct netlink_callback *); const struct nla_policy *nla_pol; int nla_max; }; struct espintcp_msg { struct sk_buff *skb; struct sk_msg skmsg; int offset; int len; }; struct espintcp_ctx { struct strparser strp; struct sk_buff_head ike_queue; struct sk_buff_head out_queue; struct espintcp_msg partial; void (*saved_data_ready)(struct sock *); void (*saved_write_space)(struct sock *); void (*saved_destruct)(struct sock *); struct work_struct work; bool tx_running; }; struct unix_stream_read_state { int (*recv_actor)(struct sk_buff *, int, int, struct unix_stream_read_state *); struct socket *socket; struct msghdr *msg; struct pipe_inode_info *pipe; size_t size; int flags; unsigned int splice_flags; }; struct ipv6_params { __s32 disable_ipv6; __s32 autoconf; }; enum flowlabel_reflect { FLOWLABEL_REFLECT_ESTABLISHED = 1, FLOWLABEL_REFLECT_TCP_RESET = 2, FLOWLABEL_REFLECT_ICMPV6_ECHO_REPLIES = 4, }; struct in6_rtmsg { struct in6_addr rtmsg_dst; struct in6_addr rtmsg_src; struct in6_addr rtmsg_gateway; __u32 rtmsg_type; __u16 rtmsg_dst_len; __u16 rtmsg_src_len; __u32 rtmsg_metric; long unsigned int rtmsg_info; __u32 rtmsg_flags; int rtmsg_ifindex; }; struct compat_in6_rtmsg { struct in6_addr rtmsg_dst; struct in6_addr rtmsg_src; struct in6_addr rtmsg_gateway; u32 rtmsg_type; u16 rtmsg_dst_len; u16 rtmsg_src_len; u32 rtmsg_metric; u32 rtmsg_info; u32 rtmsg_flags; s32 rtmsg_ifindex; }; struct ac6_iter_state { struct seq_net_private p; struct net_device *dev; struct inet6_dev *idev; }; struct ip6_fraglist_iter { struct ipv6hdr *tmp_hdr; struct sk_buff *frag; int offset; unsigned int hlen; __be32 frag_id; u8 nexthdr; }; struct ip6_frag_state { u8 *prevhdr; unsigned int hlen; unsigned int mtu; unsigned int left; int offset; int ptr; int hroom; int troom; __be32 frag_id; u8 nexthdr; }; struct ip6_ra_chain { struct ip6_ra_chain *next; struct sock *sk; int sel; void (*destructor)(struct sock *); }; struct ipcm6_cookie { struct sockcm_cookie sockc; __s16 hlimit; __s16 tclass; __s8 dontfrag; struct ipv6_txoptions *opt; __u16 gso_size; }; enum { IFLA_INET6_UNSPEC = 0, IFLA_INET6_FLAGS = 1, IFLA_INET6_CONF = 2, IFLA_INET6_STATS = 3, IFLA_INET6_MCAST = 4, IFLA_INET6_CACHEINFO = 5, IFLA_INET6_ICMP6STATS = 6, IFLA_INET6_TOKEN = 7, IFLA_INET6_ADDR_GEN_MODE = 8, __IFLA_INET6_MAX = 9, }; enum in6_addr_gen_mode { IN6_ADDR_GEN_MODE_EUI64 = 0, IN6_ADDR_GEN_MODE_NONE = 1, IN6_ADDR_GEN_MODE_STABLE_PRIVACY = 2, IN6_ADDR_GEN_MODE_RANDOM = 3, }; struct ifla_cacheinfo { __u32 max_reasm_len; __u32 tstamp; __u32 reachable_time; __u32 retrans_time; }; struct wpan_phy; struct wpan_dev_header_ops; struct wpan_dev { struct wpan_phy *wpan_phy; int iftype; struct list_head list; struct net_device *netdev; const struct wpan_dev_header_ops *header_ops; struct net_device *lowpan_dev; u32 identifier; __le16 pan_id; __le16 short_addr; __le64 extended_addr; atomic_t bsn; atomic_t dsn; u8 min_be; u8 max_be; u8 csma_retries; s8 frame_retries; bool lbt; bool promiscuous_mode; bool ackreq; }; struct prefixmsg { unsigned char prefix_family; unsigned char prefix_pad1; short unsigned int prefix_pad2; int prefix_ifindex; unsigned char prefix_type; unsigned char prefix_len; unsigned char prefix_flags; unsigned char prefix_pad3; }; enum { PREFIX_UNSPEC = 0, PREFIX_ADDRESS = 1, PREFIX_CACHEINFO = 2, __PREFIX_MAX = 3, }; struct prefix_cacheinfo { __u32 preferred_time; __u32 valid_time; }; struct in6_ifreq { struct in6_addr ifr6_addr; __u32 ifr6_prefixlen; int ifr6_ifindex; }; enum { DEVCONF_FORWARDING = 0, DEVCONF_HOPLIMIT = 1, DEVCONF_MTU6 = 2, DEVCONF_ACCEPT_RA = 3, DEVCONF_ACCEPT_REDIRECTS = 4, DEVCONF_AUTOCONF = 5, DEVCONF_DAD_TRANSMITS = 6, DEVCONF_RTR_SOLICITS = 7, DEVCONF_RTR_SOLICIT_INTERVAL = 8, DEVCONF_RTR_SOLICIT_DELAY = 9, DEVCONF_USE_TEMPADDR = 10, DEVCONF_TEMP_VALID_LFT = 11, DEVCONF_TEMP_PREFERED_LFT = 12, DEVCONF_REGEN_MAX_RETRY = 13, DEVCONF_MAX_DESYNC_FACTOR = 14, DEVCONF_MAX_ADDRESSES = 15, DEVCONF_FORCE_MLD_VERSION = 16, DEVCONF_ACCEPT_RA_DEFRTR = 17, DEVCONF_ACCEPT_RA_PINFO = 18, DEVCONF_ACCEPT_RA_RTR_PREF = 19, DEVCONF_RTR_PROBE_INTERVAL = 20, DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN = 21, DEVCONF_PROXY_NDP = 22, DEVCONF_OPTIMISTIC_DAD = 23, DEVCONF_ACCEPT_SOURCE_ROUTE = 24, DEVCONF_MC_FORWARDING = 25, DEVCONF_DISABLE_IPV6 = 26, DEVCONF_ACCEPT_DAD = 27, DEVCONF_FORCE_TLLAO = 28, DEVCONF_NDISC_NOTIFY = 29, DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL = 30, DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL = 31, DEVCONF_SUPPRESS_FRAG_NDISC = 32, DEVCONF_ACCEPT_RA_FROM_LOCAL = 33, DEVCONF_USE_OPTIMISTIC = 34, DEVCONF_ACCEPT_RA_MTU = 35, DEVCONF_STABLE_SECRET = 36, DEVCONF_USE_OIF_ADDRS_ONLY = 37, DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT = 38, DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN = 39, DEVCONF_DROP_UNICAST_IN_L2_MULTICAST = 40, DEVCONF_DROP_UNSOLICITED_NA = 41, DEVCONF_KEEP_ADDR_ON_DOWN = 42, DEVCONF_RTR_SOLICIT_MAX_INTERVAL = 43, DEVCONF_SEG6_ENABLED = 44, DEVCONF_SEG6_REQUIRE_HMAC = 45, DEVCONF_ENHANCED_DAD = 46, DEVCONF_ADDR_GEN_MODE = 47, DEVCONF_DISABLE_POLICY = 48, DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN = 49, DEVCONF_NDISC_TCLASS = 50, DEVCONF_RPL_SEG_ENABLED = 51, DEVCONF_RA_DEFRTR_METRIC = 52, DEVCONF_MAX = 53, }; enum { INET6_IFADDR_STATE_PREDAD = 0, INET6_IFADDR_STATE_DAD = 1, INET6_IFADDR_STATE_POSTDAD = 2, INET6_IFADDR_STATE_ERRDAD = 3, INET6_IFADDR_STATE_DEAD = 4, }; enum nl802154_cca_modes { __NL802154_CCA_INVALID = 0, NL802154_CCA_ENERGY = 1, NL802154_CCA_CARRIER = 2, NL802154_CCA_ENERGY_CARRIER = 3, NL802154_CCA_ALOHA = 4, NL802154_CCA_UWB_SHR = 5, NL802154_CCA_UWB_MULTIPLEXED = 6, __NL802154_CCA_ATTR_AFTER_LAST = 7, NL802154_CCA_ATTR_MAX = 6, }; enum nl802154_cca_opts { NL802154_CCA_OPT_ENERGY_CARRIER_AND = 0, NL802154_CCA_OPT_ENERGY_CARRIER_OR = 1, __NL802154_CCA_OPT_ATTR_AFTER_LAST = 2, NL802154_CCA_OPT_ATTR_MAX = 1, }; enum nl802154_supported_bool_states { NL802154_SUPPORTED_BOOL_FALSE = 0, NL802154_SUPPORTED_BOOL_TRUE = 1, __NL802154_SUPPORTED_BOOL_INVALD = 2, NL802154_SUPPORTED_BOOL_BOTH = 3, __NL802154_SUPPORTED_BOOL_AFTER_LAST = 4, NL802154_SUPPORTED_BOOL_MAX = 3, }; struct wpan_phy_supported { u32 channels[32]; u32 cca_modes; u32 cca_opts; u32 iftypes; enum nl802154_supported_bool_states lbt; u8 min_minbe; u8 max_minbe; u8 min_maxbe; u8 max_maxbe; u8 min_csma_backoffs; u8 max_csma_backoffs; s8 min_frame_retries; s8 max_frame_retries; size_t tx_powers_size; size_t cca_ed_levels_size; const s32 *tx_powers; const s32 *cca_ed_levels; }; struct wpan_phy_cca { enum nl802154_cca_modes mode; enum nl802154_cca_opts opt; }; struct wpan_phy { const void *privid; u32 flags; u8 current_channel; u8 current_page; struct wpan_phy_supported supported; s32 transmit_power; struct wpan_phy_cca cca; __le64 perm_extended_addr; s32 cca_ed_level; u8 symbol_duration; u16 lifs_period; u16 sifs_period; struct device dev; possible_net_t _net; char priv[0]; }; struct ieee802154_addr { u8 mode; __le16 pan_id; union { __le16 short_addr; __le64 extended_addr; }; }; struct wpan_dev_header_ops { int (*create)(struct sk_buff *, struct net_device *, const struct ieee802154_addr *, const struct ieee802154_addr *, unsigned int); }; union fwnet_hwaddr { u8 u[16]; struct { __be64 uniq_id; u8 max_rec; u8 sspd; __be16 fifo_hi; __be32 fifo_lo; } uc; }; struct in6_validator_info { struct in6_addr i6vi_addr; struct inet6_dev *i6vi_dev; struct netlink_ext_ack *extack; }; struct ifa6_config { const struct in6_addr *pfx; unsigned int plen; const struct in6_addr *peer_pfx; u32 rt_priority; u32 ifa_flags; u32 preferred_lft; u32 valid_lft; u16 scope; }; enum cleanup_prefix_rt_t { CLEANUP_PREFIX_RT_NOP = 0, CLEANUP_PREFIX_RT_DEL = 1, CLEANUP_PREFIX_RT_EXPIRE = 2, }; enum { IPV6_SADDR_RULE_INIT = 0, IPV6_SADDR_RULE_LOCAL = 1, IPV6_SADDR_RULE_SCOPE = 2, IPV6_SADDR_RULE_PREFERRED = 3, IPV6_SADDR_RULE_OIF = 4, IPV6_SADDR_RULE_LABEL = 5, IPV6_SADDR_RULE_PRIVACY = 6, IPV6_SADDR_RULE_ORCHID = 7, IPV6_SADDR_RULE_PREFIX = 8, IPV6_SADDR_RULE_NOT_OPTIMISTIC = 9, IPV6_SADDR_RULE_MAX = 10, }; struct ipv6_saddr_score { int rule; int addr_type; struct inet6_ifaddr *ifa; long unsigned int scorebits[1]; int scopedist; int matchlen; }; struct ipv6_saddr_dst { const struct in6_addr *addr; int ifindex; int scope; int label; unsigned int prefs; }; struct if6_iter_state { struct seq_net_private p; int bucket; int offset; }; enum addr_type_t { UNICAST_ADDR = 0, MULTICAST_ADDR = 1, ANYCAST_ADDR = 2, }; struct inet6_fill_args { u32 portid; u32 seq; int event; unsigned int flags; int netnsid; int ifindex; enum addr_type_t type; }; enum { DAD_PROCESS = 0, DAD_BEGIN = 1, DAD_ABORT = 2, }; struct ifaddrlblmsg { __u8 ifal_family; __u8 __ifal_reserved; __u8 ifal_prefixlen; __u8 ifal_flags; __u32 ifal_index; __u32 ifal_seq; }; enum { IFAL_ADDRESS = 1, IFAL_LABEL = 2, __IFAL_MAX = 3, }; struct ip6addrlbl_entry { struct in6_addr prefix; int prefixlen; int ifindex; int addrtype; u32 label; struct hlist_node list; struct callback_head rcu; }; struct ip6addrlbl_init_table { const struct in6_addr *prefix; int prefixlen; u32 label; }; struct rd_msg { struct icmp6hdr icmph; struct in6_addr target; struct in6_addr dest; __u8 opt[0]; }; struct fib6_gc_args { int timeout; int more; }; struct rt6_exception { struct hlist_node hlist; struct rt6_info *rt6i; long unsigned int stamp; struct callback_head rcu; }; typedef struct rt6_info * (*pol_lookup_t)(struct net *, struct fib6_table *, struct flowi6 *, const struct sk_buff *, int); struct route_info { __u8 type; __u8 length; __u8 prefix_len; __u8 reserved_l: 3; __u8 route_pref: 2; __u8 reserved_h: 3; __be32 lifetime; __u8 prefix[0]; }; struct rt6_rtnl_dump_arg { struct sk_buff *skb; struct netlink_callback *cb; struct net *net; struct fib_dump_filter filter; }; struct netevent_redirect { struct dst_entry *old; struct dst_entry *new; struct neighbour *neigh; const void *daddr; }; struct trace_event_raw_fib6_table_lookup { struct trace_entry ent; u32 tb_id; int err; int oif; int iif; __u8 tos; __u8 scope; __u8 flags; __u8 src[16]; __u8 dst[16]; u16 sport; u16 dport; u8 proto; u8 rt_type; u32 __data_loc_name; __u8 gw[16]; char __data[0]; }; struct trace_event_data_offsets_fib6_table_lookup { u32 name; }; typedef void (*btf_trace_fib6_table_lookup)(void *, const struct net *, const struct fib6_result *, struct fib6_table *, const struct flowi6 *); enum rt6_nud_state { RT6_NUD_FAIL_HARD = 4294967293, RT6_NUD_FAIL_PROBE = 4294967294, RT6_NUD_FAIL_DO_RR = 4294967295, RT6_NUD_SUCCEED = 1, }; struct fib6_nh_dm_arg { struct net *net; const struct in6_addr *saddr; int oif; int flags; struct fib6_nh *nh; }; struct __rt6_probe_work { struct work_struct work; struct in6_addr target; struct net_device *dev; }; struct fib6_nh_frl_arg { u32 flags; int oif; int strict; int *mpri; bool *do_rr; struct fib6_nh *nh; }; struct fib6_nh_excptn_arg { struct rt6_info *rt; int plen; }; struct fib6_nh_match_arg { const struct net_device *dev; const struct in6_addr *gw; struct fib6_nh *match; }; struct fib6_nh_age_excptn_arg { struct fib6_gc_args *gc_args; long unsigned int now; }; struct fib6_nh_rd_arg { struct fib6_result *res; struct flowi6 *fl6; const struct in6_addr *gw; struct rt6_info **ret; }; struct ip6rd_flowi { struct flowi6 fl6; struct in6_addr gateway; }; struct fib6_nh_del_cached_rt_arg { struct fib6_config *cfg; struct fib6_info *f6i; }; struct arg_dev_net_ip { struct net_device *dev; struct net *net; struct in6_addr *addr; }; struct arg_netdev_event { const struct net_device *dev; union { unsigned char nh_flags; long unsigned int event; }; }; struct rt6_mtu_change_arg { struct net_device *dev; unsigned int mtu; struct fib6_info *f6i; }; struct rt6_nh { struct fib6_info *fib6_info; struct fib6_config r_cfg; struct list_head next; }; struct fib6_nh_exception_dump_walker { struct rt6_rtnl_dump_arg *dump; struct fib6_info *rt; unsigned int flags; unsigned int skip; unsigned int count; }; enum fib6_walk_state { FWS_S = 0, FWS_L = 1, FWS_R = 2, FWS_C = 3, FWS_U = 4, }; struct fib6_walker { struct list_head lh; struct fib6_node *root; struct fib6_node *node; struct fib6_info *leaf; enum fib6_walk_state state; unsigned int skip; unsigned int count; unsigned int skip_in_node; int (*func)(struct fib6_walker *); void *args; }; struct fib6_entry_notifier_info { struct fib_notifier_info info; struct fib6_info *rt; unsigned int nsiblings; }; struct ipv6_route_iter { struct seq_net_private p; struct fib6_walker w; loff_t skip; struct fib6_table *tbl; int sernum; }; struct bpf_iter__ipv6_route { union { struct bpf_iter_meta *meta; }; union { struct fib6_info *rt; }; }; struct fib6_cleaner { struct fib6_walker w; struct net *net; int (*func)(struct fib6_info *, void *); int sernum; void *arg; bool skip_notify; }; enum { FIB6_NO_SERNUM_CHANGE = 0, }; struct fib6_dump_arg { struct net *net; struct notifier_block *nb; struct netlink_ext_ack *extack; }; struct fib6_nh_pcpu_arg { struct fib6_info *from; const struct fib6_table *table; }; struct lookup_args { int offset; const struct in6_addr *addr; }; struct ipv6_mreq { struct in6_addr ipv6mr_multiaddr; int ipv6mr_ifindex; }; struct in6_flowlabel_req { struct in6_addr flr_dst; __be32 flr_label; __u8 flr_action; __u8 flr_share; __u16 flr_flags; __u16 flr_expires; __u16 flr_linger; __u32 __flr_pad; }; struct ip6_mtuinfo { struct sockaddr_in6 ip6m_addr; __u32 ip6m_mtu; }; struct nduseroptmsg { unsigned char nduseropt_family; unsigned char nduseropt_pad1; short unsigned int nduseropt_opts_len; int nduseropt_ifindex; __u8 nduseropt_icmp_type; __u8 nduseropt_icmp_code; short unsigned int nduseropt_pad2; unsigned int nduseropt_pad3; }; enum { NDUSEROPT_UNSPEC = 0, NDUSEROPT_SRCADDR = 1, __NDUSEROPT_MAX = 2, }; struct nd_msg { struct icmp6hdr icmph; struct in6_addr target; __u8 opt[0]; }; struct rs_msg { struct icmp6hdr icmph; __u8 opt[0]; }; struct ra_msg { struct icmp6hdr icmph; __be32 reachable_time; __be32 retrans_timer; }; struct icmp6_filter { __u32 data[8]; }; struct raw6_sock { struct inet_sock inet; __u32 checksum; __u32 offset; struct icmp6_filter filter; __u32 ip6mr_table; struct ipv6_pinfo inet6; }; typedef int mh_filter_t(struct sock *, struct sk_buff *); struct raw6_frag_vec { struct msghdr *msg; int hlen; char c[4]; }; struct ipv6_destopt_hao { __u8 type; __u8 length; struct in6_addr addr; } __attribute__((packed)); typedef void ip6_icmp_send_t(struct sk_buff *, u8, u8, __u32, const struct in6_addr *, const struct inet6_skb_parm *); struct icmpv6_msg { struct sk_buff *skb; int offset; uint8_t type; }; struct icmp6_err { int err; int fatal; }; struct mld_msg { struct icmp6hdr mld_hdr; struct in6_addr mld_mca; }; struct mld2_grec { __u8 grec_type; __u8 grec_auxwords; __be16 grec_nsrcs; struct in6_addr grec_mca; struct in6_addr grec_src[0]; }; struct mld2_report { struct icmp6hdr mld2r_hdr; struct mld2_grec mld2r_grec[0]; }; struct mld2_query { struct icmp6hdr mld2q_hdr; struct in6_addr mld2q_mca; __u8 mld2q_qrv: 3; __u8 mld2q_suppress: 1; __u8 mld2q_resv2: 4; __u8 mld2q_qqic; __be16 mld2q_nsrcs; struct in6_addr mld2q_srcs[0]; }; struct igmp6_mc_iter_state { struct seq_net_private p; struct net_device *dev; struct inet6_dev *idev; }; struct igmp6_mcf_iter_state { struct seq_net_private p; struct net_device *dev; struct inet6_dev *idev; struct ifmcaddr6 *im; }; enum ip6_defrag_users { IP6_DEFRAG_LOCAL_DELIVER = 0, IP6_DEFRAG_CONNTRACK_IN = 1, __IP6_DEFRAG_CONNTRACK_IN = 65536, IP6_DEFRAG_CONNTRACK_OUT = 65537, __IP6_DEFRAG_CONNTRACK_OUT = 131072, IP6_DEFRAG_CONNTRACK_BRIDGE_IN = 131073, __IP6_DEFRAG_CONNTRACK_BRIDGE_IN = 196608, }; struct frag_queue { struct inet_frag_queue q; int iif; __u16 nhoffset; u8 ecn; }; struct tcp6_pseudohdr { struct in6_addr saddr; struct in6_addr daddr; __be32 len; __be32 protocol; }; struct rt0_hdr { struct ipv6_rt_hdr rt_hdr; __u32 reserved; struct in6_addr addr[0]; }; struct ipv6_rpl_sr_hdr { __u8 nexthdr; __u8 hdrlen; __u8 type; __u8 segments_left; __u32 cmpre: 4; __u32 cmpri: 4; __u32 reserved: 4; __u32 pad: 4; __u32 reserved1: 16; union { struct in6_addr addr[0]; __u8 data[0]; } segments; }; struct tlvtype_proc { int type; bool (*func)(struct sk_buff *, int); }; struct ip6fl_iter_state { struct seq_net_private p; struct pid_namespace *pid_ns; int bucket; }; struct sr6_tlv { __u8 type; __u8 len; __u8 data[0]; }; enum { SEG6_ATTR_UNSPEC = 0, SEG6_ATTR_DST = 1, SEG6_ATTR_DSTLEN = 2, SEG6_ATTR_HMACKEYID = 3, SEG6_ATTR_SECRET = 4, SEG6_ATTR_SECRETLEN = 5, SEG6_ATTR_ALGID = 6, SEG6_ATTR_HMACINFO = 7, __SEG6_ATTR_MAX = 8, }; enum { SEG6_CMD_UNSPEC = 0, SEG6_CMD_SETHMAC = 1, SEG6_CMD_DUMPHMAC = 2, SEG6_CMD_SET_TUNSRC = 3, SEG6_CMD_GET_TUNSRC = 4, __SEG6_CMD_MAX = 5, }; struct seg6_hmac_info { struct rhash_head node; struct callback_head rcu; u32 hmackeyid; char secret[64]; u8 slen; u8 alg_id; }; typedef short unsigned int mifi_t; typedef __u32 if_mask; struct if_set { if_mask ifs_bits[8]; }; struct mif6ctl { mifi_t mif6c_mifi; unsigned char mif6c_flags; unsigned char vifc_threshold; __u16 mif6c_pifi; unsigned int vifc_rate_limit; }; struct mf6cctl { struct sockaddr_in6 mf6cc_origin; struct sockaddr_in6 mf6cc_mcastgrp; mifi_t mf6cc_parent; struct if_set mf6cc_ifset; }; struct sioc_sg_req6 { struct sockaddr_in6 src; struct sockaddr_in6 grp; long unsigned int pktcnt; long unsigned int bytecnt; long unsigned int wrong_if; }; struct sioc_mif_req6 { mifi_t mifi; long unsigned int icount; long unsigned int ocount; long unsigned int ibytes; long unsigned int obytes; }; struct mrt6msg { __u8 im6_mbz; __u8 im6_msgtype; __u16 im6_mif; __u32 im6_pad; struct in6_addr im6_src; struct in6_addr im6_dst; }; enum { IP6MRA_CREPORT_UNSPEC = 0, IP6MRA_CREPORT_MSGTYPE = 1, IP6MRA_CREPORT_MIF_ID = 2, IP6MRA_CREPORT_SRC_ADDR = 3, IP6MRA_CREPORT_DST_ADDR = 4, IP6MRA_CREPORT_PKT = 5, __IP6MRA_CREPORT_MAX = 6, }; struct mfc6_cache_cmp_arg { struct in6_addr mf6c_mcastgrp; struct in6_addr mf6c_origin; }; struct mfc6_cache { struct mr_mfc _c; union { struct { struct in6_addr mf6c_mcastgrp; struct in6_addr mf6c_origin; }; struct mfc6_cache_cmp_arg cmparg; }; }; struct ip6mr_result { struct mr_table *mrt; }; struct compat_sioc_sg_req6 { struct sockaddr_in6 src; struct sockaddr_in6 grp; compat_ulong_t pktcnt; compat_ulong_t bytecnt; compat_ulong_t wrong_if; }; struct compat_sioc_mif_req6 { mifi_t mifi; compat_ulong_t icount; compat_ulong_t ocount; compat_ulong_t ibytes; compat_ulong_t obytes; }; struct xfrm6_protocol { int (*handler)(struct sk_buff *); int (*input_handler)(struct sk_buff *, int, __be32, int); int (*cb_handler)(struct sk_buff *, int); int (*err_handler)(struct sk_buff *, struct inet6_skb_parm *, u8, u8, int, __be32); struct xfrm6_protocol *next; int priority; }; struct br_input_skb_cb { struct net_device *brdev; u16 frag_max_size; u8 igmp; u8 mrouters_only: 1; u8 proxyarp_replied: 1; u8 src_port_isolated: 1; u8 vlan_filtered: 1; u8 br_netfilter_broute: 1; int offload_fwd_mark; }; struct nf_bridge_frag_data; struct fib6_rule { struct fib_rule common; struct rt6key src; struct rt6key dst; u8 tclass; }; struct calipso_doi; struct netlbl_calipso_ops { int (*doi_add)(struct calipso_doi *, struct netlbl_audit *); void (*doi_free)(struct calipso_doi *); int (*doi_remove)(u32, struct netlbl_audit *); struct calipso_doi * (*doi_getdef)(u32); void (*doi_putdef)(struct calipso_doi *); int (*doi_walk)(u32 *, int (*)(struct calipso_doi *, void *), void *); int (*sock_getattr)(struct sock *, struct netlbl_lsm_secattr *); int (*sock_setattr)(struct sock *, const struct calipso_doi *, const struct netlbl_lsm_secattr *); void (*sock_delattr)(struct sock *); int (*req_setattr)(struct request_sock *, const struct calipso_doi *, const struct netlbl_lsm_secattr *); void (*req_delattr)(struct request_sock *); int (*opt_getattr)(const unsigned char *, struct netlbl_lsm_secattr *); unsigned char * (*skbuff_optptr)(const struct sk_buff *); int (*skbuff_setattr)(struct sk_buff *, const struct calipso_doi *, const struct netlbl_lsm_secattr *); int (*skbuff_delattr)(struct sk_buff *); void (*cache_invalidate)(); int (*cache_add)(const unsigned char *, const struct netlbl_lsm_secattr *); }; struct calipso_doi { u32 doi; u32 type; refcount_t refcount; struct list_head list; struct callback_head rcu; }; struct calipso_map_cache_bkt { spinlock_t lock; u32 size; struct list_head list; }; struct calipso_map_cache_entry { u32 hash; unsigned char *key; size_t key_len; struct netlbl_lsm_cache *lsm_data; u32 activity; struct list_head list; }; enum { SEG6_IPTUNNEL_UNSPEC = 0, SEG6_IPTUNNEL_SRH = 1, __SEG6_IPTUNNEL_MAX = 2, }; struct seg6_iptunnel_encap { int mode; struct ipv6_sr_hdr srh[0]; }; enum { SEG6_IPTUN_MODE_INLINE = 0, SEG6_IPTUN_MODE_ENCAP = 1, SEG6_IPTUN_MODE_L2ENCAP = 2, }; struct seg6_lwt { struct dst_cache cache; struct seg6_iptunnel_encap tuninfo[0]; }; enum l3mdev_type { L3MDEV_TYPE_UNSPEC = 0, L3MDEV_TYPE_VRF = 1, __L3MDEV_TYPE_MAX = 2, }; enum { IP6_FH_F_FRAG = 1, IP6_FH_F_AUTH = 2, IP6_FH_F_SKIP_RH = 4, }; enum { SEG6_LOCAL_UNSPEC = 0, SEG6_LOCAL_ACTION = 1, SEG6_LOCAL_SRH = 2, SEG6_LOCAL_TABLE = 3, SEG6_LOCAL_NH4 = 4, SEG6_LOCAL_NH6 = 5, SEG6_LOCAL_IIF = 6, SEG6_LOCAL_OIF = 7, SEG6_LOCAL_BPF = 8, SEG6_LOCAL_VRFTABLE = 9, SEG6_LOCAL_COUNTERS = 10, __SEG6_LOCAL_MAX = 11, }; enum { SEG6_LOCAL_BPF_PROG_UNSPEC = 0, SEG6_LOCAL_BPF_PROG = 1, SEG6_LOCAL_BPF_PROG_NAME = 2, __SEG6_LOCAL_BPF_PROG_MAX = 3, }; enum { SEG6_LOCAL_CNT_UNSPEC = 0, SEG6_LOCAL_CNT_PAD = 1, SEG6_LOCAL_CNT_PACKETS = 2, SEG6_LOCAL_CNT_BYTES = 3, SEG6_LOCAL_CNT_ERRORS = 4, __SEG6_LOCAL_CNT_MAX = 5, }; struct seg6_local_lwt; struct seg6_local_lwtunnel_ops { int (*build_state)(struct seg6_local_lwt *, const void *, struct netlink_ext_ack *); void (*destroy_state)(struct seg6_local_lwt *); }; enum seg6_end_dt_mode { DT_INVALID_MODE = 4294967274, DT_LEGACY_MODE = 0, DT_VRF_MODE = 1, }; struct seg6_end_dt_info { enum seg6_end_dt_mode mode; struct net *net; int vrf_ifindex; int vrf_table; u16 family; }; struct pcpu_seg6_local_counters; struct seg6_action_desc; struct seg6_local_lwt { int action; struct ipv6_sr_hdr *srh; int table; struct in_addr nh4; struct in6_addr nh6; int iif; int oif; struct bpf_lwt_prog bpf; struct seg6_end_dt_info dt_info; struct pcpu_seg6_local_counters *pcpu_counters; int headroom; struct seg6_action_desc *desc; long unsigned int parsed_optattrs; }; struct seg6_action_desc { int action; long unsigned int attrs; long unsigned int optattrs; int (*input)(struct sk_buff *, struct seg6_local_lwt *); int static_headroom; struct seg6_local_lwtunnel_ops slwt_ops; }; struct pcpu_seg6_local_counters { u64_stats_t packets; u64_stats_t bytes; u64_stats_t errors; struct u64_stats_sync syncp; }; struct seg6_local_counters { __u64 packets; __u64 bytes; __u64 errors; }; struct seg6_action_param { int (*parse)(struct nlattr **, struct seg6_local_lwt *); int (*put)(struct sk_buff *, struct seg6_local_lwt *); int (*cmp)(struct seg6_local_lwt *, struct seg6_local_lwt *); void (*destroy)(struct seg6_local_lwt *); }; struct sr6_tlv_hmac { struct sr6_tlv tlvhdr; __u16 reserved; __be32 hmackeyid; __u8 hmac[32]; }; enum { SEG6_HMAC_ALGO_SHA1 = 1, SEG6_HMAC_ALGO_SHA256 = 2, }; struct seg6_hmac_algo { u8 alg_id; char name[64]; struct crypto_shash **tfms; struct shash_desc **shashs; }; enum { RPL_IPTUNNEL_UNSPEC = 0, RPL_IPTUNNEL_SRH = 1, __RPL_IPTUNNEL_MAX = 2, }; struct rpl_iptunnel_encap { struct ipv6_rpl_sr_hdr srh[0]; }; struct rpl_lwt { struct dst_cache cache; struct rpl_iptunnel_encap tuninfo; }; struct sockaddr_pkt { short unsigned int spkt_family; unsigned char spkt_device[14]; __be16 spkt_protocol; }; struct sockaddr_ll { short unsigned int sll_family; __be16 sll_protocol; int sll_ifindex; short unsigned int sll_hatype; unsigned char sll_pkttype; unsigned char sll_halen; unsigned char sll_addr[8]; }; struct tpacket_stats { unsigned int tp_packets; unsigned int tp_drops; }; struct tpacket_stats_v3 { unsigned int tp_packets; unsigned int tp_drops; unsigned int tp_freeze_q_cnt; }; struct tpacket_rollover_stats { __u64 tp_all; __u64 tp_huge; __u64 tp_failed; }; union tpacket_stats_u { struct tpacket_stats stats1; struct tpacket_stats_v3 stats3; }; struct tpacket_auxdata { __u32 tp_status; __u32 tp_len; __u32 tp_snaplen; __u16 tp_mac; __u16 tp_net; __u16 tp_vlan_tci; __u16 tp_vlan_tpid; }; struct tpacket_hdr { long unsigned int tp_status; unsigned int tp_len; unsigned int tp_snaplen; short unsigned int tp_mac; short unsigned int tp_net; unsigned int tp_sec; unsigned int tp_usec; }; struct tpacket2_hdr { __u32 tp_status; __u32 tp_len; __u32 tp_snaplen; __u16 tp_mac; __u16 tp_net; __u32 tp_sec; __u32 tp_nsec; __u16 tp_vlan_tci; __u16 tp_vlan_tpid; __u8 tp_padding[4]; }; struct tpacket_hdr_variant1 { __u32 tp_rxhash; __u32 tp_vlan_tci; __u16 tp_vlan_tpid; __u16 tp_padding; }; struct tpacket3_hdr { __u32 tp_next_offset; __u32 tp_sec; __u32 tp_nsec; __u32 tp_snaplen; __u32 tp_len; __u32 tp_status; __u16 tp_mac; __u16 tp_net; union { struct tpacket_hdr_variant1 hv1; }; __u8 tp_padding[8]; }; struct tpacket_bd_ts { unsigned int ts_sec; union { unsigned int ts_usec; unsigned int ts_nsec; }; }; struct tpacket_hdr_v1 { __u32 block_status; __u32 num_pkts; __u32 offset_to_first_pkt; __u32 blk_len; __u64 seq_num; struct tpacket_bd_ts ts_first_pkt; struct tpacket_bd_ts ts_last_pkt; }; union tpacket_bd_header_u { struct tpacket_hdr_v1 bh1; }; struct tpacket_block_desc { __u32 version; __u32 offset_to_priv; union tpacket_bd_header_u hdr; }; enum tpacket_versions { TPACKET_V1 = 0, TPACKET_V2 = 1, TPACKET_V3 = 2, }; struct tpacket_req { unsigned int tp_block_size; unsigned int tp_block_nr; unsigned int tp_frame_size; unsigned int tp_frame_nr; }; struct tpacket_req3 { unsigned int tp_block_size; unsigned int tp_block_nr; unsigned int tp_frame_size; unsigned int tp_frame_nr; unsigned int tp_retire_blk_tov; unsigned int tp_sizeof_priv; unsigned int tp_feature_req_word; }; union tpacket_req_u { struct tpacket_req req; struct tpacket_req3 req3; }; struct fanout_args { __u16 id; __u16 type_flags; __u32 max_num_members; }; struct virtio_net_hdr { __u8 flags; __u8 gso_type; __virtio16 hdr_len; __virtio16 gso_size; __virtio16 csum_start; __virtio16 csum_offset; }; struct packet_mclist { struct packet_mclist *next; int ifindex; int count; short unsigned int type; short unsigned int alen; unsigned char addr[32]; }; struct pgv; struct tpacket_kbdq_core { struct pgv *pkbdq; unsigned int feature_req_word; unsigned int hdrlen; unsigned char reset_pending_on_curr_blk; unsigned char delete_blk_timer; short unsigned int kactive_blk_num; short unsigned int blk_sizeof_priv; short unsigned int last_kactive_blk_num; char *pkblk_start; char *pkblk_end; int kblk_size; unsigned int max_frame_len; unsigned int knum_blocks; uint64_t knxt_seq_num; char *prev; char *nxt_offset; struct sk_buff *skb; rwlock_t blk_fill_in_prog_lock; short unsigned int retire_blk_tov; short unsigned int version; long unsigned int tov_in_jiffies; struct timer_list retire_blk_timer; }; struct pgv { char *buffer; }; struct packet_ring_buffer { struct pgv *pg_vec; unsigned int head; unsigned int frames_per_block; unsigned int frame_size; unsigned int frame_max; unsigned int pg_vec_order; unsigned int pg_vec_pages; unsigned int pg_vec_len; unsigned int *pending_refcnt; union { long unsigned int *rx_owner_map; struct tpacket_kbdq_core prb_bdqc; }; }; struct packet_fanout { possible_net_t net; unsigned int num_members; u32 max_num_members; u16 id; u8 type; u8 flags; union { atomic_t rr_cur; struct bpf_prog *bpf_prog; }; struct list_head list; spinlock_t lock; refcount_t sk_ref; long: 64; struct packet_type prot_hook; struct sock *arr[0]; }; struct packet_rollover { int sock; atomic_long_t num; atomic_long_t num_huge; atomic_long_t num_failed; long: 64; long: 64; long: 64; long: 64; u32 history[16]; }; struct packet_sock { struct sock sk; struct packet_fanout *fanout; union tpacket_stats_u stats; struct packet_ring_buffer rx_ring; struct packet_ring_buffer tx_ring; int copy_thresh; spinlock_t bind_lock; struct mutex pg_vec_lock; unsigned int running; unsigned int auxdata: 1; unsigned int origdev: 1; unsigned int has_vnet_hdr: 1; unsigned int tp_loss: 1; unsigned int tp_tx_has_off: 1; int pressure; int ifindex; __be16 num; struct packet_rollover *rollover; struct packet_mclist *mclist; atomic_t mapped; enum tpacket_versions tp_version; unsigned int tp_hdrlen; unsigned int tp_reserve; unsigned int tp_tstamp; struct completion skb_completion; struct net_device *cached_dev; int (*xmit)(struct sk_buff *); struct packet_type prot_hook; atomic_t tp_drops; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct packet_mreq_max { int mr_ifindex; short unsigned int mr_type; short unsigned int mr_alen; unsigned char mr_address[32]; }; union tpacket_uhdr { struct tpacket_hdr *h1; struct tpacket2_hdr *h2; struct tpacket3_hdr *h3; void *raw; }; struct packet_skb_cb { union { struct sockaddr_pkt pkt; union { unsigned int origlen; struct sockaddr_ll ll; }; } sa; }; struct _strp_msg { struct strp_msg strp; int accum_len; }; struct vlan_group { unsigned int nr_vlan_devs; struct hlist_node hlist; struct net_device **vlan_devices_arrays[16]; }; struct vlan_info { struct net_device *real_dev; struct vlan_group grp; struct list_head vid_list; unsigned int nr_vids; struct callback_head rcu; }; enum vlan_flags { VLAN_FLAG_REORDER_HDR = 1, VLAN_FLAG_GVRP = 2, VLAN_FLAG_LOOSE_BINDING = 4, VLAN_FLAG_MVRP = 8, VLAN_FLAG_BRIDGE_BINDING = 16, }; struct vlan_priority_tci_mapping { u32 priority; u16 vlan_qos; struct vlan_priority_tci_mapping *next; }; struct vlan_dev_priv { unsigned int nr_ingress_mappings; u32 ingress_priority_map[8]; unsigned int nr_egress_mappings; struct vlan_priority_tci_mapping *egress_priority_map[16]; __be16 vlan_proto; u16 vlan_id; u16 flags; struct net_device *real_dev; unsigned char real_dev_addr[6]; struct proc_dir_entry *dent; struct vlan_pcpu_stats *vlan_pcpu_stats; struct netpoll *netpoll; }; enum vlan_protos { VLAN_PROTO_8021Q = 0, VLAN_PROTO_8021AD = 1, VLAN_PROTO_NUM = 2, }; struct vlan_vid_info { struct list_head list; __be16 proto; u16 vid; int refcount; }; enum nl80211_iftype { NL80211_IFTYPE_UNSPECIFIED = 0, NL80211_IFTYPE_ADHOC = 1, NL80211_IFTYPE_STATION = 2, NL80211_IFTYPE_AP = 3, NL80211_IFTYPE_AP_VLAN = 4, NL80211_IFTYPE_WDS = 5, NL80211_IFTYPE_MONITOR = 6, NL80211_IFTYPE_MESH_POINT = 7, NL80211_IFTYPE_P2P_CLIENT = 8, NL80211_IFTYPE_P2P_GO = 9, NL80211_IFTYPE_P2P_DEVICE = 10, NL80211_IFTYPE_OCB = 11, NL80211_IFTYPE_NAN = 12, NUM_NL80211_IFTYPES = 13, NL80211_IFTYPE_MAX = 12, }; struct cfg80211_conn; struct cfg80211_cached_keys; enum ieee80211_bss_type { IEEE80211_BSS_TYPE_ESS = 0, IEEE80211_BSS_TYPE_PBSS = 1, IEEE80211_BSS_TYPE_IBSS = 2, IEEE80211_BSS_TYPE_MBSS = 3, IEEE80211_BSS_TYPE_ANY = 4, }; struct cfg80211_internal_bss; enum nl80211_chan_width { NL80211_CHAN_WIDTH_20_NOHT = 0, NL80211_CHAN_WIDTH_20 = 1, NL80211_CHAN_WIDTH_40 = 2, NL80211_CHAN_WIDTH_80 = 3, NL80211_CHAN_WIDTH_80P80 = 4, NL80211_CHAN_WIDTH_160 = 5, NL80211_CHAN_WIDTH_5 = 6, NL80211_CHAN_WIDTH_10 = 7, NL80211_CHAN_WIDTH_1 = 8, NL80211_CHAN_WIDTH_2 = 9, NL80211_CHAN_WIDTH_4 = 10, NL80211_CHAN_WIDTH_8 = 11, NL80211_CHAN_WIDTH_16 = 12, }; enum ieee80211_edmg_bw_config { IEEE80211_EDMG_BW_CONFIG_4 = 4, IEEE80211_EDMG_BW_CONFIG_5 = 5, IEEE80211_EDMG_BW_CONFIG_6 = 6, IEEE80211_EDMG_BW_CONFIG_7 = 7, IEEE80211_EDMG_BW_CONFIG_8 = 8, IEEE80211_EDMG_BW_CONFIG_9 = 9, IEEE80211_EDMG_BW_CONFIG_10 = 10, IEEE80211_EDMG_BW_CONFIG_11 = 11, IEEE80211_EDMG_BW_CONFIG_12 = 12, IEEE80211_EDMG_BW_CONFIG_13 = 13, IEEE80211_EDMG_BW_CONFIG_14 = 14, IEEE80211_EDMG_BW_CONFIG_15 = 15, }; struct ieee80211_edmg { u8 channels; enum ieee80211_edmg_bw_config bw_config; }; struct ieee80211_channel; struct cfg80211_chan_def { struct ieee80211_channel *chan; enum nl80211_chan_width width; u32 center_freq1; u32 center_freq2; struct ieee80211_edmg edmg; u16 freq1_offset; }; struct ieee80211_mcs_info { u8 rx_mask[10]; __le16 rx_highest; u8 tx_params; u8 reserved[3]; }; struct ieee80211_ht_cap { __le16 cap_info; u8 ampdu_params_info; struct ieee80211_mcs_info mcs; __le16 extended_ht_cap_info; __le32 tx_BF_cap_info; u8 antenna_selection_info; } __attribute__((packed)); struct key_params; struct cfg80211_ibss_params { const u8 *ssid; const u8 *bssid; struct cfg80211_chan_def chandef; const u8 *ie; u8 ssid_len; u8 ie_len; u16 beacon_interval; u32 basic_rates; bool channel_fixed; bool privacy; bool control_port; bool control_port_over_nl80211; bool userspace_handles_dfs; int: 24; int mcast_rate[5]; struct ieee80211_ht_cap ht_capa; struct ieee80211_ht_cap ht_capa_mask; struct key_params *wep_keys; int wep_tx_key; int: 32; } __attribute__((packed)); enum nl80211_auth_type { NL80211_AUTHTYPE_OPEN_SYSTEM = 0, NL80211_AUTHTYPE_SHARED_KEY = 1, NL80211_AUTHTYPE_FT = 2, NL80211_AUTHTYPE_NETWORK_EAP = 3, NL80211_AUTHTYPE_SAE = 4, NL80211_AUTHTYPE_FILS_SK = 5, NL80211_AUTHTYPE_FILS_SK_PFS = 6, NL80211_AUTHTYPE_FILS_PK = 7, __NL80211_AUTHTYPE_NUM = 8, NL80211_AUTHTYPE_MAX = 7, NL80211_AUTHTYPE_AUTOMATIC = 8, }; enum nl80211_mfp { NL80211_MFP_NO = 0, NL80211_MFP_REQUIRED = 1, NL80211_MFP_OPTIONAL = 2, }; enum nl80211_sae_pwe_mechanism { NL80211_SAE_PWE_UNSPECIFIED = 0, NL80211_SAE_PWE_HUNT_AND_PECK = 1, NL80211_SAE_PWE_HASH_TO_ELEMENT = 2, NL80211_SAE_PWE_BOTH = 3, }; struct cfg80211_crypto_settings { u32 wpa_versions; u32 cipher_group; int n_ciphers_pairwise; u32 ciphers_pairwise[5]; int n_akm_suites; u32 akm_suites[2]; bool control_port; __be16 control_port_ethertype; bool control_port_no_encrypt; bool control_port_over_nl80211; bool control_port_no_preauth; struct key_params *wep_keys; int wep_tx_key; const u8 *psk; const u8 *sae_pwd; u8 sae_pwd_len; enum nl80211_sae_pwe_mechanism sae_pwe; }; struct ieee80211_vht_mcs_info { __le16 rx_mcs_map; __le16 rx_highest; __le16 tx_mcs_map; __le16 tx_highest; }; struct ieee80211_vht_cap { __le32 vht_cap_info; struct ieee80211_vht_mcs_info supp_mcs; }; enum nl80211_bss_select_attr { __NL80211_BSS_SELECT_ATTR_INVALID = 0, NL80211_BSS_SELECT_ATTR_RSSI = 1, NL80211_BSS_SELECT_ATTR_BAND_PREF = 2, NL80211_BSS_SELECT_ATTR_RSSI_ADJUST = 3, __NL80211_BSS_SELECT_ATTR_AFTER_LAST = 4, NL80211_BSS_SELECT_ATTR_MAX = 3, }; enum nl80211_band { NL80211_BAND_2GHZ = 0, NL80211_BAND_5GHZ = 1, NL80211_BAND_60GHZ = 2, NL80211_BAND_6GHZ = 3, NL80211_BAND_S1GHZ = 4, NUM_NL80211_BANDS = 5, }; struct cfg80211_bss_select_adjust { enum nl80211_band band; s8 delta; }; struct cfg80211_bss_selection { enum nl80211_bss_select_attr behaviour; union { enum nl80211_band band_pref; struct cfg80211_bss_select_adjust adjust; } param; }; struct cfg80211_connect_params { struct ieee80211_channel *channel; struct ieee80211_channel *channel_hint; const u8 *bssid; const u8 *bssid_hint; const u8 *ssid; size_t ssid_len; enum nl80211_auth_type auth_type; int: 32; const u8 *ie; size_t ie_len; bool privacy; int: 24; enum nl80211_mfp mfp; struct cfg80211_crypto_settings crypto; const u8 *key; u8 key_len; u8 key_idx; short: 16; u32 flags; int bg_scan_period; struct ieee80211_ht_cap ht_capa; struct ieee80211_ht_cap ht_capa_mask; struct ieee80211_vht_cap vht_capa; struct ieee80211_vht_cap vht_capa_mask; bool pbss; int: 24; struct cfg80211_bss_selection bss_select; const u8 *prev_bssid; const u8 *fils_erp_username; size_t fils_erp_username_len; const u8 *fils_erp_realm; size_t fils_erp_realm_len; u16 fils_erp_next_seq_num; long: 48; const u8 *fils_erp_rrk; size_t fils_erp_rrk_len; bool want_1x; int: 24; struct ieee80211_edmg edmg; int: 32; } __attribute__((packed)); struct cfg80211_cqm_config; struct wiphy; struct wireless_dev { struct wiphy *wiphy; enum nl80211_iftype iftype; struct list_head list; struct net_device *netdev; u32 identifier; struct list_head mgmt_registrations; spinlock_t mgmt_registrations_lock; u8 mgmt_registrations_need_update: 1; struct mutex mtx; bool use_4addr; bool is_running; bool registered; bool registering; u8 address[6]; u8 ssid[32]; u8 ssid_len; u8 mesh_id_len; u8 mesh_id_up_len; struct cfg80211_conn *conn; struct cfg80211_cached_keys *connect_keys; enum ieee80211_bss_type conn_bss_type; u32 conn_owner_nlportid; struct work_struct disconnect_wk; u8 disconnect_bssid[6]; struct list_head event_list; spinlock_t event_lock; struct cfg80211_internal_bss *current_bss; struct cfg80211_chan_def preset_chandef; struct cfg80211_chan_def chandef; bool ibss_fixed; bool ibss_dfs_possible; bool ps; int ps_timeout; int beacon_interval; u32 ap_unexpected_nlportid; u32 owner_nlportid; bool nl_owner_dead; bool cac_started; long unsigned int cac_start_time; unsigned int cac_time_ms; struct { struct cfg80211_ibss_params ibss; struct cfg80211_connect_params connect; struct cfg80211_cached_keys *keys; const u8 *ie; size_t ie_len; u8 bssid[6]; u8 prev_bssid[6]; u8 ssid[32]; s8 default_key; s8 default_mgmt_key; bool prev_bssid_valid; } wext; struct cfg80211_cqm_config *cqm_config; struct list_head pmsr_list; spinlock_t pmsr_lock; struct work_struct pmsr_free_wk; long unsigned int unprot_beacon_reported; }; struct iw_encode_ext { __u32 ext_flags; __u8 tx_seq[8]; __u8 rx_seq[8]; struct sockaddr addr; __u16 alg; __u16 key_len; __u8 key[0]; }; struct iwreq { union { char ifrn_name[16]; } ifr_ifrn; union iwreq_data u; }; struct iw_event { __u16 len; __u16 cmd; union iwreq_data u; }; struct compat_iw_point { compat_caddr_t pointer; __u16 length; __u16 flags; }; struct __compat_iw_event { __u16 len; __u16 cmd; compat_caddr_t pointer; }; enum nl80211_reg_initiator { NL80211_REGDOM_SET_BY_CORE = 0, NL80211_REGDOM_SET_BY_USER = 1, NL80211_REGDOM_SET_BY_DRIVER = 2, NL80211_REGDOM_SET_BY_COUNTRY_IE = 3, }; enum nl80211_dfs_regions { NL80211_DFS_UNSET = 0, NL80211_DFS_FCC = 1, NL80211_DFS_ETSI = 2, NL80211_DFS_JP = 3, }; enum nl80211_user_reg_hint_type { NL80211_USER_REG_HINT_USER = 0, NL80211_USER_REG_HINT_CELL_BASE = 1, NL80211_USER_REG_HINT_INDOOR = 2, }; enum nl80211_mntr_flags { __NL80211_MNTR_FLAG_INVALID = 0, NL80211_MNTR_FLAG_FCSFAIL = 1, NL80211_MNTR_FLAG_PLCPFAIL = 2, NL80211_MNTR_FLAG_CONTROL = 3, NL80211_MNTR_FLAG_OTHER_BSS = 4, NL80211_MNTR_FLAG_COOK_FRAMES = 5, NL80211_MNTR_FLAG_ACTIVE = 6, __NL80211_MNTR_FLAG_AFTER_LAST = 7, NL80211_MNTR_FLAG_MAX = 6, }; enum nl80211_key_mode { NL80211_KEY_RX_TX = 0, NL80211_KEY_NO_TX = 1, NL80211_KEY_SET_TX = 2, }; enum nl80211_bss_scan_width { NL80211_BSS_CHAN_WIDTH_20 = 0, NL80211_BSS_CHAN_WIDTH_10 = 1, NL80211_BSS_CHAN_WIDTH_5 = 2, NL80211_BSS_CHAN_WIDTH_1 = 3, NL80211_BSS_CHAN_WIDTH_2 = 4, }; struct nl80211_wowlan_tcp_data_seq { __u32 start; __u32 offset; __u32 len; }; struct nl80211_wowlan_tcp_data_token { __u32 offset; __u32 len; __u8 token_stream[0]; }; struct nl80211_wowlan_tcp_data_token_feature { __u32 min_len; __u32 max_len; __u32 bufsize; }; enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_VHT_IBSS = 0, NL80211_EXT_FEATURE_RRM = 1, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER = 2, NL80211_EXT_FEATURE_SCAN_START_TIME = 3, NL80211_EXT_FEATURE_BSS_PARENT_TSF = 4, NL80211_EXT_FEATURE_SET_SCAN_DWELL = 5, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY = 6, NL80211_EXT_FEATURE_BEACON_RATE_HT = 7, NL80211_EXT_FEATURE_BEACON_RATE_VHT = 8, NL80211_EXT_FEATURE_FILS_STA = 9, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA = 10, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED = 11, NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI = 12, NL80211_EXT_FEATURE_CQM_RSSI_LIST = 13, NL80211_EXT_FEATURE_FILS_SK_OFFLOAD = 14, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK = 15, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X = 16, NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME = 17, NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP = 18, NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE = 19, NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = 20, NL80211_EXT_FEATURE_MFP_OPTIONAL = 21, NL80211_EXT_FEATURE_LOW_SPAN_SCAN = 22, NL80211_EXT_FEATURE_LOW_POWER_SCAN = 23, NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN = 24, NL80211_EXT_FEATURE_DFS_OFFLOAD = 25, NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211 = 26, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT = 27, NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT = 27, NL80211_EXT_FEATURE_TXQS = 28, NL80211_EXT_FEATURE_SCAN_RANDOM_SN = 29, NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT = 30, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0 = 31, NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER = 32, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS = 33, NL80211_EXT_FEATURE_AP_PMKSA_CACHING = 34, NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD = 35, NL80211_EXT_FEATURE_EXT_KEY_ID = 36, NL80211_EXT_FEATURE_STA_TX_PWR = 37, NL80211_EXT_FEATURE_SAE_OFFLOAD = 38, NL80211_EXT_FEATURE_VLAN_OFFLOAD = 39, NL80211_EXT_FEATURE_AQL = 40, NL80211_EXT_FEATURE_BEACON_PROTECTION = 41, NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH = 42, NL80211_EXT_FEATURE_PROTECTED_TWT = 43, NL80211_EXT_FEATURE_DEL_IBSS_STA = 44, NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS = 45, NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT = 46, NL80211_EXT_FEATURE_SCAN_FREQ_KHZ = 47, NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS = 48, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION = 49, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK = 50, NL80211_EXT_FEATURE_SAE_OFFLOAD_AP = 51, NL80211_EXT_FEATURE_FILS_DISCOVERY = 52, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP = 53, NL80211_EXT_FEATURE_BEACON_RATE_HE = 54, NL80211_EXT_FEATURE_SECURE_LTF = 55, NL80211_EXT_FEATURE_SECURE_RTT = 56, NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE = 57, NUM_NL80211_EXT_FEATURES = 58, MAX_NL80211_EXT_FEATURES = 57, }; enum nl80211_dfs_state { NL80211_DFS_USABLE = 0, NL80211_DFS_UNAVAILABLE = 1, NL80211_DFS_AVAILABLE = 2, }; struct nl80211_vendor_cmd_info { __u32 vendor_id; __u32 subcmd; }; enum nl80211_sar_type { NL80211_SAR_TYPE_POWER = 0, NUM_NL80211_SAR_TYPE = 1, }; struct ieee80211_he_cap_elem { u8 mac_cap_info[6]; u8 phy_cap_info[11]; }; struct ieee80211_he_mcs_nss_supp { __le16 rx_mcs_80; __le16 tx_mcs_80; __le16 rx_mcs_160; __le16 tx_mcs_160; __le16 rx_mcs_80p80; __le16 tx_mcs_80p80; }; struct ieee80211_he_6ghz_capa { __le16 capa; }; struct rfkill; enum environment_cap { ENVIRON_ANY = 0, ENVIRON_INDOOR = 1, ENVIRON_OUTDOOR = 2, }; struct regulatory_request { struct callback_head callback_head; int wiphy_idx; enum nl80211_reg_initiator initiator; enum nl80211_user_reg_hint_type user_reg_hint_type; char alpha2[3]; enum nl80211_dfs_regions dfs_region; bool intersect; bool processed; enum environment_cap country_ie_env; struct list_head list; }; struct ieee80211_freq_range { u32 start_freq_khz; u32 end_freq_khz; u32 max_bandwidth_khz; }; struct ieee80211_power_rule { u32 max_antenna_gain; u32 max_eirp; }; struct ieee80211_wmm_ac { u16 cw_min; u16 cw_max; u16 cot; u8 aifsn; }; struct ieee80211_wmm_rule { struct ieee80211_wmm_ac client[4]; struct ieee80211_wmm_ac ap[4]; }; struct ieee80211_reg_rule { struct ieee80211_freq_range freq_range; struct ieee80211_power_rule power_rule; struct ieee80211_wmm_rule wmm_rule; u32 flags; u32 dfs_cac_ms; bool has_wmm; }; struct ieee80211_regdomain { struct callback_head callback_head; u32 n_reg_rules; char alpha2[3]; enum nl80211_dfs_regions dfs_region; struct ieee80211_reg_rule reg_rules[0]; }; struct ieee80211_channel { enum nl80211_band band; u32 center_freq; u16 freq_offset; u16 hw_value; u32 flags; int max_antenna_gain; int max_power; int max_reg_power; bool beacon_found; u32 orig_flags; int orig_mag; int orig_mpwr; enum nl80211_dfs_state dfs_state; long unsigned int dfs_state_entered; unsigned int dfs_cac_ms; }; struct ieee80211_rate { u32 flags; u16 bitrate; u16 hw_value; u16 hw_value_short; }; struct ieee80211_sta_ht_cap { u16 cap; bool ht_supported; u8 ampdu_factor; u8 ampdu_density; struct ieee80211_mcs_info mcs; char: 8; } __attribute__((packed)); struct ieee80211_sta_vht_cap { bool vht_supported; u32 cap; struct ieee80211_vht_mcs_info vht_mcs; }; struct ieee80211_sta_he_cap { bool has_he; struct ieee80211_he_cap_elem he_cap_elem; struct ieee80211_he_mcs_nss_supp he_mcs_nss_supp; u8 ppe_thres[25]; } __attribute__((packed)); struct ieee80211_sband_iftype_data { u16 types_mask; struct ieee80211_sta_he_cap he_cap; struct ieee80211_he_6ghz_capa he_6ghz_capa; long: 40; struct { const u8 *data; unsigned int len; } vendor_elems; } __attribute__((packed)); struct ieee80211_sta_s1g_cap { bool s1g; u8 cap[10]; u8 nss_mcs[5]; }; struct ieee80211_supported_band { struct ieee80211_channel *channels; struct ieee80211_rate *bitrates; enum nl80211_band band; int n_channels; int n_bitrates; struct ieee80211_sta_ht_cap ht_cap; struct ieee80211_sta_vht_cap vht_cap; struct ieee80211_sta_s1g_cap s1g_cap; struct ieee80211_edmg edmg_cap; u16 n_iftype_data; const struct ieee80211_sband_iftype_data *iftype_data; }; struct key_params { const u8 *key; const u8 *seq; int key_len; int seq_len; u16 vlan_id; u32 cipher; enum nl80211_key_mode mode; }; struct mac_address { u8 addr[6]; }; struct cfg80211_sar_freq_ranges { u32 start_freq; u32 end_freq; }; struct cfg80211_sar_capa { enum nl80211_sar_type type; u32 num_freq_ranges; const struct cfg80211_sar_freq_ranges *freq_ranges; }; struct cfg80211_ssid { u8 ssid[32]; u8 ssid_len; }; enum cfg80211_signal_type { CFG80211_SIGNAL_TYPE_NONE = 0, CFG80211_SIGNAL_TYPE_MBM = 1, CFG80211_SIGNAL_TYPE_UNSPEC = 2, }; struct ieee80211_txrx_stypes; struct ieee80211_iface_combination; struct wiphy_iftype_akm_suites; struct wiphy_wowlan_support; struct cfg80211_wowlan; struct wiphy_iftype_ext_capab; struct wiphy_coalesce_support; struct wiphy_vendor_command; struct cfg80211_pmsr_capabilities; struct wiphy { struct mutex mtx; u8 perm_addr[6]; u8 addr_mask[6]; struct mac_address *addresses; const struct ieee80211_txrx_stypes *mgmt_stypes; const struct ieee80211_iface_combination *iface_combinations; int n_iface_combinations; u16 software_iftypes; u16 n_addresses; u16 interface_modes; u16 max_acl_mac_addrs; u32 flags; u32 regulatory_flags; u32 features; u8 ext_features[8]; u32 ap_sme_capa; enum cfg80211_signal_type signal_type; int bss_priv_size; u8 max_scan_ssids; u8 max_sched_scan_reqs; u8 max_sched_scan_ssids; u8 max_match_sets; u16 max_scan_ie_len; u16 max_sched_scan_ie_len; u32 max_sched_scan_plans; u32 max_sched_scan_plan_interval; u32 max_sched_scan_plan_iterations; int n_cipher_suites; const u32 *cipher_suites; int n_akm_suites; const u32 *akm_suites; const struct wiphy_iftype_akm_suites *iftype_akm_suites; unsigned int num_iftype_akm_suites; u8 retry_short; u8 retry_long; u32 frag_threshold; u32 rts_threshold; u8 coverage_class; char fw_version[32]; u32 hw_version; const struct wiphy_wowlan_support *wowlan; struct cfg80211_wowlan *wowlan_config; u16 max_remain_on_channel_duration; u8 max_num_pmkids; u32 available_antennas_tx; u32 available_antennas_rx; u32 probe_resp_offload; const u8 *extended_capabilities; const u8 *extended_capabilities_mask; u8 extended_capabilities_len; const struct wiphy_iftype_ext_capab *iftype_ext_capab; unsigned int num_iftype_ext_capab; const void *privid; struct ieee80211_supported_band *bands[5]; void (*reg_notifier)(struct wiphy *, struct regulatory_request *); const struct ieee80211_regdomain *regd; struct device dev; bool registered; struct dentry *debugfsdir; const struct ieee80211_ht_cap *ht_capa_mod_mask; const struct ieee80211_vht_cap *vht_capa_mod_mask; struct list_head wdev_list; possible_net_t _net; const struct iw_handler_def *wext; const struct wiphy_coalesce_support *coalesce; const struct wiphy_vendor_command *vendor_commands; const struct nl80211_vendor_cmd_info *vendor_events; int n_vendor_commands; int n_vendor_events; u16 max_ap_assoc_sta; u8 max_num_csa_counters; u32 bss_select_support; u8 nan_supported_bands; u32 txq_limit; u32 txq_memory_limit; u32 txq_quantum; long unsigned int tx_queue_len; u8 support_mbssid: 1; u8 support_only_he_mbssid: 1; const struct cfg80211_pmsr_capabilities *pmsr_capa; struct { u64 peer; u64 vif; u8 max_retry; } tid_config_support; u8 max_data_retry_count; const struct cfg80211_sar_capa *sar_capa; struct rfkill *rfkill; long: 64; char priv[0]; }; struct cfg80211_match_set { struct cfg80211_ssid ssid; u8 bssid[6]; s32 rssi_thold; s32 per_band_rssi_thold[5]; }; struct cfg80211_sched_scan_plan { u32 interval; u32 iterations; }; struct cfg80211_sched_scan_request { u64 reqid; struct cfg80211_ssid *ssids; int n_ssids; u32 n_channels; enum nl80211_bss_scan_width scan_width; const u8 *ie; size_t ie_len; u32 flags; struct cfg80211_match_set *match_sets; int n_match_sets; s32 min_rssi_thold; u32 delay; struct cfg80211_sched_scan_plan *scan_plans; int n_scan_plans; u8 mac_addr[6]; u8 mac_addr_mask[6]; bool relative_rssi_set; s8 relative_rssi; struct cfg80211_bss_select_adjust rssi_adjust; struct wiphy *wiphy; struct net_device *dev; long unsigned int scan_start; bool report_results; struct callback_head callback_head; u32 owner_nlportid; bool nl_owner_dead; struct list_head list; struct ieee80211_channel *channels[0]; }; struct cfg80211_pkt_pattern { const u8 *mask; const u8 *pattern; int pattern_len; int pkt_offset; }; struct cfg80211_wowlan_tcp { struct socket *sock; __be32 src; __be32 dst; u16 src_port; u16 dst_port; u8 dst_mac[6]; int payload_len; const u8 *payload; struct nl80211_wowlan_tcp_data_seq payload_seq; u32 data_interval; u32 wake_len; const u8 *wake_data; const u8 *wake_mask; u32 tokens_size; struct nl80211_wowlan_tcp_data_token payload_tok; }; struct cfg80211_wowlan { bool any; bool disconnect; bool magic_pkt; bool gtk_rekey_failure; bool eap_identity_req; bool four_way_handshake; bool rfkill_release; struct cfg80211_pkt_pattern *patterns; struct cfg80211_wowlan_tcp *tcp; int n_patterns; struct cfg80211_sched_scan_request *nd_config; }; struct ieee80211_iface_limit { u16 max; u16 types; }; struct ieee80211_iface_combination { const struct ieee80211_iface_limit *limits; u32 num_different_channels; u16 max_interfaces; u8 n_limits; bool beacon_int_infra_match; u8 radar_detect_widths; u8 radar_detect_regions; u32 beacon_int_min_gcd; }; struct ieee80211_txrx_stypes { u16 tx; u16 rx; }; struct wiphy_wowlan_tcp_support { const struct nl80211_wowlan_tcp_data_token_feature *tok; u32 data_payload_max; u32 data_interval_max; u32 wake_payload_max; bool seq; }; struct wiphy_wowlan_support { u32 flags; int n_patterns; int pattern_max_len; int pattern_min_len; int max_pkt_offset; int max_nd_match_sets; const struct wiphy_wowlan_tcp_support *tcp; }; struct wiphy_coalesce_support { int n_rules; int max_delay; int n_patterns; int pattern_max_len; int pattern_min_len; int max_pkt_offset; }; struct wiphy_vendor_command { struct nl80211_vendor_cmd_info info; u32 flags; int (*doit)(struct wiphy *, struct wireless_dev *, const void *, int); int (*dumpit)(struct wiphy *, struct wireless_dev *, struct sk_buff *, const void *, int, long unsigned int *); const struct nla_policy *policy; unsigned int maxattr; }; struct wiphy_iftype_ext_capab { enum nl80211_iftype iftype; const u8 *extended_capabilities; const u8 *extended_capabilities_mask; u8 extended_capabilities_len; }; struct cfg80211_pmsr_capabilities { unsigned int max_peers; u8 report_ap_tsf: 1; u8 randomize_mac_addr: 1; struct { u32 preambles; u32 bandwidths; s8 max_bursts_exponent; u8 max_ftms_per_burst; u8 supported: 1; u8 asap: 1; u8 non_asap: 1; u8 request_lci: 1; u8 request_civicloc: 1; u8 trigger_based: 1; u8 non_trigger_based: 1; } ftm; }; struct wiphy_iftype_akm_suites { u16 iftypes_mask; const u32 *akm_suites; int n_akm_suites; }; struct iw_ioctl_description { __u8 header_type; __u8 token_type; __u16 token_size; __u16 min_tokens; __u16 max_tokens; __u32 flags; }; typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, unsigned int, struct iw_request_info *, iw_handler); struct iw_thrspy { struct sockaddr addr; struct iw_quality qual; struct iw_quality low; struct iw_quality high; }; struct netlbl_af4list { __be32 addr; __be32 mask; u32 valid; struct list_head list; }; struct netlbl_af6list { struct in6_addr addr; struct in6_addr mask; u32 valid; struct list_head list; }; struct netlbl_domaddr_map { struct list_head list4; struct list_head list6; }; struct netlbl_dommap_def { u32 type; union { struct netlbl_domaddr_map *addrsel; struct cipso_v4_doi *cipso; struct calipso_doi *calipso; }; }; struct netlbl_domaddr4_map { struct netlbl_dommap_def def; struct netlbl_af4list list; }; struct netlbl_domaddr6_map { struct netlbl_dommap_def def; struct netlbl_af6list list; }; struct netlbl_dom_map { char *domain; u16 family; struct netlbl_dommap_def def; u32 valid; struct list_head list; struct callback_head rcu; }; struct netlbl_domhsh_tbl { struct list_head *tbl; u32 size; }; enum { NLBL_MGMT_C_UNSPEC = 0, NLBL_MGMT_C_ADD = 1, NLBL_MGMT_C_REMOVE = 2, NLBL_MGMT_C_LISTALL = 3, NLBL_MGMT_C_ADDDEF = 4, NLBL_MGMT_C_REMOVEDEF = 5, NLBL_MGMT_C_LISTDEF = 6, NLBL_MGMT_C_PROTOCOLS = 7, NLBL_MGMT_C_VERSION = 8, __NLBL_MGMT_C_MAX = 9, }; enum { NLBL_MGMT_A_UNSPEC = 0, NLBL_MGMT_A_DOMAIN = 1, NLBL_MGMT_A_PROTOCOL = 2, NLBL_MGMT_A_VERSION = 3, NLBL_MGMT_A_CV4DOI = 4, NLBL_MGMT_A_IPV6ADDR = 5, NLBL_MGMT_A_IPV6MASK = 6, NLBL_MGMT_A_IPV4ADDR = 7, NLBL_MGMT_A_IPV4MASK = 8, NLBL_MGMT_A_ADDRSELECTOR = 9, NLBL_MGMT_A_SELECTORLIST = 10, NLBL_MGMT_A_FAMILY = 11, NLBL_MGMT_A_CLPDOI = 12, __NLBL_MGMT_A_MAX = 13, }; struct netlbl_domhsh_walk_arg { struct netlink_callback *nl_cb; struct sk_buff *skb; u32 seq; }; enum { NLBL_UNLABEL_C_UNSPEC = 0, NLBL_UNLABEL_C_ACCEPT = 1, NLBL_UNLABEL_C_LIST = 2, NLBL_UNLABEL_C_STATICADD = 3, NLBL_UNLABEL_C_STATICREMOVE = 4, NLBL_UNLABEL_C_STATICLIST = 5, NLBL_UNLABEL_C_STATICADDDEF = 6, NLBL_UNLABEL_C_STATICREMOVEDEF = 7, NLBL_UNLABEL_C_STATICLISTDEF = 8, __NLBL_UNLABEL_C_MAX = 9, }; enum { NLBL_UNLABEL_A_UNSPEC = 0, NLBL_UNLABEL_A_ACPTFLG = 1, NLBL_UNLABEL_A_IPV6ADDR = 2, NLBL_UNLABEL_A_IPV6MASK = 3, NLBL_UNLABEL_A_IPV4ADDR = 4, NLBL_UNLABEL_A_IPV4MASK = 5, NLBL_UNLABEL_A_IFACE = 6, NLBL_UNLABEL_A_SECCTX = 7, __NLBL_UNLABEL_A_MAX = 8, }; struct netlbl_unlhsh_tbl { struct list_head *tbl; u32 size; }; struct netlbl_unlhsh_addr4 { u32 secid; struct netlbl_af4list list; struct callback_head rcu; }; struct netlbl_unlhsh_addr6 { u32 secid; struct netlbl_af6list list; struct callback_head rcu; }; struct netlbl_unlhsh_iface { int ifindex; struct list_head addr4_list; struct list_head addr6_list; u32 valid; struct list_head list; struct callback_head rcu; }; struct netlbl_unlhsh_walk_arg { struct netlink_callback *nl_cb; struct sk_buff *skb; u32 seq; }; enum { NLBL_CIPSOV4_C_UNSPEC = 0, NLBL_CIPSOV4_C_ADD = 1, NLBL_CIPSOV4_C_REMOVE = 2, NLBL_CIPSOV4_C_LIST = 3, NLBL_CIPSOV4_C_LISTALL = 4, __NLBL_CIPSOV4_C_MAX = 5, }; enum { NLBL_CIPSOV4_A_UNSPEC = 0, NLBL_CIPSOV4_A_DOI = 1, NLBL_CIPSOV4_A_MTYPE = 2, NLBL_CIPSOV4_A_TAG = 3, NLBL_CIPSOV4_A_TAGLST = 4, NLBL_CIPSOV4_A_MLSLVLLOC = 5, NLBL_CIPSOV4_A_MLSLVLREM = 6, NLBL_CIPSOV4_A_MLSLVL = 7, NLBL_CIPSOV4_A_MLSLVLLST = 8, NLBL_CIPSOV4_A_MLSCATLOC = 9, NLBL_CIPSOV4_A_MLSCATREM = 10, NLBL_CIPSOV4_A_MLSCAT = 11, NLBL_CIPSOV4_A_MLSCATLST = 12, __NLBL_CIPSOV4_A_MAX = 13, }; struct netlbl_cipsov4_doiwalk_arg { struct netlink_callback *nl_cb; struct sk_buff *skb; u32 seq; }; struct netlbl_domhsh_walk_arg___2 { struct netlbl_audit *audit_info; u32 doi; }; enum { NLBL_CALIPSO_C_UNSPEC = 0, NLBL_CALIPSO_C_ADD = 1, NLBL_CALIPSO_C_REMOVE = 2, NLBL_CALIPSO_C_LIST = 3, NLBL_CALIPSO_C_LISTALL = 4, __NLBL_CALIPSO_C_MAX = 5, }; enum { NLBL_CALIPSO_A_UNSPEC = 0, NLBL_CALIPSO_A_DOI = 1, NLBL_CALIPSO_A_MTYPE = 2, __NLBL_CALIPSO_A_MAX = 3, }; struct netlbl_calipso_doiwalk_arg { struct netlink_callback *nl_cb; struct sk_buff *skb; u32 seq; }; struct dcbmsg { __u8 dcb_family; __u8 cmd; __u16 dcb_pad; }; enum dcbnl_commands { DCB_CMD_UNDEFINED = 0, DCB_CMD_GSTATE = 1, DCB_CMD_SSTATE = 2, DCB_CMD_PGTX_GCFG = 3, DCB_CMD_PGTX_SCFG = 4, DCB_CMD_PGRX_GCFG = 5, DCB_CMD_PGRX_SCFG = 6, DCB_CMD_PFC_GCFG = 7, DCB_CMD_PFC_SCFG = 8, DCB_CMD_SET_ALL = 9, DCB_CMD_GPERM_HWADDR = 10, DCB_CMD_GCAP = 11, DCB_CMD_GNUMTCS = 12, DCB_CMD_SNUMTCS = 13, DCB_CMD_PFC_GSTATE = 14, DCB_CMD_PFC_SSTATE = 15, DCB_CMD_BCN_GCFG = 16, DCB_CMD_BCN_SCFG = 17, DCB_CMD_GAPP = 18, DCB_CMD_SAPP = 19, DCB_CMD_IEEE_SET = 20, DCB_CMD_IEEE_GET = 21, DCB_CMD_GDCBX = 22, DCB_CMD_SDCBX = 23, DCB_CMD_GFEATCFG = 24, DCB_CMD_SFEATCFG = 25, DCB_CMD_CEE_GET = 26, DCB_CMD_IEEE_DEL = 27, __DCB_CMD_ENUM_MAX = 28, DCB_CMD_MAX = 27, }; enum dcbnl_attrs { DCB_ATTR_UNDEFINED = 0, DCB_ATTR_IFNAME = 1, DCB_ATTR_STATE = 2, DCB_ATTR_PFC_STATE = 3, DCB_ATTR_PFC_CFG = 4, DCB_ATTR_NUM_TC = 5, DCB_ATTR_PG_CFG = 6, DCB_ATTR_SET_ALL = 7, DCB_ATTR_PERM_HWADDR = 8, DCB_ATTR_CAP = 9, DCB_ATTR_NUMTCS = 10, DCB_ATTR_BCN = 11, DCB_ATTR_APP = 12, DCB_ATTR_IEEE = 13, DCB_ATTR_DCBX = 14, DCB_ATTR_FEATCFG = 15, DCB_ATTR_CEE = 16, __DCB_ATTR_ENUM_MAX = 17, DCB_ATTR_MAX = 16, }; enum ieee_attrs { DCB_ATTR_IEEE_UNSPEC = 0, DCB_ATTR_IEEE_ETS = 1, DCB_ATTR_IEEE_PFC = 2, DCB_ATTR_IEEE_APP_TABLE = 3, DCB_ATTR_IEEE_PEER_ETS = 4, DCB_ATTR_IEEE_PEER_PFC = 5, DCB_ATTR_IEEE_PEER_APP = 6, DCB_ATTR_IEEE_MAXRATE = 7, DCB_ATTR_IEEE_QCN = 8, DCB_ATTR_IEEE_QCN_STATS = 9, DCB_ATTR_DCB_BUFFER = 10, __DCB_ATTR_IEEE_MAX = 11, }; enum ieee_attrs_app { DCB_ATTR_IEEE_APP_UNSPEC = 0, DCB_ATTR_IEEE_APP = 1, __DCB_ATTR_IEEE_APP_MAX = 2, }; enum cee_attrs { DCB_ATTR_CEE_UNSPEC = 0, DCB_ATTR_CEE_PEER_PG = 1, DCB_ATTR_CEE_PEER_PFC = 2, DCB_ATTR_CEE_PEER_APP_TABLE = 3, DCB_ATTR_CEE_TX_PG = 4, DCB_ATTR_CEE_RX_PG = 5, DCB_ATTR_CEE_PFC = 6, DCB_ATTR_CEE_APP_TABLE = 7, DCB_ATTR_CEE_FEAT = 8, __DCB_ATTR_CEE_MAX = 9, }; enum peer_app_attr { DCB_ATTR_CEE_PEER_APP_UNSPEC = 0, DCB_ATTR_CEE_PEER_APP_INFO = 1, DCB_ATTR_CEE_PEER_APP = 2, __DCB_ATTR_CEE_PEER_APP_MAX = 3, }; enum dcbnl_pfc_up_attrs { DCB_PFC_UP_ATTR_UNDEFINED = 0, DCB_PFC_UP_ATTR_0 = 1, DCB_PFC_UP_ATTR_1 = 2, DCB_PFC_UP_ATTR_2 = 3, DCB_PFC_UP_ATTR_3 = 4, DCB_PFC_UP_ATTR_4 = 5, DCB_PFC_UP_ATTR_5 = 6, DCB_PFC_UP_ATTR_6 = 7, DCB_PFC_UP_ATTR_7 = 8, DCB_PFC_UP_ATTR_ALL = 9, __DCB_PFC_UP_ATTR_ENUM_MAX = 10, DCB_PFC_UP_ATTR_MAX = 9, }; enum dcbnl_pg_attrs { DCB_PG_ATTR_UNDEFINED = 0, DCB_PG_ATTR_TC_0 = 1, DCB_PG_ATTR_TC_1 = 2, DCB_PG_ATTR_TC_2 = 3, DCB_PG_ATTR_TC_3 = 4, DCB_PG_ATTR_TC_4 = 5, DCB_PG_ATTR_TC_5 = 6, DCB_PG_ATTR_TC_6 = 7, DCB_PG_ATTR_TC_7 = 8, DCB_PG_ATTR_TC_MAX = 9, DCB_PG_ATTR_TC_ALL = 10, DCB_PG_ATTR_BW_ID_0 = 11, DCB_PG_ATTR_BW_ID_1 = 12, DCB_PG_ATTR_BW_ID_2 = 13, DCB_PG_ATTR_BW_ID_3 = 14, DCB_PG_ATTR_BW_ID_4 = 15, DCB_PG_ATTR_BW_ID_5 = 16, DCB_PG_ATTR_BW_ID_6 = 17, DCB_PG_ATTR_BW_ID_7 = 18, DCB_PG_ATTR_BW_ID_MAX = 19, DCB_PG_ATTR_BW_ID_ALL = 20, __DCB_PG_ATTR_ENUM_MAX = 21, DCB_PG_ATTR_MAX = 20, }; enum dcbnl_tc_attrs { DCB_TC_ATTR_PARAM_UNDEFINED = 0, DCB_TC_ATTR_PARAM_PGID = 1, DCB_TC_ATTR_PARAM_UP_MAPPING = 2, DCB_TC_ATTR_PARAM_STRICT_PRIO = 3, DCB_TC_ATTR_PARAM_BW_PCT = 4, DCB_TC_ATTR_PARAM_ALL = 5, __DCB_TC_ATTR_PARAM_ENUM_MAX = 6, DCB_TC_ATTR_PARAM_MAX = 5, }; enum dcbnl_cap_attrs { DCB_CAP_ATTR_UNDEFINED = 0, DCB_CAP_ATTR_ALL = 1, DCB_CAP_ATTR_PG = 2, DCB_CAP_ATTR_PFC = 3, DCB_CAP_ATTR_UP2TC = 4, DCB_CAP_ATTR_PG_TCS = 5, DCB_CAP_ATTR_PFC_TCS = 6, DCB_CAP_ATTR_GSP = 7, DCB_CAP_ATTR_BCN = 8, DCB_CAP_ATTR_DCBX = 9, __DCB_CAP_ATTR_ENUM_MAX = 10, DCB_CAP_ATTR_MAX = 9, }; enum dcbnl_numtcs_attrs { DCB_NUMTCS_ATTR_UNDEFINED = 0, DCB_NUMTCS_ATTR_ALL = 1, DCB_NUMTCS_ATTR_PG = 2, DCB_NUMTCS_ATTR_PFC = 3, __DCB_NUMTCS_ATTR_ENUM_MAX = 4, DCB_NUMTCS_ATTR_MAX = 3, }; enum dcbnl_bcn_attrs { DCB_BCN_ATTR_UNDEFINED = 0, DCB_BCN_ATTR_RP_0 = 1, DCB_BCN_ATTR_RP_1 = 2, DCB_BCN_ATTR_RP_2 = 3, DCB_BCN_ATTR_RP_3 = 4, DCB_BCN_ATTR_RP_4 = 5, DCB_BCN_ATTR_RP_5 = 6, DCB_BCN_ATTR_RP_6 = 7, DCB_BCN_ATTR_RP_7 = 8, DCB_BCN_ATTR_RP_ALL = 9, DCB_BCN_ATTR_BCNA_0 = 10, DCB_BCN_ATTR_BCNA_1 = 11, DCB_BCN_ATTR_ALPHA = 12, DCB_BCN_ATTR_BETA = 13, DCB_BCN_ATTR_GD = 14, DCB_BCN_ATTR_GI = 15, DCB_BCN_ATTR_TMAX = 16, DCB_BCN_ATTR_TD = 17, DCB_BCN_ATTR_RMIN = 18, DCB_BCN_ATTR_W = 19, DCB_BCN_ATTR_RD = 20, DCB_BCN_ATTR_RU = 21, DCB_BCN_ATTR_WRTT = 22, DCB_BCN_ATTR_RI = 23, DCB_BCN_ATTR_C = 24, DCB_BCN_ATTR_ALL = 25, __DCB_BCN_ATTR_ENUM_MAX = 26, DCB_BCN_ATTR_MAX = 25, }; enum dcb_general_attr_values { DCB_ATTR_VALUE_UNDEFINED = 255, }; enum dcbnl_app_attrs { DCB_APP_ATTR_UNDEFINED = 0, DCB_APP_ATTR_IDTYPE = 1, DCB_APP_ATTR_ID = 2, DCB_APP_ATTR_PRIORITY = 3, __DCB_APP_ATTR_ENUM_MAX = 4, DCB_APP_ATTR_MAX = 3, }; enum dcbnl_featcfg_attrs { DCB_FEATCFG_ATTR_UNDEFINED = 0, DCB_FEATCFG_ATTR_ALL = 1, DCB_FEATCFG_ATTR_PG = 2, DCB_FEATCFG_ATTR_PFC = 3, DCB_FEATCFG_ATTR_APP = 4, __DCB_FEATCFG_ATTR_ENUM_MAX = 5, DCB_FEATCFG_ATTR_MAX = 4, }; struct dcb_app_type { int ifindex; struct dcb_app app; struct list_head list; u8 dcbx; }; struct dcb_ieee_app_prio_map { u64 map[8]; }; struct dcb_ieee_app_dscp_map { u8 map[64]; }; enum dcbevent_notif_type { DCB_APP_EVENT = 1, }; struct reply_func { int type; int (*cb)(struct net_device *, struct nlmsghdr *, u32, struct nlattr **, struct sk_buff *); }; enum switchdev_attr_id { SWITCHDEV_ATTR_ID_UNDEFINED = 0, SWITCHDEV_ATTR_ID_PORT_STP_STATE = 1, SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS = 2, SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS = 3, SWITCHDEV_ATTR_ID_PORT_MROUTER = 4, SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME = 5, SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING = 6, SWITCHDEV_ATTR_ID_BRIDGE_VLAN_PROTOCOL = 7, SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED = 8, SWITCHDEV_ATTR_ID_BRIDGE_MROUTER = 9, SWITCHDEV_ATTR_ID_MRP_PORT_ROLE = 10, }; struct switchdev_attr { struct net_device *orig_dev; enum switchdev_attr_id id; u32 flags; void *complete_priv; void (*complete)(struct net_device *, int, void *); union { u8 stp_state; struct switchdev_brport_flags brport_flags; bool mrouter; clock_t ageing_time; bool vlan_filtering; u16 vlan_protocol; bool mc_disabled; u8 mrp_port_role; } u; }; enum switchdev_notifier_type { SWITCHDEV_FDB_ADD_TO_BRIDGE = 1, SWITCHDEV_FDB_DEL_TO_BRIDGE = 2, SWITCHDEV_FDB_ADD_TO_DEVICE = 3, SWITCHDEV_FDB_DEL_TO_DEVICE = 4, SWITCHDEV_FDB_OFFLOADED = 5, SWITCHDEV_FDB_FLUSH_TO_BRIDGE = 6, SWITCHDEV_PORT_OBJ_ADD = 7, SWITCHDEV_PORT_OBJ_DEL = 8, SWITCHDEV_PORT_ATTR_SET = 9, SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE = 10, SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE = 11, SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE = 12, SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE = 13, SWITCHDEV_VXLAN_FDB_OFFLOADED = 14, }; struct switchdev_notifier_info { struct net_device *dev; struct netlink_ext_ack *extack; const void *ctx; }; struct switchdev_notifier_port_obj_info { struct switchdev_notifier_info info; const struct switchdev_obj *obj; bool handled; }; struct switchdev_notifier_port_attr_info { struct switchdev_notifier_info info; const struct switchdev_attr *attr; bool handled; }; typedef void switchdev_deferred_func_t(struct net_device *, const void *); struct switchdev_deferred_item { struct list_head list; struct net_device *dev; switchdev_deferred_func_t *func; long unsigned int data[0]; }; typedef int (*lookup_by_table_id_t)(struct net *, u32); struct l3mdev_handler { lookup_by_table_id_t dev_lookup; }; struct ncsi_dev { int state; int link_up; struct net_device *dev; void (*handler)(struct ncsi_dev *); }; enum { NCSI_CAP_BASE = 0, NCSI_CAP_GENERIC = 0, NCSI_CAP_BC = 1, NCSI_CAP_MC = 2, NCSI_CAP_BUFFER = 3, NCSI_CAP_AEN = 4, NCSI_CAP_VLAN = 5, NCSI_CAP_MAX = 6, }; enum { NCSI_MODE_BASE = 0, NCSI_MODE_ENABLE = 0, NCSI_MODE_TX_ENABLE = 1, NCSI_MODE_LINK = 2, NCSI_MODE_VLAN = 3, NCSI_MODE_BC = 4, NCSI_MODE_MC = 5, NCSI_MODE_AEN = 6, NCSI_MODE_FC = 7, NCSI_MODE_MAX = 8, }; struct ncsi_channel_version { u32 version; u32 alpha2; u8 fw_name[12]; u32 fw_version; u16 pci_ids[4]; u32 mf_id; }; struct ncsi_channel_cap { u32 index; u32 cap; }; struct ncsi_channel_mode { u32 index; u32 enable; u32 size; u32 data[8]; }; struct ncsi_channel_mac_filter { u8 n_uc; u8 n_mc; u8 n_mixed; u64 bitmap; unsigned char *addrs; }; struct ncsi_channel_vlan_filter { u8 n_vids; u64 bitmap; u16 *vids; }; struct ncsi_channel_stats { u32 hnc_cnt_hi; u32 hnc_cnt_lo; u32 hnc_rx_bytes; u32 hnc_tx_bytes; u32 hnc_rx_uc_pkts; u32 hnc_rx_mc_pkts; u32 hnc_rx_bc_pkts; u32 hnc_tx_uc_pkts; u32 hnc_tx_mc_pkts; u32 hnc_tx_bc_pkts; u32 hnc_fcs_err; u32 hnc_align_err; u32 hnc_false_carrier; u32 hnc_runt_pkts; u32 hnc_jabber_pkts; u32 hnc_rx_pause_xon; u32 hnc_rx_pause_xoff; u32 hnc_tx_pause_xon; u32 hnc_tx_pause_xoff; u32 hnc_tx_s_collision; u32 hnc_tx_m_collision; u32 hnc_l_collision; u32 hnc_e_collision; u32 hnc_rx_ctl_frames; u32 hnc_rx_64_frames; u32 hnc_rx_127_frames; u32 hnc_rx_255_frames; u32 hnc_rx_511_frames; u32 hnc_rx_1023_frames; u32 hnc_rx_1522_frames; u32 hnc_rx_9022_frames; u32 hnc_tx_64_frames; u32 hnc_tx_127_frames; u32 hnc_tx_255_frames; u32 hnc_tx_511_frames; u32 hnc_tx_1023_frames; u32 hnc_tx_1522_frames; u32 hnc_tx_9022_frames; u32 hnc_rx_valid_bytes; u32 hnc_rx_runt_pkts; u32 hnc_rx_jabber_pkts; u32 ncsi_rx_cmds; u32 ncsi_dropped_cmds; u32 ncsi_cmd_type_errs; u32 ncsi_cmd_csum_errs; u32 ncsi_rx_pkts; u32 ncsi_tx_pkts; u32 ncsi_tx_aen_pkts; u32 pt_tx_pkts; u32 pt_tx_dropped; u32 pt_tx_channel_err; u32 pt_tx_us_err; u32 pt_rx_pkts; u32 pt_rx_dropped; u32 pt_rx_channel_err; u32 pt_rx_us_err; u32 pt_rx_os_err; }; struct ncsi_package; struct ncsi_channel { unsigned char id; int state; bool reconfigure_needed; spinlock_t lock; struct ncsi_package *package; struct ncsi_channel_version version; struct ncsi_channel_cap caps[6]; struct ncsi_channel_mode modes[8]; struct ncsi_channel_mac_filter mac_filter; struct ncsi_channel_vlan_filter vlan_filter; struct ncsi_channel_stats stats; struct { struct timer_list timer; bool enabled; unsigned int state; } monitor; struct list_head node; struct list_head link; }; struct ncsi_dev_priv; struct ncsi_package { unsigned char id; unsigned char uuid[16]; struct ncsi_dev_priv *ndp; spinlock_t lock; unsigned int channel_num; struct list_head channels; struct list_head node; bool multi_channel; u32 channel_whitelist; struct ncsi_channel *preferred_channel; }; struct ncsi_request { unsigned char id; bool used; unsigned int flags; struct ncsi_dev_priv *ndp; struct sk_buff *cmd; struct sk_buff *rsp; struct timer_list timer; bool enabled; u32 snd_seq; u32 snd_portid; struct nlmsghdr nlhdr; }; struct ncsi_dev_priv { struct ncsi_dev ndev; unsigned int flags; unsigned int gma_flag; spinlock_t lock; unsigned int package_probe_id; unsigned int package_num; struct list_head packages; struct ncsi_channel *hot_channel; struct ncsi_request requests[256]; unsigned int request_id; unsigned int pending_req_num; struct ncsi_package *active_package; struct ncsi_channel *active_channel; struct list_head channel_queue; struct work_struct work; struct packet_type ptype; struct list_head node; struct list_head vlan_vids; bool multi_package; bool mlx_multi_host; u32 package_whitelist; }; struct ncsi_cmd_arg { struct ncsi_dev_priv *ndp; unsigned char type; unsigned char id; unsigned char package; unsigned char channel; short unsigned int payload; unsigned int req_flags; union { unsigned char bytes[16]; short unsigned int words[8]; unsigned int dwords[4]; }; unsigned char *data; struct genl_info *info; }; struct ncsi_pkt_hdr { unsigned char mc_id; unsigned char revision; unsigned char reserved; unsigned char id; unsigned char type; unsigned char channel; __be16 length; __be32 reserved1[2]; }; struct ncsi_cmd_pkt_hdr { struct ncsi_pkt_hdr common; }; struct ncsi_cmd_pkt { struct ncsi_cmd_pkt_hdr cmd; __be32 checksum; unsigned char pad[26]; }; struct ncsi_cmd_sp_pkt { struct ncsi_cmd_pkt_hdr cmd; unsigned char reserved[3]; unsigned char hw_arbitration; __be32 checksum; unsigned char pad[22]; }; struct ncsi_cmd_dc_pkt { struct ncsi_cmd_pkt_hdr cmd; unsigned char reserved[3]; unsigned char ald; __be32 checksum; unsigned char pad[22]; }; struct ncsi_cmd_rc_pkt { struct ncsi_cmd_pkt_hdr cmd; __be32 reserved; __be32 checksum; unsigned char pad[22]; }; struct ncsi_cmd_ae_pkt { struct ncsi_cmd_pkt_hdr cmd; unsigned char reserved[3]; unsigned char mc_id; __be32 mode; __be32 checksum; unsigned char pad[18]; }; struct ncsi_cmd_sl_pkt { struct ncsi_cmd_pkt_hdr cmd; __be32 mode; __be32 oem_mode; __be32 checksum; unsigned char pad[18]; }; struct ncsi_cmd_svf_pkt { struct ncsi_cmd_pkt_hdr cmd; __be16 reserved; __be16 vlan; __be16 reserved1; unsigned char index; unsigned char enable; __be32 checksum; unsigned char pad[18]; }; struct ncsi_cmd_ev_pkt { struct ncsi_cmd_pkt_hdr cmd; unsigned char reserved[3]; unsigned char mode; __be32 checksum; unsigned char pad[22]; }; struct ncsi_cmd_sma_pkt { struct ncsi_cmd_pkt_hdr cmd; unsigned char mac[6]; unsigned char index; unsigned char at_e; __be32 checksum; unsigned char pad[18]; }; struct ncsi_cmd_ebf_pkt { struct ncsi_cmd_pkt_hdr cmd; __be32 mode; __be32 checksum; unsigned char pad[22]; }; struct ncsi_cmd_egmf_pkt { struct ncsi_cmd_pkt_hdr cmd; __be32 mode; __be32 checksum; unsigned char pad[22]; }; struct ncsi_cmd_snfc_pkt { struct ncsi_cmd_pkt_hdr cmd; unsigned char reserved[3]; unsigned char mode; __be32 checksum; unsigned char pad[22]; }; struct ncsi_cmd_oem_pkt { struct ncsi_cmd_pkt_hdr cmd; __be32 mfr_id; unsigned char data[0]; }; struct ncsi_cmd_handler { unsigned char type; int payload; int (*handler)(struct sk_buff *, struct ncsi_cmd_arg *); }; enum { NCSI_CAP_GENERIC_HWA = 1, NCSI_CAP_GENERIC_HDS = 2, NCSI_CAP_GENERIC_FC = 4, NCSI_CAP_GENERIC_FC1 = 8, NCSI_CAP_GENERIC_MC = 16, NCSI_CAP_GENERIC_HWA_UNKNOWN = 0, NCSI_CAP_GENERIC_HWA_SUPPORT = 32, NCSI_CAP_GENERIC_HWA_NOT_SUPPORT = 64, NCSI_CAP_GENERIC_HWA_RESERVED = 96, NCSI_CAP_GENERIC_HWA_MASK = 96, NCSI_CAP_GENERIC_MASK = 127, NCSI_CAP_BC_ARP = 1, NCSI_CAP_BC_DHCPC = 2, NCSI_CAP_BC_DHCPS = 4, NCSI_CAP_BC_NETBIOS = 8, NCSI_CAP_BC_MASK = 15, NCSI_CAP_MC_IPV6_NEIGHBOR = 1, NCSI_CAP_MC_IPV6_ROUTER = 2, NCSI_CAP_MC_DHCPV6_RELAY = 4, NCSI_CAP_MC_DHCPV6_WELL_KNOWN = 8, NCSI_CAP_MC_IPV6_MLD = 16, NCSI_CAP_MC_IPV6_NEIGHBOR_S = 32, NCSI_CAP_MC_MASK = 63, NCSI_CAP_AEN_LSC = 1, NCSI_CAP_AEN_CR = 2, NCSI_CAP_AEN_HDS = 4, NCSI_CAP_AEN_MASK = 7, NCSI_CAP_VLAN_ONLY = 1, NCSI_CAP_VLAN_NO = 2, NCSI_CAP_VLAN_ANY = 4, NCSI_CAP_VLAN_MASK = 7, }; struct ncsi_rsp_pkt_hdr { struct ncsi_pkt_hdr common; __be16 code; __be16 reason; }; struct ncsi_rsp_pkt { struct ncsi_rsp_pkt_hdr rsp; __be32 checksum; unsigned char pad[22]; }; struct ncsi_rsp_oem_pkt { struct ncsi_rsp_pkt_hdr rsp; __be32 mfr_id; unsigned char data[0]; }; struct ncsi_rsp_oem_mlx_pkt { unsigned char cmd_rev; unsigned char cmd; unsigned char param; unsigned char optional; unsigned char data[0]; }; struct ncsi_rsp_oem_bcm_pkt { unsigned char ver; unsigned char type; __be16 len; unsigned char data[0]; }; struct ncsi_rsp_gls_pkt { struct ncsi_rsp_pkt_hdr rsp; __be32 status; __be32 other; __be32 oem_status; __be32 checksum; unsigned char pad[10]; }; struct ncsi_rsp_gvi_pkt { struct ncsi_rsp_pkt_hdr rsp; __be32 ncsi_version; unsigned char reserved[3]; unsigned char alpha2; unsigned char fw_name[12]; __be32 fw_version; __be16 pci_ids[4]; __be32 mf_id; __be32 checksum; }; struct ncsi_rsp_gc_pkt { struct ncsi_rsp_pkt_hdr rsp; __be32 cap; __be32 bc_cap; __be32 mc_cap; __be32 buf_cap; __be32 aen_cap; unsigned char vlan_cnt; unsigned char mixed_cnt; unsigned char mc_cnt; unsigned char uc_cnt; unsigned char reserved[2]; unsigned char vlan_mode; unsigned char channel_cnt; __be32 checksum; }; struct ncsi_rsp_gp_pkt { struct ncsi_rsp_pkt_hdr rsp; unsigned char mac_cnt; unsigned char reserved[2]; unsigned char mac_enable; unsigned char vlan_cnt; unsigned char reserved1; __be16 vlan_enable; __be32 link_mode; __be32 bc_mode; __be32 valid_modes; unsigned char vlan_mode; unsigned char fc_mode; unsigned char reserved2[2]; __be32 aen_mode; unsigned char mac[6]; __be16 vlan; __be32 checksum; }; struct ncsi_rsp_gcps_pkt { struct ncsi_rsp_pkt_hdr rsp; __be32 cnt_hi; __be32 cnt_lo; __be32 rx_bytes; __be32 tx_bytes; __be32 rx_uc_pkts; __be32 rx_mc_pkts; __be32 rx_bc_pkts; __be32 tx_uc_pkts; __be32 tx_mc_pkts; __be32 tx_bc_pkts; __be32 fcs_err; __be32 align_err; __be32 false_carrier; __be32 runt_pkts; __be32 jabber_pkts; __be32 rx_pause_xon; __be32 rx_pause_xoff; __be32 tx_pause_xon; __be32 tx_pause_xoff; __be32 tx_s_collision; __be32 tx_m_collision; __be32 l_collision; __be32 e_collision; __be32 rx_ctl_frames; __be32 rx_64_frames; __be32 rx_127_frames; __be32 rx_255_frames; __be32 rx_511_frames; __be32 rx_1023_frames; __be32 rx_1522_frames; __be32 rx_9022_frames; __be32 tx_64_frames; __be32 tx_127_frames; __be32 tx_255_frames; __be32 tx_511_frames; __be32 tx_1023_frames; __be32 tx_1522_frames; __be32 tx_9022_frames; __be32 rx_valid_bytes; __be32 rx_runt_pkts; __be32 rx_jabber_pkts; __be32 checksum; }; struct ncsi_rsp_gns_pkt { struct ncsi_rsp_pkt_hdr rsp; __be32 rx_cmds; __be32 dropped_cmds; __be32 cmd_type_errs; __be32 cmd_csum_errs; __be32 rx_pkts; __be32 tx_pkts; __be32 tx_aen_pkts; __be32 checksum; }; struct ncsi_rsp_gnpts_pkt { struct ncsi_rsp_pkt_hdr rsp; __be32 tx_pkts; __be32 tx_dropped; __be32 tx_channel_err; __be32 tx_us_err; __be32 rx_pkts; __be32 rx_dropped; __be32 rx_channel_err; __be32 rx_us_err; __be32 rx_os_err; __be32 checksum; }; struct ncsi_rsp_gps_pkt { struct ncsi_rsp_pkt_hdr rsp; __be32 status; __be32 checksum; }; struct ncsi_rsp_gpuuid_pkt { struct ncsi_rsp_pkt_hdr rsp; unsigned char uuid[16]; __be32 checksum; }; struct ncsi_rsp_oem_handler { unsigned int mfr_id; int (*handler)(struct ncsi_request *); }; struct ncsi_rsp_handler { unsigned char type; int payload; int (*handler)(struct ncsi_request *); }; struct ncsi_aen_pkt_hdr { struct ncsi_pkt_hdr common; unsigned char reserved2[3]; unsigned char type; }; struct ncsi_aen_lsc_pkt { struct ncsi_aen_pkt_hdr aen; __be32 status; __be32 oem_status; __be32 checksum; unsigned char pad[14]; }; struct ncsi_aen_hncdsc_pkt { struct ncsi_aen_pkt_hdr aen; __be32 status; __be32 checksum; unsigned char pad[18]; }; struct ncsi_aen_handler { unsigned char type; int payload; int (*handler)(struct ncsi_dev_priv *, struct ncsi_aen_pkt_hdr *); }; enum { ncsi_dev_state_registered = 0, ncsi_dev_state_functional = 256, ncsi_dev_state_probe = 512, ncsi_dev_state_config = 768, ncsi_dev_state_suspend = 1024, }; enum { MLX_MC_RBT_SUPPORT = 1, MLX_MC_RBT_AVL = 8, }; enum { ncsi_dev_state_major = 65280, ncsi_dev_state_minor = 255, ncsi_dev_state_probe_deselect = 513, ncsi_dev_state_probe_package = 514, ncsi_dev_state_probe_channel = 515, ncsi_dev_state_probe_mlx_gma = 516, ncsi_dev_state_probe_mlx_smaf = 517, ncsi_dev_state_probe_cis = 518, ncsi_dev_state_probe_keep_phy = 519, ncsi_dev_state_probe_gvi = 520, ncsi_dev_state_probe_gc = 521, ncsi_dev_state_probe_gls = 522, ncsi_dev_state_probe_dp = 523, ncsi_dev_state_config_sp = 769, ncsi_dev_state_config_cis = 770, ncsi_dev_state_config_oem_gma = 771, ncsi_dev_state_config_clear_vids = 772, ncsi_dev_state_config_svf = 773, ncsi_dev_state_config_ev = 774, ncsi_dev_state_config_sma = 775, ncsi_dev_state_config_ebf = 776, ncsi_dev_state_config_dgmf = 777, ncsi_dev_state_config_ecnt = 778, ncsi_dev_state_config_ec = 779, ncsi_dev_state_config_ae = 780, ncsi_dev_state_config_gls = 781, ncsi_dev_state_config_done = 782, ncsi_dev_state_suspend_select = 1025, ncsi_dev_state_suspend_gls = 1026, ncsi_dev_state_suspend_dcnt = 1027, ncsi_dev_state_suspend_dc = 1028, ncsi_dev_state_suspend_deselect = 1029, ncsi_dev_state_suspend_done = 1030, }; struct vlan_vid { struct list_head list; __be16 proto; u16 vid; }; struct ncsi_oem_gma_handler { unsigned int mfr_id; int (*handler)(struct ncsi_cmd_arg *); }; enum ncsi_nl_commands { NCSI_CMD_UNSPEC = 0, NCSI_CMD_PKG_INFO = 1, NCSI_CMD_SET_INTERFACE = 2, NCSI_CMD_CLEAR_INTERFACE = 3, NCSI_CMD_SEND_CMD = 4, NCSI_CMD_SET_PACKAGE_MASK = 5, NCSI_CMD_SET_CHANNEL_MASK = 6, __NCSI_CMD_AFTER_LAST = 7, NCSI_CMD_MAX = 6, }; enum ncsi_nl_attrs { NCSI_ATTR_UNSPEC = 0, NCSI_ATTR_IFINDEX = 1, NCSI_ATTR_PACKAGE_LIST = 2, NCSI_ATTR_PACKAGE_ID = 3, NCSI_ATTR_CHANNEL_ID = 4, NCSI_ATTR_DATA = 5, NCSI_ATTR_MULTI_FLAG = 6, NCSI_ATTR_PACKAGE_MASK = 7, NCSI_ATTR_CHANNEL_MASK = 8, __NCSI_ATTR_AFTER_LAST = 9, NCSI_ATTR_MAX = 8, }; enum ncsi_nl_pkg_attrs { NCSI_PKG_ATTR_UNSPEC = 0, NCSI_PKG_ATTR = 1, NCSI_PKG_ATTR_ID = 2, NCSI_PKG_ATTR_FORCED = 3, NCSI_PKG_ATTR_CHANNEL_LIST = 4, __NCSI_PKG_ATTR_AFTER_LAST = 5, NCSI_PKG_ATTR_MAX = 4, }; enum ncsi_nl_channel_attrs { NCSI_CHANNEL_ATTR_UNSPEC = 0, NCSI_CHANNEL_ATTR = 1, NCSI_CHANNEL_ATTR_ID = 2, NCSI_CHANNEL_ATTR_VERSION_MAJOR = 3, NCSI_CHANNEL_ATTR_VERSION_MINOR = 4, NCSI_CHANNEL_ATTR_VERSION_STR = 5, NCSI_CHANNEL_ATTR_LINK_STATE = 6, NCSI_CHANNEL_ATTR_ACTIVE = 7, NCSI_CHANNEL_ATTR_FORCED = 8, NCSI_CHANNEL_ATTR_VLAN_LIST = 9, NCSI_CHANNEL_ATTR_VLAN_ID = 10, __NCSI_CHANNEL_ATTR_AFTER_LAST = 11, NCSI_CHANNEL_ATTR_MAX = 10, }; struct sockaddr_xdp { __u16 sxdp_family; __u16 sxdp_flags; __u32 sxdp_ifindex; __u32 sxdp_queue_id; __u32 sxdp_shared_umem_fd; }; struct xdp_ring_offset { __u64 producer; __u64 consumer; __u64 desc; __u64 flags; }; struct xdp_mmap_offsets { struct xdp_ring_offset rx; struct xdp_ring_offset tx; struct xdp_ring_offset fr; struct xdp_ring_offset cr; }; struct xdp_umem_reg { __u64 addr; __u64 len; __u32 chunk_size; __u32 headroom; __u32 flags; }; struct xdp_statistics { __u64 rx_dropped; __u64 rx_invalid_descs; __u64 tx_invalid_descs; __u64 rx_ring_full; __u64 rx_fill_ring_empty_descs; __u64 tx_ring_empty_descs; }; struct xdp_options { __u32 flags; }; struct xdp_desc { __u64 addr; __u32 len; __u32 options; }; struct xsk_map { struct bpf_map map; spinlock_t lock; struct xdp_sock *xsk_map[0]; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct xdp_ring; struct xsk_queue { u32 ring_mask; u32 nentries; u32 cached_prod; u32 cached_cons; struct xdp_ring *ring; u64 invalid_descs; u64 queue_empty_descs; }; struct xdp_ring_offset_v1 { __u64 producer; __u64 consumer; __u64 desc; }; struct xdp_mmap_offsets_v1 { struct xdp_ring_offset_v1 rx; struct xdp_ring_offset_v1 tx; struct xdp_ring_offset_v1 fr; struct xdp_ring_offset_v1 cr; }; struct xsk_map_node { struct list_head node; struct xsk_map *map; struct xdp_sock **map_entry; }; struct xdp_ring { u32 producer; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; u32 pad1; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; u32 consumer; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; u32 pad2; u32 flags; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; u32 pad3; long: 32; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; long: 64; }; struct xdp_rxtx_ring { struct xdp_ring ptrs; struct xdp_desc desc[0]; }; struct xdp_umem_ring { struct xdp_ring ptrs; u64 desc[0]; }; struct xsk_dma_map { dma_addr_t *dma_pages; struct device *dev; struct net_device *netdev; refcount_t users; struct list_head list; u32 dma_pages_cnt; bool dma_need_sync; }; struct mptcp_mib { long unsigned int mibs[37]; }; enum mptcp_event_type { MPTCP_EVENT_UNSPEC = 0, MPTCP_EVENT_CREATED = 1, MPTCP_EVENT_ESTABLISHED = 2, MPTCP_EVENT_CLOSED = 3, MPTCP_EVENT_ANNOUNCED = 6, MPTCP_EVENT_REMOVED = 7, MPTCP_EVENT_SUB_ESTABLISHED = 10, MPTCP_EVENT_SUB_CLOSED = 11, MPTCP_EVENT_SUB_PRIORITY = 13, }; struct mptcp_options_received { u64 sndr_key; u64 rcvr_key; u64 data_ack; u64 data_seq; u32 subflow_seq; u16 data_len; __sum16 csum; u16 mp_capable: 1; u16 mp_join: 1; u16 fastclose: 1; u16 reset: 1; u16 dss: 1; u16 add_addr: 1; u16 rm_addr: 1; u16 mp_prio: 1; u16 echo: 1; u16 csum_reqd: 1; u16 backup: 1; u16 deny_join_id0: 1; u32 token; u32 nonce; u64 thmac; u8 hmac[20]; u8 join_id; u8 use_map: 1; u8 dsn64: 1; u8 data_fin: 1; u8 use_ack: 1; u8 ack64: 1; u8 mpc_map: 1; u8 __unused: 2; struct mptcp_addr_info addr; struct mptcp_rm_list rm_list; u64 ahmac; u8 reset_reason: 4; u8 reset_transient: 1; }; struct mptcp_pm_data { struct mptcp_addr_info local; struct mptcp_addr_info remote; struct list_head anno_list; spinlock_t lock; u8 addr_signal; bool server_side; bool work_pending; bool accept_addr; bool accept_subflow; bool remote_deny_join_id0; u8 add_addr_signaled; u8 add_addr_accepted; u8 local_addr_used; u8 subflows; u8 status; struct mptcp_rm_list rm_list_tx; struct mptcp_rm_list rm_list_rx; }; struct mptcp_data_frag { struct list_head list; u64 data_seq; u16 data_len; u16 offset; u16 overhead; u16 already_sent; struct page *page; }; struct mptcp_sock { struct inet_connection_sock sk; u64 local_key; u64 remote_key; u64 write_seq; u64 snd_nxt; u64 ack_seq; u64 rcv_wnd_sent; u64 rcv_data_fin_seq; int wmem_reserved; struct sock *last_snd; int snd_burst; int old_wspace; u64 snd_una; u64 wnd_end; long unsigned int timer_ival; u32 token; int rmem_released; long unsigned int flags; bool can_ack; bool fully_established; bool rcv_data_fin; bool snd_data_fin_enable; bool rcv_fastclose; bool use_64bit_ack; bool csum_enabled; spinlock_t join_list_lock; struct work_struct work; struct sk_buff *ooo_last_skb; struct rb_root out_of_order_queue; struct sk_buff_head receive_queue; int tx_pending_data; struct list_head conn_list; struct list_head rtx_queue; struct mptcp_data_frag *first_pending; struct list_head join_list; struct socket *subflow; struct sock *first; struct mptcp_pm_data pm; struct { u32 space; u32 copied; u64 time; u64 rtt_us; } rcvq_space; u32 setsockopt_seq; char ca_name[16]; }; struct mptcp_subflow_request_sock { struct tcp_request_sock sk; u16 mp_capable: 1; u16 mp_join: 1; u16 backup: 1; u16 csum_reqd: 1; u16 allow_join_id0: 1; u8 local_id; u8 remote_id; u64 local_key; u64 idsn; u32 token; u32 ssn_offset; u64 thmac; u32 local_nonce; u32 remote_nonce; struct mptcp_sock *msk; struct hlist_nulls_node token_node; }; enum mptcp_data_avail { MPTCP_SUBFLOW_NODATA = 0, MPTCP_SUBFLOW_DATA_AVAIL = 1, }; struct mptcp_delegated_action { struct napi_struct napi; struct list_head head; }; struct mptcp_subflow_context { struct list_head node; u64 local_key; u64 remote_key; u64 idsn; u64 map_seq; u32 snd_isn; u32 token; u32 rel_write_seq; u32 map_subflow_seq; u32 ssn_offset; u32 map_data_len; __wsum map_data_csum; u32 map_csum_len; u32 request_mptcp: 1; u32 request_join: 1; u32 request_bkup: 1; u32 mp_capable: 1; u32 mp_join: 1; u32 fully_established: 1; u32 pm_notified: 1; u32 conn_finished: 1; u32 map_valid: 1; u32 map_csum_reqd: 1; u32 map_data_fin: 1; u32 mpc_map: 1; u32 backup: 1; u32 send_mp_prio: 1; u32 rx_eof: 1; u32 can_ack: 1; u32 disposable: 1; enum mptcp_data_avail data_avail; u32 remote_nonce; u64 thmac; u32 local_nonce; u32 remote_token; u8 hmac[20]; u8 local_id; u8 remote_id; u8 reset_seen: 1; u8 reset_transient: 1; u8 reset_reason: 4; long int delegated_status; struct list_head delegated_node; u32 setsockopt_seq; struct sock *tcp_sock; struct sock *conn; const struct inet_connection_sock_af_ops *icsk_af_ops; void (*tcp_data_ready)(struct sock *); void (*tcp_state_change)(struct sock *); void (*tcp_write_space)(struct sock *); void (*tcp_error_report)(struct sock *); struct callback_head rcu; }; enum linux_mptcp_mib_field { MPTCP_MIB_NUM = 0, MPTCP_MIB_MPCAPABLEPASSIVE = 1, MPTCP_MIB_MPCAPABLEACTIVE = 2, MPTCP_MIB_MPCAPABLEACTIVEACK = 3, MPTCP_MIB_MPCAPABLEPASSIVEACK = 4, MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK = 5, MPTCP_MIB_MPCAPABLEACTIVEFALLBACK = 6, MPTCP_MIB_TOKENFALLBACKINIT = 7, MPTCP_MIB_RETRANSSEGS = 8, MPTCP_MIB_JOINNOTOKEN = 9, MPTCP_MIB_JOINSYNRX = 10, MPTCP_MIB_JOINSYNACKRX = 11, MPTCP_MIB_JOINSYNACKMAC = 12, MPTCP_MIB_JOINACKRX = 13, MPTCP_MIB_JOINACKMAC = 14, MPTCP_MIB_DSSNOMATCH = 15, MPTCP_MIB_INFINITEMAPRX = 16, MPTCP_MIB_DSSTCPMISMATCH = 17, MPTCP_MIB_DATACSUMERR = 18, MPTCP_MIB_OFOQUEUETAIL = 19, MPTCP_MIB_OFOQUEUE = 20, MPTCP_MIB_OFOMERGE = 21, MPTCP_MIB_NODSSWINDOW = 22, MPTCP_MIB_DUPDATA = 23, MPTCP_MIB_ADDADDR = 24, MPTCP_MIB_ECHOADD = 25, MPTCP_MIB_PORTADD = 26, MPTCP_MIB_JOINPORTSYNRX = 27, MPTCP_MIB_JOINPORTSYNACKRX = 28, MPTCP_MIB_JOINPORTACKRX = 29, MPTCP_MIB_MISMATCHPORTSYNRX = 30, MPTCP_MIB_MISMATCHPORTACKRX = 31, MPTCP_MIB_RMADDR = 32, MPTCP_MIB_RMSUBFLOW = 33, MPTCP_MIB_MPPRIOTX = 34, MPTCP_MIB_MPPRIORX = 35, MPTCP_MIB_RCVPRUNED = 36, __MPTCP_MIB_MAX = 37, }; struct trace_event_raw_mptcp_subflow_get_send { struct trace_entry ent; bool active; bool free; u32 snd_wnd; u32 pace; u8 backup; u64 ratio; char __data[0]; }; struct trace_event_raw_mptcp_dump_mpext { struct trace_entry ent; u64 data_ack; u64 data_seq; u32 subflow_seq; u16 data_len; u16 csum; u8 use_map; u8 dsn64; u8 data_fin; u8 use_ack; u8 ack64; u8 mpc_map; u8 frozen; u8 reset_transient; u8 reset_reason; u8 csum_reqd; char __data[0]; }; struct trace_event_raw_ack_update_msk { struct trace_entry ent; u64 data_ack; u64 old_snd_una; u64 new_snd_una; u64 new_wnd_end; u64 msk_wnd_end; char __data[0]; }; struct trace_event_raw_subflow_check_data_avail { struct trace_entry ent; u8 status; const void *skb; char __data[0]; }; struct trace_event_data_offsets_mptcp_subflow_get_send {}; struct trace_event_data_offsets_mptcp_dump_mpext {}; struct trace_event_data_offsets_ack_update_msk {}; struct trace_event_data_offsets_subflow_check_data_avail {}; typedef void (*btf_trace_mptcp_subflow_get_send)(void *, struct mptcp_subflow_context *); typedef void (*btf_trace_get_mapping_status)(void *, struct mptcp_ext *); typedef void (*btf_trace_ack_update_msk)(void *, u64, u64, u64, u64, u64); typedef void (*btf_trace_subflow_check_data_avail)(void *, __u8, struct sk_buff *); struct mptcp_skb_cb { u64 map_seq; u64 end_seq; u32 offset; u8 has_rxtstamp: 1; }; enum { MPTCP_CMSG_TS = 1, }; struct mptcp_sendmsg_info { int mss_now; int size_goal; u16 limit; u16 sent; unsigned int flags; }; struct subflow_send_info { struct sock *ssk; u64 ratio; }; struct csum_pseudo_header { __be64 data_seq; __be32 subflow_seq; __be16 data_len; __sum16 csum; }; enum mapping_status { MAPPING_OK = 0, MAPPING_INVALID = 1, MAPPING_EMPTY = 2, MAPPING_DATA_FIN = 3, MAPPING_DUMMY = 4, }; enum mptcp_addr_signal_status { MPTCP_ADD_ADDR_SIGNAL = 0, MPTCP_ADD_ADDR_ECHO = 1, MPTCP_ADD_ADDR_IPV6 = 2, MPTCP_ADD_ADDR_PORT = 3, MPTCP_RM_ADDR_SIGNAL = 4, }; struct mptcp_pm_add_entry; struct token_bucket { spinlock_t lock; int chain_len; struct hlist_nulls_head req_chain; struct hlist_nulls_head msk_chain; }; struct mptcp_pernet { struct ctl_table_header *ctl_table_hdr; u8 mptcp_enabled; unsigned int add_addr_timeout; u8 checksum_enabled; u8 allow_join_initial_addr_port; }; enum mptcp_pm_status { MPTCP_PM_ADD_ADDR_RECEIVED = 0, MPTCP_PM_ADD_ADDR_SEND_ACK = 1, MPTCP_PM_RM_ADDR_RECEIVED = 2, MPTCP_PM_ESTABLISHED = 3, MPTCP_PM_ALREADY_ESTABLISHED = 4, MPTCP_PM_SUBFLOW_ESTABLISHED = 5, }; enum { INET_ULP_INFO_UNSPEC = 0, INET_ULP_INFO_NAME = 1, INET_ULP_INFO_TLS = 2, INET_ULP_INFO_MPTCP = 3, __INET_ULP_INFO_MAX = 4, }; enum { MPTCP_SUBFLOW_ATTR_UNSPEC = 0, MPTCP_SUBFLOW_ATTR_TOKEN_REM = 1, MPTCP_SUBFLOW_ATTR_TOKEN_LOC = 2, MPTCP_SUBFLOW_ATTR_RELWRITE_SEQ = 3, MPTCP_SUBFLOW_ATTR_MAP_SEQ = 4, MPTCP_SUBFLOW_ATTR_MAP_SFSEQ = 5, MPTCP_SUBFLOW_ATTR_SSN_OFFSET = 6, MPTCP_SUBFLOW_ATTR_MAP_DATALEN = 7, MPTCP_SUBFLOW_ATTR_FLAGS = 8, MPTCP_SUBFLOW_ATTR_ID_REM = 9, MPTCP_SUBFLOW_ATTR_ID_LOC = 10, MPTCP_SUBFLOW_ATTR_PAD = 11, __MPTCP_SUBFLOW_ATTR_MAX = 12, }; enum { MPTCP_PM_ATTR_UNSPEC = 0, MPTCP_PM_ATTR_ADDR = 1, MPTCP_PM_ATTR_RCV_ADD_ADDRS = 2, MPTCP_PM_ATTR_SUBFLOWS = 3, __MPTCP_PM_ATTR_MAX = 4, }; enum { MPTCP_PM_ADDR_ATTR_UNSPEC = 0, MPTCP_PM_ADDR_ATTR_FAMILY = 1, MPTCP_PM_ADDR_ATTR_ID = 2, MPTCP_PM_ADDR_ATTR_ADDR4 = 3, MPTCP_PM_ADDR_ATTR_ADDR6 = 4, MPTCP_PM_ADDR_ATTR_PORT = 5, MPTCP_PM_ADDR_ATTR_FLAGS = 6, MPTCP_PM_ADDR_ATTR_IF_IDX = 7, __MPTCP_PM_ADDR_ATTR_MAX = 8, }; enum { MPTCP_PM_CMD_UNSPEC = 0, MPTCP_PM_CMD_ADD_ADDR = 1, MPTCP_PM_CMD_DEL_ADDR = 2, MPTCP_PM_CMD_GET_ADDR = 3, MPTCP_PM_CMD_FLUSH_ADDRS = 4, MPTCP_PM_CMD_SET_LIMITS = 5, MPTCP_PM_CMD_GET_LIMITS = 6, MPTCP_PM_CMD_SET_FLAGS = 7, __MPTCP_PM_CMD_AFTER_LAST = 8, }; enum mptcp_event_attr { MPTCP_ATTR_UNSPEC = 0, MPTCP_ATTR_TOKEN = 1, MPTCP_ATTR_FAMILY = 2, MPTCP_ATTR_LOC_ID = 3, MPTCP_ATTR_REM_ID = 4, MPTCP_ATTR_SADDR4 = 5, MPTCP_ATTR_SADDR6 = 6, MPTCP_ATTR_DADDR4 = 7, MPTCP_ATTR_DADDR6 = 8, MPTCP_ATTR_SPORT = 9, MPTCP_ATTR_DPORT = 10, MPTCP_ATTR_BACKUP = 11, MPTCP_ATTR_ERROR = 12, MPTCP_ATTR_FLAGS = 13, MPTCP_ATTR_TIMEOUT = 14, MPTCP_ATTR_IF_IDX = 15, MPTCP_ATTR_RESET_REASON = 16, MPTCP_ATTR_RESET_FLAGS = 17, __MPTCP_ATTR_AFTER_LAST = 18, }; struct mptcp_pm_addr_entry { struct list_head list; struct mptcp_addr_info addr; u8 flags; int ifindex; struct socket *lsk; }; struct mptcp_pm_add_entry___2 { struct list_head list; struct mptcp_addr_info addr; struct timer_list add_timer; struct mptcp_sock *sock; u8 retrans_times; }; struct pm_nl_pernet { spinlock_t lock; struct list_head local_addr_list; unsigned int addrs; unsigned int add_addr_signal_max; unsigned int add_addr_accept_max; unsigned int local_addr_max; unsigned int subflows_max; unsigned int next_id; long unsigned int id_bitmap[4]; }; struct join_entry { u32 token; u32 remote_nonce; u32 local_nonce; u8 join_id; u8 local_id; u8 backup; u8 valid; }; struct pcibios_fwaddrmap { struct list_head list; struct pci_dev *dev; resource_size_t fw_addr[17]; }; struct pci_check_idx_range { int start; int end; }; struct pci_raw_ops { int (*read)(unsigned int, unsigned int, unsigned int, int, int, u32 *); int (*write)(unsigned int, unsigned int, unsigned int, int, int, u32); }; struct acpi_table_mcfg { struct acpi_table_header header; u8 reserved[8]; }; struct acpi_mcfg_allocation { u64 address; u16 pci_segment; u8 start_bus_number; u8 end_bus_number; u32 reserved; }; struct pci_mmcfg_hostbridge_probe { u32 bus; u32 devfn; u32 vendor; u32 device; const char * (*probe)(); }; typedef bool (*check_reserved_t)(u64, u64, enum e820_type); struct physdev_restore_msi { uint8_t bus; uint8_t devfn; }; struct physdev_setup_gsi { int gsi; uint8_t triggering; uint8_t polarity; }; struct xen_pci_frontend_ops { int (*enable_msi)(struct pci_dev *, int *); void (*disable_msi)(struct pci_dev *); int (*enable_msix)(struct pci_dev *, int *, int); void (*disable_msix)(struct pci_dev *); }; struct xen_msi_ops { int (*setup_msi_irqs)(struct pci_dev *, int, int); void (*teardown_msi_irqs)(struct pci_dev *); }; struct xen_device_domain_owner { domid_t domain; struct pci_dev *dev; struct list_head list; }; struct pci_root_info { struct acpi_pci_root_info common; struct pci_sysdata sd; bool mcfg_added; u8 start_bus; u8 end_bus; }; struct irq_info___3 { u8 bus; u8 devfn; struct { u8 link; u16 bitmap; } __attribute__((packed)) irq[4]; u8 slot; u8 rfu; }; struct irq_routing_table { u32 signature; u16 version; u16 size; u8 rtr_bus; u8 rtr_devfn; u16 exclusive_irqs; u16 rtr_vendor; u16 rtr_device; u32 miniport_data; u8 rfu[11]; u8 checksum; struct irq_info___3 slots[0]; }; struct irq_router { char *name; u16 vendor; u16 device; int (*get)(struct pci_dev *, struct pci_dev *, int); int (*set)(struct pci_dev *, struct pci_dev *, int, int); }; struct irq_router_handler { u16 vendor; int (*probe)(struct irq_router *, struct pci_dev *, u16); }; struct pci_setup_rom { struct setup_data data; uint16_t vendor; uint16_t devid; uint64_t pcilen; long unsigned int segment; long unsigned int bus; long unsigned int device; long unsigned int function; uint8_t romdata[0]; }; enum pci_bf_sort_state { pci_bf_sort_default = 0, pci_force_nobf = 1, pci_force_bf = 2, pci_dmi_bf = 3, }; struct pci_root_res { struct list_head list; struct resource res; }; struct pci_root_info___2 { struct list_head list; char name[12]; struct list_head resources; struct resource busn; int node; int link; }; struct amd_hostbridge { u32 bus; u32 slot; u32 device; }; struct saved_msr { bool valid; struct msr_info info; }; struct saved_msrs { unsigned int num; struct saved_msr *array; }; struct saved_context { struct pt_regs regs; u16 ds; u16 es; u16 fs; u16 gs; long unsigned int kernelmode_gs_base; long unsigned int usermode_gs_base; long unsigned int fs_base; long unsigned int cr0; long unsigned int cr2; long unsigned int cr3; long unsigned int cr4; u64 misc_enable; bool misc_enable_saved; struct saved_msrs saved_msrs; long unsigned int efer; u16 gdt_pad; struct desc_ptr gdt_desc; u16 idt_pad; struct desc_ptr idt; u16 ldt; u16 tss; long unsigned int tr; long unsigned int safety; long unsigned int return_address; } __attribute__((packed)); typedef int (*pm_cpu_match_t)(const struct x86_cpu_id *); struct restore_data_record { long unsigned int jump_address; long unsigned int jump_address_phys; long unsigned int cr3; long unsigned int magic; long unsigned int e820_checksum; }; #ifndef BPF_NO_PRESERVE_ACCESS_INDEX #pragma clang attribute pop #endif #endif /* __VMLINUX_H__ */ ================================================ FILE: service/firewall/interception/interception_default.go ================================================ //go:build !windows && !linux package interception import ( "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" ) // start starts the interception. func startInterception(_ chan packet.Packet) error { log.Critical("interception: this platform has no support for packet interception - a lot of functionality will be broken") return nil } // stop starts the interception. func stopInterception() error { return nil } // ResetVerdictOfAllConnections resets all connections so they are forced to go thought the firewall again. func ResetVerdictOfAllConnections() error { return nil } // UpdateVerdictOfConnection updates the verdict of the given connection in the OS integration. func UpdateVerdictOfConnection(conn *network.Connection) error { return nil } ================================================ FILE: service/firewall/interception/interception_linux.go ================================================ package interception import ( "time" bandwidth "github.com/safing/portmaster/service/firewall/interception/ebpf/bandwidth" conn_listener "github.com/safing/portmaster/service/firewall/interception/ebpf/connection_listener" "github.com/safing/portmaster/service/firewall/interception/nfq" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" ) // start starts the interception. func startInterception(packets chan packet.Packet) error { // Start packet interception via nfqueue. err := StartNfqueueInterception(packets) if err != nil { return err } // Start ebpf new connection listener. module.mgr.Go("ebpf connection listener", func(wc *mgr.WorkerCtx) error { return conn_listener.ConnectionListenerWorker(wc.Ctx(), packets) }) // Start ebpf bandwidth stats monitor. module.mgr.Go("ebpf bandwidth stats monitor", func(wc *mgr.WorkerCtx) error { return bandwidth.BandwidthStatsWorker(wc.Ctx(), 1*time.Second, BandwidthUpdates) }) return nil } // stop starts the interception. func stopInterception() error { return StopNfqueueInterception() } // ResetVerdictOfAllConnections resets all connections so they are forced to go thought the firewall again. func ResetVerdictOfAllConnections() error { return nfq.DeleteAllMarkedConnection() } // UpdateVerdictOfConnection deletes the verdict of the given connection so it can be initialized again with the next packet. func UpdateVerdictOfConnection(conn *network.Connection) error { return nfq.DeleteMarkedConnection(conn) } ================================================ FILE: service/firewall/interception/interception_windows.go ================================================ package interception import ( "fmt" "time" "github.com/safing/portmaster/base/log" kext1 "github.com/safing/portmaster/service/firewall/interception/windowskext" kext2 "github.com/safing/portmaster/service/firewall/interception/windowskext2" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" ) var useOldKext = false // start starts the interception. func startInterception(packets chan packet.Packet) error { kextFile, err := module.instance.BinaryUpdates().GetFile("portmaster-kext.sys") if err != nil { return fmt.Errorf("interception: could not get kext sys: %s", err) } err = kext2.Init(kextFile.Path()) if err != nil { return fmt.Errorf("interception: could not init windows kext: %s", err) } err = kext2.Start() if err != nil { return fmt.Errorf("interception: could not start windows kext: %s", err) } version, err := kext2.GetVersion() if err != nil { return fmt.Errorf("interception: failed to read version: %s", err) } log.Debugf("Kext version: %s", version.String()) if version.Major < 2 { useOldKext = true // Transfer ownership. kext1.SetKextHandler(kext2.GetKextHandle()) kext1.SetKextService(kext2.GetKextServiceHandle(), kextFile.Path()) // Start packet handler. module.mgr.Go("kext packet handler", func(w *mgr.WorkerCtx) error { kext1.Handler(w.Ctx(), packets) return nil }) // Start bandwidth stats monitor. module.mgr.Go("kext bandwidth stats monitor", func(w *mgr.WorkerCtx) error { return kext1.BandwidthStatsWorker(w.Ctx(), 1*time.Second, BandwidthUpdates) }) } else { // Start packet handler. module.mgr.Go("kext packet handler", func(w *mgr.WorkerCtx) error { kext2.Handler(w.Ctx(), packets, BandwidthUpdates) return nil }) // Start bandwidth stats monitor. module.mgr.Go("kext bandwidth request worker", func(w *mgr.WorkerCtx) error { timer := time.NewTicker(1 * time.Second) defer timer.Stop() for { select { case <-timer.C: err := kext2.SendBandwidthStatsRequest() if err != nil { return err } case <-w.Done(): return nil } } }) // Start kext logging. The worker will periodically send request to the kext to send logs. module.mgr.Go("kext log request worker", func(w *mgr.WorkerCtx) error { timer := time.NewTicker(1 * time.Second) defer timer.Stop() for { select { case <-timer.C: err := kext2.SendLogRequest() if err != nil { return err } case <-w.Done(): return nil } } }) module.mgr.Go("kext clean ended connection worker", func(w *mgr.WorkerCtx) error { timer := time.NewTicker(30 * time.Second) defer timer.Stop() for { select { case <-timer.C: err := kext2.SendCleanEndedConnection() if err != nil { return err } case <-w.Done(): return nil } } }) } return nil } // stop starts the interception. func stopInterception() error { if useOldKext { return kext1.Stop() } return kext2.Stop() } // ResetVerdictOfAllConnections resets all connections so they are forced to go thought the firewall again. func ResetVerdictOfAllConnections() error { if useOldKext { return kext1.ClearCache() } return kext2.ClearCache() } // UpdateVerdictOfConnection updates the verdict of the given connection in the kernel extension. func UpdateVerdictOfConnection(conn *network.Connection) error { if useOldKext { return kext1.UpdateVerdict(conn) } return kext2.UpdateVerdict(conn) } // GetKextVersion returns the version of the kernel extension. func GetKextVersion() (string, error) { if useOldKext { version, err := kext1.GetVersion() if err != nil { return "", err } return version.String(), nil } else { version, err := kext2.GetVersion() if err != nil { return "", err } return version.String(), nil } } ================================================ FILE: service/firewall/interception/introspection.go ================================================ package interception import ( "flag" "fmt" "os" "sync" "time" "github.com/safing/portmaster/base/log" ) var ( packetMetricsDestination string metrics = &packetMetrics{} ) func init() { flag.StringVar(&packetMetricsDestination, "write-packet-metrics", "", "write packet metrics to the specified file") } type ( performanceRecord struct { start int64 duration time.Duration verdict string } packetMetrics struct { done chan struct{} l sync.Mutex records []*performanceRecord } ) func (pm *packetMetrics) record(tp *tracedPacket, verdict string) { go func(start int64, duration time.Duration) { pm.l.Lock() defer pm.l.Unlock() if pm.done == nil { return } pm.records = append(pm.records, &performanceRecord{ start: start, duration: duration, verdict: verdict, }) }(tp.start.UnixNano(), time.Since(tp.start)) } func (pm *packetMetrics) stop() { pm.l.Lock() defer pm.l.Unlock() if pm.done == nil { return } close(pm.done) pm.done = nil } func (pm *packetMetrics) writeMetrics() { if packetMetricsDestination == "" { return } f, err := os.Create(packetMetricsDestination) if err != nil { log.Errorf("Failed to create packet metrics file: %s", err) return } defer func() { _ = f.Close() }() pm.l.Lock() pm.done = make(chan struct{}) done := pm.done pm.l.Unlock() for { select { case <-done: return case <-time.After(time.Second * 5): } pm.l.Lock() records := pm.records pm.records = nil pm.l.Unlock() for _, r := range records { fmt.Fprintf(f, "%d;%s;%s;%.2f\n", r.start, r.verdict, r.duration, float64(r.duration)/float64(time.Microsecond)) } } } ================================================ FILE: service/firewall/interception/module.go ================================================ package interception import ( "errors" "flag" "sync/atomic" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/updates" ) // Interception is the packet interception module. type Interception struct { mgr *mgr.Manager instance instance EventStartStopState *mgr.EventMgr[bool] // true if started, false if stopped } // Manager returns the module manager. func (i *Interception) Manager() *mgr.Manager { return i.mgr } // Start starts the module. func (i *Interception) Start() error { defer func() { i.EventStartStopState.Submit(isStarted.Load()) }() return start() } // Stop stops the module. func (i *Interception) Stop() error { defer func() { i.EventStartStopState.Submit(isStarted.Load()) }() return stop() } // IsStarted returns whether the interception is currently started. func (i *Interception) IsStarted() bool { return isStarted.Load() } var ( // Packets is a stream of interception network packets. Packets = make(chan packet.Packet, 1000) // BandwidthUpdates is a stream of bandwidth usage update for connections. BandwidthUpdates = make(chan *packet.BandwidthUpdate, 1000) disableInterception bool isStarted atomic.Bool ) func init() { flag.BoolVar(&disableInterception, "disable-interception", false, "disable packet interception; this breaks a lot of functionality") } // Start starts the interception. func start() error { if disableInterception { log.Warning("interception: packet interception is disabled via flag - this breaks a lot of functionality") return nil } if !isStarted.CompareAndSwap(false, true) { return nil // already running } inputPackets := Packets if packetMetricsDestination != "" { go metrics.writeMetrics() inputPackets = make(chan packet.Packet) go func() { for p := range inputPackets { Packets <- tracePacket(p) } }() } err := startInterception(inputPackets) if err != nil { log.Errorf("interception: failed to start module: %q", err) log.Debug("interception: cleaning up after failed start...") metrics.stop() if e := stopInterception(); e != nil { log.Debugf("interception: error cleaning up after failed start: %q", e.Error()) } isStarted.Store(false) } return err } // Stop starts the interception. func stop() error { if disableInterception { return nil } if !isStarted.CompareAndSwap(true, false) { return nil // not running } metrics.stop() if err := stopInterception(); err != nil { log.Errorf("failed to stop interception module: %s", err) } return nil } var ( module *Interception shimLoaded atomic.Bool ) // New returns a new Interception module. func New(instance instance) (*Interception, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Interception") module = &Interception{ mgr: m, instance: instance, EventStartStopState: mgr.NewEventMgr[bool]("IsStarted", m), } return module, nil } type instance interface { BinaryUpdates() *updates.Updater } ================================================ FILE: service/firewall/interception/nfq/conntrack.go ================================================ //go:build linux package nfq import ( "encoding/binary" "errors" "fmt" ct "github.com/florianl/go-conntrack" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network" ) var nfct *ct.Nfct // Conntrack handler. NFCT: Network Filter Connection Tracking. // InitNFCT initializes the network filter conntrack library. func InitNFCT() error { var err error nfct, err = ct.Open(&ct.Config{}) if err != nil { return err } return nil } // TeardownNFCT deinitializes the network filter conntrack library. func TeardownNFCT() { if nfct != nil { _ = nfct.Close() } } // DeleteAllMarkedConnection deletes all marked entries from the conntrack table. func DeleteAllMarkedConnection() error { if nfct == nil { return errors.New("nfq: nfct not initialized") } // Delete all ipv4 marked connections deleted := deleteMarkedConnections(nfct, ct.IPv4) if netenv.IPv6Enabled() { // Delete all ipv6 marked connections deleted += deleteMarkedConnections(nfct, ct.IPv6) } log.Infof("nfq: deleted %d conntrack entries to reset permanent connection verdicts", deleted) return nil } func deleteMarkedConnections(nfct *ct.Nfct, f ct.Family) (deleted int) { // initialize variables permanentFlags := []uint32{MarkAcceptAlways, MarkBlockAlways, MarkDropAlways, MarkRerouteNS, MarkRerouteSPN} filter := ct.FilterAttr{} filter.MarkMask = []byte{0xFF, 0xFF, 0xFF, 0xFF} filter.Mark = []byte{0x00, 0x00, 0x00, 0x00} // 4 zeros starting value numberOfErrors := 0 var deleteError error = nil // Get all connections from the specified family (ipv4 or ipv6) for _, mark := range permanentFlags { binary.BigEndian.PutUint32(filter.Mark, mark) // Little endian is in reverse not sure why. BigEndian makes it in correct order. currentConnections, err := nfct.Query(ct.Conntrack, f, filter) if err != nil { log.Warningf("nfq: error on conntrack query: %s", err) continue } for _, connection := range currentConnections { deleteError = nfct.Delete(ct.Conntrack, ct.IPv4, connection) if err != nil { numberOfErrors++ } else { deleted++ } } } if numberOfErrors > 0 { log.Warningf("nfq: failed to delete %d conntrack entries last error is: %s", numberOfErrors, deleteError) } return deleted } // DeleteMarkedConnection removes a specific connection from the conntrack table. func DeleteMarkedConnection(conn *network.Connection) error { if nfct == nil { return errors.New("nfq: nfct not initialized") } con := ct.Con{ Origin: &ct.IPTuple{ Src: &conn.LocalIP, Dst: &conn.Entity.IP, Proto: &ct.ProtoTuple{ Number: &conn.Entity.Protocol, SrcPort: &conn.LocalPort, DstPort: &conn.Entity.Port, }, }, } connections, err := nfct.Get(ct.Conntrack, ct.IPv4, con) if err != nil { return fmt.Errorf("nfq: failed to find entry for connection %s: %w", conn.String(), err) } if len(connections) > 1 { log.Warningf("nfq: multiple entries found for single connection: %s -> %d", conn.String(), len(connections)) } for _, connection := range connections { deleteErr := nfct.Delete(ct.Conntrack, ct.IPv4, connection) if err == nil { err = deleteErr } } if err != nil { log.Warningf("nfq: error while deleting conntrack entries for connection %s: %s", conn.String(), err) } return nil } ================================================ FILE: service/firewall/interception/nfq/nfq.go ================================================ //go:build linux // Package nfq contains a nfqueue library experiment. package nfq import ( "context" "runtime" "strings" "sync/atomic" "time" "github.com/florianl/go-nfqueue" "github.com/tevino/abool" "golang.org/x/sys/unix" "github.com/safing/portmaster/base/log" pmpacket "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/process" ) // Queue wraps a nfqueue. type Queue struct { id uint16 afFamily uint8 nf atomic.Value packets chan pmpacket.Packet cancelSocketCallback context.CancelFunc restart chan struct{} pendingVerdicts uint64 verdictCompleted chan struct{} } func (q *Queue) getNfq() *nfqueue.Nfqueue { return q.nf.Load().(*nfqueue.Nfqueue) //nolint:forcetypeassert // TODO: Check. } // New opens a new nfQueue. func New(qid uint16, v6 bool) (*Queue, error) { //nolint:gocognit afFamily := unix.AF_INET if v6 { afFamily = unix.AF_INET6 } ctx, cancel := context.WithCancel(context.Background()) q := &Queue{ id: qid, afFamily: uint8(afFamily), nf: atomic.Value{}, restart: make(chan struct{}, 1), packets: make(chan pmpacket.Packet, 1000), cancelSocketCallback: cancel, verdictCompleted: make(chan struct{}, 1), } // Do not retry if the first one fails immediately as it // might point to a deeper integration error that's not fixable // with retrying ... if err := q.open(ctx); err != nil { return nil, err } go func() { Wait: for { select { case <-ctx.Done(): return case <-q.restart: runtime.Gosched() } for { err := q.open(ctx) if err == nil { continue Wait } // Wait 100 ms and then try again ... log.Errorf("Failed to open nfqueue: %s", err) select { case <-ctx.Done(): return case <-time.After(100 * time.Millisecond): } } } }() return q, nil } // open opens a new netlink socket and creates a new nfqueue. // Upon success, the new nfqueue is atomically stored in Queue.nf. // Users must use Queue.getNfq to access it. open does not care about // any other value or queue that might be stored in Queue.nf at // the time open is called. func (q *Queue) open(ctx context.Context) error { cfg := &nfqueue.Config{ NfQueue: q.id, MaxPacketLen: 1600, // mtu is normally around 1500, make sure to capture it. MaxQueueLen: 0xffff, AfFamily: q.afFamily, Copymode: nfqueue.NfQnlCopyPacket, ReadTimeout: 1000 * time.Millisecond, WriteTimeout: 1000 * time.Millisecond, } nf, err := nfqueue.Open(cfg) if err != nil { return err } if err := nf.RegisterWithErrorFunc(ctx, q.packetHandler(ctx), q.handleError); err != nil { _ = nf.Close() return err } q.nf.Store(nf) return nil } func (q *Queue) handleError(e error) int { // embedded interface is required to work-around some // dep-vendoring weirdness if opError, ok := e.(interface { //nolint:errorlint // TODO: Check if we can remove workaround. Timeout() bool Temporary() bool }); ok { if opError.Timeout() || opError.Temporary() { c := atomic.LoadUint64(&q.pendingVerdicts) if c > 0 { log.Tracef("nfqueue: waiting for %d pending verdicts", c) for atomic.LoadUint64(&q.pendingVerdicts) > 0 { // must NOT use c here <-q.verdictCompleted } } return 0 } } // Check if the queue was already closed. Unfortunately, the exposed error // variable is in an internal stdlib package. Therefore, check for the error // string instead. :( // Official error variable is defined here: // https://github.com/golang/go/blob/0e85fd7561de869add933801c531bf25dee9561c/src/internal/poll/fd.go#L24 if !strings.HasSuffix(e.Error(), "use of closed file") { log.Errorf("nfqueue: encountered error while receiving packets: %s\n", e.Error()) } // Close the existing socket if nf := q.getNfq(); nf != nil { // Call Close() on the Con directly, as nf.Close() calls waitgroup.Wait(), which then may deadlock. _ = nf.Con.Close() } // Trigger a restart of the queue q.restart <- struct{}{} return 1 } func (q *Queue) packetHandler(ctx context.Context) func(nfqueue.Attribute) int { return func(attrs nfqueue.Attribute) int { if attrs.PacketID == nil { // we need a packet id to set a verdict, // if we don't get an ID there's hardly anything // we can do. return 0 } pkt := &packet{ pktID: *attrs.PacketID, queue: q, verdictSet: make(chan struct{}), verdictPending: abool.New(), } pkt.Info().PID = process.UndefinedProcessID pkt.Info().SeenAt = time.Now() if attrs.Payload == nil { // There is not payload. log.Warningf("nfqueue: packet #%d has no payload", pkt.pktID) return 0 } if err := pmpacket.ParseLayer3(*attrs.Payload, &pkt.Base); err != nil { log.Warningf("nfqueue: failed to parse payload: %s", err) _ = pkt.Drop() return 0 } select { case q.packets <- pkt: // DEBUG: // log.Tracef("nfqueue: queued packet %s (%s -> %s) after %s", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.Info().SeenAt)) case <-ctx.Done(): return 0 case <-time.After(time.Second): log.Warningf("nfqueue: failed to queue packet (%s since it was handed over by the kernel)", time.Since(pkt.Info().SeenAt)) } go func() { select { case <-pkt.verdictSet: case <-time.After(20 * time.Second): log.Warningf("nfqueue: no verdict set for packet %s (%s -> %s) after %s, dropping", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.Info().SeenAt)) if err := pkt.Drop(); err != nil { log.Warningf("nfqueue: failed to apply default-drop to unveridcted packet %s (%s -> %s)", pkt.ID(), pkt.Info().Src, pkt.Info().Dst) } } }() return 0 // continue calling this fn } } // Destroy destroys the queue. Any error encountered is logged. func (q *Queue) Destroy() { if q == nil { return } q.cancelSocketCallback() if nf := q.getNfq(); nf != nil { if err := nf.Close(); err != nil { log.Errorf("nfqueue: failed to close queue %d: %s", q.id, err) } } } // PacketChannel returns the packet channel. func (q *Queue) PacketChannel() <-chan pmpacket.Packet { return q.packets } ================================================ FILE: service/firewall/interception/nfq/packet.go ================================================ //go:build linux package nfq import ( "errors" "fmt" "sync/atomic" "github.com/florianl/go-nfqueue" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" pmpacket "github.com/safing/portmaster/service/network/packet" ) // Firewalling marks used by the Portmaster. // See TODO on packet.mark() on their relevance // and a possibility to remove most IPtables rules. const ( MarkAccept = 1700 MarkBlock = 1701 MarkDrop = 1702 MarkAcceptAlways = 1710 MarkBlockAlways = 1711 MarkDropAlways = 1712 MarkRerouteNS = 1799 MarkRerouteSPN = 1717 ) func markToString(mark int) string { switch mark { case MarkAccept: return "Accept" case MarkBlock: return "Block" case MarkDrop: return "Drop" case MarkAcceptAlways: return "AcceptAlways" case MarkBlockAlways: return "BlockAlways" case MarkDropAlways: return "DropAlways" case MarkRerouteNS: return "RerouteNS" case MarkRerouteSPN: return "RerouteSPN" } return "unknown" } // packet implements the packet.Packet interface. type packet struct { pmpacket.Base pktID uint32 queue *Queue verdictSet chan struct{} verdictPending *abool.AtomicBool } func (pkt *packet) ID() string { return fmt.Sprintf("pkt:%d qid:%d", pkt.pktID, pkt.queue.id) } // LoadPacketData does nothing on Linux, as data is always fully parsed. func (pkt *packet) LoadPacketData() error { return nil } // TODO(ppacher): revisit the following behavior: // // The legacy implementation of nfqueue (and the interception) module // always accept a packet but may mark it so that a subsequent rule in // the C17 chain drops, rejects or modifies it. // // For drop/return we could use the actual nfQueue verdicts Drop and Stop. // Re-routing to local NS or SPN can be done by modifying the packet here // and using SetVerdictModPacket and reject can be implemented using a simple // raw-socket. func (pkt *packet) mark(mark int) (err error) { if pkt.verdictPending.SetToIf(false, true) { defer close(pkt.verdictSet) return pkt.setMark(mark) } return errors.New("verdict already set") } func (pkt *packet) setMark(mark int) error { atomic.AddUint64(&pkt.queue.pendingVerdicts, 1) defer func() { atomic.AddUint64(&pkt.queue.pendingVerdicts, ^uint64(0)) select { case pkt.queue.verdictCompleted <- struct{}{}: default: } }() for { if err := pkt.queue.getNfq().SetVerdictWithMark(pkt.pktID, nfqueue.NfAccept, mark); err != nil { // embedded interface is required to work-around some // dep-vendoring weirdness if opErr, ok := err.(interface { //nolint:errorlint // TODO: Check if we can remove workaround. Timeout() bool Temporary() bool }); ok { if opErr.Timeout() || opErr.Temporary() { continue } } log.Tracer(pkt.Ctx()).Errorf("nfqueue: failed to set verdict %s for %s (%s -> %s): %s", markToString(mark), pkt.ID(), pkt.Info().Src, pkt.Info().Dst, err) return err } break } // DEBUG: // log.Tracer(pkt.Ctx()).Tracef( // "nfqueue: marking packet %s (%s -> %s) on queue %d with %s after %s", // pkt.ID(), pkt.Info().Src, pkt.Info().Dst, pkt.queue.id, // markToString(mark), time.Since(pkt.Info().SeenAt), // ) return nil } func (pkt *packet) Accept() error { return pkt.mark(MarkAccept) } func (pkt *packet) Block() error { if pkt.Info().Protocol == pmpacket.ICMP { // ICMP packets attributed to a blocked connection are always allowed, as // rejection ICMP packets will have the same mark as the blocked // connection. This is why we need to drop blocked ICMP packets instead. return pkt.mark(MarkDrop) } return pkt.mark(MarkBlock) } func (pkt *packet) Drop() error { return pkt.mark(MarkDrop) } func (pkt *packet) PermanentAccept() error { // If the packet is localhost only, do not permanently accept the outgoing // packet, as the packet mark will be copied to the connection mark, which // will stick and it will bypass the incoming queue. if !pkt.Info().Inbound && pkt.Info().Dst.IsLoopback() { return pkt.Accept() } return pkt.mark(MarkAcceptAlways) } func (pkt *packet) PermanentBlock() error { if pkt.Info().Protocol == pmpacket.ICMP || pkt.Info().Protocol == pmpacket.ICMPv6 { // ICMP packets attributed to a blocked connection are always allowed, as // rejection ICMP packets will have the same mark as the blocked // connection. This is why we need to drop blocked ICMP packets instead. return pkt.mark(MarkDropAlways) } return pkt.mark(MarkBlockAlways) } func (pkt *packet) PermanentDrop() error { return pkt.mark(MarkDropAlways) } func (pkt *packet) RerouteToNameserver() error { return pkt.mark(MarkRerouteNS) } func (pkt *packet) RerouteToTunnel() error { return pkt.mark(MarkRerouteSPN) } ================================================ FILE: service/firewall/interception/nfqueue_linux.go ================================================ package interception import ( "fmt" "sort" "strings" "sync/atomic" "time" "github.com/coreos/go-iptables/iptables" "github.com/hashicorp/go-multierror" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/firewall/interception/nfq" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/packet" ) var ( v4chains []string v4rules []string v4once []string v6chains []string v6rules []string v6once []string out4Queue nfQueue in4Queue nfQueue out6Queue nfQueue in6Queue nfQueue isRunning atomic.Bool shutdownSignal = make(chan struct{}) ) // nfQueue encapsulates nfQueue providers. type nfQueue interface { PacketChannel() <-chan packet.Packet Destroy() } func init() { v4chains = []string{ "mangle PORTMASTER-INGEST-OUTPUT", "mangle PORTMASTER-INGEST-INPUT", "filter PORTMASTER-FILTER", "nat PORTMASTER-REDIRECT", } v4rules = []string{ // stenya: Preserve original packet marks for permanently allowed connections (connmark 1710/AcceptAlways) // to ensure compatibility with other tools that also rely on packet marks. // This rule is placed before `CONNMARK --restore-mark` to prevent overwriting the original mark. // (Example: WireGuard/wg-quick relies on packet marks; changing them would break its routing). "mangle PORTMASTER-INGEST-OUTPUT -m mark ! --mark 0 -m connmark --mark 1710 -j RETURN", "mangle PORTMASTER-INGEST-OUTPUT -j CONNMARK --restore-mark", "mangle PORTMASTER-INGEST-OUTPUT -m mark --mark 0 -j NFQUEUE --queue-num 17040 --queue-bypass", // stenya: Preserve original packet marks, similar to the OUTPUT chain (not sure if this is really needed for INPUT). "mangle PORTMASTER-INGEST-INPUT -m mark ! --mark 0 -m connmark --mark 1710 -j RETURN", "mangle PORTMASTER-INGEST-INPUT -j CONNMARK --restore-mark", "mangle PORTMASTER-INGEST-INPUT -m mark --mark 0 -j NFQUEUE --queue-num 17140 --queue-bypass", "filter PORTMASTER-FILTER -m mark --mark 0 -j DROP", // stenya: Preserve original packet marks. "filter PORTMASTER-FILTER -m connmark --mark 1710 -j RETURN", "filter PORTMASTER-FILTER -m mark --mark 1700 -j RETURN", // Accepting ICMP packets with mark 1701 is required for rejecting to work, // as the rejection ICMP packet will have the same mark. Blocked ICMP // packets will always result in a drop within the Portmaster. "filter PORTMASTER-FILTER -m mark --mark 1701 -p icmp -j RETURN", "filter PORTMASTER-FILTER -m mark --mark 1701 -j REJECT --reject-with icmp-admin-prohibited", "filter PORTMASTER-FILTER -m mark --mark 1702 -j DROP", "filter PORTMASTER-FILTER -j CONNMARK --save-mark", "filter PORTMASTER-FILTER -m mark --mark 1710 -j RETURN", // Accepting ICMP packets with mark 1711 is required for rejecting to work, // as the rejection ICMP packet will have the same mark. Blocked ICMP // packets will always result in a drop within the Portmaster. "filter PORTMASTER-FILTER -m mark --mark 1711 -p icmp -j RETURN", "filter PORTMASTER-FILTER -m mark --mark 1711 -j REJECT --reject-with icmp-admin-prohibited", "filter PORTMASTER-FILTER -m mark --mark 1712 -j DROP", "filter PORTMASTER-FILTER -m mark --mark 1717 -j RETURN", "nat PORTMASTER-REDIRECT -m mark --mark 1799 -p udp -j DNAT --to 127.0.0.17:53", "nat PORTMASTER-REDIRECT -m mark --mark 1717 -p tcp -j DNAT --to 127.0.0.17:717", "nat PORTMASTER-REDIRECT -m mark --mark 1717 -p udp -j DNAT --to 127.0.0.17:717", // "nat PORTMASTER-REDIRECT -m mark --mark 1717 ! -p tcp ! -p udp -j DNAT --to 127.0.0.17", } v4once = []string{ "mangle OUTPUT -j PORTMASTER-INGEST-OUTPUT", "mangle INPUT -j PORTMASTER-INGEST-INPUT", "filter OUTPUT -j PORTMASTER-FILTER", "filter INPUT -j PORTMASTER-FILTER", "nat OUTPUT -j PORTMASTER-REDIRECT", } v6chains = []string{ "mangle PORTMASTER-INGEST-OUTPUT", "mangle PORTMASTER-INGEST-INPUT", "filter PORTMASTER-FILTER", "nat PORTMASTER-REDIRECT", } v6rules = []string{ "mangle PORTMASTER-INGEST-OUTPUT -m mark ! --mark 0 -m connmark --mark 1710 -j RETURN", "mangle PORTMASTER-INGEST-OUTPUT -j CONNMARK --restore-mark", "mangle PORTMASTER-INGEST-OUTPUT -m mark --mark 0 -j NFQUEUE --queue-num 17060 --queue-bypass", "mangle PORTMASTER-INGEST-INPUT -m mark ! --mark 0 -m connmark --mark 1710 -j RETURN", "mangle PORTMASTER-INGEST-INPUT -j CONNMARK --restore-mark", "mangle PORTMASTER-INGEST-INPUT -m mark --mark 0 -j NFQUEUE --queue-num 17160 --queue-bypass", "filter PORTMASTER-FILTER -m mark --mark 0 -j DROP", "filter PORTMASTER-FILTER -m connmark --mark 1710 -j RETURN", "filter PORTMASTER-FILTER -m mark --mark 1700 -j RETURN", "filter PORTMASTER-FILTER -m mark --mark 1701 -p icmpv6 -j RETURN", "filter PORTMASTER-FILTER -m mark --mark 1701 -j REJECT --reject-with icmp6-adm-prohibited", "filter PORTMASTER-FILTER -m mark --mark 1702 -j DROP", "filter PORTMASTER-FILTER -j CONNMARK --save-mark", "filter PORTMASTER-FILTER -m mark --mark 1710 -j RETURN", "filter PORTMASTER-FILTER -m mark --mark 1711 -p icmpv6 -j RETURN", "filter PORTMASTER-FILTER -m mark --mark 1711 -j REJECT --reject-with icmp6-adm-prohibited", "filter PORTMASTER-FILTER -m mark --mark 1712 -j DROP", "filter PORTMASTER-FILTER -m mark --mark 1717 -j RETURN", "nat PORTMASTER-REDIRECT -m mark --mark 1799 -p udp -j DNAT --to [::1]:53", "nat PORTMASTER-REDIRECT -m mark --mark 1717 -p tcp -j DNAT --to [::1]:717", "nat PORTMASTER-REDIRECT -m mark --mark 1717 -p udp -j DNAT --to [::1]:717", // "nat PORTMASTER-REDIRECT -m mark --mark 1717 ! -p tcp ! -p udp -j DNAT --to [::1]", } v6once = []string{ "mangle OUTPUT -j PORTMASTER-INGEST-OUTPUT", "mangle INPUT -j PORTMASTER-INGEST-INPUT", "filter OUTPUT -j PORTMASTER-FILTER", "filter INPUT -j PORTMASTER-FILTER", "nat OUTPUT -j PORTMASTER-REDIRECT", } // Reverse because we'd like to insert in a loop _ = sort.Reverse(sort.StringSlice(v4once)) // silence vet (sort is used just like in the docs) _ = sort.Reverse(sort.StringSlice(v6once)) // silence vet (sort is used just like in the docs) } func activateNfqueueFirewall() error { if err := activateIPTables(iptables.ProtocolIPv4, v4rules, v4once, v4chains); err != nil { return err } if netenv.IPv6Enabled() { if err := activateIPTables(iptables.ProtocolIPv6, v6rules, v6once, v6chains); err != nil { return err } } if err := nfq.InitNFCT(); err != nil { return err } _ = nfq.DeleteAllMarkedConnection() return nil } // DeactivateNfqueueFirewall drops portmaster related IP tables rules. // Any errors encountered accumulated into a *multierror.Error. func DeactivateNfqueueFirewall() error { // IPv4 var result *multierror.Error if err := deactivateIPTables(iptables.ProtocolIPv4, v4once, v4chains); err != nil { result = multierror.Append(result, err) } // IPv6 if netenv.IPv6Enabled() { if err := deactivateIPTables(iptables.ProtocolIPv6, v6once, v6chains); err != nil { result = multierror.Append(result, err) } } _ = nfq.DeleteAllMarkedConnection() nfq.TeardownNFCT() return result.ErrorOrNil() } func activateIPTables(protocol iptables.Protocol, rules, once, chains []string) error { tbls, err := iptables.NewWithProtocol(protocol) if err != nil { return err } for _, chain := range chains { splittedRule := strings.Split(chain, " ") if err = tbls.ClearChain(splittedRule[0], splittedRule[1]); err != nil { return err } } for _, rule := range rules { splittedRule := strings.Split(rule, " ") if err = tbls.Append(splittedRule[0], splittedRule[1], splittedRule[2:]...); err != nil { return err } } for _, rule := range once { splittedRule := strings.Split(rule, " ") err := tbls.InsertUnique(splittedRule[0], splittedRule[1], 1, splittedRule[2:]...) if err != nil { return err } } return nil } // ensureJumpRulesAtTop ensures that all "once" rules (the jump rules // into Portmaster chains) are at the first position in their respective chains // for both IPv4 and IPv6. It returns the list of rules that were out of position // and had to be reinserted. func ensureJumpRulesAtTop() (reinsertedRules []string, err error) { reinsertedRules, err = reinsertDisplacedRules(iptables.ProtocolIPv4, v4once) if err != nil { return nil, err } if netenv.IPv6Enabled() { v6ReinsertedRules, err := reinsertDisplacedRules(iptables.ProtocolIPv6, v6once) if err != nil { return nil, err } reinsertedRules = append(reinsertedRules, v6ReinsertedRules...) } return reinsertedRules, nil } // reinsertDisplacedRules checks each rule in once and, if it is not already // at position 1 of its chain, moves it there. To avoid a window where packets // can bypass the firewall, a temporary placeholder rule is inserted first, // then the original rule is deleted and reinserted at position 1, and finally // the placeholder is removed. Returns the subset of rules that required moving. // Required rules format example: "filter OUTPUT -j PORTMASTER-FILTER" func reinsertDisplacedRules(protocol iptables.Protocol, once []string) (reinsertedRules []string, err error) { tbls, err := iptables.NewWithProtocol(protocol) if err != nil { return nil, err } var rulesToUpdate []string for _, onceRule := range once { splittedRule := strings.Split(onceRule, " ") table := splittedRule[0] chain := splittedRule[1] // get the first rule of the chain firstRule, err := tbls.ListById(table, chain, 1) if err != nil { return nil, err } // check if the first rule of the chain is the portmaster rule pmChainName := splittedRule[len(splittedRule)-1] if !strings.HasSuffix(firstRule, pmChainName) { rulesToUpdate = append(rulesToUpdate, onceRule) } } comment := []string{"-m", "comment", "--comment", `TEMPORARY_RULE`} for _, rule := range rulesToUpdate { splittedRule := strings.Split(rule, " ") table := splittedRule[0] // "filter" chain := splittedRule[1] // "OUTPUT" ruleSpec := splittedRule[2:] // "-j PORTMASTER-FILTER" tmpRuleSpec := append(append([]string{}, comment...), ruleSpec...) // "-m comment --comment "TEMPORARY_RULE" -j PORTMASTER-FILTER" // Insert the temporary rule on the first position err = tbls.Insert(table, chain, 1, tmpRuleSpec...) if err != nil { return nil, fmt.Errorf("failed to insert temporary rule '%s' into chain '%s' in table '%s': %w", tmpRuleSpec, chain, table, err) } // delete the original rule and re-insert it on the first position err = tbls.Delete(table, chain, ruleSpec...) if err != nil { return nil, fmt.Errorf("failed to delete original rule '%s' from chain '%s' in table '%s': %w", ruleSpec, chain, table, err) } err = tbls.Insert(table, chain, 1, ruleSpec...) if err != nil { return nil, fmt.Errorf("failed to re-insert original rule '%s' into chain '%s' in table '%s': %w", ruleSpec, chain, table, err) } // delete the temporary rule err = tbls.Delete(table, chain, tmpRuleSpec...) if err != nil { return nil, fmt.Errorf("failed to delete temporary rule '%s' from chain '%s' in table '%s': %w", tmpRuleSpec, chain, table, err) } } return rulesToUpdate, nil } func deactivateIPTables(protocol iptables.Protocol, rules, chains []string) error { tbls, err := iptables.NewWithProtocol(protocol) if err != nil { return err } var multierr *multierror.Error for _, rule := range rules { splittedRule := strings.Split(rule, " ") ok, err := tbls.Exists(splittedRule[0], splittedRule[1], splittedRule[2:]...) if err != nil { multierr = multierror.Append(multierr, err) } if ok { if err = tbls.Delete(splittedRule[0], splittedRule[1], splittedRule[2:]...); err != nil { multierr = multierror.Append(multierr, err) } } } for _, chain := range chains { splittedRule := strings.Split(chain, " ") if err = tbls.ClearChain(splittedRule[0], splittedRule[1]); err != nil { multierr = multierror.Append(multierr, err) } if err = tbls.DeleteChain(splittedRule[0], splittedRule[1]); err != nil { multierr = multierror.Append(multierr, err) } } return multierr.ErrorOrNil() } // StartNfqueueInterception starts the nfqueue interception. func StartNfqueueInterception(packets chan<- packet.Packet) (err error) { if !isRunning.CompareAndSwap(false, true) { return nil // already running } // Reset shutdown signal shutdownSignal = make(chan struct{}) err = activateNfqueueFirewall() if err != nil { return fmt.Errorf("could not initialize nfqueue: %w", err) } out4Queue, err = nfq.New(17040, false) if err != nil { return fmt.Errorf("nfqueue(IPv4, out): %w", err) } in4Queue, err = nfq.New(17140, false) if err != nil { return fmt.Errorf("nfqueue(IPv4, in): %w", err) } if netenv.IPv6Enabled() { out6Queue, err = nfq.New(17060, true) if err != nil { return fmt.Errorf("nfqueue(IPv6, out): %w", err) } in6Queue, err = nfq.New(17160, true) if err != nil { return fmt.Errorf("nfqueue(IPv6, in): %w", err) } } else { log.Warningf("interception: no IPv6 stack detected, disabling IPv6 network integration") out6Queue = &disabledNfQueue{} in6Queue = &disabledNfQueue{} } module.mgr.Go("nfqueue packet handler", func(_ *mgr.WorkerCtx) error { return handleInterception(packets) }) // Safety check: ensure Portmaster's iptables jump rules remain at the top of their chains. // During system boot, other services may insert their own iptables rules, potentially // displacing Portmaster's rules and causing traffic to bypass the firewall. // The check runs a few times after startup with increasing delays to cover this window. // A continuous periodic check is intentionally avoided - it would not react immediately // to rule changes anyway, and the overhead is not justified after the boot stage. // TODO: consider a more reactive approach using netlink to detect iptables changes in real time. module.mgr.Go("iptables rule order maintenance (startup)", func(w *mgr.WorkerCtx) error { for _, d := range []time.Duration{5 * time.Second, 10 * time.Second, 30 * time.Second} { select { case <-time.After(d): case <-w.Done(): return nil case <-shutdownSignal: return nil } if updatedRules, err := ensureJumpRulesAtTop(); err != nil { log.Errorf("interception: failed to ensure iptables jump rules at top: %v", err) } else if len(updatedRules) > 0 { log.Warningf("interception: the following iptables rules were found out of position and have been reinserted at the top of their chains: %v", updatedRules) } } return nil }) return nil } // StopNfqueueInterception stops the nfqueue interception. func StopNfqueueInterception() error { if !isRunning.CompareAndSwap(true, false) { return nil // not running } // Signal shutdown to packet handler defer close(shutdownSignal) if out4Queue != nil { out4Queue.Destroy() } if in4Queue != nil { in4Queue.Destroy() } if out6Queue != nil { out6Queue.Destroy() } if in6Queue != nil { in6Queue.Destroy() } err := DeactivateNfqueueFirewall() if err != nil { return fmt.Errorf("interception: error while deactivating nfqueue: %w", err) } return nil } func handleInterception(packets chan<- packet.Packet) error { for { var pkt packet.Packet select { case <-shutdownSignal: return nil case pkt = <-out4Queue.PacketChannel(): pkt.SetOutbound() case pkt = <-in4Queue.PacketChannel(): pkt.SetInbound() case pkt = <-out6Queue.PacketChannel(): pkt.SetOutbound() case pkt = <-in6Queue.PacketChannel(): pkt.SetInbound() } select { case packets <- pkt: case <-shutdownSignal: return nil } } } type disabledNfQueue struct{} func (dnfq *disabledNfQueue) PacketChannel() <-chan packet.Packet { return nil } func (dnfq *disabledNfQueue) Destroy() {} ================================================ FILE: service/firewall/interception/packet_tracer.go ================================================ package interception import ( "time" "github.com/safing/portmaster/service/network/packet" ) type tracedPacket struct { start time.Time packet.Packet } func tracePacket(p packet.Packet) packet.Packet { return &tracedPacket{ start: time.Now(), Packet: p, } } func (p *tracedPacket) markServed(v string) { if packetMetricsDestination == "" { return } metrics.record(p, v) } func (p *tracedPacket) Accept() error { defer p.markServed("accept") return p.Packet.Accept() } func (p *tracedPacket) Block() error { defer p.markServed("block") return p.Packet.Block() } func (p *tracedPacket) Drop() error { defer p.markServed("drop") return p.Packet.Drop() } func (p *tracedPacket) PermanentAccept() error { defer p.markServed("perm-accept") return p.Packet.PermanentAccept() } func (p *tracedPacket) PermanentBlock() error { defer p.markServed("perm-block") return p.Packet.PermanentBlock() } func (p *tracedPacket) PermanentDrop() error { defer p.markServed("perm-drop") return p.Packet.PermanentDrop() } func (p *tracedPacket) RerouteToNameserver() error { defer p.markServed("reroute-ns") return p.Packet.RerouteToNameserver() } func (p *tracedPacket) RerouteToTunnel() error { defer p.markServed("reroute-tunnel") return p.Packet.RerouteToTunnel() } ================================================ FILE: service/firewall/interception/windowskext/bandwidth_stats.go ================================================ //go:build windows // +build windows package windowskext // This file contains example code how to read bandwidth stats from the kext. Its not ment to be used in production. import ( "context" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/packet" ) type Rxtxdata struct { rx uint64 tx uint64 } type Key struct { localIP [4]uint32 remoteIP [4]uint32 localPort uint16 remotePort uint16 ipv6 bool protocol uint8 } var m = make(map[Key]Rxtxdata) func BandwidthStatsWorker(ctx context.Context, collectInterval time.Duration, bandwidthUpdates chan *packet.BandwidthUpdate) error { // Setup ticker. ticker := time.NewTicker(collectInterval) defer ticker.Stop() // Collect bandwidth at every tick. for { select { case <-ticker.C: err := reportBandwidth(ctx, bandwidthUpdates) if err != nil { return err } case <-ctx.Done(): return nil } } } func reportBandwidth(ctx context.Context, bandwidthUpdates chan *packet.BandwidthUpdate) error { stats, err := GetConnectionsStats() if err != nil { return err } // Report all statistics. for i, stat := range stats { connID := packet.CreateConnectionID( packet.IPProtocol(stat.protocol), convertArrayToIP(stat.localIP, stat.ipV6 == 1), stat.localPort, convertArrayToIP(stat.remoteIP, stat.ipV6 == 1), stat.remotePort, false, ) update := &packet.BandwidthUpdate{ ConnID: connID, BytesReceived: stat.receivedBytes, BytesSent: stat.transmittedBytes, Method: packet.Additive, } select { case bandwidthUpdates <- update: case <-ctx.Done(): return nil default: log.Warningf("kext: bandwidth update queue is full, skipping rest of batch (%d entries)", len(stats)-i) return nil } } return nil } func StartBandwidthConsoleLogger() { go func() { ticker := time.NewTicker(2 * time.Second) defer ticker.Stop() for range ticker.C { conns, err := GetConnectionsStats() if err != nil { continue } for _, conn := range conns { if conn.receivedBytes == 0 && conn.transmittedBytes == 0 { continue } key := Key{ localIP: conn.localIP, remoteIP: conn.remoteIP, localPort: conn.localPort, remotePort: conn.remotePort, ipv6: conn.ipV6 == 1, protocol: conn.protocol, } // First we get a "copy" of the entry if entry, ok := m[key]; ok { // Then we modify the copy entry.rx += conn.receivedBytes entry.tx += conn.transmittedBytes // Then we reassign map entry m[key] = entry } else { m[key] = Rxtxdata{ rx: conn.receivedBytes, tx: conn.transmittedBytes, } } } log.Debug("----------------------------------") for key, value := range m { log.Debugf( "Conn: %d %s:%d %s:%d rx:%d tx:%d", key.protocol, convertArrayToIP(key.localIP, key.ipv6), key.localPort, convertArrayToIP(key.remoteIP, key.ipv6), key.remotePort, value.rx, value.tx, ) } } }() } ================================================ FILE: service/firewall/interception/windowskext/doc.go ================================================ // +build windows // Package windowskext provides network interception capabilities on windows via the Portmaster Kernel Extension. package windowskext ================================================ FILE: service/firewall/interception/windowskext/handler.go ================================================ //go:build windows // +build windows package windowskext import ( "context" "encoding/binary" "errors" "fmt" "net" "time" "unsafe" "github.com/safing/portmaster/service/process" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" ) const ( // VerdictRequestFlagFastTrackPermitted is set on packets that have been // already permitted by the kernel extension and the verdict request is only // informational. VerdictRequestFlagFastTrackPermitted = 1 // VerdictRequestFlagSocketAuth indicates that the verdict request is for a // connection that was intercepted on an ALE layer instead of in the network // stack itself. Thus, no packet data is available. VerdictRequestFlagSocketAuth = 2 // VerdictRequestFlagExpectSocketAuth indicates that the next verdict // requests is expected to be an informational socket auth request from // the ALE layer. VerdictRequestFlagExpectSocketAuth = 4 ) // Do not change the order of the members! The structure is used to communicate with the kernel extension. // VerdictRequest is the request structure from the Kext. type VerdictRequest struct { id uint32 // ID from RegisterPacket pid uint64 // Process ID - info only packets direction uint8 ipV6 uint8 // True: IPv6, False: IPv4 protocol uint8 // Protocol flags uint8 // Flags localIP [4]uint32 // Source Address remoteIP [4]uint32 // Destination Address localPort uint16 // Source Port remotePort uint16 // Destination port _ uint32 // compartmentID _ uint32 // interfaceIndex _ uint32 // subInterfaceIndex packetSize uint32 } // Do not change the order of the members! The structure is used to communicate with the kernel extension. type VerdictInfo struct { id uint32 // ID from RegisterPacket verdict network.Verdict // verdict for the connection } // Do not change the order of the members! The structure to communicate with the kernel extension. type VerdictUpdateInfo struct { localIP [4]uint32 // Source Address, only srcIP[0] if IPv4 remoteIP [4]uint32 // Destination Address localPort uint16 // Source Port remotePort uint16 // Destination port ipV6 uint8 // True: IPv6, False: IPv4 protocol uint8 // Protocol (UDP, TCP, ...) verdict uint8 // New verdict } type ConnectionStat struct { localIP [4]uint32 //Source Address, only srcIP[0] if IPv4 remoteIP [4]uint32 //Destination Address localPort uint16 //Source Port remotePort uint16 //Destination port receivedBytes uint64 //Number of bytes recived on this connection transmittedBytes uint64 //Number of bytes transsmited from this connection ipV6 uint8 //True: IPv6, False: IPv4 protocol uint8 //Protocol (UDP, TCP, ...) } type VersionInfo struct { major uint8 minor uint8 revision uint8 build uint8 } func (v *VersionInfo) String() string { return fmt.Sprintf("%d.%d.%d.%d", v.major, v.minor, v.revision, v.build) } // Handler transforms received packets to the Packet interface. func Handler(ctx context.Context, packets chan packet.Packet) { for { packetInfo, err := RecvVerdictRequest() if err != nil { // Check if we are done with processing. if errors.Is(err, ErrKextNotReady) { return } log.Warningf("failed to get packet from windows kext: %s", err) continue } if packetInfo == nil { continue } // log.Tracef("packet: %+v", packetInfo) // New Packet new := &Packet{ verdictRequest: packetInfo, verdictSet: abool.NewBool(false), } info := new.Info() info.Inbound = packetInfo.direction > 0 info.InTunnel = false info.Protocol = packet.IPProtocol(packetInfo.protocol) info.PID = int(packetInfo.pid) info.SeenAt = time.Now() // Check PID if info.PID == 0 { // Windows does not have zero PIDs. // Set to UndefinedProcessID. info.PID = process.UndefinedProcessID } // Set IP version if packetInfo.ipV6 == 1 { info.Version = packet.IPv6 } else { info.Version = packet.IPv4 } // Set IPs if info.Inbound { // Inbound info.Src = convertArrayToIP(packetInfo.remoteIP, info.Version == packet.IPv6) info.Dst = convertArrayToIP(packetInfo.localIP, info.Version == packet.IPv6) } else { // Outbound info.Src = convertArrayToIP(packetInfo.localIP, info.Version == packet.IPv6) info.Dst = convertArrayToIP(packetInfo.remoteIP, info.Version == packet.IPv6) } // Set Ports if info.Inbound { // Inbound info.SrcPort = packetInfo.remotePort info.DstPort = packetInfo.localPort } else { // Outbound info.SrcPort = packetInfo.localPort info.DstPort = packetInfo.remotePort } packets <- new } } // convertArrayToIP converts an array of uint32 values to a net.IP address. func convertArrayToIP(input [4]uint32, ipv6 bool) net.IP { if !ipv6 { addressBuf := make([]byte, 4) binary.BigEndian.PutUint32(addressBuf, input[0]) return net.IP(addressBuf) } addressBuf := make([]byte, 16) for i := 0; i < 4; i++ { binary.BigEndian.PutUint32(addressBuf[i*4:i*4+4], input[i]) } return net.IP(addressBuf) } func ipAddressToArray(ip net.IP, isIPv6 bool) [4]uint32 { array := [4]uint32{0} if isIPv6 { for i := 0; i < 4; i++ { binary.BigEndian.PutUint32(asByteArrayWithLength(&array[i], 4), getUInt32Value(&ip[i])) } } else { binary.BigEndian.PutUint32(asByteArrayWithLength(&array[0], 4), getUInt32Value(&ip[0])) } return array } func asByteArray[T any](obj *T) []byte { return unsafe.Slice((*byte)(unsafe.Pointer(obj)), unsafe.Sizeof(*obj)) } func asByteArrayWithLength[T any](obj *T, size uint32) []byte { return unsafe.Slice((*byte)(unsafe.Pointer(obj)), size) } func getUInt32Value[T any](obj *T) uint32 { return *(*uint32)(unsafe.Pointer(obj)) } ================================================ FILE: service/firewall/interception/windowskext/kext.go ================================================ //go:build windows // +build windows package windowskext import ( "errors" "fmt" "sync" "syscall" "unsafe" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" "golang.org/x/sys/windows" ) // Package errors var ( ErrKextNotReady = errors.New("the windows kernel extension (driver) is not ready to accept commands") ErrNoPacketID = errors.New("the packet has no ID, possibly because it was fast-tracked by the kernel extension") kextLock sync.RWMutex driverPath string kextHandle windows.Handle service *KextService ) const ( winErrInvalidData = uintptr(windows.ERROR_INVALID_DATA) winInvalidHandleValue = windows.Handle(^uintptr(0)) // Max value driverName = "PortmasterKext" ) // Init initializes the DLL and the Kext (Kernel Driver). func Init(path string) error { kextHandle = winInvalidHandleValue driverPath = path return nil } // Start intercepting. func Start() error { kextLock.Lock() defer kextLock.Unlock() // initialize and start driver service var err error service, err = createKextService(driverName, driverPath) if err != nil { return fmt.Errorf("failed to create service: %w", err) } running, err := service.isRunning() if err == nil && !running { err = service.start(true) if err != nil { return fmt.Errorf("failed to start service: %w", err) } } else if err != nil { return fmt.Errorf("service not initialized: %w", err) } // Open the driver filename := `\\.\` + driverName kextHandle, err = openDriver(filename) // driver was not installed if err != nil { return fmt.Errorf("failed to open driver: %q %w", filename, err) } return nil } func SetKextHandler(handle windows.Handle) { kextHandle = handle } func SetKextService(handle windows.Handle, path string) { service = &KextService{handle: handle} driverPath = path } // Stop intercepting. func Stop() error { // Prepare kernel for shutdown err := shutdownRequest() if err != nil { log.Warningf("winkext: shutdown request failed: %s", err) } kextLock.Lock() defer kextLock.Unlock() err = closeDriver(kextHandle) if err != nil { log.Warningf("winkext: failed to close the handle: %s", err) } err = service.stop(true) if err != nil { log.Warningf("winkext: failed to stop service: %s", err) } // Driver file may change on the next start so it's better to delete the service err = service.delete() if err != nil { log.Warningf("winkext: failed to delete service: %s", err) } kextHandle = winInvalidHandleValue return nil } func shutdownRequest() error { kextLock.RLock() defer kextLock.RUnlock() if kextHandle == winInvalidHandleValue { return ErrKextNotReady } // Sent a shutdown request so the kernel extension can prepare. _, err := deviceIOControl(kextHandle, IOCTL_SHUTDOWN_REQUEST, nil, nil) return err } // RecvVerdictRequest waits for the next verdict request from the kext. If a timeout is reached, both *VerdictRequest and error will be nil. func RecvVerdictRequest() (*VerdictRequest, error) { kextLock.RLock() defer kextLock.RUnlock() if kextHandle == winInvalidHandleValue { return nil, ErrKextNotReady } // DEBUG: // timestamp := time.Now() // defer log.Tracef("winkext: getting verdict request took %s", time.Since(timestamp)) // Initialize struct for the output data var new VerdictRequest // Make driver request data := asByteArray(&new) bytesRead, err := deviceIOControl(kextHandle, IOCTL_RECV_VERDICT_REQ, nil, data) if err != nil { return nil, err } if bytesRead == 0 { return nil, nil // no error, no new verdict request } return &new, nil } // SetVerdict sets the verdict for a packet and/or connection. func SetVerdict(pkt *Packet, verdict network.Verdict) error { if pkt.verdictRequest.pid != 0 { return nil // Ignore info only packets } if pkt.verdictRequest.id == 0 { log.Tracer(pkt.Ctx()).Errorf("kext: failed to set verdict %s: no packet ID", verdict) return ErrNoPacketID } kextLock.RLock() defer kextLock.RUnlock() if kextHandle == winInvalidHandleValue { log.Tracer(pkt.Ctx()).Errorf("kext: failed to set verdict %s: kext not ready", verdict) return ErrKextNotReady } verdictInfo := VerdictInfo{pkt.verdictRequest.id, verdict} // Make driver request data := asByteArray(&verdictInfo) _, err := deviceIOControl(kextHandle, IOCTL_SET_VERDICT, data, nil) if err != nil { log.Tracer(pkt.Ctx()).Errorf("kext: failed to set verdict %s on packet %d", verdict, pkt.verdictRequest.id) return err } return nil } // GetPayload returns the payload of a packet. func GetPayload(packetID uint32, packetSize uint32) ([]byte, error) { if packetID == 0 { return nil, ErrNoPacketID } // Check if driver is initialized kextLock.RLock() defer kextLock.RUnlock() if kextHandle == winInvalidHandleValue { return nil, ErrKextNotReady } buf := make([]byte, packetSize) // Combine id and length payload := struct { id uint32 length uint32 }{packetID, packetSize} // Make driver request data := asByteArray(&payload) bytesRead, err := deviceIOControl(kextHandle, IOCTL_GET_PAYLOAD, data, unsafe.Slice(&buf[0], packetSize)) if err != nil { return nil, err } // check the result and return if bytesRead == 0 { return nil, errors.New("windows kext did not return any data") } if bytesRead < uint32(len(buf)) { return buf[:bytesRead], nil } return buf, nil } func ClearCache() error { kextLock.RLock() defer kextLock.RUnlock() // Check if driver is initialized if kextHandle == winInvalidHandleValue { log.Error("kext: failed to clear the cache: kext not ready") return ErrKextNotReady } // Make driver request _, err := deviceIOControl(kextHandle, IOCTL_CLEAR_CACHE, nil, nil) return err } func UpdateVerdict(conn *network.Connection) error { kextLock.RLock() defer kextLock.RUnlock() // Check if driver is initialized if kextHandle == winInvalidHandleValue { log.Error("kext: failed to clear the cache: kext not ready") return ErrKextNotReady } var isIpv6 uint8 = 0 if conn.IPVersion == packet.IPv6 { isIpv6 = 1 } // initialize variables info := VerdictUpdateInfo{ ipV6: isIpv6, protocol: uint8(conn.IPProtocol), localIP: ipAddressToArray(conn.LocalIP, isIpv6 == 1), localPort: conn.LocalPort, remoteIP: ipAddressToArray(conn.Entity.IP, isIpv6 == 1), remotePort: conn.Entity.Port, verdict: uint8(conn.Verdict), } // Make driver request data := asByteArray(&info) _, err := deviceIOControl(kextHandle, IOCTL_UPDATE_VERDICT, data, nil) return err } func GetVersion() (*VersionInfo, error) { kextLock.RLock() defer kextLock.RUnlock() // Check if driver is initialized if kextHandle == winInvalidHandleValue { log.Error("kext: failed to clear the cache: kext not ready") return nil, ErrKextNotReady } data := make([]uint8, 4) _, err := deviceIOControl(kextHandle, IOCTL_VERSION, nil, data) if err != nil { return nil, err } version := &VersionInfo{ major: data[0], minor: data[1], revision: data[2], build: data[3], } return version, nil } var sizeOfConnectionStat = uint32(unsafe.Sizeof(ConnectionStat{})) func GetConnectionsStats() ([]ConnectionStat, error) { kextLock.RLock() defer kextLock.RUnlock() // Check if driver is initialized if kextHandle == winInvalidHandleValue { log.Error("kext: failed to clear the cache: kext not ready") return nil, ErrKextNotReady } var data [100]ConnectionStat size := len(data) bytesReturned, err := deviceIOControl(kextHandle, IOCTL_GET_CONNECTIONS_STAT, asByteArray(&size), asByteArray(&data)) if err != nil { return nil, err } return data[:bytesReturned/sizeOfConnectionStat], nil } func openDriver(filename string) (windows.Handle, error) { u16filename, err := syscall.UTF16FromString(filename) if err != nil { return winInvalidHandleValue, fmt.Errorf("failed to convert driver filename to UTF16 string %w", err) } handle, err := windows.CreateFile(&u16filename[0], windows.GENERIC_READ|windows.GENERIC_WRITE, 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_OVERLAPPED, 0) if err != nil { return winInvalidHandleValue, err } return handle, nil } func closeDriver(handle windows.Handle) error { if kextHandle == winInvalidHandleValue { return ErrKextNotReady } return windows.CloseHandle(handle) } ================================================ FILE: service/firewall/interception/windowskext/packet.go ================================================ //go:build windows // +build windows package windowskext import ( "sync" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" ) // Packet represents an IP packet. type Packet struct { packet.Base verdictRequest *VerdictRequest verdictSet *abool.AtomicBool payloadLoaded bool lock sync.Mutex } // FastTrackedByIntegration returns whether the packet has been fast-track // accepted by the OS integration. func (pkt *Packet) FastTrackedByIntegration() bool { return pkt.verdictRequest.flags&VerdictRequestFlagFastTrackPermitted > 0 } // InfoOnly returns whether the packet is informational only and does not // represent an actual packet. func (pkt *Packet) InfoOnly() bool { return pkt.verdictRequest.flags&VerdictRequestFlagSocketAuth > 0 } // ExpectInfo returns whether the next packet is expected to be informational only. func (pkt *Packet) ExpectInfo() bool { return pkt.verdictRequest.flags&VerdictRequestFlagExpectSocketAuth > 0 } // GetPayload returns the full raw packet. func (pkt *Packet) LoadPacketData() error { pkt.lock.Lock() defer pkt.lock.Unlock() if pkt.verdictRequest.id == 0 { return ErrNoPacketID } if !pkt.payloadLoaded { pkt.payloadLoaded = true payload, err := GetPayload(pkt.verdictRequest.id, pkt.verdictRequest.packetSize) if err != nil { log.Tracer(pkt.Ctx()).Warningf("windowskext: failed to load payload: %s", err) return packet.ErrFailedToLoadPayload } err = packet.ParseLayer3(payload, &pkt.Base) if err != nil { log.Tracer(pkt.Ctx()).Warningf("windowskext: failed to parse payload: %s", err) return packet.ErrFailedToLoadPayload } } if len(pkt.Raw()) == 0 { return packet.ErrFailedToLoadPayload } return nil } // Accept accepts the packet. func (pkt *Packet) Accept() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, -network.VerdictAccept) } return nil } // Block blocks the packet. func (pkt *Packet) Block() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, -network.VerdictBlock) } return nil } // Drop drops the packet. func (pkt *Packet) Drop() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, -network.VerdictDrop) } return nil } // PermanentAccept permanently accepts connection (and the current packet). func (pkt *Packet) PermanentAccept() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, network.VerdictAccept) } return nil } // PermanentBlock permanently blocks connection (and the current packet). func (pkt *Packet) PermanentBlock() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, network.VerdictBlock) } return nil } // PermanentDrop permanently drops connection (and the current packet). func (pkt *Packet) PermanentDrop() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, network.VerdictDrop) } return nil } // RerouteToNameserver permanently reroutes the connection to the local nameserver (and the current packet). func (pkt *Packet) RerouteToNameserver() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, network.VerdictRerouteToNameserver) } return nil } // RerouteToTunnel permanently reroutes the connection to the local tunnel entrypoint (and the current packet). func (pkt *Packet) RerouteToTunnel() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, network.VerdictRerouteToTunnel) } return nil } ================================================ FILE: service/firewall/interception/windowskext/service.go ================================================ //go:build windows // +build windows package windowskext import ( "fmt" "syscall" "time" "github.com/safing/portmaster/base/log" "golang.org/x/sys/windows" ) type KextService struct { handle windows.Handle } func createKextService(driverName string, driverPath string) (*KextService, error) { // Open the service manager: manager, err := windows.OpenSCManager(nil, nil, windows.SC_MANAGER_ALL_ACCESS) if err != nil { return nil, fmt.Errorf("failed to open service manager: %d", err) } defer windows.CloseServiceHandle(manager) // Convert the driver name to a UTF16 string driverNameU16, err := syscall.UTF16FromString(driverName) if err != nil { return nil, fmt.Errorf("failed to convert driver name to UTF16 string: %w", err) } // Check if there is an old service. service, err := windows.OpenService(manager, &driverNameU16[0], windows.SERVICE_ALL_ACCESS) if err == nil { log.Warning("kext: old driver service was found") oldService := &KextService{handle: service} err := deleteService(manager, oldService, driverNameU16) if err != nil { return nil, fmt.Errorf("failed to delete old driver service: %s", err) } service = winInvalidHandleValue log.Info("kext: old driver service was deleted successfully") } driverPathU16, err := syscall.UTF16FromString(driverPath) // Create the service service, err = windows.CreateService(manager, &driverNameU16[0], &driverNameU16[0], windows.SERVICE_ALL_ACCESS, windows.SERVICE_KERNEL_DRIVER, windows.SERVICE_DEMAND_START, windows.SERVICE_ERROR_NORMAL, &driverPathU16[0], nil, nil, nil, nil, nil) if err != nil { return nil, err } return &KextService{handle: service}, nil } func deleteService(manager windows.Handle, service *KextService, driverName []uint16) error { // Stop and wait before deleting _ = service.stop(true) // Try to delete even if stop failed err := service.delete() if err != nil { return fmt.Errorf("failed to delete old service: %s", err) } // Wait until we can no longer open the old service. // Not very efficient but NotifyServiceStatusChange cannot be used with driver service. start := time.Now() timeLimit := time.Duration(30 * time.Second) for { handle, err := windows.OpenService(manager, &driverName[0], windows.SERVICE_ALL_ACCESS) if err != nil { break } _ = windows.CloseServiceHandle(handle) if time.Since(start) > timeLimit { return fmt.Errorf("time limit reached") } time.Sleep(100 * time.Millisecond) } return nil } func (s *KextService) isValid() bool { return s != nil && s.handle != winInvalidHandleValue && s.handle != 0 } func (s *KextService) isRunning() (bool, error) { if !s.isValid() { return false, fmt.Errorf("kext service not initialized") } var status windows.SERVICE_STATUS err := windows.QueryServiceStatus(s.handle, &status) if err != nil { return false, err } return status.CurrentState == windows.SERVICE_RUNNING, nil } func waitForServiceStatus(handle windows.Handle, neededStatus uint32, timeLimit time.Duration) (bool, error) { var status windows.SERVICE_STATUS status.CurrentState = windows.SERVICE_NO_CHANGE start := time.Now() for status.CurrentState != neededStatus { err := windows.QueryServiceStatus(handle, &status) if err != nil { return false, fmt.Errorf("failed while waiting for service to start: %w", err) } if time.Since(start) > timeLimit { return false, fmt.Errorf("time limit reached") } // Sleep for 1/10 of the wait hint, recommended time from microsoft time.Sleep(time.Duration((status.WaitHint / 10)) * time.Millisecond) } return true, nil } func (s *KextService) start(wait bool) error { if !s.isValid() { return fmt.Errorf("kext service not initialized") } // Start the service: err := windows.StartService(s.handle, 0, nil) if err != nil { err = windows.GetLastError() if err != windows.ERROR_SERVICE_ALREADY_RUNNING { // Failed to start service; clean-up: var status windows.SERVICE_STATUS _ = windows.ControlService(s.handle, windows.SERVICE_CONTROL_STOP, &status) _ = windows.DeleteService(s.handle) _ = windows.CloseServiceHandle(s.handle) s.handle = winInvalidHandleValue return err } } // Wait for service to start if wait { success, err := waitForServiceStatus(s.handle, windows.SERVICE_RUNNING, time.Duration(10*time.Second)) if err != nil || !success { return fmt.Errorf("service did not start: %w", err) } } return nil } func (s *KextService) stop(wait bool) error { if !s.isValid() { return fmt.Errorf("kext service not initialized") } // Stop the service var status windows.SERVICE_STATUS err := windows.ControlService(s.handle, windows.SERVICE_CONTROL_STOP, &status) if err != nil { return fmt.Errorf("service failed to stop: %w", err) } // Wait for service to stop if wait { success, err := waitForServiceStatus(s.handle, windows.SERVICE_STOPPED, time.Duration(10*time.Second)) if err != nil || !success { return fmt.Errorf("service did not stop: %w", err) } } return nil } func (s *KextService) delete() error { if !s.isValid() { return fmt.Errorf("kext service not initialized") } err := windows.DeleteService(s.handle) if err != nil { return fmt.Errorf("failed to delete service: %s", err) } // Service wont be deleted until all handles are closed. err = windows.CloseServiceHandle(s.handle) if err != nil { return fmt.Errorf("failed to close service handle: %s", err) } s.handle = winInvalidHandleValue return nil } ================================================ FILE: service/firewall/interception/windowskext/syscall.go ================================================ //go:build windows // +build windows package windowskext import "golang.org/x/sys/windows" const ( METHOD_BUFFERED = 0 METHOD_IN_DIRECT = 1 METHOD_OUT_DIRECT = 2 METHOD_NEITHER = 3 SIOCTL_TYPE = 40000 ) var ( IOCTL_VERSION = ctlCode(SIOCTL_TYPE, 0x800, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) IOCTL_SHUTDOWN_REQUEST = ctlCode(SIOCTL_TYPE, 0x801, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) IOCTL_RECV_VERDICT_REQ = ctlCode(SIOCTL_TYPE, 0x802, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) IOCTL_SET_VERDICT = ctlCode(SIOCTL_TYPE, 0x803, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) IOCTL_GET_PAYLOAD = ctlCode(SIOCTL_TYPE, 0x804, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) IOCTL_CLEAR_CACHE = ctlCode(SIOCTL_TYPE, 0x805, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) IOCTL_UPDATE_VERDICT = ctlCode(SIOCTL_TYPE, 0x806, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) IOCTL_GET_CONNECTIONS_STAT = ctlCode(SIOCTL_TYPE, 0x807, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) ) func ctlCode(device_type, function, method, access uint32) uint32 { return (device_type << 16) | (access << 14) | (function << 2) | method } func deviceIOControlAsync(handle windows.Handle, code uint32, inData []byte, outData []byte) (*windows.Overlapped, error) { var inDataPtr *byte = nil var inDataSize uint32 = 0 if inData != nil { inDataPtr = &inData[0] inDataSize = uint32(len(inData)) } var outDataPtr *byte = nil var outDataSize uint32 = 0 if outData != nil { outDataPtr = &outData[0] outDataSize = uint32(len(outData)) } overlapped := &windows.Overlapped{} err := windows.DeviceIoControl(handle, code, inDataPtr, inDataSize, outDataPtr, outDataSize, nil, overlapped) if err != nil { return nil, err } return overlapped, nil } func deviceIOControl(handle windows.Handle, code uint32, inData []byte, outData []byte) (uint32, error) { overlapped, err := deviceIOControlAsync(handle, code, inData, outData) if err != nil { return 0, err } var bytesReturned uint32 err = windows.GetOverlappedResult(handle, overlapped, &bytesReturned, true) return bytesReturned, err } ================================================ FILE: service/firewall/interception/windowskext2/doc.go ================================================ // +build windows // Package windowskext provides network interception capabilities on windows via the Portmaster Kernel Extension. package windowskext ================================================ FILE: service/firewall/interception/windowskext2/handler.go ================================================ //go:build windows // +build windows package windowskext import ( "context" "errors" "fmt" "net" "time" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/windows_kext/kextinterface" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/packet" ) type VersionInfo struct { Major uint8 Minor uint8 Revision uint8 Build uint8 } func (v *VersionInfo) String() string { return fmt.Sprintf("%d.%d.%d.%d", v.Major, v.Minor, v.Revision, v.Build) } // Handler transforms received packets to the Packet interface. func Handler(ctx context.Context, packets chan packet.Packet, bandwidthUpdate chan *packet.BandwidthUpdate) { for { packetInfo, err := RecvVerdictRequest() if errors.Is(err, kextinterface.ErrUnexpectedInfoSize) || errors.Is(err, kextinterface.ErrUnexpectedReadError) { log.Criticalf("unexpected kext info data: %s", err) continue // Depending on the info type this may not affect the functionality. Try to continue reading the next commands. } if err != nil { log.Warningf("failed to get packet from windows kext: %s", err) // Probably IO error, nothing else we can do. return } switch { case packetInfo.ConnectionV4 != nil: { // log.Tracef("packet: %+v", packetInfo.ConnectionV4) conn := packetInfo.ConnectionV4 // New Packet newPacket := &Packet{ verdictRequest: conn.ID, payload: conn.Payload, payloadLayer: conn.PayloadLayer, verdictSet: abool.NewBool(false), } info := newPacket.Info() info.Inbound = conn.Direction > 0 info.InTunnel = false info.Protocol = packet.IPProtocol(conn.Protocol) info.PID = int(conn.ProcessID) info.SeenAt = time.Now() // Check PID if info.PID == 0 { // Windows does not have zero PIDs. // Set to UndefinedProcessID. info.PID = process.UndefinedProcessID } // Set IP version info.Version = packet.IPv4 // Set IPs if info.Inbound { // Inbound info.Src = conn.RemoteIP[:] info.Dst = conn.LocalIP[:] } else { // Outbound info.Src = conn.LocalIP[:] info.Dst = conn.RemoteIP[:] } // Set Ports if info.Inbound { // Inbound info.SrcPort = conn.RemotePort info.DstPort = conn.LocalPort } else { // Outbound info.SrcPort = conn.LocalPort info.DstPort = conn.RemotePort } packets <- newPacket } case packetInfo.ConnectionV6 != nil: { // log.Tracef("packet: %+v", packetInfo.ConnectionV6) conn := packetInfo.ConnectionV6 // New Packet newPacket := &Packet{ verdictRequest: conn.ID, payload: conn.Payload, payloadLayer: conn.PayloadLayer, verdictSet: abool.NewBool(false), } info := newPacket.Info() info.Inbound = conn.Direction > 0 info.InTunnel = false info.Protocol = packet.IPProtocol(conn.Protocol) info.PID = int(conn.ProcessID) info.SeenAt = time.Now() // Check PID if info.PID == 0 { // Windows does not have zero PIDs. // Set to UndefinedProcessID. info.PID = process.UndefinedProcessID } // Set IP version info.Version = packet.IPv6 // Set IPs if info.Inbound { // Inbound info.Src = conn.RemoteIP[:] info.Dst = conn.LocalIP[:] } else { // Outbound info.Src = conn.LocalIP[:] info.Dst = conn.RemoteIP[:] } // Set Ports if info.Inbound { // Inbound info.SrcPort = conn.RemotePort info.DstPort = conn.LocalPort } else { // Outbound info.SrcPort = conn.LocalPort info.DstPort = conn.RemotePort } packets <- newPacket } case packetInfo.LogLine != nil: { line := packetInfo.LogLine switch line.Severity { case byte(log.DebugLevel): log.Debugf("kext: %s", line.Line) case byte(log.InfoLevel): log.Infof("kext: %s", line.Line) case byte(log.WarningLevel): log.Warningf("kext: %s", line.Line) case byte(log.ErrorLevel): log.Errorf("kext: %s", line.Line) case byte(log.CriticalLevel): log.Criticalf("kext: %s", line.Line) } } case packetInfo.BandwidthStats != nil: { bandwidthStats := packetInfo.BandwidthStats for _, stat := range bandwidthStats.ValuesV4 { connID := packet.CreateConnectionID( packet.IPProtocol(bandwidthStats.Protocol), net.IP(stat.LocalIP[:]), stat.LocalPort, net.IP(stat.RemoteIP[:]), stat.RemotePort, false, ) update := &packet.BandwidthUpdate{ ConnID: connID, BytesReceived: stat.ReceivedBytes, BytesSent: stat.TransmittedBytes, Method: packet.Additive, } bandwidthUpdate <- update } for _, stat := range bandwidthStats.ValuesV6 { connID := packet.CreateConnectionID( packet.IPProtocol(bandwidthStats.Protocol), net.IP(stat.LocalIP[:]), stat.LocalPort, net.IP(stat.RemoteIP[:]), stat.RemotePort, false, ) update := &packet.BandwidthUpdate{ ConnID: connID, BytesReceived: stat.ReceivedBytes, BytesSent: stat.TransmittedBytes, Method: packet.Additive, } bandwidthUpdate <- update } } } } } ================================================ FILE: service/firewall/interception/windowskext2/kext.go ================================================ //go:build windows // +build windows package windowskext import ( "fmt" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/windows_kext/kextinterface" "golang.org/x/sys/windows" ) // Package errors var ( driverPath string service *kextinterface.KextService kextFile *kextinterface.KextFile ) const ( driverName = "PortmasterKext" ) func Init(path string) error { driverPath = path return nil } // Start intercepting. func Start() error { // initialize and start driver service var err error service, err = kextinterface.CreateKextService(driverName, driverPath) if err != nil { return fmt.Errorf("failed to create service: %w", err) } // Start service and open file err = service.Start(true) if err != nil { log.Errorf("failed to start service: %s", err) } kextFile, err = service.OpenFile(1024) if err != nil { return fmt.Errorf("failed to open driver: %w", err) } return nil } func GetKextHandle() windows.Handle { return kextFile.GetHandle() } func GetKextServiceHandle() windows.Handle { return service.GetHandle() } // Stop intercepting. func Stop() error { if kextFile == nil { return fmt.Errorf("kextfile is nil") } // Prepare kernel for shutdown err := shutdownRequest() if err != nil { log.Warningf("winkext: shutdown request failed: %s", err) } // Close the interface to the driver. Driver will continue to run. err = kextFile.Close() if err != nil { log.Warningf("winkext: failed to close kext file: %s", err) } // Stop and delete the driver. err = service.Stop(true) if err != nil { log.Warningf("winkext: failed to stop kernel service: %s", err) } err = service.Delete() if err != nil { log.Warningf("winkext: failed to delete kernel service: %s", err) } return nil } // Sends a shutdown request. func shutdownRequest() error { return kextinterface.SendShutdownCommand(kextFile) } // Send request for logs of the kext. func SendLogRequest() error { return kextinterface.SendGetLogsCommand(kextFile) } func SendBandwidthStatsRequest() error { return kextinterface.SendGetBandwidthStatsCommand(kextFile) } func SendPrintMemoryStatsCommand() error { return kextinterface.SendPrintMemoryStatsCommand(kextFile) } func SendCleanEndedConnection() error { return kextinterface.SendCleanEndedConnectionsCommand(kextFile) } // RecvVerdictRequest waits for the next verdict request from the kext. If a timeout is reached, both *VerdictRequest and error will be nil. func RecvVerdictRequest() (*kextinterface.Info, error) { return kextinterface.RecvInfo(kextFile) } // SetVerdict sets the verdict for a packet and/or connection. func SetVerdict(pkt *Packet, verdict kextinterface.KextVerdict) error { verdictCommand := kextinterface.Verdict{ID: pkt.verdictRequest, Verdict: uint8(verdict)} return kextinterface.SendVerdictCommand(kextFile, verdictCommand) } // Clears the internal connection cache. func ClearCache() error { return kextinterface.SendClearCacheCommand(kextFile) } // Updates a specific connection verdict. func UpdateVerdict(conn *network.Connection) error { if conn.IPVersion == 4 { update := kextinterface.UpdateV4{ Protocol: conn.Entity.Protocol, LocalAddress: [4]byte(conn.LocalIP), LocalPort: conn.LocalPort, RemoteAddress: [4]byte(conn.Entity.IP), RemotePort: conn.Entity.Port, Verdict: uint8(getKextVerdictFromConnection(conn)), } return kextinterface.SendUpdateV4Command(kextFile, update) } else if conn.IPVersion == 6 { update := kextinterface.UpdateV6{ Protocol: conn.Entity.Protocol, LocalAddress: [16]byte(conn.LocalIP), LocalPort: conn.LocalPort, RemoteAddress: [16]byte(conn.Entity.IP), RemotePort: conn.Entity.Port, Verdict: uint8(getKextVerdictFromConnection(conn)), } return kextinterface.SendUpdateV6Command(kextFile, update) } return nil } func getKextVerdictFromConnection(conn *network.Connection) kextinterface.KextVerdict { switch conn.Verdict { case network.VerdictUndecided: return kextinterface.VerdictUndecided case network.VerdictUndeterminable: return kextinterface.VerdictUndeterminable case network.VerdictAccept: if conn.VerdictPermanent { return kextinterface.VerdictPermanentAccept } else { return kextinterface.VerdictAccept } case network.VerdictBlock: if conn.VerdictPermanent { return kextinterface.VerdictPermanentBlock } else { return kextinterface.VerdictBlock } case network.VerdictDrop: if conn.VerdictPermanent { return kextinterface.VerdictPermanentDrop } else { return kextinterface.VerdictDrop } case network.VerdictRerouteToNameserver: return kextinterface.VerdictRerouteToNameserver case network.VerdictRerouteToTunnel: return kextinterface.VerdictRerouteToTunnel case network.VerdictFailed: return kextinterface.VerdictFailed } return kextinterface.VerdictUndeterminable } // Returns the kext version. func GetVersion() (*VersionInfo, error) { data, err := kextinterface.ReadVersion(kextFile) if err != nil { return nil, err } version := &VersionInfo{ Major: data[0], Minor: data[1], Revision: data[2], Build: data[3], } return version, nil } ================================================ FILE: service/firewall/interception/windowskext2/packet.go ================================================ //go:build windows // +build windows package windowskext import ( "fmt" "sync" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/windows_kext/kextinterface" ) // Packet represents an IP packet. type Packet struct { packet.Base verdictRequest uint64 payload []byte payloadLayer uint8 verdictSet *abool.AtomicBool payloadLoaded bool lock sync.Mutex } // FastTrackedByIntegration returns whether the packet has been fast-track // accepted by the OS integration. func (pkt *Packet) FastTrackedByIntegration() bool { return false } // InfoOnly returns whether the packet is informational only and does not // represent an actual packet. func (pkt *Packet) InfoOnly() bool { return false } // ExpectInfo returns whether the next packet is expected to be informational only. func (pkt *Packet) ExpectInfo() bool { return false } // GetPayload returns the full raw packet. func (pkt *Packet) LoadPacketData() error { pkt.lock.Lock() defer pkt.lock.Unlock() if !pkt.payloadLoaded { pkt.payloadLoaded = true if len(pkt.payload) > 0 { var err error switch pkt.payloadLayer { case 3: err = packet.ParseLayer3(pkt.payload, &pkt.Base) case 4: err = packet.ParseLayer4(pkt.payload, &pkt.Base) default: err = fmt.Errorf("unsupported payload layer: %d", pkt.payloadLayer) } if err != nil { log.Tracef("payload: %#v", pkt.payload) log.Tracer(pkt.Ctx()).Warningf("windowskext: failed to parse payload: %s", err) return packet.ErrFailedToLoadPayload } } } if len(pkt.Raw()) == 0 { return packet.ErrFailedToLoadPayload } return nil } // Accept accepts the packet. func (pkt *Packet) Accept() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, kextinterface.VerdictAccept) } return nil } // Block blocks the packet. func (pkt *Packet) Block() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, kextinterface.VerdictBlock) } return nil } // Drop drops the packet. func (pkt *Packet) Drop() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, kextinterface.VerdictDrop) } return nil } // PermanentAccept permanently accepts connection (and the current packet). func (pkt *Packet) PermanentAccept() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, kextinterface.VerdictPermanentAccept) } return nil } // PermanentBlock permanently blocks connection (and the current packet). func (pkt *Packet) PermanentBlock() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, kextinterface.VerdictPermanentBlock) } return nil } // PermanentDrop permanently drops connection (and the current packet). func (pkt *Packet) PermanentDrop() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, kextinterface.VerdictPermanentDrop) } return nil } // RerouteToNameserver permanently reroutes the connection to the local nameserver (and the current packet). func (pkt *Packet) RerouteToNameserver() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, kextinterface.VerdictRerouteToNameserver) } return nil } // RerouteToTunnel permanently reroutes the connection to the local tunnel entrypoint (and the current packet). func (pkt *Packet) RerouteToTunnel() error { if pkt.verdictSet.SetToIf(false, true) { return SetVerdict(pkt, kextinterface.VerdictRerouteToTunnel) } return nil } ================================================ FILE: service/firewall/interception/windowskext2/service.go ================================================ //go:build windows // +build windows package windowskext import "github.com/safing/portmaster/windows_kext/kextinterface" func createKextService(driverName string, driverPath string) (*kextinterface.KextService, error) { return kextinterface.CreateKextService(driverName, driverPath) } ================================================ FILE: service/firewall/master.go ================================================ package firewall import ( "context" "fmt" "net" "path/filepath" "strconv" "strings" "github.com/agext/levenshtein" "golang.org/x/net/publicsuffix" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/detection/dga" "github.com/safing/portmaster/service/intel/customlists" "github.com/safing/portmaster/service/intel/filterlists" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/endpoints" ) const noReasonOptionKey = "" type deciderFn func(context.Context, *network.Connection, *profile.LayeredProfile, packet.Packet) bool var defaultDeciders = []deciderFn{ checkPortmasterConnection, checkIfBroadcastReply, checkConnectionType, checkConnectionScope, checkEndpointLists, checkInvalidIP, checkResolverScope, checkConnectivityDomain, checkBypassPrevention, checkFilterLists, checkCustomFilterList, checkDomainHeuristics, checkAutoPermitRelated, } // decideOnConnection makes a decision about a connection. // When called, the connection and profile is already locked. func decideOnConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet) { // Check if we have a process and profile. layeredProfile := conn.Process().Profile() if layeredProfile == nil { conn.Deny("unknown process or profile", noReasonOptionKey) return } // Check if the layered profile needs updating. if layeredProfile.NeedsUpdate() { // Update revision counter in connection. conn.ProfileRevisionCounter = layeredProfile.Update( conn.Process().MatchingData(), conn.Process().CreateProfileCallback, ) conn.SaveWhenFinished() // Reset verdict for connection. log.Tracer(ctx).Infof("filter: profile updated, re-evaluating verdict of %s", conn) // Reset entity if it exists. if conn.Entity != nil { conn.Entity.ResetLists() } } else { // Check if the revision counter of the connection needs updating. revCnt := layeredProfile.RevisionCnt() if conn.ProfileRevisionCounter != revCnt { conn.ProfileRevisionCounter = revCnt conn.SaveWhenFinished() } } // prepare the entity and resolve all filterlist matches conn.Entity.ResolveSubDomainLists(ctx, layeredProfile.FilterSubDomains()) conn.Entity.EnableCNAMECheck(ctx, layeredProfile.FilterCNAMEs()) conn.Entity.LoadLists(ctx) // Run all deciders and return if they came to a conclusion. done, defaultAction := runDeciders(ctx, defaultDeciders, conn, layeredProfile, pkt) if done { return } // DNS Request are always default allowed, as the endpoint lists could not // be checked fully. if conn.Type == network.DNSRequest { conn.Accept("allowing dns request", noReasonOptionKey) return } // Deciders did not conclude, use default action. switch defaultAction { case profile.DefaultActionPermit: conn.Accept("allowed by default action", profile.CfgOptionDefaultActionKey) case profile.DefaultActionAsk: // Only prompt if there has not been a decision already. // This prevents prompts from being created when re-evaluating connections. if conn.Verdict == network.VerdictUndecided { prompt(ctx, conn) } default: conn.Deny("blocked by default action", profile.CfgOptionDefaultActionKey) } } func runDeciders(ctx context.Context, selectedDeciders []deciderFn, conn *network.Connection, layeredProfile *profile.LayeredProfile, pkt packet.Packet) (done bool, defaultAction uint8) { // Read-lock all the profiles. layeredProfile.LockForUsage() defer layeredProfile.UnlockForUsage() // Go though all deciders, return if one sets an action. for _, decider := range selectedDeciders { if decider(ctx, conn, layeredProfile, pkt) { return true, profile.DefaultActionNotSet } } // Return the default action. return false, layeredProfile.DefaultAction() } // checkPortmasterConnection allows all connection that originate from // portmaster itself. func checkPortmasterConnection(ctx context.Context, conn *network.Connection, _ *profile.LayeredProfile, _ packet.Packet) bool { // Grant own outgoing or local connections. // Blocking our own connections can lead to a very literal deadlock. // This can currently happen, as fast-tracked connections are also // reset in the OS integration and might show up in the connection // handling if a packet in the other direction hits the firewall first. // Ignore other processes. if conn.Process().Pid != ownPID { return false } // Ignore inbound connection if non-local. if conn.Inbound { myIP, err := netenv.IsMyIP(conn.Entity.IP) if err != nil { log.Tracer(ctx).Debugf("filter: failed to check if %s is own IP for granting own connection: %s", conn.Entity.IP, err) return false } if !myIP { return false } } log.Tracer(ctx).Infof("filter: granting own connection %s", conn) conn.Accept("connection by Portmaster", noReasonOptionKey) conn.Internal = true return true } func checkIfBroadcastReply(ctx context.Context, conn *network.Connection, _ *profile.LayeredProfile, _ packet.Packet) bool { // Only check inbound connections. if !conn.Inbound { return false } // Only check if the process has been identified. if !conn.Process().IsIdentified() { return false } // Check if the remote IP is part of a local network. localNet, err := netenv.GetLocalNetwork(conn.Entity.IP) if err != nil { log.Tracer(ctx).Warningf("filter: failed to get local network: %s", err) return false } if localNet == nil { return false } // Search for a matching requesting connection. requestingConn := network.GetMulticastRequestConn(conn, localNet) if requestingConn == nil { return false } conn.Accept( fmt.Sprintf( "response to multi/broadcast query to %s/%s", packet.IPProtocol(requestingConn.Entity.Protocol), net.JoinHostPort( requestingConn.Entity.IP.String(), strconv.Itoa(int(requestingConn.Entity.Port)), ), ), "", ) return true } func checkEndpointLists(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool { // DNS request from the system resolver require a special decision process, // because the original requesting process is not known. Here, we only check // global-only and the most important per-app aspects. The resulting // connection is then blocked when the original requesting process is known. if conn.Type == network.DNSRequest && conn.Process().IsSystemResolver() { return checkEndpointListsForSystemResolverDNSRequests(ctx, conn, p) } var result endpoints.EPResult var reason endpoints.Reason // check endpoints list var optionKey string if conn.Inbound { result, reason = p.MatchServiceEndpoint(ctx, conn.Entity) optionKey = profile.CfgOptionServiceEndpointsKey } else { result, reason = p.MatchEndpoint(ctx, conn.Entity) optionKey = profile.CfgOptionEndpointsKey } switch result { case endpoints.Denied, endpoints.MatchError: conn.DenyWithContext(reason.String(), optionKey, reason.Context()) return true case endpoints.Permitted: conn.AcceptWithContext(reason.String(), optionKey, reason.Context()) return true case endpoints.NoMatch: return false } return false } // checkEndpointListsForSystemResolverDNSRequests is a special version of // checkEndpointLists that is only meant for DNS queries by the system // resolver. It only checks the endpoint filter list of the local profile and // does not include the global profile. func checkEndpointListsForSystemResolverDNSRequests(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile) bool { var profileEndpoints endpoints.Endpoints var optionKey string if conn.Inbound { profileEndpoints = p.LocalProfileWithoutLocking().GetServiceEndpoints() optionKey = profile.CfgOptionServiceEndpointsKey } else { profileEndpoints = p.LocalProfileWithoutLocking().GetEndpoints() optionKey = profile.CfgOptionEndpointsKey } if profileEndpoints.IsSet() { result, reason := profileEndpoints.Match(ctx, conn.Entity) if endpoints.IsDecision(result) { switch result { case endpoints.Denied, endpoints.MatchError: conn.DenyWithContext(reason.String(), optionKey, reason.Context()) return true case endpoints.Permitted: conn.AcceptWithContext(reason.String(), optionKey, reason.Context()) return true case endpoints.NoMatch: return false } } } return false } var p2pFilterLists = []string{"17-P2P"} func checkConnectionType(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool { switch { // Block incoming connection, if not from localhost. case p.BlockInbound() && conn.Inbound && !conn.Entity.IPScope.IsLocalhost(): conn.Drop("inbound connections blocked", profile.CfgOptionBlockInboundKey) return true // Check for P2P and related connections. case p.BlockP2P() && !conn.Inbound: switch { // Block anything that is in the P2P filter list. case conn.Entity.MatchLists(p2pFilterLists): conn.Block("P2P assistive infrastructure blocked based on filter list", profile.CfgOptionBlockP2PKey) return true // Remaining P2P deciders only apply to IP connections. case conn.Type != network.IPConnection: return false // Block well known ports of P2P assistive infrastructure. case conn.Entity.DstPort() == 3478 || // STUN/TURN conn.Entity.DstPort() == 5349: // STUN/TURN over TLS/DTLS conn.Block("P2P assistive infrastructure blocked based on port", profile.CfgOptionBlockP2PKey) return true // Block direct connections with not previous DNS request. case conn.Entity.IPScope.IsGlobal() && conn.Entity.Domain == "": conn.Block("direct connections (P2P) blocked", profile.CfgOptionBlockP2PKey) return true default: return false } default: return false } } func checkConnectivityDomain(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool { switch { case conn.Entity.Domain == "": // Only applies if a domain is available. return false case netenv.GetOnlineStatus() > netenv.StatusPortal: // Special grant only applies if network status is Portal (or even more limited). return false case conn.Inbound: // Special grant only applies to outgoing connections. return false case p.BlockScopeInternet(): // Special grant only applies if application is allowed to connect to the Internet. return false case netenv.IsConnectivityDomain(conn.Entity.Domain): // Special grant! conn.Accept("special grant for connectivity domain during network bootstrap", noReasonOptionKey) return true default: // Not a special grant domain return false } } func checkConnectionScope(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool { // If we are handling a DNS request, check if we can immediately block it. if conn.Type == network.DNSRequest { // DNS is expected to resolve to LAN or Internet addresses. // Localhost queries are immediately responded to by the nameserver. if p.BlockScopeInternet() && p.BlockScopeLAN() { conn.Block("Internet and LAN access blocked", profile.CfgOptionBlockScopeInternetKey) return true } return false } // Check if the network scope is permitted. switch conn.Entity.IPScope { case netutils.Global, netutils.GlobalMulticast: if p.BlockScopeInternet() { conn.Deny("Internet access blocked", profile.CfgOptionBlockScopeInternetKey) // Block Outbound / Drop Inbound return true } case netutils.SiteLocal, netutils.LinkLocal, netutils.LocalMulticast: if p.BlockScopeLAN() { conn.Block("LAN access blocked", profile.CfgOptionBlockScopeLANKey) // Block Outbound / Drop Inbound return true } case netutils.HostLocal: if p.BlockScopeLocal() { conn.Block("Localhost access blocked", profile.CfgOptionBlockScopeLocalKey) // Block Outbound / Drop Inbound return true } case netutils.Undefined, netutils.Invalid: // Block Invalid / Undefined IPs _after_ the rules. return false default: conn.Deny("invalid IP", noReasonOptionKey) // Block Outbound / Drop Inbound return true } return false } func checkInvalidIP(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool { // Only applies to IP connections. if conn.Type != network.IPConnection { return false } // Block Invalid / Undefined IPs. switch conn.Entity.IPScope { //nolint:exhaustive // Only looking for specific values. case netutils.Undefined, netutils.Invalid: conn.Deny("invalid IP", noReasonOptionKey) // Block Outbound / Drop Inbound return true } return false } func checkBypassPrevention(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool { if p.PreventBypassing() { // check for bypass protection result, reason, reasonCtx := PreventBypassing(ctx, conn) switch result { case endpoints.Denied, endpoints.MatchError: // Also block on MatchError to be on the safe side. // PreventBypassing does not use any data that needs to be loaded, so it should not fail anyway. conn.BlockWithContext("bypass prevention: "+reason, profile.CfgOptionPreventBypassingKey, reasonCtx) return true case endpoints.Permitted: conn.AcceptWithContext("bypass prevention: "+reason, profile.CfgOptionPreventBypassingKey, reasonCtx) return true case endpoints.NoMatch: return false } } return false } func checkFilterLists(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool { // apply privacy filter lists result, reason := p.MatchFilterLists(ctx, conn.Entity) switch result { case endpoints.Denied: // If the connection matches a filter list, check if the "unbreak" list matches too and abort blocking. resolvedUnbreakFilterListIDs := filterlists.GetUnbreakFilterListIDs() for _, blockedListID := range conn.Entity.BlockedByLists { for _, unbreakListID := range resolvedUnbreakFilterListIDs { if blockedListID == unbreakListID { log.Tracer(ctx).Debugf("filter: unbreak filter %s matched, ignoring other filter list matches", unbreakListID) return false } } } // Otherwise, continue with blocking. conn.DenyWithContext(reason.String(), profile.CfgOptionFilterListsKey, reason.Context()) return true case endpoints.NoMatch: // nothing to do case endpoints.Permitted, endpoints.MatchError: fallthrough default: log.Tracer(ctx).Debugf("filter: filter lists returned unsupported verdict: %s", result) } return false } func checkResolverScope(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool { // If the IP address was resolved, check the scope of the resolver. switch { case conn.Type != network.IPConnection: // Only applies to IP connections. case !p.RemoveOutOfScopeDNS(): // Out of scope checking is not active. case conn.Resolver == nil: // IP address of connection was not resolved. case conn.Resolver.IPScope.IsGlobal() && (conn.Entity.IPScope.IsLAN() || conn.Entity.IPScope.IsLocalhost()): // Block global resolvers from returning LAN/Localhost IPs. conn.Block("DNS server horizon violation: global DNS server returned local IP address", profile.CfgOptionRemoveOutOfScopeDNSKey) return true case conn.Resolver.IPScope.IsLAN() && conn.Entity.IPScope.IsLocalhost(): // Block LAN resolvers from returning Localhost IPs. conn.Block("DNS server horizon violation: LAN DNS server returned localhost IP address", profile.CfgOptionRemoveOutOfScopeDNSKey) return true } return false } func checkDomainHeuristics(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool { // Don't check domain heuristics if no domain is available. if conn.Entity.Domain == "" { return false } // Check if domain heuristics are enabled. if !p.DomainHeuristics() { return false } trimmedDomain := strings.TrimRight(conn.Entity.Domain, ".") etld1, err := publicsuffix.EffectiveTLDPlusOne(trimmedDomain) if err != nil { // Don't run the check if the domain is a TLD. return false } domainToCheck := strings.Split(etld1, ".")[0] score := dga.LmsScore(domainToCheck) if score < 5 { log.Tracer(ctx).Debugf( "filter: possible data tunnel by %s in eTLD+1 %s: %s has an lms score of %.2f", conn.Process(), etld1, domainToCheck, score, ) conn.Block("possible DGA domain commonly used by malware", profile.CfgOptionDomainHeuristicsKey) return true } log.Tracer(ctx).Tracef("filter: LMS score of eTLD+1 %s is %.2f", etld1, score) // 100 is a somewhat arbitrary threshold to ensure we don't mess // around with CDN domain names to early. They use short second-level // domains that would trigger LMS checks but are to small to actually // exfiltrate data. if len(conn.Entity.Domain) > len(etld1)+100 { domainToCheck = trimmedDomain[0:len(etld1)] score := dga.LmsScoreOfDomain(domainToCheck) if score < 10 { log.Tracer(ctx).Debugf( "filter: possible data tunnel by %s in subdomain of %s: %s has an lms score of %.2f", conn.Process(), conn.Entity.Domain, domainToCheck, score, ) conn.Block("possible data tunnel for covert communication and protection bypassing", profile.CfgOptionDomainHeuristicsKey) return true } log.Tracer(ctx).Tracef("filter: LMS score of entire domain is %.2f", score) } return false } func checkAutoPermitRelated(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool { // Auto permit is disabled for default action permit. if p.DefaultAction() == profile.DefaultActionPermit { return false } // Check if auto permit is disabled. if p.DisableAutoPermit() { return false } // Check for relation to auto permit. related, reason := checkRelation(conn) if related { conn.Accept(reason, profile.CfgOptionDisableAutoPermitKey) return true } return false } // checkRelation tries to find a relation between a process and a communication. This is for better out of the box experience and is _not_ meant to thwart intentional malware. func checkRelation(conn *network.Connection) (related bool, reason string) { // Don't check relation if no domain is available. if conn.Entity.Domain == "" { return false, "" } // Don't check for unknown processes. if conn.Process().Pid < 0 { return false, "" } pathElements := strings.Split(conn.Process().Path, string(filepath.Separator)) // only look at the last two path segments if len(pathElements) > 2 { pathElements = pathElements[len(pathElements)-2:] } domainElements := strings.Split(conn.Entity.Domain, ".") var domainElement string var processElement string matchLoop: for _, domainElement = range domainElements { for _, pathElement := range pathElements { if levenshtein.Match(domainElement, pathElement, nil) > 0.5 { related = true processElement = pathElement break matchLoop } } if levenshtein.Match(domainElement, conn.Process().Name, nil) > 0.5 { related = true processElement = conn.Process().Name break matchLoop } if levenshtein.Match(domainElement, conn.Process().ExecName, nil) > 0.5 { related = true processElement = conn.Process().ExecName break matchLoop } } if related { reason = fmt.Sprintf("auto allowed: domain is related to process: %s is related to %s", domainElement, processElement) } return related, reason } func checkCustomFilterList(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool { // Check if any custom list is loaded at all. if !customlists.IsLoaded() { return false } // block if the domain name appears in the custom filter list (check for subdomains if enabled) if conn.Entity.Domain != "" { if ok, match := customlists.LookupDomain(conn.Entity.Domain, p.FilterSubDomains()); ok { conn.Deny(fmt.Sprintf("domain %s matches %s in custom filter list", conn.Entity.Domain, match), customlists.CfgOptionCustomListFileKey) return true } } // block if any of the CNAME appears in the custom filter list (check for subdomains if enabled) if p.FilterCNAMEs() { for _, cname := range conn.Entity.CNAME { if ok, match := customlists.LookupDomain(cname, p.FilterSubDomains()); ok { conn.Deny(fmt.Sprintf("domain alias (CNAME) %s matches %s in custom filter list", cname, match), customlists.CfgOptionCustomListFileKey) return true } } } // block if ip addresses appears in the custom filter list if conn.Entity.IP != nil { if customlists.LookupIP(conn.Entity.IP) { conn.Deny("IP address is in the custom filter list", customlists.CfgOptionCustomListFileKey) return true } } // block autonomous system by its number if it appears in the custom filter list if conn.Entity.ASN != 0 { if customlists.LookupASN(conn.Entity.ASN) { conn.Deny("AS is in the custom filter list", customlists.CfgOptionCustomListFileKey) return true } } // block if the country appears in the custom filter list if conn.Entity.Country != "" { if customlists.LookupCountry(conn.Entity.Country) { conn.Deny("country is in the custom filter list", customlists.CfgOptionCustomListFileKey) return true } } return false } ================================================ FILE: service/firewall/module.go ================================================ package firewall import ( "errors" "flag" "fmt" "path/filepath" "strings" "sync/atomic" "time" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" _ "github.com/safing/portmaster/service/core" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netquery" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/resolver" "github.com/safing/portmaster/service/updates" "github.com/safing/portmaster/spn/access" "github.com/safing/portmaster/spn/captain" ) type stringSliceFlag []string func (ss *stringSliceFlag) String() string { return strings.Join(*ss, ":") } func (ss *stringSliceFlag) Set(value string) error { *ss = append(*ss, filepath.Clean(value)) return nil } var allowedClients stringSliceFlag type Firewall struct { mgr *mgr.Manager instance instance } func init() { flag.Var(&allowedClients, "allowed-clients", "A list of binaries that are allowed to connect to the Portmaster API") } func (f *Firewall) Manager() *mgr.Manager { return f.mgr } func (f *Firewall) Start() error { if err := prep(); err != nil { log.Errorf("Failed to prepare firewall module %q", err) return err } return start() } func (f *Firewall) Stop() error { // Cancel all workers and give them a little time. // The bandwidth updater can crash the sqlite DB for some reason. // TODO: Investigate. f.mgr.Cancel() time.Sleep(100 * time.Millisecond) return stop() } func prep() error { network.SetDefaultFirewallHandler(defaultFirewallHandler) // Reset connections every time configuration changes // this will be triggered on spn enable/disable module.instance.Config().EventConfigChange.AddCallback("reset connection verdicts after global config change", func(w *mgr.WorkerCtx, _ struct{}) (bool, error) { resetAllConnectionVerdicts() return false, nil }) module.instance.Profile().EventConfigChange.AddCallback("reset connection verdicts after profile config change", func(m *mgr.WorkerCtx, profileID string) (bool, error) { // Expected event data: scoped profile ID. profileSource, profileID, ok := strings.Cut(profileID, "/") if !ok { return false, fmt.Errorf("event data does not seem to be a scoped profile ID: %v", profileID) } resetProfileConnectionVerdict(profileSource, profileID) return false, nil }, ) // Reset connections when spn is connected // connect and disconnecting is triggered on config change event but connecting takеs more time module.instance.Captain().EventSPNConnected.AddCallback("reset connection verdicts on SPN connect", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { resetAllConnectionVerdicts() return false, err }) // Reset connections when account is updated. // This will not change verdicts, but will update the feature flags on connections. module.instance.Access().EventAccountUpdate.AddCallback("update connection feature flags after account update", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { resetAllConnectionVerdicts() return false, err }) module.instance.Network().EventConnectionReattributed.AddCallback("reset connection verdicts after connection re-attribution", func(wc *mgr.WorkerCtx, connID string) (cancel bool, err error) { // Expected event data: connection ID. resetSingleConnectionVerdict(connID) return false, err }) return nil } func start() error { getConfig() startAPIAuth() module.mgr.Go("packet handler", packetHandler) module.mgr.Go("bandwidth update handler", bandwidthUpdateHandler) // Start stat logger if logging is set to trace. if log.GetLogLevel() == log.TraceLevel { module.mgr.Go("stat logger", statLogger) } return nil } func stop() error { return nil } var ( module *Firewall shimLoaded atomic.Bool ) func New(instance instance) (*Firewall, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Firewall") module = &Firewall{ mgr: m, instance: instance, } if err := prepAPIAuth(); err != nil { return nil, err } if err := registerConfig(); err != nil { return nil, err } return module, nil } type instance interface { BinDir() string Config() *config.Config BinaryUpdates() *updates.Updater Profile() *profile.ProfileModule Captain() *captain.Captain Access() *access.Access Network() *network.Network NetQuery() *netquery.NetQuery Resolver() *resolver.ResolverModule } ================================================ FILE: service/firewall/packet_handler.go ================================================ package firewall import ( "context" "errors" "fmt" "net" "os" "strings" "sync/atomic" "time" "github.com/google/gopacket/layers" "github.com/miekg/dns" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/compat" _ "github.com/safing/portmaster/service/core/base" "github.com/safing/portmaster/service/firewall/inspection" "github.com/safing/portmaster/service/firewall/interception" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/resolver" "github.com/safing/portmaster/spn/access" ) type ExtVerdictHandlerFunc func(conn *network.Connection) (verdict network.Verdict, reason string, skipTunnel bool) var ( nameserverIPMatcher func(ip net.IP) bool nameserverIPMatcherSet = abool.New() nameserverIPMatcherReady = abool.New() externalVerdictHandler atomic.Pointer[ExtVerdictHandlerFunc] packetsAccepted = new(uint64) packetsBlocked = new(uint64) packetsDropped = new(uint64) packetsFailed = new(uint64) blockedIPv4 = net.IPv4(0, 0, 0, 17) blockedIPv6 = net.ParseIP("::17") ownPID = os.Getpid() ) func resetSingleConnectionVerdict(connID string) { // Create tracing context. ctx, tracer := log.AddTracer(context.Background()) defer tracer.Submit() conn, ok := network.GetConnection(connID) if !ok { conn, ok = network.GetDNSConnection(connID) if !ok { tracer.Debugf("filter: could not find re-attributed connection %s for re-evaluation", connID) return } } resetConnectionVerdict(ctx, conn) } func resetProfileConnectionVerdict(profileSource, profileID string) { // Create tracing context. ctx, tracer := log.AddTracer(context.Background()) defer tracer.Submit() // Resetting will force all the connection to be evaluated by the firewall again // this will set new verdicts if configuration was update or spn has been disabled or enabled. tracer.Infof("filter: re-evaluating connections of %s/%s", profileSource, profileID) // Re-evaluate all connections. var changedVerdicts int for _, conn := range network.GetAllConnections() { // Check if connection is complete and attributed to the deleted profile. if conn.DataIsComplete() && conn.ProcessContext.Profile == profileID && conn.ProcessContext.Source == profileSource { if resetConnectionVerdict(ctx, conn) { changedVerdicts++ } } } tracer.Infof("filter: changed verdict on %d connections", changedVerdicts) } func resetAllConnectionVerdicts() { // Create tracing context. ctx, tracer := log.AddTracer(context.Background()) defer tracer.Submit() // Resetting will force all the connection to be evaluated by the firewall again // this will set new verdicts if configuration was update or spn has been disabled or enabled. tracer.Info("filter: re-evaluating all connections") // Re-evaluate all connections. var changedVerdicts int for _, conn := range network.GetAllConnections() { // Skip incomplete connections. if !conn.DataIsComplete() { continue } if resetConnectionVerdict(ctx, conn) { changedVerdicts++ } } tracer.Infof("filter: changed verdict on %d connections", changedVerdicts) } func resetConnectionVerdict(ctx context.Context, conn *network.Connection) (verdictChanged bool) { tracer := log.Tracer(ctx) // Remove any active prompt as the settings are being re-evaluated. conn.RemovePrompt() conn.Lock() defer conn.Unlock() // Do not re-evaluate connection that have already ended. if conn.Ended > 0 { return false } // Update feature flags. if err := conn.UpdateFeatures(); err != nil && !errors.Is(err, access.ErrNotLoggedIn) { tracer.Warningf("filter: failed to update connection feature flags: %s", err) } // Skip internal connections: // - Pre-authenticated connections from Portmaster // - Redirected DNS requests // - SPN Uplink to Home Hub if conn.Internal { // tracer.Tracef("filter: skipping internal connection %s", conn) return false } tracer.Debugf("filter: re-evaluating verdict of %s", conn) previousVerdict := conn.Verdict // Apply privacy filter and check tunneling. FilterConnection(ctx, conn, nil, true, true) // Stop existing SPN tunnel if not needed anymore. if conn.Verdict != network.VerdictRerouteToTunnel && conn.TunnelContext != nil { err := conn.TunnelContext.StopTunnel() if err != nil { tracer.Debugf("filter: failed to stopped unneeded tunnel: %s", err) } } // Save if verdict changed. if conn.Verdict != previousVerdict { err := interception.UpdateVerdictOfConnection(conn) if err != nil { log.Debugf("filter: failed to update connection verdict: %s", err) } conn.Save() tracer.Infof("filter: verdict of connection %s changed from %s to %s", conn, previousVerdict.Verb(), conn.VerdictVerb()) // Update verdict in OS integration, if an IP connection. if conn.Type == network.IPConnection { err := interception.UpdateVerdictOfConnection(conn) if err != nil { log.Debugf("filter: failed to update connection verdict: %s", err) } } return true } tracer.Tracef("filter: verdict to connection %s unchanged at %s", conn, conn.VerdictVerb()) return false } // SetNameserverIPMatcher sets a function that is used to match the internal // nameserver IP(s). Can only bet set once. func SetNameserverIPMatcher(fn func(ip net.IP) bool) error { if !nameserverIPMatcherSet.SetToIf(false, true) { return errors.New("nameserver IP matcher already set") } nameserverIPMatcher = fn nameserverIPMatcherReady.Set() return nil } func SetExternalVerdictHandler(fn ExtVerdictHandlerFunc) error { if !externalVerdictHandler.CompareAndSwap(nil, &fn) { return errors.New("external verdict handler already set") } return nil } func handlePacket(pkt packet.Packet) { // First, check for an existing connection. conn, ok := network.GetConnection(pkt.GetConnectionID()) if ok { // Add packet to connection handler queue or apply verdict directly. conn.HandlePacket(pkt) return } // Else create new incomplete connection from the packet and start the new handler. conn = network.NewIncompleteConnection(pkt) conn.Lock() defer conn.Unlock() conn.SetFirewallHandler(fastTrackHandler) // Let the new connection handler worker handle the packet. conn.HandlePacket(pkt) } // fastTrackedPermit quickly permits certain network critical or internal connections. func fastTrackedPermit(conn *network.Connection, pkt packet.Packet) (verdict network.Verdict, permanent bool) { meta := pkt.Info() // Check if packed was already fast-tracked by the OS integration. if pkt.FastTrackedByIntegration() { log.Tracer(pkt.Ctx()).Debugf("filter: fast-tracked by OS integration: %s", pkt) return network.VerdictAccept, true } // Check if connection was already blocked. if meta.Dst.Equal(blockedIPv4) || meta.Dst.Equal(blockedIPv6) { return network.VerdictBlock, true } // Some programs do a network self-check where they connects to the same // IP/Port to test network capabilities. // Eg. dig: https://gitlab.isc.org/isc-projects/bind9/-/issues/1140 if meta.SrcPort == meta.DstPort && meta.Src.Equal(meta.Dst) { log.Tracer(pkt.Ctx()).Debugf("filter: fast-track network self-check: %s", pkt) return network.VerdictAccept, true } switch meta.Protocol { //nolint:exhaustive // Checking for specific values only. case packet.ICMP, packet.ICMPv6: // Load packet data. err := pkt.LoadPacketData() if err != nil { log.Tracer(pkt.Ctx()).Debugf("filter: failed to load ICMP packet data: %s", err) return network.VerdictAccept, true } // Submit to ICMP listener. submitted := netenv.SubmitPacketToICMPListener(pkt) if submitted { // If the packet was submitted to the listener, we must not do a // permanent accept, because then we won't see any future packets of that // connection and thus cannot continue to submit them. log.Tracer(pkt.Ctx()).Debugf("filter: fast-track tracing ICMP/v6: %s", pkt) return network.VerdictAccept, false } // Handle echo request and replies regularly. // Other ICMP packets are considered system business. icmpLayers := pkt.Layers().LayerClass(layers.LayerClassIPControl) switch icmpLayer := icmpLayers.(type) { case *layers.ICMPv4: switch icmpLayer.TypeCode.Type() { case layers.ICMPv4TypeEchoRequest, layers.ICMPv4TypeEchoReply: return network.VerdictUndecided, false } case *layers.ICMPv6: switch icmpLayer.TypeCode.Type() { case layers.ICMPv6TypeEchoRequest, layers.ICMPv6TypeEchoReply: return network.VerdictUndecided, false } } // Permit all ICMP/v6 packets that are not echo requests or replies. log.Tracer(pkt.Ctx()).Debugf("filter: fast-track accepting ICMP/v6: %s", pkt) return network.VerdictAccept, true case packet.UDP, packet.TCP: switch meta.DstPort { case 67, 68, 546, 547: // Always allow DHCP, DHCPv6. // DHCP and DHCPv6 must be UDP. if meta.Protocol != packet.UDP { return network.VerdictUndecided, false } // DHCP is only valid in local network scopes. switch netutils.ClassifyIP(meta.Dst) { //nolint:exhaustive // Checking for specific values only. case netutils.HostLocal, netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast: default: return network.VerdictUndecided, false } // Log and permit. log.Tracer(pkt.Ctx()).Debugf("filter: fast-track accepting DHCP: %s", pkt) return network.VerdictAccept, true case apiPort: // Always allow direct access to the Portmaster API. // Portmaster API is TCP only. if meta.Protocol != packet.TCP { return network.VerdictUndecided, false } // Check if the api port is even set. if !apiPortSet { return network.VerdictUndecided, false } // Must be destined for the API IP. if !meta.Dst.Equal(apiIP) { return network.VerdictUndecided, false } // Only fast-track local requests. isMe, err := netenv.IsMyIP(meta.Src) switch { case err != nil: log.Tracer(pkt.Ctx()).Debugf("filter: failed to check if %s is own IP for fast-track: %s", meta.Src, err) return network.VerdictUndecided, false case !isMe: return network.VerdictUndecided, false } // Log and permit. log.Tracer(pkt.Ctx()).Debugf("filter: fast-track accepting api connection: %s", pkt) return network.VerdictAccept, true case 53: // Always allow direct access to the Portmaster Nameserver. // DNS is both UDP and TCP. // Check if a nameserver IP matcher is set. if !nameserverIPMatcherReady.IsSet() { return network.VerdictUndecided, false } // Check if packet is destined for a nameserver IP. if !nameserverIPMatcher(meta.Dst) { return network.VerdictUndecided, false } // Only fast-track local requests. isMe, err := netenv.IsMyIP(meta.Src) switch { case err != nil: log.Tracer(pkt.Ctx()).Debugf("filter: failed to check if %s is own IP for fast-track: %s", meta.Src, err) return network.VerdictUndecided, false case !isMe: return network.VerdictUndecided, false } // Log and permit. log.Tracer(pkt.Ctx()).Debugf("filter: fast-track accepting local dns: %s", pkt) // Add to DNS request connections to attribute DNS request if outgoing. if pkt.IsOutbound() { // Assign PID from packet directly, as processing stops after fast-track. conn.PID = pkt.Info().PID network.SaveDNSRequestConnection(conn, pkt) } // Accept local DNS, but only make permanent if we have the PID too. return network.VerdictAccept, conn.PID != process.UndefinedProcessID } // Handle rare case where PM UI connection to WebSocket API lost it's verdict (e.g., cleared conntrack entries). // Fast-tracking keeps the UI connection seamless when resuming from paused state. if meta.SrcPort == apiPort && // connections made from API port meta.Protocol == packet.TCP && // Portmaster API is TCP only apiPortSet && // API port is set meta.Src.Equal(apiIP) { // initiated from API IP // Only fast-track local requests. isToMe, _ := netenv.IsMyIP(meta.Dst) if isToMe { // Log and permit. log.Tracer(pkt.Ctx()).Debugf("filter: fast-track accepting api-outbound packet: %s", pkt) return network.VerdictAccept, true } } case compat.SystemIntegrationCheckProtocol: if pkt.Info().Dst.Equal(compat.SystemIntegrationCheckDstIP) { compat.SubmitSystemIntegrationCheckPacket(pkt) return network.VerdictDrop, false } } return network.VerdictUndecided, false } func fastTrackHandler(conn *network.Connection, pkt packet.Packet) { conn.SaveWhenFinished() fastTrackedVerdict, permanent := fastTrackedPermit(conn, pkt) if fastTrackedVerdict != network.VerdictUndecided { // Set verdict on connection. conn.Verdict = fastTrackedVerdict // Apply verdict to (real) packet. if !pkt.InfoOnly() { issueVerdict(conn, pkt, fastTrackedVerdict, permanent) } // Stop handler if permanent. if permanent { conn.SetVerdict(fastTrackedVerdict, "fast-tracked", "", nil) // Do not finalize verdict, as we are missing necessary data. conn.StopFirewallHandler() } // Do not continue to next handler. return } // If packet is not fast-tracked, continue with gathering more information. conn.UpdateFirewallHandler(gatherDataHandler) gatherDataHandler(conn, pkt) } func gatherDataHandler(conn *network.Connection, pkt packet.Packet) { conn.SaveWhenFinished() // Get process info _ = conn.GatherConnectionInfo(pkt) // Errors are informational and are logged to the context. // Run this handler again if data is not yet complete. if !conn.DataIsComplete() { return } // Continue to filter handler, when connection data is complete. switch conn.IPProtocol { //nolint:exhaustive case packet.ICMP, packet.ICMPv6: conn.UpdateFirewallHandler(icmpFilterHandler) icmpFilterHandler(conn, pkt) default: conn.UpdateFirewallHandler(filterHandler) filterHandler(conn, pkt) } } func filterHandler(conn *network.Connection, pkt packet.Packet) { conn.SaveWhenFinished() // Skip if data is not complete or packet is info-only. if !conn.DataIsComplete() || pkt.InfoOnly() { return } filterConnection := true // Check for special (internal) connection cases. switch { case !conn.Inbound && localPortIsPreAuthenticated(conn.Entity.Protocol, conn.LocalPort): // Approve connection. conn.Accept("connection by Portmaster", noReasonOptionKey) conn.Internal = true filterConnection = false log.Tracer(pkt.Ctx()).Infof("filter: granting own pre-authenticated connection %s", conn) // Redirect outbound DNS packets if enabled, case dnsQueryInterception() && !module.instance.Resolver().IsDisabled() && pkt.IsOutbound() && pkt.Info().DstPort == 53 && // that don't match the address of our nameserver, nameserverIPMatcherReady.IsSet() && !nameserverIPMatcher(pkt.Info().Dst) && // and are not broadcast queries by us. // Context: // - Unicast queries by the resolver are pre-authenticated. // - Unicast queries by the compat self-check should be redirected. !(conn.Process().Pid == ownPID && conn.Entity.IPScope == netutils.LocalMulticast): // Reroute rogue dns queries back to Portmaster. conn.SetVerdict(network.VerdictRerouteToNameserver, "redirecting rogue dns query", "", nil) conn.Internal = true log.Tracer(pkt.Ctx()).Infof("filter: redirecting dns query %s to Portmaster", conn) // Add to DNS request connections to attribute DNS request. network.SaveDNSRequestConnection(conn, pkt) // End directly, as no other processing is necessary. conn.StopFirewallHandler() issueVerdict(conn, pkt, 0, true) return } // Apply privacy filter and check tunneling. FilterConnection(pkt.Ctx(), conn, pkt, filterConnection, true) // Decide how to continue handling connection. switch { case conn.Inspecting && looksLikeOutgoingDNSRequest(conn): inspectDNSPacket(conn, pkt) conn.UpdateFirewallHandler(inspectDNSPacket) case conn.Inspecting: log.Tracer(pkt.Ctx()).Trace("filter: start inspecting") conn.UpdateFirewallHandler(inspectAndVerdictHandler) inspectAndVerdictHandler(conn, pkt) default: conn.StopFirewallHandler() verdictHandler(conn, pkt) } } // FilterConnection runs all the filtering (and tunneling) procedures. func FilterConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet, checkFilter, checkTunnel bool) { // Skip if data is not complete. if !conn.DataIsComplete() { return } // Check if external verdict handler is set, and if so, run it. // Note! This block can override the filter and tunnel check flags! if extHandler := externalVerdictHandler.Load(); extHandler != nil { verdict, reason, skipTunnel := (*extHandler)(conn) switch verdict { // Accept and Block - only these verdicts are supported to be returned by the external handler. case network.VerdictAccept, network.VerdictBlock: conn.SetVerdict(verdict, reason, "", nil) checkFilter = false checkTunnel = !skipTunnel log.Tracer(ctx).Infof("filter: special verdict %s %q %s for connection", verdict, reason, conn) } } if checkFilter { if filterEnabled() { log.Tracer(ctx).Trace("filter: starting decision process") decideOnConnection(ctx, conn, pkt) } else { conn.Accept("privacy filter disabled", noReasonOptionKey) } } // TODO: Enable inspection framework again. // conn.Inspecting = false // TODO: Quick fix for the SPN. // Use inspection framework for proper encryption detection. switch conn.Entity.DstPort() { case 22, // SSH 443, // HTTPS 465, // SMTP-SSL 853, // DoT 993, // IMAP-SSL 995: // POP3-SSL conn.Encrypted = true } // Check if connection should be tunneled. if checkTunnel { checkTunneling(ctx, conn) } // Request tunneling if no tunnel is set and connection should be tunneled. if conn.Verdict == network.VerdictRerouteToTunnel && conn.TunnelContext == nil { err := requestTunneling(ctx, conn) if err == nil { conn.ConnectionEstablished = true } else { // Set connection to failed, but keep tunneling data. // The tunneling data makes connection easy to recognize as a failed SPN // connection and the data will help with debugging and displaying in the UI. conn.Failed(fmt.Sprintf("failed to request tunneling: %s", err), "") } } } // defaultFirewallHandler is used when no other firewall handler is set on a connection. func defaultFirewallHandler(conn *network.Connection, pkt packet.Packet) { switch conn.IPProtocol { //nolint:exhaustive case packet.ICMP, packet.ICMPv6: // Always use the ICMP handler for ICMP connections. icmpFilterHandler(conn, pkt) default: verdictHandler(conn, pkt) } } func verdictHandler(conn *network.Connection, pkt packet.Packet) { // Ignore info-only packets in this handler. if pkt.InfoOnly() { return } issueVerdict(conn, pkt, 0, true) } func inspectAndVerdictHandler(conn *network.Connection, pkt packet.Packet) { // Ignore info-only packets in this handler. if pkt.InfoOnly() { return } // Run inspectors. pktVerdict, continueInspection := inspection.RunInspectors(conn, pkt) if continueInspection { issueVerdict(conn, pkt, pktVerdict, false) return } // we are done with inspecting conn.StopFirewallHandler() issueVerdict(conn, pkt, 0, true) } func inspectDNSPacket(conn *network.Connection, pkt packet.Packet) { // Ignore info-only packets in this handler. if pkt.InfoOnly() { return } dnsPacket := new(dns.Msg) err := pkt.LoadPacketData() if err != nil { _ = pkt.Block() log.Errorf("filter: failed to load packet payload: %s", err) return } // Parse and block invalid packets. err = dnsPacket.Unpack(pkt.Payload()) if err != nil { err = pkt.PermanentBlock() if err != nil { log.Errorf("filter: failed to block packet: %s", err) } _ = conn.SetVerdict(network.VerdictBlock, "none DNS data on DNS port", "", nil) conn.VerdictPermanent = true conn.Save() return } // Packet was parsed. // Allow it but only after the answer was added to the cache. defer func() { err = pkt.Accept() if err != nil { log.Errorf("filter: failed to accept dns packet: %s", err) } }() // Check if packet has a question. if len(dnsPacket.Question) == 0 { return } // Read create structs with the needed data. question := dnsPacket.Question[0] fqdn := dns.Fqdn(question.Name) // Check for compat check dns request. if strings.HasSuffix(fqdn, compat.DNSCheckInternalDomainScope) { subdomain := strings.TrimSuffix(fqdn, compat.DNSCheckInternalDomainScope) _ = compat.SubmitDNSCheckDomain(subdomain) log.Infof("packet_handler: self-check domain received") // No need to parse the answer. return } // Check if there is an answer. if len(dnsPacket.Answer) == 0 { return } resolverInfo := &resolver.ResolverInfo{ Name: "DNSRequestObserver", Type: resolver.ServerTypeFirewall, Source: resolver.ServerSourceFirewall, IP: conn.Entity.IP, Domain: conn.Entity.Domain, IPScope: conn.Entity.IPScope, } rrCache := &resolver.RRCache{ Domain: fqdn, Question: dns.Type(question.Qtype), RCode: dnsPacket.Rcode, Answer: dnsPacket.Answer, Ns: dnsPacket.Ns, Extra: dnsPacket.Extra, Resolver: resolverInfo, } query := &resolver.Query{ FQDN: fqdn, QType: dns.Type(question.Qtype), NoCaching: false, IgnoreFailing: false, LocalResolversOnly: false, ICANNSpace: false, DomainRoot: "", } // Save to cache UpdateIPsAndCNAMEs(query, rrCache, conn) } func icmpFilterHandler(conn *network.Connection, pkt packet.Packet) { // Load packet data. err := pkt.LoadPacketData() if err != nil { log.Tracer(pkt.Ctx()).Debugf("filter: failed to load ICMP packet data: %s", err) issueVerdict(conn, pkt, network.VerdictDrop, false) return } // Submit to ICMP listener. submitted := netenv.SubmitPacketToICMPListener(pkt) if submitted { issueVerdict(conn, pkt, network.VerdictDrop, false) return } // Handle echo request and replies regularly. // Other ICMP packets are considered system business. icmpLayers := pkt.Layers().LayerClass(layers.LayerClassIPControl) switch icmpLayer := icmpLayers.(type) { case *layers.ICMPv4: switch icmpLayer.TypeCode.Type() { case layers.ICMPv4TypeEchoRequest, layers.ICMPv4TypeEchoReply: // Continue default: issueVerdict(conn, pkt, network.VerdictAccept, false) return } case *layers.ICMPv6: switch icmpLayer.TypeCode.Type() { case layers.ICMPv6TypeEchoRequest, layers.ICMPv6TypeEchoReply: // Continue default: issueVerdict(conn, pkt, network.VerdictAccept, false) return } } // Check if we already have a verdict. switch conn.Verdict { //nolint:exhaustive case network.VerdictUndecided, network.VerdictUndeterminable: // Apply privacy filter and check tunneling. FilterConnection(pkt.Ctx(), conn, pkt, true, false) // Save and propagate changes. conn.SaveWhenFinished() } // Outbound direction has priority. if conn.Inbound && conn.Ended == 0 && pkt.IsOutbound() { // Change direction from inbound to outbound on first outbound ICMP packet. conn.Inbound = false // Apply privacy filter and check tunneling. FilterConnection(pkt.Ctx(), conn, pkt, true, false) // Save and propagate changes. conn.SaveWhenFinished() } issueVerdict(conn, pkt, 0, false) } func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.Verdict, allowPermanent bool) { // Check if packed was already fast-tracked by the OS integration. if pkt.FastTrackedByIntegration() { return } // Enable permanent verdict. if allowPermanent && !conn.VerdictPermanent && permanentVerdicts() { conn.VerdictPermanent = true conn.SaveWhenFinished() } // do not allow to circumvent decision: e.g. to ACCEPT packets from a DROP-ed connection if verdict < conn.Verdict { verdict = conn.Verdict } var err error switch verdict { case network.VerdictAccept: atomic.AddUint64(packetsAccepted, 1) if conn.VerdictPermanent { err = pkt.PermanentAccept() } else { err = pkt.Accept() } case network.VerdictBlock: atomic.AddUint64(packetsBlocked, 1) if conn.VerdictPermanent { err = pkt.PermanentBlock() } else { err = pkt.Block() } case network.VerdictDrop: atomic.AddUint64(packetsDropped, 1) if conn.VerdictPermanent { err = pkt.PermanentDrop() } else { err = pkt.Drop() } case network.VerdictRerouteToNameserver: err = pkt.RerouteToNameserver() case network.VerdictRerouteToTunnel: err = pkt.RerouteToTunnel() case network.VerdictFailed: atomic.AddUint64(packetsFailed, 1) err = pkt.Drop() case network.VerdictUndecided, network.VerdictUndeterminable: log.Tracer(pkt.Ctx()).Warningf("filter: tried to apply verdict %s to pkt %s: dropping instead", verdict, pkt) fallthrough default: atomic.AddUint64(packetsDropped, 1) err = pkt.Drop() } if err != nil { log.Tracer(pkt.Ctx()).Warningf("filter: failed to apply verdict to pkt %s: %s", pkt, err) } } // func tunnelHandler(pkt packet.Packet) { // tunnelInfo := GetTunnelInfo(pkt.Info().Dst) // if tunnelInfo == nil { // pkt.Block() // return // } // // entry.CreateTunnel(pkt, tunnelInfo.Domain, tunnelInfo.RRCache.ExportAllARecords()) // log.Tracef("filter: rerouting %s to tunnel entry point", pkt) // pkt.RerouteToTunnel() // return // } func packetHandler(w *mgr.WorkerCtx) error { for { select { case <-w.Done(): return nil case pkt := <-interception.Packets: if pkt != nil { handlePacket(pkt) } else { return errors.New("received nil packet from interception") } } } } func bandwidthUpdateHandler(w *mgr.WorkerCtx) error { for { select { case <-w.Done(): return nil case bwUpdate := <-interception.BandwidthUpdates: if bwUpdate != nil { // DEBUG: // log.Debugf("filter: bandwidth update: %s", bwUpdate) updateBandwidth(w.Ctx(), bwUpdate) } else { return errors.New("received nil bandwidth update from interception") } } } } func updateBandwidth(ctx context.Context, bwUpdate *packet.BandwidthUpdate) { // Check if update makes sense. if bwUpdate.BytesReceived == 0 && bwUpdate.BytesSent == 0 { return } // Get connection. conn, ok := network.GetConnection(bwUpdate.ConnID) if !ok { return } // Do not wait for connections that are locked. // TODO: Use atomic operations for updating bandwidth stats. if !conn.TryLock() { // DEBUG: // log.Warningf("filter: failed to lock connection for bandwidth update: %s", conn) return } defer conn.Unlock() bytesIn := bwUpdate.BytesReceived bytesOut := bwUpdate.BytesSent // Update stats according to method. switch bwUpdate.Method { case packet.Absolute: bytesIn = bwUpdate.BytesReceived - conn.BytesReceived bytesOut = bwUpdate.BytesSent - conn.BytesSent conn.BytesReceived = bwUpdate.BytesReceived conn.BytesSent = bwUpdate.BytesSent case packet.Additive: conn.BytesReceived += bwUpdate.BytesReceived conn.BytesSent += bwUpdate.BytesSent default: log.Warningf("filter: unsupported bandwidth update method: %d", bwUpdate.Method) return } // Update bandwidth in the netquery module. if module.instance.NetQuery() != nil && conn.BandwidthEnabled { if err := module.instance.NetQuery().Store.UpdateBandwidth( ctx, conn.HistoryEnabled, fmt.Sprintf("%s/%s", conn.ProcessContext.Source, conn.ProcessContext.Profile), conn.Process().GetKey(), conn.ID, bytesIn, bytesOut, ); err != nil { log.Errorf("filter: failed to persist bandwidth data: %s", err) } } } func statLogger(w *mgr.WorkerCtx) error { for { select { case <-w.Done(): return nil case <-time.After(10 * time.Second): log.Tracef( "filter: packets accepted %d, blocked %d, dropped %d, failed %d", atomic.LoadUint64(packetsAccepted), atomic.LoadUint64(packetsBlocked), atomic.LoadUint64(packetsDropped), atomic.LoadUint64(packetsFailed), ) atomic.StoreUint64(packetsAccepted, 0) atomic.StoreUint64(packetsBlocked, 0) atomic.StoreUint64(packetsDropped, 0) atomic.StoreUint64(packetsFailed, 0) } } } ================================================ FILE: service/firewall/preauth.go ================================================ package firewall import ( "fmt" "net" "strconv" "sync" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/resolver" ) var ( preAuthenticatedPorts = make(map[string]struct{}) preAuthenticatedPortsLock sync.Mutex ) func init() { resolver.SetLocalAddrFactory(PermittedAddr) netenv.SetLocalAddrFactory(PermittedAddr) } // PermittedAddr returns an already permitted local address for the given network for reliable connectivity. // Returns nil in case of error. func PermittedAddr(network string) net.Addr { switch network { case "udp": return PermittedUDPAddr() case "tcp": return PermittedTCPAddr() } return nil } // PermittedUDPAddr returns an already permitted local udp address for reliable connectivity. // Returns nil in case of error. func PermittedUDPAddr() *net.UDPAddr { preAuthdPort := GetPermittedPort(packet.UDP) if preAuthdPort == 0 { return nil } addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", preAuthdPort)) if err != nil { return nil } return addr } // PermittedTCPAddr returns an already permitted local tcp address for reliable connectivity. // Returns nil in case of error. func PermittedTCPAddr() *net.TCPAddr { preAuthdPort := GetPermittedPort(packet.TCP) if preAuthdPort == 0 { return nil } addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf(":%d", preAuthdPort)) if err != nil { return nil } return addr } // GetPermittedPort returns a local port number that is already permitted for communication. // This bypasses the process attribution step to guarantee connectivity. // Communication on the returned port is attributed to the Portmaster. // Every pre-authenticated port is only valid once. // If no unused local port number can be found, it will return 0, which is // expected to trigger automatic port selection by the underlying OS. func GetPermittedPort(protocol packet.IPProtocol) uint16 { port, ok := network.GetUnusedLocalPort(uint8(protocol)) if !ok { return 0 } preAuthenticatedPortsLock.Lock() defer preAuthenticatedPortsLock.Unlock() // Save generated port. key := generateLocalPreAuthKey(uint8(protocol), port) preAuthenticatedPorts[key] = struct{}{} return port } // localPortIsPreAuthenticated checks if the given protocol and port are // pre-authenticated and should be attributed to the Portmaster itself. func localPortIsPreAuthenticated(protocol uint8, port uint16) bool { preAuthenticatedPortsLock.Lock() defer preAuthenticatedPortsLock.Unlock() // Check if the given protocol and port are pre-authenticated. key := generateLocalPreAuthKey(protocol, port) _, ok := preAuthenticatedPorts[key] if ok { // Immediately remove pre authenticated port. delete(preAuthenticatedPorts, key) } return ok } // generateLocalPreAuthKey creates a map key for the pre-authenticated ports. func generateLocalPreAuthKey(protocol uint8, port uint16) string { return strconv.Itoa(int(protocol)) + ":" + strconv.Itoa(int(port)) } ================================================ FILE: service/firewall/prompt.go ================================================ package firewall import ( "context" "fmt" "sync" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/endpoints" ) const ( // notification action IDs. allowDomainAll = "allow-domain-all" allowDomainDistinct = "allow-domain-distinct" blockDomainAll = "block-domain-all" blockDomainDistinct = "block-domain-distinct" allowIP = "allow-ip" blockIP = "block-ip" allowServingIP = "allow-serving-ip" blockServingIP = "block-serving-ip" cancelPrompt = "cancel" ) var ( promptNotificationCreation sync.Mutex decisionTimeout int64 = 10 // in seconds ) type promptData struct { Entity *intel.Entity Profile promptProfile } type promptProfile struct { Source string ID string LinkedPath string } func prompt(ctx context.Context, conn *network.Connection) { // Create notification. n := createPrompt(ctx, conn) if n == nil { // createPrompt returns nil when no further action should be taken. return } // Add prompt to connection. conn.SetPrompt(n) // Get decision timeout and make sure it does not exceed the ask timeout. timeout := decisionTimeout if timeout > askTimeout() { timeout = askTimeout() } // wait for response/timeout select { case promptResponse := <-n.Response(): switch promptResponse { case allowDomainAll, allowDomainDistinct, allowIP, allowServingIP: // Accept conn.Accept("allowed via prompt", profile.CfgOptionEndpointsKey) case "": // Dismissed conn.Deny("prompting canceled, waiting for new decision", profile.CfgOptionDefaultActionKey) default: // Deny conn.Deny("blocked via prompt", profile.CfgOptionEndpointsKey) } case <-time.After(time.Duration(timeout) * time.Second): log.Tracer(ctx).Debugf("filter: continuing prompting async") conn.Deny("prompting in progress, please respond to prompt", profile.CfgOptionDefaultActionKey) case <-ctx.Done(): log.Tracer(ctx).Debugf("filter: aborting prompting because of shutdown") conn.Drop("shutting down", noReasonOptionKey) } } // promptIDPrefix is an identifier for privacy filter prompts. This is also used // in the UI, so don't change! const promptIDPrefix = "filter:prompt" func createPrompt(ctx context.Context, conn *network.Connection) (n *notifications.Notification) { expires := time.Now().Add(time.Duration(askTimeout()) * time.Second).Unix() // Get local profile. layeredProfile := conn.Process().Profile() if layeredProfile == nil { log.Tracer(ctx).Warningf("filter: tried creating prompt for connection without profile") return nil } localProfile := layeredProfile.LocalProfile() if localProfile == nil { log.Tracer(ctx).Warningf("filter: tried creating prompt for connection without local profile") return nil } // first check if there is an existing notification for this. // build notification ID var nID string switch { case conn.Inbound, conn.Entity.Domain == "": // connection to/from IP nID = fmt.Sprintf( "%s-%s-%v-%s", promptIDPrefix, localProfile.ID, conn.Inbound, conn.Entity.IP, ) default: // connection to domain nID = fmt.Sprintf( "%s-%s-%s", promptIDPrefix, localProfile.ID, conn.Entity.Domain, ) } // Only handle one notification at a time. promptNotificationCreation.Lock() defer promptNotificationCreation.Unlock() n = notifications.Get(nID) // If there already is a notification, just update the expiry. if n != nil { // Get notification state and action. n.Lock() state := n.State action := n.SelectedActionID n.Unlock() // If the notification is still active, extend and return. // This can happen because user input (prompts changing the endpoint // lists) can happen any time - also between checking the endpoint lists // and now. if state == notifications.Active { n.Update(expires) log.Tracer(ctx).Debugf("filter: updated existing prompt notification") return n } // The notification is not active anymore, let's check if there is an // action we can perform. // If there already is an action defined, we won't be fast enough to // receive the action with n.Response(), so we take direct action here. if action != "" { switch action { case allowDomainAll, allowDomainDistinct, allowIP, allowServingIP: conn.Accept("allowed via prompt", profile.CfgOptionEndpointsKey) default: // deny conn.Deny("blocked via prompt", profile.CfgOptionEndpointsKey) } return nil // Do not take further action. } // Continue to create a new notification because the previous one is not // active and not actionable. } // Reference relevant data for save function entity := conn.Entity // Also needed: localProfile // Create new notification. n = ¬ifications.Notification{ EventID: nID, Type: notifications.Prompt, Title: "Connection Prompt", Category: "Privacy Filter", ShowOnSystem: askWithSystemNotifications(), EventData: &promptData{ Entity: entity, Profile: promptProfile{ Source: string(localProfile.Source), ID: localProfile.ID, // LinkedPath is used to enhance the display of the prompt in the UI. // TODO: Using the process path is a workaround. Find a cleaner solution. LinkedPath: conn.Process().Path, }, }, Expires: expires, } // Set action function. n.SetActionFunction(func(_ context.Context, n *notifications.Notification) error { return saveResponse( localProfile, entity, n.SelectedActionID, ) }) // Get name of profile for notification. The profile is read-locked by the firewall handler. profileName := localProfile.Name // add message and actions switch { case conn.Inbound: n.Message = fmt.Sprintf("%s wants to accept connections from %s (%d/%d)", profileName, conn.Entity.IP.String(), conn.Entity.Protocol, conn.Entity.Port) n.AvailableActions = []*notifications.Action{ { ID: allowServingIP, Text: "Allow", }, { ID: blockServingIP, Text: "Block", }, } case conn.Entity.Domain == "": // direct connection n.Message = fmt.Sprintf("%s wants to connect to %s (%d/%d)", profileName, conn.Entity.IP.String(), conn.Entity.Protocol, conn.Entity.Port) n.AvailableActions = []*notifications.Action{ { ID: allowIP, Text: "Allow", }, { ID: blockIP, Text: "Block", }, } default: // connection to domain n.Message = fmt.Sprintf("%s wants to connect to %s", profileName, conn.Entity.Domain) n.AvailableActions = []*notifications.Action{ { ID: allowDomainAll, Text: "Allow", }, { ID: blockDomainAll, Text: "Block", }, } } n.Save() log.Tracer(ctx).Debugf("filter: sent prompt notification") return n } // promptSavingLock makes sure that only one prompt is saved at a time. // Should prompts be persisted in bulk, the next save process might load an // outdated profile and save it, losing config data. var promptSavingLock sync.Mutex func saveResponse(p *profile.Profile, entity *intel.Entity, promptResponse string) error { if promptResponse == cancelPrompt { return nil } promptSavingLock.Lock() defer promptSavingLock.Unlock() // Update the profile if necessary. if p.IsOutdated() { var err error p, err = profile.GetLocalProfile(p.ID, nil, nil) if err != nil { return err } } var ep endpoints.Endpoint switch promptResponse { case allowDomainAll: ep = &endpoints.EndpointDomain{ EndpointBase: endpoints.EndpointBase{Permitted: true}, OriginalValue: "." + entity.Domain, } case allowDomainDistinct: ep = &endpoints.EndpointDomain{ EndpointBase: endpoints.EndpointBase{Permitted: true}, OriginalValue: entity.Domain, } case blockDomainAll: ep = &endpoints.EndpointDomain{ EndpointBase: endpoints.EndpointBase{Permitted: false}, OriginalValue: "." + entity.Domain, } case blockDomainDistinct: ep = &endpoints.EndpointDomain{ EndpointBase: endpoints.EndpointBase{Permitted: false}, OriginalValue: entity.Domain, } case allowIP, allowServingIP: ep = &endpoints.EndpointIP{ EndpointBase: endpoints.EndpointBase{Permitted: true}, IP: entity.IP, } case blockIP, blockServingIP: ep = &endpoints.EndpointIP{ EndpointBase: endpoints.EndpointBase{Permitted: false}, IP: entity.IP, } case cancelPrompt: return nil default: return fmt.Errorf("unknown prompt response: %s", promptResponse) } switch promptResponse { case allowServingIP, blockServingIP: p.AddServiceEndpoint(ep.String()) log.Infof("filter: added incoming rule to profile %s (LP Rev. %d): %q", p, p.LayeredProfile().RevisionCnt(), ep.String()) default: p.AddEndpoint(ep.String()) log.Infof("filter: added outgoing rule to profile %s (LP Rev. %d): %q", p, p.LayeredProfile().RevisionCnt(), ep.String()) } return nil } ================================================ FILE: service/firewall/tunnel.go ================================================ package firewall import ( "context" "errors" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/endpoints" "github.com/safing/portmaster/service/resolver" "github.com/safing/portmaster/spn/captain" "github.com/safing/portmaster/spn/crew" "github.com/safing/portmaster/spn/navigator" "github.com/safing/portmaster/spn/sluice" ) func checkTunneling(ctx context.Context, conn *network.Connection) { // Check if the connection should be tunneled at all. switch { case !tunnelEnabled(): // Tunneling is disabled. return case !conn.Entity.IPScope.IsGlobal(): // Can't tunnel Local/LAN connections. return case conn.Inbound: // Can't tunnel incoming connections. return case conn.Verdict != network.VerdictAccept: // Connection will be blocked. return case conn.IPProtocol != packet.TCP && conn.IPProtocol != packet.UDP: // Unsupported protocol. return case conn.Process().Pid == ownPID: // Bypass tunneling for certain own connections. switch { case !captain.ClientReady(): return case captain.IsExcepted(conn.Entity.IP): return } } // Check more extensively for Local/LAN connections. localNet, err := netenv.GetLocalNetwork(conn.Entity.IP) if err != nil { log.Warningf("firewall: failed to check if %s is in my net: %s", conn.Entity.IP, err) } else if localNet != nil { // With IPv6, just checking the IP scope is not enough, as the host very // likely has a public IPv6 address. // Don't tunnel LAN connections. // TODO: We currently don't check the full LAN scope, but only the // broadcast domain of the host - ie. the networks that the host is // directly attached to. return } // Get profile. layeredProfile := conn.Process().Profile() if layeredProfile == nil { conn.Failed("no profile set", "") return } // Update profile. if layeredProfile.NeedsUpdate() { // Update revision counter in connection. conn.ProfileRevisionCounter = layeredProfile.Update( conn.Process().MatchingData(), conn.Process().CreateProfileCallback, ) conn.SaveWhenFinished() } else { // Check if the revision counter of the connection needs updating. revCnt := layeredProfile.RevisionCnt() if conn.ProfileRevisionCounter != revCnt { conn.ProfileRevisionCounter = revCnt conn.SaveWhenFinished() } } // Check if tunneling is enabled for this app at all. if !layeredProfile.UseSPN() { return } // Check if tunneling is enabled for entity. conn.Entity.FetchData(ctx) result, _ := layeredProfile.MatchSPNUsagePolicy(ctx, conn.Entity) switch result { case endpoints.MatchError: conn.Failed("failed to check SPN rules", profile.CfgOptionSPNUsagePolicyKey) return case endpoints.Denied: return case endpoints.Permitted, endpoints.NoMatch: // Continue } // Tunnel all the things! conn.SaveWhenFinished() // Check if ready. if !captain.ClientReady() { // Block connection as SPN is not ready yet. log.Tracer(ctx).Trace("SPN not ready for tunneling") conn.Failed("SPN not ready for tunneling", "") return } conn.SetVerdictDirectly(network.VerdictRerouteToTunnel) conn.Tunneled = true } func requestTunneling(ctx context.Context, conn *network.Connection) error { // Get profile. layeredProfile := conn.Process().Profile() if layeredProfile == nil { return errors.New("no profile set") } // Get tunnel options. conn.TunnelOpts = DeriveTunnelOptions(layeredProfile, conn.Process(), conn.Entity, conn.Encrypted) // Queue request in sluice. err := sluice.AwaitRequest(conn, crew.HandleSluiceRequest) if err != nil { return err } log.Tracer(ctx).Trace("filter: tunneling requested") return nil } func init() { navigator.DeriveTunnelOptions = func(lp *profile.LayeredProfile, destination *intel.Entity, connEncrypted bool) *navigator.Options { return DeriveTunnelOptions(lp, nil, destination, connEncrypted) } } // DeriveTunnelOptions derives and returns the tunnel options from the connection and profile. func DeriveTunnelOptions(lp *profile.LayeredProfile, proc *process.Process, destination *intel.Entity, connEncrypted bool) *navigator.Options { // Set options. tunnelOpts := &navigator.Options{ Transit: &navigator.TransitHubOptions{ HubPolicies: lp.StackedTransitHubPolicies(), }, Destination: &navigator.DestinationHubOptions{ HubPolicies: lp.StackedExitHubPolicies(), CheckHubPolicyWith: destination, }, RoutingProfile: lp.SPNRoutingAlgorithm(), } if !connEncrypted { tunnelOpts.Destination.Regard = tunnelOpts.Destination.Regard.Add(navigator.StateTrusted) // TODO: Add this when all Hubs are on v0.6.21+ // tunnelOpts.Destination.Regard = tunnelOpts.Destination.Regard.Add(navigator.StateAllowUnencrypted) } // Add required verified owners if community nodes should not be used. if !useCommunityNodes() { tunnelOpts.Transit.RequireVerifiedOwners = captain.NonCommunityVerifiedOwners tunnelOpts.Destination.RequireVerifiedOwners = captain.NonCommunityVerifiedOwners } // Get routing profile for checking for upgrades. routingProfile := navigator.GetRoutingProfile(tunnelOpts.RoutingProfile) // If we have any exit hub policies, we must be able to hop in order to follow the policy. // Switch to single-hop routing to allow for routing with hub selection. if routingProfile.MaxHops <= 1 && navigator.HubPoliciesAreSet(tunnelOpts.Destination.HubPolicies) { tunnelOpts.RoutingProfile = navigator.RoutingProfileSingleHopID } // If the current home node is not trusted, then upgrade at least to two hops. if routingProfile.MinHops < 2 { homeNode, _ := navigator.Main.GetHome() if homeNode != nil && !homeNode.State.Has(navigator.StateTrusted) { tunnelOpts.RoutingProfile = navigator.RoutingProfileDoubleHopID } } // Special handling for the internal DNS resolver. if proc != nil && proc.Pid == ownPID && resolver.IsResolverAddress(destination.IP, destination.Port) { dnsExitHubPolicy, err := captain.GetDNSExitHubPolicy() if err != nil { log.Errorf("firewall: failed to get dns exit hub policy: %s", err) } if err == nil && dnsExitHubPolicy.IsSet() { // Apply the dns exit hub policy, if set. tunnelOpts.Destination.HubPolicies = []endpoints.Endpoints{dnsExitHubPolicy} // Use the routing algorithm from the profile, as the home profile won't work with the policy. tunnelOpts.RoutingProfile = lp.SPNRoutingAlgorithm() // Raise the routing algorithm at least to single-hop. if tunnelOpts.RoutingProfile == navigator.RoutingProfileHomeID { tunnelOpts.RoutingProfile = navigator.RoutingProfileSingleHopID } } else { // Disable any policies for the internal DNS resolver. tunnelOpts.Destination.HubPolicies = nil // Always use the home routing profile for the internal DNS resolver. tunnelOpts.RoutingProfile = navigator.RoutingProfileHomeID } } return tunnelOpts } ================================================ FILE: service/instance.go ================================================ package service import ( "context" "fmt" "sync/atomic" "time" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" "github.com/safing/portmaster/base/metrics" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/base/runtime" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/broadcasts" "github.com/safing/portmaster/service/compat" "github.com/safing/portmaster/service/control" "github.com/safing/portmaster/service/core" "github.com/safing/portmaster/service/core/base" "github.com/safing/portmaster/service/firewall" "github.com/safing/portmaster/service/firewall/interception" "github.com/safing/portmaster/service/firewall/interception/dnsmonitor" "github.com/safing/portmaster/service/integration" "github.com/safing/portmaster/service/intel/customlists" "github.com/safing/portmaster/service/intel/filterlists" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/service/interop" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/nameserver" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/netquery" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/resolver" "github.com/safing/portmaster/service/status" "github.com/safing/portmaster/service/sync" "github.com/safing/portmaster/service/ui" "github.com/safing/portmaster/service/updates" "github.com/safing/portmaster/spn/access" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/captain" "github.com/safing/portmaster/spn/crew" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/navigator" "github.com/safing/portmaster/spn/patrol" "github.com/safing/portmaster/spn/ships" "github.com/safing/portmaster/spn/sluice" "github.com/safing/portmaster/spn/terminal" ) // Instance is an instance of a Portmaster service. type Instance struct { ctx context.Context cancelCtx context.CancelFunc shutdownCtx context.Context cancelShutdownCtx context.CancelFunc serviceGroup *mgr.Group serviceGroupInterception *mgr.GroupModule binDir string dataDir string exitCode atomic.Int32 database *dbmodule.DBModule config *config.Config api *api.API metrics *metrics.Metrics runtime *runtime.Runtime notifications *notifications.Notifications rng *rng.Rng base *base.Base core *core.Core binaryUpdates *updates.Updater intelUpdates *updates.Updater integration *integration.OSIntegration geoip *geoip.GeoIP netenv *netenv.NetEnv ui *ui.UI profile *profile.ProfileModule network *network.Network netquery *netquery.NetQuery firewall *firewall.Firewall filterLists *filterlists.FilterLists interception *interception.Interception dnsmonitor *dnsmonitor.DNSMonitor customlist *customlists.CustomList status *status.Status broadcasts *broadcasts.Broadcasts compat *compat.Compat nameserver *nameserver.NameServer process *process.ProcessModule resolver *resolver.ResolverModule sync *sync.Sync control *control.Control interop *interop.Interoperability access *access.Access // SPN modules SpnGroup *mgr.ExtendedGroup cabin *cabin.Cabin navigator *navigator.Navigator captain *captain.Captain crew *crew.Crew docks *docks.Docks patrol *patrol.Patrol ships *ships.Ships sluice *sluice.SluiceModule terminal *terminal.TerminalModule CommandLineOperation func() error ShouldRestart bool } // New returns a new Portmaster service instance. func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx // Initialize config. err := svcCfg.Init() if err != nil { return nil, fmt.Errorf("internal service config error: %w", err) } // Make sure data dir exists, so that child directories don't dictate the permissions. err = utils.EnsureDirectory(svcCfg.DataDir, utils.PublicReadExecPermission) if err != nil { return nil, fmt.Errorf("data directory %s is not accessible: %w", svcCfg.DataDir, err) } // Create instance to pass it to modules. instance := &Instance{ binDir: svcCfg.BinDir, dataDir: svcCfg.DataDir, } instance.ctx, instance.cancelCtx = context.WithCancel(context.Background()) instance.shutdownCtx, instance.cancelShutdownCtx = context.WithCancel(context.Background()) // Base modules instance.base, err = base.New(instance) if err != nil { return instance, fmt.Errorf("create base module: %w", err) } instance.database, err = dbmodule.New(instance) if err != nil { return instance, fmt.Errorf("create database module: %w", err) } instance.config, err = config.New(instance) if err != nil { return instance, fmt.Errorf("create config module: %w", err) } instance.api, err = api.New(instance) if err != nil { return instance, fmt.Errorf("create api module: %w", err) } instance.metrics, err = metrics.New(instance) if err != nil { return instance, fmt.Errorf("create metrics module: %w", err) } instance.runtime, err = runtime.New(instance) if err != nil { return instance, fmt.Errorf("create runtime module: %w", err) } instance.notifications, err = notifications.New(instance) if err != nil { return instance, fmt.Errorf("create runtime module: %w", err) } instance.rng, err = rng.New(instance) if err != nil { return instance, fmt.Errorf("create rng module: %w", err) } // Service modules binaryUpdateConfig, intelUpdateConfig, err := MakeUpdateConfigs(svcCfg) if err != nil { return instance, fmt.Errorf("create updates config: %w", err) } instance.binaryUpdates, err = updates.New(instance, "Binary Updater", *binaryUpdateConfig) if err != nil { return instance, fmt.Errorf("create updates module: %w", err) } instance.intelUpdates, err = updates.New(instance, "Intel Updater", *intelUpdateConfig) if err != nil { return instance, fmt.Errorf("create updates module: %w", err) } instance.core, err = core.New(instance) if err != nil { return instance, fmt.Errorf("create core module: %w", err) } instance.integration, err = integration.New(instance) if err != nil { return instance, fmt.Errorf("create integration module: %w", err) } instance.geoip, err = geoip.New(instance) if err != nil { return instance, fmt.Errorf("create customlist module: %w", err) } instance.netenv, err = netenv.New(instance) if err != nil { return instance, fmt.Errorf("create netenv module: %w", err) } instance.ui, err = ui.New(instance) if err != nil { return instance, fmt.Errorf("create ui module: %w", err) } instance.profile, err = profile.NewModule(instance) if err != nil { return instance, fmt.Errorf("create profile module: %w", err) } instance.network, err = network.New(instance) if err != nil { return instance, fmt.Errorf("create network module: %w", err) } instance.netquery, err = netquery.NewModule(instance) if err != nil { return instance, fmt.Errorf("create netquery module: %w", err) } instance.firewall, err = firewall.New(instance) if err != nil { return instance, fmt.Errorf("create firewall module: %w", err) } instance.filterLists, err = filterlists.New(instance) if err != nil { return instance, fmt.Errorf("create filterLists module: %w", err) } instance.interception, err = interception.New(instance) if err != nil { return instance, fmt.Errorf("create interception module: %w", err) } instance.dnsmonitor, err = dnsmonitor.New(instance) if err != nil { return instance, fmt.Errorf("create dns-listener module: %w", err) } instance.customlist, err = customlists.New(instance) if err != nil { return instance, fmt.Errorf("create customlist module: %w", err) } instance.status, err = status.New(instance) if err != nil { return instance, fmt.Errorf("create status module: %w", err) } instance.broadcasts, err = broadcasts.New(instance) if err != nil { return instance, fmt.Errorf("create broadcasts module: %w", err) } instance.compat, err = compat.New(instance) if err != nil { return instance, fmt.Errorf("create compat module: %w", err) } instance.nameserver, err = nameserver.New(instance) if err != nil { return instance, fmt.Errorf("create nameserver module: %w", err) } instance.process, err = process.New(instance) if err != nil { return instance, fmt.Errorf("create process module: %w", err) } instance.resolver, err = resolver.New(instance) if err != nil { return instance, fmt.Errorf("create resolver module: %w", err) } instance.sync, err = sync.New(instance) if err != nil { return instance, fmt.Errorf("create sync module: %w", err) } instance.control, err = control.New(instance) if err != nil { return instance, fmt.Errorf("create control module: %w", err) } instance.interop, err = interop.New(instance) if err != nil { return instance, fmt.Errorf("create interop module: %w", err) } instance.access, err = access.New(instance) if err != nil { return instance, fmt.Errorf("create access module: %w", err) } // SPN modules instance.cabin, err = cabin.New(instance) if err != nil { return instance, fmt.Errorf("create cabin module: %w", err) } instance.navigator, err = navigator.New(instance) if err != nil { return instance, fmt.Errorf("create navigator module: %w", err) } instance.captain, err = captain.New(instance) if err != nil { return instance, fmt.Errorf("create captain module: %w", err) } instance.crew, err = crew.New(instance) if err != nil { return instance, fmt.Errorf("create crew module: %w", err) } instance.docks, err = docks.New(instance) if err != nil { return instance, fmt.Errorf("create docks module: %w", err) } instance.patrol, err = patrol.New(instance) if err != nil { return instance, fmt.Errorf("create patrol module: %w", err) } instance.ships, err = ships.New(instance) if err != nil { return instance, fmt.Errorf("create ships module: %w", err) } instance.sluice, err = sluice.New(instance) if err != nil { return instance, fmt.Errorf("create sluice module: %w", err) } instance.terminal, err = terminal.New(instance) if err != nil { return instance, fmt.Errorf("create terminal module: %w", err) } // Grouped interception modules that can be paused/resumed together. instance.serviceGroupInterception = mgr.NewGroupModule("Interception Group", instance.interception, instance.dnsmonitor, instance.compat) // Add all modules to instance group. instance.serviceGroup = mgr.NewGroup( instance.base, instance.rng, instance.database, instance.config, instance.api, instance.metrics, instance.runtime, instance.notifications, instance.core, instance.binaryUpdates, instance.intelUpdates, instance.integration, instance.geoip, instance.netenv, instance.process, instance.profile, instance.network, instance.netquery, instance.firewall, instance.nameserver, instance.resolver, instance.filterLists, instance.customlist, instance.interop, // required to start before interception // Grouped pausable interception modules: // instance.interception, // instance.dnsmonitor, // instance.compat instance.serviceGroupInterception, instance.status, instance.broadcasts, instance.sync, instance.ui, instance.control, instance.access, ) // SPN Group instance.SpnGroup = mgr.NewExtendedGroup( instance.cabin, instance.navigator, instance.captain, instance.crew, instance.docks, instance.patrol, instance.ships, instance.sluice, instance.terminal, ) return instance, nil } // SleepyModule is an interface for modules that can enter some sort of sleep mode. type SleepyModule interface { SetSleep(enabled bool) } // SetSleep sets sleep mode on all modules that satisfy the SleepyModule interface. func (i *Instance) SetSleep(enabled bool) { for _, module := range i.serviceGroup.Modules() { if sm, ok := module.(SleepyModule); ok { sm.SetSleep(enabled) } } for _, module := range i.SpnGroup.Modules() { if sm, ok := module.(SleepyModule); ok { sm.SetSleep(enabled) } } } // BinDir returns the directory for binaries. // This directory may be read-only. func (i *Instance) BinDir() string { return i.binDir } // DataDir returns the directory for variable data. // This directory is expected to be read/writeable. func (i *Instance) DataDir() string { return i.dataDir } // Database returns the database module. func (i *Instance) Database() *dbmodule.DBModule { return i.database } // Config returns the config module. func (i *Instance) Config() *config.Config { return i.config } // API returns the api module. func (i *Instance) API() *api.API { return i.api } // Metrics returns the metrics module. func (i *Instance) Metrics() *metrics.Metrics { return i.metrics } // Runtime returns the runtime module. func (i *Instance) Runtime() *runtime.Runtime { return i.runtime } // Notifications returns the notifications module. func (i *Instance) Notifications() *notifications.Notifications { return i.notifications } // Rng returns the rng module. func (i *Instance) Rng() *rng.Rng { return i.rng } // Base returns the base module. func (i *Instance) Base() *base.Base { return i.base } // BinaryUpdates returns the updates module. func (i *Instance) BinaryUpdates() *updates.Updater { return i.binaryUpdates } // GetBinaryUpdateFile returns the file path of a binary update file. func (i *Instance) GetBinaryUpdateFile(name string) (path string, err error) { file, err := i.binaryUpdates.GetFile(name) if err != nil { return "", err } return file.Path(), nil } // IntelUpdates returns the updates module. func (i *Instance) IntelUpdates() *updates.Updater { return i.intelUpdates } // OSIntegration returns the integration module. func (i *Instance) OSIntegration() *integration.OSIntegration { return i.integration } // GeoIP returns the geoip module. func (i *Instance) GeoIP() *geoip.GeoIP { return i.geoip } // NetEnv returns the netenv module. func (i *Instance) NetEnv() *netenv.NetEnv { return i.netenv } // Access returns the access module. func (i *Instance) Access() *access.Access { return i.access } // Cabin returns the cabin module. func (i *Instance) Cabin() *cabin.Cabin { return i.cabin } // Captain returns the captain module. func (i *Instance) Captain() *captain.Captain { return i.captain } // Crew returns the crew module. func (i *Instance) Crew() *crew.Crew { return i.crew } // Docks returns the crew module. func (i *Instance) Docks() *docks.Docks { return i.docks } // Navigator returns the navigator module. func (i *Instance) Navigator() *navigator.Navigator { return i.navigator } // Patrol returns the patrol module. func (i *Instance) Patrol() *patrol.Patrol { return i.patrol } // Ships returns the ships module. func (i *Instance) Ships() *ships.Ships { return i.ships } // Sluice returns the ships module. func (i *Instance) Sluice() *sluice.SluiceModule { return i.sluice } // Terminal returns the terminal module. func (i *Instance) Terminal() *terminal.TerminalModule { return i.terminal } // UI returns the ui module. func (i *Instance) UI() *ui.UI { return i.ui } // Profile returns the profile module. func (i *Instance) Profile() *profile.ProfileModule { return i.profile } // Firewall returns the firewall module. func (i *Instance) Firewall() *firewall.Firewall { return i.firewall } // FilterLists returns the filterLists module. func (i *Instance) FilterLists() *filterlists.FilterLists { return i.filterLists } // Interception returns the interception module. func (i *Instance) Interception() *interception.Interception { return i.interception } // InterceptionGroup returns the grouped interception modules that can be paused together. func (i *Instance) InterceptionGroup() *mgr.GroupModule { return i.serviceGroupInterception } // DNSMonitor returns the dns-listener module. func (i *Instance) DNSMonitor() *dnsmonitor.DNSMonitor { return i.dnsmonitor } // CustomList returns the customlist module. func (i *Instance) CustomList() *customlists.CustomList { return i.customlist } // Status returns the status module. func (i *Instance) Status() *status.Status { return i.status } // Broadcasts returns the broadcast module. func (i *Instance) Broadcasts() *broadcasts.Broadcasts { return i.broadcasts } // Compat returns the compat module. func (i *Instance) Compat() *compat.Compat { return i.compat } // NameServer returns the nameserver module. func (i *Instance) NameServer() *nameserver.NameServer { return i.nameserver } // NetQuery returns the netquery module. func (i *Instance) NetQuery() *netquery.NetQuery { return i.netquery } // Network returns the network module. func (i *Instance) Network() *network.Network { return i.network } // Process returns the process module. func (i *Instance) Process() *process.ProcessModule { return i.process } // Resolver returns the resolver module. func (i *Instance) Resolver() *resolver.ResolverModule { return i.resolver } // Sync returns the sync module. func (i *Instance) Sync() *sync.Sync { return i.sync } // Core returns the core module. func (i *Instance) Core() *core.Core { return i.core } // SPNGroup returns the group of all SPN modules. func (i *Instance) SPNGroup() *mgr.ExtendedGroup { return i.SpnGroup } // Events // GetEventSPNConnected return the event manager for the SPN connected event. func (i *Instance) GetEventSPNConnected() *mgr.EventMgr[struct{}] { return i.captain.EventSPNConnected } // Special functions // SetCmdLineOperation sets a command line operation to be executed instead of starting the system. This is useful when functions need all modules to be prepared for a special operation. func (i *Instance) SetCmdLineOperation(f func() error) { i.CommandLineOperation = f } // GetStates returns the current states of all group modules. func (i *Instance) GetStates() []mgr.StateUpdate { mainStates := i.serviceGroup.GetStates() spnStates := i.SpnGroup.GetStates() updates := make([]mgr.StateUpdate, 0, len(mainStates)+len(spnStates)) updates = append(updates, mainStates...) updates = append(updates, spnStates...) return updates } // AddStatesCallback adds the given callback function to all group modules that // expose a state manager at States(). func (i *Instance) AddStatesCallback(callbackName string, callback mgr.EventCallbackFunc[mgr.StateUpdate]) { i.serviceGroup.AddStatesCallback(callbackName, callback) i.SpnGroup.AddStatesCallback(callbackName, callback) } // Ready returns whether all modules in the main service module group have been started and are still running. func (i *Instance) Ready() bool { return i.serviceGroup.Ready() } // Start starts the instance modules. func (i *Instance) Start() error { return i.serviceGroup.Start() } // Stop stops the instance modules. func (i *Instance) Stop() error { return i.serviceGroup.Stop() } // RestartExitCode will instruct portmaster-start to restart the process immediately, potentially with a new version. const RestartExitCode = 23 // Restart asynchronously restarts the instance. // This only works if the underlying system/process supports this. func (i *Instance) Restart() { // Send a restart event, give it 10ms extra to propagate. i.core.EventRestart.Submit(struct{}{}) time.Sleep(10 * time.Millisecond) // Set the restart flag and shutdown. i.ShouldRestart = true i.shutdown(RestartExitCode) } // Shutdown asynchronously stops the instance. func (i *Instance) Shutdown() { // Send a shutdown event, give it 10ms extra to propagate. i.core.EventShutdown.Submit(struct{}{}) time.Sleep(10 * time.Millisecond) i.shutdown(0) } func (i *Instance) shutdown(exitCode int) { // Only shutdown once. if i.IsShuttingDown() { return } // Cancel main context. i.cancelCtx() // Set given exit code. i.exitCode.Store(int32(exitCode)) // Start shutdown asynchronously in a separate manager. m := mgr.New("instance") m.Go("shutdown", func(w *mgr.WorkerCtx) error { // Stop all modules. if err := i.Stop(); err != nil { w.Error("failed to shutdown", "err", err) } // Cancel shutdown process context. i.cancelShutdownCtx() return nil }) } // Ctx returns the instance context. // It is canceled when shutdown is started. func (i *Instance) Ctx() context.Context { return i.ctx } // IsShuttingDown returns whether the instance is shutting down. func (i *Instance) IsShuttingDown() bool { return i.ctx.Err() != nil } // ShuttingDown returns a channel that is triggered when the instance starts shutting down. func (i *Instance) ShuttingDown() <-chan struct{} { return i.ctx.Done() } // ShutdownCtx returns the instance shutdown context. // It is canceled when shutdown is complete. func (i *Instance) ShutdownCtx() context.Context { return i.shutdownCtx } // IsShutDown returns whether the instance has stopped. func (i *Instance) IsShutDown() bool { return i.shutdownCtx.Err() != nil } // ShutDownComplete returns a channel that is triggered when the instance has shut down. func (i *Instance) ShutdownComplete() <-chan struct{} { return i.shutdownCtx.Done() } // ExitCode returns the set exit code of the instance. func (i *Instance) ExitCode() int { return int(i.exitCode.Load()) } // ShouldRestartIsSet returns whether the service/instance should be restarted. func (i *Instance) ShouldRestartIsSet() bool { return i.ShouldRestart } // CommandLineOperationIsSet returns whether the command line option is set. func (i *Instance) CommandLineOperationIsSet() bool { return i.CommandLineOperation != nil } // CommandLineOperationExecute executes the set command line option. func (i *Instance) CommandLineOperationExecute() error { return i.CommandLineOperation() } // AddModule adds a module to the service group. func (i *Instance) AddModule(m mgr.Module) { i.serviceGroup.Add(m) } ================================================ FILE: service/integration/etw_windows.go ================================================ //go:build windows // +build windows package integration import ( "fmt" "golang.org/x/sys/windows" ) type ETWFunctions struct { createState *windows.Proc initializeSession *windows.Proc startTrace *windows.Proc flushTrace *windows.Proc stopTrace *windows.Proc destroySession *windows.Proc stopOldSession *windows.Proc } func initializeETW(dll *windows.DLL) (*ETWFunctions, error) { functions := &ETWFunctions{} var err error functions.createState, err = dll.FindProc("PM_ETWCreateState") if err != nil { return functions, fmt.Errorf("failed to load function PM_ETWCreateState: %q", err) } functions.initializeSession, err = dll.FindProc("PM_ETWInitializeSession") if err != nil { return functions, fmt.Errorf("failed to load function PM_ETWInitializeSession: %q", err) } functions.startTrace, err = dll.FindProc("PM_ETWStartTrace") if err != nil { return functions, fmt.Errorf("failed to load function PM_ETWStartTrace: %q", err) } functions.flushTrace, err = dll.FindProc("PM_ETWFlushTrace") if err != nil { return functions, fmt.Errorf("failed to load function PM_ETWFlushTrace: %q", err) } functions.stopTrace, err = dll.FindProc("PM_ETWStopTrace") if err != nil { return functions, fmt.Errorf("failed to load function PM_ETWStopTrace: %q", err) } functions.destroySession, err = dll.FindProc("PM_ETWDestroySession") if err != nil { return functions, fmt.Errorf("failed to load function PM_ETWDestroySession: %q", err) } functions.stopOldSession, err = dll.FindProc("PM_ETWStopOldSession") if err != nil { return functions, fmt.Errorf("failed to load function PM_ETWDestroySession: %q", err) } return functions, nil } // CreateState calls the dll createState C function. func (etw ETWFunctions) CreateState(callback uintptr) uintptr { state, _, _ := etw.createState.Call(callback) return state } // InitializeSession calls the dll initializeSession C function. func (etw ETWFunctions) InitializeSession(state uintptr) error { rc, _, _ := etw.initializeSession.Call(state) if rc != 0 { return fmt.Errorf("failed with status code: %d", rc) } return nil } // StartTrace calls the dll startTrace C function. func (etw ETWFunctions) StartTrace(state uintptr) error { rc, _, _ := etw.startTrace.Call(state) if rc != 0 { return fmt.Errorf("failed with status code: %d", rc) } return nil } // FlushTrace calls the dll flushTrace C function. func (etw ETWFunctions) FlushTrace(state uintptr) error { rc, _, _ := etw.flushTrace.Call(state) if rc != 0 { return fmt.Errorf("failed with status code: %d", rc) } return nil } // StopTrace calls the dll stopTrace C function. func (etw ETWFunctions) StopTrace(state uintptr) error { rc, _, _ := etw.stopTrace.Call(state) if rc != 0 { return fmt.Errorf("failed with status code: %d", rc) } return nil } // DestroySession calls the dll destroySession C function. func (etw ETWFunctions) DestroySession(state uintptr) error { rc, _, _ := etw.destroySession.Call(state) if rc != 0 { return fmt.Errorf("failed with status code: %d", rc) } return nil } // StopOldSession calls the dll stopOldSession C function. func (etw ETWFunctions) StopOldSession() error { rc, _, _ := etw.stopOldSession.Call() if rc != 0 { return fmt.Errorf("failed with status code: %d", rc) } return nil } ================================================ FILE: service/integration/integration.go ================================================ //go:build !windows // +build !windows package integration type OSSpecific struct{} // Initialize is empty on any OS different then Windows. func (i *OSIntegration) Initialize() error { return nil } // CleanUp releases any resourses allocated during initializaion. func (i *OSIntegration) CleanUp() error { return nil } ================================================ FILE: service/integration/integration_windows.go ================================================ //go:build windows // +build windows package integration import ( "fmt" "sync" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "golang.org/x/sys/windows" ) type OSSpecific struct { dll *windows.DLL etwFunctions *ETWFunctions } // Initialize loads the dll and finds all the needed functions from it. func (i *OSIntegration) Initialize() error { // Try to load dll err := i.loadDLL() if err != nil { log.Errorf("integration: failed to load dll: %s", err) callbackLock := sync.Mutex{} // listen for event from the updater and try to load again if any. i.instance.BinaryUpdates().EventResourcesUpdated.AddCallback("core-dll-loader", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { // Make sure no multiple callas are executed at the same time. callbackLock.Lock() defer callbackLock.Unlock() // Try to load again. err = i.loadDLL() if err != nil { log.Errorf("integration: failed to load dll: %s", err) } else { log.Info("integration: initialize successful after updater event") } return false, nil }) } else { log.Info("integration: initialize successful") } return nil } func (i *OSIntegration) loadDLL() error { // Find path to the dll. file, err := i.instance.BinaryUpdates().GetFile("portmaster-core.dll") if err != nil { return err } // Load the DLL. i.os.dll, err = windows.LoadDLL(file.Path()) if err != nil { return fmt.Errorf("failed to load dll: %q", err) } // Enumerate all needed dll functions. i.os.etwFunctions, err = initializeETW(i.os.dll) if err != nil { return err } // Notify listeners i.OnInitializedEvent.Submit(struct{}{}) return nil } // CleanUp releases any resources allocated during initialization. func (i *OSIntegration) CleanUp() error { if i.os.dll != nil { return i.os.dll.Release() } return nil } // GetETWInterface return struct containing all the ETW related functions, and nil if it was not loaded yet func (i *OSIntegration) GetETWInterface() *ETWFunctions { return i.os.etwFunctions } ================================================ FILE: service/integration/module.go ================================================ package integration import ( "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/updates" ) // OSIntegration module provides special integration with the OS. type OSIntegration struct { m *mgr.Manager OnInitializedEvent *mgr.EventMgr[struct{}] //nolint:unused os OSSpecific instance instance } // New returns a new OSIntegration module. func New(instance instance) (*OSIntegration, error) { m := mgr.New("OSIntegration") module := &OSIntegration{ m: m, OnInitializedEvent: mgr.NewEventMgr[struct{}]("on-initialized", m), instance: instance, } return module, nil } // Manager returns the module manager. func (i *OSIntegration) Manager() *mgr.Manager { return i.m } // Start starts the module. func (i *OSIntegration) Start() error { return i.Initialize() } // Stop stops the module. func (i *OSIntegration) Stop() error { return i.CleanUp() } type instance interface { BinaryUpdates() *updates.Updater } ================================================ FILE: service/intel/block_reason.go ================================================ package intel import ( "context" "encoding/json" "fmt" "strings" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/nameserver/nsutil" ) // ListMatch represents an entity that has been // matched against filterlists. type ListMatch struct { Entity string ActiveLists []string InactiveLists []string } func (lm *ListMatch) String() string { inactive := "" if len(lm.InactiveLists) > 0 { inactive = " and in deactivated lists " + strings.Join(lm.InactiveLists, ", ") } return fmt.Sprintf( "%s in activated lists %s%s", lm.Entity, strings.Join(lm.ActiveLists, ","), inactive, ) } // ListBlockReason is a list of list matches. type ListBlockReason []ListMatch func (br ListBlockReason) String() string { if len(br) == 0 { return "" } matches := make([]string, len(br)) for idx, lm := range br { matches[idx] = lm.String() } return strings.Join(matches, " and ") } // Context returns br wrapped into a map. It implements // the endpoints.Reason interface. func (br ListBlockReason) Context() interface{} { return br } // MarshalJSON marshals the list block reason into a map // prefixed with filterlists. func (br ListBlockReason) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]interface{}{ // we convert to []ListMatch to avoid recursing // here. "filterlists": []ListMatch(br), }) } // GetExtraRRs implements the nsutil.RRProvider interface // and adds additional TXT records justifying the reason // the request was blocked. func (br ListBlockReason) GetExtraRRs(ctx context.Context, _ *dns.Msg) []dns.RR { rrs := make([]dns.RR, 0, len(br)) for _, lm := range br { blockedBy, err := nsutil.MakeMessageRecord(log.InfoLevel, fmt.Sprintf( "%s is blocked by filter lists %s", lm.Entity, strings.Join(lm.ActiveLists, ", "), )) if err == nil { rrs = append(rrs, blockedBy) } else { log.Tracer(ctx).Errorf("intel: failed to create TXT RR for block reason: %s", err) } if len(lm.InactiveLists) > 0 { wouldBeBlockedBy, err := nsutil.MakeMessageRecord(log.InfoLevel, fmt.Sprintf( "%s would be blocked by filter lists %s", lm.Entity, strings.Join(lm.InactiveLists, ", "), )) if err == nil { rrs = append(rrs, wouldBeBlockedBy) } else { log.Tracer(ctx).Errorf("intel: failed to create TXT RR for block reason: %s", err) } } } return rrs } var _ nsutil.RRProvider = ListBlockReason(nil) ================================================ FILE: service/intel/customlists/config.go ================================================ package customlists import ( "github.com/safing/portmaster/base/config" ) var ( // CfgOptionCustomListFileKey is the config key for custom filter list file. CfgOptionCustomListFileKey = "filter/customListFile" cfgOptionCustomListFileOrder = 35 cfgOptionCustomListCategoryAnnotation = "Filter Lists" ) var getFilePath config.StringOption func registerConfig() error { help := `The file (.txt) is checked every couple minutes and will be automatically reloaded when it has changed. Entries (one per line) may be one of: - Domain: "example.com" - IP Address: "10.0.0.1" - Country Code (based on IP): "US" - AS (Autonomous System): "AS1234" Everything after the first element of a line, comments starting with a '#', and empty lines are ignored. The settings "Block Subdomains of Filter List Entries" and "Block Domain Aliases" also apply to the custom filter list. Lists in the "Hosts" format are not supported. Please note that the custom filter list is fully loaded into memory. This can have a negative impact on your device if big lists are loaded.` // Register a setting for the file path in the ui err := config.Register(&config.Option{ Name: "Custom Filter List", Key: CfgOptionCustomListFileKey, Description: "Specify the file path to a custom filter list (.txt), which will be automatically refreshed. Any connections matching a domain, IP address, Country or ASN in the file will be blocked.", Help: help, OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: "", RequiresRestart: false, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionCustomListFileOrder, config.CategoryAnnotation: cfgOptionCustomListCategoryAnnotation, config.DisplayHintAnnotation: config.DisplayHintFilePicker, }, }) if err != nil { return err } getFilePath = config.GetAsString(CfgOptionCustomListFileKey, "") return nil } ================================================ FILE: service/intel/customlists/lists.go ================================================ package customlists import ( "bufio" "fmt" "net" "os" "strconv" "strings" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network/netutils" ) var ( countryCodesFilterList map[string]struct{} ipAddressesFilterList map[string]struct{} autonomousSystemsFilterList map[uint]struct{} domainsFilterList map[string]struct{} ) const ( rationForInvalidLinesUntilWarning = 0.1 parseStatusNotificationID = "customlists:parse-status" parseWarningNotificationID = "customlists:parse-warning" zeroIPNotificationID = "customlists:too-many-zero-ips" ) func initFilterLists() { countryCodesFilterList = make(map[string]struct{}) ipAddressesFilterList = make(map[string]struct{}) autonomousSystemsFilterList = make(map[uint]struct{}) domainsFilterList = make(map[string]struct{}) } // IsLoaded returns whether a custom filter list is loaded. func IsLoaded() bool { filterListLock.RLock() defer filterListLock.RUnlock() switch { case len(domainsFilterList) > 0: return true case len(ipAddressesFilterList) > 0: return true case len(countryCodesFilterList) > 0: return true case len(autonomousSystemsFilterList) > 0: return true default: return false } } func parseFile(filePath string) error { // Reset all maps, previous (if any) settings will be lost. for key := range countryCodesFilterList { delete(countryCodesFilterList, key) } for key := range ipAddressesFilterList { delete(ipAddressesFilterList, key) } for key := range autonomousSystemsFilterList { delete(autonomousSystemsFilterList, key) } for key := range domainsFilterList { delete(domainsFilterList, key) } // Ignore empty file path. if filePath == "" { return nil } // Open the file if possible file, err := os.Open(filePath) if err != nil { log.Warningf("intel/customlists: failed to parse file %s", err) module.states.Add(mgr.State{ ID: parseWarningNotificationID, Name: "Failed to open custom filter list", Message: err.Error(), Type: mgr.StateTypeWarning, }) return err } else { module.states.Remove(parseWarningNotificationID) } defer func() { _ = file.Close() }() var allLinesCount uint64 var invalidLinesCount uint64 // Read filter file line by line. scanner := bufio.NewScanner(file) // The scanner will error out if the line is greater than 64K, in this case it is enough. for scanner.Scan() { allLinesCount++ // Parse and count invalid lines (comment, empty lines, zero IPs...) if !parseLine(scanner.Text()) { invalidLinesCount++ } } // Check for scanner error. if err := scanner.Err(); err != nil { return err } invalidLinesRation := float32(invalidLinesCount) / float32(allLinesCount) if invalidLinesRation > rationForInvalidLinesUntilWarning { log.Warning("intel/customlists: Too many invalid lines") module.states.Add(mgr.State{ ID: zeroIPNotificationID, Name: "Custom filter list has many invalid lines", Message: fmt.Sprintf(`%d out of %d lines are invalid. Check if you are using the correct file format and if the path to the custom filter list is correct.`, invalidLinesCount, allLinesCount), Type: mgr.StateTypeWarning, }) } else { module.states.Remove(zeroIPNotificationID) } allEntriesCount := len(domainsFilterList) + len(ipAddressesFilterList) + len(autonomousSystemsFilterList) + len(countryCodesFilterList) log.Infof("intel/customlists: loaded %d entries from %s", allEntriesCount, filePath) notifications.NotifyInfo(parseStatusNotificationID, "Custom filter list loaded successfully.", fmt.Sprintf(`Custom filter list loaded from file %s: %d Domains %d IPs %d Autonomous Systems %d Countries`, filePath, len(domainsFilterList), len(ipAddressesFilterList), len(autonomousSystemsFilterList), len(countryCodesFilterList))) return nil } func parseLine(line string) (valid bool) { // Everything after the first field will be ignored. fields := strings.Fields(line) // Ignore empty lines. if len(fields) == 0 { return true // Not an entry, but a valid line. } field := fields[0] // Ignore comments if strings.HasPrefix(field, "#") { return true // Not an entry, but a valid line. } // Go through all possible field types. // Parsing is ordered by // 1. Parsing options (ie. the domain has most variation and goes last.) // 2. Speed // Check if it'a a country code. if isCountryCode(field) { countryCodesFilterList[field] = struct{}{} return true } // Check if it's a Autonomous system (example AS123). if isAutonomousSystem(field) { asNumber, err := strconv.ParseUint(field[2:], 10, 32) if err != nil { return false } autonomousSystemsFilterList[uint(asNumber)] = struct{}{} return true } // Try to parse IP address. ip := net.ParseIP(field) if ip != nil { // Check for zero ip. if net.IP.Equal(ip, net.IPv4zero) || net.IP.Equal(ip, net.IPv6zero) { return false } ipAddressesFilterList[ip.String()] = struct{}{} return true } // Check if it's a domain. domain := dns.Fqdn(field) if netutils.IsValidFqdn(domain) { domainsFilterList[domain] = struct{}{} return true } return false } ================================================ FILE: service/intel/customlists/module.go ================================================ package customlists import ( "errors" "net" "os" "regexp" "strings" "sync" "sync/atomic" "time" "golang.org/x/net/publicsuffix" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/mgr" ) type CustomList struct { mgr *mgr.Manager instance instance updateFilterListWorkerMgr *mgr.WorkerMgr states *mgr.StateMgr } func (cl *CustomList) Manager() *mgr.Manager { return cl.mgr } func (cl *CustomList) States() *mgr.StateMgr { return cl.states } func (cl *CustomList) Start() error { return start() } func (cl *CustomList) Stop() error { return nil } // Helper variables for parsing the input file. var ( isCountryCode = regexp.MustCompile("^[A-Z]{2}$").MatchString isAutonomousSystem = regexp.MustCompile(`^AS[0-9]+$`).MatchString ) var ( filterListFilePath string filterListFileModifiedTime time.Time filterListLock sync.RWMutex // ErrNotConfigured is returned when updating the custom filter list, but it // is not configured. ErrNotConfigured = errors.New("custom filter list not configured") ) func prep() error { initFilterLists() // Register the config in the ui. err := registerConfig() if err != nil { return err } // Register api endpoint for updating the filter list. if err := api.RegisterEndpoint(api.Endpoint{ Path: "customlists/update", Write: api.PermitUser, ActionFunc: func(ar *api.Request) (msg string, err error) { errCheck := checkAndUpdateFilterList(nil) if errCheck != nil { return "", errCheck } return "Custom filter list loaded successfully.", nil }, Name: "Update custom filter list", Description: "Reload the filter list from the configured file.", }); err != nil { return err } return nil } func start() error { // Register to hook to update after config change. module.instance.Config().EventConfigChange.AddCallback( "update custom filter list", func(wc *mgr.WorkerCtx, _ struct{}) (bool, error) { err := checkAndUpdateFilterList(wc) if !errors.Is(err, ErrNotConfigured) { return false, err } return false, nil }, ) // Create parser task and enqueue for execution. "checkAndUpdateFilterList" will schedule the next execution. module.updateFilterListWorkerMgr.Delay(20 * time.Second).Repeat(1 * time.Minute) return nil } func checkAndUpdateFilterList(_ *mgr.WorkerCtx) error { filterListLock.Lock() defer filterListLock.Unlock() // Get path and return error if empty filePath := getFilePath() if filePath == "" { return ErrNotConfigured } // Try to get file info modifiedTime := time.Now() if fileInfo, err := os.Stat(filePath); err == nil { modifiedTime = fileInfo.ModTime() } // Check if file path has changed or if modified time has changed if filterListFilePath != filePath || !filterListFileModifiedTime.Equal(modifiedTime) { err := parseFile(filePath) if err != nil { return err } filterListFileModifiedTime = modifiedTime filterListFilePath = filePath } return nil } // LookupIP checks if the IP address is in a custom filter list. func LookupIP(ip net.IP) bool { filterListLock.RLock() defer filterListLock.RUnlock() _, ok := ipAddressesFilterList[ip.String()] return ok } // LookupDomain checks if the Domain is in a custom filter list. func LookupDomain(fullDomain string, filterSubdomains bool) (bool, string) { filterListLock.RLock() defer filterListLock.RUnlock() if filterSubdomains { // Check if domain is in the list and all its subdomains. listOfDomains := splitDomain(fullDomain) for _, domain := range listOfDomains { _, ok := domainsFilterList[domain] if ok { return true, domain } } } else { // Check only if the domain is in the list _, ok := domainsFilterList[fullDomain] return ok, fullDomain } return false, "" } // LookupASN checks if the Autonomous system number is in a custom filter list. func LookupASN(number uint) bool { filterListLock.RLock() defer filterListLock.RUnlock() _, ok := autonomousSystemsFilterList[number] return ok } // LookupCountry checks if the country code is in a custom filter list. func LookupCountry(countryCode string) bool { filterListLock.RLock() defer filterListLock.RUnlock() _, ok := countryCodesFilterList[countryCode] return ok } func splitDomain(domain string) []string { domain = strings.Trim(domain, ".") suffix, _ := publicsuffix.PublicSuffix(domain) if suffix == domain { return []string{domain} } domainWithoutSuffix := domain[:len(domain)-len(suffix)] domainWithoutSuffix = strings.Trim(domainWithoutSuffix, ".") splitted := strings.FieldsFunc(domainWithoutSuffix, func(r rune) bool { return r == '.' }) domains := make([]string, 0, len(splitted)) for idx := range splitted { d := strings.Join(splitted[idx:], ".") + "." + suffix if d[len(d)-1] != '.' { d += "." } domains = append(domains, d) } return domains } var ( module *CustomList shimLoaded atomic.Bool ) // New returns a new CustomList module. func New(instance instance) (*CustomList, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("CustomList") module = &CustomList{ mgr: m, instance: instance, states: mgr.NewStateMgr(m), updateFilterListWorkerMgr: m.NewWorkerMgr( "update custom filter list", func(ctx *mgr.WorkerCtx) error { err := checkAndUpdateFilterList(ctx) if !errors.Is(err, ErrNotConfigured) { return err } return nil }, nil, ), } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface { Config() *config.Config } ================================================ FILE: service/intel/entity.go ================================================ package intel import ( "context" "net" "sort" "strconv" "strings" "sync" "golang.org/x/net/publicsuffix" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/intel/filterlists" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/service/network/netutils" ) // Entity describes a remote endpoint in many different ways. // It embeddes a sync.Mutex but none of the endpoints own // functions performs locking. The caller MUST ENSURE // proper locking and synchronization when accessing // any properties of Entity. type Entity struct { //nolint:maligned sync.Mutex // lists exist for most entity information and // we need to know which one we loaded domainListLoaded bool ipListLoaded bool countryListLoaded bool asnListLoaded bool reverseResolveEnabled bool resolveSubDomainLists bool checkCNAMEs bool // IP is the IP address of the connection. If domain is // set, IP has been resolved by following all CNAMEs. IP net.IP // IPScope holds the network scope of the IP. // For DNS requests, this signifies in which scope the DNS request was resolved. IPScope netutils.IPScope // Protocol is the protcol number used by the connection. Protocol uint8 // Port is the remote port of the connection Port uint16 // dstPort is the destination port of the connection dstPort uint16 // Domain is the target domain of the connection. Domain string // ReverseDomain is the domain the IP address points to. This is only // resolved and populated when needed. ReverseDomain string // CNAME is a list of domain names that have been // resolved for Domain. CNAME []string // Country holds the country the IP address (ASN) is // located in. Country string // Coordinates holds the approximate coordinates of the IP address. Coordinates *geoip.Coordinates // ASN holds the autonomous system number of the IP. ASN uint // ASOrg holds the owner's name of the autonomous system. ASOrg string // LocationError holds an error message if fetching the location failed. LocationError string location *geoip.Location // BlockedByLists holds list source IDs that // are used to block the entity. BlockedByLists []string // BlockedEntities holds a list of entities that // have been blocked. Values can be used as a key // for the ListOccurences map. BlockedEntities []string // ListOccurences is a map that matches an entity (Domain, IPs, ASN, Country, Sub-domain) // to a list of sources where the entity has been observed in. ListOccurences map[string][]string // ListsError holds an error message if fetching the lists failed. ListsError string // we only load each data above at most once fetchLocationOnce sync.Once reverseResolveOnce sync.Once loadDomainListOnce sync.Once loadIPListOnce sync.Once loadCountryListOnce sync.Once loadAsnListOnce sync.Once } // Init initializes internal metadata about the entity. // If the entity does not describe a destination, you can supply a different // destination port for endpoint matching. // It returns the entity itself for single line formatting. func (e *Entity) Init(dstPort uint16) *Entity { // Get IP scope. if e.IP != nil { e.IPScope = netutils.GetIPScope(e.IP) } else { e.IPScope = netutils.Undefined } // Set dst port to given value or fall back to entity. if dstPort > 0 { e.dstPort = dstPort } else { e.dstPort = e.Port } return e } // DstPort returns the destination port. func (e *Entity) DstPort() uint16 { return e.dstPort } // FetchData fetches additional information, meant to be called before persisting an entity record. func (e *Entity) FetchData(ctx context.Context) { e.getLocation(ctx) e.getLists(ctx) } // ResetLists resets the current list data and forces // all list sources to be re-acquired when calling GetLists(). func (e *Entity) ResetLists() { // TODO(ppacher): our actual goal is to reset the domain // list right now so we could be more efficient by keeping // the other lists around. e.BlockedByLists = nil e.BlockedEntities = nil e.ListOccurences = nil e.domainListLoaded = false e.ipListLoaded = false e.countryListLoaded = false e.asnListLoaded = false e.resolveSubDomainLists = false e.checkCNAMEs = false e.loadDomainListOnce = sync.Once{} e.loadIPListOnce = sync.Once{} e.loadCountryListOnce = sync.Once{} e.loadAsnListOnce = sync.Once{} } // ResolveSubDomainLists enables or disables list lookups for // sub-domains. func (e *Entity) ResolveSubDomainLists(ctx context.Context, enabled bool) { if e.domainListLoaded && enabled != e.resolveSubDomainLists { log.Tracer(ctx).Warningf("intel/filterlists: tried to change sub-domain resolving for %s but lists are already fetched", e.Domain) } e.resolveSubDomainLists = enabled } // EnableCNAMECheck enalbes or disables list lookups for // entity CNAMEs. func (e *Entity) EnableCNAMECheck(ctx context.Context, enabled bool) { if e.domainListLoaded && enabled != e.checkCNAMEs { log.Tracer(ctx).Warningf("intel/filterlists: tried to change CNAME resolving for %s but lists are already fetched", e.Domain) } e.checkCNAMEs = enabled } // CNAMECheckEnabled returns true if the entities CNAMEs should // also be checked. func (e *Entity) CNAMECheckEnabled() bool { return e.checkCNAMEs } // Domain and IP // EnableReverseResolving enables reverse resolving the domain from the IP on demand. func (e *Entity) EnableReverseResolving() { e.reverseResolveEnabled = true } func (e *Entity) reverseResolve(ctx context.Context) { e.reverseResolveOnce.Do(func() { // need IP! if e.IP == nil { return } // reverse resolve if reverseResolver == nil { return } // TODO: security level domain, err := reverseResolver(ctx, e.IP.String()) if err != nil { log.Tracer(ctx).Warningf("intel: failed to resolve IP %s: %s", e.IP, err) return } e.ReverseDomain = domain }) } // GetDomain returns the domain and whether it is set. func (e *Entity) GetDomain(ctx context.Context, mayUseReverseDomain bool) (string, bool) { if mayUseReverseDomain && e.reverseResolveEnabled { e.reverseResolve(ctx) if e.ReverseDomain == "" { return "", false } return e.ReverseDomain, true } if e.Domain == "" { return "", false } return e.Domain, true } // GetIP returns the IP and whether it is set. func (e *Entity) GetIP() (net.IP, bool) { if e.IP == nil { return nil, false } return e.IP, true } // Location func (e *Entity) getLocation(ctx context.Context) { e.fetchLocationOnce.Do(func() { // Only check if we have a global IP address. if e.IP == nil || !e.IPScope.IsGlobal() { return } // get location data loc, err := geoip.GetLocation(e.IP) if err != nil { log.Tracer(ctx).Warningf("intel: failed to get location data for %s: %s", e.IP, err) e.LocationError = err.Error() return } e.location = loc e.Country = loc.Country.Code e.Coordinates = &loc.Coordinates e.ASN = loc.AutonomousSystemNumber e.ASOrg = loc.AutonomousSystemOrganization // Log result. if log.GetLogLevel() == log.TraceLevel { // Build flags var flags string if loc.IsAnycast { flags += " anycast" } if loc.IsSatelliteProvider { flags += " satellite" } if loc.IsAnonymousProxy { flags += " anonymous" } // Log location log.Tracer(ctx).Tracef( "intel: located %s in %s (%s), as part of AS%d by %s%s", e.IP, loc.Country.Name, loc.Country.Code, loc.AutonomousSystemNumber, loc.AutonomousSystemOrganization, flags, ) } }) } // GetLocation returns the raw location data and whether it is set. func (e *Entity) GetLocation(ctx context.Context) (*geoip.Location, bool) { e.getLocation(ctx) if e.location == nil { return nil, false } return e.location, true } // GetCountry returns the two letter ISO country code and whether it is set. func (e *Entity) GetCountry(ctx context.Context) (string, bool) { e.getLocation(ctx) if e.LocationError != "" { return "", false } return e.Country, true } // GetCountryInfo returns the two letter ISO country code and whether it is set. func (e *Entity) GetCountryInfo(ctx context.Context) *geoip.CountryInfo { e.getLocation(ctx) if e.LocationError != "" { return nil } return &e.location.Country } // GetASN returns the AS number and whether it is set. func (e *Entity) GetASN(ctx context.Context) (uint, bool) { e.getLocation(ctx) if e.LocationError != "" { return 0, false } return e.ASN, true } // Lists func (e *Entity) getLists(ctx context.Context) { e.getDomainLists(ctx) e.getASNLists(ctx) e.getIPLists(ctx) e.getCountryLists(ctx) } func (e *Entity) mergeList(key string, list []string) { if len(list) == 0 { return } if e.ListOccurences == nil { e.ListOccurences = make(map[string][]string) } e.ListOccurences[key] = mergeStringList(e.ListOccurences[key], list) } func (e *Entity) getDomainLists(ctx context.Context) { if e.domainListLoaded { return } domain, ok := e.GetDomain(ctx, false /* mayUseReverseDomain */) if !ok { return } e.loadDomainListOnce.Do(func() { domainsToInspect := []string{domain} if e.checkCNAMEs && len(e.CNAME) > 0 { log.Tracer(ctx).Tracef("intel: CNAME filtering enabled, checking %v too", e.CNAME) domainsToInspect = append(domainsToInspect, e.CNAME...) } var domains []string if e.resolveSubDomainLists { for _, domain := range domainsToInspect { subdomains := splitDomain(domain) domains = append(domains, subdomains...) } } else { domains = domainsToInspect } domains = makeDistinct(domains) for _, d := range domains { list, err := filterlists.LookupDomain(d) if err != nil { log.Tracer(ctx).Errorf("intel: failed to get domain blocklists for %s: %s", d, err) e.ListsError = err.Error() return } if len(list) > 0 { log.Tracer(ctx).Tracef("intel: loaded domain lists for %s: %s", d, strings.Join(list, ", ")) e.mergeList(d, list) } } e.domainListLoaded = true }) } func splitDomain(domain string) []string { // Get suffix. d := strings.TrimSuffix(domain, ".") suffix, icann := publicsuffix.PublicSuffix(d) if suffix == d { return []string{domain} } // Split all subdomain into labels. labels := strings.FieldsFunc(d[:len(d)-len(suffix)], func(r rune) bool { return r == '.' }) // Build list of all domains up to the public suffix. domains := make([]string, 0, len(labels)+1) for idx := range labels { domains = append( domains, strings.Join(labels[idx:], ".")+"."+suffix+".", ) } // If the suffix is not a real TLD, but a public suffix, add it to the list. if !icann { domains = append(domains, suffix+".") } return domains } func (e *Entity) getASNLists(ctx context.Context) { if e.asnListLoaded { return } asn, ok := e.GetASN(ctx) if !ok || asn == 0 { return } e.loadAsnListOnce.Do(func() { asnStr := strconv.FormatUint(uint64(asn), 10) list, err := filterlists.LookupASNString(asnStr) if err != nil { log.Tracer(ctx).Errorf("intel: failed to get ASN blocklist for %d: %s", asn, err) e.ListsError = err.Error() return } if len(list) > 0 { log.Tracer(ctx).Tracef("intel: loaded ASN lists for %s: %s", asnStr, strings.Join(list, ", ")) e.mergeList(asnStr, list) } e.asnListLoaded = true }) } func (e *Entity) getCountryLists(ctx context.Context) { if e.countryListLoaded { return } country, ok := e.GetCountry(ctx) if !ok || country == "" { return } e.loadCountryListOnce.Do(func() { list, err := filterlists.LookupCountry(country) if err != nil { log.Tracer(ctx).Errorf("intel: failed to load country blocklist for %s: %s", country, err) e.ListsError = err.Error() return } if len(list) > 0 { log.Tracer(ctx).Tracef("intel: loaded country lists for %s: %s", country, strings.Join(list, ", ")) e.mergeList(country, list) } e.countryListLoaded = true }) } func (e *Entity) getIPLists(ctx context.Context) { if e.ipListLoaded { return } ip, ok := e.GetIP() if !ok || ip == nil { return } // only load lists for IP addresses that are classified as global. if !e.IPScope.IsGlobal() { return } e.loadIPListOnce.Do(func() { list, err := filterlists.LookupIP(ip) if err != nil { log.Tracer(ctx).Errorf("intel: failed to get IP blocklist for %s: %s", ip.String(), err) e.ListsError = err.Error() return } if len(list) > 0 { log.Tracer(ctx).Tracef("intel: loaded IP lists for %s: %s", ip.String(), strings.Join(list, ", ")) e.mergeList(ip.String(), list) } e.ipListLoaded = true }) } // LoadLists searches all filterlists for all occurrences of // this entity. func (e *Entity) LoadLists(ctx context.Context) { e.getLists(ctx) } // MatchLists matches the entities lists against a slice // of source IDs and updates various entity properties // like BlockedByLists, ListOccurences and BlockedEntitites. func (e *Entity) MatchLists(lists []string) bool { if len(lists) == 0 { return false } e.BlockedByLists = nil e.BlockedEntities = nil lm := makeMap(lists) for key, keyLists := range e.ListOccurences { for _, keyListID := range keyLists { if _, ok := lm[keyListID]; ok { e.BlockedByLists = append(e.BlockedByLists, keyListID) e.BlockedEntities = append(e.BlockedEntities, key) } } } e.BlockedByLists = makeDistinct(e.BlockedByLists) e.BlockedEntities = makeDistinct(e.BlockedEntities) return len(e.BlockedByLists) > 0 } // ListBlockReason returns the block reason for this entity. func (e *Entity) ListBlockReason() ListBlockReason { blockedBy := make([]ListMatch, len(e.BlockedEntities)) lm := makeMap(e.BlockedByLists) for idx, blockedEntity := range e.BlockedEntities { if entityLists, ok := e.ListOccurences[blockedEntity]; ok { var activeLists []string var inactiveLists []string for _, l := range entityLists { if _, ok := lm[l]; ok { activeLists = append(activeLists, l) } else { inactiveLists = append(inactiveLists, l) } } blockedBy[idx] = ListMatch{ Entity: blockedEntity, ActiveLists: activeLists, InactiveLists: inactiveLists, } } } return blockedBy } func mergeStringList(a, b []string) []string { listMap := make(map[string]struct{}) for _, s := range a { listMap[s] = struct{}{} } for _, s := range b { listMap[s] = struct{}{} } res := make([]string, 0, len(listMap)) for s := range listMap { res = append(res, s) } sort.Strings(res) return res } func makeDistinct(slice []string) []string { m := make(map[string]struct{}, len(slice)) result := make([]string, 0, len(slice)) for _, v := range slice { if _, ok := m[v]; ok { continue } m[v] = struct{}{} result = append(result, v) } return result } func makeMap(slice []string) map[string]struct{} { lm := make(map[string]struct{}) for _, v := range slice { lm[v] = struct{}{} } return lm } ================================================ FILE: service/intel/entity_test.go ================================================ package intel import ( "testing" "github.com/stretchr/testify/assert" ) var splitDomainTestCases = [][]string{ // Regular registered domains and subdomains. {"example.com."}, {"www.example.com.", "example.com."}, {"sub.domain.example.com.", "domain.example.com.", "example.com."}, {"example.co.uk."}, {"www.example.co.uk.", "example.co.uk."}, // TLD or public suffix: Return as is. {"com."}, {"googleapis.com."}, // Public suffix domains: Return including public suffix. {"test.googleapis.com.", "googleapis.com."}, {"sub.domain.googleapis.com.", "domain.googleapis.com.", "googleapis.com."}, } func TestSplitDomain(t *testing.T) { t.Parallel() for _, testCase := range splitDomainTestCases { splitted := splitDomain(testCase[0]) assert.Equal(t, testCase, splitted, "result must match") } } ================================================ FILE: service/intel/filterlists/bloom.go ================================================ package filterlists import ( "encoding/hex" "fmt" "strings" "sync" "github.com/tannerryan/ring" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" ) var defaultFilter = newScopedBloom() // scopedBloom is a wrapper around a bloomfilter implementation // providing scoped filters for different entity types. type scopedBloom struct { rw sync.RWMutex domain *ring.Ring asn *ring.Ring country *ring.Ring ipv4 *ring.Ring ipv6 *ring.Ring } func newScopedBloom() *scopedBloom { mustInit := func(size int) *ring.Ring { f, err := ring.Init(size, bfFalsePositiveRate) if err != nil { // we panic here as those values cannot be controlled // by the user and invalid values shouldn't be // in a release anyway. panic("Invalid bloom filter parameters!") } return f } return &scopedBloom{ domain: mustInit(domainBfSize), asn: mustInit(asnBfSize), country: mustInit(countryBfSize), ipv4: mustInit(ipv4BfSize), ipv6: mustInit(ipv6BfSize), } } func (bf *scopedBloom) getBloomForType(entityType string) (*ring.Ring, error) { var r *ring.Ring switch strings.ToLower(entityType) { case "domain": r = bf.domain case "asn": r = bf.asn case "ipv4": r = bf.ipv4 case "ipv6": r = bf.ipv6 case "country": r = bf.country default: return nil, fmt.Errorf("unsupported filterlists entity type %q", entityType) } return r, nil } func (bf *scopedBloom) add(scope, value string) { bf.rw.Lock() defer bf.rw.Unlock() r, err := bf.getBloomForType(scope) if err != nil { // If we don't have a bloom filter for that scope // we are probably running an older version that does // not have support for it. We just drop the value // as a call to Test() for that scope will always // return "true" log.Warningf("failed to add unknown entity type %q with value %q", scope, value) return } r.Add([]byte(value)) } func (bf *scopedBloom) test(scope, value string) bool { bf.rw.RLock() defer bf.rw.RUnlock() r, err := bf.getBloomForType(scope) if err != nil { log.Warningf("testing for unknown entity type %q", scope) return true // simulate a match to the caller } return r.Test([]byte(value)) } func (bf *scopedBloom) loadFromCache() error { bf.rw.Lock() defer bf.rw.Unlock() if err := loadBloomFromCache(bf.domain, "domain"); err != nil { return err } if err := loadBloomFromCache(bf.asn, "asn"); err != nil { return err } if err := loadBloomFromCache(bf.country, "country"); err != nil { return err } if err := loadBloomFromCache(bf.ipv4, "ipv4"); err != nil { return err } if err := loadBloomFromCache(bf.ipv6, "ipv6"); err != nil { return err } return nil } func (bf *scopedBloom) saveToCache() error { bf.rw.RLock() defer bf.rw.RUnlock() if err := saveBloomToCache(bf.domain, "domain"); err != nil { return err } if err := saveBloomToCache(bf.asn, "asn"); err != nil { return err } if err := saveBloomToCache(bf.country, "country"); err != nil { return err } if err := saveBloomToCache(bf.ipv4, "ipv4"); err != nil { return err } if err := saveBloomToCache(bf.ipv6, "ipv6"); err != nil { return err } return nil } func (bf *scopedBloom) replaceWith(other *scopedBloom) { bf.rw.Lock() defer bf.rw.Unlock() other.rw.RLock() defer other.rw.RUnlock() bf.domain = other.domain bf.asn = other.asn bf.country = other.country bf.ipv4 = other.ipv4 bf.ipv6 = other.ipv6 } type bloomFilterRecord struct { record.Base sync.Mutex Filter string } // loadBloomFromCache loads the bloom filter stored under scope // into bf. func loadBloomFromCache(bf *ring.Ring, scope string) error { r, err := cache.Get(makeBloomCacheKey(scope)) if err != nil { return err } var filterRecord *bloomFilterRecord if r.IsWrapped() { filterRecord = new(bloomFilterRecord) if err := record.Unwrap(r, filterRecord); err != nil { return err } } else { var ok bool filterRecord, ok = r.(*bloomFilterRecord) if !ok { return fmt.Errorf("invalid type, expected bloomFilterRecord but got %T", r) } } blob, err := hex.DecodeString(filterRecord.Filter) if err != nil { return err } if err := bf.UnmarshalBinary(blob); err != nil { return err } return nil } // saveBloomToCache saves the bitset of the bloomfilter bf // in the cache db. func saveBloomToCache(bf *ring.Ring, scope string) error { blob, err := bf.MarshalBinary() if err != nil { return err } filter := hex.EncodeToString(blob) r := &bloomFilterRecord{ Filter: filter, } r.SetKey(makeBloomCacheKey(scope)) return cache.Put(r) } ================================================ FILE: service/intel/filterlists/cache_version.go ================================================ package filterlists import ( "fmt" "sync" "github.com/hashicorp/go-version" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" ) const resetVersion = "v0.6.0" type cacheVersionRecord struct { record.Base sync.Mutex Version string Reset string } // getCacheDatabaseVersion reads and returns the cache // database version record. func getCacheDatabaseVersion() (*version.Version, error) { r, err := cache.Get(filterListCacheVersionKey) if err != nil { return nil, err } var verRecord *cacheVersionRecord if r.IsWrapped() { verRecord = new(cacheVersionRecord) if err := record.Unwrap(r, verRecord); err != nil { return nil, err } } else { var ok bool verRecord, ok = r.(*cacheVersionRecord) if !ok { return nil, fmt.Errorf("invalid type, expected cacheVersionRecord but got %T", r) } } if verRecord.Reset != resetVersion { return nil, database.ErrNotFound } ver, err := version.NewSemver(verRecord.Version) if err != nil { return nil, err } return ver, nil } // setCacheDatabaseVersion updates the cache database // version record to ver. func setCacheDatabaseVersion(ver string) error { verRecord := &cacheVersionRecord{ Version: ver, Reset: resetVersion, } verRecord.SetKey(filterListCacheVersionKey) return cache.Put(verRecord) } ================================================ FILE: service/intel/filterlists/database.go ================================================ package filterlists import ( "context" "fmt" "os" "sort" "strings" "sync" "time" "golang.org/x/sync/errgroup" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/updates" ) const ( baseListFilePath = "base.dsdl" intermediateListFilePath = "intermediate.dsdl" urgentListFilePath = "urgent.dsdl" listIndexFilePath = "index.dsd" ) // default bloomfilter element sizes (estimated). const ( domainBfSize = 1000000 asnBfSize = 1000 countryBfSize = 100 ipv4BfSize = 100 ipv6BfSize = 100 ) const bfFalsePositiveRate = 0.001 var ( filterListLock sync.RWMutex // Updater files for tracking upgrades. baseFile *updates.Artifact intermediateFile *updates.Artifact urgentFile *updates.Artifact filterListsLoaded chan struct{} ) var cache = database.NewInterface(&database.Options{ Local: true, Internal: true, CacheSize: 256, }) // getFileFunc is the function used to get a file from // the updater. It's basically updates.GetFile and used // for unit testing. // getFile points to updates.GetFile but may be set to // something different during unit testing. // var getFile getFileFunc = registry.GetFile func init() { filterListsLoaded = make(chan struct{}) } // isLoaded returns true if the filterlists have been // loaded. func isLoaded() bool { select { case <-filterListsLoaded: return true default: return false } } // processListFile opens the latest version of file and decodes it's DSDL // content. It calls processEntry for each decoded filterlists entry. func processListFile(ctx context.Context, filter *scopedBloom, file *updates.Artifact) error { f, err := os.Open(file.Path()) if err != nil { return err } defer func() { _ = f.Close() }() values := make(chan *listEntry, 100) records := make(chan record.Record, 100) g, ctx := errgroup.WithContext(ctx) // startSafe runs fn inside the error group but wrapped // in recovered function. startSafe := func(fn func() error) { g.Go(func() (err error) { defer func() { if x := recover(); x != nil { if e, ok := x.(error); ok { err = e } else { err = fmt.Errorf("%v", x) } } }() err = fn() return err }) } // Decode file and send entries to values channel. startSafe(func() (err error) { defer close(values) err = decodeFile(ctx, f, values) return }) // Process each entry and send records to records channel. startSafe(func() error { defer close(records) for entry := range values { if err := processEntry(ctx, filter, entry, records); err != nil { return err } } return nil }) // Persist records to the database. persistRecords(startSafe, records) return g.Wait() } func persistRecords(startJob func(func() error), records <-chan record.Record) { var cnt int start := time.Now() logProgress := func() { if cnt == 0 { // protection against panic return } timePerEntity := time.Since(start) / time.Duration(cnt) speed := float64(time.Second) / float64(timePerEntity) log.Debugf("intel/filterlists: processed %d entities in %s with %s / entity (%.2f entities/second)", cnt, time.Since(start), timePerEntity, speed) } batch := database.NewInterface(&database.Options{Local: true, Internal: true}) var processBatch func() error processBatch = func() error { batchPut := batch.PutMany("cache") for r := range records { if err := batchPut(r); err != nil { return err } cnt++ if cnt%10000 == 0 { logProgress() } if cnt%1000 == 0 { if err := batchPut(nil); err != nil { return err } startJob(processBatch) return nil } } // log final batch if cnt%10000 != 0 { // avoid duplicate logging logProgress() } return batchPut(nil) } startJob(processBatch) } func normalizeEntry(entry *listEntry) { switch strings.ToLower(entry.Type) { // case "domain": entry.Entity = strings.ToLower(entry.Entity) if entry.Entity[len(entry.Entity)-1] != '.' { // ensure domains from the filter list are fully qualified and end in dot. entry.Entity += "." } default: } } func processEntry(ctx context.Context, filter *scopedBloom, entry *listEntry, records chan<- record.Record) error { normalizeEntry(entry) // Only add the entry to the bloom filter if it has any sources. if len(entry.Resources) > 0 { filter.add(entry.Type, entry.Entity) } r := &entityRecord{ Value: entry.Entity, Type: entry.Type, Sources: entry.getSources(), UpdatedAt: time.Now().Unix(), } // If the entry is a "delete" update, actually delete it to save space. if entry.Whitelist { r.CreateMeta() r.Meta().Delete() } key := makeListCacheKey(strings.ToLower(r.Type), r.Value) r.SetKey(key) select { case records <- r: return nil case <-ctx.Done(): return ctx.Err() } } func mapKeys(m map[string]struct{}) []string { sl := make([]string, 0, len(m)) for s := range m { sl = append(sl, s) } sort.Strings(sl) return sl } ================================================ FILE: service/intel/filterlists/decoder.go ================================================ package filterlists import ( "compress/gzip" "context" "encoding/binary" "errors" "fmt" "io" "github.com/safing/portmaster/base/utils" "github.com/safing/structures/dsd" ) type listEntry struct { Type string `json:"type"` Entity string `json:"entity"` Whitelist bool `json:"whitelist"` Resources []entryResource `json:"resources"` } type entryResource struct { SourceID string `json:"sourceID"` ResourceID string `json:"resourceID"` } func (entry *listEntry) getSources() (sourceIDs []string) { sourceIDs = make([]string, 0, len(entry.Resources)) for _, resource := range entry.Resources { if !utils.StringInSlice(sourceIDs, resource.SourceID) { sourceIDs = append(sourceIDs, resource.SourceID) } } return } // decodeFile decodes a DSDL filterlists file and sends decoded entities to // ch. It blocks until all list entries have been consumed or ctx is cancelled. func decodeFile(ctx context.Context, r io.Reader, ch chan<- *listEntry) error { compressed, format, err := parseHeader(r) if err != nil { return fmt.Errorf("failed to parser header: %w", err) } if compressed { r, err = gzip.NewReader(r) if err != nil { return fmt.Errorf("failed to open gzip reader: %w", err) } } // we need a reader that supports io.ByteReader reader := &byteReader{r} var entryCount int for { entryCount++ length, readErr := binary.ReadUvarint(reader) if readErr != nil { if errors.Is(readErr, io.EOF) { return nil } return fmt.Errorf("failed to load varint entity length: %w", readErr) } blob := make([]byte, length) _, readErr = io.ReadFull(reader, blob) if readErr != nil { if errors.Is(readErr, io.EOF) { // there shouldn't be an EOF here because // we actually got a length above. Return // ErrUnexpectedEOF instead of just EOF. // io.ReadFull already returns ErrUnexpectedEOF // if it failed to read blob as a whole but my // return io.EOF if it read exactly 0 bytes. readErr = io.ErrUnexpectedEOF } return readErr } // we don't really care about the format here but it must be // something that can encode/decode complex structures like // JSON, BSON or GenCode. So LoadAsFormat MUST return the value // passed as the third parameter. String or RAW encoding IS AN // error here. entry := &listEntry{} err := dsd.LoadAsFormat(blob, format, entry) if err != nil { return fmt.Errorf("failed to decoded DSD encoded entity: %w", err) } select { case ch <- entry: case <-ctx.Done(): return ctx.Err() } } } func parseHeader(r io.Reader) (compressed bool, format byte, err error) { var listHeader [1]byte if _, err = r.Read(listHeader[:]); err != nil { // if we have an error here we can safely abort because // the file must be broken return compressed, format, err } if listHeader[0] != dsd.LIST { err = fmt.Errorf("unexpected file type: %d (%c), expected dsd list", listHeader[0], listHeader[0]) return compressed, format, err } var compression [1]byte if _, err = r.Read(compression[:]); err != nil { // same here, a DSDL file must have at least 2 bytes header return compressed, format, err } if compression[0] == dsd.GZIP { compressed = true var formatSlice [1]byte if _, err = r.Read(formatSlice[:]); err != nil { return compressed, format, err } format = formatSlice[0] return compressed, format, err } format = compression[0] return compressed, format, err } // byteReader extends an io.Reader to implement the ByteReader interface. type byteReader struct{ io.Reader } func (br *byteReader) ReadByte() (byte, error) { var b [1]byte _, err := br.Read(b[:]) return b[0], err } ================================================ FILE: service/intel/filterlists/index.go ================================================ package filterlists import ( "errors" "fmt" "os" "strings" "sync" "github.com/hashicorp/go-version" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/updates" "github.com/safing/structures/dsd" ) // the following definitions are copied from the intelhub repository // and stripped down to only include data required by portmaster. // Category is used to group different list sources by the type // of entity they are blocking. Categories may be nested using // the Parent field. type Category struct { // ID is a unique ID for the category. For sub-categories // this ID must be used in the Parent field of any directly // nesteded categories. ID string `json:"id"` // Parent may hold the ID of another category. If set, this // category is made a sub-category of it's parent. Parent string `json:"parent,omitempty"` // Name is a human readable name for the category and can // be used in user interfaces. Name string `json:"name"` // Description is a human readable description that may be // displayed in user interfaces. Description string `json:"description,omitempty"` } // Source defines an external filterlists source. type Source struct { // ID is a unique ID for the source. Entities always reference the // sources they have been observed in using this ID. Refer to the // Entry struct for more information. ID string `json:"id"` // Name is a human readable name for the source and can be used // in user interfaces. Name string `json:"name"` // Description may hold a human readable description for the source. // It may be used in user interfaces. Description string `json:"description"` // Type describes the type of entities the source provides. Refer // to the Type definition for more information and well-known types. Type string `json:"type"` // URL points to the filterlists file. URL string `json:"url"` // Category holds the unique ID of a category the source belongs to. Since // categories can be nested the source is automatically part of all categories // in the hierarchy. Refer to the Category struct for more information. Category string `json:"category"` // Website may holds the URL of the source maintainers website. Website string `json:"website,omitempty"` // License holds the license that is used for the source. License string `json:"license"` // Contribute may hold an opaque string that informs a user on how to // contribute to the source. This may be a URL or mail address. Contribute string `json:"contribute"` } // ListIndexFile describes the structure of the released list // index file. type ListIndexFile struct { record.Base sync.RWMutex Version string `json:"version"` SchemaVersion string `json:"schemaVersion"` Categories []Category `json:"categories"` Sources []Source `json:"sources"` } func (index *ListIndexFile) getCategorySources(id string) []string { ids := make(map[string]struct{}) // find all sources that match against cat for _, s := range index.Sources { if s.Category == id { ids[s.ID] = struct{}{} } } // find all child-categories recursing into getCategorySources. for _, c := range index.Categories { if c.Parent == id { for _, sid := range index.getCategorySources(c.ID) { ids[sid] = struct{}{} } } } return mapKeys(ids) } func (index *ListIndexFile) getSourcesMatching(id string) []string { // if id is already a source ID we just return it for _, s := range index.Sources { if s.ID == id { return []string{s.ID} } } // otherwise we need to check the category tree return index.getCategorySources(id) } func (index *ListIndexFile) getDistictSourceIDs(ids ...string) []string { index.RLock() defer index.RUnlock() distinctIDs := make(map[string]struct{}) for _, id := range ids { for _, sid := range index.getSourcesMatching(id) { distinctIDs[sid] = struct{}{} } } return mapKeys(distinctIDs) } func getListIndexFromCache() (*ListIndexFile, error) { r, err := cache.Get(filterListIndexKey) if err != nil { return nil, err } var index *ListIndexFile if r.IsWrapped() { index = new(ListIndexFile) if err := record.Unwrap(r, index); err != nil { return nil, err } } else { var ok bool index, ok = r.(*ListIndexFile) if !ok { return nil, fmt.Errorf("invalid type, expected ListIndexFile but got %T", r) } } return index, nil } var ( // listIndexUpdate must only be used by updateListIndex. listIndexUpdate *updates.Artifact listIndexUpdateLock sync.Mutex ) // Compares two version strings and returns true only if both are successfully parsed and equal func areSemversEqual(v1, v2 string) bool { parsedV1, err1 := version.NewSemver(v1) parsedV2, err2 := version.NewSemver(v2) if err1 != nil || err2 != nil { return false } return parsedV1.Equal(parsedV2) } func updateListIndex() error { listIndexUpdateLock.Lock() defer listIndexUpdateLock.Unlock() // Check if an update is needed. switch { case listIndexUpdate == nil: // This is the first time this function is run, get updater file for index. var err error listIndexUpdate, err = module.instance.IntelUpdates().GetFile(listIndexFilePath) if err != nil { return err } // Check if the version in the cache is current. index, err := getListIndexFromCache() switch { case errors.Is(err, database.ErrNotFound): log.Info("filterlists: index not in cache, starting update") case err != nil: log.Warningf("filterlists: failed to load index from cache, starting update: %s", err) case listIndexUpdate.Version != strings.TrimPrefix(index.Version, "v") && // Avoid false positives by checking if the version is actually different (e.g. "2025.04.14 == 2025.4.14") !areSemversEqual(listIndexUpdate.Version, index.Version): log.Infof( "filterlists: index from cache is outdated, starting update (%s != %s)", strings.TrimPrefix(index.Version, "v"), listIndexUpdate.Version, ) default: // List is in cache and current, there is nothing to do. log.Debugf("filterlists: index is up to date (%s == %s)", index.Version, listIndexUpdate.Version) // Update the unbreak filter list IDs on initial load. updateUnbreakFilterListIDs() return nil } default: // Index is loaded and no update is available, there is nothing to do. return nil } // Update list index from updates. blob, err := os.ReadFile(listIndexUpdate.Path()) if err != nil { return err } index := &ListIndexFile{} _, err = dsd.Load(blob, index) if err != nil { return err } index.SetKey(filterListIndexKey) if err := cache.Put(index); err != nil { return err } log.Debugf("intel/filterlists: updated list index in cache to %s", index.Version) // Update the unbreak filter list IDs after an update. updateUnbreakFilterListIDs() return nil } // ResolveListIDs resolves a slice of source or category IDs into // a slice of distinct source IDs. func ResolveListIDs(ids []string) ([]string, error) { // Try get the list index, err := getListIndexFromCache() if err != nil { if errors.Is(err, database.ErrNotFound) { // Update the list index if err = updateListIndex(); err != nil { return nil, err } // Retry getting the list. if index, err = getListIndexFromCache(); err != nil { return nil, err } } else { log.Errorf("failed to resolved ids %v: %s", ids, err) return nil, err } } resolved := index.getDistictSourceIDs(ids...) log.Debugf("intel/filterlists: resolved ids %v to %v", ids, resolved) return resolved, nil } var ( unbreakCategoryIDs = []string{"UNBREAK"} unbreakIDs []string unbreakIDsLock sync.Mutex ) // GetUnbreakFilterListIDs returns the resolved list of all unbreak filter lists. func GetUnbreakFilterListIDs() []string { unbreakIDsLock.Lock() defer unbreakIDsLock.Unlock() return unbreakIDs } func updateUnbreakFilterListIDs() { unbreakIDsLock.Lock() defer unbreakIDsLock.Unlock() resolvedIDs, err := ResolveListIDs(unbreakCategoryIDs) if err != nil { log.Warningf("filter: failed to resolve unbreak filter list IDs: %s", err) } else { unbreakIDs = resolvedIDs } } ================================================ FILE: service/intel/filterlists/keys.go ================================================ package filterlists const ( cacheDBPrefix = "cache:intel/filterlists" // filterListCacheVersionKey is used to store the highest version // of a filterlists file (base, intermediate or urgent) in the // cache database. It's used to decide if the cache database and // bloomfilters need to be resetted and rebuilt. filterListCacheVersionKey = cacheDBPrefix + "/version" // filterListIndexKey is used to store the filterlists index. filterListIndexKey = cacheDBPrefix + "/index" // filterListKeyPrefix is the prefix inside that cache database // used for filter list entries. filterListKeyPrefix = cacheDBPrefix + "/lists/" ) func makeBloomCacheKey(scope string) string { return cacheDBPrefix + "/bloom/" + scope } func makeListCacheKey(scope, key string) string { return filterListKeyPrefix + scope + "/" + key } ================================================ FILE: service/intel/filterlists/lookup.go ================================================ package filterlists import ( "errors" "net" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/log" ) // lookupBlockLists loads the entity record for key from // cache and returns the list of blocklist sources the // key is part of. It is not considered an error if // key does not exist, instead, an empty slice is // returned. func lookupBlockLists(entity, value string) ([]string, error) { key := makeListCacheKey(entity, value) if !isLoaded() { log.Warningf("intel/filterlists: not searching for %s because filterlists not loaded", key) // filterLists have not yet been loaded so // there's no point querying into the cache // database. return nil, nil } filterListLock.RLock() defer filterListLock.RUnlock() if !defaultFilter.test(entity, value) { return nil, nil } // log.Debugf("intel/filterlists: searching for entries with %s", key) entry, err := getEntityRecordByKey(key) if err != nil { if errors.Is(err, database.ErrNotFound) { return nil, nil } log.Errorf("intel/filterlists: failed to get entries for key %s: %s", key, err) return nil, err } return entry.Sources, nil } // LookupCountry returns a list of sources that mark the country // as blocked. If country is not stored in the cache database // a nil slice is returned. func LookupCountry(country string) ([]string, error) { return lookupBlockLists("country", country) } // LookupDomain returns a list of sources that mark the domain // as blocked. If domain is not stored in the cache database // a nil slice is returned. The caller is responsible for making // sure that the given domain is valid and canonical. func LookupDomain(domain string) ([]string, error) { switch domain { case "", ".": // Return no lists for empty domains and the root zone. return nil, nil default: return lookupBlockLists("domain", domain) } } // LookupASNString returns a list of sources that mark the ASN // as blocked. If ASN is not stored in the cache database // a nil slice is returned. func LookupASNString(asn string) ([]string, error) { return lookupBlockLists("asn", asn) } // LookupIP returns a list of block sources that contain // a reference to ip. LookupIP automatically checks the IPv4 or // IPv6 lists respectively. func LookupIP(ip net.IP) ([]string, error) { if ip.To4() == nil { return LookupIPv6(ip) } return LookupIPv4(ip) } // LookupIPString is like LookupIP but accepts an IPv4 or // IPv6 address in their string representations. func LookupIPString(ipStr string) ([]string, error) { ip := net.ParseIP(ipStr) if ip == nil { return nil, errors.New("invalid IP") } return LookupIP(ip) } // LookupIPv4String returns a list of block sources that // contain a reference to ip. If the IP is not stored in the // cache database a nil slice is returned. func LookupIPv4String(ipv4 string) ([]string, error) { return lookupBlockLists("ipv4", ipv4) } // LookupIPv4 is like LookupIPv4String but accepts a net.IP. func LookupIPv4(ipv4 net.IP) ([]string, error) { ip := ipv4.To4() if ip == nil { return nil, errors.New("invalid IPv4") } return LookupIPv4String(ip.String()) } // LookupIPv6String returns a list of block sources that // contain a reference to ip. If the IP is not stored in the // cache database a nil slice is returned. func LookupIPv6String(ipv6 string) ([]string, error) { return lookupBlockLists("ipv6", ipv6) } // LookupIPv6 is like LookupIPv6String but accepts a net.IP. func LookupIPv6(ipv6 net.IP) ([]string, error) { ip := ipv6.To16() if ip == nil { return nil, errors.New("invalid IPv6") } return LookupIPv6String(ip.String()) } ================================================ FILE: service/intel/filterlists/module.go ================================================ package filterlists import ( "errors" "fmt" "sync/atomic" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/updates" ) const ( filterlistsDisabled = "filterlists:disabled" filterlistsUpdateFailed = "filterlists:update-failed" filterlistsStaleDataSurvived = "filterlists:staledata" ) type FilterLists struct { mgr *mgr.Manager instance instance states *mgr.StateMgr } func (fl *FilterLists) Manager() *mgr.Manager { return fl.mgr } func (fl *FilterLists) States() *mgr.StateMgr { return fl.states } func (fl *FilterLists) Start() error { return start() } func (fl *FilterLists) Stop() error { return stop() } var ( // booleans mainly used to decouple the module during testing. ignoreUpdateEvents = abool.New() ignoreNetEnvEvents = abool.New() ) func init() { ignoreNetEnvEvents.Set() } func registerEventCallbacks() { module.instance.IntelUpdates().EventResourcesUpdated.AddCallback("Check for blocklist updates", func(wc *mgr.WorkerCtx, s struct{}) (bool, error) { if ignoreUpdateEvents.IsSet() { return false, nil } return false, tryListUpdate(wc.Ctx()) }) module.instance.NetEnv().EventOnlineStatusChange.AddCallback("Check for blocklist updates", func(wc *mgr.WorkerCtx, s netenv.OnlineStatus) (bool, error) { if ignoreNetEnvEvents.IsSet() { return false, nil } // Nothing to do if we went offline. if s == netenv.StatusOffline { return false, nil } return false, tryListUpdate(wc.Ctx()) }) } func start() error { filterListLock.Lock() defer filterListLock.Unlock() // Any call of tryListUpdate() must be only after module fully initialized defer func() { // Register event callbacks registerEventCallbacks() // Initial check filterlists updates module.Manager().Go("intel/filterlists initial check for update", func(ctx *mgr.WorkerCtx) error { if err := tryListUpdate(ctx.Ctx()); err != nil { log.Errorf("intel/filterlists: tryListUpdate() failed: %q", err.Error()) return err } return nil }) }() ver, err := getCacheDatabaseVersion() if err == nil { log.Debugf("intel/filterlists: cache database has version %s", ver.String()) if err = defaultFilter.loadFromCache(); err != nil { err = fmt.Errorf("failed to initialize bloom filters: %w", err) } } if err != nil { log.Debugf("intel/filterlists: blocklists disabled, waiting for update (%s)", err) warnAboutDisabledFilterLists() } else { log.Debugf("intel/filterlists: using cache database") close(filterListsLoaded) } return nil } func stop() error { filterListsLoaded = make(chan struct{}) return nil } func warnAboutDisabledFilterLists() { module.states.Add(mgr.State{ ID: filterlistsDisabled, Name: "Filter Lists Are Initializing", Message: "Filter lists are being downloaded and set up in the background. They will be activated as configured when finished.", Type: mgr.StateTypeWarning, }) } var ( module *FilterLists shimLoaded atomic.Bool ) // New returns a new FilterLists module. func New(instance instance) (*FilterLists, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("FilterLists") module = &FilterLists{ mgr: m, instance: instance, states: mgr.NewStateMgr(m), } return module, nil } type instance interface { IntelUpdates() *updates.Updater NetEnv() *netenv.NetEnv } ================================================ FILE: service/intel/filterlists/module_test.go ================================================ package filterlists /* func TestMain(m *testing.M) { // we completely ignore netenv events during testing. ignoreNetEnvEvents.Set() if err := updates.DisableUpdateSchedule(); err != nil { fmt.Fprintf(os.Stderr, "failed to disable update schedule: %s", err) os.Exit(1) } pmtesting.TestMainWithHooks(m, module, loadOnStart, nil) } func loadOnStart() error { log.SetLogLevel(log.TraceLevel) ch := make(chan struct{}) defer close(ch) if err := updates.TriggerUpdate(); err != nil { return fmt.Errorf("failed to trigger update: %w", err) } var err error go func() { select { case <-ch: return case <-time.After(time.Minute): err = fmt.Errorf("timeout loading") close(filterListsLoaded) // let waitUntilLoaded() return } }() waitUntilLoaded() time.Sleep(time.Second * 10) if err != nil { return err } failureStatus, failureID, failureMsg := module.FailureStatus() if failureStatus == modules.FailureError || failureStatus == modules.FailureWarning { return fmt.Errorf("module in failure state: %s %q", failureID, failureMsg) } // ignore update events from now on during testing. ignoreUpdateEvents.Set() testSources := []string{"TEST"} testEntries := []*listEntry{ { Entity: "example.com", Sources: testSources, Type: "Domain", }, { Entity: "1.1.1.1", Sources: testSources, Type: "IPv4", }, { Entity: "AT", Sources: testSources, Type: "Country", }, { Entity: "123", Sources: testSources, Type: "ASN", }, } for _, e := range testEntries { // add some test entries if err := processEntry(e); err != nil { return err } } return nil } */ ================================================ FILE: service/intel/filterlists/record.go ================================================ package filterlists import ( "fmt" "sync" "github.com/safing/portmaster/base/database/record" ) type entityRecord struct { record.Base `json:"-"` sync.Mutex `json:"-"` Value string Sources []string Type string UpdatedAt int64 } func getEntityRecordByKey(key string) (*entityRecord, error) { r, err := cache.Get(key) if err != nil { return nil, err } if r.IsWrapped() { newER := &entityRecord{} if err := record.Unwrap(r, newER); err != nil { return nil, err } return newER, nil } newER, ok := r.(*entityRecord) if !ok { return nil, fmt.Errorf("record not of type *entityRecord, but %T", r) } return newER, nil } ================================================ FILE: service/intel/filterlists/updater.go ================================================ package filterlists import ( "context" "errors" "fmt" "sort" "time" "github.com/hashicorp/go-version" "github.com/tevino/abool" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/storage" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/updates" ) var updateInProgress = abool.New() // tryListUpdate wraps performUpdate but ensures the module's // error state is correctly set or resolved. func tryListUpdate(ctx context.Context) error { err := performUpdate(ctx) if err != nil { // Check if we are shutting down, as to not raise a false alarm. if module.mgr.IsDone() { return nil } // Check if the module already has a failure status set. If not, set a // generic one with the returned error. hasWarningState := false for _, state := range module.states.Export().States { if state.Type == mgr.StateTypeWarning { hasWarningState = true } } if !hasWarningState { module.states.Add(mgr.State{ ID: filterlistsUpdateFailed, Name: "Filter Lists Update Failed", Message: fmt.Sprintf("The Portmaster failed to process a filter lists update. Filtering capabilities are currently either impaired or not available at all. Error: %s", err.Error()), Type: mgr.StateTypeWarning, }) } return err } return nil } func performUpdate(ctx context.Context) error { if !updateInProgress.SetToIf(false, true) { log.Debugf("intel/filterlists: upgrade already in progress") return nil } defer updateInProgress.UnSet() // First, update the list index. err := updateListIndex() if err != nil { log.Warningf("intel/filterlists: failed update list index: %s", err) } upgradables, err := getUpgradableFiles() if err != nil { return err } log.Debugf("intel/filterlists: resources to update: %v", upgradables) if len(upgradables) == 0 { log.Debugf("intel/filterlists: ignoring update, latest version is already used") return nil } cleanupRequired := false filterToUpdate := defaultFilter // perform the actual upgrade by processing each file // in the returned order. for idx, file := range upgradables { log.Debugf("intel/filterlists: applying update (%d) %s version %s", idx, file.Filename, file.Version) if file == baseFile { if idx != 0 { log.Warningf("intel/filterlists: upgrade order is wrong, base file needs to be updated first not at idx %d", idx) // we still continue because after processing the base // file everything is correct again, we just used some // CPU and IO resources for nothing when processing // the previous files. } cleanupRequired = true // since we are processing a base update we will create our // bloom filters from scratch. filterToUpdate = newScopedBloom() } if err := processListFile(ctx, filterToUpdate, file); err != nil { return fmt.Errorf("failed to process upgrade %s version %s: %w", file.Filename, file.Version, err) } } if filterToUpdate != defaultFilter { // replace the bloom filters in our default // filter. defaultFilter.replaceWith(filterToUpdate) } // from now on, the database is ready and can be used if // it wasn't loaded yet. if !isLoaded() { close(filterListsLoaded) } if err := defaultFilter.saveToCache(); err != nil { // just handle the error by logging as it's only consequence // is that we will need to reprocess all files during the next // start. log.Errorf("intel/filterlists: failed to persist bloom filters in cache database: %s", err) } // if we processed the base file we need to perform // some cleanup on filterlists entities that have not // been updated now. Once we are done, start a worker // for that purpose. if cleanupRequired { if err := module.mgr.Do("filterlists:cleanup", removeAllObsoleteFilterEntries); err != nil { // if we failed to remove all stale cache entries // we abort now WITHOUT updating the database version. This means // we'll try again during the next update. module.states.Add(mgr.State{ ID: filterlistsStaleDataSurvived, Name: "Filter Lists May Overblock", Message: fmt.Sprintf("The Portmaster failed to delete outdated filter list data. Filtering capabilities are fully available, but overblocking may occur. Error: %s", err.Error()), //nolint:misspell // overblocking != overclocking Type: mgr.StateTypeWarning, }) return fmt.Errorf("failed to cleanup stale cache records: %w", err) } } // try to save the highest version of our files. highestVersion := upgradables[len(upgradables)-1] if err := setCacheDatabaseVersion(highestVersion.Version); err != nil { log.Errorf("intel/filterlists: failed to save cache database version: %s", err) } else { log.Infof("intel/filterlists: successfully migrated cache database to %s", highestVersion.Version) } // The list update succeeded, resolve any states. module.states.Clear() return nil } func removeAllObsoleteFilterEntries(wc *mgr.WorkerCtx) error { log.Debugf("intel/filterlists: cleanup task started, removing obsolete filter list entries ...") // TODO: Remember the timestamp we started the last update and use that rather than "one hour ago". // First try to purge with PurgeOlderThan. n, err := cache.PurgeOlderThan(wc.Ctx(), filterListKeyPrefix, time.Now().Add(-time.Hour)) switch { case err == nil: // Success! log.Debugf("intel/filterlists: successfully removed %d obsolete entries", n) return nil case errors.Is(err, database.ErrNotImplemented) || errors.Is(err, storage.ErrNotImplemented): // Try next method. default: // Return error. return err } // Try with regular purge. n, err = cache.Purge(wc.Ctx(), query.New(filterListKeyPrefix).Where( query.Where("UpdatedAt", query.LessThan, time.Now().Add(-time.Hour).Unix()), )) if err != nil { return err } log.Debugf("intel/filterlists: successfully removed %d obsolete entries", n) return nil } // getUpgradableFiles returns a slice of filterlists files // that should be updated. The files MUST be updated and // processed in the returned order! func getUpgradableFiles() ([]*updates.Artifact, error) { var updateOrder []*updates.Artifact cacheDBInUse := isLoaded() newBaseFile, err := module.instance.IntelUpdates().GetFile(baseListFilePath) if err != nil { log.Warningf("intel/filterlists: failed to get base update: %s", err) } else if newer, _ := newBaseFile.IsNewerThan(baseFile); newer || !cacheDBInUse { log.Tracef("intel/filterlists: base file needs update to version %s", newBaseFile.Version) if newBaseFile.SemVer() == nil { log.Warningf("intel/filterlists: base file needs update to version %s, but semver is invalid", newBaseFile.Version) } else { updateOrder = append(updateOrder, newBaseFile) } } newIntermediateFile, err := module.instance.IntelUpdates().GetFile(intermediateListFilePath) if err != nil { log.Warningf("intel/filterlists: failed to get intermediate update: %s", err) } else if newer, _ := newIntermediateFile.IsNewerThan(intermediateFile); newer || !cacheDBInUse { log.Tracef("intel/filterlists: intermediate file needs update to version %s", newIntermediateFile.Version) if newIntermediateFile.SemVer() == nil { log.Warningf("intel/filterlists: intermediate file needs update to version %s, but semver is invalid", newIntermediateFile.Version) } else { updateOrder = append(updateOrder, newIntermediateFile) } } newUrgentFile, err := module.instance.IntelUpdates().GetFile(urgentListFilePath) if err != nil { log.Warningf("intel/filterlists: failed to get urgent update: %s", err) } else if newer, _ := newUrgentFile.IsNewerThan(urgentFile); newer || !cacheDBInUse { log.Tracef("intel/filterlists: urgent file needs update to version %s", newUrgentFile.Version) if newUrgentFile.SemVer() == nil { log.Warningf("intel/filterlists: urgent file needs update to version %s, but semver is invalid", newUrgentFile.Version) } else { updateOrder = append(updateOrder, newUrgentFile) } } return resolveUpdateOrder(updateOrder) } func resolveUpdateOrder(updateOrder []*updates.Artifact) ([]*updates.Artifact, error) { // sort the update order by ascending version sort.Sort(byAscVersion(updateOrder)) log.Tracef("intel/filterlists: order of potential updates: %v", updateOrder) var cacheDBVersion *version.Version if !isLoaded() { cacheDBVersion, _ = version.NewSemver("v0.0.0") } else { var err error cacheDBVersion, err = getCacheDatabaseVersion() if err != nil { if !errors.Is(err, database.ErrNotFound) { log.Errorf("intel/filterlists: failed to get cache database version: %s", err) } cacheDBVersion, _ = version.NewSemver("v0.0.0") } } startAtIdx := -1 for idx, file := range updateOrder { log.Tracef("intel/filterlists: checking file with version %s against %s", file.SemVer(), cacheDBVersion) if file.SemVer().GreaterThan(cacheDBVersion) && (startAtIdx == -1 || file == baseFile) { startAtIdx = idx } } // if startAtIdx == -1 we don't have any upgradables to // process. if startAtIdx == -1 { log.Tracef("intel/filterlists: nothing to process, latest version %s already in use", cacheDBVersion) return nil, nil } // skip any files that are lower then the current cache db version // or after which a base upgrade would be performed. return updateOrder[startAtIdx:], nil } type byAscVersion []*updates.Artifact func (fs byAscVersion) Len() int { return len(fs) } func (fs byAscVersion) Less(i, j int) bool { return fs[i].SemVer().LessThan(fs[j].SemVer()) } func (fs byAscVersion) Swap(i, j int) { fi := fs[i] fj := fs[j] fs[i] = fj fs[j] = fi } ================================================ FILE: service/intel/geoip/country_info.go ================================================ package geoip import "strings" const defaultCountryBasedAccuracy = 200 // AddCountryInfo adds missing country information to the location. func (l *Location) AddCountryInfo() { // Check if we have the country code. if l.Country.Code == "" { return } // Check for anycast. if l.IsAnycast { // Reset data for anycast. l.Country.Code = "__" l.Coordinates.Latitude = 0 l.Coordinates.Longitude = 0 } // Get country info. info, ok := countries[l.Country.Code] if !ok { return } // Apply country info to location. l.Country = info // Use country center as location coordinates if unset. if l.Coordinates.Latitude == 0 && l.Coordinates.Longitude == 0 { l.Coordinates = info.Center } } // GetCountryInfo returns the country info of the given country code, or nil // in case the data does not exist. func GetCountryInfo(countryCode string) CountryInfo { info := countries[countryCode] return info } // CountryInfo holds additional information about countries. type CountryInfo struct { Code string `maxminddb:"iso_code"` Name string Center Coordinates Continent ContinentInfo } // ContinentInfo holds additional information about continents. type ContinentInfo struct { Code string Region string Name string } // Add data to countries. func init() { for code, country := range countries { // Set country code. country.Code = code // Derive continent code from continental region. country.Continent.Code, _, _ = strings.Cut(country.Continent.Region, "-") // Add continent name. switch country.Continent.Code { case "AF": country.Continent.Name = "Africa" case "AN": country.Continent.Name = "Antarctica" case "AS": country.Continent.Name = "Asia" case "EU": country.Continent.Name = "Europe" case "NA": country.Continent.Name = "North America" case "OC": country.Continent.Name = "Oceania" case "SA": country.Continent.Name = "South America" } // Add default accuracy radius. country.Center.AccuracyRadius = defaultCountryBasedAccuracy // Apply back to map. countries[code] = country } } var countries = map[string]CountryInfo{ "__": { Name: "Anycast", Center: Coordinates{AccuracyRadius: earthCircumferenceInKm}, }, "MN": { Name: "Mongolia", Continent: ContinentInfo{Region: "AS-E"}, Center: Coordinates{Latitude: 46.000000, Longitude: 103.000000}, }, "BN": { Name: "Brunei Darussalam", Continent: ContinentInfo{Region: "AS-SE"}, Center: Coordinates{Latitude: 4.000000, Longitude: 114.000000}, }, "GI": { Name: "Gibraltar", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 36.000000, Longitude: -5.000000}, }, "SO": { Name: "Somalia", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: 5.000000, Longitude: 46.000000}, }, "GG": { Name: "Guernsey", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 49.000000, Longitude: -2.000000}, }, "CL": { Name: "Chile", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: -35.000000, Longitude: -71.000000}, }, "LR": { Name: "Liberia", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 6.000000, Longitude: -9.000000}, }, "TZ": { Name: "Tanzania", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -6.000000, Longitude: 34.000000}, }, "MU": { Name: "Mauritius", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -20.000000, Longitude: 57.000000}, }, "HM": { Name: "Heard Island and McDonald Islands", Continent: ContinentInfo{Region: "OC-S"}, Center: Coordinates{Latitude: -53.000000, Longitude: 73.000000}, }, "AR": { Name: "Argentina", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: -38.000000, Longitude: -63.000000}, }, "BV": { Name: "Bouvet Island", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: -54.000000, Longitude: 3.000000}, }, "MS": { Name: "Montserrat", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 16.000000, Longitude: -62.000000}, }, "PT": { Name: "Portugal", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 39.000000, Longitude: -8.000000}, }, "BO": { Name: "Bolivia", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: -16.000000, Longitude: -63.000000}, }, "VC": { Name: "Saint Vincent and the Grenadines", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 12.000000, Longitude: -61.000000}, }, "RO": { Name: "Romania", Continent: ContinentInfo{Region: "EU-E"}, Center: Coordinates{Latitude: 45.000000, Longitude: 24.000000}, }, "MK": { Name: "North Macedonia", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 41.000000, Longitude: 21.000000}, }, "UG": { Name: "Uganda", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: 1.000000, Longitude: 32.000000}, }, "HN": { Name: "Honduras", Continent: ContinentInfo{Region: "NA-S"}, Center: Coordinates{Latitude: 15.000000, Longitude: -86.000000}, }, "IS": { Name: "Iceland", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 64.000000, Longitude: -19.000000}, }, "HR": { Name: "Croatia", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 45.000000, Longitude: 15.000000}, }, "PL": { Name: "Poland", Continent: ContinentInfo{Region: "EU-E"}, Center: Coordinates{Latitude: 51.000000, Longitude: 19.000000}, }, "TC": { Name: "Turks and Caicos Islands", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 21.000000, Longitude: -71.000000}, }, "LC": { Name: "Saint Lucia", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 13.000000, Longitude: -60.000000}, }, "JP": { Name: "Japan", Continent: ContinentInfo{Region: "AS-E"}, Center: Coordinates{Latitude: 36.000000, Longitude: 138.000000}, }, "TN": { Name: "Tunisia", Continent: ContinentInfo{Region: "AF-N"}, Center: Coordinates{Latitude: 33.000000, Longitude: 9.000000}, }, "GS": { Name: "South Georgia and the South Sandwich Islands", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: -54.000000, Longitude: -36.000000}, }, "MY": { Name: "Malaysia", Continent: ContinentInfo{Region: "AS-SE"}, Center: Coordinates{Latitude: 4.000000, Longitude: 101.000000}, }, "TT": { Name: "Trinidad and Tobago", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 10.000000, Longitude: -61.000000}, }, "BE": { Name: "Belgium", Continent: ContinentInfo{Region: "EU-W"}, Center: Coordinates{Latitude: 50.000000, Longitude: 4.000000}, }, "GU": { Name: "Guam", Continent: ContinentInfo{Region: "OC-N"}, Center: Coordinates{Latitude: 13.000000, Longitude: 144.000000}, }, "NL": { Name: "Netherlands", Continent: ContinentInfo{Region: "EU-W"}, Center: Coordinates{Latitude: 52.000000, Longitude: 5.000000}, }, "AF": { Name: "Afghanistan", Continent: ContinentInfo{Region: "AS-S"}, Center: Coordinates{Latitude: 33.000000, Longitude: 67.000000}, }, "CK": { Name: "Cook Islands", Continent: ContinentInfo{Region: "OC-E"}, Center: Coordinates{Latitude: -21.000000, Longitude: -159.000000}, }, "PM": { Name: "Saint Pierre and Miquelon", Continent: ContinentInfo{Region: "NA-N"}, Center: Coordinates{Latitude: 46.000000, Longitude: -56.000000}, }, "OM": { Name: "Oman", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 21.000000, Longitude: 55.000000}, }, "NP": { Name: "Nepal", Continent: ContinentInfo{Region: "AS-S"}, Center: Coordinates{Latitude: 28.000000, Longitude: 84.000000}, }, "RS": { Name: "Serbia", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 44.000000, Longitude: 21.000000}, }, "MW": { Name: "Malawi", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -13.000000, Longitude: 34.000000}, }, "NE": { Name: "Niger", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 17.000000, Longitude: 8.000000}, }, "BY": { Name: "Belarus", Continent: ContinentInfo{Region: "EU-E"}, Center: Coordinates{Latitude: 53.000000, Longitude: 27.000000}, }, "TH": { Name: "Thailand", Continent: ContinentInfo{Region: "AS-SE"}, Center: Coordinates{Latitude: 15.000000, Longitude: 100.000000}, }, "CW": { Name: "Curaçao", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 12.000000, Longitude: -68.000000}, }, "AS": { Name: "American Samoa", Continent: ContinentInfo{Region: "OC-E"}, Center: Coordinates{Latitude: -14.000000, Longitude: -170.000000}, }, "BF": { Name: "Burkina Faso", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 12.000000, Longitude: -1.000000}, }, "BR": { Name: "Brazil", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: -14.000000, Longitude: -51.000000}, }, "CX": { Name: "Christmas Island", Continent: ContinentInfo{Region: "OC-S"}, Center: Coordinates{Latitude: -10.000000, Longitude: 105.000000}, }, "MG": { Name: "Madagascar", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -18.000000, Longitude: 46.000000}, }, "CY": { Name: "Cyprus", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 35.000000, Longitude: 33.000000}, }, "KW": { Name: "Kuwait", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 29.000000, Longitude: 47.000000}, }, "IT": { Name: "Italy", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 41.000000, Longitude: 12.000000}, }, "SJ": { Name: "Svalbard and Jan Mayen", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 77.000000, Longitude: 23.000000}, }, "ZM": { Name: "Zambia", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -13.000000, Longitude: 27.000000}, }, "TO": { Name: "Tonga", Continent: ContinentInfo{Region: "OC-E"}, Center: Coordinates{Latitude: -21.000000, Longitude: -175.000000}, }, "EE": { Name: "Estonia", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 58.000000, Longitude: 25.000000}, }, "LI": { Name: "Liechtenstein", Continent: ContinentInfo{Region: "EU-W"}, Center: Coordinates{Latitude: 47.000000, Longitude: 9.000000}, }, "LB": { Name: "Lebanon", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 33.000000, Longitude: 35.000000}, }, "DK": { Name: "Denmark", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 56.000000, Longitude: 9.000000}, }, "LS": { Name: "Lesotho", Continent: ContinentInfo{Region: "AF-S"}, Center: Coordinates{Latitude: -29.000000, Longitude: 28.000000}, }, "CM": { Name: "Cameroon", Continent: ContinentInfo{Region: "AF-C"}, Center: Coordinates{Latitude: 7.000000, Longitude: 12.000000}, }, "BH": { Name: "Bahrain", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 25.000000, Longitude: 50.000000}, }, "NA": { Name: "Namibia", Continent: ContinentInfo{Region: "AF-S"}, Center: Coordinates{Latitude: -22.000000, Longitude: 18.000000}, }, "ZA": { Name: "South Africa", Continent: ContinentInfo{Region: "AF-S"}, Center: Coordinates{Latitude: -30.000000, Longitude: 22.000000}, }, "PH": { Name: "Philippines", Continent: ContinentInfo{Region: "AS-SE"}, Center: Coordinates{Latitude: 12.000000, Longitude: 121.000000}, }, "JM": { Name: "Jamaica", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 18.000000, Longitude: -77.000000}, }, "PS": { Name: "Palestine", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 31.000000, Longitude: 35.000000}, }, "TM": { Name: "Turkmenistan", Continent: ContinentInfo{Region: "AS-C"}, Center: Coordinates{Latitude: 38.000000, Longitude: 59.000000}, }, "SD": { Name: "Sudan", Continent: ContinentInfo{Region: "AF-N"}, Center: Coordinates{Latitude: 12.000000, Longitude: 30.000000}, }, "KN": { Name: "Saint Kitts and Nevis", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 17.000000, Longitude: -62.000000}, }, "GF": { Name: "French Guiana", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: 3.000000, Longitude: -53.000000}, }, "WS": { Name: "Samoa", Continent: ContinentInfo{Region: "OC-E"}, Center: Coordinates{Latitude: -13.000000, Longitude: -172.000000}, }, "KE": { Name: "Kenya", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: 0.000000, Longitude: 37.000000}, }, "CG": { Name: "Congo", Continent: ContinentInfo{Region: "AF-C"}, Center: Coordinates{Latitude: 0.000000, Longitude: 15.000000}, }, "FJ": { Name: "Fiji", Continent: ContinentInfo{Region: "OC-C"}, Center: Coordinates{Latitude: -16.000000, Longitude: 179.000000}, }, "BL": { Name: "Saint Barthélemy", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 17.000000, Longitude: -62.000000}, }, "TD": { Name: "Chad", Continent: ContinentInfo{Region: "AF-C"}, Center: Coordinates{Latitude: 15.000000, Longitude: 18.000000}, }, "TW": { Name: "Taiwan", Continent: ContinentInfo{Region: "AS-E"}, Center: Coordinates{Latitude: 23.000000, Longitude: 120.000000}, }, "SA": { Name: "Saudi Arabia", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 23.000000, Longitude: 45.000000}, }, "CO": { Name: "Colombia", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: 4.000000, Longitude: -74.000000}, }, "FR": { Name: "France", Continent: ContinentInfo{Region: "EU-W"}, Center: Coordinates{Latitude: 46.000000, Longitude: 2.000000}, }, "WF": { Name: "Wallis and Futuna", Continent: ContinentInfo{Region: "OC-E"}, Center: Coordinates{Latitude: -13.000000, Longitude: -177.000000}, }, "QA": { Name: "Qatar", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 25.000000, Longitude: 51.000000}, }, "IO": { Name: "British Indian Ocean Territory", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -6.000000, Longitude: 71.000000}, }, "LT": { Name: "Lithuania", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 55.000000, Longitude: 23.000000}, }, "IE": { Name: "Ireland", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 53.000000, Longitude: -8.000000}, }, "GW": { Name: "Guinea-Bissau", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 11.000000, Longitude: -15.000000}, }, "PE": { Name: "Peru", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: -9.000000, Longitude: -75.000000}, }, "MA": { Name: "Morocco", Continent: ContinentInfo{Region: "AF-N"}, Center: Coordinates{Latitude: 31.000000, Longitude: -7.000000}, }, "CR": { Name: "Costa Rica", Continent: ContinentInfo{Region: "NA-S"}, Center: Coordinates{Latitude: 9.000000, Longitude: -83.000000}, }, "FK": { Name: "Falkland Islands (Malvinas)", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: -51.000000, Longitude: -59.000000}, }, "PW": { Name: "Palau", Continent: ContinentInfo{Region: "OC-N"}, Center: Coordinates{Latitude: 7.000000, Longitude: 134.000000}, }, "NC": { Name: "New Caledonia", Continent: ContinentInfo{Region: "OC-C"}, Center: Coordinates{Latitude: -20.000000, Longitude: 165.000000}, }, "AM": { Name: "Armenia", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 40.000000, Longitude: 45.000000}, }, "CU": { Name: "Cuba", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 21.000000, Longitude: -77.000000}, }, "DE": { Name: "Germany", Continent: ContinentInfo{Region: "EU-W"}, Center: Coordinates{Latitude: 51.000000, Longitude: 10.000000}, }, "MT": { Name: "Malta", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 35.000000, Longitude: 14.000000}, }, "YE": { Name: "Yemen", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 15.000000, Longitude: 48.000000}, }, "BA": { Name: "Bosnia and Herzegovina", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 43.000000, Longitude: 17.000000}, }, "MP": { Name: "Northern Mariana Islands", Continent: ContinentInfo{Region: "OC-N"}, Center: Coordinates{Latitude: 17.000000, Longitude: 145.000000}, }, "PY": { Name: "Paraguay", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: -23.000000, Longitude: -58.000000}, }, "MO": { Name: "Macao", Continent: ContinentInfo{Region: "AS-E"}, Center: Coordinates{Latitude: 22.000000, Longitude: 113.000000}, }, "SH": { Name: "Saint Helena", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: -24.000000, Longitude: -10.000000}, }, "PN": { Name: "Pitcairn", Continent: ContinentInfo{Region: "OC-E"}, Center: Coordinates{Latitude: -24.000000, Longitude: -127.000000}, }, "GM": { Name: "Gambia", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 13.000000, Longitude: -15.000000}, }, "TG": { Name: "Togo", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 8.000000, Longitude: 0.000000}, }, "AT": { Name: "Austria", Continent: ContinentInfo{Region: "EU-W"}, Center: Coordinates{Latitude: 47.000000, Longitude: 14.000000}, }, "GT": { Name: "Guatemala", Continent: ContinentInfo{Region: "NA-S"}, Center: Coordinates{Latitude: 15.000000, Longitude: -90.000000}, }, "AE": { Name: "United Arab Emirates", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 23.000000, Longitude: 53.000000}, }, "KR": { Name: "South Korea", Continent: ContinentInfo{Region: "AS-E"}, Center: Coordinates{Latitude: 35.000000, Longitude: 127.000000}, }, "JE": { Name: "Jersey", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 49.000000, Longitude: -2.000000}, }, "LV": { Name: "Latvia", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 56.000000, Longitude: 24.000000}, }, "AW": { Name: "Aruba", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 12.000000, Longitude: -69.000000}, }, "AO": { Name: "Angola", Continent: ContinentInfo{Region: "AF-C"}, Center: Coordinates{Latitude: -11.000000, Longitude: 17.000000}, }, "VE": { Name: "Venezuela", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: 6.000000, Longitude: -66.000000}, }, "AG": { Name: "Antigua and Barbuda", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 17.000000, Longitude: -61.000000}, }, "NU": { Name: "Niue", Continent: ContinentInfo{Region: "OC-E"}, Center: Coordinates{Latitude: -19.000000, Longitude: -169.000000}, }, "KY": { Name: "Cayman Islands", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 19.000000, Longitude: -80.000000}, }, "IM": { Name: "Isle of Man", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 54.000000, Longitude: -4.000000}, }, "FM": { Name: "Micronesia", Continent: ContinentInfo{Region: "OC-N"}, Center: Coordinates{Latitude: 7.000000, Longitude: 150.000000}, }, "SB": { Name: "Solomon Islands", Continent: ContinentInfo{Region: "OC-C"}, Center: Coordinates{Latitude: -9.000000, Longitude: 160.000000}, }, "LU": { Name: "Luxembourg", Continent: ContinentInfo{Region: "EU-W"}, Center: Coordinates{Latitude: 49.000000, Longitude: 6.000000}, }, "MF": { Name: "Saint Martin", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 18.000000, Longitude: -63.000000}, }, "AQ": { Name: "Antarctica", Continent: ContinentInfo{Region: "AN"}, Center: Coordinates{Latitude: -75.000000, Longitude: 0.000000}, }, "SC": { Name: "Seychelles", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -4.000000, Longitude: 55.000000}, }, "TL": { Name: "Timor-Leste", Continent: ContinentInfo{Region: "AS-SE"}, Center: Coordinates{Latitude: -8.000000, Longitude: 125.000000}, }, "CC": { Name: "Cocos (Keeling) Islands", Continent: ContinentInfo{Region: "OC-S"}, Center: Coordinates{Latitude: -12.000000, Longitude: 96.000000}, }, "ST": { Name: "Sao Tome and Principe", Continent: ContinentInfo{Region: "AF-C"}, Center: Coordinates{Latitude: 0.000000, Longitude: 6.000000}, }, "NO": { Name: "Norway", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 60.000000, Longitude: 8.000000}, }, "CF": { Name: "Central African Republic", Continent: ContinentInfo{Region: "AF-C"}, Center: Coordinates{Latitude: 6.000000, Longitude: 20.000000}, }, "MR": { Name: "Mauritania", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 21.000000, Longitude: -10.000000}, }, "NI": { Name: "Nicaragua", Continent: ContinentInfo{Region: "NA-S"}, Center: Coordinates{Latitude: 12.000000, Longitude: -85.000000}, }, "AI": { Name: "Anguilla", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 18.000000, Longitude: -63.000000}, }, "AZ": { Name: "Azerbaijan", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 40.000000, Longitude: 47.000000}, }, "US": { Name: "United States of America", Continent: ContinentInfo{Region: "NA-N"}, Center: Coordinates{Latitude: 37.000000, Longitude: -95.000000}, }, "LA": { Name: "Lao", Continent: ContinentInfo{Region: "AS-SE"}, Center: Coordinates{Latitude: 19.000000, Longitude: 102.000000}, }, "BB": { Name: "Barbados", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 13.000000, Longitude: -59.000000}, }, "CD": { Name: "DR Congo", Continent: ContinentInfo{Region: "AF-C"}, Center: Coordinates{Latitude: -4.000000, Longitude: 21.000000}, }, "TK": { Name: "Tokelau", Continent: ContinentInfo{Region: "OC-E"}, Center: Coordinates{Latitude: -8.000000, Longitude: -171.000000}, }, "KZ": { Name: "Kazakhstan", Continent: ContinentInfo{Region: "AS-C"}, Center: Coordinates{Latitude: 48.000000, Longitude: 66.000000}, }, "DM": { Name: "Dominica", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 15.000000, Longitude: -61.000000}, }, "EG": { Name: "Egypt", Continent: ContinentInfo{Region: "AF-N"}, Center: Coordinates{Latitude: 26.000000, Longitude: 30.000000}, }, "GH": { Name: "Ghana", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 7.000000, Longitude: -1.000000}, }, "BI": { Name: "Burundi", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -3.000000, Longitude: 29.000000}, }, "NZ": { Name: "New Zealand", Continent: ContinentInfo{Region: "OC-S"}, Center: Coordinates{Latitude: -40.000000, Longitude: 174.000000}, }, "BJ": { Name: "Benin", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 9.000000, Longitude: 2.000000}, }, "HU": { Name: "Hungary", Continent: ContinentInfo{Region: "EU-E"}, Center: Coordinates{Latitude: 47.000000, Longitude: 19.000000}, }, "BD": { Name: "Bangladesh", Continent: ContinentInfo{Region: "AS-S"}, Center: Coordinates{Latitude: 23.000000, Longitude: 90.000000}, }, "NF": { Name: "Norfolk Island", Continent: ContinentInfo{Region: "OC-S"}, Center: Coordinates{Latitude: -29.000000, Longitude: 167.000000}, }, "LY": { Name: "Libya", Continent: ContinentInfo{Region: "AF-N"}, Center: Coordinates{Latitude: 26.000000, Longitude: 17.000000}, }, "TV": { Name: "Tuvalu", Continent: ContinentInfo{Region: "OC-E"}, Center: Coordinates{Latitude: -7.000000, Longitude: 177.000000}, }, "ZW": { Name: "Zimbabwe", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -19.000000, Longitude: 29.000000}, }, "NG": { Name: "Nigeria", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 9.000000, Longitude: 8.000000}, }, "GD": { Name: "Grenada", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 12.000000, Longitude: -61.000000}, }, "SM": { Name: "San Marino", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 43.000000, Longitude: 12.000000}, }, "RU": { Name: "Russian Federation", Continent: ContinentInfo{Region: "EU-E"}, Center: Coordinates{Latitude: 61.000000, Longitude: 105.000000}, }, "DZ": { Name: "Algeria", Continent: ContinentInfo{Region: "AF-N"}, Center: Coordinates{Latitude: 28.000000, Longitude: 1.000000}, }, "DO": { Name: "Dominican Republic", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 18.000000, Longitude: -70.000000}, }, "SI": { Name: "Slovenia", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 46.000000, Longitude: 14.000000}, }, "BZ": { Name: "Belize", Continent: ContinentInfo{Region: "NA-S"}, Center: Coordinates{Latitude: 17.000000, Longitude: -88.000000}, }, "DJ": { Name: "Djibouti", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: 11.000000, Longitude: 42.000000}, }, "GN": { Name: "Guinea", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 9.000000, Longitude: -9.000000}, }, "VN": { Name: "Viet Nam", Continent: ContinentInfo{Region: "AS-SE"}, Center: Coordinates{Latitude: 14.000000, Longitude: 108.000000}, }, "IR": { Name: "Iran", Continent: ContinentInfo{Region: "AS-S"}, Center: Coordinates{Latitude: 32.000000, Longitude: 53.000000}, }, "KG": { Name: "Kyrgyzstan", Continent: ContinentInfo{Region: "AS-C"}, Center: Coordinates{Latitude: 41.000000, Longitude: 74.000000}, }, "ML": { Name: "Mali", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 17.000000, Longitude: -3.000000}, }, "GP": { Name: "Guadeloupe", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 16.000000, Longitude: -62.000000}, }, "FI": { Name: "Finland", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 61.000000, Longitude: 25.000000}, }, "UA": { Name: "Ukraine", Continent: ContinentInfo{Region: "EU-E"}, Center: Coordinates{Latitude: 48.000000, Longitude: 31.000000}, }, "KP": { Name: "North Korea (DPRK)", Continent: ContinentInfo{Region: "AS-E"}, Center: Coordinates{Latitude: 40.000000, Longitude: 127.000000}, }, "BT": { Name: "Bhutan", Continent: ContinentInfo{Region: "AS-S"}, Center: Coordinates{Latitude: 27.000000, Longitude: 90.000000}, }, "BG": { Name: "Bulgaria", Continent: ContinentInfo{Region: "EU-E"}, Center: Coordinates{Latitude: 42.000000, Longitude: 25.000000}, }, "MM": { Name: "Myanmar", Continent: ContinentInfo{Region: "AS-SE"}, Center: Coordinates{Latitude: 21.000000, Longitude: 95.000000}, }, "PK": { Name: "Pakistan", Continent: ContinentInfo{Region: "AS-S"}, Center: Coordinates{Latitude: 30.000000, Longitude: 69.000000}, }, "KI": { Name: "Kiribati", Continent: ContinentInfo{Region: "OC-N"}, Center: Coordinates{Latitude: -3.000000, Longitude: -168.000000}, }, "GL": { Name: "Greenland", Continent: ContinentInfo{Region: "NA-N"}, Center: Coordinates{Latitude: 71.000000, Longitude: -42.000000}, }, "PG": { Name: "Papua New Guinea", Continent: ContinentInfo{Region: "OC-C"}, Center: Coordinates{Latitude: -6.000000, Longitude: 143.000000}, }, "PF": { Name: "French Polynesia", Continent: ContinentInfo{Region: "OC-E"}, Center: Coordinates{Latitude: -17.000000, Longitude: -149.000000}, }, "VU": { Name: "Vanuatu", Continent: ContinentInfo{Region: "OC-C"}, Center: Coordinates{Latitude: -15.000000, Longitude: 166.000000}, }, "HT": { Name: "Haiti", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 18.000000, Longitude: -72.000000}, }, "SV": { Name: "El Salvador", Continent: ContinentInfo{Region: "NA-S"}, Center: Coordinates{Latitude: 13.000000, Longitude: -88.000000}, }, "EC": { Name: "Ecuador", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: -1.000000, Longitude: -78.000000}, }, "KM": { Name: "Comoros", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -11.000000, Longitude: 43.000000}, }, "VI": { Name: "Virgin Islands (U.S.)", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 18.000000, Longitude: -64.000000}, }, "YT": { Name: "Mayotte", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -12.000000, Longitude: 45.000000}, }, "ET": { Name: "Ethiopia", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: 9.000000, Longitude: 40.000000}, }, "JO": { Name: "Jordan", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 30.000000, Longitude: 36.000000}, }, "RE": { Name: "Réunion", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -21.000000, Longitude: 55.000000}, }, "NR": { Name: "Nauru", Continent: ContinentInfo{Region: "OC-N"}, Center: Coordinates{Latitude: 0.000000, Longitude: 166.000000}, }, "HK": { Name: "Hong Kong", Continent: ContinentInfo{Region: "AS-E"}, Center: Coordinates{Latitude: 22.000000, Longitude: 114.000000}, }, "AU": { Name: "Australia", Continent: ContinentInfo{Region: "OC-S"}, Center: Coordinates{Latitude: -25.000000, Longitude: 133.000000}, }, "FO": { Name: "Faroe Islands", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 61.000000, Longitude: -6.000000}, }, "IQ": { Name: "Iraq", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 33.000000, Longitude: 43.000000}, }, "GE": { Name: "Georgia", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 42.000000, Longitude: 43.000000}, }, "UZ": { Name: "Uzbekistan", Continent: ContinentInfo{Region: "AS-C"}, Center: Coordinates{Latitude: 41.000000, Longitude: 64.000000}, }, "IN": { Name: "India", Continent: ContinentInfo{Region: "AS-S"}, Center: Coordinates{Latitude: 20.000000, Longitude: 78.000000}, }, "MX": { Name: "Mexico", Continent: ContinentInfo{Region: "NA-S"}, Center: Coordinates{Latitude: 23.000000, Longitude: -102.000000}, }, "ER": { Name: "Eritrea", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: 15.000000, Longitude: 39.000000}, }, "AL": { Name: "Albania", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 41.000000, Longitude: 20.000000}, }, "GY": { Name: "Guyana", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: 4.000000, Longitude: -58.000000}, }, "CA": { Name: "Canada", Continent: ContinentInfo{Region: "NA-N"}, Center: Coordinates{Latitude: 56.000000, Longitude: -106.000000}, }, "SY": { Name: "Syrian Arab Republic", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 34.000000, Longitude: 38.000000}, }, "SG": { Name: "Singapore", Continent: ContinentInfo{Region: "AS-SE"}, Center: Coordinates{Latitude: 1.000000, Longitude: 103.000000}, }, "VG": { Name: "Virgin Islands (British)", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 18.000000, Longitude: -64.000000}, }, "MC": { Name: "Monaco", Continent: ContinentInfo{Region: "EU-W"}, Center: Coordinates{Latitude: 43.000000, Longitude: 7.000000}, }, "BM": { Name: "Bermuda", Continent: ContinentInfo{Region: "NA-N"}, Center: Coordinates{Latitude: 32.000000, Longitude: -64.000000}, }, "SX": { Name: "Sint Maarten", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 18.000000, Longitude: -63.000000}, }, "SR": { Name: "Suriname", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: 3.000000, Longitude: -56.000000}, }, "MD": { Name: "Moldova", Continent: ContinentInfo{Region: "EU-E"}, Center: Coordinates{Latitude: 47.000000, Longitude: 28.000000}, }, "CZ": { Name: "Czechia", Continent: ContinentInfo{Region: "EU-E"}, Center: Coordinates{Latitude: 49.000000, Longitude: 15.000000}, }, "GQ": { Name: "Equatorial Guinea", Continent: ContinentInfo{Region: "AF-C"}, Center: Coordinates{Latitude: 1.000000, Longitude: 10.000000}, }, "TF": { Name: "French Southern Territories", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -49.000000, Longitude: 69.000000}, }, "CI": { Name: "Côte d'Ivoire", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 7.000000, Longitude: -5.000000}, }, "VA": { Name: "Holy See", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 41.000000, Longitude: 12.000000}, }, "SN": { Name: "Senegal", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 14.000000, Longitude: -14.000000}, }, "PR": { Name: "Puerto Rico", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 18.000000, Longitude: -66.000000}, }, "ID": { Name: "Indonesia", Continent: ContinentInfo{Region: "AS-SE"}, Center: Coordinates{Latitude: 0.000000, Longitude: 113.000000}, }, "BS": { Name: "Bahamas", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 25.000000, Longitude: -77.000000}, }, "CV": { Name: "Cabo Verde", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 16.000000, Longitude: -24.000000}, }, "AD": { Name: "Andorra", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 42.000000, Longitude: 1.000000}, }, "SK": { Name: "Slovakia", Continent: ContinentInfo{Region: "EU-E"}, Center: Coordinates{Latitude: 48.000000, Longitude: 19.000000}, }, "MV": { Name: "Maldives", Continent: ContinentInfo{Region: "AS-S"}, Center: Coordinates{Latitude: 3.000000, Longitude: 73.000000}, }, "ME": { Name: "Montenegro", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 42.000000, Longitude: 19.000000}, }, "LK": { Name: "Sri Lanka", Continent: ContinentInfo{Region: "AS-S"}, Center: Coordinates{Latitude: 7.000000, Longitude: 80.000000}, }, "KH": { Name: "Cambodia", Continent: ContinentInfo{Region: "AS-SE"}, Center: Coordinates{Latitude: 12.000000, Longitude: 104.000000}, }, "GR": { Name: "Greece", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 39.000000, Longitude: 21.000000}, }, "SL": { Name: "Sierra Leone", Continent: ContinentInfo{Region: "AF-W"}, Center: Coordinates{Latitude: 8.000000, Longitude: -11.000000}, }, "XK": { Name: "Kosovo", Continent: ContinentInfo{Region: "EU-E"}, Center: Coordinates{Latitude: 42.000000, Longitude: 20.000000}, }, "TJ": { Name: "Tajikistan", Continent: ContinentInfo{Region: "AS-C"}, Center: Coordinates{Latitude: 38.000000, Longitude: 71.000000}, }, "SE": { Name: "Sweden", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 60.000000, Longitude: 18.000000}, }, "GA": { Name: "Gabon", Continent: ContinentInfo{Region: "AF-C"}, Center: Coordinates{Latitude: 0.000000, Longitude: 11.000000}, }, "UY": { Name: "Uruguay", Continent: ContinentInfo{Region: "SA"}, Center: Coordinates{Latitude: -32.000000, Longitude: -55.000000}, }, "MZ": { Name: "Mozambique", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -18.000000, Longitude: 35.000000}, }, "PA": { Name: "Panama", Continent: ContinentInfo{Region: "NA-S"}, Center: Coordinates{Latitude: 8.000000, Longitude: -80.000000}, }, "SZ": { Name: "Eswatini", Continent: ContinentInfo{Region: "AF-S"}, Center: Coordinates{Latitude: -26.000000, Longitude: 31.000000}, }, "IL": { Name: "Israel", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 31.000000, Longitude: 34.000000}, }, "GB": { Name: "United Kingdom", Continent: ContinentInfo{Region: "EU-N"}, Center: Coordinates{Latitude: 55.000000, Longitude: -3.000000}, }, "ES": { Name: "Spain", Continent: ContinentInfo{Region: "EU-S"}, Center: Coordinates{Latitude: 40.000000, Longitude: -3.000000}, }, "RW": { Name: "Rwanda", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: -1.000000, Longitude: 29.000000}, }, "EH": { Name: "Western Sahara", Continent: ContinentInfo{Region: "AF-N"}, Center: Coordinates{Latitude: 24.000000, Longitude: -12.000000}, }, "MH": { Name: "Marshall Islands", Continent: ContinentInfo{Region: "OC-N"}, Center: Coordinates{Latitude: 7.000000, Longitude: 171.000000}, }, "MQ": { Name: "Martinique", Continent: ContinentInfo{Region: "NA-E"}, Center: Coordinates{Latitude: 14.000000, Longitude: -61.000000}, }, "CH": { Name: "Switzerland", Continent: ContinentInfo{Region: "EU-W"}, Center: Coordinates{Latitude: 46.000000, Longitude: 8.000000}, }, "CN": { Name: "China", Continent: ContinentInfo{Region: "AS-E"}, Center: Coordinates{Latitude: 35.000000, Longitude: 104.000000}, }, "TR": { Name: "Turkey", Continent: ContinentInfo{Region: "AS-W"}, Center: Coordinates{Latitude: 38.000000, Longitude: 35.000000}, }, "BW": { Name: "Botswana", Continent: ContinentInfo{Region: "AF-S"}, Center: Coordinates{Latitude: -22.000000, Longitude: 24.000000}, }, "SS": { Name: "South Sudan", Continent: ContinentInfo{Region: "AF-E"}, Center: Coordinates{Latitude: 4.000000, Longitude: 31.000000}, }, } ================================================ FILE: service/intel/geoip/country_info_test.go ================================================ package geoip import ( "strings" "testing" ) func TestCountryInfo(t *testing.T) { t.Parallel() for key, country := range countries { // Skip special anycast country. if key == "__" { continue } if key != country.Code { t.Errorf("%s has a wrong country code of %q", key, country.Code) } if country.Name == "" { t.Errorf("%s is missing name", key) } if country.Continent.Code == "" { t.Errorf("%s is missing continent", key) } if country.Continent.Region == "" { t.Errorf("%s is missing continent region", key) } if country.Continent.Name == "" { t.Errorf("%s is missing continent name", key) } generatedContinentCode, _, _ := strings.Cut(country.Continent.Region, "-") if country.Continent.Code != generatedContinentCode { t.Errorf("%s is has wrong continent code or region", key) } if country.Center.Latitude == 0 && country.Center.Longitude == 0 { t.Errorf("%s is missing coords", key) } if country.Center.AccuracyRadius == 0 { t.Errorf("%s is missing accuracy radius", key) } // Generate map source from data: // fmt.Printf( // `"%s": {Name:%q,Region:%q,ContinentCode:%q,Center:Coordinates{AccuracyRadius:%d,Latitude:%f,Longitude:%f},},`, // key, // country.Name, // country.Region, // country.ContinentCode, // country.Center.AccuracyRadius, // country.Center.Latitude, // country.Center.Longitude, // ) // fmt.Println() } if len(countries) < 247 { t.Errorf("dataset only includes %d countries", len(countries)) } } ================================================ FILE: service/intel/geoip/database.go ================================================ package geoip import ( "fmt" "sync" "time" maxminddb "github.com/oschwald/maxminddb-golang" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/updates" ) var worker *updateWorker func init() { worker = &updateWorker{ trigger: make(chan struct{}), v4: updateBroadcaster{ dbName: v4MMDBResource, }, v6: updateBroadcaster{ dbName: v6MMDBResource, }, } } const ( v4MMDBResource = "geoipv4.mmdb" v6MMDBResource = "geoipv6.mmdb" ) type geoIPDB struct { *maxminddb.Reader update *updates.Artifact } // updateBroadcaster stores a geoIPDB and provides synchronized // access to the MMDB reader. It also supports broadcasting to // multiple waiters when a new database becomes available. type updateBroadcaster struct { rw sync.RWMutex db *geoIPDB dbName string waiter chan struct{} } // AvailableUpdate returns a new update artifact if the current broadcaster // needs a database update. func (ub *updateBroadcaster) AvailableUpdate() *updates.Artifact { ub.rw.RLock() defer ub.rw.RUnlock() // Get artifact. artifact, err := module.instance.IntelUpdates().GetFile(ub.dbName) if err != nil { log.Warningf("geoip: failed to get geoip update: %s", err) return nil } // Return artifact if not yet initialized. if ub.db == nil { return artifact } // Compare and return artifact only when confirmed newer. if newer, _ := artifact.IsNewerThan(ub.db.update); newer { return artifact } return nil } // ReplaceDatabase replaces (or initially sets) the mmdb database. // It also notifies all waiters about the availability of the new // database. func (ub *updateBroadcaster) ReplaceDatabase(db *geoIPDB) { ub.rw.Lock() defer ub.rw.Unlock() if ub.db != nil { _ = ub.db.Close() } ub.db = db ub.notifyWaiters() } // notifyWaiters notifies and removes all waiters. Must be called // with ub.rw locked. func (ub *updateBroadcaster) notifyWaiters() { if ub.waiter == nil { return } waiter := ub.waiter ub.waiter = nil close(waiter) } // getWaiter appends and returns a new waiter channel that gets closed // when a new database version is available. Must be called with // ub.rw locked. func (ub *updateBroadcaster) getWaiter() chan struct{} { if ub.waiter != nil { return ub.waiter } ub.waiter = make(chan struct{}) return ub.waiter } type updateWorker struct { trigger chan struct{} once sync.Once v4 updateBroadcaster v6 updateBroadcaster } // GetReader returns a MMDB reader for either the IPv4 or the IPv6 database. // If wait is true GetReader will wait at most 1 second for the database to // become available. If no database is available or GetReader times-out while // waiting nil is returned. func (upd *updateWorker) GetReader(v6 bool, wait bool) *maxminddb.Reader { // check which updateBroadcaster we need to use ub := &upd.v4 if v6 { ub = &upd.v6 } // lock the updateBroadcaster and - if we are allowed to wait - // create a new waiter channel, trigger an update and wait for at // least 1 second for the update to complete. ub.rw.Lock() if ub.db == nil { if wait { waiter := ub.getWaiter() ub.rw.Unlock() upd.triggerUpdate() select { case <-waiter: // call this method again but this time we don't allow // it to wait since there must be a open database anyway ... return upd.GetReader(v6, false) case <-time.After(time.Second): // we tried hard but failed so give up here return nil } } ub.rw.Unlock() return nil } rd := ub.db.Reader ub.rw.Unlock() return rd } // triggerUpdate triggers a database update check. func (upd *updateWorker) triggerUpdate() { upd.start() select { case upd.trigger <- struct{}{}: default: } } func (upd *updateWorker) start() { upd.once.Do(func() { module.mgr.Go("geoip-updater", upd.run) }) } func (upd *updateWorker) run(ctx *mgr.WorkerCtx) error { for { update := upd.v4.AvailableUpdate() if update != nil { if v4, err := getGeoIPDB(update); err == nil { upd.v4.ReplaceDatabase(v4) } else { log.Warningf("geoip: failed to get v4 database: %s", err) } } update = upd.v6.AvailableUpdate() if update != nil { if v6, err := getGeoIPDB(update); err == nil { upd.v6.ReplaceDatabase(v6) } else { log.Warningf("geoip: failed to get v6 database: %s", err) } } select { case <-ctx.Done(): return nil case <-upd.trigger: } } } func getGeoIPDB(update *updates.Artifact) (*geoIPDB, error) { log.Debugf("geoip: opening database %s", update.Path()) reader, err := maxminddb.Open(update.Path()) if err != nil { return nil, fmt.Errorf("failed to open: %w", err) } log.Debugf("geoip: successfully opened database %s", update.Filename) return &geoIPDB{ Reader: reader, update: update, }, nil } ================================================ FILE: service/intel/geoip/init_test.go ================================================ package geoip import ( "fmt" "os" "path/filepath" "testing" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/configure" "github.com/safing/portmaster/service/ui" "github.com/safing/portmaster/service/updates" ) type testInstance struct { db *dbmodule.DBModule api *api.API config *config.Config intelUpdates *updates.Updater } var _ instance = &testInstance{} func (stub *testInstance) IntelUpdates() *updates.Updater { return stub.intelUpdates } func (stub *testInstance) Config() *config.Config { return stub.config } func (stub *testInstance) Notifications() *notifications.Notifications { return nil } func (stub *testInstance) Ready() bool { return true } func (stub *testInstance) Restart() {} func (stub *testInstance) Shutdown() {} func (stub *testInstance) SetCmdLineOperation(f func() error) {} func (stub *testInstance) BinaryUpdates() *updates.Updater { return nil } func (stub *testInstance) UI() *ui.UI { return nil } func (stub *testInstance) DataDir() string { return _dataDir } var _dataDir string func runTest(m *testing.M) error { var err error // Create a temporary directory for testing _dataDir, err = os.MkdirTemp("", "") if err != nil { return fmt.Errorf("failed to create temporary data directory: %w", err) } defer func() { _ = os.RemoveAll(_dataDir) }() // Initialize the Intel update configuration intelUpdateConfig := updates.Config{ Name: configure.DefaultIntelIndexName, Directory: filepath.Join(_dataDir, "test_intel"), DownloadDirectory: filepath.Join(_dataDir, "test_download_intel"), PurgeDirectory: filepath.Join(_dataDir, "test_upgrade_obsolete_intel"), IndexURLs: configure.DefaultIntelIndexURLs, IndexFile: "index.json", AutoCheck: true, AutoDownload: true, AutoApply: true, } // Set the default API listen address api.SetDefaultAPIListenAddress("0.0.0.0:8080") // Initialize the instance with the necessary components stub := &testInstance{} stub.db, err = dbmodule.New(stub) if err != nil { return fmt.Errorf("failed to create database: %w", err) } stub.config, err = config.New(stub) if err != nil { return fmt.Errorf("failed to create config: %w", err) } stub.api, err = api.New(stub) if err != nil { return fmt.Errorf("failed to create api: %w", err) } stub.intelUpdates, err = updates.New(stub, "Intel Updater", intelUpdateConfig) if err != nil { return fmt.Errorf("failed to create updates: %w", err) } module, err = New(stub) if err != nil { return fmt.Errorf("failed to initialize module: %w", err) } err = stub.db.Start() if err != nil { return fmt.Errorf("Failed to start database: %w", err) } err = stub.config.Start() if err != nil { return fmt.Errorf("Failed to start config: %w", err) } err = stub.api.Start() if err != nil { return fmt.Errorf("Failed to start api: %w", err) } err = stub.intelUpdates.Start() if err != nil { return fmt.Errorf("Failed to start updates: %w", err) } err = module.Start() if err != nil { return fmt.Errorf("failed to start module: %w", err) } m.Run() return nil } func TestMain(m *testing.M) { if err := runTest(m); err != nil { fmt.Printf("%s\n", err) os.Exit(1) } } ================================================ FILE: service/intel/geoip/location.go ================================================ package geoip import ( "encoding/binary" "net" "strings" "github.com/umahmood/haversine" "github.com/safing/portmaster/base/utils" ) const ( earthCircumferenceInKm = 40100 // earth circumference in km defaultLocationAccuracy = 100 ) // Location holds information regarding the geographical and network location of an IP address. // TODO: We are currently re-using the Continent-Code for the region. Update this and all dependencies. type Location struct { Country CountryInfo `maxminddb:"country"` Coordinates Coordinates `maxminddb:"location"` AutonomousSystemNumber uint `maxminddb:"autonomous_system_number"` AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"` IsAnycast bool `maxminddb:"is_anycast"` IsSatelliteProvider bool `maxminddb:"is_satellite_provider"` IsAnonymousProxy bool `maxminddb:"is_anonymous_proxy"` } // Coordinates holds geographic coordinates and their estimated accuracy. type Coordinates struct { AccuracyRadius uint16 `maxminddb:"accuracy_radius"` Latitude float64 `maxminddb:"latitude"` Longitude float64 `maxminddb:"longitude"` } /* Location Estimation Distance Value - 0: Other side of the Internet. - 100: Very near, up to same network / datacenter. Weighting Goal - Exposure to different networks shall be limited as much as possible. - A single network should not see a connection over a large distance. - Latency should be low. Weighting Intentions - Being on the same continent is better than being in the same AS. - Being in the same country is better than having low coordinate distance. - Coordinate distance is only a tie breaker, as accuracy varies heavily. - Same AS with lower coordinate distance beats being on the same continent. Weighting Configuration */ const ( weightCountryMatch = 10 weightRegionMatch = 10 weightRegionalNeighborMatch = 10 weightASNMatch = 10 weightASOrgMatch = 10 weightCoordinateDistance = 50 ) /* About the Accuracy Radius - Range: 1-1000 - Seen values (estimation): 1,5,10,20,50,100,200,500,1000 - The default seems to be 100. Cxamples - 1.1.1/24 has 1000: Anycast - 8.8.0/19 has 1000: Anycast - 8.8.52/22 has 1: City of Westfield Conclusion - Ignore or penalize high accuracy radius. */ // EstimateNetworkProximity aims to calculate the distance between two network locations. Returns a proximity value between 0 (far away) and 100 (nearby). func (l *Location) EstimateNetworkProximity(to *Location) (proximity float32) { switch { case l.Country.Code != "" && l.Country.Code == to.Country.Code: proximity += weightCountryMatch + weightRegionMatch + weightRegionalNeighborMatch case l.Country.Continent.Region != "" && l.Country.Continent.Region == to.Country.Continent.Region: proximity += weightRegionMatch + weightRegionalNeighborMatch case l.IsRegionalNeighbor(to): proximity += weightRegionalNeighborMatch } switch { case l.AutonomousSystemNumber == to.AutonomousSystemNumber && l.AutonomousSystemNumber != 0: // Rely more on the ASN data, as it is more accurate than the ASOrg data, // especially when combining location data from multiple sources. proximity += weightASNMatch + weightASOrgMatch case l.AutonomousSystemOrganization == to.AutonomousSystemOrganization && l.AutonomousSystemNumber != 0 && // Check if an ASN is set. If the ASOrg is known, the ASN must be too. !ASOrgUnknown(l.AutonomousSystemOrganization): // Check if the ASOrg name is valid. proximity += weightASOrgMatch } // Check coordinates and adjust accuracy value. accuracy := l.Coordinates.AccuracyRadius switch { case l.Coordinates.Latitude == 0 && l.Coordinates.Longitude == 0: fallthrough case to.Coordinates.Latitude == 0 && to.Coordinates.Longitude == 0: // If we don't have any coordinates, return. return proximity case to.Coordinates.AccuracyRadius > accuracy: // If the destination accuracy is worse, use that one. accuracy = to.Coordinates.AccuracyRadius } // Apply the default location accuracy if there is none. if accuracy == 0 { accuracy = defaultLocationAccuracy } // Calculate coordinate distance in kilometers. fromCoords := haversine.Coord{Lat: l.Coordinates.Latitude, Lon: l.Coordinates.Longitude} toCoords := haversine.Coord{Lat: to.Coordinates.Latitude, Lon: to.Coordinates.Longitude} _, km := haversine.Distance(fromCoords, toCoords) if km <= 100 && accuracy <= 100 { // Give the full value for highly accurate coordinates within 100km. proximity += weightCoordinateDistance } else { // Else, take a percentage. proximityInPercent := (earthCircumferenceInKm - km) / earthCircumferenceInKm // Apply penalty for locations with low accuracy (targeting accuracy radius >100). // Take away at most 50% of the weight through inaccuracy. accuracyModifier := 1 - float64(accuracy)/2000 // Add proximiy weight. proximity += float32( weightCoordinateDistance * // Maxmimum weight for this data point. proximityInPercent * // Range: 0-1 accuracyModifier, // Range: 0.5-1 ) } return proximity } // PrimitiveNetworkProximity calculates the numerical distance between two IP addresses. Returns a proximity value between 0 (far away) and 100 (nearby). func PrimitiveNetworkProximity(from net.IP, to net.IP, ipVersion uint8) int { var diff float64 switch ipVersion { case 4: // TODO: use ip.To4() and :4 a := binary.BigEndian.Uint32(from[12:]) b := binary.BigEndian.Uint32(to[12:]) if a > b { diff = float64(a - b) } else { diff = float64(b - a) } case 6: a := binary.BigEndian.Uint64(from[:8]) b := binary.BigEndian.Uint64(to[:8]) if a > b { diff = float64(a - b) } else { diff = float64(b - a) } default: return 0 } switch ipVersion { case 4: diff /= 256 return int((1 - diff/16777216) * 100) case 6: return int((1 - diff/18446744073709552000) * 100) default: return 0 } } var unknownASOrgNames = []string{ "", // Expected default for unknown. "not routed", // Observed as "Not routed" in data set. "unknown", // Observed as "UNKNOWN" in online data set. "nil", // Programmatic unknown value. "null", // Programmatic unknown value. "undef", // Programmatic unknown value. "undefined", // Programmatic unknown value. } // ASOrgUnknown return whether the given AS Org string actually is meant to // mean that the AS Org is unknown. func ASOrgUnknown(asOrg string) bool { return utils.StringInSlice( unknownASOrgNames, strings.ToLower(asOrg), ) } ================================================ FILE: service/intel/geoip/location_test.go ================================================ package geoip import ( "net" "testing" ) func TestPrimitiveNetworkProximity(t *testing.T) { t.Parallel() ip4_1 := net.ParseIP("1.1.1.1") ip4_2 := net.ParseIP("1.1.1.2") ip4_3 := net.ParseIP("255.255.255.0") dist := PrimitiveNetworkProximity(ip4_1, ip4_2, 4) t.Logf("primitive proximity %s <> %s: %d", ip4_1, ip4_2, dist) if dist < 90 { t.Fatalf("unexpected distance between ip4_1 and ip4_2: %d", dist) } dist = PrimitiveNetworkProximity(ip4_1, ip4_3, 4) t.Logf("primitive proximity %s <> %s: %d", ip4_1, ip4_3, dist) if dist > 10 { t.Fatalf("unexpected distance between ip4_1 and ip4_3: %d", dist) } ip6_1 := net.ParseIP("2a02::1") ip6_2 := net.ParseIP("2a02::2") ip6_3 := net.ParseIP("ffff::1") dist = PrimitiveNetworkProximity(ip6_1, ip6_2, 6) t.Logf("primitive proximity %s <> %s: %d", ip6_1, ip6_2, dist) if dist < 90 { t.Fatalf("unexpected distance between ip6_1 and ip6_2: %d", dist) } dist = PrimitiveNetworkProximity(ip6_1, ip6_3, 6) t.Logf("primitive proximity %s <> %s: %d", ip6_1, ip6_3, dist) if dist > 20 { t.Fatalf("unexpected distance between ip6_1 and ip6_3: %d", dist) } } ================================================ FILE: service/intel/geoip/lookup.go ================================================ package geoip import ( "errors" "net" "github.com/oschwald/maxminddb-golang" ) func getReader(ip net.IP) *maxminddb.Reader { isV6 := ip.To4() == nil return worker.GetReader(isV6, true) } // GetLocation returns Location data of an IP address. func GetLocation(ip net.IP) (*Location, error) { db := getReader(ip) if db == nil { return nil, errors.New("geoip database not available") } record := &Location{} if err := db.Lookup(ip, record); err != nil { return nil, err } record.AddCountryInfo() return record, nil } // IsInitialized returns whether the geoip database has been initialized. func IsInitialized(v6, wait bool) bool { return worker.GetReader(v6, wait) != nil } ================================================ FILE: service/intel/geoip/lookup_test.go ================================================ package geoip import ( "net" "testing" "time" ) func TestLocationLookup(t *testing.T) { // Skip in CI. if testing.Short() { t.Skip() } t.Parallel() // Wait for db to be initialized worker.v4.rw.Lock() waiter := worker.v4.getWaiter() worker.v4.rw.Unlock() worker.triggerUpdate() select { case <-waiter: case <-time.After(50 * time.Second): t.Error("timeout waiting for geoip database to be initialized (updated)") } ip1 := net.ParseIP("81.2.69.142") loc1, err := GetLocation(ip1) if err != nil { t.Fatal(err) } t.Logf("%v", loc1) ip2 := net.ParseIP("1.1.1.1") loc2, err := GetLocation(ip2) if err != nil { t.Fatal(err) } t.Logf("%v", loc2) ip3 := net.ParseIP("8.8.8.8") loc3, err := GetLocation(ip3) if err != nil { t.Fatal(err) } t.Logf("%v", loc3) ip4 := net.ParseIP("81.2.70.142") loc4, err := GetLocation(ip4) if err != nil { t.Fatal(err) } t.Logf("%v", loc4) ip5 := net.ParseIP("194.232.1.1") loc5, err := GetLocation(ip5) if err != nil { t.Fatal(err) } t.Logf("%v", loc5) ip6 := net.ParseIP("151.101.1.164") loc6, err := GetLocation(ip6) if err != nil { t.Fatal(err) } t.Logf("%v", loc6) dist1 := loc1.EstimateNetworkProximity(loc2) dist2 := loc2.EstimateNetworkProximity(loc3) dist3 := loc1.EstimateNetworkProximity(loc3) dist4 := loc1.EstimateNetworkProximity(loc4) t.Logf("proximity %s <> %s: %.2f", ip1, ip2, dist1) t.Logf("proximity %s <> %s: %.2f", ip2, ip3, dist2) t.Logf("proximity %s <> %s: %.2f", ip1, ip3, dist3) t.Logf("proximity %s <> %s: %.2f", ip1, ip4, dist4) } ================================================ FILE: service/intel/geoip/module.go ================================================ package geoip import ( "errors" "sync/atomic" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/updates" ) type GeoIP struct { mgr *mgr.Manager instance instance } func (g *GeoIP) Manager() *mgr.Manager { return g.mgr } func (g *GeoIP) Start() error { module.instance.IntelUpdates().EventResourcesUpdated.AddCallback( "Check for GeoIP database updates", func(_ *mgr.WorkerCtx, _ struct{}) (bool, error) { worker.triggerUpdate() return false, nil }) return nil } func (g *GeoIP) Stop() error { return nil } var ( module *GeoIP shimLoaded atomic.Bool ) // New returns a new GeoIP module. func New(instance instance) (*GeoIP, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("geoip") module = &GeoIP{ mgr: m, instance: instance, } if err := api.RegisterEndpoint(api.Endpoint{ Path: "intel/geoip/countries", Read: api.PermitUser, // Do not attach to module, as the data is always available anyway. StructFunc: func(ar *api.Request) (i interface{}, err error) { return countries, nil }, Name: "Get Country Information", Description: "Returns a map of country information centers indexed by ISO-A2 country code", }); err != nil { return nil, err } return module, nil } type instance interface { BinaryUpdates() *updates.Updater IntelUpdates() *updates.Updater } ================================================ FILE: service/intel/geoip/regions.go ================================================ package geoip import ( "github.com/safing/portmaster/base/utils" ) // IsRegionalNeighbor returns whether the supplied location is a regional neighbor. func (l *Location) IsRegionalNeighbor(other *Location) bool { if l.Country.Continent.Region == "" || other.Country.Continent.Region == "" { return false } if region, ok := regions[l.Country.Continent.Region]; ok { return utils.StringInSlice(region.Neighbors, other.Country.Continent.Region) } return false } // Region defines a geographic region and neighboring regions. type Region struct { ID string Name string Neighbors []string } var regions = map[string]*Region{ "AF-C": { ID: "AF-C", Name: "Africa, Sub-Saharan Africa, Middle Africa", Neighbors: []string{ "AF-E", "AF-N", "AF-S", "AF-W", }, }, "AF-E": { ID: "AF-E", Name: "Africa, Sub-Saharan Africa, Eastern Africa", Neighbors: []string{ "AF-C", "AF-N", "AF-S", }, }, "AF-N": { ID: "AF-N", Name: "Africa, Northern Africa", Neighbors: []string{ "AF-C", "AF-E", "AF-W", "AS-W", "EU-S", }, }, "AF-S": { ID: "AF-S", Name: "Africa, Sub-Saharan Africa, Southern Africa", Neighbors: []string{ "AF-C", "AF-E", "AF-W", }, }, "AF-W": { ID: "AF-W", Name: "Africa, Sub-Saharan Africa, Western Africa", Neighbors: []string{ "AF-C", "AF-N", "AF-S", }, }, "AN": { ID: "AN", Name: "Antarctica", Neighbors: []string{}, }, "AS-C": { ID: "AS-C", Name: "Asia, Central Asia", Neighbors: []string{ "AS-E", "AS-S", "AS-SE", "AS-W", }, }, "AS-E": { ID: "AS-E", Name: "Asia, Eastern Asia", Neighbors: []string{ "AS-C", "AS-S", "AS-SE", }, }, "AS-S": { ID: "AS-S", Name: "Asia, Southern Asia", Neighbors: []string{ "AS-C", "AS-E", "AS-SE", "AS-W", }, }, "AS-SE": { ID: "AS-SE", Name: "Asia, South-eastern Asia", Neighbors: []string{ "AS-C", "AS-E", "AS-S", "OC-C", "OC-E", "OC-N", "OC-S", }, }, "AS-W": { ID: "AS-W", Name: "Asia, Western Asia", Neighbors: []string{ "AF-N", "AS-C", "AS-S", "EU-E", }, }, "EU-E": { ID: "EU-E", Name: "Europe, Eastern Europe", Neighbors: []string{ "AS-W", "EU-N", "EU-S", "EU-W", }, }, "EU-N": { ID: "EU-N", Name: "Europe, Northern Europe", Neighbors: []string{ "EU-E", "EU-S", "EU-W", }, }, "EU-S": { ID: "EU-S", Name: "Europe, Southern Europe", Neighbors: []string{ "AF-N", "EU-E", "EU-N", "EU-W", }, }, "EU-W": { ID: "EU-W", Name: "Europe, Western Europe", Neighbors: []string{ "EU-E", "EU-N", "EU-S", }, }, "NA-E": { ID: "NA-E", Name: "North America, Caribbean", Neighbors: []string{ "NA-N", "NA-S", "SA", }, }, "NA-N": { ID: "NA-N", Name: "North America, Northern America", Neighbors: []string{ "NA-E", "NA-N", "NA-S", }, }, "NA-S": { ID: "NA-S", Name: "North America, Central America", Neighbors: []string{ "NA-E", "NA-N", "NA-S", "SA", }, }, "OC-C": { ID: "OC-C", Name: "Oceania, Melanesia", Neighbors: []string{ "AS-SE", "OC-E", "OC-N", "OC-S", }, }, "OC-E": { ID: "OC-E", Name: "Oceania, Polynesia", Neighbors: []string{ "AS-SE", "OC-C", "OC-N", "OC-S", }, }, "OC-N": { ID: "OC-N", Name: "Oceania, Micronesia", Neighbors: []string{ "AS-SE", "OC-C", "OC-E", "OC-S", }, }, "OC-S": { ID: "OC-S", Name: "Oceania, Australia and New Zealand", Neighbors: []string{ "AS-SE", "OC-C", "OC-E", "OC-N", }, }, "SA": { // TODO: Split up ID: "SA", Name: "South America", Neighbors: []string{ "NA-E", "NA-S", }, }, } ================================================ FILE: service/intel/geoip/regions_test.go ================================================ package geoip import ( "testing" "github.com/safing/portmaster/base/utils" ) func TestRegions(t *testing.T) { t.Parallel() // Check if all neighbors are also linked back. for key, region := range regions { if key != region.ID { t.Errorf("region has different key than ID: %s != %s", key, region.ID) } for _, neighborID := range region.Neighbors { if otherRegion, ok := regions[neighborID]; ok { if !utils.StringInSlice(otherRegion.Neighbors, region.ID) { t.Errorf("region %s has neighbor %s, but is not linked back", region.ID, neighborID) } } else { t.Errorf("region %s does not exist", neighborID) } } } } ================================================ FILE: service/intel/resolver.go ================================================ package intel import ( "context" ) var reverseResolver func(ctx context.Context, ip string) (domain string, err error) // SetReverseResolver allows the resolver module to register a function to allow reverse resolving IPs to domains. func SetReverseResolver(fn func(ctx context.Context, ip string) (domain string, err error)) { if reverseResolver == nil { reverseResolver = fn } } ================================================ FILE: service/interop/api.go ================================================ package interop import ( "github.com/safing/portmaster/base/api" ) const ( APIEndpointPing = "interop/ping" ) type pingParams struct { Message bool `json:"message"` } func (i *Interoperability) registerAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Path: APIEndpointPing, Read: api.PermitAnyone, Write: api.PermitAnyone, ActionFunc: i.handlePing, Name: "Interoperability: Ping Portmaster", Description: "Ping the Portmaster Core Service for interoperability checks.", }); err != nil { return err } return nil } func (c *Interoperability) handlePing(r *api.Request) (msg string, err error) { // Received ping, possibly from IVPN client // Try to connect to IVPN client if not already connected for _, im := range c.interopModules { if err := im.PingHandler(); err != nil { c.mgr.Warn("Failed to handle ping for interoperability module: " + err.Error()) } } return "", nil } ================================================ FILE: service/interop/ivpn/ivpn.go ================================================ package ivpn import ( "encoding/json" "fmt" "net" "sync/atomic" "time" "github.com/ivpn/desktop-app/daemon/protocol/ivpnclient" "github.com/safing/portmaster/base/info" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/firewall/interception" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" ) const DNS_LOCKED_DESCRIPTION = "Portmaster controls all DNS resolution on this system, which prevents conflicts with IVPN's DNS settings. To let IVPN manage DNS again, remove all DNS servers from Portmaster's DNS configuration." // interopBase defines the interface that the InteropIvpn module expects from its owner // (the main Interoperability module). type interopBase interface { EnsureVerdictHandlerRegistered() DnsListenAddress() string DnsNameServers() []string EvtConfigChange() <-chan struct{} Interception() *interception.Interception Manager() *mgr.Manager } // vpnConnectionInfo holds information about the active VPN connection of the IVPN client, // used for identifying VPN traffic in firewall verdicts. type vpnConnectionInfo struct { dstPort uint16 dstAddress net.IP protocol uint8 } // clientStatus holds information about the connected IVPN client // and its current VPN connection status, used for providing context in firewall verdicts. type clientStatus struct { serviceBinary string vpnConnection vpnConnectionInfo } // InteropIvpn handles interoperability with the IVPN client application, // allowing Portmaster to receive information about IVPN VPN connections // and adjust firewall verdicts accordingly for better compatibility and user experience. type InteropIvpn struct { owner interopBase connHandler *mgr.WorkerMgr status atomic.Pointer[clientStatus] isCustomDnsActive atomic.Bool firstTryDone atomic.Pointer[chan struct{}] } func NewInteropIvpn(owner interopBase) *InteropIvpn { return &InteropIvpn{ owner: owner, } } func (i *InteropIvpn) Start() error { i.connHandler = i.owner.Manager().NewWorkerMgr("ivpn client interoperability", i.connectIvpnClient, nil) firstTryChan := make(chan struct{}) i.firstTryDone.Store(&firstTryChan) i.connHandler.Go() // Wait for the first connection attempt to complete. // The 'status' must be initialized before allowing firewall verdicts to proceed. // The wait is bounded by the connection timeouts inside connectIvpnClient. <-firstTryChan return nil } func (i *InteropIvpn) PingHandler() error { i.connHandler.Go() return nil } func (i *InteropIvpn) setFirstTryDone() { if chanPtr := i.firstTryDone.Load(); chanPtr != nil { close(*chanPtr) i.firstTryDone.Store(nil) } } func (i *InteropIvpn) setServiceBinary(path string) { status := clientStatus{} if old := i.status.Load(); old != nil { status = *old } status.serviceBinary = path i.status.Store(&status) } var notifWarnOldVersion atomic.Pointer[notifications.Notification] // Synchronously connects to the IVPN client, sets up message handlers func (i *InteropIvpn) connectIvpnClient(wc *mgr.WorkerCtx) error { defer func() { // Clear client status on disconnect i.status.Store(nil) // Reset DNS tracking state i.isCustomDnsActive.Store(false) // Mark that the first connection attempt is done, even if it failed i.setFirstTryDone() }() notifWarn := notifWarnOldVersion.Load() if notifWarn != nil { notifWarn.Delete() notifWarnOldVersion.Store(nil) } ci := ivpnclient.ClientInfo{ Type: ivpnclient.ClientPortmaster, Name: "Portmaster", Version: info.Version()} // Create client. // Ignoring error here, since it is expected that the client may not be in running state client, err := ivpnclient.NewClientAsRoot(nil, time.Second*10, ci) if err != nil { return nil } // Register handler for VPN connection messages client.SetMessageEventHandler("ConnectionStarting", func(messageName string, messageData string) { i.onConnectionStarting(wc, messageName, messageData) }) client.SetMessageEventHandler("ConnectionStopped", func(messageName string, messageData string) { i.onConnectionStopped(wc, messageName, messageData) }) // Connect to client. // Ignoring error here, since it is expected that the client may not be in running state err = client.Connect(time.Second * 2) if err != nil { return nil } // Send hello request, which is required to start receiving messages. hello := client.InitHelloRequest() hello.GetServiceBinaryPath = true hello.GetActiveRemoteEndpoint = true var helloResp ivpnclient.HelloResp err = client.SendRecv(&hello, &helloResp) if err != nil { client.Disconnect() return err } if helloResp.ServiceBinary == "" { // IVPN client version > v3.15.0 must provide the service binary path in the hello response. wc.Warn(fmt.Sprintf("Detected IVPN Client version '%v' is incompatible. The hello response did not include all required fields.", helloResp.Version)) notif := i.showNotificationWarnOldVersion() notifWarnOldVersion.Store(notif) return nil } // Save ServiceBinary. i.setServiceBinary(helloResp.ServiceBinary) // The status.vpnConnection must be already initialized (ConnectionStarting message already received). i.setFirstTryDone() // Notify owner that we can now provide verdicts for firewall module i.owner.EnsureVerdictHandlerRegistered() wc.Debug(fmt.Sprintf("Connected to IVPN client %s", helloResp.Version)) // Show UI notification if not suppressed by user if !isNotificationSuppressed() { notification := i.initAndShowNotification() defer notification.Delete() } // Configure IVPN client DNS settings based on current Portmaster config at startup i.updateIvpnClientDnsSettings(wc, client) // Subscribe to interception start/stop events to update IVPN client DNS settings interceptionStatus := i.owner.Interception().EventStartStopState.Subscribe("ivpn", 10) defer interceptionStatus.Cancel() done := false for !done { select { case <-interceptionStatus.Events(): i.updateIvpnClientDnsSettings(wc, client) case <-i.owner.EvtConfigChange(): i.updateIvpnClientDnsSettings(wc, client) case <-wc.Done(): client.Disconnect() done = true case <-client.Disconnected(): done = true } } wc.Debug("IVPN client disconnected") return nil } // updateIvpnClientDnsSettings configures the IVPN client to use Portmaster's local DNS resolver // when custom DNS servers are configured in Portmaster and interception is active. func (i *InteropIvpn) updateIvpnClientDnsSettings(wc *mgr.WorkerCtx, client *ivpnclient.Client) { // Custom DNS should be applied only if there are any custom DNS servers configured in Portmaster, // and interception is active (PM not in Paused state). wantCustomDns := len(i.owner.DnsNameServers()) > 0 && i.owner.Interception().IsStarted() if wantCustomDns == i.isCustomDnsActive.Load() { return } if wantCustomDns { // Configure IVPN to use Portmaster's local DNS resolver. // This prevents IVPN from modifying system DNS, letting Portmaster handle all DNS requests. customDns := ivpnclient.DnsSettings{Servers: []ivpnclient.DnsServerConfig{{Address: i.owner.DnsListenAddress()}}} if err := client.SetTempPrioritizedDns(customDns, DNS_LOCKED_DESCRIPTION); err != nil { wc.Warn(fmt.Sprintf("IVPN: Failed to set manual DNS: %v", err)) return } wc.Debug(fmt.Sprintf("IVPN: Manual DNS set successfully to %q", i.owner.DnsListenAddress())) i.isCustomDnsActive.Store(true) return } // Reset IVPN back to its default DNS handling. if err := client.SetTempPrioritizedDns(ivpnclient.DnsSettings{}, ""); err != nil { wc.Warn(fmt.Sprintf("IVPN: Failed to restore manual DNS: %v", err)) return } i.isCustomDnsActive.Store(false) wc.Debug("IVPN: Manual DNS restored successfully") } // notification handler: VPN connection is going to start func (i *InteropIvpn) onConnectionStarting(wc *mgr.WorkerCtx, _ string, messageData string) { connInfo := ivpnclient.ConnectionStarting{} err := json.Unmarshal([]byte(messageData), &connInfo) if err != nil { wc.Warn(fmt.Sprintf("IVPN: Failed to parse ConnectionStarting message: %v", err)) return } // Update/Store VPN connection info for use in firewall verdicts status := clientStatus{} if old := i.status.Load(); old != nil { status = *old // Preserve existing status fields } conn := vpnConnectionInfo{ dstPort: connInfo.Port, dstAddress: net.ParseIP(connInfo.Address), protocol: connInfo.Protocol, } if conn.dstAddress == nil { conn.dstPort = 0 // Invalidate port if address is invalid, to avoid false matches in firewall verdicts. } status.vpnConnection = conn i.status.Store(&status) wc.Debug(fmt.Sprintf("IVPN: VPN connection starting %s:%d %s", connInfo.Address, connInfo.Port, packet.IPProtocol(connInfo.Protocol))) } // notification handler: VPN connection stopped func (i *InteropIvpn) onConnectionStopped(wc *mgr.WorkerCtx, _ string, messageData string) { status := clientStatus{} if old := i.status.Load(); old != nil { status = *old } status.vpnConnection = vpnConnectionInfo{} i.status.Store(&status) wc.Debug("IVPN: VPN connection stopped") } // VerdictHandler provides firewall verdicts for IVPN client connections // based on the current VPN connection status and client binary path. // It is registered with the firewall module to be called for firewall decisions, // allowing it to adjust verdicts for better compatibility and user experience with the IVPN client. func (i *InteropIvpn) VerdictHandler(conn *network.Connection) (verdict network.Verdict, reason string, skipTunnel bool) { status := i.status.Load() if status == nil { return network.VerdictUndecided, "", false } if status.vpnConnection.dstPort != 0 { if conn.Entity.Port == status.vpnConnection.dstPort && conn.Entity.Protocol == status.vpnConnection.protocol && conn.Entity.IP.Equal(status.vpnConnection.dstAddress) { // By default, we accept outbound connections. // But UDP traffic is bidirectional, so we also accept inbound connections // to avoid breaking incoming UDP responses from the VPN server. if !conn.Inbound || status.vpnConnection.protocol == uint8(packet.UDP) { return network.VerdictAccept, "IVPN VPN connection", true } } } if status.serviceBinary != "" && conn.Process().Path == status.serviceBinary { return network.VerdictAccept, "IVPN Service connection", true } return network.VerdictUndecided, "", false } ================================================ FILE: service/interop/ivpn/notification.go ================================================ package ivpn import ( "context" "sync" "time" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/notifications" ) func (i *InteropIvpn) showNotificationWarnOldVersion() *notifications.Notification { notification := ¬ifications.Notification{ EventID: "interop:ivpn-old-version", Type: notifications.Warning, Title: "IVPN Client Compatibility Notice", Message: `Portmaster has detected the IVPN Client, but the installed version may not be fully compatible when running alongside Portmaster. Some features may not work as expected. Please consider updating to the latest version of the IVPN Client.`, ShowOnSystem: true, Expires: time.Now().Add(5 * time.Minute).Unix(), AvailableActions: []*notifications.Action{ { ID: "ack", Text: "OK", }, }, } notifications.Notify(notification) return notification } func (i *InteropIvpn) initAndShowNotification() *notifications.Notification { const actionSuppressID = "suppress" notification := ¬ifications.Notification{ EventID: "interop:ivpn", Type: notifications.Info, Title: "IVPN Client detected", Message: `Portmaster has detected the IVPN Client and will allow its VPN and service connections.`, AvailableActions: []*notifications.Action{ { ID: "ack", Text: "OK", }, { ID: actionSuppressID, Text: "Don't show again", Visibility: notifications.ActionVisibilityDetailed, }, }, } notification.SetActionFunction(func(_ context.Context, n *notifications.Notification) error { n.Lock() actionID := n.SelectedActionID n.Unlock() if actionID == actionSuppressID { if err := suppressNotification(); err != nil { return err } } n.Delete() return nil }) notifications.Notify(notification) return notification } // === Notification state persistence === // markerRecord is a minimal database record used as a presence-only marker. type markerRecord struct { record.Base sync.Mutex } var db = database.NewInterface(&database.Options{Local: true, Internal: true}) // Database key used to persist the user's choice to suppress the IVPN detected notification. const Notification_DB_ID_IvpnDetectSuppressed = "core:notifications/interop/ivpn/suppressed" // isNotificationSuppressed returns true if the user has chosen to never see the IVPN compat notification. func isNotificationSuppressed() bool { _, err := db.Get(Notification_DB_ID_IvpnDetectSuppressed) return err == nil } // suppressNotification persists the user's decision to never show the notification again. func suppressNotification() error { m := &markerRecord{} m.SetKey(Notification_DB_ID_IvpnDetectSuppressed) return db.Put(m) } ================================================ FILE: service/interop/module.go ================================================ package interop import ( "errors" "net" "sync/atomic" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/firewall" "github.com/safing/portmaster/service/firewall/interception" "github.com/safing/portmaster/service/interop/ivpn" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network" ) // Interface for separate interoperability objects. // Each object can implement interoperability with a specific third-party application, // such as IVPN client, to exchange information and coordinate actions for better compatibility and user experience. type interopModule interface { Start() error PingHandler() error VerdictHandler(conn *network.Connection) (network.Verdict, string, bool) } type instance interface { Config() *config.Config Interception() *interception.Interception } // Module for interoperability with third-party applications type Interoperability struct { mgr *mgr.Manager instance instance cfgDnsNameServers config.StringArrayOption dnsListenAddress string interopModules []interopModule verdictHandlerRegistered atomic.Bool } // Manager returns the module manager. func (u *Interoperability) Manager() *mgr.Manager { return u.mgr } // Start starts the module. func (u *Interoperability) Start() error { return start() } // Stop stops the module. func (u *Interoperability) Stop() error { return stop() } func (u *Interoperability) DnsListenAddress() string { return u.dnsListenAddress } func (u *Interoperability) DnsNameServers() []string { return u.cfgDnsNameServers() } func (u *Interoperability) EvtConfigChange() <-chan struct{} { return u.instance.Config().EventConfigChange.Subscribe("interoperability: config change detection", 10).Events() } func (u *Interoperability) Interception() *interception.Interception { return u.instance.Interception() } func start() error { for _, im := range module.interopModules { if err := im.Start(); err != nil { return errors.New("failed to start interoperability module: " + err.Error()) } } return nil } func stop() error { return nil } var ( module *Interoperability shimLoaded atomic.Bool ) func New(instance instance) (*Interoperability, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } // Determine Portmaster's local DNS resolver listen address for use in IVPN client configuration. // Ot is ok to check it once at startup, since it can only be changed by restarting Portmaster with a different config. var err error dnsListenAddress := config.GetAsString("dns/listenAddress", "")() dnsListenAddress, _, err = net.SplitHostPort(dnsListenAddress) if err != nil || net.ParseIP(dnsListenAddress) == nil || dnsListenAddress == "" || dnsListenAddress == "0.0.0.0" { dnsListenAddress = "127.0.0.1" } // Create module instance. m := mgr.New("Interop") module = &Interoperability{ mgr: m, instance: instance, cfgDnsNameServers: config.GetAsStringArray("dns/nameservers", []string{}), dnsListenAddress: dnsListenAddress, interopModules: make([]interopModule, 0, 1), } if err := module.prep(); err != nil { return nil, err } return module, nil } func (i *Interoperability) prep() error { i.interopModules = append(i.interopModules, ivpn.NewInteropIvpn(i)) return i.registerAPIEndpoints() } // EnsureVerdictHandlerRegistered registers the interoperability module's verdict handler // with the firewall module if not already registered. // We do this lazily here only when we need it. func (i *Interoperability) EnsureVerdictHandlerRegistered() { if i.verdictHandlerRegistered.CompareAndSwap(false, true) { firewall.SetExternalVerdictHandler(i.verdict_handler) } } ================================================ FILE: service/interop/verdict_handler.go ================================================ package interop import ( "github.com/safing/portmaster/service/network" ) func (i *Interoperability) verdict_handler(conn *network.Connection) (verdict network.Verdict, reason string, skipTunnel bool) { for _, im := range i.interopModules { verdict, reason, skipTunnel := im.VerdictHandler(conn) if verdict != network.VerdictUndecided { return verdict, reason, skipTunnel } } return network.VerdictUndecided, "", false } ================================================ FILE: service/mgr/doc.go ================================================ // Package mgr provides simple managing of flow control and logging. package mgr ================================================ FILE: service/mgr/events.go ================================================ //nolint:structcheck,golint // TODO: Seems broken for generics. package mgr import ( "slices" "sync" "sync/atomic" ) // EventMgr is a simple event manager. type EventMgr[T any] struct { name string mgr *Manager lock sync.Mutex subs []*EventSubscription[T] callbacks []*EventCallback[T] } // EventSubscription is a subscription to an event. type EventSubscription[T any] struct { name string events chan T canceled atomic.Bool } // EventCallback is a registered callback to an event. type EventCallback[T any] struct { name string callback EventCallbackFunc[T] canceled atomic.Bool } // EventCallbackFunc defines the event callback function. type EventCallbackFunc[T any] func(*WorkerCtx, T) (cancel bool, err error) // NewEventMgr returns a new event manager. // It is easiest used as a public field on a struct, // so that others can simply Subscribe() oder AddCallback(). func NewEventMgr[T any](eventName string, mgr *Manager) *EventMgr[T] { return &EventMgr[T]{ name: eventName, mgr: mgr, } } // Subscribe subscribes to events. // The received events are shared among all subscribers and callbacks. // Be sure to apply proper concurrency safeguards, if applicable. func (em *EventMgr[T]) Subscribe(subscriberName string, chanSize int) *EventSubscription[T] { em.lock.Lock() defer em.lock.Unlock() es := &EventSubscription[T]{ name: subscriberName, events: make(chan T, chanSize), } em.subs = append(em.subs, es) return es } // AddCallback adds a callback to executed on events. // The received events are shared among all subscribers and callbacks. // Be sure to apply proper concurrency safeguards, if applicable. func (em *EventMgr[T]) AddCallback(callbackName string, callback EventCallbackFunc[T]) { em.lock.Lock() defer em.lock.Unlock() ec := &EventCallback[T]{ name: callbackName, callback: callback, } em.callbacks = append(em.callbacks, ec) } // Submit submits a new event. func (em *EventMgr[T]) Submit(event T) { em.lock.Lock() defer em.lock.Unlock() var anyCanceled bool // Send to subscriptions. for _, sub := range em.subs { // Check if subscription was canceled. if sub.canceled.Load() { anyCanceled = true continue } // Submit via channel. select { case sub.events <- event: default: if em.mgr != nil { em.mgr.Warn( "event subscription channel overflow", "event", em.name, "subscriber", sub.name, ) } } } // Run callbacks. for _, ec := range em.callbacks { // Check if callback was canceled. if ec.canceled.Load() { anyCanceled = true continue } // Execute callback. var ( cancel bool err error ) if em.mgr != nil { // Prefer executing in worker. name := "event " + em.name + " callback " + ec.name em.mgr.Go(name, func(w *WorkerCtx) error { cancel, err = ec.callback(w, event) // Handle error and cancelation. if err != nil { w.Warn( "event callback failed", "event", em.name, "callback", ec.name, "err", err, ) } if cancel { ec.canceled.Store(true) } return nil }) } else { cancel, err = ec.callback(nil, event) // Handle error and cancelation. if err != nil && em.mgr != nil { em.mgr.Warn( "event callback failed", "event", em.name, "callback", ec.name, "err", err, ) } if cancel { ec.canceled.Store(true) anyCanceled = true } } } // If any canceled subscription/callback was seen, clean the slices. if anyCanceled { em.clean() } } // clean removes all canceled subscriptions and callbacks. func (em *EventMgr[T]) clean() { em.subs = slices.DeleteFunc[[]*EventSubscription[T], *EventSubscription[T]](em.subs, func(es *EventSubscription[T]) bool { return es.canceled.Load() }) em.callbacks = slices.DeleteFunc[[]*EventCallback[T], *EventCallback[T]](em.callbacks, func(ec *EventCallback[T]) bool { return ec.canceled.Load() }) } // Events returns a read channel for the events. // The received events are shared among all subscribers and callbacks. // Be sure to apply proper concurrency safeguards, if applicable. func (es *EventSubscription[T]) Events() <-chan T { return es.events } // Cancel cancels the subscription. // The events channel is not closed, but will not receive new events. func (es *EventSubscription[T]) Cancel() { es.canceled.Store(true) } // Done returns whether the event subscription has been canceled. func (es *EventSubscription[T]) Done() bool { return es.canceled.Load() } ================================================ FILE: service/mgr/group.go ================================================ package mgr import ( "context" "errors" "fmt" "reflect" "strings" "sync/atomic" "time" ) var ( // ErrUnsuitableGroupState is returned when an operation cannot be executed due to an unsuitable state. ErrUnsuitableGroupState = errors.New("unsuitable group state") // ErrInvalidGroupState is returned when a group is in an invalid state and cannot be recovered. ErrInvalidGroupState = errors.New("invalid group state") // ErrExecuteCmdLineOp is returned when modules are created, but request // execution of a (somewhere else set) command line operation instead. ErrExecuteCmdLineOp = errors.New("command line operation execution requested") ) const ( groupStateOff int32 = iota groupStateStarting groupStateRunning groupStateStopping groupStateInvalid ) //nolint:goconst func groupStateToString(state int32) string { switch state { case groupStateOff: return "off" case groupStateStarting: return "starting" case groupStateRunning: return "running" case groupStateStopping: return "stopping" case groupStateInvalid: return "invalid" } return "unknown" } // Group describes a group of modules. type Group struct { modules []*groupModule state atomic.Int32 } type groupModule struct { module Module mgr *Manager } // Module is an manage-able instance of some component. type Module interface { Manager() *Manager Start() error Stop() error } // NewGroup returns a new group of modules. func NewGroup(modules ...Module) *Group { // Create group. g := &Group{ modules: make([]*groupModule, 0, len(modules)), } // Initialize groups modules. for _, m := range modules { g.Add(m) } return g } // Add validates the given module and adds it to the group, if all requirements are met. // Not safe for concurrent use with any other method. // All modules must be added before anything else is done with the group. func (g *Group) Add(m Module) { mgr := m.Manager() // Check module. switch { case m == nil: // Skip nil values to allow for cleaner code. return case reflect.ValueOf(m).IsNil(): // If nil values are given via a struct, they are will be interfaces to a // nil type. Ignore these too. return case mgr == nil: // Ignore modules that do not return a manager. return case mgr.Name() == "": // Force name if none is set. // TODO: Unsafe if module is already logging, etc. mgr.setName(makeModuleName(m)) } // Add module to group. g.modules = append(g.modules, &groupModule{ module: m, mgr: mgr, }) } // Start starts all modules in the group in the defined order. // If a module fails to start, itself and all previous modules // will be stopped in the reverse order. func (g *Group) Start() error { // Check group state. switch g.state.Load() { case groupStateRunning: // Already running. return nil case groupStateInvalid: // Something went terribly wrong, cannot recover from here. return fmt.Errorf("%w: cannot recover", ErrInvalidGroupState) default: if !g.state.CompareAndSwap(groupStateOff, groupStateStarting) { return fmt.Errorf("%w: group is not off, state: %s", ErrUnsuitableGroupState, groupStateToString(g.state.Load())) } } // Start modules. for i, m := range g.modules { m.mgr.Debug("starting") startTime := time.Now() err := m.mgr.Do("start module "+m.mgr.name, func(_ *WorkerCtx) error { return m.module.Start() //nolint:scopelint // Execution is synchronous. }) if err != nil { m.mgr.Error( "failed to start", "err", err, "time", time.Since(startTime), ) if !g.stopFrom(i) { g.state.Store(groupStateInvalid) } else { g.state.Store(groupStateOff) } return fmt.Errorf("failed to start %s: %w", m.mgr.name, err) } m.mgr.Info("started", "time", time.Since(startTime)) } g.state.Store(groupStateRunning) return nil } // IsStopped returns whether the group is stopped. // It returns an error if the group is in an invalid state. func (g *Group) IsStopped() (bool, error) { state := g.state.Load() if state == groupStateInvalid { return false, errors.New("invalid group state") } return state == groupStateOff, nil } // Stop stops all modules in the group in the reverse order. func (g *Group) Stop() error { // Check group state. switch g.state.Load() { case groupStateOff: // Already stopped. return nil case groupStateInvalid: // Something went terribly wrong, cannot recover from here. return fmt.Errorf("%w: cannot recover", ErrInvalidGroupState) default: if !g.state.CompareAndSwap(groupStateRunning, groupStateStopping) { return fmt.Errorf("%w: group is not running, state: %s", ErrUnsuitableGroupState, groupStateToString(g.state.Load())) } } // Stop modules. if !g.stopFrom(len(g.modules) - 1) { g.state.Store(groupStateInvalid) return errors.New("failed to stop") } g.state.Store(groupStateOff) return nil } func (g *Group) stopFrom(index int) (ok bool) { ok = true // Stop modules. for i := index; i >= 0; i-- { m := g.modules[i] m.mgr.Debug("stopping") startTime := time.Now() err := m.mgr.Do("stop module "+m.mgr.name, func(_ *WorkerCtx) error { return m.module.Stop() }) if err != nil { m.mgr.Error( "failed to stop", "err", err, "time", time.Since(startTime), ) ok = false } m.mgr.Cancel() var waitSucceeded bool if m.mgr.hasStopWorker() { waitSucceeded = m.mgr.WaitForWorkersFromStop(0) } else { waitSucceeded = m.mgr.WaitForWorkers(0) } if waitSucceeded { m.mgr.Info("stopped", "time", time.Since(startTime)) } else { ok = false m.mgr.Error( "failed to stop", "err", "timed out", "workerCnt", m.mgr.workerCnt.Load(), "time", time.Since(startTime), ) } } // Reset modules. if !ok { // Stopping failed somewhere, reset anyway after a short wait. // This will be very uncommon and can help to mitigate race conditions in these events. time.Sleep(time.Second) } for _, m := range g.modules { m.mgr.Reset() } return ok } // Ready returns whether all modules in the group have been started and are still running. func (g *Group) Ready() bool { return g.state.Load() == groupStateRunning } // GetStates returns the current states of all group modules. func (g *Group) GetStates() []StateUpdate { updates := make([]StateUpdate, 0, len(g.modules)) for _, gm := range g.modules { if stateful, ok := gm.module.(StatefulModule); ok { updates = append(updates, stateful.States().Export()) } } return updates } // AddStatesCallback adds the given callback function to all group modules that // expose a state manager at States(). func (g *Group) AddStatesCallback(callbackName string, callback EventCallbackFunc[StateUpdate]) { for _, gm := range g.modules { if stateful, ok := gm.module.(StatefulModule); ok { stateful.States().AddCallback(callbackName, callback) } } } // Modules returns a copy of the modules. func (g *Group) Modules() []Module { copied := make([]Module, 0, len(g.modules)) for _, gm := range g.modules { copied = append(copied, gm.module) } return copied } // RunModules is a simple wrapper function to start modules and stop them again // when the given context is canceled. func RunModules(ctx context.Context, modules ...Module) error { g := NewGroup(modules...) // Start module. if err := g.Start(); err != nil { return fmt.Errorf("failed to start: %w", err) } // Stop module when context is canceled. <-ctx.Done() return g.Stop() } func makeModuleName(m Module) string { return strings.TrimPrefix(fmt.Sprintf("%T", m), "*") } ================================================ FILE: service/mgr/group_ext.go ================================================ package mgr import ( "context" "errors" "sync" "time" ) // ExtendedGroup extends the group with additional helpful functionality. type ExtendedGroup struct { *Group ensureCtx context.Context ensureCancel context.CancelFunc ensureLock sync.Mutex } // NewExtendedGroup returns a new extended group. func NewExtendedGroup(modules ...Module) *ExtendedGroup { return UpgradeGroup(NewGroup(modules...)) } // UpgradeGroup upgrades a regular group to an extended group. func UpgradeGroup(g *Group) *ExtendedGroup { return &ExtendedGroup{ Group: g, ensureCancel: func() {}, } } // EnsureStartedWorker tries to start the group until it succeeds or fails permanently. func (eg *ExtendedGroup) EnsureStartedWorker(wCtx *WorkerCtx) error { // Setup worker. var ctx context.Context func() { eg.ensureLock.Lock() defer eg.ensureLock.Unlock() eg.ensureCancel() eg.ensureCtx, eg.ensureCancel = context.WithCancel(wCtx.Ctx()) ctx = eg.ensureCtx }() for { err := eg.Group.Start() switch { case err == nil: return nil case errors.Is(err, ErrInvalidGroupState): wCtx.Debug("group start delayed", "err", err) default: return err } select { case <-ctx.Done(): return nil case <-time.After(1 * time.Second): } } } // EnsureStoppedWorker tries to stop the group until it succeeds or fails permanently. func (eg *ExtendedGroup) EnsureStoppedWorker(wCtx *WorkerCtx) error { // Setup worker. var ctx context.Context func() { eg.ensureLock.Lock() defer eg.ensureLock.Unlock() eg.ensureCancel() eg.ensureCtx, eg.ensureCancel = context.WithCancel(wCtx.Ctx()) ctx = eg.ensureCtx }() for { err := eg.Group.Stop() switch { case err == nil: return nil case errors.Is(err, ErrInvalidGroupState): wCtx.Debug("group stop delayed", "err", err) default: return err } select { case <-ctx.Done(): return nil case <-time.After(1 * time.Second): } } } ================================================ FILE: service/mgr/group_module.go ================================================ package mgr // GroupModule is a module that wraps a group of modules, // to allow nesting groups of modules in parent group. type GroupModule struct { mgr *Manager group *Group } func NewGroupModule(name string, modules ...Module) *GroupModule { return &GroupModule{ mgr: New(name), group: NewGroup(modules...), } } func (gm *GroupModule) Manager() *Manager { return gm.mgr } func (gm *GroupModule) Start() error { return gm.group.Start() } func (gm *GroupModule) Stop() error { return gm.group.Stop() } // Modules returns the modules in the group wrapped by this group module. // (mimics Group.Modules()) func (gm *GroupModule) Modules() []Module { return gm.group.Modules() } ================================================ FILE: service/mgr/manager.go ================================================ package mgr import ( "context" "log/slog" "runtime" "sync" "sync/atomic" "time" ) // ManagerNameSLogKey is used as the logging key for the name of the manager. var ManagerNameSLogKey = "manager" // Manager manages workers. type Manager struct { name string logger *slog.Logger ctx context.Context cancelCtx context.CancelFunc workerCnt atomic.Int32 workersDone chan struct{} workers []*WorkerCtx workersIndex int workersLock sync.Mutex } // New returns a new manager. func New(name string) *Manager { return newManager(name) } func newManager(name string) *Manager { m := &Manager{ name: name, logger: slog.Default().With(ManagerNameSLogKey, name), workersDone: make(chan struct{}), workers: make([]*WorkerCtx, 4), } m.ctx, m.cancelCtx = context.WithCancel(context.Background()) return m } // Name returns the manager name. func (m *Manager) Name() string { return m.name } // setName sets the manager name and resets the logger to use that name. // Not safe for concurrent use with any other module methods. func (m *Manager) setName(newName string) { m.name = newName m.logger = slog.Default().With(ManagerNameSLogKey, m.name) } // Ctx returns the worker context. func (m *Manager) Ctx() context.Context { return m.ctx } // Cancel cancels the worker context. func (m *Manager) Cancel() { m.cancelCtx() } // Done returns the context Done channel. func (m *Manager) Done() <-chan struct{} { return m.ctx.Done() } // IsDone checks whether the manager context is done. func (m *Manager) IsDone() bool { return m.ctx.Err() != nil } // LogEnabled reports whether the logger emits log records at the given level. // The manager context is automatically supplied. func (m *Manager) LogEnabled(level slog.Level) bool { return m.logger.Enabled(m.ctx, level) } // Debug logs at LevelDebug. // The manager context is automatically supplied. func (m *Manager) Debug(msg string, args ...any) { if !m.logger.Enabled(m.ctx, slog.LevelDebug) { return } m.writeLog(slog.LevelDebug, msg, args...) } // Info logs at LevelInfo. // The manager context is automatically supplied. func (m *Manager) Info(msg string, args ...any) { if !m.logger.Enabled(m.ctx, slog.LevelInfo) { return } m.writeLog(slog.LevelInfo, msg, args...) } // Warn logs at LevelWarn. // The manager context is automatically supplied. func (m *Manager) Warn(msg string, args ...any) { if !m.logger.Enabled(m.ctx, slog.LevelWarn) { return } m.writeLog(slog.LevelWarn, msg, args...) } // Error logs at LevelError. // The manager context is automatically supplied. func (m *Manager) Error(msg string, args ...any) { if !m.logger.Enabled(m.ctx, slog.LevelError) { return } m.writeLog(slog.LevelError, msg, args...) } // Log emits a log record with the current time and the given level and message. // The manager context is automatically supplied. func (m *Manager) Log(level slog.Level, msg string, args ...any) { if !m.logger.Enabled(m.ctx, level) { return } m.writeLog(level, msg, args...) } // LogAttrs is a more efficient version of Log() that accepts only Attrs. // The manager context is automatically supplied. func (m *Manager) LogAttrs(level slog.Level, msg string, attrs ...slog.Attr) { if !m.logger.Enabled(m.ctx, level) { return } var pcs [1]uintptr runtime.Callers(2, pcs[:]) // skip "Callers" and "LogAttrs". r := slog.NewRecord(time.Now(), level, msg, pcs[0]) r.AddAttrs(attrs...) _ = m.logger.Handler().Handle(m.ctx, r) } func (m *Manager) writeLog(level slog.Level, msg string, args ...any) { var pcs [1]uintptr runtime.Callers(3, pcs[:]) // skip "Callers", "writeLog" and the calling function. r := slog.NewRecord(time.Now(), level, msg, pcs[0]) r.Add(args...) _ = m.logger.Handler().Handle(m.ctx, r) } // WaitForWorkers waits for all workers of this manager to be done. // The default maximum waiting time is one minute. func (m *Manager) WaitForWorkers(max time.Duration) (done bool) { return m.waitForWorkers(max, 0) } // WaitForWorkersFromStop is a special version of WaitForWorkers, meant to be called from the stop routine. // It waits for all workers of this manager to be done, except for the Stop function. // The default maximum waiting time is one minute. func (m *Manager) WaitForWorkersFromStop(max time.Duration) (done bool) { return m.waitForWorkers(max, 1) } func (m *Manager) waitForWorkers(max time.Duration, limit int32) (done bool) { // Return immediately if there are no workers. if m.workerCnt.Load() <= limit { return true } // Setup timers. reCheckDuration := 10 * time.Millisecond if max <= 0 { max = time.Minute } reCheck := time.NewTimer(reCheckDuration) maxWait := time.NewTimer(max) defer reCheck.Stop() defer maxWait.Stop() // Wait for workers to finish, plus check the count in intervals. for { if m.workerCnt.Load() <= limit { return true } select { case <-m.workersDone: if m.workerCnt.Load() <= limit { return true } case <-reCheck.C: // Check worker count again. // This is a dead simple and effective way to avoid all the channel race conditions. reCheckDuration *= 2 reCheck.Reset(reCheckDuration) case <-maxWait.C: return m.workerCnt.Load() <= limit } } } func (m *Manager) workerStart(w *WorkerCtx) { m.registerWorker(w) m.workerCnt.Add(1) } func (m *Manager) workerDone(w *WorkerCtx) { m.unregisterWorker(w) if m.workerCnt.Add(-1) <= 1 { // Notify all waiters. for { select { case m.workersDone <- struct{}{}: default: return } } } } // Reset resets the manager in order to be able to be used again. // In the process, the current context is canceled. // As part of a module (in a group), the module might be stopped and started again. // This method is not goroutine-safe. The caller must make sure the manager is // not being used in any way during execution. func (m *Manager) Reset() { m.cancelCtx() m.ctx, m.cancelCtx = context.WithCancel(context.Background()) m.workerCnt.Store(0) m.workersDone = make(chan struct{}) } ================================================ FILE: service/mgr/sleepyticker.go ================================================ package mgr import "time" // SleepyTicker is wrapper over time.Ticker that respects the sleep mode of the module. type SleepyTicker struct { ticker *time.Ticker normalDuration time.Duration sleepDuration time.Duration sleepMode bool sleepChannel chan time.Time } // NewSleepyTicker returns a new SleepyTicker. This is a wrapper of the standard time.Ticker but it respects modules.Module sleep mode. Check https://pkg.go.dev/time#Ticker. // If sleepDuration is set to 0 ticker will not tick during sleep. func NewSleepyTicker(normalDuration time.Duration, sleepDuration time.Duration) *SleepyTicker { st := &SleepyTicker{ ticker: time.NewTicker(normalDuration), normalDuration: normalDuration, sleepDuration: sleepDuration, sleepMode: false, } return st } // Wait waits until the module is not in sleep mode and returns time.Ticker.C channel. func (st *SleepyTicker) Wait() <-chan time.Time { if st.sleepMode && st.sleepDuration == 0 { return st.sleepChannel } return st.ticker.C } // Stop turns off a ticker. After Stop, no more ticks will be sent. Stop does not close the channel, to prevent a concurrent goroutine reading from the channel from seeing an erroneous "tick". func (st *SleepyTicker) Stop() { st.ticker.Stop() } // SetSleep sets the sleep mode of the ticker. If enabled is true, the ticker will tick with sleepDuration. If enabled is false, the ticker will tick with normalDuration. func (st *SleepyTicker) SetSleep(enabled bool) { st.sleepMode = enabled if enabled { if st.sleepDuration > 0 { st.ticker.Reset(st.sleepDuration) } else { // Next call to Wait will wait until SetSleep is called with enabled == false st.sleepChannel = make(chan time.Time) } } else { st.ticker.Reset(st.normalDuration) if st.sleepDuration > 0 { // Notify that we are not sleeping anymore. close(st.sleepChannel) } } } ================================================ FILE: service/mgr/sleepyticker_test.go ================================================ package mgr import ( "testing" "time" ) func TestSleepyTickerStop(t *testing.T) { normalDuration := 100 * time.Millisecond sleepDuration := 200 * time.Millisecond st := NewSleepyTicker(normalDuration, sleepDuration) st.Stop() // no panic expected here } func TestSleepyTicker(t *testing.T) { normalDuration := 100 * time.Millisecond sleepDuration := 200 * time.Millisecond st := NewSleepyTicker(normalDuration, sleepDuration) // Test normal mode select { case <-st.Wait(): // Expected tick case <-time.After(normalDuration + 50*time.Millisecond): t.Error("expected tick in normal mode") } // Test sleep mode st.SetSleep(true) select { case <-st.Wait(): // Expected tick case <-time.After(sleepDuration + 50*time.Millisecond): t.Error("expected tick in sleep mode") } // Test sleep mode with sleepDuration == 0 st = NewSleepyTicker(normalDuration, 0) st.SetSleep(true) select { case <-st.Wait(): t.Error("did not expect tick when sleepDuration is 0") case <-time.After(normalDuration): // Expected no tick } // Test stopping the ticker st.Stop() select { case <-st.Wait(): t.Error("did not expect tick after stopping the ticker") case <-time.After(normalDuration): // Expected no tick } } ================================================ FILE: service/mgr/states.go ================================================ package mgr import ( "slices" "sync" "time" ) // StateMgr is a simple state manager. type StateMgr struct { states []State statesLock sync.Mutex statesEventMgr *EventMgr[StateUpdate] mgr *Manager } // State describes the state of a manager or module. type State struct { // ID is a program-unique ID. // It must not only be unique within the StateMgr, but for the whole program, // as it may be re-used with related systems. // Required. ID string // Name is the name of the state. // This may also serve as a notification title. // Required. Name string // Message is a more detailed message about the state. // Optional. Message string // Type defines the type of the state. // Optional. Type StateType // Time is the time when the state was created or the originating incident occurred. // Optional, will be set to current time if not set. Time time.Time // Data can hold any additional data necessary for further processing of connected systems. // Optional. Data any } // StateType defines commonly used states. type StateType string // State Types. const ( StateTypeUndefined = "" StateTypeHint = "hint" StateTypeWarning = "warning" StateTypeError = "error" ) // Severity returns a number representing the gravity of the state for ordering. func (st StateType) Severity() int { switch st { case StateTypeUndefined: return 0 case StateTypeHint: return 1 case StateTypeWarning: return 2 case StateTypeError: return 3 default: return 0 } } // StateUpdate is used to update others about a state change. type StateUpdate struct { Module string States []State } // StatefulModule is used for interface checks on modules. type StatefulModule interface { States() *StateMgr } // NewStateMgr returns a new state manager. func NewStateMgr(mgr *Manager) *StateMgr { return &StateMgr{ statesEventMgr: NewEventMgr[StateUpdate]("state update", mgr), mgr: mgr, } } // NewStateMgr returns a new state manager. func (m *Manager) NewStateMgr() *StateMgr { return NewStateMgr(m) } // Add adds a state. // If a state with the same ID already exists, it is replaced. func (m *StateMgr) Add(s State) { m.statesLock.Lock() defer m.statesLock.Unlock() if s.Time.IsZero() { s.Time = time.Now() } // Update or add state. index := slices.IndexFunc(m.states, func(es State) bool { return es.ID == s.ID }) if index >= 0 { m.states[index] = s } else { m.states = append(m.states, s) } m.statesEventMgr.Submit(m.export()) } // Remove removes the state with the given ID. func (m *StateMgr) Remove(id string) { m.statesLock.Lock() defer m.statesLock.Unlock() // Quick check if slice is empty. // It is a common pattern to remove a state when no error was encountered at // a critical operation. This means that StateMgr.Remove will be called often. if len(m.states) == 0 { return } var entryRemoved bool m.states = slices.DeleteFunc(m.states, func(s State) bool { if s.ID == id { entryRemoved = true return true } return false }) if entryRemoved { m.statesEventMgr.Submit(m.export()) } } // Clear removes all states. func (m *StateMgr) Clear() { m.statesLock.Lock() m.states = nil m.statesLock.Unlock() // Submit event without lock, because callbacks might come back to change states. defer m.statesEventMgr.Submit(m.Export()) } // Export returns the current states. func (m *StateMgr) Export() StateUpdate { m.statesLock.Lock() defer m.statesLock.Unlock() return m.export() } // export returns the current states. func (m *StateMgr) export() StateUpdate { name := "" if m.mgr != nil { name = m.mgr.name } return StateUpdate{ Module: name, States: slices.Clone(m.states), } } // Subscribe subscribes to state update events. func (m *StateMgr) Subscribe(subscriberName string, chanSize int) *EventSubscription[StateUpdate] { return m.statesEventMgr.Subscribe(subscriberName, chanSize) } // AddCallback adds a callback to state update events. func (m *StateMgr) AddCallback(callbackName string, callback EventCallbackFunc[StateUpdate]) { m.statesEventMgr.AddCallback(callbackName, callback) } ================================================ FILE: service/mgr/worker.go ================================================ package mgr import ( "context" "errors" "fmt" "log/slog" "runtime" "runtime/debug" "strings" "time" "github.com/safing/portmaster/base/log" ) // workerContextKey is a key used for the context key/value storage. type workerContextKey struct{} // WorkerCtxContextKey is the key used to add the WorkerCtx to a context. var WorkerCtxContextKey = workerContextKey{} // WorkerCtx provides workers with the necessary environment for flow control // and logging. type WorkerCtx struct { name string workFunc func(w *WorkerCtx) error ctx context.Context cancelCtx context.CancelFunc workerMgr *WorkerMgr // TODO: Attach to context instead? logger *slog.Logger // isStopWorker indicates whether this worker is responsible for stopping // the manager. When true, the manager will not wait for this worker to // finish during stop, preventing deadlocks where the stop worker // would wait for itself to complete. isStopWorker bool } // AddToCtx adds the WorkerCtx to the given context. func (w *WorkerCtx) AddToCtx(ctx context.Context) context.Context { return context.WithValue(ctx, WorkerCtxContextKey, w) } // WorkerFromCtx returns the WorkerCtx from the given context. func WorkerFromCtx(ctx context.Context) *WorkerCtx { v := ctx.Value(WorkerCtxContextKey) if w, ok := v.(*WorkerCtx); ok { return w } return nil } // Ctx returns the worker context. // Is automatically canceled after the worker stops/returns, regardless of error. func (w *WorkerCtx) Ctx() context.Context { return w.ctx } // Cancel cancels the worker context. // Is automatically called after the worker stops/returns, regardless of error. func (w *WorkerCtx) Cancel() { w.cancelCtx() } // WorkerMgr returns the worker manager the worker was started from. // Returns nil if the worker is not associated with a scheduler. func (w *WorkerCtx) WorkerMgr() *WorkerMgr { return w.workerMgr } // Done returns the context Done channel. func (w *WorkerCtx) Done() <-chan struct{} { return w.ctx.Done() } // IsDone checks whether the worker context is done. func (w *WorkerCtx) IsDone() bool { return w.ctx.Err() != nil } // Logger returns the logger used by the worker context. func (w *WorkerCtx) Logger() *slog.Logger { return w.logger } // LogEnabled reports whether the logger emits log records at the given level. // The worker context is automatically supplied. func (w *WorkerCtx) LogEnabled(level slog.Level) bool { return w.logger.Enabled(w.ctx, level) } // Debug logs at LevelDebug. // The worker context is automatically supplied. func (w *WorkerCtx) Debug(msg string, args ...any) { if !w.logger.Enabled(w.ctx, slog.LevelDebug) { return } w.writeLog(slog.LevelDebug, msg, args...) } // Info logs at LevelInfo. // The worker context is automatically supplied. func (w *WorkerCtx) Info(msg string, args ...any) { if !w.logger.Enabled(w.ctx, slog.LevelInfo) { return } w.writeLog(slog.LevelInfo, msg, args...) } // Warn logs at LevelWarn. // The worker context is automatically supplied. func (w *WorkerCtx) Warn(msg string, args ...any) { if !w.logger.Enabled(w.ctx, slog.LevelWarn) { return } w.writeLog(slog.LevelWarn, msg, args...) } // Error logs at LevelError. // The worker context is automatically supplied. func (w *WorkerCtx) Error(msg string, args ...any) { if !w.logger.Enabled(w.ctx, slog.LevelError) { return } w.writeLog(slog.LevelError, msg, args...) } // Log emits a log record with the current time and the given level and message. // The worker context is automatically supplied. func (w *WorkerCtx) Log(level slog.Level, msg string, args ...any) { if !w.logger.Enabled(w.ctx, level) { return } w.writeLog(level, msg, args...) } // LogAttrs is a more efficient version of Log() that accepts only Attrs. // The worker context is automatically supplied. func (w *WorkerCtx) LogAttrs(level slog.Level, msg string, attrs ...slog.Attr) { if !w.logger.Enabled(w.ctx, level) { return } var pcs [1]uintptr runtime.Callers(2, pcs[:]) // skip "Callers" and "LogAttrs". r := slog.NewRecord(time.Now(), level, msg, pcs[0]) r.AddAttrs(attrs...) _ = w.logger.Handler().Handle(w.ctx, r) } func (w *WorkerCtx) writeLog(level slog.Level, msg string, args ...any) { var pcs [1]uintptr runtime.Callers(3, pcs[:]) // skip "Callers", "writeLog" and the calling function. r := slog.NewRecord(time.Now(), level, msg, pcs[0]) r.Add(args...) _ = w.logger.Handler().Handle(w.ctx, r) } // Go starts the given function in a goroutine (as a "worker"). // The worker context has // - A separate context which is canceled when the functions returns. // - Access to named structure logging. // - Given function is re-run after failure (with backoff). // - Panic catching. // - Flow control helpers. func (m *Manager) Go(name string, fn func(w *WorkerCtx) error) { // m.logger.Log(m.ctx, slog.LevelInfo, "worker started", "name", name) go m.manageWorker(name, fn) } func (m *Manager) manageWorker(name string, fn func(w *WorkerCtx) error) { w := &WorkerCtx{ name: name, workFunc: fn, logger: m.logger.With("worker", name), } w.ctx = m.ctx m.workerStart(w) defer m.workerDone(w) backoff := time.Second failCnt := 0 for { panicInfo, err := m.runWorker(w, fn) switch { case err == nil: // No error means that the worker is finished. return case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded): // A canceled context or exceeded deadline also means that the worker is finished. return default: // Any other errors triggers a restart with backoff. // If manager is stopping, just log error and return. if m.IsDone() { if panicInfo != "" { w.Error( "worker failed", "err", err, "file", panicInfo, ) } else { w.Error( "worker failed", "err", err, ) } return } // Count failure and increase backoff (up to limit), failCnt++ backoff *= 2 if backoff > time.Minute { backoff = time.Minute } // Log error and retry after backoff duration. if panicInfo != "" { w.Error( "worker failed", "failCnt", failCnt, "backoff", backoff, "err", err, "file", panicInfo, ) } else { w.Error( "worker failed", "failCnt", failCnt, "backoff", backoff, "err", err, ) } select { case <-time.After(backoff): case <-m.ctx.Done(): return } } } } // Do directly executes the given function (as a "worker"). // The worker context has // - A separate context which is canceled when the functions returns. // - Access to named structure logging. // - Given function is re-run after failure (with backoff). // - Panic catching. // - Flow control helpers. func (m *Manager) Do(name string, fn func(w *WorkerCtx) error) error { return m.do(name, false, fn) } // DoAsStopWorker is like Do(...), but marks the worker as a stop worker. // This means that the manager will not wait for this worker to finish when stopping. // Only one stop worker can be started at a time. func (m *Manager) DoAsStopWorker(name string, fn func(w *WorkerCtx) error) error { if m.hasStopWorker() { return fmt.Errorf("cannot start stop worker %q: already has a stop worker", name) } return m.do(name, true, fn) } func (m *Manager) do(name string, isStopWorker bool, fn func(w *WorkerCtx) error) error { // Create context. w := &WorkerCtx{ name: name, workFunc: fn, ctx: m.Ctx(), logger: m.logger.With("worker", name), isStopWorker: isStopWorker, } m.workerStart(w) defer m.workerDone(w) // Run worker. panicInfo, err := m.runWorker(w, fn) switch { case err == nil: // No error means that the worker is finished. return nil case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded): // A canceled context or exceeded deadline also means that the worker is finished. return err default: // Log error and return. if panicInfo != "" { w.Error( "worker failed", "err", err, "file", panicInfo, ) } else { w.Error( "worker failed", "err", err, ) } return err } } func (m *Manager) runWorker(w *WorkerCtx, fn func(w *WorkerCtx) error) (panicInfo string, err error) { // Create worker context that is canceled when worker finished or dies. w.ctx, w.cancelCtx = context.WithCancel(w.ctx) defer w.Cancel() // Recover from panic. defer func() { panicVal := recover() if panicVal != nil { err = fmt.Errorf("panic: %s", panicVal) // Print panic to stderr. stackTrace := string(debug.Stack()) fmt.Fprintf( log.GlobalWriter, "===== PANIC =====\n%s\n\n%s===== END =====\n", panicVal, stackTrace, ) // Find the line in the stack trace that refers to where the panic occurred. stackLines := strings.Split(stackTrace, "\n") foundPanic := false for i, line := range stackLines { if !foundPanic { if strings.Contains(line, "panic(") { foundPanic = true } } else { if strings.Contains(line, "portmaster") { if i+1 < len(stackLines) { panicInfo = strings.SplitN(strings.TrimSpace(stackLines[i+1]), " ", 2)[0] } break } } } } }() err = fn(w) return //nolint } // Repeat executes the given function periodically in a goroutine (as a "worker"). // The worker context has // - A separate context which is canceled when the functions returns. // - Access to named structure logging. // - By default error/panic will be logged. For custom behavior supply errorFn, the argument is optional. // - Flow control helpers. // - Repeat is intended for long running tasks that are mostly idle. func (m *Manager) Repeat(name string, period time.Duration, fn func(w *WorkerCtx) error) *WorkerMgr { t := m.NewWorkerMgr(name, fn, nil) return t.Repeat(period) } // Delay starts the given function delayed in a goroutine (as a "worker"). // The worker context has // - A separate context which is canceled when the functions returns. // - Access to named structure logging. // - By default error/panic will be logged. For custom behavior supply errorFn, the argument is optional. // - Panic catching. // - Flow control helpers. func (m *Manager) Delay(name string, period time.Duration, fn func(w *WorkerCtx) error) *WorkerMgr { t := m.NewWorkerMgr(name, fn, nil) return t.Delay(period) } ================================================ FILE: service/mgr/worker_info.go ================================================ package mgr import ( "bytes" "errors" "fmt" "io" "reflect" "runtime" "slices" "strconv" "strings" "text/tabwriter" "github.com/maruel/panicparse/v2/stack" ) // WorkerInfoModule is used for interface checks on modules. type WorkerInfoModule interface { WorkerInfo(s *stack.Snapshot) (*WorkerInfo, error) } func (m *Manager) registerWorker(w *WorkerCtx) { m.workersLock.Lock() defer m.workersLock.Unlock() // Iterate forwards over the ring buffer. end := (m.workersIndex - 1 + len(m.workers)) % len(m.workers) for { // Check if entry is available. if m.workers[m.workersIndex] == nil { m.workers[m.workersIndex] = w return } // Check if we checked the whole ring buffer. if m.workersIndex == end { break } // Go to next index. m.workersIndex = (m.workersIndex + 1) % len(m.workers) } // Increase ring buffer. newRingBuf := make([]*WorkerCtx, len(m.workers)*4) copy(newRingBuf, m.workers) // Add new entry. m.workersIndex = len(m.workers) newRingBuf[m.workersIndex] = w m.workersIndex++ // Switch to new ring buffer. m.workers = newRingBuf } func (m *Manager) unregisterWorker(w *WorkerCtx) { m.workersLock.Lock() defer m.workersLock.Unlock() // Iterate backwards over the ring buffer. i := m.workersIndex end := (i + 1) % len(m.workers) for { // Check if entry is the one we want to remove. if m.workers[i] == w { m.workers[i] = nil return } // Check if we checked the whole ring buffer. if i == end { break } // Go to next index. i = (i - 1 + len(m.workers)) % len(m.workers) } } func (m *Manager) hasStopWorker() bool { m.workersLock.Lock() defer m.workersLock.Unlock() for _, w := range m.workers { if w == nil { continue } if w.isStopWorker { return true } } return false } // WorkerInfo holds status information about a managers workers. type WorkerInfo struct { Running int Waiting int Other int Missing int Workers []*WorkerInfoDetail } // WorkerInfoDetail holds status information about a single worker. type WorkerInfoDetail struct { Count int State string Mgr string Name string Func string CurrentLine string ExtraInfo string } // WorkerInfo returns status information for all running workers of this manager. func (m *Manager) WorkerInfo(s *stack.Snapshot) (*WorkerInfo, error) { m.workersLock.Lock() defer m.workersLock.Unlock() var err error if s == nil { s, _, err = stack.ScanSnapshot(bytes.NewReader(fullStack()), io.Discard, stack.DefaultOpts()) if err != nil && !errors.Is(err, io.EOF) { return nil, fmt.Errorf("get stack: %w", err) } } wi := &WorkerInfo{ Workers: make([]*WorkerInfoDetail, 0, len(m.workers)), } // Go through all registered workers of manager. for _, w := range m.workers { // Ignore empty slots. if w == nil { continue } // Setup worker detail struct. wd := &WorkerInfoDetail{ Count: 1, Mgr: m.name, } if w.workerMgr != nil { wd.Name = w.workerMgr.name wd.Func = getFuncName(w.workerMgr.fn) } else { wd.Name = w.name wd.Func = getFuncName(w.workFunc) } // Search for stack of this worker. goroutines: for _, gr := range s.Goroutines { for _, call := range gr.Stack.Calls { // Check if the can find the worker function in a call stack. fullFuncName := call.Func.ImportPath + "." + call.Func.Name if fullFuncName == wd.Func { wd.State = gr.State // Find most useful line for where the goroutine currently is at. // Cut import path prefix to domain/user, eg. github.com/safing importPathPrefix := call.ImportPath splitted := strings.SplitN(importPathPrefix, "/", 3) if len(splitted) == 3 { importPathPrefix = splitted[0] + "/" + splitted[1] + "/" } // Find "last" call within that import path prefix. for _, call = range gr.Stack.Calls { if strings.HasPrefix(call.ImportPath, importPathPrefix) { wd.CurrentLine = call.ImportPath + "/" + call.SrcName + ":" + strconv.Itoa(call.Line) break } } // Fall back to last call if no better line was found. if wd.CurrentLine == "" { wd.CurrentLine = gr.Stack.Calls[0].ImportPath + "/" + gr.Stack.Calls[0].SrcName + ":" + strconv.Itoa(gr.Stack.Calls[0].Line) } // Add some extra info in some cases. if wd.State == "sleep" { //nolint:goconst wd.ExtraInfo = gr.SleepString() } break goroutines } } } // Summarize and add to list. switch wd.State { case "idle", "runnable", "running", "syscall", "waiting", "dead", "enqueue", "copystack": wi.Running++ case "chan send", "chan receive", "select", "IO wait", "panicwait", "semacquire", "semarelease", "sleep", "sync.Mutex.Lock": wi.Waiting++ case "": if w.workerMgr != nil { wi.Waiting++ wd.State = "scheduled" wd.ExtraInfo = w.workerMgr.Status() } else { wi.Missing++ wd.State = "missing" } default: wi.Other++ } wi.Workers = append(wi.Workers, wd) } // Sort and return. wi.clean() return wi, nil } // Format formats the worker information as a readable table. func (wi *WorkerInfo) Format() string { buf := bytes.NewBuffer(nil) // Add summary. buf.WriteString(fmt.Sprintf( "%d Workers: %d running, %d waiting\n\n", len(wi.Workers), wi.Running, wi.Waiting, )) // Build table. tabWriter := tabwriter.NewWriter(buf, 4, 4, 3, ' ', 0) _, _ = fmt.Fprintf(tabWriter, "#\tState\tModule\tName\tWorker Func\tCurrent Line\tExtra Info\n") for _, wd := range wi.Workers { _, _ = fmt.Fprintf(tabWriter, "%d\t%s\t%s\t%s\t%s\t%s\t%s\n", wd.Count, wd.State, wd.Mgr, wd.Name, wd.Func, wd.CurrentLine, wd.ExtraInfo, ) } _ = tabWriter.Flush() return buf.String() } func getFuncName(fn func(w *WorkerCtx) error) string { name := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() return strings.TrimSuffix(name, "-fm") } func fullStack() []byte { buf := make([]byte, 8096) for { n := runtime.Stack(buf, true) if n < len(buf) { return buf[:n] } buf = make([]byte, 2*len(buf)) } } // MergeWorkerInfo merges multiple worker infos into one. func MergeWorkerInfo(infos ...*WorkerInfo) *WorkerInfo { // Calculate total registered workers. var totalWorkers int for _, status := range infos { totalWorkers += len(status.Workers) } // Merge all worker infos. wi := &WorkerInfo{ Workers: make([]*WorkerInfoDetail, 0, totalWorkers), } for _, info := range infos { wi.Running += info.Running wi.Waiting += info.Waiting wi.Other += info.Other wi.Missing += info.Missing wi.Workers = append(wi.Workers, info.Workers...) } // Sort and return. wi.clean() return wi } func (wi *WorkerInfo) clean() { // Check if there is anything to do. if len(wi.Workers) <= 1 { return } // Sort for deduplication. slices.SortFunc(wi.Workers, sortWorkerInfoDetail) // Count duplicate worker details. current := wi.Workers[0] for i := 1; i < len(wi.Workers); i++ { if workerDetailsAreEqual(current, wi.Workers[i]) { current.Count++ } else { current = wi.Workers[i] } } // Deduplicate worker details. wi.Workers = slices.CompactFunc(wi.Workers, workerDetailsAreEqual) // Sort for presentation. slices.SortFunc(wi.Workers, sortWorkerInfoDetailByCount) } // sortWorkerInfoDetail is a sort function to sort worker info details by their content. func sortWorkerInfoDetail(a, b *WorkerInfoDetail) int { switch { case a.State != b.State: return strings.Compare(a.State, b.State) case a.Mgr != b.Mgr: return strings.Compare(a.Mgr, b.Mgr) case a.Name != b.Name: return strings.Compare(a.Name, b.Name) case a.Func != b.Func: return strings.Compare(a.Func, b.Func) case a.CurrentLine != b.CurrentLine: return strings.Compare(a.CurrentLine, b.CurrentLine) case a.ExtraInfo != b.ExtraInfo: return strings.Compare(a.ExtraInfo, b.ExtraInfo) case a.Count != b.Count: return b.Count - a.Count default: return 0 } } // sortWorkerInfoDetailByCount is a sort function to sort worker info details by their count and then by content. func sortWorkerInfoDetailByCount(a, b *WorkerInfoDetail) int { stateA, stateB := goroutineStateOrder(a.State), goroutineStateOrder(b.State) switch { case stateA != stateB: return stateA - stateB case a.State != b.State: return strings.Compare(a.State, b.State) case a.Count != b.Count: return b.Count - a.Count case a.Mgr != b.Mgr: return strings.Compare(a.Mgr, b.Mgr) case a.Name != b.Name: return strings.Compare(a.Name, b.Name) case a.Func != b.Func: return strings.Compare(a.Func, b.Func) case a.CurrentLine != b.CurrentLine: return strings.Compare(a.CurrentLine, b.CurrentLine) case a.ExtraInfo != b.ExtraInfo: return strings.Compare(a.ExtraInfo, b.ExtraInfo) default: return 0 } } // workerDetailsAreEqual is a deduplication function for worker details. func workerDetailsAreEqual(a, b *WorkerInfoDetail) bool { switch { case a.State != b.State: return false case a.Mgr != b.Mgr: return false case a.Name != b.Name: return false case a.Func != b.Func: return false case a.CurrentLine != b.CurrentLine: return false case a.ExtraInfo != b.ExtraInfo: return false default: return true } } //nolint:goconst func goroutineStateOrder(state string) int { switch state { case "runnable", "running", "syscall": return 0 // Active. case "idle", "waiting", "dead", "enqueue", "copystack": return 1 // Active-ish. case "semacquire", "semarelease", "sleep", "panicwait", "sync.Mutex.Lock": return 2 // Bad (practice) blocking. case "chan send", "chan receive", "select": return 3 // Potentially bad (practice), but normal blocking. case "IO wait": return 4 // Normal blocking. case "scheduled": return 5 // Not running. case "missing", "": return 6 // Warning of undetected workers. default: return 9 } } ================================================ FILE: service/mgr/worker_test.go ================================================ package mgr import ( "fmt" "testing" "time" ) func TestWorkerInfo(t *testing.T) { //nolint:paralleltest mgr := New("test") mgr.Go("test func one", testFunc1) mgr.Go("test func two", testFunc2) mgr.Go("test func three", testFunc3) defer mgr.Cancel() time.Sleep(100 * time.Millisecond) info, err := mgr.WorkerInfo(nil) if err != nil { t.Fatal(err) } if info.Waiting != 3 { t.Errorf("expected three waiting workers") } fmt.Printf("%+v\n", info) } func testFunc1(ctx *WorkerCtx) error { select { case <-time.After(1 * time.Second): case <-ctx.Done(): } return nil } func testFunc2(ctx *WorkerCtx) error { select { case <-time.After(1 * time.Second): case <-ctx.Done(): } return nil } func testFunc3(ctx *WorkerCtx) error { select { case <-time.After(1 * time.Second): case <-ctx.Done(): } return nil } ================================================ FILE: service/mgr/workermgr.go ================================================ package mgr import ( "context" "errors" "sync" "time" ) // WorkerMgr schedules a worker. type WorkerMgr struct { mgr *Manager ctx *WorkerCtx // Definition. name string fn func(w *WorkerCtx) error errorFn func(c *WorkerCtx, err error, panicInfo string) // Manual trigger. run chan struct{} // Actions. actionLock sync.Mutex selectAction chan struct{} delay *workerMgrDelay repeat *workerMgrRepeat keepAlive *workerMgrNoop } type taskAction interface { Wait() <-chan time.Time Ack() } // Delay. type workerMgrDelay struct { s *WorkerMgr timer *time.Timer } func (s *WorkerMgr) newDelay(duration time.Duration) *workerMgrDelay { return &workerMgrDelay{ s: s, timer: time.NewTimer(duration), } } func (sd *workerMgrDelay) Wait() <-chan time.Time { return sd.timer.C } func (sd *workerMgrDelay) Ack() { sd.s.actionLock.Lock() defer sd.s.actionLock.Unlock() // Remove delay, as it can only fire once. sd.s.delay = nil // Reset repeat. sd.s.repeat.Reset() // Stop timer. sd.timer.Stop() } func (sd *workerMgrDelay) Stop() { if sd == nil { return } sd.timer.Stop() } // Repeat. type workerMgrRepeat struct { ticker *time.Ticker interval time.Duration } func (s *WorkerMgr) newRepeat(interval time.Duration) *workerMgrRepeat { return &workerMgrRepeat{ ticker: time.NewTicker(interval), interval: interval, } } func (sr *workerMgrRepeat) Wait() <-chan time.Time { return sr.ticker.C } func (sr *workerMgrRepeat) Ack() {} func (sr *workerMgrRepeat) Reset() { if sr == nil { return } sr.ticker.Reset(sr.interval) } func (sr *workerMgrRepeat) Stop() { if sr == nil { return } sr.ticker.Stop() } // Noop. type workerMgrNoop struct{} func (sn *workerMgrNoop) Wait() <-chan time.Time { return nil } func (sn *workerMgrNoop) Ack() {} // NewWorkerMgr creates a new scheduler for the given worker function. // Errors and panic will only be logged by default. // If custom behavior is required, supply an errorFn. // If all scheduling has ended, the scheduler will end itself, // including all related workers, except if keep-alive is enabled. func (m *Manager) NewWorkerMgr(name string, fn func(w *WorkerCtx) error, errorFn func(c *WorkerCtx, err error, panicInfo string)) *WorkerMgr { // Create task context. wCtx := &WorkerCtx{ logger: m.logger.With("worker", name), } wCtx.ctx, wCtx.cancelCtx = context.WithCancel(m.Ctx()) s := &WorkerMgr{ mgr: m, ctx: wCtx, name: name, fn: fn, errorFn: errorFn, run: make(chan struct{}, 1), selectAction: make(chan struct{}, 1), } wCtx.workerMgr = s go s.taskMgr() return s } func (s *WorkerMgr) taskMgr() { s.mgr.workerStart(s.ctx) defer s.mgr.workerDone(s.ctx) // If the task manager ends, end all descendants too. defer s.ctx.cancelCtx() // Timers and tickers. var ( action taskAction ) defer func() { s.delay.Stop() s.repeat.Stop() }() // Wait for the first action. select { case <-s.selectAction: case <-s.ctx.Done(): return } manage: for { // Select action. func() { s.actionLock.Lock() defer s.actionLock.Unlock() switch { case s.delay != nil: action = s.delay case s.repeat != nil: action = s.repeat case s.keepAlive != nil: action = s.keepAlive default: action = nil } }() if action == nil { return } // Wait for trigger or action. select { case <-action.Wait(): action.Ack() // Time-triggered execution. case <-s.run: // Manually triggered execution. case <-s.selectAction: // Re-select action. continue manage case <-s.ctx.Done(): // Abort! return } // Run worker. wCtx := &WorkerCtx{ workerMgr: s, logger: s.ctx.logger, } //nolint:fatcontext // Every run gets a new context. wCtx.ctx, wCtx.cancelCtx = context.WithCancel(s.ctx.ctx) panicInfo, err := s.mgr.runWorker(wCtx, s.fn) switch { case err == nil: // Continue with scheduling. case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded): // Worker was canceled, continue with scheduling. // A canceled context or exceeded deadline also means that the worker is finished. default: // Log error and return. if panicInfo != "" { wCtx.Error( "worker failed", "err", err, "file", panicInfo, ) } else { wCtx.Error( "worker failed", "err", err, ) } // Delegate error handling to the error function, otherwise just continue the scheduler. // The error handler can stop the scheduler if it wants to. if s.errorFn != nil { s.errorFn(s.ctx, err, panicInfo) } } } } // Status returns the current status of the worker manager. func (s *WorkerMgr) Status() string { s.actionLock.Lock() defer s.actionLock.Unlock() switch { case s.delay != nil: return "delayed" case s.repeat != nil: return "repeated every " + s.repeat.interval.String() case s.keepAlive != nil: return "on demand" default: return "created" } } // Go executes the worker immediately. // If the worker is currently being executed, // the next execution will commence afterwards. // Calling Go() implies KeepAlive() if nothing else was specified yet. func (s *WorkerMgr) Go() { s.actionLock.Lock() defer s.actionLock.Unlock() // Check if any action is already defined. switch { case s.delay != nil: case s.repeat != nil: case s.keepAlive != nil: default: s.keepAlive = &workerMgrNoop{} s.check() } // Reset repeat if set. s.repeat.Reset() // Stop delay if set. s.delay.Stop() s.delay = nil // Send run command select { case s.run <- struct{}{}: default: } } // Stop immediately stops the scheduler and all related workers. func (s *WorkerMgr) Stop() { s.ctx.cancelCtx() } // Delay will schedule the worker to run after the given duration. // If set, the repeat schedule will continue afterwards. // Disable the delay by passing 0. func (s *WorkerMgr) Delay(duration time.Duration) *WorkerMgr { s.actionLock.Lock() defer s.actionLock.Unlock() s.delay.Stop() s.delay = nil if duration > 0 { s.delay = s.newDelay(duration) } s.check() return s } // Repeat will repeatedly execute the worker using the given interval. // Disable repeating by passing 0. func (s *WorkerMgr) Repeat(interval time.Duration) *WorkerMgr { s.actionLock.Lock() defer s.actionLock.Unlock() s.repeat.Stop() s.repeat = nil if interval > 0 { s.repeat = s.newRepeat(interval) } s.check() return s } // KeepAlive instructs the scheduler to not self-destruct, // even if all scheduled work is complete. func (s *WorkerMgr) KeepAlive() *WorkerMgr { s.actionLock.Lock() defer s.actionLock.Unlock() s.keepAlive = &workerMgrNoop{} s.check() return s } func (s *WorkerMgr) check() { select { case s.selectAction <- struct{}{}: default: } } ================================================ FILE: service/mgr/workermgr_test.go ================================================ package mgr import ( "sync/atomic" "testing" "time" ) func TestWorkerMgrDelay(t *testing.T) { t.Parallel() m := New("DelayTest") value := atomic.Bool{} value.Store(false) // Create a task that will after 1 second. m.NewWorkerMgr("test", func(w *WorkerCtx) error { value.Store(true) return nil }, nil).Delay(1 * time.Second) // Check if value is set after 1 second and not before or after. iterations := 0 for !value.Load() { iterations++ time.Sleep(10 * time.Millisecond) } // 5% difference is acceptable since time.Sleep can't be perfect and it may very on different computers. if iterations < 95 || iterations > 105 { t.Errorf("WorkerMgr did not delay for a whole second it=%d", iterations) } } func TestWorkerMgrRepeat(t *testing.T) { t.Parallel() m := New("RepeatTest") value := atomic.Bool{} value.Store(false) // Create a task that should repeat every 100 milliseconds. m.NewWorkerMgr("test", func(w *WorkerCtx) error { value.Store(true) return nil }, nil).Repeat(100 * time.Millisecond) // Check 10 consecutive runs they should be delayed for around 100 milliseconds each. for range 10 { iterations := 0 for !value.Load() { iterations++ time.Sleep(10 * time.Millisecond) } // 10% difference is acceptable at this scale since time.Sleep can't be perfect and it may very on different computers. if iterations < 9 || iterations > 11 { t.Errorf("Worker was not delayed for a 100 milliseconds it=%d", iterations) return } // Reset value value.Store(false) } } func TestWorkerMgrDelayAndRepeat(t *testing.T) { //nolint:dupl t.Parallel() m := New("DelayAndRepeatTest") value := atomic.Bool{} value.Store(false) // Create a task that should delay for 1 second and then repeat every 100 milliseconds. m.NewWorkerMgr("test", func(w *WorkerCtx) error { value.Store(true) return nil }, nil).Delay(1 * time.Second).Repeat(100 * time.Millisecond) iterations := 0 for !value.Load() { iterations++ time.Sleep(10 * time.Millisecond) } // 5% difference is acceptable since time.Sleep can't be perfect and it may very on different computers. if iterations < 95 || iterations > 105 { t.Errorf("WorkerMgr did not delay for a whole second it=%d", iterations) } // Reset value value.Store(false) // Check 10 consecutive runs they should be delayed for around 100 milliseconds each. for range 10 { iterations = 0 for !value.Load() { iterations++ time.Sleep(10 * time.Millisecond) } // 10% difference is acceptable at this scale since time.Sleep can't be perfect and it may very on different computers. if iterations < 9 || iterations > 11 { t.Errorf("Worker was not delayed for a 100 milliseconds it=%d", iterations) return } // Reset value value.Store(false) } } func TestWorkerMgrRepeatAndDelay(t *testing.T) { //nolint:dupl t.Parallel() m := New("RepeatAndDelayTest") value := atomic.Bool{} value.Store(false) // Create a task that should delay for 1 second and then repeat every 100 milliseconds but with reverse command order. m.NewWorkerMgr("test", func(w *WorkerCtx) error { value.Store(true) return nil }, nil).Repeat(100 * time.Millisecond).Delay(1 * time.Second) iterations := 0 for !value.Load() { iterations++ time.Sleep(10 * time.Millisecond) } // 5% difference is acceptable since time.Sleep can't be perfect and it may very on different computers. if iterations < 95 || iterations > 105 { t.Errorf("WorkerMgr did not delay for a whole second it=%d", iterations) } // Reset value value.Store(false) // Check 10 consecutive runs they should be delayed for around 100 milliseconds each. for range 10 { iterations := 0 for !value.Load() { iterations++ time.Sleep(10 * time.Millisecond) } // 10% difference is acceptable at this scale since time.Sleep can't be perfect and it may very on different computers. if iterations < 9 || iterations > 11 { t.Errorf("Worker was not delayed for a 100 milliseconds it=%d", iterations) return } // Reset value value.Store(false) } } ================================================ FILE: service/nameserver/config.go ================================================ package nameserver import ( "flag" "runtime" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/core" ) // CfgDefaultNameserverAddressKey is the config key for the listen address.. const CfgDefaultNameserverAddressKey = "dns/listenAddress" var ( defaultNameserverAddress = "localhost:53" nameserverAddress string nameserverAddressConfig config.StringOption networkServiceMode config.BoolOption ) func init() { // On Windows, packets are redirected to the same interface. if runtime.GOOS == "windows" { defaultNameserverAddress = "0.0.0.0:53" } flag.StringVar( &nameserverAddress, "nameserver-address", defaultNameserverAddress, "set default nameserver address; configuration is stronger", ) } func registerConfig() error { err := config.Register(&config.Option{ Name: "Internal DNS Server Listen Address", Key: CfgDefaultNameserverAddressKey, Description: "Defines the IP address and port on which the internal DNS Server listens.", OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelDeveloper, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: nameserverAddress, ValidationRegex: "^(localhost|[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}|\\[[:0-9A-Fa-f]+\\]):[0-9]{1,5}$", RequiresRestart: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: 514, config.CategoryAnnotation: "Development", }, }) if err != nil { return err } nameserverAddressConfig = config.GetAsString(CfgDefaultNameserverAddressKey, nameserverAddress) networkServiceMode = config.Concurrent.GetAsBool(core.CfgNetworkServiceKey, false) return nil } ================================================ FILE: service/nameserver/conflict.go ================================================ package nameserver import ( "net" "os" processInfo "github.com/shirou/gopsutil/process" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/network/state" ) var commonResolverIPs = []net.IP{ net.IPv4zero, net.IPv4(127, 0, 0, 1), // default net.IPv4(127, 0, 0, 53), // some resolvers on Linux net.IPv6zero, net.IPv6loopback, } func findConflictingProcess(ip net.IP, port uint16) (conflictingProcess *processInfo.Process) { // Evaluate which IPs to check. var ipsToCheck []net.IP if ip.Equal(net.IPv4zero) || ip.Equal(net.IPv6zero) { ipsToCheck = commonResolverIPs } else { ipsToCheck = []net.IP{ip} } // Find the conflicting process. var err error for _, resolverIP := range ipsToCheck { conflictingProcess, err = getListeningProcess(resolverIP, port) switch { case err != nil: // Log the error and let the worker try again. log.Warningf("nameserver: failed to find conflicting service: %s", err) case conflictingProcess != nil: // Conflicting service found. return conflictingProcess } } return nil } func getListeningProcess(resolverIP net.IP, resolverPort uint16) (*processInfo.Process, error) { pid, _, err := state.Lookup(&packet.Info{ Inbound: true, Version: 0, // auto-detect Protocol: packet.UDP, Src: nil, // do not record direction SrcPort: 0, // do not record direction Dst: resolverIP, DstPort: resolverPort, }, true) if err != nil { // there may be nothing listening on :53 return nil, nil //nolint:nilerr // Treat lookup error as "not found". } // Ignore if it's us for some reason. if pid == os.Getpid() { return nil, nil } proc, err := processInfo.NewProcess(int32(pid)) if err != nil { // Process may have disappeared already. return nil, err } return proc, nil } ================================================ FILE: service/nameserver/failing.go ================================================ package nameserver import ( "sync" "time" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/resolver" ) type failingQuery struct { // Until specifies until when the query should be regarded as failing. Until time.Time // Keep specifies until when the failing status shall be kept. Keep time.Time // Times specifies how often this query failed. Times int // Err holds the error the query failed with. Err error } const ( failingDelay = 900 * time.Millisecond failingBaseDuration = 900 * time.Millisecond failingFactorDuration = 500 * time.Millisecond failingMaxDuration = 30 * time.Second failingKeepAddedDuration = 10 * time.Second ) var ( failingQueries = make(map[string]*failingQuery) failingQueriesLock sync.RWMutex failingQueriesNetworkChangedFlag = netenv.GetNetworkChangedFlag() ) func checkIfQueryIsFailing(q *resolver.Query) (failingUntil *time.Time, failingErr error) { // If the network changed, reset the failed queries. if failingQueriesNetworkChangedFlag.IsSet() { failingQueriesNetworkChangedFlag.Refresh() failingQueriesLock.Lock() defer failingQueriesLock.Unlock() // Compiler optimized map reset. for key := range failingQueries { delete(failingQueries, key) } return nil, nil } failingQueriesLock.RLock() defer failingQueriesLock.RUnlock() // Quickly return if map is empty. if len(failingQueries) == 0 { return nil, nil } // Check if query failed recently. failing, ok := failingQueries[q.ID()] if !ok { return nil, nil } // Check if failing query should still be regarded as failing. if time.Now().After(failing.Until) { return nil, nil } // Return failing error and until when it's valid. return &failing.Until, failing.Err } func addFailingQuery(q *resolver.Query, err error) { // Check if we were given an error. if err == nil { return } // Exclude reverse and mDNS queries, as they fail _often_ and are usually not // retried quickly. // if strings.HasSuffix(q.FQDN, ".in-addr.arpa.") || // strings.HasSuffix(q.FQDN, ".ip6.arpa.") || // strings.HasSuffix(q.FQDN, ".local.") { // return // } failingQueriesLock.Lock() defer failingQueriesLock.Unlock() failing, ok := failingQueries[q.ID()] if !ok { failing = &failingQuery{Err: err} failingQueries[q.ID()] = failing } // Calculate fail duration. // Initial fail duration will be at 900ms, perfect for a normal retry after 1s, // but not any earlier. failDuration := failingBaseDuration + time.Duration(failing.Times)*failingFactorDuration if failDuration > failingMaxDuration { failDuration = failingMaxDuration } // Update failing query. failing.Times++ failing.Until = time.Now().Add(failDuration) failing.Keep = failing.Until.Add(failingKeepAddedDuration) } func cleanFailingQueries(maxRemove, maxMiss int) { failingQueriesLock.Lock() defer failingQueriesLock.Unlock() now := time.Now() for key, failing := range failingQueries { if now.After(failing.Keep) { delete(failingQueries, key) maxRemove-- if maxRemove == 0 { return } } else { maxMiss-- if maxMiss == 0 { return } } } } ================================================ FILE: service/nameserver/metrics.go ================================================ package nameserver import ( "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/metrics" ) var ( requestsHistogram *metrics.Histogram totalHandledRequests *metrics.Counter ) func registerMetrics() (err error) { requestsHistogram, err = metrics.NewHistogram( "nameserver/request/duration/seconds", nil, &metrics.Options{ Permission: api.PermitUser, ExpertiseLevel: config.ExpertiseLevelExpert, }, ) if err != nil { return err } totalHandledRequests, err = metrics.NewCounter( "nameserver/request/total", nil, &metrics.Options{ InternalID: "handled_dns_requests", Permission: api.PermitUser, ExpertiseLevel: config.ExpertiseLevelExpert, Persist: true, }, ) if err != nil { return err } return nil } ================================================ FILE: service/nameserver/module.go ================================================ package nameserver import ( "errors" "fmt" "net" "os" "strconv" "sync" "sync/atomic" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/compat" "github.com/safing/portmaster/service/firewall" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" ) type NameServer struct { mgr *mgr.Manager instance instance states *mgr.StateMgr } func (ns *NameServer) Manager() *mgr.Manager { return ns.mgr } func (ns *NameServer) States() *mgr.StateMgr { return ns.states } func (ns *NameServer) Start() error { return start() } func (ns *NameServer) Stop() error { return stop() } var ( stopListeners bool stopListener1 func() error stopListener2 func() error stopListenersLock sync.Mutex eventIDConflictingService = "nameserver:conflicting-service" eventIDListenerFailed = "nameserver:listener-failed" ) func prep() error { return registerConfig() } func start() error { if err := registerMetrics(); err != nil { return err } // Get listen addresses. ip1, ip2, port, err := getListenAddresses(nameserverAddressConfig()) if err != nil { return fmt.Errorf("failed to parse nameserver listen address: %w", err) } // Tell the compat module where we are listening. compat.SetNameserverListenIP(ip1) // Get own hostname. hostname, err = os.Hostname() if err != nil { log.Warningf("nameserver: failed to get hostname: %s", err) } hostname += "." // Start listener(s). if ip2 == nil { // Start a single listener. startListener(ip1, port, true) // Set nameserver matcher in firewall to fast-track dns queries. if ip1.Equal(net.IPv4zero) || ip1.Equal(net.IPv6zero) { // Fast track dns queries destined for any of the local IPs. return firewall.SetNameserverIPMatcher(func(ip net.IP) bool { dstIsMe, err := netenv.IsMyIP(ip) if err != nil { log.Warningf("nameserver: failed to check if IP %s is local: %s", ip, err) } return dstIsMe }) } return firewall.SetNameserverIPMatcher(func(ip net.IP) bool { return ip.Equal(ip1) }) } // Dual listener. startListener(ip1, port, true) startListener(ip2, port, false) // Fast track dns queries destined for one of the listener IPs. return firewall.SetNameserverIPMatcher(func(ip net.IP) bool { return ip.Equal(ip1) || ip.Equal(ip2) }) } func startListener(ip net.IP, port uint16, first bool) { // Start DNS server as service worker. module.mgr.Go("dns resolver", func(ctx *mgr.WorkerCtx) error { // Create DNS server. dnsServer := &dns.Server{ Addr: net.JoinHostPort( ip.String(), strconv.Itoa(int(port)), ), Net: "udp", Handler: dns.HandlerFunc(handleRequestAsWorker), } // Register stop function. func() { stopListenersLock.Lock() defer stopListenersLock.Unlock() // Check if we should stop if stopListeners { _ = dnsServer.Shutdown() dnsServer = nil return } // Register stop function. if first { stopListener1 = dnsServer.Shutdown } else { stopListener2 = dnsServer.Shutdown } }() // Check if we should stop. if dnsServer == nil { return nil } // Resolve generic listener error, if primary listener. if first { module.states.Remove(eventIDListenerFailed) } // Start listening. log.Infof("nameserver: starting to listen on %s", dnsServer.Addr) err := dnsServer.ListenAndServe() if err != nil { // Stop worker without error if we are shutting down. if module.mgr.IsDone() { return nil } log.Warningf("nameserver: failed to listen on %s: %s", dnsServer.Addr, err) handleListenError(err, ip, port, first) } return err }) } func handleListenError(err error, ip net.IP, port uint16, primaryListener bool) { var n *notifications.Notification // Create suffix for secondary listener var secondaryEventIDSuffix string if !primaryListener { secondaryEventIDSuffix = "-secondary" } // Find a conflicting service. cfProcess := findConflictingProcess(ip, port) if cfProcess != nil { // Report the conflicting process. // Build conflicting process description. var cfDescription string cfName, err := cfProcess.Name() if err == nil && cfName != "" { cfDescription = cfName } cfExe, err := cfProcess.Exe() if err == nil && cfDescription != "" { if cfDescription != "" { cfDescription += " (" + cfExe + ")" } else { cfDescription = cfName } } // Notify user about conflicting service. n = notifications.Notify(¬ifications.Notification{ EventID: eventIDConflictingService + secondaryEventIDSuffix, Type: notifications.Error, Title: "Conflicting DNS Software", Message: "Restart Portmaster after you have deactivated or properly configured the conflicting software: " + cfDescription, ShowOnSystem: true, AvailableActions: []*notifications.Action{ { Text: "Open Docs", Type: notifications.ActionTypeOpenURL, Payload: "https://docs.safing.io/portmaster/install/status/software-compatibility", }, }, }) } else { // If no conflict is found, report the error directly. n = notifications.Notify(¬ifications.Notification{ EventID: eventIDListenerFailed + secondaryEventIDSuffix, Type: notifications.Error, Title: "Secure DNS Error", Message: fmt.Sprintf( "The internal DNS server failed. Restart Portmaster to try again. Error: %s", err, ), ShowOnSystem: true, }) } // Attach error to module, if primary listener. if primaryListener { n.SyncWithState(module.states) } } func stop() error { stopListenersLock.Lock() defer stopListenersLock.Unlock() // Stop listeners. stopListeners = true if stopListener1 != nil { if err := stopListener1(); err != nil { log.Warningf("nameserver: failed to stop listener1: %s", err) } } if stopListener2 != nil { if err := stopListener2(); err != nil { log.Warningf("nameserver: failed to stop listener2: %s", err) } } return nil } func getListenAddresses(listenAddress string) (ip1, ip2 net.IP, port uint16, err error) { // Split host and port. ipString, portString, err := net.SplitHostPort(listenAddress) if err != nil { return nil, nil, 0, fmt.Errorf( "failed to parse address %s: %w", listenAddress, err, ) } // Parse the IP address. If the want to listen on localhost, we need to // listen separately for IPv4 and IPv6. if ipString == "localhost" { ip1 = net.IPv4(127, 0, 0, 17) if netenv.IPv6Enabled() { ip2 = net.IPv6loopback } else { log.Warningf("nameserver: no IPv6 stack detected, disabling IPv6 nameserver listener") } } else { ip1 = net.ParseIP(ipString) if ip1 == nil { return nil, nil, 0, fmt.Errorf( "failed to parse IP %s from %s", ipString, listenAddress, ) } } // Parse the port. port64, err := strconv.ParseUint(portString, 10, 16) if err != nil { return nil, nil, 0, fmt.Errorf( "failed to parse port %s from %s: %w", portString, listenAddress, err, ) } return ip1, ip2, uint16(port64), nil } var ( module *NameServer shimLoaded atomic.Bool ) // New returns a new NameServer module. func New(instance instance) (*NameServer, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("NameServer") module = &NameServer{ mgr: m, instance: instance, states: mgr.NewStateMgr(m), } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface{} ================================================ FILE: service/nameserver/nameserver.go ================================================ package nameserver import ( "context" "errors" "fmt" "net" "strings" "time" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/firewall" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/nameserver/nsutil" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/resolver" ) var hostname string const internalError = "internal error: " func handleRequestAsWorker(w dns.ResponseWriter, query *dns.Msg) { err := module.mgr.Do("handle dns request", func(wc *mgr.WorkerCtx) error { return handleRequest(wc.Ctx(), w, query) }) if err != nil { log.Warningf("nameserver: failed to handle dns request: %s", err) } } func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg) error { //nolint:maintidx // TODO // Record metrics. startTime := time.Now() defer requestsHistogram.UpdateDuration(startTime) // Check Question, only process the first, that's how everyone does it. var originalQuestion dns.Question switch len(request.Question) { case 0: log.Warning("nameserver: received query without question") return sendResponse(ctx, w, request, nsutil.Refused("no question provided")) case 1: originalQuestion = request.Question[0] default: log.Warningf( "nameserver: received query with multiple questions, first is %s.%s", request.Question[0].Name, dns.Type(request.Question[0].Qtype), ) return sendResponse(ctx, w, request, nsutil.Refused("multiple question provided")) } // Check the Query Class. if originalQuestion.Qclass != dns.ClassINET { // We only serve IN records. log.Warningf("nameserver: received unsupported qclass %d question for %s", originalQuestion.Qclass, originalQuestion.Name) return sendResponse(ctx, w, request, nsutil.Refused("unsupported qclass")) } // Check if we are handling a non-standard query name. var nonStandardQuestionFormat bool lowerCaseQuestion := strings.ToLower(originalQuestion.Name) if lowerCaseQuestion != originalQuestion.Name { nonStandardQuestionFormat = true } // Create query for the resolver. q := &resolver.Query{ FQDN: lowerCaseQuestion, QType: dns.Type(originalQuestion.Qtype), } // Get remote address of request. remoteAddr, ok := w.RemoteAddr().(*net.UDPAddr) if !ok { log.Warningf("nameserver: failed to get remote address of dns query: is type %+T", w.RemoteAddr()) return sendResponse(ctx, w, request, nsutil.Refused("unsupported transport")) } // Start context tracer for context-aware logging. ctx, tracer := log.AddTracer(ctx) defer tracer.Submit() tracer.Tracef("nameserver: handling new request for %s from %s:%d", q.ID(), remoteAddr.IP, remoteAddr.Port) // Count request. totalHandledRequests.Inc() // Setup quick reply function. reply := func(responder nsutil.Responder, rrProviders ...nsutil.RRProvider) error { err := sendResponse(ctx, w, request, responder, rrProviders...) // Log error here instead of returning it in order to keep the context. if err != nil { tracer.Errorf("nameserver: %s", err) } return nil } // Handle request for localhost and the hostname. if strings.HasSuffix(q.FQDN, "localhost.") || q.FQDN == hostname { tracer.Tracef("nameserver: returning localhost records") return reply(nsutil.Localhost()) } // Validate domain name. if !netutils.IsValidFqdn(q.FQDN) { tracer.Debugf("nameserver: domain name %s is invalid, refusing", q.FQDN) return reply(nsutil.Refused("invalid domain")) } // Get public suffix after validation. q.InitPublicSuffixData() // Check if query is failing. // Some software retries failing queries excessively. This might not be a // problem normally, but handling a request is pretty expensive for the // Portmaster, as it has to find out who sent the query. If we know the query // will fail with a very high probability, it is beneficial to just kill the // query for some time before doing any expensive work. defer cleanFailingQueries(10, 3) failingUntil, failingErr := checkIfQueryIsFailing(q) if failingErr != nil { remainingFailingDuration := time.Until(*failingUntil) tracer.Debugf("nameserver: returning previous error for %s: %s", q.ID(), failingErr) // Delay the response a bit in order to mitigate request flooding. if remainingFailingDuration < failingDelay { // Delay for remainind fail duration. tracer.Tracef("nameserver: delaying failing lookup until end of fail duration for %s", remainingFailingDuration.Round(time.Millisecond)) time.Sleep(remainingFailingDuration) return reply(nsutil.ServerFailure( internalError+failingErr.Error(), "delayed failing query to mitigate request flooding", )) } // Delay for default duration. tracer.Tracef("nameserver: delaying failing lookup for %s", failingDelay.Round(time.Millisecond)) time.Sleep(failingDelay) return reply(nsutil.ServerFailure( internalError+failingErr.Error(), "delayed failing query to mitigate request flooding", fmt.Sprintf("error is cached for another %s", remainingFailingDuration.Round(time.Millisecond)), )) } // Check if the request is local. local, err := netenv.IsMyIP(remoteAddr.IP) if err != nil { tracer.Warningf("nameserver: failed to check if request for %s is local: %s", q.ID(), err) return reply(nsutil.ServerFailure(internalError + " failed to check if request is local")) } // Create connection ID for dns request. connID := fmt.Sprintf( "%s-%d-#%d-%s", remoteAddr.IP, remoteAddr.Port, request.Id, q.ID(), ) // Get connection for this request. This identifies the process behind the request. var conn *network.Connection switch { case local: conn = network.NewConnectionFromDNSRequest(ctx, q.FQDN, nil, connID, remoteAddr.IP, uint16(remoteAddr.Port)) case networkServiceMode(): conn, err = network.NewConnectionFromExternalDNSRequest(ctx, q.FQDN, nil, connID, remoteAddr.IP) if err != nil { tracer.Warningf("nameserver: failed to get host/profile for request for %s%s: %s", q.FQDN, q.QType, err) return reply(nsutil.ServerFailure(internalError + "failed to get profile")) } default: tracer.Warningf("nameserver: external request from %s for %s%s, ignoring", remoteAddr, q.FQDN, q.QType) return reply(nsutil.Refused("external queries are not permitted")) } conn.Lock() defer conn.Unlock() // Create reference for the rrCache. var rrCache *resolver.RRCache // Once we decided on the connection we might need to save it to the database, // so we defer that check for now. defer func() { // Add metadata to connection. if rrCache != nil { conn.DNSContext = rrCache.ToDNSRequestContext() conn.Resolver = rrCache.Resolver conn.Entity.IPScope = rrCache.Resolver.IPScope } else { // Get resolvers for this query to determine the resolving scope. resolvers, _, _ := resolver.GetResolversInScope(ctx, q) if len(resolvers) > 0 { conn.Entity.IPScope = resolvers[0].Info.IPScope } } switch conn.Verdict { // We immediately save blocked, dropped or failed verdicts so // they pop up in the UI. case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed, network.VerdictRerouteToNameserver, network.VerdictRerouteToTunnel: conn.Save() // For undecided or accepted connections we don't save them yet, because // that will happen later anyway. case network.VerdictUndecided, network.VerdictAccept: // Check if we have a response. if rrCache == nil { conn.Failed(internalError+"no reply", "") return } // Mark successful queries as internal in order to hide them in the simple interface. // These requests were most probably made for another process and only add confusion if listed. if conn.Process().IsSystemResolver() { conn.Internal = true } // Save the request as open, as we don't know if there will be a connection or not. firewall.UpdateIPsAndCNAMEs(q, rrCache, conn) network.SaveOpenDNSRequest(q, rrCache, conn) case network.VerdictUndeterminable: fallthrough default: tracer.Warningf("nameserver: unexpected verdict %s for connection %s, not saving", conn.VerdictVerb(), conn) } }() // Check request with the privacy filter before resolving. firewall.FilterConnection(ctx, conn, nil, true, false) // Check if there is a responder from the firewall. // In special cases, the firewall might want to respond the query itself. // A reason for this might be that the request is sink-holed to a forced // IP address in which case we "accept" it, but let the firewall handle // the resolving as it wishes. if responder, ok := conn.Reason.Context.(nsutil.Responder); ok { tracer.Infof("nameserver: handing over request for %s to special filter responder: %s", q.ID(), conn.Reason.Msg) return reply(responder, conn) } // Check if there is a Verdict to act upon. switch conn.Verdict { //nolint:exhaustive // Only checking for specific values. case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed: tracer.Infof( "nameserver: returning %s response for %s to %s", conn.VerdictVerb(), q.ID(), conn.Process(), ) return reply(conn, conn) } // Resolve request. rrCache, err = resolver.Resolve(ctx, q) // Handle error. if err != nil { switch { case errors.Is(err, resolver.ErrNotFound): // Try alternatives domain names for unofficial domain spaces. rrCache = checkAlternativeCaches(ctx, q) if rrCache == nil { tracer.Tracef("nameserver: %s", err) conn.Failed("domain does not exist", "") return reply(nsutil.NxDomain("nxdomain: " + err.Error())) } case errors.Is(err, resolver.ErrBlocked): tracer.Tracef("nameserver: %s", err) conn.Block(err.Error(), "") return reply(nsutil.BlockIP("blocked: " + err.Error())) case errors.Is(err, resolver.ErrLocalhost): tracer.Tracef("nameserver: returning localhost records") conn.Accept("allowing query for localhost", "") return reply(nsutil.Localhost()) case errors.Is(err, resolver.ErrOffline): if rrCache == nil { tracer.Debugf("nameserver: not resolving %s, device is offline", q.ID()) conn.Failed("not resolving, device is offline", "") return reply(nsutil.ServerFailure(err.Error())) } // If an rrCache was returned, it's usable as a backup. rrCache.IsBackup = true log.Tracer(ctx).Debugf("nameserver: device is offline, using backup cache for %s", q.ID()) default: tracer.Warningf("nameserver: failed to resolve %s: %s", q.ID(), err) conn.Failed(fmt.Sprintf("query failed: %s", err), "") addFailingQuery(q, err) return reply(nsutil.ServerFailure(internalError + err.Error())) } } // Handle special cases. switch { case rrCache == nil: tracer.Warning("nameserver: received successful, but empty reply from resolver") addFailingQuery(q, errors.New("emptry reply from resolver")) return reply(nsutil.ServerFailure(internalError + "empty reply")) case rrCache.RCode == dns.RcodeNameError: // Try alternatives domain names for unofficial domain spaces. altRRCache := checkAlternativeCaches(ctx, q) if altRRCache != nil { rrCache = altRRCache } else { // Return now if NXDomain. return reply(nsutil.NxDomain("no answer found (NXDomain)")) } } // Check with firewall again after resolving. tracer.Trace("nameserver: deciding on resolved dns") rrCache = firewall.FilterResolvedDNS(ctx, conn, q, rrCache) // Check again if there is a responder from the firewall. if responder, ok := conn.Reason.Context.(nsutil.Responder); ok { tracer.Infof("nameserver: handing over request for %s to special filter responder: %s", q.ID(), conn.Reason.Msg) return reply(responder) } // Check if there is a Verdict to act upon. switch conn.Verdict { //nolint:exhaustive // Only checking for specific values. case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed: tracer.Infof( "nameserver: returning %s response for %s to %s", conn.VerdictVerb(), q.ID(), conn.Process(), ) return reply(conn, conn, rrCache) } // Revert back to non-standard question format, if we had to convert. if nonStandardQuestionFormat { rrCache.ReplaceAnswerNames(originalQuestion.Name) } // Reply with successful response. noAnswerIndicator := "" if len(rrCache.Answer) == 0 { noAnswerIndicator = "/no answer" } tracer.Infof( "nameserver: returning %s response (%s%s) for %s to %s", conn.VerdictVerb(), dns.RcodeToString[rrCache.RCode], noAnswerIndicator, q.ID(), conn.Process(), ) return reply(rrCache, conn, rrCache) } func checkAlternativeCaches(ctx context.Context, q *resolver.Query) *resolver.RRCache { // Do not try alternatives when the query is in a public suffix. // This also includes arpa. and local. if q.ICANNSpace { return nil } // Check if the env resolver has something. pmEnvQ := &resolver.Query{ FQDN: q.FQDN + "local." + resolver.InternalSpecialUseDomain, QType: q.QType, } rrCache, err := resolver.QueryPortmasterEnv(ctx, pmEnvQ) if err == nil && rrCache != nil && rrCache.RCode == dns.RcodeSuccess { makeAlternativeRecord(ctx, q, rrCache, pmEnvQ.FQDN) return rrCache } // Check if we have anything in cache localFQDN := q.FQDN + "local." rrCache, err = resolver.GetRRCache(localFQDN, q.QType) if err == nil && rrCache != nil && rrCache.RCode == dns.RcodeSuccess { makeAlternativeRecord(ctx, q, rrCache, localFQDN) return rrCache } return nil } func makeAlternativeRecord(ctx context.Context, q *resolver.Query, rrCache *resolver.RRCache, altName string) { log.Tracer(ctx).Debugf("using %s to answer query", altName) // Duplicate answers so they match the query. copied := make([]dns.RR, 0, len(rrCache.Answer)) for _, answer := range rrCache.Answer { if strings.ToLower(answer.Header().Name) == altName { copiedAnswer := dns.Copy(answer) copiedAnswer.Header().Name = q.FQDN copied = append(copied, copiedAnswer) } } if len(copied) > 0 { rrCache.Answer = append(rrCache.Answer, copied...) } // Update the question. rrCache.Domain = q.FQDN } ================================================ FILE: service/nameserver/nsutil/nsutil.go ================================================ package nsutil import ( "context" "encoding/json" "errors" "fmt" "strings" "time" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" ) // ErrNilRR is returned when a parsed RR is nil. var ErrNilRR = errors.New("is nil") // Responder defines the interface that any block/deny reason interface // may implement to support sending custom DNS responses for a given reason. // That is, if a reason context implements the Responder interface the // ReplyWithDNS method will be called instead of creating the default // zero-ip response. type Responder interface { // ReplyWithDNS is called when a DNS response to a DNS message is // crafted because the request is either denied or blocked. ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns.Msg } // RRProvider defines the interface that any block/deny reason interface // may implement to support adding additional DNS resource records to // the DNS responses extra (additional) section. type RRProvider interface { // GetExtraRRs is called when a DNS response to a DNS message is // crafted because the request is either denied or blocked. GetExtraRRs(ctx context.Context, request *dns.Msg) []dns.RR } // ResponderFunc is a convenience type to use a function // directly as a Responder. type ResponderFunc func(ctx context.Context, request *dns.Msg) *dns.Msg // ReplyWithDNS implements the Responder interface and calls rf. func (rf ResponderFunc) ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns.Msg { return rf(ctx, request) } // MarshalJSON disables JSON marshaling for ResponderFunc. func (rf ResponderFunc) MarshalJSON() ([]byte, error) { return json.Marshal(nil) } // BlockIP is a ResponderFunc than replies with either 0.0.0.17 or ::17 for // each A or AAAA question respectively. If there is no A or AAAA question, it // defaults to replying with NXDomain. func BlockIP(msgs ...string) ResponderFunc { return createResponderFunc( "blocking", "0.0.0.17", "::17", msgs..., ) } // ZeroIP is a ResponderFunc than replies with either 0.0.0.0 or :: for each A // or AAAA question respectively. If there is no A or AAAA question, it // defaults to replying with NXDomain. func ZeroIP(msgs ...string) ResponderFunc { return createResponderFunc( "zero ip", "0.0.0.0", "::", msgs..., ) } // Localhost is a ResponderFunc than replies with localhost IP addresses. // If there is no A or AAAA question, it defaults to replying with NXDomain. func Localhost(msgs ...string) ResponderFunc { return createResponderFunc( "localhost", "127.0.0.1", "::1", msgs..., ) } func createResponderFunc(responderName, aAnswer, aaaaAnswer string, msgs ...string) ResponderFunc { return func(ctx context.Context, request *dns.Msg) *dns.Msg { reply := new(dns.Msg) hasErr := false for _, question := range request.Question { var rr dns.RR var err error switch question.Qtype { case dns.TypeA: rr, err = dns.NewRR(question.Name + " 1 IN A " + aAnswer) case dns.TypeAAAA: rr, err = dns.NewRR(question.Name + " 1 IN AAAA " + aaaaAnswer) } switch { case err != nil: log.Tracer(ctx).Errorf("nameserver: failed to create %s response for %s: %s", responderName, question.Name, err) hasErr = true case rr != nil: reply.Answer = append(reply.Answer, rr) } } switch { case hasErr && len(reply.Answer) == 0: reply.SetRcode(request, dns.RcodeServerFailure) case len(reply.Answer) == 0: reply.SetRcode(request, dns.RcodeNameError) default: reply.SetRcode(request, dns.RcodeSuccess) } AddMessagesToReply(ctx, reply, log.InfoLevel, msgs...) return reply } } // NxDomain returns a ResponderFunc that replies with NXDOMAIN. func NxDomain(msgs ...string) ResponderFunc { return func(ctx context.Context, request *dns.Msg) *dns.Msg { reply := new(dns.Msg).SetRcode(request, dns.RcodeNameError) AddMessagesToReply(ctx, reply, log.InfoLevel, msgs...) // According to RFC4074 (https://tools.ietf.org/html/rfc4074), there are // nameservers that incorrectly respond with NXDomain instead of an empty // SUCCESS response when there are other RRs for the queried domain name. // This can lead to the software thinking that no RRs exist for that // domain. In order to mitigate this a bit, we slightly delay NXDomain // responses. time.Sleep(500 * time.Millisecond) return reply } } // Refused returns a ResponderFunc that replies with REFUSED. func Refused(msgs ...string) ResponderFunc { return func(ctx context.Context, request *dns.Msg) *dns.Msg { reply := new(dns.Msg).SetRcode(request, dns.RcodeRefused) AddMessagesToReply(ctx, reply, log.InfoLevel, msgs...) return reply } } // ServerFailure returns a ResponderFunc that replies with SERVFAIL. func ServerFailure(msgs ...string) ResponderFunc { return func(ctx context.Context, request *dns.Msg) *dns.Msg { reply := new(dns.Msg).SetRcode(request, dns.RcodeServerFailure) AddMessagesToReply(ctx, reply, log.InfoLevel, msgs...) return reply } } // MakeMessageRecord creates an informational resource record that can be added // to the extra section of a reply. func MakeMessageRecord(level log.Severity, msg string) (dns.RR, error) { //nolint:interfacer rr, err := dns.NewRR(fmt.Sprintf( `%s.portmaster. 0 IN TXT "%s"`, strings.ToLower(level.String()), msg, )) if err != nil { return nil, err } if rr == nil { return nil, ErrNilRR } return rr, nil } // AddMessagesToReply creates information resource records using // MakeMessageRecord and immediately adds them to the extra section of the given // reply. If an error occurs, the resource record will not be added, and the // error will be logged. func AddMessagesToReply(ctx context.Context, reply *dns.Msg, level log.Severity, msgs ...string) { for _, msg := range msgs { // Ignore empty messages. if msg == "" { continue } // Create resources record. rr, err := MakeMessageRecord(level, msg) if err != nil { log.Tracer(ctx).Warningf("nameserver: failed to add message to reply: %s", err) continue } // Add to extra section of the reply. reply.Extra = append(reply.Extra, rr) } } ================================================ FILE: service/nameserver/response.go ================================================ package nameserver import ( "context" "fmt" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/nameserver/nsutil" ) // sendResponse sends a response to query using w. The response message is // created by responder. If addExtraRRs is not nil and implements the // RRProvider interface then it will be also used to add more RRs in the // extra section. func sendResponse( ctx context.Context, w dns.ResponseWriter, request *dns.Msg, responder nsutil.Responder, rrProviders ...nsutil.RRProvider, ) error { // Have the Responder craft a DNS reply. reply := responder.ReplyWithDNS(ctx, request) if reply == nil { // Dropping query. return nil } // Signify that we are a recursive resolver. // While we do not handle recursion directly, we can safely assume, that we // always forward to a recursive resolver. reply.RecursionAvailable = true // Add extra RRs through a custom RRProvider. for _, rrProvider := range rrProviders { if rrProvider != nil { rrs := rrProvider.GetExtraRRs(ctx, request) reply.Extra = append(reply.Extra, rrs...) } } // Write reply. if err := writeDNSResponse(ctx, w, reply); err != nil { return fmt.Errorf("failed to send response: %w", err) } return nil } func writeDNSResponse(ctx context.Context, w dns.ResponseWriter, m *dns.Msg) (err error) { defer func() { // recover from panic if panicErr := recover(); panicErr != nil { err = fmt.Errorf("panic: %s", panicErr) log.Tracer(ctx).Debugf("nameserver: panic caused by this msg: %#v", m) } }() err = w.WriteMsg(m) if err != nil { // If we receive an error we might have exceeded the message size with all // our extra information records. Retry again without the extra section. log.Tracer(ctx).Tracef("nameserver: retrying to write dns message without extra section, error was: %s", err) m.Extra = nil noExtraErr := w.WriteMsg(m) if noExtraErr == nil { return fmt.Errorf("failed to write dns message without extra section: %w", err) } } return } ================================================ FILE: service/netenv/addresses_test.go ================================================ package netenv import ( "testing" ) func TestGetAssignedAddresses(t *testing.T) { t.Parallel() ipv4, ipv6, err := GetAssignedAddresses() t.Logf("all v4: %v", ipv4) t.Logf("all v6: %v", ipv6) if err != nil { t.Fatalf("failed to get addresses: %s", err) } if len(ipv4) == 0 && len(ipv6) == 0 { t.Fatal("GetAssignedAddresses did not return any addresses") } } func TestGetAssignedGlobalAddresses(t *testing.T) { t.Parallel() ipv4, ipv6, err := GetAssignedGlobalAddresses() t.Logf("all global v4: %v", ipv4) t.Logf("all global v6: %v", ipv6) if err != nil { t.Fatalf("failed to get addresses: %s", err) } } ================================================ FILE: service/netenv/adresses.go ================================================ package netenv import ( "fmt" "net" "sync" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/netutils" ) // GetAssignedAddresses returns the assigned IPv4 and IPv6 addresses of the host. func GetAssignedAddresses() (ipv4 []net.IP, ipv6 []net.IP, err error) { addrs, err := osGetInterfaceAddrs() if err != nil { return nil, nil, err } for _, addr := range addrs { netAddr, ok := addr.(*net.IPNet) if !ok { log.Warningf("netenv: interface address of unexpected type %T", addr) continue } if ip4 := netAddr.IP.To4(); ip4 != nil { ipv4 = append(ipv4, ip4) } else { ipv6 = append(ipv6, netAddr.IP) } } return } // GetAssignedGlobalAddresses returns the assigned global IPv4 and IPv6 addresses of the host. func GetAssignedGlobalAddresses() (ipv4 []net.IP, ipv6 []net.IP, err error) { allv4, allv6, err := GetAssignedAddresses() if err != nil { return nil, nil, err } for _, ip4 := range allv4 { if netutils.GetIPScope(ip4).IsGlobal() { ipv4 = append(ipv4, ip4) } } for _, ip6 := range allv6 { if netutils.GetIPScope(ip6).IsGlobal() { ipv6 = append(ipv6, ip6) } } return } var ( myNetworks []*net.IPNet myNetworksLock sync.Mutex myNetworksNetworkChangedFlag = GetNetworkChangedFlag() myNetworksRefreshError error //nolint:errname // Not what the linter thinks this is for. myNetworksDontRefreshUntil time.Time ) // refreshMyNetworks refreshes the networks held in refreshMyNetworks. // The caller must hold myNetworksLock. func refreshMyNetworks() error { // Check if we already refreshed recently. if time.Now().Before(myNetworksDontRefreshUntil) { // Return previous error, if available. if myNetworksRefreshError != nil { return fmt.Errorf("failed to previously refresh interface addresses: %w", myNetworksRefreshError) } return nil } myNetworksRefreshError = nil myNetworksDontRefreshUntil = time.Now().Add(1 * time.Second) // Refresh assigned networks. interfaceNetworks, err := osGetInterfaceAddrs() if err != nil { // In some cases the system blocks on this call, which piles up to // literally over thousand goroutines wanting to try this again. myNetworksRefreshError = err return fmt.Errorf("failed to refresh interface addresses: %w", err) } myNetworks = make([]*net.IPNet, 0, len(interfaceNetworks)) for _, ifNet := range interfaceNetworks { ipNet, ok := ifNet.(*net.IPNet) if !ok { log.Warningf("netenv: interface network of unexpected type %T", ifNet) continue } myNetworks = append(myNetworks, ipNet) } // Reset changed flag. myNetworksNetworkChangedFlag.Refresh() return nil } // IsMyIP returns whether the given unicast IP is currently configured on the local host. // Broadcast or multicast addresses will never match, even if valid and in use. // Function is optimized with the assumption that is likely that the IP is mine. func IsMyIP(ip net.IP) (yes bool, err error) { // Check for IPs that don't need extra checks. switch netutils.GetIPScope(ip) { //nolint:exhaustive // Only looking for specific values. case netutils.HostLocal: return true, nil case netutils.LocalMulticast, netutils.GlobalMulticast: return false, nil } myNetworksLock.Lock() defer myNetworksLock.Unlock() // Check if the network changed. if myNetworksNetworkChangedFlag.IsSet() { err := refreshMyNetworks() if err != nil { return false, err } } // Check against assigned IPs. for _, myNet := range myNetworks { if ip.Equal(myNet.IP) { return true, nil } } // Check for other IPs in range and broadcast addresses. // Do this in a second loop, as an IP will match in // most cases and network matching is more expensive. for _, myNet := range myNetworks { if myNet.Contains(ip) { return false, nil } } // Could not find IP anywhere. Refresh network to be sure. err = refreshMyNetworks() if err != nil { return false, err } // Check against assigned IPs again. for _, myNet := range myNetworks { if ip.Equal(myNet.IP) { return true, nil } } return false, nil } // GetLocalNetwork uses the given IP to search for a network configured on the // device and returns it. func GetLocalNetwork(ip net.IP) (myNet *net.IPNet, err error) { myNetworksLock.Lock() defer myNetworksLock.Unlock() // Check if the network changed. if myNetworksNetworkChangedFlag.IsSet() { err := refreshMyNetworks() if err != nil { return nil, err } } // Check if the IP address is in my networks. for _, myNet := range myNetworks { if myNet.Contains(ip) { return myNet, nil } } return nil, nil } ================================================ FILE: service/netenv/api.go ================================================ package netenv import ( "errors" "github.com/safing/portmaster/base/api" ) func registerAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Path: "network/gateways", Read: api.PermitUser, StructFunc: func(ar *api.Request) (i interface{}, err error) { return Gateways(), nil }, Name: "Get Default Gateways", Description: "Returns the current active default gateways of the network.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: "network/nameservers", Read: api.PermitUser, StructFunc: func(ar *api.Request) (i interface{}, err error) { return Nameservers(), nil }, Name: "Get System Nameservers", Description: "Returns the currently configured nameservers on the OS.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: "network/location", Read: api.PermitUser, StructFunc: func(ar *api.Request) (i interface{}, err error) { locs, ok := GetInternetLocation() if ok { return locs, nil } return nil, errors.New("no location data available") }, Name: "Get Approximate Internet Location", Description: "Returns an approximation of where the device is on the Internet.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: "network/location/traceroute", Read: api.PermitUser, StructFunc: func(ar *api.Request) (i interface{}, err error) { return getLocationFromTraceroute(&DeviceLocations{}) }, Name: "Get Approximate Internet Location via Traceroute", Description: "Returns an approximation of where the device is on the Internet using a the traceroute technique.", }); err != nil { return err } return nil } ================================================ FILE: service/netenv/dbus_linux.go ================================================ //go:build !server package netenv import ( "errors" "fmt" "net" "sync" "github.com/godbus/dbus/v5" "github.com/safing/portmaster/base/log" ) var ( dbusConn *dbus.Conn dbusConnLock sync.Mutex ) func getNameserversFromDbus() ([]Nameserver, error) { //nolint:gocognit // TODO // cmdline tool for exploring: gdbus introspect --system --dest org.freedesktop.NetworkManager --object-path /org/freedesktop/NetworkManager var ns []Nameserver var err error dbusConnLock.Lock() defer dbusConnLock.Unlock() if dbusConn == nil { dbusConn, err = dbus.SystemBus() } if err != nil { return nil, err } primaryConnectionVariant, err := getNetworkManagerProperty(dbusConn, dbus.ObjectPath("/org/freedesktop/NetworkManager"), "org.freedesktop.NetworkManager.PrimaryConnection") if err != nil { return nil, fmt.Errorf("dbus: failed to access NetworkManager.PrimaryConnection: %w", err) } primaryConnection, ok := primaryConnectionVariant.Value().(dbus.ObjectPath) if !ok { return nil, errors.New("dbus: could not assert type of /org/freedesktop/NetworkManager:org.freedesktop.NetworkManager.PrimaryConnection") } activeConnectionsVariant, err := getNetworkManagerProperty(dbusConn, dbus.ObjectPath("/org/freedesktop/NetworkManager"), "org.freedesktop.NetworkManager.ActiveConnections") if err != nil { return nil, fmt.Errorf("dbus: failed to access NetworkManager.ActiveConnections: %w", err) } activeConnections, ok := activeConnectionsVariant.Value().([]dbus.ObjectPath) if !ok { return nil, errors.New("dbus: could not assert type of /org/freedesktop/NetworkManager:org.freedesktop.NetworkManager.ActiveConnections") } sortedConnections := []dbus.ObjectPath{primaryConnection} for _, activeConnection := range activeConnections { if !objectPathInSlice(activeConnection, sortedConnections) { sortedConnections = append(sortedConnections, activeConnection) } } for _, activeConnection := range sortedConnections { newNameservers, err := dbusGetInterfaceNameservers(dbusConn, activeConnection, 4) if err != nil { log.Warningf("failed to get nameserver: %s", err) } else { ns = append(ns, newNameservers...) } newNameservers, err = dbusGetInterfaceNameservers(dbusConn, activeConnection, 6) if err != nil { log.Warningf("failed to get nameserver: %s", err) } else { ns = append(ns, newNameservers...) } } return ns, nil } func dbusGetInterfaceNameservers(dbusConn *dbus.Conn, interfaceObject dbus.ObjectPath, ipVersion uint8) ([]Nameserver, error) { ipConfigPropertyKey := fmt.Sprintf("org.freedesktop.NetworkManager.Connection.Active.Ip%dConfig", ipVersion) nameserversIPsPropertyKey := fmt.Sprintf("org.freedesktop.NetworkManager.IP%dConfig.Nameservers", ipVersion) nameserversDomainsPropertyKey := fmt.Sprintf("org.freedesktop.NetworkManager.IP%dConfig.Domains", ipVersion) nameserversSearchesPropertyKey := fmt.Sprintf("org.freedesktop.NetworkManager.IP%dConfig.Searches", ipVersion) // Get Interface Configuration. ipConfigVariant, err := getNetworkManagerProperty(dbusConn, interfaceObject, ipConfigPropertyKey) if err != nil { return nil, fmt.Errorf("failed to access %s:%s: %w", interfaceObject, ipConfigPropertyKey, err) } ipConfig, ok := ipConfigVariant.Value().(dbus.ObjectPath) if !ok { return nil, fmt.Errorf("could not assert type of %s:%s", interfaceObject, ipConfigPropertyKey) } // Check if interface is active in the selected IP version if !ipConfig.IsValid() || ipConfig == "/" { return nil, nil } // Get Nameserver IPs nameserverIPsVariant, err := getNetworkManagerProperty(dbusConn, ipConfig, nameserversIPsPropertyKey) if err != nil { return nil, fmt.Errorf("failed to access %s:%s: %w", ipConfig, nameserversIPsPropertyKey, err) } var nameserverIPs []net.IP switch ipVersion { case 4: nameserverIP4s, ok := nameserverIPsVariant.Value().([]uint32) if !ok { return nil, fmt.Errorf("could not assert type of %s:%s", ipConfig, nameserversIPsPropertyKey) } for _, ip := range nameserverIP4s { a := uint8(ip / 16777216) b := uint8((ip % 16777216) / 65536) c := uint8((ip % 65536) / 256) d := uint8(ip % 256) nameserverIPs = append(nameserverIPs, net.IPv4(d, c, b, a)) } case 6: nameserverIP6s, ok := nameserverIPsVariant.Value().([][]byte) if !ok { return nil, fmt.Errorf("could not assert type of %s:%s", ipConfig, nameserversIPsPropertyKey) } for _, ip := range nameserverIP6s { if len(ip) != 16 { return nil, fmt.Errorf("query returned IPv6 address with invalid length: %q", ip) } nameserverIPs = append(nameserverIPs, net.IP(ip)) } } // Get Nameserver Domains nameserverDomainsVariant, err := getNetworkManagerProperty(dbusConn, ipConfig, nameserversDomainsPropertyKey) if err != nil { return nil, fmt.Errorf("failed to access %s:%s: %w", ipConfig, nameserversDomainsPropertyKey, err) } nameserverDomains, ok := nameserverDomainsVariant.Value().([]string) if !ok { return nil, fmt.Errorf("could not assert type of %s:%s", ipConfig, nameserversDomainsPropertyKey) } // Get Nameserver Searches nameserverSearchesVariant, err := getNetworkManagerProperty(dbusConn, ipConfig, nameserversSearchesPropertyKey) if err != nil { return nil, fmt.Errorf("failed to access %s:%s: %w", ipConfig, nameserversSearchesPropertyKey, err) } nameserverSearches, ok := nameserverSearchesVariant.Value().([]string) if !ok { return nil, fmt.Errorf("could not assert type of %s:%s", ipConfig, nameserversSearchesPropertyKey) } ns := make([]Nameserver, 0, len(nameserverIPs)) searchDomains := append(nameserverDomains, nameserverSearches...) //nolint:gocritic for _, nameserverIP := range nameserverIPs { ns = append(ns, Nameserver{ IP: nameserverIP, Search: searchDomains, }) } return ns, nil } func getConnectivityStateFromDbus() (OnlineStatus, error) { var err error dbusConnLock.Lock() defer dbusConnLock.Unlock() if dbusConn == nil { dbusConn, err = dbus.SystemBus() } if err != nil { return 0, err } connectivityStateVariant, err := getNetworkManagerProperty(dbusConn, dbus.ObjectPath("/org/freedesktop/NetworkManager"), "org.freedesktop.NetworkManager.Connectivity") if err != nil { return 0, err } connectivityState, ok := connectivityStateVariant.Value().(uint32) if !ok { return 0, errors.New("dbus: could not assert type of /org/freedesktop/NetworkManager:org.freedesktop.NetworkManager.Connectivity") } // NMConnectivityState // NM_CONNECTIVITY_UNKNOWN = 0 Network connectivity is unknown. // NM_CONNECTIVITY_NONE = 1 The host is not connected to any network. // NM_CONNECTIVITY_PORTAL = 2 The host is behind a captive portal and cannot reach the full Internet. // NM_CONNECTIVITY_LIMITED = 3 The host is connected to a network, but does not appear to be able to reach the full Internet. // NM_CONNECTIVITY_FULL = 4 The host is connected to a network, and appears to be able to reach the full Internet. switch connectivityState { case 0: return StatusUnknown, nil case 1: return StatusOffline, nil case 2: return StatusPortal, nil case 3: return StatusLimited, nil case 4: return StatusOnline, nil } return StatusUnknown, nil } func getNetworkManagerProperty(conn *dbus.Conn, objectPath dbus.ObjectPath, property string) (dbus.Variant, error) { object := conn.Object("org.freedesktop.NetworkManager", objectPath) return object.GetProperty(property) } func objectPathInSlice(a dbus.ObjectPath, list []dbus.ObjectPath) bool { for _, b := range list { if string(b) == string(a) { return true } } return false } ================================================ FILE: service/netenv/dbus_linux_test.go ================================================ package netenv import ( "errors" "io/fs" "os" "testing" ) func TestDbus(t *testing.T) { t.Parallel() if testing.Short() { t.Skip("skipping test in short mode because it fails in the CI") } if _, err := os.Stat("/var/run/dbus/system_bus_socket"); errors.Is(err, fs.ErrNotExist) { t.Logf("skipping dbus tests, as dbus does not seem to be installed: %s", err) return } nameservers, err := getNameserversFromDbus() if err != nil { t.Errorf("getNameserversFromDbus failed: %s", err) } t.Logf("getNameserversFromDbus: %v", nameservers) connectivityState, err := getConnectivityStateFromDbus() if err != nil { t.Errorf("getConnectivityStateFromDbus failed: %s", err) } t.Logf("getConnectivityStateFromDbus: %v", connectivityState) } ================================================ FILE: service/netenv/dialing.go ================================================ package netenv import "net" var localAddrFactory func(network string) net.Addr // SetLocalAddrFactory supplies the environment package with a function to get permitted local addresses for connections. func SetLocalAddrFactory(laf func(network string) net.Addr) { if localAddrFactory == nil { localAddrFactory = laf } } func getLocalAddr(network string) net.Addr { if localAddrFactory != nil { return localAddrFactory(network) } return nil } ================================================ FILE: service/netenv/environment.go ================================================ package netenv import ( "net" ) // TODO: find a good way to identify a network // best options until now: // MAC of gateway // domain parameter of dhcp // TODO: get dhcp servers on windows: // doc: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365917 // this info might already be included in the interfaces api provided by golang! // Nameserver describes a system assigned namserver. type Nameserver struct { IP net.IP Search []string } ================================================ FILE: service/netenv/environment_default.go ================================================ //+build !windows,!linux package netenv import "net" func Nameservers() []Nameserver { return nil } func Gateways() []net.IP { return nil } // TODO: implement using // ifconfig // scutil --nwi // scutil --proxy // networksetup -listallnetworkservices // networksetup -listnetworkserviceorder // networksetup -getdnsservers "Wi-Fi" // networksetup -getsearchdomains // networksetup -getftpproxy // networksetup -getwebproxy // networksetup -getsecurewebproxy // networksetup -getstreamingproxy // networksetup -getgopherproxy // networksetup -getsocksfirewallproxy // route -n get default ================================================ FILE: service/netenv/environment_linux.go ================================================ package netenv import ( "bufio" "encoding/hex" "net" "os" "strings" "sync" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/netutils" ) var ( gateways = make([]net.IP, 0) gatewaysLock sync.Mutex gatewaysNetworkChangedFlag = GetNetworkChangedFlag() nameservers = make([]Nameserver, 0) nameserversLock sync.Mutex nameserversNetworkChangedFlag = GetNetworkChangedFlag() ) // Gateways returns the currently active gateways. func Gateways() []net.IP { gatewaysLock.Lock() defer gatewaysLock.Unlock() // Check if the network changed, if not, return cache. if !gatewaysNetworkChangedFlag.IsSet() { return gateways } gatewaysNetworkChangedFlag.Refresh() gateways = make([]net.IP, 0) var decoded []byte // open file route, err := os.Open("/proc/net/route") if err != nil { log.Warningf("environment: could not read /proc/net/route: %s", err) return gateways } defer func() { _ = route.Close() }() // file scanner scanner := bufio.NewScanner(route) scanner.Split(bufio.ScanLines) // parse for scanner.Scan() { line := strings.SplitN(scanner.Text(), "\t", 4) if len(line) < 4 { continue } if line[1] == "00000000" { decoded, err = hex.DecodeString(line[2]) if err != nil { log.Warningf("environment: could not parse gateway %s from /proc/net/route: %s", line[2], err) continue } if len(decoded) != 4 { log.Warningf("environment: decoded gateway %s from /proc/net/route has wrong length", decoded) continue } gate := net.IPv4(decoded[3], decoded[2], decoded[1], decoded[0]) gateways = append(gateways, gate) } } // open file v6route, err := os.Open("/proc/net/ipv6_route") if err != nil { log.Warningf("environment: could not read /proc/net/ipv6_route: %s", err) return gateways } defer func() { _ = v6route.Close() }() // file scanner scanner = bufio.NewScanner(v6route) scanner.Split(bufio.ScanLines) // parse for scanner.Scan() { line := strings.SplitN(scanner.Text(), " ", 6) if len(line) < 6 { continue } if line[0] == "00000000000000000000000000000000" && line[4] != "00000000000000000000000000000000" { decoded, err := hex.DecodeString(line[4]) if err != nil { log.Warningf("environment: could not parse gateway %s from /proc/net/ipv6_route: %s", line[2], err) continue } if len(decoded) != 16 { log.Warningf("environment: decoded gateway %s from /proc/net/ipv6_route has wrong length", decoded) continue } gate := net.IP(decoded) gateways = append(gateways, gate) } } return gateways } // Nameservers returns the currently active nameservers. func Nameservers() []Nameserver { nameserversLock.Lock() defer nameserversLock.Unlock() // Check if the network changed, if not, return cache. if !nameserversNetworkChangedFlag.IsSet() { return nameservers } nameserversNetworkChangedFlag.Refresh() // logic // TODO: try: // 1. NetworkManager DBUS // 2. /etc/resolv.conf // 2.1. if /etc/resolv.conf has localhost nameserver, check for dnsmasq config (are there others?) nameservers = make([]Nameserver, 0) // get nameservers from DBUS dbusNameservers, err := getNameserversFromDbus() if err != nil { log.Warningf("environment: could not get nameservers from dbus: %s", err) } else { nameservers = addNameservers(nameservers, dbusNameservers) } // get nameservers from /etc/resolv.conf resolvconfNameservers, err := getNameserversFromResolvconf() if err != nil { log.Warningf("environment: could not get nameservers from resolvconf: %s", err) } else { nameservers = addNameservers(nameservers, resolvconfNameservers) } return nameservers } func getNameserversFromResolvconf() ([]Nameserver, error) { // open file resolvconf, err := os.Open("/etc/resolv.conf") if err != nil { log.Warningf("environment: could not read /etc/resolv.conf: %s", err) return nil, err } defer func() { _ = resolvconf.Close() }() // file scanner scanner := bufio.NewScanner(resolvconf) scanner.Split(bufio.ScanLines) var searchDomains []string var servers []net.IP // parse for scanner.Scan() { line := strings.SplitN(scanner.Text(), " ", 3) if len(line) < 2 { continue } switch line[0] { case "search": if netutils.IsValidFqdn(dns.Fqdn(line[1])) { searchDomains = append(searchDomains, line[1]) } case "nameserver": ip := net.ParseIP(line[1]) if ip != nil { servers = append(servers, ip) } } } // build array nameservers := make([]Nameserver, 0, len(servers)) for _, server := range servers { nameservers = append(nameservers, Nameserver{ IP: server, Search: searchDomains, }) } return nameservers, nil } func addNameservers(nameservers, newNameservers []Nameserver) []Nameserver { for _, newNameserver := range newNameservers { found := false for _, nameserver := range nameservers { if nameserver.IP.Equal(newNameserver.IP) { found = true break } } if !found { nameservers = append(nameservers, newNameserver) } } return nameservers } ================================================ FILE: service/netenv/environment_linux_test.go ================================================ package netenv import "testing" func TestLinuxEnvironment(t *testing.T) { t.Parallel() nameserversTest, err := getNameserversFromResolvconf() if err != nil { t.Errorf("failed to get namerservers from resolvconf: %s", err) } t.Logf("nameservers from resolvconf: %+v", nameserversTest) } ================================================ FILE: service/netenv/environment_test.go ================================================ package netenv import "testing" func TestEnvironment(t *testing.T) { t.Parallel() nameserversTest := Nameservers() t.Logf("nameservers: %+v", nameserversTest) gatewaysTest := Gateways() t.Logf("gateways: %+v", gatewaysTest) } ================================================ FILE: service/netenv/environment_windows.go ================================================ package netenv import ( "bufio" "bytes" "encoding/json" "fmt" "net" "strings" "sync" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils/osdetail" ) // Gateways returns the currently active gateways. func Gateways() []net.IP { defaultIf := getDefaultInterface() if defaultIf == nil { return nil } // Collect gateways. var gw []net.IP if defaultIf.IPv4DefaultGateway != nil { gw = append(gw, defaultIf.IPv4DefaultGateway) } if defaultIf.IPv6DefaultGateway != nil { gw = append(gw, defaultIf.IPv6DefaultGateway) } return gw } // Nameservers returns the currently active nameservers. func Nameservers() []Nameserver { defaultIf := getDefaultInterface() if defaultIf == nil { return nil } // Compile search list. var search []string if defaultIf.DNSServerConfig != nil { if defaultIf.DNSServerConfig.Suffix != "" { search = append(search, defaultIf.DNSServerConfig.Suffix) } if len(defaultIf.DNSServerConfig.SuffixSearchList) > 0 { search = append(search, defaultIf.DNSServerConfig.SuffixSearchList...) } } // Compile nameservers. var ns []Nameserver for _, nsIP := range defaultIf.DNSServer { ns = append(ns, Nameserver{ IP: nsIP, Search: search, }) } return ns } const ( defaultInterfaceRecheck = 2 * time.Second ) var ( defaultInterface *defaultNetInterface defaultInterfaceLock sync.Mutex defaultInterfaceNetworkChangedFlag = GetNetworkChangedFlag() ) type defaultNetInterface struct { InterfaceIndex string IPv6Address net.IP IPv4Address net.IP IPv6DefaultGateway net.IP IPv4DefaultGateway net.IP DNSServer []net.IP DNSServerConfig *dnsServerConfig } type dnsServerConfig struct { Suffix string SuffixSearchList []string } func getDefaultInterface() *defaultNetInterface { defaultInterfaceLock.Lock() defer defaultInterfaceLock.Unlock() // Check if the network changed, if not, return cache. if !defaultInterfaceNetworkChangedFlag.IsSet() { return defaultInterface } defaultInterfaceNetworkChangedFlag.Refresh() // Get interface data from Windows. interfaceData, err := osdetail.RunPowershellCmd("Get-NetRoute -DestinationPrefix '0.0.0.0/0' | Select-Object -First 1 | Get-NetIPConfiguration | Format-List") if err != nil { log.Warningf("netenv: failed to get interface data: %s", err) return nil } // TODO: It would be great to get this as json. Powershell can do this, // but it just spits out lots of weird data instead of the same strings // seen in the list. newIf := &defaultNetInterface{} // Scan data for needed fields. scanner := bufio.NewScanner(bytes.NewBuffer(interfaceData)) scanner.Split(bufio.ScanLines) var segmentKey, segmentValue, previousKey string for scanner.Scan() { segments := strings.SplitN(scanner.Text(), " : ", 2) // Check what the line gives us. switch len(segments) { case 2: // This is a new key and value. segmentKey = strings.TrimSpace(segments[0]) segmentValue = strings.TrimSpace(segments[1]) previousKey = segmentKey case 1: // This is another value for the previous key. segmentKey = previousKey segmentValue = strings.TrimSpace(segments[0]) default: continue } // Ignore empty lines. if segmentValue == "" { continue } // Parse and assign value to struct. switch segmentKey { case "InterfaceIndex": newIf.InterfaceIndex = segmentValue case "IPv6Address": newIf.IPv6Address = net.ParseIP(segmentValue) case "IPv4Address": newIf.IPv4Address = net.ParseIP(segmentValue) case "IPv6DefaultGateway": newIf.IPv6DefaultGateway = net.ParseIP(segmentValue) case "IPv4DefaultGateway": newIf.IPv4DefaultGateway = net.ParseIP(segmentValue) case "DNSServer": newIP := net.ParseIP(segmentValue) if newIP != nil { newIf.DNSServer = append(newIf.DNSServer, newIP) } } } // Get Search Scopes for this interface. if newIf.InterfaceIndex != "" { dnsConfigData, err := osdetail.RunPowershellCmd(fmt.Sprintf( "Get-DnsClient -InterfaceIndex %s | ConvertTo-Json -Depth 1", newIf.InterfaceIndex, )) if err != nil { log.Warningf("netenv: failed to get dns server config data: %s", err) } else { // Parse data into struct. dnsConfig := &dnsServerConfig{} err := json.Unmarshal([]byte(dnsConfigData), dnsConfig) if err != nil { log.Warningf("netenv: failed to get dns server config data: %s", err) } else { newIf.DNSServerConfig = dnsConfig } } } else { log.Warning("netenv: could not get dns server config data, because default interface index is missing") } // Assign new value to cache and return. defaultInterface = newIf return defaultInterface } ================================================ FILE: service/netenv/environment_windows_test.go ================================================ package netenv import "testing" func TestWindowsEnvironment(t *testing.T) { defaultIf := getDefaultInterface() if defaultIf == nil { t.Error("failed to get default interface") } t.Logf("default interface: %+v", defaultIf) } ================================================ FILE: service/netenv/icmp_listener.go ================================================ package netenv import ( "net" "sync" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/packet" ) /* This ICMP listening system is a simple system for components to listen to ICMP packets via the firewall. The main use case for this is to receive ICMP packets that are not always delivered correctly, or need special permissions and or sockets to receive them. This is the case when doing a traceroute. In order to keep it simple, the system is only designed to be used by one "user" at a time. Further calls to ListenToICMP will wait for the previous operation to complete. */ var ( // listenICMPLock locks the ICMP listening system for one user at a time. listenICMPLock sync.Mutex // listenICMPEnabled defines whether or not the firewall should submit ICMP // packets to this interface. listenICMPEnabled = abool.New() // listenICMPInput is created for every use of the ICMP listenting system. listenICMPInput chan packet.Packet listenICMPInputTargetIP net.IP listenICMPInputLock sync.Mutex ) // ListenToICMP returns a new channel for listenting to icmp packets. Please // note that any icmp packet will be passed and filtering must be done on // the side of the caller. The caller must call the returned done function when // done with the listener. func ListenToICMP(targetIP net.IP) (packets chan packet.Packet, done func()) { // Lock for single use. listenICMPLock.Lock() // Create new input channel. listenICMPInputLock.Lock() listenICMPInput = make(chan packet.Packet, 100) listenICMPInputTargetIP = targetIP listenICMPEnabled.Set() listenICMPInputLock.Unlock() return listenICMPInput, func() { // Release for someone else to use. defer listenICMPLock.Unlock() // Close input channel. listenICMPInputLock.Lock() listenICMPEnabled.UnSet() listenICMPInputTargetIP = nil close(listenICMPInput) listenICMPInputLock.Unlock() } } // SubmitPacketToICMPListener checks if an ICMP packet should be submitted to // the listener. If so, it is submitted right away. The function returns // whether or not the packet should be submitted, not if it was successful. func SubmitPacketToICMPListener(pkt packet.Packet) (submitted bool) { // Hot path. if !listenICMPEnabled.IsSet() { return false } // Slow path. return submitPacketToICMPListenerSlow(pkt) } func submitPacketToICMPListenerSlow(pkt packet.Packet) (submitted bool) { // Make sure the payload is available. if err := pkt.LoadPacketData(); err != nil { log.Warningf("netenv: failed to get payload for ICMP listener: %s", err) return false } // Send to input channel. listenICMPInputLock.Lock() defer listenICMPInputLock.Unlock() // Check if still enabled. if !listenICMPEnabled.IsSet() { return false } // Only listen for outbound packets to the target IP. if pkt.IsOutbound() && listenICMPInputTargetIP != nil && !pkt.Info().Dst.Equal(listenICMPInputTargetIP) { return false } // Send to channel, if possible. select { case listenICMPInput <- pkt: default: log.Warning("netenv: failed to send packet payload to ICMP listener: channel full") } return true } ================================================ FILE: service/netenv/init_test.go ================================================ package netenv import ( "fmt" "os" "path/filepath" "testing" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/configure" "github.com/safing/portmaster/service/ui" "github.com/safing/portmaster/service/updates" ) type testInstance struct { db *dbmodule.DBModule api *api.API config *config.Config intelUpdates *updates.Updater } var _ instance = &testInstance{} func (stub *testInstance) IntelUpdates() *updates.Updater { return stub.intelUpdates } func (stub *testInstance) API() *api.API { return stub.api } func (stub *testInstance) Config() *config.Config { return stub.config } func (stub *testInstance) Notifications() *notifications.Notifications { return nil } func (stub *testInstance) Ready() bool { return true } func (stub *testInstance) Restart() {} func (stub *testInstance) Shutdown() {} func (stub *testInstance) SetCmdLineOperation(f func() error) {} func (stub *testInstance) UI() *ui.UI { return nil } func (stub *testInstance) DataDir() string { return _dataDir } var _dataDir string func runTest(m *testing.M) error { var err error // Create a temporary directory for testing _dataDir, err = os.MkdirTemp("", "") if err != nil { return fmt.Errorf("failed to create temporary data directory: %w", err) } defer func() { _ = os.RemoveAll(_dataDir) }() // Initialize the Intel update configuration intelUpdateConfig := updates.Config{ Name: configure.DefaultIntelIndexName, Directory: filepath.Join(_dataDir, "test_intel"), DownloadDirectory: filepath.Join(_dataDir, "test_download_intel"), PurgeDirectory: filepath.Join(_dataDir, "test_upgrade_obsolete_intel"), IndexURLs: configure.DefaultIntelIndexURLs, IndexFile: "index.json", AutoCheck: true, AutoDownload: true, AutoApply: true, } // Set the default API listen address api.SetDefaultAPIListenAddress("0.0.0.0:8080") // Initialize the instance with the necessary components stub := &testInstance{} stub.db, err = dbmodule.New(stub) if err != nil { return fmt.Errorf("failed to create database: %w", err) } stub.config, err = config.New(stub) if err != nil { return fmt.Errorf("failed to create config: %w", err) } stub.api, err = api.New(stub) if err != nil { return fmt.Errorf("failed to create api: %w", err) } stub.intelUpdates, err = updates.New(stub, "Intel Updater", intelUpdateConfig) if err != nil { return fmt.Errorf("failed to create updates: %w", err) } err = stub.db.Start() if err != nil { return fmt.Errorf("Failed to start database: %w", err) } err = stub.config.Start() if err != nil { return fmt.Errorf("Failed to start config: %w", err) } err = stub.api.Start() if err != nil { return fmt.Errorf("Failed to start api: %w", err) } err = stub.intelUpdates.Start() if err != nil { return fmt.Errorf("Failed to start updates: %w", err) } _, err = New(stub) if err != nil { return fmt.Errorf("failed to initialize module %w", err) } m.Run() return nil } func TestMain(m *testing.M) { if err := runTest(m); err != nil { fmt.Printf("%s", err) os.Exit(1) } } ================================================ FILE: service/netenv/location.go ================================================ package netenv import ( "errors" "fmt" "net" "sort" "sync" "time" "github.com/google/gopacket/layers" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/network/packet" ) var ( // locationTestingIPv4 holds the IP address of the server that should be // tracerouted to find the location of the device. The ping will never reach // the destination in most cases. // The selection of this IP requires sensitivity, as the IP address must be // far enough away to produce good results. // At the same time, the IP address should be common and not raise attention. locationTestingIPv4 = "1.1.1.1" locationTestingIPv4Addr *net.IPAddr locations = &DeviceLocations{} locationsLock sync.Mutex gettingLocationsLock sync.Mutex locationNetworkChangedFlag = GetNetworkChangedFlag() ) func prepLocation() (err error) { locationTestingIPv4Addr, err = net.ResolveIPAddr("ip", locationTestingIPv4) return err } // DeviceLocations holds multiple device locations. type DeviceLocations struct { All []*DeviceLocation } // Best returns the best (most accurate) device location. func (dls *DeviceLocations) Best() *DeviceLocation { if len(dls.All) > 0 { return dls.All[0] } return nil } // BestV4 returns the best (most accurate) IPv4 device location. func (dls *DeviceLocations) BestV4() *DeviceLocation { for _, loc := range dls.All { if loc.IPVersion == packet.IPv4 { return loc } } return nil } // BestV6 returns the best (most accurate) IPv6 device location. func (dls *DeviceLocations) BestV6() *DeviceLocation { for _, loc := range dls.All { if loc.IPVersion == packet.IPv6 { return loc } } return nil } // Copy creates a copy of the locations, but not the individual entries. func (dls *DeviceLocations) Copy() *DeviceLocations { cp := &DeviceLocations{ All: make([]*DeviceLocation, len(locations.All)), } copy(cp.All, locations.All) return cp } // AddLocation adds a location. func (dls *DeviceLocations) AddLocation(dl *DeviceLocation) { if dls == nil { return } // Add to locations, if better. var exists bool for i, existing := range dls.All { if (dl.IP == nil && existing.IP == nil) || dl.IP.Equal(existing.IP) { exists = true if dl.IsMoreAccurateThan(existing) { // Replace dls.All[i] = dl break } } } if !exists { dls.All = append(dls.All, dl) } // Sort locations. sort.Sort(sortLocationsByAccuracy(dls.All)) log.Debugf("netenv: added new device location to IPv%d scope: %s from %s", dl.IPVersion, dl, dl.Source) } // DeviceLocation represents a single IP and metadata. It must not be changed // once created. type DeviceLocation struct { IP net.IP IPVersion packet.IPVersion Location *geoip.Location Source DeviceLocationSource SourceAccuracy int } // IsMoreAccurateThan checks if the device location is more accurate than the // given one. func (dl *DeviceLocation) IsMoreAccurateThan(other *DeviceLocation) bool { switch { case dl.SourceAccuracy > other.SourceAccuracy: // Higher source accuracy is better. return true case dl.IP != nil && other.IP == nil: // Location based on IP is better than without. return true case dl.Location.AutonomousSystemNumber != 0 && other.Location.AutonomousSystemNumber == 0: // Having an ASN is better than having none. return true case dl.Location.Country.Code != "" && other.Location.Country.Code == "": // Having a Country is better than having none. return true case (dl.Location.Coordinates.Latitude != 0 || dl.Location.Coordinates.Longitude != 0) && other.Location.Coordinates.Latitude == 0 && other.Location.Coordinates.Longitude == 0: // Having Coordinates is better than having none. return true case dl.Location.Coordinates.AccuracyRadius < other.Location.Coordinates.AccuracyRadius: // Higher geo accuracy is better. return true } return false } // LocationOrNil or returns the geoip location, or nil if not present. func (dl *DeviceLocation) LocationOrNil() *geoip.Location { if dl == nil { return nil } return dl.Location } func (dl *DeviceLocation) String() string { switch { case dl == nil: return "" case dl.Location == nil: return dl.IP.String() case dl.Source == SourceTimezone: return fmt.Sprintf( "TZ(%.0f/%.0f)", dl.Location.Coordinates.Latitude, dl.Location.Coordinates.Longitude, ) default: return fmt.Sprintf( "%s (AS%d in %s - %s)", dl.IP, dl.Location.AutonomousSystemNumber, dl.Location.Country.Name, dl.Location.Country.Code, ) } } // DeviceLocationSource is a location source. type DeviceLocationSource string // Location Sources. const ( SourceInterface DeviceLocationSource = "interface" SourcePeer DeviceLocationSource = "peer" SourceUPNP DeviceLocationSource = "upnp" SourceTraceroute DeviceLocationSource = "traceroute" SourceTimezone DeviceLocationSource = "timezone" SourceOther DeviceLocationSource = "other" ) // Accuracy returns the location accuracy of the source. func (dls DeviceLocationSource) Accuracy() int { switch dls { case SourceInterface: return 6 case SourcePeer: return 5 case SourceUPNP: return 4 case SourceTraceroute: return 3 case SourceOther: return 2 case SourceTimezone: return 1 default: return 0 } } type sortLocationsByAccuracy []*DeviceLocation func (a sortLocationsByAccuracy) Len() int { return len(a) } func (a sortLocationsByAccuracy) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a sortLocationsByAccuracy) Less(i, j int) bool { return !a[j].IsMoreAccurateThan(a[i]) } // SetInternetLocation provides the location management system with a possible Internet location. func SetInternetLocation(ip net.IP, source DeviceLocationSource) (dl *DeviceLocation, ok bool) { locationsLock.Lock() defer locationsLock.Unlock() return locations.AddIP(ip, source) } // AddIP adds a new location based on the given IP. func (dls *DeviceLocations) AddIP(ip net.IP, source DeviceLocationSource) (dl *DeviceLocation, ok bool) { // Check if IP is global. if netutils.GetIPScope(ip) != netutils.Global { return nil, false } // Create new location. loc := &DeviceLocation{ IP: ip, Source: source, SourceAccuracy: source.Accuracy(), } if v4 := ip.To4(); v4 != nil { loc.IPVersion = packet.IPv4 } else { loc.IPVersion = packet.IPv6 } // Get geoip information, but continue if it fails. geoLoc, err := geoip.GetLocation(ip) if err != nil { log.Warningf("netenv: failed to get geolocation data of %s (from %s): %s", ip, source, err) return nil, false } // Only use location if there is data for it. if geoLoc.Country.Code == "" { return nil, false } loc.Location = geoLoc dls.AddLocation(loc) return loc, true } // GetApproximateInternetLocation returns the approximate Internet location. // Deprecated: Please use GetInternetLocation instead. func GetApproximateInternetLocation() (net.IP, error) { loc, ok := GetInternetLocation() if !ok || loc.Best() == nil { return nil, errors.New("no device location data available") } return loc.Best().IP, nil } // GetInternetLocation returns the possible device locations. func GetInternetLocation() (deviceLocations *DeviceLocations, ok bool) { gettingLocationsLock.Lock() defer gettingLocationsLock.Unlock() // Check if the network changed, if not, return cache. if !locationNetworkChangedFlag.IsSet() { locationsLock.Lock() defer locationsLock.Unlock() return locations.Copy(), true } locationNetworkChangedFlag.Refresh() // Create new location list. dls := &DeviceLocations{} log.Debug("netenv: getting new device locations") // Check interfaces for global addresses. v4ok, v6ok := getLocationFromInterfaces(dls) // Try other methods for missing locations. if !v4ok { _, err := getLocationFromTraceroute(dls) if err != nil { log.Warningf("netenv: failed to get IPv4 device location from traceroute: %s", err) } else { v4ok = true } // Get location from timezone as final fallback. if !v4ok { getLocationFromTimezone(dls, packet.IPv4) } } if !v6ok && IPv6Enabled() { // TODO: Find more ways to get IPv6 device location // Get location from timezone as final fallback. getLocationFromTimezone(dls, packet.IPv6) } // As a last guard, make sure there is at least one location in the list. if len(dls.All) == 0 { getLocationFromTimezone(dls, packet.IPv4) } // Set new locations. locationsLock.Lock() defer locationsLock.Unlock() locations = dls // Return gathered locations. return locations.Copy(), true } func getLocationFromInterfaces(dls *DeviceLocations) (v4ok, v6ok bool) { globalIPv4, globalIPv6, err := GetAssignedGlobalAddresses() if err != nil { log.Warningf("netenv: location: failed to get assigned global addresses: %s", err) return false, false } for _, ip := range globalIPv4 { if _, ok := dls.AddIP(ip, SourceInterface); ok { v4ok = true } } for _, ip := range globalIPv6 { if _, ok := dls.AddIP(ip, SourceInterface); ok { v6ok = true } } return } // TODO: Check feasibility of getting the external IP via UPnP. /* func getLocationFromUPnP() (ok bool) { // Endoint: urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress // A first test showed that a router did offer that endpoint, but did not // return an IP address. return false } */ func getLocationFromTraceroute(dls *DeviceLocations) (dl *DeviceLocation, err error) { // Create connection. conn, err := icmp.ListenPacket("ip4:icmp", "") if err != nil { return nil, fmt.Errorf("failed to open icmp conn: %w", err) } v4Conn := conn.IPv4PacketConn() // Generate a random ID for the ICMP packets. generatedID, err := rng.Number(0xFFFF) // uint16 if err != nil { return nil, fmt.Errorf("failed to generate icmp msg ID: %w", err) } msgID := int(generatedID) var msgSeq int // Create ICMP message body. pingMessage := icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: msgID, Seq: msgSeq, // Is increased before marshalling. Data: []byte{}, }, } maxHops := 4 // add one for every reply that is not global // Get additional listener for ICMP messages via the firewall. icmpPacketsViaFirewall, doneWithListeningToICMP := ListenToICMP(locationTestingIPv4Addr.IP) defer doneWithListeningToICMP() nextHop: for i := 1; i <= maxHops; i++ { minSeq := msgSeq + 1 repeatHop: for j := 1; j <= 2; j++ { // Try every hop twice. // Increase sequence number. msgSeq++ pingMessage.Body.(*icmp.Echo).Seq = msgSeq //nolint:forcetypeassert // Can only be *icmp.Echo. // Make packet data. pingPacket, err := pingMessage.Marshal(nil) if err != nil { return nil, fmt.Errorf("failed to build icmp packet: %w", err) } // Set TTL on IP packet. err = v4Conn.SetTTL(i) if err != nil { return nil, fmt.Errorf("failed to set icmp packet TTL: %w", err) } // Send ICMP packet. // Try to send three times, as this can be flaky. sendICMP: for range 3 { _, err = conn.WriteTo(pingPacket, locationTestingIPv4Addr) if err == nil { break sendICMP } time.Sleep(30 * time.Millisecond) } if err != nil { return nil, fmt.Errorf("failed to send icmp packet: %w", err) } // Listen for replies of the ICMP packet. listen: for { remoteIP, icmpPacket, ok := recvICMP(i, icmpPacketsViaFirewall) if !ok { // Timed out. continue repeatHop } // Pre-filter by message type. switch icmpPacket.TypeCode.Type() { case layers.ICMPv4TypeEchoReply: // Check if the ID and sequence match. if icmpPacket.Id != uint16(msgID) { continue listen } if icmpPacket.Seq < uint16(minSeq) { continue listen } // We received a reply, so we did not trigger a time exceeded response on the way. // This means we were not able to find the nearest router to us. return nil, errors.New("received final echo reply without time exceeded messages") case layers.ICMPv4TypeDestinationUnreachable, layers.ICMPv4TypeTimeExceeded: // Continue processing. default: continue listen } // Parse copy of origin icmp packet that triggered the error. if len(icmpPacket.Payload) != ipv4.HeaderLen+8 { continue listen } originalMessage, err := icmp.ParseMessage(1, icmpPacket.Payload[ipv4.HeaderLen:]) if err != nil { continue listen } originalEcho, ok := originalMessage.Body.(*icmp.Echo) if !ok { continue listen } // Check if the ID and sequence match. if originalEcho.ID != msgID { continue listen } if originalEcho.Seq < minSeq { continue listen } // React based on message type. switch icmpPacket.TypeCode.Type() { case layers.ICMPv4TypeDestinationUnreachable: // We have received a valid destination unreachable response, abort. return nil, errors.New("destination unreachable") case layers.ICMPv4TypeTimeExceeded: // We have received a valid time exceeded error. // If message came from a global unicast, us it! if netutils.GetIPScope(remoteIP) == netutils.Global { dl, ok := dls.AddIP(remoteIP, SourceTraceroute) if !ok { return nil, errors.New("invalid IP address") } return dl, nil } // Add one max hop for every reply that was not global. maxHops++ // Otherwise, continue. continue nextHop } } } } // We did not receive anything actionable. return nil, errors.New("did not receive any actionable ICMP reply") } func recvICMP(currentHop int, icmpPacketsViaFirewall chan packet.Packet) ( remoteIP net.IP, imcpPacket *layers.ICMPv4, ok bool, ) { for { select { case pkt := <-icmpPacketsViaFirewall: if pkt.IsOutbound() { continue } if pkt.Layers() == nil { continue } icmpLayer := pkt.Layers().Layer(layers.LayerTypeICMPv4) if icmpLayer == nil { continue } icmp4, ok := icmpLayer.(*layers.ICMPv4) if !ok { continue } return pkt.Info().RemoteIP(), icmp4, true case <-time.After(time.Duration(currentHop*20+100) * time.Millisecond): return nil, nil, false } } } func getLocationFromTimezone(dls *DeviceLocations, ipVersion packet.IPVersion) { // Create base struct. tzLoc := &DeviceLocation{ IPVersion: ipVersion, Location: &geoip.Location{}, Source: SourceTimezone, SourceAccuracy: SourceTimezone.Accuracy(), } // Calculate longitude based on current timezone. _, offsetSeconds := time.Now().Zone() tzLoc.Location.Coordinates.AccuracyRadius = 1000 tzLoc.Location.Coordinates.Latitude = 48 tzLoc.Location.Coordinates.Longitude = float64(offsetSeconds) / 43200 * 180 dls.AddLocation(tzLoc) } ================================================ FILE: service/netenv/location_default.go ================================================ //go:build !windows package netenv import "net" func newICMPListener(_ string) (net.PacketConn, error) { //nolint:unused,deadcode // TODO: clean with Windows code later. return net.ListenPacket("ip4:icmp", "0.0.0.0") } ================================================ FILE: service/netenv/location_test.go ================================================ package netenv import ( "flag" "testing" ) var privileged bool func init() { flag.BoolVar(&privileged, "privileged", false, "run tests that require root/admin privileges") } func TestGetInternetLocation(t *testing.T) { t.Parallel() if testing.Short() { t.Skip() } if !privileged { t.Skip("skipping privileged test, active with -privileged argument") } loc, ok := GetInternetLocation() if !ok { t.Fatal("GetApproximateInternetLocation failed") } t.Logf("GetApproximateInternetLocation: %+v", loc) } ================================================ FILE: service/netenv/location_windows.go ================================================ package netenv import ( "context" "fmt" "net" "os" "syscall" "unsafe" ) // Windows specific constants for the WSAIoctl interface. //nolint:golint,stylecheck const ( SIO_RCVALL = syscall.IOC_IN | syscall.IOC_VENDOR | 1 RCVALL_OFF = 0 RCVALL_ON = 1 RCVALL_SOCKETLEVELONLY = 2 RCVALL_IPLEVEL = 3 ) func newICMPListener(address string) (net.PacketConn, error) { // This is an attempt to work around the problem described here: // https://github.com/golang/go/issues/38427 // First, get the correct local interface address, as SIO_RCVALL can't be set on a 0.0.0.0 listeners. dialedConn, err := net.Dial("ip4:icmp", address) if err != nil { return nil, fmt.Errorf("failed to dial: %s", err) } localAddr := dialedConn.LocalAddr() dialedConn.Close() // Configure the setup routine in order to extract the socket handle. var socketHandle syscall.Handle cfg := net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { return c.Control(func(s uintptr) { socketHandle = syscall.Handle(s) }) }, } // Bind to interface. conn, err := cfg.ListenPacket(context.Background(), "ip4:icmp", localAddr.String()) if err != nil { return nil, err } // Set socket option to receive all packets, such as ICMP error messages. // This is somewhat dirty, as there is guarantee that socketHandle is still valid. // WARNING: The Windows Firewall might just drop the incoming packets you might want to receive. unused := uint32(0) // Documentation states that this is unused, but WSAIoctl fails without it. flag := uint32(RCVALL_IPLEVEL) size := uint32(unsafe.Sizeof(flag)) err = syscall.WSAIoctl(socketHandle, SIO_RCVALL, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &unused, nil, 0) if err != nil { return nil, fmt.Errorf("failed to set socket to listen to all packests: %s", os.NewSyscallError("WSAIoctl", err)) } return conn, nil } ================================================ FILE: service/netenv/main.go ================================================ package netenv import ( "errors" "sync/atomic" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/updates" ) // Event Names. const ( ModuleName = "netenv" NetworkChangedEvent = "network changed" OnlineStatusChangedEvent = "online status changed" ) type NetEnv struct { m *mgr.Manager instance instance EventNetworkChange *mgr.EventMgr[struct{}] EventOnlineStatusChange *mgr.EventMgr[OnlineStatus] } func (ne *NetEnv) Manager() *mgr.Manager { return ne.m } func (ne *NetEnv) Start() error { ne.m.Go( "monitor network changes", monitorNetworkChanges, ) ne.m.Go( "monitor online status", monitorOnlineStatus, ) return nil } func (ne *NetEnv) Stop() error { return nil } func prep() error { checkForIPv6Stack() if err := registerAPIEndpoints(); err != nil { return err } if err := prepOnlineStatus(); err != nil { return err } return prepLocation() } var ipv6Enabled = abool.NewBool(true) // IPv6Enabled returns whether the device has an active IPv6 stack. // This is only checked once on startup in order to maintain consistency. func IPv6Enabled() bool { return ipv6Enabled.IsSet() } func checkForIPv6Stack() { _, v6IPs, err := GetAssignedAddresses() if err != nil { log.Warningf("netenv: failed to get assigned addresses to check for ipv6 stack: %s", err) return } // Set IPv6 as enabled if any IPv6 addresses are found. ipv6Enabled.SetTo(len(v6IPs) > 0) } var ( module *NetEnv shimLoaded atomic.Bool ) // New returns a new NetEnv module. func New(instance instance) (*NetEnv, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("NetEnv") module = &NetEnv{ m: m, instance: instance, EventNetworkChange: mgr.NewEventMgr[struct{}]("network change", m), EventOnlineStatusChange: mgr.NewEventMgr[OnlineStatus]("online status change", m), } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface { IntelUpdates() *updates.Updater } ================================================ FILE: service/netenv/main_test.go ================================================ package netenv // func TestMain(m *testing.M) { // pmtesting.TestMain(m, module) // } ================================================ FILE: service/netenv/network-change.go ================================================ package netenv import ( "bytes" "crypto/sha1" "io" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/mgr" ) var ( networkChangeCheckTrigger = make(chan struct{}, 1) networkChangedBroadcastFlag = utils.NewBroadcastFlag() ) // GetNetworkChangedFlag returns a flag to be notified about a network change. func GetNetworkChangedFlag() *utils.Flag { return networkChangedBroadcastFlag.NewFlag() } func notifyOfNetworkChange() { networkChangedBroadcastFlag.NotifyAndReset() module.EventNetworkChange.Submit(struct{}{}) } // TriggerNetworkChangeCheck triggers a network change check. func TriggerNetworkChangeCheck() { select { case networkChangeCheckTrigger <- struct{}{}: default: } } func monitorNetworkChanges(ctx *mgr.WorkerCtx) error { var lastNetworkChecksum []byte serviceLoop: for { trigger := false var ticker *time.Ticker if Online() { ticker = monitorNetworkChangeOnlineTicker } else { ticker = monitorNetworkChangeOfflineTicker } // wait for trigger select { case <-ctx.Done(): return nil case <-networkChangeCheckTrigger: // don't fall through because the online change check // triggers the networkChangeCheck this way. If we would set // trigger == true we would trigger the online check again // resulting in a loop of pointless checks. case <-ticker.C: trigger = true } // check network for changes // create hashsum of current network config hasher := sha1.New() //nolint:gosec // not used for security interfaces, err := osGetNetworkInterfaces() if err != nil { log.Warningf("netenv: failed to get interfaces: %s", err) continue } for _, iface := range interfaces { _, _ = io.WriteString(hasher, iface.Name) // log.Tracef("adding: %s", iface.Name) _, _ = io.WriteString(hasher, iface.Flags.String()) // log.Tracef("adding: %s", iface.Flags.String()) addrs, err := iface.Addrs() if err != nil { log.Warningf("netenv: failed to get addrs from interface %s: %s", iface.Name, err) continue } for _, addr := range addrs { _, _ = io.WriteString(hasher, addr.String()) // log.Tracef("adding: %s", addr.String()) } } newChecksum := hasher.Sum(nil) // compare checksum with last if !bytes.Equal(lastNetworkChecksum, newChecksum) { if len(lastNetworkChecksum) == 0 { lastNetworkChecksum = newChecksum continue serviceLoop } lastNetworkChecksum = newChecksum if trigger { TriggerOnlineStatusInvestigation() } notifyOfNetworkChange() } } } ================================================ FILE: service/netenv/notes.md ================================================ Intel: - First ever request: use first resolver as selected - If resolver fails: - stop all requesting - get network status - if failed: do nothing, return offline error - check list front to back, use first resolver that resolves one.one.one.one correctly NetEnv: - check for intercepted HTTP Request requests - if fails on: - connection establishment: OFFLINE - - check for intercepted HTTPS Request requests - check for intercepted DNS requests ================================================ FILE: service/netenv/online-status.go ================================================ package netenv import ( "context" "errors" "fmt" "net" "net/http" "net/url" "sync" "sync/atomic" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network/netutils" ) // OnlineStatus represent a state of connectivity to the Internet. type OnlineStatus uint8 // Online Status Values. const ( StatusUnknown OnlineStatus = 0 StatusOffline OnlineStatus = 1 StatusLimited OnlineStatus = 2 // local network only StatusPortal OnlineStatus = 3 // there seems to be an internet connection, but we are being intercepted, possibly by a captive portal StatusSemiOnline OnlineStatus = 4 // we seem to online, but without full connectivity StatusOnline OnlineStatus = 5 ) // Online Status and Resolver. var ( PortalTestIP = net.IPv4(192, 0, 2, 1) PortalTestURL = fmt.Sprintf("http://%s/", PortalTestIP) // IP address -> 100.127.247.245 is a special ip used by the android VPN service. Must be ignored during online check. IgnoreIPsInOnlineStatusCheck = []net.IP{net.IPv4(100, 127, 247, 245)} DNSTestDomain = "online-check.safing.io." DNSTestExpectedIP = net.IPv4(0, 65, 67, 75) // Ascii: \0ACK DNSTestQueryFunc func(ctx context.Context, fdqn string) (ips []net.IP, ok bool, err error) ConnectedToSPN = abool.New() ConnectedToDNS = abool.New() // SpecialCaptivePortalDomain is the domain name used to point to the detected captive portal IP // or the captive portal test IP. The default value should be overridden by the resolver package, // which defines the custom internal domain name to use. SpecialCaptivePortalDomain = "captiveportal.invalid." // ConnectivityDomains holds all connectivity domains. This slice must not be modified. ConnectivityDomains = []string{ SpecialCaptivePortalDomain, // Windows "dns.msftncsi.com.", // DNS Check "msftncsi.com.", // Older "www.msftncsi.com.", "microsoftconnecttest.com.", // Newer "www.microsoftconnecttest.com.", "ipv6.microsoftconnecttest.com.", // https://de.wikipedia.org/wiki/Captive_Portal // https://docs.microsoft.com/en-us/windows-hardware/drivers/mobilebroadband/captive-portals // TODO: read value from registry: HKLM:\SYSTEM\CurrentControlSet\Services\NlaSvc\Parameters\Internet // Apple "captive.apple.com.", // https://de.wikipedia.org/wiki/Captive_Portal // Linux "connectivity-check.ubuntu.com.", // Ubuntu "nmcheck.gnome.org.", // Gnome DE "network-test.debian.org.", // Debian "204.pop-os.org.", // Pop OS "conncheck.opensuse.org.", // OpenSUSE "ping.archlinux.org", // Arch // There are probably a lot more domains for all the Linux Distro/DE Variants. Please raise issues and/or submit PRs! // https://github.com/solus-project/budgie-desktop/issues/807 // https://www.lguruprasad.in/blog/2015/07/21/enabling-captive-portal-detection-in-gnome-3-14-on-debian-jessie/ // TODO: read value from NetworkManager config: /etc/NetworkManager/conf.d/*.conf // Android "connectivitycheck.gstatic.com.", // https://de.wikipedia.org/wiki/Captive_Portal // Other "neverssl.com.", // Common Community Service "detectportal.firefox.com.", // Firefox } parsedPortalTestURL *url.URL ) func prepOnlineStatus() (err error) { parsedPortalTestURL, err = url.Parse(PortalTestURL) return err } // IsConnectivityDomain checks whether the given domain (fqdn) is used for any // connectivity related network connections and should always be resolved using // the network assigned DNS server. func IsConnectivityDomain(domain string) bool { if domain == "" { return false } for _, connectivityDomain := range ConnectivityDomains { if domain == connectivityDomain { return true } } // Check for captive portal domain. captivePortal := GetCaptivePortal() if captivePortal.Domain != "" && domain == captivePortal.Domain { return true } return false } func (os OnlineStatus) String() string { switch os { case StatusOffline: return "Offline" case StatusLimited: return "Limited" case StatusPortal: return "Portal" case StatusSemiOnline: return "SemiOnline" case StatusOnline: return "Online" case StatusUnknown: fallthrough default: return "Unknown" } } var ( onlineStatus *int32 onlineStatusQuickCheck = abool.NewBool(false) onlineStatusInvestigationTrigger = make(chan struct{}, 1) onlineStatusInvestigationInProgress = abool.NewBool(false) onlineStatusInvestigationWg sync.WaitGroup onlineStatusNotification *notifications.Notification captivePortal = &CaptivePortal{} captivePortalLock sync.Mutex captivePortalNotification *notifications.Notification ) // CaptivePortal holds information about a detected captive portal. type CaptivePortal struct { URL string Domain string IP net.IP } func init() { var onlineStatusValue int32 onlineStatus = &onlineStatusValue } // Online returns true if online status is either SemiOnline or Online. func Online() bool { return onlineStatusQuickCheck.IsSet() } // GetOnlineStatus returns the current online stats. func GetOnlineStatus() OnlineStatus { return OnlineStatus(atomic.LoadInt32(onlineStatus)) } // CheckAndGetOnlineStatus triggers a new online status check and returns the result. func CheckAndGetOnlineStatus() OnlineStatus { // trigger new investigation TriggerOnlineStatusInvestigation() // wait for completion onlineStatusInvestigationWg.Wait() // return current status return GetOnlineStatus() } func updateOnlineStatus(status OnlineStatus, portalURL *url.URL, comment string) { changed := false // Update online status. currentStatus := atomic.LoadInt32(onlineStatus) if status != OnlineStatus(currentStatus) && atomic.CompareAndSwapInt32(onlineStatus, currentStatus, int32(status)) { // status changed! onlineStatusQuickCheck.SetTo( status == StatusOnline || status == StatusSemiOnline, ) changed = true } // Update captive portal. setCaptivePortal(portalURL) // Trigger events. if changed { module.EventOnlineStatusChange.Submit(status) if status == StatusPortal { log.Infof(`netenv: setting online status to %s at "%s" (%s)`, status, portalURL, comment) } else { log.Infof("netenv: setting online status to %s (%s)", status, comment) } TriggerNetworkChangeCheck() // Notify user. notifyOnlineStatus(status) // Trigger update check when coming (semi) online. if Online() { module.instance.IntelUpdates().EventResourcesUpdated.Submit(struct{}{}) } } } func notifyOnlineStatus(status OnlineStatus) { var eventID, title, message string // Check if status is worth notifying. switch status { //nolint:exhaustive // Checking for selection only. case StatusOffline: eventID = "netenv:online-status:offline" title = "Device is Offline" message = "Portmaster did not detect any network connectivity." case StatusLimited: eventID = "netenv:online-status:limited" title = "Limited network connectivity." message = "Portmaster did detect local network connectivity, but could not detect connectivity to the Internet." default: // Delete notification, if present. if onlineStatusNotification != nil { onlineStatusNotification.Delete() onlineStatusNotification = nil } return } // Update notification if not present or online status changed. switch { case onlineStatusNotification == nil: // Continue creating new notification. case onlineStatusNotification.EventID == eventID: // Notification stays the same, stick with the old one. return default: // Delete old notification before triggering updated one. onlineStatusNotification.Delete() } // Create update status notification. onlineStatusNotification = notifications.Notify(¬ifications.Notification{ EventID: eventID, Type: notifications.Info, Title: title, Message: message, }) } func setCaptivePortal(portalURL *url.URL) { captivePortalLock.Lock() defer captivePortalLock.Unlock() // Delete captive portal if no url is supplied. if portalURL == nil { captivePortal = &CaptivePortal{} if captivePortalNotification != nil { captivePortalNotification.Delete() captivePortalNotification = nil } return } // Only set captive portal once per detection. if captivePortal.URL != "" { return } // Compile captive portal data. captivePortal = &CaptivePortal{ URL: portalURL.String(), } portalIP := net.ParseIP(portalURL.Hostname()) if portalIP != nil { captivePortal.IP = portalIP captivePortal.Domain = SpecialCaptivePortalDomain } else { captivePortal.Domain = portalURL.Hostname() } // Notify user about portal. captivePortalNotification = notifications.Notify(¬ifications.Notification{ EventID: "netenv:captive-portal", Type: notifications.Info, Title: "Captive Portal Detected", Message: "The Portmaster detected a captive portal. You might experience limited network connectivity until the portal is handled.", ShowOnSystem: true, EventData: captivePortal, AvailableActions: []*notifications.Action{ { Text: "Open Portal", Type: notifications.ActionTypeOpenURL, Payload: captivePortal.URL, }, { ID: "ack", Text: "Ignore", }, }, }) } // GetCaptivePortal returns the current captive portal. The returned struct must not be edited. func GetCaptivePortal() *CaptivePortal { captivePortalLock.Lock() defer captivePortalLock.Unlock() return captivePortal } // ReportSuccessfulConnection hints the online status monitoring system that a connection attempt was successful. func ReportSuccessfulConnection() { if !onlineStatusQuickCheck.IsSet() { TriggerOnlineStatusInvestigation() } } // ReportFailedConnection hints the online status monitoring system that a connection attempt has failed. This function has extremely low overhead and may be called as much as wanted. func ReportFailedConnection() { if onlineStatusQuickCheck.IsSet() { TriggerOnlineStatusInvestigation() } } // TriggerOnlineStatusInvestigation manually triggers the online status check. // It will not trigger it again, if it is already in progress. func TriggerOnlineStatusInvestigation() { if onlineStatusInvestigationInProgress.SetToIf(false, true) { onlineStatusInvestigationWg.Add(1) } select { case onlineStatusInvestigationTrigger <- struct{}{}: default: } } func monitorOnlineStatus(ctx *mgr.WorkerCtx) error { TriggerOnlineStatusInvestigation() for { // wait for trigger select { case <-ctx.Done(): return nil case <-onlineStatusInvestigationTrigger: case <-getDynamicStatusTrigger(): } // enable waiting if onlineStatusInvestigationInProgress.SetToIf(false, true) { onlineStatusInvestigationWg.Add(1) } checkOnlineStatus(ctx.Ctx()) // finished! onlineStatusInvestigationWg.Done() onlineStatusInvestigationInProgress.UnSet() } } func getDynamicStatusTrigger() <-chan time.Time { switch GetOnlineStatus() { case StatusOffline: // Will also be triggered by network change. return time.After(10 * time.Second) case StatusLimited, StatusPortal: // Change will not be detected otherwise, but impact is minor. return time.After(5 * time.Second) case StatusSemiOnline: // Very small impact. return time.After(60 * time.Second) case StatusOnline: // Don't check until resolver reports problems. return nil case StatusUnknown: fallthrough default: return time.After(5 * time.Minute) } } func ipInList(list []net.IP, ip net.IP) bool { for _, ignoreIP := range list { if ignoreIP.Equal(ip) { return true } } return false } func checkOnlineStatus(ctx context.Context) { // TODO: implement more methods /*status, err := getConnectivityStateFromDbus() if err != nil { log.Warningf("environment: could not get connectivity: %s", err) setConnectivity(StatusUnknown) return StatusUnknown }*/ // 0) check if connected to SPN and/or DNS. if ConnectedToSPN.IsSet() { updateOnlineStatus(StatusOnline, nil, "connected to SPN") return } if ConnectedToDNS.IsSet() { updateOnlineStatus(StatusOnline, nil, "connected to DNS") return } // 1) check for addresses ipv4, ipv6, err := GetAssignedAddresses() if err != nil { log.Warningf("netenv: failed to get assigned network addresses: %s", err) } else { var lan bool for _, ip := range ipv4 { // Ignore IP if it is in the online check ignore list. if ipInList(IgnoreIPsInOnlineStatusCheck, ip) { continue } switch netutils.GetIPScope(ip) { //nolint:exhaustive // Checking to specific values only. case netutils.SiteLocal: lan = true case netutils.Global: // we _are_ the Internet ;) updateOnlineStatus(StatusOnline, nil, "global IPv4 interface detected") return } } for _, ip := range ipv6 { // Ignore IP if it is in the online check ignore list. if ipInList(IgnoreIPsInOnlineStatusCheck, ip) { continue } switch netutils.GetIPScope(ip) { //nolint:exhaustive // Checking to specific values only. case netutils.SiteLocal, netutils.Global: // IPv6 global addresses are also used in local networks lan = true } } if !lan { updateOnlineStatus(StatusOffline, nil, "no local or global interfaces detected") return } } // 2) try a http request dialer := &net.Dialer{ Timeout: 5 * time.Second, LocalAddr: getLocalAddr("tcp"), } client := &http.Client{ Transport: &http.Transport{ DialContext: dialer.DialContext, DisableKeepAlives: true, DisableCompression: true, WriteBufferSize: 1024, ReadBufferSize: 1024, }, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, Timeout: 1 * time.Second, } request := (&http.Request{ Method: http.MethodGet, URL: parsedPortalTestURL, Close: true, }).WithContext(ctx) response, err := client.Do(request) if err != nil { var netErr net.Error if !errors.As(err, &netErr) || !netErr.Timeout() { // Timeout is the expected error when there is no portal log.Debugf("netenv: http portal test failed: %s", err) // TODO: discern between errors to detect StatusLimited } } else { defer func() { _ = response.Body.Close() }() // Got a response, something is messing with the request // check location portalURL, err := response.Location() if err == nil { updateOnlineStatus(StatusPortal, portalURL, "portal test request succeeded with redirect") return } // direct response if response.StatusCode == http.StatusOK { updateOnlineStatus(StatusPortal, &url.URL{ Scheme: "http", Host: SpecialCaptivePortalDomain, Path: "/", }, "portal test request succeeded") return } log.Debugf("netenv: unexpected http portal test response code: %d", response.StatusCode) // other responses are undefined, continue with next test } // 3) resolve a query // Check if we can resolve the dns check domain. if DNSTestQueryFunc == nil { updateOnlineStatus(StatusOnline, nil, "all checks passed, dns query check disabled") return } ips, ok, err := DNSTestQueryFunc(ctx, DNSTestDomain) switch { case ok && err != nil: updateOnlineStatus(StatusOnline, nil, fmt.Sprintf( "all checks passed, acceptable result for dns query check: %s", err, )) case ok && len(ips) >= 1 && ips[0].Equal(DNSTestExpectedIP): updateOnlineStatus(StatusOnline, nil, "all checks passed") case ok && len(ips) >= 1: log.Warningf("netenv: dns query check response mismatched: got %s", ips[0]) updateOnlineStatus(StatusOnline, nil, "all checks passed, dns query check response mismatched") case ok: log.Warningf("netenv: dns query check response mismatched: empty response") updateOnlineStatus(StatusOnline, nil, "all checks passed, dns query check response was empty") default: log.Warningf("netenv: dns query check failed: %s", err) updateOnlineStatus(StatusOffline, nil, "dns query check failed") } } ================================================ FILE: service/netenv/online-status_test.go ================================================ package netenv import ( "context" "testing" ) func TestCheckOnlineStatus(t *testing.T) { t.Parallel() checkOnlineStatus(context.Background()) t.Logf("online status: %s", GetOnlineStatus()) t.Logf("captive portal: %+v", GetCaptivePortal()) } ================================================ FILE: service/netenv/os_android.go ================================================ package netenv // TODO: Re-enable Android interfaces. // Deactived for transition to new module system. // import ( // "net" // "time" // "github.com/safing/portmaster-android/go/app_interface" // ) // var ( // monitorNetworkChangeOnlineTicker = time.NewTicker(time.Second) // monitorNetworkChangeOfflineTicker = time.NewTicker(time.Second) // ) // func init() { // // Network change event is monitored by the android system. // monitorNetworkChangeOnlineTicker.Stop() // monitorNetworkChangeOfflineTicker.Stop() // } // func osGetInterfaceAddrs() ([]net.Addr, error) { // list, err := app_interface.GetNetworkAddresses() // if err != nil { // return nil, err // } // var netList []net.Addr // for _, addr := range list { // ipNetAddr, err := addr.ToIPNet() // if err == nil { // netList = append(netList, ipNetAddr) // } // } // return netList, nil // } // func osGetNetworkInterfaces() ([]app_interface.NetworkInterface, error) { // return app_interface.GetNetworkInterfaces() // } ================================================ FILE: service/netenv/os_default.go ================================================ //go:build !android package netenv import ( "net" "time" ) var ( monitorNetworkChangeOnlineTicker = time.NewTicker(15 * time.Second) monitorNetworkChangeOfflineTicker = time.NewTicker(time.Second) ) func osGetInterfaceAddrs() ([]net.Addr, error) { return net.InterfaceAddrs() } func osGetNetworkInterfaces() ([]net.Interface, error) { return net.Interfaces() } ================================================ FILE: service/netquery/active_chart_handler.go ================================================ package netquery import ( "bytes" "context" "encoding/json" "errors" "fmt" "io" "net/http" "strings" "github.com/safing/portmaster/service/netquery/orm" ) // ActiveChartHandler handles requests for connection charts. type ActiveChartHandler struct { Database *Database } func (ch *ActiveChartHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { //nolint:dupl requestPayload, err := ch.parseRequest(req) if err != nil { http.Error(resp, err.Error(), http.StatusBadRequest) return } query, paramMap, err := requestPayload.generateSQL(req.Context(), ch.Database.Schema) if err != nil { http.Error(resp, err.Error(), http.StatusBadRequest) return } // actually execute the query against the database and collect the result var result []map[string]interface{} if err := ch.Database.Execute( req.Context(), query, orm.WithNamedArgs(paramMap), orm.WithResult(&result), orm.WithSchema(*ch.Database.Schema), ); err != nil { http.Error(resp, failedQuery+err.Error(), http.StatusInternalServerError) return } // send the HTTP status code resp.WriteHeader(http.StatusOK) // prepare the result encoder. enc := json.NewEncoder(resp) enc.SetEscapeHTML(false) enc.SetIndent("", " ") _ = enc.Encode(map[string]interface{}{ //nolint:errchkjson "results": result, "query": query, "params": paramMap, }) } func (ch *ActiveChartHandler) parseRequest(req *http.Request) (*QueryActiveConnectionChartPayload, error) { //nolint:dupl var body io.Reader switch req.Method { case http.MethodPost, http.MethodPut: body = req.Body case http.MethodGet: body = strings.NewReader(req.URL.Query().Get("q")) default: return nil, fmt.Errorf("invalid HTTP method") } var requestPayload QueryActiveConnectionChartPayload blob, err := io.ReadAll(body) if err != nil { return nil, fmt.Errorf("failed to read body: %w", err) } body = bytes.NewReader(blob) dec := json.NewDecoder(body) dec.DisallowUnknownFields() if err := json.Unmarshal(blob, &requestPayload); err != nil && !errors.Is(err, io.EOF) { return nil, fmt.Errorf("invalid query: %w", err) } return &requestPayload, nil } func (req *QueryActiveConnectionChartPayload) generateSQL(ctx context.Context, schema *orm.TableSchema) (string, map[string]interface{}, error) { template := ` WITH RECURSIVE epoch(x) AS ( SELECT strftime('%%s')-600 UNION ALL SELECT x+1 FROM epoch WHERE x+1 < strftime('%%s')+0 ) SELECT x as timestamp, SUM(verdict IN (2, 5, 6)) AS value, SUM(verdict NOT IN (2, 5, 6)) as countBlocked FROM epoch JOIN connections ON strftime('%%s', connections.started)+0 <= timestamp+0 AND (connections.ended IS NULL OR strftime('%%s', connections.ended)+0 >= timestamp+0) %s GROUP BY round(timestamp/10, 0)*10;` clause, params, err := req.Query.toSQLWhereClause(ctx, "", schema, orm.DefaultEncodeConfig) if err != nil { return "", nil, err } if params == nil { params = make(map[string]interface{}) } if req.TextSearch != nil { textSearch, textParams, err := req.TextSearch.toSQLConditionClause(ctx, schema, "", orm.DefaultEncodeConfig) if err != nil { return "", nil, err } if textSearch != "" { if clause != "" { clause += " AND " } clause += textSearch for key, val := range textParams { params[key] = val } } } if clause == "" { return fmt.Sprintf(template, ""), map[string]interface{}{}, nil } return fmt.Sprintf(template, "WHERE ( "+clause+")"), params, nil } ================================================ FILE: service/netquery/bandwidth_chart_handler.go ================================================ package netquery import ( "bytes" "context" "encoding/json" "errors" "fmt" "io" "net/http" "strings" "github.com/safing/portmaster/service/netquery/orm" ) // BandwidthChartHandler handles requests for connection charts. type BandwidthChartHandler struct { Database *Database } // BandwidthChartRequest holds a request for a bandwidth chart. type BandwidthChartRequest struct { Interval int `json:"interval"` Query Query `json:"query"` GroupBy []string `json:"groupBy"` } func (ch *BandwidthChartHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { //nolint:dupl requestPayload, err := ch.parseRequest(req) if err != nil { http.Error(resp, err.Error(), http.StatusBadRequest) return } query, paramMap, err := requestPayload.generateSQL(req.Context(), ch.Database.Schema) if err != nil { http.Error(resp, err.Error(), http.StatusBadRequest) return } // actually execute the query against the database and collect the result var result []map[string]interface{} if err := ch.Database.Execute( req.Context(), query, orm.WithNamedArgs(paramMap), orm.WithResult(&result), orm.WithSchema(*ch.Database.Schema), ); err != nil { http.Error(resp, failedQuery+err.Error(), http.StatusInternalServerError) return } // send the HTTP status code resp.WriteHeader(http.StatusOK) // prepare the result encoder. enc := json.NewEncoder(resp) enc.SetEscapeHTML(false) enc.SetIndent("", " ") _ = enc.Encode(map[string]interface{}{ //nolint:errchkjson "results": result, "query": query, "params": paramMap, }) } func (ch *BandwidthChartHandler) parseRequest(req *http.Request) (*BandwidthChartRequest, error) { //nolint:dupl var body io.Reader switch req.Method { case http.MethodPost, http.MethodPut: body = req.Body case http.MethodGet: body = strings.NewReader(req.URL.Query().Get("q")) default: return nil, fmt.Errorf("invalid HTTP method") } var requestPayload BandwidthChartRequest blob, err := io.ReadAll(body) if err != nil { return nil, fmt.Errorf("failed to read body: %w", err) } body = bytes.NewReader(blob) dec := json.NewDecoder(body) dec.DisallowUnknownFields() if err := json.Unmarshal(blob, &requestPayload); err != nil && !errors.Is(err, io.EOF) { return nil, fmt.Errorf("invalid query: %w", err) } return &requestPayload, nil } func (req *BandwidthChartRequest) generateSQL(ctx context.Context, schema *orm.TableSchema) (string, map[string]interface{}, error) { if req.Interval == 0 { req.Interval = 10 } interval := fmt.Sprintf("round(time/%d, 0)*%d", req.Interval, req.Interval) // make sure there are only allowed fields specified in the request group-by for _, gb := range req.GroupBy { def := schema.GetColumnDef(gb) if def == nil { return "", nil, fmt.Errorf("unsupported groupBy key: %q", gb) } } selects := append([]string{ interval + " as timestamp", "SUM(incoming) as incoming", "SUM(outgoing) as outgoing", }, req.GroupBy...) groupBy := append([]string{interval}, req.GroupBy...) whereClause, params, err := req.Query.toSQLWhereClause(ctx, "", schema, orm.DefaultEncodeConfig) if err != nil { return "", nil, err } if whereClause != "" { whereClause = "WHERE " + whereClause } template := fmt.Sprintf( `SELECT %s FROM main.bandwidth AS bw JOIN main.connections AS conns ON bw.conn_id = conns.id %s GROUP BY %s ORDER BY time ASC`, strings.Join(selects, ", "), whereClause, strings.Join(groupBy, ", "), ) return template, params, nil } ================================================ FILE: service/netquery/database.go ================================================ package netquery import ( "context" "encoding/json" "fmt" "io" "path/filepath" "sort" "strings" "sync" "time" "github.com/hashicorp/go-multierror" "github.com/jackc/puddle/v2" "zombiezen.com/go/sqlite" "zombiezen.com/go/sqlite/sqlitex" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/netquery/orm" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/profile" ) // InMemory is the "file path" to open a new in-memory database. const InMemory = "file:inmem.db?mode=memory" // Available connection types as their string representation. const ( ConnTypeDNS = "dns" ConnTypeIP = "ip" ) // ConnectionTypeToString is a lookup map to get the string representation // of a network.ConnectionType as used by this package. var ConnectionTypeToString = map[network.ConnectionType]string{ network.DNSRequest: ConnTypeDNS, network.IPConnection: ConnTypeIP, } type ( // Database represents a SQLite3 backed connection database. // It's use is tailored for persistence and querying of network.Connection. // Access to the underlying SQLite database is synchronized. // Database struct { Schema *orm.TableSchema readConnPool *puddle.Pool[*sqlite.Conn] historyPath string l sync.Mutex writeConn *sqlite.Conn } // BatchExecute executes multiple queries in one transaction. BatchExecute struct { ID string SQL string Params map[string]any Result *[]map[string]any } // Conn is a network connection that is stored in a SQLite database and accepted // by the *Database type of this package. This also defines, using the ./orm package, // the table schema and the model that is exposed via the runtime database as well as // the query API. // // Use ConvertConnection from this package to convert a network.Connection to this // representation. Conn struct { //nolint:maligned // ID is a device-unique identifier for the connection. It is built // from network.Connection by hashing the connection ID and the start // time. We cannot just use the network.Connection.ID because it is only unique // as long as the connection is still active and might be, although unlikely, // reused afterwards. ID string `sqlite:"id,primary"` ProfileID string `sqlite:"profile"` Path string `sqlite:"path"` Type string `sqlite:"type,varchar(8)"` External bool `sqlite:"external"` IPVersion packet.IPVersion `sqlite:"ip_version"` IPProtocol packet.IPProtocol `sqlite:"ip_protocol"` LocalIP string `sqlite:"local_ip"` LocalPort uint16 `sqlite:"local_port"` RemoteIP string `sqlite:"remote_ip"` RemotePort uint16 `sqlite:"remote_port"` Domain string `sqlite:"domain"` Country string `sqlite:"country,varchar(2)"` ASN uint `sqlite:"asn"` ASOwner string `sqlite:"as_owner"` Latitude float64 `sqlite:"latitude"` Longitude float64 `sqlite:"longitude"` Scope netutils.IPScope `sqlite:"scope"` WorstVerdict network.Verdict `sqlite:"worst_verdict"` ActiveVerdict network.Verdict `sqlite:"verdict"` FirewallVerdict network.Verdict `sqlite:"firewall_verdict"` Started time.Time `sqlite:"started,text,time"` Ended *time.Time `sqlite:"ended,text,time"` Tunneled bool `sqlite:"tunneled"` Encrypted bool `sqlite:"encrypted"` Internal bool `sqlite:"internal"` Direction string `sqlite:"direction"` ExtraData json.RawMessage `sqlite:"extra_data"` Allowed *bool `sqlite:"allowed"` ProfileRevision int `sqlite:"profile_revision"` ExitNode *string `sqlite:"exit_node"` BytesReceived uint64 `sqlite:"bytes_received,default=0"` BytesSent uint64 `sqlite:"bytes_sent,default=0"` // TODO(ppacher): support "NOT" in search query to get rid of the following helper fields Active bool `sqlite:"active"` // could use "ended IS NOT NULL" or "ended IS NULL" // TODO(ppacher): we need to profile here for "suggestion" support. It would be better to keep a table of profiles in sqlite and use joins here ProfileName string `sqlite:"profile_name"` } ) // New opens a new in-memory database named path and attaches a persistent history database. // // The returned Database used connection pooling for read-only connections // (see Execute). To perform database writes use either Save() or ExecuteWrite(). // Note that write connections are serialized by the Database object before being // handed over to SQLite. func New(dbPath string) (*Database, error) { historyParentDir := filepath.Join(module.instance.DataDir(), "databases") err := utils.EnsureDirectory(historyParentDir, utils.AdminOnlyExecPermission) if err != nil { return nil, fmt.Errorf("failed to ensure database directory exists: %w", err) } // Get file location of history database. historyFile := filepath.Join(historyParentDir, "history.db") // Convert to SQLite URI path. historyURI := "file:///" + strings.TrimPrefix(filepath.ToSlash(historyFile), "/") constructor := func(ctx context.Context) (*sqlite.Conn, error) { c, err := sqlite.OpenConn( dbPath, sqlite.OpenReadOnly, sqlite.OpenSharedCache, sqlite.OpenURI, ) if err != nil { return nil, fmt.Errorf("failed to open read-only sqlite connection at %s: %w", dbPath, err) } if err := sqlitex.ExecuteTransient(c, "ATTACH DATABASE '"+historyURI+"?mode=ro' AS history", nil); err != nil { return nil, fmt.Errorf("failed to attach history database: %w", err) } return c, nil } destructor := func(resource *sqlite.Conn) { if err := resource.Close(); err != nil { log.Errorf("failed to close pooled SQlite database connection: %s", err) } } pool, err := puddle.NewPool(&puddle.Config[*sqlite.Conn]{ Constructor: constructor, Destructor: destructor, MaxSize: 10, }) if err != nil { return nil, err } schema, err := orm.GenerateTableSchema("connections", Conn{}) if err != nil { return nil, err } writeConn, err := sqlite.OpenConn( dbPath, sqlite.OpenCreate, sqlite.OpenReadWrite, sqlite.OpenWAL, sqlite.OpenSharedCache, sqlite.OpenURI, ) if err != nil { return nil, fmt.Errorf("failed to open sqlite at %s: %w", dbPath, err) } return &Database{ readConnPool: pool, Schema: schema, writeConn: writeConn, historyPath: historyURI, }, nil } // NewInMemory is like New but creates a new in-memory database and // automatically applies the connection table schema. func NewInMemory() (*Database, error) { db, err := New(InMemory) if err != nil { return nil, err } // this should actually never happen because an in-memory database // always starts empty... if err := db.ApplyMigrations(); err != nil { return nil, fmt.Errorf("failed to prepare database: %w", err) } return db, nil } // Close closes the database, including pools and connections. func (db *Database) Close() error { db.readConnPool.Close() if err := db.writeConn.Close(); err != nil { return err } return nil } // VacuumHistory rewrites the history database in order to purge deleted records. func VacuumHistory(ctx context.Context) (err error) { historyParentDir := filepath.Join(module.instance.DataDir(), "databases") err = utils.EnsureDirectory(historyParentDir, utils.AdminOnlyExecPermission) if err != nil { return fmt.Errorf("failed to ensure database directory exists: %w", err) } // Get file location of history database. historyFile := filepath.Join(historyParentDir, "history.db") // Convert to SQLite URI path. historyURI := "file:///" + strings.TrimPrefix(filepath.ToSlash(historyFile), "/") writeConn, err := sqlite.OpenConn( historyURI, sqlite.OpenCreate, sqlite.OpenReadWrite, sqlite.OpenWAL, sqlite.OpenSharedCache, sqlite.OpenURI, ) if err != nil { return err } defer func() { if closeErr := writeConn.Close(); closeErr != nil && err == nil { err = closeErr } }() return orm.RunQuery(ctx, writeConn, "VACUUM") } // ApplyMigrations applies any table and data migrations that are needed // to bring db up-to-date with the built-in schema. // TODO(ppacher): right now this only applies the current schema and ignores // any data-migrations. Once the history module is implemented this should // become/use a full migration system -- use zombiezen.com/go/sqlite/sqlitemigration. func (db *Database) ApplyMigrations() error { db.l.Lock() defer db.l.Unlock() if err := sqlitex.ExecuteTransient(db.writeConn, "ATTACH DATABASE '"+db.historyPath+"?mode=rwc' AS 'history';", nil); err != nil { return fmt.Errorf("failed to attach history database: %w", err) } dbNames := []string{"main", "history"} for _, dbName := range dbNames { // get the create-table SQL statement from the inferred schema sql := db.Schema.CreateStatement(dbName, true) log.Debugf("creating table schema for database %q", dbName) // execute the SQL if err := sqlitex.ExecuteTransient(db.writeConn, sql, nil); err != nil { return fmt.Errorf("failed to create schema on database %q: %w", dbName, err) } // create a few indexes indexes := []string{ `CREATE INDEX IF NOT EXISTS %sprofile_id_index ON %s (profile)`, `CREATE INDEX IF NOT EXISTS %sstarted_time_index ON %s (strftime('%%s', started)+0)`, `CREATE INDEX IF NOT EXISTS %sstarted_ended_time_index ON %s (strftime('%%s', started)+0, strftime('%%s', ended)+0) WHERE ended IS NOT NULL`, } for _, idx := range indexes { name := "" if dbName != "" { name = dbName + "." } stmt := fmt.Sprintf(idx, name, db.Schema.Name) if err := sqlitex.ExecuteTransient(db.writeConn, stmt, nil); err != nil { return fmt.Errorf("failed to create index on database %q: %q: %w", dbName, idx, err) } } } bwSchema := `CREATE TABLE IF NOT EXISTS main.bandwidth ( conn_id TEXT NOT NULL, time INTEGER NOT NULL, incoming INTEGER NOT NULL, outgoing INTEGER NOT NULL, CONSTRAINT fk_conn_id FOREIGN KEY(conn_id) REFERENCES connections(id) ON DELETE CASCADE )` if err := sqlitex.ExecuteTransient(db.writeConn, bwSchema, nil); err != nil { return fmt.Errorf("failed to create main.bandwidth database: %w", err) } return nil } func (db *Database) withConn(ctx context.Context, fn func(conn *sqlite.Conn) error) error { res, err := db.readConnPool.Acquire(ctx) if err != nil { return err } defer res.Release() return fn(res.Value()) } // ExecuteWrite executes a custom SQL query using a writable connection against the SQLite // database used by db. // It uses orm.RunQuery() under the hood so please refer to the orm package for // more information about available options. func (db *Database) ExecuteWrite(ctx context.Context, sql string, args ...orm.QueryOption) error { db.l.Lock() defer db.l.Unlock() return orm.RunQuery(ctx, db.writeConn, sql, args...) } // Execute executes a custom SQL query using a read-only connection against the SQLite // database used by db. // It uses orm.RunQuery() under the hood so please refer to the orm package for // more information about available options. func (db *Database) Execute(ctx context.Context, sql string, args ...orm.QueryOption) error { return db.withConn(ctx, func(conn *sqlite.Conn) error { return orm.RunQuery(ctx, conn, sql, args...) }) } // ExecuteBatch executes multiple custom SQL query using a read-only connection against the SQLite // database used by db. func (db *Database) ExecuteBatch(ctx context.Context, batches []BatchExecute) error { return db.withConn(ctx, func(conn *sqlite.Conn) error { merr := new(multierror.Error) for _, batch := range batches { if err := orm.RunQuery(ctx, conn, batch.SQL, orm.WithNamedArgs(batch.Params), orm.WithResult(batch.Result)); err != nil { merr.Errors = append(merr.Errors, fmt.Errorf("%s: %w", batch.ID, err)) } } return merr.ErrorOrNil() }) } // CountRows returns the number of rows stored in the database. func (db *Database) CountRows(ctx context.Context) (int, error) { var result []struct { Count int `sqlite:"count"` } if err := db.Execute(ctx, "SELECT COUNT(*) AS count FROM (SELECT * FROM main.connections UNION SELECT * from history.connections)", orm.WithResult(&result)); err != nil { return 0, fmt.Errorf("failed to perform query: %w", err) } if len(result) != 1 { return 0, fmt.Errorf("unexpected number of rows returned, expected 1 got %d", len(result)) } return result[0].Count, nil } // Cleanup removes all connections that have ended before threshold from the live database. // // NOTE(ppacher): there is no easy way to get the number of removed // rows other than counting them in a first step. Though, that's // probably not worth the cylces... func (db *Database) Cleanup(ctx context.Context, threshold time.Time) (int, error) { where := `WHERE ended IS NOT NULL AND datetime(ended) < datetime(:threshold)` sql := "DELETE FROM main.connections " + where + ";" args := orm.WithNamedArgs(map[string]interface{}{ ":threshold": threshold.UTC().Format(orm.SqliteTimeFormat), }) var result []struct { Count int `sqlite:"count"` } if err := db.Execute( ctx, "SELECT COUNT(*) AS count FROM connections "+where, args, orm.WithTransient(), orm.WithResult(&result), ); err != nil { return 0, fmt.Errorf("failed to perform query: %w", err) } if len(result) != 1 { return 0, fmt.Errorf("unexpected number of rows, expected 1 got %d", len(result)) } err := db.ExecuteWrite(ctx, sql, args) if err != nil { return 0, err } return result[0].Count, nil } // RemoveAllHistoryData removes all connections from the history database. func (db *Database) RemoveAllHistoryData(ctx context.Context) error { query := fmt.Sprintf("DELETE FROM %s.connections", HistoryDatabase) return db.ExecuteWrite(ctx, query) } // RemoveHistoryForProfile removes all connections from the history database // for a given profile ID (source/id). func (db *Database) RemoveHistoryForProfile(ctx context.Context, profileID string) error { query := fmt.Sprintf("DELETE FROM %s.connections WHERE profile = :profile", HistoryDatabase) return db.ExecuteWrite(ctx, query, orm.WithNamedArgs(map[string]any{ ":profile": profileID, })) } // MigrateProfileID migrates the given profile IDs in the history database. // This needs to be done when profiles are deleted and replaced by a different profile. func (db *Database) MigrateProfileID(ctx context.Context, from string, to string) error { return db.ExecuteWrite(ctx, "UPDATE history.connections SET profile = :to WHERE profile = :from", orm.WithNamedArgs(map[string]any{ ":from": from, ":to": to, })) } // dumpTo is a simple helper method that dumps all rows stored in the SQLite database // as JSON to w. // Any error aborts dumping rows and is returned. func (db *Database) dumpTo(ctx context.Context, w io.Writer) error { //nolint:unused var conns []Conn err := db.withConn(ctx, func(conn *sqlite.Conn) error { return sqlitex.ExecuteTransient(conn, "SELECT * FROM connections", &sqlitex.ExecOptions{ ResultFunc: func(stmt *sqlite.Stmt) error { var c Conn if err := orm.DecodeStmt(ctx, db.Schema, stmt, &c, orm.DefaultDecodeConfig); err != nil { return err } conns = append(conns, c) return nil }, }) }) if err != nil { return err } enc := json.NewEncoder(w) enc.SetIndent("", " ") return enc.Encode(conns) } // CleanupHistory deletes history data outside of the (per-app) retention time frame. func (db *Database) CleanupHistory(ctx context.Context) error { // Setup tracer for the clean up process. ctx, tracer := log.AddTracer(ctx) defer tracer.Submit() // Get list of profiles in history. query := "SELECT DISTINCT profile FROM history.connections" var result []struct { Profile string `sqlite:"profile"` } if err := db.Execute(ctx, query, orm.WithResult(&result)); err != nil { return fmt.Errorf("failed to get a list of profiles from the history database: %w", err) } var ( // Get global retention days - do not delete in case of error. globalRetentionDays = config.GetAsInt(profile.CfgOptionKeepHistoryKey, 0)() profileName string retentionDays int64 profileCnt int merr = new(multierror.Error) ) for _, row := range result { // Get profile and retention days. id := strings.TrimPrefix(row.Profile, string(profile.SourceLocal)+"/") p, err := profile.GetLocalProfile(id, nil, nil) if err == nil { profileName = p.String() retentionDays = p.LayeredProfile().KeepHistory() } else { // Getting profile failed, fallback to global setting. tracer.Errorf("history: failed to load profile for id %s: %s", id, err) profileName = row.Profile retentionDays = globalRetentionDays } // Skip deleting if history should be kept forever. if retentionDays == 0 { tracer.Tracef("history: retention is disabled for %s, skipping", profileName) continue } // Count profiles where connections were deleted. profileCnt++ // TODO: count cleared connections threshold := time.Now().Add(-1 * time.Duration(retentionDays) * time.Hour * 24) if err := db.ExecuteWrite(ctx, "DELETE FROM history.connections WHERE profile = :profile AND active = FALSE AND datetime(started) < datetime(:threshold)", orm.WithNamedArgs(map[string]any{ ":profile": row.Profile, ":threshold": threshold.Format(orm.SqliteTimeFormat), }), ); err != nil { tracer.Warningf("history: failed to delete connections of %s: %s", profileName, err) merr.Errors = append(merr.Errors, fmt.Errorf("profile %s: %w", row.Profile, err)) } else { tracer.Debugf( "history: deleted connections older than %d days (before %s) of %s", retentionDays, threshold.Format(time.RFC822), profileName, ) } } // Log summary. tracer.Infof("history: deleted connections outside of retention from %d profiles", profileCnt) return merr.ErrorOrNil() } // MarkAllHistoryConnectionsEnded marks all connections in the history database as ended. func (db *Database) MarkAllHistoryConnectionsEnded(ctx context.Context) error { query := fmt.Sprintf("UPDATE %s.connections SET active = FALSE, ended = :ended WHERE active = TRUE", HistoryDatabase) if err := db.ExecuteWrite(ctx, query, orm.WithNamedArgs(map[string]any{ ":ended": time.Now().Format(orm.SqliteTimeFormat), })); err != nil { return err } return nil } // UpdateBandwidth updates bandwidth data for the connection and optionally also writes // the bandwidth data to the history database. func (db *Database) UpdateBandwidth(ctx context.Context, enableHistory bool, profileKey string, processKey string, connID string, bytesReceived uint64, bytesSent uint64) error { params := map[string]any{ ":id": makeNqIDFromParts(processKey, connID), } parts := []string{} parts = append(parts, "bytes_received = (bytes_received + :bytes_received)") params[":bytes_received"] = bytesReceived parts = append(parts, "bytes_sent = (bytes_sent + :bytes_sent)") params[":bytes_sent"] = bytesSent updateSet := strings.Join(parts, ", ") updateStmts := []string{ fmt.Sprintf(`UPDATE %s.connections SET %s WHERE id = :id`, LiveDatabase, updateSet), } if enableHistory { updateStmts = append(updateStmts, fmt.Sprintf(`UPDATE %s.connections SET %s WHERE id = :id`, HistoryDatabase, updateSet), ) } merr := new(multierror.Error) for _, stmt := range updateStmts { if err := db.ExecuteWrite(ctx, stmt, orm.WithNamedArgs(params)); err != nil { merr.Errors = append(merr.Errors, err) } } // also add the date to the in-memory bandwidth database params[":time"] = time.Now().Unix() stmt := "INSERT INTO main.bandwidth (conn_id, time, incoming, outgoing) VALUES(:id, :time, :bytes_received, :bytes_sent)" if err := db.ExecuteWrite(ctx, stmt, orm.WithNamedArgs(params)); err != nil { merr.Errors = append(merr.Errors, fmt.Errorf("failed to update main.bandwidth: %w", err)) } return merr.ErrorOrNil() } // Save inserts the connection conn into the SQLite database. If conn // already exists the table row is updated instead. // // Save uses the database write connection instead of relying on the // connection pool. func (db *Database) Save(ctx context.Context, conn Conn, enableHistory bool) error { // convert the connection to a param map where each key is already translated // to the sql column name. We also skip bytes_received and bytes_sent since those // will be updated independently from the connection object. connMap, err := orm.ToParamMap(ctx, conn, "", orm.DefaultEncodeConfig, []string{ "bytes_received", "bytes_sent", }) if err != nil { return fmt.Errorf("failed to encode connection for SQL: %w", err) } columns := make([]string, 0, len(connMap)) placeholders := make([]string, 0, len(connMap)) values := make(map[string]interface{}, len(connMap)) updateSets := make([]string, 0, len(connMap)) // sort keys so we get a stable SQLite query that can be better cached. keys := make([]string, 0, len(connMap)) for key := range connMap { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { value := connMap[key] columns = append(columns, key) placeholders = append(placeholders, ":"+key) values[":"+key] = value updateSets = append(updateSets, fmt.Sprintf("%s = :%s", key, key)) } db.l.Lock() defer db.l.Unlock() // TODO(ppacher): make sure this one can be cached to speed up inserting // and save some CPU cycles for the user dbNames := []DatabaseName{LiveDatabase} // TODO: Should we only add ended connection to the history database to save // a couple INSERTs per connection? // This means we need to write the current live DB to the history DB on // shutdown in order to be able to pick the back up after a restart. // Save to history DB if enabled. if enableHistory { dbNames = append(dbNames, HistoryDatabase) } for _, dbName := range dbNames { sql := fmt.Sprintf( `INSERT INTO %s.connections (%s) VALUES(%s) ON CONFLICT(id) DO UPDATE SET %s `, dbName, strings.Join(columns, ", "), strings.Join(placeholders, ", "), strings.Join(updateSets, ", "), ) if err := sqlitex.Execute(db.writeConn, sql, &sqlitex.ExecOptions{ Named: values, ResultFunc: func(stmt *sqlite.Stmt) error { log.Errorf("netquery: got result statement with %d columns", stmt.ColumnCount()) return nil }, }); err != nil { log.Errorf("netquery: failed to execute:\n\t%q\n\treturned error was: %s\n\tparameters: %+v", sql, err, values) return err } } return nil } ================================================ FILE: service/netquery/manager.go ================================================ package netquery import ( "context" "encoding/json" "fmt" "time" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/runtime" "github.com/safing/portmaster/service/network" "github.com/safing/structures/dsd" ) type ( // ConnectionStore describes the interface that is used by Manager // to save new or updated connection objects. // It is implemented by the *Database type of this package. ConnectionStore interface { // Save is called to perists the new or updated connection. If required, // It's up to the implementation to figure out if the operation is an // insert or an update. // The ID of Conn is unique and can be trusted to never collide with other // connections of the save device. Save(ctx context.Context, conn Conn, history bool) error // MarkAllHistoryConnectionsEnded marks all active connections in the history // database as ended NOW. MarkAllHistoryConnectionsEnded(ctx context.Context) error // RemoveAllHistoryData removes all connections from the history database. RemoveAllHistoryData(ctx context.Context) error // RemoveHistoryForProfile removes all connections from the history database. // for a given profile ID (source/id) RemoveHistoryForProfile(ctx context.Context, profile string) error // UpdateBandwidth updates bandwidth data for the connection and optionally also writes // the bandwidth data to the history database. UpdateBandwidth(ctx context.Context, enableHistory bool, profileKey string, processKey string, connID string, bytesReceived uint64, bytesSent uint64) error // CleanupHistory deletes data outside of the retention time frame from the history database. CleanupHistory(ctx context.Context) error // Close closes the connection store. It must not be used afterwards. Close() error } // Manager handles new and updated network.Connections feeds and persists them // at a connection store. // Manager also registers itself as a runtime database and pushes updates to // connections using the local format. // Users should use this update feed rather than the deprecated "network:" database. Manager struct { store ConnectionStore push runtime.PushFunc runtimeReg *runtime.Registry pushPrefix string } ) // NewManager returns a new connection manager that persists all newly created or // updated connections at store. func NewManager(store ConnectionStore, pushPrefix string, reg *runtime.Registry) (*Manager, error) { mng := &Manager{ store: store, runtimeReg: reg, pushPrefix: pushPrefix, } push, err := reg.Register(pushPrefix, runtime.SimpleValueGetterFunc(mng.runtimeGet)) if err != nil { return nil, err } mng.push = push return mng, nil } func (mng *Manager) runtimeGet(keyOrPrefix string) ([]record.Record, error) { // TODO(ppacher): // we don't yet support querying using the runtime database here ... // consider exposing connection from the database at least by ID. // // NOTE(ppacher): // for debugging purposes use RuntimeQueryRunner to execute plain // SQL queries against the database using portbase/database/runtime. return nil, nil } // HandleFeed starts reading new and updated connections from feed and persists them // in the configured ConnectionStore. HandleFeed blocks until either ctx is cancelled // or feed is closed. // Any errors encountered when processing new or updated connections are logged but // otherwise ignored. // HandleFeed handles and persists updates one after each other! Depending on the system // load the user might want to use a buffered channel for feed. func (mng *Manager) HandleFeed(ctx context.Context, feed <-chan *network.Connection) { for { select { case <-ctx.Done(): return case conn, ok := <-feed: if !ok { return } func() { conn.Lock() defer conn.Unlock() if !conn.DataIsComplete() { return } model, err := convertConnection(conn) if err != nil { log.Errorf("netquery: failed to convert connection %s to sqlite model: %s", conn.ID, err) return } // DEBUG: // log.Tracef("netquery: updating connection %s", conn.ID) // Save to netquery database. // Do not include internal connections in history. if err := mng.store.Save(ctx, *model, conn.HistoryEnabled); err != nil { log.Errorf("netquery: failed to save connection %s in sqlite database: %s", conn.ID, err) return } // we clone the record metadata from the connection // into the new model so the portbase/database layer // can handle NEW/UPDATE correctly. cloned := conn.Meta().Duplicate() // push an update for the connection if err := mng.pushConnUpdate(ctx, *cloned, *model); err != nil { log.Errorf("netquery: failed to push update for conn %s via database system: %s", conn.ID, err) } }() } } } func (mng *Manager) pushConnUpdate(_ context.Context, meta record.Meta, conn Conn) error { blob, err := json.Marshal(conn) if err != nil { return fmt.Errorf("failed to marshal connection: %w", err) } key := fmt.Sprintf("%s:%s%s", mng.runtimeReg.DatabaseName(), mng.pushPrefix, conn.ID) wrapper, err := record.NewWrapper( key, &meta, dsd.JSON, blob, ) if err != nil { return fmt.Errorf("failed to create record wrapper: %w", err) } mng.push(wrapper) return nil } // convertConnection converts conn to the local representation used // to persist the information in SQLite. // The caller must hold the lock to the given network.Connection. func convertConnection(conn *network.Connection) (*Conn, error) { direction := "outbound" if conn.Inbound { direction = "inbound" } c := Conn{ ID: makeNqIDFromConn(conn), External: conn.External, IPVersion: conn.IPVersion, IPProtocol: conn.IPProtocol, LocalIP: conn.LocalIP.String(), LocalPort: conn.LocalPort, ActiveVerdict: conn.Verdict, Started: time.Unix(conn.Started, 0), Tunneled: conn.Tunneled, Encrypted: conn.Encrypted, Internal: conn.Internal, Direction: direction, Type: ConnectionTypeToString[conn.Type], ProfileID: conn.ProcessContext.Source + "/" + conn.ProcessContext.Profile, Path: conn.ProcessContext.BinaryPath, ProfileRevision: int(conn.ProfileRevisionCounter), ProfileName: conn.ProcessContext.ProfileName, } switch conn.Type { case network.DNSRequest: c.Type = "dns" case network.IPConnection: c.Type = "ip" case network.Undefined: c.Type = "" } c.Allowed = &conn.ConnectionEstablished if conn.Ended > 0 { ended := time.Unix(conn.Ended, 0) c.Ended = &ended c.Active = false } else { c.Active = true } extraData := map[string]interface{}{ "pid": conn.ProcessContext.PID, "processCreatedAt": conn.ProcessContext.CreatedAt, } if conn.TunnelContext != nil { extraData["tunnel"] = conn.TunnelContext exitNode := conn.TunnelContext.GetExitNodeID() c.ExitNode = &exitNode } if conn.DNSContext != nil { extraData["dns"] = conn.DNSContext } // TODO(ppacher): enable when TLS inspection is merged // if conn.TLSContext != nil { // extraData["tls"] = conn.TLSContext // } if conn.Entity != nil { extraData["cname"] = conn.Entity.CNAME extraData["blockedByLists"] = conn.Entity.BlockedByLists extraData["blockedEntities"] = conn.Entity.BlockedEntities extraData["reason"] = conn.Reason c.RemoteIP = conn.Entity.IP.String() c.RemotePort = conn.Entity.Port c.Domain = conn.Entity.Domain c.Country = conn.Entity.Country c.ASN = conn.Entity.ASN c.ASOwner = conn.Entity.ASOrg c.Scope = conn.Entity.IPScope if conn.Entity.Coordinates != nil { c.Latitude = conn.Entity.Coordinates.Latitude c.Longitude = conn.Entity.Coordinates.Longitude } } // pre-compute the JSON blob for the extra data column // and assign it. extraDataBlob, err := json.Marshal(extraData) if err != nil { return nil, fmt.Errorf("failed to marshal extra data: %w", err) } c.ExtraData = extraDataBlob return &c, nil } // makeNqIDFromConn creates a netquery connection ID from the network connection. func makeNqIDFromConn(conn *network.Connection) string { return makeNqIDFromParts(conn.Process().GetKey(), conn.ID) } // makeNqIDFromParts creates a netquery connection ID from the given network // connection ID and the process key. func makeNqIDFromParts(processKey string, netConnID string) string { return processKey + "-" + netConnID } ================================================ FILE: service/netquery/module_api.go ================================================ package netquery import ( "context" "encoding/json" "errors" "fmt" "sync/atomic" "time" "github.com/hashicorp/go-multierror" servertiming "github.com/mitchellh/go-server-timing" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/runtime" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/profile" ) type NetQuery struct { mgr *mgr.Manager instance instance Store *Database db *database.Interface mng *Manager feed chan *network.Connection } func (nq *NetQuery) prepare() error { var err error nq.db = database.NewInterface(&database.Options{ Local: true, Internal: true, }) // TODO: Open database in start() phase. nq.Store, err = NewInMemory() if err != nil { return fmt.Errorf("failed to create in-memory database: %w", err) } nq.mng, err = NewManager(nq.Store, "netquery/data/", runtime.DefaultRegistry) if err != nil { return fmt.Errorf("failed to create manager: %w", err) } nq.feed = make(chan *network.Connection, 1000) queryHander := &QueryHandler{ Database: nq.Store, IsDevMode: config.Concurrent.GetAsBool(config.CfgDevModeKey, false), } batchHander := &BatchQueryHandler{ Database: nq.Store, IsDevMode: config.Concurrent.GetAsBool(config.CfgDevModeKey, false), } chartHandler := &ActiveChartHandler{ Database: nq.Store, } bwChartHandler := &BandwidthChartHandler{ Database: nq.Store, } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Query Connections", Description: "Query the in-memory sqlite connection database.", Path: "netquery/query", MimeType: "application/json", Read: api.PermitUser, // Needs read+write as the query is sent using POST data. Write: api.PermitUser, // Needs read+write as the query is sent using POST data. HandlerFunc: servertiming.Middleware(queryHander, nil).ServeHTTP, }); err != nil { return fmt.Errorf("failed to register API endpoint: %w", err) } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Batch Query Connections", Description: "Batch query the in-memory sqlite connection database.", Path: "netquery/query/batch", MimeType: "application/json", Read: api.PermitUser, // Needs read+write as the query is sent using POST data. Write: api.PermitUser, // Needs read+write as the query is sent using POST data. HandlerFunc: servertiming.Middleware(batchHander, nil).ServeHTTP, }); err != nil { return fmt.Errorf("failed to register API endpoint: %w", err) } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Active Connections Chart", Description: "Query the in-memory sqlite connection database and return a chart of active connections.", Path: "netquery/charts/connection-active", MimeType: "application/json", Write: api.PermitUser, HandlerFunc: servertiming.Middleware(chartHandler, nil).ServeHTTP, }); err != nil { return fmt.Errorf("failed to register API endpoint: %w", err) } if err := api.RegisterEndpoint(api.Endpoint{ // TODO: Use query parameters instead. Path: "netquery/charts/bandwidth", MimeType: "application/json", Write: api.PermitUser, HandlerFunc: bwChartHandler.ServeHTTP, Name: "Bandwidth Chart", Description: "Query the in-memory sqlite connection database and return a chart of bytes sent/received.", }); err != nil { return fmt.Errorf("failed to register API endpoint: %w", err) } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Remove connections from profile history", Description: "Remove all connections from the history database for one or more profiles", Path: "netquery/history/clear", MimeType: "application/json", Write: api.PermitUser, ActionFunc: func(ar *api.Request) (msg string, err error) { var body struct { ProfileIDs []string `json:"profileIDs"` } if err := json.Unmarshal(ar.InputData, &body); err != nil { return "", fmt.Errorf("failed to decode parameters in body: %w", err) } if len(body.ProfileIDs) == 0 { if err := nq.mng.store.RemoveAllHistoryData(ar.Context()); err != nil { return "", fmt.Errorf("failed to remove all history: %w", err) } } else { merr := new(multierror.Error) for _, profileID := range body.ProfileIDs { if err := nq.mng.store.RemoveHistoryForProfile(ar.Context(), profileID); err != nil { merr.Errors = append(merr.Errors, fmt.Errorf("failed to clear history for %q: %w", profileID, err)) } else { log.Infof("netquery: successfully cleared history for %s", profileID) } } if err := merr.ErrorOrNil(); err != nil { return "", err } } return "Successfully cleared history.", nil }, }); err != nil { return fmt.Errorf("failed to register API endpoint: %w", err) } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Apply connection history retention threshold", Path: "netquery/history/cleanup", Write: api.PermitUser, ActionFunc: func(ar *api.Request) (msg string, err error) { if err := nq.Store.CleanupHistory(ar.Context()); err != nil { return "", err } return "Deleted outdated connections.", nil }, }); err != nil { return fmt.Errorf("failed to register API endpoint: %w", err) } return nil } func (nq *NetQuery) Manager() *mgr.Manager { return nq.mgr } func (nq *NetQuery) Start() error { nq.mgr.Go("netquery connection feed listener", func(ctx *mgr.WorkerCtx) error { sub, err := nq.db.Subscribe(query.New("network:")) if err != nil { return fmt.Errorf("failed to subscribe to network tree: %w", err) } defer close(nq.feed) defer func() { _ = sub.Cancel() }() for { select { case <-ctx.Done(): return nil case rec, ok := <-sub.Feed: if !ok { return nil } conn, ok := rec.(*network.Connection) if !ok { // This is fine as we also receive process updates on // this channel. continue } nq.feed <- conn } } }) nq.mgr.Go("netquery connection feed handler", func(ctx *mgr.WorkerCtx) error { nq.mng.HandleFeed(ctx.Ctx(), nq.feed) return nil }) nq.mgr.Go("netquery live db cleaner", func(ctx *mgr.WorkerCtx) error { for { select { case <-ctx.Done(): return nil case <-time.After(10 * time.Second): threshold := time.Now().Add(-network.DeleteConnsAfterEndedThreshold) count, err := nq.Store.Cleanup(ctx.Ctx(), threshold) if err != nil { log.Errorf("netquery: failed to removed old connections from live db: %s", err) } else { log.Tracef("netquery: successfully removed %d old connections from live db that ended before %s", count, threshold) } } } }) nq.mgr.Delay("network history cleaner delay", 10*time.Minute, func(w *mgr.WorkerCtx) error { return nq.Store.CleanupHistory(w.Ctx()) }).Repeat(1 * time.Hour) // For debugging, provide a simple direct SQL query interface using // the runtime database. // Only expose in development mode. if config.GetAsBool(config.CfgDevModeKey, false)() { _, err := NewRuntimeQueryRunner(nq.Store, "netquery/query/", runtime.DefaultRegistry) if err != nil { return fmt.Errorf("failed to set up runtime SQL query runner: %w", err) } } // Migrate profile IDs in history database when profiles are migrated/merged. nq.instance.Profile().EventMigrated.AddCallback("migrate profile IDs in history database", func(ctx *mgr.WorkerCtx, profileIDs []string) (bool, error) { if len(profileIDs) == 2 { return false, nq.Store.MigrateProfileID(ctx.Ctx(), profileIDs[0], profileIDs[1]) } return false, nil }) return nil } func (nq *NetQuery) Stop() error { // Cacnel the module context. nq.mgr.Cancel() // Wait for all workers before we start the shutdown. nq.mgr.WaitForWorkersFromStop(time.Minute) // we don't use the module ctx here because it is already canceled. // just give the clean up 1 minute to happen and abort otherwise. ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() if err := nq.mng.store.MarkAllHistoryConnectionsEnded(ctx); err != nil { // handle the error by just logging it. There's not much we can do here // and returning an error to the module system doesn't help much as well... log.Errorf("netquery: failed to mark connections in history database as ended: %s", err) } if err := nq.mng.store.Close(); err != nil { log.Errorf("netquery: failed to close sqlite database: %s", err) } else { // Clear deleted connections from database. if err := VacuumHistory(ctx); err != nil { log.Errorf("netquery: failed to execute VACUUM in history database: %s", err) } } return nil } var ( module *NetQuery shimLoaded atomic.Bool ) // NewModule returns a new NetQuery module. func NewModule(instance instance) (*NetQuery, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("NetQuery") module = &NetQuery{ mgr: m, instance: instance, } if err := module.prepare(); err != nil { return nil, fmt.Errorf("failed to prepare netquery module: %w", err) } return module, nil } type instance interface { DataDir() string Profile() *profile.ProfileModule } ================================================ FILE: service/netquery/orm/decoder.go ================================================ package orm import ( "bytes" "context" "errors" "fmt" "io" "reflect" "strings" "time" "zombiezen.com/go/sqlite" ) // Commonly used error messages when working with orm. var ( errStructExpected = errors.New("encode: can only encode structs to maps") errStructPointerExpected = errors.New("decode: result must be pointer to a struct type or map[string]interface{}") errUnexpectedColumnType = errors.New("decode: unexpected column type") ) // constants used when transforming data to and from sqlite. var ( // sqliteTimeFromat defines the string representation that is // expected by SQLite DATETIME functions. // Note that SQLite itself does not include support for a DATETIME // column type. Instead, dates and times are stored either as INTEGER, // TEXT or REAL. // This package provides support for time.Time being stored as TEXT (using a // preconfigured timezone; UTC by default) or as INTEGER (the user can choose between // unixepoch and unixnano-epoch where the nano variant is not officially supported by // SQLITE). SqliteTimeFormat = "2006-01-02 15:04:05" ) type ( // Stmt describes the interface that must be implemented in order to // be decodable to a struct type using DecodeStmt. This interface is implemented // by *sqlite.Stmt. Stmt interface { ColumnCount() int ColumnName(col int) string ColumnType(col int) sqlite.ColumnType ColumnText(col int) string ColumnBool(col int) bool ColumnFloat(col int) float64 ColumnInt(col int) int ColumnReader(col int) *bytes.Reader } // DecodeFunc is called for each non-basic type during decoding. DecodeFunc func(colIdx int, colDef *ColumnDef, stmt Stmt, fieldDef reflect.StructField, outval reflect.Value) (interface{}, bool, error) // DecodeConfig holds decoding functions. DecodeConfig struct { DecodeHooks []DecodeFunc } ) // DecodeStmt decodes the current result row loaded in Stmt into the struct or map type result. // Decoding hooks configured in cfg are executed before trying to decode basic types and may // be specified to provide support for special types. // See DatetimeDecoder() for an example of a DecodeHook that handles graceful time.Time conversion. func DecodeStmt(ctx context.Context, schema *TableSchema, stmt Stmt, result interface{}, cfg DecodeConfig) error { // make sure we got something to decode into ... if result == nil { return fmt.Errorf("%w, got %T", errStructPointerExpected, result) } // fast path for decoding into a map if mp, ok := result.(*map[string]interface{}); ok { return decodeIntoMap(ctx, schema, stmt, mp, cfg) } // make sure we got a pointer in result if reflect.TypeOf(result).Kind() != reflect.Ptr { return fmt.Errorf("%w, got %T", errStructPointerExpected, result) } // make sure it's a poiter to a struct type t := reflect.ValueOf(result).Elem().Type() if t.Kind() != reflect.Struct { return fmt.Errorf("%w, got %T", errStructPointerExpected, result) } // if result is a nil pointer make sure to allocate some space // for the resulting struct resultValue := reflect.ValueOf(result) if resultValue.IsNil() { resultValue.Set( reflect.New(t), ) } // we need access to the struct directly and not to the // pointer. target := reflect.Indirect(resultValue) // create a lookup map from field name (or sqlite:"" tag) // to the field name lm := make(map[string]string) for i := range target.NumField() { fieldType := t.Field(i) // skip unexported fields if !fieldType.IsExported() { continue } lm[sqlColumnName(fieldType)] = fieldType.Name } // iterate over all columns and assign them to the correct // fields for i := range stmt.ColumnCount() { colName := stmt.ColumnName(i) fieldName, ok := lm[colName] if !ok { // there's no target field for this column // so we can skip it continue } fieldType, _ := t.FieldByName(fieldName) value := target.FieldByName(fieldName) colType := stmt.ColumnType(i) // if the column is reported as NULL we keep // the field as it is. // TODO(ppacher): should we set it to nil here? if colType == sqlite.TypeNull { continue } // if value is a nil pointer we need to allocate some memory // first if getKind(value) == reflect.Ptr && value.IsNil() { storage := reflect.New(fieldType.Type.Elem()) value.Set(storage) // make sure value actually points the // dereferenced target storage value = storage.Elem() } colDef := schema.GetColumnDef(colName) // execute all decode hooks but make sure we use decodeBasic() as the // last one. columnValue, err := runDecodeHooks( i, colDef, stmt, fieldType, value, append(cfg.DecodeHooks, decodeBasic()), ) if err != nil { return err } // if we don't have a converted value now we try to // decode basic types if columnValue == nil { return fmt.Errorf("cannot decode column %d (type=%s)", i, colType) } // Debugging: // log.Printf("valueTypeName: %s fieldName = %s value-orig = %s value = %s (%v) newValue = %s", value.Type().String(), fieldName, target.FieldByName(fieldName).Type(), value.Type(), value, columnValue) // convert it to the target type if conversion is possible newValue := reflect.ValueOf(columnValue) if newValue.Type().ConvertibleTo(value.Type()) { newValue = newValue.Convert(value.Type()) } // assign the new value to the struct field. value.Set(newValue) } return nil } // DatetimeDecoder is capable of decoding sqlite INTEGER or TEXT storage classes into // time.Time. For INTEGER storage classes, it supports 'unixnano' struct tag value to // decide between Unix or UnixNano epoch timestamps. // // TODO(ppacher): update comment about loc parameter and TEXT storage class parsing. func DatetimeDecoder(loc *time.Location) DecodeFunc { return func(colIdx int, colDef *ColumnDef, stmt Stmt, fieldDef reflect.StructField, outval reflect.Value) (interface{}, bool, error) { // if we have the column definition available we // use the target go type from there. outType := outval.Type() if colDef != nil { outType = colDef.GoType } // we only care about "time.Time" here if outType.String() != "time.Time" || (colDef != nil && !colDef.IsTime) { // log.Printf("not decoding %s %v", outType, colDef) return nil, false, nil } switch stmt.ColumnType(colIdx) { //nolint:exhaustive // Only selecting specific types. case sqlite.TypeInteger: // stored as unix-epoch, if unixnano is set in the struct field tag // we parse it with nano-second resolution // TODO(ppacher): actually split the tag value at "," and search // the slice for "unixnano" if strings.Contains(fieldDef.Tag.Get("sqlite"), ",unixnano") { return time.Unix(0, int64(stmt.ColumnInt(colIdx))), true, nil } return time.Unix(int64(stmt.ColumnInt(colIdx)), 0), true, nil case sqlite.TypeText: // stored ISO8601 but does not have any timezone information // assigned so we always treat it as loc here. t, err := time.ParseInLocation(SqliteTimeFormat, stmt.ColumnText(colIdx), loc) if err != nil { return nil, false, fmt.Errorf("failed to parse %q in %s: %w", stmt.ColumnText(colIdx), fieldDef.Name, err) } return t, true, nil case sqlite.TypeFloat: // stored as Julian day numbers return nil, false, errors.New("REAL storage type not support for time.Time") case sqlite.TypeNull: return nil, true, nil default: return nil, false, fmt.Errorf("unsupported storage type for time.Time: %s", stmt.ColumnType(colIdx)) } } } func decodeIntoMap(_ context.Context, schema *TableSchema, stmt Stmt, mp *map[string]interface{}, cfg DecodeConfig) error { if *mp == nil { *mp = make(map[string]interface{}) } for i := range stmt.ColumnCount() { var x interface{} colDef := schema.GetColumnDef(stmt.ColumnName(i)) outVal := reflect.ValueOf(&x).Elem() fieldType := reflect.StructField{} if colDef != nil { outVal = reflect.New(colDef.GoType).Elem() fieldType = reflect.StructField{ Type: colDef.GoType, } } val, err := runDecodeHooks( i, colDef, stmt, fieldType, outVal, append(cfg.DecodeHooks, decodeBasic()), ) if err != nil { return fmt.Errorf("failed to decode column %s: %w", stmt.ColumnName(i), err) } (*mp)[stmt.ColumnName(i)] = val } return nil } func decodeBasic() DecodeFunc { return func(colIdx int, colDef *ColumnDef, stmt Stmt, fieldDef reflect.StructField, outval reflect.Value) (result interface{}, handled bool, err error) { valueKind := getKind(outval) colType := stmt.ColumnType(colIdx) colName := stmt.ColumnName(colIdx) errInvalidType := fmt.Errorf("%w %s for column %s with field type %s", errUnexpectedColumnType, colType.String(), colName, outval.Type()) // if we have the column definition available we // use the target go type from there. if colDef != nil { valueKind = NormalizeKind(colDef.GoType.Kind()) // if we have a column definition we try to convert the value to // the actual Go-type that was used in the model. // this is useful, for example, to ensure a []byte{} is always decoded into json.RawMessage // or that type aliases like (type myInt int) are decoded into myInt instead of int defer func() { if handled { t := reflect.New(colDef.GoType).Elem() if result == nil || reflect.ValueOf(result).IsZero() { return } if reflect.ValueOf(result).Type().ConvertibleTo(colDef.GoType) { result = reflect.ValueOf(result).Convert(colDef.GoType).Interface() } t.Set(reflect.ValueOf(result)) result = t.Interface() } }() } // log.Printf("decoding %s into kind %s", colName, valueKind) if colType == sqlite.TypeNull { if colDef != nil && colDef.Nullable { return nil, true, nil } if colDef != nil && !colDef.Nullable { return reflect.New(colDef.GoType).Elem().Interface(), true, nil } if outval.Kind() == reflect.Ptr { return nil, true, nil } } switch valueKind { //nolint:exhaustive case reflect.String: if colType != sqlite.TypeText { return nil, false, errInvalidType } return stmt.ColumnText(colIdx), true, nil case reflect.Bool: // sqlite does not have a BOOL type, it rather stores a 1/0 in a column // with INTEGER affinity. if colType != sqlite.TypeInteger { return nil, false, errInvalidType } return stmt.ColumnBool(colIdx), true, nil case reflect.Float64: if colType != sqlite.TypeFloat { return nil, false, errInvalidType } return stmt.ColumnFloat(colIdx), true, nil case reflect.Int, reflect.Uint: // getKind() normalizes all ints to reflect.Int/Uint because sqlite doesn't really care ... if colType != sqlite.TypeInteger { return nil, false, errInvalidType } return stmt.ColumnInt(colIdx), true, nil case reflect.Slice: if outval.Type().Elem().Kind() != reflect.Uint8 { return nil, false, errors.New("slices other than []byte for BLOB are not supported") } if colType != sqlite.TypeBlob { return nil, false, errInvalidType } columnValue, err := io.ReadAll(stmt.ColumnReader(colIdx)) if err != nil { return nil, false, fmt.Errorf("failed to read blob for column %s: %w", fieldDef.Name, err) } return columnValue, true, nil case reflect.Interface: var ( t reflect.Type x interface{} ) switch colType { case sqlite.TypeBlob: t = reflect.TypeOf([]byte{}) columnValue, err := io.ReadAll(stmt.ColumnReader(colIdx)) if err != nil { return nil, false, fmt.Errorf("failed to read blob for column %s: %w", fieldDef.Name, err) } x = columnValue case sqlite.TypeFloat: t = reflect.TypeOf(float64(0)) x = stmt.ColumnFloat(colIdx) case sqlite.TypeInteger: t = reflect.TypeOf(int(0)) x = stmt.ColumnInt(colIdx) case sqlite.TypeText: t = reflect.TypeOf(string("")) x = stmt.ColumnText(colIdx) case sqlite.TypeNull: t = nil x = nil default: return nil, false, fmt.Errorf("unsupported column type %s", colType) } if t == nil { return nil, true, nil } target := reflect.New(t).Elem() target.Set(reflect.ValueOf(x)) return target.Interface(), true, nil default: return nil, false, fmt.Errorf("cannot decode into %s", valueKind) } } } func sqlColumnName(fieldType reflect.StructField) string { tagValue, hasTag := fieldType.Tag.Lookup("sqlite") if !hasTag { return fieldType.Name } parts := strings.Split(tagValue, ",") if parts[0] != "" { return parts[0] } return fieldType.Name } // runDecodeHooks tries to decode the column value of stmt at index colIdx into outval by running all decode hooks. // The first hook that returns a non-nil interface wins, other hooks will not be executed. If an error is // returned by a decode hook runDecodeHooks stops the error is returned to the caller. func runDecodeHooks(colIdx int, colDef *ColumnDef, stmt Stmt, fieldDef reflect.StructField, outval reflect.Value, hooks []DecodeFunc) (interface{}, error) { for _, fn := range hooks { res, end, err := fn(colIdx, colDef, stmt, fieldDef, outval) if err != nil { return res, err } if end { return res, nil } } return nil, nil } // getKind returns the kind of value but normalized Int, Uint and Float variants. // to their base type. func getKind(val reflect.Value) reflect.Kind { kind := val.Kind() return NormalizeKind(kind) } // NormalizeKind returns a normalized kind of the given kind. func NormalizeKind(kind reflect.Kind) reflect.Kind { switch { case kind >= reflect.Int && kind <= reflect.Int64: return reflect.Int case kind >= reflect.Uint && kind <= reflect.Uint64: return reflect.Uint case kind >= reflect.Float32 && kind <= reflect.Float64: return reflect.Float64 default: return kind } } // DefaultDecodeConfig holds the default decoding configuration. var DefaultDecodeConfig = DecodeConfig{ DecodeHooks: []DecodeFunc{ DatetimeDecoder(time.UTC), }, } ================================================ FILE: service/netquery/orm/decoder_test.go ================================================ package orm import ( "bytes" "context" "encoding/json" "reflect" "testing" "time" "github.com/stretchr/testify/assert" "zombiezen.com/go/sqlite" ) type testStmt struct { columns []string values []interface{} types []sqlite.ColumnType } func (ts testStmt) ColumnCount() int { return len(ts.columns) } func (ts testStmt) ColumnName(i int) string { return ts.columns[i] } func (ts testStmt) ColumnBool(i int) bool { return ts.values[i].(bool) } //nolint:forcetypeassert func (ts testStmt) ColumnText(i int) string { return ts.values[i].(string) } //nolint:forcetypeassert func (ts testStmt) ColumnFloat(i int) float64 { return ts.values[i].(float64) } //nolint:forcetypeassert func (ts testStmt) ColumnInt(i int) int { return ts.values[i].(int) } //nolint:forcetypeassert func (ts testStmt) ColumnReader(i int) *bytes.Reader { return bytes.NewReader(ts.values[i].([]byte)) } //nolint:forcetypeassert func (ts testStmt) ColumnType(i int) sqlite.ColumnType { return ts.types[i] } // Compile time check. var _ Stmt = new(testStmt) type exampleFieldTypes struct { S string I int F float64 B bool } type examplePointerTypes struct { S *string I *int F *float64 B *bool } type exampleStructTags struct { S string `sqlite:"col_string"` I int `sqlite:"col_int"` } type exampleIntConv struct { I8 int8 I16 int16 I32 int32 I64 int64 I int } type exampleBlobTypes struct { B []byte } type exampleJSONRawTypes struct { B json.RawMessage } type exampleTimeTypes struct { T time.Time TP *time.Time } type exampleInterface struct { I interface{} IP *interface{} } func (ett *exampleTimeTypes) Equal(other interface{}) bool { oett, ok := other.(*exampleTimeTypes) if !ok { return false } return ett.T.Equal(oett.T) && (ett.TP != nil && oett.TP != nil && ett.TP.Equal(*oett.TP)) || (ett.TP == nil && oett.TP == nil) } type myInt int type exampleTimeNano struct { T time.Time `sqlite:",unixnano"` } func (etn *exampleTimeNano) Equal(other interface{}) bool { oetn, ok := other.(*exampleTimeNano) if !ok { return false } return etn.T.Equal(oetn.T) } func TestDecoder(t *testing.T) { //nolint:maintidx,tparallel t.Parallel() ctx := context.TODO() refTime := time.Date(2022, time.February, 15, 9, 51, 0, 0, time.UTC) cases := []struct { Desc string Stmt testStmt ColumnDef []ColumnDef Result interface{} Expected interface{} }{ { "Decoding into nil is not allowed", testStmt{ columns: nil, values: nil, types: nil, }, nil, nil, nil, }, { "Decoding into basic types", testStmt{ columns: []string{"S", "I", "F", "B"}, types: []sqlite.ColumnType{ sqlite.TypeText, sqlite.TypeInteger, sqlite.TypeFloat, sqlite.TypeInteger, }, values: []interface{}{ "string value", 1, 1.2, true, }, }, nil, &exampleFieldTypes{}, &exampleFieldTypes{ S: "string value", I: 1, F: 1.2, B: true, }, }, { "Decoding into basic types with different order", testStmt{ columns: []string{"I", "S", "B", "F"}, types: []sqlite.ColumnType{ sqlite.TypeInteger, sqlite.TypeText, sqlite.TypeInteger, sqlite.TypeFloat, }, values: []interface{}{ 1, "string value", true, 1.2, }, }, nil, &exampleFieldTypes{}, &exampleFieldTypes{ S: "string value", I: 1, F: 1.2, B: true, }, }, { "Decoding into basic types with missing values", testStmt{ columns: []string{"F", "B"}, types: []sqlite.ColumnType{ sqlite.TypeFloat, sqlite.TypeInteger, }, values: []interface{}{ 1.2, true, }, }, nil, &exampleFieldTypes{}, &exampleFieldTypes{ F: 1.2, B: true, }, }, { "Decoding into pointer types", testStmt{ columns: []string{"S", "I", "F", "B"}, types: []sqlite.ColumnType{ sqlite.TypeText, sqlite.TypeInteger, sqlite.TypeFloat, sqlite.TypeInteger, }, values: []interface{}{ "string value", 1, 1.2, true, }, }, nil, &examplePointerTypes{}, func() interface{} { s := "string value" i := 1 f := 1.2 b := true return &examplePointerTypes{ S: &s, I: &i, F: &f, B: &b, } }, }, { "Decoding into pointer types with missing values", testStmt{ columns: []string{"S", "B"}, types: []sqlite.ColumnType{ sqlite.TypeText, sqlite.TypeInteger, sqlite.TypeFloat, sqlite.TypeInteger, }, values: []interface{}{ "string value", true, }, }, nil, &examplePointerTypes{}, func() interface{} { s := "string value" b := true return &examplePointerTypes{ S: &s, B: &b, } }, }, { "Decoding into fields with struct tags", testStmt{ columns: []string{"col_string", "col_int"}, types: []sqlite.ColumnType{ sqlite.TypeText, sqlite.TypeInteger, }, values: []interface{}{ "string value", 1, }, }, nil, &exampleStructTags{}, &exampleStructTags{ S: "string value", I: 1, }, }, { "Decoding into correct int type", testStmt{ columns: []string{"I8", "I16", "I32", "I64", "I"}, types: []sqlite.ColumnType{ sqlite.TypeInteger, sqlite.TypeInteger, sqlite.TypeInteger, sqlite.TypeInteger, sqlite.TypeInteger, }, values: []interface{}{ 1, 1, 1, 1, 1, }, }, nil, &exampleIntConv{}, &exampleIntConv{ 1, 1, 1, 1, 1, }, }, { "Handling NULL values for basic types", testStmt{ columns: []string{"S", "I", "F"}, types: []sqlite.ColumnType{ sqlite.TypeNull, sqlite.TypeNull, sqlite.TypeFloat, }, values: []interface{}{ // we use nil here but actually that does not matter nil, nil, 1.0, }, }, nil, &exampleFieldTypes{}, &exampleFieldTypes{ F: 1.0, }, }, { "Handling NULL values for pointer types", testStmt{ columns: []string{"S", "I", "F"}, types: []sqlite.ColumnType{ sqlite.TypeNull, sqlite.TypeNull, sqlite.TypeFloat, }, values: []interface{}{ // we use nil here but actually that does not matter nil, nil, 1.0, }, }, nil, &examplePointerTypes{}, func() interface{} { f := 1.0 return &examplePointerTypes{F: &f} }, }, { "Handling blob types", testStmt{ columns: []string{"B"}, types: []sqlite.ColumnType{ sqlite.TypeBlob, }, values: []interface{}{ ([]byte)("hello world"), }, }, nil, &exampleBlobTypes{}, &exampleBlobTypes{ B: ([]byte)("hello world"), }, }, { "Handling blob types as json.RawMessage", testStmt{ columns: []string{"B"}, types: []sqlite.ColumnType{ sqlite.TypeBlob, }, values: []interface{}{ ([]byte)("hello world"), }, }, nil, &exampleJSONRawTypes{}, &exampleJSONRawTypes{ B: (json.RawMessage)("hello world"), }, }, { "Handling time.Time and pointers to it", testStmt{ columns: []string{"T", "TP"}, types: []sqlite.ColumnType{ sqlite.TypeInteger, sqlite.TypeInteger, }, values: []interface{}{ int(refTime.Unix()), int(refTime.Unix()), }, }, nil, &exampleTimeTypes{}, &exampleTimeTypes{ T: refTime, TP: &refTime, }, }, { "Handling time.Time in nano-second resolution (struct tags)", testStmt{ columns: []string{"T", "TP"}, types: []sqlite.ColumnType{ sqlite.TypeInteger, sqlite.TypeInteger, }, values: []interface{}{ int(refTime.UnixNano()), int(refTime.UnixNano()), }, }, nil, &exampleTimeNano{}, &exampleTimeNano{ T: refTime, }, }, { "Decoding into interface", testStmt{ columns: []string{"I", "IP"}, types: []sqlite.ColumnType{ sqlite.TypeText, sqlite.TypeText, }, values: []interface{}{ "value1", "value2", }, }, nil, &exampleInterface{}, func() interface{} { var x interface{} = "value2" return &exampleInterface{ I: "value1", IP: &x, } }, }, { "Decoding into map[string]interface{}", testStmt{ columns: []string{"I", "F", "S", "B"}, types: []sqlite.ColumnType{ sqlite.TypeInteger, sqlite.TypeFloat, sqlite.TypeText, sqlite.TypeBlob, }, values: []interface{}{ 1, 1.1, "string value", []byte("blob value"), }, }, nil, new(map[string]interface{}), &map[string]interface{}{ "I": 1, "F": 1.1, "S": "string value", "B": []byte("blob value"), }, }, { "Decoding using type-hints", testStmt{ columns: []string{"B", "T"}, types: []sqlite.ColumnType{ sqlite.TypeInteger, sqlite.TypeText, }, values: []interface{}{ true, refTime.Format(SqliteTimeFormat), }, }, []ColumnDef{ { Name: "B", Type: sqlite.TypeInteger, GoType: reflect.TypeOf(true), }, { Name: "T", Type: sqlite.TypeText, GoType: reflect.TypeOf(time.Time{}), IsTime: true, }, }, new(map[string]interface{}), &map[string]interface{}{ "B": true, "T": refTime, }, }, { "Decoding into type aliases", testStmt{ columns: []string{"B"}, types: []sqlite.ColumnType{ sqlite.TypeBlob, }, values: []interface{}{ []byte(`{"foo": "bar}`), }, }, []ColumnDef{ { Name: "B", Type: sqlite.TypeBlob, GoType: reflect.TypeOf(json.RawMessage(`{"foo": "bar}`)), }, }, new(map[string]interface{}), &map[string]interface{}{ "B": json.RawMessage(`{"foo": "bar}`), }, }, { "Decoding into type aliases #2", testStmt{ columns: []string{"I"}, types: []sqlite.ColumnType{sqlite.TypeInteger}, values: []interface{}{ 10, }, }, []ColumnDef{ { Name: "I", Type: sqlite.TypeInteger, GoType: reflect.TypeOf(myInt(0)), }, }, new(map[string]interface{}), &map[string]interface{}{ "I": myInt(10), }, }, } for idx := range cases { //nolint:paralleltest c := cases[idx] t.Run(c.Desc, func(t *testing.T) { // log.Println(c.Desc) err := DecodeStmt(ctx, &TableSchema{Columns: c.ColumnDef}, c.Stmt, c.Result, DefaultDecodeConfig) if fn, ok := c.Expected.(func() interface{}); ok { c.Expected = fn() } if c.Expected == nil { assert.Error(t, err, c.Desc) } else { assert.NoError(t, err, c.Desc) if equaler, ok := c.Expected.(interface{ Equal(x interface{}) bool }); ok { assert.True(t, equaler.Equal(c.Result)) } else { assert.Equal(t, c.Expected, c.Result) } } }) } } ================================================ FILE: service/netquery/orm/encoder.go ================================================ package orm import ( "context" "errors" "fmt" "reflect" "time" "golang.org/x/exp/slices" "zombiezen.com/go/sqlite" ) type ( // EncodeFunc is called for each non-basic type during encoding. EncodeFunc func(col *ColumnDef, valType reflect.Type, val reflect.Value) (interface{}, bool, error) // EncodeConfig holds encoding functions. EncodeConfig struct { EncodeHooks []EncodeFunc } ) // ToParamMap returns a map that contains the sqlite compatible value of each struct field of // r using the sqlite column name as a map key. It either uses the name of the // exported struct field or the value of the "sqlite" tag. func ToParamMap(ctx context.Context, r interface{}, keyPrefix string, cfg EncodeConfig, skipFields []string) (map[string]interface{}, error) { // make sure we work on a struct type val := reflect.Indirect(reflect.ValueOf(r)) if val.Kind() != reflect.Struct { return nil, fmt.Errorf("%w, got %T", errStructExpected, r) } res := make(map[string]interface{}, val.NumField()) for i := range val.NumField() { fieldType := val.Type().Field(i) field := val.Field(i) // skip unexported fields if !fieldType.IsExported() { continue } colDef, err := getColumnDef(fieldType) if err != nil { return nil, fmt.Errorf("failed to get column definition for %s: %w", fieldType.Name, err) } if slices.Contains(skipFields, colDef.Name) { continue } x, found, err := runEncodeHooks( colDef, fieldType.Type, field, append( cfg.EncodeHooks, encodeBasic(), ), ) if err != nil { return nil, fmt.Errorf("failed to run encode hooks: %w", err) } if !found { if reflect.Indirect(field).IsValid() { x = reflect.Indirect(field).Interface() } } res[keyPrefix+sqlColumnName(fieldType)] = x } return res, nil } // EncodeValue encodes the given value. func EncodeValue(ctx context.Context, colDef *ColumnDef, val interface{}, cfg EncodeConfig) (interface{}, error) { fieldValue := reflect.ValueOf(val) fieldType := reflect.TypeOf(val) x, found, err := runEncodeHooks( colDef, fieldType, fieldValue, append( cfg.EncodeHooks, encodeBasic(), ), ) if err != nil { return nil, fmt.Errorf("failed to run encode hooks: %w", err) } if !found { if reflect.Indirect(fieldValue).IsValid() { x = reflect.Indirect(fieldValue).Interface() } } return x, nil } func encodeBasic() EncodeFunc { return func(col *ColumnDef, valType reflect.Type, val reflect.Value) (interface{}, bool, error) { kind := valType.Kind() if kind == reflect.Ptr { valType = valType.Elem() kind = valType.Kind() if val.IsNil() { if !col.Nullable { // we need to set the zero value here since the column // is not marked as nullable return reflect.New(valType).Elem().Interface(), true, nil } return nil, true, nil } val = val.Elem() } switch NormalizeKind(kind) { //nolint:exhaustive case reflect.String, reflect.Float64, reflect.Bool, reflect.Int, reflect.Uint: // sqlite package handles conversion of those types // already return val.Interface(), true, nil case reflect.Slice: if valType.Elem().Kind() == reflect.Uint8 { // this is []byte return val.Interface(), true, nil } fallthrough default: return nil, false, fmt.Errorf("cannot convert value of kind %s for use in SQLite", kind) } } } // DatetimeEncoder returns a new datetime encoder for the given time zone. func DatetimeEncoder(loc *time.Location) EncodeFunc { return func(colDef *ColumnDef, valType reflect.Type, val reflect.Value) (interface{}, bool, error) { // if fieldType holds a pointer we need to dereference the value ft := valType.String() if valType.Kind() == reflect.Ptr { ft = valType.Elem().String() val = reflect.Indirect(val) } normalizedKind := NormalizeKind(valType.Kind()) // we only care about "time.Time" here var t time.Time switch { case ft == "time.Time": // handle the zero time as a NULL. if !val.IsValid() || val.IsZero() { return nil, true, nil } var ok bool valInterface := val.Interface() t, ok = valInterface.(time.Time) if !ok { return nil, false, errors.New("cannot convert reflect value to time.Time") } case valType.Kind() == reflect.String && colDef.IsTime: var err error t, err = time.Parse(time.RFC3339, val.String()) if err != nil { return nil, false, fmt.Errorf("failed to parse time as RFC3339: %w", err) } case (normalizedKind == reflect.Int || normalizedKind == reflect.Uint || normalizedKind == reflect.Float64) && colDef.IsTime: seconds := int64(0) switch normalizedKind { //nolint:exhaustive // Previous switch case assures these types. case reflect.Int: seconds = val.Int() case reflect.Uint: seconds = int64(val.Uint()) case reflect.Float64: seconds = int64(val.Float()) } t = time.Unix(seconds, 0) default: // we don't care ... return nil, false, nil } switch colDef.Type { //nolint:exhaustive case sqlite.TypeInteger: if colDef.UnixNano { return t.UnixNano(), true, nil } return t.Unix(), true, nil case sqlite.TypeText: str := t.In(loc).Format(SqliteTimeFormat) return str, true, nil } return nil, false, fmt.Errorf("cannot store time.Time in %s", colDef.Type) } } func runEncodeHooks(colDef *ColumnDef, valType reflect.Type, val reflect.Value, hooks []EncodeFunc) (interface{}, bool, error) { if valType == nil { if !colDef.Nullable { switch colDef.Type { //nolint:exhaustive case sqlite.TypeBlob: return []byte{}, true, nil case sqlite.TypeFloat: return 0.0, true, nil case sqlite.TypeText: return "", true, nil case sqlite.TypeInteger: return 0, true, nil default: return nil, false, fmt.Errorf("unsupported sqlite data type: %s", colDef.Type) } } return nil, true, nil } for _, fn := range hooks { res, end, err := fn(colDef, valType, val) if err != nil { return res, false, err } if end { return res, true, nil } } return nil, false, nil } // DefaultEncodeConfig holds the default encoding configuration. var DefaultEncodeConfig = EncodeConfig{ EncodeHooks: []EncodeFunc{ DatetimeEncoder(time.UTC), }, } ================================================ FILE: service/netquery/orm/encoder_test.go ================================================ package orm import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "zombiezen.com/go/sqlite" ) func TestEncodeAsMap(t *testing.T) { //nolint:tparallel t.Parallel() ctx := context.TODO() refTime := time.Date(2022, time.February, 15, 9, 51, 0, 0, time.UTC) cases := []struct { Desc string Input interface{} Expected map[string]interface{} }{ { "Encode basic types", struct { I int F float64 S string B []byte }{ I: 1, F: 1.2, S: "string", B: ([]byte)("bytes"), }, map[string]interface{}{ "I": 1, "F": 1.2, "S": "string", "B": ([]byte)("bytes"), }, }, { "Encode using struct tags", struct { I int `sqlite:"col_int"` S string `sqlite:"col_string"` }{ I: 1, S: "string value", }, map[string]interface{}{ "col_int": 1, "col_string": "string value", }, }, { "Ignore Private fields", struct { I int s string }{ I: 1, s: "string value", }, map[string]interface{}{ "I": 1, }, }, { "Handle Pointers", struct { I *int S *string }{ I: new(int), }, map[string]interface{}{ "I": 0, "S": nil, }, }, { "Handle time.Time types", struct { TinInt time.Time `sqlite:",integer,unixnano"` TinString time.Time `sqlite:",text"` }{ TinInt: refTime, TinString: refTime, }, map[string]interface{}{ "TinInt": refTime.UnixNano(), "TinString": refTime.Format(SqliteTimeFormat), }, }, { "Handle time.Time pointer types", struct { TinInt *time.Time `sqlite:",integer,unixnano"` TinString *time.Time `sqlite:",text"` Tnil1 *time.Time `sqlite:",text"` Tnil2 *time.Time `sqlite:",text"` }{ TinInt: &refTime, TinString: &refTime, Tnil1: nil, Tnil2: (*time.Time)(nil), }, map[string]interface{}{ "TinInt": refTime.UnixNano(), "TinString": refTime.Format(SqliteTimeFormat), "Tnil1": nil, "Tnil2": nil, }, }, } for idx := range cases { //nolint:paralleltest c := cases[idx] t.Run(c.Desc, func(t *testing.T) { res, err := ToParamMap(ctx, c.Input, "", DefaultEncodeConfig, nil) require.NoError(t, err) assert.Equal(t, c.Expected, res) }) } } func TestEncodeValue(t *testing.T) { //nolint:tparallel t.Parallel() ctx := context.TODO() refTime := time.Date(2022, time.February, 15, 9, 51, 0, 0, time.UTC) cases := []struct { Desc string Column ColumnDef Input interface{} Output interface{} }{ { "Special value time.Time as text", ColumnDef{ IsTime: true, Type: sqlite.TypeText, }, refTime, refTime.Format(SqliteTimeFormat), }, { "Special value time.Time as unix-epoch", ColumnDef{ IsTime: true, Type: sqlite.TypeInteger, }, refTime, refTime.Unix(), }, { "Special value time.Time as unixnano-epoch", ColumnDef{ IsTime: true, Type: sqlite.TypeInteger, UnixNano: true, }, refTime, refTime.UnixNano(), }, { "Special value zero time", ColumnDef{ IsTime: true, Type: sqlite.TypeText, }, time.Time{}, nil, }, { "Special value zero time pointer", ColumnDef{ IsTime: true, Type: sqlite.TypeText, }, new(time.Time), nil, }, { "Special value *time.Time as text", ColumnDef{ IsTime: true, Type: sqlite.TypeText, }, &refTime, refTime.Format(SqliteTimeFormat), }, { "Special value untyped nil", ColumnDef{ Nullable: true, IsTime: true, Type: sqlite.TypeText, }, nil, nil, }, { "Special value typed nil", ColumnDef{ IsTime: true, Type: sqlite.TypeText, }, (*time.Time)(nil), nil, }, { "Time formated as string", ColumnDef{ IsTime: true, Type: sqlite.TypeText, }, refTime.In(time.Local).Format(time.RFC3339), refTime.Format(SqliteTimeFormat), }, { "Nullable integer", ColumnDef{ Type: sqlite.TypeInteger, Nullable: true, }, nil, nil, }, { "Not-Null integer", ColumnDef{ Name: "test", Type: sqlite.TypeInteger, }, nil, 0, }, { "Not-Null string", ColumnDef{ Type: sqlite.TypeText, }, nil, "", }, } for idx := range cases { //nolint:paralleltest c := cases[idx] t.Run(c.Desc, func(t *testing.T) { res, err := EncodeValue(ctx, &c.Column, c.Input, DefaultEncodeConfig) require.NoError(t, err) assert.Equal(t, c.Output, res) }) } } ================================================ FILE: service/netquery/orm/query_runner.go ================================================ package orm import ( "context" "fmt" "reflect" "zombiezen.com/go/sqlite" "zombiezen.com/go/sqlite/sqlitex" ) type ( // QueryOption can be specified at RunQuery to alter the behavior // of the executed query. QueryOption func(opts *queryOpts) queryOpts struct { Transient bool Args []interface{} NamedArgs map[string]interface{} Result interface{} DecodeConfig DecodeConfig Schema TableSchema } ) // WithTransient marks the query as transient. // // Transient queries will not be cached for later // re-use after they have been prepared. func WithTransient() QueryOption { return func(opts *queryOpts) { opts.Transient = true } } // WithArgs adds a list of arguments for the query. Arguments // are applied in order. // // See SQL Language Expression documentation of SQLite for // details: https://sqlite.org/lang_expr.html func WithArgs(args ...interface{}) QueryOption { return func(opts *queryOpts) { opts.Args = args } } // WithNamedArgs adds args to the query. The query must used // named argument placeholders. According to the SQLite spec, // arguments must either start with ':', '@' or '$'. // // See SQL Language Expression documentation of SQLite for // details: https://sqlite.org/lang_expr.html func WithNamedArgs(args map[string]interface{}) QueryOption { return func(opts *queryOpts) { opts.NamedArgs = args } } // WithSchema returns a query option that adds the given table // schema to the query. func WithSchema(tbl TableSchema) QueryOption { return func(opts *queryOpts) { opts.Schema = tbl } } // WithResult sets the result receiver. result is expected to // be a pointer to a slice of struct or map types. // // For decoding DecodeStmt is used to decode each // row into a new slice element. It thus supports special values // like time.Time. See DecodeStmt() and WithDecodeConfig() for // more information. func WithResult(result interface{}) QueryOption { return func(opts *queryOpts) { opts.Result = result } } // WithDecodeConfig configures the DecodeConfig to use when // calling DecodeStmt to decode each row into the result slice. // // If not specified, DefaultDecodeConfig will be used. func WithDecodeConfig(cfg DecodeConfig) QueryOption { return func(opts *queryOpts) { opts.DecodeConfig = cfg } } // RunQuery executes the query stored in sql against the databased opened in // conn. Please refer to the documentation of QueryOption, especially WithResult() // for more information on how to retrieve the resulting rows. // // Example: // // var result []struct{ // Count int `sqlite:"rowCount"` // } // // err := RunQuery(ctx, conn, "SELECT COUNT(*) AS rowCount FROM table", WithResult(&result)) // fmt.Println(result[0].Count) func RunQuery(ctx context.Context, conn *sqlite.Conn, sql string, modifiers ...QueryOption) error { args := queryOpts{ DecodeConfig: DefaultDecodeConfig, } for _, fn := range modifiers { fn(&args) } opts := &sqlitex.ExecOptions{ Args: args.Args, Named: args.NamedArgs, } var ( sliceVal reflect.Value valElemType reflect.Type ) if args.Result != nil { target := args.Result outVal := reflect.ValueOf(target) if outVal.Kind() != reflect.Ptr { return fmt.Errorf("target must be a pointer, got %T", target) } sliceVal = reflect.Indirect(outVal) if !sliceVal.IsValid() || sliceVal.IsNil() { newVal := reflect.Zero(outVal.Type().Elem()) sliceVal.Set(newVal) } kind := sliceVal.Kind() if kind != reflect.Slice { return fmt.Errorf("target but be pointer to slice, got %T", target) } valType := sliceVal.Type() valElemType = valType.Elem() opts.ResultFunc = func(stmt *sqlite.Stmt) error { currentField := reflect.New(valElemType) if err := DecodeStmt(ctx, &args.Schema, stmt, currentField.Interface(), args.DecodeConfig); err != nil { resultDump := make(map[string]any) for colIdx := range stmt.ColumnCount() { name := stmt.ColumnName(colIdx) switch stmt.ColumnType(colIdx) { //nolint:exhaustive // TODO: handle type BLOB? case sqlite.TypeText: resultDump[name] = stmt.ColumnText(colIdx) case sqlite.TypeFloat: resultDump[name] = stmt.ColumnFloat(colIdx) case sqlite.TypeInteger: resultDump[name] = stmt.ColumnInt(colIdx) case sqlite.TypeNull: resultDump[name] = "" } } return fmt.Errorf("%w: %+v", err, resultDump) } sliceVal = reflect.Append(sliceVal, reflect.Indirect(currentField)) return nil } } var err error if args.Transient { err = sqlitex.ExecuteTransient(conn, sql, opts) } else { err = sqlitex.Execute(conn, sql, opts) } if err != nil { return err } if args.Result != nil { reflect.Indirect(reflect.ValueOf(args.Result)).Set(sliceVal) } return nil } ================================================ FILE: service/netquery/orm/schema_builder.go ================================================ package orm import ( "errors" "fmt" "reflect" "strconv" "strings" "zombiezen.com/go/sqlite" "github.com/safing/portmaster/base/log" ) var errSkipStructField = errors.New("struct field should be skipped") // Struct Tags. var ( TagUnixNano = "unixnano" TagPrimaryKey = "primary" TagAutoIncrement = "autoincrement" TagTime = "time" TagNotNull = "not-null" TagNullable = "nullable" TagTypeInt = "integer" TagTypeText = "text" TagTypePrefixVarchar = "varchar" TagTypeBlob = "blob" TagTypeFloat = "float" TagTypePrefixDefault = "default=" ) var sqlTypeMap = map[sqlite.ColumnType]string{ sqlite.TypeBlob: "BLOB", sqlite.TypeFloat: "REAL", sqlite.TypeInteger: "INTEGER", sqlite.TypeText: "TEXT", } type ( // TableSchema defines a SQL table schema. TableSchema struct { Name string Columns []ColumnDef } // ColumnDef defines a SQL column. ColumnDef struct { //nolint:maligned Name string Nullable bool Type sqlite.ColumnType GoType reflect.Type Length int PrimaryKey bool AutoIncrement bool UnixNano bool IsTime bool Default any } ) // GetColumnDef returns the column definition with the given name. func (ts TableSchema) GetColumnDef(name string) *ColumnDef { for _, def := range ts.Columns { if def.Name == name { return &def } } return nil } // CreateStatement build the CREATE SQL statement for the table. func (ts TableSchema) CreateStatement(databaseName string, ifNotExists bool) string { sql := "CREATE TABLE" if ifNotExists { sql += " IF NOT EXISTS" } name := ts.Name if databaseName != "" { name = databaseName + "." + ts.Name } sql += " " + name + " ( " for idx, col := range ts.Columns { sql += col.AsSQL() if idx < len(ts.Columns)-1 { sql += ", " } } sql += " );" return sql } // AsSQL builds the SQL column definition. func (def ColumnDef) AsSQL() string { sql := def.Name + " " if def.Type == sqlite.TypeText && def.Length > 0 { sql += fmt.Sprintf("VARCHAR(%d)", def.Length) } else { sql += sqlTypeMap[def.Type] } if def.PrimaryKey { sql += " PRIMARY KEY" } if def.AutoIncrement { sql += " AUTOINCREMENT" } if def.Default != nil { sql += " DEFAULT " switch def.Type { //nolint:exhaustive // TODO: handle types BLOB, NULL? case sqlite.TypeFloat: sql += strconv.FormatFloat(def.Default.(float64), 'b', 0, 64) //nolint:forcetypeassert case sqlite.TypeInteger: sql += strconv.FormatInt(def.Default.(int64), 10) //nolint:forcetypeassert case sqlite.TypeText: sql += fmt.Sprintf("%q", def.Default.(string)) //nolint:forcetypeassert default: log.Errorf("unsupported default value: %q %q", def.Type, def.Default) sql = strings.TrimSuffix(sql, " DEFAULT ") } sql += " " } if !def.Nullable { sql += " NOT NULL" } return sql } // GenerateTableSchema generates a table schema from the given struct. func GenerateTableSchema(name string, d interface{}) (*TableSchema, error) { ts := &TableSchema{ Name: name, } val := reflect.Indirect(reflect.ValueOf(d)) if val.Kind() != reflect.Struct { return nil, fmt.Errorf("%w, got %T", errStructExpected, d) } for i := range val.NumField() { fieldType := val.Type().Field(i) if !fieldType.IsExported() { continue } def, err := getColumnDef(fieldType) if err != nil { if errors.Is(err, errSkipStructField) { continue } return nil, fmt.Errorf("struct field %s: %w", fieldType.Name, err) } ts.Columns = append(ts.Columns, *def) } return ts, nil } func getColumnDef(fieldType reflect.StructField) (*ColumnDef, error) { def := &ColumnDef{ Name: fieldType.Name, Nullable: fieldType.Type.Kind() == reflect.Ptr, } ft := fieldType.Type if fieldType.Type.Kind() == reflect.Ptr { ft = fieldType.Type.Elem() } def.GoType = ft kind := NormalizeKind(ft.Kind()) switch kind { //nolint:exhaustive case reflect.Int, reflect.Uint: def.Type = sqlite.TypeInteger case reflect.Float64: def.Type = sqlite.TypeFloat case reflect.String: def.Type = sqlite.TypeText case reflect.Slice: // only []byte/[]uint8 is supported if ft.Elem().Kind() != reflect.Uint8 { return nil, fmt.Errorf("slices of type %s is not supported", ft.Elem()) } def.Type = sqlite.TypeBlob } if err := applyStructFieldTag(fieldType, def); err != nil { return nil, err } return def, nil } // applyStructFieldTag parses the sqlite:"" struct field tag and update the column // definition def accordingly. func applyStructFieldTag(fieldType reflect.StructField, def *ColumnDef) error { parts := strings.Split(fieldType.Tag.Get("sqlite"), ",") if len(parts) > 0 && parts[0] != "" { if parts[0] == "-" { return errSkipStructField } def.Name = parts[0] } if len(parts) > 1 { for _, k := range parts[1:] { switch k { // column modifiers case TagPrimaryKey: def.PrimaryKey = true case TagAutoIncrement: def.AutoIncrement = true case TagNotNull: def.Nullable = false case TagNullable: def.Nullable = true case TagUnixNano: def.UnixNano = true case TagTime: def.IsTime = true // basic column types case TagTypeInt: def.Type = sqlite.TypeInteger case TagTypeText: def.Type = sqlite.TypeText case TagTypeFloat: def.Type = sqlite.TypeFloat case TagTypeBlob: def.Type = sqlite.TypeBlob // advanced column types default: if strings.HasPrefix(k, TagTypePrefixVarchar) { lenStr := strings.TrimSuffix(strings.TrimPrefix(k, TagTypePrefixVarchar+"("), ")") length, err := strconv.ParseInt(lenStr, 10, 0) if err != nil { return fmt.Errorf("failed to parse varchar length %q: %w", lenStr, err) } def.Type = sqlite.TypeText def.Length = int(length) } if strings.HasPrefix(k, TagTypePrefixDefault) { defaultValue := strings.TrimPrefix(k, TagTypePrefixDefault) switch def.Type { //nolint:exhaustive case sqlite.TypeFloat: fv, err := strconv.ParseFloat(defaultValue, 64) if err != nil { return fmt.Errorf("failed to parse default value as float %q: %w", defaultValue, err) } def.Default = fv case sqlite.TypeInteger: fv, err := strconv.ParseInt(defaultValue, 10, 0) if err != nil { return fmt.Errorf("failed to parse default value as int %q: %w", defaultValue, err) } def.Default = fv case sqlite.TypeText: def.Default = defaultValue case sqlite.TypeBlob: return errors.New("default values for TypeBlob not yet supported") default: return fmt.Errorf("failed to apply default value for unknown sqlite column type %s", def.Type) } } } } } return nil } ================================================ FILE: service/netquery/orm/schema_builder_test.go ================================================ package orm import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestSchemaBuilder(t *testing.T) { t.Parallel() cases := []struct { Name string Model interface{} ExpectedSQL string }{ { "Simple", struct { ID int `sqlite:"id,primary,autoincrement"` Text string `sqlite:"text,nullable"` Int *int `sqlite:",not-null"` Float interface{} `sqlite:",float,nullable"` }{}, `CREATE TABLE main.Simple ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, text TEXT, Int INTEGER NOT NULL, Float REAL );`, }, { "Varchar", struct { S string `sqlite:",varchar(10)"` }{}, `CREATE TABLE main.Varchar ( S VARCHAR(10) NOT NULL );`, }, } for idx := range cases { c := cases[idx] res, err := GenerateTableSchema(c.Name, c.Model) require.NoError(t, err) assert.Equal(t, c.ExpectedSQL, res.CreateStatement("main", false)) } } ================================================ FILE: service/netquery/query.go ================================================ package netquery import ( "context" "encoding/json" "fmt" "io" "reflect" "sort" "strings" "github.com/hashicorp/go-multierror" "golang.org/x/exp/slices" "zombiezen.com/go/sqlite" "github.com/safing/portmaster/service/netquery/orm" ) // DatabaseName is a database name constant. type DatabaseName string // Databases. const ( LiveDatabase = DatabaseName("main") HistoryDatabase = DatabaseName("history") ) // Collection of Query and Matcher types. // NOTE: whenever adding support for new operators make sure // to update UnmarshalJSON as well. // //nolint:golint type ( Query map[string][]Matcher MatchType interface { Operator() string } Equal interface{} Matcher struct { Equal interface{} `json:"$eq,omitempty"` NotEqual interface{} `json:"$ne,omitempty"` In []interface{} `json:"$in,omitempty"` NotIn []interface{} `json:"$notIn,omitempty"` Like string `json:"$like,omitempty"` Greater *float64 `json:"$gt,omitempty"` GreaterOrEqual *float64 `json:"$ge,omitempty"` Less *float64 `json:"$lt,omitempty"` LessOrEqual *float64 `json:"$le,omitempty"` } Count struct { As string `json:"as"` Field string `json:"field"` Distinct bool `json:"distinct"` } Sum struct { Condition Query `json:"condition"` Field string `json:"field"` As string `json:"as"` Distinct bool `json:"distinct"` } Min struct { Condition *Query `json:"condition,omitempty"` Field string `json:"field"` As string `json:"as"` Distinct bool `json:"distinct"` } FieldSelect struct { Field string `json:"field"` As string `json:"as"` } Select struct { Field string `json:"field"` FieldSelect *FieldSelect `json:"$field"` Count *Count `json:"$count,omitempty"` Sum *Sum `json:"$sum,omitempty"` Min *Min `json:"$min,omitempty"` Distinct *string `json:"$distinct,omitempty"` } Selects []Select TextSearch struct { Fields []string `json:"fields"` Value string `json:"value"` } QueryActiveConnectionChartPayload struct { Query Query `json:"query"` TextSearch *TextSearch `json:"textSearch"` } OrderBy struct { Field string `json:"field"` Desc bool `json:"desc"` } OrderBys []OrderBy Pagination struct { PageSize int `json:"pageSize"` Page int `json:"page"` } ) // UnmarshalJSON unmarshals a Query from json. func (query *Query) UnmarshalJSON(blob []byte) error { if *query == nil { *query = make(Query) } var model map[string]json.RawMessage if err := json.Unmarshal(blob, &model); err != nil { return err } for columnName, rawColumnQuery := range model { if len(rawColumnQuery) == 0 { continue } switch rawColumnQuery[0] { case '{': m, err := parseMatcher(rawColumnQuery) if err != nil { return err } (*query)[columnName] = []Matcher{*m} case '[': var rawMatchers []json.RawMessage if err := json.Unmarshal(rawColumnQuery, &rawMatchers); err != nil { return err } (*query)[columnName] = make([]Matcher, len(rawMatchers)) for idx, val := range rawMatchers { // this should not happen if len(val) == 0 { continue } // if val starts with a { we have a matcher definition if val[0] == '{' { m, err := parseMatcher(val) if err != nil { return err } (*query)[columnName][idx] = *m continue } else if val[0] == '[' { return fmt.Errorf("invalid token [ in query for column %s", columnName) } // val is a dedicated JSON primitive and not an object or array // so we treat that as an EQUAL condition. var x interface{} if err := json.Unmarshal(val, &x); err != nil { return err } (*query)[columnName][idx] = Matcher{ Equal: x, } } default: // value is a JSON primitive and not an object or array // so we treat that as an EQUAL condition. var x interface{} if err := json.Unmarshal(rawColumnQuery, &x); err != nil { return err } (*query)[columnName] = []Matcher{ {Equal: x}, } } } return nil } // TODO(ppacher): right now we only support LIMIT and OFFSET for pagination but that // has an issue that loading the same page twice might yield different results due to // new records shifting the result slice. To overcome this, return a "PageToken" to the // user that includes the time the initial query was created so paginated queries can // ensure new records don't end up in the result set. func (page *Pagination) toSQLLimitOffsetClause() string { limit := page.PageSize // default and cap the limit to at most 100 items // per page to avoid out-of-memory conditions when loading // thousands of results at once. if limit <= 0 || limit > 100 { limit = 100 } sql := fmt.Sprintf("LIMIT %d", limit) if page.Page > 0 { sql += fmt.Sprintf(" OFFSET %d", page.Page*limit) } return sql } func parseMatcher(raw json.RawMessage) (*Matcher, error) { var m Matcher if err := json.Unmarshal(raw, &m); err != nil { return nil, err } if err := m.Validate(); err != nil { return nil, fmt.Errorf("invalid query matcher: %w", err) } // log.Printf("parsed matcher %s: %+v", string(raw), m) return &m, nil } // Validate validates the matcher. func (match Matcher) Validate() error { found := 0 if match.Equal != nil { found++ } if match.NotEqual != nil { found++ } if match.In != nil { found++ } if match.NotIn != nil { found++ } if match.Like != "" { found++ } if match.Greater != nil { found++ } if match.GreaterOrEqual != nil { found++ } if match.Less != nil { found++ } if match.LessOrEqual != nil { found++ } if found == 0 { return fmt.Errorf("no conditions specified") } return nil } func (text TextSearch) toSQLConditionClause(_ context.Context, schema *orm.TableSchema, suffix string, _ orm.EncodeConfig) (string, map[string]interface{}, error) { var ( queryParts = make([]string, 0, len(text.Fields)) params = make(map[string]interface{}) ) key := fmt.Sprintf(":t%s", suffix) params[key] = fmt.Sprintf("%%%s%%", text.Value) for _, field := range text.Fields { colDef := schema.GetColumnDef(field) if colDef == nil { return "", nil, fmt.Errorf("column %s is not allowed in text-search", colDef.Name) } if colDef.Type != sqlite.TypeText { return "", nil, fmt.Errorf("type of column %s cannot be used in text-search", colDef.Name) } queryParts = append(queryParts, fmt.Sprintf("%s LIKE %s", colDef.Name, key)) } if len(queryParts) == 0 { return "", nil, nil } return "( " + strings.Join(queryParts, " OR ") + " )", params, nil } func (match Matcher) toSQLConditionClause(ctx context.Context, suffix string, conjunction string, colDef orm.ColumnDef, encoderConfig orm.EncodeConfig) (string, map[string]interface{}, error) { var ( queryParts []string params = make(map[string]interface{}) errs = new(multierror.Error) key = fmt.Sprintf("%s%s", colDef.Name, suffix) ) add := func(operator, suffix string, list bool, values ...interface{}) { var placeholder []string for idx, value := range values { var ( encodedValue any err error ) kind := orm.NormalizeKind(reflect.TypeOf(value).Kind()) isNumber := slices.Contains([]reflect.Kind{ reflect.Uint, reflect.Int, reflect.Float64, }, kind) // if we query a time-field that is queried as a number, don't do any encoding // here as the orm.DateTimeEncoder would convert the number to a string. if colDef.IsTime && colDef.Type == sqlite.TypeText && isNumber { encodedValue = value } else { encodedValue, err = orm.EncodeValue(ctx, &colDef, value, encoderConfig) if err != nil { errs.Errors = append(errs.Errors, fmt.Errorf("failed to encode %v for column %s: %w", value, colDef.Name, err), ) return } } uniqKey := fmt.Sprintf(":%s%s%d", key, suffix, idx) placeholder = append(placeholder, uniqKey) params[uniqKey] = encodedValue } nameStmt := colDef.Name if len(values) > 0 { // NOTE(ppacher): for now we assume that the type of each element of values // is the same. We also can be sure that there is always at least one value. // // TODO(ppacher): if we start supporting values of different types here // we need to revisit the whole behavior as we might need to do more boolean // expression nesting to support that. kind := orm.NormalizeKind(reflect.TypeOf(values[0]).Kind()) isNumber := slices.Contains([]reflect.Kind{ reflect.Uint, reflect.Int, reflect.Float64, }, kind) // if this is a time column that is stored in "text" format and the provided // value is a number type, we need to wrap the property in a strftime() method // call. if colDef.IsTime && colDef.Type == sqlite.TypeText && isNumber { nameStmt = fmt.Sprintf("strftime('%%s', %s)+0", nameStmt) } } if len(placeholder) == 1 && !list { queryParts = append(queryParts, fmt.Sprintf("%s %s %s", nameStmt, operator, placeholder[0])) } else { queryParts = append(queryParts, fmt.Sprintf("%s %s ( %s )", nameStmt, operator, strings.Join(placeholder, ", "))) } } if match.Equal != nil { add("=", "eq", false, match.Equal) } if match.NotEqual != nil { add("!=", "ne", false, match.NotEqual) } if match.In != nil { add("IN", "in", true, match.In...) } if match.NotIn != nil { add("NOT IN", "notin", true, match.NotIn...) } if match.Like != "" { add("LIKE", "like", false, match.Like) } if match.Greater != nil { add(">", "gt", false, *match.Greater) } if match.GreaterOrEqual != nil { add(">=", "ge", false, *match.GreaterOrEqual) } if match.Less != nil { add("<", "lt", false, *match.Less) } if match.LessOrEqual != nil { add("<=", "le", false, *match.LessOrEqual) } if len(queryParts) == 0 { // this is an empty matcher without a single condition. // we convert that to a no-op TRUE value return "( 1 = 1 )", nil, errs.ErrorOrNil() } if len(queryParts) == 1 { return queryParts[0], params, errs.ErrorOrNil() } return "( " + strings.Join(queryParts, " "+conjunction+" ") + " )", params, errs.ErrorOrNil() } func (query Query) toSQLWhereClause(ctx context.Context, suffix string, m *orm.TableSchema, encoderConfig orm.EncodeConfig) (string, map[string]interface{}, error) { if len(query) == 0 { return "", nil, nil } // create a lookup map to validate column names lm := make(map[string]orm.ColumnDef, len(m.Columns)) for _, col := range m.Columns { lm[col.Name] = col } paramMap := make(map[string]interface{}) columnStmts := make([]string, 0, len(query)) // get all keys and sort them so we get a stable output queryKeys := make([]string, 0, len(query)) for column := range query { queryKeys = append(queryKeys, column) } sort.Strings(queryKeys) // actually create the WHERE clause parts for each // column in query. errs := new(multierror.Error) for _, column := range queryKeys { values := query[column] colDef, ok := lm[column] if !ok { errs.Errors = append(errs.Errors, fmt.Errorf("column %s is not allowed", column)) continue } queryParts := make([]string, len(values)) for idx, val := range values { matcherQuery, params, err := val.toSQLConditionClause(ctx, fmt.Sprintf("%s%d", suffix, idx), "AND", colDef, encoderConfig) if err != nil { errs.Errors = append(errs.Errors, fmt.Errorf("invalid matcher at index %d for column %s: %w", idx, colDef.Name, err), ) continue } // merge parameters up into the superior parameter map for key, val := range params { if _, ok := paramMap[key]; ok { // This is solely a developer mistake when implementing a matcher so no forgiving ... panic("sqlite parameter collision") } paramMap[key] = val } queryParts[idx] = matcherQuery } columnStmts = append(columnStmts, fmt.Sprintf("( %s )", strings.Join(queryParts, " OR ")), ) } whereClause := strings.Join(columnStmts, " AND ") return whereClause, paramMap, errs.ErrorOrNil() } // UnmarshalJSON unmarshals a Selects from json. func (sel *Selects) UnmarshalJSON(blob []byte) error { if len(blob) == 0 { return io.ErrUnexpectedEOF } // if we are looking at a slice directly decode into // a []Select if blob[0] == '[' { var result []Select if err := json.Unmarshal(blob, &result); err != nil { return err } (*sel) = result return nil } // if it's an object decode into a single select if blob[0] == '{' { var result Select if err := json.Unmarshal(blob, &result); err != nil { return err } *sel = []Select{result} return nil } // otherwise this is just the field name var field string if err := json.Unmarshal(blob, &field); err != nil { return err } return nil } // UnmarshalJSON unmarshals a Select from json. func (sel *Select) UnmarshalJSON(blob []byte) error { if len(blob) == 0 { return io.ErrUnexpectedEOF } // if we have an object at hand decode the select // directly if blob[0] == '{' { var res struct { Field string `json:"field"` Count *Count `json:"$count"` Sum *Sum `json:"$sum"` Min *Min `json:"$min"` Distinct *string `json:"$distinct"` FieldSelect *FieldSelect `json:"$field"` } if err := json.Unmarshal(blob, &res); err != nil { return err } sel.Count = res.Count sel.Field = res.Field sel.FieldSelect = res.FieldSelect sel.Distinct = res.Distinct sel.Sum = res.Sum sel.Min = res.Min if sel.Count != nil && sel.Count.As != "" { if !charOnlyRegexp.MatchString(sel.Count.As) { return fmt.Errorf("invalid characters in $count.as, value must match [a-zA-Z]+") } } if sel.Sum != nil && sel.Sum.As != "" { if !charOnlyRegexp.MatchString(sel.Sum.As) { return fmt.Errorf("invalid characters in $sum.as, value must match [a-zA-Z]+") } } if sel.Min != nil && sel.Min.As != "" { if !charOnlyRegexp.MatchString(sel.Min.As) { return fmt.Errorf("invalid characters in $min.as, value must match [a-zA-Z]+") } } if sel.FieldSelect != nil && sel.FieldSelect.As != "" { if !charOnlyRegexp.MatchString(sel.FieldSelect.As) { return fmt.Errorf("invalid characters in $field.as, value must match [a-zA-Z]+") } } return nil } var x string if err := json.Unmarshal(blob, &x); err != nil { return err } sel.Field = x return nil } // UnmarshalJSON unmarshals a OrderBys from json. func (orderBys *OrderBys) UnmarshalJSON(blob []byte) error { if len(blob) == 0 { return io.ErrUnexpectedEOF } if blob[0] == '[' { var result []OrderBy if err := json.Unmarshal(blob, &result); err != nil { return err } *orderBys = result return nil } if blob[0] == '{' { var result OrderBy if err := json.Unmarshal(blob, &result); err != nil { return err } *orderBys = []OrderBy{result} return nil } var field string if err := json.Unmarshal(blob, &field); err != nil { return err } *orderBys = []OrderBy{ { Field: field, Desc: false, }, } return nil } // UnmarshalJSON unmarshals a OrderBy from json. func (orderBy *OrderBy) UnmarshalJSON(blob []byte) error { if len(blob) == 0 { return io.ErrUnexpectedEOF } if blob[0] == '{' { var res struct { Field string `json:"field"` Desc bool `json:"desc"` } if err := json.Unmarshal(blob, &res); err != nil { return err } orderBy.Desc = res.Desc orderBy.Field = res.Field return nil } var field string if err := json.Unmarshal(blob, &field); err != nil { return err } orderBy.Field = field orderBy.Desc = false return nil } ================================================ FILE: service/netquery/query_handler.go ================================================ package netquery import ( "bytes" "encoding/json" "errors" "fmt" "io" "net/http" "regexp" "strings" "github.com/hashicorp/go-multierror" servertiming "github.com/mitchellh/go-server-timing" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/netquery/orm" ) var charOnlyRegexp = regexp.MustCompile("[a-zA-Z]+") const failedQuery = "Failed to execute query: " type ( // QueryHandler implements http.Handler and allows to perform SQL // query and aggregate functions on Database. QueryHandler struct { IsDevMode func() bool Database *Database } // BatchQueryHandler implements http.Handler and allows to perform SQL // query and aggregate functions on Database in batches. BatchQueryHandler struct { IsDevMode func() bool Database *Database } ) func (qh *QueryHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { timing := servertiming.FromContext(req.Context()) timingQueryParsed := timing.NewMetric("query_parsed"). WithDesc("Query has been parsed"). Start() requestPayload, err := parseQueryRequestPayload[QueryRequestPayload](req) if err != nil { http.Error(resp, err.Error(), http.StatusBadRequest) return } timingQueryParsed.Stop() timingQueryBuilt := timing.NewMetric("query_built"). WithDesc("The SQL query has been built"). Start() query, paramMap, err := requestPayload.generateSQL(req.Context(), qh.Database.Schema) if err != nil { http.Error(resp, err.Error(), http.StatusBadRequest) return } timingQueryBuilt.Stop() timingQueryExecute := timing.NewMetric("sql_exec"). WithDesc("SQL query execution time"). Start() // actually execute the query against the database and collect the result var result []map[string]interface{} if err := qh.Database.Execute( req.Context(), query, orm.WithNamedArgs(paramMap), orm.WithResult(&result), orm.WithSchema(*qh.Database.Schema), ); err != nil { http.Error(resp, failedQuery+err.Error(), http.StatusInternalServerError) return } timingQueryExecute.Stop() // send the HTTP status code resp.WriteHeader(http.StatusOK) // prepare the result encoder. enc := json.NewEncoder(resp) enc.SetEscapeHTML(false) enc.SetIndent("", " ") // prepare the result body that, in dev mode, contains // some diagnostics data about the query var resultBody map[string]interface{} if qh.IsDevMode() { resultBody = map[string]interface{}{ "sql_prep_stmt": query, "sql_params": paramMap, "query": requestPayload.Query, "orderBy": requestPayload.OrderBy, "groupBy": requestPayload.GroupBy, "selects": requestPayload.Select, } } else { resultBody = make(map[string]interface{}) } resultBody["results"] = result // and finally stream the response if err := enc.Encode(resultBody); err != nil { // we failed to encode the JSON body to resp so we likely either already sent a // few bytes or the pipe was already closed. In either case, trying to send the // error using http.Error() is non-sense. We just log it out here and that's all // we can do. log.Errorf("failed to encode JSON response: %s", err) return } } func (batch *BatchQueryHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { timing := servertiming.FromContext(req.Context()) timingQueryParsed := timing.NewMetric("query_parsed"). WithDesc("Query has been parsed"). Start() requestPayload, err := parseQueryRequestPayload[BatchQueryRequestPayload](req) if err != nil { http.Error(resp, err.Error(), http.StatusBadRequest) return } timingQueryParsed.Stop() response := make(map[string][]map[string]any, len(*requestPayload)) batches := make([]BatchExecute, 0, len(*requestPayload)) for key, query := range *requestPayload { timingQueryBuilt := timing.NewMetric("query_built_" + key). WithDesc("The SQL query has been built"). Start() sql, paramMap, err := query.generateSQL(req.Context(), batch.Database.Schema) if err != nil { http.Error(resp, err.Error(), http.StatusBadRequest) return } timingQueryBuilt.Stop() var result []map[string]any batches = append(batches, BatchExecute{ ID: key, SQL: sql, Params: paramMap, Result: &result, }) } timingQueryExecute := timing.NewMetric("sql_exec"). WithDesc("SQL query execution time"). Start() status := http.StatusOK if err := batch.Database.ExecuteBatch(req.Context(), batches); err != nil { status = http.StatusInternalServerError var merr *multierror.Error if errors.As(err, &merr) { for _, e := range merr.Errors { resp.Header().Add("X-Query-Error", e.Error()) } } else { // Should not happen, ExecuteBatch always returns a multierror.Error resp.WriteHeader(status) return } } timingQueryExecute.Stop() // collect the results for _, b := range batches { response[b.ID] = *b.Result } // send the HTTP status code resp.WriteHeader(status) // prepare the result encoder. enc := json.NewEncoder(resp) enc.SetEscapeHTML(false) enc.SetIndent("", " ") // and finally stream the response if err := enc.Encode(response); err != nil { // we failed to encode the JSON body to resp so we likely either already sent a // few bytes or the pipe was already closed. In either case, trying to send the // error using http.Error() is non-sense. We just log it out here and that's all // we can do. log.Errorf("failed to encode JSON response: %s", err) return } } func parseQueryRequestPayload[T any](req *http.Request) (*T, error) { //nolint:dupl var ( body io.Reader requestPayload T ) switch req.Method { case http.MethodPost, http.MethodPut: body = req.Body case http.MethodGet: body = strings.NewReader(req.URL.Query().Get("q")) default: return nil, fmt.Errorf("invalid HTTP method") } blob, err := io.ReadAll(body) if err != nil { return nil, fmt.Errorf("failed to read body: %w", err) } body = bytes.NewReader(blob) dec := json.NewDecoder(body) dec.DisallowUnknownFields() if err := json.Unmarshal(blob, &requestPayload); err != nil && !errors.Is(err, io.EOF) { return nil, fmt.Errorf("invalid query: %w", err) } return &requestPayload, nil } // Compile time check. var _ http.Handler = new(QueryHandler) ================================================ FILE: service/netquery/query_request.go ================================================ package netquery import ( "context" "fmt" "strings" "golang.org/x/exp/slices" "github.com/safing/portmaster/service/netquery/orm" ) type ( // QueryRequestPayload describes the payload of a netquery query. QueryRequestPayload struct { Select Selects `json:"select"` Query Query `json:"query"` OrderBy OrderBys `json:"orderBy"` GroupBy []string `json:"groupBy"` TextSearch *TextSearch `json:"textSearch"` // A list of databases to query. If left empty, // both, the LiveDatabase and the HistoryDatabase are queried Databases []DatabaseName `json:"databases"` Pagination selectedFields []string whitelistedFields []string paramMap map[string]interface{} } // BatchQueryRequestPayload describes the payload of a batch netquery // query. The map key is used in the response to identify the results // for each query of the batch request. BatchQueryRequestPayload map[string]QueryRequestPayload ) func (req *QueryRequestPayload) generateSQL(ctx context.Context, schema *orm.TableSchema) (string, map[string]interface{}, error) { if err := req.prepareSelectedFields(ctx, schema); err != nil { return "", nil, fmt.Errorf("perparing selected fields: %w", err) } // build the SQL where clause from the payload query whereClause, paramMap, err := req.Query.toSQLWhereClause( ctx, "", schema, orm.DefaultEncodeConfig, ) if err != nil { return "", nil, fmt.Errorf("generating where clause: %w", err) } req.mergeParams(paramMap) if req.TextSearch != nil { textClause, textParams, err := req.TextSearch.toSQLConditionClause(ctx, schema, "", orm.DefaultEncodeConfig) if err != nil { return "", nil, fmt.Errorf("generating text-search clause: %w", err) } if textClause != "" { if whereClause != "" { whereClause += " AND " } whereClause += textClause req.mergeParams(textParams) } } groupByClause, err := req.generateGroupByClause(schema) if err != nil { return "", nil, fmt.Errorf("generating group-by clause: %w", err) } orderByClause, err := req.generateOrderByClause(schema) if err != nil { return "", nil, fmt.Errorf("generating order-by clause: %w", err) } selectClause := req.generateSelectClause() if whereClause != "" { whereClause = "WHERE " + whereClause } // if no database is specified we default to LiveDatabase only. if len(req.Databases) == 0 { req.Databases = []DatabaseName{LiveDatabase} } sources := make([]string, len(req.Databases)) for idx, db := range req.Databases { sources[idx] = fmt.Sprintf("SELECT * FROM %s.connections %s", db, whereClause) } source := strings.Join(sources, " UNION ") query := `SELECT ` + selectClause + ` FROM ( ` + source + ` ) ` query += " " + groupByClause + " " + orderByClause + " " + req.Pagination.toSQLLimitOffsetClause() return strings.TrimSpace(query), req.paramMap, nil } func (req *QueryRequestPayload) prepareSelectedFields(ctx context.Context, schema *orm.TableSchema) error { for idx, s := range req.Select { var field string switch { case s.Count != nil: field = s.Count.Field case s.Distinct != nil: field = *s.Distinct case s.Sum != nil: if s.Sum.Field != "" { field = s.Sum.Field } else { field = "*" } case s.Min != nil: if s.Min.Field != "" { field = s.Min.Field } else { field = "*" } case s.FieldSelect != nil: field = s.FieldSelect.Field default: field = s.Field } colName := "*" if field != "*" || (s.Count == nil && s.Sum == nil) { var err error colName, err = req.validateColumnName(schema, field) if err != nil { return err } } switch { case s.FieldSelect != nil: as := s.FieldSelect.As if as == "" { as = s.FieldSelect.Field } req.selectedFields = append( req.selectedFields, fmt.Sprintf("%s AS %s", s.FieldSelect.Field, as), ) req.whitelistedFields = append(req.whitelistedFields, as) case s.Count != nil: as := s.Count.As if as == "" { as = fmt.Sprintf("%s_count", colName) } distinct := "" if s.Count.Distinct { distinct = "DISTINCT " } req.selectedFields = append( req.selectedFields, fmt.Sprintf("COUNT(%s%s) AS %s", distinct, colName, as), ) req.whitelistedFields = append(req.whitelistedFields, as) case s.Sum != nil: if s.Sum.As == "" { return fmt.Errorf("missing 'as' for $sum") } var ( clause string params map[string]any ) if s.Sum.Field != "" { clause = s.Sum.Field } else { var err error clause, params, err = s.Sum.Condition.toSQLWhereClause(ctx, fmt.Sprintf("sel%d", idx), schema, orm.DefaultEncodeConfig) if err != nil { return fmt.Errorf("in $sum: %w", err) } } req.mergeParams(params) req.selectedFields = append( req.selectedFields, fmt.Sprintf("SUM(%s) AS %s", clause, s.Sum.As), ) req.whitelistedFields = append(req.whitelistedFields, s.Sum.As) case s.Min != nil: if s.Min.As == "" { return fmt.Errorf("missing 'as' for $min") } var ( clause string params map[string]any ) if s.Min.Field != "" { clause = field } else { var err error clause, params, err = s.Min.Condition.toSQLWhereClause(ctx, fmt.Sprintf("sel%d", idx), schema, orm.DefaultEncodeConfig) if err != nil { return fmt.Errorf("in $min: %w", err) } } req.mergeParams(params) req.selectedFields = append( req.selectedFields, fmt.Sprintf("MIN(%s) AS %s", clause, s.Min.As), ) req.whitelistedFields = append(req.whitelistedFields, s.Min.As) case s.Distinct != nil: req.selectedFields = append(req.selectedFields, fmt.Sprintf("DISTINCT %s", colName)) req.whitelistedFields = append(req.whitelistedFields, colName) default: req.selectedFields = append(req.selectedFields, colName) } } return nil } func (req *QueryRequestPayload) mergeParams(params map[string]any) { if req.paramMap == nil { req.paramMap = make(map[string]any) } for key, value := range params { req.paramMap[key] = value } } func (req *QueryRequestPayload) generateGroupByClause(schema *orm.TableSchema) (string, error) { if len(req.GroupBy) == 0 { return "", nil } groupBys := make([]string, len(req.GroupBy)) for idx, name := range req.GroupBy { colName, err := req.validateColumnName(schema, name) if err != nil { return "", err } groupBys[idx] = colName } groupByClause := "GROUP BY " + strings.Join(groupBys, ", ") // if there are no explicitly selected fields we default to the // group-by columns as that's what's expected most of the time anyway... if len(req.selectedFields) == 0 { req.selectedFields = append(req.selectedFields, groupBys...) } return groupByClause, nil } func (req *QueryRequestPayload) generateSelectClause() string { selectClause := "*" if len(req.selectedFields) > 0 { selectClause = strings.Join(req.selectedFields, ", ") } return selectClause } func (req *QueryRequestPayload) generateOrderByClause(schema *orm.TableSchema) (string, error) { if len(req.OrderBy) == 0 { return "", nil } orderBys := make([]string, len(req.OrderBy)) for idx, sort := range req.OrderBy { colName, err := req.validateColumnName(schema, sort.Field) if err != nil { return "", err } if sort.Desc { orderBys[idx] = fmt.Sprintf("%s DESC", colName) } else { orderBys[idx] = fmt.Sprintf("%s ASC", colName) } } return "ORDER BY " + strings.Join(orderBys, ", "), nil } func (req *QueryRequestPayload) validateColumnName(schema *orm.TableSchema, field string) (string, error) { colDef := schema.GetColumnDef(field) if colDef != nil { return colDef.Name, nil } if slices.Contains(req.whitelistedFields, field) { return field, nil } if slices.Contains(req.selectedFields, field) { return field, nil } return "", fmt.Errorf("column name %q not allowed", field) } ================================================ FILE: service/netquery/query_test.go ================================================ package netquery import ( "context" "encoding/json" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/safing/portmaster/service/netquery/orm" ) func TestUnmarshalQuery(t *testing.T) { //nolint:tparallel t.Parallel() cases := []struct { Name string Input string Expected Query Error error }{ { "Parse a simple query", `{ "domain": ["example.com", "example.at"] }`, Query{ "domain": []Matcher{ { Equal: "example.com", }, { Equal: "example.at", }, }, }, nil, }, { "Parse a more complex query", ` { "domain": [ { "$in": [ "example.at", "example.com" ] }, { "$like": "microsoft.%" } ], "path": [ "/bin/ping", { "$notin": [ "/sbin/ping", "/usr/sbin/ping" ] } ] } `, Query{ "domain": []Matcher{ { In: []interface{}{ "example.at", "example.com", }, }, { Like: "microsoft.%", }, }, "path": []Matcher{ { Equal: "/bin/ping", }, { NotIn: []interface{}{ "/sbin/ping", "/usr/sbin/ping", }, }, }, }, nil, }, } for _, testCase := range cases { //nolint:paralleltest t.Run(testCase.Name, func(t *testing.T) { var q Query err := json.Unmarshal([]byte(testCase.Input), &q) if testCase.Error != nil { if assert.Error(t, err) { assert.Equal(t, testCase.Error.Error(), err.Error()) } } else { require.NoError(t, err) assert.Equal(t, testCase.Expected, q) } }) } } func TestQueryBuilder(t *testing.T) { //nolint:tparallel t.Parallel() now := time.Now() cases := []struct { N string Q Query R string P map[string]interface{} E error }{ { "No filter", nil, "", nil, nil, }, { "Simple, one-column filter", Query{"domain": []Matcher{ { Equal: "example.com", }, { Equal: "example.at", }, }}, "( domain = :domain0eq0 OR domain = :domain1eq0 )", map[string]interface{}{ ":domain0eq0": "example.com", ":domain1eq0": "example.at", }, nil, }, { "Two column filter", Query{ "domain": []Matcher{ { Equal: "example.com", }, }, "path": []Matcher{ { Equal: "/bin/curl", }, { Equal: "/bin/ping", }, }, }, "( domain = :domain0eq0 ) AND ( path = :path0eq0 OR path = :path1eq0 )", map[string]interface{}{ ":domain0eq0": "example.com", ":path0eq0": "/bin/curl", ":path1eq0": "/bin/ping", }, nil, }, { "Time based filter", Query{ "started": []Matcher{ { Equal: now.Format(time.RFC3339), }, }, }, "( started = :started0eq0 )", map[string]interface{}{ ":started0eq0": now.In(time.UTC).Format(orm.SqliteTimeFormat), }, nil, }, { "Invalid column access", Query{ "forbiddenField": []Matcher{{}}, }, "", nil, fmt.Errorf("1 error occurred:\n\t* column forbiddenField is not allowed\n\n"), //nolint:golint }, { "Complex example", Query{ "domain": []Matcher{ { In: []interface{}{"example.at", "example.com"}, }, { Like: "microsoft.%", }, }, "path": []Matcher{ { NotIn: []interface{}{ "/bin/ping", "/sbin/ping", "/usr/bin/ping", }, }, }, }, "( domain IN ( :domain0in0, :domain0in1 ) OR domain LIKE :domain1like0 ) AND ( path NOT IN ( :path0notin0, :path0notin1, :path0notin2 ) )", map[string]interface{}{ ":domain0in0": "example.at", ":domain0in1": "example.com", ":domain1like0": "microsoft.%", ":path0notin0": "/bin/ping", ":path0notin1": "/sbin/ping", ":path0notin2": "/usr/bin/ping", }, nil, }, } tbl, err := orm.GenerateTableSchema("connections", Conn{}) require.NoError(t, err) for cID, testCase := range cases { //nolint:paralleltest t.Run(testCase.N, func(t *testing.T) { str, params, err := testCase.Q.toSQLWhereClause(context.TODO(), "", tbl, orm.DefaultEncodeConfig) if testCase.E != nil { if assert.Error(t, err) { assert.Equal(t, testCase.E.Error(), err.Error(), "test case %d", cID) } } else { require.NoError(t, err, "test case %d", cID) assert.Equal(t, testCase.P, params, "test case %d", cID) assert.Equal(t, testCase.R, str, "test case %d", cID) } }) } } ================================================ FILE: service/netquery/runtime_query_runner.go ================================================ package netquery import ( "context" "encoding/json" "fmt" "strings" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/runtime" "github.com/safing/portmaster/service/netquery/orm" "github.com/safing/structures/dsd" ) // RuntimeQueryRunner provides a simple interface for the runtime database // that allows direct SQL queries to be performed against db. // Each resulting row of that query are marshaled as map[string]interface{} // and returned as a single record to the caller. // // Using portbase/database#Query is not possible because portbase/database will // complain about the SQL query being invalid. To work around that issue, // RuntimeQueryRunner uses a 'GET key' request where the SQL query is embedded into // the record key. type RuntimeQueryRunner struct { db *Database reg *runtime.Registry keyPrefix string } // NewRuntimeQueryRunner returns a new runtime SQL query runner that parses // and serves SQL queries form GET / requests. func NewRuntimeQueryRunner(db *Database, prefix string, reg *runtime.Registry) (*RuntimeQueryRunner, error) { runner := &RuntimeQueryRunner{ db: db, reg: reg, keyPrefix: prefix, } if _, err := reg.Register(prefix, runtime.SimpleValueGetterFunc(runner.get)); err != nil { return nil, fmt.Errorf("failed to register runtime value provider: %w", err) } return runner, nil } func (runner *RuntimeQueryRunner) get(keyOrPrefix string) ([]record.Record, error) { query := strings.TrimPrefix( keyOrPrefix, runner.keyPrefix, ) log.Infof("netquery: executing custom SQL query: %q", query) var result []map[string]interface{} if err := runner.db.Execute(context.Background(), query, orm.WithResult(&result)); err != nil { return nil, fmt.Errorf("failed to perform query %q: %w", query, err) } // we need to wrap the result slice into a map as portbase/database attempts // to inject a _meta field. blob, err := json.Marshal(map[string]interface{}{ "result": result, }) if err != nil { return nil, fmt.Errorf("failed to marshal result: %w", err) } // construct a new record wrapper that uses the already prepared JSON blob. key := fmt.Sprintf("%s:%s", runner.reg.DatabaseName(), keyOrPrefix) wrapper, err := record.NewWrapper(key, new(record.Meta), dsd.JSON, blob) if err != nil { return nil, fmt.Errorf("failed to create record wrapper: %w", err) } return []record.Record{wrapper}, nil } ================================================ FILE: service/network/api.go ================================================ package network import ( "fmt" "net/http" "sort" "strconv" "strings" "time" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/utils/debug" "github.com/safing/portmaster/service/network/state" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/resolver" "github.com/safing/portmaster/service/status" ) func registerAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Path: "debug/network", Read: api.PermitUser, DataFunc: debugInfo, Name: "Get Network Debug Information", Description: "Returns network debugging information, similar to debug/core, but with connection data.", Parameters: []api.Parameter{ { Method: http.MethodGet, Field: "style", Value: "github", Description: "Specify the formatting style. The default is simple markdown formatting.", }, { Method: http.MethodGet, Field: "profile", Value: "/", Description: "Specify a profile source and ID for which network connection should be reported.", }, { Method: http.MethodGet, Field: "where", Value: "", Description: "Specify a query to limit the connections included in the report. The default is to include all connections.", }, }, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: "debug/network/state", Read: api.PermitUser, StructFunc: func(ar *api.Request) (i interface{}, err error) { return state.GetInfo(), nil }, Name: "Get Network State Table Data", Description: "Returns the current network state tables from the OS.", }); err != nil { return err } return nil } // debugInfo returns the debugging information for support requests. func debugInfo(ar *api.Request) (data []byte, err error) { // Create debug information helper. di := new(debug.Info) di.Style = ar.Request.URL.Query().Get("style") // Add debug information. // Very basic information at the start. di.AddVersionInfo() di.AddPlatformInfo(ar.Context()) // Unexpected logs. di.AddLastUnexpectedLogs() // Network Connections. AddNetworkDebugData( di, ar.Request.URL.Query().Get("profile"), ar.Request.URL.Query().Get("where"), ) // Status Information from various modules. status.AddToDebugInfo(di) // captain.AddToDebugInfo(di) // TODO: Cannot use due to import loop. resolver.AddToDebugInfo(di) config.AddToDebugInfo(di) // Detailed information. // compat.AddToDebugInfo(di) // TODO: Cannot use due to interception import requirement which we don't want for SPN Hubs. di.AddGoroutineStack() // Return data. return di.Bytes(), nil } // AddNetworkDebugData adds the network debug data of the given profile to the debug data. func AddNetworkDebugData(di *debug.Info, profile, where string) { // Prepend where prefix to query if necessary. if where != "" && !strings.HasPrefix(where, "where ") { where = "where " + where } // Build query. q, err := query.ParseQuery("query network: " + where) if err != nil { di.AddSection( "Network: Debug Failed", debug.NoFlags, fmt.Sprintf("Failed to build query: %s", err), ) return } // Get iterator. it, err := dbController.Query(q, true, true) if err != nil { di.AddSection( "Network: Debug Failed", debug.NoFlags, fmt.Sprintf("Failed to run query: %s", err), ) return } // Collect matching connections. var ( //nolint:prealloc // We don't know the size. debugConns []*Connection accepted int total int ) for maybeConn := range it.Next { // Switch to correct type. conn, ok := maybeConn.(*Connection) if !ok { continue } // Check if the profile matches if profile != "" { found := false // Get layer IDs and search for a match. layerIDs := conn.Process().Profile().LayerIDs for _, layerID := range layerIDs { if profile == layerID { found = true break } } // Skip if the profile does not match. if !found { continue } } // Count. total++ switch conn.Verdict { //nolint:exhaustive case VerdictAccept, VerdictRerouteToNameserver, VerdictRerouteToTunnel: accepted++ } // Add to list. debugConns = append(debugConns, conn) } // Add it all. di.AddSection( fmt.Sprintf( "Network: %d/%d Connections", accepted, total, ), debug.UseCodeSection|debug.AddContentLineBreaks, buildNetworkDebugInfoData(debugConns), ) } func buildNetworkDebugInfoData(debugConns []*Connection) string { // Sort sort.Sort(connectionsByGroup(debugConns)) // Format lines var buf strings.Builder currentPID := process.UndefinedProcessID for _, conn := range debugConns { conn.Lock() // Add process infomration if it differs from previous connection. if currentPID != conn.ProcessContext.PID { if currentPID != process.UndefinedProcessID { buf.WriteString("\n\n\n") } buf.WriteString("ProfileName: " + conn.ProcessContext.ProfileName) buf.WriteString("\nProfile: " + conn.ProcessContext.Profile) buf.WriteString("\nSource: " + conn.ProcessContext.Source) buf.WriteString("\nProcessName: " + conn.ProcessContext.ProcessName) buf.WriteString("\nBinaryPath: " + conn.ProcessContext.BinaryPath) buf.WriteString("\nCmdLine: " + conn.ProcessContext.CmdLine) buf.WriteString("\nPID: " + strconv.Itoa(conn.ProcessContext.PID)) buf.WriteString("\n") // Set current PID in order to not print the process information again. currentPID = conn.ProcessContext.PID } // Add connection. buf.WriteString("\n") buf.WriteString(conn.debugInfoLine()) conn.Unlock() } return buf.String() } func (conn *Connection) debugInfoLine() string { var connectionData string if conn.Type == IPConnection { // Format IP/Port pair for connections. connectionData = fmt.Sprintf( "% 15s:%- 5s %s % 15s:%- 5s", conn.LocalIP, strconv.Itoa(int(conn.LocalPort)), conn.fmtProtocolAndDirectionComponent(conn.IPProtocol.String()), conn.Entity.IP, strconv.Itoa(int(conn.Entity.Port)), ) } else { // Leave empty for DNS Requests. connectionData = " " } return fmt.Sprintf( "% 14s %s%- 25s %s-%s P#%d [%s] %s - by %s @ %s", conn.VerdictVerb(), connectionData, conn.fmtDomainComponent(), time.Unix(conn.Started, 0).Format("15:04:05"), conn.fmtEndTimeComponent(), conn.ProcessContext.PID, conn.fmtFlagsComponent(), conn.Reason.Msg, conn.Reason.OptionKey, conn.fmtReasonProfileComponent(), ) } func (conn *Connection) fmtDomainComponent() string { if conn.Entity.Domain != "" { return " to " + conn.Entity.Domain } return "" } func (conn *Connection) fmtProtocolAndDirectionComponent(protocol string) string { if conn.Inbound { return "<" + protocol } return protocol + ">" } func (conn *Connection) fmtFlagsComponent() string { var f string if conn.Internal { f += "I" } if conn.Encrypted { f += "E" } if conn.Tunneled { f += "T" } if len(conn.activeInspectors) > 0 { f += "A" } if conn.addedToMetrics { f += "M" } return f } func (conn *Connection) fmtEndTimeComponent() string { if conn.Ended == 0 { return " " // Use same width as a timestamp. } return time.Unix(conn.Ended, 0).Format("15:04:05") } func (conn *Connection) fmtReasonProfileComponent() string { if conn.Reason.Profile == "" { return "global" } return conn.Reason.Profile } type connectionsByGroup []*Connection func (a connectionsByGroup) Len() int { return len(a) } func (a connectionsByGroup) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a connectionsByGroup) Less(i, j int) bool { // Sort by: // 1. Profile ID if a[i].ProcessContext.Profile != a[j].ProcessContext.Profile { return a[i].ProcessContext.Profile < a[j].ProcessContext.Profile } // 2. Process Binary if a[i].ProcessContext.BinaryPath != a[j].ProcessContext.BinaryPath { return a[i].ProcessContext.BinaryPath < a[j].ProcessContext.BinaryPath } // 3. Process ID if a[i].ProcessContext.PID != a[j].ProcessContext.PID { return a[i].ProcessContext.PID < a[j].ProcessContext.PID } // 4. Started return a[i].Started < a[j].Started } ================================================ FILE: service/network/api_test.go ================================================ package network import ( "fmt" "net" "testing" "github.com/safing/portmaster/service/intel" ) func TestDebugInfoLineFormatting(t *testing.T) { t.Parallel() for _, conn := range connectionTestData { fmt.Println(conn.debugInfoLine()) } } func TestDebugInfoFormatting(t *testing.T) { t.Parallel() fmt.Println(buildNetworkDebugInfoData(connectionTestData)) } var connectionTestData = []*Connection{ { ID: "17-255.255.255.255-29810-192.168.0.23-40672", Scope: "IL", IPVersion: 4, Inbound: true, IPProtocol: 17, LocalIP: net.ParseIP("255.255.255.255"), LocalPort: 29810, Entity: &intel.Entity{ Protocol: 17, Port: 40672, Domain: "", ReverseDomain: "", IP: net.ParseIP("192.168.0.23"), Country: "", ASN: 0, }, Verdict: 2, Reason: Reason{ Msg: "incoming connection blocked by default", OptionKey: "filter/serviceEndpoints", Profile: "", }, Started: 1614010349, Ended: 1614010350, VerdictPermanent: true, Inspecting: false, Tunneled: false, Encrypted: false, ProcessContext: ProcessContext{ ProcessName: "Unidentified Processes", ProfileName: "Unidentified Processes", BinaryPath: "", PID: -1, Profile: "_unidentified", Source: "local", }, Internal: false, ProfileRevisionCounter: 1, }, { ID: "6-192.168.0.176-55216-13.32.6.15-80", Scope: "PI", IPVersion: 4, Inbound: false, IPProtocol: 6, LocalIP: net.ParseIP("192.168.0.176"), LocalPort: 55216, Entity: &intel.Entity{ Protocol: 6, Port: 80, Domain: "", ReverseDomain: "", IP: net.ParseIP("13.32.6.15"), Country: "DE", ASN: 16509, }, Verdict: 2, Reason: Reason{ Msg: "default permit", OptionKey: "filter/defaultAction", Profile: "", }, Started: 1614010475, Ended: 1614010565, VerdictPermanent: true, Inspecting: false, Tunneled: false, Encrypted: false, ProcessContext: ProcessContext{ ProcessName: "NetworkManager", ProfileName: "Network Manager", BinaryPath: "/usr/sbin/NetworkManager", PID: 1273, Profile: "3a9b0eb5-c7fe-4bc7-9b93-a90f4ff84b5b", Source: "local", }, Internal: true, ProfileRevisionCounter: 1, }, { ID: "6-192.168.0.176-49982-142.250.74.211-443", Scope: "pkg.go.dev.", IPVersion: 4, Inbound: false, IPProtocol: 6, LocalIP: net.ParseIP("192.168.0.176"), LocalPort: 49982, Entity: &intel.Entity{ Protocol: 6, Port: 443, Domain: "pkg.go.dev.", ReverseDomain: "", CNAME: []string{ "ghs.googlehosted.com.", }, IP: net.ParseIP("142.250.74.211"), Country: "US", ASN: 15169, }, Verdict: 2, Reason: Reason{ Msg: "default permit", OptionKey: "filter/defaultAction", Profile: "", }, Started: 1614010415, Ended: 1614010745, VerdictPermanent: true, Inspecting: false, Tunneled: false, Encrypted: false, ProcessContext: ProcessContext{ ProcessName: "firefox", ProfileName: "Firefox", BinaryPath: "/usr/bin/firefox", PID: 5710, Profile: "74b30392-9e4d-4157-83a9-fffafd3e2bde", Source: "local", }, Internal: false, ProfileRevisionCounter: 1, }, } ================================================ FILE: service/network/clean.go ================================================ package network import ( "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/network/state" "github.com/safing/portmaster/service/process" ) const ( // EndConnsAfterInactiveFor defines the amount of time after not seen // connections of unsupported protocols are marked as ended. EndConnsAfterInactiveFor = 5 * time.Minute // EndICMPConnsAfterInactiveFor defines the amount of time after not seen // ICMP "connections" are marked as ended. EndICMPConnsAfterInactiveFor = 1 * time.Minute // DeleteConnsAfterEndedThreshold defines the amount of time after which // ended connections should be removed from the internal connection state. DeleteConnsAfterEndedThreshold = 10 * time.Minute // DeleteIncompleteConnsAfterStartedThreshold defines the amount of time after // which incomplete connections should be removed from the internal // connection state. DeleteIncompleteConnsAfterStartedThreshold = 1 * time.Minute cleanerTickDuration = 5 * time.Second ) func connectionCleaner(ctx *mgr.WorkerCtx) error { module.connectionCleanerTicker = mgr.NewSleepyTicker(cleanerTickDuration, 0) defer module.connectionCleanerTicker.Stop() for { select { case <-ctx.Done(): return nil case <-module.connectionCleanerTicker.Wait(): // clean connections and processes activePIDs := cleanConnections() process.CleanProcessStorage(activePIDs) // clean udp connection states state.CleanUDPStates(ctx.Ctx()) } } } func cleanConnections() (activePIDs map[int]struct{}) { activePIDs = make(map[int]struct{}) _ = module.mgr.Do("clean connections", func(ctx *mgr.WorkerCtx) error { now := time.Now().UTC() nowUnix := now.Unix() ignoreNewer := nowUnix - 2 endNotSeenSince := now.Add(-EndConnsAfterInactiveFor).Unix() endICMPNotSeenSince := now.Add(-EndICMPConnsAfterInactiveFor).Unix() deleteOlderThan := now.Add(-DeleteConnsAfterEndedThreshold).Unix() deleteIncompleteOlderThan := now.Add(-DeleteIncompleteConnsAfterStartedThreshold).Unix() // network connections for _, conn := range conns.clone() { conn.Lock() // delete inactive connections switch { case conn.Started >= ignoreNewer: // Skip very fresh connections to evade edge cases. case !conn.DataIsComplete(): // Step 0: delete old incomplete connections if conn.Started < deleteIncompleteOlderThan { // Stop the firewall handler, in case one is running. conn.StopFirewallHandler() // Remove connection from state. conn.delete() } case conn.Ended == 0: // Step 1: check if still active var connActive bool switch conn.IPProtocol { //nolint:exhaustive case packet.TCP, packet.UDP: connActive = state.Exists(&packet.Info{ Inbound: false, // src == local Version: conn.IPVersion, Protocol: conn.IPProtocol, Src: conn.LocalIP, SrcPort: conn.LocalPort, Dst: conn.Entity.IP, DstPort: conn.Entity.Port, PID: process.UndefinedProcessID, SeenAt: time.Unix(conn.Started, 0), // State tables will be updated if older than this. }, now) // Update last seen value for permanent verdict connections. if connActive && conn.VerdictPermanent { conn.lastSeen.Store(nowUnix) } case packet.ICMP, packet.ICMPv6: connActive = conn.lastSeen.Load() > endICMPNotSeenSince default: connActive = conn.lastSeen.Load() > endNotSeenSince } // Step 2: mark as ended if !connActive { conn.Ended = nowUnix // Stop the firewall handler, in case one is running. conn.StopFirewallHandler() // Save to database. conn.Save() } // If the connection has an associated process, add its PID to the active PID list. if conn.process != nil { activePIDs[conn.process.Pid] = struct{}{} } case conn.Ended < deleteOlderThan: // Step 3: delete // DEBUG: // log.Tracef("network.clean: deleted %s (ended at %s)", conn.DatabaseKey(), time.Unix(conn.Ended, 0)) // Remove connection from state. conn.delete() } conn.Unlock() } // dns requests for _, conn := range dnsConns.clone() { conn.Lock() // delete old dns connections if conn.Ended < deleteOlderThan { log.Tracef("network.clean: deleted %s (ended at %s)", conn.DatabaseKey(), time.Unix(conn.Ended, 0)) conn.delete() } conn.Unlock() } // rerouted dns requests cleanDNSRequestConnections() return nil }) return activePIDs } ================================================ FILE: service/network/connection.go ================================================ package network import ( "context" "errors" "fmt" "net" "runtime" "sync" "sync/atomic" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/network/reference" "github.com/safing/portmaster/service/process" _ "github.com/safing/portmaster/service/process/tags" "github.com/safing/portmaster/service/resolver" "github.com/safing/portmaster/spn/access" "github.com/safing/portmaster/spn/access/account" "github.com/safing/portmaster/spn/navigator" ) // FirewallHandler defines the function signature for a firewall // handle function. A firewall handler is responsible for finding // a reasonable verdict for the connection conn. The connection is // locked before the firewall handler is called. type FirewallHandler func(conn *Connection, pkt packet.Packet) // ProcessContext holds additional information about the process // that initiated a connection. type ProcessContext struct { // ProcessName is the name of the process. ProcessName string // ProfileName is the name of the profile. ProfileName string // BinaryPath is the path to the process binary. BinaryPath string // CmdLine holds the execution parameters. CmdLine string // PID is the process identifier. PID int // CreatedAt the time when the process was created. CreatedAt int64 // Profile is the ID of the main profile that // is applied to the process. Profile string // Source is the source of the profile. Source string } // ConnectionType is a type of connection. type ConnectionType int8 // Connection Types. const ( Undefined ConnectionType = iota IPConnection DNSRequest ) // Connection describes a distinct physical network connection // identified by the IP/Port pair. type Connection struct { //nolint:maligned // TODO: fix alignment record.Base sync.Mutex // ID holds a unique request/connection id and is considered immutable after // creation. ID string // Type defines the connection type. Type ConnectionType // External defines if the connection represents an external request or // connection. External bool // Scope defines the scope of a connection. For DNS requests, the // scope is always set to the domain name. For direct packet // connections the scope consists of the involved network environment // and the packet direction. Once a connection object is created, // Scope is considered immutable. // Deprecated: This field holds duplicate information, which is accessible // clearer through other attributes. Please use conn.Type, conn.Inbound // and conn.Entity.Domain instead. Scope string // IPVersion is set to the packet IP version. It is not set (0) for // connections created from a DNS request. IPVersion packet.IPVersion // Inbound is set to true if the connection is incoming. Inbound is // only set when a connection object is created and is considered // immutable afterwards. Inbound bool // IPProtocol is set to the transport protocol used by the connection. // Is is considered immutable once a connection object has been // created. IPProtocol is not set for connections that have been // created from a DNS request. IPProtocol packet.IPProtocol // LocalIP holds the local IP address of the connection. It is not // set for connections created from DNS requests. LocalIP is // considered immutable once a connection object has been created. LocalIP net.IP // LocalIPScope holds the network scope of the local IP. LocalIPScope netutils.IPScope // LocalPort holds the local port of the connection. It is not // set for connections created from DNS requests. LocalPort is // considered immutable once a connection object has been created. LocalPort uint16 // PID holds the PID of the owning process. PID int // Entity describes the remote entity that the connection has been // established to. The entity might be changed or information might // be added to it during the livetime of a connection. Access to // entity must be guarded by the connection lock. Entity *intel.Entity // Resolver holds information about the resolver used to resolve // Entity.Domain. Resolver *resolver.ResolverInfo // Verdict holds the decisions that are made for a connection // The verdict may change so any access to it must be guarded by the // connection lock. Verdict Verdict // Whether or not the connection has been established at least once. ConnectionEstablished bool // Reason holds information justifying the verdict, as well as additional // information about the reason. // Access to Reason must be guarded by the connection lock. Reason Reason // Started holds the number of seconds in UNIX epoch time at which // the connection has been initiated and first seen by the portmaster. // Started is only ever set when creating a new connection object // and is considered immutable afterwards. Started int64 // Ended is set to the number of seconds in UNIX epoch time at which // the connection is considered terminated. Ended may be set at any // time so access must be guarded by the connection lock. Ended int64 // VerdictPermanent is set to true if the final verdict is permanent // and the connection has been (or will be) handed back to the kernel. // VerdictPermanent may be changed together with the Verdict and Reason // properties and must be guarded using the connection lock. VerdictPermanent bool // Inspecting is set to true if the connection is being inspected // by one or more of the registered inspectors. This property may // be changed during the lifetime of a connection and must be guarded // using the connection lock. Inspecting bool // Tunneled is set to true when the connection has been routed through the // SPN. Tunneled bool // Encrypted is currently unused and MUST be ignored. Encrypted bool // TunnelOpts holds options for tunneling the connection. TunnelOpts *navigator.Options // ProcessContext holds additional information about the process // that initiated the connection. It is set once when the connection // object is created and is considered immutable afterwards. ProcessContext ProcessContext // DNSContext holds additional information about the DNS request that was // probably used to resolve the IP of this connection. DNSContext *resolver.DNSRequestContext // TunnelContext holds additional information about the tunnel that this // connection is using. TunnelContext interface { GetExitNodeID() string StopTunnel() error } // HistoryEnabled is set to true when the connection should be persisted // in the history database. HistoryEnabled bool // BanwidthEnabled is set to true if connection bandwidth data should be persisted // in netquery. BandwidthEnabled bool // BytesReceived holds the observed received bytes of the connection. BytesReceived uint64 // BytesSent holds the observed sent bytes of the connection. BytesSent uint64 // lastSeen holds the timestamp when the connection was last seen. // If permanent verdicts are enabled and bandwidth reporting is not active, // this value will likely not be correct. lastSeen atomic.Int64 // prompt holds the active prompt for this connection, if there is one. prompt *notifications.Notification // promptLock locks the prompt separately from the connection. // This allows goroutines to dismiss the notification, while another goroutine // is waiting for the prompt and holding a lock on the connection. promptLock sync.Mutex // pkgQueue is used to serialize packet handling for a single // connection and is served by the connections packetHandler. pktQueue chan packet.Packet // pktQueueActive signifies whether the packet queue is active and may be written to. pktQueueActive bool // pktQueueLock locks access to pktQueueActive and writing to pktQueue. pktQueueLock sync.Mutex // dataComplete signifies that all information about the connection is // available and an actual packet has been seen. // As long as this flag is not set, the connection may not be evaluated for // a verdict and may not be sent to the UI. dataComplete *abool.AtomicBool // Internal is set to true if the connection is attributed as an // Portmaster internal connection. Internal may be set at different // points and access to it must be guarded by the connection lock. Internal bool // process holds a reference to the actor process. That is, the // process instance that initiated the connection. process *process.Process // firewallHandler is the firewall handler that is called for // each packet sent to pktQueue. firewallHandler FirewallHandler // saveWhenFinished can be set to true during the life-time of // a connection and signals the firewallHandler that a Save() // should be issued after processing the connection. saveWhenFinished bool // activeInspectors is a slice of booleans where each entry // maps to the index of an available inspector. If the value // is true the inspector is currently active. False indicates // that the inspector has finished and should be skipped. activeInspectors []bool // inspectorData holds additional meta data for the inspectors. // using the inspectors index as a map key. inspectorData map[uint8]interface{} // ProfileRevisionCounter is used to track changes to the process // profile and required for correct re-evaluation of a connections // verdict. ProfileRevisionCounter uint64 // addedToMetrics signifies if the connection has already been counted in // the metrics. addedToMetrics bool } // Reason holds information justifying a verdict, as well as additional // information about the reason. type Reason struct { // Msg is a human readable description of the reason. Msg string // OptionKey is the configuration option key of the setting that // was responsible for the verdict. OptionKey string // Profile is the database key of the profile that held the setting // that was responsible for the verdict. Profile string // ReasonContext may hold additional reason-specific information and // any access must be guarded by the connection lock. Context interface{} } func getProcessContext(ctx context.Context, proc *process.Process) ProcessContext { // Gather process information. pCtx := ProcessContext{ ProcessName: proc.Name, BinaryPath: proc.Path, CmdLine: proc.CmdLine, PID: proc.Pid, CreatedAt: proc.CreatedAt, } // Get local profile. localProfile := proc.Profile().LocalProfile() if localProfile == nil { log.Tracer(ctx).Warningf("network: process %s has no profile", proc) return pCtx } // Add profile information and return. pCtx.ProfileName = localProfile.Name pCtx.Profile = localProfile.ID pCtx.Source = string(localProfile.Source) return pCtx } // NewConnectionFromDNSRequest returns a new connection based on the given dns request. func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []string, connID string, localIP net.IP, localPort uint16) *Connection { // Determine IP version. ipVersion := packet.IPv6 if localIP.To4() != nil { ipVersion = packet.IPv4 } // Create packet info for dns request connection. pi := &packet.Info{ Inbound: false, // outbound as we are looking for the process of the source address Version: ipVersion, Protocol: packet.UDP, Src: localIP, // source as in the process we are looking for SrcPort: localPort, // source as in the process we are looking for Dst: nil, // do not record direction DstPort: 0, // do not record direction PID: process.UndefinedProcessID, } // Check if the dns request connection was reported with process info. var proc *process.Process dnsRequestConn, ok := GetDNSRequestConnection(pi) switch { case !ok: // No dns request connection found. case dnsRequestConn.PID < 0: // Process is not identified or is special. case dnsRequestConn.Ended > 0 && dnsRequestConn.Ended < time.Now().Unix()-3: // Connection has already ended (too long ago). log.Tracer(ctx).Debugf("network: found ended dns request connection %s for dns request for %s", dnsRequestConn, fqdn) default: log.Tracer(ctx).Debugf("network: found matching dns request connection %s", dnsRequestConn.String()) // Inherit PID. pi.PID = dnsRequestConn.PID // Inherit process struct itself, as the PID may already be re-used. proc = dnsRequestConn.process } // Find process by remote IP/Port. if pi.PID == process.UndefinedProcessID { pi.PID, _, _ = process.GetPidOfConnection( ctx, pi, ) } // Get process and profile with PID. if proc == nil { proc, _ = process.GetProcessWithProfile(ctx, pi.PID) } timestamp := time.Now().Unix() dnsConn := &Connection{ ID: connID, Type: DNSRequest, Scope: fqdn, PID: proc.Pid, Entity: &intel.Entity{ Domain: fqdn, CNAME: cnames, IPScope: netutils.Global, // Assign a global IP scope as default. }, process: proc, ProcessContext: getProcessContext(ctx, proc), Started: timestamp, Ended: timestamp, dataComplete: abool.NewBool(true), } dnsConn.lastSeen.Store(timestamp) // Inherit internal status of profile. if localProfile := proc.Profile().LocalProfile(); localProfile != nil { dnsConn.Internal = localProfile.Internal if err := dnsConn.UpdateFeatures(); err != nil && !errors.Is(err, access.ErrNotLoggedIn) { log.Tracer(ctx).Warningf("network: failed to check for enabled features: %s", err) } } // DNS Requests are saved by the nameserver depending on the result of the // query. Blocked requests are saved immediately, accepted ones are only // saved if they are not "used" by a connection. dnsConn.UpdateMeta() return dnsConn } // NewConnectionFromExternalDNSRequest returns a connection for an external DNS request. func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cnames []string, connID string, remoteIP net.IP) (*Connection, error) { remoteHost, err := process.GetNetworkHost(ctx, remoteIP) if err != nil { return nil, err } timestamp := time.Now().Unix() dnsConn := &Connection{ ID: connID, Type: DNSRequest, External: true, Scope: fqdn, PID: process.NetworkHostProcessID, Entity: &intel.Entity{ Domain: fqdn, CNAME: cnames, IPScope: netutils.Global, // Assign a global IP scope as default. }, process: remoteHost, ProcessContext: getProcessContext(ctx, remoteHost), Started: timestamp, Ended: timestamp, dataComplete: abool.NewBool(true), } dnsConn.lastSeen.Store(timestamp) // Inherit internal status of profile. if localProfile := remoteHost.Profile().LocalProfile(); localProfile != nil { dnsConn.Internal = localProfile.Internal if err := dnsConn.UpdateFeatures(); err != nil && !errors.Is(err, access.ErrNotLoggedIn) { log.Tracer(ctx).Warningf("network: failed to check for enabled features: %s", err) } } // DNS Requests are saved by the nameserver depending on the result of the // query. Blocked requests are saved immediately, accepted ones are only // saved if they are not "used" by a connection. dnsConn.UpdateMeta() return dnsConn, nil } var tooOldTimestamp = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC).Unix() // NewIncompleteConnection creates a new incomplete connection with only minimal information. func NewIncompleteConnection(pkt packet.Packet) *Connection { info := pkt.Info() // Create new connection object. // We do not yet know the direction of the connection for sure, so we can only set minimal information. conn := &Connection{ ID: pkt.GetConnectionID(), Type: IPConnection, IPVersion: info.Version, IPProtocol: info.Protocol, Started: info.SeenAt.Unix(), PID: info.PID, Inbound: info.Inbound, dataComplete: abool.NewBool(false), } conn.lastSeen.Store(conn.Started) // Bullshit check Started timestamp. if conn.Started < tooOldTimestamp { // Fix timestamp, use current time as fallback. conn.Started = time.Now().Unix() } // Save connection to internal state in order to mitigate creation of // duplicates. Do not propagate yet, as data is not yet complete. conn.UpdateMeta() conns.add(conn) return conn } // GatherConnectionInfo gathers information on the process and remote entity. func (conn *Connection) GatherConnectionInfo(pkt packet.Packet) (err error) { // Create remote entity. if conn.Entity == nil { // Remote conn.Entity = (&intel.Entity{ IP: pkt.Info().RemoteIP(), Protocol: uint8(pkt.Info().Protocol), Port: pkt.Info().RemotePort(), }).Init(pkt.Info().DstPort) // Local conn.SetLocalIP(pkt.Info().LocalIP()) conn.LocalPort = pkt.Info().LocalPort() if conn.Inbound { switch conn.Entity.IPScope { case netutils.HostLocal: conn.Scope = IncomingHost case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast: conn.Scope = IncomingLAN case netutils.Global, netutils.GlobalMulticast: conn.Scope = IncomingInternet case netutils.Undefined, netutils.Invalid: fallthrough default: conn.Scope = IncomingInvalid } } else { // Outbound direct (possibly P2P) connection. switch conn.Entity.IPScope { case netutils.HostLocal: conn.Scope = PeerHost case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast: conn.Scope = PeerLAN case netutils.Global, netutils.GlobalMulticast: conn.Scope = PeerInternet case netutils.Undefined, netutils.Invalid: fallthrough default: conn.Scope = PeerInvalid } } } // Get PID if not yet available. if conn.PID == process.UndefinedProcessID { // Get process by looking at the system state tables. // Apply direction as reported from the state tables. conn.PID, conn.Inbound, _ = process.GetPidOfConnection(pkt.Ctx(), pkt.Info()) // Errors are informational and are logged to the context. } // Only get process and profile with first real packet. // TODO: Remove when we got full VM/Docker support. if pkt.InfoOnly() { return nil } // Get Process and Profile. if conn.process == nil { conn.process, err = process.GetProcessWithProfile(pkt.Ctx(), conn.PID) // Errors are informational and are logged to the context. if err != nil { if pkt.InfoOnly() { conn.process = nil // Try again with real packet. log.Tracer(pkt.Ctx()).Debugf("network: failed to get process and profile of PID %d: %s", conn.PID, err) } else { log.Tracer(pkt.Ctx()).Warningf("network: failed to get process and profile of PID %d: %s", conn.PID, err) } } } // Apply process/profile info to connection. if conn.ProfileRevisionCounter == 0 && conn.process != nil { // Add process/profile metadata for connection. conn.ProcessContext = getProcessContext(pkt.Ctx(), conn.process) conn.ProfileRevisionCounter = conn.process.Profile().RevisionCnt() // Inherit internal status of profile. if localProfile := conn.process.Profile().LocalProfile(); localProfile != nil { conn.Internal = localProfile.Internal if err := conn.UpdateFeatures(); err != nil && !errors.Is(err, access.ErrNotLoggedIn) { log.Tracer(pkt.Ctx()).Warningf("network: connection %s failed to check for enabled features: %s", conn, err) } } } // Find domain and DNS context of entity. if conn.Entity.Domain == "" && conn.process.Profile() != nil { profileScope := conn.process.Profile().LocalProfile().ID // check if we can find a domain for that IP ipinfo, err := resolver.GetIPInfo(profileScope, pkt.Info().RemoteIP().String()) if err != nil { // Try again with the global scope, in case DNS went through the system resolver. ipinfo, err = resolver.GetIPInfo(resolver.IPInfoProfileScopeGlobal, pkt.Info().RemoteIP().String()) } if runtime.GOOS == "windows" && err != nil { // On windows domains may come with delay. if module.instance.Resolver().IsDisabled() && conn.shouldWaitForDomain() { // Flush the dns listener buffer and try again. for i := range 4 { err = module.instance.DNSMonitor().Flush() if err != nil { // Error flushing, dont try again. break } // Try with profile scope ipinfo, err = resolver.GetIPInfo(profileScope, pkt.Info().RemoteIP().String()) if err == nil { log.Tracer(pkt.Ctx()).Debugf("network: found domain with scope (%s) from dnsmonitor after %d tries", profileScope, +1) break } // Try again with the global scope ipinfo, err = resolver.GetIPInfo(resolver.IPInfoProfileScopeGlobal, pkt.Info().RemoteIP().String()) if err == nil { log.Tracer(pkt.Ctx()).Debugf("network: found domain from dnsmonitor after %d tries", i+1) break } time.Sleep(5 * time.Millisecond) } } } if err == nil { lastResolvedDomain := ipinfo.MostRecentDomain() if lastResolvedDomain != nil { conn.Scope = lastResolvedDomain.Domain conn.Entity.Domain = lastResolvedDomain.Domain conn.Entity.CNAME = lastResolvedDomain.CNAMEs conn.DNSContext = lastResolvedDomain.DNSRequestContext conn.Resolver = lastResolvedDomain.Resolver removeOpenDNSRequest(conn.process.Pid, lastResolvedDomain.Domain) } } } // Check if destination IP is the captive portal's IP. if conn.Entity.Domain == "" { portal := netenv.GetCaptivePortal() if pkt.Info().RemoteIP().Equal(portal.IP) { conn.Scope = portal.Domain conn.Entity.Domain = portal.Domain } } // Check if we have all required data for a complete packet. switch { case pkt.InfoOnly(): // We need a full packet. case conn.process == nil: // We need a process. case conn.process.Profile() == nil: // We need a profile. case conn.Entity == nil: // We need an entity. default: // Data is complete! conn.dataComplete.Set() } conn.SaveWhenFinished() return nil } // GetConnection fetches a Connection from the database. func GetConnection(connID string) (*Connection, bool) { return conns.get(connID) } // GetAllConnections Gets all connection. func GetAllConnections() []*Connection { return conns.list() } // GetDNSConnection fetches a DNS Connection from the database. func GetDNSConnection(dnsConnID string) (*Connection, bool) { return dnsConns.get(dnsConnID) } // SetLocalIP sets the local IP address together with its network scope. The // connection is not locked for this. func (conn *Connection) SetLocalIP(ip net.IP) { conn.LocalIP = ip conn.LocalIPScope = netutils.GetIPScope(ip) } // UpdateFeatures checks which connection related features may and should be // used and sets the flags accordingly. // The caller must hold a lock on the connection. func (conn *Connection) UpdateFeatures() error { // Get user. user, err := access.GetUser() if err != nil && !errors.Is(err, access.ErrNotLoggedIn) { return err } // Caution: user may be nil! // Check if history may be used and if it is enabled for this application. conn.HistoryEnabled = false switch { case conn.Internal: // Do not record internal connections, as they are of low interest in the history. // TODO: Should we create a setting for this? case conn.Entity.IPScope.IsLocalhost(): // Do not record localhost-only connections, as they are very low interest in the history. // TODO: Should we create a setting for this? case user.MayUse(account.FeatureHistory): // Check if history may be used and is enabled. lProfile := conn.Process().Profile() if lProfile != nil { conn.HistoryEnabled = lProfile.EnableHistory() } } // Check if bandwidth visibility may be used. conn.BandwidthEnabled = user.MayUse(account.FeatureBWVis) return nil } // AcceptWithContext accepts the connection. func (conn *Connection) AcceptWithContext(reason, reasonOptionKey string, ctx interface{}) { if !conn.SetVerdict(VerdictAccept, reason, reasonOptionKey, ctx) { log.Warningf("filter: tried to accept %s, but current verdict is %s", conn, conn.Verdict) } } // Accept is like AcceptWithContext but only accepts a reason. func (conn *Connection) Accept(reason, reasonOptionKey string) { conn.AcceptWithContext(reason, reasonOptionKey, nil) } // BlockWithContext blocks the connection. func (conn *Connection) BlockWithContext(reason, reasonOptionKey string, ctx interface{}) { if !conn.SetVerdict(VerdictBlock, reason, reasonOptionKey, ctx) { log.Warningf("filter: tried to block %s, but current verdict is %s", conn, conn.Verdict) } } // Block is like BlockWithContext but does only accepts a reason. func (conn *Connection) Block(reason, reasonOptionKey string) { conn.BlockWithContext(reason, reasonOptionKey, nil) } // DropWithContext drops the connection. func (conn *Connection) DropWithContext(reason, reasonOptionKey string, ctx interface{}) { if !conn.SetVerdict(VerdictDrop, reason, reasonOptionKey, ctx) { log.Warningf("filter: tried to drop %s, but current verdict is %s", conn, conn.Verdict) } } // Drop is like DropWithContext but does only accepts a reason. func (conn *Connection) Drop(reason, reasonOptionKey string) { conn.DropWithContext(reason, reasonOptionKey, nil) } // DenyWithContext blocks or drops the link depending on the connection direction. func (conn *Connection) DenyWithContext(reason, reasonOptionKey string, ctx interface{}) { if conn.Inbound { conn.DropWithContext(reason, reasonOptionKey, ctx) } else { conn.BlockWithContext(reason, reasonOptionKey, ctx) } } // Deny is like DenyWithContext but only accepts a reason. func (conn *Connection) Deny(reason, reasonOptionKey string) { conn.DenyWithContext(reason, reasonOptionKey, nil) } // FailedWithContext marks the connection with VerdictFailed and stores the reason. func (conn *Connection) FailedWithContext(reason, reasonOptionKey string, ctx interface{}) { if !conn.SetVerdict(VerdictFailed, reason, reasonOptionKey, ctx) { log.Warningf("filter: tried to drop %s due to error but current verdict is %s", conn, conn.Verdict) } } // Failed is like FailedWithContext but only accepts a string. func (conn *Connection) Failed(reason, reasonOptionKey string) { conn.FailedWithContext(reason, reasonOptionKey, nil) } // SetVerdict sets a new verdict for the connection. func (conn *Connection) SetVerdict(newVerdict Verdict, reason, reasonOptionKey string, reasonCtx interface{}) (ok bool) { conn.SetVerdictDirectly(newVerdict) // Set reason and context. conn.Reason.Msg = reason conn.Reason.Context = reasonCtx // Reset option key. conn.Reason.OptionKey = "" conn.Reason.Profile = "" // Set option key if data is available. if reasonOptionKey != "" { lp := conn.Process().Profile() if lp != nil { conn.Reason.OptionKey = reasonOptionKey conn.Reason.Profile = lp.GetProfileSource(conn.Reason.OptionKey) } } return true // TODO: remove } // SetVerdictDirectly sets the verdict. func (conn *Connection) SetVerdictDirectly(newVerdict Verdict) { conn.Verdict = newVerdict } // VerdictVerb returns the verdict as a verb, while taking any special states // into account. func (conn *Connection) VerdictVerb() string { return conn.Verdict.Verb() } // DataIsComplete returns whether all information about the connection is // available and an actual packet has been seen. // As long as this flag is not set, the connection may not be evaluated for // a verdict and may not be sent to the UI. func (conn *Connection) DataIsComplete() bool { return conn.dataComplete.IsSet() } // Process returns the connection's process. func (conn *Connection) Process() *process.Process { return conn.process } // SaveWhenFinished marks the connection for saving it after the firewall handler. func (conn *Connection) SaveWhenFinished() { conn.saveWhenFinished = true } // Save saves the connection in the storage and propagates the change // through the database system. Save may lock dnsConnsLock or connsLock // in if Save() is called the first time. // Callers must make sure to lock the connection itself before calling // Save(). func (conn *Connection) Save() { conn.UpdateMeta() // nolint:exhaustive switch conn.Verdict { case VerdictAccept, VerdictRerouteToNameserver: conn.ConnectionEstablished = true case VerdictRerouteToTunnel: // this is already handled when the connection tunnel has been // established. default: } // Do not save/update until data is complete. if !conn.DataIsComplete() { return } if !conn.KeyIsSet() { if conn.Type == DNSRequest { conn.SetKey(makeKey(conn.process.Pid, dbScopeDNS, conn.ID)) dnsConns.add(conn) } else { conn.SetKey(makeKey(conn.process.Pid, dbScopeIP, conn.ID)) conns.add(conn) } } conn.addToMetrics() // notify database controller dbController.PushUpdate(conn) } // delete deletes a link from the storage and propagates the change. // delete may lock either the dnsConnsLock or connsLock. Callers // must still make sure to lock the connection itself. func (conn *Connection) delete() { // A connection without an ID has been created from // a DNS request rather than a packet. Choose the correct // connection store here. if conn.Type == IPConnection { conns.delete(conn) } else { dnsConns.delete(conn) } conn.Meta().Delete() // Notify database controller if data is complete and thus connection was previously exposed. if conn.DataIsComplete() { dbController.PushUpdate(conn) } } // GetActiveInspectors returns the list of active inspectors. func (conn *Connection) GetActiveInspectors() []bool { return conn.activeInspectors } // SetActiveInspectors sets the list of active inspectors. func (conn *Connection) SetActiveInspectors(newInspectors []bool) { conn.activeInspectors = newInspectors } // GetInspectorData returns the list of inspector data. func (conn *Connection) GetInspectorData() map[uint8]interface{} { return conn.inspectorData } // SetInspectorData set the list of inspector data. func (conn *Connection) SetInspectorData(newInspectorData map[uint8]interface{}) { conn.inspectorData = newInspectorData } // SetPrompt sets the given prompt on the connection. // If there already is a prompt set, the previous prompt notification is deleted. func (conn *Connection) SetPrompt(prompt *notifications.Notification) { conn.promptLock.Lock() defer conn.promptLock.Unlock() if conn.prompt != nil { conn.prompt.Delete() } conn.prompt = prompt } // RemovePrompt removes the prompt on the connection. func (conn *Connection) RemovePrompt() { conn.promptLock.Lock() defer conn.promptLock.Unlock() if conn.prompt != nil { conn.prompt.Delete() } } // String returns a string representation of conn. func (conn *Connection) String() string { switch { case conn.process == nil || conn.Entity == nil: return conn.ID case conn.Inbound: return fmt.Sprintf("%s <- %s", conn.process, conn.Entity.IP) case conn.Entity.Domain != "": return fmt.Sprintf("%s to %s (%s)", conn.process, conn.Entity.Domain, conn.Entity.IP) default: return fmt.Sprintf("%s -> %s", conn.process, conn.Entity.IP) } } func (conn *Connection) shouldWaitForDomain() bool { // Should wait for Global Unicast, outgoing and not ICMP connections switch { case conn.Entity.IPScope != netutils.Global: return false case conn.Inbound: return false case reference.IsICMP(conn.Entity.Protocol): return false } return true } ================================================ FILE: service/network/connection_android.go ================================================ package network import ( "context" "fmt" "net" "time" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/spn/navigator" "github.com/tevino/abool" ) // NewDefaultConnection creates a new connection with default values except local and remote IPs and protocols. func NewDefaultConnection(localIP net.IP, localPort uint16, remoteIP net.IP, remotePort uint16, ipVersion packet.IPVersion, protocol packet.IPProtocol) *Connection { connInfo := &Connection{ ID: fmt.Sprintf("%s-%s-%d-%s-%d", protocol.String(), localIP, localPort, remoteIP, remotePort), Type: IPConnection, External: false, IPVersion: ipVersion, Inbound: false, IPProtocol: protocol, LocalIP: localIP, LocalIPScope: netutils.Global, LocalPort: localPort, PID: process.UnidentifiedProcessID, Entity: (&intel.Entity{ IP: remoteIP, Protocol: uint8(protocol), Port: remotePort, }).Init(0), Resolver: nil, Started: time.Now().Unix(), VerdictPermanent: false, Tunneled: true, Encrypted: false, DataComplete: abool.NewBool(true), Internal: false, addedToMetrics: true, // Metrics are not needed for now. This will mark the Connection to be ignored. process: process.GetUnidentifiedProcess(context.Background()), } // TODO: Quick fix for the SPN. // Use inspection framework for proper encryption detection. switch connInfo.Entity.DstPort() { case 22, // SSH 443, // HTTPS 465, // SMTP-SSL 853, // DoT 993, // IMAP-SSL 995: // POP3-SSL connInfo.Encrypted = true } var layeredProfile = connInfo.process.Profile() connInfo.TunnelOpts = &navigator.Options{ HubPolicies: layeredProfile.StackedExitHubPolicies(), CheckHubExitPolicyWith: connInfo.Entity, RequireTrustedDestinationHubs: !connInfo.Encrypted, RoutingProfile: layeredProfile.SPNRoutingAlgorithm(), } return connInfo } ================================================ FILE: service/network/connection_handler.go ================================================ package network import ( "context" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network/packet" ) // SetFirewallHandler sets the firewall handler for this link, and starts a // worker to handle the packets. // The caller needs to hold a lock on the connection. // Cannot be called with "nil" handler. Call StopFirewallHandler() instead. func (conn *Connection) SetFirewallHandler(handler FirewallHandler) { if handler == nil { return } // Initialize packet queue, if needed. conn.pktQueueLock.Lock() defer conn.pktQueueLock.Unlock() if !conn.pktQueueActive { conn.pktQueue = make(chan packet.Packet, 100) conn.pktQueueActive = true } // Start packet handler worker when new handler is set. if conn.firewallHandler == nil { module.mgr.Go("packet handler", conn.packetHandlerWorker) } // Set new handler. conn.firewallHandler = handler } // UpdateFirewallHandler sets the firewall handler if it already set and the // given handler is not nil. // The caller needs to hold a lock on the connection. func (conn *Connection) UpdateFirewallHandler(handler FirewallHandler) { if handler != nil && conn.firewallHandler != nil { conn.firewallHandler = handler } } // StopFirewallHandler unsets the firewall handler and stops the handler worker. // The caller needs to hold a lock on the connection. func (conn *Connection) StopFirewallHandler() { conn.pktQueueLock.Lock() defer conn.pktQueueLock.Unlock() // Unset the firewall handler to revert to the default handler. conn.firewallHandler = nil // Signal the packet handler worker that it can stop. if conn.pktQueueActive { close(conn.pktQueue) conn.pktQueueActive = false } // Unset the packet queue so that it can be freed. conn.pktQueue = nil } // HandlePacket queues packet of Link for handling. func (conn *Connection) HandlePacket(pkt packet.Packet) { // Update last seen timestamp. conn.lastSeen.Store(time.Now().Unix()) conn.pktQueueLock.Lock() defer conn.pktQueueLock.Unlock() // execute handler or verdict if conn.pktQueueActive { select { case conn.pktQueue <- pkt: default: log.Debugf( "filter: dropping packet %s, as there is no space in the connection's handling queue", pkt, ) _ = pkt.Drop() } } else { // Run default handler. defaultFirewallHandler(conn, pkt) // Record metrics. packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt) } } var infoOnlyPacketsActive = abool.New() // packetHandlerWorker sequentially handles queued packets. func (conn *Connection) packetHandlerWorker(ctx *mgr.WorkerCtx) error { // Copy packet queue, so we can remove the reference from the connection // when we stop the firewall handler. var pktQueue chan packet.Packet func() { conn.pktQueueLock.Lock() defer conn.pktQueueLock.Unlock() pktQueue = conn.pktQueue }() // pktSeq counts the seen packets. var pktSeq int for { select { case pkt := <-pktQueue: if pkt == nil { return nil } pktSeq++ // Attempt to optimize packet handling order by handling info-only packets first. switch { case pktSeq > 1: // Order correction is only for first packet. case pkt.InfoOnly(): // Correct order only if first packet is not info-only. // We have observed a first packet that is info-only. // Info-only packets seem to be active and working. infoOnlyPacketsActive.Set() case pkt.ExpectInfo(): // Packet itself tells us that we should expect an info-only packet. fallthrough case infoOnlyPacketsActive.IsSet() && pkt.IsOutbound(): // Info-only packets are active and the packet is outbound. // The probability is high that we will also get an info-only packet for this connection. // TODO: Do not do this for forwarded packets in the future. // DEBUG: // log.Debugf("filter: waiting for info only packet in order to pull forward: %s", pkt) select { case infoPkt := <-pktQueue: if infoPkt != nil { // DEBUG: // log.Debugf("filter: packet #%d [pulled forward] info=%v PID=%d packet: %s", pktSeq, infoPkt.InfoOnly(), infoPkt.Info().PID, pkt) packetHandlerHandleConn(ctx.Ctx(), conn, infoPkt) pktSeq++ } case <-time.After(1 * time.Millisecond): } } // DEBUG: // switch { // case pkt.Info().Inbound: // log.Debugf("filter: packet #%d info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt) // case pktSeq == 1 && !pkt.InfoOnly(): // log.Warningf("filter: packet #%d [should be info only!] info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt) // case pktSeq >= 2 && pkt.InfoOnly(): // log.Errorf("filter: packet #%d [should not be info only!] info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt) // default: // log.Debugf("filter: packet #%d info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt) // } packetHandlerHandleConn(ctx.Ctx(), conn, pkt) case <-ctx.Done(): return nil } } } func packetHandlerHandleConn(ctx context.Context, conn *Connection, pkt packet.Packet) { conn.Lock() defer conn.Unlock() // Check if we should use the default handler. // The default handler is only for fully decided // connections and just applying the verdict. // There is no logging for these packets. if conn.firewallHandler == nil { // Run default handler. defaultFirewallHandler(conn, pkt) // Record metrics. packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt) return } // Create tracing context. // Add context tracer and set context on packet. traceCtx, tracer := log.AddTracer(ctx) if tracer != nil { // The trace is submitted in `network.Connection.packetHandler()`. tracer.Tracef("filter: handling packet: %s", pkt) } pkt.SetCtx(traceCtx) // Handle packet with set handler. conn.firewallHandler(conn, pkt) // Record metrics. packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt) // Log result and submit trace, when there are any changes. if conn.saveWhenFinished { switch { case conn.DataIsComplete(): tracer.Infof("filter: connection %s %s: %s", conn, conn.VerdictVerb(), conn.Reason.Msg) case conn.Verdict != VerdictUndecided: tracer.Debugf("filter: connection %s fast-tracked", pkt) default: tracer.Debugf("filter: gathered data on connection %s", conn) } // Submit trace logs. tracer.Submit() } // Push changes, if there are any. if conn.saveWhenFinished { conn.saveWhenFinished = false conn.Save() } } ================================================ FILE: service/network/connection_store.go ================================================ package network import ( "strings" "sync" ) type connectionStore struct { rw sync.RWMutex items map[string]*Connection } func newConnectionStore() *connectionStore { return &connectionStore{ items: make(map[string]*Connection, 100), } } func (cs *connectionStore) add(conn *Connection) { cs.rw.Lock() defer cs.rw.Unlock() cs.items[conn.ID] = conn } func (cs *connectionStore) delete(conn *Connection) { cs.rw.Lock() defer cs.rw.Unlock() delete(cs.items, conn.ID) } func (cs *connectionStore) get(id string) (*Connection, bool) { cs.rw.RLock() defer cs.rw.RUnlock() conn, ok := cs.items[id] return conn, ok } // findByPrefix returns the first connection where the key matches the given prefix. // If the prefix matches multiple entries, the result is not deterministic. func (cs *connectionStore) findByPrefix(prefix string) (*Connection, bool) { //nolint:unused cs.rw.RLock() defer cs.rw.RUnlock() for key, conn := range cs.items { if strings.HasPrefix(key, prefix) { return conn, true } } return nil, false } func (cs *connectionStore) clone() map[string]*Connection { cs.rw.RLock() defer cs.rw.RUnlock() m := make(map[string]*Connection, len(cs.items)) for key, conn := range cs.items { m[key] = conn } return m } func (cs *connectionStore) list() []*Connection { cs.rw.RLock() defer cs.rw.RUnlock() l := make([]*Connection, 0, len(cs.items)) for _, conn := range cs.items { l = append(l, conn) } return l } func (cs *connectionStore) len() int { //nolint:unused // TODO: Clean up if still unused. cs.rw.RLock() defer cs.rw.RUnlock() return len(cs.items) } func (cs *connectionStore) active() int { // Clone and count all active connections. var cnt int for _, conn := range cs.clone() { conn.Lock() if conn.Ended != 0 { cnt++ } conn.Unlock() } return cnt } ================================================ FILE: service/network/database.go ================================================ package network import ( "fmt" "strconv" "strings" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/process" ) const ( dbScopeNone = "" dbScopeDNS = "dns" dbScopeIP = "ip" ) var ( dbController *database.Controller dnsConns = newConnectionStore() conns = newConnectionStore() ) // StorageInterface provices a storage.Interface to the // configuration manager. type StorageInterface struct { storage.InjectBase } // Database prefixes: // Processes: network:tree/ // DNS Requests: network:tree//dns/ // IP Connections: network:tree//ip/ func makeKey(pid int, scope, id string) string { if scope == "" { return "network:tree/" + strconv.Itoa(pid) } return fmt.Sprintf("network:tree/%d/%s/%s", pid, scope, id) } func parseDBKey(key string) (processKey string, scope, id string, ok bool) { // Split into segments. segments := strings.Split(key, "/") // Keys have 2 or 4 segments. switch len(segments) { case 4: id = segments[3] fallthrough case 3: scope = segments[2] // Sanity check. switch scope { case dbScopeNone, dbScopeDNS, dbScopeIP: // Parsed id matches possible values. // The empty string is for matching a trailing slash for in query prefix. // TODO: For queries, also prefixes of these values are valid. default: // Unknown scope. return "", "", "", false } fallthrough case 2: processKey = segments[1] return processKey, scope, id, true case 1: // This is a valid query prefix, but not process ID was given. return "", "", "", true default: return "", "", "", false } } // Get returns a database record. func (s *StorageInterface) Get(key string) (record.Record, error) { // Parse key and check if valid. pid, scope, id, ok := parseDBKey(strings.TrimPrefix(key, "network:")) if !ok || pid == "" { return nil, storage.ErrNotFound } switch scope { case dbScopeDNS: if c, ok := dnsConns.get(id); ok && c.DataIsComplete() { return c, nil } case dbScopeIP: if c, ok := conns.get(id); ok && c.DataIsComplete() { return c, nil } case dbScopeNone: if proc, ok := process.GetProcessFromStorage(pid); ok { return proc, nil } } return nil, storage.ErrNotFound } // Query returns a an iterator for the supplied query. func (s *StorageInterface) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { it := iterator.New() module.mgr.Go("connection query", func(_ *mgr.WorkerCtx) error { s.processQuery(q, it) return nil }) return it, nil } func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) { var matches bool pid, scope, _, ok := parseDBKey(q.DatabaseKeyPrefix()) if !ok { it.Finish(nil) return } if pid == "" { // processes for _, proc := range process.All() { func() { proc.Lock() defer proc.Unlock() matches = q.Matches(proc) }() if matches { it.Next <- proc } } } if scope == dbScopeNone || scope == dbScopeDNS { // dns scopes only for _, dnsConn := range dnsConns.clone() { if !dnsConn.DataIsComplete() { continue } func() { dnsConn.Lock() defer dnsConn.Unlock() matches = q.Matches(dnsConn) }() if matches { it.Next <- dnsConn } } } if scope == dbScopeNone || scope == dbScopeIP { // connections for _, conn := range conns.clone() { if !conn.DataIsComplete() { continue } func() { conn.Lock() defer conn.Unlock() matches = q.Matches(conn) }() if matches { it.Next <- conn } } } it.Finish(nil) } func registerAsDatabase() error { _, err := database.Register(&database.Database{ Name: "network", Description: "Network and Firewall Data", StorageType: "injected", }) if err != nil { return err } controller, err := database.InjectDatabase("network", &StorageInterface{}) if err != nil { return err } dbController = controller process.SetDBController(dbController) return nil } ================================================ FILE: service/network/dns.go ================================================ package network import ( "context" "fmt" "strconv" "sync" "time" "github.com/miekg/dns" "golang.org/x/exp/slices" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/nameserver/nsutil" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/resolver" ) var ( dnsRequestConnections = make(map[string]*Connection) // key: -- dnsRequestConnectionsLock sync.RWMutex openDNSRequests = make(map[string]*Connection) // key: / openDNSRequestsLock sync.Mutex supportedDomainToIPRecordTypes = []uint16{ dns.TypeA, dns.TypeAAAA, dns.TypeSVCB, dns.TypeHTTPS, } ) const ( // writeOpenDNSRequestsTickDuration defines the interval in which open dns // requests are written. writeOpenDNSRequestsTickDuration = 5 * time.Second // openDNSRequestLimit defines the duration after which DNS requests without // a following connection are logged. openDNSRequestLimit = 3 * time.Second ) func getDNSRequestConnectionKey(packetInfo *packet.Info) (id string, ok bool) { // We only support protocols with ports. if packetInfo.SrcPort == 0 { return "", false } return fmt.Sprintf("%d-%s-%d", packetInfo.Protocol, packetInfo.Src, packetInfo.SrcPort), true } // SaveDNSRequestConnection saves a dns request connection for later retrieval. func SaveDNSRequestConnection(conn *Connection, pkt packet.Packet) { // Check connection. if conn.PID == process.UndefinedProcessID || conn.PID == process.SystemProcessID { // When re-injecting packets on Windows, they are reported with kernel PID (4). log.Tracer(pkt.Ctx()).Tracef("network: not saving dns request connection because the PID is undefined/kernel") return } // Create key. key, ok := getDNSRequestConnectionKey(pkt.Info()) if !ok { log.Tracer(pkt.Ctx()).Debugf("network: not saving dns request connection %s because the protocol is not supported", pkt) return } // Add or update DNS request connection. log.Tracer(pkt.Ctx()).Tracef("network: saving %s with PID %d as dns request connection for fast DNS request attribution", pkt, conn.PID) dnsRequestConnectionsLock.Lock() defer dnsRequestConnectionsLock.Unlock() dnsRequestConnections[key] = conn } // GetDNSRequestConnection returns a saved dns request connection. func GetDNSRequestConnection(packetInfo *packet.Info) (conn *Connection, ok bool) { // Make key. key, ok := getDNSRequestConnectionKey(packetInfo) if !ok { return nil, false } // Get and return dnsRequestConnectionsLock.RLock() defer dnsRequestConnectionsLock.RUnlock() conn, ok = dnsRequestConnections[key] return conn, ok } // deleteDNSRequestConnection removes a connection from the dns request connections. func deleteDNSRequestConnection(packetInfo *packet.Info) { //nolint:unused,deadcode dnsRequestConnectionsLock.Lock() defer dnsRequestConnectionsLock.Unlock() key, ok := getDNSRequestConnectionKey(packetInfo) if ok { delete(dnsRequestConnections, key) } } // cleanDNSRequestConnections deletes old DNS request connections. func cleanDNSRequestConnections() { deleteOlderThan := time.Now().Unix() - 3 dnsRequestConnectionsLock.Lock() defer dnsRequestConnectionsLock.Unlock() for key, conn := range dnsRequestConnections { conn.Lock() if conn.Ended > 0 && conn.Ended < deleteOlderThan { delete(dnsRequestConnections, key) } conn.Unlock() } } // IsSupportDNSRecordType returns whether the given DSN RR type is supported // by the network package, as in the requests are specially handled and can be // "merged" into the resulting connection. func IsSupportDNSRecordType(rrType uint16) bool { return slices.Contains[[]uint16, uint16](supportedDomainToIPRecordTypes, rrType) } func getDNSRequestCacheKey(pid int, fqdn string, qType uint16) string { return strconv.Itoa(pid) + "/" + fqdn + dns.Type(qType).String() } func removeOpenDNSRequest(pid int, fqdn string) { openDNSRequestsLock.Lock() defer openDNSRequestsLock.Unlock() // Delete PID-specific requests. for _, dnsType := range supportedDomainToIPRecordTypes { delete(openDNSRequests, getDNSRequestCacheKey(pid, fqdn, dnsType)) } // If process is known, also check for non-attributed requests. if pid != process.UnidentifiedProcessID { for _, dnsType := range supportedDomainToIPRecordTypes { delete(openDNSRequests, getDNSRequestCacheKey(process.UnidentifiedProcessID, fqdn, dnsType)) } } } // SaveOpenDNSRequest saves a dns request connection that was allowed to proceed. func SaveOpenDNSRequest(q *resolver.Query, rrCache *resolver.RRCache, conn *Connection) { // Only save requests that actually went out (or triggered an async resolve) to reduce clutter. if rrCache == nil || (rrCache.ServedFromCache && !rrCache.RequestingNew) { return } // Try to "merge" supported requests into the resulting connection. // Save others immediately. if !IsSupportDNSRecordType(uint16(q.QType)) { conn.Save() return } openDNSRequestsLock.Lock() defer openDNSRequestsLock.Unlock() // Do not check for an existing open DNS request, as duplicates in such quick // succession are not worth keeping. // DNS queries are usually retried pretty quick. // Save to open dns requests. key := getDNSRequestCacheKey(conn.process.Pid, conn.Entity.Domain, uint16(q.QType)) openDNSRequests[key] = conn } func openDNSRequestWriter(ctx *mgr.WorkerCtx) error { module.dnsRequestTicker = mgr.NewSleepyTicker(writeOpenDNSRequestsTickDuration, 0) defer module.dnsRequestTicker.Stop() for { select { case <-ctx.Done(): return nil case <-module.dnsRequestTicker.Wait(): writeOpenDNSRequestsToDB() } } } func writeOpenDNSRequestsToDB() { openDNSRequestsLock.Lock() defer openDNSRequestsLock.Unlock() threshold := time.Now().Add(-openDNSRequestLimit).Unix() for id, conn := range openDNSRequests { func() { conn.Lock() defer conn.Unlock() if conn.Ended < threshold { conn.Save() delete(openDNSRequests, id) } }() } } // ReplyWithDNS creates a new reply to the given request with the data from the RRCache, and additional informational records. func (conn *Connection) ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns.Msg { // Select request responder. switch conn.Verdict { case VerdictBlock: return nsutil.BlockIP().ReplyWithDNS(ctx, request) case VerdictDrop: return nil // Do not respond to request. case VerdictFailed: return nsutil.BlockIP().ReplyWithDNS(ctx, request) case VerdictUndecided, VerdictUndeterminable, VerdictAccept, VerdictRerouteToNameserver, VerdictRerouteToTunnel: fallthrough default: reply := nsutil.ServerFailure().ReplyWithDNS(ctx, request) nsutil.AddMessagesToReply(ctx, reply, log.ErrorLevel, "INTERNAL ERROR: incorrect use of Connection DNS Responder") return reply } } // GetExtraRRs returns a slice of RRs with additional informational records. func (conn *Connection) GetExtraRRs(ctx context.Context, request *dns.Msg) []dns.RR { // Select level to add the verdict record with. var level log.Severity switch conn.Verdict { case VerdictFailed: level = log.ErrorLevel case VerdictUndecided, VerdictUndeterminable, VerdictAccept, VerdictBlock, VerdictDrop, VerdictRerouteToNameserver, VerdictRerouteToTunnel: fallthrough default: level = log.InfoLevel } // Create resource record with verdict and reason. rr, err := nsutil.MakeMessageRecord(level, fmt.Sprintf("%s: %s", conn.VerdictVerb(), conn.Reason.Msg)) if err != nil { log.Tracer(ctx).Warningf("filter: failed to add informational record to reply: %s", err) return nil } extra := []dns.RR{rr} // Add additional records from Reason.Context. if rrProvider, ok := conn.Reason.Context.(nsutil.RRProvider); ok { rrs := rrProvider.GetExtraRRs(ctx, request) extra = append(extra, rrs...) } return extra } ================================================ FILE: service/network/iphelper/get.go ================================================ //go:build windows package iphelper import ( "sync" "github.com/safing/portmaster/service/network/socket" ) var ( ipHelper *IPHelper // lock locks access to the whole DLL. // TODO: It's unproven if we can access the iphlpapi.dll concurrently, especially as we might be encountering various versions of the DLL. In the future, we could possibly investigate and improve performance here. lock sync.RWMutex ) // GetTCP4Table returns the system table for IPv4 TCP activity. func GetTCP4Table() (connections []*socket.ConnectionInfo, listeners []*socket.BindInfo, err error) { lock.Lock() defer lock.Unlock() err = checkIPHelper() if err != nil { return nil, nil, err } return ipHelper.getTable(IPv4, TCP) } // GetTCP6Table returns the system table for IPv6 TCP activity. func GetTCP6Table() (connections []*socket.ConnectionInfo, listeners []*socket.BindInfo, err error) { lock.Lock() defer lock.Unlock() err = checkIPHelper() if err != nil { return nil, nil, err } return ipHelper.getTable(IPv6, TCP) } // GetUDP4Table returns the system table for IPv4 UDP activity. func GetUDP4Table() (binds []*socket.BindInfo, err error) { lock.Lock() defer lock.Unlock() err = checkIPHelper() if err != nil { return nil, err } _, binds, err = ipHelper.getTable(IPv4, UDP) return } // GetUDP6Table returns the system table for IPv6 UDP activity. func GetUDP6Table() (binds []*socket.BindInfo, err error) { lock.Lock() defer lock.Unlock() err = checkIPHelper() if err != nil { return nil, err } _, binds, err = ipHelper.getTable(IPv6, UDP) return } ================================================ FILE: service/network/iphelper/iphelper.go ================================================ //go:build windows package iphelper import ( "errors" "fmt" "github.com/tevino/abool" "golang.org/x/sys/windows" ) var ( errInvalid = errors.New("IPHelper not initialized or broken") ) // IPHelper represents a subset of the Windows iphlpapi.dll. type IPHelper struct { dll *windows.LazyDLL getExtendedTCPTable *windows.LazyProc getExtendedUDPTable *windows.LazyProc valid *abool.AtomicBool } func checkIPHelper() (err error) { if ipHelper == nil { ipHelper, err = New() return err } return nil } // New returns a new IPHelper API (with an instance of iphlpapi.dll loaded). func New() (*IPHelper, error) { new := &IPHelper{} new.valid = abool.NewBool(false) var err error // load dll new.dll = windows.NewLazySystemDLL("iphlpapi.dll") err = new.dll.Load() if err != nil { return nil, err } // load functions new.getExtendedTCPTable = new.dll.NewProc("GetExtendedTcpTable") err = new.getExtendedTCPTable.Find() if err != nil { return nil, fmt.Errorf("could find proc GetExtendedTcpTable: %s", err) } new.getExtendedUDPTable = new.dll.NewProc("GetExtendedUdpTable") err = new.getExtendedUDPTable.Find() if err != nil { return nil, fmt.Errorf("could find proc GetExtendedUdpTable: %s", err) } new.valid.Set() return new, nil } ================================================ FILE: service/network/iphelper/tables.go ================================================ //go:build windows package iphelper import ( "encoding/binary" "errors" "fmt" "net" "sync" "unsafe" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/socket" "golang.org/x/sys/windows" ) // Windows API constants const ( iphelperTCPTableOwnerPIDAll uintptr = 5 iphelperUDPTableOwnerPID uintptr = 1 iphelperTCPStateListen uint32 = 2 winErrInsufficientBuffer = uintptr(windows.ERROR_INSUFFICIENT_BUFFER) winErrInvalidParameter = uintptr(windows.ERROR_INVALID_PARAMETER) ) type iphelperTCPTable struct { // docs: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366921(v=vs.85).aspx numEntries uint32 table [maxStateTableEntries]iphelperTCPRow } type iphelperTCPRow struct { // docs: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366913(v=vs.85).aspx state uint32 localAddr uint32 localPort uint32 remoteAddr uint32 remotePort uint32 owningPid uint32 } type iphelperTCP6Table struct { // docs: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366905(v=vs.85).aspx numEntries uint32 table [maxStateTableEntries]iphelperTCP6Row } type iphelperTCP6Row struct { // docs: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366896(v=vs.85).aspx localAddr [16]byte _ uint32 // localScopeID localPort uint32 remoteAddr [16]byte _ uint32 // remoteScopeID remotePort uint32 state uint32 owningPid uint32 } type iphelperUDPTable struct { // docs: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366932(v=vs.85).aspx numEntries uint32 table [maxStateTableEntries]iphelperUDPRow } type iphelperUDPRow struct { // docs: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366928(v=vs.85).aspx localAddr uint32 localPort uint32 owningPid uint32 } type iphelperUDP6Table struct { // docs: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366925(v=vs.85).aspx numEntries uint32 table [maxStateTableEntries]iphelperUDP6Row } type iphelperUDP6Row struct { // docs: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366923(v=vs.85).aspx localAddr [16]byte _ uint32 // localScopeID localPort uint32 owningPid uint32 } // IP and Protocol constants const ( IPv4 uint8 = 4 IPv6 uint8 = 6 TCP uint8 = 6 UDP uint8 = 17 ) type learningBufSize struct { sync.Mutex size int usesLeft int useFor int start int max int } func newLearningBufSize(start, max, ttl int) *learningBufSize { return &learningBufSize{ size: start, usesLeft: ttl, useFor: ttl, start: start, max: max, } } const ( startBufSize = 1024 // bufSizeUsageTTL defines how often a buffer size is used before it is // shrunk again. bufSizeUsageTTL = 100 // maxBufSize is the maximum size we will allocate for responses. This was // previously set at 65k, which was too little for some production cases. maxBufSize = 1048576 // 2^20B, 1MB // maxStateTableEntries is the maximum supported amount of entries of the // state tables. // This is never allocated, but just casted to from an unsafe pointer. maxStateTableEntries = 65535 ) var ( tcp4BufSize = newLearningBufSize(startBufSize, maxBufSize, bufSizeUsageTTL) udp4BufSize = newLearningBufSize(startBufSize, maxBufSize, bufSizeUsageTTL) tcp6BufSize = newLearningBufSize(startBufSize, maxBufSize, bufSizeUsageTTL) udp6BufSize = newLearningBufSize(startBufSize, maxBufSize, bufSizeUsageTTL) ) func (lbf *learningBufSize) getBufSize() int { lbf.Lock() defer lbf.Unlock() // using bufSize lbf.usesLeft-- // check if we want to reset if lbf.usesLeft <= 0 { // decrease lbf.size /= 2 // not too little if lbf.size < lbf.start { lbf.size = lbf.start } // reset TTL counter lbf.usesLeft = lbf.useFor } return lbf.size } func (lbf *learningBufSize) increaseBufSize(minSize int) int { lbf.Lock() defer lbf.Unlock() // increase lbf.size *= 2 // increase until we reach the minimum size for lbf.size < minSize { lbf.size *= 2 } // not too much if lbf.size > lbf.max { lbf.size = lbf.max } // reset TTL counter lbf.usesLeft = lbf.useFor // return new bufSize return lbf.size } // getTable returns the current connection state table of Windows of the given protocol and IP version. func (ipHelper *IPHelper) getTable(ipVersion, protocol uint8) (connections []*socket.ConnectionInfo, binds []*socket.BindInfo, err error) { //nolint:gocognit,gocycle // TODO // docs: https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getextendedtcptable if !ipHelper.valid.IsSet() { return nil, nil, errInvalid } var afClass int var lbf *learningBufSize switch ipVersion { case IPv4: afClass = windows.AF_INET if protocol == TCP { lbf = tcp4BufSize } else { lbf = udp4BufSize } case IPv6: afClass = windows.AF_INET6 if protocol == TCP { lbf = tcp6BufSize } else { lbf = udp6BufSize } default: return nil, nil, errors.New("invalid protocol") } // try max 5 times maxTries := 5 usedBufSize := lbf.getBufSize() var buf []byte triesLoop: for i := 1; i <= maxTries; i++ { bufSizeParam := usedBufSize buf = make([]byte, bufSizeParam) var r1 uintptr switch protocol { case TCP: r1, _, err = ipHelper.getExtendedTCPTable.Call( uintptr(unsafe.Pointer(&buf[0])), // _Out_ PVOID pTcpTable uintptr(unsafe.Pointer(&bufSizeParam)), // _Inout_ PDWORD pdwSize 0, // _In_ BOOL bOrder uintptr(afClass), // _In_ ULONG ulAf iphelperTCPTableOwnerPIDAll, // _In_ TCP_TABLE_CLASS TableClass 0, // _In_ ULONG Reserved ) case UDP: r1, _, err = ipHelper.getExtendedUDPTable.Call( uintptr(unsafe.Pointer(&buf[0])), // _Out_ PVOID pUdpTable, uintptr(unsafe.Pointer(&bufSizeParam)), // _Inout_ PDWORD pdwSize, 0, // _In_ BOOL bOrder, uintptr(afClass), // _In_ ULONG ulAf, iphelperUDPTableOwnerPID, // _In_ UDP_TABLE_CLASS TableClass, 0, // _In_ ULONG Reserved ) } switch r1 { case winErrInsufficientBuffer: if i >= maxTries { return nil, nil, fmt.Errorf( "insufficient buffer error (tried %d times): provided %d bytes; required %d bytes - [NT 0x%X] %s", i, usedBufSize, bufSizeParam, r1, err, ) } // bufSizeParam was modified by ipHelper.getExtended*Table to hold the // required buffer size. usedBufSize = lbf.increaseBufSize(bufSizeParam) case winErrInvalidParameter: return nil, nil, fmt.Errorf("invalid parameter: [NT 0x%X] %s", r1, err) case windows.NO_ERROR: // success break triesLoop default: return nil, nil, fmt.Errorf("unexpected error: [NT 0x%X] %s", r1, err) } } // parse output switch { case protocol == TCP && ipVersion == IPv4: tcpTable := (*iphelperTCPTable)(unsafe.Pointer(&buf[0])) // Check if we got more entries than supported. tableEntries := tcpTable.numEntries if tableEntries > maxStateTableEntries { tableEntries = maxStateTableEntries log.Warningf("network/iphelper: received TCPv4 table with more entries than supported: %d/%d", tcpTable.numEntries, maxStateTableEntries) } // Cap table to actual entries. table := tcpTable.table[:tableEntries] for _, row := range table { if row.state == iphelperTCPStateListen { binds = append(binds, &socket.BindInfo{ Local: socket.Address{ IP: convertIPv4(row.localAddr), Port: convertPort(row.localPort), }, PID: int(row.owningPid), }) } else { connections = append(connections, &socket.ConnectionInfo{ Local: socket.Address{ IP: convertIPv4(row.localAddr), Port: convertPort(row.localPort), }, Remote: socket.Address{ IP: convertIPv4(row.remoteAddr), Port: convertPort(row.remotePort), }, PID: int(row.owningPid), }) } } case protocol == TCP && ipVersion == IPv6: tcpTable := (*iphelperTCP6Table)(unsafe.Pointer(&buf[0])) // Check if we got more entries than supported. tableEntries := tcpTable.numEntries if tableEntries > maxStateTableEntries { tableEntries = maxStateTableEntries log.Warningf("network/iphelper: received TCPv6 table with more entries than supported: %d/%d", tcpTable.numEntries, maxStateTableEntries) } // Cap table to actual entries. table := tcpTable.table[:tableEntries] for _, row := range table { if row.state == iphelperTCPStateListen { binds = append(binds, &socket.BindInfo{ Local: socket.Address{ IP: net.IP(row.localAddr[:]), Port: convertPort(row.localPort), }, PID: int(row.owningPid), }) } else { connections = append(connections, &socket.ConnectionInfo{ Local: socket.Address{ IP: net.IP(row.localAddr[:]), Port: convertPort(row.localPort), }, Remote: socket.Address{ IP: net.IP(row.remoteAddr[:]), Port: convertPort(row.remotePort), }, PID: int(row.owningPid), }) } } case protocol == UDP && ipVersion == IPv4: udpTable := (*iphelperUDPTable)(unsafe.Pointer(&buf[0])) // Check if we got more entries than supported. tableEntries := udpTable.numEntries if tableEntries > maxStateTableEntries { tableEntries = maxStateTableEntries log.Warningf("network/iphelper: received UDPv4 table with more entries than supported: %d/%d", udpTable.numEntries, maxStateTableEntries) } // Cap table to actual entries. table := udpTable.table[:tableEntries] for _, row := range table { binds = append(binds, &socket.BindInfo{ Local: socket.Address{ IP: convertIPv4(row.localAddr), Port: convertPort(row.localPort), }, PID: int(row.owningPid), }) } case protocol == UDP && ipVersion == IPv6: udpTable := (*iphelperUDP6Table)(unsafe.Pointer(&buf[0])) // Check if we got more entries than supported. tableEntries := udpTable.numEntries if tableEntries > maxStateTableEntries { tableEntries = maxStateTableEntries log.Warningf("network/iphelper: received UDPv6 table with more entries than supported: %d/%d", udpTable.numEntries, maxStateTableEntries) } // Cap table to actual entries. table := udpTable.table[:tableEntries] for _, row := range table { binds = append(binds, &socket.BindInfo{ Local: socket.Address{ IP: net.IP(row.localAddr[:]), Port: convertPort(row.localPort), }, PID: int(row.owningPid), }) } } return connections, binds, nil } // convertIPv4 as needed for iphlpapi.dll func convertIPv4(input uint32) net.IP { addressBuf := make([]byte, 4) binary.LittleEndian.PutUint32(addressBuf, input) return net.IP(addressBuf) } // convertPort converts ports received from iphlpapi.dll func convertPort(input uint32) uint16 { return uint16(input>>8 | input<<8) } ================================================ FILE: service/network/iphelper/tables_test.go ================================================ //go:build windows package iphelper import ( "fmt" "testing" ) func TestSockets(t *testing.T) { connections, listeners, err := GetTCP4Table() if err != nil { t.Fatal(err) } fmt.Println("\nTCP 4 connections:") for _, connection := range connections { fmt.Printf("%+v\n", connection) } fmt.Println("\nTCP 4 listeners:") for _, listener := range listeners { fmt.Printf("%+v\n", listener) } connections, listeners, err = GetTCP6Table() if err != nil { t.Fatal(err) } fmt.Println("\nTCP 6 connections:") for _, connection := range connections { fmt.Printf("%+v\n", connection) } fmt.Println("\nTCP 6 listeners:") for _, listener := range listeners { fmt.Printf("%+v\n", listener) } binds, err := GetUDP4Table() if err != nil { t.Fatal(err) } fmt.Println("\nUDP 4 binds:") for _, bind := range binds { fmt.Printf("%+v\n", bind) } binds, err = GetUDP6Table() if err != nil { t.Fatal(err) } fmt.Println("\nUDP 6 binds:") for _, bind := range binds { fmt.Printf("%+v\n", bind) } } ================================================ FILE: service/network/metrics.go ================================================ package network import ( "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/metrics" "github.com/safing/portmaster/service/process" ) var ( packetHandlingHistogram *metrics.Histogram blockedOutConnCounter *metrics.Counter encryptedAndTunneledOutConnCounter *metrics.Counter encryptedOutConnCounter *metrics.Counter tunneledOutConnCounter *metrics.Counter outConnCounter *metrics.Counter ) func registerMetrics() (err error) { // This needed to be moved here, because every packet is now handled by the // connection handler worker. packetHandlingHistogram, err = metrics.NewHistogram( "firewall/handling/duration/seconds", nil, &metrics.Options{ Permission: api.PermitUser, ExpertiseLevel: config.ExpertiseLevelExpert, }) if err != nil { return err } _, err = metrics.NewGauge( "network/connections/active/total", nil, func() float64 { return float64(conns.active()) }, &metrics.Options{ InternalID: "active_connections", Permission: api.PermitUser, ExpertiseLevel: config.ExpertiseLevelUser, }) if err != nil { return err } connCounterID := "network/connections/total" connCounterOpts := &metrics.Options{ Name: "Connections", Permission: api.PermitUser, ExpertiseLevel: config.ExpertiseLevelUser, Persist: true, } blockedOutConnCounter, err = metrics.NewCounter( connCounterID, map[string]string{ "direction": "out", "blocked": "true", }, &metrics.Options{ Name: "Connections", InternalID: "blocked_outgoing_connections", Permission: api.PermitUser, ExpertiseLevel: config.ExpertiseLevelUser, Persist: true, }, ) if err != nil { return err } encryptedAndTunneledOutConnCounter, err = metrics.NewCounter( connCounterID, map[string]string{ "direction": "out", "encrypted": "true", "tunneled": "true", }, connCounterOpts, ) if err != nil { return err } encryptedOutConnCounter, err = metrics.NewCounter( connCounterID, map[string]string{ "direction": "out", "encrypted": "true", }, connCounterOpts, ) if err != nil { return err } tunneledOutConnCounter, err = metrics.NewCounter( connCounterID, map[string]string{ "direction": "out", "tunneled": "true", }, connCounterOpts, ) if err != nil { return err } outConnCounter, err = metrics.NewCounter( connCounterID, map[string]string{ "direction": "out", }, connCounterOpts, ) if err != nil { return err } return nil } func (conn *Connection) addToMetrics() { if conn.addedToMetrics { return } // Don't count requests serviced to the network, // as we have an incomplete view here. if conn.Process() != nil && conn.Process().Pid == process.NetworkHostProcessID { return } // Only count outgoing connections for now. if conn.Inbound { return } // Check the verdict. switch conn.Verdict { //nolint:exhaustive // Not critical. case VerdictBlock, VerdictDrop: blockedOutConnCounter.Inc() conn.addedToMetrics = true return case VerdictAccept, VerdictRerouteToTunnel: // Continue to next section. default: // Connection is not counted. return } // Only count successful connections, not DNS requests. if conn.Type == DNSRequest { return } // Select counter based on attributes. switch { case conn.Encrypted && conn.Tunneled: encryptedAndTunneledOutConnCounter.Inc() case conn.Encrypted: encryptedOutConnCounter.Inc() case conn.Tunneled: tunneledOutConnCounter.Inc() default: outConnCounter.Inc() } conn.addedToMetrics = true } ================================================ FILE: service/network/module.go ================================================ package network import ( "context" "errors" "fmt" "strings" "sync" "sync/atomic" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/firewall/interception/dnsmonitor" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/state" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/resolver" ) // Events. const ( ConnectionReattributedEvent = "connection re-attributed" ) type Network struct { mgr *mgr.Manager instance instance dnsRequestTicker *mgr.SleepyTicker connectionCleanerTicker *mgr.SleepyTicker EventConnectionReattributed *mgr.EventMgr[string] } func (n *Network) Manager() *mgr.Manager { return n.mgr } func (n *Network) Start() error { return start() } func (n *Network) Stop() error { return nil } func (n *Network) SetSleep(enabled bool) { if n.dnsRequestTicker != nil { n.dnsRequestTicker.SetSleep(enabled) } if n.connectionCleanerTicker != nil { n.connectionCleanerTicker.SetSleep(enabled) } } var defaultFirewallHandler FirewallHandler // SetDefaultFirewallHandler sets the default firewall handler. func SetDefaultFirewallHandler(handler FirewallHandler) { if defaultFirewallHandler == nil { defaultFirewallHandler = handler } } func prep() error { if netenv.IPv6Enabled() { state.EnableTCPDualStack() state.EnableUDPDualStack() } return registerAPIEndpoints() } func start() error { err := registerAsDatabase() if err != nil { return err } if err := registerMetrics(); err != nil { return err } module.mgr.Go("clean connections", connectionCleaner) module.mgr.Go("write open dns requests", openDNSRequestWriter) module.instance.Profile().EventDelete.AddCallback("re-attribute connections from deleted profile", reAttributeConnections) return nil } var reAttributionLock sync.Mutex // reAttributeConnections finds all connections of a deleted profile and re-attributes them. // Expected event data: scoped profile ID. func reAttributeConnections(_ *mgr.WorkerCtx, profileID string) (bool, error) { profileSource, profileID, ok := strings.Cut(profileID, "/") if !ok { return false, fmt.Errorf("event data does not seem to be a scoped profile ID: %v", profileID) } // Hold a lock for re-attribution, to prevent simultaneous processing of the // same connections and make logging cleaner. reAttributionLock.Lock() defer reAttributionLock.Unlock() // Create tracing context. ctx, tracer := log.AddTracer(context.Background()) defer tracer.Submit() tracer.Infof("network: re-attributing connections from deleted profile %s/%s", profileSource, profileID) // Count and log how many connections were re-attributed. var reAttributed int // Re-attribute connections. for _, conn := range conns.clone() { if reAttributeConnection(ctx, conn, profileID, profileSource) { reAttributed++ tracer.Debugf("filter: re-attributed %s to %s", conn, conn.process.PrimaryProfileID) } } // Re-attribute dns connections. for _, conn := range dnsConns.clone() { if reAttributeConnection(ctx, conn, profileID, profileSource) { reAttributed++ tracer.Debugf("filter: re-attributed %s to %s", conn, conn.process.PrimaryProfileID) } } tracer.Infof("filter: re-attributed %d connections", reAttributed) return false, nil } func reAttributeConnection(ctx context.Context, conn *Connection, profileID, profileSource string) (reAttributed bool) { // Lock the connection before checking anything to avoid a race condition with connection data collection. conn.Lock() defer conn.Unlock() // Check if the connection has the profile we are looking for. switch { case !conn.DataIsComplete(): return false case conn.ProcessContext.Profile != profileID: return false case conn.ProcessContext.Source != profileSource: return false } // Attempt to assign new profile. err := conn.process.RefetchProfile(ctx) if err != nil { log.Tracer(ctx).Warningf("network: failed to refetch profile for %s: %s", conn, err) return false } // Set the new process context. conn.ProcessContext = getProcessContext(ctx, conn.process) conn.Save() // Trigger event for re-attribution. module.EventConnectionReattributed.Submit(conn.ID) log.Tracer(ctx).Debugf("filter: re-attributed %s to %s", conn, conn.process.PrimaryProfileID) return true } var ( module *Network shimLoaded atomic.Bool ) // New returns a new Network module. func New(instance instance) (*Network, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Network") module = &Network{ mgr: m, instance: instance, EventConnectionReattributed: mgr.NewEventMgr[string](ConnectionReattributedEvent, m), } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface { Profile() *profile.ProfileModule Resolver() *resolver.ResolverModule DNSMonitor() *dnsmonitor.DNSMonitor } ================================================ FILE: service/network/multicast.go ================================================ package network import ( "net" "github.com/safing/portmaster/service/network/netutils" ) // GetMulticastRequestConn searches for and returns the requesting connnection // of a possible multicast/broadcast response. func GetMulticastRequestConn(responseConn *Connection, responseFromNet *net.IPNet) *Connection { // Calculate the broadcast address the query would have gone to. responseNetBroadcastIP := netutils.GetBroadcastAddress(responseFromNet.IP, responseFromNet.Mask) // Find requesting multicast/broadcast connection. for _, conn := range conns.clone() { switch { case !conn.DataIsComplete(): // Ignore connection with incomplete data. case conn.Inbound: // Ignore incoming connections. case conn.Ended != 0: // Ignore ended connections. case conn.Entity.Protocol != responseConn.Entity.Protocol: // Ignore on protocol mismatch. case conn.LocalPort != responseConn.LocalPort: // Ignore on local port mismatch. case !conn.LocalIP.Equal(responseConn.LocalIP): // Ignore on local IP mismatch. case !conn.Process().Equal(responseConn.Process()): // Ignore if processes mismatch. case conn.Entity.IPScope == netutils.LocalMulticast && (responseConn.Entity.IPScope == netutils.LinkLocal || responseConn.Entity.IPScope == netutils.SiteLocal): // We found a (possibly routed) multicast request that matches the response! return conn case conn.Entity.IP.Equal(responseNetBroadcastIP) && responseFromNet.Contains(conn.LocalIP): // We found a (link local) broadcast request that matches the response! return conn } } return nil } ================================================ FILE: service/network/netutils/address.go ================================================ package netutils import ( "errors" "net" "strconv" "github.com/safing/portmaster/service/network/packet" ) var errInvalidIP = errors.New("invalid IP address") // IPPortFromAddr extracts or parses the IP address and port contained in the given address. func IPPortFromAddr(addr net.Addr) (ip net.IP, port uint16, err error) { // Convert addr to IP if needed. switch v := addr.(type) { case *net.TCPAddr: return v.IP, uint16(v.Port), nil case *net.UDPAddr: return v.IP, uint16(v.Port), nil case *net.IPAddr: return v.IP, 0, nil case *net.UnixAddr: return nil, 0, errors.New("unix addresses don't have IPs") default: return ParseIPPort(addr.String()) } } // ProtocolFromNetwork returns the protocol from the given net, as used in the "net" golang stdlib. func ProtocolFromNetwork(net string) (protocol packet.IPProtocol) { switch net { case "tcp", "tcp4", "tcp6": return packet.TCP case "udp", "udp4", "udp6": return packet.UDP default: return 0 } } // ParseIPPort parses a :port formatted address. func ParseIPPort(address string) (net.IP, uint16, error) { ipString, portString, err := net.SplitHostPort(address) if err != nil { return nil, 0, err } ip := net.ParseIP(ipString) if ip == nil { return nil, 0, errInvalidIP } port, err := strconv.ParseUint(portString, 10, 16) if err != nil { return nil, 0, err } return ip, uint16(port), nil } ================================================ FILE: service/network/netutils/dns.go ================================================ package netutils import ( "fmt" "net" "regexp" "strings" "github.com/miekg/dns" ) var ( cleanDomainRegex = regexp.MustCompile( `^` + // match beginning `(` + // start subdomain group `(xn--)?` + // idn prefix `[a-z0-9_-]{1,63}` + // main chunk `\.` + // ending with a dot `)*` + // end subdomain group, allow any number of subdomains `(xn--)?` + // TLD idn prefix `[a-z0-9_-]{1,63}` + // TLD main chunk with at least one character (for custom ones) `\.` + // ending with a dot `$`, // match end ) // dnsSDDomainRegex is a lot more lax to better suit the allowed characters in DNS-SD. // Not all characters have been allowed - some special characters were // removed to reduce the general attack surface. dnsSDDomainRegex = regexp.MustCompile( // Start of charset selection. `^[` + // Printable ASCII (character code 32-127), excluding some special characters. ` !#$%&()*+,\-\./0-9:;=?@A-Z[\\\]^_\a-z{|}~` + // Only latin characters from extended ASCII (character code 128-255). `ŠŒŽšœžŸ¡¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ` + // End of charset selection. `]*$`, ) ) // IsValidFqdn returns whether the given string is a valid fqdn. func IsValidFqdn(fqdn string) bool { // root zone if fqdn == "." { return true } // check max length if len(fqdn) > 256 { return false } // IsFqdn checks if a domain name is fully qualified. if !dns.IsFqdn(fqdn) { return false } // Use special check for .local domains to support DNS-SD. if strings.HasSuffix(fqdn, ".local.") { return dnsSDDomainRegex.MatchString(fqdn) } // check with regex if !cleanDomainRegex.MatchString(fqdn) { return false } // IsDomainName checks if s is a valid domain name, it returns the number of // labels and true, when a domain name is valid. Note that non fully qualified // domain name is considered valid, in this case the last label is counted in // the number of labels. When false is returned the number of labels is not // defined. Also note that this function is extremely liberal; almost any // string is a valid domain name as the DNS is 8 bit protocol. It checks if each // label fits in 63 characters and that the entire name will fit into the 255 // octet wire format limit. _, ok := dns.IsDomainName(fqdn) return ok } // IPsToRRs transforms the given IPs to resource records. func IPsToRRs(domain string, ips []net.IP) ([]dns.RR, error) { records := make([]dns.RR, 0, len(ips)) var rr dns.RR var err error for _, ip := range ips { if ip.To4() != nil { rr, err = dns.NewRR(fmt.Sprintf("%s 17 IN A %s", domain, ip)) } else { rr, err = dns.NewRR(fmt.Sprintf("%s 17 IN AAAA %s", domain, ip)) } if err != nil { return nil, fmt.Errorf("failed to create record for %s: %w", ip, err) } records = append(records, rr) } return records, nil } ================================================ FILE: service/network/netutils/dns_test.go ================================================ package netutils import "testing" func testDomainValidity(t *testing.T, domain string, isValid bool) { t.Helper() if IsValidFqdn(domain) != isValid { t.Errorf("domain %s failed check: was valid=%v, expected valid=%v", domain, IsValidFqdn(domain), isValid) } } func TestDNSValidation(t *testing.T) { t.Parallel() // valid testDomainValidity(t, ".", true) testDomainValidity(t, "at.", true) testDomainValidity(t, "orf.at.", true) testDomainValidity(t, "www.orf.at.", true) testDomainValidity(t, "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.x.y.z.example.org.", true) testDomainValidity(t, "a_a.com.", true) testDomainValidity(t, "a-a.com.", true) testDomainValidity(t, "a_a.com.", true) testDomainValidity(t, "a-a.com.", true) testDomainValidity(t, "xn--a.com.", true) testDomainValidity(t, "xn--asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasd.com.", true) // maybe valid testDomainValidity(t, "-.com.", true) testDomainValidity(t, "_.com.", true) testDomainValidity(t, "a_.com.", true) testDomainValidity(t, "a-.com.", true) testDomainValidity(t, "_a.com.", true) testDomainValidity(t, "-a.com.", true) // invalid testDomainValidity(t, ".com.", false) testDomainValidity(t, ".com.", false) testDomainValidity(t, "xn--asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf.com.", false) testDomainValidity(t, "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf.com.", false) testDomainValidity(t, "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf.com.", false) testDomainValidity(t, "asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.as.com.", false) // real world examples testDomainValidity(t, "iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com.", true) } ================================================ FILE: service/network/netutils/ip.go ================================================ package netutils import "net" // IPScope is the scope of the IP address. type IPScope int8 // Defined IP Scopes. const ( Invalid IPScope = iota - 1 Undefined HostLocal LinkLocal SiteLocal Global LocalMulticast GlobalMulticast ) // ClassifyIP returns the network scope of the given IP address. // Deprecated: Please use the new GetIPScope instead. func ClassifyIP(ip net.IP) IPScope { return GetIPScope(ip) } // GetIPScope returns the network scope of the given IP address. func GetIPScope(ip net.IP) IPScope { //nolint:gocognit if ip4 := ip.To4(); ip4 != nil { // IPv4 switch { case ip4[0] == 0 && ip4[1] == 0 && ip4[2] == 0 && ip4[3] == 0: // 0.0.0.0/32 return LocalMulticast // Used as source for L2 based protocols with no L3 addressing. case ip4[0] == 0: // 0.0.0.0/8 return Invalid case ip4[0] == 10: // 10.0.0.0/8 (RFC1918) return SiteLocal case ip4[0] == 100 && ip4[1]&0b11000000 == 64: // 100.64.0.0/10 (RFC6598) return SiteLocal case ip4[0] == 127: // 127.0.0.0/8 (RFC1918) return HostLocal case ip4[0] == 169 && ip4[1] == 254: // 169.254.0.0/16 (RFC3927) return LinkLocal case ip4[0] == 172 && ip4[1]&0b11110000 == 16: // 172.16.0.0/12 (RFC1918) return SiteLocal case ip4[0] == 192 && ip4[1] == 0 && ip4[2] == 2: // 192.0.2.0/24 (TEST-NET-1, RFC5737) return Invalid case ip4[0] == 192 && ip4[1] == 168: // 192.168.0.0/16 (RFC1918) return SiteLocal case ip4[0] == 198 && ip4[1] == 51 && ip4[2] == 100: // 198.51.100.0/24 (TEST-NET-2, RFC5737) return Invalid case ip4[0] == 203 && ip4[1] == 0 && ip4[2] == 113: // 203.0.113.0/24 (TEST-NET-3, RFC5737) return Invalid case ip4[0] == 224: // 224.0.0.0/8 (RFC5771) return LocalMulticast case ip4[0] == 233 && ip4[1] == 252 && ip4[2] == 0: // 233.252.0.0/24 (MCAST-TEST-NET; RFC5771, RFC6676) return Invalid case ip4[0] >= 225 && ip4[0] <= 238: // 225.0.0.0/8 - 238.0.0.0/8 (RFC5771) return GlobalMulticast case ip4[0] == 239: // 239.0.0.0/8 (RFC2365) return LocalMulticast case ip4[0] == 255 && ip4[1] == 255 && ip4[2] == 255 && ip4[3] == 255: // 255.255.255.255/32 return LocalMulticast case ip4[0] >= 240: // 240.0.0.0/8 - 255.0.0.0/8 (minus 255.255.255.255/32) return Invalid default: return Global } } else if len(ip) == net.IPv6len { // IPv6 // TODO: Add IPv6 RFC5771 test / doc networks // 2001:db8::/32 // 3fff::/20 switch { case ip.Equal(net.IPv6zero): return Invalid case ip.Equal(net.IPv6loopback): return HostLocal case ip[0]&0xfe == 0xfc: // fc00::/7 return SiteLocal case ip[0] == 0xfe && ip[1]&0xc0 == 0x80: // fe80::/10 return LinkLocal case ip[0] == 0xff && ip[1] <= 0x05: // ff00::/16 - ff05::/16 return LocalMulticast case ip[0] == 0xff: // other ff00::/8 return GlobalMulticast default: return Global } } return Invalid } // IsLocalhost returns whether the IP refers to the host itself. func (scope IPScope) IsLocalhost() bool { return scope == HostLocal } // IsLAN returns true if the scope is site-local or link-local. func (scope IPScope) IsLAN() bool { switch scope { //nolint:exhaustive // Looking for something specific. case SiteLocal, LinkLocal, LocalMulticast: return true default: return false } } // IsGlobal returns true if the scope is global. func (scope IPScope) IsGlobal() bool { switch scope { //nolint:exhaustive // Looking for something specific. case Global, GlobalMulticast: return true default: return false } } // GetBroadcastAddress returns the broadcast address of the given IP and network mask. // If a mixed IPv4/IPv6 input is given, it returns nil. func GetBroadcastAddress(ip net.IP, netMask net.IPMask) net.IP { // Convert to standard v4. if ip4 := ip.To4(); ip4 != nil { ip = ip4 } mask := net.IP(netMask) if ip4Mask := mask.To4(); ip4Mask != nil { mask = ip4Mask } // Check for mixed v4/v6 input. if len(ip) != len(mask) { return nil } // Merge to broadcast address n := len(ip) broadcastAddress := make(net.IP, n) for i := range n { broadcastAddress[i] = ip[i] | ^mask[i] } return broadcastAddress } ================================================ FILE: service/network/netutils/ip_test.go ================================================ package netutils import ( "net" "testing" ) func TestIPScope(t *testing.T) { t.Parallel() testScope(t, net.IPv4(71, 87, 113, 211), Global) testScope(t, net.IPv4(127, 0, 0, 1), HostLocal) testScope(t, net.IPv4(127, 255, 255, 1), HostLocal) testScope(t, net.IPv4(192, 168, 172, 24), SiteLocal) testScope(t, net.IPv4(172, 15, 1, 1), Global) testScope(t, net.IPv4(172, 16, 1, 1), SiteLocal) testScope(t, net.IPv4(172, 31, 1, 1), SiteLocal) testScope(t, net.IPv4(172, 32, 1, 1), Global) } func testScope(t *testing.T, ip net.IP, expectedScope IPScope) { t.Helper() c := GetIPScope(ip) if c != expectedScope { t.Errorf("%s is %s, expected %s", ip, scopeName(c), scopeName(expectedScope)) } } func scopeName(c IPScope) string { switch c { case Invalid: return "invalid" case Undefined: return "undefined" case HostLocal: return "hostLocal" case LinkLocal: return "linkLocal" case SiteLocal: return "siteLocal" case Global: return "global" case LocalMulticast: return "localMulticast" case GlobalMulticast: return "globalMulticast" default: return "undefined" } } ================================================ FILE: service/network/netutils/tcpassembly.go ================================================ package netutils import ( "sync" "github.com/google/gopacket" "github.com/google/gopacket/tcpassembly" ) // SimpleStreamAssemblerManager is a simple manager for github.com/google/gopacket/tcpassembly. type SimpleStreamAssemblerManager struct { InitLock sync.Mutex lastAssembler *SimpleStreamAssembler } // New returns a new stream assembler. func (m *SimpleStreamAssemblerManager) New(net, transport gopacket.Flow) tcpassembly.Stream { assembler := new(SimpleStreamAssembler) m.lastAssembler = assembler return assembler } // GetLastAssembler returns the newest created stream assembler. func (m *SimpleStreamAssemblerManager) GetLastAssembler() *SimpleStreamAssembler { return m.lastAssembler } // SimpleStreamAssembler is a simple assembler for github.com/google/gopacket/tcpassembly. type SimpleStreamAssembler struct { Cumulated []byte CumulatedLen int Complete bool } // NewSimpleStreamAssembler returns a new SimpleStreamAssembler. func NewSimpleStreamAssembler() *SimpleStreamAssembler { return &SimpleStreamAssembler{} } // Reassembled implements tcpassembly.Stream's Reassembled function. func (a *SimpleStreamAssembler) Reassembled(reassembly []tcpassembly.Reassembly) { for _, entry := range reassembly { a.Cumulated = append(a.Cumulated, entry.Bytes...) } a.CumulatedLen = len(a.Cumulated) } // ReassemblyComplete implements tcpassembly.Stream's ReassemblyComplete function. func (a *SimpleStreamAssembler) ReassemblyComplete() { a.Complete = true } ================================================ FILE: service/network/packet/bandwidth.go ================================================ package packet import "fmt" // BandwidthUpdate holds an update to the seen bandwidth of a connection. type BandwidthUpdate struct { ConnID string BytesReceived uint64 BytesSent uint64 Method BandwidthUpdateMethod } // BandwidthUpdateMethod defines how the bandwidth data of a bandwidth update should be interpreted. type BandwidthUpdateMethod uint8 // Bandwidth Update Methods. const ( Absolute BandwidthUpdateMethod = iota Additive ) func (bu *BandwidthUpdate) String() string { return fmt.Sprintf("%s: %dB recv | %dB sent [%s]", bu.ConnID, bu.BytesReceived, bu.BytesSent, bu.Method) } func (bum BandwidthUpdateMethod) String() string { switch bum { case Absolute: return "absolute" case Additive: return "additive" default: return "unknown" } } ================================================ FILE: service/network/packet/const.go ================================================ package packet import ( "errors" "fmt" ) // Basic Types. type ( // IPVersion represents an IP version. IPVersion uint8 // IPProtocol represents an IP protocol. IPProtocol uint8 // Verdict describes the decision on a packet. Verdict uint8 ) // Basic Constants. const ( IPv4 = IPVersion(4) IPv6 = IPVersion(6) InBound = true OutBound = false ICMP = IPProtocol(1) IGMP = IPProtocol(2) TCP = IPProtocol(6) UDP = IPProtocol(17) ICMPv6 = IPProtocol(58) UDPLite = IPProtocol(136) RAW = IPProtocol(255) AnyHostInternalProtocol61 = IPProtocol(61) ) // Verdicts. const ( DROP Verdict = iota BLOCK ACCEPT STOLEN QUEUE REPEAT STOP ) // ErrFailedToLoadPayload is returned by GetPayload if it failed for an unspecified reason, or is not implemented on the current system. var ErrFailedToLoadPayload = errors.New("could not load packet payload") // ByteSize returns the byte size of the ip (IPv4 = 4 bytes, IPv6 = 16). func (v IPVersion) ByteSize() int { switch v { case IPv4: return 4 case IPv6: return 16 } return 0 } // String returns the string representation of the IP version: "IPv4" or "IPv6". func (v IPVersion) String() string { switch v { case IPv4: return "IPv4" case IPv6: return "IPv6" } return fmt.Sprintf("", uint8(v)) } // String returns the string representation (abbreviation) of the protocol. func (p IPProtocol) String() string { switch p { case RAW: return "RAW" case TCP: return "TCP" case UDP: return "UDP" case UDPLite: return "UDPLite" case ICMP: return "ICMP" case ICMPv6: return "ICMPv6" case IGMP: return "IGMP" case AnyHostInternalProtocol61: fallthrough default: return fmt.Sprintf("", uint8(p)) } } // String returns the string representation of the verdict. func (v Verdict) String() string { switch v { case DROP: return "DROP" case BLOCK: return "BLOCK" case ACCEPT: return "ACCEPT" case STOLEN: return "STOLEN" case QUEUE: return "QUEUE" case REPEAT: return "REPEAT" case STOP: return "STOP" default: return fmt.Sprintf("", uint8(v)) } } ================================================ FILE: service/network/packet/info_only.go ================================================ package packet import ( "errors" "fmt" ) // InfoPacket does not represent an actual packet, but only holds metadata. // Implements the packet.Packet interface. type InfoPacket struct { Base } // NewInfoPacket returns a new InfoPacket with the given info. func NewInfoPacket(info Info) *InfoPacket { return &InfoPacket{ Base{ info: info, }, } } // InfoOnly returns whether the packet is informational only and does not // represent an actual packet. func (pkt *InfoPacket) InfoOnly() bool { return true } // LoadPacketData does nothing on Linux, as data is always fully parsed. func (pkt *InfoPacket) LoadPacketData() error { return fmt.Errorf("%w: info-only packet", ErrFailedToLoadPayload) } // ErrInfoOnlyPacket is returned for unsupported operations on an info-only packet. var ErrInfoOnlyPacket = errors.New("info-only packet") // Accept does nothing on an info-only packet. func (pkt *InfoPacket) Accept() error { return ErrInfoOnlyPacket } // Block does nothing on an info-only packet. func (pkt *InfoPacket) Block() error { return ErrInfoOnlyPacket } // Drop does nothing on an info-only packet. func (pkt *InfoPacket) Drop() error { return ErrInfoOnlyPacket } // PermanentAccept does nothing on an info-only packet. func (pkt *InfoPacket) PermanentAccept() error { return ErrInfoOnlyPacket } // PermanentBlock does nothing on an info-only packet. func (pkt *InfoPacket) PermanentBlock() error { return ErrInfoOnlyPacket } // PermanentDrop does nothing on an info-only packet. func (pkt *InfoPacket) PermanentDrop() error { return ErrInfoOnlyPacket } // RerouteToNameserver does nothing on an info-only packet. func (pkt *InfoPacket) RerouteToNameserver() error { return ErrInfoOnlyPacket } // RerouteToTunnel does nothing on an info-only packet. func (pkt *InfoPacket) RerouteToTunnel() error { return ErrInfoOnlyPacket } var _ Packet = &InfoPacket{} ================================================ FILE: service/network/packet/packet.go ================================================ package packet import ( "context" "fmt" "net" "strconv" "github.com/google/gopacket" ) // Base is a base structure for satisfying the Packet interface. type Base struct { ctx context.Context info Info connID string layers gopacket.Packet layer3Data []byte layer5Data []byte } // FastTrackedByIntegration returns whether the packet has been fast-track // accepted by the OS integration. func (pkt *Base) FastTrackedByIntegration() bool { return false } // InfoOnly returns whether the packet is informational only and does not // represent an actual packet. func (pkt *Base) InfoOnly() bool { return false } // ExpectInfo returns whether the next packet is expected to be informational only. func (pkt *Base) ExpectInfo() bool { return false } // SetCtx sets the packet context. func (pkt *Base) SetCtx(ctx context.Context) { pkt.ctx = ctx } // Ctx returns the packet context. func (pkt *Base) Ctx() context.Context { return pkt.ctx } // Info returns the packet Info. func (pkt *Base) Info() *Info { return &pkt.info } // SetPacketInfo sets a new packet Info. This must only used when initializing the packet structure. func (pkt *Base) SetPacketInfo(packetInfo Info) { pkt.info = packetInfo } // SetInbound sets a the packet direction to inbound. This must only used when initializing the packet structure. func (pkt *Base) SetInbound() { pkt.info.Inbound = true } // SetOutbound sets a the packet direction to outbound. This must only used when initializing the packet structure. func (pkt *Base) SetOutbound() { pkt.info.Inbound = false } // IsInbound checks if the packet is inbound. func (pkt *Base) IsInbound() bool { return pkt.info.Inbound } // IsOutbound checks if the packet is outbound. func (pkt *Base) IsOutbound() bool { return !pkt.info.Inbound } // HasPorts checks if the packet has a protocol that uses ports. func (pkt *Base) HasPorts() bool { switch pkt.info.Protocol { case TCP: return true case UDP, UDPLite: return true case ICMP, ICMPv6, IGMP, RAW, AnyHostInternalProtocol61: fallthrough default: return false } } // LoadPacketData loads packet data from the integration, if not yet done. func (pkt *Base) LoadPacketData() error { return ErrFailedToLoadPayload } // Layers returns the parsed layer data. func (pkt *Base) Layers() gopacket.Packet { return pkt.layers } // Raw returns the raw Layer 3 Network Data. func (pkt *Base) Raw() []byte { return pkt.layer3Data } // Payload returns the raw Layer 5 Network Data. func (pkt *Base) Payload() []byte { return pkt.layer5Data } // GetConnectionID returns the link ID for this packet. func (pkt *Base) GetConnectionID() string { if pkt.connID == "" { pkt.connID = pkt.info.CreateConnectionID() } return pkt.connID } // MatchesAddress checks if a the packet matches a given endpoint (remote or local) in protocol, network and port. // // Comparison matrix: // // ====== IN OUT // // Local Dst Src // Remote Src Dst // . func (pkt *Base) MatchesAddress(remote bool, protocol IPProtocol, network *net.IPNet, port uint16) bool { if pkt.info.Protocol != protocol { return false } if pkt.info.Inbound != remote { if !network.Contains(pkt.info.Src) { return false } if pkt.info.SrcPort != port { return false } } else { if !network.Contains(pkt.info.Dst) { return false } if pkt.info.DstPort != port { return false } } return true } // MatchesIP checks if a the packet matches a given endpoint (remote or local) IP. // // Comparison matrix: // // ====== IN OUT // // Local Dst Src // Remote Src Dst // . func (pkt *Base) MatchesIP(endpoint bool, network *net.IPNet) bool { if pkt.info.Inbound != endpoint { if network.Contains(pkt.info.Src) { return true } } else { if network.Contains(pkt.info.Dst) { return true } } return false } // FORMATTING func (pkt *Base) String() string { return pkt.FmtPacket() } // FmtPacket returns the most important information about the packet as a string. func (pkt *Base) FmtPacket() string { if pkt.info.Protocol == TCP || pkt.info.Protocol == UDP { if pkt.info.Inbound { return fmt.Sprintf("IN %s %s:%d <-> %s:%d", pkt.info.Protocol, pkt.info.Dst, pkt.info.DstPort, pkt.info.Src, pkt.info.SrcPort) } return fmt.Sprintf("OUT %s %s:%d <-> %s:%d", pkt.info.Protocol, pkt.info.Src, pkt.info.SrcPort, pkt.info.Dst, pkt.info.DstPort) } if pkt.info.Inbound { return fmt.Sprintf("IN %s %s <-> %s", pkt.info.Protocol, pkt.info.Dst, pkt.info.Src) } return fmt.Sprintf("OUT %s %s <-> %s", pkt.info.Protocol, pkt.info.Src, pkt.info.Dst) } // FmtProtocol returns the protocol as a string. func (pkt *Base) FmtProtocol() string { return pkt.info.Protocol.String() } // FmtRemoteIP returns the remote IP address as a string. func (pkt *Base) FmtRemoteIP() string { if pkt.info.Inbound { return pkt.info.Src.String() } return pkt.info.Dst.String() } // FmtRemotePort returns the remote port as a string. func (pkt *Base) FmtRemotePort() string { if pkt.info.SrcPort != 0 { if pkt.info.Inbound { return strconv.FormatUint(uint64(pkt.info.SrcPort), 10) } return strconv.FormatUint(uint64(pkt.info.DstPort), 10) } return "-" } // FmtRemoteAddress returns the full remote address (protocol, IP, port) as a string. func (pkt *Base) FmtRemoteAddress() string { return fmt.Sprintf("%s:%s:%s", pkt.info.Protocol.String(), pkt.FmtRemoteIP(), pkt.FmtRemotePort()) } // Packet is an interface to a network packet to provide object behavior the same across all systems. type Packet interface { // Verdicts. Accept() error Block() error Drop() error PermanentAccept() error PermanentBlock() error PermanentDrop() error RerouteToNameserver() error RerouteToTunnel() error FastTrackedByIntegration() bool InfoOnly() bool ExpectInfo() bool // Info. SetCtx(ctx context.Context) Ctx() context.Context Info() *Info SetPacketInfo(info Info) IsInbound() bool IsOutbound() bool SetInbound() SetOutbound() HasPorts() bool GetConnectionID() string // Payload. LoadPacketData() error Layers() gopacket.Packet Raw() []byte Payload() []byte // Matching. MatchesAddress(remote bool, protocol IPProtocol, network *net.IPNet, port uint16) bool MatchesIP(endpoint bool, network *net.IPNet) bool // Formatting. String() string FmtPacket() string FmtProtocol() string FmtRemoteIP() string FmtRemotePort() string FmtRemoteAddress() string } ================================================ FILE: service/network/packet/packetinfo.go ================================================ package packet import ( "fmt" "net" "time" ) // Info holds IP and TCP/UDP header information. type Info struct { Inbound bool InTunnel bool Version IPVersion Protocol IPProtocol SrcPort, DstPort uint16 Src, Dst net.IP PID int SeenAt time.Time } // LocalIP returns the local IP of the packet. func (pi *Info) LocalIP() net.IP { if pi.Inbound { return pi.Dst } return pi.Src } // RemoteIP returns the remote IP of the packet. func (pi *Info) RemoteIP() net.IP { if pi.Inbound { return pi.Src } return pi.Dst } // LocalPort returns the local port of the packet. func (pi *Info) LocalPort() uint16 { if pi.Inbound { return pi.DstPort } return pi.SrcPort } // RemotePort returns the remote port of the packet. func (pi *Info) RemotePort() uint16 { if pi.Inbound { return pi.SrcPort } return pi.DstPort } // CreateConnectionID creates a connection ID. // In most circumstances, this method should not be used directly, but // packet.GetConnectionID() should be called instead. func (pi *Info) CreateConnectionID() string { return CreateConnectionID(pi.Protocol, pi.Src, pi.SrcPort, pi.Dst, pi.DstPort, pi.Inbound) } // CreateConnectionID creates a connection ID. func CreateConnectionID(protocol IPProtocol, src net.IP, srcPort uint16, dst net.IP, dstPort uint16, inbound bool) string { // TODO: make this ID not depend on the packet direction for better support for forwarded packets. if protocol == TCP || protocol == UDP { if inbound { return fmt.Sprintf("%d-%s-%d-%s-%d", protocol, dst, dstPort, src, srcPort) } return fmt.Sprintf("%d-%s-%d-%s-%d", protocol, src, srcPort, dst, dstPort) } if inbound { return fmt.Sprintf("%d-%s-%s", protocol, dst, src) } return fmt.Sprintf("%d-%s-%s", protocol, src, dst) } ================================================ FILE: service/network/packet/parse.go ================================================ package packet import ( "errors" "fmt" "github.com/google/gopacket" "github.com/google/gopacket/layers" ) var layerType2IPProtocol map[gopacket.LayerType]IPProtocol func genIPProtocolFromLayerType() { layerType2IPProtocol = make(map[gopacket.LayerType]IPProtocol) for k, v := range layers.IPProtocolMetadata { layerType2IPProtocol[v.LayerType] = IPProtocol(k) } } func parseIPv4(packet gopacket.Packet, info *Info) error { if ipv4, ok := packet.NetworkLayer().(*layers.IPv4); ok { info.Version = IPv4 info.Src = ipv4.SrcIP info.Dst = ipv4.DstIP info.Protocol = IPProtocol(ipv4.Protocol) } return nil } func parseIPv6(packet gopacket.Packet, info *Info) error { if ipv6, ok := packet.NetworkLayer().(*layers.IPv6); ok { info.Version = IPv6 info.Src = ipv6.SrcIP info.Dst = ipv6.DstIP // we set Protocol to NextHeader as a fallback. If TCP or // UDP layers are detected (somewhere in the list of options) // the Protocol field is adjusted correctly. info.Protocol = IPProtocol(ipv6.NextHeader) } return nil } func parseTCP(packet gopacket.Packet, info *Info) error { if tcp, ok := packet.TransportLayer().(*layers.TCP); ok { info.Protocol = TCP info.SrcPort = uint16(tcp.SrcPort) info.DstPort = uint16(tcp.DstPort) } return nil } func parseUDP(packet gopacket.Packet, info *Info) error { if udp, ok := packet.TransportLayer().(*layers.UDP); ok { info.Protocol = UDP info.SrcPort = uint16(udp.SrcPort) info.DstPort = uint16(udp.DstPort) } return nil } /* func parseUDPLite(packet gopacket.Packet, info *Info) error { if udpLite, ok := packet.TransportLayer().(*layers.UDPLite); ok { info.Protocol = UDPLite info.SrcPort = uint16(udpLite.SrcPort) info.DstPort = uint16(udpLite.DstPort) } return nil } */ func parseICMPv4(packet gopacket.Packet, info *Info) error { if icmp, ok := packet.Layer(layers.LayerTypeICMPv4).(*layers.ICMPv4); ok { info.Protocol = ICMP _ = icmp } return nil } func parseICMPv6(packet gopacket.Packet, info *Info) error { if icmp6, ok := packet.Layer(layers.LayerTypeICMPv6).(*layers.ICMPv6); ok { info.Protocol = ICMPv6 _ = icmp6 } return nil } func parseIGMP(packet gopacket.Packet, info *Info) error { // gopacket uses LayerTypeIGMP for v1, v2 and v3 and may thus // either return layers.IGMP or layers.IGMPv1or2 if layer := packet.Layer(layers.LayerTypeIGMP); layer != nil { info.Protocol = IGMP } return nil } func checkError(packet gopacket.Packet, info *Info) error { // Check for known unparseable before checking the error layer. if info.Protocol == AnyHostInternalProtocol61 { return nil } if err := packet.ErrorLayer(); err != nil { return err.Error() } return nil } // ParseLayer3 parses an IP packet and saves the information in the given packet object. func ParseLayer3(packetData []byte, pktBase *Base) (err error) { if len(packetData) == 0 { return errors.New("empty packet") } pktBase.layer3Data = packetData ipVersion := packetData[0] >> 4 var networkLayerType gopacket.LayerType switch ipVersion { case 4: networkLayerType = layers.LayerTypeIPv4 case 6: networkLayerType = layers.LayerTypeIPv6 default: return fmt.Errorf("unknown IP version or network protocol: %02x", ipVersion) } packet := gopacket.NewPacket(packetData, networkLayerType, gopacket.DecodeOptions{ Lazy: true, NoCopy: true, }) availableDecoders := []func(gopacket.Packet, *Info) error{ parseIPv4, parseIPv6, parseTCP, parseUDP, // parseUDPLite, // We don't yet support udplite. parseICMPv4, parseICMPv6, parseIGMP, checkError, } for _, dec := range availableDecoders { if err := dec(packet, pktBase.Info()); err != nil { return err } } pktBase.layers = packet if transport := packet.TransportLayer(); transport != nil { pktBase.layer5Data = transport.LayerPayload() } return nil } // ParseLayer4 parses an layer 4 packet and saves the information in the given packet object. func ParseLayer4(packetData []byte, pktBase *Base) (err error) { if len(packetData) == 0 { return errors.New("empty packet") } var layer gopacket.LayerType switch pktBase.info.Protocol { case ICMP: layer = layers.LayerTypeICMPv4 case IGMP: layer = layers.LayerTypeIGMP case TCP: layer = layers.LayerTypeTCP case UDP: layer = layers.LayerTypeUDP case ICMPv6: layer = layers.LayerTypeICMPv6 case UDPLite: return fmt.Errorf("UDPLite not supported") case RAW: return fmt.Errorf("RAW protocol not supported") case AnyHostInternalProtocol61: return fmt.Errorf("AnyHostInternalProtocol61 protocol not supported") default: return fmt.Errorf("protocol not supported") } packet := gopacket.NewPacket(packetData, layer, gopacket.DecodeOptions{ Lazy: true, NoCopy: true, }) availableDecoders := []func(gopacket.Packet, *Info) error{ parseTCP, parseUDP, // parseUDPLite, // We don't yet support udplite. parseICMPv4, parseICMPv6, parseIGMP, checkError, } for _, dec := range availableDecoders { if err := dec(packet, pktBase.Info()); err != nil { return err } } pktBase.layers = packet if transport := packet.TransportLayer(); transport != nil { pktBase.layer5Data = transport.LayerPayload() } return nil } func init() { genIPProtocolFromLayerType() } ================================================ FILE: service/network/ports.go ================================================ package network import ( "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/rng" ) // GetUnusedLocalPort returns a local port of the specified protocol that is // currently unused and is unlikely to be used within the next seconds. func GetUnusedLocalPort(protocol uint8) (port uint16, ok bool) { allConns := conns.clone() tries := 1000 // Try up to 1000 times to find an unused port. nextPort: for i := range tries { // Generate random port between 10000 and 65535 rN, err := rng.Number(55535) if err != nil { log.Warningf("network: failed to generate random port: %s", err) return 0, false } port := uint16(rN + 10000) // Shrink range when we chew through the tries. portRangeStart := port - 10 // Check if the generated port is unused. nextConnection: for _, conn := range allConns { switch { case !conn.DataIsComplete(): // Skip connection if the data is not complete. continue nextConnection case conn.Entity.Protocol != protocol: // Skip connection if the protocol does not match the protocol of interest. continue nextConnection case conn.LocalPort <= port && conn.LocalPort >= portRangeStart: // Skip port if the local port is in dangerous proximity. // Consecutive port numbers are very common. continue nextPort } } // Log if it took more than 10 attempts. if i >= 10 { log.Warningf("network: took %d attempts to find a suitable unused port for pre-auth", i+1) } // The checks have passed. We have found a good unused port. return port, true } return 0, false } ================================================ FILE: service/network/proc/findpid.go ================================================ //go:build linux package proc import ( "errors" "io/fs" "os" "strconv" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/socket" ) // GetPID returns the already existing pid of the given socket info or searches for it. // This also acts as a getter for socket.Info.PID, as locking for that occurs here. func GetPID(socketInfo socket.Info) (pid int) { // Get currently assigned PID to the socket info. currentPid := socketInfo.GetPID() // If the current PID already is valid (ie. not unidentified), return it immediately. if currentPid != socket.UndefinedProcessID { return currentPid } // Find PID for the given UID and inode. pid = findPID(socketInfo.GetUIDandInode()) // Set the newly found PID on the socket info. socketInfo.SetPID(pid) // Return found PID. return pid } // findPID returns the pid of the given uid and socket inode. func findPID(uid, inode int) (pid int) { socketName := "socket:[" + strconv.Itoa(inode) + "]" // Always update pid table (it has a call limiter anyway) updatePids() // Get all pids for the given uid. pids, ok := getPidsByUser(uid) if !ok { return socket.UndefinedProcessID } // Look through the PIDs in reverse order, because higher/newer PIDs will be more likely to // be searched for. for j := len(pids) - 1; j >= 0; j-- { if pidHasSocket(pids[j], socketName) { return pids[j] } } return socket.UndefinedProcessID } func pidHasSocket(pid int, socketName string) bool { socketBase := "/proc/" + strconv.Itoa(pid) + "/fd" entries := readDirNames(socketBase) if len(entries) == 0 { return false } socketBase += "/" // Look through the FDs in reverse order, because higher/newer FDs will be // more likely to be searched for. for i := len(entries) - 1; i >= 0; i-- { link, err := os.Readlink(socketBase + entries[i]) if err != nil { if !errors.Is(err, fs.ErrNotExist) { log.Warningf("proc: failed to read link /proc/%d/fd/%s: %s", pid, entries[i], err) } continue } if link == socketName { return true } } return false } // readDirNames only reads the directory names. Using os.ReadDir() would call `lstat` on every // resulting directory name, which we don't need. This function will be called a lot, so we should // refrain from unnecessary work. func readDirNames(dir string) (names []string) { file, err := os.Open(dir) if err != nil { if !errors.Is(err, fs.ErrNotExist) { log.Warningf("proc: could not open directory %s: %s", dir, err) } return } defer func() { _ = file.Close() }() names, err = file.Readdirnames(0) if err != nil { log.Warningf("proc: could not get entries from directory %s: %s", dir, err) return []string{} } return } ================================================ FILE: service/network/proc/pids_by_user.go ================================================ //go:build linux package proc import ( "errors" "io/fs" "os" "strconv" "sync" "syscall" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" ) var ( // pidsByUserLock is also used for locking the socketInfo.PID on all socket.*Info structs. pidsByUser = make(map[int][]int) pidsByUserLock sync.RWMutex fetchPidsByUser = utils.NewCallLimiter2(10 * time.Millisecond) ) // getPidsByUser returns the cached PIDs for the given UID. func getPidsByUser(uid int) (pids []int, ok bool) { pidsByUserLock.RLock() defer pidsByUserLock.RUnlock() pids, ok = pidsByUser[uid] return } // updatePids fetches and creates a new pidsByUser map using a call limiter. func updatePids() { fetchPidsByUser.Do(func() { newPidsByUser := make(map[int][]int) pidCnt := 0 entries := readDirNames("/proc") if len(entries) == 0 { log.Warning("proc: found no PIDs in /proc") return } entryLoop: for _, entry := range entries { pid, err := strconv.ParseInt(entry, 10, 32) if err != nil { continue entryLoop } statData, err := os.Stat("/proc/" + strconv.FormatInt(pid, 10)) if err != nil { if !errors.Is(err, fs.ErrNotExist) { log.Warningf("proc: could not stat /proc/%d: %s", pid, err) } continue entryLoop } sys, ok := statData.Sys().(*syscall.Stat_t) if !ok { log.Warningf("proc: unable to parse /proc/%d: wrong type", pid) continue entryLoop } pids, ok := newPidsByUser[int(sys.Uid)] if ok { newPidsByUser[int(sys.Uid)] = append(pids, int(pid)) } else { newPidsByUser[int(sys.Uid)] = []int{int(pid)} } pidCnt++ } // log.Tracef("proc: updated PID table with %d entries", pidCnt) pidsByUserLock.Lock() defer pidsByUserLock.Unlock() pidsByUser = newPidsByUser }) } ================================================ FILE: service/network/proc/tables.go ================================================ //go:build linux package proc import ( "bufio" "encoding/hex" "fmt" "net" "os" "strconv" "strings" "unicode" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/socket" ) /* 1. find socket inode - by incoming (listenting sockets) or outgoing (local port + external IP + port) - also local IP? - /proc/net/{tcp|udp}[6] 2. get list of processes of uid 3. find socket inode in process fds - if not found, refresh map of uid->pids - if not found, check ALL pids: maybe euid != uid 4. gather process info Cache every step! */ // Network Related Constants. const ( TCP4 uint8 = iota UDP4 TCP6 UDP6 ICMP4 ICMP6 tcp4ProcFile = "/proc/net/tcp" tcp6ProcFile = "/proc/net/tcp6" udp4ProcFile = "/proc/net/udp" udp6ProcFile = "/proc/net/udp6" tcpListenStateHex = "0A" ) // GetTCP4Table returns the system table for IPv4 TCP activity. func GetTCP4Table() (connections []*socket.ConnectionInfo, listeners []*socket.BindInfo, err error) { return getTableFromSource(TCP4, tcp4ProcFile) } // GetTCP6Table returns the system table for IPv6 TCP activity. func GetTCP6Table() (connections []*socket.ConnectionInfo, listeners []*socket.BindInfo, err error) { return getTableFromSource(TCP6, tcp6ProcFile) } // GetUDP4Table returns the system table for IPv4 UDP activity. func GetUDP4Table() (binds []*socket.BindInfo, err error) { _, binds, err = getTableFromSource(UDP4, udp4ProcFile) return } // GetUDP6Table returns the system table for IPv6 UDP activity. func GetUDP6Table() (binds []*socket.BindInfo, err error) { _, binds, err = getTableFromSource(UDP6, udp6ProcFile) return } const ( // hint: we split fields by multiple delimiters, see procDelimiter fieldIndexLocalIP = 1 fieldIndexLocalPort = 2 fieldIndexRemoteIP = 3 fieldIndexRemotePort = 4 fieldIndexUID = 11 fieldIndexInode = 13 ) func getTableFromSource(stack uint8, procFile string) (connections []*socket.ConnectionInfo, binds []*socket.BindInfo, err error) { var ipConverter func(string) net.IP switch stack { case TCP4, UDP4: ipConverter = convertIPv4 case TCP6, UDP6: ipConverter = convertIPv6 default: return nil, nil, fmt.Errorf("unsupported table stack: %d", stack) } // open file socketData, err := os.Open(procFile) if err != nil { return nil, nil, err } defer func() { _ = socketData.Close() }() // file scanner scanner := bufio.NewScanner(socketData) scanner.Split(bufio.ScanLines) // parse scanner.Scan() // skip first row for scanner.Scan() { fields := strings.FieldsFunc(scanner.Text(), procDelimiter) if len(fields) < 14 { // log.Tracef("proc: too short: %s", fields) continue } localIP := ipConverter(fields[fieldIndexLocalIP]) if localIP == nil { continue } localPort, err := strconv.ParseUint(fields[fieldIndexLocalPort], 16, 16) if err != nil { log.Warningf("proc: could not parse port: %s", err) continue } uid, err := strconv.ParseInt(fields[fieldIndexUID], 10, 32) // log.Tracef("uid: %s", fields[fieldIndexUID]) if err != nil { log.Warningf("proc: could not parse uid %s: %s", fields[11], err) continue } inode, err := strconv.ParseInt(fields[fieldIndexInode], 10, 32) // log.Tracef("inode: %s", fields[fieldIndexInode]) if err != nil { log.Warningf("proc: could not parse inode %s: %s", fields[13], err) continue } switch stack { case UDP4, UDP6: binds = append(binds, &socket.BindInfo{ Local: socket.Address{ IP: localIP, Port: uint16(localPort), }, PID: socket.UndefinedProcessID, UID: int(uid), Inode: int(inode), }) case TCP4, TCP6: if fields[5] == tcpListenStateHex { // listener binds = append(binds, &socket.BindInfo{ Local: socket.Address{ IP: localIP, Port: uint16(localPort), }, PID: socket.UndefinedProcessID, UID: int(uid), Inode: int(inode), }) } else { // connection remoteIP := ipConverter(fields[fieldIndexRemoteIP]) if remoteIP == nil { continue } remotePort, err := strconv.ParseUint(fields[fieldIndexRemotePort], 16, 16) if err != nil { log.Warningf("proc: could not parse port: %s", err) continue } connections = append(connections, &socket.ConnectionInfo{ Local: socket.Address{ IP: localIP, Port: uint16(localPort), }, Remote: socket.Address{ IP: remoteIP, Port: uint16(remotePort), }, PID: socket.UndefinedProcessID, UID: int(uid), Inode: int(inode), }) } } } return connections, binds, nil } func procDelimiter(c rune) bool { return unicode.IsSpace(c) || c == ':' } func convertIPv4(data string) net.IP { // Decode and bullshit check the data length. decoded, err := hex.DecodeString(data) if err != nil { log.Warningf("proc: could not parse IPv4 %s: %s", data, err) return nil } if len(decoded) != 4 { log.Warningf("proc: decoded IPv4 %s has wrong length", decoded) return nil } // Build the IPv4 address with the reversed byte order. ip := net.IPv4(decoded[3], decoded[2], decoded[1], decoded[0]) return ip } func convertIPv6(data string) net.IP { // Decode and bullshit check the data length. decoded, err := hex.DecodeString(data) if err != nil { log.Warningf("proc: could not parse IPv6 %s: %s", data, err) return nil } if len(decoded) != 16 { log.Warningf("proc: decoded IPv6 %s has wrong length", decoded) return nil } // Build the IPv6 address with the translated byte order. for i := 0; i < 16; i += 4 { decoded[i], decoded[i+1], decoded[i+2], decoded[i+3] = decoded[i+3], decoded[i+2], decoded[i+1], decoded[i] } ip := net.IP(decoded) return ip } ================================================ FILE: service/network/proc/tables_test.go ================================================ //go:build linux package proc import ( "fmt" "testing" ) func TestSockets(t *testing.T) { t.Parallel() connections, listeners, err := GetTCP4Table() if err != nil { t.Fatal(err) } fmt.Println("\nTCP 4 connections:") for _, connection := range connections { pid := GetPID(connection) fmt.Printf("%d: %+v\n", pid, connection) } fmt.Println("\nTCP 4 listeners:") for _, listener := range listeners { pid := GetPID(listener) fmt.Printf("%d: %+v\n", pid, listener) } connections, listeners, err = GetTCP6Table() if err != nil { t.Fatal(err) } fmt.Println("\nTCP 6 connections:") for _, connection := range connections { pid := GetPID(connection) fmt.Printf("%d: %+v\n", pid, connection) } fmt.Println("\nTCP 6 listeners:") for _, listener := range listeners { pid := GetPID(listener) fmt.Printf("%d: %+v\n", pid, listener) } binds, err := GetUDP4Table() if err != nil { t.Fatal(err) } fmt.Println("\nUDP 4 binds:") for _, bind := range binds { pid := GetPID(bind) fmt.Printf("%d: %+v\n", pid, bind) } binds, err = GetUDP6Table() if err != nil { t.Fatal(err) } fmt.Println("\nUDP 6 binds:") for _, bind := range binds { pid := GetPID(bind) fmt.Printf("%d: %+v\n", pid, bind) } } ================================================ FILE: service/network/reference/ports.go ================================================ package reference import ( "strconv" "strings" ) var ( portNames = map[uint16]string{ 20: "FTP-DATA", 21: "FTP", 22: "SSH", 23: "TELNET", 25: "SMTP", 43: "WHOIS", 53: "DNS", 67: "DHCP_SERVER", 68: "DHCP_CLIENT", 69: "TFTP", 80: "HTTP", 110: "POP3", 123: "NTP", 143: "IMAP", 161: "SNMP", 179: "BGP", 194: "IRC", 389: "LDAP", 443: "HTTPS", 445: "SMB", 587: "SMTP_ALT", 465: "SMTP_SSL", 993: "IMAP_SSL", 995: "POP3_SSL", } portNumbers = map[string]uint16{ "FTP-DATA": 20, "FTP": 21, "SSH": 22, "TELNET": 23, "SMTP": 25, "WHOIS": 43, "DNS": 53, "DHCP-SERVER": 67, "DHCP_SERVER": 67, "DHCP-CLIENT": 68, "DHCP_CLIENT": 68, "TFTP": 69, "HTTP": 80, "POP3": 110, "NTP": 123, "IMAP": 143, "SNMP": 161, "BGP": 179, "IRC": 194, "LDAP": 389, "HTTPS": 443, "SMB": 445, "SMTP-ALT": 587, "SMTP_ALT": 587, "SMTP-SSL": 465, "SMTP_SSL": 465, "IMAP-SSL": 993, "IMAP_SSL": 993, "POP3-SSL": 995, "POP3_SSL": 995, } ) // GetPortName returns the name of a port number. func GetPortName(port uint16) (name string) { name, ok := portNames[port] if ok { return name } return strconv.Itoa(int(port)) } // GetPortNumber returns the number of a port name. func GetPortNumber(port string) (number uint16, ok bool) { number, ok = portNumbers[strings.ToUpper(port)] if ok { return number, true } return 0, false } ================================================ FILE: service/network/reference/protocols.go ================================================ package reference import ( "strconv" "strings" ) var ( protocolNames = map[uint8]string{ 1: "ICMP", 2: "IGMP", 6: "TCP", 17: "UDP", 27: "RDP", 58: "ICMP6", 33: "DCCP", 136: "UDP-LITE", } protocolNumbers = map[string]uint8{ "ICMP": 1, "IGMP": 2, "TCP": 6, "UDP": 17, "RDP": 27, "DCCP": 33, "ICMP6": 58, "UDP-LITE": 136, } ) // GetProtocolName returns the name of a IP protocol number. func GetProtocolName(protocol uint8) (name string) { name, ok := protocolNames[protocol] if ok { return name } return strconv.Itoa(int(protocol)) } // GetProtocolNumber returns the number of a IP protocol name. func GetProtocolNumber(protocol string) (number uint8, ok bool) { number, ok = protocolNumbers[strings.ToUpper(protocol)] if ok { return number, true } return 0, false } // IsPacketProtocol returns whether the given protocol number is a known packet based protocol. // Note: Not fully complete. Calling IsPacketProtocol() does not equal calling !IsStreamProtocol(). func IsPacketProtocol(protocol uint8) bool { switch protocol { case 1, // ICMP 17, // UDP 27, // RDP 58, // ICMP6 33, // DCCP 136: // UDP-LITE return true default: return false } } // IsStreamProtocol returns whether the given protocol number is a known stream based protocol. // Note: Not fully complete. Calling IsPacketProtocol() does not equal calling !IsStreamProtocol(). func IsStreamProtocol(protocol uint8) bool { switch protocol { case 6: // TCP return true default: return false } } // IsICMP returns whether the given protocol is ICMP or ICMPv6. func IsICMP(protocol uint8) bool { switch protocol { case 1, // ICMP 58: // ICMP6 return true default: return false } } ================================================ FILE: service/network/socket/socket.go ================================================ package socket import ( "net" "sync" ) const ( // UndefinedProcessID signifies that the process ID is unknown. // It must match portmaster/process.UndefinedProcessID // It is duplicated here because of import loops. UndefinedProcessID = -1 ) // ConnectionInfo holds socket information returned by the system. type ConnectionInfo struct { sync.Mutex Local Address Remote Address PID int UID int Inode int } // BindInfo holds socket information returned by the system. type BindInfo struct { sync.Mutex Local Address PID int UID int Inode int ListensAny bool } // Address is an IP + Port pair. type Address struct { IP net.IP Port uint16 } // Info is a generic interface to both ConnectionInfo and BindInfo. type Info interface { GetPID() int SetPID(pid int) GetUID() int GetUIDandInode() (int, int) } // GetPID returns the PID. func (i *ConnectionInfo) GetPID() int { i.Lock() defer i.Unlock() return i.PID } // SetPID sets the PID to the given value. func (i *ConnectionInfo) SetPID(pid int) { i.Lock() defer i.Unlock() i.PID = pid } // GetUID returns the UID. func (i *ConnectionInfo) GetUID() int { i.Lock() defer i.Unlock() return i.UID } // GetUIDandInode returns the UID and Inode. func (i *ConnectionInfo) GetUIDandInode() (int, int) { i.Lock() defer i.Unlock() return i.UID, i.Inode } // GetPID returns the PID. func (i *BindInfo) GetPID() int { i.Lock() defer i.Unlock() return i.PID } // SetPID sets the PID to the given value. func (i *BindInfo) SetPID(pid int) { i.Lock() defer i.Unlock() i.PID = pid } // GetUID returns the UID. func (i *BindInfo) GetUID() int { i.Lock() defer i.Unlock() return i.UID } // GetUIDandInode returns the UID and Inode. func (i *BindInfo) GetUIDandInode() (int, int) { i.Lock() defer i.Unlock() return i.UID, i.Inode } // Compile time checks. var ( _ Info = new(ConnectionInfo) _ Info = new(BindInfo) ) ================================================ FILE: service/network/state/exists.go ================================================ package state import ( "time" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/network/socket" ) const ( // UDPConnectionTTL defines the duration after which unseen UDP connections are regarded as ended. UDPConnectionTTL = 10 * time.Minute ) // Exists checks if the given connection is present in the system state tables. func Exists(pktInfo *packet.Info, now time.Time) (exists bool) { // TODO: create lookup maps before running a flurry of Exists() checks. switch { case pktInfo.Version == packet.IPv4 && pktInfo.Protocol == packet.TCP: return tcp4Table.exists(pktInfo) case pktInfo.Version == packet.IPv6 && pktInfo.Protocol == packet.TCP: return tcp6Table.exists(pktInfo) case pktInfo.Version == packet.IPv4 && pktInfo.Protocol == packet.UDP: return udp4Table.exists(pktInfo, now) case pktInfo.Version == packet.IPv6 && pktInfo.Protocol == packet.UDP: return udp6Table.exists(pktInfo, now) default: return false } } func (table *tcpTable) exists(pktInfo *packet.Info) (exists bool) { // Update tables if older than the connection that is checked. if table.lastUpdateAt.Load() < pktInfo.SeenAt.UnixNano() { table.updateTables() } table.lock.RLock() defer table.lock.RUnlock() localIP := pktInfo.LocalIP() localPort := pktInfo.LocalPort() remoteIP := pktInfo.RemoteIP() remotePort := pktInfo.RemotePort() // search connections for _, socketInfo := range table.connections { if localPort == socketInfo.Local.Port && remotePort == socketInfo.Remote.Port && remoteIP.Equal(socketInfo.Remote.IP) && localIP.Equal(socketInfo.Local.IP) { return true } } return false } func (table *udpTable) exists(pktInfo *packet.Info, now time.Time) (exists bool) { // Update tables if older than the connection that is checked. if table.lastUpdateAt.Load() < pktInfo.SeenAt.UnixNano() { table.updateTables() } table.lock.RLock() defer table.lock.RUnlock() localIP := pktInfo.LocalIP() localPort := pktInfo.LocalPort() remoteIP := pktInfo.RemoteIP() remotePort := pktInfo.RemotePort() connThreshhold := now.Add(-UDPConnectionTTL) // search binds for _, socketInfo := range table.binds { if localPort == socketInfo.Local.Port && (socketInfo.Local.IP[0] == 0 || localIP.Equal(socketInfo.Local.IP)) { udpConnState, ok := table.getConnState(socketInfo, socket.Address{ IP: remoteIP, Port: remotePort, }) switch { case !ok: return false case udpConnState.lastSeen.After(connThreshhold): return true default: return false } } } return false } ================================================ FILE: service/network/state/info.go ================================================ package state import ( "sync" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/socket" ) // Info holds network state information as provided by the system. type Info struct { record.Base sync.Mutex TCP4Connections []*socket.ConnectionInfo TCP4Listeners []*socket.BindInfo TCP6Connections []*socket.ConnectionInfo TCP6Listeners []*socket.BindInfo UDP4Binds []*socket.BindInfo UDP6Binds []*socket.BindInfo } // GetInfo returns all system state tables. The returned data must not be modified. func GetInfo() *Info { info := &Info{} info.TCP4Connections, info.TCP4Listeners = tcp4Table.updateTables() info.UDP4Binds = udp4Table.updateTables() if netenv.IPv6Enabled() { info.TCP6Connections, info.TCP6Listeners = tcp6Table.updateTables() info.UDP6Binds = udp6Table.updateTables() } info.UpdateMeta() return info } ================================================ FILE: service/network/state/lookup.go ================================================ package state import ( "errors" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/network/socket" ) // - TCP // - Outbound: Match listeners (in!), then connections (out!) // - Inbound: Match listeners (in!), then connections (out!) // - Clean via connections // - UDP // - Any connection: match specific local address or zero IP // - In or out: save direction of first packet: // - map[]map[]{direction, lastSeen} // - only clean if is removed by OS // - limit to 256 entries? // - clean after 72hrs? // - switch direction to outbound if outbound packet is seen? // - IP: Unidentified Process // Errors. var ( ErrConnectionNotFound = errors.New("could not find connection in system state tables") ErrPIDNotFound = errors.New("could not find pid for socket inode") ) const ( lookupTries = 5 fastLookupTries = 2 // 1. current table, 2. get table with max 10ms, could be 0ms, 3. 10ms wait ) // Lookup looks for the given connection in the system state tables and returns the PID of the associated process and whether the connection is inbound. func Lookup(pktInfo *packet.Info, fast bool) (pid int, inbound bool, err error) { // auto-detect version if pktInfo.Version == 0 { if ip := pktInfo.LocalIP().To4(); ip != nil { pktInfo.Version = packet.IPv4 } else { pktInfo.Version = packet.IPv6 } } switch { case pktInfo.Version == packet.IPv4 && pktInfo.Protocol == packet.TCP: return tcp4Table.lookup(pktInfo, fast) case pktInfo.Version == packet.IPv6 && pktInfo.Protocol == packet.TCP: return tcp6Table.lookup(pktInfo, fast) case pktInfo.Version == packet.IPv4 && pktInfo.Protocol == packet.UDP: return udp4Table.lookup(pktInfo, fast) case pktInfo.Version == packet.IPv6 && pktInfo.Protocol == packet.UDP: return udp6Table.lookup(pktInfo, fast) default: return socket.UndefinedProcessID, pktInfo.Inbound, errors.New("unsupported protocol for finding process") } } func (table *tcpTable) lookup(pktInfo *packet.Info, fast bool) ( pid int, inbound bool, err error, ) { // Prepare variables. var ( connections []*socket.ConnectionInfo listeners []*socket.BindInfo dualStackConnections []*socket.ConnectionInfo dualStackListeners []*socket.BindInfo ) // Search for the socket until found. for i := 1; i <= lookupTries; i++ { // Use existing tables for first check if packet was seen after last table update. if i == 1 && pktInfo.SeenAt.UnixNano() >= table.lastUpdateAt.Load() { connections, listeners = table.getCurrentTables() } else { connections, listeners = table.updateTables() } // Check tables for socket. socketInfo, inbound := findTCPSocket(pktInfo, connections, listeners) // If there's a match, check if we have the PID and return. if socketInfo != nil { return CheckPID(socketInfo, inbound) } // DUAL-STACK // Skip if dualStack is not enabled. if table.dualStack == nil { continue } // Use existing tables for first check if packet was seen after last table update. if i == 1 && pktInfo.SeenAt.UnixNano() >= table.dualStack.lastUpdateAt.Load() { dualStackConnections, dualStackListeners = table.dualStack.getCurrentTables() } else { dualStackConnections, dualStackListeners = table.dualStack.updateTables() } // Check tables for socket. socketInfo, inbound = findTCPSocket(pktInfo, dualStackConnections, dualStackListeners) // If there's a match, check if we have the PID and return. if socketInfo != nil { return CheckPID(socketInfo, inbound) } // Search less if we want to be fast. if fast && i >= fastLookupTries { break } } return socket.UndefinedProcessID, pktInfo.Inbound, ErrConnectionNotFound } func findTCPSocket( pktInfo *packet.Info, connections []*socket.ConnectionInfo, listeners []*socket.BindInfo, ) ( socketInfo socket.Info, inbound bool, ) { localIP := pktInfo.LocalIP() localPort := pktInfo.LocalPort() // always search listeners first for _, socketInfo := range listeners { if localPort == socketInfo.Local.Port && (socketInfo.ListensAny || localIP.Equal(socketInfo.Local.IP)) { return socketInfo, true } } remoteIP := pktInfo.RemoteIP() remotePort := pktInfo.RemotePort() // search connections for _, socketInfo := range connections { if localPort == socketInfo.Local.Port && remotePort == socketInfo.Remote.Port && remoteIP.Equal(socketInfo.Remote.IP) && localIP.Equal(socketInfo.Local.IP) { return socketInfo, false } } return nil, false } func (table *udpTable) lookup(pktInfo *packet.Info, fast bool) ( pid int, inbound bool, err error, ) { // TODO: Currently broadcast/multicast scopes are not checked, so we might // attribute an incoming broadcast/multicast packet to the wrong process if // there are multiple processes listening on the same local port, but // binding to different addresses. This highly unusual for clients. isInboundMulticast := pktInfo.Inbound && netutils.GetIPScope(pktInfo.LocalIP()) == netutils.LocalMulticast // Prepare variables. var ( binds []*socket.BindInfo dualStackBinds []*socket.BindInfo ) // Search for the socket until found. for i := 1; i <= lookupTries; i++ { // Get or update tables. if i == 1 && pktInfo.SeenAt.UnixNano() >= table.lastUpdateAt.Load() { binds = table.getCurrentTables() } else { binds = table.updateTables() } // Check tables for socket. socketInfo := findUDPSocket(pktInfo, binds, isInboundMulticast) // If there's a match, do some last checks and return. if socketInfo != nil { // If there is no remote port, do check for the direction of the // connection. This will be the case for pure checking functions // that do not want to change direction state. if pktInfo.RemotePort() == 0 { return CheckPID(socketInfo, pktInfo.Inbound) } // Get (and save) the direction of the connection. connInbound := table.getDirection(socketInfo, pktInfo) // Check we have the PID and return. return CheckPID(socketInfo, connInbound) } // DUAL-STACK // Skip if dualStack is not enabled. if table.dualStack == nil { continue } // Get or update tables. if i == 1 && pktInfo.SeenAt.UnixNano() >= table.lastUpdateAt.Load() { dualStackBinds = table.dualStack.getCurrentTables() } else { dualStackBinds = table.dualStack.updateTables() } // Check tables for socket. socketInfo = findUDPSocket(pktInfo, dualStackBinds, isInboundMulticast) // If there's a match, do some last checks and return. if socketInfo != nil { // If there is no remote port, do check for the direction of the // connection. This will be the case for pure checking functions // that do not want to change direction state. if pktInfo.RemotePort() == 0 { return CheckPID(socketInfo, pktInfo.Inbound) } // Get (and save) the direction of the connection. connInbound := table.getDirection(socketInfo, pktInfo) // Check we have the PID and return. return CheckPID(socketInfo, connInbound) } // Search less if we want to be fast. if fast && i >= fastLookupTries { break } } return socket.UndefinedProcessID, pktInfo.Inbound, ErrConnectionNotFound } func findUDPSocket(pktInfo *packet.Info, binds []*socket.BindInfo, isInboundMulticast bool) (socketInfo *socket.BindInfo) { localIP := pktInfo.LocalIP() localPort := pktInfo.LocalPort() // search binds for _, socketInfo := range binds { if localPort == socketInfo.Local.Port && (socketInfo.ListensAny || // zero IP (dual-stack) isInboundMulticast || // inbound broadcast, multicast localIP.Equal(socketInfo.Local.IP)) { return socketInfo } } return nil } ================================================ FILE: service/network/state/system_default.go ================================================ //go:build !windows && !linux // +build !windows,!linux package state import ( "time" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/network/socket" ) func init() { // This increases performance on unsupported system. // It's not critical at all and does not break anything if it fails. go func() { // Wait for one minute before we set the default value, as we // currently cannot easily integrate into the startup procedure. time.Sleep(1 * time.Minute) // We cannot use process.CfgOptionEnableProcessDetectionKey, because of an import loop. config.SetDefaultConfigOption("core/enableProcessDetection", false) }() } func getTCP4Table() (connections []*socket.ConnectionInfo, listeners []*socket.BindInfo, err error) { return nil, nil, nil } func getTCP6Table() (connections []*socket.ConnectionInfo, listeners []*socket.BindInfo, err error) { return nil, nil, nil } func getUDP4Table() (binds []*socket.BindInfo, err error) { return nil, nil } func getUDP6Table() (binds []*socket.BindInfo, err error) { return nil, nil } // CheckPID checks the if socket info already has a PID and if not, tries to find it. // Depending on the OS, this might be a no-op. func CheckPID(socketInfo socket.Info, connInbound bool) (pid int, inbound bool, err error) { return socketInfo.GetPID(), connInbound, nil } ================================================ FILE: service/network/state/system_linux.go ================================================ package state import ( "time" "github.com/safing/portmaster/service/network/proc" "github.com/safing/portmaster/service/network/socket" ) var ( getTCP4Table = proc.GetTCP4Table getTCP6Table = proc.GetTCP6Table getUDP4Table = proc.GetUDP4Table getUDP6Table = proc.GetUDP6Table checkPIDTries = 5 checkPIDBaseWaitTime = 5 * time.Millisecond ) // CheckPID checks the if socket info already has a PID and if not, tries to find it. // Depending on the OS, this might be a no-op. func CheckPID(socketInfo socket.Info, connInbound bool) (pid int, inbound bool, err error) { for i := 1; i <= checkPIDTries; i++ { // look for PID pid = proc.GetPID(socketInfo) if pid != socket.UndefinedProcessID { // if we found a PID, return break } // every time, except for the last iteration if i < checkPIDTries { // we found no PID, we could have been too fast, give the kernel some time to think // back off timer: with 5ms baseWaitTime: 5, 10, 15, 20, 25 - 75ms in total time.Sleep(time.Duration(i) * checkPIDBaseWaitTime) } } return pid, connInbound, nil } ================================================ FILE: service/network/state/system_windows.go ================================================ package state import ( "github.com/safing/portmaster/service/network/iphelper" "github.com/safing/portmaster/service/network/socket" ) var ( getTCP4Table = iphelper.GetTCP4Table getTCP6Table = iphelper.GetTCP6Table getUDP4Table = iphelper.GetUDP4Table getUDP6Table = iphelper.GetUDP6Table ) // CheckPID checks the if socket info already has a PID and if not, tries to find it. // Depending on the OS, this might be a no-op. func CheckPID(socketInfo socket.Info, connInbound bool) (pid int, inbound bool, err error) { return socketInfo.GetPID(), connInbound, nil } ================================================ FILE: service/network/state/tcp.go ================================================ package state import ( "net" "sync" "sync/atomic" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/network/socket" ) const ( minDurationBetweenTableUpdates = 10 * time.Millisecond ) type tcpTable struct { version int connections []*socket.ConnectionInfo listeners []*socket.BindInfo lock sync.RWMutex // lastUpdateAt stores the time when the tables where last updated as unix nanoseconds. lastUpdateAt atomic.Int64 fetchLimiter *utils.CallLimiter2 fetchTable func() (connections []*socket.ConnectionInfo, listeners []*socket.BindInfo, err error) dualStack *tcpTable } var ( tcp6Table = &tcpTable{ version: 6, fetchLimiter: utils.NewCallLimiter2(minDurationBetweenTableUpdates), fetchTable: getTCP6Table, } tcp4Table = &tcpTable{ version: 4, fetchLimiter: utils.NewCallLimiter2(minDurationBetweenTableUpdates), fetchTable: getTCP4Table, } ) // EnableTCPDualStack adds the TCP6 table to the TCP4 table as a dual-stack. // Must be called before any lookup operation. func EnableTCPDualStack() { tcp4Table.dualStack = tcp6Table } func (table *tcpTable) getCurrentTables() ( connections []*socket.ConnectionInfo, listeners []*socket.BindInfo, ) { table.lock.RLock() defer table.lock.RUnlock() return table.connections, table.listeners } func (table *tcpTable) updateTables() ( connections []*socket.ConnectionInfo, listeners []*socket.BindInfo, ) { // Fetch tables. table.fetchLimiter.Do(func() { // Fetch new tables from system. connections, listeners, err := table.fetchTable() if err != nil { log.Warningf("state: failed to get TCP%d socket table: %s", table.version, err) return } // Pre-check for any listeners. for _, bindInfo := range listeners { bindInfo.ListensAny = bindInfo.Local.IP.Equal(net.IPv4zero) || bindInfo.Local.IP.Equal(net.IPv6zero) } // Apply new tables. table.lock.Lock() defer table.lock.Unlock() table.connections = connections table.listeners = listeners table.lastUpdateAt.Store(time.Now().UnixNano()) }) return table.getCurrentTables() } ================================================ FILE: service/network/state/udp.go ================================================ package state import ( "context" "net" "strconv" "sync" "sync/atomic" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/network/socket" ) type udpTable struct { version int binds []*socket.BindInfo lock sync.RWMutex // lastUpdateAt stores the time when the tables where last updated as unix nanoseconds. lastUpdateAt atomic.Int64 fetchLimiter *utils.CallLimiter2 fetchTable func() (binds []*socket.BindInfo, err error) states map[string]map[string]*udpState statesLock sync.Mutex dualStack *udpTable } type udpState struct { inbound bool lastSeen time.Time } const ( // UDPConnStateTTL is the maximum time a udp connection state is held. UDPConnStateTTL = 72 * time.Hour // UDPConnStateShortenedTTL is a shortened maximum time a udp connection state is held, if there more entries than defined by AggressiveCleaningThreshold. UDPConnStateShortenedTTL = 3 * time.Hour // AggressiveCleaningThreshold defines the soft limit of udp connection state held per udp socket. AggressiveCleaningThreshold = 256 ) var ( udp6Table = &udpTable{ version: 6, fetchLimiter: utils.NewCallLimiter2(minDurationBetweenTableUpdates), fetchTable: getUDP6Table, states: make(map[string]map[string]*udpState), } udp4Table = &udpTable{ version: 4, fetchLimiter: utils.NewCallLimiter2(minDurationBetweenTableUpdates), fetchTable: getUDP4Table, states: make(map[string]map[string]*udpState), } ) // EnableUDPDualStack adds the UDP6 table to the UDP4 table as a dual-stack. // Must be called before any lookup operation. func EnableUDPDualStack() { udp4Table.dualStack = udp6Table } func (table *udpTable) getCurrentTables() (binds []*socket.BindInfo) { table.lock.RLock() defer table.lock.RUnlock() return table.binds } func (table *udpTable) updateTables() (binds []*socket.BindInfo) { // Fetch tables. table.fetchLimiter.Do(func() { // Fetch new tables from system. binds, err := table.fetchTable() if err != nil { log.Warningf("state: failed to get UDP%d socket table: %s", table.version, err) return } // Pre-check for any listeners. for _, bindInfo := range binds { bindInfo.ListensAny = bindInfo.Local.IP.Equal(net.IPv4zero) || bindInfo.Local.IP.Equal(net.IPv6zero) } // Apply new tables. table.lock.Lock() defer table.lock.Unlock() table.binds = binds table.lastUpdateAt.Store(time.Now().UnixNano()) }) return table.getCurrentTables() } // CleanUDPStates cleans the udp connection states which save connection directions. func CleanUDPStates(_ context.Context) { now := time.Now().UTC() udp4Table.updateTables() udp4Table.cleanStates(now) if netenv.IPv6Enabled() { udp6Table.updateTables() udp6Table.cleanStates(now) } } func (table *udpTable) getConnState( socketInfo *socket.BindInfo, remoteAddress socket.Address, ) (udpConnState *udpState, ok bool) { table.statesLock.Lock() defer table.statesLock.Unlock() bindMap, ok := table.states[makeUDPStateKey(socketInfo.Local)] if ok { udpConnState, ok = bindMap[makeUDPStateKey(remoteAddress)] return } return nil, false } func (table *udpTable) getDirection( socketInfo *socket.BindInfo, pktInfo *packet.Info, ) (connDirection bool) { table.statesLock.Lock() defer table.statesLock.Unlock() localKey := makeUDPStateKey(socketInfo.Local) bindMap, ok := table.states[localKey] if !ok { bindMap = make(map[string]*udpState) table.states[localKey] = bindMap } remoteKey := makeUDPStateKey(socket.Address{ IP: pktInfo.RemoteIP(), Port: pktInfo.RemotePort(), }) udpConnState, ok := bindMap[remoteKey] if !ok { bindMap[remoteKey] = &udpState{ inbound: pktInfo.Inbound, lastSeen: time.Now().UTC(), } return pktInfo.Inbound } udpConnState.lastSeen = time.Now().UTC() return udpConnState.inbound } func (table *udpTable) cleanStates(now time.Time) { // compute thresholds threshold := now.Add(-UDPConnStateTTL) shortThreshhold := now.Add(-UDPConnStateShortenedTTL) // make lookup map of all active keys bindKeys := make(map[string]struct{}) table.lock.RLock() for _, socketInfo := range table.binds { bindKeys[makeUDPStateKey(socketInfo.Local)] = struct{}{} } table.lock.RUnlock() table.statesLock.Lock() defer table.statesLock.Unlock() // clean the udp state storage for localKey, bindMap := range table.states { if _, active := bindKeys[localKey]; active { // clean old entries for remoteKey, udpConnState := range bindMap { if udpConnState.lastSeen.Before(threshold) { delete(bindMap, remoteKey) } } // if there are too many clean more aggressively if len(bindMap) > AggressiveCleaningThreshold { for remoteKey, udpConnState := range bindMap { if udpConnState.lastSeen.Before(shortThreshhold) { delete(bindMap, remoteKey) } } } } else { // delete the whole thing delete(table.states, localKey) } } } func makeUDPStateKey(address socket.Address) string { // This could potentially go wrong, but as all IPs are created by the same source, everything should be fine. return string(address.IP) + strconv.Itoa(int(address.Port)) } ================================================ FILE: service/network/status.go ================================================ package network // Verdict describes the decision made about a connection or link. type Verdict int8 // All possible verdicts that can be applied to a network // connection. const ( // VerdictUndecided is the default status of new connections. VerdictUndecided Verdict = 0 VerdictUndeterminable Verdict = 1 VerdictAccept Verdict = 2 VerdictBlock Verdict = 3 VerdictDrop Verdict = 4 VerdictRerouteToNameserver Verdict = 5 VerdictRerouteToTunnel Verdict = 6 VerdictFailed Verdict = 7 ) func (v Verdict) String() string { switch v { case VerdictUndecided: return "" case VerdictUndeterminable: return "" case VerdictAccept: return "Accept" case VerdictBlock: return "Block" case VerdictDrop: return "Drop" case VerdictRerouteToNameserver: return "RerouteToNameserver" case VerdictRerouteToTunnel: return "RerouteToTunnel" case VerdictFailed: return "Failed" default: return "" } } // Verb returns the verdict as a past tense verb. func (v Verdict) Verb() string { switch v { case VerdictUndecided: return "undecided" case VerdictUndeterminable: return "undeterminable" case VerdictAccept: return "accepted" case VerdictBlock: return "blocked" case VerdictDrop: return "dropped" case VerdictRerouteToNameserver: return "redirected to nameserver" case VerdictRerouteToTunnel: return "tunneled" case VerdictFailed: return "failed" default: return "invalid" } } // Packet Directions. const ( Inbound = true Outbound = false ) // Non-Domain Scopes. const ( IncomingHost = "IH" IncomingLAN = "IL" IncomingInternet = "II" IncomingInvalid = "IX" PeerHost = "PH" PeerLAN = "PL" PeerInternet = "PI" PeerInvalid = "PX" ) ================================================ FILE: service/process/api.go ================================================ package process import ( "errors" "net/http" "strconv" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/service/profile" ) func registerAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Name: "Get Process Tag Metadata", Description: "Get information about process tags.", Path: "process/tags", Read: api.PermitUser, StructFunc: handleProcessTagMetadata, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Get Processes by Profile", Description: "Get all recently active processes using the given profile", Path: "process/list/by-profile/{source:[a-z]+}/{id:[A-z0-9-]+}", Read: api.PermitUser, StructFunc: handleGetProcessesByProfile, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Get Process Group Leader By PID", Description: "Load a process group leader by a child PID", Path: "process/group-leader/{pid:[0-9]+}", Read: api.PermitUser, StructFunc: handleGetProcessGroupLeader, }); err != nil { return err } return nil } func handleProcessTagMetadata(ar *api.Request) (i interface{}, err error) { tagRegistryLock.Lock() defer tagRegistryLock.Unlock() // Create response struct. resp := struct { Tags []TagDescription }{ Tags: make([]TagDescription, 0, len(tagRegistry)*2), } // Get all tag descriptions. for _, th := range tagRegistry { resp.Tags = append(resp.Tags, th.TagDescriptions()...) } return resp, nil } func handleGetProcessesByProfile(ar *api.Request) (any, error) { source := ar.URLVars["source"] id := ar.URLVars["id"] if id == "" || source == "" { return nil, api.ErrorWithStatus(errors.New("missing profile source/id"), http.StatusBadRequest) } result := GetProcessesWithProfile(ar.Context(), profile.ProfileSource(source), id, true) return result, nil } func handleGetProcessGroupLeader(ar *api.Request) (any, error) { pid, err := strconv.ParseInt(ar.URLVars["pid"], 10, 0) if err != nil { return nil, api.ErrorWithStatus(err, http.StatusBadRequest) } process, err := GetOrFindProcess(ar.Context(), int(pid)) if err != nil { return nil, api.ErrorWithStatus(err, http.StatusInternalServerError) } err = process.FindProcessGroupLeader(ar.Context()) switch { case process.Leader() != nil: return process.Leader(), nil case err != nil: return nil, api.ErrorWithStatus(err, http.StatusInternalServerError) default: return nil, api.ErrorWithStatus(errors.New("leader not found"), http.StatusNotFound) } } ================================================ FILE: service/process/config.go ================================================ package process import ( "github.com/safing/portmaster/base/config" ) // Configuration Keys. var ( CfgOptionEnableProcessDetectionKey = "core/enableProcessDetection" enableProcessDetection config.BoolOption ) func registerConfiguration() error { // Enable Process Detection // This should be always enabled. Provided as an option to disable in case there are severe problems on a system, or for debugging. err := config.Register(&config.Option{ Name: "Process Detection", Key: CfgOptionEnableProcessDetectionKey, Description: "This option enables the attribution of network traffic to processes. Without it, app settings are effectively disabled.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelDeveloper, DefaultValue: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: 528, config.CategoryAnnotation: "Development", }, }) if err != nil { return err } enableProcessDetection = config.Concurrent.GetAsBool(CfgOptionEnableProcessDetectionKey, true) return nil } ================================================ FILE: service/process/database.go ================================================ package process import ( "context" "fmt" "slices" "strings" "sync" "time" processInfo "github.com/shirou/gopsutil/process" "github.com/tevino/abool" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/profile" ) const processDatabaseNamespace = "network:tree" var ( processes = make(map[string]*Process) processesLock sync.RWMutex dbController *database.Controller dbControllerFlag = abool.NewBool(false) deleteProcessesThreshold = 7 * time.Minute ) // GetProcessFromStorage returns a process from the internal storage. func GetProcessFromStorage(key string) (*Process, bool) { processesLock.RLock() defer processesLock.RUnlock() p, ok := processes[key] return p, ok } // All returns a copy of all process objects. func All() map[int]*Process { processesLock.RLock() defer processesLock.RUnlock() all := make(map[int]*Process) for _, proc := range processes { all[proc.Pid] = proc } return all } // GetProcessesWithProfile returns all processes that use the given profile. // If preferProcessGroupLeader is set, it returns the process group leader instead, if available. func GetProcessesWithProfile(ctx context.Context, profileSource profile.ProfileSource, profileID string, preferProcessGroupLeader bool) []*Process { log.Tracer(ctx).Debugf("process: searching for processes belonging to %s", profile.MakeScopedID(profileSource, profileID)) // Get all processes that match the given profile. procs := make([]*Process, 0, 8) for _, p := range All() { lp := p.profile.LocalProfile() if lp != nil && lp.Source == profileSource && lp.ID == profileID { if preferProcessGroupLeader && p.Leader() != nil { procs = append(procs, p.Leader()) } else { procs = append(procs, p) } } } // Sort and compact. slices.SortFunc[[]*Process, *Process](procs, func(a, b *Process) int { return strings.Compare(a.processKey, b.processKey) }) procs = slices.CompactFunc[[]*Process, *Process](procs, func(a, b *Process) bool { return a.processKey == b.processKey }) return procs } // Save saves the process to the internal state and pushes an update. func (p *Process) Save() { p.Lock() defer p.Unlock() p.UpdateMeta() if p.processKey == "" { p.processKey = getProcessKey(int32(p.Pid), p.CreatedAt) } if !p.KeyIsSet() { // set key p.SetKey(fmt.Sprintf("%s/%s", processDatabaseNamespace, p.processKey)) // save processesLock.Lock() processes[p.processKey] = p processesLock.Unlock() } if dbControllerFlag.IsSet() { dbController.PushUpdate(p) } } // Delete deletes a process from the storage and propagates the change. func (p *Process) Delete() { p.Lock() defer p.Unlock() // delete from internal storage processesLock.Lock() delete(processes, p.processKey) processesLock.Unlock() // propagate delete p.Meta().Delete() if dbControllerFlag.IsSet() { dbController.PushUpdate(p) } // TODO: maybe mark the assigned profiles as no longer needed? } // CleanProcessStorage cleans the storage from old processes. func CleanProcessStorage(activePIDs map[int]struct{}) { // add system table of processes pids, err := processInfo.Pids() if err != nil { log.Warningf("process: failed to get list of active PIDs: %s", err) } else { for _, pid := range pids { activePIDs[int(pid)] = struct{}{} } } processesCopy := All() threshold := time.Now().Add(-deleteProcessesThreshold).Unix() // clean primary processes for _, p := range processesCopy { // The PID of a process does not change. // Check if this is a special process. switch p.Pid { case UnidentifiedProcessID, UnsolicitedProcessID, SystemProcessID: p.profile.MarkStillActive() continue } // Check if process is active. _, active := activePIDs[p.Pid] if active { p.profile.MarkStillActive() continue } // Process is inactive, start deletion process lastSeen := p.GetLastSeen() switch { case lastSeen == 0: // add last seen timestamp p.SetLastSeen(time.Now().Unix()) case lastSeen > threshold: // within keep period default: // delete now p.Delete() log.Tracef("process: cleaned %s", p.DatabaseKey()) } } } // SetDBController sets the database controller and allows the package to push database updates on a save. It must be set by the package that registers the "network" database. func SetDBController(controller *database.Controller) { dbController = controller dbControllerFlag.Set() } ================================================ FILE: service/process/doc.go ================================================ // Package process fetches process and socket information from the operating system. // It can find the process owning a network connection. package process ================================================ FILE: service/process/executable.go ================================================ package process import ( "crypto" "encoding/hex" "hash" "io" "os" ) // GetExecHash returns the hash of the executable with the given algorithm. func (p *Process) GetExecHash(algorithm string) (string, error) { sum, ok := p.ExecHashes[algorithm] if ok { return sum, nil } var hasher hash.Hash switch algorithm { case "md5": hasher = crypto.MD5.New() case "sha1": hasher = crypto.SHA1.New() case "sha256": hasher = crypto.SHA256.New() } file, err := os.Open(p.Path) if err != nil { return "", err } defer func() { _ = file.Close() }() _, err = io.Copy(hasher, file) if err != nil { return "", err } sum = hex.EncodeToString(hasher.Sum(nil)) p.ExecHashes[algorithm] = sum return sum, nil } ================================================ FILE: service/process/find.go ================================================ package process import ( "context" "fmt" "net" "time" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/service/network/reference" "github.com/safing/portmaster/service/network/state" "github.com/safing/portmaster/service/profile" ) // GetProcessWithProfile returns the process, including the profile. // Always returns valid data. // Errors are logged and returned for information or special handling purposes. func GetProcessWithProfile(ctx context.Context, pid int) (process *Process, err error) { if !enableProcessDetection() { log.Tracer(ctx).Tracef("process: process detection disabled") return GetUnidentifiedProcess(ctx), nil } process, err = GetOrFindProcess(ctx, pid) if err != nil { log.Tracer(ctx).Debugf("process: failed to find process with PID: %s", err) return GetUnidentifiedProcess(ctx), err } // Get process group leader, which is the process "nearest" to the user and // will have more/better information for finding names ans icons, for example. err = process.FindProcessGroupLeader(ctx) if err != nil { log.Warningf("process: failed to get process group leader for %s: %s", process, err) } changed, err := process.GetProfile(ctx) if err != nil { log.Tracer(ctx).Errorf("process: failed to get profile for process %s: %s", process, err) } if changed { process.Save() } return process, nil } // GetPidOfConnection returns the PID of the process that owns the described connection. // Always returns valid data. // Errors are logged and returned for information or special handling purposes. func GetPidOfConnection(ctx context.Context, pktInfo *packet.Info) (pid int, connInbound bool, err error) { if !enableProcessDetection() { return UnidentifiedProcessID, pktInfo.Inbound, nil } // Use fast search for inbound packets, as the listening socket should // already be there for a while now. fastSearch := pktInfo.Inbound connInbound = pktInfo.Inbound // Check if we need to get the PID. if pktInfo.PID == UndefinedProcessID { log.Tracer(ctx).Tracef("process: getting pid from system network state") pid, connInbound, err = state.Lookup(pktInfo, fastSearch) if err != nil { err = fmt.Errorf("failed to find PID of connection: %w", err) log.Tracer(ctx).Tracef("process: %s", err) pid = UndefinedProcessID } } else { log.Tracer(ctx).Tracef("process: pid already set in packet (by ebpf or kext)") pid = pktInfo.PID } // Fallback to special profiles if PID could not be found. if pid == UndefinedProcessID { switch { case !connInbound: pid = UnidentifiedProcessID case netutils.ClassifyIP(pktInfo.Dst).IsLocalhost(): // Always treat localhost connections as unidentified/unknown. pid = UnidentifiedProcessID case reference.IsICMP(uint8(pktInfo.Protocol)): // Always treat ICMP as unidentified/unknown, as the direction // might change to outgoing by new ICMP echo packets. pid = UnidentifiedProcessID default: // All other inbound connections are "unsolicited". pid = UnsolicitedProcessID } } return pid, connInbound, err } // GetNetworkHost returns a *Process that represents a host on the network. func GetNetworkHost(ctx context.Context, remoteIP net.IP) (process *Process, err error) { //nolint:interfacer now := time.Now().Unix() networkHost := &Process{ Name: fmt.Sprintf("Device at %s", remoteIP), UserName: "N/A", UserID: NetworkHostProcessID, Pid: NetworkHostProcessID, ParentPid: NetworkHostProcessID, Tags: []profile.Tag{ { Key: "ip", Value: remoteIP.String(), }, }, FirstSeen: now, LastSeen: now, } // Get the (linked) local profile. networkHostProfile, err := profile.GetLocalProfile("", networkHost.MatchingData(), networkHost.CreateProfileCallback) if err != nil { return nil, err } // Assign profile to process. networkHost.PrimaryProfileID = networkHostProfile.ScopedID() networkHost.profile = networkHostProfile.LayeredProfile() return networkHost, nil } // GetProcessByRequestOrigin returns the process that initiated the API request ar. func GetProcessByRequestOrigin(ar *api.Request) (*Process, error) { // get remote IP/Port remoteIP, remotePort, err := netutils.ParseIPPort(ar.RemoteAddr) if err != nil { return nil, fmt.Errorf("failed to get remote IP/Port: %w", err) } pkt := &packet.Info{ Inbound: false, // outbound as we are looking for the process of the source address Version: packet.IPv4, Protocol: packet.TCP, Src: remoteIP, // source as in the process we are looking for SrcPort: remotePort, // source as in the process we are looking for PID: UndefinedProcessID, } pid, _, err := GetPidOfConnection(ar.Context(), pkt) if err != nil { return nil, err } proc, err := GetProcessWithProfile(ar.Context(), pid) if err != nil { return nil, err } return proc, nil } ================================================ FILE: service/process/module.go ================================================ package process import ( "errors" "runtime" "sync/atomic" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/updates" ) type ProcessModule struct { mgr *mgr.Manager instance instance portmasterUIPath string } func (pm *ProcessModule) Manager() *mgr.Manager { return pm.mgr } func (pm *ProcessModule) Start() error { identifier := "portmaster" if runtime.GOOS == "windows" { identifier += ".exe" } file, err := pm.instance.BinaryUpdates().GetFile(identifier) if err != nil { log.Errorf("process: failed to get path of ui: %s", err) } else { pm.portmasterUIPath = file.Path() } return nil } func (pm *ProcessModule) Stop() error { return nil } func prep() error { if err := registerConfiguration(); err != nil { return err } if err := registerAPIEndpoints(); err != nil { return err } return nil } var ( module *ProcessModule shimLoaded atomic.Bool ) // New returns a new Process module. func New(instance instance) (*ProcessModule, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("ProcessModule") module = &ProcessModule{ mgr: m, instance: instance, } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface { BinaryUpdates() *updates.Updater } ================================================ FILE: service/process/process.go ================================================ package process import ( "context" "errors" "fmt" "path/filepath" "runtime" "strings" "sync" "time" processInfo "github.com/shirou/gopsutil/process" "golang.org/x/sync/singleflight" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/profile" ) const onLinux = runtime.GOOS == "linux" var getProcessSingleInflight singleflight.Group // A Process represents a process running on the operating system. type Process struct { record.Base sync.Mutex // Process attributes. // Don't change; safe for concurrent access. Name string UserID int UserName string UserHome string Pid int CreatedAt int64 ParentPid int ParentCreatedAt int64 LeaderPid int leader *Process Path string ExecName string Cwd string CmdLine string FirstArg string Env map[string]string // unique process identifier ("Pid-CreatedAt") processKey string // Profile attributes. // Once set, these don't change; safe for concurrent access. // Tags holds extended information about the (virtual) process, which is used // to find a profile. Tags []profile.Tag // MatchingPath holds an alternative binary path that can be used to find a // profile. MatchingPath string // PrimaryProfileID holds the scoped ID of the primary profile. PrimaryProfileID string // profile holds the layered profile based on the primary profile. profile *profile.LayeredProfile // Mutable attributes. FirstSeen int64 LastSeen int64 Error string // Cache errors ExecHashes map[string]string } // GetTag returns the process tag with the given ID. func (p *Process) GetTag(tagID string) (profile.Tag, bool) { for _, t := range p.Tags { if t.Key == tagID { return t, true } } return profile.Tag{}, false } // Profile returns the assigned layered profile. func (p *Process) Profile() *profile.LayeredProfile { if p == nil { return nil } return p.profile } // Leader returns the process group leader that is attached to the process. // This will not trigger a new search for the process group leader, it only // returns existing data. func (p *Process) Leader() *Process { p.Lock() defer p.Unlock() return p.leader } // IsIdentified returns whether the process has been identified or if it // represents some kind of unidentified process. func (p *Process) IsIdentified() bool { // Check if process exists. if p == nil { return false } // Check for special PIDs. switch p.Pid { case UndefinedProcessID: return false case UnidentifiedProcessID: return false case UnsolicitedProcessID: return false default: return true } } // HasValidPID returns whether the process has valid PID of an actual process. func (p *Process) HasValidPID() bool { // Check if process exists. if p == nil { return false } return p.Pid >= 0 } // Equal returns if the two processes are both identified and have the same PID. func (p *Process) Equal(other *Process) bool { return p.IsIdentified() && other.IsIdentified() && p.Pid == other.Pid } const systemResolverScopedID = string(profile.SourceLocal) + "/" + profile.SystemResolverProfileID // IsSystemResolver is a shortcut to check if the process is or belongs to the // system resolver and needs special handling. func (p *Process) IsSystemResolver() bool { // Check if process exists. if p == nil { return false } // Check ID. return p.PrimaryProfileID == systemResolverScopedID } // GetLastSeen returns the unix timestamp when the process was last seen. func (p *Process) GetLastSeen() int64 { p.Lock() defer p.Unlock() return p.LastSeen } // SetLastSeen sets the unix timestamp when the process was last seen. func (p *Process) SetLastSeen(lastSeen int64) { p.Lock() defer p.Unlock() p.LastSeen = lastSeen } // String returns a string representation of process. func (p *Process) String() string { if p == nil { return "?" } return fmt.Sprintf("%s:%s:%d", p.UserName, p.Path, p.Pid) } // GetOrFindProcess returns the process for the given PID. func GetOrFindProcess(ctx context.Context, pid int) (*Process, error) { log.Tracer(ctx).Tracef("process: getting process for PID %d", pid) // Check for special processes switch pid { case UnidentifiedProcessID: return GetUnidentifiedProcess(ctx), nil case UnsolicitedProcessID: return GetUnsolicitedProcess(ctx), nil case SystemProcessID: return GetSystemProcess(ctx), nil } // Get pid and creation time for identification. pInfo, err := processInfo.NewProcessWithContext(ctx, int32(pid)) if err != nil { return nil, err } createdAt, err := pInfo.CreateTimeWithContext(ctx) if err != nil { return nil, err } key := getProcessKey(int32(pid), createdAt) // Load process and make sure it is only loaded once. p, err, _ := getProcessSingleInflight.Do(key, func() (interface{}, error) { return loadProcess(ctx, key, pInfo) }) if err != nil { return nil, err } if p == nil { return nil, errors.New("process getter returned nil") } return p.(*Process), nil // nolint:forcetypeassert // Can only be a *Process. } func loadProcess(ctx context.Context, key string, pInfo *processInfo.Process) (*Process, error) { // Check if we already have the process. process, ok := GetProcessFromStorage(key) if ok { return process, nil } // Create new a process object. process = &Process{ Pid: int(pInfo.Pid), FirstSeen: time.Now().Unix(), processKey: key, } // Get creation time of process. (The value should be cached by the library.) var err error process.CreatedAt, err = pInfo.CreateTimeWithContext(ctx) if err != nil { return nil, err } // UID // TODO: implemented for windows if onLinux { var uids []int32 uids, err = pInfo.UidsWithContext(ctx) if err != nil { return nil, fmt.Errorf("failed to get UID for p%d: %w", pInfo.Pid, err) } process.UserID = int(uids[0]) } // Username process.UserName, err = pInfo.UsernameWithContext(ctx) if err != nil { log.Tracer(ctx).Warningf("process: failed to get username (PID %d): %s", pInfo.Pid, err) } // TODO: User Home // new.UserHome, err = // Parent process ID ppid, err := pInfo.PpidWithContext(ctx) if err != nil { return nil, fmt.Errorf("failed to get PPID for p%d: %w", pInfo.Pid, err) } process.ParentPid = int(ppid) // Parent created time parentPInfo, err := processInfo.NewProcessWithContext(ctx, ppid) if err == nil { parentCreatedAt, err := parentPInfo.CreateTimeWithContext(ctx) if err != nil { return nil, err } process.ParentCreatedAt = parentCreatedAt } // Leader process ID // Get process group ID to find group leader, which is the process "nearest" // to the user and will have more/better information for finding names and // icons, for example. leaderPid, err := GetProcessGroupID(ctx, process.Pid) if err != nil { // Fail gracefully. log.Warningf("process: failed to get process group ID for p%d: %s", process.Pid, err) process.LeaderPid = UndefinedProcessID } else { process.LeaderPid = leaderPid } // Path process.Path, err = pInfo.ExeWithContext(ctx) if err != nil { return nil, fmt.Errorf("failed to get Path for p%d: %w", pInfo.Pid, err) } // remove linux " (deleted)" suffix for deleted files if onLinux { process.Path = strings.TrimSuffix(process.Path, " (deleted)") } // Executable Name _, process.ExecName = filepath.Split(process.Path) // Current working directory // not yet implemented for windows if runtime.GOOS != "windows" { process.Cwd, err = pInfo.CwdWithContext(ctx) if err != nil { log.Warningf("process: failed to get current working dir (PID %d): %s", pInfo.Pid, err) } } // Command line arguments process.CmdLine, err = pInfo.CmdlineWithContext(ctx) if err != nil { log.Tracer(ctx).Warningf("process: failed to get cmdline (PID %d): %s", pInfo.Pid, err) } // Name process.Name, err = pInfo.NameWithContext(ctx) if err != nil { log.Tracer(ctx).Warningf("process: failed to get process name (PID %d): %s", pInfo.Pid, err) } if process.Name == "" { process.Name = process.ExecName } // Get all environment variables env, err := pInfo.EnvironWithContext(ctx) if err == nil { // Split env variables in key and value. process.Env = make(map[string]string, len(env)) for _, entry := range env { splitted := strings.SplitN(entry, "=", 2) if len(splitted) == 2 { process.Env[strings.Trim(splitted[0], `'"`)] = strings.Trim(splitted[1], `'"`) } } } else { log.Tracer(ctx).Warningf("process: failed to get the process environment (PID %d): %s", pInfo.Pid, err) } // Add process tags. process.addTags() if len(process.Tags) > 0 { log.Tracer(ctx).Debugf("profile: added tags: %+v", process.Tags) } process.Save() return process, nil } // GetKey returns the key that is used internally to identify the process. // The key consists of the PID and the start time of the process as reported by // the system. func (p *Process) GetKey() string { return p.processKey } // Builds a unique identifier for a processes. func getProcessKey(pid int32, createdTime int64) string { return fmt.Sprintf("%d-%d", pid, createdTime) } // MatchingData returns the matching data for the process. func (p *Process) MatchingData() *MatchingData { return &MatchingData{p} } // MatchingData provides a interface compatible view on the process for profile matching. type MatchingData struct { p *Process } // Tags returns process.Tags. func (md *MatchingData) Tags() []profile.Tag { return md.p.Tags } // Env returns process.Env. func (md *MatchingData) Env() map[string]string { return md.p.Env } // Path returns process.Path. func (md *MatchingData) Path() string { return md.p.Path } // MatchingPath returns process.MatchingPath. func (md *MatchingData) MatchingPath() string { return md.p.MatchingPath } // Cmdline returns the command line of the process. func (md *MatchingData) Cmdline() string { return md.p.CmdLine } ================================================ FILE: service/process/process_default.go ================================================ //go:build !windows && !linux // +build !windows,!linux package process import ( "context" ) // SystemProcessID is the PID of the System/Kernel itself. const SystemProcessID = 0 // GetProcessGroupLeader returns the process that leads the process group. // Returns nil on unsupported platforms. func (p *Process) FindProcessGroupLeader(ctx context.Context) error { return nil } // GetProcessGroupID returns the process group ID of the given PID. // Returns undefined process ID on unsupported platforms. func GetProcessGroupID(ctx context.Context, pid int) (int, error) { return UndefinedProcessID, nil } ================================================ FILE: service/process/process_linux.go ================================================ package process import ( "context" "fmt" "syscall" "github.com/safing/portmaster/base/log" ) const ( // SystemProcessID is the PID of the System/Kernel itself. SystemProcessID = 0 // SystemInitID is the PID of the system init process. SystemInitID = 1 ) // FindProcessGroupLeader returns the process that leads the process group. // Returns nil when process ID is not valid (or virtual). // If the process group leader is found, it is set on the process. // If that process does not exist anymore, then the highest existing parent process is returned. // If an error occurs, the best match is set. func (p *Process) FindProcessGroupLeader(ctx context.Context) error { p.Lock() defer p.Unlock() // Return the leader if we already have it. if p.leader != nil { return nil } // Check if we have the process group leader PID. if p.LeaderPid == UndefinedProcessID { return nil } // Return nil if we already are the leader. if p.LeaderPid == p.Pid { return nil } // Get process leader process. leader, err := GetOrFindProcess(ctx, p.LeaderPid) if err == nil { p.leader = leader log.Tracer(ctx).Debugf("process: found process leader of %d: pid=%d pgid=%d", p.Pid, leader.Pid, leader.LeaderPid) return nil } // If we can't get the process leader process, it has likely already exited. // In that case, find the highest existing parent process within the process group. var ( nextParentPid = p.ParentPid lastParent *Process ) for { // Get next parent. parent, err := GetOrFindProcess(ctx, nextParentPid) if err != nil { p.leader = lastParent return fmt.Errorf("failed to find parent %d: %w", nextParentPid, err) } // Check if we are ready to return. switch { case parent.Pid == p.LeaderPid: // Found the process group leader! p.leader = parent return nil case parent.LeaderPid != p.LeaderPid: // We are leaving the process group. Return the previous parent. p.leader = lastParent log.Tracer(ctx).Debugf("process: found process leader (highest parent) of %d: pid=%d pgid=%d", p.Pid, parent.Pid, parent.LeaderPid) return nil case parent.ParentPid == SystemProcessID, parent.ParentPid == SystemInitID: // Next parent is system or init. // Use current parent. p.leader = parent log.Tracer(ctx).Debugf("process: found process leader (highest parent) of %d: pid=%d pgid=%d", p.Pid, parent.Pid, parent.LeaderPid) return nil } // Check next parent. lastParent = parent nextParentPid = parent.ParentPid } } // GetProcessGroupID returns the process group ID of the given PID. func GetProcessGroupID(ctx context.Context, pid int) (int, error) { return syscall.Getpgid(pid) } ================================================ FILE: service/process/process_windows.go ================================================ package process import ( "context" ) // SystemProcessID is the PID of the System/Kernel itself. const SystemProcessID = 4 // GetProcessGroupLeader returns the process that leads the process group. // Returns nil on Windows, as it does not have process groups. func (p *Process) FindProcessGroupLeader(ctx context.Context) error { // TODO: Get "main" process of process job object. return nil } // GetProcessGroupID returns the process group ID of the given PID. // Returns the undefined process ID on Windows, as it does not have process groups. func GetProcessGroupID(ctx context.Context, pid int) (int, error) { return UndefinedProcessID, nil } ================================================ FILE: service/process/profile.go ================================================ package process import ( "context" "fmt" "os" "path/filepath" "runtime" "strings" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/profile" ) var ownPID = os.Getpid() // GetProfile finds and assigns a profile set to the process. func (p *Process) GetProfile(ctx context.Context) (changed bool, err error) { p.Lock() defer p.Unlock() // Check if profile is already loaded. if p.profile != nil { log.Tracer(ctx).Trace("process: profile already loaded") return } // If not, continue with loading the profile. log.Tracer(ctx).Trace("process: loading profile") // Get special or regular profile. localProfile, err := profile.GetLocalProfile(p.getSpecialProfileID(), p.MatchingData(), p.CreateProfileCallback) if err != nil { return false, fmt.Errorf("failed to find profile: %w", err) } // Assign profile to process. p.PrimaryProfileID = localProfile.ScopedID() p.profile = localProfile.LayeredProfile() return true, nil } // RefetchProfile removes the profile and finds and assigns a new profile. func (p *Process) RefetchProfile(ctx context.Context) error { p.Lock() defer p.Unlock() // Get special or regular profile. localProfile, err := profile.GetLocalProfile(p.getSpecialProfileID(), p.MatchingData(), p.CreateProfileCallback) if err != nil { return fmt.Errorf("failed to find profile: %w", err) } // Assign profile to process. p.PrimaryProfileID = localProfile.ScopedID() p.profile = localProfile.LayeredProfile() return nil } // getSpecialProfileID returns the special profile ID for the process, if any. func (p *Process) getSpecialProfileID() (specialProfileID string) { // Check if we need a special profile. switch p.Pid { case UnidentifiedProcessID: specialProfileID = profile.UnidentifiedProfileID case UnsolicitedProcessID: specialProfileID = profile.UnsolicitedProfileID case SystemProcessID: specialProfileID = profile.SystemProfileID case ownPID: specialProfileID = profile.PortmasterProfileID default: // Check if this is another Portmaster component. if p.IsPortmasterUi(context.Background()) { specialProfileID = profile.PortmasterAppProfileID } // Check if this is the system resolver. switch runtime.GOOS { case "windows": // Depending on the OS version System32 may be capitalized or not. if (p.Path == `C:\Windows\System32\svchost.exe` || p.Path == `C:\Windows\system32\svchost.exe`) && // This comes from the windows tasklist command and should be pretty consistent. (profile.KeyAndValueInTags(p.Tags, "svchost", "Dnscache") || // As an alternative in case of failure, we try to match the svchost.exe service parameter. strings.Contains(p.CmdLine, "-s Dnscache")) { specialProfileID = profile.SystemResolverProfileID } case "linux": switch p.Path { case "/lib/systemd/systemd-resolved", "/usr/lib/systemd/systemd-resolved", "/lib64/systemd/systemd-resolved", "/usr/lib64/systemd/systemd-resolved", "/usr/bin/nscd", "/usr/sbin/nscd", "/usr/bin/dnsmasq", "/usr/sbin/dnsmasq": specialProfileID = profile.SystemResolverProfileID } } } return specialProfileID } // IsPortmasterUi checks if the process is the Portmaster UI or its child (up to 3 parent levels). func (p *Process) IsPortmasterUi(ctx context.Context) bool { if module.portmasterUIPath == "" { return false } // Find parent for up to two levels, if we don't match the path. const checkLevels = 3 var previousPid int proc := p hasPmWebviewEnvVar := false for i := 0; i < checkLevels; i++ { if proc.Pid == UnidentifiedProcessID || proc.Pid == SystemProcessID { break } realPath, err := filepath.EvalSymlinks(proc.Path) if err == nil && realPath == module.portmasterUIPath { if runtime.GOOS != "windows" { return true } // On Windows, avoid false positive detection of the Portmaster UI. // For example: // There may be cases where a system browser is launched from the Portmaster UI, // making it a child of the Portmaster UI process (e.g., user clicked a link in the UI). // In this case, the parent process tree may look like this: // Portmaster.exe // ├─ WebView (PM UI) // │ └─ WebView (PM UI child) // └─ System Web Browser ... // // To ensure that 'p' is the actual Portmaster UI process, we check for the presence // of the 'PORTMASTER_UI_WEBVIEW_PROCESS' environment variable in the process and its parents. // If the env var is set, we are a child (WebView window) of the Portmaster UI process. // Otherwise, the process was launched by the Portmaster UI, but should not be trusted as the Portmaster UI process. if i == 0 { return true // We are the main Portmaster UI process. } if hasPmWebviewEnvVar { return true // We are a WebView window of the Portmaster UI process. } // The process was launched by the Portmaster UI, but should not be trusted as the Portmaster UI process. log.Tracer(ctx).Warningf("process: %d %q is a child of the Portmaster UI, but does not have the PORTMASTER_UI_WEBVIEW_PROCESS environment variable set. Ignoring.", p.Pid, p.Path) return false } // Check if the process has the environment variable set. // // It is OK to check for the existence of the environment variable in all // processes in the parent chain (on all loop iterations). This increases the // chance of correct detection, even if a child or grandchild WebView process // did not inherit the environment variable for some reason. if _, ok := proc.Env["PORTMASTER_UI_WEBVIEW_PROCESS"]; ok { hasPmWebviewEnvVar = true } if i < checkLevels-1 { // no need to check parent if we are at the last level previousPid = proc.Pid proc, err = GetOrFindProcess(ctx, proc.ParentPid) if err != nil || proc.Pid == previousPid { break } } } return false } ================================================ FILE: service/process/special.go ================================================ package process import ( "context" "time" "golang.org/x/sync/singleflight" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/socket" "github.com/safing/portmaster/service/profile" ) const ( // UndefinedProcessID is not used by any (virtual) process and signifies that // the PID is unset. UndefinedProcessID = -1 // UnidentifiedProcessID is the PID used for outgoing connections that could // not be attributed to a PID for any reason. UnidentifiedProcessID = -2 // UnsolicitedProcessID is the PID used for incoming connections that could // not be attributed to a PID for any reason. UnsolicitedProcessID = -3 // NetworkHostProcessID is the PID used for requests served to the network. NetworkHostProcessID = -255 ) func init() { // Check required matching values. if UndefinedProcessID != socket.UndefinedProcessID { panic("UndefinedProcessID does not match socket.UndefinedProcessID") } } var ( // unidentifiedProcess is used for non-attributed outgoing connections. unidentifiedProcess = &Process{ UserID: UnidentifiedProcessID, UserName: "Unknown", Pid: UnidentifiedProcessID, ParentPid: UnidentifiedProcessID, Name: profile.UnidentifiedProfileName, processKey: getProcessKey(UnidentifiedProcessID, 0), } // unsolicitedProcess is used for non-attributed incoming connections. unsolicitedProcess = &Process{ UserID: UnsolicitedProcessID, UserName: "Unknown", Pid: UnsolicitedProcessID, ParentPid: UnsolicitedProcessID, Name: profile.UnsolicitedProfileName, processKey: getProcessKey(UnsolicitedProcessID, 0), } // systemProcess is used to represent the Kernel. systemProcess = &Process{ UserID: SystemProcessID, UserName: "Kernel", Pid: SystemProcessID, ParentPid: SystemProcessID, Name: profile.SystemProfileName, processKey: getProcessKey(SystemProcessID, 0), } getSpecialProcessSingleInflight singleflight.Group ) // GetUnidentifiedProcess returns the special process assigned to non-attributed outgoing connections. func GetUnidentifiedProcess(ctx context.Context) *Process { return getSpecialProcess(ctx, unidentifiedProcess) } // GetUnsolicitedProcess returns the special process assigned to non-attributed incoming connections. func GetUnsolicitedProcess(ctx context.Context) *Process { return getSpecialProcess(ctx, unsolicitedProcess) } // GetSystemProcess returns the special process used for the Kernel. func GetSystemProcess(ctx context.Context) *Process { return getSpecialProcess(ctx, systemProcess) } func getSpecialProcess(ctx context.Context, template *Process) *Process { p, _, _ := getSpecialProcessSingleInflight.Do(template.processKey, func() (interface{}, error) { // Check if we have already loaded the special process. process, ok := GetProcessFromStorage(template.processKey) if ok { return process, nil } // Create new process from template process = template process.FirstSeen = time.Now().Unix() // Get profile. _, err := process.GetProfile(ctx) if err != nil { log.Tracer(ctx).Errorf("process: failed to get profile for process %s: %s", process, err) } // Save process to storage. process.Save() return process, nil }) return p.(*Process) // nolint:forcetypeassert // Can only be a *Process. } ================================================ FILE: service/process/tags/appimage_unix.go ================================================ package tags import ( "bufio" "bytes" "os" "regexp" "strings" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/binmeta" ) func init() { err := process.RegisterTagHandler(new(AppImageHandler)) if err != nil { panic(err) } } const ( appImageName = "AppImage" appImagePathTagKey = "app-image-path" appImageMountIDTagKey = "app-image-mount-id" ) var ( appImageMountDirRegex = regexp.MustCompile(`^/tmp/.mount_[^/]+`) appImageMountNameExtractRegex = regexp.MustCompile(`^[A-Za-z0-9]+`) ) // AppImageHandler handles AppImage processes on Unix systems. type AppImageHandler struct{} // Name returns the tag handler name. func (h *AppImageHandler) Name() string { return appImageName } // TagDescriptions returns a list of all possible tags and their description // of this handler. func (h *AppImageHandler) TagDescriptions() []process.TagDescription { return []process.TagDescription{ { ID: appImagePathTagKey, Name: "AppImage Path", Description: "Path to the app image file itself.", }, { ID: appImageMountIDTagKey, Name: "AppImage Mount ID", Description: "Extracted ID from the AppImage mount name. Use AppImage Path instead, if available.", }, } } // AddTags adds tags to the given process. func (h *AppImageHandler) AddTags(p *process.Process) { // Detect app image path via ENV vars. func() { // Get and verify AppImage location. appImageLocation, ok := p.Env["APPIMAGE"] if !ok || appImageLocation == "" { return } appImageMountDir, ok := p.Env["APPDIR"] if !ok || appImageMountDir == "" { return } // Check if the process path is in the mount dir. if !strings.HasPrefix(p.Path, appImageMountDir) { return } // Add matching path for regular profile matching. p.MatchingPath = appImageLocation // Add app image tag. p.Tags = append(p.Tags, profile.Tag{ Key: appImagePathTagKey, Value: appImageLocation, }) }() // Detect app image mount point. func() { // Check if binary path matches app image mount pattern. mountDir := appImageMountDirRegex.FindString(p.Path) if mountDir == "" { return } // Get mount name of mount dir. // Also, this confirm this is actually a mounted dir. mountName, err := getAppImageMountName(mountDir) if err != nil { log.Debugf("process/tags: failed to get mount name: %s", err) return } if mountName == "" { return } // Extract a usable ID from the mount name. mountName, _ = strings.CutPrefix(mountName, "gearlever_") mountName = appImageMountNameExtractRegex.FindString(mountName) if mountName == "" { return } // Add app image tag. p.Tags = append(p.Tags, profile.Tag{ Key: appImageMountIDTagKey, Value: mountName, }) }() } // CreateProfile creates a profile based on the tags of the process. // Returns nil to skip. func (h *AppImageHandler) CreateProfile(p *process.Process) *profile.Profile { if tag, ok := p.GetTag(appImagePathTagKey); ok { return profile.New(&profile.Profile{ Source: profile.SourceLocal, Name: binmeta.GenerateBinaryNameFromPath(p.Path), PresentationPath: p.Path, UsePresentationPath: true, Fingerprints: []profile.Fingerprint{ { Type: profile.FingerprintTypeTagID, Key: tag.Key, Operation: profile.FingerprintOperationEqualsID, Value: tag.Value, // Value of appImagePathTagKey. }, }, }) } if tag, ok := p.GetTag(appImageMountIDTagKey); ok { return profile.New(&profile.Profile{ Source: profile.SourceLocal, Name: binmeta.GenerateBinaryNameFromPath(p.Path), PresentationPath: p.Path, UsePresentationPath: true, Fingerprints: []profile.Fingerprint{ { Type: profile.FingerprintTypeTagID, Key: tag.Key, Operation: profile.FingerprintOperationEqualsID, Value: tag.Value, // Value of appImageMountIDTagKey. }, }, }) } return nil } func getAppImageMountName(mountPoint string) (mountName string, err error) { // Get mounts. data, err := os.ReadFile("/proc/mounts") if err != nil { return "", err } scanner := bufio.NewScanner(bytes.NewReader(data)) for scanner.Scan() { fields := strings.Fields(scanner.Text()) if len(fields) >= 2 { switch { case fields[1] != mountPoint: case !strings.HasSuffix(strings.ToLower(fields[0]), ".appimage"): default: // Found AppImage mount! return fields[0], nil } } } if scanner.Err() != nil { return "", scanner.Err() } return "", nil } ================================================ FILE: service/process/tags/flatpak_unix.go ================================================ package tags import ( "strings" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/binmeta" ) func init() { err := process.RegisterTagHandler(new(flatpakHandler)) if err != nil { panic(err) } } const ( flatpakName = "Flatpak" flatpakIDTagKey = "flatpak-id" ) // flatpakHandler handles flatpak processes on Unix systems. type flatpakHandler struct{} // Name returns the tag handler name. func (h *flatpakHandler) Name() string { return flatpakName } // TagDescriptions returns a list of all possible tags and their description // of this handler. func (h *flatpakHandler) TagDescriptions() []process.TagDescription { return []process.TagDescription{ { ID: flatpakIDTagKey, Name: "Flatpak ID", Description: "ID of the flatpak.", }, } } // AddTags adds tags to the given process. func (h *flatpakHandler) AddTags(p *process.Process) { // Check if binary lives in the /app space. if !strings.HasPrefix(p.Path, "/app/") { return } // Get the Flatpak ID. flatpakID, ok := p.Env["FLATPAK_ID"] if !ok || flatpakID == "" { return } // Add matching path for regular profile matching. p.MatchingPath = p.Path // Add app image tag. p.Tags = append(p.Tags, profile.Tag{ Key: flatpakIDTagKey, Value: flatpakID, }) } // CreateProfile creates a profile based on the tags of the process. // Returns nil to skip. func (h *flatpakHandler) CreateProfile(p *process.Process) *profile.Profile { if tag, ok := p.GetTag(flatpakIDTagKey); ok { return profile.New(&profile.Profile{ Source: profile.SourceLocal, Name: binmeta.GenerateBinaryNameFromPath(p.Path), PresentationPath: p.Path, UsePresentationPath: true, Fingerprints: []profile.Fingerprint{ { Type: profile.FingerprintTypeTagID, Key: tag.Key, Operation: profile.FingerprintOperationEqualsID, Value: tag.Value, // Value of flatpakIDTagKey. }, }, }) } return nil } ================================================ FILE: service/process/tags/interpreter_unix.go ================================================ package tags import ( "bytes" "fmt" "io" "os" "path/filepath" "regexp" "strings" "unicode/utf8" "github.com/google/shlex" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/binmeta" ) func init() { if err := process.RegisterTagHandler(new(InterpHandler)); err != nil { panic(err) } } type interpType struct { process.TagDescription Extensions []string Regex *regexp.Regexp } var knownInterperters = []interpType{ { TagDescription: process.TagDescription{ ID: "python-script", Name: "Python Script", }, Extensions: []string{".py", ".py2", ".py3"}, Regex: regexp.MustCompile(`^(/usr)?/bin/python[23](\.[0-9]+)?$`), }, { TagDescription: process.TagDescription{ ID: "shell-script", Name: "Shell Script", }, Extensions: []string{".sh", ".bash", ".ksh", ".zsh", ".ash"}, Regex: regexp.MustCompile(`^(/usr)?/bin/(ba|k|z|a)?sh$`), }, { TagDescription: process.TagDescription{ ID: "perl-script", Name: "Perl Script", }, Extensions: []string{".pl"}, Regex: regexp.MustCompile(`^(/usr)?/bin/perl$`), }, { TagDescription: process.TagDescription{ ID: "ruby-script", Name: "Ruby Script", }, Extensions: []string{".rb"}, Regex: regexp.MustCompile(`^(/usr)?/bin/ruby$`), }, { TagDescription: process.TagDescription{ ID: "nodejs-script", Name: "NodeJS Script", }, Extensions: []string{".js"}, Regex: regexp.MustCompile(`^(/usr)?/bin/node(js)?$`), }, /* While similar to nodejs, electron is a bit harder as it uses a multiple processes like Chromium and thus a interpreter match on them will but those processes into different groups. I'm still not sure how this could work in the future. Maybe processes should try to inherit the profile of the parents if there is no profile that matches the current one.... { TagDescription: process.TagDescription{ ID: "electron-app", Name: "Electron App", }, Regex: regexp.MustCompile(`^(/usr)?/bin/electron([0-9]+)?$`), }, */ } func fileMustBeUTF8(path string) bool { f, err := os.Open(path) if err != nil { return false } defer func() { _ = f.Close() }() // read the first chunk of bytes buf := new(bytes.Buffer) size, _ := io.CopyN(buf, f, 128) if size == 0 { return false } b := buf.Bytes()[:size] for len(b) > 0 { r, runeSize := utf8.DecodeRune(b) if r == utf8.RuneError { return false } b = b[runeSize:] } return true } // InterpHandler supports adding process tags based on well-known interpreter binaries. type InterpHandler struct{} // Name returns "Interpreter". func (h *InterpHandler) Name() string { return "Interpreter" } // TagDescriptions returns a set of tag descriptions that InterpHandler provides. func (h *InterpHandler) TagDescriptions() []process.TagDescription { l := make([]process.TagDescription, len(knownInterperters)) for idx, it := range knownInterperters { l[idx] = it.TagDescription } return l } // CreateProfile creates a new profile for any process that has a tag created // by InterpHandler. func (h *InterpHandler) CreateProfile(p *process.Process) *profile.Profile { for _, it := range knownInterperters { if tag, ok := p.GetTag(it.ID); ok { // we can safely ignore the error args, err := shlex.Split(p.CmdLine) if err != nil { // this should not happen since we already called shlex.Split() // when adding the tag. Though, make the linter happy and bail out return nil } // if arg0 is the interpreter name itself strip it away // and use the next one if it.Regex.MatchString(args[0]) && len(args) > 1 { args = args[1:] } // Create a nice script name from filename. scriptName := filepath.Base(args[0]) for _, ext := range it.Extensions { scriptName, _ = strings.CutSuffix(scriptName, ext) } scriptName = binmeta.GenerateBinaryNameFromPath(scriptName) return profile.New(&profile.Profile{ Source: profile.SourceLocal, Name: fmt.Sprintf("%s: %s", it.Name, scriptName), PresentationPath: tag.Value, UsePresentationPath: true, Fingerprints: []profile.Fingerprint{ { Type: profile.FingerprintTypeTagID, Key: it.ID, Operation: profile.FingerprintOperationEqualsID, Value: tag.Value, }, }, }) } } return nil } // AddTags inspects the process p and adds any interpreter tags that InterpHandler // detects. func (h *InterpHandler) AddTags(p *process.Process) { // check if we have a matching interpreter var matched interpType for _, it := range knownInterperters { if it.Regex.MatchString(p.Path) { matched = it } } // zero value means we did not find any interpreter matches. if matched.ID == "" { return } args, err := shlex.Split(p.CmdLine) if err != nil { // give up if we failed to parse the command line return } // if args[0] matches the interpreter name we expect // the second arg to be a file-name if matched.Regex.MatchString(args[0]) { if len(args) == 1 { // there's no argument given, this is likely an interactive // interpreter session return } scriptPath := args[1] if !filepath.IsAbs(scriptPath) { scriptPath = filepath.Join( p.Cwd, scriptPath, ) } // TODO(ppacher): there could be some other arguments as well // so it may be better to scan the whole command line for a path to a UTF8 // file and use that one. if !fileMustBeUTF8(scriptPath) { return } p.Tags = append(p.Tags, profile.Tag{ Key: matched.ID, Value: scriptPath, }) p.MatchingPath = scriptPath return } // we know that this process is interpreted by some known interpreter but args[0] // does not contain the path to the interpreter. p.Tags = append(p.Tags, profile.Tag{ Key: matched.ID, Value: args[0], }) p.MatchingPath = args[0] } ================================================ FILE: service/process/tags/net.go ================================================ package tags import ( "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/profile" ) func init() { err := process.RegisterTagHandler(new(NetworkHandler)) if err != nil { panic(err) } } const ( netName = "Network" netIPTagKey = "ip" ) // NetworkHandler handles AppImage processes on Unix systems. type NetworkHandler struct{} // Name returns the tag handler name. func (h *NetworkHandler) Name() string { return netName } // TagDescriptions returns a list of all possible tags and their description // of this handler. func (h *NetworkHandler) TagDescriptions() []process.TagDescription { return []process.TagDescription{ { ID: netIPTagKey, Name: "IP Address", Description: "The remote IP address of external requests to Portmaster, if enabled.", }, } } // AddTags adds tags to the given process. func (h *NetworkHandler) AddTags(p *process.Process) { // The "net" tag is added directly when creating the virtual process. } // CreateProfile creates a profile based on the tags of the process. // Returns nil to skip. func (h *NetworkHandler) CreateProfile(p *process.Process) *profile.Profile { for _, tag := range p.Tags { if tag.Key == netIPTagKey { return profile.New(&profile.Profile{ Source: profile.SourceLocal, Name: p.Name, Fingerprints: []profile.Fingerprint{ { Type: profile.FingerprintTypeTagID, Key: tag.Key, Operation: profile.FingerprintOperationEqualsID, Value: tag.Value, }, }, }) } } return nil } ================================================ FILE: service/process/tags/snap_unix.go ================================================ package tags import ( "strings" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/binmeta" ) func init() { err := process.RegisterTagHandler(new(SnapHandler)) if err != nil { panic(err) } } const ( snapName = "Snap" snapNameKey = "snap-name" snapVersionKey = "snap-version" snapBaseDir = "/snap/" ) // SnapHandler handles Snap processes on Unix systems. type SnapHandler struct{} // Name returns the tag handler name. func (h *SnapHandler) Name() string { return snapName } // TagDescriptions returns a list of all possible tags and their description // of this handler. func (h *SnapHandler) TagDescriptions() []process.TagDescription { return []process.TagDescription{ { ID: snapNameKey, Name: "Snap Name", Description: "Name of snap package.", }, { ID: snapVersionKey, Name: "Snap Version", Description: "Version and revision of the snap package.", }, } } // AddTags adds tags to the given process. func (h *SnapHandler) AddTags(p *process.Process) { // Check for snap env and verify location. snapPkgBaseDir, ok := p.Env["SNAP"] if ok && strings.HasPrefix(p.Path, snapPkgBaseDir) { // Try adding tags from env. added := h.addTagsFromEnv(p) if added { return } } // Attempt adding tags from path instead, if env did not work out. h.addTagsFromPath(p) } func (h *SnapHandler) addTagsFromEnv(p *process.Process) (added bool) { // Get and verify snap metadata. snapPkgName, ok := p.Env["SNAP_NAME"] if !ok { return false } snapPkgVersion, ok := p.Env["SNAP_VERSION"] if !ok { return false } // Add snap tags. p.Tags = append(p.Tags, profile.Tag{ Key: snapNameKey, Value: snapPkgName, }) p.Tags = append(p.Tags, profile.Tag{ Key: snapVersionKey, Value: snapPkgVersion, }) return true } func (h *SnapHandler) addTagsFromPath(p *process.Process) { // Check if the binary is within the snap base dir. if !strings.HasPrefix(p.Path, snapBaseDir) { return } // Get snap package name from path. splitted := strings.SplitN(strings.TrimPrefix(p.Path, snapBaseDir), "/", 2) if len(splitted) < 2 || splitted[0] == "" { return } // Add snap tags. p.Tags = append(p.Tags, profile.Tag{ Key: snapNameKey, Value: splitted[0], }) } // CreateProfile creates a profile based on the tags of the process. // Returns nil to skip. func (h *SnapHandler) CreateProfile(p *process.Process) *profile.Profile { if tag, ok := p.GetTag(snapNameKey); ok { // Check if we have the snap version. // Only use presentation path if we have it. _, hasVersion := p.GetTag(snapVersionKey) return profile.New(&profile.Profile{ Source: profile.SourceLocal, Name: binmeta.GenerateBinaryNameFromPath(tag.Value), PresentationPath: p.Path, UsePresentationPath: hasVersion, Fingerprints: []profile.Fingerprint{ { Type: profile.FingerprintTypeTagID, Key: tag.Key, Operation: profile.FingerprintOperationEqualsID, Value: tag.Value, // Value of snapNameKey. }, }, }) } return nil } ================================================ FILE: service/process/tags/svchost_windows.go ================================================ package tags import ( "context" "fmt" "strings" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils/osdetail" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/binmeta" ) func init() { err := process.RegisterTagHandler(new(SVCHostTagHandler)) if err != nil { panic(err) } } const ( svchostName = "Service Host" svchostTagKey = "svchost" ) // SVCHostTagHandler handles svchost processes on Windows. type SVCHostTagHandler struct{} // Name returns the tag handler name. func (h *SVCHostTagHandler) Name() string { return svchostName } // TagDescriptions returns a list of all possible tags and their description // of this handler. func (h *SVCHostTagHandler) TagDescriptions() []process.TagDescription { return []process.TagDescription{ { ID: svchostTagKey, Name: "SvcHost Service Name", Description: "Name of a service running in svchost.exe as reported by Windows.", }, } } // TagKeys returns a list of all possible tag keys of this handler. func (h *SVCHostTagHandler) TagKeys() []string { return []string{svchostTagKey} } // AddTags adds tags to the given process. func (h *SVCHostTagHandler) AddTags(p *process.Process) { // Check for svchost.exe. if p.ExecName != "svchost.exe" { return } // Get services of svchost instance. svcNames, err := osdetail.GetServiceNames(int32(p.Pid)) switch err { case nil: // Append service names to process name. p.Name += fmt.Sprintf(" (%s)", strings.Join(svcNames, ", ")) // Add services as tags. for _, svcName := range svcNames { // Remove tags from service names, such as "CDPUserSvc_1bf5729". svcName, _, _ := strings.Cut(svcName, "_") // Add service as tag. p.Tags = append(p.Tags, profile.Tag{ Key: svchostTagKey, Value: svcName, }) } case osdetail.ErrServiceNotFound: log.Tracef("process/tags: failed to get service name for svchost.exe (pid %d): %s", p.Pid, err) default: log.Warningf("process/tags: failed to get service name for svchost.exe (pid %d): %s", p.Pid, err) } } // CreateProfile creates a profile based on the tags of the process. // Returns nil to skip. func (h *SVCHostTagHandler) CreateProfile(p *process.Process) *profile.Profile { if tag, ok := p.GetTag(svchostTagKey); ok { // Create new profile based on tag. newProfile := profile.New(&profile.Profile{ Source: profile.SourceLocal, Name: "Windows Service: " + binmeta.GenerateBinaryNameFromPath(tag.Value), UsePresentationPath: false, Fingerprints: []profile.Fingerprint{ { Type: profile.FingerprintTypeTagID, Key: tag.Key, Operation: profile.FingerprintOperationEqualsID, Value: tag.Value, // Value of svchostTagKey. }, }, }) // Load default icon for windows service. icon, err := binmeta.LoadAndSaveIcon(context.TODO(), `C:\Windows\System32\@WLOGO_48x48.png`) if err == nil { newProfile.Icons = []binmeta.Icon{*icon} } return newProfile } return nil } ================================================ FILE: service/process/tags/winstore_windows.go ================================================ package tags import ( "os" "strings" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/process" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/binmeta" ) func init() { err := process.RegisterTagHandler(new(WinStoreHandler)) if err != nil { panic(err) } // Add custom WindowsApps path. customWinStorePath := os.ExpandEnv(`%ProgramFiles%\WindowsApps\`) if !utils.StringInSlice(winStorePaths, customWinStorePath) { winStorePaths = append(winStorePaths, customWinStorePath) } } const ( winStoreName = "Windows Store" winStoreAppNameTagKey = "winstore-app-name" winStorePublisherIDTagKey = "winstore-publisher-id" ) var winStorePaths = []string{`C:\Program Files\WindowsApps\`} // WinStoreHandler handles Windows Store Apps. type WinStoreHandler struct{} // Name returns the tag handler name. func (h *WinStoreHandler) Name() string { return winStoreName } // TagDescriptions returns a list of all possible tags and their description // of this handler. func (h *WinStoreHandler) TagDescriptions() []process.TagDescription { return []process.TagDescription{ { ID: winStoreAppNameTagKey, Name: "Windows Store App Name", Description: "Name of the Windows Store App, as found in the executable path.", }, { ID: winStorePublisherIDTagKey, Name: "Windows Store Publisher ID", Description: "Publisher ID of a Windows Store App.", }, } } // AddTags adds tags to the given process. func (h *WinStoreHandler) AddTags(p *process.Process) { // Check if the path is in one of the Windows Store Apps paths. var appDir string for _, winStorePath := range winStorePaths { if strings.HasPrefix(p.Path, winStorePath) { appDir = strings.SplitN(strings.TrimPrefix(p.Path, winStorePath), `\`, 2)[0] break } } if appDir == "" { return } // Extract information from path. // Example: Microsoft.Office.OneNote_17.6769.57631.0_x64__8wekyb3d8bbwe splitted := strings.Split(appDir, "_") if len(splitted) != 5 { // Four fields, one "__". log.Debugf("profile/tags: windows store app has incompatible app dir format: %q", appDir) return } name := splitted[0] // version := splitted[1] // platform := splitted[2] publisherID := splitted[4] // Add tags. p.Tags = append(p.Tags, profile.Tag{ Key: winStoreAppNameTagKey, Value: name, }) p.Tags = append(p.Tags, profile.Tag{ Key: winStorePublisherIDTagKey, Value: publisherID, }) } // CreateProfile creates a profile based on the tags of the process. // Returns nil to skip. func (h *WinStoreHandler) CreateProfile(p *process.Process) *profile.Profile { if tag, ok := p.GetTag(winStoreAppNameTagKey); ok { return profile.New(&profile.Profile{ Source: profile.SourceLocal, Name: binmeta.GenerateBinaryNameFromPath(tag.Value), PresentationPath: p.Path, UsePresentationPath: true, Fingerprints: []profile.Fingerprint{ { Type: profile.FingerprintTypeTagID, Key: tag.Key, Operation: profile.FingerprintOperationEqualsID, Value: tag.Value, // Value of winStoreAppNameTagKey. }, }, }) } return nil } ================================================ FILE: service/process/tags.go ================================================ package process import ( "errors" "sync" "github.com/safing/portmaster/service/profile" ) var ( tagRegistry []TagHandler tagRegistryLock sync.RWMutex ) // TagHandler is a collection of process tag related interfaces. type TagHandler interface { // Name returns the tag handler name. Name() string // TagDescriptions returns a list of all possible tags and their description // of this handler. TagDescriptions() []TagDescription // AddTags adds tags to the given process. AddTags(p *Process) // CreateProfile creates a profile based on the tags of the process. // Returns nil to skip. CreateProfile(p *Process) *profile.Profile } // TagDescription describes a tag. type TagDescription struct { ID string Name string Description string } // RegisterTagHandler registers a tag handler. func RegisterTagHandler(th TagHandler) error { tagRegistryLock.Lock() defer tagRegistryLock.Unlock() // Check if the handler is already registered. for _, existingTH := range tagRegistry { if th.Name() == existingTH.Name() { return errors.New("already registered") } } tagRegistry = append(tagRegistry, th) return nil } func (p *Process) addTags() { tagRegistryLock.RLock() defer tagRegistryLock.RUnlock() for _, th := range tagRegistry { th.AddTags(p) } } // CreateProfileCallback attempts to create a profile on special attributes // of the process. func (p *Process) CreateProfileCallback() *profile.Profile { tagRegistryLock.RLock() defer tagRegistryLock.RUnlock() // Go through handlers and see which one wants to create a profile. for _, th := range tagRegistry { newProfile := th.CreateProfile(p) if newProfile != nil { return newProfile } } // No handler wanted to create a profile. return nil } ================================================ FILE: service/profile/active.go ================================================ package profile import ( "sync" "time" "github.com/safing/portmaster/service/mgr" ) const ( activeProfileCleanerTickDuration = 5 * time.Minute activeProfileCleanerThreshold = 1 * time.Hour ) var ( activeProfiles = make(map[string]*Profile) activeProfilesLock sync.RWMutex ) // getActiveProfile returns a cached copy of an active profile and // nil if it isn't found. func getActiveProfile(scopedID string) *Profile { activeProfilesLock.RLock() defer activeProfilesLock.RUnlock() return activeProfiles[scopedID] } // getAllActiveProfiles returns a slice of active profiles. func getAllActiveProfiles() []*Profile { activeProfilesLock.RLock() defer activeProfilesLock.RUnlock() result := make([]*Profile, 0, len(activeProfiles)) for _, p := range activeProfiles { result = append(result, p) } return result } // addActiveProfile registers a active profile. func addActiveProfile(profile *Profile) { activeProfilesLock.Lock() defer activeProfilesLock.Unlock() // Mark any previous profile as outdated. if previous, ok := activeProfiles[profile.ScopedID()]; ok { previous.outdated.Set() } // Mark new profile active and add to active profiles. profile.MarkStillActive() activeProfiles[profile.ScopedID()] = profile } func cleanActiveProfiles(ctx *mgr.WorkerCtx) error { for { select { case <-time.After(activeProfileCleanerTickDuration): threshold := time.Now().Add(-activeProfileCleanerThreshold).Unix() activeProfilesLock.Lock() for id, profile := range activeProfiles { // Remove profile if it hasn't been used for a while. if profile.LastActive() < threshold { profile.outdated.Set() delete(activeProfiles, id) } } activeProfilesLock.Unlock() case <-ctx.Done(): return nil } } } ================================================ FILE: service/profile/api.go ================================================ package profile import ( "errors" "fmt" "net/http" "path/filepath" "strings" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/profile/binmeta" "github.com/safing/structures/dsd" ) func registerAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Name: "Merge profiles", Description: "Merge multiple profiles into a new one.", Path: "profile/merge", Write: api.PermitUser, StructFunc: handleMergeProfiles, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Get Profile Icon", Description: "Returns the requested profile icon.", Path: "profile/icon/{id:[a-f0-9]*\\.[a-z]{3,4}}", Read: api.PermitUser, DataFunc: handleGetProfileIcon, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Update Profile Icon", Description: "Updates a profile icon.", Path: "profile/icon", Write: api.PermitUser, StructFunc: handleUpdateProfileIcon, }); err != nil { return err } return nil } type mergeProfilesRequest struct { Name string `json:"name"` // Name of the new merged profile. To string `json:"to"` // Profile scoped ID. From []string `json:"from"` // Profile scoped IDs. } type mergeprofilesResponse struct { New string `json:"new"` // Profile scoped ID. } func handleMergeProfiles(ar *api.Request) (i interface{}, err error) { request := &mergeProfilesRequest{} _, err = dsd.MimeLoad(ar.InputData, ar.Header.Get("Content-Type"), request) if err != nil { return nil, fmt.Errorf("failed to parse request: %w", err) } // Get all profiles. var ( primary *Profile secondaries = make([]*Profile, 0, len(request.From)) ) if primary, err = getProfile(request.To); err != nil { return nil, fmt.Errorf("failed to get profile %s: %w", request.To, err) } for _, from := range request.From { sp, err := getProfile(from) if err != nil { return nil, fmt.Errorf("failed to get profile %s: %w", request.To, err) } secondaries = append(secondaries, sp) } newProfile, err := MergeProfiles(request.Name, primary, secondaries...) if err != nil { return nil, fmt.Errorf("failed to merge profiles: %w", err) } return &mergeprofilesResponse{ New: newProfile.ScopedID(), }, nil } func handleGetProfileIcon(ar *api.Request) (data []byte, err error) { name := ar.URLVars["id"] ext := filepath.Ext(name) // Get profile icon. data, err = binmeta.GetProfileIcon(name) switch { case err == nil: // Continue case errors.Is(err, binmeta.ErrIconIgnored): return nil, api.ErrorWithStatus(err, http.StatusNotFound) default: return nil, err } // Set content type for icon. contentType, ok := utils.MimeTypeByExtension(ext) if ok { ar.ResponseHeader.Set("Content-Type", contentType) } return data, nil } type updateProfileIconResponse struct { Filename string `json:"filename"` } //nolint:goconst func handleUpdateProfileIcon(ar *api.Request) (any, error) { // Check input. if len(ar.InputData) == 0 { return nil, api.ErrorWithStatus(errors.New("no content"), http.StatusBadRequest) } mimeType := ar.Header.Get("Content-Type") if mimeType == "" { return nil, api.ErrorWithStatus(errors.New("no content type"), http.StatusBadRequest) } // Derive image format from content type. mimeType = strings.TrimSpace(mimeType) mimeType = strings.ToLower(mimeType) mimeType, _, _ = strings.Cut(mimeType, ";") var ext string switch mimeType { case "image/gif": ext = "gif" case "image/jpeg": ext = "jpg" case "image/jpg": ext = "jpg" case "image/png": ext = "png" case "image/svg+xml": ext = "svg" case "image/tiff": ext = "tiff" case "image/webp": ext = "webp" default: return "", api.ErrorWithStatus(errors.New("unsupported image format"), http.StatusBadRequest) } // Update profile icon. filename, err := binmeta.UpdateProfileIcon(ar.InputData, ext) if err != nil { return nil, err } return &updateProfileIconResponse{ Filename: filename, }, nil } ================================================ FILE: service/profile/binmeta/convert.go ================================================ package binmeta import ( "bytes" "fmt" "github.com/fogleman/gg" // Import the specialized ICO decoder package // This package seems to work better than "github.com/mat/besticon/ico" with ICO files // extracted from Windows binaries, particularly those containing cursor-related data ico "github.com/sergeymakinen/go-ico" ) // ConvertICOtoPNG converts a an .ico to a .png image. func ConvertICOtoPNG(icoBytes []byte) (png []byte, err error) { // Decode ICO image. // Note: The standard approach with `image.Decode(bytes.NewReader(icoBytes))` sometimes fails // when processing certain ICO files (particularly those with cursor data), // as it reads initial bytes for format detection before passing the stream to the decoder. icon, err := ico.Decode(bytes.NewReader(icoBytes)) if err != nil { return nil, fmt.Errorf("failed to decode ICO: %w", err) } // Convert to raw image. img := gg.NewContextForImage(icon) // Convert to PNG. imgBuf := &bytes.Buffer{} err = img.EncodePNG(imgBuf) if err != nil { return nil, fmt.Errorf("failed to encode PNG: %w", err) } return imgBuf.Bytes(), nil } ================================================ FILE: service/profile/binmeta/find_default.go ================================================ //go:build !linux && !windows package binmeta import "context" // GetIconAndName returns zero values for unsupported platforms. func GetIconAndName(ctx context.Context, binPath string, homeDir string) (icon *Icon, name string, err error) { return nil, "", nil } ================================================ FILE: service/profile/binmeta/find_linux.go ================================================ package binmeta import ( "context" "errors" "fmt" "os" "path/filepath" "strings" ) // GetIconAndName returns an icon and name of the given binary path. // Providing the home directory of the user running the process of that binary can improve results. // Even if an error is returned, the other return values are valid, if set. func GetIconAndName(ctx context.Context, binPath string, homeDir string) (icon *Icon, name string, err error) { // Derive name from binary. name = GenerateBinaryNameFromPath(binPath) // Search for icon. iconPath, err := searchForIcon(binPath, homeDir) if iconPath == "" { if err != nil { return nil, name, fmt.Errorf("failed to find icon for %s: %w", binPath, err) } return nil, name, nil } // Save icon to internal storage. icon, err = LoadAndSaveIcon(ctx, iconPath) if err != nil { return nil, name, fmt.Errorf("failed to store icon for %s: %w", binPath, err) } return icon, name, nil } func searchForIcon(binPath string, homeDir string) (iconPath string, err error) { binPath = strings.ToLower(binPath) // Search for icon path. for _, iconLoc := range iconLocations { basePath := iconLoc.GetPath(binPath, homeDir) if basePath == "" { continue } switch iconLoc.Type { case FlatDir: iconPath, err = searchDirectory(basePath, binPath) case XDGIcons: iconPath, err = searchXDGIconStructure(basePath, binPath) } if iconPath != "" { return } } return } func searchXDGIconStructure(baseDirectory string, binPath string) (iconPath string, err error) { for _, xdgIconDir := range xdgIconPaths { directory := filepath.Join(baseDirectory, xdgIconDir) iconPath, err = searchDirectory(directory, binPath) if iconPath != "" { return } } return } func searchDirectory(directory string, binPath string) (iconPath string, err error) { entries, err := os.ReadDir(directory) if err != nil { if errors.Is(err, os.ErrNotExist) { return "", nil } return "", fmt.Errorf("failed to read directory %s: %w", directory, err) } // DEBUG: // fmt.Println(directory) var ( bestMatch string bestMatchExcessChars int ) for _, entry := range entries { // Skip dirs. if entry.IsDir() { continue } iconName := strings.ToLower(entry.Name()) iconName = strings.TrimSuffix(iconName, filepath.Ext(iconName)) switch { case len(iconName) < len(binPath): // Continue to next. case iconName == binPath: // Exact match, return immediately. return filepath.Join(directory, entry.Name()), nil case strings.HasPrefix(iconName, binPath): excessChars := len(iconName) - len(binPath) if bestMatch == "" || excessChars < bestMatchExcessChars { bestMatch = entry.Name() bestMatchExcessChars = excessChars } } } return bestMatch, nil } ================================================ FILE: service/profile/binmeta/find_linux_test.go ================================================ package binmeta import ( "os" "testing" ) func TestFindIcon(t *testing.T) { if testing.Short() { t.Skip("test depends on linux desktop environment") } t.Parallel() home := os.Getenv("HOME") testFindIcon(t, "evolution", home) testFindIcon(t, "nextcloud", home) } func testFindIcon(t *testing.T, binName string, homeDir string) { t.Helper() iconPath, err := searchForIcon(binName, homeDir) if err != nil { t.Error(err) return } if iconPath == "" { t.Errorf("no icon found for %s", binName) return } t.Logf("icon for %s found: %s", binName, iconPath) } ================================================ FILE: service/profile/binmeta/find_windows.go ================================================ package binmeta import ( "bytes" "context" "errors" "fmt" "os" "github.com/tc-hib/winres" "github.com/tc-hib/winres/version" ) // GetIconAndName returns an icon and name of the given binary path. // Providing the home directory of the user running the process of that binary can improve results. // Even if an error is returned, the other return values are valid, if set. func GetIconAndName(ctx context.Context, binPath string, homeDir string) (icon *Icon, name string, err error) { // Get name and png from exe. png, name, err := getIconAndNamefromRSS(ctx, binPath) // Fall back to name generation if name is not set. if name == "" { name = GenerateBinaryNameFromPath(binPath) } // Handle previous error. if err != nil { return nil, name, err } // Update profile icon and return icon object. filename, err := UpdateProfileIcon(png, "png") if err != nil { return nil, name, fmt.Errorf("failed to store icon: %w", err) } return &Icon{ Type: IconTypeAPI, Value: filename, Source: IconSourceCore, }, name, nil } func getIconAndNamefromRSS(ctx context.Context, binPath string) (png []byte, name string, err error) { // Open .exe file. exeFile, err := os.Open(binPath) if err != nil { if errors.Is(err, os.ErrNotExist) { return nil, "", nil } return nil, "", fmt.Errorf("failed to open exe %s to get icon: %w", binPath, err) } defer exeFile.Close() //nolint:errcheck // Load .exe resources. rss, err := winres.LoadFromEXE(exeFile) if err != nil { return nil, "", fmt.Errorf("failed to get rss: %w", err) } // DEBUG: Print all available resources: // rss.Walk(func(typeID, resID winres.Identifier, langID uint16, data []byte) bool { // fmt.Printf("typeID=%d resID=%d langID=%d\n", typeID, resID, langID) // return true // }) // Get name from version record. var ( versionInfo *version.Info versionInfoErr error ) rss.WalkType(winres.RT_VERSION, func(resID winres.Identifier, langID uint16, data []byte) bool { versionInfo, versionInfoErr = version.FromBytes(data) switch { case versionInfoErr != nil: return true case versionInfo == nil: return true } // Get metadata table and main language. table := versionInfo.Table().GetMainTranslation() if table == nil { return true } name = table[version.ProductName] return name == "" }) name = cleanFileDescription(name) // Get first icon. var ( icon *winres.Icon iconErr error ) rss.WalkType(winres.RT_GROUP_ICON, func(resID winres.Identifier, langID uint16, _ []byte) bool { icon, iconErr = rss.GetIconTranslation(resID, langID) return iconErr != nil }) if iconErr != nil { return nil, name, fmt.Errorf("failed to get icon: %w", err) } if icon == nil { return nil, name, errors.New("no icon in resources") } // Convert icon, if it exists. icoBuf := &bytes.Buffer{} err = icon.SaveICO(icoBuf) if err != nil { return nil, name, fmt.Errorf("failed to save ico: %w", err) } png, err = ConvertICOtoPNG(icoBuf.Bytes()) if err != nil { return nil, name, fmt.Errorf("failed to convert ico to png: %w", err) } return png, name, nil } ================================================ FILE: service/profile/binmeta/find_windows_test.go ================================================ package binmeta import ( "context" "os" "testing" ) func TestFindIcon(t *testing.T) { if testing.Short() { t.Skip("test meant for compiling and running on desktop") } t.Parallel() binName := os.Args[len(os.Args)-1] t.Logf("getting name and icon for %s", binName) png, name, err := getIconAndNamefromRSS(context.Background(), binName) if err != nil { t.Fatal(err) } t.Logf("name: %s", name) err = os.WriteFile("icon.png", png, 0o0600) if err != nil { t.Fatal(err) } } ================================================ FILE: service/profile/binmeta/icon.go ================================================ package binmeta import ( "errors" "fmt" "strings" "sync" "github.com/vincent-petithory/dataurl" "golang.org/x/exp/slices" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" ) // Icon describes an icon. type Icon struct { Type IconType Value string Source IconSource } // IconType describes the type of an Icon. type IconType string // Supported icon types. const ( IconTypeFile IconType = "path" IconTypeDatabase IconType = "database" IconTypeAPI IconType = "api" ) func (t IconType) sortOrder() int { switch t { case IconTypeAPI: return 1 case IconTypeDatabase: return 2 case IconTypeFile: return 3 default: return 9 } } // IconSource describes the source of an Icon. type IconSource string // Supported icon sources. const ( IconSourceUser IconSource = "user" IconSourceImport IconSource = "import" IconSourceUI IconSource = "ui" IconSourceCore IconSource = "core" ) func (s IconSource) sortOrder() int { switch s { case IconSourceUser: return 10 case IconSourceImport: return 20 case IconSourceUI: return 30 case IconSourceCore: return 40 default: return 90 } } func (icon Icon) sortOrder() int { return icon.Source.sortOrder() + icon.Type.sortOrder() } // SortAndCompactIcons sorts and compacts a list of icons. func SortAndCompactIcons(icons []Icon) []Icon { // Sort. slices.SortFunc[[]Icon, Icon](icons, func(a, b Icon) int { aOrder := a.sortOrder() bOrder := b.sortOrder() switch { case aOrder != bOrder: return aOrder - bOrder case a.Value != b.Value: return strings.Compare(a.Value, b.Value) default: return 0 } }) // De-duplicate. icons = slices.CompactFunc[[]Icon, Icon](icons, func(a, b Icon) bool { return a.Type == b.Type && a.Value == b.Value }) return icons } // GetIconAsDataURL returns the icon data as a data URL. func (icon Icon) GetIconAsDataURL() (bloburl string, err error) { switch icon.Type { case IconTypeFile: return "", errors.New("getting icon from file is not supported") case IconTypeDatabase: if !strings.HasPrefix(icon.Value, "cache:icons/") { return "", errors.New("invalid icon db key") } r, err := iconDB.Get(icon.Value) if err != nil { return "", err } dbIcon, err := EnsureIconInDatabase(r) if err != nil { return "", err } return dbIcon.IconData, nil case IconTypeAPI: data, err := GetProfileIcon(icon.Value) if err != nil { return "", err } return dataurl.EncodeBytes(data), nil default: return "", errors.New("unknown icon type") } } var iconDB = database.NewInterface(&database.Options{ Local: true, Internal: true, }) // IconInDatabase represents an icon saved to the database. type IconInDatabase struct { sync.Mutex record.Base IconData string `json:"iconData,omitempty"` // DataURL } // EnsureIconInDatabase ensures that the given record is a *IconInDatabase, and returns it. func EnsureIconInDatabase(r record.Record) (*IconInDatabase, error) { // unwrap if r.IsWrapped() { // only allocate a new struct, if we need it newIcon := &IconInDatabase{} err := record.Unwrap(r, newIcon) if err != nil { return nil, err } return newIcon, nil } // or adjust type newIcon, ok := r.(*IconInDatabase) if !ok { return nil, fmt.Errorf("record not of type *IconInDatabase, but %T", r) } return newIcon, nil } ================================================ FILE: service/profile/binmeta/icons.go ================================================ package binmeta import ( "context" "crypto" "encoding/hex" "errors" "fmt" "net/http" "os" "path/filepath" "strings" "github.com/safing/portmaster/base/api" ) // ProfileIconStoragePath defines the location where profile icons are stored. // Must be set before anything else from this package is called. // Must not be changed once set. var ProfileIconStoragePath = "" // ErrIconIgnored is returned when the icon should be ignored. var ErrIconIgnored = errors.New("icon is ignored") // GetProfileIcon returns the profile icon with the given ID and extension. func GetProfileIcon(name string) (data []byte, err error) { // Check if enabled. if ProfileIconStoragePath == "" { return nil, errors.New("api icon storage not configured") } // Check if icon should be ignored. if IgnoreIcon(name) { return nil, ErrIconIgnored } // Build storage path. iconPath := filepath.Clean( filepath.Join(ProfileIconStoragePath, name), ) iconPath, err = filepath.Abs(iconPath) if err != nil { return nil, fmt.Errorf("failed to check icon path: %w", err) } // Do a quick check if we are still within the right directory. // This check is not entirely correct, but is sufficient for this use case. if filepath.Dir(iconPath) != ProfileIconStoragePath { return nil, api.ErrorWithStatus(errors.New("invalid icon"), http.StatusBadRequest) } return os.ReadFile(iconPath) } // UpdateProfileIcon creates or updates the given icon. func UpdateProfileIcon(data []byte, ext string) (filename string, err error) { // Check icon size. if len(data) > 1_000_000 { return "", errors.New("icon too big") } // Calculate sha1 sum of icon. h := crypto.SHA1.New() if _, err := h.Write(data); err != nil { return "", err } sum := hex.EncodeToString(h.Sum(nil)) // Check if icon should be ignored. if IgnoreIcon(sum) { return "", ErrIconIgnored } // Check ext. ext = strings.ToLower(ext) switch ext { case "gif": case "jpeg": ext = "jpg" case "jpg": case "png": case "svg": case "tiff": case "webp": default: return "", errors.New("unsupported icon format") } // Save to disk. filename = sum + "." + ext return filename, os.WriteFile(filepath.Join(ProfileIconStoragePath, filename), data, 0o0644) //nolint:gosec } // LoadAndSaveIcon loads an icon from disk, updates it in the icon database // and returns the icon object. func LoadAndSaveIcon(ctx context.Context, iconPath string) (*Icon, error) { // Load icon and save it. data, err := os.ReadFile(iconPath) if err != nil { return nil, fmt.Errorf("failed to read icon %s: %w", iconPath, err) } filename, err := UpdateProfileIcon(data, filepath.Ext(iconPath)) if err != nil { return nil, fmt.Errorf("failed to import icon %s: %w", iconPath, err) } return &Icon{ Type: IconTypeAPI, Value: filename, Source: IconSourceCore, }, nil } // TODO: Clean up icons regularly. ================================================ FILE: service/profile/binmeta/ignore.go ================================================ package binmeta import ( "strings" ) var ignoreIcons = map[string]struct{}{ // Windows Default Icons. "a27898ddfa4e0481b62c69faa196919a738fcade": {}, "5a3eea8bcd08b9336ce9c5083f26185164268ee9": {}, "573393d6ad238d255b20dc1c1b303c95debe6965": {}, "d459b2cb23c27cc31ccab5025533048d5d8301bf": {}, "d35a0d91ebfda81df5286f68ec5ddb1d6ad6b850": {}, "cc33187385498384f1b648e23be5ef1a2e9f5f71": {}, } // IgnoreIcon returns whether an icon should be ignored or not. func IgnoreIcon(name string) bool { // Make lower case. name = strings.ToLower(name) // Remove extension. extIndex := strings.Index(name, ".") if extIndex > 0 { name = name[:extIndex] } // Check if ID is in list. _, found := ignoreIcons[name] return found } ================================================ FILE: service/profile/binmeta/locations_linux.go ================================================ package binmeta import ( "fmt" ) // IconLocation describes an icon location. type IconLocation struct { Directory string Type IconLocationType PathArg PathArg } // IconLocationType describes an icon location type. type IconLocationType uint8 // Icon Location Types. const ( FlatDir IconLocationType = iota XDGIcons ) // PathArg describes an icon location path argument. type PathArg uint8 // Path Args. const ( NoPathArg PathArg = iota Home BinName ) var ( iconLocations = []IconLocation{ {Directory: "/usr/share/pixmaps", Type: FlatDir}, {Directory: "/usr/share", Type: XDGIcons}, {Directory: "%s/.local/share", Type: XDGIcons, PathArg: Home}, {Directory: "%s/.local/share/flatpak/exports/share", Type: XDGIcons, PathArg: Home}, {Directory: "/usr/share/%s", Type: XDGIcons, PathArg: BinName}, } xdgIconPaths = []string{ // UI currently uses 48x48, so 256x256 should suffice for the future, even at 2x. (12.2023) "icons/hicolor/256x256/apps", "icons/hicolor/192x192/apps", "icons/hicolor/128x128/apps", "icons/hicolor/96x96/apps", "icons/hicolor/72x72/apps", "icons/hicolor/64x64/apps", "icons/hicolor/48x48/apps", "icons/hicolor/512x512/apps", } ) // GetPath returns the path of an icon. func (il IconLocation) GetPath(binName string, homeDir string) string { switch il.PathArg { case NoPathArg: return il.Directory case Home: if homeDir != "" { return fmt.Sprintf(il.Directory, homeDir) } case BinName: return fmt.Sprintf(il.Directory, binName) } return "" } ================================================ FILE: service/profile/binmeta/name.go ================================================ package binmeta import ( "path/filepath" "regexp" "strings" ) var ( segmentsSplitter = regexp.MustCompile("[^A-Za-z0-9]*[A-Z]?[a-z0-9]*") nameOnly = regexp.MustCompile("^[A-Za-z0-9]+$") delimitersAtStart = regexp.MustCompile("^[^A-Za-z0-9]+") delimitersOnly = regexp.MustCompile("^[^A-Za-z0-9]+$") removeQuotes = strings.NewReplacer(`"`, ``, `'`, ``) ) // GenerateBinaryNameFromPath generates a more human readable binary name from // the given path. This function is used as fallback in the GetBinaryName // functions. func GenerateBinaryNameFromPath(path string) string { // Get file name from path. _, fileName := filepath.Split(path) // Split up into segments. segments := segmentsSplitter.FindAllString(fileName, -1) // Remove last segment if it's an extension. if len(segments) >= 2 { switch strings.ToLower(segments[len(segments)-1]) { case ".exe", // Windows Executable ".msi", // Windows Installer ".bat", // Windows Batch File ".cmd", // Windows Command Script ".ps1", // Windows Powershell Cmdlet ".run", // Linux Executable ".appimage", // Linux AppImage ".app", // MacOS Executable ".action", // MacOS Automator Action ".out": // Generic Compiled Executable segments = segments[:len(segments)-1] } } // Debugging snippet: // fmt.Printf("segments: %s\n", segments) // Go through segments and collect name parts. nameParts := make([]string, 0, len(segments)) var fragments string for _, segment := range segments { // Group very short segments. if len(delimitersAtStart.ReplaceAllString(segment, "")) <= 2 { fragments += segment continue } else if fragments != "" { nameParts = append(nameParts, fragments) fragments = "" } // Add segment to name. nameParts = append(nameParts, segment) } // Add last fragment. if fragments != "" { nameParts = append(nameParts, fragments) } // Debugging snippet: // fmt.Printf("parts: %s\n", nameParts) // Post-process name parts for i := range nameParts { // Remove any leading delimiters. nameParts[i] = delimitersAtStart.ReplaceAllString(nameParts[i], "") // Title-case name-only parts. if nameOnly.MatchString(nameParts[i]) { nameParts[i] = strings.Title(nameParts[i]) //nolint:staticcheck } } // Debugging snippet: // fmt.Printf("final: %s\n", nameParts) return strings.Join(nameParts, " ") } func cleanFileDescription(fileDescr string) string { fields := strings.Fields(fileDescr) // Clean out and `"` and `'`. for i := range fields { fields[i] = removeQuotes.Replace(fields[i]) } // If there is a 1 or 2 character delimiter field, only use fields before it. endIndex := len(fields) for i, field := range fields { // Ignore the first field as well as fields with more than two characters. if i >= 1 && len(field) <= 2 && !nameOnly.MatchString(field) { endIndex = i break } } // Concatenate name binName := strings.Join(fields[:endIndex], " ") // If there are multiple sentences, only use the first. if strings.Contains(binName, ". ") { binName = strings.SplitN(binName, ". ", 2)[0] } // If does not have any characters or numbers, return an empty string. if delimitersOnly.MatchString(binName) { return "" } return strings.TrimSpace(binName) } ================================================ FILE: service/profile/binmeta/name_test.go ================================================ package binmeta import ( "testing" "github.com/stretchr/testify/assert" ) func TestGenerateBinaryNameFromPath(t *testing.T) { t.Parallel() assert.Equal(t, "Nslookup", GenerateBinaryNameFromPath("nslookup.exe")) assert.Equal(t, "System Settings", GenerateBinaryNameFromPath("SystemSettings.exe")) assert.Equal(t, "One Drive Setup", GenerateBinaryNameFromPath("OneDriveSetup.exe")) assert.Equal(t, "Msedge", GenerateBinaryNameFromPath("msedge.exe")) assert.Equal(t, "SIH Client", GenerateBinaryNameFromPath("SIHClient.exe")) assert.Equal(t, "Openvpn Gui", GenerateBinaryNameFromPath("openvpn-gui.exe")) assert.Equal(t, "Portmaster Core v0-1-2", GenerateBinaryNameFromPath("portmaster-core_v0-1-2.exe")) assert.Equal(t, "Win Store App", GenerateBinaryNameFromPath("WinStore.App.exe")) assert.Equal(t, "Test Script", GenerateBinaryNameFromPath(".test-script")) assert.Equal(t, "Browser Broker", GenerateBinaryNameFromPath("browser_broker.exe")) assert.Equal(t, "Virtual Box VM", GenerateBinaryNameFromPath("VirtualBoxVM")) assert.Equal(t, "Io Elementary Appcenter", GenerateBinaryNameFromPath("io.elementary.appcenter")) assert.Equal(t, "Microsoft Windows Store", GenerateBinaryNameFromPath("Microsoft.WindowsStore")) } func TestCleanFileDescription(t *testing.T) { t.Parallel() assert.Equal(t, "Product Name", cleanFileDescription("Product Name")) assert.Equal(t, "Product Name", cleanFileDescription("Product Name. Does this and that.")) assert.Equal(t, "Product Name", cleanFileDescription("Product Name - Does this and that.")) assert.Equal(t, "Product Name", cleanFileDescription("Product Name / Does this and that.")) assert.Equal(t, "Product Name", cleanFileDescription("Product Name :: Does this and that.")) assert.Equal(t, "/ Product Name", cleanFileDescription("/ Product Name")) assert.Equal(t, "Product", cleanFileDescription("Product / Name")) assert.Equal(t, "Software 2", cleanFileDescription("Software 2")) assert.Equal(t, "Launcher for Software 2", cleanFileDescription("Launcher for 'Software 2'")) assert.Equal(t, "", cleanFileDescription(". / Name")) assert.Equal(t, "", cleanFileDescription(". ")) assert.Equal(t, "", cleanFileDescription(".")) assert.Equal(t, "N/A", cleanFileDescription("N/A")) assert.Equal(t, "Product Name a Does this and that.", cleanFileDescription("Product Name a Does this and that."), ) } ================================================ FILE: service/profile/config-update.go ================================================ package profile import ( "context" "fmt" "sync" "time" "github.com/safing/portmaster/service/intel/filterlists" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/profile/endpoints" ) var ( cfgLock sync.RWMutex cfgDefaultAction uint8 cfgEndpoints endpoints.Endpoints cfgServiceEndpoints endpoints.Endpoints cfgSPNUsagePolicy endpoints.Endpoints cfgSPNTransitHubPolicy endpoints.Endpoints cfgSPNExitHubPolicy endpoints.Endpoints cfgFilterLists []string ) func registerGlobalConfigProfileUpdater() error { module.instance.Config().EventConfigChange.AddCallback("update global config profile", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { return false, updateGlobalConfigProfile(wc.Ctx()) }) return nil } const globalConfigProfileErrorID = "profile:global-profile-error" func updateGlobalConfigProfile(_ context.Context) error { cfgLock.Lock() defer cfgLock.Unlock() var err error var lastErr error action := cfgOptionDefaultAction() switch action { case DefaultActionPermitValue: cfgDefaultAction = DefaultActionPermit case DefaultActionAskValue: cfgDefaultAction = DefaultActionAsk case DefaultActionBlockValue: cfgDefaultAction = DefaultActionBlock default: // TODO: module error? lastErr = fmt.Errorf(`default action "%s" invalid`, action) cfgDefaultAction = DefaultActionBlock // default to block in worst case } list := cfgOptionEndpoints() cfgEndpoints, err = endpoints.ParseEndpoints(list) if err != nil { // TODO: module error? lastErr = err } list = cfgOptionServiceEndpoints() cfgServiceEndpoints, err = endpoints.ParseEndpoints(list) if err != nil { // TODO: module error? lastErr = err } list = cfgOptionFilterLists() cfgFilterLists, err = filterlists.ResolveListIDs(list) if err != nil { lastErr = err } list = cfgOptionSPNUsagePolicy() cfgSPNUsagePolicy, err = endpoints.ParseEndpoints(list) if err != nil { // TODO: module error? lastErr = err } list = cfgOptionTransitHubPolicy() cfgSPNTransitHubPolicy, err = endpoints.ParseEndpoints(list) if err != nil { // TODO: module error? lastErr = err } list = cfgOptionExitHubPolicy() cfgSPNExitHubPolicy, err = endpoints.ParseEndpoints(list) if err != nil { // TODO: module error? lastErr = err } // Build config. newConfig := make(map[string]interface{}) // fill profile config options for key, value := range cfgStringOptions { newConfig[key] = value() } for key, value := range cfgStringArrayOptions { newConfig[key] = value() } for key, value := range cfgIntOptions { newConfig[key] = value() } for key, value := range cfgBoolOptions { newConfig[key] = value() } // Build global profile for reference. profile := New(&Profile{ ID: "global-config", Source: SourceSpecial, Name: "Global Configuration", Config: newConfig, Internal: true, }) // save profile err = profile.Save() if err != nil && lastErr == nil { // other errors are more important lastErr = err } // If there was any error, try again later until it succeeds. if lastErr == nil { module.states.Remove(globalConfigProfileErrorID) } else { // Create task after first failure. // Schedule task. _ = module.mgr.Delay("retry updating global config profile", 15*time.Second, func(w *mgr.WorkerCtx) error { return updateGlobalConfigProfile(w.Ctx()) }) // Add module warning to inform user. module.states.Add(mgr.State{ ID: globalConfigProfileErrorID, Name: "Internal Settings Failure", Message: fmt.Sprintf("Some global settings might not be applied correctly. You can try restarting the Portmaster to resolve this problem. Error: %s", lastErr), Type: mgr.StateTypeWarning, }) } return lastErr } ================================================ FILE: service/profile/config.go ================================================ package profile import ( "strings" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/profile/endpoints" "github.com/safing/portmaster/service/status" "github.com/safing/portmaster/spn/access/account" ) // Configuration Keys. var ( cfgStringOptions = make(map[string]config.StringOption) cfgStringArrayOptions = make(map[string]config.StringArrayOption) cfgIntOptions = make(map[string]config.IntOption) cfgBoolOptions = make(map[string]config.BoolOption) // General. // Setting "Enable Filter" at order 0. CfgOptionDefaultActionKey = "filter/defaultAction" cfgOptionDefaultAction config.StringOption cfgOptionDefaultActionOrder = 1 DefaultActionPermitValue = "permit" DefaultActionBlockValue = "block" DefaultActionAskValue = "ask" // Setting "Prompt Desktop Notifications" at order 2. // Setting "Prompt Timeout" at order 3. // Network Scopes. CfgOptionBlockScopeInternetKey = "filter/blockInternet" cfgOptionBlockScopeInternet config.BoolOption cfgOptionBlockScopeInternetOrder = 16 CfgOptionBlockScopeLANKey = "filter/blockLAN" cfgOptionBlockScopeLAN config.BoolOption cfgOptionBlockScopeLANOrder = 17 CfgOptionBlockScopeLocalKey = "filter/blockLocal" cfgOptionBlockScopeLocal config.BoolOption cfgOptionBlockScopeLocalOrder = 18 // Connection Types. CfgOptionBlockP2PKey = "filter/blockP2P" cfgOptionBlockP2P config.BoolOption cfgOptionBlockP2POrder = 19 CfgOptionBlockInboundKey = "filter/blockInbound" cfgOptionBlockInbound config.BoolOption cfgOptionBlockInboundOrder = 20 // Rules. CfgOptionEndpointsKey = "filter/endpoints" cfgOptionEndpoints config.StringArrayOption cfgOptionEndpointsOrder = 32 CfgOptionServiceEndpointsKey = "filter/serviceEndpoints" cfgOptionServiceEndpoints config.StringArrayOption cfgOptionServiceEndpointsOrder = 33 CfgOptionFilterListsKey = "filter/lists" cfgOptionFilterLists config.StringArrayOption cfgOptionFilterListsOrder = 34 // Setting "Custom Filter List" at order 35. CfgOptionFilterSubDomainsKey = "filter/includeSubdomains" cfgOptionFilterSubDomains config.BoolOption cfgOptionFilterSubDomainsOrder = 36 // DNS Filtering. CfgOptionFilterCNAMEKey = "filter/includeCNAMEs" cfgOptionFilterCNAME config.BoolOption cfgOptionFilterCNAMEOrder = 48 CfgOptionRemoveOutOfScopeDNSKey = "filter/removeOutOfScopeDNS" cfgOptionRemoveOutOfScopeDNS config.BoolOption cfgOptionRemoveOutOfScopeDNSOrder = 49 CfgOptionRemoveBlockedDNSKey = "filter/removeBlockedDNS" cfgOptionRemoveBlockedDNS config.BoolOption cfgOptionRemoveBlockedDNSOrder = 50 CfgOptionDomainHeuristicsKey = "filter/domainHeuristics" cfgOptionDomainHeuristics config.BoolOption cfgOptionDomainHeuristicsOrder = 51 // Advanced. CfgOptionPreventBypassingKey = "filter/preventBypassing" cfgOptionPreventBypassing config.BoolOption cfgOptionPreventBypassingOrder = 64 CfgOptionDisableAutoPermitKey = "filter/disableAutoPermit" cfgOptionDisableAutoPermit config.BoolOption cfgOptionDisableAutoPermitOrder = 65 // Setting "Permanent Verdicts" at order 80. // Network History. CfgOptionEnableHistoryKey = "history/enable" cfgOptionEnableHistory config.BoolOption cfgOptionEnableHistoryOrder = 96 CfgOptionKeepHistoryKey = "history/keep" cfgOptionKeepHistory config.IntOption cfgOptionKeepHistoryOrder = 97 // Setting "Enable SPN" at order 128. CfgOptionUseSPNKey = "spn/use" cfgOptionUseSPN config.BoolOption cfgOptionUseSPNOrder = 129 CfgOptionSPNUsagePolicyKey = "spn/usagePolicy" cfgOptionSPNUsagePolicy config.StringArrayOption cfgOptionSPNUsagePolicyOrder = 130 CfgOptionRoutingAlgorithmKey = "spn/routingAlgorithm" cfgOptionRoutingAlgorithm config.StringOption cfgOptionRoutingAlgorithmOrder = 144 DefaultRoutingProfileID = "double-hop" // Copied due to import loop. // Setting "Home Node Rules" at order 145. CfgOptionTransitHubPolicyKey = "spn/transitHubPolicy" cfgOptionTransitHubPolicy config.StringArrayOption cfgOptionTransitHubPolicyOrder = 146 CfgOptionExitHubPolicyKey = "spn/exitHubPolicy" cfgOptionExitHubPolicy config.StringArrayOption cfgOptionExitHubPolicyOrder = 147 // Setting "DNS Exit Node Rules" at order 148. ) var ( // SPNRulesQuickSettings are now generated automatically shorty after start. SPNRulesQuickSettings = []config.QuickSetting{ {Name: "Loading...", Action: config.QuickMergeTop, Value: []string{""}}, } // SPNRulesVerdictNames defines the verdicts names to be used for SPN Rules. SPNRulesVerdictNames = map[string]string{ "-": "Exclude", // Default. "+": "Allow", } // SPNRulesHelp defines the help text for SPN related Hub selection rules. SPNRulesHelp = strings.ReplaceAll(`Rules are checked from top to bottom, stopping after the first match. They can match the following attributes of SPN Nodes: - Country (based on IPs): "US" (two-letter country codes according to ISO 3166-1 alpha-2) - AS number: "AS123456" - Address: "192.168.0.1" - Network: "192.168.0.1/24" - Anything: "*" `, `"`, "`") ) func registerConfiguration() error { //nolint:maintidx // Default Filter Action // permit - blocklist mode: everything is allowed unless blocked // ask - ask mode: if not verdict is found, the user is consulted // block - allowlist mode: everything is blocked unless explicitly allowed err := config.Register(&config.Option{ Name: "Default Network Action", Key: CfgOptionDefaultActionKey, Description: `The default network action is applied when nothing else allows or blocks a connection. This affects both outgoing and incoming connections. This setting is the weakest of all and is commonly overruled by Force Block settings or Rules.`, OptType: config.OptTypeString, DefaultValue: DefaultActionPermitValue, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: config.DisplayHintOneOf, config.DisplayOrderAnnotation: cfgOptionDefaultActionOrder, config.CategoryAnnotation: "General", }, PossibleValues: []config.PossibleValue{ { Name: "Allow", Value: DefaultActionPermitValue, Description: "Allow all connections", }, { Name: "Block", Value: DefaultActionBlockValue, Description: "Block all connections", }, { Name: "Prompt", Value: DefaultActionAskValue, Description: "Prompt for decisions", }, }, }) if err != nil { return err } cfgOptionDefaultAction = config.Concurrent.GetAsString(CfgOptionDefaultActionKey, DefaultActionPermitValue) cfgStringOptions[CfgOptionDefaultActionKey] = cfgOptionDefaultAction // Disable Auto Permit err = config.Register(&config.Option{ // TODO: Check how to best handle negation here. Name: "Disable Auto Allow", Key: CfgOptionDisableAutoPermitKey, Description: `Auto Allow searches for a relation between an app and the destination of a connection - if there is a correlation, the connection will be allowed.`, OptType: config.OptTypeBool, ReleaseLevel: config.ReleaseLevelBeta, DefaultValue: true, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayOrderAnnotation: cfgOptionDisableAutoPermitOrder, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.CategoryAnnotation: "Advanced", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } cfgOptionDisableAutoPermit = config.Concurrent.GetAsBool(CfgOptionDisableAutoPermitKey, true) cfgBoolOptions[CfgOptionDisableAutoPermitKey] = cfgOptionDisableAutoPermit // Enable History err = config.Register(&config.Option{ Name: "Enable Network History", Key: CfgOptionEnableHistoryKey, Description: `Save connections in a database (on disk) in order to view and search them later. Changes might take a couple minutes to apply to all connections. In order to reduce noise optimize performance, internal and device-only (localhost) connections are not saved to history.`, OptType: config.OptTypeBool, ReleaseLevel: config.ReleaseLevelStable, ExpertiseLevel: config.ExpertiseLevelUser, DefaultValue: false, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayOrderAnnotation: cfgOptionEnableHistoryOrder, config.CategoryAnnotation: "General", config.RequiresFeatureIDAnnotation: account.FeatureHistory, }, }) if err != nil { return err } cfgOptionEnableHistory = config.Concurrent.GetAsBool(CfgOptionEnableHistoryKey, false) cfgBoolOptions[CfgOptionEnableHistoryKey] = cfgOptionEnableHistory err = config.Register(&config.Option{ Name: "Keep Network History", Key: CfgOptionKeepHistoryKey, Description: `Specify how many days the network history data should be kept. Please keep in mind that more available history data makes reports (coming soon) a lot more useful. Older data is deleted in intervals and cleared from the database continually. If in a hurry, shutdown or restart Portmaster to clear deleted entries immediately. Set to 0 days to keep network history forever. Depending on your device, this might affect performance.`, OptType: config.OptTypeInt, ReleaseLevel: config.ReleaseLevelStable, ExpertiseLevel: config.ExpertiseLevelUser, DefaultValue: 30, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.UnitAnnotation: "Days", config.DisplayOrderAnnotation: cfgOptionKeepHistoryOrder, config.CategoryAnnotation: "General", config.RequiresFeatureIDAnnotation: account.FeatureHistory, }, }) if err != nil { return err } cfgOptionKeepHistory = config.Concurrent.GetAsInt(CfgOptionKeepHistoryKey, 30) cfgIntOptions[CfgOptionKeepHistoryKey] = cfgOptionKeepHistory rulesHelp := strings.ReplaceAll(`Rules are checked from top to bottom, stopping after the first match. They can match: - By address: "192.168.0.1" - By network: "192.168.0.0/24" - By network scope: "Localhost", "LAN" or "Internet" - By domain: - Matching a distinct domain: "example.com" - Matching a domain with subdomains: ".example.com" - Matching with a wildcard prefix: "*xample.com" - Matching with a wildcard suffix: "example.*" - Matching domains containing text: "*example*" - By country (based on IP): "US" ([two-letter country codes according to ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)) - By continent (based on IP): "C:US" (prefix "AF", "AN", "AS", "EU", "NA", "OC", or "SA" with "C:") - By AS number: "AS123456" - By filter list - use the filterlist ID prefixed with "L:": "L:MAL" - Match anything: "*" Additionally, you may supply a protocol and port using this format: " /". Protocols and ports may be specified using numbers ("6/80") or names ("TCP/HTTP"). Port ranges are defined by using a hyphen ("TCP/1-1024"). Omit the port to match any. Use a "*" for matching any protocol. If matching ports with any protocol, protocols without ports will not match. Rules with protocol and port definitions only match if the protocol and port also match. Ports are always compared to the destination port, thus, the local listening port for incoming connections. Examples: - "192.168.0.1 TCP/HTTP" - "LAN UDP/50000-55000" - "example.com */HTTPS" - "1.1.1.1 ICMP" Important: DNS Requests are only matched against domain and filter list rules, all others require an IP address and are checked only with the following IP connection. Pro Tip: You can use "#" to add a comment to a rule. `, `"`, "`") // rulesVerdictNames defines the verdicts names to be used for filter rules. rulesVerdictNames := map[string]string{ "-": "Block", // Default. "+": "Allow", } // Endpoint Filter List err = config.Register(&config.Option{ Name: "Outgoing Rules", Key: CfgOptionEndpointsKey, Description: "Rules that apply to outgoing network connections. Cannot overrule Network Scopes and Connection Types (see above).", Help: rulesHelp, Sensitive: true, OptType: config.OptTypeStringArray, DefaultValue: []string{}, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.StackableAnnotation: true, config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, config.DisplayOrderAnnotation: cfgOptionEndpointsOrder, config.CategoryAnnotation: "Rules", endpoints.EndpointListVerdictNamesAnnotation: rulesVerdictNames, }, ValidationRegex: endpoints.ListEntryValidationRegex, ValidationFunc: endpoints.ValidateEndpointListConfigOption, }) if err != nil { return err } cfgOptionEndpoints = config.Concurrent.GetAsStringArray(CfgOptionEndpointsKey, []string{}) cfgStringArrayOptions[CfgOptionEndpointsKey] = cfgOptionEndpoints // Service Endpoint Filter List err = config.Register(&config.Option{ Name: "Incoming Rules", Key: CfgOptionServiceEndpointsKey, Description: "Rules that apply to incoming network connections. Cannot overrule Network Scopes and Connection Types (see above).", Help: rulesHelp, Sensitive: true, OptType: config.OptTypeStringArray, DefaultValue: []string{}, ExpertiseLevel: config.ExpertiseLevelExpert, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.StackableAnnotation: true, config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, config.DisplayOrderAnnotation: cfgOptionServiceEndpointsOrder, config.CategoryAnnotation: "Rules", endpoints.EndpointListVerdictNamesAnnotation: rulesVerdictNames, config.QuickSettingsAnnotation: []config.QuickSetting{ { Name: "Allow SSH", Action: config.QuickMergeTop, Value: []string{"+ * tcp/22"}, }, { Name: "Allow HTTP/s", Action: config.QuickMergeTop, Value: []string{"+ * tcp/80", "+ * tcp/443"}, }, { Name: "Allow RDP", Action: config.QuickMergeTop, Value: []string{"+ * */3389"}, }, { Name: "Allow all from LAN", Action: config.QuickMergeTop, Value: []string{"+ LAN"}, }, { Name: "Allow all from Internet", Action: config.QuickMergeTop, Value: []string{"+ Internet"}, }, { Name: "Block everything else", Action: config.QuickMergeBottom, Value: []string{"- *"}, }, }, }, ValidationRegex: endpoints.ListEntryValidationRegex, ValidationFunc: endpoints.ValidateEndpointListConfigOption, }) if err != nil { return err } cfgOptionServiceEndpoints = config.Concurrent.GetAsStringArray(CfgOptionServiceEndpointsKey, []string{}) cfgStringArrayOptions[CfgOptionServiceEndpointsKey] = cfgOptionServiceEndpoints // Filter list IDs defaultFilterListsValue := []string{"TRAC", "MAL", "BAD", "UNBREAK"} err = config.Register(&config.Option{ Name: "Filter Lists", Key: CfgOptionFilterListsKey, Description: "Block connections that match enabled filter lists.", OptType: config.OptTypeStringArray, DefaultValue: defaultFilterListsValue, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: "filter list", config.DisplayOrderAnnotation: cfgOptionFilterListsOrder, config.CategoryAnnotation: "Filter Lists", }, ValidationRegex: `^[a-zA-Z0-9\-]+$`, }) if err != nil { return err } cfgOptionFilterLists = config.Concurrent.GetAsStringArray(CfgOptionFilterListsKey, defaultFilterListsValue) cfgStringArrayOptions[CfgOptionFilterListsKey] = cfgOptionFilterLists // Include CNAMEs err = config.Register(&config.Option{ Name: "Block Domain Aliases", Key: CfgOptionFilterCNAMEKey, Description: "Block a domain if a resolved CNAME (alias) is blocked by a rule or filter list.", OptType: config.OptTypeBool, DefaultValue: true, ExpertiseLevel: config.ExpertiseLevelExpert, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.DisplayOrderAnnotation: cfgOptionFilterCNAMEOrder, config.CategoryAnnotation: "DNS Filtering", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } cfgOptionFilterCNAME = config.Concurrent.GetAsBool(CfgOptionFilterCNAMEKey, true) cfgBoolOptions[CfgOptionFilterCNAMEKey] = cfgOptionFilterCNAME // Include subdomains err = config.Register(&config.Option{ Name: "Block Subdomains of Filter List Entries", Key: CfgOptionFilterSubDomainsKey, Description: "Additionally block all subdomains of entries in selected filter lists.", OptType: config.OptTypeBool, DefaultValue: true, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.DisplayOrderAnnotation: cfgOptionFilterSubDomainsOrder, config.CategoryAnnotation: "Filter Lists", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } cfgOptionFilterSubDomains = config.Concurrent.GetAsBool(CfgOptionFilterSubDomainsKey, true) cfgBoolOptions[CfgOptionFilterSubDomainsKey] = cfgOptionFilterSubDomains // Block Scope Local err = config.Register(&config.Option{ Name: "Force Block Device-Local Connections", Key: CfgOptionBlockScopeLocalKey, Description: "Force Block all internal connections on your own device, ie. localhost. Is stronger than Rules (see below).", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, DefaultValue: false, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.DisplayOrderAnnotation: cfgOptionBlockScopeLocalOrder, config.CategoryAnnotation: "Network Scope", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } cfgOptionBlockScopeLocal = config.Concurrent.GetAsBool(CfgOptionBlockScopeLocalKey, false) cfgBoolOptions[CfgOptionBlockScopeLocalKey] = cfgOptionBlockScopeLocal // Block Scope LAN err = config.Register(&config.Option{ Name: "Force Block LAN", Key: CfgOptionBlockScopeLANKey, Description: "Force Block all connections from and to the Local Area Network. Is stronger than Rules (see below).", OptType: config.OptTypeBool, DefaultValue: false, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.DisplayOrderAnnotation: cfgOptionBlockScopeLANOrder, config.CategoryAnnotation: "Network Scope", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } cfgOptionBlockScopeLAN = config.Concurrent.GetAsBool(CfgOptionBlockScopeLANKey, false) cfgBoolOptions[CfgOptionBlockScopeLANKey] = cfgOptionBlockScopeLAN // Block Scope Internet err = config.Register(&config.Option{ Name: "Force Block Internet Access", Key: CfgOptionBlockScopeInternetKey, Description: "Force Block connections from and to the Internet. Is stronger than Rules (see below).", OptType: config.OptTypeBool, DefaultValue: false, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.DisplayOrderAnnotation: cfgOptionBlockScopeInternetOrder, config.CategoryAnnotation: "Network Scope", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } cfgOptionBlockScopeInternet = config.Concurrent.GetAsBool(CfgOptionBlockScopeInternetKey, false) cfgBoolOptions[CfgOptionBlockScopeInternetKey] = cfgOptionBlockScopeInternet // Block Peer to Peer Connections err = config.Register(&config.Option{ Name: "Force Block P2P/Direct Connections", Key: CfgOptionBlockP2PKey, Description: "These are connections that are established directly to an IP address or peer on the Internet without resolving a domain name via DNS first. Is stronger than Rules (see below).", OptType: config.OptTypeBool, DefaultValue: false, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.DisplayOrderAnnotation: cfgOptionBlockP2POrder, config.CategoryAnnotation: "Connection Types", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } cfgOptionBlockP2P = config.Concurrent.GetAsBool(CfgOptionBlockP2PKey, false) cfgBoolOptions[CfgOptionBlockP2PKey] = cfgOptionBlockP2P // Block Inbound Connections err = config.Register(&config.Option{ Name: "Force Block Incoming Connections", Key: CfgOptionBlockInboundKey, Description: "Connections initiated towards your device from the LAN or Internet. This will usually only be the case if you are running a network service or are using peer to peer software. Is stronger than Rules (see below).", OptType: config.OptTypeBool, DefaultValue: true, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.DisplayOrderAnnotation: cfgOptionBlockInboundOrder, config.CategoryAnnotation: "Connection Types", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } cfgOptionBlockInbound = config.Concurrent.GetAsBool(CfgOptionBlockInboundKey, false) cfgBoolOptions[CfgOptionBlockInboundKey] = cfgOptionBlockInbound // Filter Out-of-Scope DNS Records err = config.Register(&config.Option{ Name: "Enforce Global/Private Split-View", Key: CfgOptionRemoveOutOfScopeDNSKey, Description: "Reject private IP addresses (RFC1918 et al.) from public DNS responses. If the system resolver is in use, the resulting connection will be blocked instead of the DNS request.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelDeveloper, DefaultValue: true, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.DisplayOrderAnnotation: cfgOptionRemoveOutOfScopeDNSOrder, config.CategoryAnnotation: "DNS Filtering", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } cfgOptionRemoveOutOfScopeDNS = config.Concurrent.GetAsBool(CfgOptionRemoveOutOfScopeDNSKey, true) cfgBoolOptions[CfgOptionRemoveOutOfScopeDNSKey] = cfgOptionRemoveOutOfScopeDNS // Filter DNS Records that would be blocked err = config.Register(&config.Option{ Name: "Reject Blocked IPs", Key: CfgOptionRemoveBlockedDNSKey, Description: "Reject blocked IP addresses directly from the DNS response instead of handing them over to the app and blocking a resulting connection. This settings does not affect privacy and only takes effect when the system resolver is not in use.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelDeveloper, DefaultValue: true, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.DisplayOrderAnnotation: cfgOptionRemoveBlockedDNSOrder, config.CategoryAnnotation: "DNS Filtering", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } cfgOptionRemoveBlockedDNS = config.Concurrent.GetAsBool(CfgOptionRemoveBlockedDNSKey, true) cfgBoolOptions[CfgOptionRemoveBlockedDNSKey] = cfgOptionRemoveBlockedDNS // Domain heuristics err = config.Register(&config.Option{ Name: "Enable Domain Heuristics", Key: CfgOptionDomainHeuristicsKey, Description: "Checks for suspicious domain names and blocks them. This option currently targets domain names generated by malware and DNS data exfiltration channels.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, DefaultValue: true, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.DisplayOrderAnnotation: cfgOptionDomainHeuristicsOrder, config.CategoryAnnotation: "DNS Filtering", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } cfgOptionDomainHeuristics = config.Concurrent.GetAsBool(CfgOptionDomainHeuristicsKey, true) cfgBoolOptions[CfgOptionDomainHeuristicsKey] = cfgOptionDomainHeuristics // Bypass prevention err = config.Register(&config.Option{ Name: "Block Secure DNS Bypassing", Key: CfgOptionPreventBypassingKey, Description: `Prevent apps from bypassing Portmaster's Secure DNS resolver. If disabled, Portmaster might have reduced information to correctly enforce rules and filter lists. Important: Portmaster's firewall itself cannot be bypassed. Current Features: - Disable Firefox' internal DNS-over-HTTPs resolver - Block direct access to public DNS resolvers Please note that DNS bypass attempts might be additionally blocked in the System DNS Client App.`, OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelUser, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: true, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.DisplayOrderAnnotation: cfgOptionPreventBypassingOrder, config.CategoryAnnotation: "Advanced", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } cfgOptionPreventBypassing = config.Concurrent.GetAsBool(CfgOptionPreventBypassingKey, true) cfgBoolOptions[CfgOptionPreventBypassingKey] = cfgOptionPreventBypassing // Use SPN err = config.Register(&config.Option{ Name: "Use SPN", Key: CfgOptionUseSPNKey, Description: "Protect network traffic with the Safing Privacy Network. If the SPN is not available or the connection is interrupted, network traffic will be blocked.", OptType: config.OptTypeBool, DefaultValue: true, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayOrderAnnotation: cfgOptionUseSPNOrder, config.CategoryAnnotation: "General", }, }) if err != nil { return err } cfgOptionUseSPN = config.Concurrent.GetAsBool(CfgOptionUseSPNKey, true) cfgBoolOptions[CfgOptionUseSPNKey] = cfgOptionUseSPN // SPN Rules err = config.Register(&config.Option{ Name: "SPN Rules", Key: CfgOptionSPNUsagePolicyKey, Description: `Customize which websites should or should not be routed through the SPN. Only active if "Use SPN" is enabled.`, Help: rulesHelp, Sensitive: true, OptType: config.OptTypeStringArray, DefaultValue: []string{}, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.StackableAnnotation: true, config.CategoryAnnotation: "General", config.DisplayOrderAnnotation: cfgOptionSPNUsagePolicyOrder, config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, endpoints.EndpointListVerdictNamesAnnotation: SPNRulesVerdictNames, }, ValidationRegex: endpoints.ListEntryValidationRegex, ValidationFunc: endpoints.ValidateEndpointListConfigOption, }) if err != nil { return err } cfgOptionSPNUsagePolicy = config.Concurrent.GetAsStringArray(CfgOptionSPNUsagePolicyKey, []string{}) cfgStringArrayOptions[CfgOptionSPNUsagePolicyKey] = cfgOptionSPNUsagePolicy // Transit Node Rules err = config.Register(&config.Option{ Name: "Transit Node Rules", Key: CfgOptionTransitHubPolicyKey, Description: `Customize which countries should or should not be used as Transit Nodes. Transit Nodes are used to transit the SPN from your Home to your Exit Node.`, Help: SPNRulesHelp, Sensitive: true, OptType: config.OptTypeStringArray, ExpertiseLevel: config.ExpertiseLevelExpert, DefaultValue: []string{}, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.StackableAnnotation: true, config.CategoryAnnotation: "Routing", config.DisplayOrderAnnotation: cfgOptionTransitHubPolicyOrder, config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, config.QuickSettingsAnnotation: SPNRulesQuickSettings, endpoints.EndpointListVerdictNamesAnnotation: SPNRulesVerdictNames, }, ValidationRegex: endpoints.ListEntryValidationRegex, ValidationFunc: endpoints.ValidateEndpointListConfigOption, }) if err != nil { return err } cfgOptionTransitHubPolicy = config.Concurrent.GetAsStringArray(CfgOptionTransitHubPolicyKey, []string{}) cfgStringArrayOptions[CfgOptionTransitHubPolicyKey] = cfgOptionTransitHubPolicy // Exit Node Rules err = config.Register(&config.Option{ Name: "Exit Node Rules", Key: CfgOptionExitHubPolicyKey, Description: `Customize which countries should or should not be used for your Exit Nodes. Exit Nodes are used to exit the SPN and establish a connection to your destination. By default, the Portmaster tries to choose the node closest to the destination as the Exit Node. This reduces your exposure to the open Internet. Exit Nodes are chosen for every destination separately.`, Help: SPNRulesHelp, Sensitive: true, OptType: config.OptTypeStringArray, DefaultValue: []string{}, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.StackableAnnotation: true, config.CategoryAnnotation: "Routing", config.DisplayOrderAnnotation: cfgOptionExitHubPolicyOrder, config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, config.QuickSettingsAnnotation: SPNRulesQuickSettings, endpoints.EndpointListVerdictNamesAnnotation: SPNRulesVerdictNames, }, ValidationRegex: endpoints.ListEntryValidationRegex, ValidationFunc: endpoints.ValidateEndpointListConfigOption, }) if err != nil { return err } cfgOptionExitHubPolicy = config.Concurrent.GetAsStringArray(CfgOptionExitHubPolicyKey, []string{}) cfgStringArrayOptions[CfgOptionExitHubPolicyKey] = cfgOptionExitHubPolicy // Select SPN Routing Algorithm err = config.Register(&config.Option{ Name: "Select SPN Routing Algorithm", Key: CfgOptionRoutingAlgorithmKey, Description: "Select the routing algorithm for your connections through the SPN. Configure your preferred balance between speed and privacy. Portmaster may automatically upgrade the routing algorithm if necessary to protect your privacy.", OptType: config.OptTypeString, DefaultValue: DefaultRoutingProfileID, Annotations: config.Annotations{ config.SettablePerAppAnnotation: true, config.DisplayHintAnnotation: config.DisplayHintOneOf, config.DisplayOrderAnnotation: cfgOptionRoutingAlgorithmOrder, config.CategoryAnnotation: "Routing", }, PossibleValues: []config.PossibleValue{ { Name: "Plain VPN Mode", Value: "home", Description: "Always connect to the destination directly from the Home Hub. Only provides very basic privacy, as the Home Hub both knows where you are coming from and where you are connecting to.", }, { Name: "Speed Focused", Value: "single-hop", Description: "Optimize routes with a minimum of one hop. Provides good speeds. This will often use the Home Hub to connect to destinations near you, but will use more hops to far away destinations for better privacy over long distances.", }, { Name: "Balanced", Value: "double-hop", Description: "Optimize routes with a minimum of two hops. Provides good privacy as well as good speeds. No single node knows where you are coming from *and* where you are connecting to.", }, { Name: "Privacy Focused", Value: "triple-hop", Description: "Optimize routes with a minimum of three hops. Provides very good privacy. No single node knows where you are coming from *and* where you are connecting to - with an additional hop just to be sure.", }, }, }) if err != nil { return err } cfgOptionRoutingAlgorithm = config.Concurrent.GetAsString(CfgOptionRoutingAlgorithmKey, DefaultRoutingProfileID) cfgStringOptions[CfgOptionRoutingAlgorithmKey] = cfgOptionRoutingAlgorithm return nil } ================================================ FILE: service/profile/database.go ================================================ package profile import ( "errors" "strings" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" ) // Database paths: // core:profiles// // cache:profiles/index// // ProfilesDBPath is the base database path for profiles. const ProfilesDBPath = "core:profiles/" var profileDB = database.NewInterface(&database.Options{ Local: true, Internal: true, }) // MakeScopedID returns a scoped profile ID. func MakeScopedID(source ProfileSource, id string) string { return string(source) + "/" + id } // MakeProfileKey returns a profile key. func MakeProfileKey(source ProfileSource, id string) string { return ProfilesDBPath + string(source) + "/" + id } func registerValidationDBHook() (err error) { _, err = database.RegisterHook(query.New(ProfilesDBPath), &databaseHook{}) return } func startProfileUpdateChecker() error { module.mgr.Go("update active profiles", func(ctx *mgr.WorkerCtx) (err error) { profilesSub, err := profileDB.Subscribe(query.New(ProfilesDBPath)) if err != nil { return err } defer func() { err := profilesSub.Cancel() if err != nil { log.Warningf("profile: failed to cancel subscription for updating active profiles: %s", err) } }() profileFeed: for { select { case r := <-profilesSub.Feed: // Check if subscription was canceled. if r == nil { return errors.New("subscription canceled") } // Get active profile. scopedID := strings.TrimPrefix(r.Key(), ProfilesDBPath) activeProfile := getActiveProfile(scopedID) if activeProfile == nil { // Check if profile is being deleted. if r.Meta().IsDeleted() { meta.MarkDeleted(scopedID) } // Don't do any additional actions if the profile is not active. continue profileFeed } // Always increase the revision counter of the layer profile. // This marks previous connections in the UI as decided with outdated settings. if activeProfile.layeredProfile != nil { activeProfile.layeredProfile.increaseRevisionCounter(true) } // Always mark as outdated if the record is being deleted. if r.Meta().IsDeleted() { activeProfile.outdated.Set() meta.MarkDeleted(scopedID) module.EventDelete.Submit(scopedID) continue } // If the profile is saved externally (eg. via the API), have the // next one to use it reload the profile from the database. receivedProfile, err := EnsureProfile(r) if err != nil || !receivedProfile.savedInternally { activeProfile.outdated.Set() module.EventConfigChange.Submit(scopedID) } case <-ctx.Done(): return nil } } }) return nil } type databaseHook struct { database.HookBase } // UsesPrePut implements the Hook interface and returns false. func (h *databaseHook) UsesPrePut() bool { return true } // PrePut implements the Hook interface. func (h *databaseHook) PrePut(r record.Record) (record.Record, error) { // Do not intervene with metadata key. if r.Key() == profilesMetadataKey { return r, nil } // convert profile, err := EnsureProfile(r) if err != nil { return nil, err } // clean config config.CleanHierarchicalConfig(profile.Config) // prepare profile profile.prepProfile() // parse config err = profile.parseConfig() if err != nil { // error here, warning when loading return nil, err } return profile, nil } ================================================ FILE: service/profile/endpoints/annotations.go ================================================ package endpoints // DisplayHintEndpointList marks an option as an endpoint // list option. It's meant to be used with DisplayHintAnnotation. const DisplayHintEndpointList = "endpoint list" // EndpointListVerdictNamesAnnotation is the annotation identifier used in // configuration options to hint the UI on names to be used for endpoint list // verdicts. // If configured, it must be of type map[string]string, mapping the verdict // symbol to a name to be displayed in the UI. // May only used when config.DisplayHintAnnotation is set to DisplayHintEndpointList. const EndpointListVerdictNamesAnnotation = "safing/portmaster:ui:endpoint-list:verdict-names" ================================================ FILE: service/profile/endpoints/endpoint-any.go ================================================ package endpoints import ( "context" "github.com/safing/portmaster/service/intel" ) // EndpointAny matches anything. type EndpointAny struct { EndpointBase } // Matches checks whether the given entity matches this endpoint definition. func (ep *EndpointAny) Matches(_ context.Context, entity *intel.Entity) (EPResult, Reason) { return ep.match(ep, entity, "*", "matches") } func (ep *EndpointAny) String() string { return ep.renderPPP("*") } func parseTypeAny(fields []string) (Endpoint, error) { if fields[1] == "*" { ep := &EndpointAny{} return ep.parsePPP(ep, fields) } return nil, nil } ================================================ FILE: service/profile/endpoints/endpoint-asn.go ================================================ package endpoints import ( "context" "fmt" "regexp" "strconv" "strings" "github.com/safing/portmaster/service/intel" ) var asnRegex = regexp.MustCompile("^AS[0-9]+$") // EndpointASN matches ASNs. type EndpointASN struct { EndpointBase ASN uint } // Matches checks whether the given entity matches this endpoint definition. func (ep *EndpointASN) Matches(ctx context.Context, entity *intel.Entity) (EPResult, Reason) { if entity.IP == nil { return NoMatch, nil } if !entity.IPScope.IsGlobal() { return NoMatch, nil } asn, ok := entity.GetASN(ctx) if !ok { asnStr := strconv.Itoa(int(ep.ASN)) return MatchError, ep.makeReason(ep, asnStr, "ASN data not available to match") } if asn == ep.ASN { asnStr := strconv.Itoa(int(ep.ASN)) return ep.match(ep, entity, asnStr, "IP is part of AS") } return NoMatch, nil } func (ep *EndpointASN) String() string { return ep.renderPPP("AS" + strconv.FormatInt(int64(ep.ASN), 10)) } func parseTypeASN(fields []string) (Endpoint, error) { if asnRegex.MatchString(fields[1]) { asnString := strings.TrimPrefix(fields[1], "AS") asn, err := strconv.ParseUint(asnString, 10, 64) if err != nil { return nil, fmt.Errorf("failed to parse AS number %s", asnString) } ep := &EndpointASN{ ASN: uint(asn), } return ep.parsePPP(ep, fields) } return nil, nil } ================================================ FILE: service/profile/endpoints/endpoint-continent.go ================================================ package endpoints import ( "context" "fmt" "regexp" "strings" "github.com/safing/portmaster/service/intel" ) var ( continentCodePrefix = "C:" continentRegex = regexp.MustCompile(`^C:[A-Z]{2}$`) ) // EndpointContinent matches countries. type EndpointContinent struct { EndpointBase ContinentCode string } // Matches checks whether the given entity matches this endpoint definition. func (ep *EndpointContinent) Matches(ctx context.Context, entity *intel.Entity) (EPResult, Reason) { if entity.IP == nil { return NoMatch, nil } if !entity.IPScope.IsGlobal() { return NoMatch, nil } countryInfo := entity.GetCountryInfo(ctx) if countryInfo == nil { return MatchError, ep.makeReason(ep, "", "country data not available to match") } if ep.ContinentCode == countryInfo.Continent.Code { return ep.match( ep, entity, fmt.Sprintf("%s (%s)", countryInfo.Continent.Name, countryInfo.Continent.Code), "IP is located in", ) } return NoMatch, nil } func (ep *EndpointContinent) String() string { return ep.renderPPP(continentCodePrefix + ep.ContinentCode) } func parseTypeContinent(fields []string) (Endpoint, error) { if continentRegex.MatchString(fields[1]) { ep := &EndpointContinent{ ContinentCode: strings.TrimPrefix(strings.ToUpper(fields[1]), continentCodePrefix), } return ep.parsePPP(ep, fields) } return nil, nil } ================================================ FILE: service/profile/endpoints/endpoint-country.go ================================================ package endpoints import ( "context" "fmt" "regexp" "strings" "github.com/safing/portmaster/service/intel" ) var countryRegex = regexp.MustCompile(`^[A-Z]{2}$`) // EndpointCountry matches countries. type EndpointCountry struct { EndpointBase CountryCode string } // Matches checks whether the given entity matches this endpoint definition. func (ep *EndpointCountry) Matches(ctx context.Context, entity *intel.Entity) (EPResult, Reason) { if entity.IP == nil { return NoMatch, nil } if !entity.IPScope.IsGlobal() { return NoMatch, nil } countryInfo := entity.GetCountryInfo(ctx) if countryInfo == nil { return MatchError, ep.makeReason(ep, "", "country data not available to match") } if ep.CountryCode == countryInfo.Code { return ep.match( ep, entity, fmt.Sprintf("%s (%s)", countryInfo.Name, countryInfo.Code), "IP is located in", ) } return NoMatch, nil } func (ep *EndpointCountry) String() string { return ep.renderPPP(ep.CountryCode) } func parseTypeCountry(fields []string) (Endpoint, error) { if countryRegex.MatchString(fields[1]) { ep := &EndpointCountry{ CountryCode: strings.ToUpper(fields[1]), } return ep.parsePPP(ep, fields) } return nil, nil } ================================================ FILE: service/profile/endpoints/endpoint-domain.go ================================================ package endpoints import ( "context" "errors" "regexp" "strings" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/network/netutils" ) const ( domainMatchTypeExact uint8 = iota domainMatchTypeZone domainMatchTypeSuffix domainMatchTypePrefix domainMatchTypeContains ) var ( allowedDomainChars = regexp.MustCompile(`^[a-z0-9\.-]+$`) // looksLikeAnIP matches domains that look like an IP address. looksLikeAnIP = regexp.MustCompile(`^[0-9\.:]+$`) ) // EndpointDomain matches domains. type EndpointDomain struct { EndpointBase OriginalValue string Domain string DomainZone string MatchType uint8 } func (ep *EndpointDomain) check(entity *intel.Entity, domain string) (EPResult, Reason) { result, reason := ep.match(ep, entity, ep.OriginalValue, "domain matches") switch ep.MatchType { case domainMatchTypeExact: if domain == ep.Domain { return result, reason } case domainMatchTypeZone: if domain == ep.Domain { return result, reason } if strings.HasSuffix(domain, ep.DomainZone) { return result, reason } case domainMatchTypeSuffix: if strings.HasSuffix(domain, ep.Domain) { return result, reason } case domainMatchTypePrefix: if strings.HasPrefix(domain, ep.Domain) { return result, reason } case domainMatchTypeContains: if strings.Contains(domain, ep.Domain) { return result, reason } } return NoMatch, nil } // Matches checks whether the given entity matches this endpoint definition. func (ep *EndpointDomain) Matches(ctx context.Context, entity *intel.Entity) (EPResult, Reason) { domain, ok := entity.GetDomain(ctx, true /* mayUseReverseDomain */) if !ok { return NoMatch, nil } result, reason := ep.check(entity, domain) if result != NoMatch { return result, reason } if entity.CNAMECheckEnabled() { for _, cname := range entity.CNAME { result, reason = ep.check(entity, cname) if result == Denied { return result, reason } } } return NoMatch, nil } func (ep *EndpointDomain) String() string { return ep.renderPPP(ep.OriginalValue) } func parseTypeDomain(fields []string) (Endpoint, error) { domain := fields[1] ep := &EndpointDomain{ OriginalValue: domain, } // Fix domain ending. switch domain[len(domain)-1] { case '.', '*': default: domain += "." } // Check if this looks like an IP address. // At least the TLDs has characters. if looksLikeAnIP.MatchString(domain) { return nil, nil } // Fix domain case. domain = strings.ToLower(domain) needValidFQDN := true switch { case strings.HasPrefix(domain, "*") && strings.HasSuffix(domain, "*"): ep.MatchType = domainMatchTypeContains ep.Domain = strings.TrimPrefix(domain, "*") ep.Domain = strings.TrimSuffix(ep.Domain, "*") needValidFQDN = false case strings.HasSuffix(domain, "*"): ep.MatchType = domainMatchTypePrefix ep.Domain = strings.TrimSuffix(domain, "*") needValidFQDN = false // Prefix matching cannot be combined with zone matching if strings.HasPrefix(ep.Domain, ".") { return nil, nil } // Do not accept domains that look like an IP address and have a suffix wildcard. // This is confusing, because it looks like an IP Netmask matching rule. if looksLikeAnIP.MatchString(ep.Domain) { return nil, errors.New("use CIDR notation (eg. 10.0.0.0/24) for matching ip address ranges") } case strings.HasPrefix(domain, "*"): ep.MatchType = domainMatchTypeSuffix ep.Domain = strings.TrimPrefix(domain, "*") needValidFQDN = false case strings.HasPrefix(domain, "."): ep.MatchType = domainMatchTypeZone ep.Domain = strings.TrimPrefix(domain, ".") ep.DomainZone = "." + ep.Domain default: ep.MatchType = domainMatchTypeExact ep.Domain = domain } // Validate domain "content". switch { case needValidFQDN && !netutils.IsValidFqdn(ep.Domain): return nil, nil case !needValidFQDN && !allowedDomainChars.MatchString(ep.Domain): return nil, nil case strings.Contains(ep.Domain, ".."): // The above regex does not catch double dots. return nil, nil } return ep.parsePPP(ep, fields) } ================================================ FILE: service/profile/endpoints/endpoint-ip.go ================================================ package endpoints import ( "context" "net" "github.com/safing/portmaster/service/intel" ) // EndpointIP matches IPs. type EndpointIP struct { EndpointBase IP net.IP } // Matches checks whether the given entity matches this endpoint definition. func (ep *EndpointIP) Matches(_ context.Context, entity *intel.Entity) (EPResult, Reason) { if entity.IP == nil { return NoMatch, nil } if ep.IP.Equal(entity.IP) { return ep.match(ep, entity, ep.IP.String(), "IP matches") } return NoMatch, nil } func (ep *EndpointIP) String() string { return ep.renderPPP(ep.IP.String()) } func parseTypeIP(fields []string) (Endpoint, error) { ip := net.ParseIP(fields[1]) if ip != nil { ep := &EndpointIP{ IP: ip, } return ep.parsePPP(ep, fields) } return nil, nil } ================================================ FILE: service/profile/endpoints/endpoint-iprange.go ================================================ package endpoints import ( "context" "net" "github.com/safing/portmaster/service/intel" ) // EndpointIPRange matches IP ranges. type EndpointIPRange struct { EndpointBase Net *net.IPNet } // Matches checks whether the given entity matches this endpoint definition. func (ep *EndpointIPRange) Matches(_ context.Context, entity *intel.Entity) (EPResult, Reason) { if entity.IP == nil { return NoMatch, nil } if ep.Net.Contains(entity.IP) { return ep.match(ep, entity, ep.Net.String(), "IP is in") } return NoMatch, nil } func (ep *EndpointIPRange) String() string { return ep.renderPPP(ep.Net.String()) } func parseTypeIPRange(fields []string) (Endpoint, error) { _, net, err := net.ParseCIDR(fields[1]) if err == nil { ep := &EndpointIPRange{ Net: net, } return ep.parsePPP(ep, fields) } return nil, nil } ================================================ FILE: service/profile/endpoints/endpoint-lists.go ================================================ package endpoints import ( "context" "strings" "github.com/safing/portmaster/service/intel" ) // EndpointLists matches endpoint lists. type EndpointLists struct { EndpointBase ListSet []string Lists string } // Matches checks whether the given entity matches this endpoint definition. func (ep *EndpointLists) Matches(ctx context.Context, entity *intel.Entity) (EPResult, Reason) { if entity.MatchLists(ep.ListSet) { return ep.match(ep, entity, ep.Lists, "filterlist contains", "filterlist", entity.ListBlockReason()) } return NoMatch, nil } func (ep *EndpointLists) String() string { return ep.renderPPP(ep.Lists) } func parseTypeList(fields []string) (Endpoint, error) { if strings.HasPrefix(fields[1], "L:") { lists := strings.Split(strings.TrimPrefix(fields[1], "L:"), ",") ep := &EndpointLists{ ListSet: lists, Lists: "L:" + strings.Join(lists, ","), } return ep.parsePPP(ep, fields) } return nil, nil } ================================================ FILE: service/profile/endpoints/endpoint-scopes.go ================================================ package endpoints import ( "context" "strings" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/network/netutils" ) const ( scopeLocalhost = 1 scopeLocalhostName = "Localhost" scopeLocalhostMatcher = "localhost" scopeLAN = 2 scopeLANName = "LAN" scopeLANMatcher = "lan" scopeInternet = 4 scopeInternetName = "Internet" scopeInternetMatcher = "internet" ) // EndpointScope matches network scopes. type EndpointScope struct { EndpointBase scopes uint8 } // Matches checks whether the given entity matches this endpoint definition. func (ep *EndpointScope) Matches(_ context.Context, entity *intel.Entity) (EPResult, Reason) { if entity.IP == nil { return NoMatch, nil } var scope uint8 switch entity.IPScope { case netutils.HostLocal: scope = scopeLocalhost case netutils.LinkLocal: scope = scopeLAN case netutils.SiteLocal: scope = scopeLAN case netutils.Global: scope = scopeInternet case netutils.LocalMulticast: scope = scopeLAN case netutils.GlobalMulticast: scope = scopeInternet case netutils.Undefined, netutils.Invalid: return NoMatch, nil } if ep.scopes&scope > 0 { return ep.match(ep, entity, ep.Scopes(), "scope matches") } return NoMatch, nil } // Scopes returns the string representation of all scopes. func (ep *EndpointScope) Scopes() string { // single scope switch ep.scopes { case scopeLocalhost: return scopeLocalhostName case scopeLAN: return scopeLANName case scopeInternet: return scopeInternetName } // multiple scopes var s []string if ep.scopes&scopeLocalhost > 0 { s = append(s, scopeLocalhostName) } if ep.scopes&scopeLAN > 0 { s = append(s, scopeLANName) } if ep.scopes&scopeInternet > 0 { s = append(s, scopeInternetName) } return strings.Join(s, ",") } func (ep *EndpointScope) String() string { return ep.renderPPP(ep.Scopes()) } func parseTypeScope(fields []string) (Endpoint, error) { ep := &EndpointScope{} for _, val := range strings.Split(strings.ToLower(fields[1]), ",") { switch val { case scopeLocalhostMatcher: ep.scopes ^= scopeLocalhost case scopeLANMatcher: ep.scopes ^= scopeLAN case scopeInternetMatcher: ep.scopes ^= scopeInternet default: return nil, nil } } return ep.parsePPP(ep, fields) } ================================================ FILE: service/profile/endpoints/endpoint.go ================================================ package endpoints import ( "context" "fmt" "strconv" "strings" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/network/reference" ) // Endpoint describes an Endpoint Matcher. type Endpoint interface { Matches(ctx context.Context, entity *intel.Entity) (EPResult, Reason) String() string } // EndpointBase provides general functions for implementing an Endpoint to reduce boilerplate. type EndpointBase struct { //nolint:maligned // TODO Protocol uint8 StartPort uint16 EndPort uint16 Permitted bool } func (ep *EndpointBase) match(s fmt.Stringer, entity *intel.Entity, value, desc string, keyval ...interface{}) (EPResult, Reason) { result := ep.matchesPPP(entity) if result == NoMatch { return result, nil } return result, ep.makeReason(s, value, desc, keyval...) } func (ep *EndpointBase) makeReason(s fmt.Stringer, value, desc string, keyval ...interface{}) Reason { r := &reason{ description: desc, Filter: s.String(), Permitted: ep.Permitted, Value: value, } r.Extra = make(map[string]interface{}) for idx := 0; idx < len(keyval)/2; idx += 2 { key := keyval[idx] val := keyval[idx+1] if keyName, ok := key.(string); ok { r.Extra[keyName] = val } } return r } func (ep *EndpointBase) matchesPPP(entity *intel.Entity) (result EPResult) { // only check if protocol is defined if ep.Protocol > 0 { // if protocol does not match, return NoMatch if entity.Protocol != ep.Protocol { return NoMatch } } // only check if port is defined if ep.StartPort > 0 { // if port does not match, return NoMatch if entity.DstPort() < ep.StartPort || entity.DstPort() > ep.EndPort { return NoMatch } } // protocol and port matched or were defined as any if ep.Permitted { return Permitted } return Denied } func (ep *EndpointBase) renderPPP(s string) string { var rendered string if ep.Permitted { rendered = "+ " + s } else { rendered = "- " + s } if ep.Protocol > 0 || ep.StartPort > 0 { if ep.Protocol > 0 { rendered += " " + reference.GetProtocolName(ep.Protocol) } else { rendered += " *" } if ep.StartPort > 0 { if ep.StartPort == ep.EndPort { rendered += "/" + reference.GetPortName(ep.StartPort) } else { rendered += "/" + strconv.Itoa(int(ep.StartPort)) + "-" + strconv.Itoa(int(ep.EndPort)) } } } return rendered } func (ep *EndpointBase) parsePPP(typedEp Endpoint, fields []string) (Endpoint, error) { //nolint:gocognit // TODO switch len(fields) { case 2: // nothing else to do here case 3: // parse protocol and port(s) var ok bool splitted := strings.Split(fields[2], "/") if len(splitted) > 2 { return nil, invalidDefinitionError(fields, "protocol and port must be in format /") } // protocol switch splitted[0] { case "": return nil, invalidDefinitionError(fields, "protocol can't be empty") case "*": // any protocol that supports ports default: n, err := strconv.ParseUint(splitted[0], 10, 8) n8 := uint8(n) if err != nil { // maybe it's a name? n8, ok = reference.GetProtocolNumber(splitted[0]) if !ok { return nil, invalidDefinitionError(fields, "protocol number parsing error") } } ep.Protocol = n8 } // port(s) if len(splitted) > 1 { switch splitted[1] { case "", "*": return nil, invalidDefinitionError(fields, "omit port if should match any") default: portSplitted := strings.Split(splitted[1], "-") if len(portSplitted) > 2 { return nil, invalidDefinitionError(fields, "ports must be in format from-to") } // parse start port n, err := strconv.ParseUint(portSplitted[0], 10, 16) n16 := uint16(n) if err != nil { // maybe it's a name? n16, ok = reference.GetPortNumber(portSplitted[0]) if !ok { return nil, invalidDefinitionError(fields, "port number parsing error") } } if n16 == 0 { return nil, invalidDefinitionError(fields, "port number cannot be 0") } ep.StartPort = n16 // parse end port if len(portSplitted) > 1 { n, err = strconv.ParseUint(portSplitted[1], 10, 16) n16 = uint16(n) if err != nil { // maybe it's a name? n16, ok = reference.GetPortNumber(portSplitted[1]) if !ok { return nil, invalidDefinitionError(fields, "port number parsing error") } } } if n16 == 0 { return nil, invalidDefinitionError(fields, "port number cannot be 0") } ep.EndPort = n16 } } // check if anything was parsed if ep.Protocol == 0 && ep.StartPort == 0 { return nil, invalidDefinitionError(fields, "omit protocol/port if should match any") } default: return nil, invalidDefinitionError(fields, "there should be only 2 or 3 segments") } switch fields[0] { case "+": ep.Permitted = true case "-": ep.Permitted = false default: return nil, invalidDefinitionError(fields, "invalid permission prefix") } return typedEp, nil } func invalidDefinitionError(fields []string, msg string) error { return fmt.Errorf(`invalid endpoint definition: "%s" - %s`, strings.Join(fields, " "), msg) } //nolint:gocognit,nakedret func parseEndpoint(value string) (endpoint Endpoint, err error) { fields := strings.Fields(value) if len(fields) < 2 { return nil, fmt.Errorf(`invalid endpoint definition: "%s"`, value) } // Remove comment. for i, field := range fields { if strings.HasPrefix(field, "#") { fields = fields[:i] break } } // any if endpoint, err = parseTypeAny(fields); endpoint != nil || err != nil { return } // ip if endpoint, err = parseTypeIP(fields); endpoint != nil || err != nil { return } // ip range if endpoint, err = parseTypeIPRange(fields); endpoint != nil || err != nil { return } // country if endpoint, err = parseTypeCountry(fields); endpoint != nil || err != nil { return } // continent if endpoint, err = parseTypeContinent(fields); endpoint != nil || err != nil { return } // asn if endpoint, err = parseTypeASN(fields); endpoint != nil || err != nil { return } // scopes if endpoint, err = parseTypeScope(fields); endpoint != nil || err != nil { return } // lists if endpoint, err = parseTypeList(fields); endpoint != nil || err != nil { return } // domain if endpoint, err = parseTypeDomain(fields); endpoint != nil || err != nil { return } return nil, fmt.Errorf(`unknown endpoint definition: "%s"`, value) } ================================================ FILE: service/profile/endpoints/endpoint_test.go ================================================ package endpoints import ( "strings" "testing" ) func TestEndpointParsing(t *testing.T) { t.Parallel() // any (basics) testParsing(t, "- *") testParsing(t, "+ *") // domain testDomainParsing(t, "- *bad*", domainMatchTypeContains, "bad") testDomainParsing(t, "- bad*", domainMatchTypePrefix, "bad") testDomainParsing(t, "- *bad.com", domainMatchTypeSuffix, "bad.com.") testDomainParsing(t, "- .bad.com", domainMatchTypeZone, "bad.com.") testDomainParsing(t, "- bad.com", domainMatchTypeExact, "bad.com.") testDomainParsing(t, "- www.bad.com.", domainMatchTypeExact, "www.bad.com.") testDomainParsing(t, "- www.bad.com", domainMatchTypeExact, "www.bad.com.") // ip testParsing(t, "+ 127.0.0.1") testParsing(t, "+ 192.168.0.1") testParsing(t, "+ ::1") testParsing(t, "+ 2606:4700:4700::1111") // ip testParsing(t, "+ 127.0.0.0/8") testParsing(t, "+ 192.168.0.0/24") testParsing(t, "+ 2606:4700:4700::/48") // country testParsing(t, "+ DE") testParsing(t, "+ AT") testParsing(t, "+ CH") testParsing(t, "+ AS") // asn testParsing(t, "+ AS1") testParsing(t, "+ AS12") testParsing(t, "+ AS123") testParsing(t, "+ AS1234") testParsing(t, "+ AS12345") // network scope testParsing(t, "+ Localhost") testParsing(t, "+ LAN") testParsing(t, "+ Internet") testParsing(t, "+ Localhost,LAN,Internet") // protocol and ports testParsing(t, "+ * TCP/1-1024") testParsing(t, "+ * */DNS") testParsing(t, "+ * ICMP") testParsing(t, "+ * 127") testParsing(t, "+ * UDP/1234") testParsing(t, "+ * TCP/HTTP") testParsing(t, "+ * TCP/80-443") // TODO: Test fails: // testParsing(t, "+ 1234") } func testParsing(t *testing.T, value string) { t.Helper() ep, err := parseEndpoint(value) if err != nil { t.Error(err) return } // t.Logf("%T: %+v", ep, ep) if value != ep.String() { t.Errorf(`stringified endpoint mismatch: original was "%s", parsed is "%s"`, value, ep.String()) } } func testDomainParsing(t *testing.T, value string, matchType uint8, matchValue string) { t.Helper() testParsing(t, value) epGeneric, err := parseTypeDomain(strings.Fields(value)) if err != nil { t.Error(err) return } ep := epGeneric.(*EndpointDomain) //nolint:forcetypeassert if ep.MatchType != matchType { t.Errorf(`error parsing domain endpoint "%s": match type should be %d, was %d`, value, matchType, ep.MatchType) } if ep.Domain != matchValue { t.Errorf(`error parsing domain endpoint "%s": match domain value should be %s, was %s`, value, matchValue, ep.Domain) } } ================================================ FILE: service/profile/endpoints/endpoints.go ================================================ package endpoints import ( "context" "errors" "fmt" "strings" "github.com/safing/portmaster/service/intel" ) // Endpoints is a list of permitted or denied endpoints. type Endpoints []Endpoint // EPResult represents the result of a check against an EndpointPermission. type EPResult uint8 // Endpoint matching return values. const ( NoMatch EPResult = iota MatchError Denied Permitted ) // IsDecision returns true if result represents a decision // and false if result is NoMatch or Undeterminable. func IsDecision(result EPResult) bool { return result == Denied || result == Permitted || result == MatchError } // ParseEndpoints parses a list of endpoints and returns a list of Endpoints for matching. func ParseEndpoints(entries []string) (Endpoints, error) { var firstErr error var errCnt int endpoints := make(Endpoints, 0, len(entries)) entriesLoop: for _, entry := range entries { ep, err := parseEndpoint(entry) if err != nil { errCnt++ if firstErr == nil { firstErr = err } continue entriesLoop } endpoints = append(endpoints, ep) } if firstErr != nil { if errCnt > 0 { return endpoints, fmt.Errorf("encountered %d errors, first was: %w", errCnt, firstErr) } return endpoints, firstErr } return endpoints, nil } // ListEntryValidationRegex is a regex to bullshit check endpoint list entries. var ListEntryValidationRegex = strings.Join([]string{ `^(\+|\-) `, // Rule verdict. `(! +)?`, // Invert matching. `[A-z0-9\.:\-*/]+`, // Entity matching. `( `, // Start of optional matching. `[A-z0-9*]+`, // Protocol matching. `(/[A-z0-9]+(\-[A-z0-9]+)?)?`, // Port and port range matching. `)?`, // End of optional matching. `( +#.*)?`, // Optional comment. }, "") // ValidateEndpointListConfigOption validates the given value. func ValidateEndpointListConfigOption(value interface{}) error { list, ok := value.([]string) if !ok { return errors.New("invalid type") } _, err := ParseEndpoints(list) return err } // IsSet returns whether the Endpoints object is "set". func (e Endpoints) IsSet() bool { return len(e) > 0 } // Match checks whether the given entity matches any of the endpoint definitions in the list. func (e Endpoints) Match(ctx context.Context, entity *intel.Entity) (result EPResult, reason Reason) { for _, entry := range e { if entry == nil { continue } if result, reason = entry.Matches(ctx, entity); result != NoMatch { return } } return NoMatch, nil } // MatchMulti checks whether the given entities match any of the endpoint // definitions in the list. Every rule is evaluated against all given entities // and only if not match was registered, the next rule is evaluated. func (e Endpoints) MatchMulti(ctx context.Context, entities ...*intel.Entity) (result EPResult, reason Reason) { for _, entry := range e { if entry == nil { continue } for _, entity := range entities { if entity == nil { continue } if result, reason = entry.Matches(ctx, entity); result != NoMatch { return } } } return NoMatch, nil } func (e Endpoints) String() string { s := make([]string, 0, len(e)) for _, entry := range e { s = append(s, entry.String()) } return fmt.Sprintf("[%s]", strings.Join(s, ", ")) } func (epr EPResult) String() string { switch epr { case NoMatch: return "No Match" case MatchError: return "Match Error" case Denied: return "Denied" case Permitted: return "Permitted" default: return "Unknown" } } ================================================ FILE: service/profile/endpoints/endpoints_test.go ================================================ package endpoints import ( "context" "fmt" "net" "os" "path/filepath" "runtime" "testing" "github.com/stretchr/testify/assert" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/configure" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/service/ui" "github.com/safing/portmaster/service/updates" ) type testInstance struct { db *dbmodule.DBModule api *api.API config *config.Config intelUpdates *updates.Updater geoip *geoip.GeoIP } func (stub *testInstance) IntelUpdates() *updates.Updater { return stub.intelUpdates } func (stub *testInstance) API() *api.API { return stub.api } func (stub *testInstance) Config() *config.Config { return stub.config } func (stub *testInstance) Notifications() *notifications.Notifications { return nil } func (stub *testInstance) Ready() bool { return true } func (stub *testInstance) Restart() {} func (stub *testInstance) Shutdown() {} func (stub *testInstance) SetCmdLineOperation(f func() error) {} func (stub *testInstance) BinaryUpdates() *updates.Updater { return nil } func (stub *testInstance) UI() *ui.UI { return nil } func (stub *testInstance) DataDir() string { return _dataDir } var _dataDir string func runTest(m *testing.M) error { var err error // Create a temporary directory for testing _dataDir, err = os.MkdirTemp("", "") if err != nil { return fmt.Errorf("failed to create temporary data directory: %w", err) } defer func() { _ = os.RemoveAll(_dataDir) }() // Initialize the Intel update configuration intelUpdateConfig := updates.Config{ Name: configure.DefaultIntelIndexName, Directory: filepath.Join(_dataDir, "test_intel"), DownloadDirectory: filepath.Join(_dataDir, "test_download_intel"), PurgeDirectory: filepath.Join(_dataDir, "test_upgrade_obsolete_intel"), IndexURLs: configure.DefaultIntelIndexURLs, IndexFile: "index.json", AutoCheck: true, AutoDownload: true, AutoApply: true, } // Set the default API listen address api.SetDefaultAPIListenAddress("0.0.0.0:8080") // Initialize the instance with the necessary components stub := &testInstance{} stub.db, err = dbmodule.New(stub) if err != nil { return fmt.Errorf("failed to create database: %w", err) } stub.config, err = config.New(stub) if err != nil { return fmt.Errorf("failed to create config: %w", err) } stub.api, err = api.New(stub) if err != nil { return fmt.Errorf("failed to create api: %w", err) } stub.intelUpdates, err = updates.New(stub, "Intel Updater", intelUpdateConfig) if err != nil { return fmt.Errorf("failed to create updates: %w", err) } stub.geoip, err = geoip.New(stub) if err != nil { return fmt.Errorf("failed to create geoip: %w", err) } err = stub.db.Start() if err != nil { return fmt.Errorf("Failed to start database: %w", err) } err = stub.config.Start() if err != nil { return fmt.Errorf("Failed to start config: %w", err) } err = stub.api.Start() if err != nil { return fmt.Errorf("Failed to start api: %w", err) } err = stub.intelUpdates.Start() if err != nil { return fmt.Errorf("Failed to start updates: %w", err) } err = stub.geoip.Start() if err != nil { return fmt.Errorf("Failed to start geoip: %w", err) } m.Run() return nil } func TestMain(m *testing.M) { if err := runTest(m); err != nil { fmt.Printf("%s", err) os.Exit(1) } } func testEndpointMatch(t *testing.T, ep Endpoint, entity *intel.Entity, expectedResult EPResult) { t.Helper() result, _ := ep.Matches(context.TODO(), entity) if result != expectedResult { t.Errorf( "line %d: unexpected result for endpoint %s and entity %+v: result=%s, expected=%s", getLineNumberOfCaller(1), ep, entity, result, expectedResult, ) } } func testFormat(t *testing.T, endpoint string, shouldSucceed bool) { t.Helper() _, err := parseEndpoint(endpoint) if shouldSucceed { assert.NoError(t, err) } else { assert.Error(t, err) } } func TestEndpointFormat(t *testing.T) { t.Parallel() testFormat(t, "+ .", false) testFormat(t, "+ .at", true) testFormat(t, "+ .at.", true) testFormat(t, "+ 1.at", true) testFormat(t, "+ 1.at.", true) testFormat(t, "+ 1.f.ix.de.", true) testFormat(t, "+ *contains*", true) testFormat(t, "+ *has.suffix", true) testFormat(t, "+ *.has.suffix", true) testFormat(t, "+ *has.prefix*", true) testFormat(t, "+ *has.prefix.*", true) testFormat(t, "+ .sub.and.prefix.*", false) testFormat(t, "+ *.sub..and.prefix.*", false) } func TestEndpointMatching(t *testing.T) { //nolint:maintidx // TODO t.Parallel() // ANY ep, err := parseEndpoint("+ *") if err != nil { t.Fatal(err) } testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), Permitted) // DOMAIN // wildcard domains ep, err = parseEndpoint("+ *example.com") if err != nil { t.Fatal(err) } testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc.example.com.", }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc-example.com.", }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc.example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc-example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), Permitted) ep, err = parseEndpoint("+ *.example.com") if err != nil { t.Fatal(err) } testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", }).Init(0), NoMatch) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc.example.com.", }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc-example.com.", }).Init(0), NoMatch) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), NoMatch) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc.example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc-example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), NoMatch) ep, err = parseEndpoint("+ .example.com") if err != nil { t.Fatal(err) } testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc.example.com.", }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc-example.com.", }).Init(0), NoMatch) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc.example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc-example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), NoMatch) ep, err = parseEndpoint("+ example.*") if err != nil { t.Fatal(err) } testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc.example.com.", }).Init(0), NoMatch) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc.example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), NoMatch) ep, err = parseEndpoint("+ *.exampl*") if err != nil { t.Fatal(err) } testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", }).Init(0), NoMatch) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc.example.com.", }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), NoMatch) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "abc.example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), Permitted) ep, err = parseEndpoint("+ *.com.") if err != nil { t.Fatal(err) } testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.org.", }).Init(0), NoMatch) // protocol ep, err = parseEndpoint("+ example.com UDP") if err != nil { t.Fatal(err) } testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), NoMatch) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 17, Port: 443, }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", }).Init(0), NoMatch) // ports ep, err = parseEndpoint("+ example.com 17/442-444") if err != nil { t.Fatal(err) } entity := (&intel.Entity{ Domain: "example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 17, Port: 441, }).Init(0) testEndpointMatch(t, ep, entity, NoMatch) entity.Port = 442 entity.Init(0) testEndpointMatch(t, ep, entity, Permitted) entity.Port = 443 entity.Init(0) testEndpointMatch(t, ep, entity, Permitted) entity.Port = 444 entity.Init(0) testEndpointMatch(t, ep, entity, Permitted) entity.Port = 445 entity.Init(0) testEndpointMatch(t, ep, entity, NoMatch) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", }).Init(0), NoMatch) // IP ep, err = parseEndpoint("+ 10.2.3.4") if err != nil { t.Fatal(err) } testEndpointMatch(t, ep, (&intel.Entity{ Domain: "", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", IP: net.ParseIP("10.2.3.4"), Protocol: 17, Port: 443, }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "", IP: net.ParseIP("10.2.3.3"), Protocol: 6, Port: 443, }).Init(0), NoMatch) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", IP: net.ParseIP("10.2.3.5"), Protocol: 17, Port: 443, }).Init(0), NoMatch) testEndpointMatch(t, ep, (&intel.Entity{ Domain: "example.com.", }).Init(0), NoMatch) // IP Range ep, err = parseEndpoint("+ 10.2.3.0/24") if err != nil { t.Fatal(err) } testEndpointMatch(t, ep, (&intel.Entity{ IP: net.ParseIP("10.2.2.4"), }).Init(0), NoMatch) testEndpointMatch(t, ep, (&intel.Entity{ IP: net.ParseIP("10.2.3.4"), }).Init(0), Permitted) testEndpointMatch(t, ep, (&intel.Entity{ IP: net.ParseIP("10.2.4.4"), }).Init(0), NoMatch) // Skip test that need the geoip database in CI. if !testing.Short() { // ASN ep, err = parseEndpoint("+ AS15169") if err != nil { t.Fatal(err) } entity = (&intel.Entity{IP: net.IPv4(8, 8, 8, 8)}).Init(0) testEndpointMatch(t, ep, entity, Permitted) entity = (&intel.Entity{IP: net.IPv4(1, 1, 1, 1)}).Init(0) testEndpointMatch(t, ep, entity, NoMatch) // Country ep, err = parseEndpoint("+ AT") if err != nil { t.Fatal(err) } entity = (&intel.Entity{IP: net.IPv4(194, 232, 104, 1)}).Init(0) // orf.at testEndpointMatch(t, ep, entity, Permitted) entity = (&intel.Entity{IP: net.IPv4(151, 101, 1, 164)}).Init(0) // nytimes.com testEndpointMatch(t, ep, entity, NoMatch) } // Scope ep, err = parseEndpoint("+ Localhost,LAN") if err != nil { t.Fatal(err) } entity = (&intel.Entity{IP: net.IPv4(192, 168, 0, 1)}).Init(0) testEndpointMatch(t, ep, entity, Permitted) entity = (&intel.Entity{IP: net.IPv4(151, 101, 1, 164)}).Init(0) // nytimes.com testEndpointMatch(t, ep, entity, NoMatch) // Port with protocol wildcard ep, err = parseEndpoint("+ * */443") if err != nil { t.Fatal(err) } entity = &intel.Entity{ Domain: "", IP: net.ParseIP("10.2.3.4"), Protocol: 6, Port: 443, } entity.Init(0) testEndpointMatch(t, ep, entity, Permitted) // Lists // Skip test that need the filter lists in CI. if !testing.Short() { _, err = parseEndpoint("+ L:A,B,C") if err != nil { t.Fatal(err) } } // TODO: write test for lists matcher } func getLineNumberOfCaller(levels int) int { _, _, line, _ := runtime.Caller(levels + 1) //nolint:dogsled return line } ================================================ FILE: service/profile/endpoints/reason.go ================================================ package endpoints // Reason describes the reason why an endpoint has been // permitted or blocked. type Reason interface { // String should return a human readable string // describing the decision reason. String() string // Context returns the context that was used // for the decision. Context() interface{} } type reason struct { description string Filter string Value string Permitted bool Extra map[string]interface{} } func (r *reason) String() string { prefix := "denied by rule: " if r.Permitted { prefix = "allowed by rule: " } return prefix + r.description + " " + r.Filter[2:] } func (r *reason) Context() interface{} { return r } ================================================ FILE: service/profile/fingerprint.go ================================================ package profile import ( "fmt" "regexp" "strings" "golang.org/x/exp/slices" "github.com/safing/jess/lhash" "github.com/safing/structures/container" ) // # Matching and Scores // // There are three levels: // // 1. Type: What matched? // 1. Tag: 50.000 points // 2. Cmdline: 40.000 points // 3. Env: 30.000 points // 4. MatchingPath: 20.000 points // 5. Path: 10.000 points // 2. Operation: How was it mached? // 1. Equals: 3.000 points // 2. Prefix: 2.000 points // 3. Regex: 1.000 points // 3. How "strong" was the match? // 1. Equals: Length of path (irrelevant) // 2. Prefix: Length of prefix // 3. Regex: Length of match // Fingerprint Type IDs. const ( FingerprintTypeTagID = "tag" FingerprintTypeCmdlineID = "cmdline" FingerprintTypeEnvID = "env" FingerprintTypePathID = "path" // Matches both MatchingPath and Path. FingerprintOperationEqualsID = "equals" FingerprintOperationPrefixID = "prefix" FingerprintOperationRegexID = "regex" tagMatchBaseScore = 50_000 cmdlineMatchBaseScore = 40_000 envMatchBaseScore = 30_000 matchingPathMatchBaseScore = 20_000 pathMatchBaseScore = 10_000 fingerprintEqualsBaseScore = 3_000 fingerprintPrefixBaseScore = 2_000 fingerprintRegexBaseScore = 1_000 maxMatchStrength = 499 ) type ( // Fingerprint defines a way of matching a process. // The Key is only valid - but required - for some types. Fingerprint struct { Type string Key string // Key must always fully match. Operation string Value string // MergedFrom holds the ID of the profile from which this fingerprint was // merged from. The merged profile should create a new profile ID derived // from the new fingerprints and add all fingerprints with this field set // to the originating profile ID MergedFrom string // `json:"mergedFrom,omitempty"` } // Tag represents a simple key/value kind of tag used in process metadata // and fingerprints. Tag struct { Key string Value string } // MatchingData is an interface to fetching data in the matching process. MatchingData interface { Tags() []Tag Env() map[string]string Path() string MatchingPath() string Cmdline() string } matchingFingerprint interface { MatchesKey(key string) bool Match(value string) (score int) } ) // MatchesKey returns whether the optional fingerprint key (for some types // only) matches the given key. func (fp Fingerprint) MatchesKey(key string) bool { return key == fp.Key } // KeyInTags checks is the given key is in the tags. func KeyInTags(tags []Tag, key string) bool { for _, tag := range tags { if key == tag.Key { return true } } return false } // KeyAndValueInTags checks is the given key/value pair is in the tags. func KeyAndValueInTags(tags []Tag, key, value string) bool { for _, tag := range tags { if key == tag.Key && value == tag.Value { return true } } return false } type fingerprintEquals struct { Fingerprint } func (fp fingerprintEquals) Match(value string) (score int) { if value == fp.Value { return fingerprintEqualsBaseScore + checkMatchStrength(len(fp.Value)) } return 0 } type fingerprintPrefix struct { Fingerprint } func (fp fingerprintPrefix) Match(value string) (score int) { if strings.HasPrefix(value, fp.Value) { return fingerprintPrefixBaseScore + checkMatchStrength(len(fp.Value)) } return 0 } type fingerprintRegex struct { Fingerprint regex *regexp.Regexp } func (fp fingerprintRegex) Match(value string) (score int) { // Find best match. for _, match := range fp.regex.FindAllString(value, -1) { // Save match length if higher than score. // This will also ignore empty matches. if len(match) > score { score = len(match) } } // Add base score and return if anything was found. if score > 0 { return fingerprintRegexBaseScore + checkMatchStrength(score) } return 0 } // ParsedFingerprints holds parsed fingerprints for fast usage. type ParsedFingerprints struct { tagPrints []matchingFingerprint envPrints []matchingFingerprint pathPrints []matchingFingerprint cmdlinePrints []matchingFingerprint } // ParseFingerprints parses the fingerprints to make them ready for matching. func ParseFingerprints(raw []Fingerprint, deprecatedLinkedPath string) (parsed *ParsedFingerprints, firstErr error) { parsed = &ParsedFingerprints{} // Add deprecated LinkedPath to fingerprints, if they are empty. // TODO: Remove in v1.5 if len(raw) == 0 && deprecatedLinkedPath != "" { parsed.pathPrints = append(parsed.pathPrints, &fingerprintEquals{ Fingerprint: Fingerprint{ Type: FingerprintTypePathID, Operation: FingerprintOperationEqualsID, Value: deprecatedLinkedPath, }, }) } // Parse all fingerprints. // Do not fail when one fails, instead return the first encountered error. for _, entry := range raw { // Check type and required key. switch entry.Type { case FingerprintTypeTagID, FingerprintTypeEnvID: if entry.Key == "" { if firstErr == nil { firstErr = fmt.Errorf("%s fingerprint is missing key", entry.Type) } continue } case FingerprintTypePathID, FingerprintTypeCmdlineID: // Don't need a key. default: // Unknown type. if firstErr == nil { firstErr = fmt.Errorf("unknown fingerprint type: %q", entry.Type) } continue } // Create and/or collect operation match functions. switch entry.Operation { case FingerprintOperationEqualsID: parsed.addMatchingFingerprint(entry, fingerprintEquals{entry}) case FingerprintOperationPrefixID: parsed.addMatchingFingerprint(entry, fingerprintPrefix{entry}) case FingerprintOperationRegexID: regex, err := regexp.Compile(entry.Value) if err != nil { if firstErr == nil { firstErr = fmt.Errorf("failed to compile regex fingerprint: %s", entry.Value) } } else { parsed.addMatchingFingerprint(entry, fingerprintRegex{ Fingerprint: entry, regex: regex, }) } default: if firstErr == nil { firstErr = fmt.Errorf("unknown fingerprint operation: %q", entry.Operation) } } } return parsed, firstErr } func (parsed *ParsedFingerprints) addMatchingFingerprint(fp Fingerprint, matchingPrint matchingFingerprint) { switch fp.Type { case FingerprintTypeTagID: parsed.tagPrints = append(parsed.tagPrints, matchingPrint) case FingerprintTypeEnvID: parsed.envPrints = append(parsed.envPrints, matchingPrint) case FingerprintTypePathID: parsed.pathPrints = append(parsed.pathPrints, matchingPrint) case FingerprintTypeCmdlineID: parsed.cmdlinePrints = append(parsed.cmdlinePrints, matchingPrint) default: // This should never happen, as the types are checked already. panic(fmt.Sprintf("unknown fingerprint type: %q", fp.Type)) } } // MatchFingerprints returns the highest matching score of the given // fingerprints and matching data. func MatchFingerprints(prints *ParsedFingerprints, md MatchingData) (highestScore int) { // Check tags. tags := md.Tags() if len(tags) > 0 { for _, tagPrint := range prints.tagPrints { for _, tag := range tags { // Check if tag key matches. if !tagPrint.MatchesKey(tag.Key) { continue } // Try matching the tag value. score := tagPrint.Match(tag.Value) if score > highestScore { highestScore = score } } } // If something matched, add base score and return. if highestScore > 0 { return tagMatchBaseScore + highestScore } } // Check cmdline. cmdline := md.Cmdline() if cmdline != "" { for _, cmdlinePrint := range prints.cmdlinePrints { if score := cmdlinePrint.Match(cmdline); score > highestScore { highestScore = score } } if highestScore > 0 { return cmdlineMatchBaseScore + highestScore } } // Check env. for _, envPrint := range prints.envPrints { for key, value := range md.Env() { // Check if env key matches. if !envPrint.MatchesKey(key) { continue } // Try matching the env value. score := envPrint.Match(value) if score > highestScore { highestScore = score } } } // If something matched, add base score and return. if highestScore > 0 { return envMatchBaseScore + highestScore } // Check matching path. matchingPath := md.MatchingPath() if matchingPath != "" { for _, pathPrint := range prints.pathPrints { // Try matching the path value. score := pathPrint.Match(matchingPath) if score > highestScore { highestScore = score } } // If something matched, add base score and return. if highestScore > 0 { return matchingPathMatchBaseScore + highestScore } } // Check path. path := md.Path() if path != "" { for _, pathPrint := range prints.pathPrints { // Try matching the path value. score := pathPrint.Match(path) if score > highestScore { highestScore = score } } // If something matched, add base score and return. if highestScore > 0 { return pathMatchBaseScore + highestScore } } // Nothing matched. return 0 } func checkMatchStrength(value int) int { if value > maxMatchStrength { return maxMatchStrength } if value < -maxMatchStrength { return -maxMatchStrength } return value } const ( deriveFPKeyIDForItemStart = iota + 1 deriveFPKeyIDForType deriveFPKeyIDForKey deriveFPKeyIDForOperation deriveFPKeyIDForValue ) // DeriveProfileID derives a profile ID from the given fingerprints. func DeriveProfileID(fps []Fingerprint) string { // Sort the fingerprints. sortAndCompactFingerprints(fps) // Compile data for hashing. c := container.New(nil) c.AppendInt(len(fps)) for _, fp := range fps { c.AppendNumber(deriveFPKeyIDForItemStart) if fp.Type != "" { c.AppendNumber(deriveFPKeyIDForType) c.AppendAsBlock([]byte(fp.Type)) } if fp.Key != "" { c.AppendNumber(deriveFPKeyIDForKey) c.AppendAsBlock([]byte(fp.Key)) } if fp.Operation != "" { c.AppendNumber(deriveFPKeyIDForOperation) c.AppendAsBlock([]byte(fp.Operation)) } if fp.Value != "" { c.AppendNumber(deriveFPKeyIDForValue) c.AppendAsBlock([]byte(fp.Value)) } } // Hash and return. h := lhash.Digest(lhash.SHA3_256, c.CompileData()) return h.Base58() } func sortAndCompactFingerprints(fps []Fingerprint) []Fingerprint { // Sort. slices.SortFunc[[]Fingerprint, Fingerprint](fps, func(a, b Fingerprint) int { switch { case a.Type != b.Type: return strings.Compare(a.Type, b.Type) case a.Key != b.Key: return strings.Compare(a.Key, b.Key) case a.Operation != b.Operation: return strings.Compare(a.Operation, b.Operation) case a.Value != b.Value: return strings.Compare(a.Value, b.Value) case a.MergedFrom != b.MergedFrom: return strings.Compare(a.MergedFrom, b.MergedFrom) default: return 0 } }) // De-duplicate. // Important: Even if the fingerprint is the same, but MergedFrom is // different, we need to keep the separate fingerprint, so that new installs // will cleanly update to the synced state: Auto-generated profiles need to // be automatically replaced by the merged version. fps = slices.CompactFunc[[]Fingerprint, Fingerprint](fps, func(a, b Fingerprint) bool { return a.Type == b.Type && a.Key == b.Key && a.Operation == b.Operation && a.Value == b.Value && a.MergedFrom == b.MergedFrom }) return fps } ================================================ FILE: service/profile/fingerprint_test.go ================================================ package profile import ( "math/rand" "testing" "time" "github.com/stretchr/testify/assert" ) func TestDeriveProfileID(t *testing.T) { t.Parallel() fps := []Fingerprint{ { Type: FingerprintTypePathID, Operation: FingerprintOperationEqualsID, Value: "/sbin/init", }, { Type: FingerprintTypePathID, Operation: FingerprintOperationPrefixID, Value: "/", }, { Type: FingerprintTypeEnvID, Key: "PORTMASTER_PROFILE", Operation: FingerprintOperationEqualsID, Value: "TEST-1", }, { Type: FingerprintTypeTagID, Key: "tag-key-1", Operation: FingerprintOperationEqualsID, Value: "tag-key-2", }, } // Create rand source for shuffling. rnd := rand.New(rand.NewSource(time.Now().UnixNano())) //nolint:gosec // Test 100 times. for range 100 { // Shuffle fingerprints. rnd.Shuffle(len(fps), func(i, j int) { fps[i], fps[j] = fps[j], fps[i] }) // Check if fingerprint matches. id := DeriveProfileID(fps) assert.Equal(t, "PTSRP7rdCnmvdjRoPMTrtjj7qk7PxR1a9YdBWUGwnZXJh2", id) } } ================================================ FILE: service/profile/framework.go ================================================ package profile // DEACTIVATED // import ( // "fmt" // "os" // "path/filepath" // "regexp" // "strings" // // "github.com/safing/portmaster/base/log" // ) // // type Framework struct { // // go hirarchy up // FindParent uint8 `json:",omitempty bson:",omitempty"` // // get path from parent, amount of levels to go up the tree (1 means parent, 2 means parent of parents, and so on) // MergeWithParent bool `json:",omitempty bson:",omitempty"` // // instead of getting the path of the parent, merge with it by presenting connections as if they were from that parent // // // go hirarchy down // Find string `json:",omitempty bson:",omitempty"` // // Regular expression for finding path elements // Build string `json:",omitempty bson:",omitempty"` // // Path definitions for building path // Virtual bool `json:",omitempty bson:",omitempty"` // // Treat resulting path as virtual, do not check if valid // } // // func (f *Framework) GetNewPath(command string, cwd string) (string, error) { // // "/usr/bin/python script" // // to // // "/path/to/script" // regex, err := regexp.Compile(f.Find) // if err != nil { // return "", fmt.Errorf("profiles(framework): failed to compile framework regex: %s", err) // } // matched := regex.FindAllStringSubmatch(command, -1) // if len(matched) == 0 || len(matched[0]) < 2 { // return "", fmt.Errorf("profiles(framework): regex \"%s\" for constructing path did not match command \"%s\"", f.Find, command) // } // // var lastError error // var buildPath string // for _, buildPath = range strings.Split(f.Build, "|") { // // buildPath = strings.Replace(buildPath, "{CWD}", cwd, -1) // for i := 1; i < len(matched[0]); i++ { // buildPath = strings.Replace(buildPath, fmt.Sprintf("{%d}", i), matched[0][i], -1) // } // // buildPath = filepath.Clean(buildPath) // // if !f.Virtual { // if !strings.HasPrefix(buildPath, "~/") && !filepath.IsAbs(buildPath) { // lastError = fmt.Errorf("constructed path \"%s\" from framework is not absolute", buildPath) // continue // } // if _, err := os.Stat(buildPath); errors.Is(err, fs.ErrNotExist) { // lastError = fmt.Errorf("constructed path \"%s\" does not exist", buildPath) // continue // } // } // // lastError = nil // break // // } // // if lastError != nil { // return "", fmt.Errorf("profiles(framework): failed to construct valid path, last error: %s", lastError) // } // log.Tracef("profiles(framework): transformed \"%s\" (%s) to \"%s\"", command, cwd, buildPath) // return buildPath, nil // } ================================================ FILE: service/profile/framework_test.go ================================================ package profile // DEACTIVATED // import ( // "testing" // ) // // func testGetNewPath(t *testing.T, f *Framework, command, cwd, expect string) { // newPath, err := f.GetNewPath(command, cwd) // if err != nil { // t.Errorf("GetNewPath failed: %s", err) // } // if newPath != expect { // t.Errorf("GetNewPath return unexpected result: got %s, expected %s", newPath, expect) // } // } // // func TestFramework(t *testing.T) { // f1 := &Framework{ // Find: "([^ ]+)$", // Build: "{CWD}/{1}", // } // testGetNewPath(t, f1, "/usr/bin/python bash", "/bin", "/bin/bash") // f2 := &Framework{ // Find: "([^ ]+)$", // Build: "{1}|{CWD}/{1}", // } // testGetNewPath(t, f2, "/usr/bin/python /bin/bash", "/tmp", "/bin/bash") // } ================================================ FILE: service/profile/get.go ================================================ package profile import ( "errors" "fmt" "path" "strings" "sync" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/mgr" ) var getProfileLock sync.Mutex // GetLocalProfile fetches a profile. This function ensures that the loaded profile // is shared among all callers. Always provide all available data points. // Passing an ID without MatchingData is valid, but could lead to inconsistent // data - use with caution. func GetLocalProfile(id string, md MatchingData, createProfileCallback func() *Profile) ( //nolint:gocognit profile *Profile, err error, ) { // Globally lock getting a profile. // This does not happen too often, and it ensures we really have integrity // and no race conditions. getProfileLock.Lock() defer getProfileLock.Unlock() var previousVersion *Profile // Get active profile based on the ID, if available. if id != "" { // Check if there already is an active profile. profile = getActiveProfile(MakeScopedID(SourceLocal, id)) if profile != nil { // Mark active and return if not outdated. if profile.outdated.IsNotSet() { profile.MarkStillActive() return profile, nil } // If outdated, get from database. previousVersion = profile profile = nil } } // In some cases, we might need to get a profile directly, without matching data. // This could lead to inconsistent data - use with caution. // Example: Saving prompt results to profile should always be to the same ID! if md == nil { if id == "" { return nil, errors.New("cannot get local profiles without ID and matching data") } profile, err = getProfile(MakeScopedID(SourceLocal, id)) if err != nil { return nil, fmt.Errorf("failed to load profile %s by ID: %w", MakeScopedID(SourceLocal, id), err) } } // Check if we are requesting a special profile. var created, special bool if id != "" && isSpecialProfileID(id) { special = true // Get special profile from DB. if profile == nil { profile, err = getProfile(MakeScopedID(SourceLocal, id)) if err != nil && !errors.Is(err, database.ErrNotFound) { log.Warningf("profile: failed to get special profile %s: %s", id, err) } } // Create profile if not found or if it needs a reset. if profile == nil || specialProfileNeedsReset(profile) { profile = createSpecialProfile(id, md.Path()) created = true } } // If we don't have a profile yet, find profile based on matching data. if profile == nil { profile, err = findProfile(SourceLocal, md) if err != nil { return nil, fmt.Errorf("failed to search for profile: %w", err) } } // If we still don't have a profile, create a new one. if profile == nil { created = true // Try the profile creation callback, if we have one. if createProfileCallback != nil { profile = createProfileCallback() } // If that did not work, create a standard profile. if profile == nil { fpPath := md.MatchingPath() if fpPath == "" { fpPath = md.Path() } profile = New(&Profile{ ID: id, Source: SourceLocal, PresentationPath: md.Path(), UsePresentationPath: true, Fingerprints: []Fingerprint{ { Type: FingerprintTypePathID, Operation: FingerprintOperationEqualsID, Value: fpPath, }, }, }) } } // Initialize and update profile. // Update metadata. var changed bool if md != nil { if special { changed = updateSpecialProfileMetadata(profile, md.Path()) } else { changed = profile.updateMetadata(md.Path()) } } // Save if created or changed. if created || changed { // Save profile. err := profile.Save() if err != nil { log.Warningf("profile: failed to save profile %s after creation: %s", profile.ScopedID(), err) } } // Trigger further metadata fetching from system if profile was created. if created && profile.UsePresentationPath && !special { module.mgr.Go("get profile metadata", func(wc *mgr.WorkerCtx) error { return profile.updateMetadataFromSystem(wc.Ctx(), md) }) } // Prepare profile for first use. // Process profiles are coming directly from the database or are new. // As we don't use any caching, these will be new objects. // Add a layeredProfile. // If we are refetching, assign the layered profile from the previous version. // The internal references will be updated when the layered profile checks for updates. if previousVersion != nil && previousVersion.layeredProfile != nil { profile.layeredProfile = previousVersion.layeredProfile } // Profiles must have a layered profile, create a new one if it // does not yet exist. if profile.layeredProfile == nil { profile.layeredProfile = NewLayeredProfile(profile) } // Add the profile to the currently active profiles. addActiveProfile(profile) return profile, nil } // getProfile fetches the profile for the given scoped ID. func getProfile(scopedID string) (profile *Profile, err error) { // Get profile from the database. r, err := profileDB.Get(ProfilesDBPath + scopedID) if err != nil { return nil, err } // Parse and prepare the profile, return the result. return loadProfile(r) } // findProfile searches for a profile with the given linked path. If it cannot // find one, it will create a new profile for the given linked path. func findProfile(source ProfileSource, md MatchingData) (profile *Profile, err error) { // TODO: Loading every profile from database and parsing it for every new // process might be quite expensive. Measure impact and possibly improve. // Get iterator over all profiles. it, err := profileDB.Query(query.New(ProfilesDBPath + MakeScopedID(source, ""))) if err != nil { return nil, fmt.Errorf("failed to query for profiles: %w", err) } // Find best matching profile. var ( highestScore int bestMatch record.Record ) profileFeed: for r := range it.Next { // Parse fingerprints. prints, err := loadProfileFingerprints(r) if err != nil { log.Debugf("profile: failed to load fingerprints of %s: %s", r.Key(), err) } // Continue with any returned fingerprints. if prints == nil { continue profileFeed } // Get matching score and compare. score := MatchFingerprints(prints, md) switch { case score == 0: // Continue to next. case score > highestScore: highestScore = score bestMatch = r case score == highestScore: // Notify user of conflict and abort. // Use first match - this should be consistent. notifyConflictingProfiles(bestMatch, r, md) it.Cancel() break profileFeed } } // Check if there was an error while iterating. if it.Err() != nil { return nil, fmt.Errorf("failed to iterate over profiles: %w", err) } // Return nothing if no profile matched. if bestMatch == nil { return nil, nil } // If we have a match, parse and return the profile. profile, err = loadProfile(bestMatch) if err != nil { return nil, fmt.Errorf("failed to parse selected profile %s: %w", bestMatch.Key(), err) } // Check if this profile is already active and return the active version instead. if activeProfile := getActiveProfile(profile.ScopedID()); activeProfile != nil && !activeProfile.IsOutdated() { return activeProfile, nil } // Return nothing if no profile matched. return profile, nil } func loadProfileFingerprints(r record.Record) (parsed *ParsedFingerprints, err error) { // Ensure it's a profile. profile, err := EnsureProfile(r) if err != nil { return nil, err } // Parse and return fingerprints. return ParseFingerprints(profile.Fingerprints, profile.LinkedPath) } func loadProfile(r record.Record) (*Profile, error) { // ensure its a profile profile, err := EnsureProfile(r) if err != nil { return nil, err } // prepare profile profile.prepProfile() // parse config err = profile.parseConfig() if err != nil { log.Errorf("profiles: profile %s has (partly) invalid configuration: %s", profile.ID, err) } // Set saved internally to suppress outdating profiles if saving internally. profile.savedInternally = true // Mark as recently seen. meta.UpdateLastSeen(profile.ScopedID()) // return parsed profile return profile, nil } func notifyConflictingProfiles(a, b record.Record, md MatchingData) { // Get profile names. var idA, nameA, idB, nameB string profileA, err := EnsureProfile(a) if err == nil { idA = profileA.ScopedID() nameA = profileA.Name } else { idA = strings.TrimPrefix(a.Key(), ProfilesDBPath) nameA = path.Base(idA) } profileB, err := EnsureProfile(b) if err == nil { idB = profileB.ScopedID() nameB = profileB.Name } else { idB = strings.TrimPrefix(b.Key(), ProfilesDBPath) nameB = path.Base(idB) } // Notify user about conflict. notifications.NotifyWarn( fmt.Sprintf("profiles:match-conflict:%s:%s", idA, idB), "App Settings Match Conflict", fmt.Sprintf( "Multiple app settings match the app at %q with the same priority, please change one of them: %q or %q", md.Path(), nameA, nameB, ), notifications.Action{ Text: "Change (1)", Type: notifications.ActionTypeOpenProfile, Payload: idA, }, notifications.Action{ Text: "Change (2)", Type: notifications.ActionTypeOpenProfile, Payload: idB, }, notifications.Action{ ID: "ack", Text: "OK", }, ) } ================================================ FILE: service/profile/merge.go ================================================ package profile import ( "errors" "fmt" "sync" "time" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/service/profile/binmeta" ) // MergeProfiles merges multiple profiles into a new one. // The new profile is saved and returned. // Only the icon and fingerprints are inherited from other profiles. // All other information is taken only from the primary profile. func MergeProfiles(name string, primary *Profile, secondaries ...*Profile) (newProfile *Profile, err error) { if primary == nil || len(secondaries) == 0 { return nil, errors.New("must supply both a primary and at least one secondary profile for merging") } // Fill info from primary profile. nowUnix := time.Now().Unix() newProfile = &Profile{ Base: record.Base{}, RWMutex: sync.RWMutex{}, ID: "", // Omit ID to derive it from the new fingerprints. Source: primary.Source, Name: name, Description: primary.Description, Homepage: primary.Homepage, UsePresentationPath: false, // Disable presentation path. Config: primary.Config, Created: nowUnix, } // Fall back to name of primary profile, if none is set. if newProfile.Name == "" { newProfile.Name = primary.Name } // If any profile was edited, set LastEdited to now. if primary.LastEdited > 0 { newProfile.LastEdited = nowUnix } else { for _, sp := range secondaries { if sp.LastEdited > 0 { newProfile.LastEdited = nowUnix break } } } // Collect all icons. newProfile.Icons = make([]binmeta.Icon, 0, len(secondaries)+1) // Guess the needed space. newProfile.Icons = append(newProfile.Icons, primary.Icons...) for _, sp := range secondaries { newProfile.Icons = append(newProfile.Icons, sp.Icons...) } newProfile.Icons = binmeta.SortAndCompactIcons(newProfile.Icons) // Collect all fingerprints. newProfile.Fingerprints = make([]Fingerprint, 0, len(primary.Fingerprints)+len(secondaries)) // Guess the needed space. newProfile.Fingerprints = addFingerprints(newProfile.Fingerprints, primary.Fingerprints, primary.ScopedID()) for _, sp := range secondaries { newProfile.Fingerprints = addFingerprints(newProfile.Fingerprints, sp.Fingerprints, sp.ScopedID()) } newProfile.Fingerprints = sortAndCompactFingerprints(newProfile.Fingerprints) // Save new profile. newProfile = New(newProfile) if err := newProfile.Save(); err != nil { return nil, fmt.Errorf("failed to save merged profile: %w", err) } // Delete all previous profiles. if err := primary.delete(); err != nil { return nil, fmt.Errorf("failed to delete primary profile %s: %w", primary.ScopedID(), err) } module.EventMigrated.Submit([]string{primary.ScopedID(), newProfile.ScopedID()}) for _, sp := range secondaries { if err := sp.delete(); err != nil { return nil, fmt.Errorf("failed to delete secondary profile %s: %w", sp.ScopedID(), err) } module.EventMigrated.Submit([]string{sp.ScopedID(), newProfile.ScopedID()}) } return newProfile, nil } func addFingerprints(existing, add []Fingerprint, from string) []Fingerprint { // Copy all fingerprints and add the they are from. for _, addFP := range add { existing = append(existing, Fingerprint{ Type: addFP.Type, Key: addFP.Key, Operation: addFP.Operation, Value: addFP.Value, MergedFrom: from, }) } return existing } ================================================ FILE: service/profile/meta.go ================================================ package profile import ( "fmt" "sync" "time" "github.com/safing/portmaster/base/database/record" ) // ProfilesMetadata holds metadata about all profiles that are not fit to be // stored with the profiles themselves. type ProfilesMetadata struct { record.Base sync.Mutex States map[string]*MetaState } // MetaState describes the state of a profile. type MetaState struct { State string At time.Time } // Profile metadata states. const ( MetaStateSeen = "seen" MetaStateDeleted = "deleted" ) // EnsureProfilesMetadata ensures that the given record is a *ProfilesMetadata, and returns it. func EnsureProfilesMetadata(r record.Record) (*ProfilesMetadata, error) { // unwrap if r.IsWrapped() { // only allocate a new struct, if we need it newMeta := &ProfilesMetadata{} err := record.Unwrap(r, newMeta) if err != nil { return nil, err } return newMeta, nil } // or adjust type newMeta, ok := r.(*ProfilesMetadata) if !ok { return nil, fmt.Errorf("record not of type *Profile, but %T", r) } return newMeta, nil } var ( profilesMetadataKey = "core:profile-states" meta *ProfilesMetadata removeDeletedEntriesAfter = 30 * 24 * time.Hour ) // loadProfilesMetadata loads the profile metadata from the database. // It may only be called during module starting, as there is no lock for "meta" itself. func loadProfilesMetadata() error { r, err := profileDB.Get(profilesMetadataKey) if err != nil { return err } loadedMeta, err := EnsureProfilesMetadata(r) if err != nil { return err } // Set package variable. meta = loadedMeta return nil } func (meta *ProfilesMetadata) check() { if meta.States == nil { meta.States = make(map[string]*MetaState) } } // Save saves the profile metadata to the database. func (meta *ProfilesMetadata) Save() error { if meta == nil { return nil } func() { meta.Lock() defer meta.Unlock() if !meta.KeyIsSet() { meta.SetKey(profilesMetadataKey) } }() meta.Clean() return profileDB.Put(meta) } // Clean removes old entries. func (meta *ProfilesMetadata) Clean() { if meta == nil { return } meta.Lock() defer meta.Unlock() for key, state := range meta.States { switch { case state == nil: delete(meta.States, key) case state.State != MetaStateDeleted: continue case time.Since(state.At) > removeDeletedEntriesAfter: delete(meta.States, key) } } } // GetLastSeen returns when the profile with the given ID was last seen. func (meta *ProfilesMetadata) GetLastSeen(scopedID string) *time.Time { if meta == nil { return nil } meta.Lock() defer meta.Unlock() state := meta.States[scopedID] switch { case state == nil: return nil case state.State == MetaStateSeen: return &state.At default: return nil } } // UpdateLastSeen sets the profile with the given ID as last seen now. func (meta *ProfilesMetadata) UpdateLastSeen(scopedID string) { if meta == nil { return } meta.Lock() defer meta.Unlock() meta.States[scopedID] = &MetaState{ State: MetaStateSeen, At: time.Now().UTC(), } } // MarkDeleted marks the profile with the given ID as deleted. func (meta *ProfilesMetadata) MarkDeleted(scopedID string) { if meta == nil { return } meta.Lock() defer meta.Unlock() meta.States[scopedID] = &MetaState{ State: MetaStateDeleted, At: time.Now().UTC(), } } // RemoveState removes any state of the profile with the given ID. func (meta *ProfilesMetadata) RemoveState(scopedID string) { if meta == nil { return } meta.Lock() defer meta.Unlock() delete(meta.States, scopedID) } ================================================ FILE: service/profile/migrations.go ================================================ package profile import ( "context" "fmt" "regexp" "github.com/hashicorp/go-version" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/migration" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/profile/binmeta" ) func registerMigrations() error { return migrations.Add( migration.Migration{ Description: "Migrate from LinkedPath to Fingerprints and PresentationPath", Version: "v0.9.9", MigrateFunc: migrateLinkedPath, }, migration.Migration{ Description: "Migrate from Icon Fields to Icon List", Version: "v1.4.7", MigrateFunc: migrateIcons, }, migration.Migration{ Description: "Migrate from random profile IDs to fingerprint-derived IDs", Version: "v1.6.3", // Re-run after mixed results in v1.6.0 MigrateFunc: migrateToDerivedIDs, }, ) } func migrateLinkedPath(ctx context.Context, _, to *version.Version, db *database.Interface) error { // Get iterator over all profiles. it, err := db.Query(query.New(ProfilesDBPath)) if err != nil { log.Tracer(ctx).Errorf("profile: failed to migrate from linked path: failed to start query: %s", err) return nil } // Migrate all profiles. for r := range it.Next { // Parse profile. profile, err := EnsureProfile(r) if err != nil { log.Tracer(ctx).Debugf("profiles: failed to parse profile %s for migration: %s", r.Key(), err) continue } // Skip if there is no LinkedPath to migrate from. if profile.LinkedPath == "" { continue } // Update metadata and save if changed. if profile.updateMetadata("") { err = db.Put(profile) if err != nil { log.Tracer(ctx).Debugf("profiles: failed to save profile %s after migration: %s", r.Key(), err) } else { log.Tracer(ctx).Tracef("profiles: migrated profile %s to %s", r.Key(), to) } } } // Check if there was an error while iterating. if err := it.Err(); err != nil { log.Tracer(ctx).Errorf("profile: failed to migrate from linked path: failed to iterate over profiles for migration: %s", err) } return nil } func migrateIcons(ctx context.Context, _, to *version.Version, db *database.Interface) error { // Get iterator over all profiles. it, err := db.Query(query.New(ProfilesDBPath)) if err != nil { log.Tracer(ctx).Errorf("profile: failed to migrate from icon fields: failed to start query: %s", err) return nil } // Migrate all profiles. var ( lastErr error failed int total int ) for r := range it.Next { // Parse profile. profile, err := EnsureProfile(r) if err != nil { log.Tracer(ctx).Debugf("profiles: failed to parse profile %s for migration: %s", r.Key(), err) continue } // Skip if there is no (valid) icon defined or the icon list is already populated. if profile.Icon == "" || profile.IconType == "" || len(profile.Icons) > 0 { continue } // Migrate to icon list. profile.Icons = []binmeta.Icon{{ Type: profile.IconType, Value: profile.Icon, }} // Save back to DB. err = db.Put(profile) if err != nil { failed++ lastErr = err log.Tracer(ctx).Debugf("profiles: failed to save profile %s after migration: %s", r.Key(), err) } else { log.Tracer(ctx).Tracef("profiles: migrated profile %s to %s", r.Key(), to) } total++ } // Check if there was an error while iterating. if err := it.Err(); err != nil { log.Tracer(ctx).Errorf("profile: failed to migrate from icon fields: failed to iterate over profiles for migration: %s", err) } // Log migration failure and try again next time. if lastErr != nil { // Normally, an icon migration would not be such a big error, but this is a test // run for the profile IDs and we absolutely need to know if anything went wrong. module.states.Add(mgr.State{ ID: "migration-failed-icons", Name: "Profile Migration Failed", Message: fmt.Sprintf("Failed to migrate icons of %d profiles (out of %d pending). The last error was: %s\n\nPlease restart Portmaster to try the migration again.", failed, total, lastErr), Type: mgr.StateTypeError, }) return fmt.Errorf("failed to migrate %d profiles (out of %d pending) - last error: %w", failed, total, lastErr) } return lastErr } var randomUUIDRegex = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`) func migrateToDerivedIDs(ctx context.Context, _, to *version.Version, db *database.Interface) error { var profilesToDelete []string //nolint:prealloc // We don't know how many profiles there are. // Get iterator over all profiles. it, err := db.Query(query.New(ProfilesDBPath)) if err != nil { log.Tracer(ctx).Errorf("profile: failed to migrate to derived profile IDs: failed to start query: %s", err) return nil } // Migrate all profiles. var ( lastErr error failed int total int ) for r := range it.Next { // Parse profile. profile, err := EnsureProfile(r) if err != nil { failed++ lastErr = err log.Tracer(ctx).Debugf("profiles: failed to parse profile %s for migration: %s", r.Key(), err) continue } // Skip if the ID does not look like a random UUID. if !randomUUIDRegex.MatchString(profile.ID) { continue } // Generate new ID. oldScopedID := profile.ScopedID() newID := DeriveProfileID(profile.Fingerprints) // If they match, skip migration for this profile. if profile.ID == newID { continue } // Reset key. profile.ResetKey() // Set new ID and rebuild the key. profile.ID = newID profile.makeKey() // Save back to DB. err = db.Put(profile) if err != nil { failed++ lastErr = err log.Tracer(ctx).Debugf("profiles: failed to save profile %s after migration: %s", r.Key(), err) } else { log.Tracer(ctx).Tracef("profiles: migrated profile %s to %s", r.Key(), to) // Add old ID to profiles that we need to delete. profilesToDelete = append(profilesToDelete, oldScopedID) } total++ } // Check if there was an error while iterating. if err := it.Err(); err != nil { log.Tracer(ctx).Errorf("profile: failed to migrate to derived profile IDs: failed to iterate over profiles for migration: %s", err) } // Delete old migrated profiles. for _, scopedID := range profilesToDelete { if err := db.Delete(ProfilesDBPath + scopedID); err != nil { log.Tracer(ctx).Errorf("profile: failed to delete old profile %s during migration: %s", scopedID, err) } } // Log migration failure and try again next time. if lastErr != nil { module.states.Add(mgr.State{ ID: "migration-failed-derived-IDs", Name: "Profile Migration Failed", Message: fmt.Sprintf("Failed to migrate profile IDs of %d profiles (out of %d pending). The last error was: %s\n\nPlease restart Portmaster to try the migration again.", failed, total, lastErr), Type: mgr.StateTypeError, }) return fmt.Errorf("failed to migrate %d profiles (out of %d pending) - last error: %w", failed, total, lastErr) } return nil } ================================================ FILE: service/profile/module.go ================================================ package profile import ( "errors" "fmt" "path/filepath" "sync/atomic" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/migration" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" _ "github.com/safing/portmaster/service/core/base" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/profile/binmeta" ) var migrations = migration.New("core:migrations/profile") // Events. const ( ConfigChangeEvent = "profile config change" DeletedEvent = "profile deleted" MigratedEvent = "profile migrated" ) type ProfileModule struct { mgr *mgr.Manager instance instance EventConfigChange *mgr.EventMgr[string] EventDelete *mgr.EventMgr[string] EventMigrated *mgr.EventMgr[[]string] states *mgr.StateMgr } func (pm *ProfileModule) Manager() *mgr.Manager { return pm.mgr } func (pm *ProfileModule) States() *mgr.StateMgr { return pm.states } func (pm *ProfileModule) Start() error { return start() } func (pm *ProfileModule) Stop() error { return stop() } func prep() error { if err := registerConfiguration(); err != nil { return err } if err := registerMigrations(); err != nil { return err } if err := registerAPIEndpoints(); err != nil { return err } // Setup icon storage location. databaseDir := filepath.Join(module.instance.DataDir(), "databases") // Ensure folder existents and permission err := utils.EnsureDirectory(databaseDir, utils.AdminOnlyExecPermission) if err != nil { return fmt.Errorf("failed to ensure directory existence %s: %w", databaseDir, err) } iconsDir := filepath.Join(databaseDir, "icons") err = utils.EnsureDirectory(iconsDir, utils.AdminOnlyExecPermission) if err != nil { return fmt.Errorf("failed to ensure directory existence %s: %w", iconsDir, err) } binmeta.ProfileIconStoragePath = iconsDir return nil } func start() error { if err := loadProfilesMetadata(); err != nil { if !errors.Is(err, database.ErrNotFound) { log.Warningf("profile: failed to load profiles metadata, falling back to empty state: %s", err) } meta = &ProfilesMetadata{} } meta.check() if err := migrations.Migrate(module.mgr.Ctx()); err != nil { log.Errorf("profile: migrations failed: %s", err) } err := registerValidationDBHook() if err != nil { return err } err = registerRevisionProvider() if err != nil { return err } err = startProfileUpdateChecker() if err != nil { return err } module.mgr.Go("clean active profiles", cleanActiveProfiles) // Register config callback when starting, as it depends on the updates module, // but the config system will already submit events earlier. if err := registerGlobalConfigProfileUpdater(); err != nil { return err } err = updateGlobalConfigProfile(module.mgr.Ctx()) if err != nil { log.Warningf("profile: error during loading global profile from configuration: %s", err) } return nil } func stop() error { return meta.Save() } var ( module *ProfileModule shimLoaded atomic.Bool ) func NewModule(instance instance) (*ProfileModule, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Profile") module = &ProfileModule{ mgr: m, instance: instance, EventConfigChange: mgr.NewEventMgr[string](ConfigChangeEvent, m), EventDelete: mgr.NewEventMgr[string](DeletedEvent, m), EventMigrated: mgr.NewEventMgr[[]string](MigratedEvent, m), states: mgr.NewStateMgr(m), } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface { DataDir() string Config() *config.Config } ================================================ FILE: service/profile/profile-layered-provider.go ================================================ package profile import ( "errors" "strings" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/runtime" ) const ( revisionProviderPrefix = "layeredProfile/" ) var ( errProfileNotActive = errors.New("profile not active") errNoLayeredProfile = errors.New("profile has no layered profile") pushLayeredProfile runtime.PushFunc = func(...record.Record) {} ) func registerRevisionProvider() error { push, err := runtime.Register( revisionProviderPrefix, runtime.SimpleValueGetterFunc(getRevisions), ) if err != nil { return err } pushLayeredProfile = push return nil } func getRevisions(key string) ([]record.Record, error) { key = strings.TrimPrefix(key, revisionProviderPrefix) var profiles []*Profile if key == "" { profiles = getAllActiveProfiles() } else { // Get active profile. profile := getActiveProfile(key) if profile == nil { return nil, errProfileNotActive } profiles = append(profiles, profile) } records := make([]record.Record, 0, len(profiles)) for _, p := range profiles { layered, err := getProfileRevision(p) if err != nil { log.Warningf("failed to get layered profile for %s: %s", p.ID, err) continue } records = append(records, layered) } return records, nil } // getProfileRevision returns the layered profile for p. // It also updates the layered profile if required. func getProfileRevision(p *Profile) (*LayeredProfile, error) { // Get layered profile. layeredProfile := p.LayeredProfile() if layeredProfile == nil { return nil, errNoLayeredProfile } // Update profiles if necessary. // TODO: Cannot update as we have too little information. // Just return the current state. Previous code: // if layeredProfile.NeedsUpdate() { // layeredProfile.Update() // } return layeredProfile, nil } ================================================ FILE: service/profile/profile-layered.go ================================================ package profile import ( "context" "sync" "sync/atomic" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/runtime" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/profile/endpoints" ) // LayeredProfile combines multiple Profiles. type LayeredProfile struct { record.Base sync.RWMutex localProfile *Profile layers []*Profile LayerIDs []string RevisionCounter uint64 globalValidityFlag *config.ValidityFlag securityLevel *uint32 // These functions give layered access to configuration options and require // the layered profile to be read locked. // TODO(ppacher): we need JSON tags here so the layeredProfile can be exposed // via the API. If we ever switch away from JSON to something else supported // by DSD this WILL BREAK! DisableAutoPermit config.BoolOption `json:"-"` BlockScopeLocal config.BoolOption `json:"-"` BlockScopeLAN config.BoolOption `json:"-"` BlockScopeInternet config.BoolOption `json:"-"` BlockP2P config.BoolOption `json:"-"` BlockInbound config.BoolOption `json:"-"` RemoveOutOfScopeDNS config.BoolOption `json:"-"` RemoveBlockedDNS config.BoolOption `json:"-"` FilterSubDomains config.BoolOption `json:"-"` FilterCNAMEs config.BoolOption `json:"-"` PreventBypassing config.BoolOption `json:"-"` DomainHeuristics config.BoolOption `json:"-"` UseSPN config.BoolOption `json:"-"` SPNRoutingAlgorithm config.StringOption `json:"-"` EnableHistory config.BoolOption `json:"-"` KeepHistory config.IntOption `json:"-"` } // NewLayeredProfile returns a new layered profile based on the given local profile. func NewLayeredProfile(localProfile *Profile) *LayeredProfile { var securityLevelVal uint32 lp := &LayeredProfile{ localProfile: localProfile, layers: make([]*Profile, 0, 1), LayerIDs: make([]string, 0, 1), globalValidityFlag: config.NewValidityFlag(), RevisionCounter: 1, securityLevel: &securityLevelVal, } lp.DisableAutoPermit = lp.wrapBoolOption( CfgOptionDisableAutoPermitKey, cfgOptionDisableAutoPermit, ) lp.BlockScopeLocal = lp.wrapBoolOption( CfgOptionBlockScopeLocalKey, cfgOptionBlockScopeLocal, ) lp.BlockScopeLAN = lp.wrapBoolOption( CfgOptionBlockScopeLANKey, cfgOptionBlockScopeLAN, ) lp.BlockScopeInternet = lp.wrapBoolOption( CfgOptionBlockScopeInternetKey, cfgOptionBlockScopeInternet, ) lp.BlockP2P = lp.wrapBoolOption( CfgOptionBlockP2PKey, cfgOptionBlockP2P, ) lp.BlockInbound = lp.wrapBoolOption( CfgOptionBlockInboundKey, cfgOptionBlockInbound, ) lp.RemoveOutOfScopeDNS = lp.wrapBoolOption( CfgOptionRemoveOutOfScopeDNSKey, cfgOptionRemoveOutOfScopeDNS, ) lp.RemoveBlockedDNS = lp.wrapBoolOption( CfgOptionRemoveBlockedDNSKey, cfgOptionRemoveBlockedDNS, ) lp.FilterSubDomains = lp.wrapBoolOption( CfgOptionFilterSubDomainsKey, cfgOptionFilterSubDomains, ) lp.FilterCNAMEs = lp.wrapBoolOption( CfgOptionFilterCNAMEKey, cfgOptionFilterCNAME, ) lp.PreventBypassing = lp.wrapBoolOption( CfgOptionPreventBypassingKey, cfgOptionPreventBypassing, ) lp.DomainHeuristics = lp.wrapBoolOption( CfgOptionDomainHeuristicsKey, cfgOptionDomainHeuristics, ) lp.UseSPN = lp.wrapBoolOption( CfgOptionUseSPNKey, cfgOptionUseSPN, ) lp.SPNRoutingAlgorithm = lp.wrapStringOption( CfgOptionRoutingAlgorithmKey, cfgOptionRoutingAlgorithm, ) lp.EnableHistory = lp.wrapBoolOption( CfgOptionEnableHistoryKey, cfgOptionEnableHistory, ) lp.KeepHistory = lp.wrapIntOption( CfgOptionKeepHistoryKey, cfgOptionKeepHistory, ) lp.LayerIDs = append(lp.LayerIDs, localProfile.ScopedID()) lp.layers = append(lp.layers, localProfile) // TODO: Load additional profiles. lp.CreateMeta() lp.SetKey(runtime.DefaultRegistry.DatabaseName() + ":" + revisionProviderPrefix + localProfile.ScopedID()) // Inform database subscribers about the new layered profile. lp.Lock() defer lp.Unlock() pushLayeredProfile(lp) return lp } // LockForUsage locks the layered profile, including all layers individually. func (lp *LayeredProfile) LockForUsage() { lp.RLock() for _, layer := range lp.layers { layer.RLock() } } // UnlockForUsage unlocks the layered profile, including all layers individually. func (lp *LayeredProfile) UnlockForUsage() { lp.RUnlock() for _, layer := range lp.layers { layer.RUnlock() } } // LocalProfile returns the local profile associated with this layered profile. func (lp *LayeredProfile) LocalProfile() *Profile { if lp == nil { return nil } lp.RLock() defer lp.RUnlock() return lp.localProfile } // LocalProfileWithoutLocking returns the local profile associated with this // layered profile, but without locking the layered profile. // This method my only be used when the caller already has a lock on the layered profile. func (lp *LayeredProfile) LocalProfileWithoutLocking() *Profile { if lp == nil { return nil } return lp.localProfile } // increaseRevisionCounter increases the revision counter and pushes the // layered profile to listeners. func (lp *LayeredProfile) increaseRevisionCounter(lock bool) (revisionCounter uint64) { //nolint:unparam // This is documentation. if lp == nil { return 0 } if lock { lp.Lock() defer lp.Unlock() } // Increase the revision counter. lp.RevisionCounter++ // Push the increased counter to the UI. pushLayeredProfile(lp) return lp.RevisionCounter } // RevisionCnt returns the current profile revision counter. func (lp *LayeredProfile) RevisionCnt() (revisionCounter uint64) { if lp == nil { return 0 } lp.RLock() defer lp.RUnlock() return lp.RevisionCounter } // MarkStillActive marks all the layers as still active. func (lp *LayeredProfile) MarkStillActive() { if lp == nil { return } lp.RLock() defer lp.RUnlock() for _, layer := range lp.layers { layer.MarkStillActive() } } // NeedsUpdate checks for outdated profiles. func (lp *LayeredProfile) NeedsUpdate() (outdated bool) { lp.RLock() defer lp.RUnlock() // Check global config state. if !lp.globalValidityFlag.IsValid() { return true } // Check config in layers. for _, layer := range lp.layers { if layer.outdated.IsSet() { return true } } return false } // Update checks for and replaces any outdated profiles. func (lp *LayeredProfile) Update(md MatchingData, createProfileCallback func() *Profile) (revisionCounter uint64) { lp.Lock() defer lp.Unlock() var changed bool for i, layer := range lp.layers { if layer.outdated.IsSet() { // Check for unsupported sources. if layer.Source != SourceLocal { log.Warningf("profile: updating profiles outside of local source is not supported: %s", layer.ScopedID()) layer.outdated.UnSet() continue } // Update layer. changed = true newLayer, err := GetLocalProfile(layer.ID, md, createProfileCallback) if err != nil { log.Errorf("profiles: failed to update profile %s: %s", layer.ScopedID(), err) } else { lp.layers[i] = newLayer } // Update local profile reference. if i == 0 { lp.localProfile = newLayer } } } if !lp.globalValidityFlag.IsValid() { changed = true } if changed { // get global config validity flag lp.globalValidityFlag.Refresh() // bump revision counter lp.increaseRevisionCounter(false) } return lp.RevisionCounter } // SecurityLevel returns the highest security level of all layered profiles. This function is atomic and does not require any locking. func (lp *LayeredProfile) SecurityLevel() uint8 { return uint8(atomic.LoadUint32(lp.securityLevel)) } // DefaultAction returns the active default action ID. This functions requires the layered profile to be read locked. func (lp *LayeredProfile) DefaultAction() uint8 { for _, layer := range lp.layers { if layer.defaultAction > 0 { return layer.defaultAction } } cfgLock.RLock() defer cfgLock.RUnlock() return cfgDefaultAction } // MatchEndpoint checks if the given endpoint matches an entry in any of the profiles. This functions requires the layered profile to be read locked. func (lp *LayeredProfile) MatchEndpoint(ctx context.Context, entity *intel.Entity) (endpoints.EPResult, endpoints.Reason) { for _, layer := range lp.layers { if layer.endpoints.IsSet() { result, reason := layer.endpoints.Match(ctx, entity) if endpoints.IsDecision(result) { return result, reason } } } cfgLock.RLock() defer cfgLock.RUnlock() return cfgEndpoints.Match(ctx, entity) } // MatchServiceEndpoint checks if the given endpoint of an inbound connection matches an entry in any of the profiles. This functions requires the layered profile to be read locked. func (lp *LayeredProfile) MatchServiceEndpoint(ctx context.Context, entity *intel.Entity) (endpoints.EPResult, endpoints.Reason) { entity.EnableReverseResolving() for _, layer := range lp.layers { if layer.serviceEndpoints.IsSet() { result, reason := layer.serviceEndpoints.Match(ctx, entity) if endpoints.IsDecision(result) { return result, reason } } } cfgLock.RLock() defer cfgLock.RUnlock() return cfgServiceEndpoints.Match(ctx, entity) } // MatchSPNUsagePolicy checks if the given endpoint matches an entry in any of the profiles. This functions requires the layered profile to be read locked. func (lp *LayeredProfile) MatchSPNUsagePolicy(ctx context.Context, entity *intel.Entity) (endpoints.EPResult, endpoints.Reason) { for _, layer := range lp.layers { if layer.spnUsagePolicy.IsSet() { result, reason := layer.spnUsagePolicy.Match(ctx, entity) if endpoints.IsDecision(result) { return result, reason } } } cfgLock.RLock() defer cfgLock.RUnlock() return cfgSPNUsagePolicy.Match(ctx, entity) } // StackedTransitHubPolicies returns all transit hub policies of the layered profile, including the global one. func (lp *LayeredProfile) StackedTransitHubPolicies() []endpoints.Endpoints { policies := make([]endpoints.Endpoints, 0, len(lp.layers)+3) // +1 for global policy, +2 for intel policies for _, layer := range lp.layers { if layer.spnTransitHubPolicy.IsSet() { policies = append(policies, layer.spnTransitHubPolicy) } } cfgLock.RLock() defer cfgLock.RUnlock() policies = append(policies, cfgSPNTransitHubPolicy) return policies } // StackedExitHubPolicies returns all exit hub policies of the layered profile, including the global one. func (lp *LayeredProfile) StackedExitHubPolicies() []endpoints.Endpoints { policies := make([]endpoints.Endpoints, 0, len(lp.layers)+3) // +1 for global policy, +2 for intel policies for _, layer := range lp.layers { if layer.spnExitHubPolicy.IsSet() { policies = append(policies, layer.spnExitHubPolicy) } } cfgLock.RLock() defer cfgLock.RUnlock() policies = append(policies, cfgSPNExitHubPolicy) return policies } // MatchFilterLists matches the entity against the set of filter // lists. This functions requires the layered profile to be read locked. func (lp *LayeredProfile) MatchFilterLists(ctx context.Context, entity *intel.Entity) (endpoints.EPResult, endpoints.Reason) { entity.ResolveSubDomainLists(ctx, lp.FilterSubDomains()) entity.EnableCNAMECheck(ctx, lp.FilterCNAMEs()) for _, layer := range lp.layers { // Search for the first layer that has filter lists set. if layer.filterListsSet { if entity.MatchLists(layer.filterListIDs) { return endpoints.Denied, entity.ListBlockReason() } return endpoints.NoMatch, nil } } cfgLock.RLock() defer cfgLock.RUnlock() if len(cfgFilterLists) > 0 { if entity.MatchLists(cfgFilterLists) { return endpoints.Denied, entity.ListBlockReason() } } return endpoints.NoMatch, nil } func (lp *LayeredProfile) wrapBoolOption(configKey string, globalConfig config.BoolOption) config.BoolOption { var revCnt uint64 = 0 var value bool var refreshLock sync.Mutex return func() bool { refreshLock.Lock() defer refreshLock.Unlock() // Check if we need to refresh the value. if revCnt != lp.RevisionCounter { revCnt = lp.RevisionCounter // Go through all layers to find an active value. found := false for _, layer := range lp.layers { layerValue, ok := layer.configPerspective.GetAsBool(configKey) if ok { found = true value = layerValue break } } if !found { value = globalConfig() } } return value } } func (lp *LayeredProfile) wrapIntOption(configKey string, globalConfig config.IntOption) config.IntOption { var revCnt uint64 = 0 var value int64 var refreshLock sync.Mutex return func() int64 { refreshLock.Lock() defer refreshLock.Unlock() // Check if we need to refresh the value. if revCnt != lp.RevisionCounter { revCnt = lp.RevisionCounter // Go through all layers to find an active value. found := false for _, layer := range lp.layers { layerValue, ok := layer.configPerspective.GetAsInt(configKey) if ok { found = true value = layerValue break } } if !found { value = globalConfig() } } return value } } // GetProfileSource returns the database key of the first profile in the // layers that has the given configuration key set. If it returns an empty // string, the global profile can be assumed to have been effective. func (lp *LayeredProfile) GetProfileSource(configKey string) string { for _, layer := range lp.layers { if layer.configPerspective.Has(configKey) { return layer.Key() } } // Global Profile return "" } func (lp *LayeredProfile) wrapStringOption(configKey string, globalConfig config.StringOption) config.StringOption { var revCnt uint64 = 0 var value string var refreshLock sync.Mutex return func() string { refreshLock.Lock() defer refreshLock.Unlock() // Check if we need to refresh the value. if revCnt != lp.RevisionCounter { revCnt = lp.RevisionCounter // Go through all layers to find an active value. found := false for _, layer := range lp.layers { layerValue, ok := layer.configPerspective.GetAsString(configKey) if ok { found = true value = layerValue break } } if !found { value = globalConfig() } } return value } } ================================================ FILE: service/profile/profile.go ================================================ package profile import ( "context" "errors" "fmt" "strings" "sync" "sync/atomic" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/intel/filterlists" "github.com/safing/portmaster/service/profile/binmeta" "github.com/safing/portmaster/service/profile/endpoints" ) // ProfileSource is the source of the profile. type ProfileSource string //nolint:golint // Profile Sources. const ( SourceLocal ProfileSource = "local" // local, editable SourceSpecial ProfileSource = "special" // specials (read-only) ) // Default Action IDs. const ( DefaultActionNotSet uint8 = 0 DefaultActionBlock uint8 = 1 DefaultActionAsk uint8 = 2 DefaultActionPermit uint8 = 3 ) // Profile is used to predefine a security profile for applications. type Profile struct { //nolint:maligned // not worth the effort record.Base sync.RWMutex // ID is a unique identifier for the profile. ID string // constant // Source describes the source of the profile. Source ProfileSource // constant // Name is a human readable name of the profile. It // defaults to the basename of the application. Name string // Description may hold an optional description of the // profile or the purpose of the application. Description string // Warning may hold an optional warning about this application. // It may be static or be added later on when the Portmaster detected an // issue with the application. Warning string // WarningLastUpdated holds the timestamp when the Warning field was last // updated. WarningLastUpdated time.Time // Homepage may refer to the website of the application // vendor. Homepage string // Deprecated: Icon holds the icon of the application. The value // may either be a filepath, a database key or a blob URL. // See IconType for more information. Icon string // Deprecated: IconType describes the type of the Icon property. IconType binmeta.IconType // Icons holds a list of icons to represent the application. Icons []binmeta.Icon // Deprecated: LinkedPath used to point to the executables this // profile was created for. // Until removed, it will be added to the Fingerprints as an exact path match. LinkedPath string // constant // PresentationPath holds the path of an executable that should be used for // get representative information from, like the name of the program or the icon. // Is automatically removed when the path does not exist. // Is automatically populated with the next match when empty. PresentationPath string // UsePresentationPath can be used to enable/disable fetching information // from the executable at PresentationPath. In some cases, this is not // desirable. UsePresentationPath bool // Fingerprints holds process matching information. Fingerprints []Fingerprint // Config holds profile specific setttings. It's a nested // object with keys defining the settings database path. All keys // until the actual settings value (which is everything that is not // an object) need to be concatenated for the settings database // path. Config map[string]interface{} // LastEdited holds the UTC timestamp in seconds when the profile was last // edited by the user. This is not set automatically, but has to be manually // set by the user interface. LastEdited int64 // Created holds the UTC timestamp in seconds when the // profile has been created. Created int64 // Internal is set to true if the profile is attributed to a // Portmaster internal process. Internal is set during profile // creation and may be accessed without lock. Internal bool // layeredProfile is a link to the layered profile with this profile as the // main profile. // All processes with the same binary should share the same instance of the // local profile and the associated layered profile. layeredProfile *LayeredProfile // Interpreted Data configPerspective *config.Perspective dataParsed bool defaultAction uint8 endpoints endpoints.Endpoints serviceEndpoints endpoints.Endpoints filterListsSet bool filterListIDs []string spnUsagePolicy endpoints.Endpoints spnTransitHubPolicy endpoints.Endpoints spnExitHubPolicy endpoints.Endpoints // Lifecycle Management outdated *abool.AtomicBool lastActive *int64 // savedInternally is set to true for profiles that are saved internally. savedInternally bool } func (profile *Profile) prepProfile() { // prepare configuration profile.outdated = abool.New() profile.lastActive = new(int64) // Migration of LinkedPath to PresentationPath if profile.PresentationPath == "" && profile.LinkedPath != "" { profile.PresentationPath = profile.LinkedPath } } func (profile *Profile) parseConfig() error { // Check if already parsed. if profile.dataParsed { return nil } // Create new perspective and marked as parsed. var err error profile.configPerspective, err = config.NewPerspective(profile.Config) if err != nil { return fmt.Errorf("failed to create config perspective: %w", err) } profile.dataParsed = true var lastErr error action, ok := profile.configPerspective.GetAsString(CfgOptionDefaultActionKey) profile.defaultAction = DefaultActionNotSet if ok { switch action { case DefaultActionPermitValue: profile.defaultAction = DefaultActionPermit case DefaultActionAskValue: profile.defaultAction = DefaultActionAsk case DefaultActionBlockValue: profile.defaultAction = DefaultActionBlock default: lastErr = fmt.Errorf(`default action "%s" invalid`, action) } } list, ok := profile.configPerspective.GetAsStringArray(CfgOptionEndpointsKey) profile.endpoints = nil if ok { profile.endpoints, err = endpoints.ParseEndpoints(list) if err != nil { lastErr = err } } list, ok = profile.configPerspective.GetAsStringArray(CfgOptionServiceEndpointsKey) profile.serviceEndpoints = nil if ok { profile.serviceEndpoints, err = endpoints.ParseEndpoints(list) if err != nil { lastErr = err } } list, ok = profile.configPerspective.GetAsStringArray(CfgOptionFilterListsKey) profile.filterListsSet = false if ok { profile.filterListIDs, err = filterlists.ResolveListIDs(list) if err != nil { log.Warningf("profiles: failed to resolve filter list IDs: %s", err) } else { profile.filterListsSet = true } } list, ok = profile.configPerspective.GetAsStringArray(CfgOptionSPNUsagePolicyKey) profile.spnUsagePolicy = nil if ok { profile.spnUsagePolicy, err = endpoints.ParseEndpoints(list) if err != nil { lastErr = err } } list, ok = profile.configPerspective.GetAsStringArray(CfgOptionTransitHubPolicyKey) profile.spnTransitHubPolicy = nil if ok { profile.spnTransitHubPolicy, err = endpoints.ParseEndpoints(list) if err != nil { lastErr = err } } list, ok = profile.configPerspective.GetAsStringArray(CfgOptionExitHubPolicyKey) profile.spnExitHubPolicy = nil if ok { profile.spnExitHubPolicy, err = endpoints.ParseEndpoints(list) if err != nil { lastErr = err } } return lastErr } // New returns a new Profile. // Optionally, you may supply custom configuration in the flat (key=value) form. func New(profile *Profile) *Profile { // Create profile if none is given. if profile == nil { profile = &Profile{} } // Set default and internal values. profile.Created = time.Now().Unix() profile.savedInternally = true // Expand any given configuration. if profile.Config != nil { profile.Config = config.Expand(profile.Config) } else { profile.Config = make(map[string]interface{}) } // Generate ID if none is given. if profile.ID == "" { if len(profile.Fingerprints) > 0 { // Derive from fingerprints. profile.ID = DeriveProfileID(profile.Fingerprints) } else { // Generate random ID as fallback. log.Warningf("profile: creating new profile without fingerprints to derive ID from") profile.ID = utils.RandomUUID("").String() } } // Make key from ID and source. profile.makeKey() // Prepare and parse initial profile config. profile.prepProfile() if err := profile.parseConfig(); err != nil { log.Errorf("profile: failed to parse new profile: %s", err) } return profile } // ScopedID returns the scoped ID (Source + ID) of the profile. func (profile *Profile) ScopedID() string { return MakeScopedID(profile.Source, profile.ID) } // makeKey derives and sets the record Key from the profile attributes. func (profile *Profile) makeKey() { profile.SetKey(MakeProfileKey(profile.Source, profile.ID)) } // Save saves the profile to the database. func (profile *Profile) Save() error { if profile.ID == "" { return errors.New("profile: tried to save profile without ID") } if profile.Source == "" { return fmt.Errorf("profile: profile %s does not specify a source", profile.ID) } return profileDB.Put(profile) } // delete deletes the profile from the database. func (profile *Profile) delete() error { // Check if a key is set. if !profile.KeyIsSet() { return errors.New("key is not set") } // Delete from database. profile.Meta().Delete() err := profileDB.Put(profile) if err != nil { return err } // Post handling is done by the profile update feed. return nil } // MarkStillActive marks the profile as still active. func (profile *Profile) MarkStillActive() { atomic.StoreInt64(profile.lastActive, time.Now().Unix()) } // LastActive returns the unix timestamp when the profile was last marked as // still active. func (profile *Profile) LastActive() int64 { return atomic.LoadInt64(profile.lastActive) } // String returns a string representation of the Profile. func (profile *Profile) String() string { return fmt.Sprintf("<%s %s/%s>", profile.Name, profile.Source, profile.ID) } // IsOutdated returns whether the this instance of the profile is marked as outdated. func (profile *Profile) IsOutdated() bool { return profile.outdated.IsSet() } // GetEndpoints returns the endpoint list of the profile. This functions // requires the profile to be read locked. func (profile *Profile) GetEndpoints() endpoints.Endpoints { return profile.endpoints } // GetServiceEndpoints returns the service endpoint list of the profile. This // functions requires the profile to be read locked. func (profile *Profile) GetServiceEndpoints() endpoints.Endpoints { return profile.serviceEndpoints } // AddEndpoint adds an endpoint to the endpoint list, saves the profile and reloads the configuration. func (profile *Profile) AddEndpoint(newEntry string) { profile.addEndpointEntry(CfgOptionEndpointsKey, newEntry) } // AddServiceEndpoint adds a service endpoint to the endpoint list, saves the profile and reloads the configuration. func (profile *Profile) AddServiceEndpoint(newEntry string) { profile.addEndpointEntry(CfgOptionServiceEndpointsKey, newEntry) } func (profile *Profile) addEndpointEntry(cfgKey, newEntry string) { changed := false // When finished, save the profile. defer func() { if !changed { return } err := profile.Save() if err != nil { log.Warningf("profile: failed to save profile %s after add an endpoint rule: %s", profile.ScopedID(), err) } }() // Lock the profile for editing. profile.Lock() defer profile.Unlock() // Get the endpoint list configuration value and add the new entry. endpointList, ok := profile.configPerspective.GetAsStringArray(cfgKey) if ok { // A list already exists, check for duplicates within the same prefix. newEntryPrefix := strings.Split(newEntry, " ")[0] + " " for _, entry := range endpointList { if !strings.HasPrefix(entry, newEntryPrefix) { // We found an entry with a different prefix than the new entry. // Beyond this entry we cannot possibly know if identical entries will // match, so we will have to add the new entry no matter what the rest // of the list has. break } if entry == newEntry { // An identical entry is already in the list, abort. log.Debugf("profile: ignoring new endpoint rule for %s, as identical is already present: %s", profile, newEntry) return } } endpointList = append([]string{newEntry}, endpointList...) } else { endpointList = []string{newEntry} } // Save new value back to profile. config.PutValueIntoHierarchicalConfig(profile.Config, cfgKey, endpointList) changed = true // Reload the profile manually in order to parse the newly added entry. profile.dataParsed = false err := profile.parseConfig() if err != nil { log.Errorf("profile: failed to parse %s config after adding endpoint: %s", profile, err) } } // LayeredProfile returns the layered profile associated with this profile. func (profile *Profile) LayeredProfile() *LayeredProfile { profile.Lock() defer profile.Unlock() return profile.layeredProfile } // EnsureProfile ensures that the given record is a *Profile, and returns it. func EnsureProfile(r record.Record) (*Profile, error) { // unwrap if r.IsWrapped() { // only allocate a new struct, if we need it newProfile := &Profile{} err := record.Unwrap(r, newProfile) if err != nil { return nil, err } return newProfile, nil } // or adjust type newProfile, ok := r.(*Profile) if !ok { return nil, fmt.Errorf("record not of type *Profile, but %T", r) } return newProfile, nil } // updateMetadata updates meta data fields on the profile and returns whether // the profile was changed. func (profile *Profile) updateMetadata(binaryPath string) (changed bool) { // Check if this is a local profile, else warn and return. if profile.Source != SourceLocal { log.Warningf("tried to update metadata for non-local profile %s", profile.ScopedID()) return false } // Set PresentationPath if unset. if profile.PresentationPath == "" && binaryPath != "" { profile.PresentationPath = binaryPath changed = true } // Migrate LinkedPath to PresentationPath. // TODO: Remove in v1.5 if profile.PresentationPath == "" && profile.LinkedPath != "" { profile.PresentationPath = profile.LinkedPath changed = true } // Set Name if unset. if profile.Name == "" && profile.PresentationPath != "" { // Generate a default profile name from path. profile.Name = binmeta.GenerateBinaryNameFromPath(profile.PresentationPath) changed = true } // Migrate to Fingerprints. // TODO: Remove in v1.5 if len(profile.Fingerprints) == 0 && profile.LinkedPath != "" { profile.Fingerprints = []Fingerprint{ { Type: FingerprintTypePathID, Operation: FingerprintOperationEqualsID, Value: profile.LinkedPath, }, } changed = true } // UI Backward Compatibility: // Fill LinkedPath with PresentationPath // TODO: Remove in v1.1 if profile.LinkedPath == "" && profile.PresentationPath != "" { profile.LinkedPath = profile.PresentationPath changed = true } return changed } // updateMetadataFromSystem updates the profile metadata with data from the // operating system and saves it afterwards. func (profile *Profile) updateMetadataFromSystem(ctx context.Context, md MatchingData) error { var changed bool // This function is only valid for local profiles. if profile.Source != SourceLocal || profile.PresentationPath == "" { return fmt.Errorf("tried to update metadata for non-local or non-path profile %s", profile.ScopedID()) } // Get home from ENV. var home string if env := md.Env(); env != nil { home = env["HOME"] } // Get binary icon and name. newIcon, newName, err := binmeta.GetIconAndName(ctx, profile.PresentationPath, home) switch { case err == nil: // Continue case errors.Is(err, binmeta.ErrIconIgnored): newIcon = nil // Continue default: log.Warningf("profile: failed to get binary icon/name for %s: %s", profile.PresentationPath, err) } // Apply new data to profile. func() { // Lock profile for applying metadata. profile.Lock() defer profile.Unlock() // Apply new name if it changed. if newName != "" && profile.Name != newName { profile.Name = newName changed = true } // Apply new icon if found. if newIcon != nil && !profile.iconExists(newIcon) { if len(profile.Icons) == 0 { profile.Icons = []binmeta.Icon{*newIcon} } else { profile.Icons = append(profile.Icons, *newIcon) profile.Icons = binmeta.SortAndCompactIcons(profile.Icons) } changed = true } }() // If anything changed, save the profile. // profile.Lock must not be held! if changed { err := profile.Save() if err != nil { log.Warningf("profile: failed to save %s after metadata update: %s", profile.ScopedID(), err) } } return nil } // Checks if the given icon already assigned to the profile. func (profile *Profile) iconExists(newIcon *binmeta.Icon) bool { for _, icon := range profile.Icons { if icon.Value == newIcon.Value && icon.Type == newIcon.Type && icon.Source == newIcon.Source { return true } } return false } ================================================ FILE: service/profile/special.go ================================================ package profile import ( "time" "github.com/safing/portmaster/base/log" ) const ( // UnidentifiedProfileID is the profile ID used for unidentified processes. UnidentifiedProfileID = "_unidentified" // UnidentifiedProfileName is the name used for unidentified processes. UnidentifiedProfileName = "Other Connections" // UnidentifiedProfileDescription is the description used for unidentified processes. UnidentifiedProfileDescription = `Connections that could not be attributed to a specific app. The Portmaster attributes connections (only TCP/UDP) to specific apps. When attribution for a connection fails, it ends up here. Connections from unsupported protocols (like ICMP/"ping") are always collected here. ` // UnsolicitedProfileID is the profile ID used for unsolicited connections. UnsolicitedProfileID = "_unsolicited" // UnsolicitedProfileName is the name used for unsolicited connections. UnsolicitedProfileName = "Network Noise" // UnsolicitedProfileDescription is the description used for unsolicited connections. UnsolicitedProfileDescription = `Common connections coming from your Local Area Network. Local Area Networks usually have quite a lot of traffic from applications that are trying to find things on the network. This might be a computer trying to find a printer, or a file sharing application searching for local peers. These network packets will automatically arrive at your device. These connections - the "network noise" - can be found in this app.` // SystemProfileID is the profile ID used for the system/kernel. SystemProfileID = "_system" // SystemProfileName is the name used for the system/kernel. SystemProfileName = "Operating System" // SystemProfileDescription is the description used for the system/kernel. SystemProfileDescription = "This is the operation system itself." // SystemResolverProfileID is the profile ID used for the system's DNS resolver. SystemResolverProfileID = "_system-resolver" // SystemResolverProfileName is the name used for the system's DNS resolver. SystemResolverProfileName = "System DNS Client" // SystemResolverProfileDescription is the description used for the system's DNS resolver. SystemResolverProfileDescription = `The System DNS Client is a system service that requires special handling. For regular network connections, the configured settings will apply as usual. DNS Requests coming from the System DNS Client, however, could actually be coming from any other application on the system: The System DNS Client resolves domain names on behalf of other applications. In order to correctly handle these, DNS Requests (not regular connections), do not take the globally configured Outgoing Rules into account. Additionally, the settings for the System DNS Client are specially pre-configured. If you are having issues or want to revert to the default settings, please delete this profile below. It will be automatically recreated with the default settings. ` // PortmasterProfileID is the profile ID used for the Portmaster Core itself. PortmasterProfileID = "_portmaster" // PortmasterProfileName is the name used for the Portmaster Core itself. PortmasterProfileName = "Portmaster Core Service" // PortmasterProfileDescription is the description used for the Portmaster Core itself. PortmasterProfileDescription = `This is the Portmaster itself, which runs in the background as a system service. App specific settings have no effect.` // PortmasterAppProfileID is the profile ID used for the Portmaster App. PortmasterAppProfileID = "_portmaster-app" // PortmasterAppProfileName is the name used for the Portmaster App. PortmasterAppProfileName = "Portmaster User Interface" // PortmasterAppProfileDescription is the description used for the Portmaster App. PortmasterAppProfileDescription = `This is the Portmaster UI Windows.` // PortmasterNotifierProfileID is the profile ID used for the Portmaster Notifier. PortmasterNotifierProfileID = "_portmaster-notifier" // PortmasterNotifierProfileName is the name used for the Portmaster Notifier. PortmasterNotifierProfileName = "Portmaster Notifier" // PortmasterNotifierProfileDescription is the description used for the Portmaster Notifier. PortmasterNotifierProfileDescription = `This is the Portmaster UI Tray Notifier.` ) func isSpecialProfileID(id string) bool { switch id { case UnidentifiedProfileID, UnsolicitedProfileID, SystemProfileID, SystemResolverProfileID, PortmasterProfileID, PortmasterAppProfileID, PortmasterNotifierProfileID: return true default: return false } } func updateSpecialProfileMetadata(profile *Profile, binaryPath string) (changed bool) { // Get new profile name and check if profile is applicable to special handling. var newProfileName, newDescription string switch profile.ID { case UnidentifiedProfileID: newProfileName = UnidentifiedProfileName newDescription = UnidentifiedProfileDescription case UnsolicitedProfileID: newProfileName = UnsolicitedProfileName newDescription = UnsolicitedProfileDescription case SystemProfileID: newProfileName = SystemProfileName newDescription = SystemProfileDescription case SystemResolverProfileID: newProfileName = SystemResolverProfileName newDescription = SystemResolverProfileDescription case PortmasterProfileID: newProfileName = PortmasterProfileName newDescription = PortmasterProfileDescription case PortmasterAppProfileID: newProfileName = PortmasterAppProfileName newDescription = PortmasterAppProfileDescription case PortmasterNotifierProfileID: newProfileName = PortmasterNotifierProfileName newDescription = PortmasterNotifierProfileDescription default: return false } // Update profile name if needed. if profile.Name != newProfileName { profile.Name = newProfileName changed = true } // Update description if needed. if profile.Description != newDescription { profile.Description = newDescription changed = true } // Update PresentationPath to new value. if profile.PresentationPath != binaryPath { profile.PresentationPath = binaryPath changed = true } return changed } func createSpecialProfile(profileID string, path string) *Profile { switch profileID { case UnidentifiedProfileID: return New(&Profile{ ID: UnidentifiedProfileID, Source: SourceLocal, PresentationPath: path, }) case UnsolicitedProfileID: return New(&Profile{ ID: UnsolicitedProfileID, Source: SourceLocal, PresentationPath: path, }) case SystemProfileID: return New(&Profile{ ID: SystemProfileID, Source: SourceLocal, PresentationPath: path, }) case SystemResolverProfileID: return New(&Profile{ ID: SystemResolverProfileID, Source: SourceLocal, PresentationPath: path, Config: map[string]interface{}{ // Explicitly setting the default action to "permit" will improve the // user experience for people who set the global default to "prompt". // Resolved domain from the system resolver are checked again when // attributed to a connection of a regular process. Otherwise, users // would see two connection prompts for the same domain. CfgOptionDefaultActionKey: DefaultActionPermitValue, // Disable force blockers. CfgOptionBlockScopeInternetKey: false, CfgOptionBlockScopeLANKey: false, CfgOptionBlockScopeLocalKey: false, CfgOptionBlockP2PKey: false, CfgOptionBlockInboundKey: false, // Explicitly allow localhost and answers to multicast protocols that // are commonly used by system resolvers. // TODO: When the Portmaster gains the ability to attribute multicast // responses to their requests, these rules can probably be removed // again. CfgOptionServiceEndpointsKey: []string{ "+ Localhost", // Allow everything from localhost. "+ LAN UDP/5353", // Allow inbound mDNS requests and multicast replies. "+ LAN UDP/5355", // Allow inbound LLMNR requests and multicast replies. "+ LAN UDP/1900", // Allow inbound SSDP requests and multicast replies. "- *", // Deny everything else. }, // Explicitly disable all filter lists, as these will be checked later // with the attributed connection. As this is the system resolver, this // list can instead be used as a global enforcement of filter lists, if // the system resolver is used. Users who want to CfgOptionFilterListsKey: []string{}, }, }) case PortmasterProfileID: return New(&Profile{ ID: PortmasterProfileID, Source: SourceLocal, PresentationPath: path, Config: map[string]interface{}{ // In case anything slips through the internal self-allow, be sure to // allow everything explicitly. // Blocking connections here can lead to a very literal deadlock. // This can currently happen, as fast-tracked connections are also // reset in the OS integration and might show up in the connection // handling if a packet in the other direction hits the firewall first. CfgOptionDefaultActionKey: DefaultActionPermitValue, CfgOptionBlockScopeInternetKey: false, CfgOptionBlockScopeLANKey: false, CfgOptionBlockScopeLocalKey: false, CfgOptionBlockP2PKey: false, CfgOptionBlockInboundKey: false, CfgOptionEndpointsKey: []string{ "+ *", }, CfgOptionServiceEndpointsKey: []string{ "+ Localhost", "+ LAN", "- *", }, }, Internal: true, }) case PortmasterAppProfileID: return New(&Profile{ ID: PortmasterAppProfileID, Source: SourceLocal, PresentationPath: path, Config: map[string]interface{}{ // Block all connections by default for the Portmaster UI profile, // since the only required connections are to the Portmaster Core, // which are fast-tracked. // // This ensures that any unexpected connections — // possibly made by the internal WebView implementation — // are blocked. CfgOptionDefaultActionKey: DefaultActionBlockValue, CfgOptionBlockScopeInternetKey: false, // This is stronger than the rules, and thus must be false in order to access safing.io. CfgOptionBlockScopeLANKey: true, CfgOptionBlockScopeLocalKey: true, CfgOptionBlockP2PKey: true, CfgOptionBlockInboundKey: true, CfgOptionEndpointsKey: []string{ "+ .safing.io", }, }, Internal: true, }) case PortmasterNotifierProfileID: return New(&Profile{ ID: PortmasterNotifierProfileID, Source: SourceLocal, PresentationPath: path, Config: map[string]interface{}{ CfgOptionDefaultActionKey: DefaultActionBlockValue, CfgOptionBlockScopeInternetKey: false, CfgOptionBlockScopeLANKey: false, CfgOptionBlockScopeLocalKey: false, CfgOptionBlockP2PKey: false, CfgOptionBlockInboundKey: true, CfgOptionEndpointsKey: []string{ "+ Localhost", }, }, Internal: true, }) default: return nil } } // specialProfileNeedsReset is used as a workaround until we can properly use // profile layering in a way that it is also correctly handled by the UI. We // check if the special profile has not been changed by the user and if not, // check if the profile is outdated and can be upgraded. func specialProfileNeedsReset(profile *Profile) bool { if profile == nil { return false } switch { case profile.Source != SourceLocal: // Special profiles live in the local scope only. return false case profile.LastEdited > 0: // Profile was edited - don't override user settings. return false } switch profile.ID { case SystemResolverProfileID: return canBeUpgraded(profile, "22.8.2023") case PortmasterProfileID: return canBeUpgraded(profile, "22.8.2023") case PortmasterAppProfileID: return canBeUpgraded(profile, "22.8.2023") default: // Not a special profile or no upgrade available yet. return false } } func canBeUpgraded(profile *Profile, upgradeDate string) bool { // Parse upgrade date. upgradeTime, err := time.Parse("2.1.2006", upgradeDate) if err != nil { log.Warningf("profile: failed to parse date %q: %s", upgradeDate, err) return false } // Check if the upgrade is applicable. if profile.Created < upgradeTime.Unix() { log.Infof("profile: upgrading special profile %s", profile.ScopedID()) return true } return false } ================================================ FILE: service/resolver/api.go ================================================ package resolver import ( "net/http" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/database/record" ) func registerAPI() error { if err := api.RegisterEndpoint(api.Endpoint{ Path: "dns/clear", Write: api.PermitUser, ActionFunc: clearNameCacheHandler, Name: "Clear cached DNS records", Description: "Deletes all saved DNS records from the database.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: "dns/resolvers", Read: api.PermitAnyone, StructFunc: exportDNSResolvers, Name: "List DNS Resolvers", Description: "List currently configured DNS resolvers and their status.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `dns/cache/{query:[a-z0-9\.-]{0,512}\.[A-Z]{1,32}}`, Read: api.PermitUser, RecordFunc: func(r *api.Request) (record.Record, error) { return recordDatabase.Get(nameRecordsKeyPrefix + r.URLVars["query"]) }, Name: "Get DNS Record from Cache", Description: "Returns cached dns records from the internal cache.", Parameters: []api.Parameter{{ Method: http.MethodGet, Field: "query (in path)", Value: "fqdn and query type", Description: "Specify the query like this: `example.com.A`.", }}, }); err != nil { return err } return nil } type resolverExport struct { *Resolver Failing bool } func exportDNSResolvers(*api.Request) (interface{}, error) { resolversLock.RLock() defer resolversLock.RUnlock() export := make([]resolverExport, 0, len(globalResolvers)) for _, r := range globalResolvers { export = append(export, resolverExport{ Resolver: r, Failing: r.Conn.IsFailing(), }) } return export, nil } ================================================ FILE: service/resolver/block-detection.go ================================================ package resolver import ( "net" "github.com/miekg/dns" ) // Supported upstream block detections. const ( BlockDetectionRefused = "refused" BlockDetectionZeroIP = "zeroip" BlockDetectionEmptyAnswer = "empty" BlockDetectionDisabled = "disabled" ) func isBlockedUpstream(resolver *Resolver, answer *dns.Msg) bool { if resolver.UpstreamBlockDetection == BlockDetectionDisabled { return false } switch resolver.UpstreamBlockDetection { case BlockDetectionRefused: return answer.Rcode == dns.RcodeRefused case BlockDetectionZeroIP: if answer.Rcode != dns.RcodeSuccess { return false } var ips []net.IP for _, rr := range answer.Answer { switch v := rr.(type) { case *dns.A: ips = append(ips, v.A) case *dns.AAAA: ips = append(ips, v.AAAA) } } if len(ips) == 0 { return false // we expected an empty IP } for _, ip := range ips { if ip.To4() != nil { if !ip.Equal(net.IPv4zero) { return false } } else { if !ip.To16().Equal(net.IPv6zero) { return false } } } return true case BlockDetectionEmptyAnswer: return answer.Rcode == dns.RcodeNameError && len(answer.Ns) == 0 && len(answer.Answer) == 0 && len(answer.Extra) == 0 } return false } ================================================ FILE: service/resolver/compat.go ================================================ package resolver import "net" // This is a workaround for enabling the resolver to work with the compat // module without importing it. Long-term, the network module should not import // the resolver package, as this breaks the SPN hub. var ( CompatDNSCheckInternalDomainScope string CompatSelfCheckIsFailing func() bool CompatSubmitDNSCheckDomain func(subdomain string) (respondWith net.IP) ) ================================================ FILE: service/resolver/config.go ================================================ package resolver import ( "errors" "fmt" "strings" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/status" ) // Configuration Keys. var ( defaultNameServers = []string{ // Collection of default DNS Servers // For a detailed explanation how we choose our default resolvers, check out // https://safing.io/blog/2020/07/07/how-safing-selects-its-default-dns-providers/ // These resolvers define a working set. Which provider we selected as the // primary depends on the current situation. // We encourage everyone who has the technical abilities to set their own preferred servers. // For a list of configuration options, see // https://github.com/safing/portmaster/service/wiki/DNS-Server-Settings // Quad9 (encrypted DNS) // "dot://dns.quad9.net?ip=9.9.9.9&name=Quad9&blockedif=empty", // "dot://dns.quad9.net?ip=149.112.112.112&name=Quad9&blockedif=empty", // Cloudflare (encrypted DNS, with malware protection) "dot://cloudflare-dns.com?ip=1.1.1.2&name=Cloudflare&blockedif=zeroip", "dot://cloudflare-dns.com?ip=1.0.0.2&name=Cloudflare&blockedif=zeroip", // AdGuard (encrypted DNS, default flavor) // "dot://dns.adguard.com?ip=94.140.14.14&name=AdGuard&blockedif=zeroip", // "dot://dns.adguard.com?ip=94.140.15.15&name=AdGuard&blockedif=zeroip", // Foundation for Applied Privacy (encrypted DNS) // "dot://dot1.applied-privacy.net?ip=146.255.56.98&name=AppliedPrivacy", // Quad9 (plain DNS) // `dns://9.9.9.9:53?name=Quad9&blockedif=empty`, // `dns://149.112.112.112:53?name=Quad9&blockedif=empty`, // Cloudflare (plain DNS, with malware protection) // `dns://1.1.1.2:53?name=Cloudflare&blockedif=zeroip`, // `dns://1.0.0.2:53?name=Cloudflare&blockedif=zeroip`, // AdGuard (plain DNS, default flavor) // `dns://94.140.14.14&name=AdGuard&blockedif=zeroip`, // `dns://94.140.15.15&name=AdGuard&blockedif=zeroip`, } CfgOptionNameServersKey = "dns/nameservers" configuredNameServers config.StringArrayOption cfgOptionNameServersOrder = 0 CfgOptionNoAssignedNameserversKey = "dns/noAssignedNameservers" noAssignedNameservers config.BoolOption cfgOptionNoAssignedNameserversOrder = 1 CfgOptionUseStaleCacheKey = "dns/useStaleCache" useStaleCacheConfigOption *config.Option useStaleCache config.BoolOption cfgOptionUseStaleCacheOrder = 2 CfgOptionNoMulticastDNSKey = "dns/noMulticastDNS" noMulticastDNS config.BoolOption cfgOptionNoMulticastDNSOrder = 3 CfgOptionNoInsecureProtocolsKey = "dns/noInsecureProtocols" noInsecureProtocols config.BoolOption cfgOptionNoInsecureProtocolsOrder = 4 CfgOptionDontResolveSpecialDomainsKey = "dns/dontResolveSpecialDomains" dontResolveSpecialDomains config.BoolOption cfgOptionDontResolveSpecialDomainsOrder = 16 CfgOptionNameserverRetryRateKey = "dns/nameserverRetryRate" nameserverRetryRate config.IntOption cfgOptionNameserverRetryRateOrder = 32 ) func prepConfig() error { err := config.Register(&config.Option{ Name: "DNS Servers", Key: CfgOptionNameServersKey, Description: "DNS servers to use for resolving DNS requests.", Help: strings.ReplaceAll(`DNS servers are used in the order as entered. The first one will be used as the primary DNS Server. Only if it fails, will the other servers be used as a fallback - in their respective order. If all fail, or if no DNS Server is configured here, the Portmaster will use the one configured in your system or network. Additionally, if it is more likely that the DNS server of your system or network has a (better) answer to a request, they will be asked first. This will be the case for special local domains and domain spaces announced on the current network. DNS servers are configured in a URL format. This allows you to specify special settings for a resolver. If you just want to use a resolver at IP 10.2.3.4, please enter: "dns://10.2.3.4" The format is: "protocol://host:port?parameter=value¶meter=value" For DoH servers, you can also just paste the URL given by the DNS provider. When referring to the DNS server using a domain name, as with DoH, it is highly recommended to also specify the IP address using the "ip" parameter, so Portmaster does not have to resolve it. - Protocol - "dot": DNS-over-TLS (or "tls"; recommended) - "doh": DNS-over-HTTPS (or "https") - "dns": plain old DNS - "tcp": plain old DNS over TCP - Host: specify the domain or IP of the resolver - Port: optionally define a custom port - Parameters: - "name": give your DNS Server a name that is used for messages and logs - "verify": domain name to verify for "dot", only valid for "dot" and "doh" - "ip": IP address (if using a domain), so Portmaster does not need to resolve it using the system resolver - this is highly recommended - "blockedif": detect if the name server blocks a query, options: - "empty": server replies with NXDomain status, but without any other record in any section - "refused": server replies with Refused status - "zeroip": server replies with an IP address, but it is zero - "search": specify prioritized domains/TLDs for this resolver (delimited by ",") - "search-only": use this resolver for domains in the "search" parameter only (no value) `, `"`, "`"), Sensitive: true, OptType: config.OptTypeStringArray, ExpertiseLevel: config.ExpertiseLevelUser, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: defaultNameServers, ValidationRegex: fmt.Sprintf("^(%s|%s|%s|%s|%s|%s)://.*", ServerTypeDoT, ServerTypeDoH, ServerTypeDNS, ServerTypeTCP, HTTPSProtocol, TLSProtocol), ValidationFunc: validateNameservers, Annotations: config.Annotations{ config.DisplayHintAnnotation: config.DisplayHintOrdered, config.DisplayOrderAnnotation: cfgOptionNameServersOrder, config.CategoryAnnotation: "Servers", config.QuickSettingsAnnotation: []config.QuickSetting{ { Name: "Set Cloudflare (with Malware Filter)", Action: config.QuickReplace, Value: []string{ "dot://cloudflare-dns.com?ip=1.1.1.2&name=Cloudflare&blockedif=zeroip", "dot://cloudflare-dns.com?ip=1.0.0.2&name=Cloudflare&blockedif=zeroip", }, }, { Name: "Set Quad9", Action: config.QuickReplace, Value: []string{ "dot://dns.quad9.net?ip=9.9.9.9&name=Quad9&blockedif=empty", "dot://dns.quad9.net?ip=149.112.112.112&name=Quad9&blockedif=empty", }, }, { Name: "Set AdGuard", Action: config.QuickReplace, Value: []string{ "dot://dns.adguard.com?ip=94.140.14.14&name=AdGuard&blockedif=zeroip", "dot://dns.adguard.com?ip=94.140.15.15&name=AdGuard&blockedif=zeroip", }, }, { Name: "Set Foundation for Applied Privacy", Action: config.QuickReplace, Value: []string{ "dot://dot1.applied-privacy.net?ip=146.255.56.98&name=AppliedPrivacy", }, }, { Name: "Add Cloudflare (as fallback)", Action: config.QuickMergeBottom, Value: []string{ "dot://cloudflare-dns.com?ip=1.1.1.1&name=Cloudflare&blockedif=zeroip", "dot://cloudflare-dns.com?ip=1.0.0.1&name=Cloudflare&blockedif=zeroip", }, }, }, "self:detail:internalSpecialUseDomains": internalSpecialUseDomains, "self:detail:connectivityDomains": netenv.ConnectivityDomains, }, }) if err != nil { return err } configuredNameServers = config.Concurrent.GetAsStringArray(CfgOptionNameServersKey, defaultNameServers) err = config.Register(&config.Option{ Name: "Retry Failing DNS Servers", Key: CfgOptionNameserverRetryRateKey, Description: "Duration in seconds how often failing DNS server should be retried. This is done continuously in the background.", OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelDeveloper, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: 300, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionNameserverRetryRateOrder, config.UnitAnnotation: "seconds", config.CategoryAnnotation: "Servers", }, ValidationRegex: `^[1-9][0-9]{1,5}$`, }) if err != nil { return err } nameserverRetryRate = config.Concurrent.GetAsInt(CfgOptionNameserverRetryRateKey, 300) err = config.Register(&config.Option{ Name: "Ignore System/Network Servers", Key: CfgOptionNoAssignedNameserversKey, Description: "Ignore DNS servers configured in your system or network. This may break domains from your local network.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: false, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionNoAssignedNameserversOrder, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.CategoryAnnotation: "Servers", "self:detail:specialUseDomains": specialUseDomains, }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } noAssignedNameservers = config.Concurrent.GetAsBool(CfgOptionNoAssignedNameserversKey, false) useStaleCacheConfigOption = &config.Option{ Name: "Always Use DNS Cache", Key: CfgOptionUseStaleCacheKey, Description: "Always use the DNS cache, even if entries have expired. Expired entries are refreshed afterwards in the background. This can improve DNS resolving performance a lot, but may lead to occasional connection errors due to outdated DNS records.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelUser, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: false, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionUseStaleCacheOrder, config.CategoryAnnotation: "Resolving", }, } err = config.Register(useStaleCacheConfigOption) if err != nil { return err } useStaleCache = config.Concurrent.GetAsBool(CfgOptionUseStaleCacheKey, false) err = config.Register(&config.Option{ Name: "Ignore Multicast DNS", Key: CfgOptionNoMulticastDNSKey, Description: "Do not resolve using Multicast DNS. This may break certain Plug and Play devices and services.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: false, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionNoMulticastDNSOrder, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.CategoryAnnotation: "Resolving", "self:detail:multicastDomains": multicastDomains, }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } noMulticastDNS = config.Concurrent.GetAsBool(CfgOptionNoMulticastDNSKey, false) err = config.Register(&config.Option{ Name: "Use Secure Protocols Only", Key: CfgOptionNoInsecureProtocolsKey, Description: "Never resolve using insecure protocols, ie. plain DNS. This may break certain local DNS services, which always use plain DNS.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: false, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionNoInsecureProtocolsOrder, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.CategoryAnnotation: "Resolving", }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } noInsecureProtocols = config.Concurrent.GetAsBool(CfgOptionNoInsecureProtocolsKey, false) err = config.Register(&config.Option{ Name: "Block Unofficial TLDs", Key: CfgOptionDontResolveSpecialDomainsKey, Description: fmt.Sprintf( "Block %s. Unofficial domains may pose a security risk. This setting does not affect .onion domains in the Tor Browser.", formatScopeList(specialServiceDomains), ), OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionDontResolveSpecialDomainsOrder, config.DisplayHintAnnotation: status.DisplayHintSecurityLevel, config.CategoryAnnotation: "Resolving", "self:detail:specialServiceDomains": specialServiceDomains, }, Migrations: []config.MigrationFunc{status.MigrateSecurityLevelToBoolean}, }) if err != nil { return err } dontResolveSpecialDomains = config.Concurrent.GetAsBool(CfgOptionDontResolveSpecialDomainsKey, false) return nil } func validateNameservers(value interface{}) error { list, ok := value.([]string) if !ok { return errors.New("invalid type") } for i, entry := range list { _, _, err := createResolver(entry, ServerSourceConfigured) if err != nil { return fmt.Errorf("failed to parse DNS server \"%s\" (#%d): %w", entry, i+1, err) } } return nil } func formatScopeList(list []string) string { formatted := make([]string, 0, len(list)) for _, domain := range list { formatted = append(formatted, strings.TrimRight(domain, ".")) } return strings.Join(formatted, ", ") } ================================================ FILE: service/resolver/doc.go ================================================ /* Package resolver is responsible for querying DNS. # DNS Servers Internal lists of resolvers to use are built on start and rebuilt on every config or network change. Configured DNS servers are prioritized over servers assigned by dhcp. Domain and search options (here referred to as "search scopes") are being considered. # Security Usage of DNS Servers can be regulated using the configuration: DoNotUseAssignedDNS // Do not use DNS servers assigned by DHCP DoNotUseMDNS // Do not use mDNS DoNotForwardSpecialDomains // Do not forward special domains to local resolvers, except if they have a search scope for it Note: The DHCP options "domain" and "search" are ignored for servers assigned by DHCP that do not reside within local address space. # Resolving DNS Various different queries require the resolver to behave in different manner: Queries for "localhost." are immediately responded with 127.0.0.1 and ::1, for A and AAAA queries and NXDomain for others. Reverse lookups on local address ranges (10/8, 172.16/12, 192.168/16, fe80::/7) will be tried against every local resolver and finally mDNS until a successful, non-NXDomain answer is received. Special domains ("example.", "example.com.", "example.net.", "example.org.", "invalid.", "test.", "onion.") are resolved using search scopes and local resolvers. All other domains are resolved using search scopes and all available resolvers. */ package resolver ================================================ FILE: service/resolver/failing.go ================================================ package resolver import ( "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" ) var ( // FailThreshold is amount of errors a resolvers must experience in order to be regarded as failed. FailThreshold = 5 // FailObserveDuration is the duration in which failures are counted in order to mark a resolver as failed. FailObserveDuration = time.Duration(FailThreshold) * 10 * time.Second ) // IsFailing returns if this resolver is currently failing. func (brc *BasicResolverConn) IsFailing() bool { return brc.failing.IsSet() } // ReportFailure reports that an error occurred with this resolver. func (brc *BasicResolverConn) ReportFailure() { // Don't mark resolver as failed if we are offline. if !netenv.Online() { return } // Ingore report when we are already failing. if brc.IsFailing() { return } brc.failLock.Lock() defer brc.failLock.Unlock() // Check if we are within the observation period. if time.Since(brc.failingStarted) > FailObserveDuration { brc.fails = 1 brc.failingStarted = time.Now() return } // Increase and check if we need to set to failing. brc.fails++ if brc.fails > FailThreshold { brc.failing.Set() } // Report to netenv that a configured server failed. if brc.resolver.Info.Source == ServerSourceConfigured { netenv.ConnectedToDNS.UnSet() } } // ResetFailure resets the failure status. func (brc *BasicResolverConn) ResetFailure() { if brc.failing.SetToIf(true, false) { brc.failLock.Lock() defer brc.failLock.Unlock() brc.fails = 0 brc.failingStarted = time.Time{} } // Report to netenv that a configured server succeeded. if brc.resolver.Info.Source == ServerSourceConfigured { netenv.ConnectedToDNS.Set() } } func checkFailingResolvers(wc *mgr.WorkerCtx) error { var resolvers []*Resolver // Set next execution time. module.failingResolverWorkerMgr.Delay(time.Duration(nameserverRetryRate()) * time.Second) // Make a copy of the resolver list. func() { resolversLock.Lock() defer resolversLock.Unlock() resolvers = make([]*Resolver, len(globalResolvers)) copy(resolvers, globalResolvers) }() // Start logging. ctx, tracer := log.AddTracer(wc.Ctx()) tracer.Debugf("resolver: checking failed resolvers") defer tracer.Submit() // Go through all resolvers and check if they are reachable again. for i, resolver := range resolvers { // Skip resolver that are not failing. if !resolver.Conn.IsFailing() { continue } tracer.Tracef("resolver: testing failed resolver [%d/%d] %s", i+1, len(resolvers), resolver) // Test if we can resolve via this resolver. ips, _, err := testConnectivity(ctx, netenv.DNSTestDomain, resolver) switch { case err != nil: tracer.Debugf("resolver: failed resolver %s is still failing: %s", resolver, err) case len(ips) == 0 || !ips[0].Equal(netenv.DNSTestExpectedIP): tracer.Debugf("resolver: failed resolver %s received unexpected A records: %s", resolver, ips) default: // Resolver test successful. tracer.Infof("resolver: check successful, resolver %s is available again", resolver) resolver.Conn.ResetFailure() } // Check if context was canceled. if ctx.Err() != nil { return ctx.Err() } } return nil } ================================================ FILE: service/resolver/ipinfo.go ================================================ package resolver import ( "fmt" "strings" "sync" "time" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" ) const ( // IPInfoProfileScopeGlobal is the profile scope used for unscoped IPInfo entries. IPInfoProfileScopeGlobal = "global" ) var ipInfoDatabase = database.NewInterface(&database.Options{ Local: true, Internal: true, // Cache entries because new/updated entries will often be queries soon // after inserted. CacheSize: 256, // We only use the cache database here, so we can delay and batch all our // writes. Also, no one else accesses these records, so we are fine using // this. DelayCachedWrites: "cache", }) // ResolvedDomain holds a Domain name and a list of // CNAMES that have been resolved. type ResolvedDomain struct { // Domain is the domain as requested by the application. Domain string // CNAMEs is a list of CNAMEs that have been resolved for // Domain. CNAMEs []string // Resolver holds basic information about the resolver that provided this // information. Resolver *ResolverInfo // DNSRequestContext holds the DNS request context. DNSRequestContext *DNSRequestContext // Expires holds the timestamp when this entry expires. // This does not mean that the entry may not be used anymore afterwards, // but that this is used to calcuate the TTL of the database record. Expires int64 } // AddCNAMEs adds all cnames from the map related to its set Domain. func (resolved *ResolvedDomain) AddCNAMEs(cnames map[string]string) { // Resolve all CNAMEs in the correct order and add the to the record - up to max 50 layers. domain := resolved.Domain domainLoop: for range 50 { nextDomain, isCNAME := cnames[domain] switch { case !isCNAME: break domainLoop case nextDomain == resolved.Domain: break domainLoop case nextDomain == domain: break domainLoop } resolved.CNAMEs = append(resolved.CNAMEs, nextDomain) domain = nextDomain } } // String returns a string representation of ResolvedDomain including // the CNAME chain. It implements fmt.Stringer. func (resolved *ResolvedDomain) String() string { ret := resolved.Domain cnames := "" if len(resolved.CNAMEs) > 0 { cnames = " (-> " + strings.Join(resolved.CNAMEs, "->") + ")" } return ret + cnames } // ResolvedDomains is a helper type for operating on a slice // of ResolvedDomain. type ResolvedDomains []ResolvedDomain // String returns a string representation of all domains joined // to a single string. func (rds ResolvedDomains) String() string { domains := make([]string, len(rds)) for idx, n := range rds { domains[idx] = n.String() } return strings.Join(domains, " or ") } // IPInfo represents various information about an IP. type IPInfo struct { record.Base sync.Mutex // IP holds the actual IP address. IP string // ProfileID is used to scope this entry to a process group. ProfileID string // ResolvedDomain is a slice of domains that // have been requested by various applications // and have been resolved to IP. ResolvedDomains ResolvedDomains } // AddDomain adds a new resolved domain to IPInfo. func (info *IPInfo) AddDomain(resolved ResolvedDomain) { info.Lock() defer info.Unlock() // Delete old for the same domain. for idx, d := range info.ResolvedDomains { if d.Domain == resolved.Domain { info.ResolvedDomains = append(info.ResolvedDomains[:idx], info.ResolvedDomains[idx+1:]...) break } } // Add new entry to the end. info.ResolvedDomains = append(info.ResolvedDomains, resolved) } // MostRecentDomain returns the most recent domain. func (info *IPInfo) MostRecentDomain() *ResolvedDomain { info.Lock() defer info.Unlock() if len(info.ResolvedDomains) == 0 { return nil } mostRecent := info.ResolvedDomains[len(info.ResolvedDomains)-1] return &mostRecent } func makeIPInfoKey(profileID, ip string) string { return fmt.Sprintf("cache:intel/ipInfo/%s/%s", profileID, ip) } // GetIPInfo gets an IPInfo record from the database. func GetIPInfo(profileID, ip string) (*IPInfo, error) { r, err := ipInfoDatabase.Get(makeIPInfoKey(profileID, ip)) if err != nil { return nil, err } // unwrap if r.IsWrapped() { // only allocate a new struct, if we need it newInfo := &IPInfo{} err = record.Unwrap(r, newInfo) if err != nil { return nil, err } return newInfo, nil } // or adjust type newInfo, ok := r.(*IPInfo) if !ok { return nil, fmt.Errorf("record not of type *IPInfo, but %T", r) } return newInfo, nil } // Save saves the IPInfo record to the database. func (info *IPInfo) Save() error { info.Lock() // Set database key if not yet set already. if !info.KeyIsSet() { // Default to global scope if scope is unset. if info.ProfileID == "" { info.ProfileID = IPInfoProfileScopeGlobal } info.SetKey(makeIPInfoKey(info.ProfileID, info.IP)) } // Calculate and set cache expiry. expires := time.Now().Unix() + 86400 // Minimum TTL of one day. for _, rd := range info.ResolvedDomains { if rd.Expires > expires { expires = rd.Expires } } info.UpdateMeta() expires += 3600 // Add one hour to expiry as a buffer. info.Meta().SetAbsoluteExpiry(expires) info.Unlock() return ipInfoDatabase.Put(info) } // String returns a string consisting of the domains that have seen to use this IP. func (info *IPInfo) String() string { info.Lock() defer info.Unlock() return fmt.Sprintf("", info.Key(), info.IP, info.ResolvedDomains.String()) } ================================================ FILE: service/resolver/ipinfo_test.go ================================================ package resolver import ( "testing" "github.com/stretchr/testify/assert" ) func TestIPInfo(t *testing.T) { t.Parallel() example := ResolvedDomain{ Domain: "example.com.", } subExample := ResolvedDomain{ Domain: "sub1.example.com", CNAMEs: []string{"example.com"}, } info := &IPInfo{ IP: "1.2.3.4", ResolvedDomains: ResolvedDomains{ example, subExample, }, } sub2Example := ResolvedDomain{ Domain: "sub2.example.com", CNAMEs: []string{"sub1.example.com", "example.com"}, } info.AddDomain(sub2Example) assert.Equal(t, ResolvedDomains{example, subExample, sub2Example}, info.ResolvedDomains) // try again, should do nothing now info.AddDomain(sub2Example) assert.Equal(t, ResolvedDomains{example, subExample, sub2Example}, info.ResolvedDomains) subOverWrite := ResolvedDomain{ Domain: "sub1.example.com", CNAMEs: []string{}, // now without CNAMEs } info.AddDomain(subOverWrite) assert.Equal(t, ResolvedDomains{example, sub2Example, subOverWrite}, info.ResolvedDomains) } ================================================ FILE: service/resolver/main.go ================================================ package resolver import ( "context" "errors" "fmt" "net" "strings" "sync" "sync/atomic" "github.com/tevino/abool" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/base/utils/debug" _ "github.com/safing/portmaster/service/core/base" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" ) // ResolverModule is the DNS resolver module. type ResolverModule struct { //nolint mgr *mgr.Manager instance instance failingResolverWorkerMgr *mgr.WorkerMgr suggestUsingStaleCacheTask *mgr.WorkerMgr isDisabled atomic.Bool states *mgr.StateMgr } // Manager returns the module manager. func (rm *ResolverModule) Manager() *mgr.Manager { return rm.mgr } // States returns the module state manager. func (rm *ResolverModule) States() *mgr.StateMgr { return rm.states } // Start starts the module. func (rm *ResolverModule) Start() error { return start() } // Stop stops the module. func (rm *ResolverModule) Stop() error { return nil } func (rm *ResolverModule) IsDisabled() bool { return rm.isDisabled.Load() } func prep() error { // Set DNS test connectivity function for the online status check netenv.DNSTestQueryFunc = func(ctx context.Context, fdqn string) (ips []net.IP, ok bool, err error) { return testConnectivity(ctx, fdqn, nil) } intel.SetReverseResolver(ResolveIPAndValidate) if err := registerAPI(); err != nil { return err } if err := prepEnvResolver(); err != nil { return err } return prepConfig() } func start() error { // load resolvers from config and environment loadResolvers() // reload after network change module.instance.NetEnv().EventNetworkChange.AddCallback( "update nameservers", func(_ *mgr.WorkerCtx, _ struct{}) (bool, error) { loadResolvers() log.Debug("resolver: reloaded nameservers due to network change") return false, nil }, ) // Force resolvers to reconnect when SPN has connected. module.instance.GetEventSPNConnected().AddCallback( "force resolver reconnect", func(ctx *mgr.WorkerCtx, _ struct{}) (bool, error) { ForceResolverReconnect(ctx.Ctx()) return false, nil }) // reload after config change prevNameservers := strings.Join(configuredNameServers(), " ") module.instance.Config().EventConfigChange.AddCallback( "update nameservers", func(_ *mgr.WorkerCtx, _ struct{}) (bool, error) { newNameservers := strings.Join(configuredNameServers(), " ") if newNameservers != prevNameservers { prevNameservers = newNameservers loadResolvers() log.Debug("resolver: reloaded nameservers due to config change") } return false, nil }) // Check failing resolvers regularly and when the network changes. module.failingResolverWorkerMgr = module.mgr.NewWorkerMgr("check failing resolvers", checkFailingResolvers, nil) module.failingResolverWorkerMgr.Go() module.instance.NetEnv().EventNetworkChange.AddCallback( "check failing resolvers", func(wc *mgr.WorkerCtx, _ struct{}) (bool, error) { return false, checkFailingResolvers(wc) }) module.suggestUsingStaleCacheTask = module.mgr.NewWorkerMgr("suggest using stale cache", suggestUsingStaleCacheTask, nil) module.suggestUsingStaleCacheTask.Go() module.mgr.Go( "mdns handler", listenToMDNS, ) module.mgr.Go("name record delayed cache writer", recordDatabase.DelayedCacheWriter) module.mgr.Go("ip info delayed cache writer", ipInfoDatabase.DelayedCacheWriter) return nil } var localAddrFactory func(network string) net.Addr // SetLocalAddrFactory supplies the intel package with a function to get permitted local addresses for connections. func SetLocalAddrFactory(laf func(network string) net.Addr) { if localAddrFactory == nil { localAddrFactory = laf } } func getLocalAddr(network string) net.Addr { if localAddrFactory != nil { return localAddrFactory(network) } return nil } var ( failingResolverNotification *notifications.Notification failingResolverNotificationSet = abool.New() failingResolverNotificationLock sync.Mutex failingResolverErrorID = "resolver:all-configured-resolvers-failed" ) func notifyAboutFailingResolvers() { failingResolverNotificationLock.Lock() defer failingResolverNotificationLock.Unlock() failingResolverNotificationSet.Set() // Check if already set. if failingResolverNotification != nil { return } // Create new notification. n := ¬ifications.Notification{ EventID: failingResolverErrorID, Type: notifications.Error, Title: "Configured DNS Servers Failing", Message: `All configured DNS servers in Portmaster are failing. You might not be able to connect to these servers, or all of these servers are offline. Choosing different DNS servers might fix this problem. While the issue persists, Portmaster will use the DNS servers from your system or network, if permitted by configuration. Alternatively, there might be something on your device that is interfering with Portmaster. This could be a firewall or another secure DNS resolver software. If that is your suspicion, please [check if you are running incompatible software here](https://docs.safing.io/portmaster/install/status/software-compatibility). This notification will go away when Portmaster detects a working configured DNS server.`, ShowOnSystem: true, AvailableActions: []*notifications.Action{{ Text: "Change DNS Servers", Type: notifications.ActionTypeOpenSetting, Payload: ¬ifications.ActionTypeOpenSettingPayload{ Key: CfgOptionNameServersKey, }, }}, } notifications.Notify(n) failingResolverNotification = n n.SyncWithState(module.states) } func resetFailingResolversNotification() { if failingResolverNotificationSet.IsNotSet() { return } failingResolverNotificationLock.Lock() defer failingResolverNotificationLock.Unlock() // Remove the notification. if failingResolverNotification != nil { failingResolverNotification.Delete() failingResolverNotification = nil } // Additionally, resolve the module error, if not done through the notification. module.states.Remove(failingResolverErrorID) } // AddToDebugInfo adds the system status to the given debug.Info. func AddToDebugInfo(di *debug.Info) { resolversLock.Lock() defer resolversLock.Unlock() content := make([]string, 0, (len(globalResolvers)*4)-1) var working, total int for i, resolver := range globalResolvers { // Count for summary. total++ failing := resolver.Conn.IsFailing() if !failing { working++ } // Add section. content = append(content, resolver.Info.DescriptiveName()) content = append(content, fmt.Sprintf(" %s", resolver.Info.ID())) if resolver.SearchOnly { content = append(content, " Used for search domains only!") } if len(resolver.Search) > 0 { content = append(content, fmt.Sprintf(" Search Domains: %v", strings.Join(resolver.Search, ", "))) } if resolver.LinkLocalUnavailable { content = append(content, " Link-local, but not available: ignoring") } content = append(content, fmt.Sprintf(" Failing: %v", resolver.Conn.IsFailing())) // Add a empty line for all but the last entry. if i+1 < len(globalResolvers) { content = append(content, "") } } di.AddSection( fmt.Sprintf("Resolvers: %d/%d", working, total), debug.UseCodeSection|debug.AddContentLineBreaks, content..., ) } var ( module *ResolverModule shimLoaded atomic.Bool ) // New returns a new Resolver module. func New(instance instance) (*ResolverModule, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Resolver") module = &ResolverModule{ mgr: m, instance: instance, states: mgr.NewStateMgr(m), } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface { NetEnv() *netenv.NetEnv Config() *config.Config GetEventSPNConnected() *mgr.EventMgr[struct{}] } ================================================ FILE: service/resolver/main_test.go ================================================ package resolver import ( "fmt" "os" "testing" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" "github.com/safing/portmaster/service/core/base" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/ui" "github.com/safing/portmaster/service/updates" ) var domainFeed = make(chan string) type testInstance struct { db *dbmodule.DBModule base *base.Base config *config.Config netenv *netenv.NetEnv } func (stub *testInstance) GetEventSPNConnected() *mgr.EventMgr[struct{}] { return mgr.NewEventMgr[struct{}]("spn connect", nil) } func (stub *testInstance) IntelUpdates() *updates.Updater { return nil } func (stub *testInstance) Config() *config.Config { return stub.config } func (stub *testInstance) NetEnv() *netenv.NetEnv { return stub.netenv } func (stub *testInstance) Ready() bool { return true } func (stub *testInstance) SetCmdLineOperation(f func() error) {} func (stub *testInstance) UI() *ui.UI { return nil } func (stub *testInstance) DataDir() string { return _dataDir } var _dataDir string func runTest(m *testing.M) error { var err error // Create a temporary directory for testing _dataDir, err = os.MkdirTemp("", "") if err != nil { fmt.Printf("failed to create temporary data directory: %s", err) os.Exit(0) } defer func() { _ = os.RemoveAll(_dataDir) }() // Set the default API listen address api.SetDefaultAPIListenAddress("0.0.0.0:8080") // Initialize the instance with the necessary components stub := &testInstance{} stub.db, err = dbmodule.New(stub) if err != nil { return fmt.Errorf("failed to create database: %w", err) } stub.config, err = config.New(stub) if err != nil { return fmt.Errorf("failed to create config: %w", err) } stub.base, err = base.New(stub) if err != nil { return fmt.Errorf("failed to create base: %w", err) } stub.netenv, err = netenv.New(stub) if err != nil { return fmt.Errorf("failed to create netenv: %w", err) } module, err := New(stub) if err != nil { return fmt.Errorf("failed to create module: %w", err) } err = stub.db.Start() if err != nil { return fmt.Errorf("Failed to start database: %w", err) } err = stub.config.Start() if err != nil { return fmt.Errorf("Failed to start config: %w", err) } err = stub.base.Start() if err != nil { return fmt.Errorf("Failed to start base: %w", err) } err = stub.netenv.Start() if err != nil { return fmt.Errorf("Failed to start netenv: %w", err) } err = module.Start() if err != nil { return fmt.Errorf("Failed to start module: %w", err) } m.Run() return nil } func TestMain(m *testing.M) { if err := runTest(m); err != nil { fmt.Printf("%s", err) os.Exit(1) } } func init() { go feedDomains() } func feedDomains() { for { for _, domain := range testDomains { domainFeed <- domain } } } // Data var testDomains = []string{ "facebook.com.", "google.com.", "youtube.com.", "twitter.com.", "instagram.com.", "linkedin.com.", "microsoft.com.", "apple.com.", "wikipedia.org.", "plus.google.com.", "en.wikipedia.org.", "googletagmanager.com.", "youtu.be.", "adobe.com.", "vimeo.com.", "pinterest.com.", "itunes.apple.com.", "play.google.com.", "maps.google.com.", "goo.gl.", "wordpress.com.", "blogspot.com.", "bit.ly.", "github.com.", "player.vimeo.com.", "amazon.com.", "wordpress.org.", "docs.google.com.", "yahoo.com.", "mozilla.org.", "tumblr.com.", "godaddy.com.", "flickr.com.", "parked-content.godaddy.com.", "drive.google.com.", "support.google.com.", "apache.org.", "gravatar.com.", "europa.eu.", "qq.com.", "w3.org.", "nytimes.com.", "reddit.com.", "macromedia.com.", "get.adobe.com.", "soundcloud.com.", "sourceforge.net.", "sites.google.com.", "nih.gov.", "amazonaws.com.", "t.co.", "support.microsoft.com.", "forbes.com.", "theguardian.com.", "cnn.com.", "github.io.", "bbc.co.uk.", "dropbox.com.", "whatsapp.com.", "medium.com.", "creativecommons.org.", "www.ncbi.nlm.nih.gov.", "httpd.apache.org.", "archive.org.", "ec.europa.eu.", "php.net.", "apps.apple.com.", "weebly.com.", "support.apple.com.", "weibo.com.", "wixsite.com.", "issuu.com.", "who.int.", "paypal.com.", "m.facebook.com.", "oracle.com.", "msn.com.", "gnu.org.", "tinyurl.com.", "reuters.com.", "l.facebook.com.", "cloudflare.com.", "wsj.com.", "washingtonpost.com.", "domainmarket.com.", "imdb.com.", "bbc.com.", "bing.com.", "accounts.google.com.", "vk.com.", "api.whatsapp.com.", "opera.com.", "cdc.gov.", "slideshare.net.", "wpa.qq.com.", "harvard.edu.", "mit.edu.", "code.google.com.", "wikimedia.org.", } ================================================ FILE: service/resolver/metrics.go ================================================ package resolver import ( "context" "sync" "sync/atomic" "time" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/mgr" ) var ( slowQueriesSensorCnt atomic.Int64 slowQueriesSensorSum atomic.Int64 ) // reportRequestDuration reports successful query request duration. func reportRequestDuration(started time.Time, resolver *Resolver) { // TODO: Record prometheus metrics for all resolvers separately. // Add query times from system and configured resolvers to slow queries sensor. switch resolver.Info.Source { case ServerSourceConfigured, ServerSourceOperatingSystem: slowQueriesSensorCnt.Add(1) slowQueriesSensorSum.Add(int64(time.Since(started))) default: } } // getSlowQueriesSensorValue returns the current avg query time recorded by the // slow queries sensor. func getSlowQueriesSensorValue() (avgQueryTime time.Duration) { // Get values and check them. sum := slowQueriesSensorSum.Load() cnt := slowQueriesSensorCnt.Load() if cnt < 1 { cnt = 1 } return time.Duration(sum / cnt) } // resetSlowQueriesSensorValue reset the slow queries sensor values. func resetSlowQueriesSensorValue() { slowQueriesSensorCnt.Store(0) slowQueriesSensorSum.Store(0) } var suggestUsingStaleCacheNotification *notifications.Notification var isFirstNotification = true func suggestUsingStaleCacheTask(_ *mgr.WorkerCtx) error { scheduleNextCall := true switch { case useStaleCache() || useStaleCacheConfigOption.IsSetByUser() || isNotificationSuppressed(): // If setting is already active, disable task repeating. scheduleNextCall = false // Delete local reference, if used. if suggestUsingStaleCacheNotification != nil { suggestUsingStaleCacheNotification.Delete() suggestUsingStaleCacheNotification = nil } case suggestUsingStaleCacheNotification != nil: // Check if notification is already active. suggestUsingStaleCacheNotification.Lock() defer suggestUsingStaleCacheNotification.Unlock() if suggestUsingStaleCacheNotification.Meta().IsDeleted() { // Reset local reference if notification was deleted. suggestUsingStaleCacheNotification = nil } case getSlowQueriesSensorValue() > 100*time.Millisecond: log.Warningf( "resolver: suggesting user to use stale dns cache with avg query time of %s for config and system resolvers", getSlowQueriesSensorValue().Round(time.Millisecond), ) const actionSuppressID = "suppress" // Notify user. suggestUsingStaleCacheNotification = ¬ifications.Notification{ EventID: "resolver:suggest-using-stale-cache", Type: notifications.Info, Title: "Speed Up Website Loading", Message: "Portmaster has detected that websites may load slower because DNS queries are currently slower than expected. You may want to switch your DNS provider or enable using expired DNS cache entries for better performance.", ShowOnSystem: isFirstNotification && getSlowQueriesSensorValue() > 500*time.Millisecond, Expires: time.Now().Add(10 * time.Minute).Unix(), AvailableActions: []*notifications.Action{ { Text: "Open Setting", Type: notifications.ActionTypeOpenSetting, Payload: ¬ifications.ActionTypeOpenSettingPayload{ Key: CfgOptionUseStaleCacheKey, }, Visibility: notifications.ActionVisibilityInAppOnly, }, { ID: actionSuppressID, Text: "Don't show again", Visibility: notifications.ActionVisibilityDetailed, }, { ID: "ack", Text: "Got it!", }, }, } // Only show the notification on the system for the first time, // and do not bother user with multiple system notifications isFirstNotification = false suggestUsingStaleCacheNotification.SetActionFunction(func(_ context.Context, n *notifications.Notification) error { n.Lock() actionID := n.SelectedActionID n.Unlock() if actionID == actionSuppressID { if err := suppressNotification(); err != nil { return err } } n.Delete() return nil }) notifications.Notify(suggestUsingStaleCacheNotification) } if scheduleNextCall { _ = module.suggestUsingStaleCacheTask.Delay(2 * time.Minute) } resetSlowQueriesSensorValue() return nil } // === Notification state persistence === // markerRecord is a minimal database record used as a presence-only marker. type markerRecord struct { record.Base sync.Mutex } var db = database.NewInterface(&database.Options{Local: true, Internal: true}) // Database key used to persist the user's choice to suppress the stale cache notification. const Notification_DB_ID_StaleCacheSuppressed = "core:notifications/resolver/StaleCache/suppressed" // isNotificationSuppressed returns true if the user has chosen to never see the stale cache notification. func isNotificationSuppressed() bool { _, err := db.Get(Notification_DB_ID_StaleCacheSuppressed) return err == nil } // suppressNotification persists the user's decision to never show the notification again. func suppressNotification() error { m := &markerRecord{} m.SetKey(Notification_DB_ID_StaleCacheSuppressed) return db.Put(m) } ================================================ FILE: service/resolver/namerecord.go ================================================ package resolver import ( "context" "errors" "fmt" "sync" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" ) const ( // databaseOvertime defines how much longer than the TTL name records are // cached in the database. databaseOvertime = 86400 * 14 // two weeks ) var ( recordDatabase = database.NewInterface(&database.Options{ Local: true, Internal: true, // Cache entries because application often resolve domains multiple times. CacheSize: 256, // We only use the cache database here, so we can delay and batch all our // writes. Also, no one else accesses these records, so we are fine using // this. DelayCachedWrites: "cache", }) nameRecordsKeyPrefix = "cache:intel/nameRecord/" ) // NameRecord is helper struct to RRCache to better save data to the database. type NameRecord struct { record.Base sync.Mutex Domain string Question string RCode int Answer []string Ns []string Extra []string Expires int64 Resolver *ResolverInfo } // IsValid returns whether the NameRecord is valid and may be used. Otherwise, // it should be disregarded. func (nameRecord *NameRecord) IsValid() bool { switch { case nameRecord.Resolver == nil || nameRecord.Resolver.Type == "": // Changed in v0.6.7: Introduced Resolver *ResolverInfo return false default: // Up to date! return true } } func makeNameRecordKey(domain string, question string) string { return nameRecordsKeyPrefix + domain + question } // GetNameRecord gets a NameRecord from the database. func GetNameRecord(domain, question string) (*NameRecord, error) { key := makeNameRecordKey(domain, question) r, err := recordDatabase.Get(key) if err != nil { return nil, err } // Unwrap record if it's wrapped. if r.IsWrapped() { // only allocate a new struct, if we need it newNR := &NameRecord{} err = record.Unwrap(r, newNR) if err != nil { return nil, err } // Check if the record is valid. if !newNR.IsValid() { return nil, errors.New("record is invalid (outdated format)") } return newNR, nil } // Or just adjust the type. newNR, ok := r.(*NameRecord) if !ok { return nil, fmt.Errorf("record not of type *NameRecord, but %T", r) } // Check if the record is valid. if !newNR.IsValid() { return nil, errors.New("record is invalid (outdated format)") } return newNR, nil } // ResetCachedRecord deletes a NameRecord from the cache database. func ResetCachedRecord(domain, question string) error { // In order to properly delete an entry, we must also clear the caches. recordDatabase.FlushCache() recordDatabase.ClearCache() key := makeNameRecordKey(domain, question) return recordDatabase.Delete(key) } // Save saves the NameRecord to the database. func (nameRecord *NameRecord) Save() error { if nameRecord.Domain == "" || nameRecord.Question == "" { return errors.New("could not save NameRecord, missing Domain and/or Question") } nameRecord.SetKey(makeNameRecordKey(nameRecord.Domain, nameRecord.Question)) nameRecord.UpdateMeta() nameRecord.Meta().SetAbsoluteExpiry(nameRecord.Expires + databaseOvertime) return recordDatabase.PutNew(nameRecord) } // clearNameCacheHandler is an API handler that clears all dns caches from the database. func clearNameCacheHandler(ar *api.Request) (msg string, err error) { log.Info("resolver: user requested dns cache clearing via action") return clearNameCache(ar.Context()) } // clearNameCache clears all dns caches from the database. func clearNameCache(ctx context.Context) (msg string, err error) { recordDatabase.FlushCache() recordDatabase.ClearCache() n, err := recordDatabase.Purge(ctx, query.New(nameRecordsKeyPrefix)) if err != nil { return "", err } log.Debugf("resolver: cleared %d entries from dns cache", n) return fmt.Sprintf("cleared %d dns cache entries", n), nil } ================================================ FILE: service/resolver/namerecord_test.go ================================================ package resolver import "testing" func TestNameRecordStorage(t *testing.T) { t.Parallel() testDomain := "Mk35mMqOWEHXSMk11MYcbjLOjTE8PQvDiAVUxf4BvwtgR.example.com." testQuestion := "A" testNameRecord := &NameRecord{ Domain: testDomain, Question: testQuestion, Resolver: &ResolverInfo{ Type: "dns", }, } err := testNameRecord.Save() if err != nil { t.Fatal(err) } r, err := GetNameRecord(testDomain, testQuestion) if err != nil { t.Fatal(err) } if r.Domain != testDomain || r.Question != testQuestion { t.Fatal("mismatch") } } ================================================ FILE: service/resolver/resolve.go ================================================ package resolver import ( "context" "errors" "fmt" "net" "strings" "sync" "time" "github.com/miekg/dns" "golang.org/x/net/publicsuffix" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" ) // Errors. var ( // Basic Errors. // ErrNotFound is a basic error that will match all "not found" errors. ErrNotFound = errors.New("record could not be found") // ErrBlocked is basic error that will match all "blocked" errors. ErrBlocked = errors.New("query was blocked") // ErrLocalhost is returned to *.localhost queries. ErrLocalhost = errors.New("query for localhost") // ErrTimeout is returned when a query times out. ErrTimeout = errors.New("query timed out") // ErrOffline is returned when no network connection is detected. ErrOffline = errors.New("device is offine") // ErrFailure is returned when the type of failure is unclear. ErrFailure = errors.New("query failed") // ErrContinue is returned when the resolver has no answer, and the next resolver should be asked. ErrContinue = errors.New("resolver has no answer") // ErrShuttingDown is returned when the resolver is shutting down. ErrShuttingDown = errors.New("resolver is shutting down") // Detailed Errors. // ErrTestDomainsDisabled wraps ErrBlocked. ErrTestDomainsDisabled = fmt.Errorf("%w: test domains disabled", ErrBlocked) // ErrSpecialDomainsDisabled wraps ErrBlocked. ErrSpecialDomainsDisabled = fmt.Errorf("%w: special domains disabled", ErrBlocked) // ErrInvalid wraps ErrNotFound. ErrInvalid = fmt.Errorf("%w: invalid request", ErrNotFound) // ErrNoCompliance wraps ErrBlocked and is returned when no resolvers were able to comply with the current settings. ErrNoCompliance = fmt.Errorf("%w: no compliant resolvers for this query", ErrBlocked) ) const ( minTTL = 60 // 1 Minute refreshTTL = minTTL / 2 minMDnsTTL = 60 // 1 Minute maxTTL = 24 * 60 * 60 // 24 hours ) var ( dupReqMap = make(map[string]*dedupeStatus) dupReqLock sync.Mutex ) type dedupeStatus struct { completed chan struct{} waitUntil time.Time superseded bool } // BlockedUpstreamError is returned when a DNS request // has been blocked by the upstream server. type BlockedUpstreamError struct { ResolverName string } func (blocked *BlockedUpstreamError) Error() string { return fmt.Sprintf("%s by upstream DNS resolver %s", ErrBlocked, blocked.ResolverName) } // Unwrap implements errors.Unwrapper. func (blocked *BlockedUpstreamError) Unwrap() error { return ErrBlocked } // Query describes a dns query. type Query struct { FQDN string QType dns.Type NoCaching bool IgnoreFailing bool LocalResolversOnly bool // ICANNSpace signifies if the domain is within ICANN managed domain space. ICANNSpace bool // Domain root is the effective TLD +1. DomainRoot string // internal dotPrefixedFQDN string } // ID returns the ID of the query consisting of the domain and question type. func (q *Query) ID() string { return q.FQDN + q.QType.String() } // InitPublicSuffixData initializes the public suffix data. func (q *Query) InitPublicSuffixData() { // Get public suffix and derive if domain is in ICANN space. suffix, icann := publicsuffix.PublicSuffix(strings.TrimSuffix(q.FQDN, ".")) if icann || strings.Contains(suffix, ".") { q.ICANNSpace = true } // Override some cases. switch suffix { case "example": q.ICANNSpace = true // Defined by ICANN. case "invalid": q.ICANNSpace = true // Defined by ICANN. case "local": q.ICANNSpace = true // Defined by ICANN. case "localhost": q.ICANNSpace = true // Defined by ICANN. case "onion": q.ICANNSpace = false // Defined by ICANN, but special. case "test": q.ICANNSpace = true // Defined by ICANN. } // Add suffix to adhere to FQDN format. suffix += "." switch { case len(q.FQDN) == len(suffix): // We are at or below the domain root, reset. q.DomainRoot = "" case len(q.FQDN) > len(suffix): domainRootStart := strings.LastIndex(q.FQDN[:len(q.FQDN)-len(suffix)-1], ".") + 1 q.DomainRoot = q.FQDN[domainRootStart:] } } // check runs sanity checks and does some initialization. Returns whether the query passed the basic checks. func (q *Query) check() (ok bool) { if q.FQDN == "" { return false } // init q.FQDN = dns.Fqdn(q.FQDN) if q.FQDN == "." { q.dotPrefixedFQDN = q.FQDN } else { q.dotPrefixedFQDN = "." + q.FQDN } return true } // Resolve resolves the given query for a domain and type and returns a RRCache object or nil, if the query failed. func Resolve(ctx context.Context, q *Query) (rrCache *RRCache, err error) { // sanity check if q == nil || !q.check() { return nil, ErrInvalid } // log // try adding a context tracer ctx, tracer := log.AddTracer(ctx) defer tracer.Submit() log.Tracer(ctx).Tracef("resolver: resolving %s%s", q.FQDN, q.QType) // check query compliance if err = q.checkCompliance(); err != nil { return nil, err } // check the cache if !q.NoCaching { rrCache = checkCache(ctx, q) if rrCache != nil { switch { case !rrCache.Expired(): // Return non-expired cached entry immediately. return rrCache, nil case rrCache.RCode == dns.RcodeSuccess && useStaleCache(): // Return expired cache if we should use stale cache entries, // but start an async query instead. log.Tracer(ctx).Tracef( "resolver: using stale cache entry that expired %s ago", time.Since(time.Unix(rrCache.Expires, 0)).Round(time.Second), ) startAsyncQuery(ctx, q, rrCache) return rrCache, nil } } // dedupe! markRequestFinished := deduplicateRequest(ctx, q) if markRequestFinished == nil { // we waited for another request, recheck the cache! rrCache = checkCache(ctx, q) if rrCache != nil && (!rrCache.Expired() || useStaleCache()) { // Return non-expired or expired entry if we should use stale cache entries. // There just was a request, so do not trigger an async query. return rrCache, nil } log.Tracer(ctx).Debugf("resolver: waited for another %s%s query, but cache missed!", q.FQDN, q.QType) // if cache is still empty or non-compliant, go ahead and just query } else { // we are the first! defer markRequestFinished() } } return resolveAndCache(ctx, q, rrCache) } func checkCache(ctx context.Context, q *Query) *RRCache { // Never ask cache for connectivity domains. if netenv.IsConnectivityDomain(q.FQDN) { return nil } // Get data from cache. rrCache, err := GetRRCache(q.FQDN, q.QType) // Return if entry is not in cache. if err != nil { if !errors.Is(err, database.ErrNotFound) { log.Tracer(ctx).Warningf("resolver: getting RRCache %s%s from database failed: %s", q.FQDN, q.QType.String(), err) } return nil } // Get the resolver that the rrCache was resolved with. resolver := getActiveResolverByIDWithLocking(rrCache.Resolver.ID()) if resolver == nil { log.Tracer(ctx).Debugf("resolver: ignoring RRCache %s%s because source server %q has been removed", q.FQDN, q.QType.String(), rrCache.Resolver.ID()) return nil } // Check compliance of the resolver, return if non-compliant. err = resolver.checkCompliance(ctx, q) if err != nil { log.Tracer(ctx).Debugf("resolver: cached entry for %s%s does not comply to query parameters: %s", q.FQDN, q.QType.String(), err) return nil } switch { case shouldResetCache(q): // Check if we want to reset the cache for this entry. err := ResetCachedRecord(q.FQDN, q.QType.String()) switch { case err == nil: log.Tracer(ctx).Infof("resolver: cache for %s%s was reset", q.FQDN, q.QType) case errors.Is(err, database.ErrNotFound): log.Tracer(ctx).Tracef("resolver: cache for %s%s was already reset (is empty)", q.FQDN, q.QType) default: log.Tracer(ctx).Warningf("resolver: failed to reset cache for %s%s: %s", q.FQDN, q.QType, err) } return nil case rrCache.Expired(): // Check if the cache has already expired. // We still return the cache, if it isn't NXDomain, as it will be used if the // new query fails. if rrCache.RCode == dns.RcodeSuccess { return rrCache } return nil case rrCache.ExpiresSoon(): // Check if the cache will expire soon and start an async request. startAsyncQuery(ctx, q, rrCache) return rrCache default: // Return still valid cache entry. log.Tracer(ctx).Tracef( "resolver: using cached RR (expires in %s)", time.Until(time.Unix(rrCache.Expires, 0)).Round(time.Second), ) return rrCache } } func startAsyncQuery(ctx context.Context, q *Query, currentRRCache *RRCache) { // Check if an async query was already started. if currentRRCache.RequestingNew { return } // Set flag and log that we are refreshing this entry. currentRRCache.RequestingNew = true if currentRRCache.Expired() { log.Tracer(ctx).Tracef( "resolver: cache for %s has expired %s ago, refreshing async now", q.ID(), time.Since(time.Unix(currentRRCache.Expires, 0)).Round(time.Second), ) } else { log.Tracer(ctx).Tracef( "resolver: cache for %s will expire in %s, refreshing async now", q.ID(), time.Until(time.Unix(currentRRCache.Expires, 0)).Round(time.Second), ) } // resolve async module.mgr.Go("resolve async", func(wc *mgr.WorkerCtx) error { tracingCtx, tracer := log.AddTracer(wc.Ctx()) defer tracer.Submit() tracer.Tracef("resolver: resolving %s async", q.ID()) _, err := resolveAndCache(tracingCtx, q, nil) if err != nil { tracer.Warningf("resolver: async query for %s failed: %s", q.ID(), err) } else { tracer.Infof("resolver: async query for %s succeeded", q.ID()) } return nil }) } func deduplicateRequest(ctx context.Context, q *Query) (finishRequest func()) { // create identifier key dupKey := q.ID() // restart here if waiting timed out retry: dupReqLock.Lock() // get duplicate request waitgroup status, requestActive := dupReqMap[dupKey] // check if the request ist active if requestActive { // someone else is already on it! if time.Now().Before(status.waitUntil) { dupReqLock.Unlock() // log that we are waiting log.Tracer(ctx).Tracef("resolver: waiting for duplicate query for %s to complete", dupKey) // wait select { case <-status.completed: // done! return nil case <-time.After(maxRequestTimeout): // something went wrong with the query, retry goto retry case <-ctx.Done(): return nil } } else { // but that someone is taking too long status.superseded = true } } // we are currently the only one doing a request for this // create new status status = &dedupeStatus{ completed: make(chan struct{}), waitUntil: time.Now().Add(maxRequestTimeout), } // add to registry dupReqMap[dupKey] = status dupReqLock.Unlock() // return function to mark request as finished return func() { dupReqLock.Lock() defer dupReqLock.Unlock() // mark request as done close(status.completed) // delete from registry if !status.superseded { delete(dupReqMap, dupKey) } } } func resolveAndCache(ctx context.Context, q *Query, oldCache *RRCache) (rrCache *RRCache, err error) { //nolint:gocognit,gocyclo // get resolvers resolvers, primarySource, tryAll := GetResolversInScope(ctx, q) if len(resolvers) == 0 { return nil, ErrNoCompliance } // check if we are online if netenv.GetOnlineStatus() == netenv.StatusOffline && primarySource != ServerSourceEnv { if q.FQDN != netenv.DNSTestDomain && !netenv.IsConnectivityDomain(q.FQDN) { // we are offline and this is not an online check query return oldCache, ErrOffline } log.Tracer(ctx).Debugf("resolver: allowing online status test domain %s to resolve even though offline", q.FQDN) } // Report when all configured resolvers are failing. var failureReported bool defer func() { if failureReported && netenv.Online() && primarySource == ServerSourceConfigured && allConfiguredResolversAreFailing() { notifyAboutFailingResolvers() } }() // start resolving for _, resolver := range resolvers { if module.mgr.IsDone() { return nil, ErrShuttingDown } // Skip failing resolvers. if resolver.Conn.IsFailing() { log.Tracer(ctx).Tracef("resolver: skipping resolver %s, because it is failing", resolver) continue } // Skip unreachable link-local resolvers. if resolver.LinkLocalUnavailable { log.Tracer(ctx).Tracef("resolver: skipping resolver %s, because it is link-local and not available", resolver) continue } // resolve log.Tracer(ctx).Tracef("resolver: sending query for %s to %s", q.ID(), resolver.Info.ID()) rrCache, err = resolver.Conn.Query(ctx, q) if err != nil { switch { case errors.Is(err, ErrNotFound): // NXDomain, or similar if tryAll { continue } return nil, err case errors.Is(err, ErrBlocked): // some resolvers might also block return nil, err case netenv.GetOnlineStatus() == netenv.StatusOffline && q.FQDN != netenv.DNSTestDomain && !netenv.IsConnectivityDomain(q.FQDN): // we are offline and this is not an online check query return oldCache, ErrOffline case errors.Is(err, ErrContinue): continue case errors.Is(err, ErrTimeout): resolver.Conn.ReportFailure() failureReported = true log.Tracer(ctx).Debugf("resolver: query to %s timed out", resolver.Info.ID()) continue case errors.Is(err, context.Canceled): return nil, err case errors.Is(err, context.DeadlineExceeded): return nil, err case errors.Is(err, ErrShuttingDown): return nil, err default: resolver.Conn.ReportFailure() failureReported = true log.Tracer(ctx).Warningf("resolver: query to %s failed: %s", resolver.Info.ID(), err) continue } } if rrCache == nil { // Defensive: This should normally not happen. continue } // Check if request succeeded and whether we should try another resolver. if rrCache.RCode != dns.RcodeSuccess && tryAll { continue } // Report a successful connection. resolver.Conn.ResetFailure() // Reset failing resolvers notification, if querying in global scope. if primarySource == ServerSourceConfigured && !allConfiguredResolversAreFailing() { resetFailingResolversNotification() } break } // Validate return values. if err == nil && rrCache == nil { err = ErrNotFound } // Handle error. if err != nil { // Check if we can return an older cache instead of the error. if oldCache != nil { oldCache.IsBackup = true log.Tracer(ctx).Debugf("resolver: serving backup cache of %s because query failed: %s", q.ID(), err) return oldCache, nil } return nil, fmt.Errorf("all %d query-compliant resolvers failed, last error: %w", len(resolvers), err) } // Adjust TTLs. rrCache.Clean(minTTL) // Save the new entry if cache is enabled and the record may be cached. if !q.NoCaching && rrCache.Cacheable() { err = rrCache.Save() if err != nil { log.Tracer(ctx).Warningf("resolver: failed to cache RR for %s: %s", q.ID(), err) } } return rrCache, nil } var ( cacheResetLock sync.Mutex cacheResetID string cacheResetSeenCnt int ) func shouldResetCache(q *Query) (reset bool) { cacheResetLock.Lock() defer cacheResetLock.Unlock() // reset to new domain qID := q.ID() if qID != cacheResetID { cacheResetID = qID cacheResetSeenCnt = 1 return false } // increase and check if threshold is reached cacheResetSeenCnt++ if cacheResetSeenCnt >= 3 { // 3 to trigger reset cacheResetSeenCnt = -7 // 10 for follow-up resets return true } return false } // testConnectivity test if resolving a query succeeds and returns whether the // query itself succeeded, separate from interpreting the result. // Provide a resolver to use or automatically select one if nil. func testConnectivity(ctx context.Context, fdqn string, resolver *Resolver) (ips []net.IP, ok bool, err error) { q := &Query{ FQDN: fdqn, QType: dns.Type(dns.TypeA), NoCaching: true, } if !q.check() { return nil, false, ErrInvalid } // Resolve with given resolver or auto-select. var rrCache *RRCache if resolver != nil { rrCache, err = resolver.Conn.Query(ctx, q) } else { rrCache, err = resolveAndCache(ctx, q, nil) } // Enhance results. switch { case err == nil: switch rrCache.RCode { case dns.RcodeNameError: return nil, true, ErrNotFound case dns.RcodeRefused: return nil, true, errors.New("refused") default: ips := rrCache.ExportAllARecords() if len(ips) > 0 { return ips, true, nil } return nil, true, ErrNotFound } case errors.Is(err, ErrNotFound): return nil, true, err case errors.Is(err, ErrNoCompliance): return nil, true, err case errors.Is(err, ErrBlocked): return nil, true, err default: return nil, false, err } } ================================================ FILE: service/resolver/resolver-env.go ================================================ package resolver import ( "context" "fmt" "net" "strings" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/netutils" ) const ( // InternalSpecialUseDomain is the domain scope used for internal services. InternalSpecialUseDomain = "portmaster.home.arpa." routerDomain = "router.local." + InternalSpecialUseDomain captivePortalDomain = "captiveportal.local." + InternalSpecialUseDomain ) var ( envResolver = &Resolver{ ConfigURL: ServerSourceEnv, Info: &ResolverInfo{ Type: ServerTypeEnv, Source: ServerSourceEnv, IPScope: netutils.SiteLocal, }, Conn: &envResolverConn{}, } envResolvers = []*Resolver{envResolver} internalSpecialUseSOA dns.RR internalSpecialUseComment dns.RR ) func prepEnvResolver() (err error) { netenv.SpecialCaptivePortalDomain = captivePortalDomain internalSpecialUseSOA, err = dns.NewRR(fmt.Sprintf( "%s 17 IN SOA localhost. none.localhost. 0 0 0 0 0", InternalSpecialUseDomain, )) if err != nil { return err } internalSpecialUseComment, err = dns.NewRR(fmt.Sprintf( `%s 17 IN TXT "This is a special use TLD of the Portmaster."`, InternalSpecialUseDomain, )) return err } type envResolverConn struct{} func (er *envResolverConn) Query(ctx context.Context, q *Query) (*RRCache, error) { switch uint16(q.QType) { case dns.TypeA, dns.TypeAAAA: // We respond with all IPv4/6 addresses we can find. // Check for exact matches. switch q.FQDN { case captivePortalDomain: // Get IP address of the captive portal. portal := netenv.GetCaptivePortal() portalIP := portal.IP if portalIP == nil { portalIP = netenv.PortalTestIP } // Convert IP to record and respond. records, err := netutils.IPsToRRs(q.FQDN, []net.IP{portalIP}) if err != nil { log.Warningf("nameserver: failed to create captive portal response to %s: %s", q.FQDN, err) return er.nxDomain(q), nil } return er.makeRRCache(q, records), nil case routerDomain: // Get gateways from netenv system. routers := netenv.Gateways() if len(routers) == 0 { return er.nxDomain(q), nil } // Convert IP to record and respond. records, err := netutils.IPsToRRs(q.FQDN, routers) if err != nil { log.Warningf("nameserver: failed to create gateway response to %s: %s", q.FQDN, err) return er.nxDomain(q), nil } return er.makeRRCache(q, records), nil } // Check for suffix matches. if strings.HasSuffix(q.FQDN, CompatDNSCheckInternalDomainScope) { subdomain := strings.TrimSuffix(q.FQDN, CompatDNSCheckInternalDomainScope) respondWith := CompatSubmitDNSCheckDomain(subdomain) // We'll get an A record. Only respond if it's an A question. if respondWith != nil && uint16(q.QType) == dns.TypeA { records, err := netutils.IPsToRRs(q.FQDN, []net.IP{respondWith}) if err != nil { log.Warningf("nameserver: failed to create dns check response to %s: %s", q.FQDN, err) return er.nxDomain(q), nil } return er.makeRRCache(q, records), nil } } case dns.TypeSOA: // Direct query for the SOA record. if q.FQDN == InternalSpecialUseDomain { return er.makeRRCache(q, []dns.RR{internalSpecialUseSOA}), nil } } // No match, reply with NXDOMAIN and SOA record reply := er.nxDomain(q) reply.Ns = []dns.RR{internalSpecialUseSOA} return reply, nil } func (er *envResolverConn) nxDomain(q *Query) *RRCache { return er.makeRRCache(q, nil) } func (er *envResolverConn) makeRRCache(q *Query, answers []dns.RR) *RRCache { // Disable caching, as the env always has the raw data available. q.NoCaching = true rrCache := &RRCache{ Domain: q.FQDN, Question: q.QType, RCode: dns.RcodeSuccess, Answer: answers, Extra: []dns.RR{internalSpecialUseComment}, // Always add comment about this TLD. Resolver: envResolver.Info.Copy(), } if len(rrCache.Answer) == 0 { rrCache.RCode = dns.RcodeNameError } return rrCache } func (er *envResolverConn) ReportFailure() {} func (er *envResolverConn) IsFailing() bool { return false } func (er *envResolverConn) ResetFailure() {} func (er *envResolverConn) ForceReconnect(_ context.Context) {} // QueryPortmasterEnv queries the environment resolver directly. func QueryPortmasterEnv(ctx context.Context, q *Query) (*RRCache, error) { return envResolver.Conn.Query(ctx, q) } ================================================ FILE: service/resolver/resolver-https.go ================================================ package resolver import ( "context" "crypto/tls" "encoding/base64" "fmt" "io" "net/http" "net/url" "sync" "time" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/netenv" ) // HTTPSResolver is a resolver using just a single tcp connection with pipelining. type HTTPSResolver struct { BasicResolverConn client *http.Client clientLock sync.RWMutex } // HTTPSQuery holds the query information for a hTTPSResolverConn. type HTTPSQuery struct { Query *Query Response chan *dns.Msg } // MakeCacheRecord creates an RRCache record from a reply. func (tq *HTTPSQuery) MakeCacheRecord(reply *dns.Msg, resolverInfo *ResolverInfo) *RRCache { return &RRCache{ Domain: tq.Query.FQDN, Question: tq.Query.QType, RCode: reply.Rcode, Answer: reply.Answer, Ns: reply.Ns, Extra: reply.Extra, Resolver: resolverInfo.Copy(), } } // NewHTTPSResolver returns a new HTTPSResolver. func NewHTTPSResolver(resolver *Resolver) *HTTPSResolver { newResolver := &HTTPSResolver{ BasicResolverConn: BasicResolverConn{ resolver: resolver, }, } newResolver.BasicResolverConn.init() newResolver.refreshClient() return newResolver } // Query executes the given query against the resolver. func (hr *HTTPSResolver) Query(ctx context.Context, q *Query) (*RRCache, error) { queryStarted := time.Now() dnsQuery := new(dns.Msg) dnsQuery.SetQuestion(q.FQDN, uint16(q.QType)) // Pack query and convert to base64 string buf, err := dnsQuery.Pack() if err != nil { return nil, err } b64dns := base64.RawURLEncoding.EncodeToString(buf) // Build and execute http request url := &url.URL{ Scheme: "https", Host: hr.resolver.ServerAddress, Path: hr.resolver.Path, ForceQuery: true, RawQuery: fmt.Sprintf("dns=%s", b64dns), } request, err := http.NewRequestWithContext(ctx, http.MethodGet, url.String(), nil) if err != nil { return nil, err } // Lock client for usage. hr.clientLock.RLock() defer hr.clientLock.RUnlock() // TODO: Check age of client and force a refresh similar to the TCP resolver. resp, err := hr.client.Do(request) if err != nil { // Hint network environment at failed connection. netenv.ReportFailedConnection() return nil, err } defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("http request failed with %s", resp.Status) } // Try to read the result body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } reply := new(dns.Msg) err = reply.Unpack(body) if err != nil { return nil, err } // Hint network environment at successful connection. netenv.ReportSuccessfulConnection() // Report request duration for metrics. reportRequestDuration(queryStarted, hr.resolver) newRecord := &RRCache{ Domain: q.FQDN, Question: q.QType, RCode: reply.Rcode, Answer: reply.Answer, Ns: reply.Ns, Extra: reply.Extra, Resolver: hr.resolver.Info.Copy(), } // TODO: check if reply.Answer is valid return newRecord, nil } // ForceReconnect forces the resolver to re-establish the connection to the server. func (hr *HTTPSResolver) ForceReconnect(ctx context.Context) { hr.refreshClient() log.Tracer(ctx).Tracef("resolver: created new HTTP client for %s", hr.resolver) } func (hr *HTTPSResolver) refreshClient() { // Lock client for changing. hr.clientLock.Lock() defer hr.clientLock.Unlock() // Attempt to close connection of previous client. if hr.client != nil { hr.client.CloseIdleConnections() } // Create new client. tr := &http.Transport{ TLSClientConfig: &tls.Config{ MinVersion: tls.VersionTLS12, ServerName: hr.resolver.Info.Domain, // TODO: use portbase rng }, IdleConnTimeout: 1 * time.Minute, TLSHandshakeTimeout: defaultConnectTimeout, } hr.client = &http.Client{ Transport: tr, Timeout: maxRequestTimeout, } } ================================================ FILE: service/resolver/resolver-mdns.go ================================================ package resolver import ( "context" "errors" "fmt" "net" "strings" "sync" "time" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/netutils" ) // DNS Classes. const ( DNSClassMulticast = dns.ClassINET | 1<<15 ) var ( multicast4Conn *net.UDPConn multicast6Conn *net.UDPConn unicast4Conn *net.UDPConn unicast6Conn *net.UDPConn questions = make(map[uint16]*savedQuestion) questionsLock sync.Mutex mDNSResolver = &Resolver{ ConfigURL: ServerSourceMDNS, Info: &ResolverInfo{ Type: ServerTypeMDNS, Source: ServerSourceMDNS, IPScope: netutils.SiteLocal, }, Conn: &mDNSResolverConn{}, } mDNSResolvers = []*Resolver{mDNSResolver} ) type mDNSResolverConn struct{} func (mrc *mDNSResolverConn) Query(ctx context.Context, q *Query) (*RRCache, error) { return queryMulticastDNS(ctx, q) } func (mrc *mDNSResolverConn) ReportFailure() {} func (mrc *mDNSResolverConn) IsFailing() bool { return false } func (mrc *mDNSResolverConn) ResetFailure() {} func (mrc *mDNSResolverConn) ForceReconnect(_ context.Context) {} type savedQuestion struct { question dns.Question expires time.Time response chan *RRCache } func indexOfRR(entry *dns.RR_Header, list *[]dns.RR) int { for k, v := range *list { if entry.Name == v.Header().Name && entry.Rrtype == v.Header().Rrtype { return k } } return -1 } //nolint:gocyclo,gocognit // TODO: make simpler func listenToMDNS(wc *mgr.WorkerCtx) error { var err error messages := make(chan *dns.Msg, 32) // TODO: init and start every listener in its own service worker // this will make the more resilient and actually able to restart multicast4Conn, err = net.ListenMulticastUDP("udp4", nil, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 251), Port: 5353}) if err != nil { // TODO: retry after some time log.Warningf("intel(mdns): failed to create udp4 listen multicast socket: %s", err) } else { module.mgr.Go("mdns udp4 multicast listener", func(wc *mgr.WorkerCtx) error { return listenForDNSPackets(wc.Ctx(), multicast4Conn, messages) }) defer func() { _ = multicast4Conn.Close() }() } unicast4Conn, err = net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) if err != nil { // TODO: retry after some time log.Warningf("intel(mdns): failed to create udp4 listen socket: %s", err) } else { module.mgr.Go("mdns udp4 unicast listener", func(wc *mgr.WorkerCtx) error { return listenForDNSPackets(wc.Ctx(), unicast4Conn, messages) }) defer func() { _ = unicast4Conn.Close() }() } if netenv.IPv6Enabled() { multicast6Conn, err = net.ListenMulticastUDP("udp6", nil, &net.UDPAddr{IP: net.IP([]byte{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb}), Port: 5353}) if err != nil { // TODO: retry after some time log.Warningf("intel(mdns): failed to create udp6 listen multicast socket: %s", err) } else { module.mgr.Go("mdns udp6 multicast listener", func(wc *mgr.WorkerCtx) error { return listenForDNSPackets(wc.Ctx(), multicast6Conn, messages) }) defer func() { _ = multicast6Conn.Close() }() } unicast6Conn, err = net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero, Port: 0}) if err != nil { // TODO: retry after some time log.Warningf("intel(mdns): failed to create udp6 listen socket: %s", err) } else { module.mgr.Go("mdns udp6 unicast listener", func(wc *mgr.WorkerCtx) error { return listenForDNSPackets(wc.Ctx(), unicast6Conn, messages) }) defer func() { _ = unicast6Conn.Close() }() } } else { log.Warningf("resolver: no IPv6 stack detected, disabling IPv6 mDNS resolver") } // start message handler module.mgr.Go("mdns message handler", func(wc *mgr.WorkerCtx) error { return handleMDNSMessages(wc.Ctx(), messages) }) // wait for shutdown <-wc.Done() return nil } func handleMDNSMessages(ctx context.Context, messages chan *dns.Msg) error { //nolint:maintidx // TODO: Improve. for { select { case <-ctx.Done(): return nil case message := <-messages: // log.Tracef("resolver: got net mdns message: %s", message) var err error var question *dns.Question var saveFullRequest bool scavengedRecords := make(map[string]dns.RR) var rrCache *RRCache // save every received response // if previous save was less than 2 seconds ago, add to response, else replace // pick out A and AAAA records and save separately // continue if not response if !message.Response { // log.Tracef("resolver: mdns message has no response, ignoring") continue } // continue if rcode is not success if message.Rcode != dns.RcodeSuccess { // log.Tracef("resolver: mdns message has error, ignoring") continue } // continue if answer section is empty if len(message.Answer) == 0 { // log.Tracef("resolver: mdns message has no answers, ignoring") continue } // return saved question questionsLock.Lock() savedQ := questions[message.MsgHdr.Id] questionsLock.Unlock() // get question, some servers do not reply with question if len(message.Question) > 0 { question = &message.Question[0] // if questions do not match, disregard saved question if savedQ != nil && message.Question[0].String() != savedQ.question.String() { savedQ = nil } } else if savedQ != nil { question = &savedQ.question } if question != nil { // continue if class is not INTERNET if question.Qclass != dns.ClassINET && question.Qclass != DNSClassMulticast { continue } // mark request to be saved saveFullRequest = true } // get entry from database if saveFullRequest { // get from database rrCache, err = GetRRCache(question.Name, dns.Type(question.Qtype)) // if we have no cached entry, or it has been updated more than two seconds ago, or if it expired: // create new and do not append if err != nil || rrCache.Modified < time.Now().Add(-2*time.Second).Unix() || rrCache.Expired() { rrCache = &RRCache{ Domain: question.Name, Question: dns.Type(question.Qtype), RCode: dns.RcodeSuccess, Resolver: mDNSResolver.Info.Copy(), } } } // add all entries to RRCache for _, entry := range message.Answer { if domainInScope(entry.Header().Name, multicastDomains) { if saveFullRequest { k := indexOfRR(entry.Header(), &rrCache.Answer) if k == -1 { rrCache.Answer = append(rrCache.Answer, entry) } else { rrCache.Answer[k] = entry } } switch entry.(type) { case *dns.A: scavengedRecords[fmt.Sprintf("%sA", entry.Header().Name)] = entry case *dns.AAAA: scavengedRecords[fmt.Sprintf("%sAAAA", entry.Header().Name)] = entry case *dns.PTR: if !strings.HasPrefix(entry.Header().Name, "_") { scavengedRecords[fmt.Sprintf("%sPTR", entry.Header().Name)] = entry } } } } for _, entry := range message.Ns { if domainInScope(entry.Header().Name, multicastDomains) { if saveFullRequest { k := indexOfRR(entry.Header(), &rrCache.Ns) if k == -1 { rrCache.Ns = append(rrCache.Ns, entry) } else { rrCache.Ns[k] = entry } } switch entry.(type) { case *dns.A: scavengedRecords[fmt.Sprintf("%sA", entry.Header().Name)] = entry case *dns.AAAA: scavengedRecords[fmt.Sprintf("%sAAAA", entry.Header().Name)] = entry case *dns.PTR: if !strings.HasPrefix(entry.Header().Name, "_") { scavengedRecords[fmt.Sprintf("%sPTR", entry.Header().Name)] = entry } } } } for _, entry := range message.Extra { if domainInScope(entry.Header().Name, multicastDomains) { if saveFullRequest { k := indexOfRR(entry.Header(), &rrCache.Extra) if k == -1 { rrCache.Extra = append(rrCache.Extra, entry) } else { rrCache.Extra[k] = entry } } switch entry.(type) { case *dns.A: scavengedRecords[fmt.Sprintf("%sA", entry.Header().Name)] = entry case *dns.AAAA: scavengedRecords[fmt.Sprintf("%sAAAA", entry.Header().Name)] = entry case *dns.PTR: if !strings.HasPrefix(entry.Header().Name, "_") { scavengedRecords[fmt.Sprintf("%sPTR", entry.Header().Name)] = entry } } } } var questionID string if saveFullRequest { rrCache.Clean(minMDnsTTL) err := rrCache.Save() if err != nil { log.Warningf("resolver: failed to cache RR %s: %s", rrCache.Domain, err) } // return finished response if savedQ != nil { select { case savedQ.response <- rrCache: default: } } questionID = fmt.Sprintf("%s%s", question.Name, dns.Type(question.Qtype).String()) } for k, v := range scavengedRecords { if saveFullRequest && k == questionID { continue } rrCache = &RRCache{ Domain: v.Header().Name, Question: dns.Type(v.Header().Class), RCode: dns.RcodeSuccess, Answer: []dns.RR{v}, Resolver: mDNSResolver.Info.Copy(), } rrCache.Clean(minMDnsTTL) err := rrCache.Save() if err != nil { log.Warningf("resolver: failed to cache RR %s: %s", rrCache.Domain, err) } // log.Tracef("resolver: mdns scavenged %s", k) } } cleanSavedQuestions() } } func listenForDNSPackets(ctx context.Context, conn *net.UDPConn, messages chan *dns.Msg) error { buf := make([]byte, 65536) for { n, err := conn.Read(buf) if err != nil { if module.mgr.IsDone() { return nil } log.Debugf("resolver: failed to read packet: %s", err) return err } message := new(dns.Msg) if err = message.Unpack(buf[:n]); err != nil { log.Debugf("resolver: failed to unpack message: %s", err) continue } select { case messages <- message: case <-ctx.Done(): return nil } } } func queryMulticastDNS(ctx context.Context, q *Query) (*RRCache, error) { // check for active connections if unicast4Conn == nil && unicast6Conn == nil { return nil, errors.New("unicast mdns connections not initialized") } // trace log log.Tracer(ctx).Trace("resolver: resolving with mDNS") // create query dnsQuery := new(dns.Msg) dnsQuery.SetQuestion(q.FQDN, uint16(q.QType)) // request unicast response // q.Question[0].Qclass |= 1 << 15 dnsQuery.RecursionDesired = false // create response channel response := make(chan *RRCache) // save question questionsLock.Lock() defer questionsLock.Unlock() questions[dnsQuery.MsgHdr.Id] = &savedQuestion{ question: dnsQuery.Question[0], expires: time.Now().Add(10 * time.Second), response: response, } // pack qeury buf, err := dnsQuery.Pack() if err != nil { return nil, fmt.Errorf("failed to pack query: %w", err) } // send queries if unicast4Conn != nil && uint16(q.QType) != dns.TypeAAAA { err = unicast4Conn.SetWriteDeadline(time.Now().Add(1 * time.Second)) if err != nil { return nil, fmt.Errorf("failed to configure query (set timout): %w", err) } _, err = unicast4Conn.WriteToUDP(buf, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 251), Port: 5353}) if err != nil { return nil, fmt.Errorf("failed to send query: %w", err) } } if unicast6Conn != nil && uint16(q.QType) != dns.TypeA { err = unicast6Conn.SetWriteDeadline(time.Now().Add(1 * time.Second)) if err != nil { return nil, fmt.Errorf("failed to configure query (set timout): %w", err) } _, err = unicast6Conn.WriteToUDP(buf, &net.UDPAddr{IP: net.IP([]byte{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb}), Port: 5353}) if err != nil { return nil, fmt.Errorf("failed to send query: %w", err) } } // wait for response or timeout select { case rrCache := <-response: if rrCache != nil { return rrCache, nil } case <-time.After(1 * time.Second): // check cache again rrCache, err := GetRRCache(q.FQDN, q.QType) if err == nil { return rrCache, nil } case <-ctx.Done(): return nil, ctx.Err() } // Respond with NXDomain. return &RRCache{ Domain: q.FQDN, Question: q.QType, RCode: dns.RcodeNameError, Resolver: mDNSResolver.Info.Copy(), }, nil } func cleanSavedQuestions() { questionsLock.Lock() defer questionsLock.Unlock() now := time.Now() for msgID, savedQuestion := range questions { if now.After(savedQuestion.expires) { delete(questions, msgID) } } } ================================================ FILE: service/resolver/resolver-plain.go ================================================ package resolver import ( "context" "errors" "net" "time" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/netenv" ) var ( defaultClientTTL = 5 * time.Minute defaultRequestTimeout = 3 * time.Second // dns query defaultConnectTimeout = 5 * time.Second // tcp/tls maxRequestTimeout = 5 * time.Second ) // PlainResolver is a resolver using plain DNS. type PlainResolver struct { BasicResolverConn } // NewPlainResolver returns a new TPCResolver. func NewPlainResolver(resolver *Resolver) *PlainResolver { newResolver := &PlainResolver{ BasicResolverConn: BasicResolverConn{ resolver: resolver, }, } newResolver.BasicResolverConn.init() return newResolver } // Query executes the given query against the resolver. func (pr *PlainResolver) Query(ctx context.Context, q *Query) (*RRCache, error) { queryStarted := time.Now() // create query dnsQuery := new(dns.Msg) dnsQuery.SetQuestion(q.FQDN, uint16(q.QType)) // get timeout from context and config var timeout time.Duration if deadline, ok := ctx.Deadline(); !ok { timeout = 0 } else { timeout = time.Until(deadline) } if timeout > defaultRequestTimeout { timeout = defaultRequestTimeout } // create client dnsClient := &dns.Client{ UDPSize: 1024, Timeout: timeout, Dialer: &net.Dialer{ Timeout: timeout, LocalAddr: getLocalAddr("udp"), }, } // query server reply, ttl, err := dnsClient.Exchange(dnsQuery, pr.resolver.ServerAddress) log.Tracer(ctx).Tracef("resolver: query took %s", ttl) // error handling if err != nil { // Hint network environment at failed connection if err is not a timeout. var nErr net.Error if errors.As(err, &nErr) && !nErr.Timeout() { netenv.ReportFailedConnection() } return nil, err } // check if blocked if pr.resolver.IsBlockedUpstream(reply) { return nil, &BlockedUpstreamError{pr.resolver.Info.DescriptiveName()} } // Hint network environment at successful connection. netenv.ReportSuccessfulConnection() // Report request duration for metrics. reportRequestDuration(queryStarted, pr.resolver) newRecord := &RRCache{ Domain: q.FQDN, Question: q.QType, RCode: reply.Rcode, Answer: reply.Answer, Ns: reply.Ns, Extra: reply.Extra, Resolver: pr.resolver.Info.Copy(), } // TODO: check if reply.Answer is valid return newRecord, nil } // ForceReconnect forces the resolver to re-establish the connection to the server. // Does nothing for PlainResolver, as every request uses its own connection. func (pr *PlainResolver) ForceReconnect(_ context.Context) {} ================================================ FILE: service/resolver/resolver-tcp.go ================================================ package resolver import ( "context" "crypto/tls" "errors" "fmt" "io" "net" "time" "github.com/miekg/dns" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" ) const ( tcpConnectionEstablishmentTimeout = 3 * time.Second tcpWriteTimeout = 2 * time.Second heartbeatTimeout = 5 * time.Second ) // TCPResolver is a resolver using just a single tcp connection with pipelining. type TCPResolver struct { BasicResolverConn // dnsClient holds the connection configuration of the resolver. dnsClient *dns.Client // resolverConn holds a connection to the DNS server, including query management. resolverConn *tcpResolverConn // resolverConnInstanceID holds the current ID of the resolverConn. resolverConnInstanceID int } // tcpResolverConn represents a single connection to an upstream DNS server. type tcpResolverConn struct { // ctx is the context of the tcpResolverConn. ctx context.Context // cancelCtx cancels ctx cancelCtx context.CancelFunc // id is the ID assigned to the resolver conn. id int // conn is the connection to the DNS server. conn *dns.Conn // resolverInfo holds information about the resolver to enhance error messages. resolverInfo *ResolverInfo // queries is used to submit queries to be sent to the connected DNS server. queries chan *tcpQuery // responses is used to hand the responses from the reader to the handler. responses chan *dns.Msg // inFlightQueries holds all in-flight queries of this connection. inFlightQueries map[uint16]*tcpQuery // heartbeat is a alive-checking channel from which the resolver conn must // always read asap. heartbeat chan struct{} // abandoned signifies if the resolver conn has been abandoned. abandoned *abool.AtomicBool } // tcpQuery holds the query information for a tcpResolverConn. type tcpQuery struct { Query *Query Response chan *dns.Msg } // MakeCacheRecord creates an RRCache record from a reply. func (tq *tcpQuery) MakeCacheRecord(reply *dns.Msg, resolverInfo *ResolverInfo) *RRCache { return &RRCache{ Domain: tq.Query.FQDN, Question: tq.Query.QType, RCode: reply.Rcode, Answer: reply.Answer, Ns: reply.Ns, Extra: reply.Extra, Resolver: resolverInfo.Copy(), } } // NewTCPResolver returns a new TPCResolver. func NewTCPResolver(resolver *Resolver) *TCPResolver { newResolver := &TCPResolver{ BasicResolverConn: BasicResolverConn{ resolver: resolver, }, dnsClient: &dns.Client{ Net: "tcp", Timeout: defaultConnectTimeout, WriteTimeout: tcpWriteTimeout, }, } newResolver.BasicResolverConn.init() return newResolver } // UseTLS enabled TLS for the TCPResolver. TLS settings must be correctly configured in the Resolver. func (tr *TCPResolver) UseTLS() *TCPResolver { tr.dnsClient.Net = "tcp-tls" tr.dnsClient.TLSConfig = &tls.Config{ MinVersion: tls.VersionTLS12, ServerName: tr.resolver.Info.Domain, // TODO: use portbase rng } return tr } func (tr *TCPResolver) getOrCreateResolverConn(ctx context.Context) (*tcpResolverConn, error) { var existingConn *tcpResolverConn // Minimize the time we hold the lock to avoid blocking other threads. tr.Lock() if tr.resolverConn != nil && tr.resolverConn.abandoned.IsNotSet() { existingConn = tr.resolverConn } tr.Unlock() // Check if we have a resolver. if existingConn != nil { // If there is one, check if it's alive! select { case existingConn.heartbeat <- struct{}{}: return existingConn, nil case <-time.After(heartbeatTimeout): log.Warningf("resolver: heartbeat for dns client %s failed", tr.resolver.Info.DescriptiveName()) case <-ctx.Done(): return nil, ctx.Err() case <-module.mgr.Done(): return nil, ErrShuttingDown } } else { // If there is no resolver, check if we are shutting down before dialing! select { case <-ctx.Done(): return nil, ctx.Err() case <-module.mgr.Done(): return nil, ErrShuttingDown default: } } // Create a new if no active one is available. // Refresh the dialer in order to set an authenticated local address. tr.dnsClient.Dialer = &net.Dialer{ LocalAddr: getLocalAddr("tcp"), Timeout: tcpConnectionEstablishmentTimeout, KeepAlive: defaultClientTTL, } // Connect to server. conn, err := tr.dnsClient.Dial(tr.resolver.ServerAddress) if err != nil { // Hint network environment at failed connection. netenv.ReportFailedConnection() log.Debugf("resolver: failed to connect to %s: %s", tr.resolver.Info.DescriptiveName(), err) return nil, fmt.Errorf("%w: failed to connect to %s: %w", ErrFailure, tr.resolver.Info.DescriptiveName(), err) } // Hint network environment at successful connection. netenv.ReportSuccessfulConnection() // Log that a connection to the resolver was established. log.Debugf( "resolver: connected to %s", tr.resolver.Info.DescriptiveName(), ) // Thread-safe resolverConn creation. tr.Lock() defer tr.Unlock() // Create resolver connection. tr.resolverConnInstanceID++ resolverConn := &tcpResolverConn{ id: tr.resolverConnInstanceID, conn: conn, resolverInfo: tr.resolver.Info, queries: make(chan *tcpQuery, 10), responses: make(chan *dns.Msg, 10), inFlightQueries: make(map[uint16]*tcpQuery, 10), heartbeat: make(chan struct{}), abandoned: abool.New(), } // Start worker. module.mgr.Go("dns client", resolverConn.handler) // Set resolver conn for reuse. tr.resolverConn = resolverConn return resolverConn, nil } // Query executes the given query against the resolver. func (tr *TCPResolver) Query(ctx context.Context, q *Query) (*RRCache, error) { queryStarted := time.Now() // Get resolver connection. resolverConn, err := tr.getOrCreateResolverConn(ctx) if err != nil { return nil, err } // Create query request. tq := &tcpQuery{ Query: q, Response: make(chan *dns.Msg), } // Submit query request to live connection. select { case resolverConn.queries <- tq: case <-ctx.Done(): return nil, ctx.Err() case <-module.mgr.Done(): return nil, ErrShuttingDown case <-time.After(defaultRequestTimeout): return nil, ErrTimeout } // Wait for reply. var reply *dns.Msg select { case reply = <-tq.Response: case <-ctx.Done(): return nil, ctx.Err() case <-module.mgr.Done(): return nil, ErrShuttingDown case <-time.After(defaultRequestTimeout): return nil, ErrTimeout } // Check if we have a reply. if reply == nil { // Resolver is shutting down. The Portmaster may be shutting down, or // there is a connection error. return nil, ErrFailure } // Check if the reply was blocked upstream. if tr.resolver.IsBlockedUpstream(reply) { return nil, &BlockedUpstreamError{tr.resolver.Info.DescriptiveName()} } // Report request duration for metrics. reportRequestDuration(queryStarted, tr.resolver) // Create RRCache from reply and return it. return tq.MakeCacheRecord(reply, tr.resolver.Info), nil } // ForceReconnect forces the resolver to re-establish the connection to the server. func (tr *TCPResolver) ForceReconnect(ctx context.Context) { tr.Lock() defer tr.Unlock() // Do nothing if no connection is available. if tr.resolverConn == nil { return } // Set the abandoned to force a new connection on next request. // This will leave the previous connection and handler running until all requests are handled. tr.resolverConn.abandoned.Set() log.Tracer(ctx).Tracef("resolver: marked %s for reconnecting", tr.resolver) } // shutdown cleanly shuts down the resolver connection. // Must only be called once. func (trc *tcpResolverConn) shutdown() { // Set abandoned status and close connection to the DNS server. trc.abandoned.Set() _ = trc.conn.Close() // Close all response channels for in-flight queries. for _, tq := range trc.inFlightQueries { close(tq.Response) } // Respond to any incoming queries for some time in order to not leave them // hanging longer than necessary. for { select { case tq := <-trc.queries: close(tq.Response) case <-time.After(100 * time.Millisecond): return } } } func (trc *tcpResolverConn) handler(workerCtx *mgr.WorkerCtx) error { // Set up context and cleanup. trc.ctx, trc.cancelCtx = context.WithCancel(workerCtx.Ctx()) defer trc.shutdown() // Set up variables. var readyToRecycle bool ttlTimer := time.After(defaultClientTTL) // Start connection reader. module.mgr.Go("dns client reader", trc.reader) // Handle requests. for { select { case <-trc.heartbeat: // Respond to alive checks. case <-trc.ctx.Done(): // Respond to module shutdown or conn error. return nil case <-ttlTimer: // Recycle the connection after the TTL is reached. readyToRecycle = true // Send dummy response to trigger the check. select { case trc.responses <- nil: default: // The response queue is full. // The check will be triggered by another response. } case tq := <-trc.queries: // Handle DNS query request. // Create dns request message. msg := &dns.Msg{} msg.SetQuestion(tq.Query.FQDN, uint16(tq.Query.QType)) // Assign a unique message ID. trc.assignUniqueID(msg) // Add query to in flight registry. trc.inFlightQueries[msg.Id] = tq // Write query to connected DNS server. _ = trc.conn.SetWriteDeadline(time.Now().Add(tcpWriteTimeout)) err := trc.conn.WriteMsg(msg) if err != nil { trc.logConnectionError(err, false) return nil } case msg := <-trc.responses: if msg != nil { trc.handleQueryResponse(msg) } // If we are ready to recycle and we have no in-flight queries, we can // shutdown the connection and create a new one for the next query. if readyToRecycle || trc.abandoned.IsSet() { if len(trc.inFlightQueries) == 0 { log.Debugf("resolver: recycling connection to %s", trc.resolverInfo.DescriptiveName()) return nil } } } } } // assignUniqueID makes sure that ID assigned to msg is unique. func (trc *tcpResolverConn) assignUniqueID(msg *dns.Msg) { // try a random ID 10000 times for range 10000 { // don't try forever _, exists := trc.inFlightQueries[msg.Id] if !exists { return // we are unique, yay! } msg.Id = dns.Id() // regenerate ID } // go through the complete space var id uint16 for ; id <= (1<<16)-1; id++ { // don't try forever _, exists := trc.inFlightQueries[id] if !exists { msg.Id = id return // we are unique, yay! } } } func (trc *tcpResolverConn) handleQueryResponse(msg *dns.Msg) { // Get in flight from registry. tq, ok := trc.inFlightQueries[msg.Id] if ok { delete(trc.inFlightQueries, msg.Id) } else { log.Debugf( "resolver: received possibly unsolicited reply from %s: txid=%d q=%+v", trc.resolverInfo.DescriptiveName(), msg.Id, msg.Question, ) return } // Send response to waiting query handler. select { case tq.Response <- msg: return default: // No one is listening for that response. } // If caching is disabled for this query, we are done. if tq.Query.NoCaching { return } // Otherwise, we can persist the answer in case the request is repeated. rrCache := tq.MakeCacheRecord(msg, trc.resolverInfo) rrCache.Clean(minTTL) err := rrCache.Save() if err != nil { log.Warningf( "resolver: failed to cache RR for %s: %s", tq.Query.ID(), err, ) } } func (trc *tcpResolverConn) reader(workerCtx *mgr.WorkerCtx) error { defer trc.cancelCtx() for { msg, err := trc.conn.ReadMsg() if err != nil { trc.logConnectionError(err, true) return nil } trc.responses <- msg } } func (trc *tcpResolverConn) logConnectionError(err error, reading bool) { // Check if we are the first to see an error. if trc.abandoned.SetToIf(false, true) { // Log error. switch { case errors.Is(err, io.EOF): log.Debugf( "resolver: connection to %s was closed", trc.resolverInfo.DescriptiveName(), ) case reading: log.Warningf( "resolver: read error from %s: %s", trc.resolverInfo.DescriptiveName(), err, ) default: log.Warningf( "resolver: write error to %s: %s", trc.resolverInfo.DescriptiveName(), err, ) } } } ================================================ FILE: service/resolver/resolver.go ================================================ package resolver import ( "context" "fmt" "net" "sync" "time" "github.com/miekg/dns" "github.com/tevino/abool" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/netutils" ) // DNS Resolver Attributes. const ( ServerTypeDNS = "dns" ServerTypeTCP = "tcp" ServerTypeDoT = "dot" ServerTypeDoH = "doh" ServerTypeMDNS = "mdns" ServerTypeEnv = "env" ServerTypeMonitor = "monitor" ServerTypeFirewall = "firewall" ServerSourceConfigured = "config" ServerSourceOperatingSystem = "system" ServerSourceMDNS = "mdns" ServerSourceEnv = "env" ServerSourceETW = "etw" ServerSourceSystemd = "systemd" ServerSourceFirewall = "firewall" ) // DNS resolver scheme aliases. const ( HTTPSProtocol = "https" TLSProtocol = "tls" ) // Resolver holds information about an active resolver. type Resolver struct { // Server config url (and ID) // Supported parameters: // - `verify=domain`: verify domain (dot only) // - `name=name`: human readable name for resolver // - `blockedif=empty`: how to detect if the dns service blocked something // - `empty`: NXDomain result, but without any other record in any section // - `refused`: Request was refused // - `zeroip`: Answer only contains zeroip ConfigURL string // Info holds the parsed configuration. Info *ResolverInfo // ServerAddress holds the resolver address for easier use. ServerAddress string // UpstreamBlockDetection defines the detection type // to identifier upstream DNS query blocking. // Valid values are: // - zeroip // - empty // - refused (default) // - disabled UpstreamBlockDetection string // Special Options Search []string SearchOnly bool Path string // Special States LinkLocalUnavailable bool // logic interface Conn ResolverConn `json:"-"` } // ResolverInfo is a subset of resolver attributes that is attached to answers // from that server in order to use it later for decision making. It must not // be changed by anyone after creation and initialization is complete. type ResolverInfo struct { //nolint:golint,maligned // TODO // Name describes the name given to the resolver. The name is configured in the config URL using the name parameter. Name string // Type describes the type of the resolver. // Possible values include dns, tcp, dot, doh, mdns, env, monitor, firewall. Type string // Source describes where the resolver configuration came from. // Possible values include config, system, mdns, env, etw, systemd, firewall. Source string // IP is the IP address of the resolver IP net.IP // Domain of the dns server if it has one Domain string // IPScope is the network scope of the IP address. IPScope netutils.IPScope // Port is the udp/tcp port of the resolver. Port uint16 // id holds a unique ID for this resolver. id string idGen sync.Once } // ID returns the unique ID of the resolver. func (info *ResolverInfo) ID() string { // Generate the ID the first time. info.idGen.Do(func() { switch info.Type { case ServerTypeMDNS: info.id = ServerTypeMDNS case ServerTypeEnv: info.id = ServerTypeEnv case ServerTypeDoH: info.id = fmt.Sprintf( //nolint:nosprintfhostport // Not used as URL. "https://%s:%d#%s", info.Domain, info.Port, info.Source, ) case ServerTypeDoT: info.id = fmt.Sprintf( //nolint:nosprintfhostport // Not used as URL. "dot://%s:%d#%s", info.Domain, info.Port, info.Source, ) default: info.id = fmt.Sprintf( "%s://%s:%d#%s", info.Type, info.IP, info.Port, info.Source, ) } }) return info.id } // DescriptiveName returns a human readable, but also detailed representation // of the resolver. func (info *ResolverInfo) DescriptiveName() string { switch { case info.Type == ServerTypeMDNS: return "MDNS" case info.Type == ServerTypeEnv: return "Portmaster Environment" case info.Name != "": return fmt.Sprintf( "%s (%s)", info.Name, info.ID(), ) case info.Domain != "": return fmt.Sprintf( "%s (%s)", info.Domain, info.ID(), ) default: return fmt.Sprintf( "%s (%s)", info.IP.String(), info.ID(), ) } } // Copy returns a full copy of the ResolverInfo. func (info *ResolverInfo) Copy() *ResolverInfo { // Force idGen to run before we copy. _ = info.ID() // Copy manually in order to not copy the mutex. cp := &ResolverInfo{ Name: info.Name, Type: info.Type, Source: info.Source, IP: info.IP, Domain: info.Domain, IPScope: info.IPScope, Port: info.Port, id: info.id, } // Trigger idGen.Do(), as the ID is already generated. cp.idGen.Do(func() {}) return cp } // IsBlockedUpstream returns true if the request has been blocked // upstream. func (resolver *Resolver) IsBlockedUpstream(answer *dns.Msg) bool { return isBlockedUpstream(resolver, answer) } // String returns the URL representation of the resolver. func (resolver *Resolver) String() string { return resolver.Info.DescriptiveName() } // ResolverConn is an interface to implement different types of query backends. type ResolverConn interface { //nolint:golint // TODO Query(ctx context.Context, q *Query) (*RRCache, error) ReportFailure() IsFailing() bool ResetFailure() ForceReconnect(ctx context.Context) } // BasicResolverConn implements ResolverConn for standard dns clients. type BasicResolverConn struct { sync.Mutex // Also used by inheriting structs. resolver *Resolver failing *abool.AtomicBool failingStarted time.Time fails int failLock sync.Mutex networkChangedFlag *utils.Flag } // init initializes the basic resolver connection. func (brc *BasicResolverConn) init() { brc.failing = abool.New() brc.networkChangedFlag = netenv.GetNetworkChangedFlag() } ================================================ FILE: service/resolver/resolver_test.go ================================================ package resolver import ( "context" "flag" "sync" "testing" "time" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/safing/portmaster/base/log" ) var ( testResolver string silencingTraceCtx, _ = log.AddTracer(context.Background()) ) func init() { flag.StringVar( &testResolver, "resolver", "dot://1.1.1.2:853?verify=cloudflare-dns.com&name=Cloudflare&blockedif=zeroip", "set custom resolver for testing", ) } func startQuery(t *testing.T, wg *sync.WaitGroup, rc ResolverConn, q *Query) { t.Helper() start := time.Now() _, err := rc.Query(silencingTraceCtx, q) if err != nil { t.Logf("client failed: %s", err) wg.Done() return } t.Logf("resolved %s in %s", q.FQDN, time.Since(start)) wg.Done() } func TestSingleResolving(t *testing.T) { t.Parallel() // skip if short - this test depends on the Internet and might fail randomly if testing.Short() { t.Skip() } defaultRequestTimeout = 30 * time.Second // create separate resolver for this test resolver, _, err := createResolver(testResolver, "config") if err != nil { t.Fatal(err) } t.Logf("running bulk query test with resolver %s", resolver.Info.DescriptiveName()) started := time.Now() wg := &sync.WaitGroup{} wg.Add(100) for range 100 { startQuery(t, wg, resolver.Conn, &Query{ FQDN: <-domainFeed, QType: dns.Type(dns.TypeA), }) } wg.Wait() t.Logf("total time taken: %s", time.Since(started)) } func TestBulkResolving(t *testing.T) { t.Parallel() // skip if short - this test depends on the Internet and might fail randomly if testing.Short() { t.Skip() } defaultRequestTimeout = 30 * time.Second // create separate resolver for this test resolver, _, err := createResolver(testResolver, "config") if err != nil { t.Fatal(err) } t.Logf("running bulk query test with resolver %s", resolver.Info.DescriptiveName()) started := time.Now() wg := &sync.WaitGroup{} wg.Add(100) for range 100 { go startQuery(t, wg, resolver.Conn, &Query{ FQDN: <-domainFeed, QType: dns.Type(dns.TypeA), }) } wg.Wait() t.Logf("total time taken: %s", time.Since(started)) } func TestPublicSuffix(t *testing.T) { t.Parallel() testSuffix(t, "co.uk.", "", true) testSuffix(t, "amazon.co.uk.", "amazon.co.uk.", true) testSuffix(t, "books.amazon.co.uk.", "amazon.co.uk.", true) testSuffix(t, "www.books.amazon.co.uk.", "amazon.co.uk.", true) testSuffix(t, "com.", "", true) testSuffix(t, "amazon.com.", "amazon.com.", true) testSuffix(t, "example0.debian.net.", "example0.debian.net.", true) testSuffix(t, "example1.debian.org.", "debian.org.", true) testSuffix(t, "golang.dev.", "golang.dev.", true) testSuffix(t, "golang.net.", "golang.net.", true) testSuffix(t, "play.golang.org.", "golang.org.", true) testSuffix(t, "gophers.in.space.museum.", "space.museum.", true) testSuffix(t, "0emm.com.", "0emm.com.", true) testSuffix(t, "a.0emm.com.", "", true) testSuffix(t, "b.c.d.0emm.com.", "c.d.0emm.com.", true) testSuffix(t, "org.", "", true) testSuffix(t, "foo.org.", "foo.org.", true) testSuffix(t, "foo.co.uk.", "foo.co.uk.", true) testSuffix(t, "foo.dyndns.org.", "foo.dyndns.org.", true) testSuffix(t, "foo.blogspot.co.uk.", "foo.blogspot.co.uk.", true) testSuffix(t, "there.is.no.such-tld.", "no.such-tld.", false) testSuffix(t, "www.some.bit.", "some.bit.", false) testSuffix(t, "cromulent.", "", false) testSuffix(t, "arpa.", "", true) testSuffix(t, "in-addr.arpa.", "", true) testSuffix(t, "1.in-addr.arpa.", "1.in-addr.arpa.", true) testSuffix(t, "ip6.arpa.", "", true) testSuffix(t, "1.ip6.arpa.", "1.ip6.arpa.", true) testSuffix(t, "www.some.arpa.", "some.arpa.", true) testSuffix(t, "www.some.home.arpa.", "home.arpa.", true) testSuffix(t, ".", "", false) testSuffix(t, "", "", false) // Test edge case domains. testSuffix(t, "www.some.example.", "some.example.", true) testSuffix(t, "www.some.invalid.", "some.invalid.", true) testSuffix(t, "www.some.local.", "some.local.", true) testSuffix(t, "www.some.localhost.", "some.localhost.", true) testSuffix(t, "www.some.onion.", "some.onion.", false) testSuffix(t, "www.some.test.", "some.test.", true) } func testSuffix(t *testing.T, fqdn, domainRoot string, icannSpace bool) { t.Helper() q := &Query{FQDN: fqdn} q.InitPublicSuffixData() assert.Equal(t, domainRoot, q.DomainRoot) assert.Equal(t, icannSpace, q.ICANNSpace) } ================================================ FILE: service/resolver/resolvers.go ================================================ package resolver import ( "context" "fmt" "net" "net/url" "sort" "strconv" "strings" "sync" "github.com/miekg/dns" "golang.org/x/net/publicsuffix" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/netutils" ) const maxSearchDomains = 100 // Scope defines a domain scope and which resolvers can resolve it. type Scope struct { Domain string Resolvers []*Resolver } const ( parameterName = "name" parameterVerify = "verify" parameterIP = "ip" parameterBlockedIf = "blockedif" parameterSearch = "search" parameterSearchOnly = "search-only" parameterLinkLocal = "link-local" ) var ( globalResolvers []*Resolver // all (global) resolvers localResolvers []*Resolver // all resolvers that are in site-local or link-local IP ranges systemResolvers []*Resolver // all resolvers that were assigned by the system localScopes []*Scope // list of scopes with a list of local resolvers that can resolve the scope activeResolvers map[string]*Resolver // lookup map of all resolvers currentResolverConfig []string // current active resolver config, to detect changes resolverInitDomains map[string]struct{} // a set with all domains of the dns resolvers resolversLock sync.RWMutex ) func indexOfScope(domain string, list []*Scope) int { for k, v := range list { if v.Domain == domain { return k } } return -1 } func getActiveResolverByIDWithLocking(server string) *Resolver { resolversLock.RLock() defer resolversLock.RUnlock() resolver, ok := activeResolvers[server] if ok { return resolver } return nil } func formatIPAndPort(ip net.IP, port uint16) string { var address string if ipv4 := ip.To4(); ipv4 != nil { address = fmt.Sprintf("%s:%d", ipv4.String(), port) } else { address = fmt.Sprintf("[%s]:%d", ip.String(), port) } return address } func resolverConnFactory(resolver *Resolver) ResolverConn { switch resolver.Info.Type { case ServerTypeTCP: return NewTCPResolver(resolver) case ServerTypeDoT: return NewTCPResolver(resolver).UseTLS() case ServerTypeDoH: return NewHTTPSResolver(resolver) case ServerTypeDNS: return NewPlainResolver(resolver) default: return nil } } func createResolver(resolverURL, source string) (*Resolver, bool, error) { u, err := url.Parse(resolverURL) if err != nil { return nil, false, err } if resolverInitDomains == nil { resolverInitDomains = make(map[string]struct{}) } switch u.Scheme { case ServerTypeDNS, ServerTypeDoT, ServerTypeDoH, ServerTypeTCP: case HTTPSProtocol: u.Scheme = ServerTypeDoH case TLSProtocol: u.Scheme = ServerTypeDoT default: return nil, false, fmt.Errorf("DNS resolver scheme %q invalid", u.Scheme) } query := u.Query() // Create Resolver object newResolver := &Resolver{ ConfigURL: resolverURL, Info: &ResolverInfo{ Name: query.Get(parameterName), Type: u.Scheme, Source: source, IP: nil, Domain: "", IPScope: netutils.Global, Port: 0, }, ServerAddress: "", Path: u.Path, // Used for DoH UpstreamBlockDetection: "", } // Get parameters and check if keys exist. err = checkAndSetResolverParamters(u, newResolver) if err != nil { return nil, false, err } // Check block detection type. newResolver.UpstreamBlockDetection = query.Get(parameterBlockedIf) if newResolver.UpstreamBlockDetection == "" { newResolver.UpstreamBlockDetection = BlockDetectionZeroIP } switch newResolver.UpstreamBlockDetection { case BlockDetectionDisabled, BlockDetectionEmptyAnswer, BlockDetectionRefused, BlockDetectionZeroIP: default: return nil, false, fmt.Errorf("invalid value for upstream block detection (blockedif=)") } // Get ip scope if we have ip if newResolver.Info.IP != nil { newResolver.Info.IPScope = netutils.GetIPScope(newResolver.Info.IP) // Skip localhost resolvers from the OS, but not if configured. if newResolver.Info.IPScope.IsLocalhost() && source == ServerSourceOperatingSystem { return nil, true, nil // skip } } // Parse search domains. searchDomains := query.Get(parameterSearch) if searchDomains != "" { err = configureSearchDomains(newResolver, strings.Split(searchDomains, ","), true) if err != nil { return nil, false, err } } // Check if searchOnly is set and valid. if query.Has(parameterSearchOnly) { newResolver.SearchOnly = true if query.Get(parameterSearchOnly) != "" { return nil, false, fmt.Errorf("%s may only be used as an empty parameter", parameterSearchOnly) } if len(newResolver.Search) == 0 { return nil, false, fmt.Errorf("cannot use %s without search scopes", parameterSearchOnly) } } // Check if this is a link-local resolver. if query.Has(parameterLinkLocal) { if query.Get(parameterLinkLocal) != "" { return nil, false, fmt.Errorf("%s may only be used as an empty parameter", parameterLinkLocal) } // Check if resolver IP is link-local. resolverNet, err := netenv.GetLocalNetwork(newResolver.Info.IP) switch { case err != nil: newResolver.LinkLocalUnavailable = true case resolverNet == nil: newResolver.LinkLocalUnavailable = true } } newResolver.Conn = resolverConnFactory(newResolver) return newResolver, false, nil } func checkAndSetResolverParamters(u *url.URL, resolver *Resolver) error { // Check if we are using domain name and if it's in a valid scheme ip := net.ParseIP(u.Hostname()) hostnameIsDomaion := (ip == nil) if ip == nil && u.Scheme != ServerTypeDoH && u.Scheme != ServerTypeDoT { return fmt.Errorf("resolver IP %q is invalid", u.Hostname()) } // Add default port for scheme if it is missing. port, err := parsePortFromURL(u) if err != nil { return err } resolver.Info.Port = port resolver.Info.IP = ip query := u.Query() for key := range query { switch key { case parameterName, parameterVerify, parameterIP, parameterBlockedIf, parameterSearch, parameterSearchOnly, parameterLinkLocal: // Known key, continue. default: // Unknown key, abort. return fmt.Errorf(`unknown parameter "%q"`, key) } } resolver.Info.Domain = query.Get(parameterVerify) paramterServerIP := query.Get(parameterIP) if u.Scheme == ServerTypeDoT || u.Scheme == ServerTypeDoH { // Check if IP and Domain are set correctly switch { case hostnameIsDomaion && resolver.Info.Domain != "": return fmt.Errorf("cannot set the domain name via both the hostname in the URL and the verify parameter") case !hostnameIsDomaion && resolver.Info.Domain == "": return fmt.Errorf("verify parameter must be set when using ip as domain") case !hostnameIsDomaion && paramterServerIP != "": return fmt.Errorf("cannot set the IP address via both the hostname in the URL and the ip parameter") } // Parse and set IP and Domain to the resolver switch { case hostnameIsDomaion && paramterServerIP != "": // domain and ip as parameter resolver.Info.IP = net.ParseIP(paramterServerIP) resolver.ServerAddress = net.JoinHostPort(paramterServerIP, strconv.Itoa(int(resolver.Info.Port))) resolver.Info.Domain = u.Hostname() case !hostnameIsDomaion && resolver.Info.Domain != "": // ip and domain as parameter resolver.ServerAddress = net.JoinHostPort(ip.String(), strconv.Itoa(int(resolver.Info.Port))) case hostnameIsDomaion && resolver.Info.Domain == "" && paramterServerIP == "": // only domain resolver.Info.Domain = u.Hostname() resolver.ServerAddress = net.JoinHostPort(resolver.Info.Domain, strconv.Itoa(int(port))) } if ip == nil { resolverInitDomains[dns.Fqdn(resolver.Info.Domain)] = struct{}{} } } else { if resolver.Info.Domain != "" { return fmt.Errorf("domain verification is only supported by DoT and DoH servers") } resolver.ServerAddress = net.JoinHostPort(ip.String(), strconv.Itoa(int(resolver.Info.Port))) } return nil } func parsePortFromURL(url *url.URL) (uint16, error) { var port uint16 hostPort := url.Port() if hostPort != "" { // There is a port in the url parsedPort, err := strconv.ParseUint(hostPort, 10, 16) if err != nil { return 0, fmt.Errorf("invalid port %q", url.Port()) } port = uint16(parsedPort) } else { // set the default port for the protocol switch { case url.Scheme == ServerTypeDNS, url.Scheme == ServerTypeTCP: port = 53 case url.Scheme == ServerTypeDoH: port = 443 case url.Scheme == ServerTypeDoT: port = 853 default: return 0, fmt.Errorf("cannot determine port for %q", url.Scheme) } } return port, nil } func configureSearchDomains(resolver *Resolver, searches []string, hardfail bool) error { resolver.Search = make([]string, 0, len(searches)) // Check all search domains. for i, value := range searches { trimmedDomain := strings.ToLower(strings.Trim(value, ".")) err := checkSearchScope(trimmedDomain) if err != nil { if hardfail { resolver.Search = nil return fmt.Errorf("failed to validate search domain #%d: %w", i+1, err) } log.Warningf("resolver: skipping invalid search domain for resolver %s: %s", resolver, utils.SafeFirst16Chars(value)) } else { resolver.Search = append(resolver.Search, fmt.Sprintf(".%s.", trimmedDomain)) } } // Limit search domains to mitigate exploitation via malicious local resolver. if len(resolver.Search) > maxSearchDomains { if hardfail { return fmt.Errorf("too many search domains, for security reasons only %d search domains are supported", maxSearchDomains) } log.Warningf("resolver: limiting search domains for resolver %s to %d for security reasons", resolver, maxSearchDomains) resolver.Search = resolver.Search[:maxSearchDomains] } return nil } func getConfiguredResolvers(list []string) (resolvers []*Resolver) { for _, server := range list { resolver, skip, err := createResolver(server, ServerSourceConfigured) if err != nil { // TODO(ppacher): module error log.Errorf("cannot use resolver %s: %s", server, err) continue } if skip { continue } resolvers = append(resolvers, resolver) } return resolvers } func getSystemResolvers() (resolvers []*Resolver) { for _, nameserver := range netenv.Nameservers() { serverURL := fmt.Sprintf("dns://%s", formatIPAndPort(nameserver.IP, 53)) resolver, skip, err := createResolver(serverURL, ServerSourceOperatingSystem) if err != nil { // that shouldn't happen but handle it anyway ... log.Errorf("cannot use system resolver %s: %s", serverURL, err) continue } if skip { continue } if resolver.Info.IPScope.IsLAN() { _ = configureSearchDomains(resolver, nameserver.Search, false) } resolvers = append(resolvers, resolver) } return resolvers } const missingResolversErrorID = "missing-resolvers" func loadResolvers() { defer func() { if !allConfiguredResolversAreFailing() { resetFailingResolversNotification() } }() // TODO: what happens when a lot of processes want to reload at once? we do not need to run this multiple times in a short time frame. resolversLock.Lock() defer resolversLock.Unlock() // Resolve module error about missing resolvers. module.states.Remove(missingResolversErrorID) // Check if settings were changed and clear name cache when they did. newResolverConfig := configuredNameServers() if len(currentResolverConfig) > 0 && !utils.StringSliceEqual(currentResolverConfig, newResolverConfig) { module.mgr.Go("clear dns cache", func(ctx *mgr.WorkerCtx) error { log.Info("resolver: clearing dns cache due to changed resolver config") _, err := clearNameCache(ctx.Ctx()) return err }) } // If no resolvers are configure set the disabled state. So other modules knows that the users does not want to use Portmaster resolver. if len(newResolverConfig) == 0 { module.isDisabled.Store(true) } else { module.isDisabled.Store(false) } currentResolverConfig = newResolverConfig newResolvers := append( getConfiguredResolvers(newResolverConfig), getSystemResolvers()..., ) if len(newResolvers) == 0 { // load defaults directly, overriding config system newResolvers = getConfiguredResolvers(defaultNameServers) if len(newResolvers) > 0 { log.Warning("resolver: no (valid) dns server found in config or system, falling back to global defaults") module.states.Add(mgr.State{ ID: missingResolversErrorID, Name: "Using Factory Default DNS Servers", Message: "The Portmaster could not find any (valid) DNS servers in the settings or system. In order to prevent being disconnected, the factory defaults are being used instead. If you just switched your network, this should be resolved shortly.", Type: mgr.StateTypeWarning, }) } else { log.Critical("resolver: no (valid) dns server found in config, system or global defaults") module.states.Add(mgr.State{ ID: missingResolversErrorID, Name: "No DNS Servers Configured", Message: "The Portmaster could not find any (valid) DNS servers in the settings or system. You will experience severe connectivity problems until resolved. If you just switched your network, this should be resolved shortly.", Type: mgr.StateTypeError, }) } } // save resolvers globalResolvers = newResolvers // assign resolvers to scopes setScopedResolvers(globalResolvers) // set active resolvers (for cache validation) // reset activeResolvers = make(map[string]*Resolver) // add for _, resolver := range newResolvers { activeResolvers[resolver.Info.ID()] = resolver } activeResolvers[mDNSResolver.Info.ID()] = mDNSResolver activeResolvers[envResolver.Info.ID()] = envResolver // log global resolvers if len(globalResolvers) > 0 { log.Trace("resolver: loaded global resolvers:") for _, resolver := range globalResolvers { log.Tracef("resolver: %s", resolver.ConfigURL) } } else { log.Warning("resolver: no global resolvers loaded") } // log local resolvers if len(localResolvers) > 0 { log.Trace("resolver: loaded local resolvers:") for _, resolver := range localResolvers { log.Tracef("resolver: %s", resolver.ConfigURL) } } else { log.Info("resolver: no local resolvers loaded") } // log system resolvers if len(systemResolvers) > 0 { log.Trace("resolver: loaded system/network-assigned resolvers:") for _, resolver := range systemResolvers { log.Tracef("resolver: %s", resolver.ConfigURL) } } else { log.Info("resolver: no system/network-assigned resolvers loaded") } // log scopes if len(localScopes) > 0 { log.Trace("resolver: loaded scopes:") for _, scope := range localScopes { var scopeServers []string for _, resolver := range scope.Resolvers { scopeServers = append(scopeServers, resolver.ConfigURL) } log.Tracef("resolver: %s: %s", scope.Domain, strings.Join(scopeServers, ", ")) } } else { log.Info("resolver: no scopes loaded") } // alert if no resolvers are loaded if len(globalResolvers) == 0 && len(localResolvers) == 0 { log.Critical("resolver: no resolvers loaded!") } } func setScopedResolvers(resolvers []*Resolver) { // make list with local resolvers localResolvers = make([]*Resolver, 0) systemResolvers = make([]*Resolver, 0) localScopes = make([]*Scope, 0) for _, resolver := range resolvers { if resolver.Info.IPScope.IsLAN() { localResolvers = append(localResolvers, resolver) } else if net, _ := netenv.GetLocalNetwork(resolver.Info.IP); net != nil { localResolvers = append(localResolvers, resolver) } if resolver.Info.Source == ServerSourceOperatingSystem { systemResolvers = append(systemResolvers, resolver) } if resolver.Search != nil { // add resolver to custom searches for _, search := range resolver.Search { if search == "." { continue } key := indexOfScope(search, localScopes) if key == -1 { localScopes = append(localScopes, &Scope{ Domain: search, Resolvers: []*Resolver{resolver}, }) continue } localScopes[key].Resolvers = append(localScopes[key].Resolvers, resolver) } } } // sort scopes by length sort.Slice(localScopes, func(i, j int) bool { return len(localScopes[i].Domain) > len(localScopes[j].Domain) }, ) } func checkSearchScope(searchDomain string) error { // Sanity check the input. if len(searchDomain) == 0 || searchDomain[0] == '.' || searchDomain[len(searchDomain)-1] == '.' { return fmt.Errorf("invalid (1) search domain: %s", searchDomain) } // Domains that are specifically listed in the special service domains may be // diverted completely. if domainInScope("."+searchDomain+".", specialServiceDomains) { return nil } // In order to check if the search domain is too high up in the hierarchy, we // need to add some more subdomains. augmentedSearchDomain := "*.*.*.*.*." + searchDomain // Get the public suffix of the search domain and if the TLD is managed by ICANN. suffix, icann := publicsuffix.PublicSuffix(augmentedSearchDomain) // Sanity check the result. if len(suffix) == 0 { return fmt.Errorf("invalid (2) search domain: %s", searchDomain) } // TLDs that are not managed by ICANN (ie. are unofficial) may be // diverted completely. // This might include special service domains like ".onion" and ".bit". if !icann && !strings.Contains(suffix, ".") { return nil } // Build the eTLD+1 domain, which is the highest that may be diverted. split := len(augmentedSearchDomain) - len(suffix) - 1 eTLDplus1 := augmentedSearchDomain[1+strings.LastIndex(augmentedSearchDomain[:split], "."):] // Check if the scope is being violated by checking if the eTLD+1 contains a wildcard. if strings.Contains(eTLDplus1, "*") { return fmt.Errorf(`search domain "%s" is dangerously high up the hierarchy, stay at or below "%s"`, searchDomain, eTLDplus1) } return nil } // IsResolverAddress returns whether the given ip and port match a configured resolver. func IsResolverAddress(ip net.IP, port uint16) bool { resolversLock.RLock() defer resolversLock.RUnlock() // Check if the given IP and port matches a resolver. for _, r := range globalResolvers { if port == r.Info.Port && r.Info.IP.Equal(ip) { return true } } return false } // ForceResolverReconnect forces all resolvers to reconnect. func ForceResolverReconnect(ctx context.Context) { resolversLock.RLock() defer resolversLock.RUnlock() ctx, tracer := log.AddTracer(ctx) defer tracer.Submit() tracer.Trace("resolver: forcing all active resolvers to reconnect") for _, r := range globalResolvers { r.Conn.ForceReconnect(ctx) } tracer.Info("resolver: all active resolvers were forced to reconnect") } // allConfiguredResolversAreFailing reports whether all configured resolvers are failing. // Return false if there are no configured resolvers. func allConfiguredResolversAreFailing() bool { resolversLock.RLock() defer resolversLock.RUnlock() // If there are no configured resolvers, return as not failing. if len(currentResolverConfig) == 0 { return false } // Return as not failing, if we can find any non-failing configured resolver. for _, resolver := range globalResolvers { if !resolver.Conn.IsFailing() && resolver.Info.Source == ServerSourceConfigured { // We found a non-failing configured resolver. return false } } return true } ================================================ FILE: service/resolver/resolvers_test.go ================================================ package resolver import ( "testing" "github.com/stretchr/testify/assert" ) func TestCheckResolverSearchScope(t *testing.T) { t.Parallel() // should fail (invalid) assert.Error(t, checkSearchScope(".")) assert.Error(t, checkSearchScope(".com.")) assert.Error(t, checkSearchScope("com.")) assert.Error(t, checkSearchScope(".com")) // should fail (too high scope) assert.Error(t, checkSearchScope("com")) assert.Error(t, checkSearchScope("net")) assert.Error(t, checkSearchScope("org")) assert.Error(t, checkSearchScope("pvt.k12.ma.us")) // should succeed assert.NoError(t, checkSearchScope("a.com")) assert.NoError(t, checkSearchScope("b.a.com")) assert.NoError(t, checkSearchScope("c.b.a.com")) assert.NoError(t, checkSearchScope("test.pvt.k12.ma.us")) assert.NoError(t, checkSearchScope("onion")) assert.NoError(t, checkSearchScope("a.onion")) assert.NoError(t, checkSearchScope("b.a.onion")) assert.NoError(t, checkSearchScope("c.b.a.onion")) assert.NoError(t, checkSearchScope("bit")) assert.NoError(t, checkSearchScope("a.bit")) assert.NoError(t, checkSearchScope("b.a.bit")) assert.NoError(t, checkSearchScope("c.b.a.bit")) assert.NoError(t, checkSearchScope("doesnotexist")) assert.NoError(t, checkSearchScope("a.doesnotexist")) assert.NoError(t, checkSearchScope("b.a.doesnotexist")) assert.NoError(t, checkSearchScope("c.b.a.doesnotexist")) } ================================================ FILE: service/resolver/reverse.go ================================================ package resolver import ( "context" "fmt" "strings" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" ) // ResolveIPAndValidate finds (reverse DNS), validates (forward DNS) and returns the domain name assigned to the given IP. func ResolveIPAndValidate(ctx context.Context, ip string) (domain string, err error) { // get reversed DNS address reverseIP, err := dns.ReverseAddr(ip) if err != nil { log.Tracer(ctx).Tracef("resolver: failed to get reverse address of %s: %s", ip, err) return "", ErrInvalid } // get PTR record q := &Query{ FQDN: reverseIP, QType: dns.Type(dns.TypePTR), } rrCache, err := Resolve(ctx, q) if err != nil || rrCache == nil { return "", fmt.Errorf("failed to resolve %s%s: %w", q.FQDN, q.QType, err) } // get result from record var ptrName string for _, rr := range rrCache.Answer { ptrRec, ok := rr.(*dns.PTR) if ok { ptrName = ptrRec.Ptr break } } // check for nxDomain if ptrName == "" { return "", fmt.Errorf("%w: %s%s", ErrNotFound, q.FQDN, q.QType) } // get forward record q = &Query{ FQDN: ptrName, } // IPv4/6 switch if strings.Contains(ip, ":") { q.QType = dns.Type(dns.TypeAAAA) } else { q.QType = dns.Type(dns.TypeA) } // resolve rrCache, err = Resolve(ctx, q) if err != nil || rrCache == nil { return "", fmt.Errorf("failed to resolve %s%s: %w", q.FQDN, q.QType, err) } // check for matching A/AAAA record for _, rr := range rrCache.Answer { switch v := rr.(type) { case *dns.A: // log.Debugf("A: %s", v.A.String()) if ip == v.A.String() { return ptrName, nil } case *dns.AAAA: // log.Debugf("AAAA: %s", v.AAAA.String()) if ip == v.AAAA.String() { return ptrName, nil } } } // no match return "", ErrBlocked } ================================================ FILE: service/resolver/reverse_test.go ================================================ package resolver import ( "context" "testing" "github.com/safing/portmaster/base/log" ) func testReverse(t *testing.T, ip, result, expectedErr string) { t.Helper() ctx, tracer := log.AddTracer(context.Background()) defer tracer.Submit() domain, err := ResolveIPAndValidate(ctx, ip) if err != nil { tracer.Warning(err.Error()) if expectedErr == "" || err.Error() != expectedErr { t.Errorf("reverse-validating %s: unexpected error: %s", ip, err) } return } if domain != result { t.Errorf("reverse-validating %s: unexpected result: %s", ip, domain) } } func TestResolveIPAndValidate(t *testing.T) { t.Parallel() testReverse(t, "198.41.0.4", "a.root-servers.net.", "") // testReverse(t, "9.9.9.9", "dns.quad9.net.", "") // started resolving to dns9.quad9.net. // testReverse(t, "2620:fe::fe", "dns.quad9.net.", "") // fails sometimes for some (external) reason testReverse(t, "1.1.1.1", "one.one.one.one.", "") testReverse(t, "2606:4700:4700::1111", "one.one.one.one.", "") testReverse(t, "93.184.216.34", "example.com.", "record could not be found: 34.216.184.93.in-addr.arpa.PTR") testReverse(t, "185.199.109.153", "cdn-185-199-109-153.github.com.", "record could not be found: 153.109.199.185.in-addr.arpa.PTR") } ================================================ FILE: service/resolver/rr_context.go ================================================ package resolver import ( "time" "github.com/miekg/dns" ) // DNSRequestContext is a static structure to add information to DNS request connections. type DNSRequestContext struct { Domain string Question string RCode string ServedFromCache bool RequestingNew bool IsBackup bool Filtered bool Modified time.Time Expires time.Time } // ToDNSRequestContext returns a new DNSRequestContext of the RRCache. func (rrCache *RRCache) ToDNSRequestContext() *DNSRequestContext { return &DNSRequestContext{ Domain: rrCache.Domain, Question: rrCache.Question.String(), RCode: dns.RcodeToString[rrCache.RCode], ServedFromCache: rrCache.ServedFromCache, RequestingNew: rrCache.RequestingNew, IsBackup: rrCache.IsBackup, Filtered: rrCache.Filtered, Modified: time.Unix(rrCache.Modified, 0), Expires: time.Unix(rrCache.Expires, 0), } } ================================================ FILE: service/resolver/rrcache.go ================================================ package resolver import ( "context" "fmt" "net" "time" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/nameserver/nsutil" "github.com/safing/portmaster/service/netenv" ) // RRCache is a single-use structure to hold a DNS response. // Persistence is handled through NameRecords because of a limitation of the // underlying dns library. // //nolint:maligned type RRCache struct { // Respnse Header Domain string Question dns.Type RCode int // Response Content Answer []dns.RR `json:"-"` Ns []dns.RR `json:"-"` Extra []dns.RR `json:"-"` Expires int64 // Resolver Information Resolver *ResolverInfo `json:"-"` // Metadata about the request and handling ServedFromCache bool RequestingNew bool IsBackup bool Filtered bool FilteredEntries []string // Modified holds when this entry was last changed, ie. saved to database. // This field is only populated when the entry comes from the cache. Modified int64 } // ID returns the ID of the RRCache consisting of the domain and question type. func (rrCache *RRCache) ID() string { return rrCache.Domain + rrCache.Question.String() } // Expired returns whether the record has expired. func (rrCache *RRCache) Expired() bool { return rrCache.Expires <= time.Now().Unix() } // ExpiresSoon returns whether the record will expire soon (or already has) and // should already be refreshed. func (rrCache *RRCache) ExpiresSoon() bool { return rrCache.Expires <= time.Now().Unix()+refreshTTL } // Clean sets all TTLs to 17 and sets cache expiry with specified minimum. func (rrCache *RRCache) Clean(minExpires uint32) { var lowestTTL uint32 = 0xFFFFFFFF var header *dns.RR_Header // set TTLs to 17 // TODO: double append? is there something more elegant? for _, rr := range append(rrCache.Answer, append(rrCache.Ns, rrCache.Extra...)...) { header = rr.Header() if lowestTTL > header.Ttl { lowestTTL = header.Ttl } header.Ttl = 17 } // TTL range limits switch { case lowestTTL < minExpires: lowestTTL = minExpires case lowestTTL > maxTTL: lowestTTL = maxTTL } // shorten caching switch { case rrCache.RCode != dns.RcodeSuccess: // Any sort of error. lowestTTL = 10 case netenv.IsConnectivityDomain(rrCache.Domain): // Responses from these domains might change very quickly depending on the environment. lowestTTL = 3 case len(rrCache.Answer) == 0: // Empty answer section: Domain exists, but not the queried RR. lowestTTL = 60 case !netenv.Online(): // Not being fully online could mean that we get funny responses. lowestTTL = 60 } // log.Tracef("lowest TTL is %d", lowestTTL) rrCache.Expires = time.Now().Unix() + int64(lowestTTL) } // ExportAllARecords return of a list of all A and AAAA IP addresses. func (rrCache *RRCache) ExportAllARecords() (ips []net.IP) { for _, rr := range rrCache.Answer { if rr.Header().Class != dns.ClassINET { continue } switch rr.Header().Rrtype { case dns.TypeA: aRecord, ok := rr.(*dns.A) if ok { ips = append(ips, aRecord.A) } case dns.TypeAAAA: aaaaRecord, ok := rr.(*dns.AAAA) if ok { ips = append(ips, aaaaRecord.AAAA) } } } return } // ToNameRecord converts the RRCache to a NameRecord for cleaner persistence. func (rrCache *RRCache) ToNameRecord() *NameRecord { newRecord := &NameRecord{ Domain: rrCache.Domain, Question: rrCache.Question.String(), RCode: rrCache.RCode, Expires: rrCache.Expires, Resolver: rrCache.Resolver, } // Serialize RR entries to strings. newRecord.Answer = toNameRecordSection(rrCache.Answer) newRecord.Ns = toNameRecordSection(rrCache.Ns) newRecord.Extra = toNameRecordSection(rrCache.Extra) return newRecord } func toNameRecordSection(rrSection []dns.RR) []string { serialized := make([]string, 0, len(rrSection)) for _, entry := range rrSection { // Ignore some RR types. switch entry.Header().Rrtype { case dns.TypeOPT: // This record type cannot be unserialized again and only consists of // additional metadata. case dns.TypeNULL: default: serialized = append(serialized, entry.String()) } } return serialized } // rcodeIsCacheable returns whether a record with the given RCode should be cached. func rcodeIsCacheable(rCode int) bool { switch rCode { case dns.RcodeSuccess, dns.RcodeNameError, dns.RcodeRefused: return true default: return false } } // Cacheable returns whether the record should be cached. func (rrCache *RRCache) Cacheable() bool { return rcodeIsCacheable(rrCache.RCode) } // Save saves the RRCache to the database as a NameRecord. func (rrCache *RRCache) Save() error { if !rrCache.Cacheable() { return nil } return rrCache.ToNameRecord().Save() } // GetRRCache tries to load the corresponding NameRecord from the database and convert it. func GetRRCache(domain string, question dns.Type) (*RRCache, error) { rrCache := &RRCache{ Domain: domain, Question: question, } nameRecord, err := GetNameRecord(domain, question.String()) if err != nil { return nil, err } rrCache.RCode = nameRecord.RCode rrCache.Expires = nameRecord.Expires for _, entry := range nameRecord.Answer { rrCache.Answer = parseRR(rrCache.Answer, entry) } for _, entry := range nameRecord.Ns { rrCache.Ns = parseRR(rrCache.Ns, entry) } for _, entry := range nameRecord.Extra { rrCache.Extra = parseRR(rrCache.Extra, entry) } rrCache.Resolver = nameRecord.Resolver rrCache.ServedFromCache = true rrCache.Modified = nameRecord.Meta().Modified return rrCache, nil } func parseRR(section []dns.RR, entry string) []dns.RR { rr, err := dns.NewRR(entry) switch { case err != nil: log.Warningf("resolver: failed to parse cached record %q: %s", entry, err) case rr == nil: log.Warningf("resolver: failed to parse cached record %q: resulted in nil record", entry) default: return append(section, rr) } return section } // Flags formats ServedFromCache and RequestingNew to a condensed, flag-like format. func (rrCache *RRCache) Flags() string { var s string if rrCache.ServedFromCache { s += "C" } if rrCache.RequestingNew { s += "R" } if rrCache.IsBackup { s += "B" } if rrCache.Filtered { s += "F" } if s != "" { return fmt.Sprintf(" [%s]", s) } return "" } // ShallowCopy returns a shallow copy of the cache. slices are not copied, but referenced. func (rrCache *RRCache) ShallowCopy() *RRCache { return &RRCache{ Domain: rrCache.Domain, Question: rrCache.Question, RCode: rrCache.RCode, Answer: rrCache.Answer, Ns: rrCache.Ns, Extra: rrCache.Extra, Expires: rrCache.Expires, Resolver: rrCache.Resolver, ServedFromCache: rrCache.ServedFromCache, RequestingNew: rrCache.RequestingNew, IsBackup: rrCache.IsBackup, Filtered: rrCache.Filtered, FilteredEntries: rrCache.FilteredEntries, Modified: rrCache.Modified, } } // ReplaceAnswerNames is a helper function that replaces all answer names, that // match the query domain, with another value. This is used to support handling // non-standard query names, which are resolved normalized, but have to be // reverted back for the origin non-standard query name in order for the // clients to recognize the response. func (rrCache *RRCache) ReplaceAnswerNames(fqdn string) { for _, answer := range rrCache.Answer { if answer.Header().Name == rrCache.Domain { answer.Header().Name = fqdn } } } // ReplyWithDNS creates a new reply to the given query with the data from the // RRCache, and additional informational records. func (rrCache *RRCache) ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns.Msg { // reply to query reply := new(dns.Msg) reply.SetRcode(request, rrCache.RCode) reply.Answer = rrCache.Answer reply.Ns = rrCache.Ns reply.Extra = rrCache.Extra return reply } // GetExtraRRs returns a slice of RRs with additional informational records. func (rrCache *RRCache) GetExtraRRs(ctx context.Context, query *dns.Msg) (extra []dns.RR) { // Add cache status and source of data. if rrCache.ServedFromCache { extra = addExtra(ctx, extra, "served from cache, resolved by "+rrCache.Resolver.DescriptiveName()) } else { extra = addExtra(ctx, extra, "freshly resolved by "+rrCache.Resolver.DescriptiveName()) } // Add expiry and cache information. switch { case rrCache.Expires == 0: extra = addExtra(ctx, extra, "record does not expire") case rrCache.Expired(): extra = addExtra(ctx, extra, fmt.Sprintf("record expired since %s", time.Since(time.Unix(rrCache.Expires, 0)).Round(time.Second))) default: extra = addExtra(ctx, extra, fmt.Sprintf("record valid for %s", time.Until(time.Unix(rrCache.Expires, 0)).Round(time.Second))) } if rrCache.RequestingNew { extra = addExtra(ctx, extra, "async request to refresh the cache has been started") } if rrCache.IsBackup { extra = addExtra(ctx, extra, "this record is served because a fresh request was unsuccessful") } // Add information about filtered entries. if rrCache.Filtered { if len(rrCache.FilteredEntries) > 1 { extra = addExtra(ctx, extra, fmt.Sprintf("%d RRs have been filtered:", len(rrCache.FilteredEntries))) } else { extra = addExtra(ctx, extra, fmt.Sprintf("%d RR has been filtered:", len(rrCache.FilteredEntries))) } for _, filteredRecord := range rrCache.FilteredEntries { extra = addExtra(ctx, extra, filteredRecord) } } return extra } func addExtra(ctx context.Context, extra []dns.RR, msg string) []dns.RR { rr, err := nsutil.MakeMessageRecord(log.InfoLevel, msg) if err != nil { log.Tracer(ctx).Warningf("resolver: failed to add informational record to reply: %s", err) return extra } extra = append(extra, rr) return extra } ================================================ FILE: service/resolver/rrcache_test.go ================================================ package resolver import ( "testing" "github.com/miekg/dns" ) func TestCaching(t *testing.T) { t.Parallel() testDomain := "Mk35mMqOWEHXSMk11MYcbjLOjTE8PQvDiAVUxf4BvwtgR.example.com." testQuestion := "A" testNameRecord := &NameRecord{ Domain: testDomain, Question: testQuestion, Resolver: &ResolverInfo{ Type: "dns", }, } err := testNameRecord.Save() if err != nil { t.Fatal(err) } rrCache, err := GetRRCache(testDomain, dns.Type(dns.TypeA)) if err != nil { t.Fatal(err) } err = rrCache.Save() if err != nil { t.Fatal(err) } rrCache2, err := GetRRCache(testDomain, dns.Type(dns.TypeA)) if err != nil { t.Fatal(err) } if rrCache2.Domain != rrCache.Domain { t.Fatal("something very is wrong") } } ================================================ FILE: service/resolver/scopes.go ================================================ package resolver import ( "context" "errors" "strings" "github.com/miekg/dns" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/netenv" ) // Domain Scopes. var ( // Localhost Domain // Handling: Respond with 127.0.0.1 and ::1 to A and AAAA queries, respectively. // See RFC6761. localhostDomain = ".localhost." // Invalid Domain // Handling: Always respond with NXDOMAIN. // See RFC6761. invalidDomain = ".invalid." // Internal Special-Use Domain // Used by Portmaster for special addressing. internalSpecialUseDomains = []string{ "." + InternalSpecialUseDomain, } // Multicast DNS // Handling: Send to nameservers with matching search scope, then MDNS // See RFC6762. multicastDomains = []string{ ".local.", ".254.169.in-addr.arpa.", ".8.e.f.ip6.arpa.", ".9.e.f.ip6.arpa.", ".a.e.f.ip6.arpa.", ".b.e.f.ip6.arpa.", } // Special-Use Domain Names // Handling: Send to nameservers with matching search scope, then local and system assigned nameservers // IANA Ref: https://www.iana.org/assignments/special-use-domain-names specialUseDomains = []string{ // RFC8375: Designated for non-unique use in residential home networks. ".home.arpa.", // RFC6762 (Appendix G): Non-official, but officially listed, private use domains. ".intranet.", ".internal.", ".private.", ".corp.", ".home.", ".lan.", // RFC6761: IPv4 private-address reverse-mapping domains. ".10.in-addr.arpa.", ".16.172.in-addr.arpa.", ".17.172.in-addr.arpa.", ".18.172.in-addr.arpa.", ".19.172.in-addr.arpa.", ".20.172.in-addr.arpa.", ".21.172.in-addr.arpa.", ".22.172.in-addr.arpa.", ".23.172.in-addr.arpa.", ".24.172.in-addr.arpa.", ".25.172.in-addr.arpa.", ".26.172.in-addr.arpa.", ".27.172.in-addr.arpa.", ".28.172.in-addr.arpa.", ".29.172.in-addr.arpa.", ".30.172.in-addr.arpa.", ".31.172.in-addr.arpa.", ".168.192.in-addr.arpa.", // RFC4193: IPv6 private-address reverse-mapping domains. ".d.f.ip6.arpa.", ".c.f.ip6.arpa.", // RFC6761: Special use domains for documentation and testing. ".example.", ".example.com.", ".example.net.", ".example.org.", ".test.", } // Special-Service Domain Names // Handling: Send to nameservers with matching search scope, then local and system assigned nameservers. specialServiceDomains = []string{ // RFC7686: Tor Hidden Services, https://www.torproject.org/ ".onion.", // I2P: Fully encrypted private network layer, https://geti2p.net/ ".i2p.", // Lokinet: Internal services on the decentralised network, https://lokinet.org/ ".loki.", // Namecoin: Blockchain based nameservice, https://www.namecoin.org/ ".bit.", // Ethereum Name Service (ENS): naming system based on the Ethereum blockchain, https://ens.domains/ ".eth.", // Unstoppable Domains: NFT based domain names, https://unstoppabledomains.com/ ".888.", ".bitcoin.", ".coin.", ".crypto.", ".dao.", ".nft.", ".wallet.", ".x.", ".zil.", // EmerDNS: Domain name registration on EmerCoin, https://emercoin.com/en/emerdns/ ".bazar.", ".coin.", ".emc.", ".lib.", // OpenNIC TLDs: Democratic alternative to ICANN, https://www.opennic.org/ ".bbs.", ".chan.", ".dyn.", ".free.", ".fur.", ".geek.", ".glue.", ".gopher.", ".indy.", ".libre.", ".neo.", ".null.", ".o.", ".oss.", ".oz.", ".parody.", ".pirate.", // NewNations: TLDs for countries/regions without a ccTLD, http://new-nations.net/ ".ku.", ".te.", ".ti.", ".uu.", } ) func domainInScope(dotPrefixedFQDN string, scopeList []string) bool { for _, scope := range scopeList { if strings.HasSuffix(dotPrefixedFQDN, scope) { return true } } return false } // GetResolversInScope returns all resolvers that are in scope the resolve the given query and options. func GetResolversInScope(ctx context.Context, q *Query) (selected []*Resolver, primarySource string, tryAll bool) { //nolint:gocognit // TODO resolversLock.RLock() defer resolversLock.RUnlock() // Internal use domains if domainInScope(q.dotPrefixedFQDN, internalSpecialUseDomains) { return envResolvers, ServerSourceEnv, false } // Special connectivity domains if netenv.IsConnectivityDomain(q.FQDN) && len(systemResolvers) > 0 { selected = addResolvers(ctx, q, selected, systemResolvers) if len(selected) == 0 { selected = addResolvers(ctx, q, selected, localResolvers) selected = addResolvers(ctx, q, selected, globalResolvers) } return selected, ServerSourceOperatingSystem, false } // Prioritize search scopes for _, scope := range localScopes { if strings.HasSuffix(q.dotPrefixedFQDN, scope.Domain) { selected = addResolvers(ctx, q, selected, scope.Resolvers) } } // Handle multicast domains if domainInScope(q.dotPrefixedFQDN, multicastDomains) { selected = addResolvers(ctx, q, selected, mDNSResolvers) selected = addResolvers(ctx, q, selected, localResolvers) selected = addResolvers(ctx, q, selected, systemResolvers) return selected, ServerSourceMDNS, true } // Special use domains if domainInScope(q.dotPrefixedFQDN, specialUseDomains) || domainInScope(q.dotPrefixedFQDN, specialServiceDomains) { selected = addResolvers(ctx, q, selected, localResolvers) return selected, "special", true } // Global domains selected = addResolvers(ctx, q, selected, globalResolvers) return selected, ServerSourceConfigured, false } func addResolvers(ctx context.Context, q *Query, selected []*Resolver, addResolvers []*Resolver) []*Resolver { addNextResolver: for _, resolver := range addResolvers { // check for compliance if err := resolver.checkCompliance(ctx, q); err != nil { log.Tracer(ctx).Tracef("skipping non-compliant resolver %s: %s", resolver.Info.DescriptiveName(), err) continue } // deduplicate for _, selectedResolver := range selected { if selectedResolver.Info.ID() == resolver.Info.ID() { continue addNextResolver } } // the domains from the configured resolvers should not be resolved with the same resolvers if resolver.Info.Source == ServerSourceConfigured && resolver.Info.IP == nil { if _, ok := resolverInitDomains[q.FQDN]; ok { continue addNextResolver } } // add compliant and unique resolvers to selected resolvers selected = append(selected, resolver) } return selected } var ( errInsecureProtocol = errors.New("insecure protocols disabled") errAssignedServer = errors.New("assigned (dhcp) nameservers disabled") errMulticastDNS = errors.New("multicast DNS disabled") errOutOfScope = errors.New("query out of scope for resolver") ) func (q *Query) checkCompliance() error { // RFC6761 - always respond with nxdomain if strings.HasSuffix(q.dotPrefixedFQDN, invalidDomain) { return ErrNotFound } // RFC6761 - respond with 127.0.0.1 and ::1 to A and AAAA queries respectively, else nxdomain if strings.HasSuffix(q.dotPrefixedFQDN, localhostDomain) { switch uint16(q.QType) { case dns.TypeA, dns.TypeAAAA: return ErrLocalhost default: return ErrNotFound } } // special TLDs if dontResolveSpecialDomains() && domainInScope(q.dotPrefixedFQDN, specialServiceDomains) { return ErrSpecialDomainsDisabled } return nil } func (resolver *Resolver) checkCompliance(_ context.Context, q *Query) error { if noInsecureProtocols() { switch resolver.Info.Type { case ServerTypeDNS: return errInsecureProtocol case ServerTypeTCP: return errInsecureProtocol case ServerTypeDoT: // compliant case ServerTypeDoH: // compliant case ServerTypeEnv: // compliant (data is sourced from local network only and is highly limited) default: return errInsecureProtocol } } if noAssignedNameservers() { if resolver.Info.Source == ServerSourceOperatingSystem { return errAssignedServer } } if noMulticastDNS() { if resolver.Info.Source == ServerSourceMDNS { return errMulticastDNS } } // Check if the resolver should only be used for the search scopes. if resolver.SearchOnly && !domainInScope(q.dotPrefixedFQDN, resolver.Search) { return errOutOfScope } return nil } ================================================ FILE: service/resolver/test/resolving.bash ================================================ #!/bin/bash DOMAINS="twitter.com news.ycombinator.com" while true; do for domain in $DOMAINS; do # query domain Q=$(dig $domain | tr '\n' '§') # check result if [[ $(echo $Q | grep NOERROR | wc -l) -gt 0 ]]; then echo "$(date "+%y%m%d %H:%M:%S") [OK] $domain ($(echo $Q | grep -aoE 'valid for [a-z0-9]+'))" else echo "" echo "$(date "+%y%m%d %H:%M:%S") [FAILED] $domain" echo $Q | tr '§' '\n' echo "#####" echo "" fi # wait sleep 5 done done ================================================ FILE: service/status/module.go ================================================ package status import ( "errors" "fmt" "sync" "sync/atomic" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/base/runtime" "github.com/safing/portmaster/base/utils/debug" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" ) // Status Module manages status information. type Status struct { mgr *mgr.Manager instance instance publishUpdate runtime.PushFunc triggerUpdate chan struct{} states map[string]mgr.StateUpdate statesLock sync.Mutex notifications map[string]map[string]*notifications.Notification notificationsLock sync.Mutex } // Manager returns the module manager. func (s *Status) Manager() *mgr.Manager { return s.mgr } // Start starts the module. func (s *Status) Start() error { s.mgr.Go("status publisher", s.statusPublisher) s.instance.NetEnv().EventOnlineStatusChange.AddCallback("update online status in system status", func(_ *mgr.WorkerCtx, _ netenv.OnlineStatus) (bool, error) { s.triggerPublishStatus() return false, nil }, ) // Make an initial status query. s.statesLock.Lock() defer s.statesLock.Unlock() // Add status callback within the lock so we can force the right order. s.instance.AddStatesCallback("status update", s.handleModuleStatusUpdate) // Get initial states. for _, stateUpdate := range s.instance.GetStates() { s.states[stateUpdate.Module] = stateUpdate s.deriveNotificationsFromStateUpdate(stateUpdate) } return nil } // Stop stops the module. func (s *Status) Stop() error { return nil } func (s *Status) prep() error { // register status provider as soon as possible if err := s.setupRuntimeProvider(); err != nil { return err } return nil } // AddToDebugInfo adds the system status to the given debug.Info. func AddToDebugInfo(di *debug.Info) { di.AddSection( fmt.Sprintf("Status: %s", netenv.GetOnlineStatus()), debug.UseCodeSection|debug.AddContentLineBreaks, fmt.Sprintf("OnlineStatus: %s", netenv.GetOnlineStatus()), "CaptivePortal: "+netenv.GetCaptivePortal().URL, ) } var ( module *Status shimLoaded atomic.Bool ) // New returns a new status module. func New(instance instance) (*Status, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Status") module = &Status{ mgr: m, instance: instance, triggerUpdate: make(chan struct{}, 1), states: make(map[string]mgr.StateUpdate), notifications: make(map[string]map[string]*notifications.Notification), } if err := module.prep(); err != nil { return nil, err } return module, nil } type instance interface { NetEnv() *netenv.NetEnv GetStates() []mgr.StateUpdate AddStatesCallback(callbackName string, callback mgr.EventCallbackFunc[mgr.StateUpdate]) } ================================================ FILE: service/status/notifications.go ================================================ package status import ( "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/mgr" ) func (s *Status) deriveNotificationsFromStateUpdate(update mgr.StateUpdate) { s.notificationsLock.Lock() defer s.notificationsLock.Unlock() notifs := s.notifications[update.Module] if notifs == nil { notifs = make(map[string]*notifications.Notification) s.notifications[update.Module] = notifs } // Add notifications. seenStateIDs := make(map[string]struct{}, len(update.States)) for _, state := range update.States { seenStateIDs[state.ID] = struct{}{} // Check if we already have a notification registered. if _, ok := notifs[state.ID]; ok { continue } // Check if the notification was pre-created. // If a matching notification is found, assign it. n := notifications.Get(state.ID) if n != nil { notifs[state.ID] = n continue } // Create a new notification. n = ¬ifications.Notification{ EventID: state.ID, Title: state.Name, Message: state.Message, AvailableActions: []*notifications.Action{ { Text: "Get Help", Type: notifications.ActionTypeOpenURL, Payload: "https://safing.io/support/", }, }, } switch state.Type { case mgr.StateTypeWarning: n.Type = notifications.Warning n.ShowOnSystem = true case mgr.StateTypeError: n.Type = notifications.Error n.ShowOnSystem = true case mgr.StateTypeHint, mgr.StateTypeUndefined: fallthrough default: n.Type = notifications.Info n.AvailableActions = nil } notifs[state.ID] = n notifications.Notify(n) } // Remove notifications. for stateID, n := range notifs { if _, ok := seenStateIDs[stateID]; !ok { n.Delete() delete(notifs, stateID) } } } ================================================ FILE: service/status/security_level.go ================================================ package status import "github.com/safing/portmaster/base/config" // MigrateSecurityLevelToBoolean migrates a security level (int) option value to a boolean option value. func MigrateSecurityLevelToBoolean(option *config.Option, value any) any { // Check new (target) option type. if option.OptType != config.OptTypeBool { // This migration converts to boolean. // Thus, conversion is not applicable. return value } // Convert value to uint8. var nVal uint8 switch v := value.(type) { case int: nVal = uint8(v) case int8: nVal = uint8(v) case int16: nVal = uint8(v) case int32: nVal = uint8(v) case int64: nVal = uint8(v) case uint: nVal = uint8(v) case uint8: nVal = v case uint16: nVal = uint8(v) case uint32: nVal = uint8(v) case uint64: nVal = uint8(v) case float32: nVal = uint8(v) case float64: nVal = uint8(v) default: // Input type not compatible. return value } // Convert to boolean. return nVal&SecurityLevelNormal > 0 } // DisplayHintSecurityLevel is an external option hint for security levels. // It's meant to be used as a value for config.DisplayHintAnnotation. const DisplayHintSecurityLevel string = "security level" // Security levels. const ( SecurityLevelOff uint8 = 0 SecurityLevelNormal uint8 = 1 SecurityLevelHigh uint8 = 2 SecurityLevelExtreme uint8 = 4 ) ================================================ FILE: service/status/status.go ================================================ package status import ( "slices" "strings" "sync" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/runtime" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" ) // SystemStatusRecord describes the overall status of the Portmaster. // It's a read-only record exposed via runtime:system/status. type SystemStatusRecord struct { record.Base sync.Mutex // OnlineStatus holds the current online status as // seen by the netenv package. OnlineStatus netenv.OnlineStatus // CaptivePortal holds all information about the captive // portal of the network the portmaster is currently // connected to, if any. CaptivePortal *netenv.CaptivePortal Modules []mgr.StateUpdate WorstState struct { Module string mgr.State } } func (s *Status) handleModuleStatusUpdate(_ *mgr.WorkerCtx, update mgr.StateUpdate) (cancel bool, err error) { s.statesLock.Lock() defer s.statesLock.Unlock() s.states[update.Module] = update s.deriveNotificationsFromStateUpdate(update) s.triggerPublishStatus() return false, nil } func (s *Status) triggerPublishStatus() { select { case s.triggerUpdate <- struct{}{}: default: } } func (s *Status) statusPublisher(w *mgr.WorkerCtx) error { for { select { case <-w.Done(): return nil case <-s.triggerUpdate: s.publishSystemStatus() } } } func (s *Status) setupRuntimeProvider() (err error) { // register the system status getter statusProvider := runtime.SimpleValueGetterFunc(func(_ string) ([]record.Record, error) { return []record.Record{s.buildSystemStatus()}, nil }) s.publishUpdate, err = runtime.Register("system/status", statusProvider) if err != nil { return err } return nil } // buildSystemStatus build a new system status record. func (s *Status) buildSystemStatus() *SystemStatusRecord { s.statesLock.Lock() defer s.statesLock.Unlock() status := &SystemStatusRecord{ CaptivePortal: netenv.GetCaptivePortal(), OnlineStatus: netenv.GetOnlineStatus(), Modules: make([]mgr.StateUpdate, 0, len(s.states)), } for _, newStateUpdate := range s.states { // Deep copy state. newStateUpdate.States = append([]mgr.State(nil), newStateUpdate.States...) status.Modules = append(status.Modules, newStateUpdate) // Check if state is worst so far. for _, state := range newStateUpdate.States { if state.Type.Severity() > status.WorstState.Type.Severity() { s.mgr.Error("new worst state", "state", state) status.WorstState.State = state status.WorstState.Module = newStateUpdate.Module } } } slices.SortFunc(status.Modules, func(a, b mgr.StateUpdate) int { return strings.Compare(a.Module, b.Module) }) status.CreateMeta() status.SetKey("runtime:system/status") return status } // publishSystemStatus pushes a new system status via // the runtime database. func (s *Status) publishSystemStatus() { if s.publishUpdate == nil { return } record := s.buildSystemStatus() record.Lock() defer record.Unlock() s.publishUpdate(record) } ================================================ FILE: service/sync/module.go ================================================ package sync import ( "errors" "sync/atomic" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/service/mgr" ) type Sync struct { mgr *mgr.Manager instance instance } func (s *Sync) Manager() *mgr.Manager { return s.mgr } func (s *Sync) Start() error { return nil } func (s *Sync) Stop() error { return nil } var db = database.NewInterface(&database.Options{ Local: true, Internal: true, }) func prep() error { if err := registerSettingsAPI(); err != nil { return err } if err := registerSingleSettingAPI(); err != nil { return err } if err := registerProfileAPI(); err != nil { return err } return nil } var ( module *Sync shimLoaded atomic.Bool ) // New returns a new NetEnv module. func New(instance instance) (*Sync, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Sync") module = &Sync{ mgr: m, instance: instance, } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface{} ================================================ FILE: service/sync/profile.go ================================================ package sync import ( "encoding/json" "errors" "fmt" "net/http" "strings" "time" "github.com/vincent-petithory/dataurl" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/binmeta" ) // ProfileExport holds an export of a profile. type ProfileExport struct { //nolint:maligned Type Type `json:"type" yaml:"type"` // Identification ID string `json:"id,omitempty" yaml:"id,omitempty"` Source profile.ProfileSource `json:"source,omitempty" yaml:"source,omitempty"` // Human Metadata Name string `json:"name" yaml:"name"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Homepage string `json:"homepage,omitempty" yaml:"homepage,omitempty"` PresentationPath string `json:"presPath,omitempty" yaml:"presPath,omitempty"` UsePresentationPath bool `json:"usePresPath,omitempty" yaml:"usePresPath,omitempty"` IconData string `json:"iconData,omitempty" yaml:"iconData,omitempty"` // DataURL // Process matching Fingerprints []ProfileFingerprint `json:"fingerprints" yaml:"fingerprints"` // Settings Config map[string]any `json:"config,omitempty" yaml:"config,omitempty"` // Metadata LastEdited *time.Time `json:"lastEdited,omitempty" yaml:"lastEdited,omitempty"` Created *time.Time `json:"created,omitempty" yaml:"created,omitempty"` Internal bool `json:"internal,omitempty" yaml:"internal,omitempty"` } // ProfileIcon represents a profile icon only. type ProfileIcon struct { IconData string `json:"iconData,omitempty" yaml:"iconData,omitempty"` // DataURL } // ProfileFingerprint represents a profile fingerprint. type ProfileFingerprint struct { Type string `json:"type" yaml:"type"` Key string `json:"key,omitempty" yaml:"key,omitempty"` Operation string `json:"operation" yaml:"operation"` Value string `json:"value" yaml:"value"` MergedFrom string `json:"mergedFrom,omitempty" yaml:"mergedFrom,omitempty"` } // ProfileExportRequest is a request for a profile export. type ProfileExportRequest struct { ID string `json:"id"` } // ProfileImportRequest is a request to import Profile. type ProfileImportRequest struct { ImportRequest `json:",inline"` // AllowUnknown allows the import of unknown settings. // Otherwise, attempting to import an unknown setting will result in an error. AllowUnknown bool `json:"allowUnknown"` // AllowReplace allows the import to replace other existing profiles. AllowReplace bool `json:"allowReplaceProfiles"` Export *ProfileExport `json:"export"` } // ProfileImportResult is returned by successful import operations. type ProfileImportResult struct { ImportResult `json:",inline"` ReplacesProfiles []string `json:"replacesProfiles"` } func registerProfileAPI() error { if err := api.RegisterEndpoint(api.Endpoint{ Name: "Export App Profile", Description: "Exports app fingerprints, settings and metadata in a share-able format.", Path: "sync/profile/export", Read: api.PermitAdmin, Write: api.PermitAdmin, Parameters: []api.Parameter{{ Method: http.MethodGet, Field: "id", Description: "Specify scoped profile ID to export.", }}, DataFunc: handleExportProfile, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Import App Profile", Description: "Imports full app profiles, including fingerprints, setting and metadata from the share-able format.", Path: "sync/profile/import", Read: api.PermitAdmin, Write: api.PermitAdmin, Parameters: []api.Parameter{ { Method: http.MethodPost, Field: "allowReplace", Description: "Allow replacing existing profiles.", }, { Method: http.MethodPost, Field: "validate", Description: "Validate only.", }, { Method: http.MethodPost, Field: "reset", Description: "Replace all existing settings.", }, { Method: http.MethodPost, Field: "allowUnknown", Description: "Allow importing of unknown values.", }, }, StructFunc: handleImportProfile, }); err != nil { return err } return nil } func handleExportProfile(ar *api.Request) (data []byte, err error) { var request *ProfileExportRequest // Get parameters. q := ar.URL.Query() if len(q) > 0 { request = &ProfileExportRequest{ ID: q.Get("id"), } } else { request = &ProfileExportRequest{} if err := json.Unmarshal(ar.InputData, request); err != nil { return nil, fmt.Errorf("%w: failed to parse export request: %w", ErrExportFailed, err) } } // Check parameters. if request.ID == "" { return nil, errors.New("missing parameters") } // Export. export, err := ExportProfile(request.ID) if err != nil { return nil, err } return serializeProfileExport(export, ar) } func handleImportProfile(ar *api.Request) (any, error) { var request ProfileImportRequest // Get parameters. q := ar.URL.Query() if len(q) > 0 { request = ProfileImportRequest{ ImportRequest: ImportRequest{ ValidateOnly: q.Has("validate"), RawExport: string(ar.InputData), RawMime: ar.Header.Get("Content-Type"), }, AllowUnknown: q.Has("allowUnknown"), AllowReplace: q.Has("allowReplace"), } } else { if err := json.Unmarshal(ar.InputData, &request); err != nil { return nil, fmt.Errorf("%w: failed to parse import request: %w", ErrInvalidImportRequest, err) } } // Check if we need to parse the export. switch { case request.Export != nil && request.RawExport != "": return nil, fmt.Errorf("%w: both Export and RawExport are defined", ErrInvalidImportRequest) case request.RawExport != "": // Parse export. export := &ProfileExport{} if err := parseExport(&request.ImportRequest, export); err != nil { return nil, err } request.Export = export case request.Export != nil: // Export is aleady parsed. default: return nil, ErrInvalidImportRequest } // Import. return ImportProfile(&request, profile.SourceLocal) } // ExportProfile exports a profile. func ExportProfile(scopedID string) (*ProfileExport, error) { // Get Profile. r, err := db.Get(profile.ProfilesDBPath + scopedID) if err != nil { return nil, fmt.Errorf("%w: failed to find profile: %w", ErrTargetNotFound, err) } p, err := profile.EnsureProfile(r) if err != nil { return nil, fmt.Errorf("%w: failed to load profile: %w", ErrExportFailed, err) } // Copy exportable profile data. export := &ProfileExport{ Type: TypeProfile, // Identification ID: p.ID, Source: p.Source, // Human Metadata Name: p.Name, Description: p.Description, Homepage: p.Homepage, PresentationPath: p.PresentationPath, UsePresentationPath: p.UsePresentationPath, // Process matching Fingerprints: convertFingerprintsToExport(p.Fingerprints), // Settings Config: p.Config, // Metadata Internal: p.Internal, } // Add optional timestamps. if p.LastEdited > 0 { lastEdited := time.Unix(p.LastEdited, 0) export.LastEdited = &lastEdited } if p.Created > 0 { created := time.Unix(p.Created, 0) export.Created = &created } // Derive ID to ensure the ID is always correct. export.ID = profile.DeriveProfileID(p.Fingerprints) // Add first exportable icon to export. if len(p.Icons) > 0 { var err error for _, icon := range p.Icons { var iconDataURL string iconDataURL, err = icon.GetIconAsDataURL() if err == nil { export.IconData = iconDataURL break } } if err != nil { return nil, fmt.Errorf("%w: failed to export icon: %w", ErrExportFailed, err) } } // Remove presentation path if both Name and Icon are set. if export.Name != "" && export.IconData != "" { p.UsePresentationPath = false } if !p.UsePresentationPath { p.PresentationPath = "" } return export, nil } // ImportProfile imports a profile. func ImportProfile(r *ProfileImportRequest, requiredProfileSource profile.ProfileSource) (*ProfileImportResult, error) { // Check import. if r.Export.Type != TypeProfile { return nil, ErrMismatch } // Check Source. if r.Export.Source != "" && r.Export.Source != requiredProfileSource { return nil, ErrMismatch } // Convert fingerprints to internal representation. fingerprints := convertFingerprintsToInternal(r.Export.Fingerprints) if len(fingerprints) == 0 { return nil, fmt.Errorf("%w: the export contains no fingerprints", ErrInvalidProfileData) } // Derive ID from fingerprints. profileID := profile.DeriveProfileID(fingerprints) if r.Export.ID != "" && r.Export.ID != profileID { return nil, fmt.Errorf("%w: the export profile ID does not match the fingerprints, remove to ignore", ErrInvalidProfileData) } r.Export.ID = profileID // Check Fingerprints. _, err := profile.ParseFingerprints(fingerprints, "") if err != nil { return nil, fmt.Errorf("%w: the export contains invalid fingerprints: %w", ErrInvalidProfileData, err) } // Flatten config. settings := config.Flatten(r.Export.Config) // Check settings. settingsResult, globalOnlySettingFound, err := checkSettings(settings) if err != nil { return nil, err } if settingsResult.ContainsUnknown && !r.AllowUnknown && !r.ValidateOnly { return nil, fmt.Errorf("%w: the export contains unknown settings", ErrInvalidImportRequest) } // Check if a setting is settable per app. if globalOnlySettingFound { return nil, fmt.Errorf("%w: export contains settings that cannot be set per app", ErrNotSettablePerApp) } // Create result based on settings result. result := &ProfileImportResult{ ImportResult: *settingsResult, } // Check if the profile already exists. exists, err := db.Exists(profile.MakeProfileKey(r.Export.Source, r.Export.ID)) if err != nil { return nil, fmt.Errorf("internal import error: %w", err) } if exists { result.ReplacesExisting = true } // Check if import will delete any profiles. requiredSourcePrefix := string(r.Export.Source) + "/" result.ReplacesProfiles = make([]string, 0, len(r.Export.Fingerprints)) for _, fp := range r.Export.Fingerprints { if fp.MergedFrom != "" { if !strings.HasPrefix(fp.MergedFrom, requiredSourcePrefix) { return nil, fmt.Errorf("%w: exported profile was merged from different profile source", ErrInvalidImportRequest) } exists, err := db.Exists(profile.ProfilesDBPath + fp.MergedFrom) if err != nil { return nil, fmt.Errorf("internal import error: %w", err) } if exists { result.ReplacesProfiles = append(result.ReplacesProfiles, fp.MergedFrom) } } } // Stop here if we are only validating. if r.ValidateOnly { return result, nil } if result.ReplacesExisting && !r.AllowReplace { return nil, fmt.Errorf("%w: import would replace existing profile", ErrImportFailed) } // Create profile from export. // Note: Don't use profile.New(), as this will not trigger a profile refresh if active. in := r.Export p := &profile.Profile{ // Identification ID: in.ID, Source: requiredProfileSource, // Human Metadata Name: in.Name, Description: in.Description, Homepage: in.Homepage, PresentationPath: in.PresentationPath, UsePresentationPath: in.UsePresentationPath, // Process matching Fingerprints: fingerprints, // Settings Config: in.Config, // Metadata Internal: in.Internal, } // Add optional timestamps. if in.LastEdited != nil { p.LastEdited = in.LastEdited.Unix() } if in.Created != nil { p.Created = in.Created.Unix() } // Fill in required values. if p.Config == nil { p.Config = make(map[string]any) } if p.Created == 0 { p.Created = time.Now().Unix() } // Add icon to profile, if set. if in.IconData != "" { du, err := dataurl.DecodeString(in.IconData) if err != nil { return nil, fmt.Errorf("%w: icon data is invalid: %w", ErrImportFailed, err) } filename, err := binmeta.UpdateProfileIcon(du.Data, du.MediaType.Subtype) if err != nil { return nil, fmt.Errorf("%w: icon is invalid: %w", ErrImportFailed, err) } p.Icons = []binmeta.Icon{{ Type: binmeta.IconTypeAPI, Value: filename, Source: binmeta.IconSourceImport, }} } // Save profile to db. p.SetKey(profile.MakeProfileKey(p.Source, p.ID)) err = p.Save() if err != nil { return nil, fmt.Errorf("%w: failed to save profile: %w", ErrImportFailed, err) } // Delete profiles that were merged into the imported profile. for _, profileID := range result.ReplacesProfiles { err := db.Delete(profile.ProfilesDBPath + profileID) if err != nil { log.Errorf("sync: failed to delete merged profile %s on import: %s", profileID, err) } } return result, nil } func convertFingerprintsToExport(fingerprints []profile.Fingerprint) []ProfileFingerprint { converted := make([]ProfileFingerprint, 0, len(fingerprints)) for _, fp := range fingerprints { converted = append(converted, ProfileFingerprint{ Type: fp.Type, Key: fp.Key, Operation: fp.Operation, Value: fp.Value, MergedFrom: fp.MergedFrom, }) } return converted } func convertFingerprintsToInternal(fingerprints []ProfileFingerprint) []profile.Fingerprint { converted := make([]profile.Fingerprint, 0, len(fingerprints)) for _, fp := range fingerprints { converted = append(converted, profile.Fingerprint{ Type: fp.Type, Key: fp.Key, Operation: fp.Operation, Value: fp.Value, MergedFrom: fp.MergedFrom, }) } return converted } ================================================ FILE: service/sync/setting_single.go ================================================ package sync import ( "encoding/json" "errors" "fmt" "net/http" "time" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/profile" "github.com/safing/structures/dsd" ) // SingleSettingExport holds an export of a single setting. type SingleSettingExport struct { Type Type `json:"type" yaml:"type"` // Must be TypeSingleSetting ID string `json:"id" yaml:"id"` // Settings Key Value any `json:"value" yaml:"value"` } // SingleSettingImportRequest is a request to import a single setting. type SingleSettingImportRequest struct { ImportRequest `json:",inline"` Export *SingleSettingExport `json:"export"` } func registerSingleSettingAPI() error { if err := api.RegisterEndpoint(api.Endpoint{ Name: "Export Single Setting", Description: "Exports a single setting in a share-able format.", Path: "sync/single-setting/export", Read: api.PermitAdmin, Write: api.PermitAdmin, Parameters: []api.Parameter{{ Method: http.MethodGet, Field: "from", Description: "Specify where to export from.", }, { Method: http.MethodGet, Field: "key", Description: "Specify which settings key to export.", }}, DataFunc: handleExportSingleSetting, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Import Single Setting", Description: "Imports a single setting from the share-able format.", Path: "sync/single-setting/import", Read: api.PermitAdmin, Write: api.PermitAdmin, Parameters: []api.Parameter{{ Method: http.MethodPost, Field: "to", Description: "Specify where to import to.", }, { Method: http.MethodPost, Field: "key", Description: "Specify which setting key to import.", }, { Method: http.MethodPost, Field: "validate", Description: "Validate only.", }}, StructFunc: handleImportSingleSetting, }); err != nil { return err } return nil } func handleExportSingleSetting(ar *api.Request) (data []byte, err error) { var request *ExportRequest // Get parameters. q := ar.URL.Query() if len(q) > 0 { request = &ExportRequest{ From: q.Get("from"), Keys: q["key"], // Get []string by direct map access. } } else { request = &ExportRequest{} if err := json.Unmarshal(ar.InputData, request); err != nil { return nil, fmt.Errorf("%w: failed to parse export request: %w", ErrExportFailed, err) } } // Check parameters. if request.From == "" || len(request.Keys) != 1 { return nil, errors.New("missing or malformed parameters") } // Export. export, err := ExportSingleSetting(request.Keys[0], request.From) if err != nil { return nil, err } return serializeExport(export, ar) } func handleImportSingleSetting(ar *api.Request) (any, error) { var request *SingleSettingImportRequest // Get parameters. q := ar.URL.Query() if len(q) > 0 { request = &SingleSettingImportRequest{ ImportRequest: ImportRequest{ Target: q.Get("to"), ValidateOnly: q.Has("validate"), RawExport: string(ar.InputData), RawMime: ar.Header.Get("Content-Type"), }, } } else { request = &SingleSettingImportRequest{} if _, err := dsd.MimeLoad(ar.InputData, ar.Header.Get("Accept"), request); err != nil { return nil, fmt.Errorf("%w: failed to parse import request: %w", ErrInvalidImportRequest, err) } } // Check if we need to parse the export. switch { case request.Export != nil && request.RawExport != "": return nil, fmt.Errorf("%w: both Export and RawExport are defined", ErrInvalidImportRequest) case request.RawExport != "": // Parse export. export := &SingleSettingExport{} if err := parseExport(&request.ImportRequest, export); err != nil { return nil, err } request.Export = export case request.Export != nil: // Export is aleady parsed. default: return nil, ErrInvalidImportRequest } // Optional check if the setting key matches. if len(q) > 0 && q.Has("key") && q.Get("key") != request.Export.ID { return nil, ErrMismatch } // Import. return ImportSingeSetting(request) } // ExportSingleSetting export a single setting. func ExportSingleSetting(key, from string) (*SingleSettingExport, error) { option, err := config.GetOption(key) if err != nil { return nil, fmt.Errorf("%w: configuration %w", ErrSettingNotFound, err) } var value any if from == ExportTargetGlobal { value = option.UserValue() if value == nil { return nil, ErrUnchanged } } else { // Check if the setting is settable per app. if !option.AnnotationEquals(config.SettablePerAppAnnotation, true) { return nil, ErrNotSettablePerApp } // Get and load profile. r, err := db.Get(profile.ProfilesDBPath + from) if err != nil { return nil, fmt.Errorf("%w: failed to find profile: %w", ErrTargetNotFound, err) } p, err := profile.EnsureProfile(r) if err != nil { return nil, fmt.Errorf("%w: failed to load profile: %w", ErrExportFailed, err) } // Flatten config and get key we are looking for. flattened := config.Flatten(p.Config) value = flattened[key] if value == nil { return nil, ErrUnchanged } } return &SingleSettingExport{ Type: TypeSingleSetting, ID: key, Value: value, }, nil } // ImportSingeSetting imports a single setting. func ImportSingeSetting(r *SingleSettingImportRequest) (*ImportResult, error) { // Check import. if r.Export.Type != TypeSingleSetting { return nil, ErrMismatch } // Get option and validate value. option, err := config.GetOption(r.Export.ID) if err != nil { return nil, fmt.Errorf("%w: configuration %w", ErrSettingNotFound, err) } if err := option.ValidateValue(r.Export.Value); err != nil { return nil, fmt.Errorf("%w: %w", ErrInvalidSettingValue, err) } // Import single global setting. if r.Target == ExportTargetGlobal { // Stop here if we are only validating. if r.ValidateOnly { return &ImportResult{ RestartRequired: option.RequiresRestart, ReplacesExisting: option.IsSetByUser(), }, nil } // Actually import the setting. if err := config.SetConfigOption(r.Export.ID, r.Export.Value); err != nil { return nil, fmt.Errorf("%w: %w", ErrInvalidSettingValue, err) } } else { // Check if the setting is settable per app. if !option.AnnotationEquals(config.SettablePerAppAnnotation, true) { return nil, ErrNotSettablePerApp } // Import single setting into profile. rec, err := db.Get(profile.ProfilesDBPath + r.Target) if err != nil { return nil, fmt.Errorf("%w: failed to find profile: %w", ErrTargetNotFound, err) } p, err := profile.EnsureProfile(rec) if err != nil { return nil, fmt.Errorf("%w: failed to load profile: %w", ErrImportFailed, err) } // Stop here if we are only validating. if r.ValidateOnly { return &ImportResult{ RestartRequired: option.RequiresRestart, ReplacesExisting: option.IsSetByUser(), }, nil } // Set imported setting on profile. config.PutValueIntoHierarchicalConfig(p.Config, r.Export.ID, r.Export.Value) // Mark profile as edited by user. p.LastEdited = time.Now().Unix() // Save profile back to db. if err := p.Save(); err != nil { return nil, fmt.Errorf("%w: failed to save profile: %w", ErrImportFailed, err) } } return &ImportResult{ RestartRequired: option.RequiresRestart, ReplacesExisting: option.IsSetByUser(), }, nil } ================================================ FILE: service/sync/settings.go ================================================ package sync import ( "encoding/json" "errors" "fmt" "net/http" "strings" "time" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/profile" ) // SettingsExport holds an export of settings. type SettingsExport struct { Type Type `json:"type" yaml:"type"` Config map[string]any `json:"config" yaml:"config"` } // SettingsImportRequest is a request to import settings. type SettingsImportRequest struct { ImportRequest `json:",inline" yaml:",inline"` // Reset all settings of target before import. // The ImportResult also reacts to this flag and correctly reports whether // any settings would be replaced or deleted. Reset bool `json:"reset" yaml:"reset"` // AllowUnknown allows the import of unknown settings. // Otherwise, attempting to import an unknown setting will result in an error. AllowUnknown bool `json:"allowUnknown" yaml:"allowUnknown"` Export *SettingsExport `json:"export" yaml:"export"` } func registerSettingsAPI() error { if err := api.RegisterEndpoint(api.Endpoint{ Name: "Export Settings", Description: "Exports settings in a share-able format.", Path: "sync/settings/export", Read: api.PermitAdmin, Write: api.PermitAdmin, Parameters: []api.Parameter{{ Method: http.MethodGet, Field: "from", Description: "Specify where to export from.", }, { Method: http.MethodGet, Field: "key", Description: "Optionally select a single setting to export. Repeat to export selection.", }}, DataFunc: handleExportSettings, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Name: "Import Settings", Description: "Imports settings from the share-able format.", Path: "sync/settings/import", Read: api.PermitAdmin, Write: api.PermitAdmin, Parameters: []api.Parameter{{ Method: http.MethodPost, Field: "to", Description: "Specify where to import to.", }, { Method: http.MethodPost, Field: "validate", Description: "Validate only.", }, { Method: http.MethodPost, Field: "reset", Description: "Replace all existing settings.", }, { Method: http.MethodPost, Field: "allowUnknown", Description: "Allow importing of unknown values.", }}, StructFunc: handleImportSettings, }); err != nil { return err } return nil } func handleExportSettings(ar *api.Request) (data []byte, err error) { var request *ExportRequest // Get parameters. q := ar.URL.Query() if len(q) > 0 { request = &ExportRequest{ From: q.Get("from"), Keys: q["key"], // Get []string by direct map access. } } else { request = &ExportRequest{} if err := json.Unmarshal(ar.InputData, request); err != nil { return nil, fmt.Errorf("%w: failed to parse export request: %w", ErrExportFailed, err) } } // Check parameters. if request.From == "" { return nil, errors.New("missing parameters") } // Export. export, err := ExportSettings(request.From, request.Keys) if err != nil { return nil, err } return serializeExport(export, ar) } func handleImportSettings(ar *api.Request) (any, error) { var request *SettingsImportRequest // Get parameters. q := ar.URL.Query() if len(q) > 0 { request = &SettingsImportRequest{ ImportRequest: ImportRequest{ Target: q.Get("to"), ValidateOnly: q.Has("validate"), RawExport: string(ar.InputData), RawMime: ar.Header.Get("Content-Type"), }, Reset: q.Has("reset"), AllowUnknown: q.Has("allowUnknown"), } } else { request = &SettingsImportRequest{} if err := json.Unmarshal(ar.InputData, request); err != nil { return nil, fmt.Errorf("%w: failed to parse import request: %w", ErrInvalidImportRequest, err) } } // Check if we need to parse the export. switch { case request.Export != nil && request.RawExport != "": return nil, fmt.Errorf("%w: both Export and RawExport are defined", ErrInvalidImportRequest) case request.RawExport != "": // Parse export. export := &SettingsExport{} if err := parseExport(&request.ImportRequest, export); err != nil { return nil, err } request.Export = export case request.Export != nil: // Export is already parsed. default: return nil, ErrInvalidImportRequest } // Import. return ImportSettings(request) } // ExportSettings exports the global settings. func ExportSettings(from string, keys []string) (*SettingsExport, error) { var settings map[string]any if from == ExportTargetGlobal { // Collect all changed global settings. settings = make(map[string]any) _ = config.ForEachOption(func(option *config.Option) error { v := option.UserValue() if v != nil { settings[option.Key] = v } return nil }) } else { r, err := db.Get(profile.ProfilesDBPath + from) if err != nil { return nil, fmt.Errorf("%w: failed to find profile: %w", ErrTargetNotFound, err) } p, err := profile.EnsureProfile(r) if err != nil { return nil, fmt.Errorf("%w: failed to load profile: %w", ErrExportFailed, err) } settings = config.Flatten(p.Config) } // Only extract some setting keys, if wanted. if len(keys) > 0 { selection := make(map[string]any, len(keys)) for _, key := range keys { if v, ok := settings[key]; ok { selection[key] = v } } settings = selection } // Check if there any changed settings. if len(settings) == 0 { return nil, ErrUnchanged } // Expand config to hierarchical form. settings = config.Expand(settings) return &SettingsExport{ Type: TypeSettings, Config: settings, }, nil } // ImportSettings imports the global settings. func ImportSettings(r *SettingsImportRequest) (*ImportResult, error) { // Check import. if r.Export.Type != TypeSettings { return nil, ErrMismatch } // Flatten config. settings := config.Flatten(r.Export.Config) // Check settings. result, globalOnlySettingFound, err := checkSettings(settings) if err != nil { return nil, err } if result.ContainsUnknown && !r.AllowUnknown && !r.ValidateOnly { return nil, fmt.Errorf("%w: the export contains unknown settings", ErrInvalidImportRequest) } // Import global settings. if r.Target == ExportTargetGlobal { // Stop here if we are only validating. if r.ValidateOnly { return result, nil } // Import to global config. vErrs, restartRequired := config.ReplaceConfig(settings) if len(vErrs) > 0 { s := make([]string, 0, len(vErrs)) for _, err := range vErrs { s = append(s, err.Error()) } return nil, fmt.Errorf( "%w: the supplied configuration could not be applied:\n%s", ErrImportFailed, strings.Join(s, "\n"), ) } // Save new config to disk. err := config.SaveConfig() if err != nil { return nil, fmt.Errorf("failed to save config: %w", err) } result.RestartRequired = restartRequired return result, nil } // Check if a setting is settable per app. if globalOnlySettingFound { return nil, fmt.Errorf("%w: export contains settings that cannot be set per app", ErrNotSettablePerApp) } // Get and load profile. rec, err := db.Get(profile.ProfilesDBPath + r.Target) if err != nil { return nil, fmt.Errorf("%w: failed to find profile: %w", ErrTargetNotFound, err) } p, err := profile.EnsureProfile(rec) if err != nil { return nil, fmt.Errorf("%w: failed to load profile: %w", ErrImportFailed, err) } // Stop here if we are only validating. if r.ValidateOnly { return result, nil } // Import settings into profile. if r.Reset { p.Config = config.Expand(settings) } else { for k, v := range settings { config.PutValueIntoHierarchicalConfig(p.Config, k, v) } } // Mark profile as edited by user. p.LastEdited = time.Now().Unix() // Save profile back to db. err = p.Save() if err != nil { return nil, fmt.Errorf("%w: failed to save profile: %w", ErrImportFailed, err) } return result, nil } func checkSettings(settings map[string]any) (result *ImportResult, globalOnlySettingFound bool, err error) { result = &ImportResult{} // Validate config and gather some metadata. var checked int err = config.ForEachOption(func(option *config.Option) error { // Check if any setting is set. // TODO: Fix this - it only checks for global settings. // if r.Reset && option.IsSetByUser() { // result.ReplacesExisting = true // } newValue, ok := settings[option.Key] if ok { checked++ // Validate the new value. if err := option.ValidateValue(newValue); err != nil { return fmt.Errorf("%w: configuration value for %s is invalid: %w", ErrInvalidSettingValue, option.Key, err) } // Collect metadata. if option.RequiresRestart { result.RestartRequired = true } // TODO: Fix this - it only checks for global settings. // if !r.Reset && option.IsSetByUser() { // result.ReplacesExisting = true // } if !option.AnnotationEquals(config.SettablePerAppAnnotation, true) { globalOnlySettingFound = true } } return nil }) if err != nil { return nil, false, err } if checked < len(settings) { result.ContainsUnknown = true } return result, globalOnlySettingFound, nil } ================================================ FILE: service/sync/util.go ================================================ package sync import ( "encoding/json" "errors" "fmt" "net/http" yaml "gopkg.in/yaml.v3" "github.com/safing/jess/filesig" "github.com/safing/portmaster/base/api" "github.com/safing/structures/container" "github.com/safing/structures/dsd" ) // Type is the type of an export. type Type string // Export Types. const ( TypeProfile = "profile" TypeSettings = "settings" TypeSingleSetting = "single-setting" ) // Export IDs. const ( ExportTargetGlobal = "global" ) // Messages. var ( MsgNone = "" MsgValid = "Import is valid." MsgSuccess = "Import successful." MsgRequireRestart = "Import successful. Restart required for setting to take effect." ) // ExportRequest is a request for an export. type ExportRequest struct { From string `json:"from"` Keys []string `json:"keys"` } // ImportRequest is a request to import an export. type ImportRequest struct { // Where the export should be import to. Target string `json:"target"` // Only validate, but do not actually change anything. ValidateOnly bool `json:"validateOnly"` RawExport string `json:"rawExport"` RawMime string `json:"rawMime"` } // ImportResult is returned by successful import operations. type ImportResult struct { RestartRequired bool `json:"restartRequired"` ReplacesExisting bool `json:"replacesExisting"` ContainsUnknown bool `json:"containsUnknown"` } // Errors. var ( ErrMismatch = api.ErrorWithStatus( errors.New("the supplied export cannot be imported here"), http.StatusPreconditionFailed, ) ErrSettingNotFound = api.ErrorWithStatus( errors.New("setting not found"), http.StatusPreconditionFailed, ) ErrTargetNotFound = api.ErrorWithStatus( errors.New("import/export target does not exist"), http.StatusGone, ) ErrUnchanged = api.ErrorWithStatus( errors.New("cannot export unchanged setting"), http.StatusGone, ) ErrNotSettablePerApp = api.ErrorWithStatus( errors.New("cannot be set per app"), http.StatusGone, ) ErrInvalidImportRequest = api.ErrorWithStatus( errors.New("invalid import request"), http.StatusUnprocessableEntity, ) ErrInvalidSettingValue = api.ErrorWithStatus( errors.New("invalid setting value"), http.StatusUnprocessableEntity, ) ErrInvalidProfileData = api.ErrorWithStatus( errors.New("invalid profile data"), http.StatusUnprocessableEntity, ) ErrImportFailed = api.ErrorWithStatus( errors.New("import failed"), http.StatusInternalServerError, ) ErrExportFailed = api.ErrorWithStatus( errors.New("export failed"), http.StatusInternalServerError, ) ) func serializeExport(export any, ar *api.Request) (data []byte, err error) { // Get format. format := dsd.FormatFromAccept(ar.Header.Get("Accept")) // Serialize and add checksum. switch format { case dsd.JSON: data, err = json.Marshal(export) if err == nil { data, err = filesig.AddJSONChecksum(data) } case dsd.YAML: data, err = yaml.Marshal(export) if err == nil { data, err = filesig.AddYAMLChecksum(data, filesig.TextPlacementBottom) } default: return nil, dsd.ErrIncompatibleFormat } if err != nil { return nil, fmt.Errorf("failed to serialize: %w", err) } // Set Content-Type HTTP Header. ar.ResponseHeader.Set("Content-Type", dsd.FormatToMimeType[format]) return data, nil } func serializeProfileExport(export *ProfileExport, ar *api.Request) ([]byte, error) { // Do a regular serialize, if we don't need parts. switch { case export.IconData == "": // With no icon, do a regular export. return serializeExport(export, ar) case dsd.FormatFromAccept(ar.Header.Get("Accept")) != dsd.YAML: // Only export in parts for yaml. return serializeExport(export, ar) } // Step 1: Separate profile icon. profileIconExport := &ProfileIcon{ IconData: export.IconData, } export.IconData = "" // Step 2: Serialize main export. profileData, err := yaml.Marshal(export) if err != nil { return nil, fmt.Errorf("failed to serialize profile data: %w", err) } // Step 3: Serialize icon only. iconData, err := yaml.Marshal(profileIconExport) if err != nil { return nil, fmt.Errorf("failed to serialize profile icon: %w", err) } // Step 4: Stitch data together and add copyright notice for icon. exportData := container.New( profileData, []byte(` # The application icon below is the property of its respective owner. # The icon is used for identification purposes only, and does not imply any endorsement or affiliation with their respective owners. # It is the sole responsibility of the individual or entity sharing this dataset to ensure they have the necessary permissions to do so. `), iconData, ).CompileData() // Step 4: Add checksum. exportData, err = filesig.AddYAMLChecksum(exportData, filesig.TextPlacementBottom) if err != nil { return nil, fmt.Errorf("failed to add checksum: %w", err) } // Set Content-Type HTTP Header. ar.ResponseHeader.Set("Content-Type", dsd.FormatToMimeType[dsd.YAML]) return exportData, nil } func parseExport(request *ImportRequest, export any) error { format, err := dsd.MimeLoad([]byte(request.RawExport), request.RawMime, export) if err != nil { return fmt.Errorf("%w: failed to parse export: %w", ErrInvalidImportRequest, err) } // Verify checksum, if available. switch format { case dsd.JSON: err = filesig.VerifyJSONChecksum([]byte(request.RawExport)) case dsd.YAML: err = filesig.VerifyYAMLChecksum([]byte(request.RawExport)) default: // Checksums not supported. } if err != nil && !errors.Is(err, filesig.ErrChecksumMissing) { return fmt.Errorf("failed to verify checksum: %w", err) } return nil } ================================================ FILE: service/ui/api.go ================================================ package ui import ( "github.com/safing/portmaster/base/api" ) func (ui *UI) registerAPIEndpoints() error { return api.RegisterEndpoint(api.Endpoint{ Path: "ui/reload", Write: api.PermitUser, ActionFunc: ui.reloadUI, Name: "Reload UI Assets", Description: "Removes all assets from the cache and reloads the current (possibly updated) version from disk when requested.", }) } func (ui *UI) reloadUI(_ *api.Request) (msg string, err error) { // Close all archives. ui.CloseArchives() return "all ui archives successfully reloaded", nil } ================================================ FILE: service/ui/module.go ================================================ package ui import ( "os" "path/filepath" "sync" "sync/atomic" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/mgr" "github.com/spkg/zipfs" ) // UI serves the user interface files. type UI struct { mgr *mgr.Manager instance instance archives map[string]*zipfs.FileSystem archivesLock sync.RWMutex upgradeLock atomic.Bool } // New returns a new UI module. func New(instance instance) (*UI, error) { m := mgr.New("UI") ui := &UI{ mgr: m, instance: instance, archives: make(map[string]*zipfs.FileSystem), } if err := ui.registerAPIEndpoints(); err != nil { return nil, err } if err := ui.registerRoutes(); err != nil { return nil, err } return ui, nil } func (ui *UI) Manager() *mgr.Manager { return ui.mgr } // Start starts the module. func (ui *UI) Start() error { // Create a dummy directory to which processes change their working directory // to. Currently this includes the App and the Notifier. The aim is protect // all other directories and increase compatibility should any process want // to read or write something to the current working directory. This can also // be useful in the future to dump data to for debugging. The permission used // may seem dangerous, but proper permission on the parent directory provide // (some) protection. // Processes must _never_ read from this directory. execDir := filepath.Join(ui.instance.DataDir(), "exec") err := os.MkdirAll(execDir, 0o0777) //nolint:gosec // This is intentional. if err != nil { log.Warningf("ui: failed to create safe exec dir: %s", err) } // Ensure directory permission err = utils.EnsureDirectory(execDir, utils.PublicWriteExecPermission) if err != nil { log.Warningf("ui: failed to set permissions to directory %s: %s", execDir, err) } return nil } // Stop stops the module. func (ui *UI) Stop() error { return nil } func (ui *UI) getArchive(name string) (archive *zipfs.FileSystem, ok bool) { ui.archivesLock.RLock() defer ui.archivesLock.RUnlock() archive, ok = ui.archives[name] return } func (ui *UI) setArchive(name string, archive *zipfs.FileSystem) { ui.archivesLock.Lock() defer ui.archivesLock.Unlock() ui.archives[name] = archive } // CloseArchives closes all open archives. func (ui *UI) CloseArchives() { if ui == nil { return } ui.archivesLock.Lock() defer ui.archivesLock.Unlock() // Close archives. for _, archive := range ui.archives { if err := archive.Close(); err != nil { ui.mgr.Warn("failed to close ui archive", "err", err) } } // Reset map. clear(ui.archives) } // EnableUpgradeLock enables the upgrade lock and closes all open archives. func (ui *UI) EnableUpgradeLock() { if ui == nil { return } ui.upgradeLock.Store(true) ui.CloseArchives() } // DisableUpgradeLock disables the upgrade lock. func (ui *UI) DisableUpgradeLock() { if ui == nil { return } ui.upgradeLock.Store(false) } type instance interface { DataDir() string API() *api.API GetBinaryUpdateFile(name string) (path string, err error) } ================================================ FILE: service/ui/serve.go ================================================ package ui import ( "errors" "fmt" "io" "io/fs" "net/http" "net/url" "path/filepath" "strings" "github.com/spkg/zipfs" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" ) func (ui *UI) registerRoutes() error { // Server assets. api.RegisterHandler( "/assets/{resPath:[a-zA-Z0-9/\\._-]+}", &archiveServer{ui: ui, defaultModuleName: "assets"}, ) // Add slash to plain module namespaces. api.RegisterHandler( "/ui/modules/{moduleName:[a-z]+}", api.WrapInAuthHandler(redirAddSlash, api.PermitAnyone, api.NotSupported), ) // Serve modules. srv := &archiveServer{ui: ui} api.RegisterHandler("/ui/modules/{moduleName:[a-z]+}/", srv) api.RegisterHandler("/ui/modules/{moduleName:[a-z]+}/{resPath:[a-zA-Z0-9/\\._-]+}", srv) // Redirect "/" to default module. api.RegisterHandler( "/", api.WrapInAuthHandler(redirectToDefault, api.PermitAnyone, api.NotSupported), ) return nil } type archiveServer struct { ui *UI defaultModuleName string } func (bs *archiveServer) ReadPermission(*http.Request) api.Permission { return api.PermitAnyone } func (bs *archiveServer) WritePermission(*http.Request) api.Permission { return api.NotSupported } func (bs *archiveServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Get request context. ar := api.GetAPIRequest(r) if ar == nil { log.Errorf("ui: missing api request context") http.Error(w, "Internal server error.", http.StatusInternalServerError) return } moduleName, ok := ar.URLVars["moduleName"] if !ok { moduleName = bs.defaultModuleName if moduleName == "" { http.Error(w, "missing module name", http.StatusBadRequest) return } } resPath, ok := ar.URLVars["resPath"] if !ok || strings.HasSuffix(resPath, "/") { resPath = "index.html" } archiveFS, ok := bs.ui.getArchive(moduleName) if ok { ServeFileFromArchive(w, r, moduleName, archiveFS, resPath) return } // Check if the upgrade lock is enabled. if bs.ui.upgradeLock.Load() { http.Error(w, "Resources locked, upgrade in progress.", http.StatusLocked) return } // get file from update system zipFile, err := bs.ui.instance.GetBinaryUpdateFile(fmt.Sprintf("%s.zip", moduleName)) if err != nil { log.Tracef("ui: error loading module %s: %s", moduleName, err) http.Error(w, err.Error(), http.StatusInternalServerError) return } // Open archive from disk. archiveFS, err = zipfs.New(zipFile) if err != nil { log.Tracef("ui: error prepping module %s: %s", moduleName, err) http.Error(w, err.Error(), http.StatusInternalServerError) return } bs.ui.setArchive(moduleName, archiveFS) ServeFileFromArchive(w, r, moduleName, archiveFS, resPath) } // ServeFileFromArchive serves a file from the given archive. func ServeFileFromArchive(w http.ResponseWriter, r *http.Request, archiveName string, archiveFS *zipfs.FileSystem, path string) { readCloser, err := archiveFS.Open(path) if err != nil { if errors.Is(err, fs.ErrNotExist) { // Check if there is a base index.html file we can serve instead. var indexErr error path = "index.html" readCloser, indexErr = archiveFS.Open(path) if indexErr != nil { // If we cannot get an index, continue with handling the original error. log.Tracef("ui: requested resource \"%s\" not found in archive %s: %s", path, archiveName, err) http.Error(w, err.Error(), http.StatusNotFound) return } } else { log.Tracef("ui: error opening module %s: %s", archiveName, err) http.Error(w, err.Error(), http.StatusInternalServerError) return } } // set content type _, ok := w.Header()["Content-Type"] if !ok { contentType, _ := utils.MimeTypeByExtension(filepath.Ext(path)) w.Header().Set("Content-Type", contentType) } w.WriteHeader(http.StatusOK) if r.Method != http.MethodHead { _, err = io.Copy(w, readCloser) if err != nil { log.Errorf("ui: failed to serve file: %s", err) return } } _ = readCloser.Close() } // redirectToDefault redirects the request to the default UI module. func redirectToDefault(w http.ResponseWriter, r *http.Request) { u, err := url.Parse("/ui/modules/portmaster/") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } http.Redirect(w, r, r.URL.ResolveReference(u).String(), http.StatusTemporaryRedirect) } // redirAddSlash redirects the request to the same, but with a trailing slash. func redirAddSlash(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, r.RequestURI+"/", http.StatusPermanentRedirect) } ================================================ FILE: service/updates/downloader.go ================================================ package updates import ( "archive/zip" "bytes" "compress/gzip" "context" "crypto/sha256" "encoding/hex" "errors" "fmt" "io" "io/fs" "net/http" "os" "path/filepath" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" ) type Downloader struct { u *Updater index *Index indexURLs []string existingFiles map[string]string httpClient http.Client } func NewDownloader(u *Updater, indexURLs []string) *Downloader { return &Downloader{ u: u, indexURLs: indexURLs, } } func (d *Downloader) updateIndex(ctx context.Context) error { // Make sure dir exists. err := utils.EnsureDirectory(d.u.cfg.DownloadDirectory, utils.PublicReadExecPermission) if err != nil { return fmt.Errorf("create download directory: %s", d.u.cfg.DownloadDirectory) } // Try to download the index from one of the index URLs. var ( indexData []byte index *Index ) for _, url := range d.indexURLs { // Download and verify index. indexData, index, err = d.getIndex(ctx, url) if err == nil { // Valid index found! break } log.Warningf("updates/%s: failed to update index from %q: %s", d.u.cfg.Name, url, err) err = fmt.Errorf("update index file from %q: %w", url, err) } if err != nil { return fmt.Errorf("all index URLs failed, last error: %w", err) } d.index = index // Write the index into a file. indexFilepath := filepath.Join(d.u.cfg.DownloadDirectory, d.u.cfg.IndexFile) err = os.WriteFile(indexFilepath, indexData, utils.PublicReadExecPermission.AsUnixPermission()) if err != nil { return fmt.Errorf("write index file: %w", err) } return nil } func (d *Downloader) getIndex(ctx context.Context, url string) (indexData []byte, bundle *Index, err error) { // Download data from URL. indexData, err = d.downloadData(ctx, url) if err != nil { return nil, nil, fmt.Errorf("GET index: %w", err) } // Verify and parse index. bundle, err = ParseIndex(indexData, d.u.cfg.Platform, d.u.cfg.Verify) if err != nil { return nil, nil, fmt.Errorf("parse index: %w", err) } return indexData, bundle, nil } // gatherExistingFiles gathers the checksums on existing files. func (d *Downloader) gatherExistingFiles(dir string) error { // Make sure map is initialized. if d.existingFiles == nil { d.existingFiles = make(map[string]string) } // Walk directory, just log errors. err := filepath.WalkDir(dir, func(fullpath string, entry fs.DirEntry, err error) error { // Fail on access error. if err != nil { return err } // Skip folders. if entry.IsDir() { return nil } // Read full file. fileData, err := os.ReadFile(fullpath) if err != nil { log.Debugf("updates/%s: failed to read file %q while searching for existing files: %s", d.u.cfg.Name, fullpath, err) return fmt.Errorf("failed to read file %s: %w", fullpath, err) } // Calculate checksum and add it to the existing files. hashSum := sha256.Sum256(fileData) d.existingFiles[hex.EncodeToString(hashSum[:])] = fullpath return nil }) if err != nil { return fmt.Errorf("searching for existing files: %w", err) } return nil } func (d *Downloader) downloadArtifacts(ctx context.Context) error { // Make sure dir exists. err := utils.EnsureDirectory(d.u.cfg.DownloadDirectory, utils.PublicReadExecPermission) if err != nil { return fmt.Errorf("create download directory: %s", d.u.cfg.DownloadDirectory) } artifacts: for _, artifact := range d.index.Artifacts { dstFilePath := filepath.Join(d.u.cfg.DownloadDirectory, artifact.Filename) // Check if we can copy the artifact from disk instead. if existingFile, ok := d.existingFiles[artifact.SHA256]; ok { // Check if this is the same file. if existingFile == dstFilePath { continue artifacts } // Copy and check. err = copyAndCheckSHA256Sum(existingFile, dstFilePath, artifact.SHA256, artifact.GetFileMode()) if err == nil { continue artifacts } log.Debugf("updates/%s: failed to copy existing file %s: %s", d.u.cfg.Name, artifact.Filename, err) } // Check if the artifact has download URLs. if len(artifact.URLs) == 0 { return fmt.Errorf("artifact %s is missing download URLs", artifact.Filename) } // Try to download the artifact from one of the URLs. var artifactData []byte artifactURLs: for _, url := range artifact.URLs { // Download and verify index. artifactData, err = d.getArtifact(ctx, artifact, url) if err == nil { // Valid artifact found! break artifactURLs } err = fmt.Errorf("update index file from %q: %w", url, err) } if err != nil { return fmt.Errorf("all artifact URLs for %s failed, last error: %w", artifact.Filename, err) } // Write artifact to temporary file. tmpFilename := dstFilePath + ".download" err = os.WriteFile(tmpFilename, artifactData, artifact.GetFileMode().AsUnixPermission()) if err != nil { return fmt.Errorf("write %s to temp file: %w", artifact.Filename, err) } _ = utils.SetFilePermission(tmpFilename, artifact.GetFileMode()) // Rename/Move to actual location. err = os.Rename(tmpFilename, dstFilePath) if err != nil { return fmt.Errorf("rename %s after write: %w", artifact.Filename, err) } log.Infof("updates/%s: downloaded and verified %s", d.u.cfg.Name, artifact.Filename) } return nil } func (d *Downloader) getArtifact(ctx context.Context, artifact *Artifact, url string) ([]byte, error) { // Download data from URL. artifactData, err := d.downloadData(ctx, url) if err != nil { return nil, fmt.Errorf("GET artifact: %w", err) } // Decompress artifact data, if configured. // TODO: Normally we should do operations on "untrusted" data _after_ verification, // but we really want the checksum to be for the unpacked data. Should we add another checksum, or is HTTPS enough? if artifact.Unpack != "" { artifactData, err = Decompress(artifact.Unpack, artifactData) if err != nil { return nil, fmt.Errorf("decompress: %w", err) } } // Verify checksum. if err := CheckSHA256Sum(artifactData, artifact.SHA256); err != nil { return nil, err } return artifactData, nil } func (d *Downloader) downloadData(ctx context.Context, url string) ([]byte, error) { // Setup request. req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody) if err != nil { return nil, fmt.Errorf("failed to create GET request to %s: %w", url, err) } if UserAgent != "" { req.Header.Set("User-Agent", UserAgent) } // Start request with shared http client. resp, err := d.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("failed a get file request to: %w", err) } defer func() { _ = resp.Body.Close() }() // Check for HTTP status errors. if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("server returned non-OK status: %d %s", resp.StatusCode, resp.Status) } // Read the full body and return it. content, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read body of response: %w", err) } return content, nil } // Decompress decompresses the given data according to the specified type. func Decompress(cType string, fileBytes []byte) ([]byte, error) { switch cType { case "zip": return decompressZip(fileBytes) case "gz": return decompressGzip(fileBytes) default: return nil, fmt.Errorf("unsupported compression type") } } func decompressGzip(data []byte) ([]byte, error) { // Create a gzip reader from the byte slice. gzipReader, err := gzip.NewReader(bytes.NewReader(data)) if err != nil { return nil, fmt.Errorf("create gzip reader: %w", err) } defer func() { _ = gzipReader.Close() }() // Copy from the gzip reader into a new buffer. var buf bytes.Buffer _, err = io.CopyN(&buf, gzipReader, MaxUnpackSize) if err != nil && !errors.Is(err, io.EOF) { return nil, fmt.Errorf("read gzip file: %w", err) } return buf.Bytes(), nil } func decompressZip(data []byte) ([]byte, error) { // Create a zip reader from the byte slice. zipReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) if err != nil { return nil, fmt.Errorf("create zip reader: %w", err) } // Ensure there is only one file in the zip. if len(zipReader.File) != 1 { return nil, fmt.Errorf("zip file must contain exactly one file") } // Open single file in the zip. file := zipReader.File[0] fileReader, err := file.Open() if err != nil { return nil, fmt.Errorf("open file in zip: %w", err) } defer func() { _ = fileReader.Close() }() // Copy from the zip reader into a new buffer. var buf bytes.Buffer _, err = io.CopyN(&buf, fileReader, MaxUnpackSize) if err != nil && !errors.Is(err, io.EOF) { return nil, fmt.Errorf("read file in zip: %w", err) } return buf.Bytes(), nil } ================================================ FILE: service/updates/index.go ================================================ package updates import ( "crypto/sha256" "crypto/subtle" "encoding/hex" "encoding/json" "errors" "fmt" "io" "os" "path/filepath" "runtime" "time" semver "github.com/hashicorp/go-version" "github.com/safing/jess" "github.com/safing/jess/filesig" "github.com/safing/portmaster/base/utils" ) // MaxUnpackSize defines the maximum size that is allowed to be unpacked. const MaxUnpackSize = 1 << 30 // 2^30 == 1GB const currentPlatform = runtime.GOOS + "_" + runtime.GOARCH var zeroVersion = semver.Must(semver.NewVersion("0.0.0")) // Artifact represents a single file with metadata. type Artifact struct { Filename string `json:"Filename"` SHA256 string `json:"SHA256"` URLs []string `json:"URLs"` Platform string `json:"Platform,omitempty"` Unpack string `json:"Unpack,omitempty"` Version string `json:"Version,omitempty"` localFile string versionNum *semver.Version } // GetFileMode returns the required filesystem permission for the artifact. func (a *Artifact) GetFileMode() utils.FSPermission { if a.Platform == currentPlatform { return utils.PublicReadExecPermission } return utils.PublicReadPermission } // Path returns the absolute path to the local file. func (a *Artifact) Path() string { return a.localFile } // SemVer returns the version of the artifact. func (a *Artifact) SemVer() *semver.Version { return a.versionNum } func (a *Artifact) String() string { return fmt.Sprintf("%s(v%s)", a.Filename, a.Version) } // IsNewerThan returns whether the artifact is newer than the given artifact. // Returns true if the given artifact is nil. // The second return value "ok" is false when version could not be compared. // In this case, it is up to the caller to decide how to proceed. func (a *Artifact) IsNewerThan(b *Artifact) (newer, ok bool) { switch { case a == nil: return false, false case b == nil: return true, true case a.versionNum == nil: return false, false case b.versionNum == nil: return false, false case a.versionNum.GreaterThan(b.versionNum): return true, true default: return false, true } } func (a *Artifact) export(dir string, indexVersion *semver.Version) *Artifact { copied := &Artifact{ Filename: a.Filename, SHA256: a.SHA256, URLs: a.URLs, Platform: a.Platform, Unpack: a.Unpack, Version: a.Version, localFile: filepath.Join(dir, a.Filename), versionNum: a.versionNum, } // Make sure we have a version number. switch { case copied.versionNum != nil: // Version already parsed. case copied.Version != "": // Need to parse version. v, err := semver.NewVersion(copied.Version) if err == nil { copied.versionNum = v } default: // No version defined, inherit index version. copied.versionNum = indexVersion } return copied } // Index represents a collection of artifacts with metadata. type Index struct { Name string `json:"Name"` Version string `json:"Version"` Published time.Time `json:"Published"` Artifacts []*Artifact `json:"Artifacts"` versionNum *semver.Version // isLocallyGenerated indicates whether the index was generated from a local directory // rather than downloaded from an official release channel. // // When true, the Published field reflects the local generation time rather than an // official release date. Time-based sanity checks (version/date mismatch detection) // are therefore skipped when this index is the current one. isLocallyGenerated bool } // LoadIndex loads and parses an index from the given filename. // Leave platform empty to use current platform. func LoadIndex(filename string, platform string, trustStore jess.TrustStore) (*Index, error) { // Read index file from disk. content, err := os.ReadFile(filename) if err != nil { return nil, fmt.Errorf("read index file: %w", err) } // Parse and return. return ParseIndex(content, platform, trustStore) } // ParseIndex parses an index from a json string. // Leave platform empty to use current platform. func ParseIndex(jsonContent []byte, platform string, trustStore jess.TrustStore) (*Index, error) { // Verify signature. if trustStore != nil { if err := filesig.VerifyJSONSignature(jsonContent, trustStore); err != nil { return nil, fmt.Errorf("verify: %w", err) } } // Parse json. index := &Index{} err := json.Unmarshal(jsonContent, index) if err != nil { return nil, fmt.Errorf("parse index: %w", err) } // Check platform. if platform == "" { platform = currentPlatform } // Initialize data. err = index.init(platform) if err != nil { return nil, err } return index, nil } func (index *Index) init(platform string) error { // Parse version number, if set. if index.Version != "" { versionNum, err := semver.NewVersion(index.Version) if err != nil { return fmt.Errorf("invalid index version %q: %w", index.Version, err) } index.versionNum = versionNum } // Filter artifacts by platform. filtered := make([]*Artifact, 0) for _, a := range index.Artifacts { if a.Platform == "" || a.Platform == platform { filtered = append(filtered, a) } } index.Artifacts = filtered // Parse artifact version numbers. for _, a := range index.Artifacts { if a.Version != "" { v, err := semver.NewVersion(a.Version) if err == nil { a.versionNum = v } } else { a.Version = index.Version a.versionNum = index.versionNum } } return nil } // CanDoUpgrades returns whether the index is able to follow a secure upgrade path. func (index *Index) CanDoUpgrades() error { switch { case index.versionNum == nil: return errors.New("missing version number") case index.Published.IsZero(): return errors.New("missing publish date") case index.Published.After(time.Now().Add(15 * time.Minute)): return fmt.Errorf("is from the future (%s)", time.Until(index.Published).Round(time.Minute)) default: return nil } } // ShouldUpgradeTo returns whether the given index is a successor and should be upgraded to. func (index *Index) ShouldUpgradeTo(newIndex *Index) error { // Check if both indexes can do upgrades. if err := index.CanDoUpgrades(); err != nil { return fmt.Errorf("current index cannot do upgrades: %w", err) } if err := newIndex.CanDoUpgrades(); err != nil { return fmt.Errorf("new index cannot do upgrade: %w", err) } switch { case index.versionNum.Equal(zeroVersion): // The zero version is used for bootstrapping. // Upgrade in any case. return nil case index.Name != newIndex.Name: return fmt.Errorf( "new index name (%q) does not match current index name (%q)", newIndex.Name, index.Name, ) case index.versionNum.LessThan(newIndex.versionNum) && index.Published.After(newIndex.Published) && !index.isLocallyGenerated: // The new index is newer in version (upgrade), but older in publish date. This is suspicious and should be prevented. return errors.New("new index has newer version but older publish date") case index.versionNum.GreaterThan(newIndex.versionNum) && index.Published.Before(newIndex.Published) && !index.isLocallyGenerated: // The new index is older in version (downgrade), but newer in publish date. This is suspicious and should be prevented. return errors.New("new index has older version but newer publish date") case index.versionNum.Segments()[0] > newIndex.versionNum.Segments()[0]: // Downgrades are allowed, if they are not breaking changes. return errors.New("new index is a breaking change downgrade") case index.Published.Equal(newIndex.Published): // "Do nothing". return ErrSameIndex case index.versionNum.Equal(newIndex.versionNum) && index.isLocallyGenerated: // This is especially important for locally generated indexes, where the publish date is not a reliable indicator of the index's age. // "Do nothing". return ErrSameIndex default: // Upgrade! return nil } } // VerifyArtifacts checks if all artifacts are present in the given dir and have the correct hash. func (index *Index) VerifyArtifacts(dir string) error { for _, artifact := range index.Artifacts { err := CheckSHA256SumFile(filepath.Join(dir, artifact.Filename), artifact.SHA256) if err != nil { return fmt.Errorf("verify %s: %w", artifact.Filename, err) } } return nil } func (index *Index) Export(signingKey *jess.Signet, trustStore jess.TrustStore) ([]byte, error) { // Serialize to json. indexData, err := json.Marshal(index) if err != nil { return nil, fmt.Errorf("serialize: %w", err) } // Do not sign if signing key is not given. if signingKey == nil { return indexData, nil } // Make envelope. envelope := jess.NewUnconfiguredEnvelope() envelope.SuiteID = jess.SuiteSignV1 envelope.Senders = []*jess.Signet{signingKey} // Sign json data. signedIndex, err := filesig.AddJSONSignature(indexData, envelope, trustStore) if err != nil { return nil, fmt.Errorf("sign: %w", err) } return signedIndex, nil } // CheckSHA256SumFile checks the sha256sum of the given file. func CheckSHA256SumFile(filename string, sha256sum string) error { // Check expected hash. expectedDigest, err := hex.DecodeString(sha256sum) if err != nil { return fmt.Errorf("invalid hex encoding for expected hash %s: %w", sha256sum, err) } if len(expectedDigest) != sha256.Size { return fmt.Errorf("invalid size for expected hash %s: %w", sha256sum, err) } // Open file for checking. file, err := os.Open(filename) if err != nil { return fmt.Errorf("open file: %w", err) } defer func() { _ = file.Close() }() // Calculate hash of the file. fileHash := sha256.New() if _, err := io.Copy(fileHash, file); err != nil { return fmt.Errorf("read file: %w", err) } if subtle.ConstantTimeCompare(fileHash.Sum(nil), expectedDigest) != 1 { return errors.New("sha256sum mismatch") } return nil } // CheckSHA256Sum checks the sha256sum of the given data. func CheckSHA256Sum(fileData []byte, sha256sum string) error { // Check expected hash. expectedDigest, err := hex.DecodeString(sha256sum) if err != nil { return fmt.Errorf("invalid hex encoding for expected hash %s: %w", sha256sum, err) } if len(expectedDigest) != sha256.Size { return fmt.Errorf("invalid size for expected hash %s: %w", sha256sum, err) } // Calculate and compare hash of the file. hashSum := sha256.Sum256(fileData) if subtle.ConstantTimeCompare(hashSum[:], expectedDigest) != 1 { return errors.New("sha256sum mismatch") } return nil } // copyAndCheckSHA256Sum copies the file from src to dst and check the sha256 sum. // As a special case, if the sha256sum is not given, it is not checked. func copyAndCheckSHA256Sum(src, dst, sha256sum string, filePermission utils.FSPermission) error { // Check expected hash. var expectedDigest []byte if sha256sum != "" { expectedDigest, err := hex.DecodeString(sha256sum) if err != nil { return fmt.Errorf("invalid hex encoding for expected hash %s: %w", sha256sum, err) } if len(expectedDigest) != sha256.Size { return fmt.Errorf("invalid size for expected hash %s: %w", sha256sum, err) } } // Read file from source. fileData, err := os.ReadFile(src) if err != nil { return fmt.Errorf("read src file: %w", err) } // Calculate and compare hash of the file. if len(expectedDigest) > 0 { hashSum := sha256.Sum256(fileData) if subtle.ConstantTimeCompare(hashSum[:], expectedDigest) != 1 { return errors.New("sha256sum mismatch") } } // Write to temporary file. tmpDst := dst + ".copy" err = os.WriteFile(tmpDst, fileData, filePermission.AsUnixPermission()) if err != nil { return fmt.Errorf("write temp dst file: %w", err) } // Rename/Move to actual location. err = os.Rename(tmpDst, dst) if err != nil { return fmt.Errorf("rename dst file after write: %w", err) } utils.SetFilePermission(dst, filePermission) return nil } ================================================ FILE: service/updates/index_scan.go ================================================ package updates import ( "crypto/sha256" "encoding/hex" "errors" "fmt" "io/fs" "os" "path" "path/filepath" "regexp" "slices" "strings" "time" "github.com/gobwas/glob" semver "github.com/hashicorp/go-version" ) type IndexScanConfig struct { Name string Version string PrimaryArtifact string BaseURL string Templates map[string]Artifact IgnoreFiles []string UnpackFiles map[string]string cleanedBaseURL string ignoreFilesGlobs []glob.Glob unpackFilesGlobs map[string]glob.Glob } func (bs *IndexScanConfig) init() error { // Transform base URL into expected format. bs.cleanedBaseURL = strings.TrimSuffix(bs.BaseURL, "/") + "/" // Parse ignore files patterns. bs.ignoreFilesGlobs = make([]glob.Glob, 0, len(bs.IgnoreFiles)) for _, pattern := range bs.IgnoreFiles { g, err := glob.Compile(pattern, os.PathSeparator) if err != nil { return fmt.Errorf("invalid ingore files pattern %q: %w", pattern, err) } bs.ignoreFilesGlobs = append(bs.ignoreFilesGlobs, g) } // Parse unpack files patterns. bs.unpackFilesGlobs = make(map[string]glob.Glob) for setting, pattern := range bs.UnpackFiles { g, err := glob.Compile(pattern, os.PathSeparator) if err != nil { return fmt.Errorf("invalid unpack files pattern %q: %w", pattern, err) } bs.unpackFilesGlobs[setting] = g } return nil } // IsIgnored returns whether a filename should be ignored. func (bs *IndexScanConfig) IsIgnored(filename string) bool { for _, ignoreGlob := range bs.ignoreFilesGlobs { if ignoreGlob.Match(filename) { return true } } return false } // UnpackSetting returns the unpack setings for the given filename. func (bs *IndexScanConfig) UnpackSetting(filename string) (string, error) { var foundSetting string settings: for unpackSetting, matchGlob := range bs.unpackFilesGlobs { switch { case !matchGlob.Match(filename): // Check next if glob does not match. continue settings case foundSetting == "": // First find, save setting. foundSetting = unpackSetting case foundSetting != unpackSetting: // Additional find, and setting is not the same. return "", errors.New("matches contradicting unpack settings") } } return foundSetting, nil } // GenerateIndexFromDir generates a index from a given folder. func GenerateIndexFromDir(sourceDir string, cfg IndexScanConfig) (*Index, error) { //nolint:maintidx artifacts := make(map[string]*Artifact) // Initialize. err := cfg.init() if err != nil { return nil, fmt.Errorf("invalid index scan config: %w", err) } sourceDir, err = filepath.Abs(sourceDir) if err != nil { return nil, fmt.Errorf("invalid index dir: %w", err) } var indexVersion *semver.Version if cfg.Version != "" { indexVersion, err = semver.NewVersion(cfg.Version) if err != nil { return nil, fmt.Errorf("invalid index version: %w", err) } } err = filepath.WalkDir(sourceDir, func(fullpath string, d fs.DirEntry, err error) error { // Fail on access error. if err != nil { return err } // Step 1: Extract information and check ignores. // Skip folders. if d.IsDir() { return nil } // Get relative path for processing. relpath, err := filepath.Rel(sourceDir, fullpath) if err != nil { return fmt.Errorf("invalid relative path for %s: %w", fullpath, err) } // Check if file is in the ignore list. if cfg.IsIgnored(relpath) { return nil } // Extract version, if present. identifier, version, ok := getIdentifierAndVersion(d.Name()) if !ok { // Fallback to using filename as identifier, which is normal for the simplified system. identifier = d.Name() version = "" } var versionNum *semver.Version if version != "" { versionNum, err = semver.NewVersion(version) if err != nil { return fmt.Errorf("invalid version %s for %s: %w", relpath, version, err) } } // Extract platform. platform := "all" before, _, found := strings.Cut(relpath, string(os.PathSeparator)) if found { platform = before } // Step 2: Check and compare file version. // Make the key platform specific since there can be same filename for multiple platforms. key := platform + "/" + identifier existing, ok := artifacts[key] if ok { // Check for duplicates and mixed versioned/non-versioned. switch { case existing.Version == version: return fmt.Errorf("duplicate version for %s: %s and %s", key, existing.localFile, fullpath) case (existing.Version == "") != (version == ""): return fmt.Errorf("both a versioned and non-versioned file for: %s: %s and %s", key, existing.localFile, fullpath) } // Compare versions. existingVersion, _ := semver.NewVersion(existing.Version) switch { case existingVersion.Equal(versionNum): return fmt.Errorf("duplicate version for %s: %s and %s", key, existing.localFile, fullpath) case existingVersion.GreaterThan(versionNum): // New version is older, skip. return nil } } // Step 3: Create new Artifact. artifact := &Artifact{} // Check if the caller provided a template for the artifact. if t, ok := cfg.Templates[identifier]; ok { fromTemplate := t artifact = &fromTemplate } // Set artifact properties. if artifact.Filename == "" { artifact.Filename = identifier } if len(artifact.URLs) == 0 && cfg.BaseURL != "" { artifact.URLs = []string{cfg.cleanedBaseURL + relpath} } if artifact.Platform == "" { artifact.Platform = platform } if artifact.Unpack == "" { unpackSetting, err := cfg.UnpackSetting(relpath) if err != nil { return fmt.Errorf("invalid unpack setting for %s at %s: %w", key, relpath, err) } artifact.Unpack = unpackSetting } if artifact.Version == "" { artifact.Version = version } // Remove unpack suffix. if artifact.Unpack != "" { artifact.Filename, _ = strings.CutSuffix(artifact.Filename, "."+artifact.Unpack) } // Set local file path. artifact.localFile = fullpath // Save new artifact to map. artifacts[key] = artifact return nil }) if err != nil { return nil, fmt.Errorf("scanning dir: %w", err) } // Create base index. index := &Index{ Name: cfg.Name, Version: cfg.Version, Published: time.Now(), versionNum: indexVersion, isLocallyGenerated: true, } if index.Version == "" && cfg.PrimaryArtifact != "" { pv, ok := artifacts[cfg.PrimaryArtifact] if ok { index.Version = pv.Version } } if index.Name == "" { index.Name = strings.Trim(filepath.Base(sourceDir), "./\\") } // Convert to slice and compute hashes. export := make([]*Artifact, 0, len(artifacts)) for _, artifact := range artifacts { // Compute hash. hash, err := GetSHA256(artifact.localFile, artifact.Unpack) if err != nil { return nil, fmt.Errorf("calculate hash of file: %s %w", artifact.localFile, err) } artifact.SHA256 = hash // Remove "all" platform IDs. if artifact.Platform == "all" { artifact.Platform = "" } // Remove default versions. if artifact.Version == index.Version { artifact.Version = "" } // Add to export slice. export = append(export, artifact) } // Sort final artifacts. slices.SortFunc(export, func(a, b *Artifact) int { switch { case a.Filename != b.Filename: return strings.Compare(a.Filename, b.Filename) case a.Platform != b.Platform: return strings.Compare(a.Platform, b.Platform) case a.Version != b.Version: return strings.Compare(a.Version, b.Version) case a.SHA256 != b.SHA256: return strings.Compare(a.SHA256, b.SHA256) default: return 0 } }) // Assign and return. index.Artifacts = export return index, nil } // GetSHA256 gets the sha256sum of the given file and unpacks it if necessary. func GetSHA256(path string, unpackType string) (string, error) { content, err := os.ReadFile(path) if err != nil { return "", err } // Decompress if compression was applied to the file. if unpackType != "" { content, err = Decompress(unpackType, content) if err != nil { return "", err } } // Calculate hash hash := sha256.Sum256(content) return hex.EncodeToString(hash[:]), nil } var fileVersionRegex = regexp.MustCompile(`_v[0-9]+-[0-9]+-[0-9]+(-[a-z]+)?`) func getIdentifierAndVersion(versionedPath string) (identifier, version string, ok bool) { dirPath, filename := path.Split(versionedPath) // Extract version from filename. rawVersion := fileVersionRegex.FindString(filename) if rawVersion == "" { // No version present in file, making it invalid. return "", "", false } // Trim the `_v` that gets caught by the regex and // replace `-` with `.` to get the version string. version = strings.Replace(strings.TrimLeft(rawVersion, "_v"), "-", ".", 2) // Put the filename back together without version. i := strings.Index(filename, rawVersion) if i < 0 { // extracted version not in string (impossible) return "", "", false } filename = filename[:i] + filename[i+len(rawVersion):] // Put the full path back together and return it. // `dirPath + filename` is guaranteed by path.Split() return dirPath + filename, version, true } ================================================ FILE: service/updates/module.go ================================================ package updates import ( "errors" "fmt" "os" "path/filepath" "runtime" "slices" "strings" "sync" "time" "github.com/tevino/abool" "github.com/safing/jess" "github.com/safing/portmaster/base/info" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/ui" ) const ( updateTaskRepeatDuration = 1 * time.Hour noNewUpdateNotificationID = "updates:no-new-update" updateAvailableNotificationID = "updates:update-available" restartRequiredNotificationID = "updates:restart-required" updateFailedNotificationID = "updates:update-failed" corruptInstallationNotificationID = "updates:corrupt-installation" // ResourceUpdateEvent is emitted every time the // updater successfully performed a resource update. ResourceUpdateEvent = "resource update" ) // UserAgent is an HTTP User-Agent that is used to add // more context to requests made by the registry when // fetching resources from the update server. var UserAgent = fmt.Sprintf("Portmaster (%s %s)", runtime.GOOS, runtime.GOARCH) // Errors. var ( ErrNotFound = errors.New("file not found") ErrSameIndex = errors.New("same index") ErrAutoCheckDisabled = errors.New("automatic update checks are disabled") ErrNoUpdateAvailable = errors.New("no update available") ErrActionRequired = errors.New("action required") ) // UpdateCommandConfig defines the configuration for a shell command // that is executed when an update is applied type UpdateCommandConfig struct { // Shell command to execute Command string // Arguments to pass to the command Args []string // Execute triggers: if not empty, the command will be executed only if specified file was updated // if empty, the command will be executed always TriggerArtifactFName string // FailOnError defines whether the upgrade should fail if the command fails // true - upgrade will fail if the command fails // false - upgrade will continue even if the command fails FailOnError bool } // Config holds the configuration for the updates module. type Config struct { // Name of the updater. Name string // Directory is the main directory where the currently to-be-used artifacts live. Directory string // DownloadDirectory is the directory where new artifacts are downloaded to and prepared for upgrading. // After the upgrade, this directory is cleared. DownloadDirectory string // PurgeDirectory is the directory where old artifacts are moved to during the upgrade process. // After the upgrade, this directory is cleared. PurgeDirectory string // Ignore defines file and directory names within the main directory that should be ignored during the upgrade. Ignore []string // IndexURLs defines file IndexURLs []string // IndexFile is the name of the index file used in the directories. IndexFile string // Verify enables and specifies the trust the index signatures will be checked against. Verify jess.TrustStore // Platform defines the platform to download artifacts for. Defaults to current platform. Platform string // AutoCheck defines that new indexes may be downloaded automatically without outside trigger. AutoCheck bool // AutoDownload defines that updates may be downloaded automatically without outside trigger. AutoDownload bool // AutoApply defines that updates may be automatically applied without outside trigger. // Requires AutoDownload the be enabled. AutoApply bool // NeedsRestart defines that a restart is required after an upgrade has been completed. // Restart is triggered automatically, if Notify is disabled. NeedsRestart bool // Notify defines whether the user shall be informed about events via notifications. // If enabled, disables automatic restart after upgrade. Notify bool // list of shell commands needed to run after the upgrade (if any) PostUpgradeCommands []UpdateCommandConfig } // Check looks for obvious configuration errors. func (cfg *Config) Check() error { // Check if required fields are set. switch { case cfg.Name == "": return errors.New("name must be set") case cfg.Directory == "": return errors.New("directory must be set") case cfg.DownloadDirectory == "": return errors.New("download directory must be set") case cfg.PurgeDirectory == "": return errors.New("purge directory must be set") case cfg.IndexFile == "": return errors.New("index file must be set") case cfg.AutoApply && !cfg.AutoDownload: return errors.New("auto apply is set, but auto download is not") } // Check if Ignore contains paths. for i, s := range cfg.Ignore { if strings.ContainsRune(s, filepath.Separator) { return fmt.Errorf("ignore entry #%d invalid: must be file or directory name, not path", i+1) } } // Check if IndexURLs are HTTPS. for i, url := range cfg.IndexURLs { if !strings.HasPrefix(url, "https://") { return fmt.Errorf("index URL #%d invalid: is not a HTTPS url", i+1) } } // Check platform. if cfg.Platform == "" { cfg.Platform = currentPlatform } return nil } // Updater provides access to released artifacts. type Updater struct { m *mgr.Manager states *mgr.StateMgr cfg Config index *Index indexLock sync.Mutex updateCheckWorkerMgr *mgr.WorkerMgr upgradeWorkerMgr *mgr.WorkerMgr EventResourcesUpdated *mgr.EventMgr[struct{}] corruptedInstallation error isUpdateRunning *abool.AtomicBool started *abool.AtomicBool configureLock sync.Mutex instance instance } // New returns a new Updates module. func New(instance instance, name string, cfg Config) (*Updater, error) { m := mgr.New(name) module := &Updater{ m: m, states: m.NewStateMgr(), cfg: cfg, EventResourcesUpdated: mgr.NewEventMgr[struct{}](ResourceUpdateEvent, m), isUpdateRunning: abool.NewBool(false), started: abool.NewBool(false), instance: instance, } // Check config. if err := module.cfg.Check(); err != nil { return nil, fmt.Errorf("config is invalid: %w", err) } // Make sure main dir exists. err := utils.EnsureDirectory(module.cfg.Directory, utils.PublicReadExecPermission) if err != nil { return nil, fmt.Errorf("create update target directory: %s", module.cfg.DownloadDirectory) } // Create Workers. module.updateCheckWorkerMgr = m.NewWorkerMgr("update checker", module.updateCheckWorker, nil) module.upgradeWorkerMgr = m.NewWorkerMgr("upgrader", module.upgradeWorker, nil) // Load index. index, err := LoadIndex(filepath.Join(cfg.Directory, cfg.IndexFile), cfg.Platform, cfg.Verify) if err == nil { // Verify artifacts. if err := index.VerifyArtifacts(cfg.Directory); err != nil { module.corruptedInstallation = fmt.Errorf("invalid artifact: %w", err) } // Save index to module and return. module.index = index return module, nil } // Fall back to scanning the directory. if !errors.Is(err, os.ErrNotExist) { log.Errorf("updates/%s: invalid index file, falling back to dir scan: %s", cfg.Name, err) module.corruptedInstallation = fmt.Errorf("invalid index: %w", err) } index, err = GenerateIndexFromDir(cfg.Directory, IndexScanConfig{ Name: cfg.Name, Version: info.VersionNumber(), }) if err == nil && index.init(currentPlatform) == nil { module.index = index return module, nil } // Fall back to empty index. return module, nil } func (u *Updater) updateAndUpgrade(w *mgr.WorkerCtx, indexURLs []string, ignoreVersion, forceApply bool) (err error) { //nolint:maintidx // Make sure only one update process is running. if !u.isUpdateRunning.SetToIf(false, true) { return fmt.Errorf("an updater task is already running, please try again later") } defer u.isUpdateRunning.UnSet() // Create a new downloader. downloader := NewDownloader(u, indexURLs) // Update or load the index file. if len(indexURLs) > 0 { // Download fresh copy, if indexURLs are given. err = downloader.updateIndex(w.Ctx()) if err != nil { return fmt.Errorf("update index file: %w", err) } } else { // Otherwise, load index from download dir. downloader.index, err = LoadIndex(filepath.Join(u.cfg.DownloadDirectory, u.cfg.IndexFile), u.cfg.Platform, u.cfg.Verify) if err != nil { return fmt.Errorf("load previously downloaded index file: %w", err) } } // Get index to check version. u.indexLock.Lock() index := u.index u.indexLock.Unlock() // Check if there is a new version. if !ignoreVersion && index != nil { // Check with local pointer to index. if err := index.ShouldUpgradeTo(downloader.index); err != nil { if errors.Is(err, ErrSameIndex) { log.Infof("updates/%s: no new update", u.cfg.Name) if u.cfg.Notify && u.instance.Notifications() != nil { u.instance.Notifications().Notify(¬ifications.Notification{ EventID: noNewUpdateNotificationID, Type: notifications.Info, Title: "Portmaster Is Up-To-Date", Message: "Portmaster v" + index.Version + " is the newest version.", Expires: time.Now().Add(1 * time.Minute).Unix(), AvailableActions: []*notifications.Action{ { ID: "ack", Text: "OK", }, }, }) } } else { log.Warningf("updates/%s: cannot update: %s", u.cfg.Name, err) if u.cfg.Notify && u.instance.Notifications() != nil { u.instance.Notifications().Notify(¬ifications.Notification{ EventID: noNewUpdateNotificationID, Type: notifications.Info, Title: "Portmaster Is Up-To-Date*", Message: "While Portmaster v" + index.Version + " is the newest version, there is an internal issue with checking for updates: " + err.Error(), Expires: time.Now().Add(1 * time.Minute).Unix(), AvailableActions: []*notifications.Action{ { ID: "ack", Text: "OK", }, }, }) } } return fmt.Errorf("%w: %w", ErrNoUpdateAvailable, err) } } // Check if automatic downloads are enabled. if !u.cfg.AutoDownload && !forceApply { log.Infof("updates/%s: new update to v%s available, action required to download and upgrade", u.cfg.Name, downloader.index.Version) if u.cfg.Notify && u.instance.Notifications() != nil { u.instance.Notifications().Notify(¬ifications.Notification{ EventID: updateAvailableNotificationID, Type: notifications.Info, Title: "New Update Available", Message: "Portmaster v" + downloader.index.Version + " is available. Click Upgrade to download and upgrade now.", AvailableActions: []*notifications.Action{ { ID: "ack", Text: "OK", }, { ID: "upgrade", Text: "Upgrade Now", Type: notifications.ActionTypeWebhook, Payload: notifications.ActionTypeWebhookPayload{ Method: "POST", URL: "updates/apply", }, }, }, }) } return fmt.Errorf("%w: apply updates to download and upgrade", ErrActionRequired) } // Check for existing resources before starting to download. _ = downloader.gatherExistingFiles(u.cfg.Directory) // Artifacts are re-used between versions. _ = downloader.gatherExistingFiles(u.cfg.DownloadDirectory) // Previous download may have been interrupted. _ = downloader.gatherExistingFiles(u.cfg.PurgeDirectory) // Revover faster from a failed upgrade. // Download any remaining needed files. // If everything is already found in the download directory, then this is a no-op. log.Infof("updates/%s: downloading new version: %s %s", u.cfg.Name, downloader.index.Name, downloader.index.Version) err = downloader.downloadArtifacts(w.Ctx()) if err != nil { log.Errorf("updates/%s: failed to download update: %s", u.cfg.Name, err) if err := u.deleteUnfinishedFiles(u.cfg.DownloadDirectory); err != nil { log.Debugf("updates/%s: failed to delete unfinished files in download directory %s", u.cfg.Name, u.cfg.DownloadDirectory) } return fmt.Errorf("downloading failed: %w", err) } // Notify the user that an upgrade is available. if !u.cfg.AutoApply && !forceApply { log.Infof("updates/%s: new update to v%s available, action required to upgrade", u.cfg.Name, downloader.index.Version) if u.cfg.Notify && u.instance.Notifications() != nil { u.instance.Notifications().Notify(¬ifications.Notification{ EventID: updateAvailableNotificationID, Type: notifications.Info, Title: "New Update Ready", Message: "Portmaster v" + downloader.index.Version + " is available. Click Upgrade to upgrade now.", AvailableActions: []*notifications.Action{ { ID: "ack", Text: "OK", }, { ID: "upgrade", Text: "Upgrade Now", Type: notifications.ActionTypeWebhook, Payload: notifications.ActionTypeWebhookPayload{ Method: "POST", URL: "updates/apply", }, }, }, }) } return fmt.Errorf("%w: apply updates to download and upgrade", ErrActionRequired) } // Run upgrade procedure. err = u.upgrade(downloader, ignoreVersion) if err != nil { if err := u.deleteUnfinishedFiles(u.cfg.PurgeDirectory); err != nil { log.Debugf("updates/%s: failed to delete unfinished files in purge directory %s", u.cfg.Name, u.cfg.PurgeDirectory) } return err } // Install is complete! // Clean up and notify modules of changed files. err = u.cleanupAfterUpgrade() if err != nil { log.Debugf("updates/%s: failed to clean up after upgrade: %s", u.cfg.Name, err) } u.EventResourcesUpdated.Submit(struct{}{}) // If no restart is needed, we are done. if !u.cfg.NeedsRestart { return nil } // Notify user that a restart is required. if u.cfg.Notify { if u.instance.Notifications() != nil { u.instance.Notifications().Notify(¬ifications.Notification{ EventID: restartRequiredNotificationID, Type: notifications.Info, Title: "Restart Required", Message: "Portmaster v" + downloader.index.Version + " is installed. Restart to use new version.", AvailableActions: []*notifications.Action{ { ID: "ack", Text: "Later", }, { ID: "restart", Text: "Restart Now", Type: notifications.ActionTypeWebhook, Payload: notifications.ActionTypeWebhookPayload{ Method: "POST", URL: "core/restart", }, }, }, }) } return fmt.Errorf("%w: restart required", ErrActionRequired) } // Otherwise, trigger restart immediately. u.instance.Restart() return nil } func (u *Updater) getIndexURLsWithLock() []string { u.configureLock.Lock() defer u.configureLock.Unlock() return u.cfg.IndexURLs } func (u *Updater) updateCheckWorker(w *mgr.WorkerCtx) error { err := u.updateAndUpgrade(w, u.getIndexURLsWithLock(), false, false) switch { case err == nil: return nil // Success! case errors.Is(err, ErrSameIndex): return nil // Nothing to do. case errors.Is(err, ErrNoUpdateAvailable): return nil // Already logged. case errors.Is(err, ErrActionRequired) && !u.cfg.Notify: return fmt.Errorf("user action required, but notifying user is disabled: %w", err) default: return fmt.Errorf("udpating failed: %w", err) } } func (u *Updater) upgradeWorker(w *mgr.WorkerCtx) error { err := u.updateAndUpgrade(w, u.getIndexURLsWithLock(), false, true) switch { case err == nil: return nil // Success! case errors.Is(err, ErrSameIndex): return nil // Nothing to do. case errors.Is(err, ErrNoUpdateAvailable): return nil // Already logged. case errors.Is(err, ErrActionRequired) && !u.cfg.Notify: return fmt.Errorf("user action required, but notifying user is disabled: %w", err) default: return fmt.Errorf("udpating failed: %w", err) } } // ForceUpdate executes a forced update and upgrade directly and synchronously // and is intended to be used only within a tool, not a service. func (u *Updater) ForceUpdate() error { return u.m.Do("update and upgrade", func(w *mgr.WorkerCtx) error { return u.updateAndUpgrade(w, u.getIndexURLsWithLock(), true, true) }) } // UpdateFromURL installs an update from the provided url. func (u *Updater) UpdateFromURL(url string) error { u.m.Go("custom update from url", func(w *mgr.WorkerCtx) error { _ = u.updateAndUpgrade(w, []string{url}, true, true) return nil }) return nil } // Configure makes slight configuration changes to the updater. // It locks the index, which can take a while an update is running. func (u *Updater) Configure(autoCheck bool, indexURLs []string) { u.configureLock.Lock() defer u.configureLock.Unlock() // Apply new config. var changed bool if u.cfg.AutoCheck != autoCheck { u.cfg.AutoCheck = autoCheck changed = true } if !slices.Equal(u.cfg.IndexURLs, indexURLs) { u.cfg.IndexURLs = indexURLs changed = true } // Trigger update check if enabled and something changed. if changed && u.started.IsSet() { if autoCheck { u.updateCheckWorkerMgr.Repeat(updateTaskRepeatDuration).Go() } else { u.updateCheckWorkerMgr.Repeat(0) } } } // TriggerUpdateCheck triggers an update check. func (u *Updater) TriggerUpdateCheck() { u.updateCheckWorkerMgr.Go() } // TriggerApplyUpdates triggers upgrade. func (u *Updater) TriggerApplyUpdates() { u.upgradeWorkerMgr.Go() } // States returns the state manager. func (u *Updater) States() *mgr.StateMgr { return u.states } // Manager returns the module manager. func (u *Updater) Manager() *mgr.Manager { return u.m } // Start starts the module. func (u *Updater) Start() error { u.configureLock.Lock() defer u.configureLock.Unlock() if u.corruptedInstallation != nil && u.cfg.Notify && u.instance.Notifications() != nil { u.states.Add(mgr.State{ ID: corruptInstallationNotificationID, Name: "Install Corruption", Message: "Portmaster has detected that one or more of its own files have been corrupted. Please re-install the software. Error: " + u.corruptedInstallation.Error(), Type: mgr.StateTypeError, Data: u.corruptedInstallation, }) } // Check for updates automatically, if enabled. if u.cfg.AutoCheck { u.updateCheckWorkerMgr. Repeat(updateTaskRepeatDuration). Delay(15 * time.Second) } u.started.SetTo(true) return nil } func (u *Updater) GetMainDir() string { return u.cfg.Directory } // GetIndex returns a copy of the index. func (u *Updater) GetIndex() (*Index, error) { // Copy Artifacts. artifacts, err := u.GetFiles() if err != nil { return nil, err } u.indexLock.Lock() defer u.indexLock.Unlock() // Check if any index is active. if u.index == nil { return nil, ErrNotFound } return &Index{ Name: u.index.Name, Version: u.index.Version, Published: u.index.Published, Artifacts: artifacts, versionNum: u.index.versionNum, }, nil } // GetFiles returns all artifacts. Returns ErrNotFound if no artifacts are found. func (u *Updater) GetFiles() ([]*Artifact, error) { u.indexLock.Lock() defer u.indexLock.Unlock() // Check if any index is active. if u.index == nil { return nil, ErrNotFound } // Export all artifacts. export := make([]*Artifact, 0, len(u.index.Artifacts)) for _, artifact := range u.index.Artifacts { switch { case artifact.Platform != "" && artifact.Platform != u.cfg.Platform: // Platform is defined and does not match. // Platforms are usually pre-filtered, but just to be sure. default: // Artifact matches! export = append(export, artifact.export(u.cfg.Directory, u.index.versionNum)) } } // Check if anything was exported. if len(export) == 0 { return nil, ErrNotFound } return export, nil } // GetFile returns the path of a file given the name. Returns ErrNotFound if file is not found. func (u *Updater) GetFile(name string) (*Artifact, error) { u.indexLock.Lock() defer u.indexLock.Unlock() // Check if any index is active. if u.index == nil { return nil, ErrNotFound } for _, artifact := range u.index.Artifacts { switch { case artifact.Filename != name: // Name does not match. case artifact.Platform != "" && artifact.Platform != u.cfg.Platform: // Platform is defined and does not match. // Platforms are usually pre-filtered, but just to be sure. default: // Artifact matches! return artifact.export(u.cfg.Directory, u.index.versionNum), nil } } return nil, ErrNotFound } // Stop stops the module. func (u *Updater) Stop() error { u.started.SetTo(false) return nil } type instance interface { Restart() Shutdown() Notifications() *notifications.Notifications UI() *ui.UI } ================================================ FILE: service/updates/updates_test.go ================================================ package updates import ( "encoding/json" "fmt" "os" "path/filepath" "testing" "time" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/ui" ) type testInstance struct{} func (i *testInstance) Restart() {} func (i *testInstance) Shutdown() {} func (i *testInstance) Notifications() *notifications.Notifications { return nil } func (i *testInstance) Ready() bool { return true } func (i *testInstance) SetCmdLineOperation(f func() error) {} func (i *testInstance) UI() *ui.UI { return nil } func TestPerformUpdate(t *testing.T) { t.Parallel() // Initialize mock instance stub := &testInstance{} // Make tmp dirs installedDir, err := os.MkdirTemp("", "updates_current_") if err != nil { t.Fatal(err) } defer func() { _ = os.RemoveAll(installedDir) }() updateDir, err := os.MkdirTemp("", "updates_new_") if err != nil { t.Fatal(err) } defer func() { _ = os.RemoveAll(updateDir) }() purgeDir, err := os.MkdirTemp("", "updates_purge_") if err != nil { t.Fatal(err) } defer func() { _ = os.RemoveAll(purgeDir) }() // Generate mock files now := time.Now() if err := GenerateMockFolder(installedDir, "Test", "1.0.0", now); err != nil { t.Fatal(err) } if err := GenerateMockFolder(updateDir, "Test", "1.0.1", now.Add(1*time.Minute)); err != nil { t.Fatal(err) } // Create updater (loads index). updater, err := New(stub, "Test", Config{ Name: "Test", Directory: installedDir, DownloadDirectory: updateDir, PurgeDirectory: purgeDir, IndexFile: "index.json", AutoDownload: true, AutoApply: true, }) if err != nil { t.Fatal(err) } // Try to apply the updates m := mgr.New("updates test") _ = m.Do("test update and upgrade", func(w *mgr.WorkerCtx) error { if err := updater.updateAndUpgrade(w, nil, false, false); err != nil { if data, err := os.ReadFile(filepath.Join(installedDir, "index.json")); err == nil { fmt.Println(string(data)) fmt.Println(updater.index.Version) fmt.Println(updater.index.versionNum) } if data, err := os.ReadFile(filepath.Join(updateDir, "index.json")); err == nil { fmt.Println(string(data)) idx, err := ParseIndex(data, updater.cfg.Platform, nil) if err == nil { fmt.Println(idx.Version) fmt.Println(idx.versionNum) } } t.Fatal(err) } return nil }) // Check if the current version is now the new. newIndex, err := LoadIndex(filepath.Join(installedDir, "index.json"), updater.cfg.Platform, nil) if err != nil { t.Fatal(err) } if newIndex.Version != "1.0.1" { t.Fatalf("expected version 1.0.1 found %s", newIndex.Version) } } // GenerateMockFolder generates mock index folder for testing. func GenerateMockFolder(dir, name, version string, published time.Time) error { // Make sure dir exists _ = os.MkdirAll(dir, 0o750) // Create empty files file, err := os.Create(filepath.Join(dir, "portmaster")) if err != nil { return err } _ = file.Close() file, err = os.Create(filepath.Join(dir, "portmaster-core")) if err != nil { return err } _ = file.Close() file, err = os.Create(filepath.Join(dir, "portmaster.zip")) if err != nil { return err } _ = file.Close() file, err = os.Create(filepath.Join(dir, "assets.zip")) if err != nil { return err } _ = file.Close() index, err := GenerateIndexFromDir(dir, IndexScanConfig{ Name: name, Version: version, }) if err != nil { return err } index.Published = published indexJSON, err := json.MarshalIndent(index, "", " ") if err != nil { fmt.Fprintf(os.Stderr, "failed to marshal index: %s\n", err) } err = os.WriteFile(filepath.Join(dir, "index.json"), indexJSON, 0o600) if err != nil { return err } return nil } ================================================ FILE: service/updates/upgrade.go ================================================ package updates import ( "errors" "fmt" "os" "os/exec" "path/filepath" "slices" "strings" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" ) func (u *Updater) upgrade(downloader *Downloader, ignoreVersion bool) error { // Lock index for the upgrade. u.indexLock.Lock() defer u.indexLock.Unlock() // Check if we should upgrade at all. if !ignoreVersion && u.index != nil { if err := u.index.ShouldUpgradeTo(downloader.index); err != nil { return fmt.Errorf("cannot upgrade: %w", ErrNoUpdateAvailable) } } // If we are running in a UI instance, we need to unload the UI assets if u.instance != nil { u.instance.UI().EnableUpgradeLock() defer u.instance.UI().DisableUpgradeLock() } // Execute the upgrade. upgradeError := u.upgradeMoveFiles(downloader) if upgradeError == nil { // Files upgraded successfully. // Applying post-upgrade tasks, if any. upgradeError = u.applyPostUpgradeCommands() } if upgradeError == nil { return nil } // Attempt to recover from failed upgrade. recoveryErr := u.recoverFromFailedUpgrade() if recoveryErr == nil { return fmt.Errorf("upgrade failed, but recovery was successful: %w", upgradeError) } // Recovery failed too. return fmt.Errorf("upgrade (including recovery) failed: %w", upgradeError) } func (u *Updater) upgradeMoveFiles(downloader *Downloader) error { // Important: // We assume that the downloader has done its job and all artifacts are verified. // Files will just be moved here. // In case the files are copied, they are verified in the process. // Reset purge directory, so that we can do a clean rollback later. _ = os.RemoveAll(u.cfg.PurgeDirectory) err := utils.EnsureDirectory(u.cfg.PurgeDirectory, utils.PublicReadExecPermission) if err != nil { return fmt.Errorf("failed to create purge directory: %w", err) } // Move current version files into purge folder. if u.index != nil { log.Debugf("updates/%s: removing the old version (v%s from %s)", u.cfg.Name, u.index.Version, u.index.Published) } files, err := os.ReadDir(u.cfg.Directory) if err != nil { if !errors.Is(err, os.ErrNotExist) { return fmt.Errorf("read current directory: %w", err) } err := utils.EnsureDirectory(u.cfg.PurgeDirectory, utils.PublicReadExecPermission) if err != nil { return fmt.Errorf("create current directory: %w", err) } } else { // Move files. for _, file := range files { // Check if file is ignored. if slices.Contains(u.cfg.Ignore, file.Name()) { continue } // ignore PurgeDirectory itself if strings.EqualFold(u.cfg.PurgeDirectory, filepath.Join(u.cfg.Directory, file.Name())) { continue } // Otherwise, move file to purge dir. src := filepath.Join(u.cfg.Directory, file.Name()) dst := filepath.Join(u.cfg.PurgeDirectory, file.Name()) err := u.moveFile(src, dst, "", utils.PublicReadPermission) if err != nil { return fmt.Errorf("failed to move current file %s to purge dir: %w", file.Name(), err) } } } // Move the new index file into main directory. log.Debugf("updates/%s: installing the new version (v%s from %s)", u.cfg.Name, downloader.index.Version, downloader.index.Published) src := filepath.Join(u.cfg.DownloadDirectory, u.cfg.IndexFile) dst := filepath.Join(u.cfg.Directory, u.cfg.IndexFile) err = u.moveFile(src, dst, "", utils.PublicReadPermission) if err != nil { return fmt.Errorf("failed to move index file to %s: %w", dst, err) } // Move downloaded files to the current version folder. for _, artifact := range downloader.index.Artifacts { src = filepath.Join(u.cfg.DownloadDirectory, artifact.Filename) dst = filepath.Join(u.cfg.Directory, artifact.Filename) err = u.moveFile(src, dst, artifact.SHA256, artifact.GetFileMode()) if err != nil { return fmt.Errorf("failed to move file %s: %w", artifact.Filename, err) } else { log.Debugf("updates/%s: %s moved", u.cfg.Name, artifact.Filename) } } // Set new index on module. u.index = downloader.index log.Infof("updates/%s: update complete (v%s from %s)", u.cfg.Name, u.index.Version, u.index.Published) return nil } // moveFile moves a file and falls back to copying if it fails. func (u *Updater) moveFile(currentPath, newPath string, sha256sum string, filePermission utils.FSPermission) error { // Try to simply move file. err := os.Rename(currentPath, newPath) if err == nil { // Moving was successful, return. utils.SetFilePermission(newPath, filePermission) return nil } log.Tracef("updates/%s: failed to move to %q, falling back to copy+delete: %s", u.cfg.Name, newPath, err) // Copy and check the checksum while we are at it. err = copyAndCheckSHA256Sum(currentPath, newPath, sha256sum, filePermission) if err != nil { return fmt.Errorf("move failed, copy+delete fallback failed: %w", err) } return nil } // recoverFromFailedUpgrade attempts to roll back any moved files by the upgrade process. func (u *Updater) recoverFromFailedUpgrade() error { // Get list of files from purge dir. files, err := os.ReadDir(u.cfg.PurgeDirectory) if err != nil { return err } // Move all files back to main dir. for _, file := range files { purgedFile := filepath.Join(u.cfg.PurgeDirectory, file.Name()) activeFile := filepath.Join(u.cfg.Directory, file.Name()) err := u.moveFile(purgedFile, activeFile, "", utils.PublicReadPermission) if err != nil { // Only warn and continue to recover as many files as possible. log.Warningf("updates/%s: failed to roll back file %s: %s", u.cfg.Name, file.Name(), err) } } return nil } func (u *Updater) cleanupAfterUpgrade() error { err := os.RemoveAll(u.cfg.PurgeDirectory) if err != nil { return fmt.Errorf("delete purge dir: %w", err) } err = os.RemoveAll(u.cfg.DownloadDirectory) if err != nil { return fmt.Errorf("delete download dir: %w", err) } return nil } func (u *Updater) deleteUnfinishedFiles(dir string) error { entries, err := os.ReadDir(dir) if err != nil { return err } for _, e := range entries { switch { case e.IsDir(): // Continue. case strings.HasSuffix(e.Name(), ".download"): path := filepath.Join(dir, e.Name()) log.Warningf("updates/%s: deleting unfinished download file: %s", u.cfg.Name, path) err := os.Remove(path) if err != nil { log.Errorf("updates/%s: failed to delete unfinished download file %s: %s", u.cfg.Name, path, err) } case strings.HasSuffix(e.Name(), ".copy"): path := filepath.Join(dir, e.Name()) log.Warningf("updates/%s: deleting unfinished copied file: %s", u.cfg.Name, path) err := os.Remove(path) if err != nil { log.Errorf("updates/%s: failed to delete unfinished copied file %s: %s", u.cfg.Name, path, err) } } } return nil } func (u *Updater) applyPostUpgradeCommands() error { // At this point, we assume that the upgrade was successful and all files are in place. // We need to execute the post-upgrade commands, if any. if len(u.cfg.PostUpgradeCommands) == 0 { return nil } // collect full paths to files that were upgraded, required to check the trigger. upgradedFiles := make(map[string]struct{}) for _, artifact := range u.index.Artifacts { upgradedFiles[filepath.Join(u.cfg.Directory, artifact.Filename)] = struct{}{} } // Execute post-upgrade commands. for _, puCmd := range u.cfg.PostUpgradeCommands { // Check trigger to ensure that we need to run this command. if len(puCmd.TriggerArtifactFName) > 0 { if _, ok := upgradedFiles[puCmd.TriggerArtifactFName]; !ok { continue } } log.Debugf("updates/%s: executing post-upgrade command: '%s %s'", u.cfg.Name, puCmd.Command, strings.Join(puCmd.Args, " ")) output, err := exec.Command(puCmd.Command, puCmd.Args...).CombinedOutput() if err != nil { if puCmd.FailOnError { return fmt.Errorf("post-upgrade command '%s %s' failed: %w, output: %s", puCmd.Command, strings.Join(puCmd.Args, " "), err, string(output)) } log.Warningf("updates/%s: post-upgrade command '%s %s' failed, but ignored. Error: %s", u.cfg.Name, puCmd.Command, strings.Join(puCmd.Args, " "), err) } } return nil } ================================================ FILE: spn/TESTING.md ================================================ # Testing SPN This page documents ways to test if the SPN works as intended. ⚠ Work in Progress. Currently we are just collecting helpful things we find. ## Test Multi-Identity Routing In order to test if the multi-identity routing is working, you can request multiple websites to display your public IP. If they show different values, multi-identity routing is working. ### Websites - - - - ### Terminal ```sh curl https://icanhazip.com curl https://ipecho.net/plain curl https://ipinfo.io/ip curl https://ipinfo.tw/ip ``` ================================================ FILE: spn/TRADEMARKS ================================================ The names "Safing", "Portmaster", "SPN" and their logos are trademarks owned by Safing ICS Technologies GmbH (Austria). Although our code is free, it is very important that we strictly enforce our trademark rights, in order to be able to protect our users against people who use the marks to commit fraud. This means that, while you have considerable freedom to redistribute and modify our software, there are tight restrictions on your ability to use our names and logos in ways which fall in the domain of trademark law, even when built into binaries that we provide. This file is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Parts of it were taken from https://www.mozilla.org/en-US/foundation/licensing/. ================================================ FILE: spn/access/account/auth.go ================================================ package account import ( "errors" "net/http" ) // Authentication Headers. const ( AuthHeaderDevice = "Device-17" AuthHeaderToken = "Token-17" AuthHeaderNextToken = "Next-Token-17" AuthHeaderNextTokenDeprecated = "Next_token_17" ) // Errors. var ( ErrMissingDeviceID = errors.New("missing device ID") ErrMissingToken = errors.New("missing token") ) // AuthToken holds an authentication token. type AuthToken struct { Device string Token string } // GetAuthTokenFromRequest extracts an authentication token from a request. func GetAuthTokenFromRequest(request *http.Request) (*AuthToken, error) { device := request.Header.Get(AuthHeaderDevice) if device == "" { return nil, ErrMissingDeviceID } token := request.Header.Get(AuthHeaderToken) if token == "" { return nil, ErrMissingToken } return &AuthToken{ Device: device, Token: token, }, nil } // ApplyTo applies the authentication token to a request. func (at *AuthToken) ApplyTo(request *http.Request) { request.Header.Set(AuthHeaderDevice, at.Device) request.Header.Set(AuthHeaderToken, at.Token) } // GetNextTokenFromResponse extracts an authentication token from a response. func GetNextTokenFromResponse(resp *http.Response) (token string, ok bool) { token = resp.Header.Get(AuthHeaderNextToken) if token == "" { // TODO: Remove when fixed on server. token = resp.Header.Get(AuthHeaderNextTokenDeprecated) } return token, token != "" } // ApplyNextTokenToResponse applies the next authentication token to a response. func ApplyNextTokenToResponse(w http.ResponseWriter, token string) { w.Header().Set(AuthHeaderNextToken, token) } ================================================ FILE: spn/access/account/client.go ================================================ package account // Customer Agent URLs. const ( CAAuthenticateURL = "/authenticate" CAProfileURL = "/user/profile" CAGetTokensURL = "/tokens" ) // Customer Hub URLs. const ( CHAuthenticateURL = "/v1/authenticate" CHUserProfileURL = "/v1/user_profile" ) ================================================ FILE: spn/access/account/types.go ================================================ package account import ( "time" "golang.org/x/exp/slices" ) // User, Subscription and Charge states. const ( // UserStateNone is only used within Portmaster for saving information for // logging into the same device. UserStateNone = "" UserStateFresh = "fresh" UserStateQueued = "queued" UserStateApproved = "approved" UserStateSuspended = "suspended" UserStateLoggedOut = "loggedout" // Portmaster only. SubscriptionStateManual = "manual" // Manual renewal. SubscriptionStateActive = "active" // Automatic renewal. SubscriptionStateCancelled = "cancelled" // Automatic, but canceled. ChargeStatePending = "pending" ChargeStateCompleted = "completed" ChargeStateDead = "dead" ) // Agent and Hub return statuses. const ( // StatusInvalidAuth [401 Unauthorized] is returned when the credentials are // invalid or the user was logged out. StatusInvalidAuth = 401 // StatusNoAccess [403 Forbidden] is returned when the user does not have // an active subscription or the subscription does not include the required // feature for the request. StatusNoAccess = 403 // StatusInvalidDevice [410 Gone] is returned when the device trying to // log into does not exist. StatusInvalidDevice = 410 // StatusReachedDeviceLimit [409 Conflict] is returned when the device limit is reached. StatusReachedDeviceLimit = 409 // StatusDeviceInactive [423 Locked] is returned when the device is locked. StatusDeviceInactive = 423 // StatusNotLoggedIn [412 Precondition] is returned by the Portmaster, if an action required to be logged in, but the user is not logged in. StatusNotLoggedIn = 412 // StatusUnknownError is a special status code that signifies an unknown or // unexpected error by the API. StatusUnknownError = -1 // StatusConnectionError is a special status code that signifies a // connection error. StatusConnectionError = -2 ) // User describes an SPN user account. type User struct { Username string `json:"username"` State string `json:"state"` Balance int `json:"balance"` Device *Device `json:"device"` Subscription *Subscription `json:"subscription"` CurrentPlan *Plan `json:"current_plan"` NextPlan *Plan `json:"next_plan"` View *View `json:"view"` } // MayUseSPN returns whether the user may currently use the SPN. func (u *User) MayUseSPN() bool { return u.MayUse(FeatureSPN) } // MayUsePrioritySupport returns whether the user may currently use the priority support. func (u *User) MayUsePrioritySupport() bool { return u.MayUse(FeatureSafingSupport) } // MayUse returns whether the user may currently use the feature identified by // the given feature ID. // Leave feature ID empty to check without feature. func (u *User) MayUse(featureID FeatureID) bool { switch { case u == nil: // We need a user, obviously. case u.State != UserStateApproved: // Only approved users may use the SPN. case u.Subscription == nil: // Need a subscription. case u.Subscription.EndsAt == nil: case time.Now().After(*u.Subscription.EndsAt): // Subscription needs to be active. case u.CurrentPlan == nil: // Need a plan / package. case featureID != "" && !slices.Contains(u.CurrentPlan.FeatureIDs, featureID): // Required feature ID must be in plan / package feature IDs. default: // All checks passed! return true } return false } // Device describes a device of an SPN user. type Device struct { Name string `json:"name"` ID string `json:"id"` } // Subscription describes an SPN subscription. type Subscription struct { EndsAt *time.Time `json:"ends_at"` State string `json:"state"` NextBillingDate *time.Time `json:"next_billing_date"` PaymentProvider string `json:"payment_provider"` } // FeatureID defines a feature that requires a plan/subscription. type FeatureID string // A list of all supported features. const ( FeatureSPN = FeatureID("spn") FeatureSafingSupport = FeatureID("support") FeatureHistory = FeatureID("history") FeatureBWVis = FeatureID("bw-vis") FeatureVPNCompat = FeatureID("vpn-compat") ) // Plan describes an SPN subscription plan. type Plan struct { Name string `json:"name"` Amount int `json:"amount"` Months int `json:"months"` Renewable bool `json:"renewable"` FeatureIDs []FeatureID `json:"feature_ids"` } ================================================ FILE: spn/access/account/view.go ================================================ package account import ( "fmt" "strings" "time" ) // View holds metadata that assists in displaying account information. type View struct { Message string ShowAccountData bool ShowAccountButton bool ShowLoginButton bool ShowRefreshButton bool ShowLogoutButton bool } // UpdateView updates the view and handles plan/package fallbacks. func (u *User) UpdateView(requestStatusCode int) { v := &View{} // Clean up naming and fallbacks when finished. defer func() { // Display "Free" package if no plan is set or if it expired. switch { case u.CurrentPlan == nil, u.Subscription == nil, u.Subscription.EndsAt == nil: // Reset to free plan. u.CurrentPlan = &Plan{ Name: "Free", } u.Subscription = nil case u.Subscription.NextBillingDate != nil: // Subscription is on auto-renew. // Wait for update from server. case time.Since(*u.Subscription.EndsAt) > 0: // Reset to free plan. u.CurrentPlan = &Plan{ Name: "Free", } u.Subscription = nil } // Prepend "Portmaster " to plan name. // TODO: Remove when Plan/Package naming has been updated. if u.CurrentPlan != nil && !strings.HasPrefix(u.CurrentPlan.Name, "Portmaster ") { u.CurrentPlan.Name = "Portmaster " + u.CurrentPlan.Name } // Apply new view to user. u.View = v }() // Set view data based on return code. switch requestStatusCode { case StatusInvalidAuth, StatusInvalidDevice, StatusDeviceInactive: // Account deleted or Device inactive or deleted. // When using token based auth, there is no difference between these cases. v.Message = "This device may have been deactivated or removed from your account. Please log in again." v.ShowAccountData = true v.ShowAccountButton = true v.ShowLoginButton = true v.ShowLogoutButton = true return case StatusUnknownError: v.Message = "There is an unknown error in the communication with the account server. The shown information may not be accurate. " case StatusConnectionError: v.Message = "Portmaster could not connect to the account server. The shown information may not be accurate. " } // Set view data based on profile data. switch { case u.State == UserStateLoggedOut: // User logged out. v.ShowAccountButton = true v.ShowLoginButton = true return case u.State == UserStateSuspended: // Account is suspended. v.Message += fmt.Sprintf("Your account (%s) was suspended. Please contact support for details.", u.Username) v.ShowAccountButton = true v.ShowRefreshButton = true v.ShowLogoutButton = true return case u.Subscription == nil || u.Subscription.EndsAt == nil: // Account has never had a subscription. v.Message += "Get more features. Upgrade today." case u.Subscription.NextBillingDate != nil: switch { case time.Since(*u.Subscription.NextBillingDate) > 0: v.Message += "Your auto-renewal seems to be delayed. Please refresh and check the status of your payment. Payment information may be delayed." case time.Until(*u.Subscription.NextBillingDate) < 24*time.Hour: v.Message += "Your subscription will auto-renew soon. Please note that payment information may be delayed." } case time.Since(*u.Subscription.EndsAt) > 0: // Subscription expired. if u.CurrentPlan != nil { v.Message += fmt.Sprintf("Your package %s has ended. Extend it on the Account Page.", u.CurrentPlan.Name) } else { v.Message += "Your package has ended. Extend it on the Account Page." } case time.Until(*u.Subscription.EndsAt) < 7*24*time.Hour: // Add generic ending soon message if the package ends in less than 7 days. v.Message += "Your package ends soon. Extend it on the Account Page." } // Defaults for generally good accounts. v.ShowAccountData = true v.ShowAccountButton = true v.ShowRefreshButton = true v.ShowLogoutButton = true } ================================================ FILE: spn/access/api.go ================================================ package access import ( "errors" "fmt" "net/http" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/access/account" ) func registerAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Path: `spn/account/login`, Write: api.PermitAdmin, WriteMethod: http.MethodPost, HandlerFunc: handleLogin, Name: "SPN Login", Description: "Log into your SPN account.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `spn/account/logout`, Write: api.PermitAdmin, WriteMethod: http.MethodDelete, ActionFunc: handleLogout, Name: "SPN Logout", Description: "Logout from your SPN account.", Parameters: []api.Parameter{ { Method: http.MethodDelete, Field: "purge", Value: "", Description: "If set, account data is purged. Otherwise, the username and device ID are kept in order to log into the same device when logging in with the same user again.", }, }, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `spn/account/user/profile`, Read: api.PermitUser, ReadMethod: http.MethodGet, RecordFunc: handleGetUserProfile, Name: "SPN User Profile", Description: "Get the user profile of the logged in SPN account.", Parameters: []api.Parameter{ { Method: http.MethodGet, Field: "refresh", Value: "", Description: "If set, the user profile is freshly fetched from the account server.", }, }, }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `account/features`, Read: api.PermitUser, ReadMethod: http.MethodGet, StructFunc: func(_ *api.Request) (i interface{}, err error) { return struct { Features []Feature }{ Features: features, }, nil }, Name: "Get Account Features", Description: "Returns all account features.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `account/features/{id:[A-Za-z0-9_-]+}/icon`, Read: api.PermitUser, ReadMethod: http.MethodGet, Name: "Returns the image of the featuare", MimeType: "image/svg+xml", DataFunc: func(ar *api.Request) (data []byte, err error) { featureID, ok := ar.URLVars["id"] if !ok { return nil, errors.New("invalid feature id") } for _, feature := range features { if feature.ID == featureID { return []byte(feature.icon), nil } } return nil, errors.New("feature id not found") }, }); err != nil { return err } return nil } func handleLogin(w http.ResponseWriter, r *http.Request) { // Get username and password. username, password, ok := r.BasicAuth() // Request, if omitted. if !ok || username == "" || password == "" { w.Header().Set("WWW-Authenticate", "Basic realm=SPN Login") http.Error(w, "Login with your SPN account.", http.StatusUnauthorized) return } // Process login. user, code, err := Login(username, password) if err != nil { log.Warningf("spn/access: failed to login: %s", err) if code == 0 { http.Error(w, "Internal error: "+err.Error(), http.StatusInternalServerError) } else { http.Error(w, err.Error(), code) } return } // Return success. _, _ = w.Write([]byte( fmt.Sprintf("Now logged in as %s as device %s", user.Username, user.Device.Name), )) } func handleLogout(ar *api.Request) (msg string, err error) { purge := ar.URL.Query().Get("purge") != "" err = Logout(false, purge) switch { case err != nil: log.Warningf("spn/access: failed to logout: %s", err) return "", err case purge: return "Logged out and user data purged.", nil default: return "Logged out.", nil } } func handleGetUserProfile(ar *api.Request) (r record.Record, err error) { // Check if we are already authenticated. user, err := GetUser() if err != nil || user.State == account.UserStateNone { return nil, api.ErrorWithStatus( ErrNotLoggedIn, account.StatusInvalidAuth, ) } // Should we refresh the user profile? if ar.URL.Query().Get("refresh") != "" { user, _, err = UpdateUser() if err != nil { return nil, err } } return user, nil } ================================================ FILE: spn/access/client.go ================================================ package access import ( "context" "errors" "fmt" "net/http" "sync" "time" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/access/account" "github.com/safing/portmaster/spn/access/token" "github.com/safing/structures/dsd" ) // Client URLs. const ( AccountServer = "https://api.account.safing.io" LoginPath = "/api/v1/authenticate" UserProfilePath = "/api/v1/user/profile" TokenRequestSetupPath = "/api/v1/token/request/setup" //nolint:gosec TokenRequestIssuePath = "/api/v1/token/request/issue" //nolint:gosec HealthCheckPath = "/api/v1/health" defaultDataFormat = dsd.CBOR defaultRequestTimeout = 30 * time.Second ) var ( accountClient = &http.Client{} clientRequestLock sync.Mutex // EnableAfterLogin automatically enables the SPN subsystem/module after login. EnableAfterLogin = true ) type clientRequestOptions struct { method string url string send interface{} recv interface{} requestTimeout time.Duration dataFormat uint8 setAuthToken bool requireNextAuthToken bool logoutOnAuthError bool requestSetupFunc func(*http.Request) error } func makeClientRequest(opts *clientRequestOptions) (resp *http.Response, err error) { // Get request timeout. if opts.requestTimeout == 0 { opts.requestTimeout = defaultRequestTimeout } // Get context for request. var ctx context.Context var cancel context.CancelFunc ctx, cancel = context.WithTimeout(module.mgr.Ctx(), opts.requestTimeout) defer cancel() // Create new request. request, err := http.NewRequestWithContext(ctx, opts.method, opts.url, nil) if err != nil { return nil, fmt.Errorf("failed to create request structure: %w", err) } // Prepare body and content type. if opts.dataFormat == dsd.AUTO { opts.dataFormat = defaultDataFormat } if opts.send != nil { // Add data to body. err = dsd.DumpToHTTPRequest(request, opts.send, opts.dataFormat) if err != nil { return nil, fmt.Errorf("failed to add request body: %w", err) } } else { // Set requested HTTP response format. _, err = dsd.RequestHTTPResponseFormat(request, opts.dataFormat) if err != nil { return nil, fmt.Errorf("failed to set requested response format: %w", err) } } // Get auth token to apply to request. var authToken *AuthTokenRecord if opts.setAuthToken { authToken, err = GetAuthToken() if err != nil { return nil, ErrNotLoggedIn } authToken.Token.ApplyTo(request) } // Do any additional custom request setup. if opts.requestSetupFunc != nil { err = opts.requestSetupFunc(request) if err != nil { return nil, err } } // Make request. resp, err = accountClient.Do(request) if err != nil { updateUserWithFailedRequest(account.StatusConnectionError, false) tokenIssuerFailed() return nil, fmt.Errorf("http request failed: %w", err) } log.Debugf("spn/access: request to %s returned %s", request.URL, resp.Status) defer func() { _ = resp.Body.Close() }() // Handle request error. switch resp.StatusCode { case http.StatusOK, http.StatusCreated: // All good! case account.StatusInvalidAuth, account.StatusInvalidDevice: // Wrong username / password. updateUserWithFailedRequest(resp.StatusCode, true) return resp, ErrInvalidCredentials case account.StatusReachedDeviceLimit: // Device limit is reached. updateUserWithFailedRequest(resp.StatusCode, true) return resp, ErrDeviceLimitReached case account.StatusDeviceInactive: // Device is locked. updateUserWithFailedRequest(resp.StatusCode, true) return resp, ErrDeviceIsLocked default: updateUserWithFailedRequest(account.StatusUnknownError, false) tokenIssuerFailed() return resp, fmt.Errorf("unexpected reply: [%d] %s", resp.StatusCode, resp.Status) } // Save next auth token. if authToken != nil { err = authToken.Update(resp) if err != nil { if errors.Is(err, account.ErrMissingToken) { if opts.requireNextAuthToken { return resp, fmt.Errorf("failed to save next auth token: %w", err) } } else { return resp, fmt.Errorf("failed to save next auth token: %w", err) } } } else if opts.requireNextAuthToken { return resp, fmt.Errorf("failed to save next auth token: %w", account.ErrMissingToken) } // Load response data. if opts.recv != nil { _, err = dsd.LoadFromHTTPResponse(resp, opts.recv) if err != nil { return resp, fmt.Errorf("failed to parse response: %w", err) } } tokenIssuerIsFailing.UnSet() return resp, nil } func updateUserWithFailedRequest(statusCode int, disableSubscription bool) { // Get user from database. user, err := GetUser() if err != nil { if !errors.Is(err, ErrNotLoggedIn) { log.Warningf("spn/access: failed to get user to update with failed request: %s", err) } return } func() { user.Lock() defer user.Unlock() // Ignore update if user state is undefined or logged out. if user.State == "" || user.State == account.UserStateLoggedOut { return } // Disable the subscription if desired. if disableSubscription && user.Subscription != nil { user.Subscription.EndsAt = nil } // Update view with the status code and save user. user.UpdateView(statusCode) }() err = user.Save() if err != nil { log.Warningf("spn/access: failed to save user after update with failed request: %s", err) } } // Login logs the user into the SPN account with the given username and password. func Login(username, password string) (user *UserRecord, code int, err error) { clientRequestLock.Lock() defer clientRequestLock.Unlock() // Trigger account update when done. defer module.EventAccountUpdate.Submit(struct{}{}) // Get previous user. previousUser, err := GetUser() if err != nil { if !errors.Is(err, ErrNotLoggedIn) { log.Warningf("spn/access: failed to get previous for re-login: %s", err) } previousUser = nil } // Create request options. userAccount := &account.User{} requestOptions := &clientRequestOptions{ method: http.MethodPost, url: AccountServer + LoginPath, recv: userAccount, dataFormat: dsd.JSON, requestSetupFunc: func(request *http.Request) error { // Add username and password. request.SetBasicAuth(username, password) // Try to reuse the device ID, if the username matches the previous user. if previousUser != nil && username == previousUser.Username { request.Header.Set(account.AuthHeaderDevice, previousUser.Device.ID) } return nil }, } // Make request. resp, err := makeClientRequest(requestOptions) //nolint:bodyclose // Body is closed in function. if err != nil { if resp != nil && resp.StatusCode == account.StatusInvalidDevice { // Try again without the previous device ID. previousUser = nil log.Info("spn/access: retrying log in without re-using previous device ID") resp, err = makeClientRequest(requestOptions) //nolint:bodyclose // Body is closed in function. } if err != nil { if resp != nil { return nil, resp.StatusCode, err } return nil, 0, err } } // Save new user. now := time.Now() user = &UserRecord{ User: userAccount, LoggedInAt: &now, } user.UpdateView(0) err = user.Save() if err != nil { return user, resp.StatusCode, fmt.Errorf("failed to save new user profile: %w", err) } // Save initial auth token. err = SaveNewAuthToken(user.Device.ID, resp) if err != nil { return user, resp.StatusCode, fmt.Errorf("failed to save initial auth token: %w", err) } // Enable the SPN right after login. if user.MayUseSPN() && EnableAfterLogin { enableSPN() } log.Infof("spn/access: logged in as %q on device %q", user.Username, user.Device.Name) return user, resp.StatusCode, nil } // Logout logs the user out of the SPN account. // Specify "shallow" to keep user data in order to display data in the // UI - preferably when logged out be the server. // Specify "purge" in order to fully delete all user account data, even // the device ID so that logging in again will create a new device. func Logout(shallow, purge bool) error { clientRequestLock.Lock() defer clientRequestLock.Unlock() // Trigger account update when done. defer module.EventAccountUpdate.Submit(struct{}{}) // Clear caches. clearUserCaches() // Clear tokens. clearTokens() // Delete auth token. err := db.Delete(authTokenRecordKey) if err != nil && !errors.Is(err, database.ErrNotFound) { return fmt.Errorf("failed to delete auth token: %w", err) } // Delete all user data if purging. if purge { err := db.Delete(userRecordKey) if err != nil && !errors.Is(err, database.ErrNotFound) { return fmt.Errorf("failed to delete user: %w", err) } // Disable SPN when the user logs out directly. disableSPN() log.Info("spn/access: logged out and purged data") return nil } // Else, just update the user. user, err := GetUser() if err != nil { if errors.Is(err, ErrNotLoggedIn) { return nil } return fmt.Errorf("failed to load user for logout: %w", err) } func() { user.Lock() defer user.Unlock() if shallow { // Shallow logout: User stays logged in the UI to display status when // logged out from the Portmaster or Customer Hub. user.User.State = account.UserStateLoggedOut } else { // Proper logout: User is logged out from UI. // Reset all user data, except for username and device ID in order to log // into the same device again. user.User = &account.User{ Username: user.Username, Device: &account.Device{ ID: user.Device.ID, }, } user.LoggedInAt = &time.Time{} } user.UpdateView(0) }() err = user.Save() if err != nil { return fmt.Errorf("failed to save user for logout: %w", err) } if shallow { log.Info("spn/access: logged out shallow") } else { log.Info("spn/access: logged out") // Disable SPN when the user logs out directly. disableSPN() } return nil } // UpdateUser fetches the current user information from the server. func UpdateUser() (user *UserRecord, statusCode int, err error) { clientRequestLock.Lock() defer clientRequestLock.Unlock() // Trigger account update when done. defer module.EventAccountUpdate.Submit(struct{}{}) // Create request options. userData := &account.User{} requestOptions := &clientRequestOptions{ method: http.MethodGet, url: AccountServer + UserProfilePath, recv: userData, dataFormat: dsd.JSON, setAuthToken: true, requireNextAuthToken: true, logoutOnAuthError: true, } // Make request. resp, err := makeClientRequest(requestOptions) //nolint:bodyclose // Body is closed in function. if err != nil { if resp != nil { return nil, resp.StatusCode, err } return nil, 0, err } // Save to previous user, if exists. previousUser, err := GetUser() if err == nil { func() { previousUser.Lock() defer previousUser.Unlock() previousUser.User = userData previousUser.UpdateView(resp.StatusCode) }() err := previousUser.Save() if err != nil { log.Warningf("spn/access: failed to save updated user profile: %s", err) } // Notify user of nearing end of package. notifyOfPackageEnd(previousUser) log.Infof("spn/access: got user profile, updated existing") return previousUser, resp.StatusCode, nil } // Else, save as new user. now := time.Now() newUser := &UserRecord{ User: userData, LoggedInAt: &now, } newUser.UpdateView(resp.StatusCode) err = newUser.Save() if err != nil { log.Warningf("spn/access: failed to save new user profile: %s", err) } // Notify user of nearing end of package. notifyOfPackageEnd(newUser) log.Infof("spn/access: got user profile, saved as new") return newUser, resp.StatusCode, nil } // UpdateTokens fetches more tokens for handlers that need it. func UpdateTokens() error { clientRequestLock.Lock() defer clientRequestLock.Unlock() // Check if the user may request tokens. user, err := GetUser() if err != nil { return fmt.Errorf("failed to get user: %w", err) } if !user.MayUseTheSPN() { return ErrMayNotUseSPN } // Create setup request, return if not required. setupRequest, setupRequired := token.CreateSetupRequest() var setupResponse *token.SetupResponse if setupRequired { // Request setup data. setupResponse = &token.SetupResponse{} _, err := makeClientRequest(&clientRequestOptions{ //nolint:bodyclose // Body is closed in function. method: http.MethodPost, url: AccountServer + TokenRequestSetupPath, send: setupRequest, recv: setupResponse, dataFormat: dsd.MsgPack, setAuthToken: true, logoutOnAuthError: true, }) if err != nil { return fmt.Errorf("failed to request setup data: %w", err) } } // Create request for issuing new tokens. tokenRequest, requestRequired, err := token.CreateTokenRequest(setupResponse) if err != nil { return fmt.Errorf("failed to create token request: %w", err) } if !requestRequired { return nil } // Request issuing new tokens. issuedTokens := &token.IssuedTokens{} _, err = makeClientRequest(&clientRequestOptions{ //nolint:bodyclose // Body is closed in function. method: http.MethodPost, url: AccountServer + TokenRequestIssuePath, send: tokenRequest, recv: issuedTokens, dataFormat: dsd.MsgPack, setAuthToken: true, logoutOnAuthError: true, }) if err != nil { return fmt.Errorf("failed to request tokens: %w", err) } // Save tokens to handlers. err = token.ProcessIssuedTokens(issuedTokens) if err != nil { return fmt.Errorf("failed to process issued tokens: %w", err) } // Log new status. regular, fallback := GetTokenAmount(ExpandAndConnectZones) log.Infof( "spn/access: got new tokens, now at %d regular and %d fallback tokens for expand and connect", regular, fallback, ) return nil } var ( lastHealthCheckExpires time.Time lastHealthCheckLock sync.Mutex lastHealthCheckValidityDuration = 30 * time.Second ) func healthCheck() (ok bool) { lastHealthCheckLock.Lock() defer lastHealthCheckLock.Unlock() // Return current value if recently checked. if time.Now().Before(lastHealthCheckExpires) { return tokenIssuerIsFailing.IsNotSet() } // Check health. _, err := makeClientRequest(&clientRequestOptions{ //nolint:bodyclose // Body is closed in function. method: http.MethodGet, url: AccountServer + HealthCheckPath, }) if err != nil { log.Warningf("spn/access: token issuer health check failed: %s", err) } // Update health check expiry. lastHealthCheckExpires = time.Now().Add(lastHealthCheckValidityDuration) return tokenIssuerIsFailing.IsNotSet() } ================================================ FILE: spn/access/client_test.go ================================================ package access import ( "os" "testing" ) var ( testUsername = os.Getenv("SPN_TEST_USERNAME") testPassword = os.Getenv("SPN_TEST_PASSWORD") ) func TestClient(t *testing.T) { // Skip test in CI. if testing.Short() { t.Skip() } t.Parallel() if testUsername == "" || testPassword == "" { t.Fatal("test username or password not configured") } loginAndRefresh(t, true, 5) clearUserCaches() loginAndRefresh(t, false, 1) err := Logout(false, false) if err != nil { t.Fatalf("failed to log out: %s", err) } t.Logf("logged out") loginAndRefresh(t, true, 1) err = Logout(false, true) if err != nil { t.Fatalf("failed to log out: %s", err) } t.Logf("logged out with purge") loginAndRefresh(t, true, 1) } func loginAndRefresh(t *testing.T, doLogin bool, refreshTimes int) { t.Helper() if doLogin { _, _, err := Login(testUsername, testPassword) if err != nil { t.Fatalf("login failed: %s", err) } user, err := GetUser() if err != nil { t.Fatalf("failed to get user: %s", err) } t.Logf("user (from login): %+v", user.User) t.Logf("device (from login): %+v", user.User.Device) authToken, err := GetAuthToken() if err != nil { t.Fatalf("failed to get auth token: %s", err) } t.Logf("auth token: %+v", authToken.Token) } for range refreshTimes { user, _, err := UpdateUser() if err != nil { t.Fatalf("getting profile failed: %s", err) } t.Logf("user (from refresh): %+v", user.User) authToken, err := GetAuthToken() if err != nil { t.Fatalf("failed to get auth token: %s", err) } t.Logf("auth token: %+v", authToken.Token) } } ================================================ FILE: spn/access/database.go ================================================ package access import ( "errors" "fmt" "net/http" "sync" "time" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/spn/access/account" ) const ( userRecordKey = "core:spn/account/user" authTokenRecordKey = "core:spn/account/authtoken" //nolint:gosec // Not a credential. tokenStorageKeyTemplate = "core:spn/account/tokens/%s" //nolint:gosec // Not a credential. ) var db = database.NewInterface(&database.Options{ Local: true, Internal: true, }) // UserRecord holds a SPN user account. type UserRecord struct { record.Base sync.Mutex *account.User LastNotifiedOfEnd *time.Time LoggedInAt *time.Time } // MayUseSPN returns whether the user may currently use the SPN. func (user *UserRecord) MayUseSPN() bool { // Shadow this function in order to allow calls on a nil user. if user == nil || user.User == nil { return false } return user.User.MayUseSPN() } // MayUsePrioritySupport returns whether the user may currently use the priority support. func (user *UserRecord) MayUsePrioritySupport() bool { // Shadow this function in order to allow calls on a nil user. if user == nil || user.User == nil { return false } return user.User.MayUsePrioritySupport() } // MayUse returns whether the user may currently use the feature identified by // the given feature ID. // Leave feature ID empty to check without feature. func (user *UserRecord) MayUse(featureID account.FeatureID) bool { // Shadow this function in order to allow calls on a nil user. if user == nil || user.User == nil { return false } return user.User.MayUse(featureID) } // AuthTokenRecord holds an authentication token. type AuthTokenRecord struct { record.Base sync.Mutex Token *account.AuthToken } // GetToken returns the token from the record. func (authToken *AuthTokenRecord) GetToken() *account.AuthToken { authToken.Lock() defer authToken.Unlock() return authToken.Token } // SaveNewAuthToken saves a new auth token to the database. func SaveNewAuthToken(deviceID string, resp *http.Response) error { token, ok := account.GetNextTokenFromResponse(resp) if !ok { return account.ErrMissingToken } newAuthToken := &AuthTokenRecord{ Token: &account.AuthToken{ Device: deviceID, Token: token, }, } return newAuthToken.Save() } // Update updates an existing auth token with the next token from a response. func (authToken *AuthTokenRecord) Update(resp *http.Response) error { token, ok := account.GetNextTokenFromResponse(resp) if !ok { return account.ErrMissingToken } // Update token with new account.AuthToken. func() { authToken.Lock() defer authToken.Unlock() authToken.Token = &account.AuthToken{ Device: authToken.Token.Device, Token: token, } }() return authToken.Save() } var ( accountCacheLock sync.Mutex cachedUser *UserRecord cachedUserSet bool cachedAuthToken *AuthTokenRecord ) func clearUserCaches() { accountCacheLock.Lock() defer accountCacheLock.Unlock() cachedUser = nil cachedUserSet = false cachedAuthToken = nil } // GetUser returns the current user account. // Returns nil when no user is logged in. func GetUser() (*UserRecord, error) { // Check cache. accountCacheLock.Lock() defer accountCacheLock.Unlock() if cachedUserSet { if cachedUser == nil { return nil, ErrNotLoggedIn } return cachedUser, nil } // Load from disk. r, err := db.Get(userRecordKey) if err != nil { if errors.Is(err, database.ErrNotFound) { cachedUser = nil cachedUserSet = true return nil, ErrNotLoggedIn } return nil, err } // Unwrap record. if r.IsWrapped() { // only allocate a new struct, if we need it newUser := &UserRecord{} err = record.Unwrap(r, newUser) if err != nil { return nil, err } cachedUser = newUser cachedUserSet = true return cachedUser, nil } // Or adjust type. newUser, ok := r.(*UserRecord) if !ok { return nil, fmt.Errorf("record not of type *UserRecord, but %T", r) } cachedUser = newUser cachedUserSet = true return cachedUser, nil } // Save saves the User. func (user *UserRecord) Save() error { // Update cache. accountCacheLock.Lock() defer accountCacheLock.Unlock() cachedUser = user cachedUserSet = true // Update view if unset. if user.View == nil { user.UpdateView(0) } // Set, check and update metadata. if !user.KeyIsSet() { user.SetKey(userRecordKey) } user.UpdateMeta() return db.Put(user) } // GetAuthToken returns the current auth token. func GetAuthToken() (*AuthTokenRecord, error) { // Check cache. accountCacheLock.Lock() defer accountCacheLock.Unlock() if cachedAuthToken != nil { return cachedAuthToken, nil } // Load from disk. r, err := db.Get(authTokenRecordKey) if err != nil { return nil, err } // Unwrap record. if r.IsWrapped() { // only allocate a new struct, if we need it newAuthRecord := &AuthTokenRecord{} err = record.Unwrap(r, newAuthRecord) if err != nil { return nil, err } cachedAuthToken = newAuthRecord return newAuthRecord, nil } // Or adjust type. newAuthRecord, ok := r.(*AuthTokenRecord) if !ok { return nil, fmt.Errorf("record not of type *AuthTokenRecord, but %T", r) } cachedAuthToken = newAuthRecord return newAuthRecord, nil } // Save saves the auth token to the database. func (authToken *AuthTokenRecord) Save() error { // Update cache. accountCacheLock.Lock() defer accountCacheLock.Unlock() cachedAuthToken = authToken // Set, check and update metadata. if !authToken.KeyIsSet() { authToken.SetKey(authTokenRecordKey) } authToken.UpdateMeta() authToken.Meta().MakeSecret() authToken.Meta().MakeCrownJewel() return db.Put(authToken) } ================================================ FILE: spn/access/features.go ================================================ package access import "github.com/safing/portmaster/spn/access/account" // Feature describes a notable part of the program. type Feature struct { Name string ID string RequiredFeatureID account.FeatureID ConfigKey string ConfigScope string InPackage *Package Comment string Beta bool ComingSoon bool icon string } // Package combines a set of features. type Package struct { Name string HexColor string InfoURL string } var ( infoURL = "https://safing.io/pricing/" packageFree = &Package{ Name: "Free", HexColor: "#ffffff", InfoURL: infoURL, } packagePlus = &Package{ Name: "Plus", HexColor: "#2fcfae", InfoURL: infoURL, } packagePro = &Package{ Name: "Pro", HexColor: "#029ad0", InfoURL: infoURL, } features = []Feature{ { Name: "Secure DNS", ID: "dns", ConfigScope: "dns/", InPackage: packageFree, icon: ` `, }, { Name: "Privacy Filter", ID: "filter", ConfigScope: "filter/", InPackage: packageFree, icon: ` `, }, { Name: "Network History", ID: string(account.FeatureHistory), RequiredFeatureID: account.FeatureHistory, ConfigKey: "history/enable", ConfigScope: "history/", InPackage: packagePlus, icon: ` `, }, { Name: "Bandwidth Visibility", ID: string(account.FeatureBWVis), RequiredFeatureID: account.FeatureBWVis, InPackage: packagePlus, Beta: true, icon: ` `, }, { Name: "Safing Support", ID: string(account.FeatureSafingSupport), RequiredFeatureID: account.FeatureSafingSupport, InPackage: packagePlus, icon: ` `, }, { Name: "Safing Privacy Network", ID: string(account.FeatureSPN), RequiredFeatureID: account.FeatureSPN, ConfigKey: "spn/enable", ConfigScope: "spn/", InPackage: packagePro, icon: ` `, }, } ) ================================================ FILE: spn/access/module.go ================================================ package access import ( "errors" "fmt" "sync/atomic" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/access/account" "github.com/safing/portmaster/spn/access/token" "github.com/safing/portmaster/spn/conf" ) type Access struct { mgr *mgr.Manager instance instance updateAccountWorkerMgr *mgr.WorkerMgr EventAccountUpdate *mgr.EventMgr[struct{}] } func (a *Access) Manager() *mgr.Manager { return a.mgr } func (a *Access) Start() error { return start() } func (a *Access) Stop() error { return stop() } var ( module *Access shimLoaded atomic.Bool tokenIssuerIsFailing = abool.New() tokenIssuerRetryDuration = 10 * time.Minute // AccountUpdateEvent is fired when the account has changed in any way. AccountUpdateEvent = "account update" ) // Errors. var ( ErrDeviceIsLocked = errors.New("device is locked") ErrDeviceLimitReached = errors.New("device limit reached") ErrFallbackNotAvailable = errors.New("fallback tokens not available, token issuer is online") ErrInvalidCredentials = errors.New("invalid credentials") ErrMayNotUseSPN = errors.New("may not use SPN") ErrNotLoggedIn = errors.New("not logged in") ) func prep() error { // Register API handlers. if conf.Integrated() { err := registerAPIEndpoints() if err != nil { return err } } return nil } func start() error { // Initialize zones. if err := InitializeZones(); err != nil { return err } if conf.Integrated() { // Add config listener to enable/disable SPN. module.instance.Config().EventConfigChange.AddCallback("spn enable check", func(wc *mgr.WorkerCtx, s struct{}) (bool, error) { // Do not do anything when we are shutting down. if module.instance.IsShuttingDown() { return true, nil } enabled := config.GetAsBool("spn/enable", false) if enabled() { log.Info("spn: starting SPN") module.mgr.Go("ensure SPN is started", module.instance.SPNGroup().EnsureStartedWorker) } else { log.Info("spn: stopping SPN") module.mgr.Go("ensure SPN is stopped", module.instance.SPNGroup().EnsureStoppedWorker) } return false, nil }) // Load tokens from database. loadTokens() // Check if we need to enable SPN now. enabled := config.GetAsBool("spn/enable", false) if enabled() { module.mgr.Go("ensure SPN is started", module.instance.SPNGroup().EnsureStartedWorker) } // Register new task. module.updateAccountWorkerMgr.Delay(1 * time.Minute) } return nil } func stop() error { if conf.Integrated() { // Make sure SPN is stopped before we proceed. err := module.mgr.Do("ensure SPN is shut down", module.instance.SPNGroup().EnsureStoppedWorker) if err != nil { log.Errorf("access: stop SPN: %s", err) } // Store tokens to database. storeTokens() } // Reset zones. token.ResetRegistry() return nil } // UpdateAccount updates the user account and fetches new tokens, if needed. func UpdateAccount(_ *mgr.WorkerCtx) error { // Schedule next call - this will change if other conditions are met bellow. module.updateAccountWorkerMgr.Delay(24 * time.Hour) // Retry sooner if the token issuer is failing. defer func() { if tokenIssuerIsFailing.IsSet() { module.updateAccountWorkerMgr.Delay(tokenIssuerRetryDuration) } }() // Get current user. u, err := GetUser() if err == nil { // Do not update if we just updated. if time.Since(time.Unix(u.Meta().Modified, 0)) < 2*time.Minute { return nil } } u, _, err = UpdateUser() if err != nil { return fmt.Errorf("failed to update user profile: %w", err) } err = UpdateTokens() if err != nil { return fmt.Errorf("failed to get tokens: %w", err) } // Schedule next check. switch { case u == nil: // No user. case u.Subscription == nil: // No subscription. case u.Subscription.EndsAt == nil: // Subscription not active case time.Until(*u.Subscription.EndsAt) < 24*time.Hour && time.Since(*u.Subscription.EndsAt) < 24*time.Hour: // Update account every hour for 24h hours before and after the subscription ends. module.updateAccountWorkerMgr.Delay(1 * time.Hour) case u.Subscription.NextBillingDate == nil: // No auto-subscription. case time.Until(*u.Subscription.NextBillingDate) < 24*time.Hour && time.Since(*u.Subscription.NextBillingDate) < 24*time.Hour: // Update account every hour 24h hours before and after the next billing date. module.updateAccountWorkerMgr.Delay(1 * time.Hour) } return nil } func enableSPN() { err := config.SetConfigOption("spn/enable", true) if err != nil { log.Warningf("spn/access: failed to enable the SPN during login: %s", err) } } func disableSPN() { err := config.SetConfigOption("spn/enable", false) if err != nil { log.Warningf("spn/access: failed to disable the SPN during logout: %s", err) } } // TokenIssuerIsFailing returns whether token issuing is currently failing. func TokenIssuerIsFailing() bool { return tokenIssuerIsFailing.IsSet() } func tokenIssuerFailed() { if !tokenIssuerIsFailing.SetToIf(false, true) { return } module.updateAccountWorkerMgr.Delay(tokenIssuerRetryDuration) } // IsLoggedIn returns whether a User is currently logged in. func (user *UserRecord) IsLoggedIn() bool { user.Lock() defer user.Unlock() switch user.State { case account.UserStateNone, account.UserStateLoggedOut: return false default: return true } } // MayUseTheSPN returns whether the currently logged in User may use the SPN. func (user *UserRecord) MayUseTheSPN() bool { user.Lock() defer user.Unlock() return user.User.MayUseSPN() } // New returns a new Access module. func New(instance instance) (*Access, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Access") module = &Access{ mgr: m, instance: instance, EventAccountUpdate: mgr.NewEventMgr[struct{}](AccountUpdateEvent, m), updateAccountWorkerMgr: m.NewWorkerMgr("update account", UpdateAccount, nil), } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface { Config() *config.Config SPNGroup() *mgr.ExtendedGroup IsShuttingDown() bool } ================================================ FILE: spn/access/module_test.go ================================================ package access import ( "fmt" "os" "testing" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database" _ "github.com/safing/portmaster/base/database/storage/hashmap" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" ) type testInstance struct { config *config.Config } func (stub *testInstance) Config() *config.Config { return stub.config } func (stub *testInstance) SPNGroup() *mgr.ExtendedGroup { return nil } func (stub *testInstance) Stopping() bool { return false } func (stub *testInstance) IsShuttingDown() bool { return false } func (stub *testInstance) SetCmdLineOperation(f func() error) {} func (stub *testInstance) DataDir() string { return _dataDir } var _dataDir string func TestMain(m *testing.M) { exitCode := 1 defer func() { if exitCode != 0 { os.Exit(exitCode) } }() var err error // Create a temporary directory for the data _dataDir, err = os.MkdirTemp("", "") if err != nil { fmt.Printf("failed to create temporary data directory: %s", err) return // Exit with error } defer func() { _ = os.RemoveAll(_dataDir) }() // Initialize the database module err = database.Initialize(_dataDir) if err != nil { fmt.Printf("failed to initialize database module: %s", err) return // Exit with error } _, err = database.Register(&database.Database{ Name: "core", Description: "Holds core data, such as settings and profiles", StorageType: "hashmap", }) if err != nil { fmt.Printf("failed to register core database: %s", err) return // Exit with error } // Initialize the instance instance := &testInstance{} instance.config, err = config.New(instance) if err != nil { fmt.Printf("failed to create config module: %s", err) return // Exit with error } module, err = New(instance) if err != nil { fmt.Printf("failed to create access module: %s", err) return // Exit with error } err = instance.config.Start() if err != nil { fmt.Printf("failed to start config module: %s", err) return // Exit with error } err = module.Start() if err != nil { fmt.Printf("failed to start access module: %s", err) return // Exit with error } conf.EnableClient(true) m.Run() exitCode = 0 // success } ================================================ FILE: spn/access/notify.go ================================================ package access import ( "fmt" "strings" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" ) const ( day = 24 * time.Hour week = 7 * day endOfPackageNearNotifID = "access:end-of-package-near" ) func notifyOfPackageEnd(u *UserRecord) { // TODO: Check if subscription auto-renews. // Skip if there is not active subscription or if it has ended already. switch { case u.Subscription == nil, // No subscription. u.Subscription.EndsAt == nil, // Subscription not active. u.Subscription.NextBillingDate != nil, // Subscription is auto-renewing. time.Now().After(*u.Subscription.EndsAt): // Subscription has ended. return } // Calculate durations. sinceLastNotified := 52 * week // Never. if u.LastNotifiedOfEnd != nil { sinceLastNotified = time.Since(*u.LastNotifiedOfEnd) } untilEnd := time.Until(*u.Subscription.EndsAt) // Notify every two days in the week before end. notifType := notifications.Info switch { case untilEnd < week && sinceLastNotified > 2*day: // Notify 7, 5, 3 and 1 days before end. if untilEnd < 4*day { notifType = notifications.Warning } fallthrough case u.CurrentPlan != nil && u.CurrentPlan.Months >= 6 && untilEnd < 4*week && sinceLastNotified > week: // Notify 4, 3 and 2 weeks before end - on long running packages. // Get names and messages. packageNameTitle := "Portmaster Package" if u.CurrentPlan != nil { packageNameTitle = u.CurrentPlan.Name } packageNameBody := packageNameTitle if !strings.HasSuffix(packageNameBody, " Package") { packageNameBody += " Package" } var endsText string daysUntilEnd := untilEnd / day switch daysUntilEnd { //nolint:exhaustive case 0: endsText = "today" case 1: endsText = "tomorrow" default: endsText = fmt.Sprintf("in %d days", daysUntilEnd) } // Send notification. notifications.Notify(¬ifications.Notification{ EventID: endOfPackageNearNotifID, Type: notifType, Title: fmt.Sprintf("%s About to Expire", packageNameTitle), Message: fmt.Sprintf( "Your current %s ends %s. Extend it to keep your full privacy protections.", packageNameBody, endsText, ), ShowOnSystem: notifType == notifications.Warning, AvailableActions: []*notifications.Action{ { Text: "Open Account Page", Type: notifications.ActionTypeOpenURL, Payload: "https://account.safing.io", }, { ID: "ack", Text: "Got it!", }, }, }) // Save that we sent a notification. now := time.Now() u.LastNotifiedOfEnd = &now err := u.Save() if err != nil { log.Warningf("spn/access: failed to save user after sending subscription ending soon notification: %s", err) } } } ================================================ FILE: spn/access/op_auth.go ================================================ package access import ( "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/access/token" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" ) // OpTypeAccessCodeAuth is the type ID of the auth operation. const OpTypeAccessCodeAuth = "auth" func init() { terminal.RegisterOpType(terminal.OperationFactory{ Type: OpTypeAccessCodeAuth, Start: checkAccessCode, }) } // AuthorizeOp is used to authorize a session. type AuthorizeOp struct { terminal.OneOffOperationBase } // Type returns the type ID. func (op *AuthorizeOp) Type() string { return OpTypeAccessCodeAuth } // AuthorizeToTerminal starts an authorization operation. func AuthorizeToTerminal(t terminal.Terminal) (*AuthorizeOp, *terminal.Error) { op := &AuthorizeOp{} op.Init() newToken, err := GetToken(ExpandAndConnectZones) if err != nil { return nil, terminal.ErrInternalError.With("failed to get access token: %w", err) } tErr := t.StartOperation(op, container.New(newToken.Raw()), 10*time.Second) if tErr != nil { return nil, terminal.ErrInternalError.With("failed to init auth op: %w", tErr) } return op, nil } func checkAccessCode(t terminal.Terminal, opID uint32, initData *container.Container) (terminal.Operation, *terminal.Error) { // Parse provided access token. receivedToken, err := token.ParseRawToken(initData.CompileData()) if err != nil { return nil, terminal.ErrMalformedData.With("failed to parse access token: %w", err) } // Check if token is valid. granted, err := VerifyToken(receivedToken) if err != nil { return nil, terminal.ErrPermissionDenied.With("invalid access token: %w", err) } // Get the authorizing terminal for applying the granted permission. authTerm, ok := t.(terminal.AuthorizingTerminal) if !ok { return nil, terminal.ErrIncorrectUsage.With("terminal does not handle authorization") } // Grant permissions. authTerm.GrantPermission(granted) log.Debugf("spn/access: granted %s permissions via %s zone", t.FmtID(), receivedToken.Zone) // End successfully. return nil, terminal.ErrExplicitAck } ================================================ FILE: spn/access/storage.go ================================================ package access import ( "context" "errors" "fmt" "time" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/access/token" "github.com/safing/structures/dsd" ) func loadTokens() { for _, zone := range persistentZones { // Get handler of zone. handler, ok := token.GetHandler(zone) if !ok { log.Warningf("spn/access: could not find zone %s for loading tokens", zone) continue } // Get data from database. r, err := db.Get(fmt.Sprintf(tokenStorageKeyTemplate, zone)) if err != nil { if errors.Is(err, database.ErrNotFound) { log.Debugf("spn/access: no %s tokens to load", zone) } else { log.Warningf("spn/access: failed to load %s tokens: %s", zone, err) } continue } // Get wrapper. wrapper, ok := r.(*record.Wrapper) if !ok { log.Warningf("spn/access: failed to parse %s tokens: expected wrapper, got %T", zone, r) continue } // Load into handler. err = handler.Load(wrapper.Data) if err != nil { log.Warningf("spn/access: failed to load %s tokens: %s", zone, err) } log.Infof("spn/access: loaded %d %s tokens", handler.Amount(), zone) } } func storeTokens() { for _, zone := range persistentZones { // Get handler of zone. handler, ok := token.GetHandler(zone) if !ok { log.Warningf("spn/access: could not find zone %s for storing tokens", zone) continue } // Generate storage key. storageKey := fmt.Sprintf(tokenStorageKeyTemplate, zone) // Check if there is data to save. amount := handler.Amount() if amount == 0 { // Remove possible old entry from database. err := db.Delete(storageKey) if err != nil { log.Warningf("spn/access: failed to delete possible old %s tokens from storage: %s", zone, err) } log.Debugf("spn/access: no %s tokens to store", zone) continue } // Export data. data, err := handler.Save() if err != nil { log.Warningf("spn/access: failed to export %s tokens for storing: %s", zone, err) continue } // Wrap data into raw record. r, err := record.NewWrapper(storageKey, nil, dsd.RAW, data) if err != nil { log.Warningf("spn/access: failed to prepare %s token export for storing: %s", zone, err) continue } // Let tokens expire after one month. // This will regularly happen when we switch zones. r.UpdateMeta() r.Meta().MakeSecret() r.Meta().MakeCrownJewel() r.Meta().SetRelativateExpiry(30 * 86400) // Save to database. err = db.Put(r) if err != nil { log.Warningf("spn/access: failed to store %s tokens: %s", zone, err) continue } log.Infof("spn/access: stored %d %s tokens", amount, zone) } } func clearTokens() { for _, zone := range persistentZones { // Get handler of zone. handler, ok := token.GetHandler(zone) if !ok { log.Warningf("spn/access: could not find zone %s for clearing tokens", zone) continue } // Clear tokens. handler.Clear() } // Purge database storage prefix. ctx, cancel := context.WithTimeout(module.mgr.Ctx(), 10*time.Second) defer cancel() n, err := db.Purge(ctx, query.New(fmt.Sprintf(tokenStorageKeyTemplate, ""))) if err != nil { log.Warningf("spn/access: failed to clear token storages: %s", err) return } log.Infof("spn/access: cleared %d token storages", n) } ================================================ FILE: spn/access/token/errors.go ================================================ package token import "errors" // Errors. var ( ErrEmpty = errors.New("token storage is empty") ErrNoZone = errors.New("no zone specified") ErrTokenInvalid = errors.New("token is invalid") ErrTokenMalformed = errors.New("token malformed") ErrTokenUsed = errors.New("token already used") ErrZoneMismatch = errors.New("zone mismatch") ErrZoneTaken = errors.New("zone taken") ErrZoneUnknown = errors.New("zone unknown") ) ================================================ FILE: spn/access/token/module_test.go ================================================ package token import ( "fmt" "os" "testing" "github.com/safing/portmaster/base/rng" ) type testInstance struct{} func TestMain(m *testing.M) { rng, err := rng.New(testInstance{}) if err != nil { fmt.Printf("failed to create RNG module: %s", err) os.Exit(1) } err = rng.Start() if err != nil { fmt.Printf("failed to start RNG module: %s", err) os.Exit(1) } m.Run() } ================================================ FILE: spn/access/token/pblind.go ================================================ package token import ( "crypto/elliptic" "crypto/rand" "errors" "fmt" "math" "math/big" mrand "math/rand" "sync" "github.com/mr-tron/base58" "github.com/rot256/pblind" "github.com/safing/structures/container" "github.com/safing/structures/dsd" ) const pblindSecretSize = 32 // PBlindToken is token based on the pblind library. type PBlindToken struct { Serial int `json:"N,omitempty"` Token []byte `json:"T,omitempty"` Signature *pblind.Signature `json:"S,omitempty"` } // Pack packs the token. func (pbt *PBlindToken) Pack() ([]byte, error) { return dsd.Dump(pbt, dsd.CBOR) } // UnpackPBlindToken unpacks the token. func UnpackPBlindToken(token []byte) (*PBlindToken, error) { t := &PBlindToken{} _, err := dsd.Load(token, t) if err != nil { return nil, err } return t, nil } // PBlindHandler is a handler for the pblind tokens. type PBlindHandler struct { sync.Mutex opts *PBlindOptions publicKey *pblind.PublicKey privateKey *pblind.SecretKey storageLock sync.Mutex Storage []*PBlindToken // Client request state. requestStateLock sync.Mutex requestState []RequestState } // PBlindOptions are options for the PBlindHandler. type PBlindOptions struct { Zone string CurveName string Curve elliptic.Curve PublicKey string PrivateKey string BatchSize int UseSerials bool RandomizeOrder bool Fallback bool SignalShouldRequest func(Handler) DoubleSpendProtection func([]byte) error } // PBlindSignerState is a signer state. type PBlindSignerState struct { signers []*pblind.StateSigner } // PBlindSetupResponse is a setup response. type PBlindSetupResponse struct { Msgs []*pblind.Message1 } // PBlindTokenRequest is a token request. type PBlindTokenRequest struct { Msgs []*pblind.Message2 } // IssuedPBlindTokens are issued pblind tokens. type IssuedPBlindTokens struct { Msgs []*pblind.Message3 } // RequestState is a request state. type RequestState struct { Token []byte State *pblind.StateRequester } // NewPBlindHandler creates a new pblind handler. func NewPBlindHandler(opts PBlindOptions) (*PBlindHandler, error) { pbh := &PBlindHandler{ opts: &opts, } // Check curve, get from name. if opts.Curve == nil { switch opts.CurveName { case "P-256": opts.Curve = elliptic.P256() case "P-384": opts.Curve = elliptic.P384() case "P-521": opts.Curve = elliptic.P521() default: return nil, errors.New("no curve supplied") } } else if opts.CurveName != "" { return nil, errors.New("both curve and curve name supplied") } // Load keys. switch { case pbh.opts.PrivateKey != "": keyData, err := base58.Decode(pbh.opts.PrivateKey) if err != nil { return nil, fmt.Errorf("failed to decode private key: %w", err) } pivateKey := pblind.SecretKeyFromBytes(pbh.opts.Curve, keyData) pbh.privateKey = &pivateKey publicKey := pbh.privateKey.GetPublicKey() pbh.publicKey = &publicKey // Check public key if also provided. if pbh.opts.PublicKey != "" { if pbh.opts.PublicKey != base58.Encode(pbh.publicKey.Bytes()) { return nil, errors.New("private and public mismatch") } } case pbh.opts.PublicKey != "": keyData, err := base58.Decode(pbh.opts.PublicKey) if err != nil { return nil, fmt.Errorf("failed to decode public key: %w", err) } publicKey, err := pblind.PublicKeyFromBytes(pbh.opts.Curve, keyData) if err != nil { return nil, fmt.Errorf("failed to decode public key: %w", err) } pbh.publicKey = &publicKey default: return nil, errors.New("no key supplied") } return pbh, nil } func (pbh *PBlindHandler) makeInfo(serial int) (*pblind.Info, error) { // Gather data for info. infoData := container.New() infoData.AppendAsBlock([]byte(pbh.opts.Zone)) if pbh.opts.UseSerials { infoData.AppendInt(serial) } // Compress to point. info, err := pblind.CompressInfo(pbh.opts.Curve, infoData.CompileData()) if err != nil { return nil, fmt.Errorf("failed to compress info: %w", err) } return &info, nil } // Zone returns the zone name. func (pbh *PBlindHandler) Zone() string { return pbh.opts.Zone } // ShouldRequest returns whether the new tokens should be requested. func (pbh *PBlindHandler) ShouldRequest() bool { pbh.storageLock.Lock() defer pbh.storageLock.Unlock() return pbh.shouldRequest() } func (pbh *PBlindHandler) shouldRequest() bool { // Return true if storage is at or below 10%. return len(pbh.Storage) == 0 || pbh.opts.BatchSize/len(pbh.Storage) > 10 } // Amount returns the current amount of tokens in this handler. func (pbh *PBlindHandler) Amount() int { pbh.storageLock.Lock() defer pbh.storageLock.Unlock() return len(pbh.Storage) } // IsFallback returns whether this handler should only be used as a fallback. func (pbh *PBlindHandler) IsFallback() bool { return pbh.opts.Fallback } // CreateSetup sets up signers for a request. func (pbh *PBlindHandler) CreateSetup() (state *PBlindSignerState, setupResponse *PBlindSetupResponse, err error) { state = &PBlindSignerState{ signers: make([]*pblind.StateSigner, pbh.opts.BatchSize), } setupResponse = &PBlindSetupResponse{ Msgs: make([]*pblind.Message1, pbh.opts.BatchSize), } // Go through the batch. for i := range pbh.opts.BatchSize { info, err := pbh.makeInfo(i + 1) if err != nil { return nil, nil, fmt.Errorf("failed to create info #%d: %w", i, err) } // Create signer. signer, err := pblind.CreateSigner(*pbh.privateKey, *info) if err != nil { return nil, nil, fmt.Errorf("failed to create signer #%d: %w", i, err) } state.signers[i] = signer // Create request setup. setupMsg, err := signer.CreateMessage1() if err != nil { return nil, nil, fmt.Errorf("failed to create setup msg #%d: %w", i, err) } setupResponse.Msgs[i] = &setupMsg } return state, setupResponse, nil } // CreateTokenRequest creates a token request to be sent to the token server. func (pbh *PBlindHandler) CreateTokenRequest(requestSetup *PBlindSetupResponse) (request *PBlindTokenRequest, err error) { // Check request setup data. if len(requestSetup.Msgs) != pbh.opts.BatchSize { return nil, fmt.Errorf("invalid request setup msg count of %d", len(requestSetup.Msgs)) } // Lock and reset the request state. pbh.requestStateLock.Lock() defer pbh.requestStateLock.Unlock() pbh.requestState = make([]RequestState, pbh.opts.BatchSize) request = &PBlindTokenRequest{ Msgs: make([]*pblind.Message2, pbh.opts.BatchSize), } // Go through the batch. for i := range pbh.opts.BatchSize { // Check if we have setup data. if requestSetup.Msgs[i] == nil { return nil, fmt.Errorf("missing setup data #%d", i) } // Generate secret token. token := make([]byte, pblindSecretSize) n, err := rand.Read(token) //nolint:gosec // False positive - check the imports. if err != nil { return nil, fmt.Errorf("failed to get random token #%d: %w", i, err) } if n != pblindSecretSize { return nil, fmt.Errorf("failed to get full random token #%d: only got %d bytes", i, n) } pbh.requestState[i].Token = token // Create public metadata. info, err := pbh.makeInfo(i + 1) if err != nil { return nil, fmt.Errorf("failed to make token info #%d: %w", i, err) } // Create request and request state. requester, err := pblind.CreateRequester(*pbh.publicKey, *info, token) if err != nil { return nil, fmt.Errorf("failed to create request state #%d: %w", i, err) } pbh.requestState[i].State = requester err = requester.ProcessMessage1(*requestSetup.Msgs[i]) if err != nil { return nil, fmt.Errorf("failed to process setup message #%d: %w", i, err) } // Create request message. requestMsg, err := requester.CreateMessage2() if err != nil { return nil, fmt.Errorf("failed to create request message #%d: %w", i, err) } request.Msgs[i] = &requestMsg } return request, nil } // IssueTokens sign the requested tokens. func (pbh *PBlindHandler) IssueTokens(state *PBlindSignerState, request *PBlindTokenRequest) (response *IssuedPBlindTokens, err error) { // Check request data. if len(request.Msgs) != pbh.opts.BatchSize { return nil, fmt.Errorf("invalid request msg count of %d", len(request.Msgs)) } if len(state.signers) != pbh.opts.BatchSize { return nil, fmt.Errorf("invalid request state count of %d", len(request.Msgs)) } // Create response. response = &IssuedPBlindTokens{ Msgs: make([]*pblind.Message3, pbh.opts.BatchSize), } // Go through the batch. for i := range pbh.opts.BatchSize { // Check if we have request data. if request.Msgs[i] == nil { return nil, fmt.Errorf("missing request data #%d", i) } // Process request msg. err = state.signers[i].ProcessMessage2(*request.Msgs[i]) if err != nil { return nil, fmt.Errorf("failed to process request msg #%d: %w", i, err) } // Issue token. responseMsg, err := state.signers[i].CreateMessage3() if err != nil { return nil, fmt.Errorf("failed to issue token #%d: %w", i, err) } response.Msgs[i] = &responseMsg } return response, nil } // ProcessIssuedTokens processes the issued token from the server. func (pbh *PBlindHandler) ProcessIssuedTokens(issuedTokens *IssuedPBlindTokens) error { // Check data. if len(issuedTokens.Msgs) != pbh.opts.BatchSize { return fmt.Errorf("invalid issued token count of %d", len(issuedTokens.Msgs)) } // Step 1: Process issued tokens. // Lock and reset the request state. pbh.requestStateLock.Lock() defer pbh.requestStateLock.Unlock() defer func() { pbh.requestState = make([]RequestState, pbh.opts.BatchSize) }() finalizedTokens := make([]*PBlindToken, pbh.opts.BatchSize) // Go through the batch. for i := range pbh.opts.BatchSize { // Finalize token. err := pbh.requestState[i].State.ProcessMessage3(*issuedTokens.Msgs[i]) if err != nil { return fmt.Errorf("failed to create final signature #%d: %w", i, err) } // Get and check final signature. signature, err := pbh.requestState[i].State.Signature() if err != nil { return fmt.Errorf("failed to create final signature #%d: %w", i, err) } info, err := pbh.makeInfo(i + 1) if err != nil { return fmt.Errorf("failed to make token info #%d: %w", i, err) } if !pbh.publicKey.Check(signature, *info, pbh.requestState[i].Token) { return fmt.Errorf("invalid signature on #%d", i) } // Save to temporary slice. newToken := &PBlindToken{ Token: pbh.requestState[i].Token, Signature: &signature, } if pbh.opts.UseSerials { newToken.Serial = i + 1 } finalizedTokens[i] = newToken } // Step 2: Randomize received tokens if pbh.opts.RandomizeOrder { rInt, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) if err != nil { return fmt.Errorf("failed to get seed for shuffle: %w", err) } mr := mrand.New(mrand.NewSource(rInt.Int64())) //nolint:gosec mr.Shuffle(len(finalizedTokens), func(i, j int) { finalizedTokens[i], finalizedTokens[j] = finalizedTokens[j], finalizedTokens[i] }) } // Step 3: Add tokens to storage. // Wait for all processing to be complete, as using tokens from a faulty // batch can be dangerous, as the server could be doing this purposely to // create conditions that may benefit an attacker. pbh.storageLock.Lock() defer pbh.storageLock.Unlock() // Add finalized tokens to storage. pbh.Storage = append(pbh.Storage, finalizedTokens...) return nil } // GetToken returns a token. func (pbh *PBlindHandler) GetToken() (token *Token, err error) { pbh.storageLock.Lock() defer pbh.storageLock.Unlock() // Check if we have supply. if len(pbh.Storage) == 0 { return nil, ErrEmpty } // Pack token. data, err := pbh.Storage[0].Pack() if err != nil { return nil, fmt.Errorf("failed to pack token: %w", err) } // Shift to next token. pbh.Storage = pbh.Storage[1:] // Check if we should signal that we should request tokens. if pbh.opts.SignalShouldRequest != nil && pbh.shouldRequest() { pbh.opts.SignalShouldRequest(pbh) } return &Token{ Zone: pbh.opts.Zone, Data: data, }, nil } // Verify verifies the given token. func (pbh *PBlindHandler) Verify(token *Token) error { // Check if zone matches. if token.Zone != pbh.opts.Zone { return ErrZoneMismatch } // Unpack token. t, err := UnpackPBlindToken(token.Data) if err != nil { return fmt.Errorf("%w: %w", ErrTokenMalformed, err) } // Check if serial is valid. switch { case pbh.opts.UseSerials && t.Serial > 0 && t.Serial <= pbh.opts.BatchSize: // Using serials in accepted range. case !pbh.opts.UseSerials && t.Serial == 0: // Not using serials and serial is zero. default: return fmt.Errorf("%w: invalid serial", ErrTokenMalformed) } // Build info for checking signature. info, err := pbh.makeInfo(t.Serial) if err != nil { return fmt.Errorf("%w: %w", ErrTokenMalformed, err) } // Check signature. if !pbh.publicKey.Check(*t.Signature, *info, t.Token) { return ErrTokenInvalid } // Check for double spending. if pbh.opts.DoubleSpendProtection != nil { if err := pbh.opts.DoubleSpendProtection(t.Token); err != nil { return fmt.Errorf("%w: %w", ErrTokenUsed, err) } } return nil } // PBlindStorage is a storage for pblind tokens. type PBlindStorage struct { Storage []*PBlindToken } // Save serializes and returns the current tokens. func (pbh *PBlindHandler) Save() ([]byte, error) { pbh.storageLock.Lock() defer pbh.storageLock.Unlock() if len(pbh.Storage) == 0 { return nil, ErrEmpty } s := &PBlindStorage{ Storage: pbh.Storage, } return dsd.Dump(s, dsd.CBOR) } // Load loads the given tokens into the handler. func (pbh *PBlindHandler) Load(data []byte) error { pbh.storageLock.Lock() defer pbh.storageLock.Unlock() s := &PBlindStorage{} _, err := dsd.Load(data, s) if err != nil { return err } // Check signatures on load. for _, t := range s.Storage { // Build info for checking signature. info, err := pbh.makeInfo(t.Serial) if err != nil { return err } // Check signature. if !pbh.publicKey.Check(*t.Signature, *info, t.Token) { return ErrTokenInvalid } } pbh.Storage = s.Storage return nil } // Clear clears all the tokens in the handler. func (pbh *PBlindHandler) Clear() { pbh.storageLock.Lock() defer pbh.storageLock.Unlock() pbh.Storage = nil } ================================================ FILE: spn/access/token/pblind_gen_test.go ================================================ package token import ( "crypto/elliptic" "fmt" "testing" "github.com/mr-tron/base58" "github.com/rot256/pblind" ) func TestGeneratePBlindKeys(t *testing.T) { t.Parallel() for _, curve := range []elliptic.Curve{ elliptic.P256(), elliptic.P384(), elliptic.P521(), } { privateKey, err := pblind.NewSecretKey(curve) if err != nil { t.Fatal(err) } publicKey := privateKey.GetPublicKey() fmt.Printf( "%s (%dbit) private key: %s\n", curve.Params().Name, curve.Params().BitSize, base58.Encode(privateKey.Bytes()), ) fmt.Printf( "%s (%dbit) public key: %s\n", curve.Params().Name, curve.Params().BitSize, base58.Encode(publicKey.Bytes()), ) } } ================================================ FILE: spn/access/token/pblind_test.go ================================================ package token import ( "crypto/elliptic" "encoding/asn1" "testing" "time" "github.com/rot256/pblind" ) const PBlindTestZone = "test-pblind" func init() { // Combined testing config. h, err := NewPBlindHandler(PBlindOptions{ Zone: PBlindTestZone, Curve: elliptic.P256(), PrivateKey: "HbwGtLsqek1Fdwuz1MhNQfiY7tj9EpWHeMWHPZ9c6KYY", UseSerials: true, BatchSize: 1000, RandomizeOrder: true, }) if err != nil { panic(err) } err = RegisterPBlindHandler(h) if err != nil { panic(err) } } func TestPBlind(t *testing.T) { t.Parallel() opts := &PBlindOptions{ Zone: PBlindTestZone, Curve: elliptic.P256(), UseSerials: true, BatchSize: 1000, RandomizeOrder: true, } // Issuer opts.PrivateKey = "HbwGtLsqek1Fdwuz1MhNQfiY7tj9EpWHeMWHPZ9c6KYY" issuer, err := NewPBlindHandler(*opts) if err != nil { t.Fatal(err) } // Client opts.PrivateKey = "" opts.PublicKey = "285oMDh3w5mxyFgpmmURifKfhkcqwwsdnePpPZ6Nqm8cc" client, err := NewPBlindHandler(*opts) if err != nil { t.Fatal(err) } // Verifier verifier, err := NewPBlindHandler(*opts) if err != nil { t.Fatal(err) } // Play through the whole use case. signerState, setupResponse, err := issuer.CreateSetup() if err != nil { t.Fatal(err) } request, err := client.CreateTokenRequest(setupResponse) if err != nil { t.Fatal(err) } issuedTokens, err := issuer.IssueTokens(signerState, request) if err != nil { t.Fatal(err) } err = client.ProcessIssuedTokens(issuedTokens) if err != nil { t.Fatal(err) } token, err := client.GetToken() if err != nil { t.Fatal(err) } err = verifier.Verify(token) if err != nil { t.Fatal(err) } } func TestPBlindLibrary(t *testing.T) { t.Parallel() // generate a key-pair curve := elliptic.P256() sk, _ := pblind.NewSecretKey(curve) pk := sk.GetPublicKey() msgStr := []byte("128b_accesstoken") infoStr := []byte("v=1 serial=12345") info, err := pblind.CompressInfo(curve, infoStr) if err != nil { t.Fatal(err) } totalStart := time.Now() batchSize := 1000 signers := make([]*pblind.StateSigner, batchSize) requesters := make([]*pblind.StateRequester, batchSize) toServer := make([][]byte, batchSize) toClient := make([][]byte, batchSize) // Create signers and prep requests. start := time.Now() for i := range batchSize { signer, err := pblind.CreateSigner(sk, info) if err != nil { t.Fatal(err) } signers[i] = signer msg1S, err := signer.CreateMessage1() if err != nil { t.Fatal(err) } ser1S, err := asn1.Marshal(msg1S) if err != nil { t.Fatal(err) } toClient[i] = ser1S } t.Logf("created %d signers and request preps in %s", batchSize, time.Since(start)) t.Logf("sending %d bytes to client", lenOfByteSlices(toClient)) // Create requesters and create requests. start = time.Now() for i := range batchSize { requester, err := pblind.CreateRequester(pk, info, msgStr) if err != nil { t.Fatal(err) } requesters[i] = requester var msg1R pblind.Message1 _, err = asn1.Unmarshal(toClient[i], &msg1R) if err != nil { t.Fatal(err) } err = requester.ProcessMessage1(msg1R) if err != nil { t.Fatal(err) } msg2R, err := requester.CreateMessage2() if err != nil { t.Fatal(err) } ser2R, err := asn1.Marshal(msg2R) if err != nil { t.Fatal(err) } toServer[i] = ser2R } t.Logf("created %d requesters and requests in %s", batchSize, time.Since(start)) t.Logf("sending %d bytes to server", lenOfByteSlices(toServer)) // Sign requests start = time.Now() for i := range batchSize { var msg2S pblind.Message2 _, err = asn1.Unmarshal(toServer[i], &msg2S) if err != nil { t.Fatal(err) } err = signers[i].ProcessMessage2(msg2S) if err != nil { t.Fatal(err) } msg3S, err := signers[i].CreateMessage3() if err != nil { t.Fatal(err) } ser3S, err := asn1.Marshal(msg3S) if err != nil { t.Fatal(err) } toClient[i] = ser3S } t.Logf("signed %d requests in %s", batchSize, time.Since(start)) t.Logf("sending %d bytes to client", lenOfByteSlices(toClient)) // Verify signed requests start = time.Now() for i := range batchSize { var msg3R pblind.Message3 _, err := asn1.Unmarshal(toClient[i], &msg3R) if err != nil { t.Fatal(err) } err = requesters[i].ProcessMessage3(msg3R) if err != nil { t.Fatal(err) } signature, err := requesters[i].Signature() if err != nil { t.Fatal(err) } sig, err := asn1.Marshal(signature) if err != nil { t.Fatal(err) } toServer[i] = sig // check signature if !pk.Check(signature, info, msgStr) { t.Fatal("signature invalid") } } t.Logf("finalized and verified %d signed tokens in %s", batchSize, time.Since(start)) t.Logf("stored %d signed tokens in %d bytes", batchSize, lenOfByteSlices(toServer)) // Verify on server start = time.Now() for i := range batchSize { var sig pblind.Signature _, err := asn1.Unmarshal(toServer[i], &sig) if err != nil { t.Fatal(err) } // check signature if !pk.Check(sig, info, msgStr) { t.Fatal("signature invalid") } } t.Logf("verified %d signed tokens in %s", batchSize, time.Since(start)) t.Logf("process complete") t.Logf("simulated the whole process for %d tokens in %s", batchSize, time.Since(totalStart)) } func lenOfByteSlices(v [][]byte) (length int) { for _, s := range v { length += len(s) } return } ================================================ FILE: spn/access/token/registry.go ================================================ package token import "sync" // Handler represents a token handling system. type Handler interface { // Zone returns the zone name. Zone() string // ShouldRequest returns whether the new tokens should be requested. ShouldRequest() bool // Amount returns the current amount of tokens in this handler. Amount() int // IsFallback returns whether this handler should only be used as a fallback. IsFallback() bool // GetToken returns a token. GetToken() (token *Token, err error) // Verify verifies the given token. Verify(token *Token) error // Save serializes and returns the current tokens. Save() ([]byte, error) // Load loads the given tokens into the handler. Load(data []byte) error // Clear clears all the tokens in the handler. Clear() } var ( registry map[string]Handler pblindRegistry []*PBlindHandler scrambleRegistry []*ScrambleHandler registryLock sync.RWMutex ) func init() { initRegistry() } func initRegistry() { registry = make(map[string]Handler) pblindRegistry = make([]*PBlindHandler, 0, 1) scrambleRegistry = make([]*ScrambleHandler, 0, 1) } // RegisterPBlindHandler registers a pblind handler with the registry. func RegisterPBlindHandler(h *PBlindHandler) error { registryLock.Lock() defer registryLock.Unlock() if err := registerHandler(h, h.opts.Zone); err != nil { return err } pblindRegistry = append(pblindRegistry, h) return nil } // RegisterScrambleHandler registers a scramble handler with the registry. func RegisterScrambleHandler(h *ScrambleHandler) error { registryLock.Lock() defer registryLock.Unlock() if err := registerHandler(h, h.opts.Zone); err != nil { return err } scrambleRegistry = append(scrambleRegistry, h) return nil } func registerHandler(h Handler, zone string) error { if zone == "" { return ErrNoZone } _, ok := registry[zone] if ok { return ErrZoneTaken } registry[zone] = h return nil } // GetHandler returns the handler of the given zone. func GetHandler(zone string) (handler Handler, ok bool) { registryLock.RLock() defer registryLock.RUnlock() handler, ok = registry[zone] return } // ResetRegistry resets the token handler registry. func ResetRegistry() { registryLock.Lock() defer registryLock.Unlock() initRegistry() } // RegistrySize returns the amount of handler registered. func RegistrySize() int { registryLock.Lock() defer registryLock.Unlock() return len(registry) } ================================================ FILE: spn/access/token/request.go ================================================ package token import ( "crypto/rand" "errors" "fmt" "github.com/mr-tron/base58" ) const sessionIDSize = 32 // RequestHandlingState is a request handling state. type RequestHandlingState struct { SessionID string PBlind map[string]*PBlindSignerState } // SetupRequest is a setup request. type SetupRequest struct { PBlind map[string]struct{} `json:"PB,omitempty"` } // SetupResponse is a setup response. type SetupResponse struct { SessionID string `json:"ID,omitempty"` PBlind map[string]*PBlindSetupResponse `json:"PB,omitempty"` } // TokenRequest is a token request. type TokenRequest struct { //nolint:golint // Be explicit. SessionID string `json:"ID,omitempty"` PBlind map[string]*PBlindTokenRequest `json:"PB,omitempty"` Scramble map[string]*ScrambleTokenRequest `json:"S,omitempty"` } // IssuedTokens are issued tokens. type IssuedTokens struct { PBlind map[string]*IssuedPBlindTokens `json:"PB,omitempty"` Scramble map[string]*IssuedScrambleTokens `json:"SC,omitempty"` } // CreateSetupRequest creates a combined setup request for all registered tokens, if needed. func CreateSetupRequest() (request *SetupRequest, setupRequired bool) { registryLock.RLock() defer registryLock.RUnlock() request = &SetupRequest{ PBlind: make(map[string]struct{}, len(pblindRegistry)), } // Go through handlers and create request setups. for _, pblindHandler := range pblindRegistry { // Check if we need to request with this handler. if pblindHandler.ShouldRequest() { request.PBlind[pblindHandler.Zone()] = struct{}{} setupRequired = true } } return } // HandleSetupRequest handles a setup request for all registered tokens. func HandleSetupRequest(request *SetupRequest) (*RequestHandlingState, *SetupResponse, error) { registryLock.RLock() defer registryLock.RUnlock() // Generate session token. randomID := make([]byte, sessionIDSize) n, err := rand.Read(randomID) if err != nil { return nil, nil, fmt.Errorf("failed to generate session ID: %w", err) } if n != sessionIDSize { return nil, nil, fmt.Errorf("failed to get full session ID: only got %d bytes", n) } sessionID := base58.Encode(randomID) // Create state and response. state := &RequestHandlingState{ SessionID: sessionID, PBlind: make(map[string]*PBlindSignerState, len(pblindRegistry)), } setup := &SetupResponse{ SessionID: sessionID, PBlind: make(map[string]*PBlindSetupResponse, len(pblindRegistry)), } // Go through handlers and create setups. for _, pblindHandler := range pblindRegistry { // Check if we have a request for this handler. _, ok := request.PBlind[pblindHandler.Zone()] if !ok { continue } plindState, pblindSetup, err := pblindHandler.CreateSetup() if err != nil { return nil, nil, fmt.Errorf("failed to create setup for %s: %w", pblindHandler.Zone(), err) } state.PBlind[pblindHandler.Zone()] = plindState setup.PBlind[pblindHandler.Zone()] = pblindSetup } return state, setup, nil } // CreateTokenRequest creates a token request for all registered tokens. func CreateTokenRequest(setup *SetupResponse) (request *TokenRequest, requestRequired bool, err error) { registryLock.RLock() defer registryLock.RUnlock() // Check setup data. if setup != nil && setup.SessionID == "" { return nil, false, errors.New("setup data is missing a session ID") } // Create token request. request = &TokenRequest{ PBlind: make(map[string]*PBlindTokenRequest, len(pblindRegistry)), Scramble: make(map[string]*ScrambleTokenRequest, len(scrambleRegistry)), } if setup != nil { request.SessionID = setup.SessionID } // Go through handlers and create requests. if setup != nil { for _, pblindHandler := range pblindRegistry { // Check if we have setup data for this handler. pblindSetup, ok := setup.PBlind[pblindHandler.Zone()] if !ok { // TODO: Abort if we should have received request data. continue } // Create request. pblindRequest, err := pblindHandler.CreateTokenRequest(pblindSetup) if err != nil { return nil, false, fmt.Errorf("failed to create token request for %s: %w", pblindHandler.Zone(), err) } requestRequired = true request.PBlind[pblindHandler.Zone()] = pblindRequest } } for _, scrambleHandler := range scrambleRegistry { // Check if we need to request with this handler. if scrambleHandler.ShouldRequest() { requestRequired = true request.Scramble[scrambleHandler.Zone()] = scrambleHandler.CreateTokenRequest() } } return request, requestRequired, nil } // IssueTokens issues tokens for all registered tokens. func IssueTokens(state *RequestHandlingState, request *TokenRequest) (response *IssuedTokens, err error) { registryLock.RLock() defer registryLock.RUnlock() // Create token response. response = &IssuedTokens{ PBlind: make(map[string]*IssuedPBlindTokens, len(pblindRegistry)), Scramble: make(map[string]*IssuedScrambleTokens, len(scrambleRegistry)), } // Go through handlers and create requests. for _, pblindHandler := range pblindRegistry { // Check if we have all the data for issuing. pblindState, ok := state.PBlind[pblindHandler.Zone()] if !ok { continue } pblindRequest, ok := request.PBlind[pblindHandler.Zone()] if !ok { continue } // Issue tokens. pblindTokens, err := pblindHandler.IssueTokens(pblindState, pblindRequest) if err != nil { return nil, fmt.Errorf("failed to issue tokens for %s: %w", pblindHandler.Zone(), err) } response.PBlind[pblindHandler.Zone()] = pblindTokens } for _, scrambleHandler := range scrambleRegistry { // Check if we have all the data for issuing. scrambleRequest, ok := request.Scramble[scrambleHandler.Zone()] if !ok { continue } // Issue tokens. scrambleTokens, err := scrambleHandler.IssueTokens(scrambleRequest) if err != nil { return nil, fmt.Errorf("failed to issue tokens for %s: %w", scrambleHandler.Zone(), err) } response.Scramble[scrambleHandler.Zone()] = scrambleTokens } return response, nil } // ProcessIssuedTokens processes issued tokens for all registered tokens. func ProcessIssuedTokens(response *IssuedTokens) error { registryLock.RLock() defer registryLock.RUnlock() // Go through handlers and create requests. for _, pblindHandler := range pblindRegistry { // Check if we received tokens. pblindResponse, ok := response.PBlind[pblindHandler.Zone()] if !ok { continue } // Process issued tokens. err := pblindHandler.ProcessIssuedTokens(pblindResponse) if err != nil { return fmt.Errorf("failed to process issued tokens for %s: %w", pblindHandler.Zone(), err) } } for _, scrambleHandler := range scrambleRegistry { // Check if we received tokens. scrambleResponse, ok := response.Scramble[scrambleHandler.Zone()] if !ok { continue } // Process issued tokens. err := scrambleHandler.ProcessIssuedTokens(scrambleResponse) if err != nil { return fmt.Errorf("failed to process issued tokens for %s: %w", scrambleHandler.Zone(), err) } } return nil } ================================================ FILE: spn/access/token/request_test.go ================================================ package token import ( "testing" "time" "github.com/safing/structures/dsd" ) func TestFull(t *testing.T) { t.Parallel() testStart := time.Now() // Roundtrip 1 start := time.Now() setupRequest, setupRequired := CreateSetupRequest() if !setupRequired { t.Fatal("setup should be required") } setupRequestData, err := dsd.Dump(setupRequest, dsd.CBOR) if err != nil { t.Fatal(err) } setupRequest = nil // nolint:ineffassign,wastedassign // Just to be sure. t.Logf("setupRequest: %s, %d bytes", time.Since(start), len(setupRequestData)) start = time.Now() loadedSetupRequest := &SetupRequest{} _, err = dsd.Load(setupRequestData, loadedSetupRequest) if err != nil { t.Fatal(err) } serverState, setupResponse, err := HandleSetupRequest(loadedSetupRequest) if err != nil { t.Fatal(err) } setupResponseData, err := dsd.Dump(setupResponse, dsd.CBOR) if err != nil { t.Fatal(err) } setupResponse = nil // nolint:ineffassign,wastedassign // Just to be sure. t.Logf("setupResponse: %s, %d bytes", time.Since(start), len(setupResponseData)) // Roundtrip 2 start = time.Now() loadedSetupResponse := &SetupResponse{} _, err = dsd.Load(setupResponseData, loadedSetupResponse) if err != nil { t.Fatal(err) } request, requestRequired, err := CreateTokenRequest(loadedSetupResponse) if err != nil { t.Fatal(err) } if !requestRequired { t.Fatal("request should be required") } requestData, err := dsd.Dump(request, dsd.CBOR) if err != nil { t.Fatal(err) } request = nil // nolint:ineffassign,wastedassign // Just to be sure. t.Logf("request: %s, %d bytes", time.Since(start), len(requestData)) start = time.Now() loadedRequest := &TokenRequest{} _, err = dsd.Load(requestData, loadedRequest) if err != nil { t.Fatal(err) } response, err := IssueTokens(serverState, loadedRequest) if err != nil { t.Fatal(err) } responseData, err := dsd.Dump(response, dsd.CBOR) if err != nil { t.Fatal(err) } response = nil // nolint:ineffassign,wastedassign // Just to be sure. t.Logf("response: %s, %d bytes", time.Since(start), len(responseData)) start = time.Now() loadedResponse := &IssuedTokens{} _, err = dsd.Load(responseData, loadedResponse) if err != nil { t.Fatal(err) } err = ProcessIssuedTokens(loadedResponse) if err != nil { t.Fatal(err) } t.Logf("processing: %s", time.Since(start)) // Token Usage for _, testZone := range []string{ PBlindTestZone, ScrambleTestZone, } { start = time.Now() token, err := GetToken(testZone) if err != nil { t.Fatal(err) } tokenData := token.Raw() token = nil // nolint:wastedassign // Just to be sure. loadedToken, err := ParseRawToken(tokenData) if err != nil { t.Fatal(err) } err = VerifyToken(loadedToken) if err != nil { t.Fatal(err) } t.Logf("using %s token: %s", testZone, time.Since(start)) } t.Logf("full simulation took %s", time.Since(testStart)) } ================================================ FILE: spn/access/token/scramble.go ================================================ package token import ( "fmt" "sync" "github.com/mr-tron/base58" "github.com/safing/jess/lhash" "github.com/safing/structures/dsd" ) const ( scrambleSecretSize = 32 ) // ScrambleToken is token based on hashing. type ScrambleToken struct { Token []byte } // Pack packs the token. func (pbt *ScrambleToken) Pack() ([]byte, error) { return pbt.Token, nil } // UnpackScrambleToken unpacks the token. func UnpackScrambleToken(token []byte) (*ScrambleToken, error) { return &ScrambleToken{Token: token}, nil } // ScrambleHandler is a handler for the scramble tokens. type ScrambleHandler struct { sync.Mutex opts *ScrambleOptions storageLock sync.Mutex Storage []*ScrambleToken verifiersLock sync.RWMutex verifiers map[string]*ScrambleToken } // ScrambleOptions are options for the ScrambleHandler. type ScrambleOptions struct { Zone string Algorithm lhash.Algorithm InitialTokens []string InitialVerifiers []string Fallback bool } // ScrambleTokenRequest is a token request. type ScrambleTokenRequest struct{} // IssuedScrambleTokens are issued scrambled tokens. type IssuedScrambleTokens struct { Tokens []*ScrambleToken } // NewScrambleHandler creates a new scramble handler. func NewScrambleHandler(opts ScrambleOptions) (*ScrambleHandler, error) { sh := &ScrambleHandler{ opts: &opts, verifiers: make(map[string]*ScrambleToken, len(opts.InitialTokens)+len(opts.InitialVerifiers)), } // Add initial tokens. sh.Storage = make([]*ScrambleToken, len(opts.InitialTokens)) for i, token := range opts.InitialTokens { // Add to storage. tokenData, err := base58.Decode(token) if err != nil { return nil, fmt.Errorf("failed to decode initial token %q: %w", token, err) } sh.Storage[i] = &ScrambleToken{ Token: tokenData, } // Add to verifiers. scrambledToken := lhash.Digest(sh.opts.Algorithm, tokenData).Bytes() sh.verifiers[string(scrambledToken)] = sh.Storage[i] } // Add initial verifiers. for _, verifier := range opts.InitialVerifiers { verifierData, err := base58.Decode(verifier) if err != nil { return nil, fmt.Errorf("failed to decode verifier %q: %w", verifier, err) } sh.verifiers[string(verifierData)] = &ScrambleToken{} } return sh, nil } // Zone returns the zone name. func (sh *ScrambleHandler) Zone() string { return sh.opts.Zone } // ShouldRequest returns whether the new tokens should be requested. func (sh *ScrambleHandler) ShouldRequest() bool { sh.storageLock.Lock() defer sh.storageLock.Unlock() return len(sh.Storage) == 0 } // Amount returns the current amount of tokens in this handler. func (sh *ScrambleHandler) Amount() int { sh.storageLock.Lock() defer sh.storageLock.Unlock() return len(sh.Storage) } // IsFallback returns whether this handler should only be used as a fallback. func (sh *ScrambleHandler) IsFallback() bool { return sh.opts.Fallback } // CreateTokenRequest creates a token request to be sent to the token server. func (sh *ScrambleHandler) CreateTokenRequest() (request *ScrambleTokenRequest) { return &ScrambleTokenRequest{} } // IssueTokens sign the requested tokens. func (sh *ScrambleHandler) IssueTokens(request *ScrambleTokenRequest) (response *IssuedScrambleTokens, err error) { // Copy the storage. tokens := make([]*ScrambleToken, len(sh.Storage)) copy(tokens, sh.Storage) return &IssuedScrambleTokens{ Tokens: tokens, }, nil } // ProcessIssuedTokens processes the issued token from the server. func (sh *ScrambleHandler) ProcessIssuedTokens(issuedTokens *IssuedScrambleTokens) error { sh.verifiersLock.RLock() defer sh.verifiersLock.RUnlock() // Validate tokens. for i, newToken := range issuedTokens.Tokens { // Scramle token. scrambledToken := lhash.Digest(sh.opts.Algorithm, newToken.Token).Bytes() // Check if token is valid. _, ok := sh.verifiers[string(scrambledToken)] if !ok { return fmt.Errorf("invalid token on #%d", i) } } // Copy to storage. sh.Storage = issuedTokens.Tokens return nil } // Verify verifies the given token. func (sh *ScrambleHandler) Verify(token *Token) error { if token.Zone != sh.opts.Zone { return ErrZoneMismatch } // Hash the data. scrambledToken := lhash.Digest(sh.opts.Algorithm, token.Data).Bytes() sh.verifiersLock.RLock() defer sh.verifiersLock.RUnlock() // Check if token is valid. _, ok := sh.verifiers[string(scrambledToken)] if !ok { return ErrTokenInvalid } return nil } // GetToken returns a token. func (sh *ScrambleHandler) GetToken() (*Token, error) { sh.storageLock.Lock() defer sh.storageLock.Unlock() if len(sh.Storage) == 0 { return nil, ErrEmpty } return &Token{ Zone: sh.opts.Zone, Data: sh.Storage[0].Token, }, nil } // ScrambleStorage is a storage for scramble tokens. type ScrambleStorage struct { Storage []*ScrambleToken } // Save serializes and returns the current tokens. func (sh *ScrambleHandler) Save() ([]byte, error) { sh.storageLock.Lock() defer sh.storageLock.Unlock() if len(sh.Storage) == 0 { return nil, ErrEmpty } s := &ScrambleStorage{ Storage: sh.Storage, } return dsd.Dump(s, dsd.CBOR) } // Load loads the given tokens into the handler. func (sh *ScrambleHandler) Load(data []byte) error { sh.storageLock.Lock() defer sh.storageLock.Unlock() s := &ScrambleStorage{} _, err := dsd.Load(data, s) if err != nil { return err } sh.Storage = s.Storage return nil } // Clear clears all the tokens in the handler. func (sh *ScrambleHandler) Clear() { sh.storageLock.Lock() defer sh.storageLock.Unlock() sh.Storage = nil } ================================================ FILE: spn/access/token/scramble_gen_test.go ================================================ package token import ( "crypto/rand" "fmt" "testing" "github.com/mr-tron/base58" "github.com/safing/jess/lhash" ) type genAlgs struct { alg lhash.Algorithm name string } func TestGenerateScrambleKeys(t *testing.T) { t.Parallel() for _, alg := range []genAlgs{ {alg: lhash.SHA2_256, name: "SHA2_256"}, {alg: lhash.SHA3_256, name: "SHA3_256"}, {alg: lhash.SHA3_512, name: "SHA3_512"}, {alg: lhash.BLAKE2b_256, name: "BLAKE2b_256"}, } { token := make([]byte, scrambleSecretSize) n, err := rand.Read(token) if err != nil { t.Fatal(err) } if n != scrambleSecretSize { t.Fatalf("only got %d bytes", n) } scrambledToken := lhash.Digest(alg.alg, token).Bytes() fmt.Printf( "%s secret token: %s\n", alg.name, base58.Encode(token), ) fmt.Printf( "%s scrambled (public) token: %s\n", alg.name, base58.Encode(scrambledToken), ) } } ================================================ FILE: spn/access/token/scramble_test.go ================================================ package token import ( "testing" "github.com/safing/jess/lhash" ) const ScrambleTestZone = "test-scramble" func init() { // Combined testing config. h, err := NewScrambleHandler(ScrambleOptions{ Zone: ScrambleTestZone, Algorithm: lhash.SHA2_256, InitialTokens: []string{"2VqJ8BvDew1tUpytZhR7tuvq7ToPpW3tQtHvu3veE3iW"}, }) if err != nil { panic(err) } err = RegisterScrambleHandler(h) if err != nil { panic(err) } } func TestScramble(t *testing.T) { t.Parallel() opts := &ScrambleOptions{ Zone: ScrambleTestZone, Algorithm: lhash.SHA2_256, } // Issuer opts.InitialTokens = []string{"2VqJ8BvDew1tUpytZhR7tuvq7ToPpW3tQtHvu3veE3iW"} issuer, err := NewScrambleHandler(*opts) if err != nil { t.Fatal(err) } // Client opts.InitialTokens = nil opts.InitialVerifiers = []string{"Cy9tz37Xq9NiXGDRU9yicjGU62GjXskE9KqUmuoddSxaE3"} client, err := NewScrambleHandler(*opts) if err != nil { t.Fatal(err) } // Verifier verifier, err := NewScrambleHandler(*opts) if err != nil { t.Fatal(err) } // Play through the whole use case. request := client.CreateTokenRequest() if err != nil { t.Fatal(err) } issuedTokens, err := issuer.IssueTokens(request) if err != nil { t.Fatal(err) } err = client.ProcessIssuedTokens(issuedTokens) if err != nil { t.Fatal(err) } token, err := client.GetToken() if err != nil { t.Fatal(err) } err = verifier.Verify(token) if err != nil { t.Fatal(err) } } ================================================ FILE: spn/access/token/token.go ================================================ package token import ( "bytes" "errors" "fmt" "strings" "github.com/mr-tron/base58" "github.com/safing/structures/container" ) // Token represents a token, consisting of a zone (name) and some data. type Token struct { Zone string Data []byte } // GetToken returns a token of the given zone. func GetToken(zone string) (*Token, error) { handler, ok := GetHandler(zone) if !ok { return nil, ErrZoneUnknown } return handler.GetToken() } // VerifyToken verifies the given token. func VerifyToken(token *Token) error { handler, ok := GetHandler(token.Zone) if !ok { return ErrZoneUnknown } return handler.Verify(token) } // Raw returns the raw format of the token. func (c *Token) Raw() []byte { cont := container.New() cont.Append([]byte(c.Zone)) cont.Append([]byte(":")) cont.Append(c.Data) return cont.CompileData() } // String returns the stringified format of the token. func (c *Token) String() string { return c.Zone + ":" + base58.Encode(c.Data) } // ParseRawToken parses a raw token. func ParseRawToken(code []byte) (*Token, error) { splitted := bytes.SplitN(code, []byte(":"), 2) if len(splitted) < 2 { return nil, errors.New("invalid code format: zone/data separator missing") } return &Token{ Zone: string(splitted[0]), Data: splitted[1], }, nil } // ParseToken parses a stringified token. func ParseToken(code string) (*Token, error) { splitted := strings.SplitN(code, ":", 2) if len(splitted) < 2 { return nil, errors.New("invalid code format: zone/data separator missing") } data, err := base58.Decode(splitted[1]) if err != nil { return nil, fmt.Errorf("invalid code format: %w", err) } return &Token{ Zone: splitted[0], Data: data, }, nil } ================================================ FILE: spn/access/token/token_test.go ================================================ package token import ( "testing" "github.com/safing/portmaster/base/rng" ) func TestToken(t *testing.T) { t.Parallel() randomData, err := rng.Bytes(32) if err != nil { t.Fatal(err) } c := &Token{ Zone: "test", Data: randomData, } s := c.String() _, err = ParseToken(s) if err != nil { t.Fatal(err) } r := c.Raw() _, err = ParseRawToken(r) if err != nil { t.Fatal(err) } } ================================================ FILE: spn/access/zones.go ================================================ package access import ( "errors" "fmt" "os" "strings" "github.com/tevino/abool" "github.com/safing/jess/lhash" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/access/token" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/terminal" ) var ( // ExpandAndConnectZones are the zones that grant access to the expand and // connect operations. ExpandAndConnectZones = []string{"pblind1", "alpha2", "fallback1"} zonePermissions = map[string]terminal.Permission{ "pblind1": terminal.AddPermissions(terminal.MayExpand, terminal.MayConnect), "alpha2": terminal.AddPermissions(terminal.MayExpand, terminal.MayConnect), "fallback1": terminal.AddPermissions(terminal.MayExpand, terminal.MayConnect), } persistentZones = ExpandAndConnectZones enableTestMode = abool.New() ) // EnableTestMode enables the test mode, leading the access module to only // register a test zone. // This should not be used to test the access module itself. func EnableTestMode() { enableTestMode.Set() } // InitializeZones initialized the permission zones. // It initializes the test zones, if EnableTestMode was called before. // Must only be called once. func InitializeZones() error { // Check if we are testing. if enableTestMode.IsSet() { return initializeTestZone() } // Special client zone config. var requestSignalHandler func(token.Handler) if conf.Integrated() { requestSignalHandler = shouldRequestTokensHandler } // Register pblind1 as the first primary zone. ph, err := token.NewPBlindHandler(token.PBlindOptions{ Zone: "pblind1", CurveName: "P-256", PublicKey: "eXoJXzXbM66UEsM2eVi9HwyBPLMfVnNrC7gNrsfMUJDs", UseSerials: true, BatchSize: 1000, RandomizeOrder: true, SignalShouldRequest: requestSignalHandler, }) if err != nil { return fmt.Errorf("failed to create pblind1 token handler: %w", err) } err = token.RegisterPBlindHandler(ph) if err != nil { return fmt.Errorf("failed to register pblind1 token handler: %w", err) } // Register fallback1 zone as fallback when the issuer is not available. sh, err := token.NewScrambleHandler(token.ScrambleOptions{ Zone: "fallback1", Algorithm: lhash.BLAKE2b_256, InitialVerifiers: []string{"ZwkQoaAttVBMURzeLzNXokFBMAMUUwECfM1iHojcVKBmjk"}, Fallback: true, }) if err != nil { return fmt.Errorf("failed to create fallback1 token handler: %w", err) } err = token.RegisterScrambleHandler(sh) if err != nil { return fmt.Errorf("failed to register fallback1 token handler: %w", err) } // Register alpha2 zone for transition phase. sh, err = token.NewScrambleHandler(token.ScrambleOptions{ Zone: "alpha2", Algorithm: lhash.BLAKE2b_256, InitialVerifiers: []string{"ZwojEvXZmAv7SZdNe7m94Xzu7F9J8vULqKf7QYtoTpN2tH"}, }) if err != nil { return fmt.Errorf("failed to create alpha2 token handler: %w", err) } err = token.RegisterScrambleHandler(sh) if err != nil { return fmt.Errorf("failed to register alpha2 token handler: %w", err) } return nil } func initializeTestZone() error { // Safeguard checks if we should really enable the test zone. if !strings.HasSuffix(os.Args[0], ".test") { return errors.New("tried to enable test mode, but no test binary was detected") } if token.RegistrySize() > 0 { return fmt.Errorf("tried to enable test zone, but %d handlers are already registered", token.RegistrySize()) } // Reset zones. token.ResetRegistry() // Set eligible zones. ExpandAndConnectZones = []string{"unittest"} zonePermissions = map[string]terminal.Permission{ "unittest": terminal.AddPermissions(terminal.MayExpand, terminal.MayConnect), } // Register unittest zone as for testing. sh, err := token.NewScrambleHandler(token.ScrambleOptions{ Zone: "unittest", Algorithm: lhash.BLAKE2b_256, InitialTokens: []string{"6jFqLA93uSLL52utGKrvctG3ZfopSQ8WFqjsRK1c2Svt"}, InitialVerifiers: []string{"ZwoEoL59sr81s7WnF2vydGzjeejE3u8CqVafig1NTQzUr7"}, }) if err != nil { return fmt.Errorf("failed to create unittest token handler: %w", err) } err = token.RegisterScrambleHandler(sh) if err != nil { return fmt.Errorf("failed to register unittest token handler: %w", err) } return nil } func shouldRequestTokensHandler(_ token.Handler) { // Run the account update task as now. module.updateAccountWorkerMgr.Go() } // GetTokenAmount returns the amount of tokens for the given zones. func GetTokenAmount(zones []string) (regular, fallback int) { handlerLoop: for _, zone := range zones { // Get handler and check if it should be used. handler, ok := token.GetHandler(zone) if !ok { log.Warningf("spn/access: use of non-registered zone %q", zone) continue handlerLoop } if handler.IsFallback() { fallback += handler.Amount() } else { regular += handler.Amount() } } return } // ShouldRequest returns whether tokens should be requested for the given zones. func ShouldRequest(zones []string) (shouldRequest bool) { handlerLoop: for _, zone := range zones { // Get handler and check if it should be used. handler, ok := token.GetHandler(zone) if !ok { log.Warningf("spn/access: use of non-registered zone %q", zone) continue handlerLoop } // Go through all handlers every time as this will be the case anyway most // of the time and will help us better catch zone misconfiguration. if handler.ShouldRequest() { shouldRequest = true } } return shouldRequest } // GetToken returns a token of one of the given zones. func GetToken(zones []string) (t *token.Token, err error) { handlerSelection: for _, zone := range zones { // Get handler and check if it should be used. handler, ok := token.GetHandler(zone) switch { case !ok: log.Warningf("spn/access: use of non-registered zone %q", zone) continue handlerSelection case handler.IsFallback() && !TokenIssuerIsFailing(): // Skip fallback zone if everything works. continue handlerSelection } // Get token from handler. t, err = token.GetToken(zone) if err == nil { return t, nil } } // Return existing error, if exists. if err != nil { return nil, err } return nil, token.ErrEmpty } // VerifyRawToken verifies a raw token. func VerifyRawToken(data []byte) (granted terminal.Permission, err error) { t, err := token.ParseRawToken(data) if err != nil { return 0, fmt.Errorf("failed to parse token: %w", err) } return VerifyToken(t) } // VerifyToken verifies a token. func VerifyToken(t *token.Token) (granted terminal.Permission, err error) { handler, ok := token.GetHandler(t.Zone) if !ok { return terminal.NoPermission, token.ErrZoneUnknown } // Check if the token is a fallback token. if handler.IsFallback() && !healthCheck() { return terminal.NoPermission, ErrFallbackNotAvailable } // Verify token. err = handler.Verify(t) if err != nil { return 0, fmt.Errorf("failed to verify token: %w", err) } // Return permission of zone. granted, ok = zonePermissions[t.Zone] if !ok { return terminal.NoPermission, nil } return granted, nil } ================================================ FILE: spn/cabin/config-public.go ================================================ package cabin import ( "fmt" "net" "os" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/profile/endpoints" "github.com/safing/portmaster/spn/hub" ) // Configuration Keys. var ( // Name of the node. publicCfgOptionNameKey = "spn/publicHub/name" publicCfgOptionName config.StringOption publicCfgOptionNameDefault = "" publicCfgOptionNameOrder = 512 // Person or organisation, who is in control of the node (should be same for all nodes of this person or organisation). publicCfgOptionGroupKey = "spn/publicHub/group" publicCfgOptionGroup config.StringOption publicCfgOptionGroupDefault = "" publicCfgOptionGroupOrder = 513 // Contact possibility (recommended, but optional). publicCfgOptionContactAddressKey = "spn/publicHub/contactAddress" publicCfgOptionContactAddress config.StringOption publicCfgOptionContactAddressDefault = "" publicCfgOptionContactAddressOrder = 514 // Type of service of the contact address, if not email. publicCfgOptionContactServiceKey = "spn/publicHub/contactService" publicCfgOptionContactService config.StringOption publicCfgOptionContactServiceDefault = "" publicCfgOptionContactServiceOrder = 515 // Hosters - supply chain (reseller, hosting provider, datacenter operator, ...). publicCfgOptionHostersKey = "spn/publicHub/hosters" publicCfgOptionHosters config.StringArrayOption publicCfgOptionHostersDefault = []string{} publicCfgOptionHostersOrder = 516 // Datacenter // Format: CC-COMPANY-INTERNALCODE // Eg: DE-Hetzner-FSN1-DC5 //. publicCfgOptionDatacenterKey = "spn/publicHub/datacenter" publicCfgOptionDatacenter config.StringOption publicCfgOptionDatacenterDefault = "" publicCfgOptionDatacenterOrder = 517 // Network Location and Access. // IPv4 must be global and accessible. publicCfgOptionIPv4Key = "spn/publicHub/ip4" publicCfgOptionIPv4 config.StringOption publicCfgOptionIPv4Default = "" publicCfgOptionIPv4Order = 518 // IPv6 must be global and accessible. publicCfgOptionIPv6Key = "spn/publicHub/ip6" publicCfgOptionIPv6 config.StringOption publicCfgOptionIPv6Default = "" publicCfgOptionIPv6Order = 519 // Transports. publicCfgOptionTransportsKey = "spn/publicHub/transports" publicCfgOptionTransports config.StringArrayOption publicCfgOptionTransportsDefault = []string{ "tcp:17", } publicCfgOptionTransportsOrder = 520 // Entry Policy. publicCfgOptionEntryKey = "spn/publicHub/entry" publicCfgOptionEntry config.StringArrayOption publicCfgOptionEntryDefault = []string{} publicCfgOptionEntryOrder = 521 // Exit Policy. publicCfgOptionExitKey = "spn/publicHub/exit" publicCfgOptionExit config.StringArrayOption publicCfgOptionExitDefault = []string{"- * TCP/25"} publicCfgOptionExitOrder = 522 // Allow Unencrypted. publicCfgOptionAllowUnencryptedKey = "spn/publicHub/allowUnencrypted" publicCfgOptionAllowUnencrypted config.BoolOption publicCfgOptionAllowUnencryptedDefault = false publicCfgOptionAllowUnencryptedOrder = 523 ) func prepPublicHubConfig() error { err := config.Register(&config.Option{ Name: "Name", Key: publicCfgOptionNameKey, Description: "Human readable name of the Hub.", OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, RequiresRestart: true, DefaultValue: publicCfgOptionNameDefault, Annotations: config.Annotations{ config.DisplayOrderAnnotation: publicCfgOptionNameOrder, }, }) if err != nil { return err } publicCfgOptionName = config.GetAsString(publicCfgOptionNameKey, publicCfgOptionNameDefault) err = config.Register(&config.Option{ Name: "Group", Key: publicCfgOptionGroupKey, Description: "Name of the hub group this Hub belongs to.", OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, RequiresRestart: true, DefaultValue: publicCfgOptionGroupDefault, Annotations: config.Annotations{ config.DisplayOrderAnnotation: publicCfgOptionGroupOrder, }, }) if err != nil { return err } publicCfgOptionGroup = config.GetAsString(publicCfgOptionGroupKey, publicCfgOptionGroupDefault) err = config.Register(&config.Option{ Name: "Contact Address", Key: publicCfgOptionContactAddressKey, Description: "Contact address where the Hub operator can be reached.", OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, RequiresRestart: true, DefaultValue: publicCfgOptionContactAddressDefault, Annotations: config.Annotations{ config.DisplayOrderAnnotation: publicCfgOptionContactAddressOrder, }, }) if err != nil { return err } publicCfgOptionContactAddress = config.GetAsString(publicCfgOptionContactAddressKey, publicCfgOptionContactAddressDefault) err = config.Register(&config.Option{ Name: "Contact Service", Key: publicCfgOptionContactServiceKey, Description: "Name of the service the contact address corresponds to, if not email.", OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, RequiresRestart: true, DefaultValue: publicCfgOptionContactServiceDefault, Annotations: config.Annotations{ config.DisplayOrderAnnotation: publicCfgOptionContactServiceOrder, }, }) if err != nil { return err } publicCfgOptionContactService = config.GetAsString(publicCfgOptionContactServiceKey, publicCfgOptionContactServiceDefault) err = config.Register(&config.Option{ Name: "Hosters", Key: publicCfgOptionHostersKey, Description: "List of all involved entities and organisations that are involved in hosting this Hub.", OptType: config.OptTypeStringArray, ExpertiseLevel: config.ExpertiseLevelExpert, RequiresRestart: true, DefaultValue: publicCfgOptionHostersDefault, Annotations: config.Annotations{ config.DisplayOrderAnnotation: publicCfgOptionHostersOrder, }, }) if err != nil { return err } publicCfgOptionHosters = config.GetAsStringArray(publicCfgOptionHostersKey, publicCfgOptionHostersDefault) err = config.Register(&config.Option{ Name: "Datacenter", Key: publicCfgOptionDatacenterKey, Description: "Identifier of the datacenter this Hub is hosted in.", OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, RequiresRestart: true, DefaultValue: publicCfgOptionDatacenterDefault, Annotations: config.Annotations{ config.DisplayOrderAnnotation: publicCfgOptionDatacenterOrder, }, }) if err != nil { return err } publicCfgOptionDatacenter = config.GetAsString(publicCfgOptionDatacenterKey, publicCfgOptionDatacenterDefault) err = config.Register(&config.Option{ Name: "IPv4", Key: publicCfgOptionIPv4Key, Description: "IPv4 address of this Hub. Must be globally reachable.", OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, RequiresRestart: true, DefaultValue: publicCfgOptionIPv4Default, Annotations: config.Annotations{ config.DisplayOrderAnnotation: publicCfgOptionIPv4Order, }, }) if err != nil { return err } publicCfgOptionIPv4 = config.GetAsString(publicCfgOptionIPv4Key, publicCfgOptionIPv4Default) err = config.Register(&config.Option{ Name: "IPv6", Key: publicCfgOptionIPv6Key, Description: "IPv6 address of this Hub. Must be globally reachable.", OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, RequiresRestart: true, DefaultValue: publicCfgOptionIPv6Default, Annotations: config.Annotations{ config.DisplayOrderAnnotation: publicCfgOptionIPv6Order, }, }) if err != nil { return err } publicCfgOptionIPv6 = config.GetAsString(publicCfgOptionIPv6Key, publicCfgOptionIPv6Default) err = config.Register(&config.Option{ Name: "Transports", Key: publicCfgOptionTransportsKey, Description: "List of transports this Hub supports.", OptType: config.OptTypeStringArray, ExpertiseLevel: config.ExpertiseLevelExpert, RequiresRestart: true, DefaultValue: publicCfgOptionTransportsDefault, ValidationFunc: func(value any) error { if transports, ok := value.([]string); ok { for i, transport := range transports { if _, err := hub.ParseTransport(transport); err != nil { return fmt.Errorf("failed to parse transport #%d: %w", i, err) } } } else { return fmt.Errorf("not a []string, but %T", value) } return nil }, Annotations: config.Annotations{ config.DisplayOrderAnnotation: publicCfgOptionTransportsOrder, }, }) if err != nil { return err } publicCfgOptionTransports = config.GetAsStringArray(publicCfgOptionTransportsKey, publicCfgOptionTransportsDefault) err = config.Register(&config.Option{ Name: "Entry", Key: publicCfgOptionEntryKey, Description: "Define an entry policy. The format is the same for the endpoint lists. Default is permit.", OptType: config.OptTypeStringArray, ExpertiseLevel: config.ExpertiseLevelExpert, RequiresRestart: true, DefaultValue: publicCfgOptionEntryDefault, Annotations: config.Annotations{ config.DisplayOrderAnnotation: publicCfgOptionEntryOrder, config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, }, }) if err != nil { return err } publicCfgOptionEntry = config.GetAsStringArray(publicCfgOptionEntryKey, publicCfgOptionEntryDefault) err = config.Register(&config.Option{ Name: "Exit", Key: publicCfgOptionExitKey, Description: "Define an exit policy. The format is the same for the endpoint lists. Default is permit.", OptType: config.OptTypeStringArray, ExpertiseLevel: config.ExpertiseLevelExpert, RequiresRestart: true, DefaultValue: publicCfgOptionExitDefault, Annotations: config.Annotations{ config.DisplayOrderAnnotation: publicCfgOptionExitOrder, config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, }, }) if err != nil { return err } publicCfgOptionExit = config.GetAsStringArray(publicCfgOptionExitKey, publicCfgOptionExitDefault) err = config.Register(&config.Option{ Name: "Allow Unencrypted Connections", Key: publicCfgOptionAllowUnencryptedKey, Description: "Advertise that this Hub is available for handling unencrypted connections, as detected by clients.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, RequiresRestart: true, DefaultValue: publicCfgOptionAllowUnencryptedDefault, Annotations: config.Annotations{ config.DisplayOrderAnnotation: publicCfgOptionAllowUnencryptedOrder, }, }) if err != nil { return err } publicCfgOptionAllowUnencrypted = config.GetAsBool(publicCfgOptionAllowUnencryptedKey, publicCfgOptionAllowUnencryptedDefault) // update defaults from system setDynamicPublicDefaults() return nil } func getPublicHubInfo() *hub.Announcement { // get configuration info := &hub.Announcement{ Name: publicCfgOptionName(), Group: publicCfgOptionGroup(), ContactAddress: publicCfgOptionContactAddress(), ContactService: publicCfgOptionContactService(), Hosters: publicCfgOptionHosters(), Datacenter: publicCfgOptionDatacenter(), Transports: publicCfgOptionTransports(), Entry: publicCfgOptionEntry(), Exit: publicCfgOptionExit(), Flags: []string{}, } if publicCfgOptionAllowUnencrypted() { info.Flags = append(info.Flags, hub.FlagAllowUnencrypted) } ip4 := publicCfgOptionIPv4() if ip4 != "" { ip := net.ParseIP(ip4) if ip == nil { log.Warningf("spn/cabin: invalid %s config: %s", publicCfgOptionIPv4Key, ip4) } else { info.IPv4 = ip } } ip6 := publicCfgOptionIPv6() if ip6 != "" { ip := net.ParseIP(ip6) if ip == nil { log.Warningf("spn/cabin: invalid %s config: %s", publicCfgOptionIPv6Key, ip6) } else { info.IPv6 = ip } } return info } func setDynamicPublicDefaults() { // name hostname, err := os.Hostname() if err == nil { err := config.SetDefaultConfigOption(publicCfgOptionNameKey, hostname) if err != nil { log.Warningf("spn/cabin: failed to set %s default to %s", publicCfgOptionNameKey, hostname) } } // IPs v4IPs, v6IPs, err := netenv.GetAssignedGlobalAddresses() if err != nil { log.Warningf("spn/cabin: failed to get assigned addresses: %s", err) return } if len(v4IPs) == 1 { err = config.SetDefaultConfigOption(publicCfgOptionIPv4Key, v4IPs[0].String()) if err != nil { log.Warningf("spn/cabin: failed to set %s default to %s", publicCfgOptionIPv4Key, v4IPs[0].String()) } } if len(v6IPs) == 1 { err = config.SetDefaultConfigOption(publicCfgOptionIPv6Key, v6IPs[0].String()) if err != nil { log.Warningf("spn/cabin: failed to set %s default to %s", publicCfgOptionIPv6Key, v6IPs[0].String()) } } } ================================================ FILE: spn/cabin/database.go ================================================ package cabin import ( "errors" "fmt" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/spn/hub" ) var db = database.NewInterface(nil) // LoadIdentity loads an identify with the given key. func LoadIdentity(key string) (id *Identity, changed bool, err error) { r, err := db.Get(key) if err != nil { return nil, false, err } id, err = EnsureIdentity(r) if err != nil { return nil, false, fmt.Errorf("failed to parse identity: %w", err) } // Check if required fields are present. switch { case id.Hub == nil: return nil, false, errors.New("missing id.Hub") case id.Signet == nil: return nil, false, errors.New("missing id.Signet") case id.Hub.Info == nil: return nil, false, errors.New("missing hub.Info") case id.Hub.Status == nil: return nil, false, errors.New("missing hub.Status") case id.ID != id.Hub.ID: return nil, false, errors.New("hub.ID mismatch") case id.ID != id.Hub.Info.ID: return nil, false, errors.New("hub.Info.ID mismatch") case id.Map == "": return nil, false, errors.New("invalid id.Map") case id.Hub.Map == "": return nil, false, errors.New("invalid hub.Map") case id.Hub.FirstSeen.IsZero(): return nil, false, errors.New("missing hub.FirstSeen") case id.Hub.Info.Timestamp == 0: return nil, false, errors.New("missing hub.Info.Timestamp") case id.Hub.Status.Timestamp == 0: return nil, false, errors.New("missing hub.Status.Timestamp") } // Run a initial maintenance routine. infoChanged, err := id.MaintainAnnouncement(nil, true) if err != nil { return nil, false, fmt.Errorf("failed to initialize announcement: %w", err) } statusChanged, err := id.MaintainStatus(nil, nil, nil, true) if err != nil { return nil, false, fmt.Errorf("failed to initialize status: %w", err) } // Ensure the Measurements reset the values. measurements := id.Hub.GetMeasurements() measurements.SetLatency(0) measurements.SetCapacity(0) measurements.SetCalculatedCost(hub.MaxCalculatedCost) return id, infoChanged || statusChanged, nil } // EnsureIdentity makes sure a database record is an Identity. func EnsureIdentity(r record.Record) (*Identity, error) { // unwrap if r.IsWrapped() { // only allocate a new struct, if we need it id := &Identity{} err := record.Unwrap(r, id) if err != nil { return nil, err } return id, nil } // or adjust type id, ok := r.(*Identity) if !ok { return nil, fmt.Errorf("record not of type *Identity, but %T", r) } return id, nil } // Save saves the Identity to the database. func (id *Identity) Save() error { if !id.KeyIsSet() { return errors.New("no key set") } return db.Put(id) } ================================================ FILE: spn/cabin/identity.go ================================================ package cabin import ( "context" "errors" "fmt" "time" "github.com/safing/jess" "github.com/safing/jess/tools" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/info" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/hub" ) const ( // DefaultIDKeyScheme is the default jess tool for creating ID keys. DefaultIDKeyScheme = "Ed25519" // DefaultIDKeySecurityLevel is the default security level for creating ID keys. DefaultIDKeySecurityLevel = 256 // Ed25519 security level is fixed, setting is ignored. ) // Identity holds the identity of a Hub. type Identity struct { record.Base ID string Map string Hub *hub.Hub Signet *jess.Signet ExchKeys map[string]*ExchKey infoExportCache []byte statusExportCache []byte } // Lock locks the Identity through the Hub lock. func (id *Identity) Lock() { id.Hub.Lock() } // Unlock unlocks the Identity through the Hub lock. func (id *Identity) Unlock() { id.Hub.Unlock() } // ExchKey holds the private information of a HubKey. type ExchKey struct { Created time.Time Expires time.Time key *jess.Signet tool *tools.Tool } // CreateIdentity creates a new identity. func CreateIdentity(ctx context.Context, mapName string) (*Identity, error) { id := &Identity{ Map: mapName, ExchKeys: make(map[string]*ExchKey), } // create signet signet, recipient, err := hub.CreateHubSignet(DefaultIDKeyScheme, DefaultIDKeySecurityLevel) if err != nil { return nil, err } id.Signet = signet id.ID = signet.ID id.Hub = &hub.Hub{ ID: id.ID, Map: mapName, PublicKey: recipient, } // initial maintenance routine _, err = id.MaintainAnnouncement(nil, true) if err != nil { return nil, fmt.Errorf("failed to initialize announcement: %w", err) } _, err = id.MaintainStatus([]*hub.Lane{}, new(int), nil, true) if err != nil { return nil, fmt.Errorf("failed to initialize status: %w", err) } return id, nil } // MaintainAnnouncement maintains the Hub's Announcenemt and returns whether // there was a change that should be communicated to other Hubs. // If newInfo is nil, it will be derived from configuration. func (id *Identity) MaintainAnnouncement(newInfo *hub.Announcement, selfcheck bool) (changed bool, err error) { id.Lock() defer id.Unlock() // Populate new info with data. if newInfo == nil { newInfo = getPublicHubInfo() } newInfo.ID = id.Hub.ID if id.Hub.Info != nil { newInfo.Timestamp = id.Hub.Info.Timestamp } if !newInfo.Equal(id.Hub.Info) { changed = true } if changed { // Update timestamp. newInfo.Timestamp = time.Now().Unix() } if changed || selfcheck { // Export new data. newInfoData, err := newInfo.Export(id.signingEnvelope()) if err != nil { return false, fmt.Errorf("failed to export: %w", err) } // Apply the status as all other Hubs would in order to check if it's valid. _, _, _, err = hub.ApplyAnnouncement(id.Hub, newInfoData, conf.MainMapName, conf.MainMapScope, true) if err != nil { return false, fmt.Errorf("failed to apply new announcement: %w", err) } id.infoExportCache = newInfoData // Save message to hub message storage. err = hub.SaveHubMsg(id.ID, conf.MainMapName, hub.MsgTypeAnnouncement, newInfoData) if err != nil { log.Warningf("spn/cabin: failed to save own new/updated announcement of %s: %s", id.ID, err) } } return changed, nil } // MaintainStatus maintains the Hub's Status and returns whether there was a change that should be communicated to other Hubs. func (id *Identity) MaintainStatus(lanes []*hub.Lane, load *int, flags []string, selfcheck bool) (changed bool, err error) { id.Lock() defer id.Unlock() // Create a new status or make a copy of the status for editing. var newStatus *hub.Status if id.Hub.Status != nil { newStatus = id.Hub.Status.Copy() } else { newStatus = &hub.Status{} } // Update software version. if newStatus.Version != info.Version() { newStatus.Version = info.Version() changed = true } // Update keys. keysChanged, err := id.MaintainExchKeys(newStatus, time.Now()) if err != nil { return false, fmt.Errorf("failed to maintain keys: %w", err) } if keysChanged { changed = true } // Update lanes. if lanes != nil && !hub.LanesEqual(newStatus.Lanes, lanes) { newStatus.Lanes = lanes changed = true } // Update load. if load != nil && newStatus.Load != *load { newStatus.Load = *load changed = true } // Update flags. if !hub.FlagsEqual(newStatus.Flags, flags) { newStatus.Flags = flags changed = true } // Update timestamp if something changed. if changed { newStatus.Timestamp = time.Now().Unix() } if changed || selfcheck { // Export new data. newStatusData, err := newStatus.Export(id.signingEnvelope()) if err != nil { return false, fmt.Errorf("failed to export: %w", err) } // Apply the status as all other Hubs would in order to check if it's valid. _, _, _, err = hub.ApplyStatus(id.Hub, newStatusData, conf.MainMapName, conf.MainMapScope, true) if err != nil { return false, fmt.Errorf("failed to apply new status: %w", err) } id.statusExportCache = newStatusData // Save message to hub message storage. err = hub.SaveHubMsg(id.ID, conf.MainMapName, hub.MsgTypeStatus, newStatusData) if err != nil { log.Warningf("spn/cabin: failed to save own new/updated status: %s", err) } } return changed, nil } // MakeOfflineStatus creates and signs an offline status message. func (id *Identity) MakeOfflineStatus() (offlineStatusExport []byte, err error) { // Make offline status. newStatus := &hub.Status{ Timestamp: time.Now().Unix(), Version: info.Version(), Flags: []string{hub.FlagOffline}, } // Export new data. newStatusData, err := newStatus.Export(id.signingEnvelope()) if err != nil { return nil, fmt.Errorf("failed to export: %w", err) } return newStatusData, nil } func (id *Identity) signingEnvelope() *jess.Envelope { env := jess.NewUnconfiguredEnvelope() env.SuiteID = jess.SuiteSignV1 env.Senders = []*jess.Signet{id.Signet} return env } // ExportAnnouncement serializes and signs the Announcement. func (id *Identity) ExportAnnouncement() ([]byte, error) { id.Lock() defer id.Unlock() if id.infoExportCache == nil { return nil, errors.New("announcement not exported") } return id.infoExportCache, nil } // ExportStatus serializes and signs the Status. func (id *Identity) ExportStatus() ([]byte, error) { id.Lock() defer id.Unlock() if id.statusExportCache == nil { return nil, errors.New("status not exported") } return id.statusExportCache, nil } // SignHubMsg signs a data blob with the identity's private key. func (id *Identity) SignHubMsg(data []byte) ([]byte, error) { return hub.SignHubMsg(data, id.signingEnvelope(), false) } // GetSignet returns the private exchange key with the given ID. func (id *Identity) GetSignet(keyID string, recipient bool) (*jess.Signet, error) { if recipient { return nil, errors.New("cabin.Identity only serves private keys") } id.Lock() defer id.Unlock() key, ok := id.ExchKeys[keyID] if !ok { return nil, errors.New("the requested key does not exist") } if time.Now().After(key.Expires) || key.key == nil { return nil, errors.New("the requested key has expired") } return key.key, nil } func (ek *ExchKey) toHubKey() (*hub.Key, error) { if ek.key == nil { return nil, errors.New("no key") } // export public key rcpt, err := ek.key.AsRecipient() if err != nil { return nil, err } err = rcpt.StoreKey() if err != nil { return nil, err } // repackage return &hub.Key{ Scheme: rcpt.Scheme, Key: rcpt.Key, Expires: ek.Expires.Unix(), }, nil } ================================================ FILE: spn/cabin/identity_test.go ================================================ package cabin import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/hub" ) func TestIdentity(t *testing.T) { t.Parallel() // Register config options for public hub. if err := prepPublicHubConfig(); err != nil { t.Fatal(err) } // Create new identity. identityTestKey := "core:spn/public/identity-test" id, err := CreateIdentity(module.m.Ctx(), conf.MainMapName) if err != nil { t.Fatal(err) } id.SetKey(identityTestKey) // Check values // Identity assert.NotEmpty(t, id.ID, "id.ID must be set") assert.NotEmpty(t, id.Map, "id.Map must be set") assert.NotNil(t, id.Signet, "id.Signet must be set") assert.NotNil(t, id.infoExportCache, "id.infoExportCache must be set") assert.NotNil(t, id.statusExportCache, "id.statusExportCache must be set") // Hub assert.NotEmpty(t, id.Hub.ID, "hub.ID must be set") assert.NotEmpty(t, id.Hub.Map, "hub.Map must be set") assert.NotZero(t, id.Hub.FirstSeen, "hub.FirstSeen must be set") // Info assert.NotEmpty(t, id.Hub.Info.ID, "info.ID must be set") assert.NotEqual(t, 0, id.Hub.Info.Timestamp, "info.Timestamp must be set") assert.NotEqual(t, "", id.Hub.Info.Name, "info.Name must be set (to hostname)") // Status assert.NotEqual(t, 0, id.Hub.Status.Timestamp, "status.Timestamp must be set") assert.NotEmpty(t, id.Hub.Status.Keys, "status.Keys must be set") fmt.Printf("id: %+v\n", id) fmt.Printf("id.hub: %+v\n", id.Hub) fmt.Printf("id.Hub.Info: %+v\n", id.Hub.Info) fmt.Printf("id.Hub.Status: %+v\n", id.Hub.Status) // Maintenance is run in creation, so nothing should change now. changed, err := id.MaintainAnnouncement(nil, false) if err != nil { t.Fatal(err) } if changed { t.Error("unexpected change of announcement") } changed, err = id.MaintainStatus(nil, nil, nil, false) if err != nil { t.Fatal(err) } if changed { t.Error("unexpected change of status") } // Change lanes. lanes := []*hub.Lane{ { ID: "A", Capacity: 1, Latency: 2, }, { ID: "B", Capacity: 3, Latency: 4, }, { ID: "C", Capacity: 5, Latency: 6, }, } changed, err = id.MaintainStatus(lanes, new(int), nil, false) if err != nil { t.Fatal(err) } if !changed { t.Error("status should have changed") } // Change nothing. changed, err = id.MaintainStatus(lanes, new(int), nil, false) if err != nil { t.Fatal(err) } if changed { t.Error("unexpected change of status") } // Exporting _, err = id.ExportAnnouncement() if err != nil { t.Fatal(err) } _, err = id.ExportStatus() if err != nil { t.Fatal(err) } // Ensure the Measurements reset the values. measurements := id.Hub.GetMeasurements() measurements.SetLatency(0) measurements.SetCapacity(0) measurements.SetCalculatedCost(hub.MaxCalculatedCost) // Save to and load from database. err = id.Save() if err != nil { t.Fatal(err) } id2, _, err := LoadIdentity(identityTestKey) if err != nil { t.Fatal(err) } // Reset everything that should not be compared. id.infoExportCache = nil id2.infoExportCache = nil id.statusExportCache = nil id2.statusExportCache = nil id.ExchKeys = nil id2.ExchKeys = nil id.Hub.Status = nil id2.Hub.Status = nil id.Hub.PublicKey = nil id2.Hub.PublicKey = nil // Check important aspects of the identities. assert.Equal(t, id.ID, id2.ID, "identity IDs must be equal") assert.Equal(t, id.Map, id2.Map, "identity Maps should be equal") assert.Equal(t, id.Hub, id2.Hub, "identity Hubs should be equal") assert.Equal(t, id.Signet, id2.Signet, "identity Signets should be equal") } ================================================ FILE: spn/cabin/keys.go ================================================ package cabin import ( "encoding/base64" "errors" "fmt" "time" "github.com/safing/jess" "github.com/safing/jess/tools" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/spn/hub" ) type providedExchKeyScheme struct { id string securityLevel int //nolint:structcheck // TODO tool *tools.Tool } var ( // validFor defines how long keys are valid for use by clients. validFor = 48 * time.Hour // 2 days // renewBeforeExpiry defines the duration how long before expiry keys should be renewed. renewBeforeExpiry = 24 * time.Hour // 1 day // burnAfter defines how long after expiry keys are burnt/deleted. burnAfter = 12 * time.Hour // 1/2 day // reuseAfter defines how long IDs should be blocked after expiry (and not be reused for new keys). reuseAfter = 2 * 7 * 24 * time.Hour // 2 weeks // provideExchKeySchemes defines the jess tools for creating exchange keys. provideExchKeySchemes = []*providedExchKeyScheme{ { id: "ECDH-X25519", securityLevel: 128, // informative only, security level of ECDH-X25519 is fixed }, // TODO: test with rsa keys } ) func initProvidedExchKeySchemes() error { for _, eks := range provideExchKeySchemes { tool, err := tools.Get(eks.id) if err != nil { return err } eks.tool = tool } return nil } // MaintainExchKeys maintains the exchange keys, creating new ones and // deprecating and deleting old ones. func (id *Identity) MaintainExchKeys(newStatus *hub.Status, now time.Time) (changed bool, err error) { // create Keys map if id.ExchKeys == nil { id.ExchKeys = make(map[string]*ExchKey) } // lifecycle management for keyID, exchKey := range id.ExchKeys { if exchKey.key != nil && now.After(exchKey.Expires.Add(burnAfter)) { // delete key err := exchKey.tool.StaticLogic.BurnKey(exchKey.key) if err != nil { log.Warningf( "spn/cabin: failed to burn key %s (%s) of %s: %s", keyID, exchKey.tool.Info.Name, id.Hub.ID, err, ) } // remove reference exchKey.key = nil } if now.After(exchKey.Expires.Add(reuseAfter)) { // remove key delete(id.ExchKeys, keyID) } } // find or create current keys for _, eks := range provideExchKeySchemes { found := false for _, exchKey := range id.ExchKeys { if exchKey.key != nil && exchKey.key.Scheme == eks.id && now.Before(exchKey.Expires.Add(-renewBeforeExpiry)) { found = true break } } if !found { err := id.createExchKey(eks, now) if err != nil { return false, fmt.Errorf("failed to create %s exchange key: %w", eks.tool.Info.Name, err) } changed = true } } // export most recent keys to HubStatus if changed || len(newStatus.Keys) == 0 { // reset newStatus.Keys = make(map[string]*hub.Key) // find longest valid key for every provided scheme for _, eks := range provideExchKeySchemes { // find key of scheme that is valid the longest longestValid := &ExchKey{ Expires: now, } for _, exchKey := range id.ExchKeys { if exchKey.key != nil && exchKey.key.Scheme == eks.id && exchKey.Expires.After(longestValid.Expires) { longestValid = exchKey } } // check result if longestValid.key == nil { log.Warningf("spn/cabin: could not find export candidate for exchange key scheme %s", eks.id) continue } // export hubKey, err := longestValid.toHubKey() if err != nil { return false, fmt.Errorf("failed to export %s exchange key: %w", longestValid.tool.Info.Name, err) } // add newStatus.Keys[longestValid.key.ID] = hubKey } } return changed, nil } func (id *Identity) createExchKey(eks *providedExchKeyScheme, now time.Time) error { // get ID var keyID string for range 1000000 { // not forever // generate new ID b, err := rng.Bytes(3) if err != nil { return fmt.Errorf("failed to get random data for key ID: %w", err) } keyID = base64.RawURLEncoding.EncodeToString(b) _, exists := id.ExchKeys[keyID] if !exists { break } } if keyID == "" { return errors.New("unable to find available exchange key ID") } // generate key signet := jess.NewSignetBase(eks.tool) signet.ID = keyID // TODO: use security level for key generation if err := signet.GenerateKey(); err != nil { return fmt.Errorf("failed to get new exchange key: %w", err) } // add to key map id.ExchKeys[keyID] = &ExchKey{ Created: now, Expires: now.Add(validFor), key: signet, tool: eks.tool, } return nil } ================================================ FILE: spn/cabin/keys_test.go ================================================ package cabin import ( "testing" "time" "github.com/safing/portmaster/spn/conf" ) func TestKeyMaintenance(t *testing.T) { t.Parallel() id, err := CreateIdentity(module.m.Ctx(), conf.MainMapName) if err != nil { t.Fatal(err) } iterations := 1000 changeCnt := 0 now := time.Now() for range iterations { changed, err := id.MaintainExchKeys(id.Hub.Status, now) if err != nil { t.Fatal(err) } if changed { changeCnt++ t.Logf("===== exchange keys updated at %s:\n", now) for keyID, exchKey := range id.ExchKeys { t.Logf("[%s] %s %v\n", exchKey.Created, keyID, exchKey.key) } } now = now.Add(1 * time.Hour) } if iterations/changeCnt > 25 { // one new key every 24 hours/ticks t.Fatal("more changes than expected") } if len(id.ExchKeys) > 17 { // one new key every day for two weeks + 3 in use t.Fatal("more keys than expected") } } ================================================ FILE: spn/cabin/module.go ================================================ package cabin import ( "errors" "sync/atomic" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" ) type Cabin struct { m *mgr.Manager instance instance } func (c *Cabin) Manager() *mgr.Manager { return c.m } func (c *Cabin) Start() error { return nil } func (c *Cabin) Stop() error { return nil } var ( module *Cabin shimLoaded atomic.Bool ) func prep() error { if err := initProvidedExchKeySchemes(); err != nil { return err } if conf.PublicHub() { if err := prepPublicHubConfig(); err != nil { return err } } return nil } // New returns a new Cabin module. func New(instance instance) (*Cabin, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Cabin") module = &Cabin{ m: m, instance: instance, } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface{} ================================================ FILE: spn/cabin/module_test.go ================================================ package cabin import ( "fmt" "os" "testing" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/core/base" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" ) type testInstance struct { db *dbmodule.DBModule api *api.API config *config.Config rng *rng.Rng base *base.Base } func (stub *testInstance) Config() *config.Config { return stub.config } func (stub *testInstance) SPNGroup() *mgr.ExtendedGroup { return nil } func (stub *testInstance) Stopping() bool { return false } func (stub *testInstance) Ready() bool { return true } func (stub *testInstance) SetCmdLineOperation(f func() error) {} func (stub *testInstance) DataDir() string { return _dataDir } var _dataDir string func runTest(m *testing.M) error { api.SetDefaultAPIListenAddress("0.0.0.0:8080") var err error // Create a temporary directory for the data _dataDir, err = os.MkdirTemp("", "") if err != nil { return fmt.Errorf("failed to initialize dataroot: %w", err) } defer func() { _ = os.RemoveAll(_dataDir) }() // Init instance := &testInstance{} instance.db, err = dbmodule.New(instance) if err != nil { return fmt.Errorf("failed to create database: %w", err) } instance.api, err = api.New(instance) if err != nil { return fmt.Errorf("failed to create api: %w", err) } instance.config, err = config.New(instance) if err != nil { return fmt.Errorf("failed to create config module: %w", err) } instance.rng, err = rng.New(instance) if err != nil { return fmt.Errorf("failed to create rng module: %w", err) } instance.base, err = base.New(instance) if err != nil { return fmt.Errorf("failed to create base module: %w", err) } module, err = New(struct{}{}) if err != nil { return fmt.Errorf("failed to create cabin module: %w", err) } // Start err = instance.db.Start() if err != nil { return fmt.Errorf("failed to start database: %w", err) } err = instance.api.Start() if err != nil { return fmt.Errorf("failed to start api: %w", err) } err = instance.config.Start() if err != nil { return fmt.Errorf("failed to start config module: %w", err) } err = instance.rng.Start() if err != nil { return fmt.Errorf("failed to start rng module: %w", err) } err = instance.base.Start() if err != nil { return fmt.Errorf("failed to start base module: %w", err) } err = module.Start() if err != nil { return fmt.Errorf("failed to start cabin module: %w", err) } conf.EnablePublicHub(true) m.Run() return nil } func TestMain(m *testing.M) { if err := runTest(m); err != nil { fmt.Printf("%s\n", err) os.Exit(1) } } ================================================ FILE: spn/cabin/verification.go ================================================ package cabin import ( "crypto/subtle" "errors" "fmt" "github.com/safing/jess" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/spn/hub" "github.com/safing/structures/dsd" ) var ( verificationChallengeSize = 32 verificationChallengeMinSize = 16 verificationSigningSuite = jess.SuiteSignV1 verificationRequirements = jess.NewRequirements(). Remove(jess.Confidentiality). Remove(jess.Integrity). Remove(jess.RecipientAuthentication) ) // Verification is used to verify certain aspects of another Hub. type Verification struct { // Challenge is a random value chosen by the client. Challenge []byte `json:"c"` // Purpose defines the purpose of the verification. Protects against using verification for other purposes. Purpose string `json:"p"` // ClientReference is an optional field for exchanging metadata about the client. Protects against forwarding/relay attacks. ClientReference string `json:"cr"` // ServerReference is an optional field for exchanging metadata about the server. Protects against forwarding/relay attacks. ServerReference string `json:"sr"` } // CreateVerificationRequest creates a new verification request with the given // purpose and references. func CreateVerificationRequest(purpose, clientReference, serverReference string) (v *Verification, request []byte, err error) { // Generate random challenge. challenge, err := rng.Bytes(verificationChallengeSize) if err != nil { return nil, nil, fmt.Errorf("failed to generate challenge: %w", err) } // Create verification object. v = &Verification{ Purpose: purpose, ClientReference: clientReference, Challenge: challenge, } // Serialize verification. request, err = dsd.Dump(v, dsd.JSON) if err != nil { return nil, nil, fmt.Errorf("failed to serialize verification request: %w", err) } // The server reference is not sent to the server, but needs to be supplied // by the server. v.ServerReference = serverReference return v, request, nil } // SignVerificationRequest sign a verification request. // The purpose and references must match the request, else the verification // will fail. func (id *Identity) SignVerificationRequest(request []byte, purpose, clientReference, serverReference string) (response []byte, err error) { // Parse request. v := new(Verification) _, err = dsd.Load(request, v) if err != nil { return nil, fmt.Errorf("failed to parse request: %w", err) } // Validate request. if len(v.Challenge) < verificationChallengeMinSize { return nil, errors.New("challenge too small") } if v.Purpose != purpose { return nil, errors.New("purpose mismatch") } if v.ClientReference != clientReference { return nil, errors.New("client reference mismatch") } // Assign server reference and serialize. v.ServerReference = serverReference dataToSign, err := dsd.Dump(v, dsd.JSON) if err != nil { return nil, fmt.Errorf("failed to serialize verification response: %w", err) } // Sign response. e := jess.NewUnconfiguredEnvelope() e.SuiteID = verificationSigningSuite e.Senders = []*jess.Signet{id.Signet} jession, err := e.Correspondence(nil) if err != nil { return nil, fmt.Errorf("failed to setup signer: %w", err) } letter, err := jession.Close(dataToSign) if err != nil { return nil, fmt.Errorf("failed to sign: %w", err) } // Serialize and return. signedResponse, err := letter.ToDSD(dsd.JSON) if err != nil { return nil, fmt.Errorf("failed to serialize letter: %w", err) } return signedResponse, nil } // Verify verifies the verification response and checks if everything is valid. func (v *Verification) Verify(response []byte, h *hub.Hub) error { // Parse response. letter, err := jess.LetterFromDSD(response) if err != nil { return fmt.Errorf("failed to parse response: %w", err) } // Verify response. responseData, err := letter.Open( verificationRequirements, &hub.SingleTrustStore{ Signet: h.PublicKey, }, ) if err != nil { return fmt.Errorf("failed to verify response: %w", err) } // Parse verified response. responseV := new(Verification) _, err = dsd.Load(responseData, responseV) if err != nil { return fmt.Errorf("failed to parse verified response: %w", err) } // Validate request. if subtle.ConstantTimeCompare(v.Challenge, responseV.Challenge) != 1 { return errors.New("challenge mismatch") } if subtle.ConstantTimeCompare([]byte(v.Purpose), []byte(responseV.Purpose)) != 1 { return errors.New("purpose mismatch") } if subtle.ConstantTimeCompare([]byte(v.ClientReference), []byte(responseV.ClientReference)) != 1 { return errors.New("client reference mismatch") } if subtle.ConstantTimeCompare([]byte(v.ServerReference), []byte(responseV.ServerReference)) != 1 { return errors.New("server reference mismatch") } return nil } ================================================ FILE: spn/cabin/verification_test.go ================================================ package cabin import ( "fmt" "testing" ) func TestVerification(t *testing.T) { t.Parallel() id, err := CreateIdentity(module.m.Ctx(), "test") if err != nil { t.Fatal(err) } if err := testVerificationWith( t, id, "a", "b", "c", "a", "b", "c", "", "", "", nil, ); err != nil { t.Fatal(err) } if err := testVerificationWith( t, id, "a", "b", "c", "x", "b", "c", "", "", "", nil, ); err == nil { t.Fatal("should fail on purpose mismatch") } if err := testVerificationWith( t, id, "a", "b", "c", "a", "x", "c", "", "", "", nil, ); err == nil { t.Fatal("should fail on client ref mismatch") } if err := testVerificationWith( t, id, "a", "b", "c", "a", "b", "x", "", "", "", nil, ); err == nil { t.Fatal("should fail on server ref mismatch") } if err := testVerificationWith( t, id, "a", "b", "c", "a", "b", "c", "x", "", "", nil, ); err == nil { t.Fatal("should fail on purpose mismatch") } if err := testVerificationWith( t, id, "a", "b", "c", "a", "b", "c", "", "x", "", nil, ); err == nil { t.Fatal("should fail on client ref mismatch") } if err := testVerificationWith( t, id, "a", "b", "c", "a", "b", "c", "", "", "x", nil, ); err == nil { t.Fatal("should fail on server ref mismatch") } if err := testVerificationWith( t, id, "a", "b", "c", "a", "b", "c", "", "", "", []byte{1, 2, 3, 4}, ); err == nil { t.Fatal("should fail on challenge mismatch") } } func testVerificationWith( t *testing.T, id *Identity, purpose1, clientRef1, serverRef1 string, //nolint:unparam purpose2, clientRef2, serverRef2 string, mitmPurpose, mitmClientRef, mitmServerRef string, mitmChallenge []byte, ) error { t.Helper() v, request, err := CreateVerificationRequest(purpose1, clientRef1, serverRef1) if err != nil { return fmt.Errorf("failed to create verification request: %w", err) } response, err := id.SignVerificationRequest(request, purpose2, clientRef2, serverRef2) if err != nil { return fmt.Errorf("failed to sign verification response: %w", err) } if mitmPurpose != "" { v.Purpose = mitmPurpose } if mitmClientRef != "" { v.ClientReference = mitmClientRef } if mitmServerRef != "" { v.ServerReference = mitmServerRef } if mitmChallenge != nil { v.Challenge = mitmChallenge } err = v.Verify(response, id.Hub) if err != nil { return fmt.Errorf("failed to verify: %w", err) } return nil } ================================================ FILE: spn/captain/api.go ================================================ package captain import ( "fmt" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/spn/conf" ) const ( apiPathForSPNReInit = "spn/reinit" ) func registerAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Path: apiPathForSPNReInit, Write: api.PermitAdmin, ActionFunc: handleReInit, Name: "Re-initialize SPN", Description: "Stops the SPN, resets all caches and starts it again. The SPN account and settings are not changed.", }); err != nil { return err } return nil } func handleReInit(ar *api.Request) (msg string, err error) { if !conf.Client() && !conf.Integrated() { return "", fmt.Errorf("re-initialization only possible on integrated clients") } // Make sure SPN is stopped and wait for it to complete. err = module.mgr.DoAsStopWorker("stop SPN for re-init", module.instance.SPNGroup().EnsureStoppedWorker) if err != nil { return "", fmt.Errorf("failed to stop SPN for re-init: %w", err) } // Delete SPN cache. db := database.NewInterface(&database.Options{ Local: true, Internal: true, }) deletedRecords, err := db.Purge(ar.Context(), query.New("cache:spn/")) if err != nil { return "", fmt.Errorf("failed to delete SPN cache: %w", err) } // Start SPN if it is enabled. enabled := config.GetAsBool("spn/enable", false) if enabled() { module.mgr.Go("ensure SPN is started", module.instance.SPNGroup().EnsureStartedWorker) } return fmt.Sprintf( "Completed SPN re-initialization and deleted %d cache records in the process.", deletedRecords, ), nil } ================================================ FILE: spn/captain/bootstrap.go ================================================ package captain import ( "errors" "flag" "fmt" "io/fs" "os" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/navigator" "github.com/safing/structures/dsd" ) // BootstrapFile is used for sideloading bootstrap data. type BootstrapFile struct { Main BootstrapFileEntry } // BootstrapFileEntry is the bootstrap data structure for one map. type BootstrapFileEntry struct { Hubs []string } var ( bootstrapHubFlag string bootstrapFileFlag string ) func init() { flag.StringVar(&bootstrapHubFlag, "bootstrap-hub", "", "transport address of hub for bootstrapping with the hub ID in the fragment") flag.StringVar(&bootstrapFileFlag, "bootstrap-file", "", "bootstrap file containing bootstrap hubs - will be initialized if running a public hub and it doesn't exist") } // prepBootstrapHubFlag checks the bootstrap-hub argument if it is valid. func prepBootstrapHubFlag() error { if bootstrapHubFlag != "" { _, _, _, err := hub.ParseBootstrapHub(bootstrapHubFlag) return err } return nil } // processBootstrapHubFlag processes the bootstrap-hub argument. func processBootstrapHubFlag() error { if bootstrapHubFlag != "" { return navigator.Main.AddBootstrapHubs([]string{bootstrapHubFlag}) } return nil } // processBootstrapFileFlag processes the bootstrap-file argument. func processBootstrapFileFlag() error { if bootstrapFileFlag == "" { return nil } _, err := os.Stat(bootstrapFileFlag) if err != nil { if errors.Is(err, fs.ErrNotExist) { return createBootstrapFile(bootstrapFileFlag) } return fmt.Errorf("failed to access bootstrap hub file: %w", err) } return loadBootstrapFile(bootstrapFileFlag) } // bootstrapWithUpdates loads bootstrap hubs from the updates server and imports them. func bootstrapWithUpdates() error { if bootstrapFileFlag != "" { return errors.New("using the bootstrap-file argument disables bootstrapping via the update system") } return updateSPNIntel(module.mgr.Ctx(), nil) } // loadBootstrapFile loads a file with bootstrap hub entries and imports them. func loadBootstrapFile(filename string) (err error) { // Load bootstrap file from disk and parse it. data, err := os.ReadFile(filename) if err != nil { return fmt.Errorf("failed to load bootstrap file: %w", err) } bootstrapFile := &BootstrapFile{} _, err = dsd.Load(data, bootstrapFile) if err != nil { return fmt.Errorf("failed to parse bootstrap file: %w", err) } if len(bootstrapFile.Main.Hubs) == 0 { return errors.New("bootstrap holds no hubs for main map") } // Add Hubs to map. err = navigator.Main.AddBootstrapHubs(bootstrapFile.Main.Hubs) if err == nil { log.Infof("spn/captain: loaded bootstrap file %s", filename) } return err } // createBootstrapFile save a bootstrap hub file with an entry of the public identity. func createBootstrapFile(filename string) error { if !conf.PublicHub() { log.Infof("spn/captain: skipped writing a bootstrap hub file, as this is not a public hub") return nil } // create bootstrap hub if len(publicIdentity.Hub.Info.Transports) == 0 { return errors.New("public identity has no transports available") } // parse first transport t, err := hub.ParseTransport(publicIdentity.Hub.Info.Transports[0]) if err != nil { return fmt.Errorf("failed to parse transport of public identity: %w", err) } // add IP address switch { case publicIdentity.Hub.Info.IPv4 != nil: t.Domain = publicIdentity.Hub.Info.IPv4.String() case publicIdentity.Hub.Info.IPv6 != nil: t.Domain = "[" + publicIdentity.Hub.Info.IPv6.String() + "]" default: return errors.New("public identity has no IP address available") } // add Hub ID t.Option = publicIdentity.Hub.ID // put together bs := &BootstrapFile{ Main: BootstrapFileEntry{ Hubs: []string{t.String()}, }, } // serialize fileData, err := dsd.Dump(bs, dsd.JSON) if err != nil { return err } // save to disk err = os.WriteFile(filename, fileData, 0o0664) //nolint:gosec // Should be able to be read by others. if err != nil { return err } log.Infof("spn/captain: created bootstrap file %s", filename) return nil } ================================================ FILE: spn/captain/client.go ================================================ package captain import ( "errors" "fmt" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/spn/access" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/crew" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/navigator" "github.com/safing/portmaster/spn/terminal" ) var ( ready = abool.New() spnLoginButton = notifications.Action{ Text: "Login", Type: notifications.ActionTypeOpenPage, Payload: "spn", } spnOpenAccountPage = notifications.Action{ Text: "Open Account Page", Type: notifications.ActionTypeOpenURL, Payload: "https://account.safing.io", } ) // ClientReady signifies if the SPN client is fully ready to handle connections. func ClientReady() bool { return ready.IsSet() } type ( clientComponentFunc func(ctx *mgr.WorkerCtx) clientComponentResult clientComponentResult uint8 ) const ( clientResultOk clientComponentResult = iota // Continue and clean module status. clientResultRetry // Go back to start of current step, don't clear module status. clientResultReconnect // Stop current connection and start from zero. clientResultShutdown // SPN Module is shutting down. ) var ( clientNetworkChangedFlag = netenv.GetNetworkChangedFlag() clientIneligibleAccountUpdateDelay = 1 * time.Minute clientRetryConnectBackoffDuration = 5 * time.Second clientInitialHealthCheckDelay = 10 * time.Second clientHealthCheckTickDuration = 1 * time.Minute clientHealthCheckTickDurationSleepMode = 5 * time.Minute clientHealthCheckTimeout = 15 * time.Second clientHealthCheckTrigger = make(chan struct{}, 1) lastHealthCheck time.Time ) func triggerClientHealthCheck() { select { case clientHealthCheckTrigger <- struct{}{}: default: } } func clientManager(ctx *mgr.WorkerCtx) error { defer func() { ready.UnSet() netenv.ConnectedToSPN.UnSet() resetSPNStatus(StatusDisabled, true) module.states.Clear() clientStopHomeHub(ctx) }() module.states.Add(mgr.State{ ID: "spn:establishing-home-hub", Name: "Connecting to SPN...", Message: "Connecting to the SPN network is in progress.", Type: mgr.StateTypeHint, }) // TODO: When we are starting and the SPN module is faster online than the // nameserver, then updating the account will fail as the DNS query is // redirected to a closed port. // We also can't add the nameserver as a module dependency, as the nameserver // is not part of the server. select { case <-time.After(1 * time.Second): case <-ctx.Done(): return nil } module.healthCheckTicker = mgr.NewSleepyTicker(clientHealthCheckTickDuration, clientHealthCheckTickDurationSleepMode) defer module.healthCheckTicker.Stop() reconnect: for { // Check if we are shutting down. if ctx.IsDone() { return nil } // Reset SPN status. if ready.SetToIf(true, false) { netenv.ConnectedToSPN.UnSet() log.Info("spn/captain: client not ready") } resetSPNStatus(StatusConnecting, true) // Check everything and connect to the SPN. for _, clientFunc := range []clientComponentFunc{ clientStopHomeHub, clientCheckNetworkReady, clientCheckAccountAndTokens, clientConnectToHomeHub, clientSetActiveConnectionStatus, } { switch clientFunc(ctx) { case clientResultOk: // Continue case clientResultRetry, clientResultReconnect: // Wait for a short time to not loop too quickly. select { case <-time.After(clientRetryConnectBackoffDuration): continue reconnect case <-ctx.Done(): return nil } case clientResultShutdown: return nil } } log.Info("spn/captain: client is ready") ready.Set() netenv.ConnectedToSPN.Set() module.EventSPNConnected.Submit(struct{}{}) if conf.Integrated() { module.mgr.Go("update quick setting countries", navigator.Main.UpdateConfigQuickSettings) } // Reset last health check value, as we have just connected. lastHealthCheck = time.Now() // Back off before starting initial health checks. select { case <-time.After(clientInitialHealthCheckDelay): case <-ctx.Done(): return nil } for { // Check health of the current SPN connection and monitor the user status. maintainers: for _, clientFunc := range []clientComponentFunc{ clientCheckHomeHubConnection, clientCheckAccountAndTokens, clientSetActiveConnectionStatus, } { switch clientFunc(ctx) { case clientResultOk: // Continue case clientResultRetry: // Abort and wait for the next run. break maintainers case clientResultReconnect: continue reconnect case clientResultShutdown: return nil } } // Wait for signal to run maintenance again. select { case <-module.healthCheckTicker.Wait(): case <-clientHealthCheckTrigger: case <-crew.ConnectErrors(): case <-clientNetworkChangedFlag.Signal(): clientNetworkChangedFlag.Refresh() case <-ctx.Done(): return nil } } } } func clientCheckNetworkReady(ctx *mgr.WorkerCtx) clientComponentResult { // Check if we are online enough for connecting. switch netenv.GetOnlineStatus() { //nolint:exhaustive case netenv.StatusOffline, netenv.StatusLimited: select { case <-ctx.Done(): return clientResultShutdown case <-time.After(1 * time.Second): return clientResultRetry } } return clientResultOk } // DisableAccount disables using any account related SPN functionality. // Attempts to use the same will result in errors. var DisableAccount bool func clientCheckAccountAndTokens(ctx *mgr.WorkerCtx) clientComponentResult { if DisableAccount { return clientResultOk } // Get SPN user. user, err := access.GetUser() if err != nil && !errors.Is(err, access.ErrNotLoggedIn) { notifications.NotifyError( "spn:failed-to-get-user", "SPN Internal Error", `Please restart Portmaster.`, // TODO: Add restart button. // TODO: Use special UI restart action in order to reload UI on restart. ).SyncWithState(module.states) resetSPNStatus(StatusFailed, true) log.Errorf("spn/captain: client internal error: %s", err) return clientResultReconnect } // Check if user is logged in. if user == nil || !user.IsLoggedIn() { notifications.NotifyWarn( "spn:not-logged-in", "SPN Login Required", `Please log in to access the SPN.`, spnLoginButton, ).SyncWithState(module.states) resetSPNStatus(StatusFailed, true) log.Warningf("spn/captain: enabled but not logged in") return clientResultReconnect } // Check if user is eligible. if !user.MayUseTheSPN() { // Update user in case there was a change. // Only update here if we need to - there is an update task in the access // module for periodic updates. if time.Now().Add(-clientIneligibleAccountUpdateDelay).After(time.Unix(user.Meta().Modified, 0)) { _, _, err := access.UpdateUser() if err != nil { notifications.NotifyError( "spn:failed-to-update-user", "SPN Account Server Error", fmt.Sprintf(`The status of your SPN account could not be updated: %s`, err), ).SyncWithState(module.states) resetSPNStatus(StatusFailed, true) log.Errorf("spn/captain: failed to update ineligible account: %s", err) return clientResultReconnect } } // Check if user is eligible after a possible update. if !user.MayUseTheSPN() { // If package is generally valid, then the current package does not have access to the SPN. if user.MayUse("") { notifications.NotifyError( "spn:package-not-eligible", "SPN Not Included In Package", "Your current Portmaster Package does not include access to the SPN. Please upgrade your package on the Account Page.", spnOpenAccountPage, ).SyncWithState(module.states) resetSPNStatus(StatusFailed, true) return clientResultReconnect } // Otherwise, include the message from the user view. message := "There is an issue with your Portmaster Package. Please check the Account Page." if user.View != nil && user.View.Message != "" { message = user.View.Message } notifications.NotifyError( "spn:subscription-inactive", "Portmaster Package Issue", "Cannot enable SPN: "+message, spnOpenAccountPage, ).SyncWithState(module.states) resetSPNStatus(StatusFailed, true) return clientResultReconnect } } // Check if we have enough tokens. if access.ShouldRequest(access.ExpandAndConnectZones) { err := access.UpdateTokens() if err != nil { log.Errorf("spn/captain: failed to get tokens: %s", err) // There was an error updating the account. // Check if we have enough tokens to continue anyway. regular, _ := access.GetTokenAmount(access.ExpandAndConnectZones) if regular == 0 /* && fallback == 0 */ { // TODO: Add fallback token check when fallback was tested on servers. notifications.NotifyError( "spn:tokens-exhausted", "SPN Access Tokens Exhausted", `The Portmaster failed to get new access tokens to access the SPN. The Portmaster will automatically retry to get new access tokens.`, ).SyncWithState(module.states) resetSPNStatus(StatusFailed, false) } return clientResultRetry } } return clientResultOk } func clientStopHomeHub(ctx *mgr.WorkerCtx) clientComponentResult { // Don't use the context in this function, as it will likely be canceled // already and would disrupt any context usage in here. // Get crane connecting to home. home, _ := navigator.Main.GetHome() if home == nil { return clientResultOk } crane := docks.GetAssignedCrane(home.Hub.ID) if crane == nil { return clientResultOk } // Stop crane and all connected terminals. crane.Stop(nil) return clientResultOk } func clientConnectToHomeHub(ctx *mgr.WorkerCtx) clientComponentResult { err := establishHomeHub(ctx) if err != nil { if ctx.IsDone() { return clientResultShutdown } log.Errorf("spn/captain: failed to establish connection to home hub: %s", err) resetSPNStatus(StatusFailed, true) switch { case errors.Is(err, ErrAllHomeHubsExcluded): notifications.NotifyError( "spn:all-home-hubs-excluded", "All Home Nodes Excluded", "Your current Home Node Rules exclude all available and eligible SPN Nodes. Please change your rules to allow for at least one available and eligible Home Node.", notifications.Action{ Text: "Configure", Type: notifications.ActionTypeOpenSetting, Payload: ¬ifications.ActionTypeOpenSettingPayload{ Key: CfgOptionHomeHubPolicyKey, }, }, ).SyncWithState(module.states) case errors.Is(err, ErrReInitSPNSuggested): notifications.NotifyError( "spn:cannot-bootstrap", "SPN Cannot Bootstrap", "The local state of the SPN network is likely outdated. Portmaster was not able to identify a server to connect to. Please re-initialize the SPN using the tools menu or the button on the notification.", notifications.Action{ ID: "re-init", Text: "Re-Init SPN", Type: notifications.ActionTypeWebhook, Payload: ¬ifications.ActionTypeWebhookPayload{ URL: apiPathForSPNReInit, ResultAction: "display", }, }, ).SyncWithState(module.states) default: notifications.NotifyWarn( "spn:home-hub-failure", "SPN Failed to Connect", fmt.Sprintf("Failed to connect to a home hub: %s. The Portmaster will retry to connect automatically.", err), ).SyncWithState(module.states) } return clientResultReconnect } // Log new connection. home, _ := navigator.Main.GetHome() if home != nil { log.Infof("spn/captain: established new home %s", home.Hub) } return clientResultOk } func clientSetActiveConnectionStatus(ctx *mgr.WorkerCtx) clientComponentResult { // Get current home. home, homeTerminal := navigator.Main.GetHome() if home == nil || homeTerminal == nil { return clientResultReconnect } // Resolve any connection error. module.states.Clear() // Update SPN Status with connection information, if not already correctly set. spnStatus.Lock() defer spnStatus.Unlock() if spnStatus.Status != StatusConnected || spnStatus.HomeHubID != home.Hub.ID { // Fill connection status data. spnStatus.Status = StatusConnected spnStatus.HomeHubID = home.Hub.ID spnStatus.HomeHubName = home.Hub.Info.Name connectedIP, _, err := netutils.IPPortFromAddr(homeTerminal.RemoteAddr()) if err != nil { spnStatus.ConnectedIP = homeTerminal.RemoteAddr().String() } else { spnStatus.ConnectedIP = connectedIP.String() } spnStatus.ConnectedTransport = homeTerminal.Transport().String() geoLoc := home.GetLocation(connectedIP) if geoLoc != nil { spnStatus.ConnectedCountry = &geoLoc.Country } now := time.Now() spnStatus.ConnectedSince = &now // Push new status. pushSPNStatusUpdate() } return clientResultOk } func clientCheckHomeHubConnection(ctx *mgr.WorkerCtx) clientComponentResult { // Check the status of the Home Hub. home, homeTerminal := navigator.Main.GetHome() if home == nil || homeTerminal == nil || homeTerminal.IsBeingAbandoned() { return clientResultReconnect } // Get crane controller for health check. crane := docks.GetAssignedCrane(home.Hub.ID) if crane == nil { log.Errorf("spn/captain: could not find home hub crane for health check") return clientResultOk } // Ping home hub. latency, tErr := pingHome(ctx, crane.Controller, clientHealthCheckTimeout) if tErr != nil { log.Warningf("spn/captain: failed to ping home hub: %s", tErr) // Prepare to reconnect to the network. // Reset all failing states, as these might have been caused by the failing home hub. navigator.Main.ResetFailingStates() // If the last health check is clearly too long ago, assume that the device was sleeping and do not set the home node to failing yet. if time.Since(lastHealthCheck) > clientHealthCheckTickDuration+ clientHealthCheckTickDurationSleepMode+ (clientHealthCheckTimeout*2) { return clientResultReconnect } // Mark the home hub itself as failing, as we want to try to connect to somewhere else. home.MarkAsFailingFor(5 * time.Minute) return clientResultReconnect } lastHealthCheck = time.Now() log.Debugf("spn/captain: pinged home hub in %s", latency) return clientResultOk } func pingHome(ctx *mgr.WorkerCtx, t terminal.Terminal, timeout time.Duration) (latency time.Duration, err *terminal.Error) { started := time.Now() // Start ping operation. pingOp, tErr := crew.NewPingOp(t) if tErr != nil { return 0, tErr } // Wait for response. select { case <-ctx.Done(): return 0, terminal.ErrCanceled case <-time.After(timeout): return 0, terminal.ErrTimeout case result := <-pingOp.Result: if result.Is(terminal.ErrExplicitAck) { return time.Since(started), nil } if result.IsOK() { return 0, result.Wrap("unexpected response") } return 0, result } } ================================================ FILE: spn/captain/config.go ================================================ package captain import ( "sync" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/endpoints" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/navigator" ) var ( // CfgOptionEnableSPNKey is the configuration key for the SPN module. CfgOptionEnableSPNKey = "spn/enable" cfgOptionEnableSPNOrder = 128 // CfgOptionHomeHubPolicyKey is the configuration key for the SPN home policy. CfgOptionHomeHubPolicyKey = "spn/homePolicy" cfgOptionHomeHubPolicy config.StringArrayOption cfgOptionHomeHubPolicyOrder = 145 // CfgOptionDNSExitHubPolicyKey is the configuration key for the SPN DNS exit policy. CfgOptionDNSExitHubPolicyKey = "spn/dnsExitPolicy" cfgOptionDNSExitHubPolicy config.StringArrayOption cfgOptionDNSExitHubPolicyOrder = 148 // CfgOptionUseCommunityNodesKey is the configuration key for whether to use community nodes. CfgOptionUseCommunityNodesKey = "spn/useCommunityNodes" cfgOptionUseCommunityNodes config.BoolOption cfgOptionUseCommunityNodesOrder = 149 // NonCommunityVerifiedOwners holds a list of verified owners that are not // considered "community". NonCommunityVerifiedOwners = []string{"Safing"} // CfgOptionTrustNodeNodesKey is the configuration key for whether additional trusted nodes. CfgOptionTrustNodeNodesKey = "spn/trustNodes" cfgOptionTrustNodeNodes config.StringArrayOption cfgOptionTrustNodeNodesOrder = 150 // Special Access Code. cfgOptionSpecialAccessCodeKey = "spn/specialAccessCode" cfgOptionSpecialAccessCodeDefault = "none" cfgOptionSpecialAccessCode config.StringOption //nolint:unused // Linter, you drunk? cfgOptionSpecialAccessCodeOrder = 160 // IPv6 must be global and accessible. cfgOptionBindToAdvertisedKey = "spn/publicHub/bindToAdvertised" cfgOptionBindToAdvertised config.BoolOption cfgOptionBindToAdvertisedDefault = false cfgOptionBindToAdvertisedOrder = 161 // Config options for use. cfgOptionRoutingAlgorithm config.StringOption ) func prepConfig() error { // Register spn module setting. err := config.Register(&config.Option{ Name: "SPN Module", Key: CfgOptionEnableSPNKey, Description: "Start the Safing Privacy Network module. If turned off, the SPN is fully disabled on this device.", OptType: config.OptTypeBool, DefaultValue: false, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionEnableSPNOrder, config.CategoryAnnotation: "General", }, }) if err != nil { return err } // Home Node Rules err = config.Register(&config.Option{ Name: "Home Node Rules", Key: CfgOptionHomeHubPolicyKey, Description: `Customize which countries should or should not be used for your Home Node. The Home Node is your entry into the SPN. You connect directly to it and all your connections are routed through it. By default, the Portmaster tries to choose the nearest node as your Home Node in order to reduce your exposure to the open Internet. Reconnect to the SPN in order to apply new rules.`, Help: profile.SPNRulesHelp, Sensitive: true, OptType: config.OptTypeStringArray, RequiresRestart: true, ExpertiseLevel: config.ExpertiseLevelExpert, DefaultValue: []string{}, Annotations: config.Annotations{ config.CategoryAnnotation: "Routing", config.DisplayOrderAnnotation: cfgOptionHomeHubPolicyOrder, config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, config.QuickSettingsAnnotation: profile.SPNRulesQuickSettings, endpoints.EndpointListVerdictNamesAnnotation: profile.SPNRulesVerdictNames, }, ValidationRegex: endpoints.ListEntryValidationRegex, ValidationFunc: endpoints.ValidateEndpointListConfigOption, }) if err != nil { return err } cfgOptionHomeHubPolicy = config.Concurrent.GetAsStringArray(CfgOptionHomeHubPolicyKey, []string{}) // DNS Exit Node Rules err = config.Register(&config.Option{ Name: "DNS Exit Node Rules", Key: CfgOptionDNSExitHubPolicyKey, Description: `Customize which countries should or should not be used as DNS Exit Nodes. By default, the Portmaster will exit DNS requests directly at your Home Node in order to keep them fast and close to your location. This is important, as DNS resolution often takes your approximate location into account when deciding which optimized DNS records are returned to you. As the Portmaster encrypts your DNS requests by default, you effectively gain a two-hop security level for your DNS requests in order to protect your privacy. This setting mainly exists for when you need to simulate your presence in another location on a lower level too. This might be necessary to defeat more intelligent geo-blocking systems.`, Help: profile.SPNRulesHelp, Sensitive: true, OptType: config.OptTypeStringArray, RequiresRestart: true, ExpertiseLevel: config.ExpertiseLevelExpert, DefaultValue: []string{}, Annotations: config.Annotations{ config.CategoryAnnotation: "Routing", config.DisplayOrderAnnotation: cfgOptionDNSExitHubPolicyOrder, config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, config.QuickSettingsAnnotation: profile.SPNRulesQuickSettings, endpoints.EndpointListVerdictNamesAnnotation: profile.SPNRulesVerdictNames, }, ValidationRegex: endpoints.ListEntryValidationRegex, ValidationFunc: endpoints.ValidateEndpointListConfigOption, }) if err != nil { return err } cfgOptionDNSExitHubPolicy = config.Concurrent.GetAsStringArray(CfgOptionDNSExitHubPolicyKey, []string{}) err = config.Register(&config.Option{ Name: "Use Community Nodes", Key: CfgOptionUseCommunityNodesKey, Description: "Use nodes (servers) not operated by Safing themselves. The use of community nodes is recommended as it diversifies the ownership of the nodes you use for your connections and further strengthens your privacy. Plain connections (eg. http, smtp, ...) will never exit via community nodes, making this setting safe to use.", Sensitive: true, OptType: config.OptTypeBool, RequiresRestart: true, DefaultValue: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionUseCommunityNodesOrder, config.CategoryAnnotation: "Routing", }, }) if err != nil { return err } cfgOptionUseCommunityNodes = config.Concurrent.GetAsBool(CfgOptionUseCommunityNodesKey, true) err = config.Register(&config.Option{ Name: "Trust Nodes", Key: CfgOptionTrustNodeNodesKey, Description: "Specify which community nodes to additionally trust. These nodes may then also be used as a Home Node, as well as an Exit Node for unencrypted connections.", Help: "You can specify nodes by their ID or their verified operator.", Sensitive: true, OptType: config.OptTypeStringArray, ExpertiseLevel: config.ExpertiseLevelExpert, DefaultValue: []string{}, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionTrustNodeNodesOrder, config.CategoryAnnotation: "Routing", }, }) if err != nil { return err } cfgOptionTrustNodeNodes = config.Concurrent.GetAsStringArray(CfgOptionTrustNodeNodesKey, []string{}) err = config.Register(&config.Option{ Name: "Special Access Code", Key: cfgOptionSpecialAccessCodeKey, Description: "Special Access Codes grant access to the SPN for testing or evaluation purposes.", Sensitive: true, OptType: config.OptTypeString, DefaultValue: cfgOptionSpecialAccessCodeDefault, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionSpecialAccessCodeOrder, config.CategoryAnnotation: "Advanced", }, }) if err != nil { return err } cfgOptionSpecialAccessCode = config.Concurrent.GetAsString(cfgOptionSpecialAccessCodeKey, "") if conf.PublicHub() { err = config.Register(&config.Option{ Name: "Connect From Advertised IPs Only", Key: cfgOptionBindToAdvertisedKey, Description: "Only connect from (bind to) the advertised IP addresses.", OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, DefaultValue: cfgOptionBindToAdvertisedDefault, RequiresRestart: true, Annotations: config.Annotations{ config.DisplayOrderAnnotation: cfgOptionBindToAdvertisedOrder, }, }) if err != nil { return err } cfgOptionBindToAdvertised = config.GetAsBool(cfgOptionBindToAdvertisedKey, cfgOptionBindToAdvertisedDefault) } // Config options for use. if conf.Integrated() { cfgOptionRoutingAlgorithm = config.Concurrent.GetAsString(profile.CfgOptionRoutingAlgorithmKey, navigator.DefaultRoutingProfileID) } else { cfgOptionRoutingAlgorithm = func() string { return navigator.DefaultRoutingProfileID } } return nil } var ( homeHubPolicy endpoints.Endpoints homeHubPolicyLock sync.Mutex homeHubPolicyConfigFlag = config.NewValidityFlag() ) func getHomeHubPolicy() (endpoints.Endpoints, error) { homeHubPolicyLock.Lock() defer homeHubPolicyLock.Unlock() // Return cached value if config is still valid. if homeHubPolicyConfigFlag.IsValid() { return homeHubPolicy, nil } homeHubPolicyConfigFlag.Refresh() // Parse new policy. policy, err := endpoints.ParseEndpoints(cfgOptionHomeHubPolicy()) if err != nil { homeHubPolicy = nil return nil, err } // Save and return the new policy. homeHubPolicy = policy return homeHubPolicy, nil } var ( dnsExitHubPolicy endpoints.Endpoints dnsExitHubPolicyLock sync.Mutex dnsExitHubPolicyConfigFlag = config.NewValidityFlag() ) // GetDNSExitHubPolicy return the current DNS exit policy. func GetDNSExitHubPolicy() (endpoints.Endpoints, error) { dnsExitHubPolicyLock.Lock() defer dnsExitHubPolicyLock.Unlock() // Return cached value if config is still valid. if dnsExitHubPolicyConfigFlag.IsValid() { return dnsExitHubPolicy, nil } dnsExitHubPolicyConfigFlag.Refresh() // Parse new policy. policy, err := endpoints.ParseEndpoints(cfgOptionDNSExitHubPolicy()) if err != nil { dnsExitHubPolicy = nil return nil, err } // Save and return the new policy. dnsExitHubPolicy = policy return dnsExitHubPolicy, nil } ================================================ FILE: spn/captain/establish.go ================================================ package captain import ( "context" "errors" "fmt" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/ships" "github.com/safing/portmaster/spn/terminal" ) // EstablishCrane establishes a crane to another Hub. func EstablishCrane(callerCtx context.Context, dst *hub.Hub) (*docks.Crane, error) { if conf.PublicHub() && dst.ID == publicIdentity.ID { return nil, errors.New("connecting to self") } if docks.GetAssignedCrane(dst.ID) != nil { return nil, fmt.Errorf("route to %s already exists", dst.ID) } ship, err := ships.Launch(callerCtx, dst, nil, nil) if err != nil { return nil, fmt.Errorf("failed to launch ship: %w", err) } // If not a public hub, mark all ships as public in order to show unmasked data in logs. if !conf.PublicHub() { ship.MarkPublic() } crane, err := docks.NewCrane(ship, dst, publicIdentity) if err != nil { return nil, fmt.Errorf("failed to create crane: %w", err) } err = crane.Start(callerCtx) if err != nil { return nil, fmt.Errorf("failed to start crane: %w", err) } // Start gossip op for live map updates. _, tErr := NewGossipOp(crane.Controller) if tErr != nil { crane.Stop(tErr) return nil, fmt.Errorf("failed to start gossip op: %w", tErr) } return crane, nil } // EstablishPublicLane establishes a crane to another Hub and publishes it. func EstablishPublicLane(ctx context.Context, dst *hub.Hub) (*docks.Crane, *terminal.Error) { // Create new context with timeout. // The maximum timeout is a worst case safeguard. // Keep in mind that multiple IPs and protocols may be tried in all configurations. // Some servers will be (possibly on purpose) hard to reach. ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() // Connect to destination and establish communication. crane, err := EstablishCrane(ctx, dst) if err != nil { return nil, terminal.ErrInternalError.With("failed to establish crane: %w", err) } // Publish as Lane. publishOp, tErr := NewPublishOp(crane.Controller, publicIdentity) if tErr != nil { return nil, terminal.ErrInternalError.With("failed to publish: %w", err) } // Wait for publishing to complete. select { case tErr := <-publishOp.Result(): if !tErr.Is(terminal.ErrExplicitAck) { // Stop crane again, because we failed to publish it. defer crane.Stop(nil) return nil, terminal.ErrInternalError.With("failed to publish lane: %w", tErr) } case <-crane.Controller.Ctx().Done(): defer crane.Stop(nil) return nil, terminal.ErrStopping case <-ctx.Done(): defer crane.Stop(nil) if errors.Is(ctx.Err(), context.DeadlineExceeded) { return nil, terminal.ErrTimeout } return nil, terminal.ErrCanceled } // Query all gossip msgs. _, tErr = NewGossipQueryOp(crane.Controller) if tErr != nil { log.Warningf("spn/captain: failed to start initial gossip query: %s", tErr) } return crane, nil } ================================================ FILE: spn/captain/exceptions.go ================================================ package captain import ( "net" "sync" ) var ( exceptionLock sync.Mutex exceptIPv4 net.IP exceptIPv6 net.IP ) func setExceptions(ipv4, ipv6 net.IP) { exceptionLock.Lock() defer exceptionLock.Unlock() exceptIPv4 = ipv4 exceptIPv6 = ipv6 } // IsExcepted checks if the given IP is currently excepted from the SPN. func IsExcepted(ip net.IP) bool { exceptionLock.Lock() defer exceptionLock.Unlock() return ip.Equal(exceptIPv4) || ip.Equal(exceptIPv6) } ================================================ FILE: spn/captain/gossip.go ================================================ package captain import ( "sync" ) var ( gossipOps = make(map[string]*GossipOp) gossipOpsLock sync.RWMutex ) func registerGossipOp(craneID string, op *GossipOp) { gossipOpsLock.Lock() defer gossipOpsLock.Unlock() gossipOps[craneID] = op } func deleteGossipOp(craneID string) { gossipOpsLock.Lock() defer gossipOpsLock.Unlock() delete(gossipOps, craneID) } func gossipRelayMsg(receivedFrom string, msgType GossipMsgType, data []byte) { gossipOpsLock.RLock() defer gossipOpsLock.RUnlock() for craneID, gossipOp := range gossipOps { // Don't return same msg back to sender. if craneID == receivedFrom { continue } gossipOp.sendMsg(msgType, data) } } ================================================ FILE: spn/captain/hooks.go ================================================ package captain import ( "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/docks" ) func startDockHooks() { docks.RegisterCraneUpdateHook(handleCraneUpdate) } func stopDockHooks() { docks.ResetCraneUpdateHook() } func handleCraneUpdate(crane *docks.Crane) { if crane == nil { return } if conf.Client() && crane.Controller != nil && crane.Controller.Abandoning.IsSet() { // Check connection to home hub. triggerClientHealthCheck() } if conf.PublicHub() && crane.Public() { // Update Hub status. updateConnectionStatus() } } func updateConnectionStatus() { // Delay updating status for a better chance to combine multiple changes. module.statusUpdater.Delay(maintainStatusUpdateDelay) // Check if we lost all connections and trigger a pending restart if we did. for _, crane := range docks.GetAllAssignedCranes() { if crane.Public() && !crane.Stopped() { // There is at least one public and active crane, so don't restart now. return } } // TODO(vladimir): what was this needed for? // updates.TriggerRestartIfPending() } ================================================ FILE: spn/captain/intel.go ================================================ package captain import ( "context" "fmt" "os" "sync" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/updates" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/navigator" "github.com/safing/portmaster/spn/ships" ) var ( intelResource *updates.Artifact intelResourceName = "main-intel.yaml" intelResourceMapName = "main" intelResourceUpdateLock sync.Mutex ) func registerIntelUpdateHook() error { module.instance.IntelUpdates().EventResourcesUpdated.AddCallback("update SPN intel", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { return false, updateSPNIntel(wc.Ctx(), nil) }) module.instance.Config().EventConfigChange.AddCallback("update SPN intel", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { return false, updateSPNIntel(wc.Ctx(), nil) }) return nil } func updateSPNIntel(_ context.Context, _ interface{}) (err error) { intelResourceUpdateLock.Lock() defer intelResourceUpdateLock.Unlock() // Only update SPN intel when using the matching map. if conf.MainMapName != intelResourceMapName { return fmt.Errorf("intel resource not for map %q", conf.MainMapName) } // Get possibly updated file. file, err := module.instance.IntelUpdates().GetFile(intelResourceName) if err != nil { return fmt.Errorf("failed to get SPN intel update: %w", err) } // If intel is not initialized, skip version comparison. if navigator.Main.GetIntel() != nil { // Check if file is newer. // Continue on check failure. newer, ok := file.IsNewerThan(intelResource) if ok && !newer { return nil } } // Load intel file from disk. intelData, err := os.ReadFile(file.Path()) if err != nil { return fmt.Errorf("failed to load SPN intel update: %w", err) } // Parse and apply intel data. intel, err := hub.ParseIntel(intelData) if err != nil { return fmt.Errorf("failed to parse SPN intel update: %w", err) } // Apply new intel. setVirtualNetworkConfig(intel.VirtualNetworks) err = navigator.Main.UpdateIntel(intel, cfgOptionTrustNodeNodes()) if err != nil { return fmt.Errorf("failed to update intel on map: %w", err) } intelResource = file return nil } func resetSPNIntel() { intelResourceUpdateLock.Lock() defer intelResourceUpdateLock.Unlock() intelResource = nil } func setVirtualNetworkConfig(configs []*hub.VirtualNetworkConfig) { // Do nothing if not public Hub. if !conf.PublicHub() { return } // Reset if there are no virtual networks configured. if len(configs) == 0 { ships.SetVirtualNetworkConfig(nil) } // Check if we are in a virtual network. for _, config := range configs { if _, ok := config.Mapping[publicIdentity.Hub.ID]; ok { ships.SetVirtualNetworkConfig(config) return } } // If not, reset - we might have been in one before. ships.SetVirtualNetworkConfig(nil) } ================================================ FILE: spn/captain/module.go ================================================ package captain import ( "errors" "fmt" "net" "net/http" "sync/atomic" "time" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/updates" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/crew" "github.com/safing/portmaster/spn/navigator" "github.com/safing/portmaster/spn/patrol" "github.com/safing/portmaster/spn/ships" ) // SPNConnectedEvent is the name of the event that is fired when the SPN has connected and is ready. const SPNConnectedEvent = "spn connect" // Captain is the main module of the SPN. type Captain struct { mgr *mgr.Manager instance instance healthCheckTicker *mgr.SleepyTicker publicIdentityUpdater *mgr.WorkerMgr statusUpdater *mgr.WorkerMgr states *mgr.StateMgr EventSPNConnected *mgr.EventMgr[struct{}] } // Manager returns the module manager. func (c *Captain) Manager() *mgr.Manager { return c.mgr } // States returns the module states. func (c *Captain) States() *mgr.StateMgr { return c.states } // Start starts the module. func (c *Captain) Start() error { return start() } // Stop stops the module. func (c *Captain) Stop() error { return stop() } // SetSleep sets the sleep mode of the module. func (c *Captain) SetSleep(enabled bool) { if c.healthCheckTicker != nil { c.healthCheckTicker.SetSleep(enabled) } } func (c *Captain) prep() error { // Check if we can parse the bootstrap hub flag. if err := prepBootstrapHubFlag(); err != nil { return err } // Register SPN status provider. if err := registerSPNStatusProvider(); err != nil { return err } // Register API endpoints. if err := registerAPIEndpoints(); err != nil { return err } if conf.PublicHub() { // Register API authenticator. if err := api.SetAuthenticator(apiAuthenticator); err != nil { return err } c.instance.Patrol().EventChangeSignal.AddCallback( "trigger hub status maintenance", func(_ *mgr.WorkerCtx, _ struct{}) (bool, error) { TriggerHubStatusMaintenance() return false, nil }, ) } return prepConfig() } func start() error { maskingBytes, err := rng.Bytes(16) if err != nil { return fmt.Errorf("failed to get random bytes for masking: %w", err) } ships.EnableMasking(maskingBytes) // Initialize identity and piers. if conf.PublicHub() { // Load identity. if err := loadPublicIdentity(); err != nil { return fmt.Errorf("load public identity: %w", err) } // Check if any networks are configured. if !conf.HubHasIPv4() && !conf.HubHasIPv6() { return errors.New("no IP addresses for Hub configured (or detected)") } // Start management of identity and piers. if err := prepPublicIdentityMgmt(); err != nil { return err } // Set ID to display on http info page. ships.DisplayHubID = publicIdentity.ID // Start listeners. if err := startPiers(); err != nil { return err } // Enable connect operation. crew.EnableConnecting(publicIdentity.Hub) } // Initialize SPN intel. // Do this synchronously to ensure everything is up to date before err = module.mgr.Do("start", func(wc *mgr.WorkerCtx) error { if err := registerIntelUpdateHook(); err != nil { return err } if err := updateSPNIntel(module.mgr.Ctx(), nil); err != nil { log.Errorf("spn/captain: failed to update SPN intel: %s", err) } return nil }) if err != nil { return err } // Subscribe to updates of cranes. startDockHooks() // bootstrapping if err := processBootstrapHubFlag(); err != nil { return err } if err := processBootstrapFileFlag(); err != nil { return err } // network optimizer if conf.PublicHub() { module.mgr.Delay("optimize network delay", 15*time.Second, optimizeNetwork).Repeat(1 * time.Minute) } // client + home hub manager if conf.Client() { module.mgr.Go("client manager", clientManager) // Reset failing hubs when the network changes while not connected. module.instance.NetEnv().EventNetworkChange.AddCallback("reset failing hubs", func(_ *mgr.WorkerCtx, _ struct{}) (bool, error) { if ready.IsNotSet() { navigator.Main.ResetFailingStates() } return false, nil }) } return nil } func stop() error { // Reset intel resource so that it is loaded again when starting. resetSPNIntel() // Unregister crane update hook. stopDockHooks() // Send shutdown status message. if conf.PublicHub() { publishShutdownStatus() stopPiers() } return nil } // apiAuthenticator grants User permissions for local API requests. func apiAuthenticator(r *http.Request, s *http.Server) (*api.AuthToken, error) { // Get remote IP. host, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { return nil, fmt.Errorf("failed to split host/port: %w", err) } remoteIP := net.ParseIP(host) if remoteIP == nil { return nil, fmt.Errorf("failed to parse remote address %s", host) } if !netutils.GetIPScope(remoteIP).IsLocalhost() { return nil, api.ErrAPIAccessDeniedMessage } return &api.AuthToken{ Read: api.PermitUser, Write: api.PermitUser, }, nil } var ( module *Captain shimLoaded atomic.Bool ) // New returns a new Captain module. func New(instance instance) (*Captain, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Captain") module = &Captain{ mgr: m, instance: instance, states: mgr.NewStateMgr(m), EventSPNConnected: mgr.NewEventMgr[struct{}](SPNConnectedEvent, m), publicIdentityUpdater: m.NewWorkerMgr("maintain public identity", maintainPublicIdentity, nil), statusUpdater: m.NewWorkerMgr("maintain public status", maintainPublicStatus, nil), } if err := module.prep(); err != nil { return nil, err } return module, nil } type instance interface { NetEnv() *netenv.NetEnv Patrol() *patrol.Patrol Config() *config.Config IntelUpdates() *updates.Updater SPNGroup() *mgr.ExtendedGroup } ================================================ FILE: spn/captain/navigation.go ================================================ package captain import ( "context" "errors" "fmt" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/profile/endpoints" "github.com/safing/portmaster/spn/access" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/navigator" "github.com/safing/portmaster/spn/terminal" ) const stopCraneAfterBeingUnsuggestedFor = 6 * time.Hour var ( // ErrAllHomeHubsExcluded is returned when all available home hubs were excluded. ErrAllHomeHubsExcluded = errors.New("all home hubs are excluded") // ErrReInitSPNSuggested is returned when no home hub can be found, even without rules. ErrReInitSPNSuggested = errors.New("SPN re-init suggested") ) func establishHomeHub(ctx *mgr.WorkerCtx) error { // Get own IP. locations, ok := netenv.GetInternetLocation() if !ok || len(locations.All) == 0 { return errors.New("failed to locate own device") } log.Debugf( "spn/captain: looking for new home hub near %s and %s", locations.BestV4(), locations.BestV6(), ) // Get own entity. // Checking the entity against the entry policies is somewhat hit and miss // anyway, as the device location is an approximation. var myEntity *intel.Entity if dl := locations.BestV4(); dl != nil && dl.IP != nil { myEntity = (&intel.Entity{IP: dl.IP}).Init(0) myEntity.FetchData(ctx.Ctx()) } else if dl := locations.BestV6(); dl != nil && dl.IP != nil { myEntity = (&intel.Entity{IP: dl.IP}).Init(0) myEntity.FetchData(ctx.Ctx()) } // Get home hub policy for selecting the home hub. homePolicy, err := getHomeHubPolicy() if err != nil { return err } // Build navigation options for searching for a home hub. opts := &navigator.Options{ Home: &navigator.HomeHubOptions{ HubPolicies: []endpoints.Endpoints{homePolicy}, CheckHubPolicyWith: myEntity, }, } // Add requirement to only use Safing nodes when not using community nodes. if !cfgOptionUseCommunityNodes() { opts.Home.RequireVerifiedOwners = NonCommunityVerifiedOwners } // Require a trusted home node when the routing profile requires less than two hops. routingProfile := navigator.GetRoutingProfile(cfgOptionRoutingAlgorithm()) if routingProfile.MinHops < 2 { opts.Home.Regard = opts.Home.Regard.Add(navigator.StateTrusted) } // Find nearby hubs. findCandidates: candidates, err := navigator.Main.FindNearestHubs( locations.BestV4().LocationOrNil(), locations.BestV6().LocationOrNil(), opts, navigator.HomeHub, ) if err != nil { switch { case errors.Is(err, navigator.ErrEmptyMap): // bootstrap to the network! err := bootstrapWithUpdates() if err != nil { return err } goto findCandidates case errors.Is(err, navigator.ErrAllPinsDisregarded): if len(homePolicy) > 0 { return ErrAllHomeHubsExcluded } return ErrReInitSPNSuggested default: return fmt.Errorf("failed to find nearby hubs: %w", err) } } // Try connecting to a hub. var tries int var candidate *hub.Hub for tries, candidate = range candidates { err = connectToHomeHub(ctx, candidate) if err != nil { // Check if context is canceled. if ctx.IsDone() { return ctx.Ctx().Err() } // Check if the SPN protocol is stopping again. if errors.Is(err, terminal.ErrStopping) { return err } log.Warningf("spn/captain: failed to connect to %s as new home: %s", candidate, err) } else { log.Infof("spn/captain: established connection to %s as new home with %d failed tries", candidate, tries) return nil } } if err != nil { return fmt.Errorf("failed to connect to a new home hub - tried %d hubs: %w", tries+1, err) } return errors.New("no home hub candidates available") } func connectToHomeHub(wCtx *mgr.WorkerCtx, dst *hub.Hub) error { // Create new context with timeout. // The maximum timeout is a worst case safeguard. // Keep in mind that multiple IPs and protocols may be tried in all configurations. // Some servers will be (possibly on purpose) hard to reach. ctx, cancel := context.WithTimeout(wCtx.Ctx(), 5*time.Minute) defer cancel() // Set and clean up exceptions. setExceptions(dst.Info.IPv4, dst.Info.IPv6) defer setExceptions(nil, nil) // Connect to hub. crane, err := EstablishCrane(ctx, dst) if err != nil { return err } // Cleanup connection in case of failure. var success bool defer func() { if !success { crane.Stop(nil) } }() // Query all gossip msgs on first connection. gossipQuery, tErr := NewGossipQueryOp(crane.Controller) if tErr != nil { log.Warningf("spn/captain: failed to start initial gossip query: %s", tErr) } // Wait for gossip query to complete. select { case <-gossipQuery.ctx.Done(): case <-ctx.Done(): return context.Canceled } // Create communication terminal. homeTerminal, initData, tErr := docks.NewLocalCraneTerminal(crane, nil, terminal.DefaultHomeHubTerminalOpts()) if tErr != nil { return tErr.Wrap("failed to create home terminal") } tErr = crane.EstablishNewTerminal(homeTerminal, initData) if tErr != nil { return tErr.Wrap("failed to connect home terminal") } if !DisableAccount { // Authenticate to home hub. authOp, tErr := access.AuthorizeToTerminal(homeTerminal) if tErr != nil { return tErr.Wrap("failed to authorize") } select { case tErr := <-authOp.Result: if !tErr.Is(terminal.ErrExplicitAck) { return tErr.Wrap("failed to authenticate to") } case <-time.After(3 * time.Second): return terminal.ErrTimeout.With("waiting for auth to complete") case <-ctx.Done(): return terminal.ErrStopping } } // Set new home on map. ok := navigator.Main.SetHome(dst.ID, homeTerminal) if !ok { return errors.New("failed to set home hub on map") } // Assign crane to home hub in order to query it later. docks.AssignCrane(crane.ConnectedHub.ID, crane) success = true return nil } func optimizeNetwork(ctx *mgr.WorkerCtx) error { if publicIdentity == nil { return nil } optimize: result, err := navigator.Main.Optimize(nil) if err != nil { if errors.Is(err, navigator.ErrEmptyMap) { // bootstrap to the network! err := bootstrapWithUpdates() if err != nil { return err } goto optimize } return err } // Create any new connections. var createdConnections int var attemptedConnections int for _, connectTo := range result.SuggestedConnections { // Skip duplicates. if connectTo.Duplicate { continue } // Check if connection already exists. crane := docks.GetAssignedCrane(connectTo.Hub.ID) if crane != nil { // Update last suggested timestamp. crane.NetState.UpdateLastSuggestedAt() // Continue crane if stopping. if crane.AbortStopping() { log.Infof("spn/captain: optimization aborted retiring of %s, removed stopping mark", crane) crane.NotifyUpdate() } // Create new connections if we have connects left. } else if createdConnections < result.MaxConnect { attemptedConnections++ crane, tErr := EstablishPublicLane(ctx.Ctx(), connectTo.Hub) if !tErr.IsOK() { log.Warningf("spn/captain: failed to establish lane to %s: %s", connectTo.Hub, tErr) } else { createdConnections++ crane.NetState.UpdateLastSuggestedAt() log.Infof("spn/captain: established lane to %s", connectTo.Hub) } } } // Log optimization result. if attemptedConnections > 0 { log.Infof( "spn/captain: created %d/%d new connections for %s optimization", createdConnections, attemptedConnections, result.Purpose) } else { log.Infof( "spn/captain: checked %d connections for %s optimization", len(result.SuggestedConnections), result.Purpose, ) } // Retire cranes if unsuggested for a while. if result.StopOthers { for _, crane := range docks.GetAllAssignedCranes() { switch { case crane.Stopped(): // Crane already stopped. case crane.IsStopping(): // Crane is stopping, forcibly stop if mine and suggested. if crane.IsMine() && crane.NetState.StopSuggested() { crane.Stop(nil) } case crane.IsMine() && crane.NetState.StoppingSuggested(): // Mark as stopping if mine and suggested. crane.MarkStopping() case crane.NetState.RequestStoppingSuggested(stopCraneAfterBeingUnsuggestedFor): // Mark as stopping requested. crane.MarkStoppingRequested() } } } return nil } ================================================ FILE: spn/captain/op_gossip.go ================================================ package captain import ( "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" "github.com/safing/structures/varint" ) // GossipOpType is the type ID of the gossip operation. const GossipOpType string = "gossip" // GossipMsgType is the gossip message type. type GossipMsgType uint8 // Gossip Message Types. const ( GossipHubAnnouncementMsg GossipMsgType = 1 GossipHubStatusMsg GossipMsgType = 2 ) func (msgType GossipMsgType) String() string { switch msgType { case GossipHubAnnouncementMsg: return "hub announcement" case GossipHubStatusMsg: return "hub status" default: return "unknown gossip msg" } } // GossipOp is used to gossip Hub messages. type GossipOp struct { terminal.OperationBase craneID string } // Type returns the type ID. func (op *GossipOp) Type() string { return GossipOpType } func init() { terminal.RegisterOpType(terminal.OperationFactory{ Type: GossipOpType, Requires: terminal.IsCraneController, Start: runGossipOp, }) } // NewGossipOp start a new gossip operation. func NewGossipOp(controller *docks.CraneControllerTerminal) (*GossipOp, *terminal.Error) { // Create and init. op := &GossipOp{ craneID: controller.Crane.ID, } err := controller.StartOperation(op, nil, 1*time.Minute) if err != nil { return nil, err } op.InitOperationBase(controller, op.ID()) // Register and return. registerGossipOp(controller.Crane.ID, op) return op, nil } func runGossipOp(t terminal.Terminal, opID uint32, data *container.Container) (terminal.Operation, *terminal.Error) { // Check if we are run by a controller. controller, ok := t.(*docks.CraneControllerTerminal) if !ok { return nil, terminal.ErrIncorrectUsage.With("gossip op may only be started by a crane controller terminal, but was started by %T", t) } // Create, init, register and return. op := &GossipOp{ craneID: controller.Crane.ID, } op.InitOperationBase(t, opID) registerGossipOp(controller.Crane.ID, op) return op, nil } func (op *GossipOp) sendMsg(msgType GossipMsgType, data []byte) { // Create message. msg := op.NewEmptyMsg() msg.Data = container.New( varint.Pack8(uint8(msgType)), data, ) msg.Unit.MakeHighPriority() // Send. err := op.Send(msg, 1*time.Second) if err != nil { log.Debugf("spn/captain: failed to forward %s via %s: %s", msgType, op.craneID, err) } } // Deliver delivers a message to the operation. func (op *GossipOp) Deliver(msg *terminal.Msg) *terminal.Error { defer msg.Finish() gossipMsgTypeN, err := msg.Data.GetNextN8() if err != nil { return terminal.ErrMalformedData.With("failed to parse gossip message type") } gossipMsgType := GossipMsgType(gossipMsgTypeN) // Prepare data. data := msg.Data.CompileData() var announcementData, statusData []byte switch gossipMsgType { case GossipHubAnnouncementMsg: announcementData = data case GossipHubStatusMsg: statusData = data default: log.Warningf("spn/captain: received unknown gossip message type from %s: %d", op.craneID, gossipMsgType) return nil } // Import and verify. h, forward, tErr := docks.ImportAndVerifyHubInfo(module.mgr.Ctx(), "", announcementData, statusData, conf.MainMapName, conf.MainMapScope) if tErr != nil { if tErr.Is(hub.ErrOldData) { log.Debugf("spn/captain: ignoring old %s from %s", gossipMsgType, op.craneID) } else { log.Warningf("spn/captain: failed to import %s from %s: %s", gossipMsgType, op.craneID, tErr) } } else if forward { // Only log if we received something to save/forward. log.Infof("spn/captain: received %s for %s", gossipMsgType, h) } // Relay data. if forward { gossipRelayMsg(op.craneID, gossipMsgType, data) } return nil } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. func (op *GossipOp) HandleStop(err *terminal.Error) (errorToSend *terminal.Error) { deleteGossipOp(op.craneID) return err } ================================================ FILE: spn/captain/op_gossip_query.go ================================================ package captain import ( "context" "strings" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" "github.com/safing/structures/varint" ) // GossipQueryOpType is the type ID of the gossip query operation. const GossipQueryOpType string = "gossip/query" // GossipQueryOp is used to query gossip messages. type GossipQueryOp struct { terminal.OperationBase t terminal.Terminal client bool importCnt int ctx context.Context cancelCtx context.CancelFunc } // Type returns the type ID. func (op *GossipQueryOp) Type() string { return GossipQueryOpType } func init() { terminal.RegisterOpType(terminal.OperationFactory{ Type: GossipQueryOpType, Requires: terminal.IsCraneController, Start: runGossipQueryOp, }) } // NewGossipQueryOp starts a new gossip query operation. func NewGossipQueryOp(t terminal.Terminal) (*GossipQueryOp, *terminal.Error) { // Create and init. op := &GossipQueryOp{ t: t, client: true, } op.ctx, op.cancelCtx = context.WithCancel(t.Ctx()) err := t.StartOperation(op, nil, 1*time.Minute) if err != nil { return nil, err } return op, nil } func runGossipQueryOp(t terminal.Terminal, opID uint32, data *container.Container) (terminal.Operation, *terminal.Error) { // Create, init, register and return. op := &GossipQueryOp{t: t} op.ctx, op.cancelCtx = context.WithCancel(t.Ctx()) op.InitOperationBase(t, opID) module.mgr.Go("gossip query handler", op.handler) return op, nil } func (op *GossipQueryOp) handler(_ *mgr.WorkerCtx) error { tErr := op.sendMsgs(hub.MsgTypeAnnouncement) if tErr != nil { op.Stop(op, tErr) return nil // Clean worker exit. } tErr = op.sendMsgs(hub.MsgTypeStatus) if tErr != nil { op.Stop(op, tErr) return nil // Clean worker exit. } op.Stop(op, nil) return nil // Clean worker exit. } func (op *GossipQueryOp) sendMsgs(msgType hub.MsgType) *terminal.Error { it, err := hub.QueryRawGossipMsgs(conf.MainMapName, msgType) if err != nil { return terminal.ErrInternalError.With("failed to query: %w", err) } defer it.Cancel() iterating: for { select { case r := <-it.Next: // Check if we are done. if r == nil { return nil } // Ensure we're handling a hub msg. hubMsg, err := hub.EnsureHubMsg(r) if err != nil { log.Warningf("spn/captain: failed to load hub msg: %s", err) continue iterating } // Create gossip msg. var c *container.Container switch hubMsg.Type { case hub.MsgTypeAnnouncement: c = container.New( varint.Pack8(uint8(GossipHubAnnouncementMsg)), hubMsg.Data, ) case hub.MsgTypeStatus: c = container.New( varint.Pack8(uint8(GossipHubStatusMsg)), hubMsg.Data, ) default: log.Warningf("spn/captain: unknown hub msg for gossip query at %q: %s", hubMsg.Key(), hubMsg.Type) } // Send msg. if c != nil { msg := op.NewEmptyMsg() msg.Unit.MakeHighPriority() msg.Data = c tErr := op.Send(msg, 1*time.Second) if tErr != nil { return tErr.Wrap("failed to send msg") } } case <-op.ctx.Done(): return terminal.ErrStopping } } } // Deliver delivers the message to the operation. func (op *GossipQueryOp) Deliver(msg *terminal.Msg) *terminal.Error { defer msg.Finish() gossipMsgTypeN, err := msg.Data.GetNextN8() if err != nil { return terminal.ErrMalformedData.With("failed to parse gossip message type") } gossipMsgType := GossipMsgType(gossipMsgTypeN) // Prepare data. data := msg.Data.CompileData() var announcementData, statusData []byte switch gossipMsgType { case GossipHubAnnouncementMsg: announcementData = data case GossipHubStatusMsg: statusData = data default: log.Warningf("spn/captain: received unknown gossip message type from gossip query: %d", gossipMsgType) return nil } // Import and verify. h, forward, tErr := docks.ImportAndVerifyHubInfo(module.mgr.Ctx(), "", announcementData, statusData, conf.MainMapName, conf.MainMapScope) if tErr != nil { log.Warningf("spn/captain: failed to import %s from gossip query: %s", gossipMsgType, tErr) } else { log.Infof("spn/captain: received %s for %s from gossip query", gossipMsgType, h) op.importCnt++ } // Relay data. if forward { // TODO: Find better way to get craneID. craneID := strings.SplitN(op.t.FmtID(), "#", 2)[0] gossipRelayMsg(craneID, gossipMsgType, data) } return nil } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. func (op *GossipQueryOp) HandleStop(err *terminal.Error) (errorToSend *terminal.Error) { if op.client { log.Infof("spn/captain: gossip query imported %d entries", op.importCnt) } op.cancelCtx() return err } ================================================ FILE: spn/captain/op_publish.go ================================================ package captain import ( "time" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" ) // PublishOpType is the type ID of the publish operation. const PublishOpType string = "publish" // PublishOp is used to publish a connection. type PublishOp struct { terminal.OperationBase controller *docks.CraneControllerTerminal identity *cabin.Identity requestingHub *hub.Hub verification *cabin.Verification result chan *terminal.Error } // Type returns the type ID. func (op *PublishOp) Type() string { return PublishOpType } func init() { terminal.RegisterOpType(terminal.OperationFactory{ Type: PublishOpType, Requires: terminal.IsCraneController, Start: runPublishOp, }) } // NewPublishOp start a new publish operation. func NewPublishOp(controller *docks.CraneControllerTerminal, identity *cabin.Identity) (*PublishOp, *terminal.Error) { // Create and init. op := &PublishOp{ controller: controller, identity: identity, result: make(chan *terminal.Error, 1), } msg := container.New() // Add Hub Announcement. announcementData, err := identity.ExportAnnouncement() if err != nil { return nil, terminal.ErrInternalError.With("failed to export announcement: %w", err) } msg.AppendAsBlock(announcementData) // Add Hub Status. statusData, err := identity.ExportStatus() if err != nil { return nil, terminal.ErrInternalError.With("failed to export status: %w", err) } msg.AppendAsBlock(statusData) tErr := controller.StartOperation(op, msg, 10*time.Second) if tErr != nil { return nil, tErr } return op, nil } func runPublishOp(t terminal.Terminal, opID uint32, data *container.Container) (terminal.Operation, *terminal.Error) { // Check if we are run by a controller. controller, ok := t.(*docks.CraneControllerTerminal) if !ok { return nil, terminal.ErrIncorrectUsage.With("publish op may only be started by a crane controller terminal, but was started by %T", t) } // Parse and import Announcement and Status. announcementData, err := data.GetNextBlock() if err != nil { return nil, terminal.ErrMalformedData.With("failed to get announcement: %w", err) } statusData, err := data.GetNextBlock() if err != nil { return nil, terminal.ErrMalformedData.With("failed to get status: %w", err) } h, forward, tErr := docks.ImportAndVerifyHubInfo(module.mgr.Ctx(), "", announcementData, statusData, conf.MainMapName, conf.MainMapScope) if tErr != nil { return nil, tErr.Wrap("failed to import and verify hub") } // Update reference in case it was changed by the import. controller.Crane.ConnectedHub = h // Relay data. if forward { gossipRelayMsg(controller.Crane.ID, GossipHubAnnouncementMsg, announcementData) gossipRelayMsg(controller.Crane.ID, GossipHubStatusMsg, statusData) } // Create verification request. v, request, err := cabin.CreateVerificationRequest(PublishOpType, "", "") if err != nil { return nil, terminal.ErrInternalError.With("failed to create verification request: %w", err) } // Create operation. op := &PublishOp{ controller: controller, requestingHub: h, verification: v, result: make(chan *terminal.Error, 1), } op.InitOperationBase(controller, opID) // Reply with verification request. tErr = op.Send(op.NewMsg(request), 10*time.Second) if tErr != nil { return nil, tErr.Wrap("failed to send verification request") } return op, nil } // Deliver delivers a message to the operation. func (op *PublishOp) Deliver(msg *terminal.Msg) *terminal.Error { defer msg.Finish() if op.identity != nil { // Client // Sign the received verification request. response, err := op.identity.SignVerificationRequest(msg.Data.CompileData(), PublishOpType, "", "") if err != nil { return terminal.ErrPermissionDenied.With("signing verification request failed: %w", err) } return op.Send(op.NewMsg(response), 10*time.Second) } else if op.requestingHub != nil { // Server // Verify the signed request. err := op.verification.Verify(msg.Data.CompileData(), op.requestingHub) if err != nil { return terminal.ErrPermissionDenied.With("checking verification request failed: %w", err) } return terminal.ErrExplicitAck } return terminal.ErrInternalError.With("invalid operation state") } // Result returns the result (end error) of the operation. func (op *PublishOp) Result() <-chan *terminal.Error { return op.result } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. func (op *PublishOp) HandleStop(tErr *terminal.Error) (errorToSend *terminal.Error) { if tErr.Is(terminal.ErrExplicitAck) { // TODO: Check for concurrenct access. if op.controller.Crane.ConnectedHub == nil { op.controller.Crane.ConnectedHub = op.requestingHub } // Publish crane, abort if it fails. err := op.controller.Crane.Publish() if err != nil { tErr = terminal.ErrInternalError.With("failed to publish crane: %w", err) op.controller.Crane.Stop(tErr) } else { op.controller.Crane.NotifyUpdate() } } select { case op.result <- tErr: default: } return tErr } ================================================ FILE: spn/captain/piers.go ================================================ package captain import ( "context" "errors" "fmt" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/profile/endpoints" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/ships" ) var ( dockingRequests = make(chan ships.Ship, 100) piers []ships.Pier ) func startPiers() error { // Get and check transports. transports := publicIdentity.Hub.Info.Transports if len(transports) == 0 { return errors.New("no transports defined") } piers = make([]ships.Pier, 0, len(transports)) for _, t := range transports { // Parse transport. transport, err := hub.ParseTransport(t) if err != nil { return fmt.Errorf("cannot build pier for invalid transport %q: %w", t, err) } // Establish pier / listener. pier, err := ships.EstablishPier(transport, dockingRequests) if err != nil { return fmt.Errorf("failed to establish pier for transport %q: %w", t, err) } piers = append(piers, pier) log.Infof("spn/captain: pier for transport %q built", t) } // Start worker to handle docking requests. module.mgr.Go("docking request handler", dockingRequestHandler) return nil } func stopPiers() { for _, pier := range piers { pier.Abolish() } } func dockingRequestHandler(wc *mgr.WorkerCtx) error { // Sink all waiting ships when this worker ends. // But don't be destructive so the service worker could recover. defer func() { for { select { case ship := <-dockingRequests: if ship != nil { ship.Sink() } default: return } } }() for { select { case <-wc.Done(): return nil case ship := <-dockingRequests: // Ignore nil ships. if ship == nil { continue } if err := checkDockingPermission(wc.Ctx(), ship); err != nil { log.Warningf("spn/captain: denied ship from %s to dock at pier %s: %s", ship.RemoteAddr(), ship.Transport().String(), err) } else { handleDockingRequest(ship) } } } } func checkDockingPermission(ctx context.Context, ship ships.Ship) error { remoteIP, remotePort, err := netutils.IPPortFromAddr(ship.RemoteAddr()) if err != nil { return fmt.Errorf("failed to parse remote IP: %w", err) } // Create entity. entity := (&intel.Entity{ IP: remoteIP, Protocol: uint8(netutils.ProtocolFromNetwork(ship.RemoteAddr().Network())), Port: remotePort, }).Init(ship.Transport().Port) entity.FetchData(ctx) // Check against policy. result, reason := publicIdentity.Hub.GetInfo().EntryPolicy().Match(ctx, entity) if result == endpoints.Denied { return fmt.Errorf("entry policy violated: %s", reason) } return nil } func handleDockingRequest(ship ships.Ship) { log.Infof("spn/captain: pemitting %s to dock", ship) crane, err := docks.NewCrane(ship, nil, publicIdentity) if err != nil { log.Warningf("spn/captain: failed to commission crane for %s: %s", ship, err) return } module.mgr.Go("start crane", func(wc *mgr.WorkerCtx) error { _ = crane.Start(wc.Ctx()) // Crane handles errors internally. return nil }) } ================================================ FILE: spn/captain/public.go ================================================ package captain import ( "errors" "fmt" "sort" "time" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/metrics" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/navigator" "github.com/safing/portmaster/spn/patrol" ) const ( maintainStatusInterval = 15 * time.Minute maintainStatusUpdateDelay = 5 * time.Second ) var ( publicIdentity *cabin.Identity publicIdentityKey = "core:spn/public/identity" ) func loadPublicIdentity() (err error) { var changed bool publicIdentity, changed, err = cabin.LoadIdentity(publicIdentityKey) switch { case err == nil: // load was successful log.Infof("spn/captain: loaded public hub identity %s", publicIdentity.Hub.ID) case errors.Is(err, database.ErrNotFound): // does not exist, create new publicIdentity, err = cabin.CreateIdentity(module.mgr.Ctx(), conf.MainMapName) if err != nil { return fmt.Errorf("failed to create new identity: %w", err) } publicIdentity.SetKey(publicIdentityKey) changed = true log.Infof("spn/captain: created new public hub identity %s", publicIdentity.ID) default: // loading error, abort return fmt.Errorf("failed to load public identity: %w", err) } // Save to database if the identity changed. if changed { err = publicIdentity.Save() if err != nil { return fmt.Errorf("failed to save new/updated identity to database: %w", err) } } // Set available networks. conf.SetHubNetworks( publicIdentity.Hub.Info.IPv4 != nil, publicIdentity.Hub.Info.IPv6 != nil, ) if cfgOptionBindToAdvertised() { conf.SetBindAddr(publicIdentity.Hub.Info.IPv4, publicIdentity.Hub.Info.IPv6) } // Set Home Hub before updating the hub on the map, as this would trigger a // recalculation without a Home Hub. ok := navigator.Main.SetHome(publicIdentity.ID, nil) // Always update the navigator in any case in order to sync the reference to // the active struct of the identity. navigator.Main.UpdateHub(publicIdentity.Hub) // Setting the Home Hub will have failed if the identidy was only just // created - try again if it failed. if !ok { ok = navigator.Main.SetHome(publicIdentity.ID, nil) if !ok { return errors.New("failed to set self as home hub") } } return nil } func prepPublicIdentityMgmt() error { module.statusUpdater.Repeat(maintainStatusInterval) module.instance.Config().EventConfigChange.AddCallback("update public identity from config", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { module.publicIdentityUpdater.Delay(5 * time.Minute) return false, nil }) return nil } // TriggerHubStatusMaintenance queues the Hub status update task to be executed. func TriggerHubStatusMaintenance() { module.statusUpdater.Go() } func maintainPublicIdentity(_ *mgr.WorkerCtx) error { changed, err := publicIdentity.MaintainAnnouncement(nil, false) if err != nil { return fmt.Errorf("failed to maintain announcement: %w", err) } if !changed { return nil } // Update on map. navigator.Main.UpdateHub(publicIdentity.Hub) log.Debug("spn/captain: updated own hub on map after announcement change") // export announcement announcementData, err := publicIdentity.ExportAnnouncement() if err != nil { return fmt.Errorf("failed to export announcement: %w", err) } // forward to other connected Hubs gossipRelayMsg("", GossipHubAnnouncementMsg, announcementData) return nil } func maintainPublicStatus(ctx *mgr.WorkerCtx) error { // Get current lanes. cranes := docks.GetAllAssignedCranes() lanes := make([]*hub.Lane, 0, len(cranes)) for _, crane := range cranes { // Ignore private, stopped or stopping cranes. if !crane.Public() || crane.Stopped() || crane.IsStopping() { continue } // Get measurements. measurements := crane.ConnectedHub.GetMeasurements() latency, _ := measurements.GetLatency() capacity, _ := measurements.GetCapacity() // Add crane lane. lanes = append(lanes, &hub.Lane{ ID: crane.ConnectedHub.ID, Latency: latency, Capacity: capacity, }) } // Sort Lanes for comparing. hub.SortLanes(lanes) // Get system load and convert to fixed steps. var load int loadAvg, ok := metrics.LoadAvg15() switch { case !ok: load = -1 case loadAvg >= 1: load = 100 case loadAvg >= 0.95: load = 95 case loadAvg >= 0.8: load = 80 default: load = 0 } if loadAvg >= 0.8 { log.Warningf("spn/captain: publishing 15m system load average of %.2f as %d", loadAvg, load) } // Set flags. var flags []string if !patrol.HTTPSConnectivityConfirmed() { flags = append(flags, hub.FlagNetError) } // Sort Lanes for comparing. sort.Strings(flags) // Run maintenance with the new data. changed, err := publicIdentity.MaintainStatus(lanes, &load, flags, false) if err != nil { return fmt.Errorf("failed to maintain status: %w", err) } if !changed { return nil } // Update on map. navigator.Main.UpdateHub(publicIdentity.Hub) log.Debug("spn/captain: updated own hub on map after status change") // export status statusData, err := publicIdentity.ExportStatus() if err != nil { return fmt.Errorf("failed to export status: %w", err) } // forward to other connected Hubs gossipRelayMsg("", GossipHubStatusMsg, statusData) log.Infof( "spn/captain: updated status with load %d and current lanes: %v", publicIdentity.Hub.Status.Load, publicIdentity.Hub.Status.Lanes, ) return nil } func publishShutdownStatus() { // Create offline status. offlineStatusData, err := publicIdentity.MakeOfflineStatus() if err != nil { log.Errorf("spn/captain: failed to create offline status: %s", err) return } // Forward to other connected Hubs. gossipRelayMsg("", GossipHubStatusMsg, offlineStatusData) // Leave some time for the message to broadcast. time.Sleep(2 * time.Second) log.Infof("spn/captain: broadcasted offline status") } ================================================ FILE: spn/captain/status.go ================================================ package captain import ( "fmt" "sort" "sync" "time" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/runtime" "github.com/safing/portmaster/base/utils/debug" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/navigator" ) // SPNStatus holds SPN status information. type SPNStatus struct { record.Base sync.Mutex Status SPNStatusName HomeHubID string HomeHubName string ConnectedIP string ConnectedTransport string ConnectedCountry *geoip.CountryInfo ConnectedSince *time.Time } // SPNStatusName is a SPN status. type SPNStatusName string // SPN Stati. const ( StatusFailed SPNStatusName = "failed" StatusDisabled SPNStatusName = "disabled" StatusConnecting SPNStatusName = "connecting" StatusConnected SPNStatusName = "connected" ) var ( spnStatus = &SPNStatus{ Status: StatusDisabled, } spnStatusPushFunc runtime.PushFunc ) func registerSPNStatusProvider() (err error) { spnStatus.SetKey("runtime:spn/status") spnStatus.UpdateMeta() spnStatusPushFunc, err = runtime.Register("spn/status", runtime.ProvideRecord(spnStatus)) return } func resetSPNStatus(statusName SPNStatusName, overrideEvenIfConnected bool) { // Lock for updating values. spnStatus.Lock() defer spnStatus.Unlock() // Ignore when connected and not overriding if !overrideEvenIfConnected && spnStatus.Status == StatusConnected { return } // Reset status. spnStatus.Status = statusName spnStatus.HomeHubID = "" spnStatus.HomeHubName = "" spnStatus.ConnectedIP = "" spnStatus.ConnectedTransport = "" spnStatus.ConnectedCountry = nil spnStatus.ConnectedSince = nil // Push new status. pushSPNStatusUpdate() } // pushSPNStatusUpdate pushes an update of spnStatus, which must be locked. func pushSPNStatusUpdate() { spnStatus.UpdateMeta() spnStatusPushFunc(spnStatus) } // GetSPNStatus returns the current SPN status. func GetSPNStatus() *SPNStatus { spnStatus.Lock() defer spnStatus.Unlock() return &SPNStatus{ Status: spnStatus.Status, HomeHubID: spnStatus.HomeHubID, HomeHubName: spnStatus.HomeHubName, ConnectedIP: spnStatus.ConnectedIP, ConnectedTransport: spnStatus.ConnectedTransport, ConnectedCountry: spnStatus.ConnectedCountry, ConnectedSince: spnStatus.ConnectedSince, } } // AddToDebugInfo adds the SPN status to the given debug.Info. func AddToDebugInfo(di *debug.Info) { spnStatus.Lock() defer spnStatus.Unlock() // Check if SPN module is enabled. var moduleStatus string spnEnabled := config.GetAsBool(CfgOptionEnableSPNKey, false) if spnEnabled() { moduleStatus = "enabled" } else { moduleStatus = "disabled" } // Collect status data. lines := make([]string, 0, 20) lines = append(lines, fmt.Sprintf("HomeHubID: %v", spnStatus.HomeHubID)) lines = append(lines, fmt.Sprintf("HomeHubName: %v", spnStatus.HomeHubName)) lines = append(lines, fmt.Sprintf("HomeHubIP: %v", spnStatus.ConnectedIP)) lines = append(lines, fmt.Sprintf("Transport: %v", spnStatus.ConnectedTransport)) if spnStatus.ConnectedSince != nil { lines = append(lines, fmt.Sprintf("Connected: %v ago", time.Since(*spnStatus.ConnectedSince).Round(time.Minute))) } lines = append(lines, "---") lines = append(lines, fmt.Sprintf("Client: %v", conf.Client())) lines = append(lines, fmt.Sprintf("PublicHub: %v", conf.PublicHub())) lines = append(lines, fmt.Sprintf("HubHasIPv4: %v", conf.HubHasIPv4())) lines = append(lines, fmt.Sprintf("HubHasIPv6: %v", conf.HubHasIPv6())) // Collect status data of map. if navigator.Main != nil { lines = append(lines, "---") mainMapStats := navigator.Main.Stats() lines = append(lines, fmt.Sprintf("Map %s:", navigator.Main.Name)) lines = append(lines, fmt.Sprintf("Active Terminals: %d Hubs", mainMapStats.ActiveTerminals)) // Collect hub states. mapStateSummary := make([]string, 0, len(mainMapStats.States)) for state, cnt := range mainMapStats.States { if cnt > 0 { mapStateSummary = append(mapStateSummary, fmt.Sprintf("State %s: %d Hubs", state, cnt)) } } sort.Strings(mapStateSummary) lines = append(lines, mapStateSummary...) } // Add all data as section. di.AddSection( fmt.Sprintf("SPN: %s (module %s)", spnStatus.Status, moduleStatus), debug.UseCodeSection|debug.AddContentLineBreaks, lines..., ) } ================================================ FILE: spn/conf/map.go ================================================ package conf import ( "flag" "github.com/safing/portmaster/spn/hub" ) // Primary Map Configuration. var ( MainMapName = "main" MainMapScope = hub.ScopePublic ) func init() { flag.StringVar(&MainMapName, "spn-map", "main", "set main SPN map - use only for testing") } ================================================ FILE: spn/conf/mode.go ================================================ package conf import ( "sync/atomic" ) var ( publicHub atomic.Bool client atomic.Bool integrated atomic.Bool ) // PublicHub returns whether this is a public Hub. func PublicHub() bool { return publicHub.Load() } // EnablePublicHub enables the public hub mode. func EnablePublicHub(enable bool) { publicHub.Store(enable) } // Client returns whether this is a client. func Client() bool { return client.Load() } // EnableClient enables the client mode. func EnableClient(enable bool) { client.Store(enable) } // Integrated returns whether SPN is running integrated into Portmaster. func Integrated() bool { return integrated.Load() } // EnableIntegration enables the integrated mode. func EnableIntegration(enable bool) { integrated.Store(enable) } ================================================ FILE: spn/conf/networks.go ================================================ package conf import ( "net" "sync" "github.com/tevino/abool" ) var ( hubHasV4 = abool.New() hubHasV6 = abool.New() ) // SetHubNetworks sets the available IP networks on the Hub. func SetHubNetworks(v4, v6 bool) { hubHasV4.SetTo(v4) hubHasV6.SetTo(v6) } // HubHasIPv4 returns whether the Hub has IPv4 support. func HubHasIPv4() bool { return hubHasV4.IsSet() } // HubHasIPv6 returns whether the Hub has IPv6 support. func HubHasIPv6() bool { return hubHasV6.IsSet() } var ( bindIPv4 net.IP bindIPv6 net.IP bindIPLock sync.Mutex ) // SetBindAddr sets the preferred connect (bind) addresses. func SetBindAddr(ip4, ip6 net.IP) { bindIPLock.Lock() defer bindIPLock.Unlock() bindIPv4 = ip4 bindIPv6 = ip6 } // BindAddrIsSet returns whether any bind address is set. func BindAddrIsSet() bool { bindIPLock.Lock() defer bindIPLock.Unlock() return bindIPv4 != nil || bindIPv6 != nil } // GetBindAddr returns an address with the preferred binding address for the // given dial network. // The dial network must have a suffix specifying the IP version. func GetBindAddr(dialNetwork string) net.Addr { bindIPLock.Lock() defer bindIPLock.Unlock() switch dialNetwork { case "ip4": if bindIPv4 != nil { return &net.IPAddr{IP: bindIPv4} } case "ip6": if bindIPv6 != nil { return &net.IPAddr{IP: bindIPv6} } case "tcp4": if bindIPv4 != nil { return &net.TCPAddr{IP: bindIPv4} } case "tcp6": if bindIPv6 != nil { return &net.TCPAddr{IP: bindIPv6} } case "udp4": if bindIPv4 != nil { return &net.UDPAddr{IP: bindIPv4} } case "udp6": if bindIPv6 != nil { return &net.UDPAddr{IP: bindIPv6} } } return nil } // GetBindIPs returns the preferred binding IPs. // Returns a slice with a single nil IP if no preferred binding IPs are set. func GetBindIPs() []net.IP { bindIPLock.Lock() defer bindIPLock.Unlock() switch { case bindIPv4 == nil && bindIPv6 == nil: // Match most common case first. return []net.IP{nil} case bindIPv4 != nil && bindIPv6 != nil: return []net.IP{bindIPv4, bindIPv6} case bindIPv4 != nil: return []net.IP{bindIPv4} case bindIPv6 != nil: return []net.IP{bindIPv6} } return []net.IP{nil} } ================================================ FILE: spn/conf/version.go ================================================ package conf const ( // VersionOne is the first protocol version. VersionOne = 1 // CurrentVersion always holds the newest version in production. CurrentVersion = 1 ) ================================================ FILE: spn/crew/connect.go ================================================ package crew import ( "context" "errors" "fmt" "net" "strings" "sync" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/profile/endpoints" "github.com/safing/portmaster/spn/access" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/navigator" "github.com/safing/portmaster/spn/terminal" ) // connectLock locks all routing operations to mitigate racy stuff for now. // TODO: Find a nice way to parallelize route creation. var connectLock sync.Mutex // HandleSluiceRequest handles a sluice request to build a tunnel. func HandleSluiceRequest(connInfo *network.Connection, conn net.Conn) { if conn == nil { log.Debugf("spn/crew: closing tunnel for %s before starting because of shutdown", connInfo) // This is called within the connInfo lock. connInfo.Failed("tunnel entry closed", "") connInfo.SaveWhenFinished() return } t := &Tunnel{ connInfo: connInfo, conn: conn, } module.mgr.Go("tunnel handler", t.connectWorker) } // Tunnel represents the local information and endpoint of a data tunnel. type Tunnel struct { connInfo *network.Connection conn net.Conn dstPin *navigator.Pin dstTerminal terminal.Terminal route *navigator.Route failedTries int stickied bool } func (t *Tunnel) connectWorker(wc *mgr.WorkerCtx) (err error) { // Get tracing logger. ctx, tracer := log.AddTracer(wc.Ctx()) defer tracer.Submit() // Save start time. started := time.Now() // Check the status of the Home Hub. home, homeTerminal := navigator.Main.GetHome() if home == nil || homeTerminal == nil || homeTerminal.IsBeingAbandoned() { reportConnectError(terminal.ErrUnknownError.With("home terminal is abandoned")) t.connInfo.Lock() defer t.connInfo.Unlock() t.connInfo.Failed("SPN not ready for tunneling", "") t.connInfo.Save() tracer.Infof("spn/crew: not tunneling %s, as the SPN is not ready", t.connInfo) return nil } // Create path through the SPN. err = t.establish(ctx) if err != nil { log.Warningf("spn/crew: failed to establish route for %s: %s", t.connInfo, err) // TODO: Clean this up. t.connInfo.Lock() defer t.connInfo.Unlock() t.connInfo.Failed("SPN failed to establish route: "+err.Error(), "") t.connInfo.Save() tracer.Warningf("spn/crew: failed to establish route for %s: %s", t.connInfo, err) return nil } // Connect via established tunnel. _, tErr := NewConnectOp(t) if tErr != nil { tErr = tErr.Wrap("failed to initialize tunnel") reportConnectError(tErr) t.connInfo.Lock() defer t.connInfo.Unlock() t.connInfo.Failed("SPN failed to initialize data tunnel (connect op): "+tErr.Error(), "") t.connInfo.Save() // TODO: try with another route? tracer.Warningf("spn/crew: failed to initialize data tunnel (connect op) for %s: %s", t.connInfo, err) return tErr } // Report time taken to find, build and check route and send connect request. connectOpTTCRDurationHistogram.UpdateDuration(started) t.connInfo.Lock() defer t.connInfo.Unlock() addTunnelContextToConnection(t) t.connInfo.Save() tracer.Infof("spn/crew: connected %s via %s", t.connInfo, t.dstPin.Hub) return nil } func (t *Tunnel) establish(ctx context.Context) (err error) { var routes *navigator.Routes // Check if the destination sticks to a Hub. sticksTo := getStickiedHub(t.connInfo) switch { case sticksTo == nil: // Continue. case sticksTo.Avoid: log.Tracer(ctx).Tracef("spn/crew: avoiding %s", sticksTo.Pin.Hub) // Avoid this Hub. // TODO: Remember more than one hub to avoid. avoidPolicy := []endpoints.Endpoint{ &endpoints.EndpointDomain{ OriginalValue: sticksTo.Pin.Hub.ID, Domain: strings.ToLower(sticksTo.Pin.Hub.ID) + ".", }, } // Append to policies. t.connInfo.TunnelOpts.Destination.HubPolicies = append(t.connInfo.TunnelOpts.Destination.HubPolicies, avoidPolicy) default: log.Tracer(ctx).Tracef("spn/crew: using stickied %s", sticksTo.Pin.Hub) // Check if the stickied Hub has an active terminal. dstTerminal := sticksTo.Pin.GetActiveTerminal() if dstTerminal != nil { t.dstPin = sticksTo.Pin t.dstTerminal = dstTerminal t.route = sticksTo.Route t.stickied = true return nil } // If not, attempt to find a route to the stickied hub. routes, err = navigator.Main.FindRouteToHub( sticksTo.Pin.Hub.ID, t.connInfo.TunnelOpts, ) if err != nil { log.Tracer(ctx).Tracef("spn/crew: failed to find route to stickied %s: %s", sticksTo.Pin.Hub, err) routes = nil } else { t.stickied = true } } // Find possible routes to destination. if routes == nil { log.Tracer(ctx).Trace("spn/crew: finding routes...") routes, err = navigator.Main.FindRoutes( t.connInfo.Entity.IP, t.connInfo.TunnelOpts, ) if err != nil { return fmt.Errorf("failed to find routes to %s: %w", t.connInfo.Entity.IP, err) } } // Check if routes are okay (again). if len(routes.All) == 0 { return fmt.Errorf("no routes to %s", t.connInfo.Entity.IP) } // Try routes until one succeeds. log.Tracer(ctx).Trace("spn/crew: establishing route...") var dstPin *navigator.Pin var dstTerminal terminal.Terminal for tries, route := range routes.All { dstPin, dstTerminal, err = establishRoute(route) if err != nil { continue } // Assign route data to tunnel. t.dstPin = dstPin t.dstTerminal = dstTerminal t.route = route t.failedTries = tries // Push changes to Pins and return. navigator.Main.PushPinChanges() return nil } return fmt.Errorf("failed to establish a route to %s: %w", t.connInfo.Entity.IP, err) } type hopCheck struct { pin *navigator.Pin route *navigator.Route expansion *docks.ExpansionTerminal authOp *access.AuthorizeOp pingOp *PingOp } func establishRoute(route *navigator.Route) (dstPin *navigator.Pin, dstTerminal terminal.Terminal, err error) { connectLock.Lock() defer connectLock.Unlock() // Check for path length. if len(route.Path) < 1 { return nil, nil, errors.New("path too short") } // Check for failing hubs in path. for _, hop := range route.Path[1:] { if hop.Pin().GetState().Has(navigator.StateFailing) { return nil, nil, fmt.Errorf("failing hub in path: %s", hop.Pin().Hub.Name()) } } // Get home hub. previousHop, homeTerminal := navigator.Main.GetHome() if previousHop == nil || homeTerminal == nil { return nil, nil, navigator.ErrHomeHubUnset } // Convert to interface for later use. var previousTerminal terminal.Terminal = homeTerminal // Check if first hub in path is the home hub. if route.Path[0].HubID != previousHop.Hub.ID { return nil, nil, errors.New("path start does not match home hub") } // Check if path only exists of home hub. if len(route.Path) == 1 { return previousHop, previousTerminal, nil } // TODO: Check what needs locking. // Build path and save created paths. hopChecks := make([]*hopCheck, 0, len(route.Path)-1) for i, hop := range route.Path[1:] { // Check if we already have a connection to the Hub. activeTerminal := hop.Pin().GetActiveTerminal() if activeTerminal != nil { // Ping terminal if not recently checked. if activeTerminal.NeedsReachableCheck(1 * time.Minute) { pingOp, tErr := NewPingOp(activeTerminal) if tErr.IsError() { return nil, nil, tErr.Wrap("failed start ping to %s", hop.Pin()) } // Add for checking results later. hopChecks = append(hopChecks, &hopCheck{ pin: hop.Pin(), route: route.CopyUpTo(i + 2), expansion: activeTerminal, pingOp: pingOp, }) } previousHop = hop.Pin() previousTerminal = activeTerminal continue } // Expand to next Hub. expansion, authOp, tErr := expand(previousTerminal, previousHop, hop.Pin()) if tErr != nil { return nil, nil, tErr.Wrap("failed to expand to %s", hop.Pin()) } // Add for checking results later. hopChecks = append(hopChecks, &hopCheck{ pin: hop.Pin(), route: route.CopyUpTo(i + 2), expansion: expansion, authOp: authOp, }) // Save previous pin for next loop or end. previousHop = hop.Pin() previousTerminal = expansion } // Check results. for _, check := range hopChecks { switch { case check.authOp != nil: // Wait for authOp result. select { case tErr := <-check.authOp.Result: switch { case tErr.IsError(): // There was a network or authentication error. check.pin.MarkAsFailingFor(3 * time.Minute) log.Warningf("spn/crew: failed to auth to %s: %s", check.pin.Hub, tErr) return nil, nil, tErr.Wrap("failed to authenticate to %s: %w", check.pin.Hub, tErr) case tErr.Is(terminal.ErrExplicitAck): // Authentication was successful. default: // Authentication was aborted. if tErr != nil { tErr = terminal.ErrUnknownError } log.Warningf("spn/crew: auth to %s aborted with %s", check.pin.Hub, tErr) return nil, nil, tErr.Wrap("authentication to %s aborted: %w", check.pin.Hub, tErr) } case <-time.After(5 * time.Second): // Mark as failing for just a minute, until server load may be less. check.pin.MarkAsFailingFor(1 * time.Minute) log.Warningf("spn/crew: auth to %s timed out", check.pin.Hub) return nil, nil, terminal.ErrTimeout.With("waiting for auth to %s", check.pin.Hub) } // Add terminal extension to the map. check.pin.SetActiveTerminal(&navigator.PinConnection{ Terminal: check.expansion, Route: check.route, }) check.expansion.MarkReachable() log.Infof("spn/crew: added conn to %s via %s", check.pin, check.route) case check.pingOp != nil: // Wait for ping result. select { case tErr := <-check.pingOp.Result: if !tErr.Is(terminal.ErrExplicitAck) { // Mark as failing long enough to expire connections and session and shutdown connections. // TODO: Should we forcibly disconnect instead? // TODO: This might also be triggered if a relay fails and ends the operation. check.pin.MarkAsFailingFor(7 * time.Minute) // Forget about existing active terminal, re-create if needed. check.pin.SetActiveTerminal(nil) log.Warningf("spn/crew: failed to check reachability of %s: %s", check.pin.Hub, tErr) return nil, nil, tErr.Wrap("failed to check reachability of %s: %w", check.pin.Hub, tErr) } case <-time.After(5 * time.Second): // Mark as failing for just a minute, until server load may be less. check.pin.MarkAsFailingFor(1 * time.Minute) // Forget about existing active terminal, re-create if needed. check.pin.SetActiveTerminal(nil) log.Warningf("spn/crew: reachability check to %s timed out", check.pin.Hub) return nil, nil, terminal.ErrTimeout.With("waiting for ping to %s", check.pin.Hub) } check.expansion.MarkReachable() log.Debugf("spn/crew: checked conn to %s via %s", check.pin.Hub, check.route) default: log.Errorf("spn/crew: invalid hop check for %s", check.pin.Hub) return nil, nil, terminal.ErrInternalError.With("invalid hop check") } } // Return last hop. return previousHop, previousTerminal, nil } func expand(fromTerminal terminal.Terminal, from, to *navigator.Pin) (expansion *docks.ExpansionTerminal, authOp *access.AuthorizeOp, tErr *terminal.Error) { expansion, tErr = docks.ExpandTo(fromTerminal, to.Hub.ID, to.Hub) if tErr != nil { return nil, nil, tErr.Wrap("failed to expand to %s", to.Hub) } authOp, tErr = access.AuthorizeToTerminal(expansion) if tErr != nil { expansion.Abandon(nil) return nil, nil, tErr.Wrap("failed to authorize") } log.Infof("spn/crew: expanded to %s (from %s)", to.Hub, from.Hub) return expansion, authOp, nil } // TunnelContext holds additional information about the tunnel to be added to a // connection. type TunnelContext struct { Path []*TunnelContextHop PathCost float32 RoutingAlg string tunnel *Tunnel } // GetExitNodeID returns the ID of the exit node. // It returns an empty string in case no path exists. func (tc *TunnelContext) GetExitNodeID() string { if len(tc.Path) == 0 { return "" } return tc.Path[len(tc.Path)-1].ID } // StopTunnel stops the tunnel. func (tc *TunnelContext) StopTunnel() error { if tc.tunnel != nil && tc.tunnel.conn != nil { return tc.tunnel.conn.Close() } return nil } // TunnelContextHop holds hop data for TunnelContext. type TunnelContextHop struct { ID string Name string IPv4 *TunnelContextHopIPInfo `json:",omitempty"` IPv6 *TunnelContextHopIPInfo `json:",omitempty"` } // TunnelContextHopIPInfo holds hop IP data for TunnelContextHop. type TunnelContextHopIPInfo struct { IP net.IP Country string ASN uint ASOwner string } func addTunnelContextToConnection(t *Tunnel) { // Create and add basic info. tunnelCtx := &TunnelContext{ Path: make([]*TunnelContextHop, len(t.route.Path)), PathCost: t.route.TotalCost, RoutingAlg: t.route.Algorithm, tunnel: t, } t.connInfo.TunnelContext = tunnelCtx // Add path info. for i, hop := range t.route.Path { // Add hub info. hopCtx := &TunnelContextHop{ ID: hop.HubID, Name: hop.Pin().Hub.Info.Name, } tunnelCtx.Path[i] = hopCtx // Add hub IPv4 info. if hop.Pin().Hub.Info.IPv4 != nil { hopCtx.IPv4 = &TunnelContextHopIPInfo{ IP: hop.Pin().Hub.Info.IPv4, } if hop.Pin().LocationV4 != nil { hopCtx.IPv4.Country = hop.Pin().LocationV4.Country.Code hopCtx.IPv4.ASN = hop.Pin().LocationV4.AutonomousSystemNumber hopCtx.IPv4.ASOwner = hop.Pin().LocationV4.AutonomousSystemOrganization } } // Add hub IPv6 info. if hop.Pin().Hub.Info.IPv6 != nil { hopCtx.IPv6 = &TunnelContextHopIPInfo{ IP: hop.Pin().Hub.Info.IPv6, } if hop.Pin().LocationV6 != nil { hopCtx.IPv6.Country = hop.Pin().LocationV6.Country.Code hopCtx.IPv6.ASN = hop.Pin().LocationV6.AutonomousSystemNumber hopCtx.IPv6.ASOwner = hop.Pin().LocationV6.AutonomousSystemOrganization } } } } ================================================ FILE: spn/crew/metrics.go ================================================ package crew import ( "sync/atomic" "github.com/tevino/abool" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/metrics" ) var ( connectOpCnt *metrics.Counter connectOpCntError *metrics.Counter connectOpCntBadRequest *metrics.Counter connectOpCntCanceled *metrics.Counter connectOpCntFailed *metrics.Counter connectOpCntConnected *metrics.Counter connectOpCntRateLimited *metrics.Counter connectOpIncomingBytes *metrics.Counter connectOpOutgoingBytes *metrics.Counter connectOpTTCRDurationHistogram *metrics.Histogram connectOpTTFBDurationHistogram *metrics.Histogram connectOpDurationHistogram *metrics.Histogram connectOpIncomingDataHistogram *metrics.Histogram connectOpOutgoingDataHistogram *metrics.Histogram metricsRegistered = abool.New() ) func registerMetrics() (err error) { // Only register metrics once. if !metricsRegistered.SetToIf(false, true) { return nil } // Connect Op Stats on client. connectOpCnt, err = metrics.NewCounter( "spn/op/connect/total", nil, &metrics.Options{ Name: "SPN Total Connect Operations", InternalID: "spn_connect_count", Permission: api.PermitUser, Persist: true, }, ) if err != nil { return err } // Connect Op Stats on server. connectOpCntOptions := &metrics.Options{ Name: "SPN Total Connect Operations", Permission: api.PermitUser, Persist: true, } connectOpCntError, err = metrics.NewCounter( "spn/op/connect/total", map[string]string{"result": "error"}, connectOpCntOptions, ) if err != nil { return err } connectOpCntBadRequest, err = metrics.NewCounter( "spn/op/connect/total", map[string]string{"result": "bad_request"}, connectOpCntOptions, ) if err != nil { return err } connectOpCntCanceled, err = metrics.NewCounter( "spn/op/connect/total", map[string]string{"result": "canceled"}, connectOpCntOptions, ) if err != nil { return err } connectOpCntFailed, err = metrics.NewCounter( "spn/op/connect/total", map[string]string{"result": "failed"}, connectOpCntOptions, ) if err != nil { return err } connectOpCntConnected, err = metrics.NewCounter( "spn/op/connect/total", map[string]string{"result": "connected"}, connectOpCntOptions, ) if err != nil { return err } connectOpCntRateLimited, err = metrics.NewCounter( "spn/op/connect/total", map[string]string{"result": "rate_limited"}, connectOpCntOptions, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/op/connect/active", nil, getActiveConnectOpsStat, &metrics.Options{ Name: "SPN Active Connect Operations", Permission: api.PermitUser, }, ) if err != nil { return err } connectOpIncomingBytes, err = metrics.NewCounter( "spn/op/connect/incoming/bytes", nil, &metrics.Options{ Name: "SPN Connect Operation Incoming Bytes", InternalID: "spn_connect_in_bytes", Permission: api.PermitUser, Persist: true, }, ) if err != nil { return err } connectOpOutgoingBytes, err = metrics.NewCounter( "spn/op/connect/outgoing/bytes", nil, &metrics.Options{ Name: "SPN Connect Operation Outgoing Bytes", InternalID: "spn_connect_out_bytes", Permission: api.PermitUser, Persist: true, }, ) if err != nil { return err } connectOpTTCRDurationHistogram, err = metrics.NewHistogram( "spn/op/connect/histogram/ttcr/seconds", nil, &metrics.Options{ Name: "SPN Connect Operation time-to-connect-request Histogram", Permission: api.PermitUser, }, ) if err != nil { return err } connectOpTTFBDurationHistogram, err = metrics.NewHistogram( "spn/op/connect/histogram/ttfb/seconds", nil, &metrics.Options{ Name: "SPN Connect Operation time-to-first-byte (from TTCR) Histogram", Permission: api.PermitUser, }, ) if err != nil { return err } connectOpDurationHistogram, err = metrics.NewHistogram( "spn/op/connect/histogram/duration/seconds", nil, &metrics.Options{ Name: "SPN Connect Operation Duration Histogram", Permission: api.PermitUser, }, ) if err != nil { return err } connectOpIncomingDataHistogram, err = metrics.NewHistogram( "spn/op/connect/histogram/incoming/bytes", nil, &metrics.Options{ Name: "SPN Connect Operation Downloaded Data Histogram", Permission: api.PermitUser, }, ) if err != nil { return err } connectOpOutgoingDataHistogram, err = metrics.NewHistogram( "spn/op/connect/histogram/outgoing/bytes", nil, &metrics.Options{ Name: "SPN Connect Operation Outgoing Data Histogram", Permission: api.PermitUser, }, ) if err != nil { return err } return nil } func getActiveConnectOpsStat() float64 { return float64(atomic.LoadInt64(activeConnectOps)) } ================================================ FILE: spn/crew/module.go ================================================ package crew import ( "errors" "sync/atomic" "time" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/terminal" ) type Crew struct { mgr *mgr.Manager instance instance } func (c *Crew) Manager() *mgr.Manager { return c.mgr } func (c *Crew) Start() error { return start() } func (c *Crew) Stop() error { return stop() } func start() error { _ = module.mgr.Repeat("sticky cleaner", 10*time.Minute, cleanStickyHubs) return registerMetrics() } func stop() error { clearStickyHubs() terminal.StopScheduler() return nil } var connectErrors = make(chan *terminal.Error, 10) func reportConnectError(tErr *terminal.Error) { select { case connectErrors <- tErr: default: } } // ConnectErrors returns errors of connect operations. // It only has a small and shared buffer and may only be used for indications, // not for full monitoring. func ConnectErrors() <-chan *terminal.Error { return connectErrors } var ( module *Crew shimLoaded atomic.Bool ) // New returns a new Config module. func New(instance instance) (*Crew, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Crew") module = &Crew{ mgr: m, instance: instance, } return module, nil } type instance interface{} ================================================ FILE: spn/crew/module_test.go ================================================ package crew import ( "fmt" "os" "testing" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" "github.com/safing/portmaster/base/metrics" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/core/base" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/terminal" ) type testInstance struct { db *dbmodule.DBModule config *config.Config metrics *metrics.Metrics rng *rng.Rng base *base.Base terminal *terminal.TerminalModule cabin *cabin.Cabin } func (stub *testInstance) Config() *config.Config { return stub.config } func (stub *testInstance) Metrics() *metrics.Metrics { return stub.metrics } func (stub *testInstance) SPNGroup() *mgr.ExtendedGroup { return nil } func (stub *testInstance) Stopping() bool { return false } func (stub *testInstance) SetCmdLineOperation(f func() error) {} func (stub *testInstance) DataDir() string { return _dataDir } var _dataDir string func runTest(m *testing.M) error { conf.EnablePublicHub(true) // Make hub config available. var err error // Create a temporary directory for the data _dataDir, err = os.MkdirTemp("", "") if err != nil { return fmt.Errorf("failed to initialize dataroot: %w", err) } defer func() { _ = os.RemoveAll(_dataDir) }() instance := &testInstance{} // Init instance.db, err = dbmodule.New(instance) if err != nil { return fmt.Errorf("failed to create database module: %w", err) } instance.config, err = config.New(instance) if err != nil { return fmt.Errorf("failed to create config module: %w", err) } instance.metrics, err = metrics.New(instance) if err != nil { return fmt.Errorf("failed to create metrics module: %w", err) } instance.rng, err = rng.New(instance) if err != nil { return fmt.Errorf("failed to create rng module: %w", err) } instance.base, err = base.New(instance) if err != nil { return fmt.Errorf("failed to create base module: %w", err) } instance.terminal, err = terminal.New(instance) if err != nil { return fmt.Errorf("failed to create terminal module: %w", err) } instance.cabin, err = cabin.New(instance) if err != nil { return fmt.Errorf("failed to create cabin module: %w", err) } module, err = New(instance) if err != nil { return fmt.Errorf("failed to create crew module: %w", err) } // Start err = instance.db.Start() if err != nil { return fmt.Errorf("failed to start db module: %w", err) } err = instance.config.Start() if err != nil { return fmt.Errorf("failed to start config module: %w", err) } err = instance.metrics.Start() if err != nil { return fmt.Errorf("failed to start metrics module: %w", err) } err = instance.rng.Start() if err != nil { return fmt.Errorf("failed to start rng module: %w", err) } err = instance.base.Start() if err != nil { return fmt.Errorf("failed to start base module: %w", err) } err = instance.terminal.Start() if err != nil { return fmt.Errorf("failed to start terminal module: %w", err) } err = instance.cabin.Start() if err != nil { return fmt.Errorf("failed to start cabin module: %w", err) } err = module.Start() if err != nil { return fmt.Errorf("failed to start crew module: %w", err) } conf.EnablePublicHub(true) m.Run() return nil } func TestMain(m *testing.M) { if err := runTest(m); err != nil { fmt.Printf("%s", err) os.Exit(1) } } ================================================ FILE: spn/crew/op_connect.go ================================================ package crew import ( "context" "errors" "fmt" "io" "net" "strconv" "sync/atomic" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" "github.com/safing/structures/dsd" ) // ConnectOpType is the type ID for the connection operation. const ConnectOpType string = "connect" var activeConnectOps = new(int64) // ConnectOp is used to connect data tunnels to servers on the Internet. type ConnectOp struct { terminal.OperationBase // Flow Control dfq *terminal.DuplexFlowQueue // Context and shutdown handling // ctx is the context of the Terminal. ctx context.Context // cancelCtx cancels ctx. cancelCtx context.CancelFunc // doneWriting signals that the writer has finished writing. doneWriting chan struct{} // Metrics incomingTraffic atomic.Uint64 outgoingTraffic atomic.Uint64 started time.Time // Connection t terminal.Terminal conn net.Conn request *ConnectRequest entry bool tunnel *Tunnel } // Type returns the type ID. func (op *ConnectOp) Type() string { return ConnectOpType } // Ctx returns the operation context. func (op *ConnectOp) Ctx() context.Context { return op.ctx } // ConnectRequest holds all the information necessary for a connect operation. type ConnectRequest struct { Domain string `json:"d,omitempty"` IP net.IP `json:"ip,omitempty"` UsePriorityDataMsgs bool `json:"pr,omitempty"` Protocol packet.IPProtocol `json:"p,omitempty"` Port uint16 `json:"po,omitempty"` QueueSize uint32 `json:"qs,omitempty"` } // DialNetwork returns the address of the connect request. func (r *ConnectRequest) DialNetwork() string { if ip4 := r.IP.To4(); ip4 != nil { switch r.Protocol { //nolint:exhaustive // Only looking for supported protocols. case packet.TCP: return "tcp4" case packet.UDP: return "udp4" } } else { switch r.Protocol { //nolint:exhaustive // Only looking for supported protocols. case packet.TCP: return "tcp6" case packet.UDP: return "udp6" } } return "" } // Address returns the address of the connext request. func (r *ConnectRequest) Address() string { return net.JoinHostPort(r.IP.String(), strconv.Itoa(int(r.Port))) } func (r *ConnectRequest) String() string { if r.Domain != "" { return fmt.Sprintf("%s (%s %s)", r.Domain, r.Protocol, r.Address()) } return fmt.Sprintf("%s %s", r.Protocol, r.Address()) } func init() { terminal.RegisterOpType(terminal.OperationFactory{ Type: ConnectOpType, Requires: terminal.MayConnect, Start: startConnectOp, }) } // NewConnectOp starts a new connect operation. func NewConnectOp(tunnel *Tunnel) (*ConnectOp, *terminal.Error) { // Submit metrics. connectOpCnt.Inc() // Create request. request := &ConnectRequest{ Domain: tunnel.connInfo.Entity.Domain, IP: tunnel.connInfo.Entity.IP, Protocol: packet.IPProtocol(tunnel.connInfo.Entity.Protocol), Port: tunnel.connInfo.Entity.Port, UsePriorityDataMsgs: terminal.UsePriorityDataMsgs, } // Set defaults. if request.QueueSize == 0 { request.QueueSize = terminal.DefaultQueueSize } // Create new op. op := &ConnectOp{ doneWriting: make(chan struct{}), t: tunnel.dstTerminal, conn: tunnel.conn, request: request, entry: true, tunnel: tunnel, } op.ctx, op.cancelCtx = context.WithCancel(module.mgr.Ctx()) op.dfq = terminal.NewDuplexFlowQueue(op.Ctx(), request.QueueSize, op.submitUpstream) // Prepare init msg. data, err := dsd.Dump(request, dsd.CBOR) if err != nil { return nil, terminal.ErrInternalError.With("failed to pack connect request: %w", err) } // Initialize. tErr := op.t.StartOperation(op, container.New(data), 5*time.Second) if tErr != nil { return nil, tErr } // Setup metrics. op.started = time.Now() module.mgr.Go("connect op conn reader", op.connReader) module.mgr.Go("connect op conn writer", op.connWriter) module.mgr.Go("connect op flow handler", op.dfq.FlowHandler) log.Infof("spn/crew: connected to %s via %s", request, tunnel.dstPin.Hub) return op, nil } func startConnectOp(t terminal.Terminal, opID uint32, data *container.Container) (terminal.Operation, *terminal.Error) { // Check if we are running a public hub. if !conf.PublicHub() { return nil, terminal.ErrPermissionDenied.With("connecting is only allowed on public hubs") } // Parse connect request. request := &ConnectRequest{} _, err := dsd.Load(data.CompileData(), request) if err != nil { connectOpCntError.Inc() // More like a protocol/system error than a bad request. return nil, terminal.ErrMalformedData.With("failed to parse connect request: %w", err) } if request.QueueSize == 0 || request.QueueSize > terminal.MaxQueueSize { connectOpCntError.Inc() // More like a protocol/system error than a bad request. return nil, terminal.ErrInvalidOptions.With("invalid queue size of %d", request.QueueSize) } // Check if IP seems valid. if len(request.IP) != net.IPv4len && len(request.IP) != net.IPv6len { connectOpCntError.Inc() // More like a protocol/system error than a bad request. return nil, terminal.ErrInvalidOptions.With("ip address is not valid") } // Create and initialize operation. op := &ConnectOp{ doneWriting: make(chan struct{}), t: t, request: request, } op.InitOperationBase(t, opID) op.ctx, op.cancelCtx = context.WithCancel(t.Ctx()) op.dfq = terminal.NewDuplexFlowQueue(op.Ctx(), request.QueueSize, op.submitUpstream) // Start worker to complete setting up the connection. module.mgr.Go("connect op setup", op.handleSetup) return op, nil } func (op *ConnectOp) handleSetup(_ *mgr.WorkerCtx) error { // Get terminal session for rate limiting. var session *terminal.Session if sessionTerm, ok := op.t.(terminal.SessionTerminal); ok { session = sessionTerm.GetSession() } else { connectOpCntError.Inc() log.Errorf("spn/crew: %T is not a session terminal, aborting op %s#%d", op.t, op.t.FmtID(), op.ID()) op.Stop(op, terminal.ErrInternalError.With("no session available")) return nil } // Limit concurrency of connecting. cancelErr := session.LimitConcurrency(op.Ctx(), func() { op.setup(session) }) // If context was canceled, stop operation. if cancelErr != nil { connectOpCntCanceled.Inc() op.Stop(op, terminal.ErrCanceled.With(cancelErr.Error())) } // Do not return a worker error. return nil } func (op *ConnectOp) setup(session *terminal.Session) { // Rate limit before connecting. if tErr := session.RateLimit(); tErr != nil { // Add rate limit info to error. if tErr.Is(terminal.ErrRateLimited) { connectOpCntRateLimited.Inc() op.Stop(op, tErr.With(session.RateLimitInfo())) return } connectOpCntError.Inc() op.Stop(op, tErr) return } // Check if connection target is in global scope. ipScope := netutils.GetIPScope(op.request.IP) if ipScope != netutils.Global { session.ReportSuspiciousActivity(terminal.SusFactorQuiteUnusual) connectOpCntBadRequest.Inc() op.Stop(op, terminal.ErrPermissionDenied.With("denied request to connect to non-global IP %s", op.request.IP)) return } // Check exit policy. if tErr := checkExitPolicy(op.request); tErr != nil { session.ReportSuspiciousActivity(terminal.SusFactorQuiteUnusual) connectOpCntBadRequest.Inc() op.Stop(op, tErr) return } // Check one last time before connecting if operation was not canceled. if op.Ctx().Err() != nil { op.Stop(op, terminal.ErrCanceled.With(op.Ctx().Err().Error())) connectOpCntCanceled.Inc() return } // Connect to destination. dialNet := op.request.DialNetwork() if dialNet == "" { session.ReportSuspiciousActivity(terminal.SusFactorCommon) connectOpCntBadRequest.Inc() op.Stop(op, terminal.ErrIncorrectUsage.With("protocol %s is not supported", op.request.Protocol)) return } dialer := &net.Dialer{ Timeout: 10 * time.Second, LocalAddr: conf.GetBindAddr(dialNet), FallbackDelay: -1, // Disables Fast Fallback from IPv6 to IPv4. KeepAlive: -1, // Disable keep-alive. } conn, err := dialer.DialContext(op.Ctx(), dialNet, op.request.Address()) if err != nil { // Connection errors are common, but still a bit suspicious. var netError net.Error switch { case errors.As(err, &netError) && netError.Timeout(): session.ReportSuspiciousActivity(terminal.SusFactorCommon) connectOpCntFailed.Inc() case errors.Is(err, context.Canceled): session.ReportSuspiciousActivity(terminal.SusFactorCommon) connectOpCntCanceled.Inc() default: session.ReportSuspiciousActivity(terminal.SusFactorWeirdButOK) connectOpCntFailed.Inc() } op.Stop(op, terminal.ErrConnectionError.With("failed to connect to %s: %w", op.request, err)) return } op.conn = conn // Start worker. module.mgr.Go("connect op conn reader", op.connReader) module.mgr.Go("connect op conn writer", op.connWriter) module.mgr.Go("connect op flow handler", op.dfq.FlowHandler) connectOpCntConnected.Inc() log.Infof("spn/crew: connected op %s#%d to %s", op.t.FmtID(), op.ID(), op.request) } func (op *ConnectOp) submitUpstream(msg *terminal.Msg, timeout time.Duration) { err := op.Send(msg, timeout) if err != nil { msg.Finish() op.Stop(op, err.Wrap("failed to send data (op) read from %s", op.connectedType())) } } const ( readBufSize = 1500 // High priority up to first 10MB. highPrioThreshold = 10_000_000 // Rate limit to 128 Mbit/s after 1GB traffic. // Do NOT use time.Sleep per packet, as it is very inaccurate and will sleep a lot longer than desired. rateLimitThreshold = 1_000_000_000 rateLimitMaxMbit = 128 ) func (op *ConnectOp) connReader(_ *mgr.WorkerCtx) error { // Metrics setup and submitting. atomic.AddInt64(activeConnectOps, 1) defer func() { atomic.AddInt64(activeConnectOps, -1) connectOpDurationHistogram.UpdateDuration(op.started) connectOpIncomingDataHistogram.Update(float64(op.incomingTraffic.Load())) }() rateLimiter := terminal.NewRateLimiter(rateLimitMaxMbit) for { // Read from connection. buf := make([]byte, readBufSize) n, err := op.conn.Read(buf) if err != nil { if errors.Is(err, io.EOF) { op.Stop(op, terminal.ErrStopping.With("connection to %s was closed on read", op.connectedType())) } else { op.Stop(op, terminal.ErrConnectionError.With("failed to read from %s: %w", op.connectedType(), err)) } return nil } if n == 0 { log.Tracef("spn/crew: connect op %s>%d read 0 bytes from %s", op.t.FmtID(), op.ID(), op.connectedType()) continue } // Submit metrics. connectOpIncomingBytes.Add(n) inBytes := op.incomingTraffic.Add(uint64(n)) // Rate limit if over threshold. if inBytes > rateLimitThreshold { rateLimiter.Limit(uint64(n)) } // Create message from data. msg := op.NewMsg(buf[:n]) // Define priority and possibly wait for slot. switch { case inBytes > highPrioThreshold: msg.Unit.WaitForSlot() case op.request.UsePriorityDataMsgs: msg.Unit.MakeHighPriority() } // Send packet. tErr := op.dfq.Send( msg, 30*time.Second, ) if tErr != nil { msg.Finish() op.Stop(op, tErr.Wrap("failed to send data (dfq) from %s", op.connectedType())) return nil } } } // Deliver delivers a messages to the operation. func (op *ConnectOp) Deliver(msg *terminal.Msg) *terminal.Error { return op.dfq.Deliver(msg) } func (op *ConnectOp) connWriter(_ *mgr.WorkerCtx) error { // Metrics submitting. defer func() { connectOpOutgoingDataHistogram.Update(float64(op.outgoingTraffic.Load())) }() defer func() { // Signal that we are done with writing. close(op.doneWriting) // Close connection. _ = op.conn.Close() }() var msg *terminal.Msg defer msg.Finish() rateLimiter := terminal.NewRateLimiter(rateLimitMaxMbit) writing: for { msg.Finish() select { case msg = <-op.dfq.Receive(): case <-op.ctx.Done(): op.Stop(op, terminal.ErrCanceled) return nil default: // Handle all data before also listening for the context cancel. // This ensures all data is written properly before stopping. select { case msg = <-op.dfq.Receive(): case op.doneWriting <- struct{}{}: op.Stop(op, terminal.ErrStopping) return nil case <-op.ctx.Done(): op.Stop(op, terminal.ErrCanceled) return nil } } // TODO: Instead of compiling data here again, can we send it as in the container? data := msg.Data.CompileData() if len(data) == 0 { continue writing } // Submit metrics. connectOpOutgoingBytes.Add(len(data)) out := op.outgoingTraffic.Add(uint64(len(data))) // Rate limit if over threshold. if out > rateLimitThreshold { rateLimiter.Limit(uint64(len(data))) } // Special handling after first data was received on client. if op.entry && out == uint64(len(data)) { // Report time taken to receive first byte. connectOpTTFBDurationHistogram.UpdateDuration(op.started) // If not stickied yet, stick destination to Hub. if !op.tunnel.stickied { op.tunnel.stickDestinationToHub() } } // Send all given data. for { n, err := op.conn.Write(data) switch { case err != nil: if errors.Is(err, io.EOF) { op.Stop(op, terminal.ErrStopping.With("connection to %s was closed on write", op.connectedType())) } else { op.Stop(op, terminal.ErrConnectionError.With("failed to send to %s: %w", op.connectedType(), err)) } return nil case n == 0: op.Stop(op, terminal.ErrConnectionError.With("sent 0 bytes to %s", op.connectedType())) return nil case n < len(data): // If not all data was sent, try again. log.Debugf("spn/crew: %s#%d only sent %d/%d bytes to %s", op.t.FmtID(), op.ID(), n, len(data), op.connectedType()) data = data[n:] default: continue writing } } } } func (op *ConnectOp) connectedType() string { if op.entry { return "origin" } return "destination" } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. func (op *ConnectOp) HandleStop(err *terminal.Error) (errorToSend *terminal.Error) { if err.IsError() { reportConnectError(err) } // If the connection has sent or received any data so far, finish the data // flows as it makes sense. if op.incomingTraffic.Load() > 0 || op.outgoingTraffic.Load() > 0 { // If the op was ended locally, send all data before closing. // If the op was ended remotely, don't bother sending remaining data. if !err.IsExternal() { // Flushing could mean sending a full buffer of 50000 packets. op.dfq.Flush(5 * time.Minute) } // If the op was ended remotely, write all remaining received data. // If the op was ended locally, don't bother writing remaining data. if err.IsExternal() { select { case <-op.doneWriting: default: select { case <-op.doneWriting: case <-time.After(5 * time.Second): } } } } // Cancel workers. op.cancelCtx() // Special client-side handling. if op.entry { // Mark the connection as failed if there was an error and no data was sent to the app yet. if err.IsError() && op.outgoingTraffic.Load() == 0 { // Set connection to failed and save it to propagate the update. c := op.tunnel.connInfo func() { c.Lock() defer c.Unlock() if err.IsExternal() { c.Failed(fmt.Sprintf( "the exit node reported an error: %s", err, ), "") } else { c.Failed(fmt.Sprintf( "connection failed locally: %s", err, ), "") } c.Save() }() } // Avoid connecting to the destination via this Hub if: // - The error is external - ie. from the server. // - The error is a connection error. // - No data was received. // This indicates that there is some network level issue that we can // possibly work around by using another exit node. if err.IsError() && err.IsExternal() && err.Is(terminal.ErrConnectionError) && op.outgoingTraffic.Load() == 0 { op.tunnel.avoidDestinationHub() } // Don't leak local errors to the server. if !err.IsExternal() { // Change error that is reported. return terminal.ErrStopping } } return err } ================================================ FILE: spn/crew/op_connect_test.go ================================================ package crew import ( "fmt" "net" "net/http" "net/url" "testing" "time" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/navigator" "github.com/safing/portmaster/spn/terminal" ) const ( testPadding = 8 testQueueSize = 10 ) func TestConnectOp(t *testing.T) { t.Parallel() if testing.Short() { t.Skip("skipping test in short mode, as it interacts with the network") } // Create test terminal pair. a, b, err := terminal.NewSimpleTestTerminalPair(0, 0, &terminal.TerminalOpts{ FlowControl: terminal.FlowControlDFQ, FlowControlSize: testQueueSize, Padding: testPadding, }, ) if err != nil { t.Fatalf("failed to create test terminal pair: %s", err) } // Set up connect op. b.GrantPermission(terminal.MayConnect) conf.EnablePublicHub(true) identity, err := cabin.CreateIdentity(module.mgr.Ctx(), "test") if err != nil { t.Fatalf("failed to create identity: %s", err) } _, err = identity.MaintainAnnouncement(&hub.Announcement{ Transports: []string{ "tcp:17", }, Exit: []string{ "+ * */80", "- *", }, }, true) if err != nil { t.Fatalf("failed to update identity: %s", err) } EnableConnecting(identity.Hub) { appConn, sluiceConn := net.Pipe() _, tErr := NewConnectOp(&Tunnel{ connInfo: &network.Connection{ Entity: (&intel.Entity{ Protocol: 6, Port: 80, Domain: "orf.at.", IP: net.IPv4(194, 232, 104, 142), }).Init(0), }, conn: sluiceConn, dstTerminal: a, dstPin: &navigator.Pin{ Hub: identity.Hub, }, }) if tErr != nil { t.Fatalf("failed to start connect op: %s", tErr) } // Send request. requestURL, err := url.Parse("http://orf.at/") if err != nil { t.Fatalf("failed to parse request url: %s", err) } r := http.Request{ Method: http.MethodHead, URL: requestURL, } err = r.Write(appConn) if err != nil { t.Fatalf("failed to write request: %s", err) } // Recv response. data := make([]byte, 1500) n, err := appConn.Read(data) if err != nil { t.Fatalf("failed to read request: %s", err) } if n == 0 { t.Fatal("received empty reply") } t.Log("received data:") fmt.Println(string(data[:n])) time.Sleep(500 * time.Millisecond) } } ================================================ FILE: spn/crew/op_ping.go ================================================ package crew import ( "crypto/subtle" "time" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" "github.com/safing/structures/dsd" ) const ( // PingOpType is the type ID of the latency test operation. PingOpType = "ping" pingOpNonceSize = 16 pingOpTimeout = 3 * time.Second ) // PingOp is used to measure latency. type PingOp struct { terminal.OneOffOperationBase started time.Time nonce []byte } // PingOpRequest is a ping request. type PingOpRequest struct { Nonce []byte `json:"n,omitempty"` } // PingOpResponse is a ping response. type PingOpResponse struct { Nonce []byte `json:"n,omitempty"` Time time.Time `json:"t,omitempty"` } // Type returns the type ID. func (op *PingOp) Type() string { return PingOpType } func init() { terminal.RegisterOpType(terminal.OperationFactory{ Type: PingOpType, Start: startPingOp, }) } // NewPingOp runs a latency test. func NewPingOp(t terminal.Terminal) (*PingOp, *terminal.Error) { // Generate nonce. nonce, err := rng.Bytes(pingOpNonceSize) if err != nil { return nil, terminal.ErrInternalError.With("failed to generate ping nonce: %w", err) } // Create operation and init. op := &PingOp{ started: time.Now().UTC(), nonce: nonce, } op.OneOffOperationBase.Init() // Create request. pingRequest, err := dsd.Dump(&PingOpRequest{ Nonce: op.nonce, }, dsd.CBOR) if err != nil { return nil, terminal.ErrInternalError.With("failed to create ping request: %w", err) } // Send ping. tErr := t.StartOperation(op, container.New(pingRequest), pingOpTimeout) if tErr != nil { return nil, tErr } return op, nil } // Deliver delivers a message to the operation. func (op *PingOp) Deliver(msg *terminal.Msg) *terminal.Error { defer msg.Finish() // Parse response. response := &PingOpResponse{} _, err := dsd.Load(msg.Data.CompileData(), response) if err != nil { return terminal.ErrMalformedData.With("failed to parse ping response: %w", err) } // Check if the nonce matches. if subtle.ConstantTimeCompare(op.nonce, response.Nonce) != 1 { return terminal.ErrIntegrity.With("ping nonce mismatched") } return terminal.ErrExplicitAck } func startPingOp(t terminal.Terminal, opID uint32, data *container.Container) (terminal.Operation, *terminal.Error) { // Parse request. request := &PingOpRequest{} _, err := dsd.Load(data.CompileData(), request) if err != nil { return nil, terminal.ErrMalformedData.With("failed to parse ping request: %w", err) } // Create response. response, err := dsd.Dump(&PingOpResponse{ Nonce: request.Nonce, Time: time.Now().UTC(), }, dsd.CBOR) if err != nil { return nil, terminal.ErrInternalError.With("failed to create ping response: %w", err) } // Send response. msg := terminal.NewMsg(response) msg.FlowID = opID msg.Unit.MakeHighPriority() if terminal.UsePriorityDataMsgs { msg.Type = terminal.MsgTypePriorityData } tErr := t.Send(msg, pingOpTimeout) if tErr != nil { // Finish message unit on failure. msg.Finish() return nil, tErr.With("failed to send ping response") } // Operation is just one response and finished successfully. return nil, nil } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. func (op *PingOp) HandleStop(err *terminal.Error) (errorToSend *terminal.Error) { // Prevent remote from sending explicit ack, as we use it as a success signal internally. if err.Is(terminal.ErrExplicitAck) && err.IsExternal() { err = terminal.ErrStopping.AsExternal() } // Continue with usual handling of inherited base. return op.OneOffOperationBase.HandleStop(err) } ================================================ FILE: spn/crew/op_ping_test.go ================================================ package crew import ( "testing" "time" "github.com/safing/portmaster/spn/terminal" ) func TestPingOp(t *testing.T) { t.Parallel() // Create test terminal pair. a, _, err := terminal.NewSimpleTestTerminalPair(0, 0, nil) if err != nil { t.Fatalf("failed to create test terminal pair: %s", err) } // Create ping op. op, tErr := NewPingOp(a) if tErr.IsError() { t.Fatal(tErr) } // Wait for result. select { case result := <-op.Result: t.Logf("ping result: %s", result.Error()) case <-time.After(pingOpTimeout): t.Fatal("timed out") } } ================================================ FILE: spn/crew/policy.go ================================================ package crew import ( "context" "sync" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/profile/endpoints" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/terminal" ) var ( connectingHubLock sync.Mutex connectingHub *hub.Hub ) // EnableConnecting enables connecting from this Hub. func EnableConnecting(my *hub.Hub) { connectingHubLock.Lock() defer connectingHubLock.Unlock() connectingHub = my } func checkExitPolicy(request *ConnectRequest) *terminal.Error { connectingHubLock.Lock() defer connectingHubLock.Unlock() // Check if connect requests are allowed. if connectingHub == nil { return terminal.ErrPermissionDenied.With("connect requests disabled") } // Create entity. entity := (&intel.Entity{ IP: request.IP, Protocol: uint8(request.Protocol), Port: request.Port, Domain: request.Domain, }).Init(0) entity.FetchData(context.TODO()) // Check against policy. result, reason := connectingHub.GetInfo().ExitPolicy().Match(context.TODO(), entity) if result == endpoints.Denied { return terminal.ErrPermissionDenied.With("connect request for %s violates the exit policy: %s", request, reason) } return nil } ================================================ FILE: spn/crew/sticky.go ================================================ package crew import ( "fmt" "sync" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" "github.com/safing/portmaster/spn/navigator" ) const ( stickyTTL = 1 * time.Hour ) var ( stickyIPs = make(map[string]*stickyHub) stickyDomains = make(map[string]*stickyHub) stickyLock sync.Mutex ) type stickyHub struct { Pin *navigator.Pin Route *navigator.Route LastSeen time.Time Avoid bool } func (sh *stickyHub) isExpired() bool { return time.Now().Add(-stickyTTL).After(sh.LastSeen) } func makeStickyIPKey(conn *network.Connection) string { if p := conn.Process().Profile(); p != nil { return fmt.Sprintf( "%s/%s>%s", p.LocalProfile().Source, p.LocalProfile().ID, conn.Entity.IP, ) } return "?>" + string(conn.Entity.IP) } func makeStickyDomainKey(conn *network.Connection) string { if p := conn.Process().Profile(); p != nil { return fmt.Sprintf( "%s/%s>%s", p.LocalProfile().Source, p.LocalProfile().ID, conn.Entity.Domain, ) } return "?>" + conn.Entity.Domain } func getStickiedHub(conn *network.Connection) (sticksTo *stickyHub) { stickyLock.Lock() defer stickyLock.Unlock() // Check if IP is sticky. sticksTo = stickyIPs[makeStickyIPKey(conn)] // byte comparison if sticksTo != nil && !sticksTo.isExpired() { sticksTo.LastSeen = time.Now() } // If the IP did not stick and we have a domain, check if that sticks. if sticksTo == nil && conn.Entity.Domain != "" { var ok bool sticksTo, ok = stickyDomains[makeStickyDomainKey(conn)] if ok && !sticksTo.isExpired() { sticksTo.LastSeen = time.Now() } } // If nothing sticked, return now. if sticksTo == nil { return nil } // Get intel from map before locking pin to avoid simultaneous locking. mapIntel := navigator.Main.GetIntel() // Lock Pin for checking. sticksTo.Pin.Lock() defer sticksTo.Pin.Unlock() // Check if the stickied Hub supports the needed IP version. switch { case conn.IPVersion == packet.IPv4 && sticksTo.Pin.EntityV4 == nil: // Connection is IPv4, but stickied Hub has no IPv4. return nil case conn.IPVersion == packet.IPv6 && sticksTo.Pin.EntityV6 == nil: // Connection is IPv4, but stickied Hub has no IPv4. return nil } // Disregard stickied Hub if it is disregard with the current options. matcher := conn.TunnelOpts.Destination.Matcher(mapIntel) if !matcher(sticksTo.Pin) { return nil } // Return fully checked stickied Hub. return sticksTo } func (t *Tunnel) stickDestinationToHub() { stickyLock.Lock() defer stickyLock.Unlock() // Stick to IP. ipKey := makeStickyIPKey(t.connInfo) stickyIPs[ipKey] = &stickyHub{ Pin: t.dstPin, Route: t.route, LastSeen: time.Now(), } log.Infof("spn/crew: sticking %s to %s", ipKey, t.dstPin.Hub) // Stick to Domain, if present. if t.connInfo.Entity.Domain != "" { domainKey := makeStickyDomainKey(t.connInfo) stickyDomains[domainKey] = &stickyHub{ Pin: t.dstPin, Route: t.route, LastSeen: time.Now(), } log.Infof("spn/crew: sticking %s to %s", domainKey, t.dstPin.Hub) } } func (t *Tunnel) avoidDestinationHub() { stickyLock.Lock() defer stickyLock.Unlock() // Stick to Hub/IP Pair. ipKey := makeStickyIPKey(t.connInfo) stickyIPs[ipKey] = &stickyHub{ Pin: t.dstPin, LastSeen: time.Now(), Avoid: true, } log.Warningf("spn/crew: avoiding %s for %s", t.dstPin.Hub, ipKey) // TODO: Question: Should we avoid the domain as well? (stickyDomains) } func cleanStickyHubs(ctx *mgr.WorkerCtx) error { stickyLock.Lock() defer stickyLock.Unlock() for _, stickyRegistry := range []map[string]*stickyHub{stickyIPs, stickyDomains} { for key, stickedEntry := range stickyRegistry { if stickedEntry.isExpired() { delete(stickyRegistry, key) } } } return nil } func clearStickyHubs() { stickyLock.Lock() defer stickyLock.Unlock() for _, stickyRegistry := range []map[string]*stickyHub{stickyIPs, stickyDomains} { for key := range stickyRegistry { delete(stickyRegistry, key) } } } ================================================ FILE: spn/debug.go ================================================ package spn import ( "bytes" "errors" "fmt" "io" "runtime" "github.com/maruel/panicparse/v2/stack" "github.com/safing/portmaster/base/utils/debug" "github.com/safing/portmaster/service/mgr" ) // GetWorkerInfo returns the worker info of all running workers. func (i *Instance) GetWorkerInfo() (*mgr.WorkerInfo, error) { snapshot, _, err := stack.ScanSnapshot(bytes.NewReader(fullStack()), io.Discard, stack.DefaultOpts()) if err != nil && !errors.Is(err, io.EOF) { return nil, fmt.Errorf("get stack: %w", err) } infos := make([]*mgr.WorkerInfo, 0, 32) for _, m := range i.serviceGroup.Modules() { wi, _ := m.Manager().WorkerInfo(snapshot) // Does not fail when we provide a snapshot. infos = append(infos, wi) } return mgr.MergeWorkerInfo(infos...), nil } // AddWorkerInfoToDebugInfo adds the worker info of all running workers to the debug info. func (i *Instance) AddWorkerInfoToDebugInfo(di *debug.Info) { info, err := i.GetWorkerInfo() if err != nil { di.AddSection( "Worker Status Failed", debug.UseCodeSection, err.Error(), ) return } di.AddSection( fmt.Sprintf("Worker Status: %d/%d (%d?)", info.Running, len(info.Workers), info.Missing+info.Other), debug.UseCodeSection, info.Format(), ) } func fullStack() []byte { buf := make([]byte, 8096) for { n := runtime.Stack(buf, true) if n < len(buf) { return buf[:n] } buf = make([]byte, 2*len(buf)) } } ================================================ FILE: spn/docks/bandwidth_test.go ================================================ package docks import ( "testing" "time" "github.com/tevino/abool" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" "github.com/safing/structures/dsd" ) func TestEffectiveBandwidth(t *testing.T) { //nolint:paralleltest // Run alone. // Skip in CI. if testing.Short() { t.Skip() } var ( bwTestDelay = 50 * time.Millisecond bwTestQueueSize uint32 = 1000 bwTestVolume = 10000000 // 10MB bwTestTime = 10 * time.Second ) // Create test terminal pair. a, b, err := terminal.NewSimpleTestTerminalPair( bwTestDelay, int(bwTestQueueSize), &terminal.TerminalOpts{ FlowControl: terminal.FlowControlDFQ, FlowControlSize: bwTestQueueSize, }, ) if err != nil { t.Fatalf("failed to create test terminal pair: %s", err) } // Grant permission for op on remote terminal and start op. b.GrantPermission(terminal.IsCraneController) // Re-use the capacity test for the bandwidth test. op := &CapacityTestOp{ opts: &CapacityTestOptions{ TestVolume: bwTestVolume, MaxTime: bwTestTime, testing: true, }, recvQueue: make(chan *terminal.Msg), dataSent: new(int64), dataSentWasAckd: abool.New(), result: make(chan *terminal.Error, 1), } // Disable sender again. op.senderStarted = true op.dataSentWasAckd.Set() // Make capacity test request. request, err := dsd.Dump(op.opts, dsd.CBOR) if err != nil { t.Fatal(terminal.ErrInternalError.With("failed to serialize capactity test options: %w", err)) } // Send test request. tErr := a.StartOperation(op, container.New(request), 1*time.Second) if tErr != nil { t.Fatal(tErr) } // Start handler. module.mgr.Go("op capacity handler", op.handler) // Wait for result and check error. tErr = <-op.Result() if !tErr.IsOK() { t.Fatalf("op failed: %s", tErr) } t.Logf("measured capacity: %d bit/s", op.testResult) // Calculate expected bandwidth. expectedBitsPerSecond := (float64(capacityTestMsgSize*8*int64(bwTestQueueSize)) / float64(bwTestDelay)) * float64(time.Second) t.Logf("expected capacity: %f bit/s", expectedBitsPerSecond) // Check if measured bandwidth is within parameters. if float64(op.testResult) > expectedBitsPerSecond*1.6 { t.Fatal("measured capacity too high") } // TODO: Check if we can raise this to at least 90%. if float64(op.testResult) < expectedBitsPerSecond*0.2 { t.Fatal("measured capacity too low") } } ================================================ FILE: spn/docks/controller.go ================================================ package docks import ( "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" ) // CraneControllerTerminal is a terminal for the crane itself. type CraneControllerTerminal struct { *terminal.TerminalBase Crane *Crane } // NewLocalCraneControllerTerminal returns a new local crane controller. func NewLocalCraneControllerTerminal( crane *Crane, initMsg *terminal.TerminalOpts, ) (*CraneControllerTerminal, *container.Container, *terminal.Error) { // Remove unnecessary options from the crane controller. initMsg.Padding = 0 // Create Terminal Base. t, initData, err := terminal.NewLocalBaseTerminal( crane.ctx, 0, crane.ID, nil, initMsg, terminal.UpstreamSendFunc(crane.sendImportantTerminalMsg), ) if err != nil { return nil, nil, err } return initCraneController(crane, t, initMsg), initData, nil } // NewRemoteCraneControllerTerminal returns a new remote crane controller. func NewRemoteCraneControllerTerminal( crane *Crane, initData *container.Container, ) (*CraneControllerTerminal, *terminal.TerminalOpts, *terminal.Error) { // Create Terminal Base. t, initMsg, err := terminal.NewRemoteBaseTerminal( crane.ctx, 0, crane.ID, nil, initData, terminal.UpstreamSendFunc(crane.sendImportantTerminalMsg), ) if err != nil { return nil, nil, err } return initCraneController(crane, t, initMsg), initMsg, nil } func initCraneController( crane *Crane, t *terminal.TerminalBase, initMsg *terminal.TerminalOpts, ) *CraneControllerTerminal { // Create Crane Terminal and assign it as the extended Terminal. cct := &CraneControllerTerminal{ TerminalBase: t, Crane: crane, } t.SetTerminalExtension(cct) // Assign controller to crane. crane.Controller = cct crane.terminals[cct.ID()] = cct // Copy the options to the crane itself. crane.opts = *initMsg // Grant crane controller permission. t.GrantPermission(terminal.IsCraneController) // Start workers. t.StartWorkers(module.mgr, "crane controller terminal") return cct } // HandleAbandon gives the terminal the ability to cleanly shut down. func (controller *CraneControllerTerminal) HandleAbandon(err *terminal.Error) (errorToSend *terminal.Error) { // Abandon terminal. controller.Crane.AbandonTerminal(0, err) return err } // HandleDestruction gives the terminal the ability to clean up. func (controller *CraneControllerTerminal) HandleDestruction(err *terminal.Error) { // Stop controlled crane. controller.Crane.Stop(nil) } ================================================ FILE: spn/docks/crane.go ================================================ package docks import ( "context" "errors" "fmt" "io" "net" "sync" "time" "github.com/tevino/abool" "github.com/safing/jess" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/ships" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" "github.com/safing/structures/varint" ) const ( // QOTD holds the quote of the day to return on idling unused connections. QOTD = "Privacy is not an option, and it shouldn't be the price we accept for just getting on the Internet.\nGary Kovacs\n" // maxUnloadSize defines the maximum size of a message to unload. maxUnloadSize = 16384 maxSegmentLength = 16384 maxCraneStoppingDuration = 6 * time.Hour maxCraneStopDuration = 10 * time.Second ) var ( // optimalMinLoadSize defines minimum for Crane.targetLoadSize. optimalMinLoadSize = 3072 // Targeting around 4096. // loadingMaxWaitDuration is the maximum time a crane will wait for // additional data to send. loadingMaxWaitDuration = 5 * time.Millisecond ) // Errors. var ( ErrDone = errors.New("crane is done") ) // Crane is the primary duplexer and connection manager. type Crane struct { // ID is the ID of the Crane. ID string // opts holds options. opts terminal.TerminalOpts // ctx is the context of the Terminal. ctx context.Context // cancelCtx cancels ctx. cancelCtx context.CancelFunc // stopping indicates if the Crane will be stopped soon. The Crane may still // be used until stopped, but must not be advertised anymore. stopping *abool.AtomicBool // stopped indicates if the Crane has been stopped. Whoever stopped the Crane // already took care of notifying everyone, so a silent fail is normally the // best response. stopped *abool.AtomicBool // authenticated indicates if there is has been any successful authentication. authenticated *abool.AtomicBool // ConnectedHub is the identity of the remote Hub. ConnectedHub *hub.Hub // NetState holds the network optimization state. // It must always be set and the reference must not be changed. // Access to fields within are coordinated by itself. NetState *NetworkOptimizationState // identity is identity of this instance and is usually only populated on a server. identity *cabin.Identity // jession is the jess session used for encryption. jession *jess.Session // jessionLock locks jession. jessionLock sync.Mutex // Controller is the Crane's Controller Terminal. Controller *CraneControllerTerminal // ship represents the underlying physical connection. ship ships.Ship // unloading moves containers from the ship to the crane. unloading chan *container.Container // loading moves containers from the crane to the ship. loading chan *container.Container // terminalMsgs holds containers from terminals waiting to be laoded. terminalMsgs chan *terminal.Msg // controllerMsgs holds important containers from terminals waiting to be laoded. controllerMsgs chan *terminal.Msg // terminals holds all the connected terminals. terminals map[uint32]terminal.Terminal // terminalsLock locks terminals. terminalsLock sync.Mutex // nextTerminalID holds the next terminal ID. nextTerminalID uint32 // targetLoadSize defines the optimal loading size. targetLoadSize int } // NewCrane returns a new crane. func NewCrane(ship ships.Ship, connectedHub *hub.Hub, id *cabin.Identity) (*Crane, error) { // Cranes always run in module context. ctx, cancelCtx := context.WithCancel(module.mgr.Ctx()) newCrane := &Crane{ ctx: ctx, cancelCtx: cancelCtx, stopping: abool.NewBool(false), stopped: abool.NewBool(false), authenticated: abool.NewBool(false), ConnectedHub: connectedHub, NetState: newNetworkOptimizationState(), identity: id, ship: ship, unloading: make(chan *container.Container), loading: make(chan *container.Container, 100), terminalMsgs: make(chan *terminal.Msg, 100), controllerMsgs: make(chan *terminal.Msg, 100), terminals: make(map[uint32]terminal.Terminal), } err := registerCrane(newCrane) if err != nil { return nil, fmt.Errorf("failed to register crane: %w", err) } // Shift next terminal IDs on the server. if !ship.IsMine() { newCrane.nextTerminalID += 4 } // Calculate target load size. loadSize := ship.LoadSize() if loadSize <= 0 { loadSize = ships.BaseMTU } newCrane.targetLoadSize = loadSize for newCrane.targetLoadSize < optimalMinLoadSize { newCrane.targetLoadSize += loadSize } // Subtract overhead needed for encryption. newCrane.targetLoadSize -= 25 // Manually tested for jess.SuiteWireV1 // Subtract space needed for length encoding the final chunk. newCrane.targetLoadSize -= varint.EncodedSize(uint64(newCrane.targetLoadSize)) return newCrane, nil } // IsMine returns whether the crane was started on this side. func (crane *Crane) IsMine() bool { return crane.ship.IsMine() } // Public returns whether the crane has been published. func (crane *Crane) Public() bool { return crane.ship.Public() } // IsStopping returns whether the crane is stopping. func (crane *Crane) IsStopping() bool { return crane.stopping.IsSet() } // MarkStoppingRequested marks the crane as stopping requested. func (crane *Crane) MarkStoppingRequested() { crane.NetState.lock.Lock() defer crane.NetState.lock.Unlock() if !crane.NetState.stoppingRequested { crane.NetState.stoppingRequested = true crane.startSyncStateOp() } } // MarkStopping marks the crane as stopping. func (crane *Crane) MarkStopping() (stopping bool) { // Can only stop owned cranes. if !crane.IsMine() { return false } if !crane.stopping.SetToIf(false, true) { return false } crane.NetState.lock.Lock() defer crane.NetState.lock.Unlock() crane.NetState.markedStoppingAt = time.Now() crane.startSyncStateOp() return true } // AbortStopping aborts the stopping. func (crane *Crane) AbortStopping() (aborted bool) { aborted = crane.stopping.SetToIf(true, false) crane.NetState.lock.Lock() defer crane.NetState.lock.Unlock() abortedStoppingRequest := crane.NetState.stoppingRequested crane.NetState.stoppingRequested = false crane.NetState.markedStoppingAt = time.Time{} // Sync if any state changed. if aborted || abortedStoppingRequest { crane.startSyncStateOp() } return aborted } // Authenticated returns whether the other side of the crane has authenticated // itself with an access code. func (crane *Crane) Authenticated() bool { return crane.authenticated.IsSet() } // Publish publishes the connection as a lane. func (crane *Crane) Publish() error { // Check if crane is connected. if crane.ConnectedHub == nil { return fmt.Errorf("spn/docks: %s: cannot publish without defined connected hub", crane) } // Submit metrics. if !crane.Public() { newPublicCranes.Inc() } // Mark crane as public. maskedID := crane.ship.MaskAddress(crane.ship.RemoteAddr()) crane.ship.MarkPublic() // Assign crane to make it available to others. AssignCrane(crane.ConnectedHub.ID, crane) log.Infof("spn/docks: %s (was %s) is now public", crane, maskedID) return nil } // LocalAddr returns ship's local address. func (crane *Crane) LocalAddr() net.Addr { return crane.ship.LocalAddr() } // RemoteAddr returns ship's local address. func (crane *Crane) RemoteAddr() net.Addr { return crane.ship.RemoteAddr() } // Transport returns ship's transport. func (crane *Crane) Transport() *hub.Transport { return crane.ship.Transport() } func (crane *Crane) getNextTerminalID() uint32 { crane.terminalsLock.Lock() defer crane.terminalsLock.Unlock() for { // Bump to next ID. crane.nextTerminalID += 8 // Check if it's free. _, ok := crane.terminals[crane.nextTerminalID] if !ok { return crane.nextTerminalID } } } func (crane *Crane) terminalCount() int { crane.terminalsLock.Lock() defer crane.terminalsLock.Unlock() return len(crane.terminals) } func (crane *Crane) getTerminal(id uint32) (t terminal.Terminal, ok bool) { crane.terminalsLock.Lock() defer crane.terminalsLock.Unlock() t, ok = crane.terminals[id] return } func (crane *Crane) setTerminal(t terminal.Terminal) { crane.terminalsLock.Lock() defer crane.terminalsLock.Unlock() crane.terminals[t.ID()] = t } func (crane *Crane) deleteTerminal(id uint32) (t terminal.Terminal, ok bool) { crane.terminalsLock.Lock() defer crane.terminalsLock.Unlock() t, ok = crane.terminals[id] if ok { delete(crane.terminals, id) return t, true } return nil, false } // AbandonTerminal abandons the terminal with the given ID. func (crane *Crane) AbandonTerminal(id uint32, err *terminal.Error) { // Get active terminal. t, ok := crane.deleteTerminal(id) if ok { // If the terminal was registered, abandon it. // Log reason the terminal is ending. Override stopping error with nil. switch { case err == nil || err.IsOK(): log.Debugf("spn/docks: %T %s is being abandoned", t, t.FmtID()) case err.Is(terminal.ErrStopping): err = nil log.Debugf("spn/docks: %T %s is being abandoned by peer", t, t.FmtID()) case err.Is(terminal.ErrNoActivity): err = nil log.Debugf("spn/docks: %T %s is being abandoned due to no activity", t, t.FmtID()) default: log.Warningf("spn/docks: %T %s: %s", t, t.FmtID(), err) } // Call the terminal's abandon function. t.Abandon(err) } else { //nolint:gocritic // When a crane terminal is abandoned, it calls crane.AbandonTerminal when // finished. This time, the terminal won't be in the registry anymore and // it finished shutting down, so we can now check if the crane needs to be // stopped. // If the crane is stopping, check if we can stop. // We can stop when all terminals are abandoned or after a timeout. // FYI: The crane controller will always take up one slot. if crane.stopping.IsSet() && crane.terminalCount() <= 1 { // Stop the crane in worker, so the caller can do some work. module.mgr.Go("retire crane", func(_ *mgr.WorkerCtx) error { // Let enough time for the last errors to be sent, as terminals are abandoned in a goroutine. time.Sleep(3 * time.Second) crane.Stop(nil) return nil }) } } } func (crane *Crane) sendImportantTerminalMsg(msg *terminal.Msg, timeout time.Duration) *terminal.Error { select { case crane.controllerMsgs <- msg: return nil case <-crane.ctx.Done(): msg.Finish() return terminal.ErrCanceled } } // Send is used by others to send a message through the crane. func (crane *Crane) Send(msg *terminal.Msg, timeout time.Duration) *terminal.Error { select { case crane.terminalMsgs <- msg: return nil case <-crane.ctx.Done(): msg.Finish() return terminal.ErrCanceled } } func (crane *Crane) encrypt(shipment *container.Container) (encrypted *container.Container, err error) { // Skip if encryption is not enabled. if crane.jession == nil { return shipment, nil } crane.jessionLock.Lock() defer crane.jessionLock.Unlock() letter, err := crane.jession.Close(shipment.CompileData()) if err != nil { return nil, err } encrypted, err = letter.ToWire() if err != nil { return nil, fmt.Errorf("failed to pack letter: %w", err) } return encrypted, nil } func (crane *Crane) decrypt(shipment *container.Container) (decrypted *container.Container, err error) { // Skip if encryption is not enabled. if crane.jession == nil { return shipment, nil } crane.jessionLock.Lock() defer crane.jessionLock.Unlock() letter, err := jess.LetterFromWire(shipment) if err != nil { return nil, fmt.Errorf("failed to parse letter: %w", err) } decryptedData, err := crane.jession.Open(letter) if err != nil { return nil, err } return container.New(decryptedData), nil } func (crane *Crane) unloader(workerCtx *mgr.WorkerCtx) error { // Unclean shutdown safeguard. defer crane.Stop(terminal.ErrUnknownError.With("unloader died")) for { // Get first couple bytes to get the packet length. // 2 bytes are enough to encode 65535. // On the other hand, packets can be only 2 bytes small. lenBuf := make([]byte, 2) err := crane.unloadUntilFull(lenBuf) if err != nil { if errors.Is(err, io.EOF) { crane.Stop(terminal.ErrStopping.With("connection closed")) } else { crane.Stop(terminal.ErrInternalError.With("failed to unload: %w", err)) } return nil } // Unpack length. containerLen, n, err := varint.Unpack64(lenBuf) if err != nil { crane.Stop(terminal.ErrMalformedData.With("failed to get container length: %w", err)) return nil } switch { case containerLen <= 0: crane.Stop(terminal.ErrMalformedData.With("received empty container with length %d", containerLen)) return nil case containerLen > maxUnloadSize: crane.Stop(terminal.ErrMalformedData.With("received oversized container with length %d", containerLen)) return nil } // Build shipment. var shipmentBuf []byte leftovers := len(lenBuf) - n if leftovers == int(containerLen) { // We already have all the shipment data. shipmentBuf = lenBuf[n:] } else { // Create a shipment buffer, copy leftovers and read the rest from the connection. shipmentBuf = make([]byte, containerLen) if leftovers > 0 { copy(shipmentBuf, lenBuf[n:]) } // Read remaining shipment. err = crane.unloadUntilFull(shipmentBuf[leftovers:]) if err != nil { crane.Stop(terminal.ErrInternalError.With("failed to unload: %w", err)) return nil } } // Submit to handler. select { case <-crane.ctx.Done(): crane.Stop(nil) return nil case crane.unloading <- container.New(shipmentBuf): } } } func (crane *Crane) unloadUntilFull(buf []byte) error { var bytesRead int for { // Get shipment from ship. n, err := crane.ship.UnloadTo(buf[bytesRead:]) if err != nil { return err } if n == 0 { log.Tracef("spn/docks: %s unloaded 0 bytes", crane) } bytesRead += n // Return if buffer has been fully filled. if bytesRead == len(buf) { // Submit metrics. crane.submitCraneTrafficStats(bytesRead) crane.NetState.ReportTraffic(uint64(bytesRead), true) return nil } } } func (crane *Crane) handler(workerCtx *mgr.WorkerCtx) error { var partialShipment *container.Container var segmentLength uint32 // Unclean shutdown safeguard. defer crane.Stop(terminal.ErrUnknownError.With("handler died")) handling: for { select { case <-crane.ctx.Done(): crane.Stop(nil) return nil case shipment := <-crane.unloading: // log.Debugf("spn/crane %s: before decrypt: %v ... %v", crane.ID, c.CompileData()[:10], c.CompileData()[c.Length()-10:]) // Decrypt shipment. shipment, err := crane.decrypt(shipment) if err != nil { crane.Stop(terminal.ErrIntegrity.With("failed to decrypt: %w", err)) return nil } // Process all segments/containers of the shipment. for shipment.HoldsData() { if partialShipment != nil { // Continue processing partial segment. // Append new shipment to previous partial segment. partialShipment.AppendContainer(shipment) shipment, partialShipment = partialShipment, nil } // Get next segment length. if segmentLength == 0 { segmentLength, err = shipment.GetNextN32() if err != nil { if errors.Is(err, varint.ErrBufTooSmall) { // Continue handling when there is not yet enough data. partialShipment = shipment segmentLength = 0 continue handling } crane.Stop(terminal.ErrMalformedData.With("failed to get segment length: %w", err)) return nil } if segmentLength == 0 { // Remainder is padding. continue handling } // Check if the segment is within the boundary. if segmentLength > maxSegmentLength { crane.Stop(terminal.ErrMalformedData.With("received oversized segment with length %d", segmentLength)) return nil } } // Check if we have enough data for the segment. if uint32(shipment.Length()) < segmentLength { partialShipment = shipment continue handling } // Get segment from shipment. segment, err := shipment.GetAsContainer(int(segmentLength)) if err != nil { crane.Stop(terminal.ErrMalformedData.With("failed to get segment: %w", err)) return nil } segmentLength = 0 // Get terminal ID and message type of segment. terminalID, terminalMsgType, err := terminal.ParseIDType(segment) if err != nil { crane.Stop(terminal.ErrMalformedData.With("failed to get terminal ID and msg type: %w", err)) return nil } switch terminalMsgType { case terminal.MsgTypeInit: crane.establishTerminal(terminalID, segment) case terminal.MsgTypeData, terminal.MsgTypePriorityData: // Get terminal and let it further handle the message. t, ok := crane.getTerminal(terminalID) if ok { // Create msg and set priority. msg := terminal.NewEmptyMsg() msg.FlowID = terminalID msg.Type = terminalMsgType msg.Data = segment if msg.Type == terminal.MsgTypePriorityData { msg.Unit.MakeHighPriority() } // Deliver to terminal. deliveryErr := t.Deliver(msg) if deliveryErr != nil { msg.Finish() // This is a hot path. Start a worker for abandoning the terminal. module.mgr.Go("end terminal", func(_ *mgr.WorkerCtx) error { crane.AbandonTerminal(t.ID(), deliveryErr.Wrap("failed to deliver data")) return nil }) } } else { log.Tracef("spn/docks: %s received msg for unknown terminal %d", crane, terminalID) } case terminal.MsgTypeStop: // Parse error. receivedErr, err := terminal.ParseExternalError(segment.CompileData()) if err != nil { log.Warningf("spn/docks: %s failed to parse abandon error: %s", crane, err) receivedErr = terminal.ErrUnknownError.AsExternal() } // This is a hot path. Start a worker for abandoning the terminal. module.mgr.Go("end terminal", func(_ *mgr.WorkerCtx) error { crane.AbandonTerminal(terminalID, receivedErr) return nil }) } } } } } func (crane *Crane) loader(workerCtx *mgr.WorkerCtx) (err error) { shipment := container.New() var partialShipment *container.Container var loadingTimer *time.Timer // Unclean shutdown safeguard. defer crane.Stop(terminal.ErrUnknownError.With("loader died")) // Return the loading wait channel if waiting. loadNow := func() <-chan time.Time { if loadingTimer != nil { return loadingTimer.C } return nil } // Make sure any received message is finished var msg, firstMsg *terminal.Msg defer msg.Finish() defer firstMsg.Finish() for { // Reset first message in shipment. firstMsg.Finish() firstMsg = nil fillingShipment: for shipment.Length() < crane.targetLoadSize { // Gather segments until shipment is filled. // Prioritize messages from the controller. select { case msg = <-crane.controllerMsgs: case <-crane.ctx.Done(): crane.Stop(nil) return nil default: // Then listen for all. select { case msg = <-crane.controllerMsgs: case msg = <-crane.terminalMsgs: case <-loadNow(): break fillingShipment case <-crane.ctx.Done(): crane.Stop(nil) return nil } } // Debug unit leaks. msg.Debug() // Handle new message. if msg != nil { // Pack msg and add to segment. msg.Pack() newSegment := msg.Data // Check if this is the first message. // This is the only message where we wait for a slot. if firstMsg == nil { firstMsg = msg firstMsg.Unit.WaitForSlot() } else { msg.Finish() } // Check length. if newSegment.Length() > maxSegmentLength { log.Warningf("spn/docks: %s ignored oversized segment with length %d", crane, newSegment.Length()) continue fillingShipment } // Append to shipment. shipment.AppendContainer(newSegment) // Set loading max wait timer on first segment. if loadingTimer == nil { loadingTimer = time.NewTimer(loadingMaxWaitDuration) } } else if crane.stopped.IsSet() { // If there is no new segment, this might have been triggered by a // closed channel. Check if the crane is still active. return nil } } sendingShipment: for { // Check if we are over the target load size and split the shipment. if shipment.Length() > crane.targetLoadSize { partialShipment, err = shipment.GetAsContainer(crane.targetLoadSize) if err != nil { crane.Stop(terminal.ErrInternalError.With("failed to split segment: %w", err)) return nil } shipment, partialShipment = partialShipment, shipment } // Load shipment. err = crane.load(shipment) if err != nil { crane.Stop(terminal.ErrShipSunk.With("failed to load shipment: %w", err)) return nil } // Reset loading timer. loadingTimer = nil // Continue loading with partial shipment, or a new one. if partialShipment != nil { // Continue loading with a partial previous shipment. shipment, partialShipment = partialShipment, nil // If shipment is not big enough to send immediately, wait for more data. if shipment.Length() < crane.targetLoadSize { loadingTimer = time.NewTimer(loadingMaxWaitDuration) break sendingShipment } } else { // Continue loading with new shipment. shipment = container.New() break sendingShipment } } } } func (crane *Crane) load(c *container.Container) error { // Add Padding if needed. if crane.opts.Padding > 0 { paddingNeeded := int(crane.opts.Padding) - ((c.Length() + varint.EncodedSize(uint64(c.Length()))) % int(crane.opts.Padding)) // As the length changes slightly with the padding, we should avoid loading // lengths around the varint size hops: // - 128 // - 16384 // - 2097152 // - 268435456 // Pad to target load size at maximum. maxPadding := crane.targetLoadSize - c.Length() if paddingNeeded > maxPadding { paddingNeeded = maxPadding } if paddingNeeded > 0 { // Add padding indicator. c.Append([]byte{0}) paddingNeeded-- // Add needed padding data. if paddingNeeded > 0 { padding, err := rng.Bytes(paddingNeeded) if err != nil { log.Debugf("spn/docks: %s failed to get random padding data, using zeros instead", crane) padding = make([]byte, paddingNeeded) } c.Append(padding) } } } // Encrypt shipment. c, err := crane.encrypt(c) if err != nil { return fmt.Errorf("failed to encrypt: %w", err) } // Finalize data. c.PrependLength() readyToSend := c.CompileData() // Submit metrics. crane.submitCraneTrafficStats(len(readyToSend)) crane.NetState.ReportTraffic(uint64(len(readyToSend)), false) // Load onto ship. err = crane.ship.Load(readyToSend) if err != nil { return fmt.Errorf("failed to load ship: %w", err) } return nil } // Stop stops the crane. func (crane *Crane) Stop(err *terminal.Error) { if !crane.stopped.SetToIf(false, true) { return } // Log error message. if err != nil { if err.IsOK() { log.Infof("spn/docks: %s is done", crane) } else { log.Warningf("spn/docks: %s is stopping: %s", crane, err) } } // Unregister crane. unregisterCrane(crane) // Stop all terminals. for _, t := range crane.allTerms() { t.Abandon(err) // Async! } // Stop controller. if crane.Controller != nil { crane.Controller.Abandon(err) // Async! } // Wait shortly for all terminals to finish abandoning. waitStep := 50 * time.Millisecond for i := time.Duration(0); i < maxCraneStopDuration; i += waitStep { // Check if all terminals are done. if crane.terminalCount() == 0 { break } time.Sleep(waitStep) } // Close connection. crane.ship.Sink() // Cancel crane context. crane.cancelCtx() // Notify about change. crane.NotifyUpdate() } func (crane *Crane) allTerms() []terminal.Terminal { crane.terminalsLock.Lock() defer crane.terminalsLock.Unlock() terms := make([]terminal.Terminal, 0, len(crane.terminals)) for _, term := range crane.terminals { terms = append(terms, term) } return terms } func (crane *Crane) String() string { remoteAddr := crane.ship.RemoteAddr() switch { case remoteAddr == nil: return fmt.Sprintf("crane %s", crane.ID) case crane.ship.IsMine(): return fmt.Sprintf("crane %s to %s", crane.ID, crane.ship.MaskAddress(crane.ship.RemoteAddr())) default: return fmt.Sprintf("crane %s from %s", crane.ID, crane.ship.MaskAddress(crane.ship.RemoteAddr())) } } // Stopped returns whether the crane has stopped. func (crane *Crane) Stopped() bool { return crane.stopped.IsSet() } ================================================ FILE: spn/docks/crane_establish.go ================================================ package docks import ( "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" ) const ( defaultTerminalIdleTimeout = 15 * time.Minute remoteTerminalIdleTimeout = 30 * time.Minute ) // EstablishNewTerminal establishes a new terminal with the crane. func (crane *Crane) EstablishNewTerminal( localTerm terminal.Terminal, initData *container.Container, ) *terminal.Error { // Create message. msg := terminal.NewEmptyMsg() msg.FlowID = localTerm.ID() msg.Type = terminal.MsgTypeInit msg.Data = initData // Register terminal with crane. crane.setTerminal(localTerm) // Send message. select { case crane.controllerMsgs <- msg: log.Debugf("spn/docks: %s initiated new terminal %d", crane, localTerm.ID()) return nil case <-crane.ctx.Done(): crane.AbandonTerminal(localTerm.ID(), terminal.ErrStopping.With("initiation aborted")) return terminal.ErrStopping } } func (crane *Crane) establishTerminal(id uint32, initData *container.Container) { // Create new remote crane terminal. newTerminal, _, err := NewRemoteCraneTerminal( crane, id, initData, ) if err == nil { // Connections via public cranes have a timeout. if crane.Public() { newTerminal.TerminalBase.SetTimeout(remoteTerminalIdleTimeout) } // Register terminal with crane. crane.setTerminal(newTerminal) log.Debugf("spn/docks: %s established new crane terminal %d", crane, newTerminal.ID()) return } // If something goes wrong, send an error back. log.Warningf("spn/docks: %s failed to establish crane terminal: %s", crane, err) // Build abandon message. msg := terminal.NewMsg(err.Pack()) msg.FlowID = id msg.Type = terminal.MsgTypeStop // Send message directly, or async. select { case crane.terminalMsgs <- msg: default: // Send error async. module.mgr.Go("abandon terminal", func(ctx *mgr.WorkerCtx) error { select { case crane.terminalMsgs <- msg: case <-ctx.Done(): } return nil }) } } ================================================ FILE: spn/docks/crane_init.go ================================================ package docks import ( "context" "time" "github.com/safing/jess" "github.com/safing/portmaster/base/info" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" "github.com/safing/structures/dsd" "github.com/safing/structures/varint" ) /* Crane Init Message Format: used by init procedures - Data [bytes block] - MsgType [varint] - Data [bytes; only when MsgType is Verify or Start*] Crane Init Response Format: - Data [bytes block] Crane Operational Message Format: - Data [bytes block] - possibly encrypted */ // Crane Msg Types. const ( CraneMsgTypeEnd = 0 CraneMsgTypeInfo = 1 CraneMsgTypeRequestHubInfo = 2 CraneMsgTypeVerify = 3 CraneMsgTypeStartEncrypted = 4 CraneMsgTypeStartUnencrypted = 5 ) // Start starts the crane. func (crane *Crane) Start(callerCtx context.Context) error { log.Infof("spn/docks: %s is starting", crane) // Submit metrics. newCranes.Inc() // Start crane depending on situation. var tErr *terminal.Error if crane.ship.IsMine() { tErr = crane.startLocal(callerCtx) } else { tErr = crane.startRemote(callerCtx) } // Stop crane again if starting failed. if tErr != nil { crane.Stop(tErr) return tErr } log.Debugf("spn/docks: %s started", crane) // Return an explicit nil for working "!= nil" checks. return nil } func (crane *Crane) startLocal(callerCtx context.Context) *terminal.Error { module.mgr.Go("crane unloader", crane.unloader) if !crane.ship.IsSecure() { // Start encrypted channel. // Check if we have all the data we need from the Hub. if crane.ConnectedHub == nil { return terminal.ErrIncorrectUsage.With("cannot start encrypted channel without connected hub") } // Always request hub info, as we don't know if the hub has restarted in // the meantime and lost ephemeral keys. hubInfoRequest := container.New( varint.Pack8(CraneMsgTypeRequestHubInfo), ) hubInfoRequest.PrependLength() err := crane.ship.Load(hubInfoRequest.CompileData()) if err != nil { return terminal.ErrShipSunk.With("failed to request hub info: %w", err) } // Wait for reply. var reply *container.Container select { case reply = <-crane.unloading: case <-time.After(30 * time.Second): return terminal.ErrTimeout.With("waiting for hub info") case <-crane.ctx.Done(): return terminal.ErrShipSunk.With("waiting for hub info") case <-callerCtx.Done(): return terminal.ErrCanceled.With("waiting for hub info") } // Parse and import Announcement and Status. announcementData, err := reply.GetNextBlock() if err != nil { return terminal.ErrMalformedData.With("failed to get announcement: %w", err) } statusData, err := reply.GetNextBlock() if err != nil { return terminal.ErrMalformedData.With("failed to get status: %w", err) } h, _, tErr := ImportAndVerifyHubInfo( callerCtx, crane.ConnectedHub.ID, announcementData, statusData, conf.MainMapName, conf.MainMapScope, ) if tErr != nil { return tErr.Wrap("failed to import and verify hub") } // Update reference in case it was changed by the import. crane.ConnectedHub = h // Now, try to select a public key again. signet := crane.ConnectedHub.SelectSignet() if signet == nil { return terminal.ErrHubNotReady.With("failed to select signet (after updating hub info)") } // Configure encryption. env := jess.NewUnconfiguredEnvelope() env.SuiteID = jess.SuiteWireV1 env.Recipients = []*jess.Signet{signet} // Do not encrypt directly, rather get session for future use, then encrypt. crane.jession, err = env.WireCorrespondence(nil) if err != nil { return terminal.ErrInternalError.With("failed to create encryption session: %w", err) } } // Create crane controller. _, initData, tErr := NewLocalCraneControllerTerminal(crane, terminal.DefaultCraneControllerOpts()) if tErr != nil { return tErr.Wrap("failed to set up controller") } // Prepare init message for sending. if crane.ship.IsSecure() { initData.PrependNumber(CraneMsgTypeStartUnencrypted) } else { // Encrypt controller initializer. letter, err := crane.jession.Close(initData.CompileData()) if err != nil { return terminal.ErrInternalError.With("failed to encrypt initial packet: %w", err) } initData, err = letter.ToWire() if err != nil { return terminal.ErrInternalError.With("failed to pack initial packet: %w", err) } initData.PrependNumber(CraneMsgTypeStartEncrypted) } // Send start message. initData.PrependLength() err := crane.ship.Load(initData.CompileData()) if err != nil { return terminal.ErrShipSunk.With("failed to send init msg: %w", err) } // Start remaining workers. module.mgr.Go("crane loader", crane.loader) module.mgr.Go("crane handler", crane.handler) return nil } func (crane *Crane) startRemote(callerCtx context.Context) *terminal.Error { var initMsg *container.Container module.mgr.Go("crane unloader", crane.unloader) handling: for { // Wait for request. var request *container.Container select { case request = <-crane.unloading: case <-time.After(30 * time.Second): return terminal.ErrTimeout.With("waiting for crane init msg") case <-crane.ctx.Done(): return terminal.ErrShipSunk.With("waiting for crane init msg") case <-callerCtx.Done(): return terminal.ErrCanceled.With("waiting for crane init msg") } msgType, err := request.GetNextN8() if err != nil { return terminal.ErrMalformedData.With("failed to parse crane msg type: %s", err) } switch msgType { case CraneMsgTypeEnd: // End connection. return terminal.ErrStopping case CraneMsgTypeInfo: // Info is a terminating request. err := crane.handleCraneInfo() if err != nil { return err } log.Debugf("spn/docks: %s sent version info", crane) case CraneMsgTypeRequestHubInfo: // Handle Hub info request. err := crane.handleCraneHubInfo() if err != nil { return err } log.Debugf("spn/docks: %s sent hub info", crane) case CraneMsgTypeVerify: // Verify is a terminating request. err := crane.handleCraneVerification(request) if err != nil { return err } log.Infof("spn/docks: %s sent hub verification", crane) case CraneMsgTypeStartUnencrypted: initMsg = request // Start crane with initMsg. log.Debugf("spn/docks: %s initiated unencrypted channel", crane) break handling case CraneMsgTypeStartEncrypted: if crane.identity == nil { return terminal.ErrIncorrectUsage.With("cannot start incoming crane without designated identity") } // Set up encryption. letter, err := jess.LetterFromWire(container.New(request.CompileData())) if err != nil { return terminal.ErrMalformedData.With("failed to unpack initial packet: %w", err) } crane.jession, err = letter.WireCorrespondence(crane.identity) if err != nil { return terminal.ErrInternalError.With("failed to create encryption session: %w", err) } initMsgData, err := crane.jession.Open(letter) if err != nil { return terminal.ErrIntegrity.With("failed to decrypt initial packet: %w", err) } initMsg = container.New(initMsgData) // Start crane with initMsg. log.Debugf("spn/docks: %s initiated encrypted channel", crane) break handling } } _, _, err := NewRemoteCraneControllerTerminal(crane, initMsg) if err != nil { return err.Wrap("failed to start crane controller") } // Start remaining workers. module.mgr.Go("crane loader", crane.loader) module.mgr.Go("crane handler", crane.handler) return nil } func (crane *Crane) endInit() *terminal.Error { endMsg := container.New( varint.Pack8(CraneMsgTypeEnd), ) endMsg.PrependLength() err := crane.ship.Load(endMsg.CompileData()) if err != nil { return terminal.ErrShipSunk.With("failed to send end msg: %w", err) } return nil } func (crane *Crane) handleCraneInfo() *terminal.Error { // Pack info data. infoData, err := dsd.Dump(info.GetInfo(), dsd.JSON) if err != nil { return terminal.ErrInternalError.With("failed to pack info: %w", err) } msg := container.New(infoData) // Manually send reply. msg.PrependLength() err = crane.ship.Load(msg.CompileData()) if err != nil { return terminal.ErrShipSunk.With("failed to send info reply: %w", err) } return nil } func (crane *Crane) handleCraneHubInfo() *terminal.Error { msg := container.New() // Check if we have an identity. if crane.identity == nil { return terminal.ErrIncorrectUsage.With("cannot handle hub info request without designated identity") } // Add Hub Announcement. announcementData, err := crane.identity.ExportAnnouncement() if err != nil { return terminal.ErrInternalError.With("failed to export announcement: %w", err) } msg.AppendAsBlock(announcementData) // Add Hub Status. statusData, err := crane.identity.ExportStatus() if err != nil { return terminal.ErrInternalError.With("failed to export status: %w", err) } msg.AppendAsBlock(statusData) // Manually send reply. msg.PrependLength() err = crane.ship.Load(msg.CompileData()) if err != nil { return terminal.ErrShipSunk.With("failed to send hub info reply: %w", err) } return nil } ================================================ FILE: spn/docks/crane_netstate.go ================================================ package docks import ( "sync" "sync/atomic" "time" ) // NetStatePeriodInterval defines the interval some of the net state should be reset. const NetStatePeriodInterval = 15 * time.Minute // NetworkOptimizationState holds data for optimization purposes. type NetworkOptimizationState struct { lock sync.Mutex // lastSuggestedAt holds the time when the connection to the connected Hub was last suggested by the network optimization. lastSuggestedAt time.Time // stoppingRequested signifies whether stopping this lane is requested. stoppingRequested bool // stoppingRequestedByPeer signifies whether stopping this lane is requested by the peer. stoppingRequestedByPeer bool // markedStoppingAt holds the time when the crane was last marked as stopping. markedStoppingAt time.Time lifetimeBytesIn *uint64 lifetimeBytesOut *uint64 lifetimeStarted time.Time periodBytesIn *uint64 periodBytesOut *uint64 periodStarted time.Time } func newNetworkOptimizationState() *NetworkOptimizationState { return &NetworkOptimizationState{ lifetimeBytesIn: new(uint64), lifetimeBytesOut: new(uint64), lifetimeStarted: time.Now(), periodBytesIn: new(uint64), periodBytesOut: new(uint64), periodStarted: time.Now(), } } // UpdateLastSuggestedAt sets when the lane was last suggested to the current time. func (netState *NetworkOptimizationState) UpdateLastSuggestedAt() { netState.lock.Lock() defer netState.lock.Unlock() netState.lastSuggestedAt = time.Now() } // StoppingState returns when the stopping state. func (netState *NetworkOptimizationState) StoppingState() (requested, requestedByPeer bool, markedAt time.Time) { netState.lock.Lock() defer netState.lock.Unlock() return netState.stoppingRequested, netState.stoppingRequestedByPeer, netState.markedStoppingAt } // RequestStoppingSuggested returns whether the crane should request stopping. func (netState *NetworkOptimizationState) RequestStoppingSuggested(maxNotSuggestedDuration time.Duration) bool { netState.lock.Lock() defer netState.lock.Unlock() return time.Now().Add(-maxNotSuggestedDuration).After(netState.lastSuggestedAt) } // StoppingSuggested returns whether the crane should be marked as stopping. func (netState *NetworkOptimizationState) StoppingSuggested() bool { netState.lock.Lock() defer netState.lock.Unlock() return netState.stoppingRequested && netState.stoppingRequestedByPeer } // StopSuggested returns whether the crane should be stopped. func (netState *NetworkOptimizationState) StopSuggested() bool { netState.lock.Lock() defer netState.lock.Unlock() return netState.stoppingRequested && netState.stoppingRequestedByPeer && !netState.markedStoppingAt.IsZero() && time.Now().Add(-maxCraneStoppingDuration).After(netState.markedStoppingAt) } // ReportTraffic adds the reported transferred data to the traffic stats. func (netState *NetworkOptimizationState) ReportTraffic(bytes uint64, in bool) { if in { atomic.AddUint64(netState.lifetimeBytesIn, bytes) atomic.AddUint64(netState.periodBytesIn, bytes) } else { atomic.AddUint64(netState.lifetimeBytesOut, bytes) atomic.AddUint64(netState.periodBytesOut, bytes) } } // LapsePeriod lapses the net state period, if needed. func (netState *NetworkOptimizationState) LapsePeriod() { netState.lock.Lock() defer netState.lock.Unlock() // Reset period if interval elapsed. if time.Now().Add(-NetStatePeriodInterval).After(netState.periodStarted) { atomic.StoreUint64(netState.periodBytesIn, 0) atomic.StoreUint64(netState.periodBytesOut, 0) netState.periodStarted = time.Now() } } // GetTrafficStats returns the traffic stats. func (netState *NetworkOptimizationState) GetTrafficStats() ( lifetimeBytesIn uint64, lifetimeBytesOut uint64, lifetimeStarted time.Time, periodBytesIn uint64, periodBytesOut uint64, periodStarted time.Time, ) { netState.lock.Lock() defer netState.lock.Unlock() return atomic.LoadUint64(netState.lifetimeBytesIn), atomic.LoadUint64(netState.lifetimeBytesOut), netState.lifetimeStarted, atomic.LoadUint64(netState.periodBytesIn), atomic.LoadUint64(netState.periodBytesOut), netState.periodStarted } ================================================ FILE: spn/docks/crane_terminal.go ================================================ package docks import ( "net" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" ) // CraneTerminal is a terminal started by a crane. type CraneTerminal struct { *terminal.TerminalBase // Add-Ons terminal.SessionAddOn crane *Crane } // NewLocalCraneTerminal returns a new local crane terminal. func NewLocalCraneTerminal( crane *Crane, remoteHub *hub.Hub, initMsg *terminal.TerminalOpts, ) (*CraneTerminal, *container.Container, *terminal.Error) { // Create Terminal Base. t, initData, err := terminal.NewLocalBaseTerminal( crane.ctx, crane.getNextTerminalID(), crane.ID, remoteHub, initMsg, crane, ) if err != nil { return nil, nil, err } return initCraneTerminal(crane, t), initData, nil } // NewRemoteCraneTerminal returns a new remote crane terminal. func NewRemoteCraneTerminal( crane *Crane, id uint32, initData *container.Container, ) (*CraneTerminal, *terminal.TerminalOpts, *terminal.Error) { // Create Terminal Base. t, initMsg, err := terminal.NewRemoteBaseTerminal( crane.ctx, id, crane.ID, crane.identity, initData, crane, ) if err != nil { return nil, nil, err } return initCraneTerminal(crane, t), initMsg, nil } func initCraneTerminal( crane *Crane, t *terminal.TerminalBase, ) *CraneTerminal { // Create Crane Terminal and assign it as the extended Terminal. ct := &CraneTerminal{ TerminalBase: t, crane: crane, } t.SetTerminalExtension(ct) // Start workers. t.StartWorkers(module.mgr, "crane terminal") return ct } // GrantPermission grants the given permissions. // Additionally, it will mark the crane as authenticated, if not public. func (t *CraneTerminal) GrantPermission(grant terminal.Permission) { // Forward granted permission to base terminal. t.TerminalBase.GrantPermission(grant) // Mark crane as authenticated if not public or already authenticated. if !t.crane.Public() && !t.crane.Authenticated() { t.crane.authenticated.Set() // Submit metrics. newAuthenticatedCranes.Inc() } } // LocalAddr returns the crane's local address. func (t *CraneTerminal) LocalAddr() net.Addr { return t.crane.LocalAddr() } // RemoteAddr returns the crane's remote address. func (t *CraneTerminal) RemoteAddr() net.Addr { return t.crane.RemoteAddr() } // Transport returns the crane's transport. func (t *CraneTerminal) Transport() *hub.Transport { return t.crane.Transport() } // IsBeingAbandoned returns whether the terminal is being abandoned. func (t *CraneTerminal) IsBeingAbandoned() bool { return t.Abandoning.IsSet() } // HandleDestruction gives the terminal the ability to clean up. // The terminal has already fully shut down at this point. // Should never be called directly. Call Abandon() instead. func (t *CraneTerminal) HandleDestruction(err *terminal.Error) { t.crane.AbandonTerminal(t.ID(), err) } ================================================ FILE: spn/docks/crane_test.go ================================================ package docks import ( "context" "fmt" "os" "runtime/pprof" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/ships" "github.com/safing/portmaster/spn/terminal" ) func TestCraneCommunication(t *testing.T) { t.Parallel() testCraneWithCounter(t, "plain-counter-load-100", false, 100, 1000) testCraneWithCounter(t, "plain-counter-load-1000", false, 1000, 1000) testCraneWithCounter(t, "plain-counter-load-10000", false, 10000, 1000) testCraneWithCounter(t, "encrypted-counter", true, 1000, 1000) } func testCraneWithCounter(t *testing.T, testID string, encrypting bool, loadSize int, countTo uint64) { //nolint:unparam,thelper var identity *cabin.Identity var connectedHub *hub.Hub if encrypting { identity, connectedHub = getTestIdentity(t) } // Build ship and cranes. optimalMinLoadSize = loadSize * 2 ship := ships.NewTestShip(!encrypting, loadSize) var crane1, crane2 *Crane var craneWg sync.WaitGroup craneWg.Add(2) go func() { var err error crane1, err = NewCrane(ship, connectedHub, nil) if err != nil { panic(fmt.Sprintf("crane test %s could not create crane1: %s", testID, err)) } err = crane1.Start(module.mgr.Ctx()) if err != nil { panic(fmt.Sprintf("crane test %s could not start crane1: %s", testID, err)) } craneWg.Done() }() go func() { var err error crane2, err = NewCrane(ship.Reverse(), nil, identity) if err != nil { panic(fmt.Sprintf("crane test %s could not create crane2: %s", testID, err)) } err = crane2.Start(module.mgr.Ctx()) if err != nil { panic(fmt.Sprintf("crane test %s could not start crane2: %s", testID, err)) } craneWg.Done() }() craneWg.Wait() t.Logf("crane test %s setup complete", testID) // Wait async for test to complete, print stack after timeout. finished := make(chan struct{}) go func() { select { case <-finished: case <-time.After(10 * time.Second): t.Logf("crane test %s is taking too long, print stack:", testID) _ = pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) os.Exit(1) } }() t.Logf("crane1 controller: %+v", crane1.Controller) t.Logf("crane2 controller: %+v", crane2.Controller) // Start counters for testing. op1, tErr := terminal.NewCounterOp(crane1.Controller, terminal.CounterOpts{ ClientCountTo: countTo, ServerCountTo: countTo, }) if tErr != nil { t.Fatalf("crane test %s failed to run counter op: %s", testID, tErr) } // Wait for completion. op1.Wait() close(finished) // Wait a little so that all errors can be propagated, so we can truly see // if we succeeded. time.Sleep(1 * time.Second) // Check errors. if op1.Error != nil { t.Fatalf("crane test %s counter op1 failed: %s", testID, op1.Error) } } type StreamingTerminal struct { terminal.BareTerminal test *testing.T id uint32 crane *Crane recv chan *terminal.Msg testData []byte } func (t *StreamingTerminal) ID() uint32 { return t.id } func (t *StreamingTerminal) Ctx() context.Context { return module.mgr.Ctx() } func (t *StreamingTerminal) Deliver(msg *terminal.Msg) *terminal.Error { t.recv <- msg msg.Finish() return nil } func (t *StreamingTerminal) Abandon(err *terminal.Error) { t.crane.AbandonTerminal(t.ID(), err) if err != nil { t.test.Errorf("streaming terminal %d failed: %s", t.id, err) } } func (t *StreamingTerminal) FmtID() string { return fmt.Sprintf("test-%d", t.id) } func TestCraneLoadingUnloading(t *testing.T) { t.Parallel() testCraneWithStreaming(t, "plain-streaming", false, 100) testCraneWithStreaming(t, "encrypted-streaming", true, 100) } func testCraneWithStreaming(t *testing.T, testID string, encrypting bool, loadSize int) { //nolint:thelper var identity *cabin.Identity var connectedHub *hub.Hub if encrypting { identity, connectedHub = getTestIdentity(t) } // Build ship and cranes. optimalMinLoadSize = loadSize * 2 ship := ships.NewTestShip(!encrypting, loadSize) var crane1, crane2 *Crane var craneWg sync.WaitGroup craneWg.Add(2) go func() { var err error crane1, err = NewCrane(ship, connectedHub, nil) if err != nil { panic(fmt.Sprintf("crane test %s could not create crane1: %s", testID, err)) } err = crane1.Start(module.mgr.Ctx()) if err != nil { panic(fmt.Sprintf("crane test %s could not start crane1: %s", testID, err)) } craneWg.Done() }() go func() { var err error crane2, err = NewCrane(ship.Reverse(), nil, identity) if err != nil { panic(fmt.Sprintf("crane test %s could not create crane2: %s", testID, err)) } err = crane2.Start(module.mgr.Ctx()) if err != nil { panic(fmt.Sprintf("crane test %s could not start crane2: %s", testID, err)) } craneWg.Done() }() craneWg.Wait() t.Logf("crane test %s setup complete", testID) // Wait async for test to complete, print stack after timeout. finished := make(chan struct{}) go func() { select { case <-finished: case <-time.After(10 * time.Second): t.Logf("crane test %s is taking too long, print stack:", testID) _ = pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) os.Exit(1) } }() t.Logf("crane1 controller: %+v", crane1.Controller) t.Logf("crane2 controller: %+v", crane2.Controller) // Create terminals and run test. st := &StreamingTerminal{ test: t, id: 8, crane: crane2, recv: make(chan *terminal.Msg), testData: []byte("The quick brown fox jumps over the lazy dog."), } crane2.terminals[st.ID()] = st // Run streaming test. var streamingWg sync.WaitGroup streamingWg.Add(2) count := 10000 go func() { for i := 1; i <= count; i++ { msg := terminal.NewMsg(st.testData) msg.FlowID = st.id err := crane1.Send(msg, 1*time.Second) if err != nil { msg.Finish() crane1.Stop(err.Wrap("failed to submit terminal msg")) } // log.Tracef("spn/testing: + %d", i) } t.Logf("crane test %s done with sending", testID) streamingWg.Done() }() go func() { for i := 1; i <= count; i++ { msg := <-st.recv assert.Equal(t, st.testData, msg.Data.CompileData(), "data mismatched") // log.Tracef("spn/testing: - %d", i) } t.Logf("crane test %s done with receiving", testID) streamingWg.Done() }() // Wait for completion. streamingWg.Wait() close(finished) } var testIdentity *cabin.Identity func getTestIdentity(t *testing.T) (*cabin.Identity, *hub.Hub) { t.Helper() if testIdentity == nil { var err error testIdentity, err = cabin.CreateIdentity(module.mgr.Ctx(), "test") if err != nil { t.Fatalf("failed to create identity: %s", err) } } return testIdentity, testIdentity.Hub } ================================================ FILE: spn/docks/crane_verify.go ================================================ package docks import ( "context" "errors" "fmt" "time" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" "github.com/safing/structures/varint" ) const ( hubVerificationPurpose = "hub identify verification" ) // VerifyConnectedHub verifies the connected Hub. func (crane *Crane) VerifyConnectedHub(callerCtx context.Context) error { if !crane.ship.IsMine() || crane.nextTerminalID != 0 || crane.Public() { return errors.New("hub verification can only be executed in init phase by the client") } // Create verification request. v, request, err := cabin.CreateVerificationRequest(hubVerificationPurpose, "", "") if err != nil { return fmt.Errorf("failed to create verification request: %w", err) } // Send it. msg := container.New( varint.Pack8(CraneMsgTypeVerify), request, ) msg.PrependLength() err = crane.ship.Load(msg.CompileData()) if err != nil { return terminal.ErrShipSunk.With("failed to send verification request: %w", err) } // Wait for reply. var reply *container.Container select { case reply = <-crane.unloading: case <-time.After(2 * time.Minute): // Use a big timeout here, as this might keep servers from joining the // network at all, as every servers needs to verify every server, no // matter how far away. return terminal.ErrTimeout.With("waiting for verification reply") case <-crane.ctx.Done(): return terminal.ErrShipSunk.With("waiting for verification reply") case <-callerCtx.Done(): return terminal.ErrShipSunk.With("waiting for verification reply") } // Verify reply. return v.Verify(reply.CompileData(), crane.ConnectedHub) } func (crane *Crane) handleCraneVerification(request *container.Container) *terminal.Error { // Check if we have an identity. if crane.identity == nil { return terminal.ErrIncorrectUsage.With("cannot handle verification request without designated identity") } response, err := crane.identity.SignVerificationRequest( request.CompileData(), hubVerificationPurpose, "", "", ) if err != nil { return terminal.ErrInternalError.With("failed to sign verification request: %w", err) } msg := container.New(response) // Manually send reply. msg.PrependLength() err = crane.ship.Load(msg.CompileData()) if err != nil { return terminal.ErrShipSunk.With("failed to send verification reply: %w", err) } return nil } ================================================ FILE: spn/docks/cranehooks.go ================================================ package docks import ( "sync" "github.com/safing/portmaster/base/log" ) var ( craneUpdateHook func(crane *Crane) craneUpdateHookLock sync.Mutex ) // RegisterCraneUpdateHook allows the captain to hook into receiving updates for cranes. func RegisterCraneUpdateHook(fn func(crane *Crane)) { craneUpdateHookLock.Lock() defer craneUpdateHookLock.Unlock() if craneUpdateHook == nil { craneUpdateHook = fn } else { log.Error("spn/docks: crane update hook already registered") } } // ResetCraneUpdateHook resets the hook for receiving updates for cranes. func ResetCraneUpdateHook() { craneUpdateHookLock.Lock() defer craneUpdateHookLock.Unlock() craneUpdateHook = nil } // NotifyUpdate calls the registers crane update hook function. func (crane *Crane) NotifyUpdate() { if crane == nil { return } craneUpdateHookLock.Lock() defer craneUpdateHookLock.Unlock() if craneUpdateHook != nil { craneUpdateHook(crane) } } ================================================ FILE: spn/docks/hub_import.go ================================================ package docks import ( "context" "fmt" "net" "sync" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/ships" "github.com/safing/portmaster/spn/terminal" ) var hubImportLock sync.Mutex // ImportAndVerifyHubInfo imports the given hub message and verifies them. func ImportAndVerifyHubInfo(ctx context.Context, hubID string, announcementData, statusData []byte, mapName string, scope hub.Scope) (h *hub.Hub, forward bool, tErr *terminal.Error) { var firstErr *terminal.Error // Synchronize import, as we might easily learn of a new hub from different // gossip channels simultaneously. hubImportLock.Lock() defer hubImportLock.Unlock() // Check arguments. if announcementData == nil && statusData == nil { return nil, false, terminal.ErrInternalError.With("no announcement or status supplied") } // Import Announcement, if given. var hubKnown, hubChanged bool if announcementData != nil { hubFromMsg, known, changed, err := hub.ApplyAnnouncement(nil, announcementData, mapName, scope, false) if err != nil { firstErr = terminal.ErrInternalError.With("failed to apply announcement: %w", err) } if known { hubKnown = true } if changed { hubChanged = true } if hubFromMsg != nil { h = hubFromMsg } } // Import Status, if given. if statusData != nil { hubFromMsg, known, changed, err := hub.ApplyStatus(h, statusData, mapName, scope, false) if err != nil && firstErr == nil { firstErr = terminal.ErrInternalError.With("failed to apply status: %w", err) } if known && announcementData == nil { // If we parsed an announcement before, "known" will always be true here, // as we supply hub.ApplyStatus with a hub. hubKnown = true } if changed { hubChanged = true } if hubFromMsg != nil { h = hubFromMsg } } // Only continue if we now have a Hub. if h == nil { if firstErr != nil { return nil, false, firstErr } return nil, false, terminal.ErrInternalError.With("got not hub after data import") } // Abort if the given hub ID does not match. // We may have just connected to the wrong IP address. if hubID != "" && h.ID != hubID { return nil, false, terminal.ErrInternalError.With("hub mismatch") } // Verify hub if: // - There is no error up until here. // - There has been any change. // - The hub is not verified yet. // - We're a public Hub. // - We're not testing. if firstErr == nil && hubChanged && !h.Verified() && conf.PublicHub() && !runningTests { if !conf.HubHasIPv4() && !conf.HubHasIPv6() { firstErr = terminal.ErrInternalError.With("no hub networks set") } if h.Info.IPv4 != nil && conf.HubHasIPv4() { err := verifyHubIP(ctx, h, h.Info.IPv4) if err != nil { firstErr = terminal.ErrIntegrity.With("failed to verify IPv4 address %s of %s: %w", h.Info.IPv4, h, err) } } if h.Info.IPv6 != nil && conf.HubHasIPv6() { err := verifyHubIP(ctx, h, h.Info.IPv6) if err != nil { firstErr = terminal.ErrIntegrity.With("failed to verify IPv6 address %s of %s: %w", h.Info.IPv6, h, err) } } if firstErr != nil { func() { h.Lock() defer h.Unlock() h.InvalidInfo = true }() log.Warningf("spn/docks: failed to verify IPs of %s: %s", h, firstErr) } else { func() { h.Lock() defer h.Unlock() h.VerifiedIPs = true }() log.Infof("spn/docks: verified IPs of %s: IPv4=%s IPv6=%s", h, h.Info.IPv4, h.Info.IPv6) } } // Dismiss initial imports with errors. if !hubKnown && firstErr != nil { return nil, false, firstErr } // Don't do anything if nothing changed. if !hubChanged { return h, false, firstErr } // We now have one of: // - A unknown Hub without error. // - A known Hub without error. // - A known Hub with error, which we want to save and propagate. // Save the Hub to the database. err := h.Save() if err != nil { log.Errorf("spn/docks: failed to persist %s: %s", h, err) } // Save the raw messages to the database. if announcementData != nil { err = hub.SaveHubMsg(h.ID, h.Map, hub.MsgTypeAnnouncement, announcementData) if err != nil { log.Errorf("spn/docks: failed to save raw announcement msg of %s: %s", h, err) } } if statusData != nil { err = hub.SaveHubMsg(h.ID, h.Map, hub.MsgTypeStatus, statusData) if err != nil { log.Errorf("spn/docks: failed to save raw status msg of %s: %s", h, err) } } return h, true, firstErr } func verifyHubIP(ctx context.Context, h *hub.Hub, ip net.IP) error { // Create connection. ship, err := ships.Launch(ctx, h, nil, ip) if err != nil { return fmt.Errorf("failed to launch ship to %s: %w", ip, err) } // Start crane for receiving reply. crane, err := NewCrane(ship, h, nil) if err != nil { return fmt.Errorf("failed to create crane: %w", err) } module.mgr.Go("crane unloader", crane.unloader) defer crane.Stop(nil) // Verify Hub. err = crane.VerifyConnectedHub(ctx) if err != nil { return err } // End connection. tErr := crane.endInit() if tErr != nil { log.Debugf("spn/docks: failed to end verification connection to %s: %s", ip, tErr) } return nil } ================================================ FILE: spn/docks/measurements.go ================================================ package docks import ( "context" "fmt" "time" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/ships" "github.com/safing/portmaster/spn/terminal" ) // Measurement Configuration. const ( CraneMeasurementTTLDefault = 30 * time.Minute CraneMeasurementTTLByCostBase = 1 * time.Minute CraneMeasurementTTLByCostMin = 30 * time.Minute CraneMeasurementTTLByCostMax = 3 * time.Hour // With a base TTL of 1m, this leads to: // 20c -> 20m -> raised to 30m // 50c -> 50m // 100c -> 1h40m // 1000c -> 16h40m -> capped to 3h. ) // MeasureHub measures the connection to this Hub and saves the results to the // Hub. func MeasureHub(ctx context.Context, h *hub.Hub, checkExpiryWith time.Duration) *terminal.Error { // Check if we are measuring before building a connection. if capacityTestRunning.IsSet() { return terminal.ErrTryAgainLater.With("another capacity op is already running") } // Check if we have a connection to this Hub. crane := GetAssignedCrane(h.ID) if crane == nil { // Connect to Hub. var err error crane, err = establishCraneForMeasuring(ctx, h) if err != nil { return terminal.ErrConnectionError.With("failed to connect to %s: %s", h, err) } // Stop crane if established just for measuring. defer crane.Stop(nil) } // Run latency test. _, expires := h.GetMeasurements().GetLatency() if checkExpiryWith == 0 || time.Now().Add(-checkExpiryWith).After(expires) { latOp, tErr := NewLatencyTestOp(crane.Controller) if !tErr.IsOK() { return tErr } select { case tErr = <-latOp.Result(): if !tErr.IsOK() { return tErr } case <-ctx.Done(): return terminal.ErrCanceled case <-time.After(1 * time.Minute): crane.Controller.StopOperation(latOp, terminal.ErrTimeout) return terminal.ErrTimeout.With("waiting for latency test") } } // Run capacity test. _, expires = h.GetMeasurements().GetCapacity() if checkExpiryWith == 0 || time.Now().Add(-checkExpiryWith).After(expires) { capOp, tErr := NewCapacityTestOp(crane.Controller, nil) if !tErr.IsOK() { return tErr } select { case tErr = <-capOp.Result(): if !tErr.IsOK() { return tErr } case <-ctx.Done(): return terminal.ErrCanceled case <-time.After(1 * time.Minute): crane.Controller.StopOperation(capOp, terminal.ErrTimeout) return terminal.ErrTimeout.With("waiting for capacity test") } } return nil } func establishCraneForMeasuring(ctx context.Context, dst *hub.Hub) (*Crane, error) { ship, err := ships.Launch(ctx, dst, nil, nil) if err != nil { return nil, fmt.Errorf("failed to launch ship: %w", err) } crane, err := NewCrane(ship, dst, nil) if err != nil { return nil, fmt.Errorf("failed to create crane: %w", err) } err = crane.Start(ctx) if err != nil { return nil, fmt.Errorf("failed to start crane: %w", err) } return crane, nil } ================================================ FILE: spn/docks/metrics.go ================================================ package docks import ( "sync" "sync/atomic" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/metrics" ) var ( newCranes *metrics.Counter newPublicCranes *metrics.Counter newAuthenticatedCranes *metrics.Counter trafficBytesPublicCranes *metrics.Counter trafficBytesAuthenticatedCranes *metrics.Counter trafficBytesPrivateCranes *metrics.Counter newExpandOp *metrics.Counter expandOpDurationHistogram *metrics.Histogram expandOpRelayedDataHistogram *metrics.Histogram metricsRegistered = abool.New() ) func registerMetrics() (err error) { // Only register metrics once. if !metricsRegistered.SetToIf(false, true) { return nil } // Total Crane Stats. newCranes, err = metrics.NewCounter( "spn/cranes/total", nil, &metrics.Options{ Name: "SPN New Cranes", Permission: api.PermitUser, }, ) if err != nil { return err } newPublicCranes, err = metrics.NewCounter( "spn/cranes/public/total", nil, &metrics.Options{ Name: "SPN New Public Cranes", Permission: api.PermitUser, }, ) if err != nil { return err } newAuthenticatedCranes, err = metrics.NewCounter( "spn/cranes/authenticated/total", nil, &metrics.Options{ Name: "SPN New Authenticated Cranes", Permission: api.PermitUser, }, ) if err != nil { return err } // Active Crane Stats. _, err = metrics.NewGauge( "spn/cranes/active", map[string]string{ "status": "public", }, getActivePublicCranes, &metrics.Options{ Name: "SPN Active Public Cranes", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/cranes/active", map[string]string{ "status": "authenticated", }, getActiveAuthenticatedCranes, &metrics.Options{ Name: "SPN Active Authenticated Cranes", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/cranes/active", map[string]string{ "status": "private", }, getActivePrivateCranes, &metrics.Options{ Name: "SPN Active Private Cranes", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/cranes/active", map[string]string{ "status": "stopping", }, getActiveStoppingCranes, &metrics.Options{ Name: "SPN Active Stopping Cranes", Permission: api.PermitUser, }, ) if err != nil { return err } // Crane Traffic Stats. trafficBytesPublicCranes, err = metrics.NewCounter( "spn/cranes/bytes", map[string]string{ "status": "public", }, &metrics.Options{ Name: "SPN Public Crane Traffic", Permission: api.PermitUser, }, ) if err != nil { return err } trafficBytesAuthenticatedCranes, err = metrics.NewCounter( "spn/cranes/bytes", map[string]string{ "status": "authenticated", }, &metrics.Options{ Name: "SPN Authenticated Crane Traffic", Permission: api.PermitUser, }, ) if err != nil { return err } trafficBytesPrivateCranes, err = metrics.NewCounter( "spn/cranes/bytes", map[string]string{ "status": "private", }, &metrics.Options{ Name: "SPN Private Crane Traffic", Permission: api.PermitUser, }, ) if err != nil { return err } // Lane Stats. _, err = metrics.NewGauge( "spn/lanes/latency/avg/seconds", nil, getAvgLaneLatencyStat, &metrics.Options{ Name: "SPN Avg Lane Latency", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/lanes/latency/min/seconds", nil, getMinLaneLatencyStat, &metrics.Options{ Name: "SPN Min Lane Latency", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/lanes/capacity/avg/bytes", nil, getAvgLaneCapacityStat, &metrics.Options{ Name: "SPN Avg Lane Capacity", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/lanes/capacity/max/bytes", nil, getMaxLaneCapacityStat, &metrics.Options{ Name: "SPN Max Lane Capacity", Permission: api.PermitUser, }, ) if err != nil { return err } // Expand Op Stats. newExpandOp, err = metrics.NewCounter( "spn/op/expand/total", nil, &metrics.Options{ Name: "SPN Total Expand Operations", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/op/expand/active", nil, getActiveExpandOpsStat, &metrics.Options{ Name: "SPN Active Expand Operations", Permission: api.PermitUser, }, ) if err != nil { return err } expandOpDurationHistogram, err = metrics.NewHistogram( "spn/op/expand/histogram/duration/seconds", nil, &metrics.Options{ Name: "SPN Expand Operation Duration Histogram", Permission: api.PermitUser, }, ) if err != nil { return err } expandOpRelayedDataHistogram, err = metrics.NewHistogram( "spn/op/expand/histogram/traffic/bytes", nil, &metrics.Options{ Name: "SPN Expand Operation Relayed Data Histogram", Permission: api.PermitUser, }, ) if err != nil { return err } return err } func getActiveExpandOpsStat() float64 { return float64(atomic.LoadInt64(activeExpandOps)) } var ( craneStats *craneGauges craneStatsExpires time.Time craneStatsLock sync.Mutex craneStatsTTL = 55 * time.Second ) type craneGauges struct { publicActive float64 authenticatedActive float64 privateActive float64 stoppingActive float64 laneLatencyAvg float64 laneLatencyMin float64 laneCapacityAvg float64 laneCapacityMax float64 } func getActivePublicCranes() float64 { return getCraneStats().publicActive } func getActiveAuthenticatedCranes() float64 { return getCraneStats().authenticatedActive } func getActivePrivateCranes() float64 { return getCraneStats().privateActive } func getActiveStoppingCranes() float64 { return getCraneStats().stoppingActive } func getAvgLaneLatencyStat() float64 { return getCraneStats().laneLatencyAvg } func getMinLaneLatencyStat() float64 { return getCraneStats().laneLatencyMin } func getAvgLaneCapacityStat() float64 { return getCraneStats().laneCapacityAvg } func getMaxLaneCapacityStat() float64 { return getCraneStats().laneCapacityMax } func getCraneStats() *craneGauges { craneStatsLock.Lock() defer craneStatsLock.Unlock() // Return cache if still valid. if time.Now().Before(craneStatsExpires) { return craneStats } // Refresh. craneStats = &craneGauges{} var laneStatCnt float64 for _, crane := range getAllCranes() { switch { case crane.Stopped(): continue case crane.IsStopping(): craneStats.stoppingActive++ continue case crane.Public(): craneStats.publicActive++ case crane.Authenticated(): craneStats.authenticatedActive++ continue default: craneStats.privateActive++ continue } // Get lane stats. if crane.ConnectedHub == nil { continue } measurements := crane.ConnectedHub.GetMeasurements() laneLatency, _ := measurements.GetLatency() if laneLatency == 0 { continue } laneCapacity, _ := measurements.GetCapacity() if laneCapacity == 0 { continue } // Only use data if both latency and capacity is available. laneStatCnt++ // Convert to base unit: seconds. latency := laneLatency.Seconds() // Add to avg and set min if lower. craneStats.laneLatencyAvg += latency if craneStats.laneLatencyMin > latency || craneStats.laneLatencyMin == 0 { craneStats.laneLatencyMin = latency } // Convert in base unit: bytes. capacity := float64(laneCapacity) / 8 // Add to avg and set max if higher. craneStats.laneCapacityAvg += capacity if craneStats.laneCapacityMax < capacity { craneStats.laneCapacityMax = capacity } } // Create averages. if laneStatCnt > 0 { craneStats.laneLatencyAvg /= laneStatCnt craneStats.laneCapacityAvg /= laneStatCnt } craneStatsExpires = time.Now().Add(craneStatsTTL) return craneStats } func (crane *Crane) submitCraneTrafficStats(bytes int) { switch { case crane.Stopped(): return case crane.Public(): trafficBytesPublicCranes.Add(bytes) case crane.Authenticated(): trafficBytesAuthenticatedCranes.Add(bytes) default: trafficBytesPrivateCranes.Add(bytes) } } ================================================ FILE: spn/docks/module.go ================================================ package docks import ( "encoding/hex" "errors" "fmt" "sync" "sync/atomic" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/mgr" _ "github.com/safing/portmaster/spn/access" ) // Docks handles connections to other network participants. type Docks struct { mgr *mgr.Manager instance instance } // Manager returns the module manager. func (d *Docks) Manager() *mgr.Manager { return d.mgr } // Start starts the module. func (d *Docks) Start() error { return start() } // Stop stops the module. func (d *Docks) Stop() error { return stopAllCranes() } var ( allCranes = make(map[string]*Crane) // ID = Crane ID assignedCranes = make(map[string]*Crane) // ID = connected Hub ID cranesLock sync.RWMutex runningTests bool ) func start() error { return registerMetrics() } func registerCrane(crane *Crane) error { cranesLock.Lock() defer cranesLock.Unlock() // Generate new IDs until a unique one is found. for range 100 { // Generate random ID. randomID, err := rng.Bytes(3) if err != nil { return fmt.Errorf("failed to generate crane ID: %w", err) } newID := hex.EncodeToString(randomID) // Check if ID already exists. _, ok := allCranes[newID] if !ok { crane.ID = newID allCranes[crane.ID] = crane return nil } } return errors.New("failed to find unique crane ID") } func unregisterCrane(crane *Crane) { cranesLock.Lock() defer cranesLock.Unlock() delete(allCranes, crane.ID) if crane.ConnectedHub != nil { delete(assignedCranes, crane.ConnectedHub.ID) } } func stopAllCranes() error { for _, crane := range getAllCranes() { crane.Stop(nil) } return nil } // AssignCrane assigns a crane to the given Hub ID. func AssignCrane(hubID string, crane *Crane) { cranesLock.Lock() defer cranesLock.Unlock() assignedCranes[hubID] = crane } // GetAssignedCrane returns the assigned crane of the given Hub ID. func GetAssignedCrane(hubID string) *Crane { cranesLock.RLock() defer cranesLock.RUnlock() crane, ok := assignedCranes[hubID] if ok { return crane } return nil } func getAllCranes() map[string]*Crane { copiedCranes := make(map[string]*Crane, len(allCranes)) cranesLock.RLock() defer cranesLock.RUnlock() for id, crane := range allCranes { copiedCranes[id] = crane } return copiedCranes } // GetAllAssignedCranes returns a copy of the map of all assigned cranes. func GetAllAssignedCranes() map[string]*Crane { copiedCranes := make(map[string]*Crane, len(assignedCranes)) cranesLock.RLock() defer cranesLock.RUnlock() for destination, crane := range assignedCranes { copiedCranes[destination] = crane } return copiedCranes } var ( module *Docks shimLoaded atomic.Bool ) // New returns a new Docks module. func New(instance instance) (*Docks, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Docks") module = &Docks{ mgr: m, instance: instance, } return module, nil } type instance interface{} ================================================ FILE: spn/docks/module_test.go ================================================ package docks import ( "fmt" "os" "testing" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/metrics" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/core/base" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/access" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/terminal" ) type testInstance struct { db *dbmodule.DBModule config *config.Config metrics *metrics.Metrics rng *rng.Rng base *base.Base access *access.Access terminal *terminal.TerminalModule cabin *cabin.Cabin } func (stub *testInstance) Config() *config.Config { return stub.config } func (stub *testInstance) SPNGroup() *mgr.ExtendedGroup { return nil } func (stub *testInstance) SetCmdLineOperation(f func() error) {} func (stub *testInstance) IsShuttingDown() bool { return false } func (stub *testInstance) DataDir() string { return _dataDir } var _dataDir string func runTest(m *testing.M) error { _ = log.Start("info", true, "") var err error // Create a temporary directory for the data _dataDir, err = os.MkdirTemp("", "") if err != nil { return fmt.Errorf("failed to initialize dataroot: %w", err) } defer func() { _ = os.RemoveAll(_dataDir) }() instance := &testInstance{} runningTests = true conf.EnablePublicHub(true) // Make hub config available. access.EnableTestMode() // Register test zone instead of real ones. // Init instance.db, err = dbmodule.New(instance) if err != nil { return fmt.Errorf("failed to create database module: %w", err) } instance.config, err = config.New(instance) if err != nil { return fmt.Errorf("failed to create config module: %w", err) } instance.metrics, err = metrics.New(instance) if err != nil { return fmt.Errorf("failed to create metrics module: %w", err) } instance.rng, err = rng.New(instance) if err != nil { return fmt.Errorf("failed to create rng module: %w", err) } instance.base, err = base.New(instance) if err != nil { return fmt.Errorf("failed to create base module: %w", err) } instance.access, err = access.New(instance) if err != nil { return fmt.Errorf("failed to create access module: %w", err) } instance.terminal, err = terminal.New(instance) if err != nil { return fmt.Errorf("failed to create terminal module: %w", err) } instance.cabin, err = cabin.New(instance) if err != nil { return fmt.Errorf("failed to create cabin module: %w", err) } module, err = New(instance) if err != nil { return fmt.Errorf("failed to create docks module: %w", err) } // Start err = instance.db.Start() if err != nil { return fmt.Errorf("failed to start db module: %w", err) } err = instance.config.Start() if err != nil { return fmt.Errorf("failed to start config module: %w", err) } err = instance.metrics.Start() if err != nil { return fmt.Errorf("failed to start metrics module: %w", err) } err = instance.rng.Start() if err != nil { return fmt.Errorf("failed to start rng module: %w", err) } err = instance.base.Start() if err != nil { return fmt.Errorf("failed to start base module: %w", err) } err = instance.access.Start() if err != nil { return fmt.Errorf("failed to start access module: %w", err) } err = instance.terminal.Start() if err != nil { return fmt.Errorf("failed to start terminal module: %w", err) } err = instance.cabin.Start() if err != nil { return fmt.Errorf("failed to start cabin module: %w", err) } err = module.Start() if err != nil { return fmt.Errorf("failed to start docks module: %w", err) } m.Run() return nil } func TestMain(m *testing.M) { if err := runTest(m); err != nil { fmt.Printf("%s\n", err) os.Exit(1) } } ================================================ FILE: spn/docks/op_capacity.go ================================================ package docks import ( "bytes" "sync/atomic" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" "github.com/safing/structures/dsd" ) const ( // CapacityTestOpType is the type ID of the capacity test operation. CapacityTestOpType = "capacity" defaultCapacityTestVolume = 50000000 // 50MB maxCapacityTestVolume = 100000000 // 100MB defaultCapacityTestMaxTime = 5 * time.Second maxCapacityTestMaxTime = 15 * time.Second capacityTestTimeout = 30 * time.Second capacityTestMsgSize = 1000 capacityTestSendTimeout = 1000 * time.Millisecond ) var ( capacityTestSendData = make([]byte, capacityTestMsgSize) capacityTestDataReceivedSignal = []byte("ACK") capacityTestRunning = abool.New() ) // CapacityTestOp is used for capacity test operations. type CapacityTestOp struct { //nolint:maligned terminal.OperationBase opts *CapacityTestOptions started bool startTime time.Time senderStarted bool recvQueue chan *terminal.Msg dataReceived int dataReceivedAckWasAckd bool dataSent *int64 dataSentWasAckd *abool.AtomicBool testResult int result chan *terminal.Error } // CapacityTestOptions holds options for the capacity test. type CapacityTestOptions struct { TestVolume int MaxTime time.Duration testing bool } // Type returns the type ID. func (op *CapacityTestOp) Type() string { return CapacityTestOpType } func init() { terminal.RegisterOpType(terminal.OperationFactory{ Type: CapacityTestOpType, Requires: terminal.IsCraneController, Start: startCapacityTestOp, }) } // NewCapacityTestOp runs a capacity test. func NewCapacityTestOp(t terminal.Terminal, opts *CapacityTestOptions) (*CapacityTestOp, *terminal.Error) { // Check options. if opts == nil { opts = &CapacityTestOptions{ TestVolume: defaultCapacityTestVolume, MaxTime: defaultCapacityTestMaxTime, } } // Check if another test is already running. if !opts.testing && !capacityTestRunning.SetToIf(false, true) { return nil, terminal.ErrTryAgainLater.With("another capacity op is already running") } // Create and init. op := &CapacityTestOp{ opts: opts, recvQueue: make(chan *terminal.Msg), dataSent: new(int64), dataSentWasAckd: abool.New(), result: make(chan *terminal.Error, 1), } // Make capacity test request. request, err := dsd.Dump(op.opts, dsd.CBOR) if err != nil { capacityTestRunning.UnSet() return nil, terminal.ErrInternalError.With("failed to serialize capactity test options: %w", err) } // Send test request. tErr := t.StartOperation(op, container.New(request), 1*time.Second) if tErr != nil { capacityTestRunning.UnSet() return nil, tErr } // Start handler. module.mgr.Go("op capacity handler", op.handler) return op, nil } func startCapacityTestOp(t terminal.Terminal, opID uint32, data *container.Container) (terminal.Operation, *terminal.Error) { // Check if another test is already running. if !capacityTestRunning.SetToIf(false, true) { return nil, terminal.ErrTryAgainLater.With("another capacity op is already running") } // Parse options. opts := &CapacityTestOptions{} _, err := dsd.Load(data.CompileData(), opts) if err != nil { capacityTestRunning.UnSet() return nil, terminal.ErrMalformedData.With("failed to parse options: %w", err) } // Check options. if opts.TestVolume > maxCapacityTestVolume { capacityTestRunning.UnSet() return nil, terminal.ErrInvalidOptions.With("maximum volume exceeded") } if opts.MaxTime > maxCapacityTestMaxTime { capacityTestRunning.UnSet() return nil, terminal.ErrInvalidOptions.With("maximum maxtime exceeded") } // Create operation. op := &CapacityTestOp{ opts: opts, recvQueue: make(chan *terminal.Msg, 1000), dataSent: new(int64), dataSentWasAckd: abool.New(), result: make(chan *terminal.Error, 1), } op.InitOperationBase(t, opID) // Start handler and sender. op.senderStarted = true module.mgr.Go("op capacity handler", op.handler) module.mgr.Go("op capacity sender", op.sender) return op, nil } func (op *CapacityTestOp) handler(ctx *mgr.WorkerCtx) error { defer capacityTestRunning.UnSet() returnErr := terminal.ErrStopping defer func() { // Linters don't get that returnErr is used when directly used as defer. op.Stop(op, returnErr) }() var maxTestTimeReached <-chan time.Time opTimeout := time.After(capacityTestTimeout) // Setup unit handling var msg *terminal.Msg defer msg.Finish() // Handle receives. for { msg.Finish() select { case <-ctx.Done(): returnErr = terminal.ErrCanceled return nil case <-opTimeout: returnErr = terminal.ErrTimeout return nil case <-maxTestTimeReached: returnErr = op.reportMeasuredCapacity() return nil case msg = <-op.recvQueue: // Record start time and start sender. if !op.started { op.started = true op.startTime = time.Now() maxTestTimeReached = time.After(op.opts.MaxTime) if !op.senderStarted { op.senderStarted = true module.mgr.Go("op capacity sender", op.sender) } } // Add to received data counter. op.dataReceived += msg.Data.Length() // Check if we received the data received signal. if msg.Data.Length() == len(capacityTestDataReceivedSignal) && bytes.Equal(msg.Data.CompileData(), capacityTestDataReceivedSignal) { op.dataSentWasAckd.Set() } // Send the data received signal when we received the full test volume. if op.dataReceived >= op.opts.TestVolume && !op.dataReceivedAckWasAckd { tErr := op.Send(op.NewMsg(capacityTestDataReceivedSignal), capacityTestSendTimeout) if tErr != nil { returnErr = tErr.Wrap("failed to send data received signal") return nil } atomic.AddInt64(op.dataSent, int64(len(capacityTestDataReceivedSignal))) op.dataReceivedAckWasAckd = true // Flush last message. op.Flush(10 * time.Second) } // Check if we can complete the test. if op.dataReceivedAckWasAckd && op.dataSentWasAckd.IsSet() { returnErr = op.reportMeasuredCapacity() return nil } } } } func (op *CapacityTestOp) sender(ctx *mgr.WorkerCtx) error { for { // Send next chunk. msg := op.NewMsg(capacityTestSendData) msg.Unit.MakeHighPriority() tErr := op.Send(msg, capacityTestSendTimeout) if tErr != nil { op.Stop(op, tErr.Wrap("failed to send capacity test data")) return nil } // Add to sent data counter and stop sending if sending is complete. if atomic.AddInt64(op.dataSent, int64(len(capacityTestSendData))) >= int64(op.opts.TestVolume) { return nil } // Check if we have received an ack. if op.dataSentWasAckd.IsSet() { return nil } // Check if op has ended. if op.Stopped() { return nil } } } func (op *CapacityTestOp) reportMeasuredCapacity() *terminal.Error { // Calculate lane capacity and set it. timeNeeded := time.Since(op.startTime) if timeNeeded <= 0 { timeNeeded = 1 } duplexBits := float64((int64(op.dataReceived) + atomic.LoadInt64(op.dataSent)) * 8) duplexNSBitRate := duplexBits / float64(timeNeeded) bitRate := (duplexNSBitRate / 2) * float64(time.Second) op.testResult = int(bitRate) // Save the result to the crane. if controller, ok := op.Terminal().(*CraneControllerTerminal); ok { if controller.Crane.ConnectedHub != nil { controller.Crane.ConnectedHub.GetMeasurements().SetCapacity(op.testResult) log.Infof( "docks: measured capacity to %s: %.2f Mbit/s (%.2fMB down / %.2fMB up in %s)", controller.Crane.ConnectedHub, float64(op.testResult)/1000000, float64(op.dataReceived)/1000000, float64(atomic.LoadInt64(op.dataSent))/1000000, timeNeeded, ) return nil } else if controller.Crane.IsMine() { return terminal.ErrInternalError.With("capacity operation was run on %s without a connected hub set", controller.Crane) } } else if !runningTests { return terminal.ErrInternalError.With("capacity operation was run on terminal that is not a crane controller, but %T", op.Terminal()) } return nil } // Deliver delivers a message. func (op *CapacityTestOp) Deliver(msg *terminal.Msg) *terminal.Error { // Optimized delivery with 1s timeout. select { case op.recvQueue <- msg: default: select { case op.recvQueue <- msg: case <-time.After(1 * time.Second): msg.Finish() return terminal.ErrTimeout } } return nil } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. func (op *CapacityTestOp) HandleStop(tErr *terminal.Error) (errorToSend *terminal.Error) { // Return result to waiting routine. select { case op.result <- tErr: default: } // Drain the recvQueue to finish the message units. drain: for { select { case msg := <-op.recvQueue: msg.Finish() default: select { case msg := <-op.recvQueue: msg.Finish() case <-time.After(3 * time.Millisecond): // Give some additional time buffer to drain the queue. break drain } } } // Return error as is. return tErr } // Result returns the result (end error) of the operation. func (op *CapacityTestOp) Result() <-chan *terminal.Error { return op.result } ================================================ FILE: spn/docks/op_capacity_test.go ================================================ package docks import ( "testing" "time" "github.com/safing/portmaster/spn/terminal" ) var ( testCapacityTestVolume = 1_000_000 testCapacitytestMaxTime = 1 * time.Second ) func TestCapacityOp(t *testing.T) { //nolint:paralleltest // Performance test. // Defaults. testCapacityOp(t, &CapacityTestOptions{ TestVolume: testCapacityTestVolume, MaxTime: testCapacitytestMaxTime, testing: true, }) // Hit max time first. testCapacityOp(t, &CapacityTestOptions{ TestVolume: testCapacityTestVolume, MaxTime: 100 * time.Millisecond, testing: true, }) // Hit volume first. testCapacityOp(t, &CapacityTestOptions{ TestVolume: 100_000, MaxTime: testCapacitytestMaxTime, testing: true, }) } func testCapacityOp(t *testing.T, opts *CapacityTestOptions) { t.Helper() var ( capTestDelay = 5 * time.Millisecond capTestQueueSize uint32 = 10 ) // Create test terminal pair. a, b, err := terminal.NewSimpleTestTerminalPair( capTestDelay, int(capTestQueueSize), &terminal.TerminalOpts{ FlowControl: terminal.FlowControlDFQ, FlowControlSize: capTestQueueSize, }, ) if err != nil { t.Fatalf("failed to create test terminal pair: %s", err) } // Grant permission for op on remote terminal and start op. b.GrantPermission(terminal.IsCraneController) op, tErr := NewCapacityTestOp(a, opts) if tErr != nil { t.Fatalf("failed to start op: %s", tErr) } // Wait for result and check error. tErr = <-op.Result() if !tErr.IsOK() { t.Fatalf("op failed: %s", tErr) } t.Logf("measured capacity: %d bit/s", op.testResult) // Calculate expected bandwidth. expectedBitsPerSecond := float64(capacityTestMsgSize*8*int64(capTestQueueSize)) / float64(capTestDelay) * float64(time.Second) t.Logf("expected capacity: %f bit/s", expectedBitsPerSecond) // Check if measured bandwidth is within parameters. if float64(op.testResult) > expectedBitsPerSecond*1.6 { t.Fatal("measured capacity too high") } // TODO: Check if we can raise this to at least 90%. if float64(op.testResult) < expectedBitsPerSecond*0.2 { t.Fatal("measured capacity too low") } } ================================================ FILE: spn/docks/op_expand.go ================================================ package docks import ( "context" "fmt" "sync/atomic" "time" "github.com/tevino/abool" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" ) // ExpandOpType is the type ID of the expand operation. const ExpandOpType string = "expand" var activeExpandOps = new(int64) // ExpandOp is used to expand to another Hub. type ExpandOp struct { terminal.OperationBase opts *terminal.TerminalOpts // ctx is the context of the Terminal. ctx context.Context // cancelCtx cancels ctx. cancelCtx context.CancelFunc dataRelayed *uint64 ended *abool.AtomicBool relayTerminal *ExpansionRelayTerminal // flowControl holds the flow control system. flowControl terminal.FlowControl // deliverProxy is populated with the configured deliver function deliverProxy func(msg *terminal.Msg) *terminal.Error // recvProxy is populated with the configured recv function recvProxy func() <-chan *terminal.Msg // sendProxy is populated with the configured send function sendProxy func(msg *terminal.Msg, timeout time.Duration) } // ExpansionRelayTerminal is a relay used for expansion. type ExpansionRelayTerminal struct { terminal.BareTerminal op *ExpandOp id uint32 crane *Crane abandoning *abool.AtomicBool // flowControl holds the flow control system. flowControl terminal.FlowControl // deliverProxy is populated with the configured deliver function deliverProxy func(msg *terminal.Msg) *terminal.Error // recvProxy is populated with the configured recv function recvProxy func() <-chan *terminal.Msg // sendProxy is populated with the configured send function sendProxy func(msg *terminal.Msg, timeout time.Duration) } // Type returns the type ID. func (op *ExpandOp) Type() string { return ExpandOpType } // ID returns the operation ID. func (t *ExpansionRelayTerminal) ID() uint32 { return t.id } // Ctx returns the operation context. func (op *ExpandOp) Ctx() context.Context { return op.ctx } // Ctx returns the relay terminal context. func (t *ExpansionRelayTerminal) Ctx() context.Context { return t.op.ctx } // Deliver delivers a message to the relay operation. func (op *ExpandOp) Deliver(msg *terminal.Msg) *terminal.Error { return op.deliverProxy(msg) } // Deliver delivers a message to the relay terminal. func (t *ExpansionRelayTerminal) Deliver(msg *terminal.Msg) *terminal.Error { return t.deliverProxy(msg) } // Flush writes all data in the queues. func (op *ExpandOp) Flush(timeout time.Duration) { if op.flowControl != nil { op.flowControl.Flush(timeout) } } // Flush writes all data in the queues. func (t *ExpansionRelayTerminal) Flush(timeout time.Duration) { if t.flowControl != nil { t.flowControl.Flush(timeout) } } func init() { terminal.RegisterOpType(terminal.OperationFactory{ Type: ExpandOpType, Requires: terminal.MayExpand, Start: expand, }) } func expand(t terminal.Terminal, opID uint32, data *container.Container) (terminal.Operation, *terminal.Error) { // Submit metrics. newExpandOp.Inc() // Check if we are running a public hub. if !conf.PublicHub() { return nil, terminal.ErrPermissionDenied.With("expanding is only allowed on public hubs") } // Parse destination hub ID. dstData, err := data.GetNextBlock() if err != nil { return nil, terminal.ErrMalformedData.With("failed to parse destination: %w", err) } // Parse terminal options. opts, tErr := terminal.ParseTerminalOpts(data) if tErr != nil { return nil, tErr.Wrap("failed to parse terminal options") } // Get crane with destination. relayCrane := GetAssignedCrane(string(dstData)) if relayCrane == nil { return nil, terminal.ErrHubUnavailable.With("no crane assigned to %q", string(dstData)) } // TODO: Expand outside of hot path. // Create operation and terminal. op := &ExpandOp{ opts: opts, dataRelayed: new(uint64), ended: abool.New(), relayTerminal: &ExpansionRelayTerminal{ crane: relayCrane, id: relayCrane.getNextTerminalID(), abandoning: abool.New(), }, } op.InitOperationBase(t, opID) op.ctx, op.cancelCtx = context.WithCancel(t.Ctx()) op.relayTerminal.op = op // Create flow control. switch opts.FlowControl { case terminal.FlowControlDFQ: // Operation op.flowControl = terminal.NewDuplexFlowQueue(op.ctx, opts.FlowControlSize, op.submitBackwardUpstream) op.deliverProxy = op.flowControl.Deliver op.recvProxy = op.flowControl.Receive op.sendProxy = op.submitBackwardFlowControl // Relay Terminal op.relayTerminal.flowControl = terminal.NewDuplexFlowQueue(op.ctx, opts.FlowControlSize, op.submitForwardUpstream) op.relayTerminal.deliverProxy = op.relayTerminal.flowControl.Deliver op.relayTerminal.recvProxy = op.relayTerminal.flowControl.Receive op.relayTerminal.sendProxy = op.submitForwardFlowControl case terminal.FlowControlNone: // Operation deliverToOp := make(chan *terminal.Msg, opts.FlowControlSize) op.deliverProxy = terminal.MakeDirectDeliveryDeliverFunc(op.ctx, deliverToOp) op.recvProxy = terminal.MakeDirectDeliveryRecvFunc(deliverToOp) op.sendProxy = op.submitBackwardUpstream // Relay Terminal deliverToRelay := make(chan *terminal.Msg, opts.FlowControlSize) op.relayTerminal.deliverProxy = terminal.MakeDirectDeliveryDeliverFunc(op.ctx, deliverToRelay) op.relayTerminal.recvProxy = terminal.MakeDirectDeliveryRecvFunc(deliverToRelay) op.relayTerminal.sendProxy = op.submitForwardUpstream case terminal.FlowControlDefault: fallthrough default: return nil, terminal.ErrInternalError.With("unknown flow control type %d", opts.FlowControl) } // Establish terminal on destination. newInitData, tErr := opts.Pack() if tErr != nil { return nil, terminal.ErrInternalError.With("failed to re-pack options: %w", err) } tErr = op.relayTerminal.crane.EstablishNewTerminal(op.relayTerminal, newInitData) if tErr != nil { return nil, tErr } // Start workers. module.mgr.Go("expand op forward relay", op.forwardHandler) module.mgr.Go("expand op backward relay", op.backwardHandler) if op.flowControl != nil { op.flowControl.StartWorkers(module.mgr, "expand op") } if op.relayTerminal.flowControl != nil { op.relayTerminal.flowControl.StartWorkers(module.mgr, "expand op terminal") } return op, nil } func (op *ExpandOp) submitForwardFlowControl(msg *terminal.Msg, timeout time.Duration) { err := op.relayTerminal.flowControl.Send(msg, timeout) if err != nil { msg.Finish() op.Stop(op, err.Wrap("failed to submit to forward flow control")) } } func (op *ExpandOp) submitBackwardFlowControl(msg *terminal.Msg, timeout time.Duration) { err := op.flowControl.Send(msg, timeout) if err != nil { msg.Finish() op.Stop(op, err.Wrap("failed to submit to backward flow control")) } } func (op *ExpandOp) submitForwardUpstream(msg *terminal.Msg, timeout time.Duration) { msg.FlowID = op.relayTerminal.id if msg.Unit.IsHighPriority() && op.opts.UsePriorityDataMsgs { msg.Type = terminal.MsgTypePriorityData } else { msg.Type = terminal.MsgTypeData } err := op.relayTerminal.crane.Send(msg, timeout) if err != nil { msg.Finish() op.Stop(op, err.Wrap("failed to submit to forward upstream")) } } func (op *ExpandOp) submitBackwardUpstream(msg *terminal.Msg, timeout time.Duration) { msg.FlowID = op.relayTerminal.id if msg.Unit.IsHighPriority() && op.opts.UsePriorityDataMsgs { msg.Type = terminal.MsgTypePriorityData } else { msg.Type = terminal.MsgTypeData msg.Unit.RemovePriority() } // Note: op.Send() will transform high priority units to priority data msgs. err := op.Send(msg, timeout) if err != nil { msg.Finish() op.Stop(op, err.Wrap("failed to submit to backward upstream")) } } func (op *ExpandOp) forwardHandler(_ *mgr.WorkerCtx) error { // Metrics setup and submitting. atomic.AddInt64(activeExpandOps, 1) started := time.Now() defer func() { atomic.AddInt64(activeExpandOps, -1) expandOpDurationHistogram.UpdateDuration(started) expandOpRelayedDataHistogram.Update(float64(atomic.LoadUint64(op.dataRelayed))) }() for { select { case msg := <-op.recvProxy(): // Debugging: // log.Debugf("spn/testing: forwarding at %s: %s", op.FmtID(), spew.Sdump(c.CompileData())) // Wait for processing slot. msg.Unit.WaitForSlot() // Count relayed data for metrics. atomic.AddUint64(op.dataRelayed, uint64(msg.Data.Length())) // Receive data from the origin and forward it to the relay. op.relayTerminal.sendProxy(msg, 1*time.Minute) case <-op.ctx.Done(): return nil } } } func (op *ExpandOp) backwardHandler(_ *mgr.WorkerCtx) error { for { select { case msg := <-op.relayTerminal.recvProxy(): // Debugging: // log.Debugf("spn/testing: backwarding at %s: %s", op.FmtID(), spew.Sdump(c.CompileData())) // Wait for processing slot. msg.Unit.WaitForSlot() // Count relayed data for metrics. atomic.AddUint64(op.dataRelayed, uint64(msg.Data.Length())) // Receive data from the relay and forward it to the origin. op.sendProxy(msg, 1*time.Minute) case <-op.ctx.Done(): return nil } } } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. func (op *ExpandOp) HandleStop(err *terminal.Error) (errorToSend *terminal.Error) { // Flush all messages before stopping. op.Flush(1 * time.Minute) op.relayTerminal.Flush(1 * time.Minute) // Stop connected workers. op.cancelCtx() // Abandon connected terminal. op.relayTerminal.Abandon(nil) // Add context to error. if err.IsError() { return err.Wrap("relay operation failed with") } return err } // Abandon shuts down the terminal unregistering it from upstream and calling HandleAbandon(). func (t *ExpansionRelayTerminal) Abandon(err *terminal.Error) { if t.abandoning.SetToIf(false, true) { module.mgr.Go("terminal abandon procedure", func(_ *mgr.WorkerCtx) error { t.handleAbandonProcedure(err) return nil }) } } // HandleAbandon gives the terminal the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Abandon() instead. func (t *ExpansionRelayTerminal) HandleAbandon(err *terminal.Error) (errorToSend *terminal.Error) { // Stop the connected relay operation. t.op.Stop(t.op, err) // Add context to error. if err.IsError() { return err.Wrap("relay terminal failed with") } return err } // HandleDestruction gives the terminal the ability to clean up. // The terminal has already fully shut down at this point. // Should never be called directly. Call Abandon() instead. func (t *ExpansionRelayTerminal) HandleDestruction(err *terminal.Error) {} func (t *ExpansionRelayTerminal) handleAbandonProcedure(err *terminal.Error) { // Call operation stop handle function for proper shutdown cleaning up. err = t.HandleAbandon(err) // Flush all messages before stopping. t.Flush(1 * time.Minute) // Send error to the connected Operation, if the error is internal. if !err.IsExternal() { if err == nil { err = terminal.ErrStopping } msg := terminal.NewMsg(err.Pack()) msg.FlowID = t.ID() msg.Type = terminal.MsgTypeStop t.op.submitForwardUpstream(msg, 1*time.Second) } } // FmtID returns the expansion ID hierarchy. func (op *ExpandOp) FmtID() string { return fmt.Sprintf("%s>%d %s#%d", op.Terminal().FmtID(), op.ID(), op.relayTerminal.crane.ID, op.relayTerminal.id) } // FmtID returns the expansion ID hierarchy. func (t *ExpansionRelayTerminal) FmtID() string { return fmt.Sprintf("%s#%d", t.crane.ID, t.id) } ================================================ FILE: spn/docks/op_latency.go ================================================ package docks import ( "bytes" "fmt" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" "github.com/safing/structures/varint" ) const ( // LatencyTestOpType is the type ID of the latency test operation. LatencyTestOpType = "latency" latencyPingRequest = 1 latencyPingResponse = 2 latencyTestNonceSize = 16 latencyTestRuns = 10 ) var ( latencyTestPauseDuration = 1 * time.Second latencyTestOpTimeout = latencyTestRuns * latencyTestPauseDuration * 3 ) // LatencyTestOp is used to measure latency. type LatencyTestOp struct { terminal.OperationBase } // LatencyTestClientOp is the client version of LatencyTestOp. type LatencyTestClientOp struct { LatencyTestOp lastPingSentAt time.Time lastPingNonce []byte measuredLatencies []time.Duration responses chan *terminal.Msg testResult time.Duration result chan *terminal.Error } // Type returns the type ID. func (op *LatencyTestOp) Type() string { return LatencyTestOpType } func init() { terminal.RegisterOpType(terminal.OperationFactory{ Type: LatencyTestOpType, Requires: terminal.IsCraneController, Start: startLatencyTestOp, }) } // NewLatencyTestOp runs a latency test. func NewLatencyTestOp(t terminal.Terminal) (*LatencyTestClientOp, *terminal.Error) { // Create and init. op := &LatencyTestClientOp{ responses: make(chan *terminal.Msg), measuredLatencies: make([]time.Duration, 0, latencyTestRuns), result: make(chan *terminal.Error, 1), } // Make ping request. pingRequest, err := op.createPingRequest() if err != nil { return nil, terminal.ErrInternalError.With("%w", err) } // Send ping. tErr := t.StartOperation(op, pingRequest, 1*time.Second) if tErr != nil { return nil, tErr } // Start handler. module.mgr.Go("op latency handler", op.handler) return op, nil } func (op *LatencyTestClientOp) handler(ctx *mgr.WorkerCtx) error { returnErr := terminal.ErrStopping defer func() { // Linters don't get that returnErr is used when directly used as defer. op.Stop(op, returnErr) }() var nextTest <-chan time.Time opTimeout := time.After(latencyTestOpTimeout) for { select { case <-ctx.Done(): return nil case <-opTimeout: return nil case <-nextTest: // Create ping request msg. pingRequest, err := op.createPingRequest() if err != nil { returnErr = terminal.ErrInternalError.With("%w", err) return nil } msg := op.NewEmptyMsg() msg.Unit.MakeHighPriority() msg.Data = pingRequest // Send it. tErr := op.Send(msg, latencyTestOpTimeout) if tErr != nil { returnErr = tErr.Wrap("failed to send ping request") return nil } op.Flush(1 * time.Second) nextTest = nil case msg := <-op.responses: // Check if the op ended. if msg == nil { return nil } // Handle response tErr := op.handleResponse(msg) if tErr != nil { returnErr = tErr return nil //nolint:nilerr } // Check if we have enough latency tests. if len(op.measuredLatencies) >= latencyTestRuns { returnErr = op.reportMeasuredLatencies() return nil } // Schedule next latency test, if not yet scheduled. if nextTest == nil { nextTest = time.After(latencyTestPauseDuration) } } } } func (op *LatencyTestClientOp) createPingRequest() (*container.Container, error) { // Generate nonce. nonce, err := rng.Bytes(latencyTestNonceSize) if err != nil { return nil, fmt.Errorf("failed to create ping nonce") } // Set client request state. op.lastPingSentAt = time.Now() op.lastPingNonce = nonce return container.New( varint.Pack8(latencyPingRequest), nonce, ), nil } func (op *LatencyTestClientOp) handleResponse(msg *terminal.Msg) *terminal.Error { defer msg.Finish() rType, err := msg.Data.GetNextN8() if err != nil { return terminal.ErrMalformedData.With("failed to get response type: %w", err) } switch rType { case latencyPingResponse: // Check if the ping nonce matches. if !bytes.Equal(op.lastPingNonce, msg.Data.CompileData()) { return terminal.ErrIntegrity.With("ping nonce mismatch") } op.lastPingNonce = nil // Save latency. op.measuredLatencies = append(op.measuredLatencies, time.Since(op.lastPingSentAt)) return nil default: return terminal.ErrIncorrectUsage.With("unknown response type") } } func (op *LatencyTestClientOp) reportMeasuredLatencies() *terminal.Error { // Find lowest value. lowestLatency := time.Hour for _, latency := range op.measuredLatencies { if latency < lowestLatency { lowestLatency = latency } } op.testResult = lowestLatency // Save the result to the crane. if controller, ok := op.Terminal().(*CraneControllerTerminal); ok { if controller.Crane.ConnectedHub != nil { controller.Crane.ConnectedHub.GetMeasurements().SetLatency(op.testResult) log.Infof("spn/docks: measured latency to %s: %s", controller.Crane.ConnectedHub, op.testResult) return nil } else if controller.Crane.IsMine() { return terminal.ErrInternalError.With("latency operation was run on %s without a connected hub set", controller.Crane) } } else if !runningTests { return terminal.ErrInternalError.With("latency operation was run on terminal that is not a crane controller, but %T", op.Terminal()) } return nil } // Deliver delivers a message to the operation. func (op *LatencyTestClientOp) Deliver(msg *terminal.Msg) *terminal.Error { // Optimized delivery with 1s timeout. select { case op.responses <- msg: default: select { case op.responses <- msg: case <-time.After(1 * time.Second): return terminal.ErrTimeout } } return nil } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. func (op *LatencyTestClientOp) HandleStop(tErr *terminal.Error) (errorToSend *terminal.Error) { close(op.responses) select { case op.result <- tErr: default: } return tErr } // Result returns the result (end error) of the operation. func (op *LatencyTestClientOp) Result() <-chan *terminal.Error { return op.result } func startLatencyTestOp(t terminal.Terminal, opID uint32, data *container.Container) (terminal.Operation, *terminal.Error) { // Create operation. op := &LatencyTestOp{} op.InitOperationBase(t, opID) // Handle first request. msg := op.NewEmptyMsg() msg.Data = data tErr := op.Deliver(msg) if tErr != nil { return nil, tErr } return op, nil } // Deliver delivers a message to the operation. func (op *LatencyTestOp) Deliver(msg *terminal.Msg) *terminal.Error { // Get request type. rType, err := msg.Data.GetNextN8() if err != nil { return terminal.ErrMalformedData.With("failed to get response type: %w", err) } switch rType { case latencyPingRequest: // Keep the nonce and just replace the msg type. msg.Data.PrependNumber(latencyPingResponse) msg.Type = terminal.MsgTypeData msg.Unit.ReUse() msg.Unit.MakeHighPriority() // Send response. tErr := op.Send(msg, latencyTestOpTimeout) if tErr != nil { return tErr.Wrap("failed to send ping response") } op.Flush(1 * time.Second) return nil default: return terminal.ErrIncorrectUsage.With("unknown request type") } } ================================================ FILE: spn/docks/op_latency_test.go ================================================ package docks import ( "testing" "time" "github.com/safing/portmaster/spn/terminal" ) func TestLatencyOp(t *testing.T) { t.Parallel() var ( latTestDelay = 10 * time.Millisecond latTestQueueSize uint32 = 10 ) // Reduce waiting time. latencyTestPauseDuration = 100 * time.Millisecond // Create test terminal pair. a, b, err := terminal.NewSimpleTestTerminalPair( latTestDelay, int(latTestQueueSize), &terminal.TerminalOpts{ FlowControl: terminal.FlowControlNone, FlowControlSize: latTestQueueSize, }, ) if err != nil { t.Fatalf("failed to create test terminal pair: %s", err) } // Grant permission for op on remote terminal and start op. b.GrantPermission(terminal.IsCraneController) op, tErr := NewLatencyTestOp(a) if tErr != nil { t.Fatalf("failed to start op: %s", tErr) } // Wait for result and check error. tErr = <-op.Result() if tErr.IsError() { t.Fatalf("op failed: %s", tErr) } t.Logf("measured latency: %f ms", float64(op.testResult)/float64(time.Millisecond)) // Calculate expected latency. expectedLatency := float64(latTestDelay * 2) t.Logf("expected latency: %f ms", expectedLatency/float64(time.Millisecond)) // Check if measured latency is within parameters. if float64(op.testResult) > expectedLatency*1.2 { t.Fatal("measured latency too high") } if float64(op.testResult) < expectedLatency*0.9 { t.Fatal("measured latency too low") } } ================================================ FILE: spn/docks/op_sync_state.go ================================================ package docks import ( "context" "time" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" "github.com/safing/structures/dsd" ) // SyncStateOpType is the type ID of the sync state operation. const SyncStateOpType = "sync/state" // SyncStateOp is used to sync the crane state. type SyncStateOp struct { terminal.OneOffOperationBase } // SyncStateMessage holds the sync data. type SyncStateMessage struct { Stopping bool RequestStopping bool } // Type returns the type ID. func (op *SyncStateOp) Type() string { return SyncStateOpType } func init() { terminal.RegisterOpType(terminal.OperationFactory{ Type: SyncStateOpType, Requires: terminal.IsCraneController, Start: runSyncStateOp, }) } // startSyncStateOp starts a worker that runs the sync state operation. func (crane *Crane) startSyncStateOp() { module.mgr.Go("sync crane state", func(wc *mgr.WorkerCtx) error { tErr := crane.Controller.SyncState(wc.Ctx()) if tErr != nil { return tErr } return nil }) } // SyncState runs a sync state operation. func (controller *CraneControllerTerminal) SyncState(ctx context.Context) *terminal.Error { // Check if we are a public Hub, whether we own the crane and whether the lane is public too. if !conf.PublicHub() || !controller.Crane.Public() { return nil } // Create and init. op := &SyncStateOp{} op.Init() // Get optimization states. requestStopping := false func() { controller.Crane.NetState.lock.Lock() defer controller.Crane.NetState.lock.Unlock() requestStopping = controller.Crane.NetState.stoppingRequested }() // Create sync message. msg := &SyncStateMessage{ Stopping: controller.Crane.stopping.IsSet(), RequestStopping: requestStopping, } data, err := dsd.Dump(msg, dsd.CBOR) if err != nil { return terminal.ErrInternalError.With("%w", err) } // Send message. tErr := controller.StartOperation(op, container.New(data), 30*time.Second) if tErr != nil { return tErr } // Wait for reply select { case tErr = <-op.Result: if tErr.IsError() { return tErr } return nil case <-ctx.Done(): return nil case <-time.After(1 * time.Minute): return terminal.ErrTimeout.With("timed out while waiting for sync crane result") } } func runSyncStateOp(t terminal.Terminal, opID uint32, data *container.Container) (terminal.Operation, *terminal.Error) { // Check if we are a on a crane controller. var ok bool var controller *CraneControllerTerminal if controller, ok = t.(*CraneControllerTerminal); !ok { return nil, terminal.ErrIncorrectUsage.With("can only be used with a crane controller") } // Check if we are a public Hub and whether the lane is public too. if !conf.PublicHub() || !controller.Crane.Public() { return nil, terminal.ErrPermissionDenied.With("only public lanes can sync crane status") } // Load message. syncState := &SyncStateMessage{} _, err := dsd.Load(data.CompileData(), syncState) if err != nil { return nil, terminal.ErrMalformedData.With("failed to load sync state message: %w", err) } // Apply optimization state. controller.Crane.NetState.lock.Lock() defer controller.Crane.NetState.lock.Unlock() controller.Crane.NetState.stoppingRequestedByPeer = syncState.RequestStopping // Apply crane state only when we don't own the crane. if !controller.Crane.IsMine() { // Apply sync state. var changed bool if syncState.Stopping { if controller.Crane.stopping.SetToIf(false, true) { controller.Crane.NetState.markedStoppingAt = time.Now() changed = true } } else { if controller.Crane.stopping.SetToIf(true, false) { controller.Crane.NetState.markedStoppingAt = time.Time{} changed = true } } // Notify of change. if changed { controller.Crane.NotifyUpdate() } } return nil, nil } ================================================ FILE: spn/docks/op_whoami.go ================================================ package docks import ( "time" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" "github.com/safing/structures/dsd" ) const ( // WhoAmIType is the type ID of the latency test operation. WhoAmIType = "whoami" whoAmITimeout = 3 * time.Second ) // WhoAmIOp is used to request some metadata about the other side. type WhoAmIOp struct { terminal.OneOffOperationBase response *WhoAmIResponse } // WhoAmIResponse is a whoami response. type WhoAmIResponse struct { // Timestamp in nanoseconds Timestamp int64 `cbor:"t,omitempty" json:"t,omitempty"` // Addr is the remote address as reported by the crane terminal (IP and port). Addr string `cbor:"a,omitempty" json:"a,omitempty"` } // Type returns the type ID. func (op *WhoAmIOp) Type() string { return WhoAmIType } func init() { terminal.RegisterOpType(terminal.OperationFactory{ Type: WhoAmIType, Start: startWhoAmI, }) } // WhoAmI executes a whoami operation and returns the response. func WhoAmI(t terminal.Terminal) (*WhoAmIResponse, *terminal.Error) { whoami, err := NewWhoAmIOp(t) if err.IsError() { return nil, err } // Wait for response. select { case tErr := <-whoami.Result: if tErr.IsError() { return nil, tErr } return whoami.response, nil case <-time.After(whoAmITimeout * 2): return nil, terminal.ErrTimeout } } // NewWhoAmIOp starts a new whoami operation. func NewWhoAmIOp(t terminal.Terminal) (*WhoAmIOp, *terminal.Error) { // Create operation and init. op := &WhoAmIOp{} op.OneOffOperationBase.Init() // Send ping. tErr := t.StartOperation(op, nil, whoAmITimeout) if tErr != nil { return nil, tErr } return op, nil } // Deliver delivers a message to the operation. func (op *WhoAmIOp) Deliver(msg *terminal.Msg) *terminal.Error { defer msg.Finish() // Parse response. response := &WhoAmIResponse{} _, err := dsd.Load(msg.Data.CompileData(), response) if err != nil { return terminal.ErrMalformedData.With("failed to parse ping response: %w", err) } op.response = response return terminal.ErrExplicitAck } func startWhoAmI(t terminal.Terminal, opID uint32, data *container.Container) (terminal.Operation, *terminal.Error) { // Get crane terminal, if available. ct, _ := t.(*CraneTerminal) // Create response. r := &WhoAmIResponse{ Timestamp: time.Now().UnixNano(), } if ct != nil { r.Addr = ct.RemoteAddr().String() } response, err := dsd.Dump(r, dsd.CBOR) if err != nil { return nil, terminal.ErrInternalError.With("failed to create whoami response: %w", err) } // Send response. msg := terminal.NewMsg(response) msg.FlowID = opID msg.Unit.MakeHighPriority() if terminal.UsePriorityDataMsgs { msg.Type = terminal.MsgTypePriorityData } tErr := t.Send(msg, whoAmITimeout) if tErr != nil { // Finish message unit on failure. msg.Finish() return nil, tErr.With("failed to send ping response") } // Operation is just one response and finished successfully. return nil, nil } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. func (op *WhoAmIOp) HandleStop(err *terminal.Error) (errorToSend *terminal.Error) { // Continue with usual handling of inherited base. return op.OneOffOperationBase.HandleStop(err) } ================================================ FILE: spn/docks/op_whoami_test.go ================================================ package docks import ( "testing" "github.com/safing/portmaster/spn/terminal" ) func TestWhoAmIOp(t *testing.T) { t.Parallel() // Create test terminal pair. a, _, err := terminal.NewSimpleTestTerminalPair(0, 0, nil) if err != nil { t.Fatalf("failed to create test terminal pair: %s", err) } // Run op. resp, tErr := WhoAmI(a) if tErr.IsError() { t.Fatal(tErr) } t.Logf("whoami: %+v", resp) } ================================================ FILE: spn/docks/terminal_expansion.go ================================================ package docks import ( "fmt" "sync" "time" "github.com/tevino/abool" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/terminal" "github.com/safing/structures/container" ) // ExpansionTerminal is used for expanding to another Hub. type ExpansionTerminal struct { *terminal.TerminalBase relayOp *ExpansionTerminalRelayOp changeNotifyFuncReady *abool.AtomicBool changeNotifyFunc func() reachableChecked time.Time reachableLock sync.Mutex } // ExpansionTerminalRelayOp is the operation that connects to the relay. type ExpansionTerminalRelayOp struct { terminal.OperationBase expansionTerminal *ExpansionTerminal } // Type returns the type ID. func (op *ExpansionTerminalRelayOp) Type() string { return ExpandOpType } // ExpandTo initiates an expansion. func ExpandTo(from terminal.Terminal, routeTo string, encryptFor *hub.Hub) (*ExpansionTerminal, *terminal.Error) { // First, create the local endpoint terminal to generate the init data. // Create options and bare expansion terminal. opts := terminal.DefaultExpansionTerminalOpts() opts.Encrypt = encryptFor != nil expansion := &ExpansionTerminal{ changeNotifyFuncReady: abool.New(), } expansion.relayOp = &ExpansionTerminalRelayOp{ expansionTerminal: expansion, } // Create base terminal for expansion. base, initData, tErr := terminal.NewLocalBaseTerminal( module.mgr.Ctx(), 0, // Ignore; The ID of the operation is used for communication. from.FmtID(), encryptFor, opts, expansion.relayOp, ) if tErr != nil { return nil, tErr.Wrap("failed to create expansion terminal base") } expansion.TerminalBase = base base.SetTerminalExtension(expansion) base.SetTimeout(defaultTerminalIdleTimeout) // Second, start the actual relay operation. // Create setup message for relay operation. opInitData := container.New() opInitData.AppendAsBlock([]byte(routeTo)) opInitData.AppendContainer(initData) // Start relay operation on connected Hub. tErr = from.StartOperation(expansion.relayOp, opInitData, 5*time.Second) if tErr != nil { return nil, tErr.Wrap("failed to start expansion operation") } // Start Workers. base.StartWorkers(module.mgr, "expansion terminal") return expansion, nil } // SetChangeNotifyFunc sets a callback function that is called when the terminal state changes. func (t *ExpansionTerminal) SetChangeNotifyFunc(f func()) { if t.changeNotifyFuncReady.IsSet() { return } t.changeNotifyFunc = f t.changeNotifyFuncReady.Set() } // NeedsReachableCheck returns whether the terminal should be checked if it is // reachable via the existing network internal relayed connection. func (t *ExpansionTerminal) NeedsReachableCheck(maxCheckAge time.Duration) bool { t.reachableLock.Lock() defer t.reachableLock.Unlock() return time.Since(t.reachableChecked) > maxCheckAge } // MarkReachable marks the terminal as reachable via the existing network // internal relayed connection. func (t *ExpansionTerminal) MarkReachable() { t.reachableLock.Lock() defer t.reachableLock.Unlock() t.reachableChecked = time.Now() } // HandleDestruction gives the terminal the ability to clean up. // The terminal has already fully shut down at this point. // Should never be called directly. Call Abandon() instead. func (t *ExpansionTerminal) HandleDestruction(err *terminal.Error) { // Trigger update of connected Pin. if t.changeNotifyFuncReady.IsSet() { t.changeNotifyFunc() } // Stop the relay operation. // The error message is arlready sent by the terminal. t.relayOp.Stop(t.relayOp, nil) } // CustomIDFormat formats the terminal ID. func (t *ExpansionTerminal) CustomIDFormat() string { return fmt.Sprintf("%s~%d", t.relayOp.Terminal().FmtID(), t.relayOp.ID()) } // Deliver delivers a message to the operation. func (op *ExpansionTerminalRelayOp) Deliver(msg *terminal.Msg) *terminal.Error { // Proxy directly to expansion terminal. return op.expansionTerminal.Deliver(msg) } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. func (op *ExpansionTerminalRelayOp) HandleStop(err *terminal.Error) (errorToSend *terminal.Error) { // Stop the expansion terminal. // The error message will be sent by the operation. op.expansionTerminal.Abandon(nil) return err } ================================================ FILE: spn/docks/terminal_expansion_test.go ================================================ package docks import ( "context" "fmt" "os" "runtime/pprof" "sync" "testing" "time" "github.com/safing/portmaster/spn/access" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/hub" "github.com/safing/portmaster/spn/ships" "github.com/safing/portmaster/spn/terminal" ) const defaultTestQueueSize = 200 func TestExpansion(t *testing.T) { t.Parallel() // Test without and with encryption. for _, encrypt := range []bool{false, true} { // Test down/up separately and in parallel. for _, parallel := range []bool{false, true} { // Test with different flow controls. for _, fc := range []struct { flowControl terminal.FlowControlType flowControlSize uint32 }{ { flowControl: terminal.FlowControlNone, flowControlSize: 5, }, { flowControl: terminal.FlowControlDFQ, flowControlSize: defaultTestQueueSize, }, } { // Run tests with combined options. testExpansion( t, "expansion-hop-test", &terminal.TerminalOpts{ Encrypt: encrypt, Padding: 8, FlowControl: fc.flowControl, FlowControlSize: fc.flowControlSize, }, defaultTestQueueSize, defaultTestQueueSize, parallel, ) } } } stressTestOpts := &terminal.TerminalOpts{ Encrypt: true, Padding: 8, FlowControl: terminal.FlowControlDFQ, FlowControlSize: defaultTestQueueSize, } testExpansion(t, "expansion-stress-test-down", stressTestOpts, defaultTestQueueSize*100, 0, false) testExpansion(t, "expansion-stress-test-up", stressTestOpts, 0, defaultTestQueueSize*100, false) testExpansion(t, "expansion-stress-test-duplex", stressTestOpts, defaultTestQueueSize*100, defaultTestQueueSize*100, false) } func testExpansion( //nolint:maintidx,thelper t *testing.T, testID string, terminalOpts *terminal.TerminalOpts, clientCountTo, serverCountTo uint64, inParallel bool, ) { testID += fmt.Sprintf(":encrypt=%v,flowCtrl=%d,parallel=%v", terminalOpts.Encrypt, terminalOpts.FlowControl, inParallel) var identity2, identity3, identity4 *cabin.Identity var connectedHub2, connectedHub3, connectedHub4 *hub.Hub if terminalOpts.Encrypt { identity2, connectedHub2 = getTestIdentity(t) identity3, connectedHub3 = getTestIdentity(t) identity4, connectedHub4 = getTestIdentity(t) } // Build ships and cranes. optimalMinLoadSize = 100 ship1to2 := ships.NewTestShip(!terminalOpts.Encrypt, 100) ship2to3 := ships.NewTestShip(!terminalOpts.Encrypt, 100) ship3to4 := ships.NewTestShip(!terminalOpts.Encrypt, 100) var crane1, crane2to1, crane2to3, crane3to2, crane3to4, crane4 *Crane var craneWg sync.WaitGroup craneWg.Add(6) craneCtx, cancelCraneCtx := context.WithCancel(context.Background()) defer cancelCraneCtx() go func() { var err error crane1, err = NewCrane(ship1to2, connectedHub2, nil) if err != nil { panic(fmt.Sprintf("expansion test %s could not create crane1: %s", testID, err)) } crane1.ID = "c1" err = crane1.Start(craneCtx) if err != nil { panic(fmt.Sprintf("expansion test %s could not start crane1: %s", testID, err)) } crane1.ship.MarkPublic() craneWg.Done() }() go func() { var err error crane2to1, err = NewCrane(ship1to2.Reverse(), nil, identity2) if err != nil { panic(fmt.Sprintf("expansion test %s could not create crane2to1: %s", testID, err)) } crane2to1.ID = "c2to1" err = crane2to1.Start(craneCtx) if err != nil { panic(fmt.Sprintf("expansion test %s could not start crane2to1: %s", testID, err)) } crane2to1.ship.MarkPublic() craneWg.Done() }() go func() { var err error crane2to3, err = NewCrane(ship2to3, connectedHub3, nil) if err != nil { panic(fmt.Sprintf("expansion test %s could not create crane2to3: %s", testID, err)) } crane2to3.ID = "c2to3" err = crane2to3.Start(craneCtx) if err != nil { panic(fmt.Sprintf("expansion test %s could not start crane2to3: %s", testID, err)) } crane2to3.ship.MarkPublic() craneWg.Done() }() go func() { var err error crane3to2, err = NewCrane(ship2to3.Reverse(), nil, identity3) if err != nil { panic(fmt.Sprintf("expansion test %s could not create crane3to2: %s", testID, err)) } crane3to2.ID = "c3to2" err = crane3to2.Start(craneCtx) if err != nil { panic(fmt.Sprintf("expansion test %s could not start crane3to2: %s", testID, err)) } crane3to2.ship.MarkPublic() craneWg.Done() }() go func() { var err error crane3to4, err = NewCrane(ship3to4, connectedHub4, nil) if err != nil { panic(fmt.Sprintf("expansion test %s could not create crane3to4: %s", testID, err)) } crane3to4.ID = "c3to4" err = crane3to4.Start(craneCtx) if err != nil { panic(fmt.Sprintf("expansion test %s could not start crane3to4: %s", testID, err)) } crane3to4.ship.MarkPublic() craneWg.Done() }() go func() { var err error crane4, err = NewCrane(ship3to4.Reverse(), nil, identity4) if err != nil { panic(fmt.Sprintf("expansion test %s could not create crane4: %s", testID, err)) } crane4.ID = "c4" err = crane4.Start(craneCtx) if err != nil { panic(fmt.Sprintf("expansion test %s could not start crane4: %s", testID, err)) } crane4.ship.MarkPublic() craneWg.Done() }() craneWg.Wait() // Assign cranes. crane3HubID := testID + "-crane3HubID" AssignCrane(crane3HubID, crane2to3) crane4HubID := testID + "-crane4HubID" AssignCrane(crane4HubID, crane3to4) t.Logf("expansion test %s: initial setup complete", testID) // Wait async for test to complete, print stack after timeout. finished := make(chan struct{}) go func() { select { case <-finished: case <-time.After(30 * time.Second): fmt.Printf("expansion test %s is taking too long, print stack:\n", testID) _ = pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) os.Exit(1) } }() // Start initial crane. homeTerminal, initData, tErr := NewLocalCraneTerminal(crane1, nil, &terminal.TerminalOpts{}) if tErr != nil { t.Fatalf("expansion test %s failed to create home terminal: %s", testID, tErr) } tErr = crane1.EstablishNewTerminal(homeTerminal, initData) if tErr != nil { t.Fatalf("expansion test %s failed to connect home terminal: %s", testID, tErr) } t.Logf("expansion test %s: home terminal setup complete", testID) time.Sleep(100 * time.Millisecond) // Start counters for testing. op0, tErr := terminal.NewCounterOp(homeTerminal, terminal.CounterOpts{ ClientCountTo: clientCountTo, ServerCountTo: serverCountTo, }) if tErr != nil { t.Fatalf("expansion test %s failed to run counter op: %s", testID, tErr) } t.Logf("expansion test %s: home terminal counter setup complete", testID) if !inParallel { op0.Wait() } // Start expansion to crane 3. opAuthTo2, tErr := access.AuthorizeToTerminal(homeTerminal) if tErr != nil { t.Fatalf("expansion test %s failed to auth with home terminal: %s", testID, tErr) } tErr = <-opAuthTo2.Result if tErr.IsError() { t.Fatalf("expansion test %s failed to auth with home terminal: %s", testID, tErr) } expansionTerminalTo3, err := ExpandTo(homeTerminal, crane3HubID, connectedHub3) if err != nil { t.Fatalf("expansion test %s failed to expand to %s: %s", testID, crane3HubID, tErr) } // Start counters for testing. op1, tErr := terminal.NewCounterOp(expansionTerminalTo3, terminal.CounterOpts{ ClientCountTo: clientCountTo, ServerCountTo: serverCountTo, }) if tErr != nil { t.Fatalf("expansion test %s failed to run counter op: %s", testID, tErr) } t.Logf("expansion test %s: expansion to crane3 and counter setup complete", testID) if !inParallel { op1.Wait() } // Start expansion to crane 4. opAuthTo3, tErr := access.AuthorizeToTerminal(expansionTerminalTo3) if tErr != nil { t.Fatalf("expansion test %s failed to auth with extenstion terminal: %s", testID, tErr) } tErr = <-opAuthTo3.Result if tErr.IsError() { t.Fatalf("expansion test %s failed to auth with extenstion terminal: %s", testID, tErr) } expansionTerminalTo4, err := ExpandTo(expansionTerminalTo3, crane4HubID, connectedHub4) if err != nil { t.Fatalf("expansion test %s failed to expand to %s: %s", testID, crane4HubID, tErr) } // Start counters for testing. op2, tErr := terminal.NewCounterOp(expansionTerminalTo4, terminal.CounterOpts{ ClientCountTo: clientCountTo, ServerCountTo: serverCountTo, }) if tErr != nil { t.Fatalf("expansion test %s failed to run counter op: %s", testID, tErr) } t.Logf("expansion test %s: expansion to crane4 and counter setup complete", testID) op2.Wait() // Wait for op1 if not already. if inParallel { op0.Wait() op1.Wait() } // Signal completion. close(finished) // Wait a little so that all errors can be propagated, so we can truly see // if we succeeded. time.Sleep(100 * time.Millisecond) // Check errors. if op1.Error != nil { t.Fatalf("crane test %s counter op1 failed: %s", testID, op1.Error) } if op2.Error != nil { t.Fatalf("crane test %s counter op2 failed: %s", testID, op2.Error) } } ================================================ FILE: spn/hub/database.go ================================================ package hub import ( "errors" "fmt" "sync" "time" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" ) var ( db = database.NewInterface(&database.Options{ Local: true, Internal: true, }) getFromNavigator func(mapName, hubID string) *Hub ) // MakeHubDBKey makes a hub db key. func MakeHubDBKey(mapName, hubID string) string { return fmt.Sprintf("cache:spn/hubs/%s/%s", mapName, hubID) } // MakeHubMsgDBKey makes a hub msg db key. func MakeHubMsgDBKey(mapName string, msgType MsgType, hubID string) string { return fmt.Sprintf("cache:spn/msgs/%s/%s/%s", mapName, msgType, hubID) } // SetNavigatorAccess sets a shortcut function to access hubs from the navigator instead of having go through the database. // This also reduces the number of object in RAM and better caches parsed attributes. func SetNavigatorAccess(fn func(mapName, hubID string) *Hub) { if getFromNavigator == nil { getFromNavigator = fn } } // GetHub get a Hub from the database - or the navigator, if configured. func GetHub(mapName string, hubID string) (*Hub, error) { if getFromNavigator != nil { hub := getFromNavigator(mapName, hubID) if hub != nil { return hub, nil } } return GetHubByKey(MakeHubDBKey(mapName, hubID)) } // GetHubByKey returns a hub by its raw DB key. func GetHubByKey(key string) (*Hub, error) { r, err := db.Get(key) if err != nil { return nil, err } hub, err := EnsureHub(r) if err != nil { return nil, err } return hub, nil } // EnsureHub makes sure a database record is a Hub. func EnsureHub(r record.Record) (*Hub, error) { // unwrap if r.IsWrapped() { // only allocate a new struct, if we need it newHub := &Hub{} err := record.Unwrap(r, newHub) if err != nil { return nil, err } newHub = prepHub(newHub) // Fully validate when getting from database. if err := newHub.Info.validateFormatting(); err != nil { return nil, fmt.Errorf("announcement failed format validation: %w", err) } if err := newHub.Status.validateFormatting(); err != nil { return nil, fmt.Errorf("status failed format validation: %w", err) } if err := newHub.Info.prepare(false); err != nil { return nil, fmt.Errorf("failed to prepare announcement: %w", err) } return newHub, nil } // or adjust type newHub, ok := r.(*Hub) if !ok { return nil, fmt.Errorf("record not of type *Hub, but %T", r) } newHub = prepHub(newHub) // Prepare only when already parsed. if err := newHub.Info.prepare(false); err != nil { return nil, fmt.Errorf("failed to prepare announcement: %w", err) } // ensure status return newHub, nil } func prepHub(h *Hub) *Hub { if h.Status == nil { h.Status = &Status{} } h.Measurements = getSharedMeasurements(h.ID, h.Measurements) return h } // Save saves to Hub to the correct scope in the database. func (h *Hub) Save() error { if !h.KeyIsSet() { h.SetKey(MakeHubDBKey(h.Map, h.ID)) } return db.Put(h) } // RemoveHubAndMsgs deletes a Hub and it's saved messages from the database. func RemoveHubAndMsgs(mapName string, hubID string) (err error) { err = db.Delete(MakeHubDBKey(mapName, hubID)) if err != nil && !errors.Is(err, database.ErrNotFound) { return fmt.Errorf("failed to delete main hub entry: %w", err) } err = db.Delete(MakeHubMsgDBKey(mapName, MsgTypeAnnouncement, hubID)) if err != nil && !errors.Is(err, database.ErrNotFound) { return fmt.Errorf("failed to delete hub announcement data: %w", err) } err = db.Delete(MakeHubMsgDBKey(mapName, MsgTypeStatus, hubID)) if err != nil && !errors.Is(err, database.ErrNotFound) { return fmt.Errorf("failed to delete hub status data: %w", err) } return nil } // HubMsg stores raw Hub messages. type HubMsg struct { //nolint:golint record.Base sync.Mutex ID string Map string Type MsgType Data []byte Received int64 } // SaveHubMsg saves a raw (and signed) message received by another Hub. func SaveHubMsg(id string, mapName string, msgType MsgType, data []byte) error { // create wrapper record msg := &HubMsg{ ID: id, Map: mapName, Type: msgType, Data: data, Received: time.Now().Unix(), } // set key msg.SetKey(MakeHubMsgDBKey(msg.Map, msg.Type, msg.ID)) // save return db.PutNew(msg) } // QueryRawGossipMsgs queries the database for raw gossip messages. func QueryRawGossipMsgs(mapName string, msgType MsgType) (it *iterator.Iterator, err error) { it, err = db.Query(query.New(MakeHubMsgDBKey(mapName, msgType, ""))) return } // EnsureHubMsg makes sure a database record is a HubMsg. func EnsureHubMsg(r record.Record) (*HubMsg, error) { // unwrap if r.IsWrapped() { // only allocate a new struct, if we need it newHubMsg := &HubMsg{} err := record.Unwrap(r, newHubMsg) if err != nil { return nil, err } return newHubMsg, nil } // or adjust type newHubMsg, ok := r.(*HubMsg) if !ok { return nil, fmt.Errorf("record not of type *Hub, but %T", r) } return newHubMsg, nil } ================================================ FILE: spn/hub/errors.go ================================================ package hub import "errors" var ( // ErrMissingInfo signifies that the hub is missing the HubAnnouncement. ErrMissingInfo = errors.New("hub has no announcement") // ErrMissingTransports signifies that the hub announcement did not specify any transports. ErrMissingTransports = errors.New("hub announcement has no transports") // ErrMissingIPs signifies that the hub announcement did not specify any IPs, // or none of the IPs is supported by the client. ErrMissingIPs = errors.New("hub announcement has no (supported) IPs") // ErrTemporaryValidationError is returned when a validation error might be temporary. ErrTemporaryValidationError = errors.New("temporary validation error") // ErrOldData is returned when received data is outdated. ErrOldData = errors.New("") ) ================================================ FILE: spn/hub/format.go ================================================ package hub import ( "fmt" "net" "regexp" "github.com/safing/portmaster/service/network/netutils" ) // BaselineCharset defines the permitted characters. var BaselineCharset = regexp.MustCompile( // Start of charset selection. `^[` + // Printable ASCII (character code 32-127), excluding common control characters of different languages: "$%&';<>\` and DELETE. ` !#()*+,\-\./0-9:=?@A-Z[\]^_a-z{|}~` + // Only latin characters from extended ASCII (character code 128-255). `ŠŒŽšœžŸ¡¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ` + // End of charset selection. `]*$`, ) func checkStringFormat(fieldName, value string, maxLength int) error { switch { case len(value) > maxLength: return fmt.Errorf("field %s with length of %d exceeds max length of %d", fieldName, len(value), maxLength) case !BaselineCharset.MatchString(value): return fmt.Errorf("field %s contains characters not permitted by baseline validation", fieldName) default: return nil } } func checkStringSliceFormat(fieldName string, value []string, maxLength, maxStringLength int) error { //nolint:unparam if len(value) > maxLength { return fmt.Errorf("field %s with array/slice length of %d exceeds max length of %d", fieldName, len(value), maxLength) } for _, s := range value { if err := checkStringFormat(fieldName, s, maxStringLength); err != nil { return err } } return nil } func checkByteSliceFormat(fieldName string, value []byte, maxLength int) error { switch { case len(value) > maxLength: return fmt.Errorf("field %s with length of %d exceeds max length of %d", fieldName, len(value), maxLength) default: return nil } } func checkIPFormat(fieldName string, value net.IP) error { // Check if there is an IP address. if value == nil { return nil } switch { case len(value) != 4 && len(value) != 16: return fmt.Errorf("field %s has an invalid length of %d for an IP address", fieldName, len(value)) case netutils.GetIPScope(value) == netutils.Invalid: return fmt.Errorf("field %s holds an invalid IP address: %s", fieldName, value) default: return nil } } ================================================ FILE: spn/hub/format_test.go ================================================ package hub import ( "fmt" "net" "testing" "github.com/stretchr/testify/require" ) func TestCheckStringFormat(t *testing.T) { t.Parallel() testSet := map[string]bool{ // Printable ASCII (character code 32-127) " ": true, "!": true, `"`: false, "#": true, "$": false, "%": false, "&": false, "'": false, "(": true, ")": true, "*": true, "+": true, ",": true, "-": true, ".": true, "/": true, "0": true, "1": true, "2": true, "3": true, "4": true, "5": true, "6": true, "7": true, "8": true, "9": true, ":": true, ";": false, "<": false, "=": true, ">": false, "?": true, "@": true, "A": true, "B": true, "C": true, "D": true, "E": true, "F": true, "G": true, "H": true, "I": true, "J": true, "K": true, "L": true, "M": true, "N": true, "O": true, "P": true, "Q": true, "R": true, "S": true, "T": true, "U": true, "V": true, "W": true, "X": true, "Y": true, "Z": true, "[": true, `\`: false, "]": true, "^": true, "_": true, "`": false, "a": true, "b": true, "c": true, "d": true, "e": true, "f": true, "g": true, "h": true, "i": true, "j": true, "k": true, "l": true, "m": true, "n": true, "o": true, "p": true, "q": true, "r": true, "s": true, "t": true, "u": true, "v": true, "w": true, "x": true, "y": true, "z": true, "{": true, "|": true, "}": true, "~": true, // Not testing for DELETE character. // Extended ASCII (character code 128-255) "€": false, "‚": false, "ƒ": false, "„": false, "…": false, "†": false, "‡": false, "ˆ": false, "‰": false, "Š": true, "‹": false, "Œ": true, "Ž": true, "‘": false, "’": false, "“": false, "”": false, "•": false, "–": false, "—": false, "˜": false, "™": false, "š": true, "›": false, "œ": true, "ž": true, "Ÿ": true, "¡": true, "¢": false, "£": false, "¤": false, "¥": false, "¦": false, "§": false, "¨": false, "©": false, "ª": false, "«": false, "¬": false, "®": false, "¯": false, "°": false, "±": false, "²": false, "³": false, "´": false, "µ": false, "¶": false, "·": false, "¸": false, "¹": false, "º": false, "»": false, "¼": false, "½": false, "¾": false, "¿": true, "À": true, "Á": true, "Â": true, "Ã": true, "Ä": true, "Å": true, "Æ": true, "Ç": true, "È": true, "É": true, "Ê": true, "Ë": true, "Ì": true, "Í": true, "Î": true, "Ï": true, "Ð": true, "Ñ": true, "Ò": true, "Ó": true, "Ô": true, "Õ": true, "Ö": true, "×": false, "Ø": true, "Ù": true, "Ú": true, "Û": true, "Ü": true, "Ý": true, "Þ": true, "ß": true, "à": true, "á": true, "â": true, "ã": true, "ä": true, "å": true, "æ": true, "ç": true, "è": true, "é": true, "ê": true, "ë": true, "ì": true, "í": true, "î": true, "ï": true, "ð": true, "ñ": true, "ò": true, "ó": true, "ô": true, "õ": true, "ö": true, "÷": false, "ø": true, "ù": true, "ú": true, "û": true, "ü": true, "ý": true, "þ": true, "ÿ": true, } for testCharacter, isPermitted := range testSet { if isPermitted { require.NoError(t, checkStringFormat(fmt.Sprintf("test character %q", testCharacter), testCharacter, 3)) } else { require.Error(t, checkStringFormat(fmt.Sprintf("test character %q", testCharacter), testCharacter, 3)) } } } func TestCheckIPFormat(t *testing.T) { t.Parallel() // IPv4 require.NoError(t, checkIPFormat("test IP 1.1.1.1", net.IPv4(1, 1, 1, 1))) require.NoError(t, checkIPFormat("test IP 192.168.1.1", net.IPv4(192, 168, 1, 1))) require.Error(t, checkIPFormat("test IP 255.0.0.1", net.IPv4(255, 0, 0, 1))) // IPv6 require.NoError(t, checkIPFormat("test IP ::1", net.ParseIP("::1"))) require.NoError(t, checkIPFormat("test IP 2606:4700:4700::1111", net.ParseIP("2606:4700:4700::1111"))) // Invalid require.Error(t, checkIPFormat("test IP with length 3", net.IP([]byte{0, 0, 0}))) require.Error(t, checkIPFormat("test IP with length 5", net.IP([]byte{0, 0, 0, 0, 0}))) require.Error(t, checkIPFormat( "test IP with length 15", net.IP([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}), )) require.Error(t, checkIPFormat( "test IP with length 17", net.IP([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}), )) } ================================================ FILE: spn/hub/hub.go ================================================ package hub import ( "fmt" "net" "sync" "time" "golang.org/x/exp/slices" "github.com/safing/jess" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/profile/endpoints" ) // Scope is the network scope a Hub can be in. type Scope uint8 const ( // ScopeInvalid defines an invalid scope. ScopeInvalid Scope = 0 // ScopeLocal identifies local Hubs. ScopeLocal Scope = 1 // ScopePublic identifies public Hubs. ScopePublic Scope = 2 // ScopeTest identifies Hubs for testing. ScopeTest Scope = 0xFF ) const ( obsoleteValidAfter = 30 * 24 * time.Hour obsoleteInvalidAfter = 7 * 24 * time.Hour ) // MsgType defines the message type. type MsgType string // Message Types. const ( MsgTypeAnnouncement = "announcement" MsgTypeStatus = "status" ) // Hub represents a network node in the SPN. type Hub struct { //nolint:maligned sync.Mutex record.Base ID string PublicKey *jess.Signet Map string Info *Announcement Status *Status Measurements *Measurements measurementsInitialized bool FirstSeen time.Time VerifiedIPs bool InvalidInfo bool InvalidStatus bool } // Announcement is the main message type to publish Hub Information. This only changes if updated manually. type Announcement struct { // Primary Key // hash of public key // must be checked if it matches the public key ID string `cbor:"i"` // via jess.LabeledHash // PublicKey *jess.Signet // PublicKey // if not part of signature // Signature *jess.Letter Timestamp int64 `cbor:"t"` // Unix timestamp in seconds // Node Information Name string `cbor:"n"` // name of the node Group string `cbor:"g,omitempty" json:",omitempty"` // person or organisation, who is in control of the node (should be same for all nodes of this person or organisation) ContactAddress string `cbor:"ca,omitempty" json:",omitempty"` // contact possibility (recommended, but optional) ContactService string `cbor:"cs,omitempty" json:",omitempty"` // type of service of the contact address, if not email // currently unused, but collected for later use Hosters []string `cbor:"ho,omitempty" json:",omitempty"` // hoster supply chain (reseller, hosting provider, datacenter operator, ...) Datacenter string `cbor:"dc,omitempty" json:",omitempty"` // datacenter will be bullshit checked // Format: CC-COMPANY-INTERNALCODE // Eg: DE-Hetzner-FSN1-DC5 // Network Location and Access // If node is behind NAT (or similar), IP addresses must be configured IPv4 net.IP `cbor:"ip4,omitempty" json:",omitempty"` // must be global and accessible IPv6 net.IP `cbor:"ip6,omitempty" json:",omitempty"` // must be global and accessible Transports []string `cbor:"tp,omitempty" json:",omitempty"` // { // "spn:17", // "smtp:25", // also support "smtp://:25 // "smtp:587", // "imap:143", // "http:80", // "http://example.com:80", // HTTP (based): use full path for request // "https:443", // "ws:80", // "wss://example.com:443/spn", // } // protocols with metadata parsedTransports []*Transport // Policies - default permit Entry []string `cbor:"pi,omitempty" json:",omitempty"` entryPolicy endpoints.Endpoints // {"+ ", "- *"} Exit []string `cbor:"po,omitempty" json:",omitempty"` exitPolicy endpoints.Endpoints // {"- * TCP/25", "- US"} // Flags holds flags that signify special states. Flags []string `cbor:"f,omitempty" json:",omitempty"` } // Copy returns a deep copy of the Announcement. func (a *Announcement) Copy() *Announcement { return &Announcement{ ID: a.ID, Timestamp: a.Timestamp, Name: a.Name, ContactAddress: a.ContactAddress, ContactService: a.ContactService, Hosters: slices.Clone(a.Hosters), Datacenter: a.Datacenter, IPv4: a.IPv4, IPv6: a.IPv6, Transports: slices.Clone(a.Transports), parsedTransports: slices.Clone(a.parsedTransports), Entry: slices.Clone(a.Entry), entryPolicy: slices.Clone(a.entryPolicy), Exit: slices.Clone(a.Exit), exitPolicy: slices.Clone(a.exitPolicy), Flags: slices.Clone(a.Flags), } } // GetInfo returns the hub info. func (h *Hub) GetInfo() *Announcement { h.Lock() defer h.Unlock() return h.Info } // GetStatus returns the hub status. func (h *Hub) GetStatus() *Status { h.Lock() defer h.Unlock() return h.Status } // GetMeasurements returns the hub measurements. // This method should always be used instead of direct access. func (h *Hub) GetMeasurements() *Measurements { h.Lock() defer h.Unlock() return h.GetMeasurementsWithLockedHub() } // GetMeasurementsWithLockedHub returns the hub measurements. // The caller must hold the lock to Hub. // This method should always be used instead of direct access. func (h *Hub) GetMeasurementsWithLockedHub() *Measurements { if !h.measurementsInitialized { h.Measurements = getSharedMeasurements(h.ID, h.Measurements) h.Measurements.check() h.measurementsInitialized = true } return h.Measurements } // Verified return whether the Hub has been verified. func (h *Hub) Verified() bool { h.Lock() defer h.Unlock() return h.VerifiedIPs } // String returns a human-readable representation of the Hub. func (h *Hub) String() string { h.Lock() defer h.Unlock() return "" } // StringWithoutLocking returns a human-readable representation of the Hub without locking it. func (h *Hub) StringWithoutLocking() string { return "" } // Name returns a human-readable version of a Hub's name. This name will likely consist of two parts: the given name and the ending of the ID to make it unique. func (h *Hub) Name() string { h.Lock() defer h.Unlock() return h.getName() } func (h *Hub) getName() string { // Check for a short ID that is sometimes used for testing. if len(h.ID) < 8 { return h.ID } shortenedID := h.ID[len(h.ID)-8:len(h.ID)-4] + "-" + h.ID[len(h.ID)-4:] // Be more careful, as the Hub name is user input. switch { case h.Info.Name == "": return shortenedID case len(h.Info.Name) > 16: return h.Info.Name[:16] + " " + shortenedID default: return h.Info.Name + " " + shortenedID } } // Obsolete returns if the Hub is obsolete and may be deleted. func (h *Hub) Obsolete() bool { h.Lock() defer h.Unlock() // Check if Hub is valid. var valid bool switch { case h.InvalidInfo: case h.InvalidStatus: case h.HasFlag(FlagOffline): // Treat offline as invalid. default: valid = true } // Check when Hub was last seen. lastSeen := h.FirstSeen if h.Status.Timestamp != 0 { lastSeen = time.Unix(h.Status.Timestamp, 0) } // Check if Hub is obsolete. if valid { return time.Now().Add(-obsoleteValidAfter).After(lastSeen) } return time.Now().Add(-obsoleteInvalidAfter).After(lastSeen) } // HasFlag returns whether the Announcement or Status has the given flag set. func (h *Hub) HasFlag(flagName string) bool { switch { case h.Status != nil && slices.Contains[[]string, string](h.Status.Flags, flagName): return true case h.Info != nil && slices.Contains[[]string, string](h.Info.Flags, flagName): return true } return false } // Equal returns whether the given Announcements are equal. func (a *Announcement) Equal(b *Announcement) bool { switch { case a == nil || b == nil: return false case a.ID != b.ID: return false case a.Timestamp != b.Timestamp: return false case a.Name != b.Name: return false case a.ContactAddress != b.ContactAddress: return false case a.ContactService != b.ContactService: return false case !equalStringSlice(a.Hosters, b.Hosters): return false case a.Datacenter != b.Datacenter: return false case !a.IPv4.Equal(b.IPv4): return false case !a.IPv6.Equal(b.IPv6): return false case !equalStringSlice(a.Transports, b.Transports): return false case !equalStringSlice(a.Entry, b.Entry): return false case !equalStringSlice(a.Exit, b.Exit): return false case !equalStringSlice(a.Flags, b.Flags): return false default: return true } } // validateFormatting check if all values conform to the basic format. func (a *Announcement) validateFormatting() error { if err := checkStringFormat("ID", a.ID, 255); err != nil { return err } if err := checkStringFormat("Name", a.Name, 32); err != nil { return err } if err := checkStringFormat("Group", a.Group, 32); err != nil { return err } if err := checkStringFormat("ContactAddress", a.ContactAddress, 255); err != nil { return err } if err := checkStringFormat("ContactService", a.ContactService, 255); err != nil { return err } if err := checkStringSliceFormat("Hosters", a.Hosters, 255, 255); err != nil { return err } if err := checkStringFormat("Datacenter", a.Datacenter, 255); err != nil { return err } if err := checkIPFormat("IPv4", a.IPv4); err != nil { return err } if err := checkIPFormat("IPv6", a.IPv6); err != nil { return err } if err := checkStringSliceFormat("Transports", a.Transports, 255, 255); err != nil { return err } if err := checkStringSliceFormat("Entry", a.Entry, 255, 255); err != nil { return err } if err := checkStringSliceFormat("Exit", a.Exit, 255, 255); err != nil { return err } if err := checkStringSliceFormat("Flags", a.Flags, 16, 32); err != nil { return err } return nil } // Prepare prepares the announcement by parsing policies and transports. // If fields are already parsed, they will only be parsed again, when force is set to true. func (a *Announcement) prepare(force bool) error { var err error // Parse policies. if len(a.entryPolicy) == 0 || force { if a.entryPolicy, err = endpoints.ParseEndpoints(a.Entry); err != nil { return fmt.Errorf("failed to parse entry policy: %w", err) } } if len(a.exitPolicy) == 0 || force { if a.exitPolicy, err = endpoints.ParseEndpoints(a.Exit); err != nil { return fmt.Errorf("failed to parse exit policy: %w", err) } } // Parse transports. if len(a.parsedTransports) == 0 || force { parsed, errs := ParseTransports(a.Transports) // Log parsing warnings. for _, err := range errs { log.Warningf("hub: Hub %s (%s) has configured an %s", a.Name, a.ID, err) } // Check if there are any valid transports. if len(parsed) == 0 { return ErrMissingTransports } a.parsedTransports = parsed } return nil } // EntryPolicy returns the Hub's entry policy. func (a *Announcement) EntryPolicy() endpoints.Endpoints { return a.entryPolicy } // ExitPolicy returns the Hub's exit policy. func (a *Announcement) ExitPolicy() endpoints.Endpoints { return a.exitPolicy } // ParsedTransports returns the Hub's parsed transports. func (a *Announcement) ParsedTransports() []*Transport { return a.parsedTransports } // HasFlag returns whether the Announcement has the given flag set. func (a *Announcement) HasFlag(flagName string) bool { return slices.Contains[[]string, string](a.Flags, flagName) } // String returns the string representation of the scope. func (s Scope) String() string { switch s { case ScopeInvalid: return "invalid" case ScopeLocal: return "local" case ScopePublic: return "public" case ScopeTest: return "test" default: return "unknown" } } func equalStringSlice(a, b []string) bool { if len(a) != len(b) { return false } for i := range len(a) { if a[i] != b[i] { return false } } return true } ================================================ FILE: spn/hub/hub_test.go ================================================ package hub import ( "fmt" "net" "os" "testing" "github.com/stretchr/testify/assert" "github.com/safing/portmaster/base/database/dbmodule" "github.com/safing/portmaster/service/core/base" ) type testInstance struct { db *dbmodule.DBModule base *base.Base } func (stub *testInstance) SetCmdLineOperation(f func() error) {} func (stub *testInstance) DataDir() string { return _dataDir } var _dataDir string func runTest(m *testing.M) error { var err error // Create a temporary directory for testing _dataDir, err = os.MkdirTemp("", "") if err != nil { return fmt.Errorf("failed to create temporary data directory: %w", err) } defer func() { _ = os.RemoveAll(_dataDir) }() // Initialize the instance with the necessary components stub := &testInstance{} // Init stub.db, err = dbmodule.New(stub) if err != nil { return fmt.Errorf("failed to create database: %w", err) } stub.base, err = base.New(stub) if err != nil { return fmt.Errorf("failed to base updates: %w", err) } // Start err = stub.db.Start() if err != nil { return fmt.Errorf("failed to start database: %w", err) } err = stub.base.Start() if err != nil { return fmt.Errorf("failed to start base: %w", err) } m.Run() return nil } func TestMain(m *testing.M) { if err := runTest(m); err != nil { fmt.Printf("%s", err) os.Exit(1) } } func TestEquality(t *testing.T) { t.Parallel() // empty match a := &Announcement{} assert.True(t, a.Equal(a), "should match itself") //nolint:gocritic // This is a test. // full match a = &Announcement{ ID: "a", Timestamp: 1, Name: "a", ContactAddress: "a", ContactService: "a", Hosters: []string{"a", "b"}, Datacenter: "a", IPv4: net.IPv4(1, 2, 3, 4), IPv6: net.ParseIP("::1"), Transports: []string{"a", "b"}, Entry: []string{"a", "b"}, Exit: []string{"a", "b"}, } assert.True(t, a.Equal(a), "should match itself") //nolint:gocritic // This is a test. // no match b := &Announcement{ID: "b"} assert.False(t, a.Equal(b), "should not match") b = &Announcement{Timestamp: 2} assert.False(t, a.Equal(b), "should not match") b = &Announcement{Name: "b"} assert.False(t, a.Equal(b), "should not match") b = &Announcement{ContactAddress: "b"} assert.False(t, a.Equal(b), "should not match") b = &Announcement{ContactService: "b"} assert.False(t, a.Equal(b), "should not match") b = &Announcement{Hosters: []string{"b", "c"}} assert.False(t, a.Equal(b), "should not match") b = &Announcement{Datacenter: "b"} assert.False(t, a.Equal(b), "should not match") b = &Announcement{IPv4: net.IPv4(1, 2, 3, 5)} assert.False(t, a.Equal(b), "should not match") b = &Announcement{IPv6: net.ParseIP("::2")} assert.False(t, a.Equal(b), "should not match") b = &Announcement{Transports: []string{"b", "c"}} assert.False(t, a.Equal(b), "should not match") b = &Announcement{Entry: []string{"b", "c"}} assert.False(t, a.Equal(b), "should not match") b = &Announcement{Exit: []string{"b", "c"}} assert.False(t, a.Equal(b), "should not match") } func TestStringify(t *testing.T) { t.Parallel() assert.Equal(t, "", (&Hub{ID: "abcdefg", Info: &Announcement{}}).String()) assert.Equal(t, "", (&Hub{ID: "abcdefgh", Info: &Announcement{}}).String()) assert.Equal(t, "", (&Hub{ID: "abcdefghi", Info: &Announcement{}}).String()) assert.Equal(t, "", (&Hub{ID: "abcdefghi", Info: &Announcement{Name: "Franz"}}).String()) assert.Equal(t, "", (&Hub{ID: "abcdefghi", Info: &Announcement{Name: "AProbablyAutoGeneratedName"}}).String()) } ================================================ FILE: spn/hub/intel.go ================================================ package hub import ( "errors" "fmt" "net" "github.com/ghodss/yaml" "github.com/safing/jess/lhash" "github.com/safing/portmaster/service/profile/endpoints" ) // Intel holds a collection of various security related data collections on Hubs. type Intel struct { // BootstrapHubs is list of transports that also contain an IP and the Hub's ID. BootstrapHubs []string // Hubs holds intel regarding specific Hubs. Hubs map[string]*HubIntel // AdviseOnlyTrustedHubs advises to only use trusted Hubs regardless of intended purpose. AdviseOnlyTrustedHubs bool // AdviseOnlyTrustedHomeHubs advises to only use trusted Hubs for Home Hubs. AdviseOnlyTrustedHomeHubs bool // AdviseOnlyTrustedDestinationHubs advises to only use trusted Hubs for Destination Hubs. AdviseOnlyTrustedDestinationHubs bool // Hub Advisories advise on the usage of Hubs and take the form of Endpoint Lists that match on both IPv4 and IPv6 addresses and their related data. // HubAdvisory always affects all Hubs. HubAdvisory []string // HomeHubAdvisory is only taken into account when selecting a Home Hub. HomeHubAdvisory []string // DestinationHubAdvisory is only taken into account when selecting a Destination Hub. DestinationHubAdvisory []string // Regions defines regions to assist network optimization. Regions []*RegionConfig // VirtualNetworks holds network configurations for virtual cloud networks. VirtualNetworks []*VirtualNetworkConfig parsed *ParsedIntel } // HubIntel holds Hub-related data. type HubIntel struct { //nolint:golint // Trusted specifies if the Hub is specially designated for more sensitive tasks, such as handling unencrypted traffic. Trusted bool // Discontinued specifies if the Hub has been discontinued and should be marked as offline and removed. Discontinued bool // VerifiedOwner holds the name of the verified owner / operator of the Hub. VerifiedOwner string // Override is used to override certain Hub information. Override *InfoOverride } // RegionConfig holds the configuration of a region. type RegionConfig struct { // ID is the internal identifier of the region. ID string // Name is a human readable name of the region. Name string // MemberPolicy specifies a list for including members. MemberPolicy []string // RegionalMinLanes specifies how many lanes other regions should build // to this region. RegionalMinLanes int // RegionalMinLanesPerHub specifies how many lanes other regions should // build to this region, per Hub in this region. // This value will usually be below one. RegionalMinLanesPerHub float64 // RegionalMaxLanesOnHub specifies how many lanes from or to another region may be // built on one Hub per region. RegionalMaxLanesOnHub int // SatelliteMinLanes specifies how many lanes satellites (Hubs without // region) should build to this region. SatelliteMinLanes int // SatelliteMinLanesPerHub specifies how many lanes satellites (Hubs without // region) should build to this region, per Hub in this region. // This value will usually be below one. SatelliteMinLanesPerHub float64 // InternalMinLanesOnHub specifies how many lanes every Hub should create // within the region at minimum. InternalMinLanesOnHub int // InternalMaxHops specifies the max hop constraint for internally optimizing // the region. InternalMaxHops int } // VirtualNetworkConfig holds configuration of a virtual network that binds multiple Hubs together. type VirtualNetworkConfig struct { // Name is a human readable name of the virtual network. Name string // Force forces the use of the mapped IP addresses after the Hub's IPs have been verified. Force bool // Mapping maps Hub IDs to internal IP addresses. Mapping map[string]net.IP } // ParsedIntel holds a collection of parsed intel data. type ParsedIntel struct { // HubAdvisory always affects all Hubs. HubAdvisory endpoints.Endpoints // HomeHubAdvisory is only taken into account when selecting a Home Hub. HomeHubAdvisory endpoints.Endpoints // DestinationHubAdvisory is only taken into account when selecting a Destination Hub. DestinationHubAdvisory endpoints.Endpoints } // Parsed returns the collection of parsed intel data. func (i *Intel) Parsed() *ParsedIntel { return i.parsed } // ParseIntel parses Hub intelligence data. func ParseIntel(data []byte) (*Intel, error) { // Load data into struct. intel := &Intel{} err := yaml.Unmarshal(data, intel) if err != nil { return nil, fmt.Errorf("failed to parse data: %w", err) } // Parse all endpoint lists. err = intel.ParseAdvisories() if err != nil { return nil, err } return intel, nil } // ParseAdvisories parses all advisory endpoint lists. func (i *Intel) ParseAdvisories() (err error) { i.parsed = &ParsedIntel{} i.parsed.HubAdvisory, err = endpoints.ParseEndpoints(i.HubAdvisory) if err != nil { return fmt.Errorf("failed to parse HubAdvisory list: %w", err) } i.parsed.HomeHubAdvisory, err = endpoints.ParseEndpoints(i.HomeHubAdvisory) if err != nil { return fmt.Errorf("failed to parse HomeHubAdvisory list: %w", err) } i.parsed.DestinationHubAdvisory, err = endpoints.ParseEndpoints(i.DestinationHubAdvisory) if err != nil { return fmt.Errorf("failed to parse DestinationHubAdvisory list: %w", err) } return nil } // ParseBootstrapHub parses a bootstrap hub. func ParseBootstrapHub(bootstrapTransport string) (t *Transport, hubID string, hubIP net.IP, err error) { // Parse transport and check Hub ID. t, err = ParseTransport(bootstrapTransport) if err != nil { return nil, "", nil, fmt.Errorf("failed to parse transport: %w", err) } if t.Option == "" { return nil, "", nil, errors.New("missing hub ID in URL fragment") } if _, err := lhash.FromBase58(t.Option); err != nil { return nil, "", nil, fmt.Errorf("hub ID is invalid: %w", err) } // Parse IP address from transport. ip := net.ParseIP(t.Domain) if ip == nil { return nil, "", nil, errors.New("invalid IP address (domains are not supported for bootstrapping)") } // Clean up transport for hub info. id := t.Option t.Domain = "" t.Option = "" return t, id, ip, nil } ================================================ FILE: spn/hub/intel_override.go ================================================ package hub import "github.com/safing/portmaster/service/intel/geoip" // InfoOverride holds data to overide hub info information. type InfoOverride struct { // ContinentCode overrides the continent code of the geoip data. ContinentCode string // CountryCode overrides the country code of the geoip data. CountryCode string // Coordinates overrides the geo coordinates code of the geoip data. Coordinates *geoip.Coordinates // ASN overrides the Autonomous System Number of the geoip data. ASN uint // ASOrg overrides the Autonomous System Organization of the geoip data. ASOrg string } ================================================ FILE: spn/hub/measurements.go ================================================ package hub import ( "sync" "time" "github.com/tevino/abool" ) // MaxCalculatedCost specifies the max calculated cost to be used for an unknown high cost. const MaxCalculatedCost = 1000000 // Measurements holds various measurements relating to a Hub. // Fields may not be accessed directly. type Measurements struct { sync.Mutex // Latency designates the latency between these Hubs. // It is specified in nanoseconds. Latency time.Duration // LatencyMeasuredAt holds when the latency was measured. LatencyMeasuredAt time.Time // Capacity designates the available bandwidth between these Hubs. // It is specified in bit/s. Capacity int // CapacityMeasuredAt holds when the capacity measurement expires. CapacityMeasuredAt time.Time // CalculatedCost stores the calculated cost for direct access. // It is not set automatically, but needs to be set when needed. CalculatedCost float32 // GeoProximity stores an approximation of the geolocation proximity. // The value is between 0 (other side of the world) and 100 (same location). GeoProximity float32 // persisted holds whether the Measurements have been persisted to the // database. persisted *abool.AtomicBool } // NewMeasurements returns a new measurements struct. func NewMeasurements() *Measurements { m := &Measurements{ CalculatedCost: MaxCalculatedCost, // Push to back when sorting without data. } m.check() return m } // Copy returns a copy of the measurements. func (m *Measurements) Copy() *Measurements { copied := &Measurements{ Latency: m.Latency, LatencyMeasuredAt: m.LatencyMeasuredAt, Capacity: m.Capacity, CapacityMeasuredAt: m.CapacityMeasuredAt, CalculatedCost: m.CalculatedCost, } copied.check() return copied } // Check checks if the Measurements are properly initialized and ready to use. func (m *Measurements) check() { if m == nil { return } m.Lock() defer m.Unlock() if m.persisted == nil { m.persisted = abool.NewBool(true) } } // IsPersisted return whether changes to the measurements have been persisted. func (m *Measurements) IsPersisted() bool { return m.persisted.IsSet() } // Valid returns whether there is a valid value . func (m *Measurements) Valid() bool { m.Lock() defer m.Unlock() switch { case m.Latency == 0: // Latency is not set. case m.Capacity == 0: // Capacity is not set. case m.CalculatedCost == 0: // CalculatedCost is not set. case m.CalculatedCost == MaxCalculatedCost: // CalculatedCost is set to static max value. default: return true } return false } // Expired returns whether any of the measurements has expired - calculated // with the given TTL. func (m *Measurements) Expired(ttl time.Duration) bool { expiry := time.Now().Add(-ttl) m.Lock() defer m.Unlock() switch { case expiry.After(m.LatencyMeasuredAt): return true case expiry.After(m.CapacityMeasuredAt): return true default: return false } } // SetLatency sets the latency to the given value. func (m *Measurements) SetLatency(latency time.Duration) { m.Lock() defer m.Unlock() m.Latency = latency m.LatencyMeasuredAt = time.Now() m.persisted.UnSet() } // GetLatency returns the latency and when it expires. func (m *Measurements) GetLatency() (latency time.Duration, measuredAt time.Time) { m.Lock() defer m.Unlock() return m.Latency, m.LatencyMeasuredAt } // SetCapacity sets the capacity to the given value. // The capacity is measued in bit/s. func (m *Measurements) SetCapacity(capacity int) { m.Lock() defer m.Unlock() m.Capacity = capacity m.CapacityMeasuredAt = time.Now() m.persisted.UnSet() } // GetCapacity returns the capacity and when it expires. // The capacity is measued in bit/s. func (m *Measurements) GetCapacity() (capacity int, measuredAt time.Time) { m.Lock() defer m.Unlock() return m.Capacity, m.CapacityMeasuredAt } // SetCalculatedCost sets the calculated cost to the given value. // The calculated cost is not set automatically, but needs to be set when needed. func (m *Measurements) SetCalculatedCost(cost float32) { m.Lock() defer m.Unlock() m.CalculatedCost = cost m.persisted.UnSet() } // GetCalculatedCost returns the calculated cost. // The calculated cost is not set automatically, but needs to be set when needed. func (m *Measurements) GetCalculatedCost() (cost float32) { if m == nil { return MaxCalculatedCost } m.Lock() defer m.Unlock() return m.CalculatedCost } // SetGeoProximity sets the geolocation proximity to the given value. func (m *Measurements) SetGeoProximity(geoProximity float32) { m.Lock() defer m.Unlock() m.GeoProximity = geoProximity m.persisted.UnSet() } // GetGeoProximity returns the geolocation proximity. func (m *Measurements) GetGeoProximity() (geoProximity float32) { if m == nil { return 0 } m.Lock() defer m.Unlock() return m.GeoProximity } var ( measurementsRegistry = make(map[string]*Measurements) measurementsRegistryLock sync.Mutex ) func getSharedMeasurements(hubID string, existing *Measurements) *Measurements { measurementsRegistryLock.Lock() defer measurementsRegistryLock.Unlock() // 1. Check registry and return shared measurements. m, ok := measurementsRegistry[hubID] if ok { return m } // 2. Use existing and make it shared, if available. if existing != nil { existing.check() measurementsRegistry[hubID] = existing return existing } // 3. Create new measurements. m = NewMeasurements() measurementsRegistry[hubID] = m return m } ================================================ FILE: spn/hub/status.go ================================================ package hub import ( "errors" "fmt" "sort" "time" "golang.org/x/exp/slices" "github.com/safing/jess" ) // VersionOffline is a special version used to signify that the Hub has gone offline. // This is depracated, please use FlagOffline instead. const VersionOffline = "offline" // Status Flags. const ( // FlagNetError signifies that the Hub reports a network connectivity failure or impairment. FlagNetError = "net-error" // FlagOffline signifies that the Hub has gone offline by itself. FlagOffline = "offline" // FlagAllowUnencrypted signifies that the Hub is available to handle unencrypted connections. FlagAllowUnencrypted = "allow-unencrypted" ) // Status is the message type used to update changing Hub Information. Changes are made automatically. type Status struct { Timestamp int64 `cbor:"t"` // Version holds the current software version of the Hub. Version string `cbor:"v"` // Routing Information Keys map[string]*Key `cbor:"k,omitempty" json:",omitempty"` // public keys (with type) Lanes []*Lane `cbor:"c,omitempty" json:",omitempty"` // Connections to other Hubs. // Status Information // Load describes max(CPU, Memory) in percent, averaged over at least 15 // minutes. Load is published in fixed steps only. Load int `cbor:"l,omitempty" json:",omitempty"` // Flags holds flags that signify special states. Flags []string `cbor:"f,omitempty" json:",omitempty"` } // Key represents a semi-ephemeral public key used for 0-RTT connection establishment. type Key struct { Scheme string Key []byte Expires int64 } // Lane represents a connection to another Hub. type Lane struct { // ID is the Hub ID of the peer. ID string // Capacity designates the available bandwidth between these Hubs. // It is specified in bit/s. Capacity int // Lateny designates the latency between these Hubs. // It is specified in nanoseconds. Latency time.Duration } // Copy returns a deep copy of the Status. func (s *Status) Copy() *Status { newStatus := &Status{ Timestamp: s.Timestamp, Version: s.Version, Lanes: slices.Clone(s.Lanes), Load: s.Load, Flags: slices.Clone(s.Flags), } // Copy map. newStatus.Keys = make(map[string]*Key, len(s.Keys)) for k, v := range s.Keys { newStatus.Keys[k] = v } return newStatus } // SelectSignet selects the public key to use for initiating connections to that Hub. func (h *Hub) SelectSignet() *jess.Signet { h.Lock() defer h.Unlock() // Return no Signet if we don't have a Status. if h.Status == nil { return nil } // TODO: select key based on preferred alg? now := time.Now().Unix() for id, key := range h.Status.Keys { if now < key.Expires { return &jess.Signet{ ID: id, Scheme: key.Scheme, Key: key.Key, Public: true, } } } return nil } // GetSignet returns the public key identified by the given ID from the Hub Status. func (h *Hub) GetSignet(id string, recipient bool) (*jess.Signet, error) { h.Lock() defer h.Unlock() // check if public key is being requested if !recipient { return nil, jess.ErrSignetNotFound } // check if ID exists key, ok := h.Status.Keys[id] if !ok { return nil, jess.ErrSignetNotFound } // transform and return return &jess.Signet{ ID: id, Scheme: key.Scheme, Key: key.Key, Public: true, }, nil } // AddLane adds a new Lane to the Hub Status. func (h *Hub) AddLane(newLane *Lane) error { h.Lock() defer h.Unlock() // validity check if h.Status == nil { return ErrMissingInfo } // check if duplicate for _, lane := range h.Status.Lanes { if newLane.ID == lane.ID { return errors.New("lane already exists") } } // add h.Status.Lanes = append(h.Status.Lanes, newLane) return nil } // RemoveLane removes a Lane from the Hub Status. func (h *Hub) RemoveLane(hubID string) error { h.Lock() defer h.Unlock() // validity check if h.Status == nil { return ErrMissingInfo } for key, lane := range h.Status.Lanes { if lane.ID == hubID { h.Status.Lanes = append(h.Status.Lanes[:key], h.Status.Lanes[key+1:]...) break } } return nil } // GetLaneTo returns the lane to the given Hub, if it exists. func (h *Hub) GetLaneTo(hubID string) *Lane { h.Lock() defer h.Unlock() // validity check if h.Status == nil { return nil } for _, lane := range h.Status.Lanes { if lane.ID == hubID { return lane } } return nil } // Equal returns whether the Lane is equal to the given one. func (l *Lane) Equal(other *Lane) bool { switch { case l == nil || other == nil: return false case l.ID != other.ID: return false case l.Capacity != other.Capacity: return false case l.Latency != other.Latency: return false } return true } // validateFormatting check if all values conform to the basic format. func (s *Status) validateFormatting() error { // public keys if len(s.Keys) > 255 { return fmt.Errorf("field Keys with array/slice length of %d exceeds max length of %d", len(s.Keys), 255) } for keyID, key := range s.Keys { if err := checkStringFormat("Keys#ID", keyID, 255); err != nil { return err } if err := checkStringFormat("Keys.Scheme", key.Scheme, 255); err != nil { return err } if err := checkByteSliceFormat("Keys.Key", key.Key, 1024); err != nil { return err } } // connections if len(s.Lanes) > 255 { return fmt.Errorf("field Lanes with array/slice length of %d exceeds max length of %d", len(s.Lanes), 255) } for _, lanes := range s.Lanes { if err := checkStringFormat("Lanes.ID", lanes.ID, 255); err != nil { return err } } // Flags if err := checkStringSliceFormat("Flags", s.Flags, 255, 255); err != nil { return err } return nil } func (l *Lane) String() string { return fmt.Sprintf("<%s cap=%d lat=%d>", l.ID, l.Capacity, l.Latency) } // LanesEqual returns whether the given []*Lane are equal. func LanesEqual(a, b []*Lane) bool { if len(a) != len(b) { return false } for i, l := range a { if !l.Equal(b[i]) { return false } } return true } type lanes []*Lane func (l lanes) Len() int { return len(l) } func (l lanes) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l lanes) Less(i, j int) bool { return l[i].ID < l[j].ID } // SortLanes sorts a slice of Lanes. func SortLanes(l []*Lane) { sort.Sort(lanes(l)) } // HasFlag returns whether the Status has the given flag set. func (s *Status) HasFlag(flagName string) bool { return slices.Contains[[]string, string](s.Flags, flagName) } // FlagsEqual returns whether the given status flags are equal. func FlagsEqual(a, b []string) bool { // Cannot be equal if lengths are different. if len(a) != len(b) { return false } // If both are empty, they are equal. if len(a) == 0 { return true } // Make sure flags are sorted before comparing values. sort.Strings(a) sort.Strings(b) // Compare values. for i, v := range a { if v != b[i] { return false } } return true } ================================================ FILE: spn/hub/transport.go ================================================ package hub import ( "errors" "fmt" "net/url" "strconv" "strings" "golang.org/x/exp/slices" ) // Examples: // "spn:17", // "smtp:25", // "smtp:587", // "imap:143", // "http:80", // "http://example.com:80/example", // HTTP (based): use full path for request // "https:443", // "ws:80", // "wss://example.com:443/spn", // Transport represents a "endpoint" that others can connect to. This allows for use of different protocols, ports and infrastructure integration. type Transport struct { Protocol string Domain string Port uint16 Path string Option string } // ParseTransports returns a list of parsed transports and errors from parsing // the given definitions. func ParseTransports(definitions []string) (transports []*Transport, errs []error) { transports = make([]*Transport, 0, len(definitions)) for _, definition := range definitions { parsed, err := ParseTransport(definition) if err != nil { errs = append(errs, fmt.Errorf( "unknown or invalid transport %q: %w", definition, err, )) } else { transports = append(transports, parsed) } } SortTransports(transports) return transports, errs } // ParseTransport parses a transport definition. func ParseTransport(definition string) (*Transport, error) { u, err := url.Parse(definition) if err != nil { return nil, err } // check for invalid parts if u.User != nil { return nil, errors.New("user/pass is not allowed") } // put into transport t := &Transport{ Protocol: u.Scheme, Domain: u.Hostname(), Path: u.RequestURI(), Option: u.Fragment, } // parse port portData := u.Port() if portData == "" { // no port available - it might be in u.Opaque, which holds both the port and possibly a path portData = strings.SplitN(u.Opaque, "/", 2)[0] // get port t.Path = strings.TrimPrefix(t.Path, portData) // trim port from path // check again for port if portData == "" { return nil, errors.New("missing port") } } port, err := strconv.ParseUint(portData, 10, 16) if err != nil { return nil, errors.New("invalid port") } t.Port = uint16(port) // check port if t.Port == 0 { return nil, errors.New("invalid port") } // remove root paths if t.Path == "/" { t.Path = "" } // check for protocol if t.Protocol == "" { return nil, errors.New("missing scheme/protocol") } return t, nil } // String returns the definition form of the transport. func (t *Transport) String() string { switch { case t.Option != "": return fmt.Sprintf("%s://%s:%d%s#%s", t.Protocol, t.Domain, t.Port, t.Path, t.Option) case t.Domain != "": return fmt.Sprintf("%s://%s:%d%s", t.Protocol, t.Domain, t.Port, t.Path) default: return fmt.Sprintf("%s:%d%s", t.Protocol, t.Port, t.Path) } } // SortTransports sorts the transports to emphasize certain protocols, but // otherwise leaves the order intact. func SortTransports(ts []*Transport) { slices.SortStableFunc[[]*Transport, *Transport](ts, func(a, b *Transport) int { aOrder := a.protocolOrder() bOrder := b.protocolOrder() switch { case aOrder != bOrder: return aOrder - bOrder // case a.Port != b.Port: // return int(a.Port) - int(b.Port) // case a.Domain != b.Domain: // return strings.Compare(a.Domain, b.Domain) // case a.Path != b.Path: // return strings.Compare(a.Path, b.Path) // case a.Option != b.Option: // return strings.Compare(a.Option, b.Option) default: return 0 } }) } func (t *Transport) protocolOrder() int { switch t.Protocol { case "http": return 1 case "spn": return 2 default: return 100 } } ================================================ FILE: spn/hub/transport_test.go ================================================ package hub import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func parseT(t *testing.T, definition string) *Transport { t.Helper() tr, err := ParseTransport(definition) if err != nil { t.Fatal(err) return nil } return tr } func parseTError(definition string) error { _, err := ParseTransport(definition) return err } func TestTransportParsing(t *testing.T) { t.Parallel() // test parsing assert.Equal(t, &Transport{ Protocol: "spn", Port: 17, }, parseT(t, "spn:17"), "should match") assert.Equal(t, &Transport{ Protocol: "smtp", Port: 25, }, parseT(t, "smtp:25"), "should match") assert.Equal(t, &Transport{ Protocol: "smtp", Port: 25, }, parseT(t, "smtp://:25"), "should match") assert.Equal(t, &Transport{ Protocol: "smtp", Port: 587, }, parseT(t, "smtp:587"), "should match") assert.Equal(t, &Transport{ Protocol: "imap", Port: 143, }, parseT(t, "imap:143"), "should match") assert.Equal(t, &Transport{ Protocol: "http", Port: 80, }, parseT(t, "http:80"), "should match") assert.Equal(t, &Transport{ Protocol: "http", Domain: "example.com", Port: 80, }, parseT(t, "http://example.com:80"), "should match") assert.Equal(t, &Transport{ Protocol: "https", Port: 443, }, parseT(t, "https:443"), "should match") assert.Equal(t, &Transport{ Protocol: "ws", Port: 80, }, parseT(t, "ws:80"), "should match") assert.Equal(t, &Transport{ Protocol: "wss", Domain: "example.com", Port: 443, Path: "/spn", }, parseT(t, "wss://example.com:443/spn"), "should match") assert.Equal(t, &Transport{ Protocol: "http", Domain: "example.com", Port: 80, }, parseT(t, "http://example.com:80"), "should match") assert.Equal(t, &Transport{ Protocol: "http", Domain: "example.com", Port: 80, Path: "/test%20test", }, parseT(t, "http://example.com:80/test test"), "should match") assert.Equal(t, &Transport{ Protocol: "http", Domain: "example.com", Port: 80, Path: "/test%20test", }, parseT(t, "http://example.com:80/test%20test"), "should match") assert.Equal(t, &Transport{ Protocol: "http", Domain: "example.com", Port: 80, Path: "/test?key=value", }, parseT(t, "http://example.com:80/test?key=value"), "should match") // test parsing and formatting assert.Equal(t, "spn:17", parseT(t, "spn:17").String(), "should match") assert.Equal(t, "smtp:25", parseT(t, "smtp:25").String(), "should match") assert.Equal(t, "smtp:25", parseT(t, "smtp://:25").String(), "should match") assert.Equal(t, "smtp:587", parseT(t, "smtp:587").String(), "should match") assert.Equal(t, "imap:143", parseT(t, "imap:143").String(), "should match") assert.Equal(t, "http:80", parseT(t, "http:80").String(), "should match") assert.Equal(t, "http://example.com:80", parseT(t, "http://example.com:80").String(), "should match") assert.Equal(t, "https:443", parseT(t, "https:443").String(), "should match") assert.Equal(t, "ws:80", parseT(t, "ws:80").String(), "should match") assert.Equal(t, "wss://example.com:443/spn", parseT(t, "wss://example.com:443/spn").String(), "should match") assert.Equal(t, "http://example.com:80", parseT(t, "http://example.com:80").String(), "should match") assert.Equal(t, "http://example.com:80/test%20test", parseT(t, "http://example.com:80/test test").String(), "should match") assert.Equal(t, "http://example.com:80/test%20test", parseT(t, "http://example.com:80/test%20test").String(), "should match") assert.Equal(t, "http://example.com:80/test?key=value", parseT(t, "http://example.com:80/test?key=value").String(), "should match") // test invalid require.Error(t, parseTError("spn"), "should fail") require.Error(t, parseTError("spn:"), "should fail") require.Error(t, parseTError("spn:0"), "should fail") require.Error(t, parseTError("spn:65536"), "should fail") } ================================================ FILE: spn/hub/truststores.go ================================================ package hub import "github.com/safing/jess" // SingleTrustStore is a simple truststore that always returns the same Signet. type SingleTrustStore struct { Signet *jess.Signet } // GetSignet implements the truststore interface. func (ts *SingleTrustStore) GetSignet(id string, recipient bool) (*jess.Signet, error) { if ts.Signet.ID != id || recipient != ts.Signet.Public { return nil, jess.ErrSignetNotFound } return ts.Signet, nil } ================================================ FILE: spn/hub/update.go ================================================ package hub import ( "errors" "fmt" "time" "github.com/safing/jess" "github.com/safing/jess/lhash" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/structures/container" "github.com/safing/structures/dsd" ) var ( // hubMsgRequirements defines which security attributes message need to have. hubMsgRequirements = jess.NewRequirements(). Remove(jess.RecipientAuthentication). // Recipient don't need a private key. Remove(jess.Confidentiality). // Message contents are out in the open. Remove(jess.Integrity) // Only applies to decryption. // SenderAuthentication provides pre-decryption integrity. That is all we need. clockSkewTolerance = 12 * time.Hour ) // SignHubMsg signs the given serialized hub msg with the given configuration. func SignHubMsg(msg []byte, env *jess.Envelope, enableTofu bool) ([]byte, error) { // start session from envelope session, err := env.Correspondence(nil) if err != nil { return nil, fmt.Errorf("failed to initiate signing session: %w", err) } // sign the data letter, err := session.Close(msg) if err != nil { return nil, fmt.Errorf("failed to sign msg: %w", err) } if enableTofu { // smuggle the public key // letter.Keys is usually only used for key exchanges and encapsulation // neither is used when signing, so we can use letter.Keys to transport public keys for _, sender := range env.Senders { // get public key public, err := sender.AsRecipient() if err != nil { return nil, fmt.Errorf("failed to get public key of %s: %w", sender.ID, err) } // serialize key err = public.StoreKey() if err != nil { return nil, fmt.Errorf("failed to serialize public key %s: %w", sender.ID, err) } // add to keys letter.Keys = append(letter.Keys, &jess.Seal{ Value: public.Key, }) } } // pack data, err := letter.ToDSD(dsd.JSON) if err != nil { return nil, err } return data, nil } // OpenHubMsg opens a signed hub msg and verifies the signature using the // provided hub or the local database. If TOFU is enabled, the signature is // always accepted, if valid. func OpenHubMsg(hub *Hub, data []byte, mapName string, tofu bool) (msg []byte, sendingHub *Hub, known bool, err error) { letter, err := jess.LetterFromDSD(data) if err != nil { return nil, nil, false, fmt.Errorf("malformed letter: %w", err) } // check signatures var seal *jess.Seal switch len(letter.Signatures) { case 0: return nil, nil, false, errors.New("missing signature") case 1: seal = letter.Signatures[0] default: return nil, nil, false, fmt.Errorf("too many signatures (%d)", len(letter.Signatures)) } // check signature signer ID if seal.ID == "" { return nil, nil, false, errors.New("signature is missing signer ID") } // get hub for public key if hub == nil { hub, err = GetHub(mapName, seal.ID) if err != nil { if !errors.Is(err, database.ErrNotFound) { return nil, nil, false, fmt.Errorf("failed to get existing hub %s: %w", seal.ID, err) } hub = nil } else { known = true } } else { known = true } var truststore jess.TrustStore if hub != nil && hub.PublicKey != nil { // bootstrap entries will not have a public key // check ID integrity if hub.ID != seal.ID { return nil, hub, known, fmt.Errorf("ID mismatch with hub msg ID %s and hub ID %s", seal.ID, hub.ID) } if !verifyHubID(seal.ID, hub.PublicKey.Scheme, hub.PublicKey.Key) { return nil, hub, known, fmt.Errorf("ID integrity of %s violated with existing key", seal.ID) } } else { if !tofu { return nil, nil, false, fmt.Errorf("hub msg ID %s unknown (missing announcement)", seal.ID) } // trust on first use, extract key from keys // TODO: Test if works without TOFU. // get key var pubkey *jess.Seal switch len(letter.Keys) { case 0: return nil, nil, false, fmt.Errorf("missing key for TOFU of %s", seal.ID) case 1: pubkey = letter.Keys[0] default: return nil, nil, false, fmt.Errorf("too many keys (%d) for TOFU of %s", len(letter.Keys), seal.ID) } // check ID integrity if !verifyHubID(seal.ID, seal.Scheme, pubkey.Value) { return nil, nil, false, fmt.Errorf("ID integrity of %s violated with new key", seal.ID) } hub = &Hub{ ID: seal.ID, Map: mapName, PublicKey: &jess.Signet{ ID: seal.ID, Scheme: seal.Scheme, Key: pubkey.Value, Public: true, }, } err = hub.PublicKey.LoadKey() if err != nil { return nil, nil, false, err } } // create trust store truststore = &SingleTrustStore{hub.PublicKey} // remove keys from letter, as they are only used to transfer the public key letter.Keys = nil // check signature err = letter.Verify(hubMsgRequirements, truststore) if err != nil { return nil, nil, false, err } return letter.Data, hub, known, nil } // Export exports the announcement with the given signature configuration. func (a *Announcement) Export(env *jess.Envelope) ([]byte, error) { // pack msg, err := dsd.Dump(a, dsd.JSON) if err != nil { return nil, fmt.Errorf("failed to pack announcement: %w", err) } return SignHubMsg(msg, env, true) } // ApplyAnnouncement applies the announcement to the Hub if it passes all the // checks. If no Hub is provided, it is loaded from the database or created. func ApplyAnnouncement(existingHub *Hub, data []byte, mapName string, scope Scope, selfcheck bool) (hub *Hub, known, changed bool, err error) { // Set valid/invalid status based on the return error. var announcement *Announcement defer func() { if hub != nil { if err != nil && !errors.Is(err, ErrOldData) { hub.InvalidInfo = true } else { hub.InvalidInfo = false } } }() // open and verify var msg []byte msg, hub, known, err = OpenHubMsg(existingHub, data, mapName, true) // Lock hub if we have one. if hub != nil && !selfcheck { hub.Lock() defer hub.Unlock() } // Check if there was an error with the Hub msg. if err != nil { return //nolint:nakedret } // parse announcement = &Announcement{} _, err = dsd.Load(msg, announcement) if err != nil { return //nolint:nakedret } // integrity check // `hub.ID` is taken from the first ever received announcement message. // `announcement.ID` is additionally present in the message as we need // a signed version of the ID to mitigate fake IDs. // Fake IDs are possible because the hash algorithm of the ID is dynamic. if hub.ID != announcement.ID { err = fmt.Errorf("announcement ID %q mismatches hub ID %q", announcement.ID, hub.ID) return //nolint:nakedret } // version check if hub.Info != nil { // check if we already have this version switch { case announcement.Timestamp == hub.Info.Timestamp && !selfcheck: // The new copy is not saved, as we expect the versions to be identical. // Also, the new version has not been validated at this point. return //nolint:nakedret case announcement.Timestamp < hub.Info.Timestamp: // Received an old version, do not update. err = fmt.Errorf( "%wannouncement from %s @ %s is older than current status @ %s", ErrOldData, hub.StringWithoutLocking(), time.Unix(announcement.Timestamp, 0), time.Unix(hub.Info.Timestamp, 0), ) return //nolint:nakedret } } // We received a new version. changed = true // Update timestamp here already in case validation fails. if hub.Info != nil { hub.Info.Timestamp = announcement.Timestamp } // Validate the announcement. err = hub.validateAnnouncement(announcement, scope) if err != nil { if selfcheck || hub.FirstSeen.IsZero() { err = fmt.Errorf("failed to validate announcement of %s: %w", hub.StringWithoutLocking(), err) return //nolint:nakedret } log.Warningf("spn/hub: received an invalid announcement of %s: %s", hub.StringWithoutLocking(), err) // If a previously fully validated Hub publishes an update that breaks it, a // soft-fail will accept the faulty changes, but mark is as invalid and // forward it to neighbors. This way the invalid update is propagated through // the network and all nodes will mark it as invalid an thus ingore the Hub // until the issue is fixed. } // Only save announcement if it is valid. if err == nil { hub.Info = announcement } // Set FirstSeen timestamp when we see this Hub for the first time. if hub.FirstSeen.IsZero() { hub.FirstSeen = time.Now().UTC() } return //nolint:nakedret } func (h *Hub) validateAnnouncement(announcement *Announcement, scope Scope) error { // value formatting if err := announcement.validateFormatting(); err != nil { return err } // check parsables if err := announcement.prepare(true); err != nil { return fmt.Errorf("failed to prepare announcement: %w", err) } // check timestamp if announcement.Timestamp > time.Now().Add(clockSkewTolerance).Unix() { return fmt.Errorf( "announcement from %s @ %s is from the future", announcement.ID, time.Unix(announcement.Timestamp, 0), ) } // check for illegal IP address changes if h.Info != nil { switch { case h.Info.IPv4 != nil && announcement.IPv4 == nil: h.VerifiedIPs = false return errors.New("previously announced IPv4 address missing") case h.Info.IPv4 != nil && !announcement.IPv4.Equal(h.Info.IPv4): h.VerifiedIPs = false return errors.New("IPv4 address changed") case h.Info.IPv6 != nil && announcement.IPv6 == nil: h.VerifiedIPs = false return errors.New("previously announced IPv6 address missing") case h.Info.IPv6 != nil && !announcement.IPv6.Equal(h.Info.IPv6): h.VerifiedIPs = false return errors.New("IPv6 address changed") } } // validate IP scopes if announcement.IPv4 != nil { ipScope := netutils.GetIPScope(announcement.IPv4) switch { case scope == ScopeLocal && !ipScope.IsLAN(): return errors.New("IPv4 scope violation: outside of local scope") case scope == ScopePublic && !ipScope.IsGlobal(): return errors.New("IPv4 scope violation: outside of global scope") } // Reset IP verification flag if IPv4 was added. if h.Info == nil || h.Info.IPv4 == nil { h.VerifiedIPs = false } } if announcement.IPv6 != nil { ipScope := netutils.GetIPScope(announcement.IPv6) switch { case scope == ScopeLocal && !ipScope.IsLAN(): return errors.New("IPv6 scope violation: outside of local scope") case scope == ScopePublic && !ipScope.IsGlobal(): return errors.New("IPv6 scope violation: outside of global scope") } // Reset IP verification flag if IPv6 was added. if h.Info == nil || h.Info.IPv6 == nil { h.VerifiedIPs = false } } return nil } // Export exports the status with the given signature configuration. func (s *Status) Export(env *jess.Envelope) ([]byte, error) { // pack msg, err := dsd.Dump(s, dsd.JSON) if err != nil { return nil, fmt.Errorf("failed to pack status: %w", err) } return SignHubMsg(msg, env, false) } // ApplyStatus applies a status update if it passes all the checks. func ApplyStatus(existingHub *Hub, data []byte, mapName string, scope Scope, selfcheck bool) (hub *Hub, known, changed bool, err error) { // Set valid/invalid status based on the return error. defer func() { if hub != nil { if err != nil && !errors.Is(err, ErrOldData) { hub.InvalidStatus = true } else { hub.InvalidStatus = false } } }() // open and verify var msg []byte msg, hub, known, err = OpenHubMsg(existingHub, data, mapName, false) // Lock hub if we have one. if hub != nil && !selfcheck { hub.Lock() defer hub.Unlock() } // Check if there was an error with the Hub msg. if err != nil { return //nolint:nakedret } // parse status := &Status{} _, err = dsd.Load(msg, status) if err != nil { return //nolint:nakedret } // version check if hub.Status != nil { // check if we already have this version switch { case status.Timestamp == hub.Status.Timestamp && !selfcheck: // The new copy is not saved, as we expect the versions to be identical. // Also, the new version has not been validated at this point. return //nolint:nakedret case status.Timestamp < hub.Status.Timestamp: // Received an old version, do not update. err = fmt.Errorf( "%wstatus from %s @ %s is older than current status @ %s", ErrOldData, hub.StringWithoutLocking(), time.Unix(status.Timestamp, 0), time.Unix(hub.Status.Timestamp, 0), ) return //nolint:nakedret } } // We received a new version. changed = true // Update timestamp here already in case validation fails. if hub.Status != nil { hub.Status.Timestamp = status.Timestamp } // Validate the status. err = hub.validateStatus(status) if err != nil { if selfcheck { err = fmt.Errorf("failed to validate status of %s: %w", hub.StringWithoutLocking(), err) return //nolint:nakedret } log.Warningf("spn/hub: received an invalid status of %s: %s", hub.StringWithoutLocking(), err) // If a previously fully validated Hub publishes an update that breaks it, a // soft-fail will accept the faulty changes, but mark is as invalid and // forward it to neighbors. This way the invalid update is propagated through // the network and all nodes will mark it as invalid an thus ingore the Hub // until the issue is fixed. } // Only save status if it is valid, else mark it as invalid. if err == nil { hub.Status = status } return //nolint:nakedret } func (h *Hub) validateStatus(status *Status) error { // value formatting if err := status.validateFormatting(); err != nil { return err } // check timestamp if status.Timestamp > time.Now().Add(clockSkewTolerance).Unix() { return fmt.Errorf( "status from %s @ %s is from the future", h.ID, time.Unix(status.Timestamp, 0), ) } // TODO: validate status.Keys return nil } // CreateHubSignet creates a signet with the correct ID for usage as a Hub Identity. func CreateHubSignet(toolID string, securityLevel int) (private, public *jess.Signet, err error) { private, err = jess.GenerateSignet(toolID, securityLevel) if err != nil { return nil, nil, fmt.Errorf("failed to generate key: %w", err) } err = private.StoreKey() if err != nil { return nil, nil, fmt.Errorf("failed to store private key: %w", err) } // get public key for creating the Hub ID public, err = private.AsRecipient() if err != nil { return nil, nil, fmt.Errorf("failed to get public key: %w", err) } err = public.StoreKey() if err != nil { return nil, nil, fmt.Errorf("failed to store public key: %w", err) } // assign IDs private.ID = createHubID(public.Scheme, public.Key) public.ID = private.ID return private, public, nil } func createHubID(scheme string, pubkey []byte) string { // compile scheme and public key c := container.New() c.AppendAsBlock([]byte(scheme)) c.AppendAsBlock(pubkey) return lhash.Digest(lhash.BLAKE2b_256, c.CompileData()).Base58() } func verifyHubID(id string, scheme string, pubkey []byte) (ok bool) { // load labeled hash from ID labeledHash, err := lhash.FromBase58(id) if err != nil { return false } // compile scheme and public key c := container.New() c.AppendAsBlock([]byte(scheme)) c.AppendAsBlock(pubkey) // check if it matches return labeledHash.MatchesData(c.CompileData()) } ================================================ FILE: spn/hub/update_test.go ================================================ package hub import ( "fmt" "testing" "github.com/safing/jess" "github.com/safing/structures/dsd" ) func TestHubUpdate(t *testing.T) { t.Parallel() // message signing testData := []byte{0} s1, err := jess.GenerateSignet("Ed25519", 0) if err != nil { t.Fatal(err) } err = s1.StoreKey() if err != nil { t.Fatal(err) } fmt.Printf("s1: %+v\n", s1) s1e, err := s1.AsRecipient() if err != nil { t.Fatal(err) } err = s1e.StoreKey() if err != nil { t.Fatal(err) } s1e.ID = createHubID(s1e.Scheme, s1e.Key) s1.ID = s1e.ID t.Logf("generated hub ID: %s", s1.ID) env := jess.NewUnconfiguredEnvelope() env.SuiteID = jess.SuiteSignV1 env.Senders = []*jess.Signet{s1} s, err := env.Correspondence(nil) if err != nil { t.Fatal(err) } letter, err := s.Close(testData) if err != nil { t.Fatal(err) } // smuggle the key letter.Keys = append(letter.Keys, &jess.Seal{ Value: s1e.Key, }) t.Logf("letter with smuggled key: %+v", letter) // pack data, err := letter.ToDSD(dsd.JSON) if err != nil { t.Fatal(err) } _, _, _, err = OpenHubMsg(nil, data, "test", true) //nolint:dogsled if err != nil { t.Fatal(err) } } ================================================ FILE: spn/instance.go ================================================ package spn import ( "context" "fmt" "sync/atomic" "time" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" "github.com/safing/portmaster/base/metrics" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/base/runtime" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service" "github.com/safing/portmaster/service/core" "github.com/safing/portmaster/service/core/base" "github.com/safing/portmaster/service/intel/filterlists" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/ui" "github.com/safing/portmaster/service/updates" "github.com/safing/portmaster/spn/access" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/captain" "github.com/safing/portmaster/spn/crew" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/navigator" "github.com/safing/portmaster/spn/patrol" "github.com/safing/portmaster/spn/ships" "github.com/safing/portmaster/spn/sluice" "github.com/safing/portmaster/spn/terminal" ) // Instance is an instance of a Portmaster service. type Instance struct { ctx context.Context cancelCtx context.CancelFunc shutdownCtx context.Context cancelShutdownCtx context.CancelFunc serviceGroup *mgr.Group binDir string dataDir string exitCode atomic.Int32 base *base.Base database *dbmodule.DBModule config *config.Config api *api.API metrics *metrics.Metrics runtime *runtime.Runtime rng *rng.Rng core *core.Core binaryUpdates *updates.Updater intelUpdates *updates.Updater geoip *geoip.GeoIP netenv *netenv.NetEnv filterLists *filterlists.FilterLists access *access.Access cabin *cabin.Cabin navigator *navigator.Navigator captain *captain.Captain crew *crew.Crew docks *docks.Docks patrol *patrol.Patrol ships *ships.Ships sluice *sluice.SluiceModule terminal *terminal.TerminalModule ui *ui.UI CommandLineOperation func() error ShouldRestart bool } // New returns a new Portmaster service instance. func New(svcCfg *service.ServiceConfig) (*Instance, error) { // Initialize config. err := svcCfg.Init() if err != nil { return nil, fmt.Errorf("internal service config error: %w", err) } // Make sure data dir exists, so that child directories don't dictate the permissions. err = utils.EnsureDirectory(svcCfg.DataDir, utils.PublicReadExecPermission) if err != nil { return nil, fmt.Errorf("data directory %s is not accessible: %w", svcCfg.DataDir, err) } // Create instance to pass it to modules. instance := &Instance{ binDir: svcCfg.BinDir, dataDir: svcCfg.DataDir, } instance.ctx, instance.cancelCtx = context.WithCancel(context.Background()) instance.shutdownCtx, instance.cancelShutdownCtx = context.WithCancel(context.Background()) // Base modules instance.base, err = base.New(instance) if err != nil { return instance, fmt.Errorf("create base module: %w", err) } instance.database, err = dbmodule.New(instance) if err != nil { return instance, fmt.Errorf("create database module: %w", err) } instance.config, err = config.New(instance) if err != nil { return instance, fmt.Errorf("create config module: %w", err) } instance.api, err = api.New(instance) if err != nil { return instance, fmt.Errorf("create api module: %w", err) } instance.metrics, err = metrics.New(instance) if err != nil { return instance, fmt.Errorf("create metrics module: %w", err) } instance.runtime, err = runtime.New(instance) if err != nil { return instance, fmt.Errorf("create runtime module: %w", err) } instance.rng, err = rng.New(instance) if err != nil { return instance, fmt.Errorf("create rng module: %w", err) } // Service modules binaryUpdateConfig, intelUpdateConfig, err := service.MakeUpdateConfigs(svcCfg) if err != nil { return instance, fmt.Errorf("create updates config: %w", err) } //Enable autodownload and autoapply binaryUpdateConfig.AutoDownload = true binaryUpdateConfig.AutoApply = true instance.binaryUpdates, err = updates.New(instance, "Binary Updater", *binaryUpdateConfig) if err != nil { return instance, fmt.Errorf("create updates module: %w", err) } instance.intelUpdates, err = updates.New(instance, "Intel Updater", *intelUpdateConfig) if err != nil { return instance, fmt.Errorf("create updates module: %w", err) } instance.core, err = core.New(instance) if err != nil { return instance, fmt.Errorf("create core module: %w", err) } instance.geoip, err = geoip.New(instance) if err != nil { return instance, fmt.Errorf("create customlist module: %w", err) } instance.netenv, err = netenv.New(instance) if err != nil { return instance, fmt.Errorf("create netenv module: %w", err) } instance.filterLists, err = filterlists.New(instance) if err != nil { return instance, fmt.Errorf("create filterLists module: %w", err) } // SPN modules instance.access, err = access.New(instance) if err != nil { return instance, fmt.Errorf("create access module: %w", err) } instance.cabin, err = cabin.New(instance) if err != nil { return instance, fmt.Errorf("create cabin module: %w", err) } instance.navigator, err = navigator.New(instance) if err != nil { return instance, fmt.Errorf("create navigator module: %w", err) } instance.crew, err = crew.New(instance) if err != nil { return instance, fmt.Errorf("create crew module: %w", err) } instance.docks, err = docks.New(instance) if err != nil { return instance, fmt.Errorf("create docks module: %w", err) } instance.patrol, err = patrol.New(instance) if err != nil { return instance, fmt.Errorf("create patrol module: %w", err) } instance.ships, err = ships.New(instance) if err != nil { return instance, fmt.Errorf("create ships module: %w", err) } instance.sluice, err = sluice.New(instance) if err != nil { return instance, fmt.Errorf("create sluice module: %w", err) } instance.terminal, err = terminal.New(instance) if err != nil { return instance, fmt.Errorf("create terminal module: %w", err) } instance.captain, err = captain.New(instance) if err != nil { return instance, fmt.Errorf("create captain module: %w", err) } // Add all modules to instance group. instance.serviceGroup = mgr.NewGroup( instance.base, instance.database, instance.config, instance.api, instance.metrics, instance.runtime, instance.rng, instance.core, instance.binaryUpdates, instance.intelUpdates, instance.geoip, instance.netenv, instance.access, instance.cabin, instance.navigator, instance.captain, instance.crew, instance.docks, instance.patrol, instance.ships, instance.sluice, instance.terminal, ) return instance, nil } // AddModule validates the given module and adds it to the service group, if all requirements are met. // All modules must be added before anything is done with the instance. func (i *Instance) AddModule(m mgr.Module) { i.serviceGroup.Add(m) } // SleepyModule is an interface for modules that can enter some sort of sleep mode. type SleepyModule interface { SetSleep(enabled bool) } // SetSleep sets sleep mode on all modules that satisfy the SleepyModule interface. func (i *Instance) SetSleep(enabled bool) { for _, module := range i.serviceGroup.Modules() { if sm, ok := module.(SleepyModule); ok { sm.SetSleep(enabled) } } } // BinDir returns the directory for binaries. // This directory may be read-only. func (i *Instance) BinDir() string { return i.binDir } // DataDir returns the directory for variable data. // This directory is expected to be read/writeable. func (i *Instance) DataDir() string { return i.dataDir } // Database returns the database module. func (i *Instance) Database() *dbmodule.DBModule { return i.database } // Config returns the config module. func (i *Instance) Config() *config.Config { return i.config } // API returns the api module. func (i *Instance) API() *api.API { return i.api } // Metrics returns the metrics module. func (i *Instance) Metrics() *metrics.Metrics { return i.metrics } // Runtime returns the runtime module. func (i *Instance) Runtime() *runtime.Runtime { return i.runtime } // Rng returns the rng module. func (i *Instance) Rng() *rng.Rng { return i.rng } // Base returns the base module. func (i *Instance) Base() *base.Base { return i.base } // BinaryUpdates returns the updates module. func (i *Instance) BinaryUpdates() *updates.Updater { return i.binaryUpdates } // IntelUpdates returns the updates module. func (i *Instance) IntelUpdates() *updates.Updater { return i.intelUpdates } // GeoIP returns the geoip module. func (i *Instance) GeoIP() *geoip.GeoIP { return i.geoip } // NetEnv returns the netenv module. func (i *Instance) NetEnv() *netenv.NetEnv { return i.netenv } // Access returns the access module. func (i *Instance) Access() *access.Access { return i.access } // Cabin returns the cabin module. func (i *Instance) Cabin() *cabin.Cabin { return i.cabin } // Captain returns the captain module. func (i *Instance) Captain() *captain.Captain { return i.captain } // Crew returns the crew module. func (i *Instance) Crew() *crew.Crew { return i.crew } // Docks returns the crew module. func (i *Instance) Docks() *docks.Docks { return i.docks } // Navigator returns the navigator module. func (i *Instance) Navigator() *navigator.Navigator { return i.navigator } // Patrol returns the patrol module. func (i *Instance) Patrol() *patrol.Patrol { return i.patrol } // Ships returns the ships module. func (i *Instance) Ships() *ships.Ships { return i.ships } // Sluice returns the ships module. func (i *Instance) Sluice() *sluice.SluiceModule { return i.sluice } // Terminal returns the terminal module. func (i *Instance) Terminal() *terminal.TerminalModule { return i.terminal } // UI returns the ui module. func (i *Instance) UI() *ui.UI { return i.ui } // FilterLists returns the filterLists module. func (i *Instance) FilterLists() *filterlists.FilterLists { return i.filterLists } // Core returns the core module. func (i *Instance) Core() *core.Core { return i.core } // Events // GetEventSPNConnected return the event manager for the SPN connected event. func (i *Instance) GetEventSPNConnected() *mgr.EventMgr[struct{}] { return i.captain.EventSPNConnected } // Special functions // SetCmdLineOperation sets a command line operation to be executed instead of starting the system. This is useful when functions need all modules to be prepared for a special operation. func (i *Instance) SetCmdLineOperation(f func() error) { i.CommandLineOperation = f } // GetStates returns the current states of all group modules. func (i *Instance) GetStates() []mgr.StateUpdate { return i.serviceGroup.GetStates() } // AddStatesCallback adds the given callback function to all group modules that // expose a state manager at States(). func (i *Instance) AddStatesCallback(callbackName string, callback mgr.EventCallbackFunc[mgr.StateUpdate]) { i.serviceGroup.AddStatesCallback(callbackName, callback) } // Ready returns whether all modules in the main service module group have been started and are still running. func (i *Instance) Ready() bool { return i.serviceGroup.Ready() } // Start starts the instance. func (i *Instance) Start() error { return i.serviceGroup.Start() } // Stop stops the instance and cancels the instance context when done. func (i *Instance) Stop() error { return i.serviceGroup.Stop() } // RestartExitCode will instruct portmaster-start to restart the process immediately, potentially with a new version. const RestartExitCode = 23 // Restart asynchronously restarts the instance. // This only works if the underlying system/process supports this. func (i *Instance) Restart() { // Send a restart event, give it 10ms extra to propagate. i.core.EventRestart.Submit(struct{}{}) time.Sleep(10 * time.Millisecond) // Set the restart flag and shutdown. i.ShouldRestart = true i.shutdown(RestartExitCode) } // Shutdown asynchronously stops the instance. func (i *Instance) Shutdown() { // Send a shutdown event, give it 10ms extra to propagate. i.core.EventShutdown.Submit(struct{}{}) time.Sleep(10 * time.Millisecond) i.shutdown(0) } func (i *Instance) shutdown(exitCode int) { // Only shutdown once. if i.IsShuttingDown() { return } // Cancel main context. i.cancelCtx() // Set given exit code. i.exitCode.Store(int32(exitCode)) // Start shutdown asynchronously in a separate manager. m := mgr.New("instance") m.Go("shutdown", func(w *mgr.WorkerCtx) error { // Stop all modules. if err := i.Stop(); err != nil { w.Error("failed to shutdown", "err", err) } // Cancel shutdown process context. i.cancelShutdownCtx() return nil }) } // Ctx returns the instance context. // It is canceled when shutdown is started. func (i *Instance) Ctx() context.Context { return i.ctx } // IsShuttingDown returns whether the instance is shutting down. func (i *Instance) IsShuttingDown() bool { return i.ctx.Err() != nil } // ShuttingDown returns a channel that is triggered when the instance starts shutting down. func (i *Instance) ShuttingDown() <-chan struct{} { return i.ctx.Done() } // ShutdownCtx returns the instance shutdown context. // It is canceled when shutdown is complete. func (i *Instance) ShutdownCtx() context.Context { return i.shutdownCtx } // IsShutDown returns whether the instance has stopped. func (i *Instance) IsShutDown() bool { return i.shutdownCtx.Err() != nil } // ShutDownComplete returns a channel that is triggered when the instance has shut down. func (i *Instance) ShutdownComplete() <-chan struct{} { return i.shutdownCtx.Done() } // ExitCode returns the set exit code of the instance. func (i *Instance) ExitCode() int { return int(i.exitCode.Load()) } // ShouldRestartIsSet returns whether the service/instance should be restarted. func (i *Instance) ShouldRestartIsSet() bool { return i.ShouldRestart } // CommandLineOperationIsSet returns whether the command line option is set. func (i *Instance) CommandLineOperationIsSet() bool { return i.CommandLineOperation != nil } // CommandLineOperationExecute executes the set command line option. func (i *Instance) CommandLineOperationExecute() error { return i.CommandLineOperation() } // SPNGroup fakes interface conformance. // SPNGroup is only needed on SPN clients. func (i *Instance) SPNGroup() *mgr.ExtendedGroup { return nil } // Unsupported Modules. // Notifications returns nil. func (i *Instance) Notifications() *notifications.Notifications { return nil } ================================================ FILE: spn/navigator/api.go ================================================ package navigator import ( "bytes" "errors" "fmt" "math" "net/http" "sort" "strconv" "strings" "sync" "text/tabwriter" "time" "github.com/awalterschulze/gographviz" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/hub" ) var ( apiMapsLock sync.Mutex apiMaps = make(map[string]*Map) ) func addMapToAPI(m *Map) { apiMapsLock.Lock() defer apiMapsLock.Unlock() apiMaps[m.Name] = m } func getMapForAPI(name string) (m *Map, ok bool) { apiMapsLock.Lock() defer apiMapsLock.Unlock() m, ok = apiMaps[name] return } func removeMapFromAPI(name string) { apiMapsLock.Lock() defer apiMapsLock.Unlock() delete(apiMaps, name) } func registerAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/pins`, Read: api.PermitUser, StructFunc: handleMapPinsRequest, Name: "Get SPN map pins", Description: "Returns a list of pins on the map.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/intel/update`, Write: api.PermitSelf, ActionFunc: handleIntelUpdateRequest, Name: "Update map intelligence.", Description: "Updates the intel data of the map.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/optimization`, Read: api.PermitUser, StructFunc: handleMapOptimizationRequest, Name: "Get SPN map optimization", Description: "Returns the calculated optimization for the map.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/optimization/table`, Read: api.PermitUser, DataFunc: handleMapOptimizationTableRequest, Name: "Get SPN map optimization as a table", Description: "Returns the calculated optimization for the map as a table.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/measurements`, Read: api.PermitUser, StructFunc: handleMapMeasurementsRequest, Name: "Get SPN map measurements", Description: "Returns the measurements of the map.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/measurements/table`, MimeType: api.MimeTypeText, Read: api.PermitUser, DataFunc: handleMapMeasurementsTableRequest, Name: "Get SPN map measurements as a table", Description: "Returns the measurements of the map as a table.", }); err != nil { return err } if err := api.RegisterEndpoint(api.Endpoint{ Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/graph{format:\.[a-z]{2,4}}`, Read: api.PermitUser, HandlerFunc: handleMapGraphRequest, Name: "Get SPN map graph", Description: "Returns a graph of the given SPN map.", Parameters: []api.Parameter{ { Method: http.MethodGet, Field: "map (in path)", Value: "name of map", Description: "Specify the map you want to get the map for. The main map is called `main`.", }, { Method: http.MethodGet, Field: "format (in path)", Value: "file type", Description: "Specify the format you want to get the map in. Available values: `dot`, `html`. Please note that the html format is only available in development mode.", }, }, }); err != nil { return err } // Register API endpoints from other files. if err := registerRouteAPIEndpoints(); err != nil { return err } return nil } func handleMapPinsRequest(ar *api.Request) (i interface{}, err error) { // Get map. m, ok := getMapForAPI(ar.URLVars["map"]) if !ok { return nil, errors.New("map not found") } // Export all pins. sortedPins := m.sortedPins(true) exportedPins := make([]*PinExport, len(sortedPins)) for key, pin := range sortedPins { exportedPins[key] = pin.Export() } return exportedPins, nil } func handleIntelUpdateRequest(ar *api.Request) (msg string, err error) { // Get map. m, ok := getMapForAPI(ar.URLVars["map"]) if !ok { return "", errors.New("map not found") } // Parse new intel data. newIntel, err := hub.ParseIntel(ar.InputData) if err != nil { return "", fmt.Errorf("failed to parse intel data: %w", err) } // Apply intel data. err = m.UpdateIntel(newIntel, cfgOptionTrustNodeNodes()) if err != nil { return "", fmt.Errorf("failed to apply intel data: %w", err) } return "successfully applied given intel data", nil } func handleMapOptimizationRequest(ar *api.Request) (i interface{}, err error) { // Get map. m, ok := getMapForAPI(ar.URLVars["map"]) if !ok { return nil, errors.New("map not found") } return m.Optimize(nil) } func handleMapOptimizationTableRequest(ar *api.Request) (data []byte, err error) { // Get map. m, ok := getMapForAPI(ar.URLVars["map"]) if !ok { return nil, errors.New("map not found") } // Get optimization result. result, err := m.Optimize(nil) if err != nil { return nil, err } // Read lock map, as we access pins. m.RLock() defer m.RUnlock() // Get cranes for additional metadata. assignedCranes := docks.GetAllAssignedCranes() // Write metadata. buf := bytes.NewBuffer(nil) buf.WriteString("Optimization:\n") fmt.Fprintf(buf, "Purpose: %s\n", result.Purpose) if len(result.Approach) == 1 { fmt.Fprintf(buf, "Approach: %s\n", result.Approach[0]) } else if len(result.Approach) > 1 { buf.WriteString("Approach:\n") for _, approach := range result.Approach { fmt.Fprintf(buf, " - %s\n", approach) } } fmt.Fprintf(buf, "MaxConnect: %d\n", result.MaxConnect) fmt.Fprintf(buf, "StopOthers: %v\n", result.StopOthers) // Build table of suggested connections. buf.WriteString("\nSuggested Connections:\n") tabWriter := tabwriter.NewWriter(buf, 8, 4, 3, ' ', 0) fmt.Fprint(tabWriter, "Hub Name\tReason\tDuplicate\tCountry\tRegion\tLatency\tCapacity\tCost\tGeo Prox.\tHub ID\tLifetime Usage\tPeriod Usage\tProt\tStatus\n") for _, suggested := range result.SuggestedConnections { var dupe string if suggested.Duplicate { dupe = "yes" } else { // Only lock dupes once. suggested.pin.measurements.Lock() defer suggested.pin.measurements.Unlock() } // Add row. fmt.Fprintf(tabWriter, "%s\t%s\t%s\t%s\t%s\t%s\t%.2fMbit/s\t%.2fc\t%.2f%%\t%s", suggested.Hub.Info.Name, suggested.Reason, dupe, getPinCountry(suggested.pin), suggested.pin.region.getName(), suggested.pin.measurements.Latency, float64(suggested.pin.measurements.Capacity)/1000000, suggested.pin.measurements.CalculatedCost, suggested.pin.measurements.GeoProximity, suggested.Hub.ID, ) // Add usage stats. if crane, ok := assignedCranes[suggested.Hub.ID]; ok { addUsageStatsToTable(crane, tabWriter) } // Add linebreak. fmt.Fprint(tabWriter, "\n") } _ = tabWriter.Flush() return buf.Bytes(), nil } // addUsageStatsToTable compiles some usage stats of a lane and addes them to the table. // Table Fields: Lifetime Usage, Period Usage, Prot, Mine. func addUsageStatsToTable(crane *docks.Crane, tabWriter *tabwriter.Writer) { ltIn, ltOut, ltStart, pIn, pOut, pStart := crane.NetState.GetTrafficStats() ltDuration := time.Since(ltStart) pDuration := time.Since(pStart) // Build ownership and stopping info. var status string isMine := crane.IsMine() isStopping := crane.IsStopping() stoppingRequested, stoppingRequestedByPeer, markedStoppingAt := crane.NetState.StoppingState() if isMine { status = "mine" } if isStopping || stoppingRequested || stoppingRequestedByPeer { if isMine { status += " - " } status += "stopping " if stoppingRequested { status += "
`, "`", graph.String(), "`", )) } // Write response. w.Header().Set("Content-Type", mimeType+"; charset=utf-8") w.Header().Set("Content-Length", strconv.Itoa(len(responseData))) w.WriteHeader(http.StatusOK) _, err := w.Write(responseData) if err != nil { log.Tracer(r.Context()).Warningf("api: failed to write response: %s", err) } } func graphNodeLabel(pin *Pin) (s string) { var comment string switch { case pin.State == StateNone: comment = "dead" case pin.State.Has(StateIsHomeHub): comment = "Home" case pin.State.HasAnyOf(StateSummaryDisregard): comment = "disregarded" case !pin.State.Has(StateSummaryRegard): comment = "not regarded" case pin.State.Has(StateTrusted): comment = "trusted" } if comment != "" { comment = fmt.Sprintf("\n(%s)", comment) } if pin.Hub.Status.Load >= 80 { comment += fmt.Sprintf("\nHIGH LOAD: %d", pin.Hub.Status.Load) } return fmt.Sprintf( `"%s%s"`, strings.ReplaceAll(pin.Hub.Name(), " ", "\n"), comment, ) } func graphNodeTooltip(pin *Pin) string { // Gather IP info. var v4Info, v6Info string if pin.Hub.Info.IPv4 != nil { if pin.LocationV4 != nil { v4Info = fmt.Sprintf( "%s (%s AS%d %s)", pin.Hub.Info.IPv4.String(), pin.LocationV4.Country.Code, pin.LocationV4.AutonomousSystemNumber, pin.LocationV4.AutonomousSystemOrganization, ) } else { v4Info = pin.Hub.Info.IPv4.String() } } if pin.Hub.Info.IPv6 != nil { if pin.LocationV6 != nil { v6Info = fmt.Sprintf( "%s (%s AS%d %s)", pin.Hub.Info.IPv6.String(), pin.LocationV6.Country.Code, pin.LocationV6.AutonomousSystemNumber, pin.LocationV6.AutonomousSystemOrganization, ) } else { v6Info = pin.Hub.Info.IPv6.String() } } return fmt.Sprintf( `"ID: %s States: %s Version: %s IPv4: %s IPv6: %s Load: %d Cost: %.2f"`, pin.Hub.ID, pin.State, pin.Hub.Status.Version, v4Info, v6Info, pin.Hub.Status.Load, pin.Cost, ) } func graphEdgeTooltip(from, to *Pin, lane *Lane) string { return fmt.Sprintf( `"%s <> %s Latency: %s Capacity: %.2f Mbit/s Cost: %.2f"`, from.Hub.Info.Name, to.Hub.Info.Name, lane.Latency, float64(lane.Capacity)/1000000, lane.Cost, ) } // Graphviz colors. // See https://graphviz.org/doc/info/colors.html const ( graphColorWarning = "orange2" graphColorError = "red2" graphColorHomeAndConnected = "steelblue2" graphColorDisregard = "tomato2" graphColorNotRegard = "tan2" graphColorTrusted = "seagreen2" graphColorDefaultNode = "seashell2" graphColorDefaultEdge = "black" graphColorNone = "transparent" ) func graphNodeColor(pin *Pin) string { switch { case pin.State == StateNone: return graphColorNone case pin.Hub.Status.Load >= 95: return graphColorError case pin.Hub.Status.Load >= 80: return graphColorWarning case pin.State.Has(StateIsHomeHub): return graphColorHomeAndConnected case pin.State.HasAnyOf(StateSummaryDisregard): return graphColorDisregard case !pin.State.Has(StateSummaryRegard): return graphColorNotRegard case pin.State.Has(StateTrusted): return graphColorTrusted default: return graphColorDefaultNode } } func graphNodeBorderColor(pin *Pin) string { switch { case pin.HasActiveTerminal(): return graphColorHomeAndConnected default: return graphColorNone } } func graphEdgeColor(from, to *Pin, lane *Lane) string { // Check lane stats. if lane.Capacity == 0 || lane.Latency == 0 { return graphColorWarning } // Alert if capacity is under 10Mbit/s or latency is over 100ms. if lane.Capacity < 10000000 || lane.Latency > 100*time.Millisecond { return graphColorError } // Check for active edge forward. if to.HasActiveTerminal() && len(to.Connection.Route.Path) >= 2 { secondLastHopIndex := len(to.Connection.Route.Path) - 2 if to.Connection.Route.Path[secondLastHopIndex].HubID == from.Hub.ID { return graphColorHomeAndConnected } } // Check for active edge backward. if from.HasActiveTerminal() && len(from.Connection.Route.Path) >= 2 { secondLastHopIndex := len(from.Connection.Route.Path) - 2 if from.Connection.Route.Path[secondLastHopIndex].HubID == to.Hub.ID { return graphColorHomeAndConnected } } // Return default color if edge is not active. return graphColorDefaultEdge } ================================================ FILE: spn/navigator/api_route.go ================================================ package navigator import ( "bytes" "errors" "fmt" mrand "math/rand" "net" "net/http" "strings" "text/tabwriter" "time" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/endpoints" ) func registerRouteAPIEndpoints() error { if err := api.RegisterEndpoint(api.Endpoint{ Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/route/to/{destination:[a-z0-9_\.:-]{1,255}}`, Read: api.PermitUser, ActionFunc: handleRouteCalculationRequest, Name: "Calculate Route through SPN", Description: "Returns a textual representation of the routing process.", Parameters: []api.Parameter{ { Method: http.MethodGet, Field: "profile", Value: "|global", Description: "Specify a profile ID to load more settings for simulation.", }, { Method: http.MethodGet, Field: "encrypted", Value: "true", Description: "Specify to signify that the simulated connection should be regarded as encrypted. Only valid with a profile.", }, }, }); err != nil { return err } return nil } func handleRouteCalculationRequest(ar *api.Request) (msg string, err error) { //nolint:maintidx // Get map. m, ok := getMapForAPI(ar.URLVars["map"]) if !ok { return "", errors.New("map not found") } // Get profile ID. profileID := ar.Request.URL.Query().Get("profile") // Parse destination and prepare options. entity := &intel.Entity{} destination := ar.URLVars["destination"] matchFor := DestinationHub var ( introText string locationV4, locationV6 *geoip.Location opts *Options ) switch { case destination == "": // Destination is required. return "", errors.New("no destination provided") case destination == "home": if profileID != "" { return "", errors.New("cannot apply profile to home hub route") } // Simulate finding home hub. locations, ok := netenv.GetInternetLocation() if !ok || len(locations.All) == 0 { return "", errors.New("failed to locate own device for finding home hub") } introText = fmt.Sprintf("looking for home hub near %s and %s", locations.BestV4(), locations.BestV6()) locationV4 = locations.BestV4().LocationOrNil() locationV6 = locations.BestV6().LocationOrNil() matchFor = HomeHub // START of copied from captain/navigation.go // Get own entity. // Checking the entity against the entry policies is somewhat hit and miss // anyway, as the device location is an approximation. var myEntity *intel.Entity if dl := locations.BestV4(); dl != nil && dl.IP != nil { myEntity = (&intel.Entity{IP: dl.IP}).Init(0) myEntity.FetchData(ar.Context()) } else if dl := locations.BestV6(); dl != nil && dl.IP != nil { myEntity = (&intel.Entity{IP: dl.IP}).Init(0) myEntity.FetchData(ar.Context()) } // Build navigation options for searching for a home hub. homePolicy, err := endpoints.ParseEndpoints(config.GetAsStringArray("spn/homePolicy", []string{})()) if err != nil { return "", fmt.Errorf("failed to parse home hub policy: %w", err) } opts = &Options{ Home: &HomeHubOptions{ HubPolicies: []endpoints.Endpoints{homePolicy}, CheckHubPolicyWith: myEntity, }, } // Add requirement to only use Safing nodes when not using community nodes. if !config.GetAsBool("spn/useCommunityNodes", true)() { opts.Home.RequireVerifiedOwners = []string{"Safing"} } // Require a trusted home node when the routing profile requires less than two hops. routingProfile := GetRoutingProfile(config.GetAsString(profile.CfgOptionRoutingAlgorithmKey, DefaultRoutingProfileID)()) if routingProfile.MinHops < 2 { opts.Home.Regard = opts.Home.Regard.Add(StateTrusted) } // END of copied case net.ParseIP(destination) != nil: entity.IP = net.ParseIP(destination) fallthrough case netutils.IsValidFqdn(destination): fallthrough case netutils.IsValidFqdn(destination + "."): // Resolve domain to IP, if not inherired from a previous case. var ignoredIPs int if entity.IP == nil { entity.Domain = destination // Resolve name to IPs. ips, err := net.DefaultResolver.LookupIP(ar.Context(), "ip", destination) if err != nil { return "", fmt.Errorf("failed to lookup IP address of %s: %w", destination, err) } if len(ips) == 0 { return "", fmt.Errorf("failed to lookup IP address of %s: no result", destination) } // Shuffle IPs. if len(ips) >= 2 { mr := mrand.New(mrand.NewSource(time.Now().UnixNano())) //nolint:gosec mr.Shuffle(len(ips), func(i, j int) { ips[i], ips[j] = ips[j], ips[i] }) } entity.IP = ips[0] ignoredIPs = len(ips) - 1 } entity.Init(0) // Get location of IP. location, ok := entity.GetLocation(ar.Context()) if !ok { return "", fmt.Errorf("failed to get geoip location for %s: %s", entity.IP, entity.LocationError) } // Assign location to separate variables. if entity.IP.To4() != nil { locationV4 = location } else { locationV6 = location } // Set intro text. if entity.Domain != "" { introText = fmt.Sprintf("looking for route to %s at %s\n(ignoring %d additional IPs returned by DNS)", entity.IP, formatLocation(location), ignoredIPs) } else { introText = fmt.Sprintf("looking for route to %s at %s", entity.IP, formatLocation(location)) } // Get profile. if profileID != "" { var lp *profile.LayeredProfile if profileID == "global" { // Create new empty profile for easy access to global settings. lp = profile.NewLayeredProfile(profile.New(nil)) } else { // Get local profile by ID. localProfile, err := profile.GetLocalProfile(profileID, nil, nil) if err != nil { return "", fmt.Errorf("failed to get profile: %w", err) } lp = localProfile.LayeredProfile() } opts = DeriveTunnelOptions( lp, entity, ar.Request.URL.Query().Has("encrypted"), ) } else { opts = m.defaultOptions() } default: return "", errors.New("invalid destination provided") } // Finalize entity. entity.Init(0) // Start formatting output. lines := []string{ "Routing simulation: " + introText, "Please note that this routing simulation does match the behavior of regular routing to 100%.", "", } // Print options. // ================== lines = append(lines, "Routing Options:") lines = append(lines, "Algorithm: "+opts.RoutingProfile) if opts.Home != nil { lines = append(lines, "Home Options:") lines = append(lines, fmt.Sprintf(" Regard: %s", opts.Home.Regard)) lines = append(lines, fmt.Sprintf(" Disregard: %s", opts.Home.Disregard)) lines = append(lines, fmt.Sprintf(" No Default: %v", opts.Home.NoDefaults)) lines = append(lines, fmt.Sprintf(" Hub Policies: %v", opts.Home.HubPolicies)) lines = append(lines, fmt.Sprintf(" Require Verified Owners: %v", opts.Home.RequireVerifiedOwners)) } if opts.Transit != nil { lines = append(lines, "Transit Options:") lines = append(lines, fmt.Sprintf(" Regard: %s", opts.Transit.Regard)) lines = append(lines, fmt.Sprintf(" Disregard: %s", opts.Transit.Disregard)) lines = append(lines, fmt.Sprintf(" No Default: %v", opts.Transit.NoDefaults)) lines = append(lines, fmt.Sprintf(" Hub Policies: %v", opts.Transit.HubPolicies)) lines = append(lines, fmt.Sprintf(" Require Verified Owners: %v", opts.Transit.RequireVerifiedOwners)) } if opts.Destination != nil { lines = append(lines, "Destination Options:") lines = append(lines, fmt.Sprintf(" Regard: %s", opts.Destination.Regard)) lines = append(lines, fmt.Sprintf(" Disregard: %s", opts.Destination.Disregard)) lines = append(lines, fmt.Sprintf(" No Default: %v", opts.Destination.NoDefaults)) lines = append(lines, fmt.Sprintf(" Hub Policies: %v", opts.Destination.HubPolicies)) lines = append(lines, fmt.Sprintf(" Require Verified Owners: %v", opts.Destination.RequireVerifiedOwners)) if opts.Destination.CheckHubPolicyWith != nil { lines = append(lines, " Check Hub Policy With:") if opts.Destination.CheckHubPolicyWith.Domain != "" { lines = append(lines, fmt.Sprintf(" Domain: %v", opts.Destination.CheckHubPolicyWith.Domain)) } if opts.Destination.CheckHubPolicyWith.IP != nil { lines = append(lines, fmt.Sprintf(" IP: %v", opts.Destination.CheckHubPolicyWith.IP)) } if opts.Destination.CheckHubPolicyWith.Port != 0 { lines = append(lines, fmt.Sprintf(" Port: %v", opts.Destination.CheckHubPolicyWith.Port)) } } } lines = append(lines, "\n") // Find nearest hubs. // ================== // Start operating in map. m.RLock() defer m.RUnlock() // Check if map is populated. if m.isEmpty() { return "", ErrEmptyMap } // Find nearest hubs. nbPins, err := m.findNearestPins(locationV4, locationV6, opts, matchFor, true) if err != nil { lines = append(lines, fmt.Sprintf("FAILED to find any suitable exit hub: %s", err)) return strings.Join(lines, "\n"), nil // return "", fmt.Errorf("failed to search for nearby pins: %w", err) } // Print found exits to table. lines = append(lines, "Considered Exits (cheapest 10% are shuffled)") buf := bytes.NewBuffer(nil) tabWriter := tabwriter.NewWriter(buf, 8, 4, 3, ' ', 0) fmt.Fprint(tabWriter, "Hub Name\tCost\tLocation\n") for _, nbPin := range nbPins.pins { fmt.Fprintf(tabWriter, "%s\t%.0f\t%s\n", nbPin.pin.Hub.Name(), nbPin.cost, formatMultiLocation(nbPin.pin.LocationV4, nbPin.pin.LocationV6), ) } _ = tabWriter.Flush() lines = append(lines, buf.String()) // Print too expensive exits to table. lines = append(lines, "Too Expensive Exits:") buf = bytes.NewBuffer(nil) tabWriter = tabwriter.NewWriter(buf, 8, 4, 3, ' ', 0) fmt.Fprint(tabWriter, "Hub Name\tCost\tLocation\n") for _, nbPin := range nbPins.debug.tooExpensive { fmt.Fprintf(tabWriter, "%s\t%.0f\t%s\n", nbPin.pin.Hub.Name(), nbPin.cost, formatMultiLocation(nbPin.pin.LocationV4, nbPin.pin.LocationV6), ) } _ = tabWriter.Flush() lines = append(lines, buf.String()) // Print disregarded exits to table. lines = append(lines, "Disregarded Exits:") buf = bytes.NewBuffer(nil) tabWriter = tabwriter.NewWriter(buf, 8, 4, 3, ' ', 0) fmt.Fprint(tabWriter, "Hub Name\tReason\tStates\n") for _, nbPin := range nbPins.debug.disregarded { fmt.Fprintf(tabWriter, "%s\t%s\t%s\n", nbPin.pin.Hub.Name(), nbPin.reason, nbPin.pin.State, ) } _ = tabWriter.Flush() lines = append(lines, buf.String()) // Find routes. // ============ // Unless we looked for a home node. if destination == "home" { return strings.Join(lines, "\n"), nil } // Find routes. routes, err := m.findRoutes(nbPins, opts) if err != nil { lines = append(lines, fmt.Sprintf("FAILED to find routes: %s", err)) return strings.Join(lines, "\n"), nil // return "", fmt.Errorf("failed to find routes: %w", err) } // Print found routes to table. lines = append(lines, "Considered Routes (cheapest 10% are shuffled)") buf = bytes.NewBuffer(nil) tabWriter = tabwriter.NewWriter(buf, 8, 4, 3, ' ', 0) fmt.Fprint(tabWriter, "Cost\tPath\n") for _, route := range routes.All { fmt.Fprintf(tabWriter, "%.0f\t%s\n", route.TotalCost, formatRoute(route, entity.IP), ) } _ = tabWriter.Flush() lines = append(lines, buf.String()) return strings.Join(lines, "\n"), nil } func formatLocation(loc *geoip.Location) string { return fmt.Sprintf( "%s (%s - AS%d %s)", loc.Country.Name, loc.Country.Code, loc.AutonomousSystemNumber, loc.AutonomousSystemOrganization, ) } func formatMultiLocation(a, b *geoip.Location) string { switch { case a != nil: return formatLocation(a) case b != nil: return formatLocation(b) default: return "" } } func formatRoute(r *Route, dst net.IP) string { s := make([]string, 0, len(r.Path)+1) for i, hop := range r.Path { if i == 0 { s = append(s, hop.pin.Hub.Name()) } else { s = append(s, fmt.Sprintf(">> %.2fc >> %s", hop.Cost, hop.pin.Hub.Name())) } } s = append(s, fmt.Sprintf(">> %.2fc >> %s", r.DstCost, dst)) return strings.Join(s, " ") } ================================================ FILE: spn/navigator/costs.go ================================================ package navigator import "time" const ( nearestPinsMaxCostDifference = 5000 nearestPinsMinimum = 10 ) // CalculateLaneCost calculates the cost of using a Lane based on the given // Lane latency and capacity. // Ranges from 0 to 10000. func CalculateLaneCost(latency time.Duration, capacity int) (cost float32) { // - One point for every ms in latency (linear) if latency != 0 { cost += float32(latency) / float32(time.Millisecond) } else { // Add cautious default cost if latency is not available. cost += 1000 } capacityFloat := float32(capacity) switch { case capacityFloat == 0: // Add cautious default cost if capacity is not available. cost += 4000 case capacityFloat < cap1Mbit: // - Between 1000 and 10000 points for ranges below 1Mbit/s cost += 1000 + 9000*((cap1Mbit-capacityFloat)/cap1Mbit) case capacityFloat < cap10Mbit: // - Between 100 and 1000 points for ranges below 10Mbit/s cost += 100 + 900*((cap10Mbit-capacityFloat)/cap10Mbit) case capacityFloat < cap100Mbit: // - Between 20 and 100 points for ranges below 100Mbit/s cost += 20 + 80*((cap100Mbit-capacityFloat)/cap100Mbit) case capacityFloat < cap1Gbit: // - Between 5 and 20 points for ranges below 1Gbit/s cost += 5 + 15*((cap1Gbit-capacityFloat)/cap1Gbit) case capacityFloat < cap10Gbit: // - Between 0 and 5 points for ranges below 10Gbit/s cost += 5 * ((cap10Gbit - float32(capacity)) / cap10Gbit) } return cost } // CalculateHubCost calculates the cost of using a Hub based on the given Hub load. // Ranges from 100 to 10000. func CalculateHubCost(load int) (cost float32) { switch { case load >= 100: return 10000 case load >= 95: return 1000 case load >= 80: return 500 default: return 100 } } // CalculateDestinationCost calculates the cost of a destination hub to a // destination server based on the given proximity. // Ranges from 0 to 2500. func CalculateDestinationCost(proximity float32) (cost float32) { // Invert from proximity (0-100) to get a distance value. distance := 100 - proximity // Take the distance to the power of three and then divide by hundred in order to // make high distances exponentially more expensive. return (distance * distance * distance) / 100 } ================================================ FILE: spn/navigator/database.go ================================================ package navigator import ( "fmt" "strings" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/storage" "github.com/safing/portmaster/service/mgr" ) var mapDBController *database.Controller // StorageInterface provices a storage.Interface to the // configuration manager. type StorageInterface struct { storage.InjectBase } // Database prefixes: // Pins: map:main/ // DNS Requests: network:tree//dns/ // IP Connections: network:tree//ip/ func makeDBKey(mapName, hubID string) string { return fmt.Sprintf("map:%s/%s", mapName, hubID) } func parseDBKey(key string) (mapName, hubID string) { // Split into segments. segments := strings.Split(key, "/") // Keys have 1 or 2 segments. switch len(segments) { case 1: return segments[0], "" case 2: return segments[0], segments[1] default: return "", "" } } // Get returns a database record. func (s *StorageInterface) Get(key string) (record.Record, error) { // Parse key and check if valid. mapName, hubID := parseDBKey(key) if mapName == "" || hubID == "" { return nil, storage.ErrNotFound } // Get map. m, ok := getMapForAPI(mapName) if !ok { return nil, storage.ErrNotFound } // Get Pin from map. pin, ok := m.GetPin(hubID) if !ok { return nil, storage.ErrNotFound } return pin.Export(), nil } // Query returns a an iterator for the supplied query. func (s *StorageInterface) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) { // Parse key and check if valid. mapName, _ := parseDBKey(q.DatabaseKeyPrefix()) if mapName == "" { return nil, storage.ErrNotFound } // Get map. m, ok := getMapForAPI(mapName) if !ok { return nil, storage.ErrNotFound } // Start query worker. it := iterator.New() module.mgr.Go("map query", func(_ *mgr.WorkerCtx) error { s.processQuery(m, q, it) return nil }) return it, nil } func (s *StorageInterface) processQuery(m *Map, q *query.Query, it *iterator.Iterator) { // Return all matching pins. for _, pin := range m.sortedPins(true) { export := pin.Export() if q.Matches(export) { select { case it.Next <- export: case <-it.Done: return } } } it.Finish(nil) } func registerMapDatabase() error { _, err := database.Register(&database.Database{ Name: "map", Description: "SPN Network Maps", StorageType: database.StorageTypeInjected, }) if err != nil { return err } controller, err := database.InjectDatabase("map", &StorageInterface{}) if err != nil { return err } mapDBController = controller return nil } func withdrawMapDatabase() { mapDBController.Withdraw() } // PushPinChanges pushes all changed pins to subscribers. func (m *Map) PushPinChanges() { module.mgr.Go("push pin changes", m.pushPinChangesWorker) } func (m *Map) pushPinChangesWorker(ctx *mgr.WorkerCtx) error { m.RLock() defer m.RUnlock() for _, pin := range m.all { if pin.pushChanges.SetToIf(true, false) { mapDBController.PushUpdate(pin.Export()) } } return nil } // pushChange pushes changes of the pin, if the pushChanges flag is set. func (pin *Pin) pushChange() { // Check before starting the worker. if pin.pushChanges.IsNotSet() { return } // Start worker to push changes. module.mgr.Go("push pin change", func(ctx *mgr.WorkerCtx) error { if pin.pushChanges.SetToIf(true, false) { mapDBController.PushUpdate(pin.Export()) } return nil }) } ================================================ FILE: spn/navigator/findnearest.go ================================================ package navigator import ( "errors" "fmt" mrand "math/rand" "sort" "strings" "time" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/spn/hub" ) const ( // defaultMaxNearbyMatches defines a default value of how many matches a // nearby pin find operation in a map should return. defaultMaxNearbyMatches = 100 // defaultRandomizeNearbyPinTopPercent defines the top percent of a nearby // pins set that should be randomized for balancing purposes. // Range: 0-1. defaultRandomizeNearbyPinTopPercent = 0.1 ) // nearbyPins is a list of nearby Pins to a certain location. type nearbyPins struct { pins []*nearbyPin minPins int maxPins int maxCost float32 cutOffLimit float32 randomizeTopPercent float32 debug *nearbyPinsDebug } // nearbyPinsDebug holds additional debugging for nearbyPins. type nearbyPinsDebug struct { tooExpensive []*nearbyPin disregarded []*nearbyDisregardedPin } // nearbyDisregardedPin represents a disregarded pin. type nearbyDisregardedPin struct { pin *Pin reason string } // nearbyPin represents a Pin and the proximity to a certain location. type nearbyPin struct { pin *Pin cost float32 } // Len is the number of elements in the collection. func (nb *nearbyPins) Len() int { return len(nb.pins) } // Less reports whether the element with index i should sort before the element // with index j. func (nb *nearbyPins) Less(i, j int) bool { return nb.pins[i].cost < nb.pins[j].cost } // Swap swaps the elements with indexes i and j. func (nb *nearbyPins) Swap(i, j int) { nb.pins[i], nb.pins[j] = nb.pins[j], nb.pins[i] } // add potentially adds a Pin to the list of nearby Pins. func (nb *nearbyPins) add(pin *Pin, cost float32) { if len(nb.pins) > nb.minPins && nb.maxCost > 0 && cost > nb.maxCost { // Add debug data if enabled. if nb.debug != nil { nb.debug.tooExpensive = append(nb.debug.tooExpensive, &nearbyPin{ pin: pin, cost: cost, }, ) } return } nb.pins = append(nb.pins, &nearbyPin{ pin: pin, cost: cost, }) } // contains checks if the collection contains a Pin. func (nb *nearbyPins) get(id string) *nearbyPin { for _, nbPin := range nb.pins { if nbPin.pin.Hub.ID == id { return nbPin } } return nil } // clean sort and shortens the list to the configured maximum. func (nb *nearbyPins) clean() { // Sort nearby Pins so that the closest one is on top. sort.Sort(nb) // Set maximum cost based on max difference, if we have enough pins. if len(nb.pins) >= nb.minPins { nb.maxCost = nb.pins[0].cost + nb.cutOffLimit } // Remove superfluous Pins from the list. if len(nb.pins) > nb.maxPins { // Add debug data if enabled. if nb.debug != nil { nb.debug.tooExpensive = append(nb.debug.tooExpensive, nb.pins[nb.maxPins:]...) } nb.pins = nb.pins[:nb.maxPins] } // Remove Pins that are too costly. if len(nb.pins) > nb.minPins { // Search for first pin that is too costly. okUntil := nb.minPins for ; okUntil < len(nb.pins); okUntil++ { if nb.pins[okUntil].cost > nb.maxCost { break } } // Add debug data if enabled. if nb.debug != nil { nb.debug.tooExpensive = append(nb.debug.tooExpensive, nb.pins[okUntil:]...) } // Cut off the list at that point. nb.pins = nb.pins[:okUntil] } } // randomizeTop randomized to the top nearest pins for balancing the network. func (nb *nearbyPins) randomizeTop() { switch { case nb.randomizeTopPercent == 0: // Check if randomization is enabled. return case len(nb.pins) < 2: // Check if we have enough pins to work with. return } // Find randomization set. randomizeUpTo := len(nb.pins) threshold := nb.pins[0].cost * (1 + nb.randomizeTopPercent) for i, nb := range nb.pins { // Find first value above the threshold to stop. if nb.cost > threshold { randomizeUpTo = i break } } // Shuffle top set. if randomizeUpTo >= 2 { mr := mrand.New(mrand.NewSource(time.Now().UnixNano())) //nolint:gosec mr.Shuffle(randomizeUpTo, nb.Swap) } } // FindNearestHubs searches for the nearest Hubs to the given IP address. The returned Hubs must not be modified in any way. func (m *Map) FindNearestHubs(locationV4, locationV6 *geoip.Location, opts *Options, matchFor HubType) ([]*hub.Hub, error) { m.RLock() defer m.RUnlock() // Check if map is populated. if m.isEmpty() { return nil, ErrEmptyMap } // Set default options if unset. if opts == nil { opts = m.defaultOptions() } // Find nearest Pins. nearby, err := m.findNearestPins(locationV4, locationV6, opts, matchFor, false) if err != nil { return nil, err } // Convert to Hub list and return. hubs := make([]*hub.Hub, 0, len(nearby.pins)) for _, nbPin := range nearby.pins { hubs = append(hubs, nbPin.pin.Hub) } return hubs, nil } func (m *Map) findNearestPins(locationV4, locationV6 *geoip.Location, opts *Options, matchFor HubType, debug bool) (*nearbyPins, error) { // Fail if no location is provided. if locationV4 == nil && locationV6 == nil { return nil, errors.New("no location provided") } // Raise maxMatches to nearestPinsMinimum. maxMatches := defaultMaxNearbyMatches if maxMatches < nearestPinsMinimum { maxMatches = nearestPinsMinimum } // Create nearby Pins list. nearby := &nearbyPins{ minPins: nearestPinsMinimum, maxPins: maxMatches, cutOffLimit: nearestPinsMaxCostDifference, randomizeTopPercent: defaultRandomizeNearbyPinTopPercent, } if debug { nearby.debug = &nearbyPinsDebug{} } // Create pin matcher. matcher := opts.Matcher(matchFor, m.intel) // Iterate over all Pins in the Map to find the nearest ones. for _, pin := range m.all { var cost float32 // Check if the Pin matches the criteria. if !matcher(pin) { // Add debug data if enabled. if nearby.debug != nil && pin.State.Has(StateActive|StateReachable) { nearby.debug.disregarded = append(nearby.debug.disregarded, &nearbyDisregardedPin{ pin: pin, reason: "does not match general criteria", }, ) } // Debugging: // log.Tracef("spn/navigator: skipping %s with states %s for finding nearest", pin, pin.State) continue } // Check if the Hub supports at least one IP version we are looking for. switch { case locationV4 != nil && pin.LocationV4 != nil: // Both have IPv4! case locationV6 != nil && pin.LocationV6 != nil: // Both have IPv6! default: // Hub does not support any IP version we need. // Add debug data if enabled. if nearby.debug != nil { nearby.debug.disregarded = append(nearby.debug.disregarded, &nearbyDisregardedPin{ pin: pin, reason: "does not support the required IP version", }, ) } continue } // If finding a home hub and the global routing profile is set to home ("VPN"), // check if all local IP versions are available on the Hub. if matchFor == HomeHub && cfgOptionRoutingAlgorithm() == RoutingProfileHomeID { switch { case locationV4 != nil && pin.LocationV4 == nil: // Device has IPv4, but Hub does not! fallthrough case locationV6 != nil && pin.LocationV6 == nil: // Device has IPv6, but Hub does not! // Add debug data if enabled. if nearby.debug != nil { nearby.debug.disregarded = append(nearby.debug.disregarded, &nearbyDisregardedPin{ pin: pin, reason: "home hub needs all IP versions of client (when Home/VPN routing)", }, ) } continue } } // 1. Calculate cost based on distance if locationV4 != nil && pin.LocationV4 != nil { if locationV4.IsAnycast && m.home != nil { // If the destination is anycast, calculate cost though proximity to home hub instead, if possible. cost = lessButPositive(cost, CalculateDestinationCost( proximityBetweenPins(pin, m.home), )) } else { // Regular cost calculation through proximity. cost = lessButPositive(cost, CalculateDestinationCost( locationV4.EstimateNetworkProximity(pin.LocationV4), )) } } if locationV6 != nil && pin.LocationV6 != nil { if locationV6.IsAnycast && m.home != nil { // If the destination is anycast, calculate cost though proximity to home hub instead, if possible. cost = lessButPositive(cost, CalculateDestinationCost( proximityBetweenPins(pin, m.home), )) } else { // Regular cost calculation through proximity. cost = lessButPositive(cost, CalculateDestinationCost( locationV6.EstimateNetworkProximity(pin.LocationV6), )) } } // If no cost could be calculated, fall back to a default value. if cost == 0 { cost = CalculateDestinationCost(50) // proximity out of 0-100 } // Debugging: // if matchFor == HomeHub { // log.Tracef("spn/navigator: adding %.2f proximity cost to home hub %s", cost, pin.Hub) // } // 2. Add cost based on Hub status cost += CalculateHubCost(pin.Hub.Status.Load) // Debugging: // if matchFor == HomeHub { // log.Tracef("spn/navigator: adding %.2f hub cost to home hub %s", CalculateHubCost(pin.Hub.Status.Load), pin.Hub) // } // 3. If matching a home hub, add cost based on capacity/latency performance. if matchFor == HomeHub { // Find best capacity/latency values. var ( bestCapacity int bestLatency time.Duration ) for _, lane := range pin.Hub.Status.Lanes { if lane.Capacity > bestCapacity { bestCapacity = lane.Capacity } if bestLatency == 0 || lane.Latency < bestLatency { bestLatency = lane.Latency } } // Add cost of best capacity/latency values. cost += CalculateLaneCost(bestLatency, bestCapacity) // Debugging: // log.Tracef("spn/navigator: adding %.2f lane cost to home hub %s", CalculateLaneCost(bestLatency, bestCapacity), pin.Hub) // log.Debugf("spn/navigator: total cost of %.2f to home hub %s", cost, pin.Hub) } nearby.add(pin, cost) // Clean the nearby list if have collected more than two times the max amount. if len(nearby.pins) >= nearby.maxPins*2 { nearby.clean() } } // Check if we found any nearby pins if nearby.Len() == 0 { return nil, ErrAllPinsDisregarded } // Clean one last time and return the list. nearby.clean() // Randomize top nearest pins for load balancing. nearby.randomizeTop() // Debugging: // if matchFor == HomeHub { // log.Debug("spn/navigator: nearby pins:") // for _, nbPin := range nearby.pins { // log.Debugf("spn/navigator: nearby pin %s", nbPin) // } // } return nearby, nil } func (nb *nearbyPins) String() string { s := make([]string, 0, len(nb.pins)) for _, nbPin := range nb.pins { s = append(s, nbPin.String()) } return strings.Join(s, ", ") } func (nb *nearbyPin) String() string { return fmt.Sprintf("%s at %.2fc", nb.pin, nb.cost) } func proximityBetweenPins(a, b *Pin) float32 { var x, y float32 // Get IPv4 network proximity. if a.LocationV4 != nil && b.LocationV4 != nil { x = a.LocationV4.EstimateNetworkProximity(b.LocationV4) } // Get IPv6 network proximity. if a.LocationV6 != nil && b.LocationV6 != nil { y = a.LocationV6.EstimateNetworkProximity(b.LocationV6) } // Return higher proximity. if x > y { return x } return y } func lessButPositive(a, b float32) float32 { switch { case a == 0: return b case b == 0: return a case a < b: return a default: return b } } ================================================ FILE: spn/navigator/findnearest_test.go ================================================ package navigator import ( "testing" ) func TestFindNearest(t *testing.T) { t.Parallel() // Create map and lock faking in order to guarantee reproducability of faked data. m := getDefaultTestMap() fakeLock.Lock() defer fakeLock.Unlock() for range 100 { // Create a random destination address ip4, loc4 := createGoodIP(true) nbPins, err := m.findNearestPins(loc4, nil, m.DefaultOptions(), DestinationHub, false) if err != nil { t.Error(err) } else { t.Logf("Pins near %s: %s", ip4, nbPins) } } for range 100 { // Create a random destination address ip6, loc6 := createGoodIP(true) nbPins, err := m.findNearestPins(nil, loc6, m.DefaultOptions(), DestinationHub, false) if err != nil { t.Error(err) } else { t.Logf("Pins near %s: %s", ip6, nbPins) } } } /* TODO: Find a way to quickly generate good geoip data on the fly, as we don't want to measure IP address generation, but only finding the nearest pins. func BenchmarkFindNearest(b *testing.B) { // Create map and lock faking in order to guarantee reproducability of faked data. m := getDefaultTestMap() fakeLock.Lock() defer fakeLock.Unlock() b.ResetTimer() for i := 0; i < b.N; i++ { // Create a random destination address var dstIP net.IP if i%2 == 0 { dstIP = net.ParseIP(gofakeit.IPv4Address()) } else { dstIP = net.ParseIP(gofakeit.IPv6Address()) } _, err := m.findNearestPins(dstIP, m.DefaultOptions(),DestinationHub if err != nil { b.Error(err) } } } */ func findFakeHomeHub(m *Map) { // Create fake IP address. _, loc4 := createGoodIP(true) _, loc6 := createGoodIP(false) nbPins, err := m.findNearestPins(loc4, loc6, m.defaultOptions(), HomeHub, false) if err != nil { panic(err) } if len(nbPins.pins) == 0 { panic("could not find a Home Hub") } // Set Home. m.home = nbPins.pins[0].pin // Recalculate reachability. if err := m.recalculateReachableHubs(); err != nil { panic(err) } } func TestNearbyPinsCleaning(t *testing.T) { t.Parallel() testCleaning(t, []float32{10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, 3) testCleaning(t, []float32{10, 11, 12, 13, 50, 60, 70, 80, 90, 100}, 4) testCleaning(t, []float32{10, 11, 12, 40, 50, 60, 70, 80, 90, 100}, 3) testCleaning(t, []float32{10, 11, 30, 40, 50, 60, 70, 80, 90, 100}, 3) } func testCleaning(t *testing.T, costs []float32, expectedLeftOver int) { t.Helper() nb := &nearbyPins{ minPins: 3, maxPins: 5, cutOffLimit: 10, } // Simulate usage. for _, cost := range costs { // Add to list. nb.add(nil, cost) // Clean once in a while. if len(nb.pins) > nb.maxPins { nb.clean() } } // Final clean. nb.clean() // Check results. t.Logf("result: %+v", nb.pins) if len(nb.pins) != expectedLeftOver { t.Errorf("unexpected amount of left over pins: %+v", nb.pins) } } ================================================ FILE: spn/navigator/findroutes.go ================================================ package navigator import ( "errors" "fmt" "net" "github.com/safing/portmaster/service/intel/geoip" ) const ( // defaultMaxRouteMatches defines a default value of how many matches a // route find operation in a map should return. defaultMaxRouteMatches = 10 // defaultRandomizeRoutesTopPercent defines the top percent of a routes // set that should be randomized for balancing purposes. // Range: 0-1. defaultRandomizeRoutesTopPercent = 0.1 ) // FindRoutes finds possible routes to the given IP, with the given options. func (m *Map) FindRoutes(ip net.IP, opts *Options) (*Routes, error) { m.Lock() defer m.Unlock() // Check if map is populated. if m.isEmpty() { return nil, ErrEmptyMap } // Check if home hub is set. if m.home == nil { return nil, ErrHomeHubUnset } // Get the location of the given IP address. var locationV4, locationV6 *geoip.Location var err error // Save whether the given IP address is a IPv4 or IPv6 address. if v4 := ip.To4(); v4 != nil { locationV4, err = geoip.GetLocation(ip) } else { locationV6, err = geoip.GetLocation(ip) } if err != nil { return nil, fmt.Errorf("failed to get IP location: %w", err) } // Set default options if unset. if opts == nil { opts = m.defaultOptions() } // Handle special home routing profile. if opts.RoutingProfile == RoutingProfileHomeID { switch { case locationV4 != nil && m.home.LocationV4 == nil: // Destination is IPv4, but Hub has no IPv4! // Upgrade routing profile. opts.RoutingProfile = RoutingProfileSingleHopID case locationV6 != nil && m.home.LocationV6 == nil: // Destination is IPv6, but Hub has no IPv6! // Upgrade routing profile. opts.RoutingProfile = RoutingProfileSingleHopID default: // Return route with only home hub for home hub routing. return &Routes{ All: []*Route{{ Path: []*Hop{{ pin: m.home, HubID: m.home.Hub.ID, }}, Algorithm: RoutingProfileHomeID, }}, }, nil } } // Find nearest Pins. nearby, err := m.findNearestPins(locationV4, locationV6, opts, DestinationHub, false) if err != nil { return nil, err } return m.findRoutes(nearby, opts) } // FindRouteToHub finds possible routes to the given Hub, with the given options. func (m *Map) FindRouteToHub(hubID string, opts *Options) (*Routes, error) { m.Lock() defer m.Unlock() // Get Pin. pin, ok := m.all[hubID] if !ok { return nil, ErrHubNotFound } // Create a nearby with a single Pin. nearby := &nearbyPins{ pins: []*nearbyPin{ { pin: pin, }, }, } // Find a route to the given Hub. return m.findRoutes(nearby, opts) } func (m *Map) findRoutes(dsts *nearbyPins, opts *Options) (*Routes, error) { if m.home == nil { return nil, ErrHomeHubUnset } // Initialize matchers. var done bool transitMatcher := opts.Transit.Matcher(m.intel) destinationMatcher := opts.Destination.Matcher(m.intel) routingProfile := GetRoutingProfile(opts.RoutingProfile) // Create routes collector. routes := &Routes{ maxRoutes: defaultMaxRouteMatches, randomizeTopPercent: defaultRandomizeRoutesTopPercent, } // TODO: // Start from the destination and use HopDistance to prioritize // exploring routes that are in the right direction. // How would we handle selecting the destination node based on route to client? // Should we just try all destinations? // Create initial route. route := &Route{ // Estimate how much space we will need, else it'll just expand. Path: make([]*Hop, 1, routingProfile.MinHops+routingProfile.MaxExtraHops), } route.Path[0] = &Hop{ pin: m.home, // TODO: add initial cost } // exploreHop explores a hop (Lane) to a connected Pin. var exploreHop func(route *Route, lane *Lane) // exploreLanes explores all Lanes of a Pin. exploreLanes := func(route *Route) { for _, lane := range route.Path[len(route.Path)-1].pin.ConnectedTo { // Check if we are done and can skip the rest. if done { return } // Explore! exploreHop(route, lane) } } exploreHop = func(route *Route, lane *Lane) { // Check if the Pin should be regarded as Transit Hub. if !transitMatcher(lane.Pin) { return } // Add Pin to the current path and remove when done. route.addHop(lane.Pin, lane.Cost+lane.Pin.Cost) defer route.removeHop() // Check if the route would even make it into the list. if !routes.isGoodEnough(route) { return } // Check route compliance. // This also includes some algorithm-based optimizations. switch routingProfile.checkRouteCompliance(route, routes) { case routeOk: // Route would be compliant. // Now, check if the last hop qualifies as a Destination Hub. if destinationMatcher(lane.Pin) { // Get Pin as nearby Pin. nbPin := dsts.get(lane.Pin.Hub.ID) if nbPin != nil { // Pin is listed as selected Destination Hub! // Complete route to add destination ("last mile") cost. route.completeRoute(nbPin.cost) routes.add(route) // We have found a route and have come to an end here. return } } // The Route is compliant, but we haven't found a Destination Hub yet. fallthrough case routeNonCompliant: // Continue exploration. exploreLanes(route) case routeDisqualified: fallthrough default: // Route is disqualified and we can return without further exploration. } } // Start the hop exploration tree. // This will fork into about a gazillion branches and add all the found valid // routes to the list. exploreLanes(route) // Check if we found anything. if len(routes.All) == 0 { return nil, errors.New("failed to find any routes") } // Randomize top routes for load balancing. routes.randomizeTop() // Copy remaining data to routes. routes.makeExportReady(opts.RoutingProfile) // Debugging: // log.Debug("spn/navigator: routes:") // for _, route := range routes.All { // log.Debugf("spn/navigator: %s", route) // } return routes, nil } ================================================ FILE: spn/navigator/findroutes_test.go ================================================ package navigator import ( "net" "testing" ) func TestFindRoutes(t *testing.T) { t.Parallel() // Create map and lock faking in order to guarantee reproducability of faked data. m := getOptimizedDefaultTestMap(t) fakeLock.Lock() defer fakeLock.Unlock() for i := range 1 { // Create a random destination address dstIP, _ := createGoodIP(i%2 == 0) routes, err := m.FindRoutes(dstIP, m.DefaultOptions()) switch { case err != nil: t.Error(err) case len(routes.All) == 0: t.Logf("No routes for %s", dstIP) default: t.Logf("Best route for %s: %s", dstIP, routes.All[0]) } } } func BenchmarkFindRoutes(b *testing.B) { // Create map and lock faking in order to guarantee reproducability of faked data. m := getOptimizedDefaultTestMap(nil) fakeLock.Lock() defer fakeLock.Unlock() // Pre-generate 100 IPs preGenIPs := make([]net.IP, 0, 100) for i := range cap(preGenIPs) { ip, _ := createGoodIP(i%2 == 0) preGenIPs = append(preGenIPs, ip) } b.ResetTimer() for i := range b.N { routes, err := m.FindRoutes(preGenIPs[i%len(preGenIPs)], m.DefaultOptions()) if err != nil { b.Error(err) } else { b.Logf("Best route for %s: %s", preGenIPs[i%len(preGenIPs)], routes.All[0]) } } } ================================================ FILE: spn/navigator/intel.go ================================================ package navigator import ( "context" "errors" "golang.org/x/exp/slices" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/service/profile/endpoints" "github.com/safing/portmaster/spn/hub" ) // UpdateIntel supplies the map with new intel data. The data is not copied, so // it must not be modified after being supplied. If the map is empty, the // bootstrap hubs will be added to the map. func (m *Map) UpdateIntel(update *hub.Intel, trustNodes []string) error { // Check if intel data is already parsed. if update.Parsed() == nil { return errors.New("intel data is not parsed") } m.Lock() defer m.Unlock() // Update the map's reference to the intel data. m.intel = update // Update pins with new intel data. for _, pin := range m.all { // Add/Update location data from IP addresses. pin.updateLocationData() // Override Pin Data. m.updateInfoOverrides(pin) // Update Trust and Advisory Statuses. m.updateIntelStatuses(pin, trustNodes) // Push changes. // TODO: Only set when pin changed. pin.pushChanges.Set() } // Configure the map's regions. m.updateRegions(m.intel.Regions) // Push pin changes. m.PushPinChanges() log.Infof("spn/navigator: updated intel on map %s", m.Name) // Add bootstrap hubs if map is empty. if m.isEmpty() { return m.addBootstrapHubs(m.intel.BootstrapHubs) } return nil } // GetIntel returns the map's intel data. func (m *Map) GetIntel() *hub.Intel { m.RLock() defer m.RUnlock() return m.intel } func (m *Map) updateIntelStatuses(pin *Pin, trustNodes []string) { // Reset all related states (StateSummaryStatusesAppliedFromIntel). pin.stateIntelApplied.UnSet() pin.removeStates(StateTrusted | StateUsageDiscouraged | StateUsageAsHomeDiscouraged | StateUsageAsDestinationDiscouraged) // same as: StateSummaryStatusesAppliedFromIntel // Check if Intel data is loaded. if m.intel == nil { return } // Indicate that intel statuses have been applied to the pin defer pin.stateIntelApplied.Set() // Check Hub Intel hubIntel, ok := m.intel.Hubs[pin.Hub.ID] if ok { // Apply the verified owner, if any. pin.VerifiedOwner = hubIntel.VerifiedOwner // Check if Hub is discontinued. if hubIntel.Discontinued { // Reset state, set offline and return. pin.State = StateNone pin.addStates(StateOffline) return } // Check if Hub is trusted. if hubIntel.Trusted { pin.addStates(StateTrusted) } } // Check manual trust status. switch { case slices.Contains[[]string, string](trustNodes, pin.VerifiedOwner): pin.addStates(StateTrusted) case slices.Contains[[]string, string](trustNodes, pin.Hub.ID): pin.addStates(StateTrusted) } // Check advisories. // Check for UsageDiscouraged. checkStatusList( pin, StateUsageDiscouraged, m.intel.AdviseOnlyTrustedHubs, m.intel.Parsed().HubAdvisory, ) // Check for UsageAsHomeDiscouraged. checkStatusList( pin, StateUsageAsHomeDiscouraged, m.intel.AdviseOnlyTrustedHomeHubs, m.intel.Parsed().HomeHubAdvisory, ) // Check for UsageAsDestinationDiscouraged. checkStatusList( pin, StateUsageAsDestinationDiscouraged, m.intel.AdviseOnlyTrustedDestinationHubs, m.intel.Parsed().DestinationHubAdvisory, ) } func checkStatusList(pin *Pin, state PinState, requireTrusted bool, endpointList endpoints.Endpoints) { if requireTrusted && !pin.State.Has(StateTrusted) { pin.addStates(state) return } if pin.EntityV4 != nil { result, _ := endpointList.Match(context.TODO(), pin.EntityV4) if result == endpoints.Denied { pin.addStates(state) return } } if pin.EntityV6 != nil { result, _ := endpointList.Match(context.TODO(), pin.EntityV6) if result == endpoints.Denied { pin.addStates(state) } } } func (m *Map) updateInfoOverrides(pin *Pin) { // Check if Intel data is loaded and if there are any overrides. if m.intel == nil { return } // Get overrides for this pin. hubIntel, ok := m.intel.Hubs[pin.Hub.ID] if !ok || hubIntel.Override == nil { return } overrides := hubIntel.Override // Apply overrides if overrides.CountryCode != "" { if pin.LocationV4 != nil { pin.LocationV4.Country = geoip.GetCountryInfo(overrides.CountryCode) } if pin.EntityV4 != nil { pin.EntityV4.Country = overrides.CountryCode } if pin.LocationV6 != nil { pin.LocationV6.Country = geoip.GetCountryInfo(overrides.CountryCode) } if pin.EntityV6 != nil { pin.EntityV6.Country = overrides.CountryCode } } if overrides.Coordinates != nil { if pin.LocationV4 != nil { pin.LocationV4.Coordinates = *overrides.Coordinates } if pin.EntityV4 != nil { pin.EntityV4.Coordinates = overrides.Coordinates } if pin.LocationV6 != nil { pin.LocationV6.Coordinates = *overrides.Coordinates } if pin.EntityV6 != nil { pin.EntityV6.Coordinates = overrides.Coordinates } } if overrides.ASN != 0 { if pin.LocationV4 != nil { pin.LocationV4.AutonomousSystemNumber = overrides.ASN } if pin.EntityV4 != nil { pin.EntityV4.ASN = overrides.ASN } if pin.LocationV6 != nil { pin.LocationV6.AutonomousSystemNumber = overrides.ASN } if pin.EntityV6 != nil { pin.EntityV6.ASN = overrides.ASN } } if overrides.ASOrg != "" { if pin.LocationV4 != nil { pin.LocationV4.AutonomousSystemOrganization = overrides.ASOrg } if pin.EntityV4 != nil { pin.EntityV4.ASOrg = overrides.ASOrg } if pin.LocationV6 != nil { pin.LocationV6.AutonomousSystemOrganization = overrides.ASOrg } if pin.EntityV6 != nil { pin.EntityV6.ASOrg = overrides.ASOrg } } } ================================================ FILE: spn/navigator/map.go ================================================ package navigator import ( "sort" "sync" "time" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/hub" ) // Map represent a collection of Pins and their relationship and status. type Map struct { sync.RWMutex Name string all map[string]*Pin intel *hub.Intel regions []*Region home *Pin homeTerminal *docks.CraneTerminal measuringEnabled bool hubUpdateHook *database.RegisteredHook // analysisLock guards access to all of this map's Pin.analysis, // regardedPins and the lastDesegrationAttempt fields. analysisLock sync.Mutex regardedPins []*Pin lastDesegrationAttempt time.Time } // NewMap returns a new and empty Map. func NewMap(name string, enableMeasuring bool) *Map { m := &Map{ Name: name, all: make(map[string]*Pin), measuringEnabled: enableMeasuring, } addMapToAPI(m) return m } // Close removes the map's integration, taking it "offline". func (m *Map) Close() { removeMapFromAPI(m.Name) } // GetPin returns the Pin of the Hub with the given ID. func (m *Map) GetPin(hubID string) (pin *Pin, ok bool) { m.RLock() defer m.RUnlock() pin, ok = m.all[hubID] return } // GetHome returns the current home and it's accompanying terminal. // Both may be nil. func (m *Map) GetHome() (*Pin, *docks.CraneTerminal) { m.RLock() defer m.RUnlock() return m.home, m.homeTerminal } // SetHome sets the given hub as the new home. Optionally, a terminal may be // supplied to accompany the home hub. func (m *Map) SetHome(id string, t *docks.CraneTerminal) (ok bool) { m.Lock() defer m.Unlock() // Get pin from map. newHome, ok := m.all[id] if !ok { return false } // Remove home hub state from all pins. for _, pin := range m.all { pin.removeStates(StateIsHomeHub) } // Set pin as home. m.home = newHome m.homeTerminal = t m.home.addStates(StateIsHomeHub) // Recalculate reachable. err := m.recalculateReachableHubs() if err != nil { log.Warningf("spn/navigator: failed to recalculate reachable hubs: %s", err) } m.PushPinChanges() return true } // GetAvailableCountries returns a map of countries including their information // where the map has pins suitable for the given type. func (m *Map) GetAvailableCountries(opts *Options, forType HubType) map[string]*geoip.CountryInfo { if opts == nil { opts = m.defaultOptions() } m.RLock() defer m.RUnlock() matcher := opts.Matcher(forType, m.intel) countries := make(map[string]*geoip.CountryInfo) for _, pin := range m.all { if !matcher(pin) { continue } if pin.LocationV4 != nil && countries[pin.LocationV4.Country.Code] == nil { countries[pin.LocationV4.Country.Code] = &pin.LocationV4.Country } if pin.LocationV6 != nil && countries[pin.LocationV6.Country.Code] == nil { countries[pin.LocationV6.Country.Code] = &pin.LocationV6.Country } } return countries } // isEmpty returns whether the Map is regarded as empty. func (m *Map) isEmpty() bool { if m.home != nil { // When a home hub is set, we also regard a map with only one entry to be // empty, as this will be the case for Hubs, which will have their own // entry in the Map. return len(m.all) <= 1 } return len(m.all) == 0 } func (m *Map) pinList(lockMap bool) []*Pin { if lockMap { m.RLock() defer m.RUnlock() } // Copy into slice. list := make([]*Pin, 0, len(m.all)) for _, pin := range m.all { list = append(list, pin) } return list } func (m *Map) sortedPins(lockMap bool) []*Pin { // Get list. list := m.pinList(lockMap) // Sort list. sort.Sort(sortByPinID(list)) return list } ================================================ FILE: spn/navigator/map_stats.go ================================================ package navigator import ( "fmt" "sort" "strings" ) // MapStats holds generic map statistics. type MapStats struct { Name string States map[PinState]int Lanes map[int]int ActiveTerminals int } // Stats collects and returns statistics from the map. func (m *Map) Stats() *MapStats { m.Lock() defer m.Unlock() // Create stats struct. stats := &MapStats{ Name: m.Name, States: make(map[PinState]int), Lanes: make(map[int]int), } for _, state := range allStates { stats.States[state] = 0 } // Iterate over all Pins to collect data. for _, pin := range m.all { // Count active terminals. if pin.HasActiveTerminal() { stats.ActiveTerminals++ } // Check all states. for _, state := range allStates { if pin.State.Has(state) { stats.States[state]++ } } // Count lanes. laneCnt, ok := stats.Lanes[len(pin.ConnectedTo)] if ok { stats.Lanes[len(pin.ConnectedTo)] = laneCnt + 1 } else { stats.Lanes[len(pin.ConnectedTo)] = 1 } } return stats } func (ms *MapStats) String() string { var builder strings.Builder // Write header. fmt.Fprintf(&builder, "Stats for Map %s:\n", ms.Name) // Write State Stats stateSummary := make([]string, 0, len(ms.States)) for state, cnt := range ms.States { stateSummary = append(stateSummary, fmt.Sprintf("State %s: %d Hubs", state, cnt)) } sort.Strings(stateSummary) for _, stateSum := range stateSummary { fmt.Fprintln(&builder, stateSum) } // Write Lane Stats laneStats := make([]string, 0, len(ms.Lanes)) for laneCnt, pinCnt := range ms.Lanes { laneStats = append(laneStats, fmt.Sprintf("%d Lanes: %d Hubs", laneCnt, pinCnt)) } sort.Strings(laneStats) for _, laneStat := range laneStats { fmt.Fprintln(&builder, laneStat) } return builder.String() } ================================================ FILE: spn/navigator/map_test.go ================================================ package navigator import ( "fmt" "net" "sync" "testing" "time" "github.com/brianvoe/gofakeit" "github.com/safing/jess/lhash" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/spn/hub" ) var ( fakeLock sync.Mutex defaultMapCreate sync.Once defaultMap *Map ) func getDefaultTestMap() *Map { defaultMapCreate.Do(func() { defaultMap = createRandomTestMap(1, 200) }) return defaultMap } func TestRandomMapCreation(t *testing.T) { t.Parallel() m := getDefaultTestMap() fmt.Println("All Pins:") for _, pin := range m.all { fmt.Printf("%s: %s %s\n", pin, pin.Hub.Info.IPv4, pin.Hub.Info.IPv6) } // Print stats fmt.Printf("\n%s\n", m.Stats()) // Print home fmt.Printf("Selected Home Hub: %s\n", m.home) } func createRandomTestMap(seed int64, size int) *Map { fakeLock.Lock() defer fakeLock.Unlock() // Seed with parameter to make it reproducible. gofakeit.Seed(seed) // Enforce minimum size. if size < 10 { size = 10 } // Create Hub list. hubs := make([]*hub.Hub, 0, size) // Create Intel data structure. mapIntel := &hub.Intel{ Hubs: make(map[string]*hub.HubIntel), } // Define periodic values. var currentGroup string // Create [size] fake Hubs. for i := range size { // Change group every 5 Hubs. if i%5 == 0 { currentGroup = gofakeit.Username() } // Create new fake Hub and add to the list. h := createFakeHub(currentGroup, true, mapIntel) hubs = append(hubs, h) } // Fake three superseeded Hubs. for i := range 3 { h := hubs[size-1-i] // Set FirstSeen in the past and copy an IP address of an existing Hub. h.FirstSeen = time.Now().Add(-1 * time.Hour) if i%2 == 0 { h.Info.IPv4 = hubs[i].Info.IPv4 } else { h.Info.IPv6 = hubs[i].Info.IPv6 } } // Create Lanes between Hubs in order to create the network. totalConnections := size * 10 for range totalConnections { // Get new random indexes. indexA := gofakeit.Number(0, size-1) indexB := gofakeit.Number(0, size-1) if indexA == indexB { continue } // Get Hubs and check if they are already connected. hubA := hubs[indexA] hubB := hubs[indexB] if hubA.GetLaneTo(hubB.ID) != nil { // already connected continue } if hubB.GetLaneTo(hubA.ID) != nil { // already connected continue } // Create connections. _ = hubA.AddLane(createLane(hubB.ID)) // Add the second connection in 99% of cases. // If this is missing, the Pins should not show up as connected. if gofakeit.Number(0, 100) != 0 { _ = hubB.AddLane(createLane(hubA.ID)) } } // Parse constructed intel data err := mapIntel.ParseAdvisories() if err != nil { panic(err) } // Create map and add Pins. m := NewMap(fmt.Sprintf("Test-Map-%d", seed), true) m.intel = mapIntel for _, h := range hubs { m.UpdateHub(h) } // Fake communication error with three Hubs. var i int for _, pin := range m.all { pin.MarkAsFailingFor(1 * time.Hour) pin.addStates(StateFailing) if i++; i >= 3 { break } } // Set a Home Hub. findFakeHomeHub(m) return m } func createFakeHub(group string, randomFailes bool, mapIntel *hub.Intel) *hub.Hub { // Create fake Hub ID. idSrc := gofakeit.Password(true, true, true, true, true, 64) id := lhash.Digest(lhash.BLAKE2b_256, []byte(idSrc)).Base58() ip4, _ := createGoodIP(true) ip6, _ := createGoodIP(false) // Create and return new fake Hub. h := &hub.Hub{ ID: id, Info: &hub.Announcement{ ID: id, Timestamp: time.Now().Unix(), Name: gofakeit.Username(), Group: group, // ContactAddress // TODO // ContactService // TODO // Hosters []string // TODO // Datacenter string // TODO IPv4: ip4, IPv6: ip6, }, Status: &hub.Status{ Timestamp: time.Now().Unix(), Keys: map[string]*hub.Key{ "a": { Expires: time.Now().Add(48 * time.Hour).Unix(), }, }, Load: gofakeit.Number(10, 100), }, Measurements: hub.NewMeasurements(), FirstSeen: time.Now(), } h.Measurements.Latency = createLatency() h.Measurements.Capacity = createCapacity() h.Measurements.CalculatedCost = CalculateLaneCost( h.Measurements.Latency, h.Measurements.Capacity, ) // Return if not failures of any kind should be simulated. if !randomFailes { return h } // Set hub-based states. if gofakeit.Number(0, 100) == 0 { // Fake Info message error. h.InvalidInfo = true } if gofakeit.Number(0, 100) == 0 { // Fake Status message error. h.InvalidStatus = true } if gofakeit.Number(0, 100) == 0 { // Fake expired exchange keys. for _, key := range h.Status.Keys { key.Expires = time.Now().Add(-1 * time.Hour).Unix() } } // Return if not failures of any kind should be simulated. if mapIntel == nil { return h } // Set advisory-based states. if gofakeit.Number(0, 10) == 0 { // Make Trusted State mapIntel.Hubs[h.ID] = &hub.HubIntel{ Trusted: true, } } if gofakeit.Number(0, 100) == 0 { // Discourage any usage. mapIntel.HubAdvisory = append(mapIntel.HubAdvisory, "- "+h.Info.IPv4.String()) } if gofakeit.Number(0, 100) == 0 { // Discourage Home Hub usage. mapIntel.HomeHubAdvisory = append(mapIntel.HomeHubAdvisory, "- "+h.Info.IPv4.String()) } if gofakeit.Number(0, 100) == 0 { // Discourage Destination Hub usage. mapIntel.DestinationHubAdvisory = append(mapIntel.DestinationHubAdvisory, "- "+h.Info.IPv4.String()) } return h } func createGoodIP(v4 bool) (net.IP, *geoip.Location) { var candidate net.IP for range 100 { if v4 { candidate = net.ParseIP(gofakeit.IPv4Address()) } else { candidate = net.ParseIP(gofakeit.IPv6Address()) } loc, err := geoip.GetLocation(candidate) if err == nil && loc.Coordinates.Latitude != 0 { return candidate, loc } } return candidate, nil } func createLane(toHubID string) *hub.Lane { return &hub.Lane{ ID: toHubID, Latency: createLatency(), Capacity: createCapacity(), } } func createLatency() time.Duration { // Return a value between 10ms and 100ms. return time.Duration(gofakeit.Float64Range(10, 100) * float64(time.Millisecond)) } func createCapacity() int { // Return a value between 10Mbit/s and 1Gbit/s. return gofakeit.Number(10000000, 1000000000) } ================================================ FILE: spn/navigator/measurements.go ================================================ package navigator import ( "sort" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/terminal" ) // Measurements Configuration. const ( NavigatorMeasurementTTLDefault = 4 * time.Hour NavigatorMeasurementTTLByCostBase = 6 * time.Minute NavigatorMeasurementTTLByCostMin = 4 * time.Hour NavigatorMeasurementTTLByCostMax = 50 * time.Hour // With a base TTL of 3m, this leads to: // 20c -> 2h -> raised to 4h. // 50c -> 5h // 100c -> 10h // 1000c -> 100h -> capped to 50h. ) func (m *Map) measureHubs(wc *mgr.WorkerCtx) error { if home, _ := m.GetHome(); home == nil { log.Debug("spn/navigator: skipping measuring, no home hub set") return nil } var unknownErrCnt int matcher := m.DefaultOptions().Transit.Matcher(m.GetIntel()) // Get list and sort in order to check near/low-cost hubs earlier. list := m.pinList(true) sort.Sort(sortByLowestMeasuredCost(list)) // Find first pin where any measurement has expired. for _, pin := range list { // Check if measuring is enabled. if pin.measurements == nil { continue } // Check if Pin is regarded. if !matcher(pin) { continue } // Calculate dynamic TTL. var checkWithTTL time.Duration if pin.HopDistance == 2 { // Hub is directly connected. checkWithTTL = calculateMeasurementTTLByCost( pin.measurements.GetCalculatedCost(), docks.CraneMeasurementTTLByCostBase, docks.CraneMeasurementTTLByCostMin, docks.CraneMeasurementTTLByCostMax, ) } else { checkWithTTL = calculateMeasurementTTLByCost( pin.measurements.GetCalculatedCost(), NavigatorMeasurementTTLByCostBase, NavigatorMeasurementTTLByCostMin, NavigatorMeasurementTTLByCostMax, ) } // Check if we have measured the pin within the TTL. if !pin.measurements.Expired(checkWithTTL) { continue } // Measure connection. tErr := docks.MeasureHub(wc.Ctx(), pin.Hub, checkWithTTL) // Independent of outcome, recalculate the cost. latency, _ := pin.measurements.GetLatency() capacity, _ := pin.measurements.GetCapacity() calculatedCost := CalculateLaneCost(latency, capacity) pin.measurements.SetCalculatedCost(calculatedCost) // Log result. log.Infof( "spn/navigator: updated measurements for connection to %s: %s %.2fMbit/s %.2fc", pin.Hub, latency, float64(capacity)/1000000, calculatedCost, ) switch { case tErr.IsOK(): // All good, continue. case tErr.Is(terminal.ErrTryAgainLater): if tErr.IsExternal() { // Remote is measuring, just continue with next. log.Debugf("spn/navigator: remote %s is measuring, continuing with next", pin.Hub) } else { // We are measuring, abort and restart measuring again later. log.Debugf("spn/navigator: postponing measuring because we are currently engaged in measuring") return nil } default: log.Warningf("spn/navigator: failed to measure connection to %s: %s", pin.Hub, tErr) unknownErrCnt++ if unknownErrCnt >= 3 { log.Warningf("spn/navigator: postponing measuring task because of multiple errors") return nil } } } return nil } // SaveMeasuredHubs saves all Hubs that have unsaved measurements. func (m *Map) SaveMeasuredHubs() { m.RLock() defer m.RUnlock() for _, pin := range m.all { if !pin.measurements.IsPersisted() { if err := pin.Hub.Save(); err != nil { log.Warningf("spn/navigator: failed to save Hub %s to persist measurements: %s", pin.Hub, err) } } } } func calculateMeasurementTTLByCost(cost float32, base, min, max time.Duration) time.Duration { calculated := time.Duration(cost) * base switch { case calculated < min: return min case calculated > max: return max default: return calculated } } ================================================ FILE: spn/navigator/metrics.go ================================================ package navigator import ( "sort" "sync" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/metrics" ) var metricsRegistered = abool.New() func registerMetrics() (err error) { // Only register metrics once. if !metricsRegistered.SetToIf(false, true) { return nil } // Map Stats. _, err = metrics.NewGauge( "spn/map/main/latency/all/lowest/seconds", nil, getLowestLatency, &metrics.Options{ Name: "SPN Map Lowest Latency", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/map/main/latency/fas/lowest/seconds", nil, getLowestLatencyFromFas, &metrics.Options{ Name: "SPN Map Lowest Latency", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/map/main/capacity/all/highest/bytes", nil, getHighestCapacity, &metrics.Options{ Name: "SPN Map Lowest Latency", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/map/main/capacity/fas/highest/bytes", nil, getHighestCapacityFromFas, &metrics.Options{ Name: "SPN Map Lowest Latency", Permission: api.PermitUser, }, ) if err != nil { return err } return nil } var ( mapStats *mapMetrics mapStatsExpires time.Time mapStatsLock sync.Mutex mapStatsTTL = 55 * time.Second ) type mapMetrics struct { lowestLatency float64 lowestForeignASLatency float64 highestCapacity float64 highestForeignASCapacity float64 } func getLowestLatency() float64 { return getMapStats().lowestLatency } func getLowestLatencyFromFas() float64 { return getMapStats().lowestForeignASLatency } func getHighestCapacity() float64 { return getMapStats().highestCapacity } func getHighestCapacityFromFas() float64 { return getMapStats().highestForeignASCapacity } func getMapStats() *mapMetrics { mapStatsLock.Lock() defer mapStatsLock.Unlock() // Return cache if still valid. if time.Now().Before(mapStatsExpires) { return mapStats } // Refresh. mapStats = &mapMetrics{} // Get all pins and home. list := Main.pinList(true) home, _ := Main.GetHome() // Return empty stats if we have incomplete data. if len(list) <= 1 || home == nil { mapStatsExpires = time.Now().Add(mapStatsTTL) return mapStats } // Sort by latency. sort.Sort(sortByLowestMeasuredLatency(list)) // Get lowest latency. lowestLatency, _ := list[0].measurements.GetLatency() mapStats.lowestLatency = lowestLatency.Seconds() // Find best foreign AS latency. bestForeignASPin := findFirstForeignASStatsPin(home, list) if bestForeignASPin != nil { lowestForeignASLatency, _ := bestForeignASPin.measurements.GetLatency() mapStats.lowestForeignASLatency = lowestForeignASLatency.Seconds() } // Sort by capacity. sort.Sort(sortByHighestMeasuredCapacity(list)) // Get highest capacity. highestCapacity, _ := list[0].measurements.GetCapacity() mapStats.highestCapacity = float64(highestCapacity) / 8 // Find best foreign AS capacity. bestForeignASPin = findFirstForeignASStatsPin(home, list) if bestForeignASPin != nil { highestForeignASCapacity, _ := bestForeignASPin.measurements.GetCapacity() mapStats.highestForeignASCapacity = float64(highestForeignASCapacity) / 8 } mapStatsExpires = time.Now().Add(mapStatsTTL) return mapStats } func findFirstForeignASStatsPin(home *Pin, list []*Pin) *Pin { // Find best foreign AS latency. for _, pin := range list { compared := false // Skip if IPv4 AS matches. if home.LocationV4 != nil && pin.LocationV4 != nil { if home.LocationV4.AutonomousSystemNumber == pin.LocationV4.AutonomousSystemNumber { continue } compared = true } // Skip if IPv6 AS matches. if home.LocationV6 != nil && pin.LocationV6 != nil { if home.LocationV6.AutonomousSystemNumber == pin.LocationV6.AutonomousSystemNumber { continue } compared = true } // Skip if no data was compared if !compared { continue } return pin } return nil } ================================================ FILE: spn/navigator/module.go ================================================ package navigator import ( "errors" "sync/atomic" "time" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" ) const ( // cfgOptionRoutingAlgorithmKey is copied from profile/config.go to avoid import loop. cfgOptionRoutingAlgorithmKey = "spn/routingAlgorithm" // cfgOptionRoutingAlgorithmKey is copied from captain/config.go to avoid import loop. cfgOptionTrustNodeNodesKey = "spn/trustNodes" ) var ( // ErrHomeHubUnset is returned when the Home Hub is required and not set. ErrHomeHubUnset = errors.New("map has no Home Hub set") // ErrEmptyMap is returned when the Map is empty. ErrEmptyMap = errors.New("map is empty") // ErrHubNotFound is returned when the Hub was not found on the Map. ErrHubNotFound = errors.New("hub not found") // ErrAllPinsDisregarded is returned when all pins have been disregarded. ErrAllPinsDisregarded = errors.New("all pins have been disregarded") ) type Navigator struct { mgr *mgr.Manager instance instance } func (n *Navigator) Manager() *mgr.Manager { return n.mgr } func (n *Navigator) Start() error { return start() } func (n *Navigator) Stop() error { return stop() } var ( module *Navigator shimLoaded atomic.Bool // Main is the primary map used. Main *Map devMode config.BoolOption cfgOptionRoutingAlgorithm config.StringOption cfgOptionTrustNodeNodes config.StringArrayOption ) func prep() error { return registerAPIEndpoints() } func start() error { Main = NewMap(conf.MainMapName, true) devMode = config.Concurrent.GetAsBool(config.CfgDevModeKey, false) cfgOptionTrustNodeNodes = config.Concurrent.GetAsStringArray(cfgOptionTrustNodeNodesKey, []string{}) if conf.Integrated() { cfgOptionRoutingAlgorithm = config.Concurrent.GetAsString(cfgOptionRoutingAlgorithmKey, DefaultRoutingProfileID) } else { cfgOptionRoutingAlgorithm = func() string { return DefaultRoutingProfileID } } err := registerMapDatabase() if err != nil { return err } module.mgr.Go("initializing hubs", func(wc *mgr.WorkerCtx) error { // Wait for geoip databases to be ready. // Try again if not yet ready, as this is critical. // The "wait" parameter times out after 1 second. // Allow 30 seconds for both databases to load. geoInitCheck: for range 30 { switch { case !geoip.IsInitialized(false, true): // First, IPv4. case !geoip.IsInitialized(true, true): // Then, IPv6. default: break geoInitCheck } } err = Main.InitializeFromDatabase() if err != nil { // Wait for three seconds, then try again. time.Sleep(3 * time.Second) err = Main.InitializeFromDatabase() if err != nil { // Even if the init fails, we can try to start without it and get data along the way. log.Warningf("spn/navigator: %s", err) } } err = Main.RegisterHubUpdateHook() if err != nil { return err } // TODO: delete superseded hubs after x amount of time _ = module.mgr.Delay("update states", 3*time.Minute, Main.updateStates).Repeat(1 * time.Hour) _ = module.mgr.Delay("update failing states", 3*time.Minute, Main.updateFailingStates).Repeat(1 * time.Minute) if conf.PublicHub() { // Only measure Hubs on public Hubs. module.mgr.Delay("measure hubs", 5*time.Minute, Main.measureHubs).Repeat(1 * time.Minute) // Only register metrics on Hubs, as they only make sense there. err := registerMetrics() if err != nil { return err } } return nil }) return nil } func stop() error { withdrawMapDatabase() Main.CancelHubUpdateHook() Main.SaveMeasuredHubs() Main.Close() return nil } // New returns a new Navigator module. func New(instance instance) (*Navigator, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Navigator") module = &Navigator{ mgr: m, instance: instance, } if err := prep(); err != nil { return nil, err } return module, nil } type instance interface{} ================================================ FILE: spn/navigator/module_test.go ================================================ package navigator import ( "fmt" "os" "path/filepath" "testing" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/service/configure" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/service/ui" "github.com/safing/portmaster/service/updates" ) type testInstance struct { db *dbmodule.DBModule config *config.Config intelUpdates *updates.Updater geoip *geoip.GeoIP } func (stub *testInstance) IntelUpdates() *updates.Updater { return stub.intelUpdates } func (stub *testInstance) Config() *config.Config { return stub.config } func (stub *testInstance) Notifications() *notifications.Notifications { return nil } func (stub *testInstance) Ready() bool { return true } func (stub *testInstance) Restart() {} func (stub *testInstance) Shutdown() {} func (stub *testInstance) SetCmdLineOperation(f func() error) {} func (stub *testInstance) BinaryUpdates() *updates.Updater { return nil } func (stub *testInstance) UI() *ui.UI { return nil } func (stub *testInstance) DataDir() string { return _dataDir } var _dataDir string func runTest(m *testing.M) error { var err error // Create a temporary directory for testing _dataDir, err = os.MkdirTemp("", "") if err != nil { return fmt.Errorf("failed to create temporary data directory: %w", err) } defer func() { _ = os.RemoveAll(_dataDir) }() // Initialize the Intel update configuration intelUpdateConfig := updates.Config{ Name: configure.DefaultIntelIndexName, Directory: filepath.Join(_dataDir, "test_intel"), DownloadDirectory: filepath.Join(_dataDir, "test_download_intel"), PurgeDirectory: filepath.Join(_dataDir, "test_upgrade_obsolete_intel"), IndexURLs: configure.DefaultIntelIndexURLs, IndexFile: "index.json", AutoCheck: true, AutoDownload: true, AutoApply: true, } // Set the default API listen address api.SetDefaultAPIListenAddress("0.0.0.0:8080") // Initialize the instance with the necessary components stub := &testInstance{} log.SetLogLevel(log.DebugLevel) // Init stub.db, err = dbmodule.New(stub) if err != nil { return fmt.Errorf("failed to create db: %w", err) } stub.config, err = config.New(stub) if err != nil { return fmt.Errorf("failed to create config: %w", err) } stub.intelUpdates, err = updates.New(stub, "Intel Updater", intelUpdateConfig) if err != nil { return fmt.Errorf("failed to create updates: %w", err) } stub.geoip, err = geoip.New(stub) if err != nil { return fmt.Errorf("failed to create geoip: %w", err) } module, err = New(stub) if err != nil { return fmt.Errorf("failed to create navigator module: %w", err) } // Start err = stub.db.Start() if err != nil { return fmt.Errorf("failed to start db module: %w", err) } err = stub.config.Start() if err != nil { return fmt.Errorf("failed to start config: %w", err) } err = stub.intelUpdates.Start() if err != nil { return fmt.Errorf("failed to start updates: %w", err) } err = stub.geoip.Start() if err != nil { return fmt.Errorf("failed to start geoip module: %w", err) } err = module.Start() if err != nil { return fmt.Errorf("failed to start navigator module: %w", err) } m.Run() return nil } func TestMain(m *testing.M) { if err := runTest(m); err != nil { fmt.Printf("%s\n", err) os.Exit(1) } } ================================================ FILE: spn/navigator/optimize.go ================================================ package navigator import ( "fmt" "sort" "time" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/hub" ) const ( optimizationLowestCostConnections = 3 optimizationHopDistanceTarget = 3 waitUntilMeasuredUpToPercent = 0.5 desegrationAttemptBackoff = time.Hour ) // Optimization Purposes. const ( OptimizePurposeBootstrap = "bootstrap" OptimizePurposeDesegregate = "desegregate" OptimizePurposeWait = "wait" OptimizePurposeTargetStructure = "target-structure" ) // AnalysisState holds state for analyzing the network for optimizations. type AnalysisState struct { //nolint:maligned // Suggested signifies that a direct connection to this Hub is suggested by // the optimization algorithm. Suggested bool // SuggestedHopDistance holds the hop distance to this Hub when only // considering the suggested Hubs as connected. SuggestedHopDistance int // SuggestedHopDistanceInRegion holds the hop distance to this Hub in the // same region when only considering the suggested Hubs as connected. SuggestedHopDistanceInRegion int // CrossRegionalConnections holds the amount of connections a Pin has from // the current region. CrossRegionalConnections int // CrossRegionalLowestCostLane holds the lowest cost of the counted // connections from the current region. CrossRegionalLowestCostLane float32 // CrossRegionalLaneCosts holds all the cross regional lane costs. CrossRegionalLaneCosts []float32 // CrossRegionalHighestCostInHubLimit holds to highest cost of the lowest // cost connections within the maximum allowed lanes on a Hub from the // current region. CrossRegionalHighestCostInHubLimit float32 } // initAnalysis creates all Pin.analysis fields. // The caller needs to hold the map and analysis lock.. func (m *Map) initAnalysis(result *OptimizationResult) { // Compile lists of regarded pins. m.regardedPins = make([]*Pin, 0, len(m.all)) for _, region := range m.regions { region.regardedPins = make([]*Pin, 0, len(m.all)) } // Find all regarded pins. for _, pin := range m.all { if result.matcher(pin) { m.regardedPins = append(m.regardedPins, pin) // Add to region. if pin.region != nil { pin.region.regardedPins = append(pin.region.regardedPins, pin) } } } // Initialize analysis state. for _, pin := range m.all { pin.analysis = &AnalysisState{} } } // clearAnalysis reset all Pin.analysis fields. // The caller needs to hold the map and analysis lock. func (m *Map) clearAnalysis() { m.regardedPins = nil for _, region := range m.regions { region.regardedPins = nil } for _, pin := range m.all { pin.analysis = nil } } // OptimizationResult holds the result of an optimizaion analysis. type OptimizationResult struct { // Purpose holds a semi-human readable constant of the optimization purpose. Purpose string // Approach holds human readable descriptions of how the stated purpose // should be achieved. Approach []string // SuggestedConnections holds the Hubs to which connections are suggested. SuggestedConnections []*SuggestedConnection // MaxConnect specifies how many connections should be created at maximum // based on this optimization. MaxConnect int // StopOthers specifies if other connections than the suggested ones may // be stopped. StopOthers bool // opts holds the options for matching Hubs in this optimization. opts *HubOptions // matcher is the matcher used to create the regarded Pins. // Required for updating suggested hop distance. matcher PinMatcher } // SuggestedConnection holds suggestions by the optimization system. type SuggestedConnection struct { // Hub holds the Hub to which a connection is suggested. Hub *hub.Hub // pin holds the Pin of the Hub. pin *Pin // Reason holds a reason why this connection is suggested. Reason string // Duplicate marks duplicate entries. These should be ignored when // connecting, but are helpful for understand the optimization result. Duplicate bool } func (or *OptimizationResult) addApproach(description string) { or.Approach = append(or.Approach, description) } func (or *OptimizationResult) addSuggested(reason string, pins ...*Pin) { for _, pin := range pins { // Mark as suggested. pin.analysis.Suggested = true // Check if this is a duplicate. var duplicate bool for _, sc := range or.SuggestedConnections { if pin.Hub.ID == sc.Hub.ID { duplicate = true break } } // Add to suggested connections. or.SuggestedConnections = append(or.SuggestedConnections, &SuggestedConnection{ Hub: pin.Hub, pin: pin, Reason: reason, Duplicate: duplicate, }) // Update hop distances if we have a matcher. if or.matcher != nil { or.markSuggestedReachable(pin, 2) or.markSuggestedReachableInRegion(pin, 2) } } } func (or *OptimizationResult) markSuggestedReachable(suggested *Pin, hopDistance int) { // Don't update if distance is greater or equal than current one. if hopDistance >= suggested.analysis.SuggestedHopDistance { return } // Set suggested hop distance. suggested.analysis.SuggestedHopDistance = hopDistance // Increase distance and apply to matching Pins. hopDistance++ for _, lane := range suggested.ConnectedTo { if or.matcher(lane.Pin) { or.markSuggestedReachable(lane.Pin, hopDistance) } } } // Optimize analyzes the map and suggests changes. func (m *Map) Optimize(opts *HubOptions) (result *OptimizationResult, err error) { m.RLock() defer m.RUnlock() // Check if the map is empty. if m.isEmpty() { return nil, ErrEmptyMap } // Set default options if unset. if opts == nil { opts = &HubOptions{} } return m.optimize(opts) } func (m *Map) optimize(opts *HubOptions) (result *OptimizationResult, err error) { if m.home == nil { return nil, ErrHomeHubUnset } // Set default options if unset. if opts == nil { opts = &HubOptions{} } // Create result. result = &OptimizationResult{ opts: opts, matcher: opts.Matcher(TransitHub, m.intel), } // Setup analyis. m.analysisLock.Lock() defer m.analysisLock.Unlock() m.initAnalysis(result) defer m.clearAnalysis() // Bootstrap to the network and desegregate map. // If there is a result, return it immediately. returnImmediately := m.optimizeForBootstrappingAndDesegregation(result) if returnImmediately { return result, nil } // Check if we have the measurements we need. if m.measuringEnabled { // Cound pins with valid measurements. var validMeasurements float32 for _, pin := range m.regardedPins { if pin.measurements.Valid() { validMeasurements++ } } // If less than the required amount of regarded Pins have valid // measurements, let's wait until we have that. if validMeasurements/float32(len(m.regardedPins)) < waitUntilMeasuredUpToPercent { return &OptimizationResult{ Purpose: OptimizePurposeWait, Approach: []string{"Wait for measurements of 80% of regarded nodes for better optimization."}, }, nil } } // Set default values for target structure optimization. result.Purpose = OptimizePurposeTargetStructure result.MaxConnect = 3 result.StopOthers = true // Optimize for lowest cost. m.optimizeForLowestCost(result, optimizationLowestCostConnections) // Optimize for lowest cost in region. m.optimizeForLowestCostInRegion(result) // Optimize for distance constraint in region. m.optimizeForDistanceConstraintInRegion(result, 3) // Optimize for region-to-region connectivity. m.optimizeForRegionConnectivity(result) // Optimize for satellite-to-region connectivity. m.optimizeForSatelliteConnectivity(result) // Lapse traffic stats after optimizing for good fresh data next time. for _, crane := range docks.GetAllAssignedCranes() { crane.NetState.LapsePeriod() } // Clean and return. return result, nil } func (m *Map) optimizeForBootstrappingAndDesegregation(result *OptimizationResult) (returnImmediately bool) { // All regarded Pins are reachable. reachable := len(m.regardedPins) // Count Pins that may be connectable. connectable := make([]*Pin, 0, len(m.all)) // Copy opts as we are going to make changes. opts := result.opts.Copy() opts.NoDefaults = true opts.Regard = StateNone opts.Disregard = StateSummaryDisregard // Collect Pins with matcher. matcher := opts.Matcher(TransitHub, m.intel) for _, pin := range m.all { if matcher(pin) { connectable = append(connectable, pin) } } switch { case reachable == 0: // Sort by lowest cost. sort.Sort(sortByLowestMeasuredCost(connectable)) // Return bootstrap optimization. result.Purpose = OptimizePurposeBootstrap result.Approach = []string{"Connect to a near Hub to connect to the network."} result.MaxConnect = 1 result.addSuggested("bootstrap", connectable...) return true case reachable > len(connectable)/2: // We are part of the majority network, continue with regular optimization. case time.Now().Add(-desegrationAttemptBackoff).Before(m.lastDesegrationAttempt): // We tried to desegregate recently, continue with regular optimization. default: // We are in a network comprised of less than half of the known nodes. // Attempt to connect to an unconnected one to desegregate the network. // Copy opts as we are going to make changes. opts = opts.Copy() opts.NoDefaults = true opts.Regard = StateNone opts.Disregard = StateSummaryDisregard | StateReachable // Iterate over all Pins to find any matching Pin. desegregateWith := make([]*Pin, 0, len(m.all)-reachable) matcher := opts.Matcher(TransitHub, m.intel) for _, pin := range m.all { if matcher(pin) { desegregateWith = append(desegregateWith, pin) } } // Sort by lowest connection cost. sort.Sort(sortByLowestMeasuredCost(desegregateWith)) // Build desegration optimization. result.Purpose = OptimizePurposeDesegregate result.Approach = []string{"Attempt to desegregate network by connection to an unreachable Hub."} result.MaxConnect = 1 result.addSuggested("desegregate", desegregateWith...) // Record desegregation attempt. m.lastDesegrationAttempt = time.Now() return true } return false } func (m *Map) optimizeForLowestCost(result *OptimizationResult, max int) { // Add approach. result.addApproach(fmt.Sprintf("Connect to best (lowest cost) %d Hubs globally.", max)) // Sort by lowest cost. sort.Sort(sortByLowestMeasuredCost(m.regardedPins)) // Add to suggested pins. if len(m.regardedPins) <= max { result.addSuggested("best globally", m.regardedPins...) } else { result.addSuggested("best globally", m.regardedPins[:max]...) } } func (m *Map) optimizeForDistanceConstraint(result *OptimizationResult, max int) { //nolint:unused // TODO: Likely to be used again. // Add approach. result.addApproach(fmt.Sprintf("Satisfy max hop constraint of %d globally.", optimizationHopDistanceTarget)) for range max { // Sort by lowest cost. sort.Sort(sortBySuggestedHopDistanceAndLowestMeasuredCost(m.regardedPins)) // Return when all regarded Pins are within the distance constraint. if m.regardedPins[0].analysis.SuggestedHopDistance <= optimizationHopDistanceTarget { return } // If not, suggest a connection to the best match. result.addSuggested("satisfy global hop constraint", m.regardedPins[0]) } } ================================================ FILE: spn/navigator/optimize_region.go ================================================ package navigator import ( "fmt" "sort" ) func (or *OptimizationResult) markSuggestedReachableInRegion(suggested *Pin, hopDistance int) { // Abort if suggested Pin has no region. if suggested.region == nil { return } // Don't update if distance is greater or equal than current one. if hopDistance >= suggested.analysis.SuggestedHopDistanceInRegion { return } // Set suggested hop distance. suggested.analysis.SuggestedHopDistanceInRegion = hopDistance // Increase distance and apply to matching Pins. hopDistance++ for _, lane := range suggested.ConnectedTo { if lane.Pin.region != nil && lane.Pin.region.ID == suggested.region.ID && or.matcher(lane.Pin) { or.markSuggestedReachableInRegion(lane.Pin, hopDistance) } } } func (m *Map) optimizeForLowestCostInRegion(result *OptimizationResult) { if m.home == nil || m.home.region == nil { return } region := m.home.region // Add approach. result.addApproach(fmt.Sprintf("Connect to best (lowest cost) %d Hubs within the region.", region.internalMinLanesOnHub)) // Sort by lowest cost. sort.Sort(sortByLowestMeasuredCost(region.regardedPins)) // Add to suggested pins. if len(region.regardedPins) <= region.internalMinLanesOnHub { result.addSuggested("best in region", region.regardedPins...) } else { result.addSuggested("best in region", region.regardedPins[:region.internalMinLanesOnHub]...) } } func (m *Map) optimizeForDistanceConstraintInRegion(result *OptimizationResult, max int) { if m.home == nil || m.home.region == nil { return } region := m.home.region // Add approach. result.addApproach(fmt.Sprintf("Satisfy max hop constraint of %d within the region.", region.internalMaxHops)) // Sort by lowest cost. sort.Sort(sortBySuggestedHopDistanceInRegionAndLowestMeasuredCost(region.regardedPins)) for i := 0; i < max && i < len(region.regardedPins); i++ { // Return when all regarded Pins are within the distance constraint. if region.regardedPins[i].analysis.SuggestedHopDistanceInRegion <= region.internalMaxHops { return } // If not, suggest a connection to the best match. result.addSuggested("satisfy regional hop constraint", region.regardedPins[i]) } } func (m *Map) optimizeForRegionConnectivity(result *OptimizationResult) { if m.home == nil || m.home.region == nil { return } region := m.home.region // Add approach. result.addApproach("Connect region to other regions.") // Optimize for every region. checkRegions: for _, otherRegion := range m.regions { // Skip own region. if region.ID == otherRegion.ID { continue } // Collect data on connections to that region. lanesToRegion, highestCostWithinLaneLimit := m.countConnectionsToRegion(result, region, otherRegion) // Sort by lowest cost. sort.Sort(sortByLowestMeasuredCost(otherRegion.regardedPins)) // Find cheapest connections with a free slot or better values. var lanesSuggested int for _, pin := range otherRegion.regardedPins { myCost := pin.measurements.GetCalculatedCost() // Check if we are done or region is satisfied. switch { case lanesSuggested >= region.regionalMaxLanesOnHub: // We hit our max. continue checkRegions case lanesToRegion >= otherRegion.regionalMinLanes && myCost >= highestCostWithinLaneLimit: // Region has enough lanes and we are not better. continue checkRegions } // Check if we can contribute on this Pin. switch { case pin.analysis.CrossRegionalConnections < otherRegion.regionalMaxLanesOnHub && lanesToRegion < otherRegion.regionalMinLanes: // There is a free spot on this Pin and the region needs more connections. result.addSuggested("occupy cross-region lane on pin", pin) lanesSuggested++ lanesToRegion++ // Because our own Pin is not counted, this should be the default // suggestion for a stable network. case myCost < pin.analysis.CrossRegionalHighestCostInHubLimit: // We have a better connection to this Pin than at least one other existing connection (within the limit!). result.addSuggested("replace cross-region lane on pin", pin) lanesSuggested++ lanesToRegion++ case myCost < highestCostWithinLaneLimit && pin.analysis.CrossRegionalConnections < otherRegion.regionalMaxLanesOnHub: // We have a better connection to this Pin than another existing region-to-region connection. result.addSuggested("replace unrelated cross-region lane", pin) lanesSuggested++ lanesToRegion++ } } } } // countConnectionsToRegion analyzes existing lanes from this to another // region, with taking lanes from this Hub into account. func (m *Map) countConnectionsToRegion(result *OptimizationResult, region *Region, otherRegion *Region) (lanesToRegion int, highestCostWithinLaneLimit float32) { for _, pin := range region.regardedPins { // Skip self. if m.home.Hub.ID == pin.Hub.ID { continue } // Find lanes to other region. for _, lane := range pin.ConnectedTo { if lane.Pin.region != nil && lane.Pin.region.ID == otherRegion.ID && result.matcher(lane.Pin) { // This is a lane from this region to a regarded Pin in the other region. lanesToRegion++ // Count cross region connection. lane.Pin.analysis.CrossRegionalConnections++ // Collect lane costs. lane.Pin.analysis.CrossRegionalLaneCosts = append( lane.Pin.analysis.CrossRegionalLaneCosts, lane.Cost, ) } } } // Calculate lane costs from collected lane costs. for _, pin := range otherRegion.regardedPins { sort.Sort(sortCostsByLowest(pin.analysis.CrossRegionalLaneCosts)) switch { case len(pin.analysis.CrossRegionalLaneCosts) == 0: // Nothing to do. case len(pin.analysis.CrossRegionalLaneCosts) < otherRegion.regionalMaxLanesOnHub: pin.analysis.CrossRegionalLowestCostLane = pin.analysis.CrossRegionalLaneCosts[0] pin.analysis.CrossRegionalHighestCostInHubLimit = pin.analysis.CrossRegionalLaneCosts[len(pin.analysis.CrossRegionalLaneCosts)-1] default: pin.analysis.CrossRegionalLowestCostLane = pin.analysis.CrossRegionalLaneCosts[0] pin.analysis.CrossRegionalHighestCostInHubLimit = pin.analysis.CrossRegionalLaneCosts[otherRegion.regionalMaxLanesOnHub-1] } // Find highest cost within limit. if pin.analysis.CrossRegionalHighestCostInHubLimit > highestCostWithinLaneLimit { highestCostWithinLaneLimit = pin.analysis.CrossRegionalHighestCostInHubLimit } } return lanesToRegion, highestCostWithinLaneLimit } func (m *Map) optimizeForSatelliteConnectivity(result *OptimizationResult) { if m.home == nil { return } // This is only for Hubs that are not in a region. if m.home.region != nil { return } // Add approach. result.addApproach("Connect satellite to regions.") // Optimize for every region. for _, region := range m.regions { // Sort by lowest cost. sort.Sort(sortByLowestMeasuredCost(region.regardedPins)) // Add to suggested pins. if len(region.regardedPins) <= region.satelliteMinLanes { result.addSuggested("best to region "+region.ID, region.regardedPins...) } else { result.addSuggested("best to region "+region.ID, region.regardedPins[:region.satelliteMinLanes]...) } } } type sortCostsByLowest []float32 func (a sortCostsByLowest) Len() int { return len(a) } func (a sortCostsByLowest) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a sortCostsByLowest) Less(i, j int) bool { return a[i] < a[j] } ================================================ FILE: spn/navigator/optimize_test.go ================================================ package navigator import ( "strings" "sync" "testing" "github.com/safing/portmaster/spn/hub" ) var ( optimizedDefaultMapCreate sync.Once optimizedDefaultMap *Map ) func getOptimizedDefaultTestMap(t *testing.T) *Map { t.Helper() optimizedDefaultMapCreate.Do(func() { optimizedDefaultMap = createRandomTestMap(2, 100) optimizedDefaultMap.optimizeTestMap(t) }) return optimizedDefaultMap } func (m *Map) optimizeTestMap(t *testing.T) { t.Helper() t.Logf("optimizing test map %s with %d pins", m.Name, len(m.all)) // Save original Home, as we will be switching around the home for the // optimization. run := 0 newLanes := 0 originalHome := m.home mcf := newMeasurementCachedFactory() for { run++ newLanesInRun := 0 // Let's check if we have a run without any map changes. lastRun := true for _, pin := range m.all { // Set Home to this Pin for this iteration. if !m.SetHome(pin.Hub.ID, nil) { panic("failed to set home") } // Update measurements for the new home. updateMeasurements(m, mcf) optimizeResult, err := m.optimize(nil) if err != nil { panic(err) } lanesCreatedWithResult := 0 for _, connectTo := range optimizeResult.SuggestedConnections { // Check if lane to suggested Hub already exists. if m.home.Hub.GetLaneTo(connectTo.Hub.ID) != nil { continue } // Add lanes to the Hub status. _ = m.home.Hub.AddLane(createLane(connectTo.Hub.ID)) _ = connectTo.Hub.AddLane(createLane(m.home.Hub.ID)) // Update Hubs in map. m.UpdateHub(m.home.Hub) m.UpdateHub(connectTo.Hub) newLanes++ newLanesInRun++ // We are changing the map in this run, so this is not the last. lastRun = false // Only create as many lanes as suggested by the result. lanesCreatedWithResult++ if lanesCreatedWithResult >= optimizeResult.MaxConnect { break } } if optimizeResult.Purpose != OptimizePurposeTargetStructure { // If we aren't yet building the target structure, we need to keep building. lastRun = false } } // Log progress. if t != nil { t.Logf( "optimizing: added %d lanes in run #%d (%d Hubs) - %d new lanes in total", newLanesInRun, run, len(m.all), newLanes, ) } // End optimization after last run. if lastRun { break } } // Log what was done and set home back to the original value. if t != nil { t.Logf("finished optimizing test map %s: added %d lanes in %d runs", m.Name, newLanes, run) } m.home = originalHome } func TestOptimize(t *testing.T) { t.Parallel() m := getOptimizedDefaultTestMap(t) matcher := m.defaultOptions().Destination.Matcher(m.intel) originalHome := m.home for _, pin := range m.all { // Set Home to this Pin for this iteration. m.home = pin err := m.recalculateReachableHubs() if err != nil { panic(err) } for _, peer := range m.all { // Check if the Pin matches the criteria. if !matcher(peer) { continue } // TODO: Adapt test to new regions. if peer.HopDistance > 5 { t.Errorf("Optimization error: %s is %d hops away from %s", peer, peer.HopDistance, pin) } } } // Print stats t.Logf("optimized map:\n%s\n", m.Stats()) m.home = originalHome } func updateMeasurements(m *Map, mcf *measurementCachedFactory) { for _, pin := range m.all { pin.measurements = mcf.getOrCreate(m.home.Hub.ID, pin.Hub.ID) } } type measurementCachedFactory struct { cache map[string]*hub.Measurements } func newMeasurementCachedFactory() *measurementCachedFactory { return &measurementCachedFactory{ cache: make(map[string]*hub.Measurements), } } func (mcf *measurementCachedFactory) getOrCreate(from, to string) *hub.Measurements { var id string comparison := strings.Compare(from, to) switch { case comparison == 0: return nil case comparison > 0: id = from + "-" + to case comparison < 0: id = to + "-" + from } m, ok := mcf.cache[id] if ok { return m } m = hub.NewMeasurements() m.Latency = createLatency() m.Capacity = createCapacity() m.CalculatedCost = CalculateLaneCost( m.Latency, m.Capacity, ) mcf.cache[id] = m return m } ================================================ FILE: spn/navigator/options.go ================================================ package navigator import ( "context" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/service/profile/endpoints" "github.com/safing/portmaster/spn/hub" ) // HubType is the usage type of a Hub in routing. type HubType uint8 // Hub Types. const ( HomeHub HubType = iota TransitHub DestinationHub ) // DeriveTunnelOptions derives and returns the tunnel options from the connection and profile. // This function lives in firewall/tunnel.go and is set here to avoid import loops. var DeriveTunnelOptions func(lp *profile.LayeredProfile, destination *intel.Entity, connEncrypted bool) *Options // Options holds configuration options for operations with the Map. type Options struct { //nolint:maligned // Home holds the options for Home Hubs. Home *HomeHubOptions // Transit holds the options for Transit Hubs. Transit *TransitHubOptions // Destination holds the options for Destination Hubs. Destination *DestinationHubOptions // RoutingProfile defines the algorithm to use to find a route. RoutingProfile string } // HomeHubOptions holds configuration options for Home Hub operations with the Map. type HomeHubOptions HubOptions // TransitHubOptions holds configuration options for Transit Hub operations with the Map. type TransitHubOptions HubOptions // DestinationHubOptions holds configuration options for Destination Hub operations with the Map. type DestinationHubOptions HubOptions // HubOptions holds configuration options for a specific hub type for operations with the Map. type HubOptions struct { // Regard holds required States. Only Hubs where all of these are present // will taken into account for the operation. If NoDefaults is not set, a // basic set of desirable states is added automatically. Regard PinState // Disregard holds disqualifying States. Only Hubs where none of these are // present will be taken into account for the operation. If NoDefaults is not // set, a basic set of undesirable states is added automatically. Disregard PinState // NoDefaults declares whether default and recommended Regard and Disregard states should not be used. NoDefaults bool // HubPolicies is a collection of endpoint lists that Hubs must pass in order // to be taken into account for the operation. HubPolicies []endpoints.Endpoints // RequireVerifiedOwners specifies which verified owners are allowed to be used. // If the list is empty, all owners are allowed. RequireVerifiedOwners []string // CheckHubPolicyWith provides an entity that must match the Hubs entry or exit // policy (depending on type) in order to be taken into account for the operation. CheckHubPolicyWith *intel.Entity } // Copy returns a shallow copy of the Options. func (o *Options) Copy() *Options { copied := &Options{ RoutingProfile: o.RoutingProfile, } if o.Home != nil { c := HomeHubOptions(HubOptions(*o.Home).Copy()) copied.Home = &c } if o.Transit != nil { c := TransitHubOptions(HubOptions(*o.Transit).Copy()) copied.Transit = &c } if o.Destination != nil { c := DestinationHubOptions(HubOptions(*o.Destination).Copy()) copied.Destination = &c } return copied } // Copy returns a shallow copy of the Options. func (o HubOptions) Copy() HubOptions { return HubOptions{ Regard: o.Regard, Disregard: o.Disregard, NoDefaults: o.NoDefaults, HubPolicies: o.HubPolicies, RequireVerifiedOwners: o.RequireVerifiedOwners, CheckHubPolicyWith: o.CheckHubPolicyWith, } } // PinMatcher is a stateful matching function generated by Options. type PinMatcher func(pin *Pin) bool // DefaultOptions returns the default options for this Map. func (m *Map) DefaultOptions() *Options { m.Lock() defer m.Unlock() return m.defaultOptions() } func (m *Map) defaultOptions() *Options { opts := &Options{ RoutingProfile: DefaultRoutingProfileID, } return opts } // HubPoliciesAreSet returns whether any of the given hub policies are set and non-empty. func HubPoliciesAreSet(policies []endpoints.Endpoints) bool { for _, policy := range policies { if policy.IsSet() { return true } } return false } var emptyHubOptions = &HubOptions{} // Matcher generates a PinMatcher based on the Options. func (o *HomeHubOptions) Matcher(hubIntel *hub.Intel) PinMatcher { if o == nil { return emptyHubOptions.Matcher(HomeHub, hubIntel) } // Convert and call base func. ho := HubOptions(*o) return ho.Matcher(HomeHub, hubIntel) } // Matcher generates a PinMatcher based on the Options. func (o *TransitHubOptions) Matcher(hubIntel *hub.Intel) PinMatcher { if o == nil { return emptyHubOptions.Matcher(TransitHub, hubIntel) } // Convert and call base func. ho := HubOptions(*o) return ho.Matcher(TransitHub, hubIntel) } // Matcher generates a PinMatcher based on the Options. func (o *DestinationHubOptions) Matcher(hubIntel *hub.Intel) PinMatcher { if o == nil { return emptyHubOptions.Matcher(DestinationHub, hubIntel) } // Convert and call base func. ho := HubOptions(*o) return ho.Matcher(DestinationHub, hubIntel) } // Matcher generates a PinMatcher based on the Options. // Always use the Matcher on option structs if you can. func (o *Options) Matcher(hubType HubType, hubIntel *hub.Intel) PinMatcher { switch hubType { case HomeHub: return o.Home.Matcher(hubIntel) case TransitHub: return o.Transit.Matcher(hubIntel) case DestinationHub: return o.Destination.Matcher(hubIntel) default: return nil // This will panic, but should never be used. } } // Matcher generates a PinMatcher based on the Options. func (o *HubOptions) Matcher(hubType HubType, hubIntel *hub.Intel) PinMatcher { // Fallback to empty hub options. if o == nil { o = emptyHubOptions } // Compile states to regard and disregard. regard := o.Regard disregard := o.Disregard // Add default states. if !o.NoDefaults { // Add default States. regard = regard.Add(StateSummaryRegard) disregard = disregard.Add(StateSummaryDisregard) // Add type based Advisories. switch hubType { case HomeHub: // Home Hubs don't need to be reachable and don't need keys ready to be used. regard = regard.Remove(StateReachable) regard = regard.Remove(StateActive) // Follow advisory. disregard = disregard.Add(StateUsageAsHomeDiscouraged) // Home Hub may be the current Home Hub. disregard = disregard.Remove(StateIsHomeHub) case TransitHub: // Transit Hubs get no additional states. case DestinationHub: // Follow advisory. disregard = disregard.Add(StateUsageAsDestinationDiscouraged) // Do not use if Hub reports network issues. disregard = disregard.Add(StateConnectivityIssues) } } // Add intel policies. hubPolicies := o.HubPolicies if hubIntel != nil && hubIntel.Parsed() != nil { switch hubType { case HomeHub: hubPolicies = append(hubPolicies, hubIntel.Parsed().HubAdvisory, hubIntel.Parsed().HomeHubAdvisory) case TransitHub: hubPolicies = append(hubPolicies, hubIntel.Parsed().HubAdvisory) case DestinationHub: hubPolicies = append(hubPolicies, hubIntel.Parsed().HubAdvisory, hubIntel.Parsed().DestinationHubAdvisory) } } // Add entry/exit policiy checks. checkHubPolicyWith := o.CheckHubPolicyWith return func(pin *Pin) bool { // Check required Pin States. if !pin.State.Has(regard) || pin.State.HasAnyOf(disregard) { return false } // Check if all required states from intel were applied. if regard.HasAnyOf(StateSummaryStatusesAppliedFromIntel) || disregard.HasAnyOf(StateSummaryStatusesAppliedFromIntel) { if pin.stateIntelApplied.IsNotSet() { log.Warningf("spn/navigator: pin %s skipped as intel statuses were not applied", pin.Hub.ID) return false } } // Check verified owners. if len(o.RequireVerifiedOwners) > 0 { // Check if Pin has a verified owner at all. if pin.VerifiedOwner == "" { return false } // Check if verified owner is in the list. inList := false for _, allowed := range o.RequireVerifiedOwners { if pin.VerifiedOwner == allowed { inList = true break } } // Pin does not have a verified owner from the allowed list. if !inList { return false } } // Check policies. policyCheck: for _, policy := range hubPolicies { // Check if policy is set. if !policy.IsSet() { continue } // Check if policy matches. result, reason := policy.MatchMulti(context.TODO(), pin.EntityV4, pin.EntityV6) switch result { case endpoints.NoMatch: // Continue with check. case endpoints.MatchError: log.Warningf("spn/navigator: failed to match policy: %s", reason) // Continue with check for now. // TODO: Rethink how to do this. If eg. the geoip database has a // problem, then no Hub will match. For now, just continue to the // next rule set. Not optimal, but fail safe. case endpoints.Denied: // Explicitly denied, abort immediately. return false case endpoints.Permitted: // Explicitly allowed, abort check and continue. break policyCheck } } // Check entry/exit policies. if checkHubPolicyWith != nil { switch hubType { case HomeHub: if endpointListMatch(pin.Hub.Info.EntryPolicy(), checkHubPolicyWith) == endpoints.Denied { // Hub does not allow entry from the given entity. return false } case TransitHub: // Transit Hubs do not have a hub policy. case DestinationHub: if endpointListMatch(pin.Hub.Info.ExitPolicy(), checkHubPolicyWith) == endpoints.Denied { // Hub does not allow exit to the given entity. return false } } } return true // All checks have passed. } } func endpointListMatch(list endpoints.Endpoints, entity *intel.Entity) endpoints.EPResult { // Check if endpoint list and entity are available. if !list.IsSet() || entity == nil { return endpoints.NoMatch } // Match and return result only. result, _ := list.Match(context.TODO(), entity) return result } ================================================ FILE: spn/navigator/pin.go ================================================ package navigator import ( "context" "net" "strings" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/spn/docks" "github.com/safing/portmaster/spn/hub" ) // Pin represents a Hub on a Map. type Pin struct { //nolint:maligned // Hub Information Hub *hub.Hub EntityV4 *intel.Entity EntityV6 *intel.Entity LocationV4 *geoip.Location LocationV6 *geoip.Location // Hub Status State PinState // stateIntelApplied signifies that states from intel file were applied to the pin. stateIntelApplied *abool.AtomicBool // VerifiedOwner holds the name of the verified owner / operator of the Hub. VerifiedOwner string // HopDistance signifies the needed hops to reach this Hub. // HopDistance is measured from the view of a client. // A Hub itself will have itself at distance 1. // Directly connected Hubs have a distance of 2. HopDistance int // Cost is the routing cost of this Hub. Cost float32 // ConnectedTo holds validated lanes. ConnectedTo map[string]*Lane // Key is Hub ID. // FailingUntil specifies until when this Hub should be regarded as failing. // This is connected to StateFailing. FailingUntil time.Time // Connection holds a information about a connection to the Hub of this Pin. Connection *PinConnection // Internal // pushChanges is set to true if something noteworthy on the Pin changed and // an update needs to be pushed by the database storage interface to whoever // is listening. pushChanges *abool.AtomicBool // measurements holds Measurements regarding this Pin. // It must always be set and the reference must not be changed when measuring // is enabled. // Access to fields within are coordinated by itself. measurements *hub.Measurements // analysis holds the analysis state. // Should only be set during analysis and be reset at the start and removed at the end of an analysis. analysis *AnalysisState // region is the region this Pin belongs to. region *Region } // PinConnection represents a connection to a terminal on the Hub. type PinConnection struct { // Terminal holds the active terminal session. Terminal *docks.ExpansionTerminal // Route is the route built for this terminal. Route *Route } // Lane is a connection to another Hub. type Lane struct { // Pin is the Pin/Hub this Lane connects to. Pin *Pin // Capacity designates the available bandwidth between these Hubs. // It is specified in bit/s. Capacity int // Lateny designates the latency between these Hubs. // It is specified in nanoseconds. Latency time.Duration // Cost is the routing cost of this lane. Cost float32 // active is a helper flag in order help remove abandoned Lanes. active bool } // Lock locks the Pin via the Hub's lock. func (pin *Pin) Lock() { pin.Hub.Lock() } // Unlock unlocks the Pin via the Hub's lock. func (pin *Pin) Unlock() { pin.Hub.Unlock() } // String returns a human-readable representation of the Pin. func (pin *Pin) String() string { return "" } // GetState returns the state of the pin. func (pin *Pin) GetState() PinState { pin.Lock() defer pin.Unlock() return pin.State } // updateLocationData fetches the necessary location data in order to correctly map out the Pin. func (pin *Pin) updateLocationData() { // TODO: We are currently assigning the Hub ID to the entity domain to // support matching a Hub by its ID. The issue here is that the domain // rules are lower-cased, so we have to lower-case the ID here too. // This is not optimal from a security perspective, but there are still // enough bits left that this cannot be easily exploited. if pin.Hub.Info.IPv4 != nil { pin.EntityV4 = (&intel.Entity{ IP: pin.Hub.Info.IPv4, Domain: strings.ToLower(pin.Hub.ID) + ".", }).Init(0) var ok bool pin.LocationV4, ok = pin.EntityV4.GetLocation(context.TODO()) if !ok { log.Warningf("spn/navigator: failed to get location of %s of %s", pin.Hub.Info.IPv4, pin.Hub.StringWithoutLocking()) return } } else { pin.EntityV4 = nil pin.LocationV4 = nil } if pin.Hub.Info.IPv6 != nil { pin.EntityV6 = (&intel.Entity{ IP: pin.Hub.Info.IPv6, Domain: strings.ToLower(pin.Hub.ID) + ".", }).Init(0) var ok bool pin.LocationV6, ok = pin.EntityV6.GetLocation(context.TODO()) if !ok { log.Warningf("spn/navigator: failed to get location of %s of %s", pin.Hub.Info.IPv6, pin.Hub.StringWithoutLocking()) return } } else { pin.EntityV6 = nil pin.LocationV6 = nil } } // GetLocation returns the geoip location of the Pin, preferring first the given IP, then IPv4. func (pin *Pin) GetLocation(ip net.IP) *geoip.Location { pin.Lock() defer pin.Unlock() switch { case ip != nil && ip.Equal(pin.Hub.Info.IPv4) && pin.LocationV4 != nil: return pin.LocationV4 case ip != nil && ip.Equal(pin.Hub.Info.IPv6) && pin.LocationV6 != nil: return pin.LocationV6 case pin.LocationV4 != nil: return pin.LocationV4 case pin.LocationV6 != nil: return pin.LocationV6 default: return nil } } // SetActiveTerminal sets an active terminal for the pin. func (pin *Pin) SetActiveTerminal(pc *PinConnection) { pin.Lock() defer pin.Unlock() pin.Connection = pc if pin.Connection != nil && pin.Connection.Terminal != nil { pin.Connection.Terminal.SetChangeNotifyFunc(pin.NotifyTerminalChange) } pin.pushChanges.Set() } // GetActiveTerminal returns the active terminal of the pin. func (pin *Pin) GetActiveTerminal() *docks.ExpansionTerminal { pin.Lock() defer pin.Unlock() if !pin.hasActiveTerminal() { return nil } return pin.Connection.Terminal } // HasActiveTerminal returns whether the Pin has an active terminal. func (pin *Pin) HasActiveTerminal() bool { pin.Lock() defer pin.Unlock() return pin.hasActiveTerminal() } func (pin *Pin) hasActiveTerminal() bool { return pin.Connection != nil && pin.Connection.Terminal.Abandoning.IsNotSet() } // NotifyTerminalChange notifies subscribers of the changed terminal. func (pin *Pin) NotifyTerminalChange() { pin.pushChanges.Set() pin.pushChange() } // IsFailing returns whether the pin should be treated as failing. // The Pin is locked for this. func (pin *Pin) IsFailing() bool { pin.Lock() defer pin.Unlock() return time.Now().Before(pin.FailingUntil) } // MarkAsFailingFor marks the pin as failing. // The Pin is locked for this. // Changes are pushed. func (pin *Pin) MarkAsFailingFor(duration time.Duration) { pin.Lock() defer pin.Unlock() until := time.Now().Add(duration) // Only ever increase failing until, never reduce. if until.After(pin.FailingUntil) { pin.FailingUntil = until } pin.addStates(StateFailing) pin.pushChanges.Set() pin.pushChange() } // ResetFailingState resets the failing state. // The Pin is locked for this. // Changes are not pushed, but Pins are marked. func (pin *Pin) ResetFailingState() { pin.Lock() defer pin.Unlock() if time.Now().Before(pin.FailingUntil) { pin.FailingUntil = time.Now() pin.pushChanges.Set() } if pin.State.Has(StateFailing) { pin.removeStates(StateFailing) pin.pushChanges.Set() } } ================================================ FILE: spn/navigator/pin_export.go ================================================ package navigator import ( "fmt" "sync" "time" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/service/intel" "github.com/safing/portmaster/spn/hub" ) // PinExport is the exportable version of a Pin. type PinExport struct { record.Base sync.Mutex ID string Name string Map string FirstSeen time.Time EntityV4 *intel.Entity EntityV6 *intel.Entity // TODO: add coords States []string // From pin.State VerifiedOwner string HopDistance int ConnectedTo map[string]*LaneExport // Key is Hub ID. Route []string // Includes Home Hub and this Pin's ID. SessionActive bool Info *hub.Announcement Status *hub.Status } // LaneExport is the exportable version of a Lane. type LaneExport struct { HubID string // Capacity designates the available bandwidth between these Hubs. // It is specified in bit/s. Capacity int // Lateny designates the latency between these Hubs. // It is specified in nanoseconds. Latency time.Duration } // Export puts the Pin's information into an exportable format. func (pin *Pin) Export() *PinExport { pin.Lock() defer pin.Unlock() // Shallow copy static values. export := &PinExport{ ID: pin.Hub.ID, Name: pin.Hub.Info.Name, Map: pin.Hub.Map, FirstSeen: pin.Hub.FirstSeen, EntityV4: pin.EntityV4, EntityV6: pin.EntityV6, States: pin.State.Export(), VerifiedOwner: pin.VerifiedOwner, HopDistance: pin.HopDistance, SessionActive: pin.hasActiveTerminal() || pin.State.Has(StateIsHomeHub), Info: pin.Hub.Info, // Is updated as a whole, no need to copy. Status: pin.Hub.Status, // Is updated as a whole, no need to copy. } // Export lanes. export.ConnectedTo = make(map[string]*LaneExport, len(pin.ConnectedTo)) for key, lane := range pin.ConnectedTo { export.ConnectedTo[key] = &LaneExport{ HubID: lane.Pin.Hub.ID, Capacity: lane.Capacity, Latency: lane.Latency, } } // Export route to Pin, if connected. if pin.Connection != nil && pin.Connection.Route != nil { export.Route = make([]string, len(pin.Connection.Route.Path)) for key, hop := range pin.Connection.Route.Path { export.Route[key] = hop.HubID } } // Create database record metadata. export.SetKey(makeDBKey(export.Map, export.ID)) export.SetMeta(&record.Meta{ Created: export.FirstSeen.Unix(), Modified: time.Now().Unix(), }) return export } // HumanName returns a human-readable version of a Hub's name. // This name will likely consist of two parts: the given name and the ending of the ID to make it unique. func (h *PinExport) HumanName() string { if len(h.ID) < 8 { return fmt.Sprintf("", h.ID) } shortenedID := h.ID[len(h.ID)-8:len(h.ID)-4] + "-" + h.ID[len(h.ID)-4:] // Be more careful, as the Hub name is user input. switch { case h.Info.Name == "": return fmt.Sprintf("", shortenedID) case len(h.Info.Name) > 16: return fmt.Sprintf("", h.Info.Name[:16], shortenedID) default: return fmt.Sprintf("", h.Info.Name, shortenedID) } } ================================================ FILE: spn/navigator/region.go ================================================ package navigator import ( "context" "math" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/profile/endpoints" "github.com/safing/portmaster/spn/hub" ) const ( defaultRegionalMinLanesPerHub = 0.5 defaultRegionalMaxLanesOnHub = 2 defaultSatelliteMinLanesPerHub = 0.3 defaultInternalMinLanesOnHub = 3 defaultInternalMaxHops = 3 ) // Region specifies a group of Hubs for optimization purposes. type Region struct { ID string Name string config *hub.RegionConfig memberPolicy endpoints.Endpoints pins []*Pin regardedPins []*Pin regionalMinLanes int regionalMaxLanesOnHub int satelliteMinLanes int internalMinLanesOnHub int internalMaxHops int } func (region *Region) getName() string { switch { case region == nil: return "-" case region.Name != "": return region.Name default: return region.ID } } func (m *Map) updateRegions(config []*hub.RegionConfig) { // Reset map and pins. m.regions = make([]*Region, 0, len(config)) for _, pin := range m.all { pin.region = nil } // Stop if not regions are defined. if len(config) == 0 { return } // Build regions from config. for _, regionConfig := range config { // Check if region has an ID. if regionConfig.ID == "" { log.Error("spn/navigator: region is missing ID") // Abort adding this region to the map. continue } // Create new region. region := &Region{ ID: regionConfig.ID, Name: regionConfig.Name, config: regionConfig, } // Parse member policy. if len(regionConfig.MemberPolicy) == 0 { log.Errorf("spn/navigator: member policy of region %s is missing", region.ID) // Abort adding this region to the map. continue } memberPolicy, err := endpoints.ParseEndpoints(regionConfig.MemberPolicy) if err != nil { log.Errorf("spn/navigator: failed to parse member policy of region %s: %s", region.ID, err) // Abort adding this region to the map. continue } region.memberPolicy = memberPolicy // Recalculate region properties. region.recalculateProperties() // Add region to map. m.regions = append(m.regions, region) } // Update region in all Pins. for _, pin := range m.all { m.updatePinRegion(pin) } } func (region *Region) addPin(pin *Pin) { // Find pin in region. for _, regionPin := range region.pins { if pin.Hub.ID == regionPin.Hub.ID { // Pin is already part of region. return } } // Check if pin is already part of this region. if pin.region != nil && pin.region.ID == region.ID { return } // Remove pin from previous region. if pin.region != nil { pin.region.removePin(pin) } // Add new pin to region. region.pins = append(region.pins, pin) pin.region = region // Recalculate region properties. region.recalculateProperties() } func (region *Region) removePin(pin *Pin) { // Find pin index in region. removeIndex := -1 for index, regionPin := range region.pins { if pin.Hub.ID == regionPin.Hub.ID { removeIndex = index break } } if removeIndex < 0 { // Pin is not part of region. return } // Remove pin from region. region.pins = append(region.pins[:removeIndex], region.pins[removeIndex+1:]...) // Recalculate region properties. region.recalculateProperties() } func (region *Region) recalculateProperties() { // Regional properties. region.regionalMinLanes = calculateMinLanes( len(region.pins), region.config.RegionalMinLanes, region.config.RegionalMinLanesPerHub, defaultRegionalMinLanesPerHub, ) region.regionalMaxLanesOnHub = region.config.RegionalMaxLanesOnHub if region.regionalMaxLanesOnHub <= 0 { region.regionalMaxLanesOnHub = defaultRegionalMaxLanesOnHub } // Satellite properties. region.satelliteMinLanes = calculateMinLanes( len(region.pins), region.config.SatelliteMinLanes, region.config.SatelliteMinLanesPerHub, defaultSatelliteMinLanesPerHub, ) // Internal properties. region.internalMinLanesOnHub = region.config.InternalMinLanesOnHub if region.internalMinLanesOnHub <= 0 { region.internalMinLanesOnHub = defaultInternalMinLanesOnHub } region.internalMaxHops = region.config.InternalMaxHops if region.internalMaxHops <= 0 { region.internalMaxHops = defaultInternalMaxHops } // Values below 2 do not make any sense for max hops. if region.internalMaxHops < 2 { region.internalMaxHops = 2 } } func calculateMinLanes(regionHubCount, minLanes int, minLanesPerHub, defaultMinLanesPerHub float64) (minLaneCount int) { // Validate hub count. if regionHubCount <= 0 { // Reset to safe value. regionHubCount = 1 } // Set to configured minimum lanes. minLaneCount = minLanes // Raise to configured minimum lanes per Hub. if minLanesPerHub != 0 { minLanesFromSize := int(math.Ceil(float64(regionHubCount) * minLanesPerHub)) if minLanesFromSize > minLaneCount { minLaneCount = minLanesFromSize } } // Raise to default minimum lanes per Hub, if still 0. if minLaneCount <= 0 { minLaneCount = int(math.Ceil(float64(regionHubCount) * defaultMinLanesPerHub)) } return minLaneCount } func (m *Map) updatePinRegion(pin *Pin) { for _, region := range m.regions { // Check if pin matches the region's member policy. if pin.EntityV4 != nil { result, _ := region.memberPolicy.Match(context.TODO(), pin.EntityV4) if result == endpoints.Permitted { region.addPin(pin) return } } if pin.EntityV6 != nil { result, _ := region.memberPolicy.Match(context.TODO(), pin.EntityV6) if result == endpoints.Permitted { region.addPin(pin) return } } } } ================================================ FILE: spn/navigator/route.go ================================================ package navigator import ( "fmt" mrand "math/rand" "sort" "strings" "time" ) // Routes holds a collection of Routes. type Routes struct { All []*Route randomizeTopPercent float32 maxCost float32 // automatic maxRoutes int // manual setting } // Len is the number of elements in the collection. func (r *Routes) Len() int { return len(r.All) } // Less reports whether the element with index i should sort before the element // with index j. func (r *Routes) Less(i, j int) bool { return r.All[i].TotalCost < r.All[j].TotalCost } // Swap swaps the elements with indexes i and j. func (r *Routes) Swap(i, j int) { r.All[i], r.All[j] = r.All[j], r.All[i] } // isGoodEnough reports whether the route would survive a clean process. func (r *Routes) isGoodEnough(route *Route) bool { if r.maxCost > 0 && route.TotalCost > r.maxCost { return false } return true } // add adds a Route if it is good enough. func (r *Routes) add(route *Route) { if !r.isGoodEnough(route) { return } r.All = append(r.All, route.CopyUpTo(0)) r.clean() } // clean sort and shortens the list to the configured maximum. func (r *Routes) clean() { // Sort Routes so that the best ones are on top. sort.Sort(r) // Remove all remaining from the list. if len(r.All) > r.maxRoutes { r.All = r.All[:r.maxRoutes] } // Set new maximum total cost. if len(r.All) >= r.maxRoutes { r.maxCost = r.All[len(r.All)-1].TotalCost } } // randomizeTop randomized to the top nearest pins for balancing the network. func (r *Routes) randomizeTop() { switch { case r.randomizeTopPercent == 0: // Check if randomization is enabled. return case len(r.All) < 2: // Check if we have enough pins to work with. return } // Find randomization set. randomizeUpTo := len(r.All) threshold := r.All[0].TotalCost * (1 + r.randomizeTopPercent) for i, r := range r.All { // Find first value above the threshold to stop. if r.TotalCost > threshold { randomizeUpTo = i break } } // Shuffle top set. if randomizeUpTo >= 2 { mr := mrand.New(mrand.NewSource(time.Now().UnixNano())) //nolint:gosec mr.Shuffle(randomizeUpTo, r.Swap) } } // Route is a path through the map. type Route struct { // Path is a list of Transit Hubs and the Destination Hub, including the Cost // for each Hop. Path []*Hop // DstCost is the calculated cost between the Destination Hub and the destination IP. DstCost float32 // TotalCost is the sum of all costs of this Route. TotalCost float32 // Algorithm is the ID of the algorithm used to calculate the route. Algorithm string } // Hop is one hop of a route's path. type Hop struct { pin *Pin // HubID is the Hub ID. HubID string // Cost is the cost for both Lane to this Hub and the Hub itself. Cost float32 } // addHop adds a hop to the route. func (r *Route) addHop(pin *Pin, cost float32) { r.Path = append(r.Path, &Hop{ pin: pin, Cost: cost, }) r.recalculateTotalCost() } // completeRoute completes the route by adding the destination cost of the // connection between the last hop and the destination IP. func (r *Route) completeRoute(dstCost float32) { r.DstCost = dstCost r.recalculateTotalCost() } // removeHop removes the last hop from the Route. func (r *Route) removeHop() { // Reset DstCost, as the route might have been completed. r.DstCost = 0 if len(r.Path) >= 1 { r.Path = r.Path[:len(r.Path)-1] } r.recalculateTotalCost() } // recalculateTotalCost recalculates to total cost of this route. func (r *Route) recalculateTotalCost() { r.TotalCost = r.DstCost for _, hop := range r.Path { if hop.pin.HasActiveTerminal() { // If we have an active connection, only take 80% of the cost. r.TotalCost += hop.Cost * 0.8 } else { r.TotalCost += hop.Cost } } } // CopyUpTo makes a somewhat deep copy of the Route up to the specified amount // and returns it. Hops themselves are not copied, because their data does not // change. Therefore, returned Hops may not be edited. // Specify an amount of 0 to copy all. func (r *Route) CopyUpTo(n int) *Route { // Check amount. if n == 0 || n > len(r.Path) { n = len(r.Path) } newRoute := &Route{ Path: make([]*Hop, n), DstCost: r.DstCost, TotalCost: r.TotalCost, } copy(newRoute.Path, r.Path) return newRoute } // makeExportReady fills in all the missing data fields which are meant for // exporting only. func (r *Routes) makeExportReady(algorithm string) { for _, route := range r.All { route.makeExportReady(algorithm) } } // makeExportReady fills in all the missing data fields which are meant for // exporting only. func (r *Route) makeExportReady(algorithm string) { r.Algorithm = algorithm for _, hop := range r.Path { hop.makeExportReady() } } // makeExportReady fills in all the missing data fields which are meant for // exporting only. func (hop *Hop) makeExportReady() { hop.HubID = hop.pin.Hub.ID } // Pin returns the Pin of the Hop. func (hop *Hop) Pin() *Pin { return hop.pin } func (r *Route) String() string { s := make([]string, 0, len(r.Path)+2) s = append(s, fmt.Sprintf("route with %.2fc:", r.TotalCost)) for i, hop := range r.Path { if i == 0 { s = append(s, hop.pin.String()) } else { s = append(s, fmt.Sprintf("--> %.2fc %s", hop.Cost, hop.pin)) } } s = append(s, fmt.Sprintf("--> %.2fc", r.DstCost)) return strings.Join(s, " ") } ================================================ FILE: spn/navigator/routing-profiles.go ================================================ package navigator import ( "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/profile" ) // RoutingProfile defines a routing algorithm with some options. type RoutingProfile struct { ID string // Name is the human readable name of the profile. Name string // MinHops defines how many hops a route must have at minimum. In order to // reduce confusion, the Home Hub is also counted. MinHops int // MaxHops defines the limit on how many hops a route may have. In order to // reduce confusion, the Home Hub is also counted. MaxHops int // MaxExtraHops sets a limit on how many extra hops are allowed in addition // to the amount of Hops in the currently best route. This is an optimization // option and should not interfere with finding the best route, but might // reduce the amount of routes found. MaxExtraHops int // MaxExtraCost sets a limit on the extra cost allowed in addition to the // cost of the currently best route. This is an optimization option and // should not interfere with finding the best route, but might reduce the // amount of routes found. MaxExtraCost float32 } // Routing Profile Names. const ( RoutingProfileHomeID = "home" RoutingProfileSingleHopID = "single-hop" RoutingProfileDoubleHopID = "double-hop" RoutingProfileTripleHopID = "triple-hop" ) // Routing Profiles. var ( DefaultRoutingProfileID = profile.DefaultRoutingProfileID RoutingProfileHome = &RoutingProfile{ ID: "home", Name: "Plain VPN Mode", MinHops: 1, MaxHops: 1, } RoutingProfileSingleHop = &RoutingProfile{ ID: "single-hop", Name: "Speed Focused", MinHops: 1, MaxHops: 3, MaxExtraHops: 1, MaxExtraCost: 10000, } RoutingProfileDoubleHop = &RoutingProfile{ ID: "double-hop", Name: "Balanced", MinHops: 2, MaxHops: 4, MaxExtraHops: 2, MaxExtraCost: 10000, } RoutingProfileTripleHop = &RoutingProfile{ ID: "triple-hop", Name: "Privacy Focused", MinHops: 3, MaxHops: 5, MaxExtraHops: 3, MaxExtraCost: 10000, } ) // GetRoutingProfile returns the routing profile with the given ID. func GetRoutingProfile(id string) *RoutingProfile { switch id { case RoutingProfileHomeID: return RoutingProfileHome case RoutingProfileSingleHopID: return RoutingProfileSingleHop case RoutingProfileDoubleHopID: return RoutingProfileDoubleHop case RoutingProfileTripleHopID: return RoutingProfileTripleHop default: return RoutingProfileDoubleHop } } type routeCompliance uint8 const ( routeOk routeCompliance = iota // Route is fully compliant and can be used. routeNonCompliant // Route is not compliant, but this might change if more hops are added. routeDisqualified // Route is disqualified and won't be able to become compliant. ) func (rp *RoutingProfile) checkRouteCompliance(route *Route, foundRoutes *Routes) routeCompliance { switch { case len(route.Path) < rp.MinHops: // Route is shorter than the defined minimum. return routeNonCompliant case len(route.Path) > rp.MaxHops: // Route is longer than the defined maximum. return routeDisqualified } // Check for hub re-use. if len(route.Path) >= 2 { lastHop := route.Path[len(route.Path)-1] for _, hop := range route.Path[:len(route.Path)-1] { if lastHop.pin.Hub.ID == hop.pin.Hub.ID { return routeDisqualified } } } // Check if hub is already in use, if so check if the route matches. if len(route.Path) >= 2 { // Get active connection to the last pin of the current path. lastPinConnection := route.Path[len(route.Path)-1].pin.Connection switch { case lastPinConnection == nil: // Last pin is not yet connected. case len(lastPinConnection.Route.Path) < 2: // Path of last pin does not have enough hops. // This is unexpected and should not happen. log.Errorf( "navigator: expected active connection to %s to have 2 hops or more on path, but it had %d", route.Path[len(route.Path)-1].pin.Hub.StringWithoutLocking(), len(lastPinConnection.Route.Path), ) case lastPinConnection.Route.Path[len(lastPinConnection.Route.Path)-2].pin.Hub.ID != route.Path[len(route.Path)-2].pin.Hub.ID: // The previous hop of the existing route and the one we are evaluating don't match. // Currently, we only allow one session per Hub. return routeDisqualified } } // Abort route exploration when we are outside the optimization boundaries. if len(foundRoutes.All) > 0 { // Get the best found route. best := foundRoutes.All[0] // Abort if current route exceeds max extra costs. if route.TotalCost > best.TotalCost+rp.MaxExtraCost { return routeDisqualified } // Abort if current route exceeds max extra hops. if len(route.Path) > len(best.Path)+rp.MaxExtraHops { return routeDisqualified } } return routeOk } ================================================ FILE: spn/navigator/sort.go ================================================ package navigator type sortByPinID []*Pin func (a sortByPinID) Len() int { return len(a) } func (a sortByPinID) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a sortByPinID) Less(i, j int) bool { return a[i].Hub.ID < a[j].Hub.ID } type sortByLowestMeasuredCost []*Pin func (a sortByLowestMeasuredCost) Len() int { return len(a) } func (a sortByLowestMeasuredCost) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a sortByLowestMeasuredCost) Less(i, j int) bool { x := a[i].measurements.GetCalculatedCost() y := a[j].measurements.GetCalculatedCost() if x != y { return x < y } // Fall back to geo proximity. gx := a[i].measurements.GetGeoProximity() gy := a[j].measurements.GetGeoProximity() if gx != gy { return gx > gy } // Fall back to Hub ID. return a[i].Hub.ID < a[j].Hub.ID } type sortBySuggestedHopDistanceAndLowestMeasuredCost []*Pin func (a sortBySuggestedHopDistanceAndLowestMeasuredCost) Len() int { return len(a) } func (a sortBySuggestedHopDistanceAndLowestMeasuredCost) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a sortBySuggestedHopDistanceAndLowestMeasuredCost) Less(i, j int) bool { // First sort by suggested hop distance. if a[i].analysis.SuggestedHopDistance != a[j].analysis.SuggestedHopDistance { return a[i].analysis.SuggestedHopDistance > a[j].analysis.SuggestedHopDistance } // Then by cost. x := a[i].measurements.GetCalculatedCost() y := a[j].measurements.GetCalculatedCost() if x != y { return x < y } // Fall back to geo proximity. gx := a[i].measurements.GetGeoProximity() gy := a[j].measurements.GetGeoProximity() if gx != gy { return gx > gy } // Fall back to Hub ID. return a[i].Hub.ID < a[j].Hub.ID } type sortBySuggestedHopDistanceInRegionAndLowestMeasuredCost []*Pin func (a sortBySuggestedHopDistanceInRegionAndLowestMeasuredCost) Len() int { return len(a) } func (a sortBySuggestedHopDistanceInRegionAndLowestMeasuredCost) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a sortBySuggestedHopDistanceInRegionAndLowestMeasuredCost) Less(i, j int) bool { // First sort by suggested hop distance. if a[i].analysis.SuggestedHopDistanceInRegion != a[j].analysis.SuggestedHopDistanceInRegion { return a[i].analysis.SuggestedHopDistanceInRegion > a[j].analysis.SuggestedHopDistanceInRegion } // Then by cost. x := a[i].measurements.GetCalculatedCost() y := a[j].measurements.GetCalculatedCost() if x != y { return x < y } // Fall back to geo proximity. gx := a[i].measurements.GetGeoProximity() gy := a[j].measurements.GetGeoProximity() if gx != gy { return gx > gy } // Fall back to Hub ID. return a[i].Hub.ID < a[j].Hub.ID } type sortByLowestMeasuredLatency []*Pin func (a sortByLowestMeasuredLatency) Len() int { return len(a) } func (a sortByLowestMeasuredLatency) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a sortByLowestMeasuredLatency) Less(i, j int) bool { x, _ := a[i].measurements.GetLatency() y, _ := a[j].measurements.GetLatency() switch { case x == y: // Go to fallbacks. case x == 0: // Ignore zero values. return false // j/y is better. case y == 0: // Ignore zero values. return true // i/x is better. default: return x < y } // Fall back to geo proximity. gx := a[i].measurements.GetGeoProximity() gy := a[j].measurements.GetGeoProximity() if gx != gy { return gx > gy } // Fall back to Hub ID. return a[i].Hub.ID < a[j].Hub.ID } type sortByHighestMeasuredCapacity []*Pin func (a sortByHighestMeasuredCapacity) Len() int { return len(a) } func (a sortByHighestMeasuredCapacity) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a sortByHighestMeasuredCapacity) Less(i, j int) bool { x, _ := a[i].measurements.GetCapacity() y, _ := a[j].measurements.GetCapacity() if x != y { return x > y } // Fall back to geo proximity. gx := a[i].measurements.GetGeoProximity() gy := a[j].measurements.GetGeoProximity() if gx != gy { return gx > gy } // Fall back to Hub ID. return a[i].Hub.ID < a[j].Hub.ID } ================================================ FILE: spn/navigator/sort_test.go ================================================ package navigator import ( "sort" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/safing/portmaster/spn/hub" ) func TestSorting(t *testing.T) { t.Parallel() list := []*Pin{ { Hub: &hub.Hub{ ID: "a", }, measurements: &hub.Measurements{ Latency: 3, Capacity: 4, CalculatedCost: 5, }, analysis: &AnalysisState{ SuggestedHopDistance: 3, }, }, { Hub: &hub.Hub{ ID: "b", }, measurements: &hub.Measurements{ Latency: 4, Capacity: 3, CalculatedCost: 1, }, analysis: &AnalysisState{ SuggestedHopDistance: 2, }, }, { Hub: &hub.Hub{ ID: "c", }, measurements: &hub.Measurements{ Latency: 5, Capacity: 2, CalculatedCost: 2, }, analysis: &AnalysisState{ SuggestedHopDistance: 4, }, }, { Hub: &hub.Hub{ ID: "d", }, measurements: &hub.Measurements{ Latency: 1, Capacity: 1, CalculatedCost: 3, }, analysis: &AnalysisState{ SuggestedHopDistance: 4, }, }, { Hub: &hub.Hub{ ID: "e", }, measurements: &hub.Measurements{ Latency: 2, Capacity: 5, CalculatedCost: 4, }, analysis: &AnalysisState{ SuggestedHopDistance: 4, }, }, } sort.Sort(sortByLowestMeasuredCost(list)) checkSorting(t, list, "b-c-d-e-a") sort.Sort(sortBySuggestedHopDistanceAndLowestMeasuredCost(list)) checkSorting(t, list, "c-d-e-a-b") sort.Sort(sortByLowestMeasuredLatency(list)) checkSorting(t, list, "d-e-a-b-c") sort.Sort(sortByHighestMeasuredCapacity(list)) checkSorting(t, list, "e-a-b-c-d") sort.Sort(sortByPinID(list)) checkSorting(t, list, "a-b-c-d-e") } func checkSorting(t *testing.T, sortedList []*Pin, expectedOrder string) { t.Helper() // Build list ID string. ids := make([]string, 0, len(sortedList)) for _, pin := range sortedList { ids = append(ids, pin.Hub.ID) } sortedIDs := strings.Join(ids, "-") // Check for matching order. assert.Equal(t, expectedOrder, sortedIDs, "should match") } ================================================ FILE: spn/navigator/state.go ================================================ package navigator import ( "strings" "time" ) // PinState holds a bit-mapped collection of Pin states, or a single state used // for assigment and matching. type PinState uint16 const ( // StateNone represents an empty state. StateNone PinState = 0 // Negative States. // StateInvalid signifies that there was an error while processing or // handling this Hub. StateInvalid PinState = 1 << (iota - 1) // 1 << 0 => 00000001 => 0x01 // StateSuperseded signifies that this Hub was superseded by another. This is // the case if any other Hub with a matching IP was verified after this one. // Verification timestamp equals Hub.FirstSeen. StateSuperseded // 0x02 // StateFailing signifies that a recent error was encountered while // communicating with this Hub. Pin.FailingUntil specifies when this state is // re-evaluated at earliest. StateFailing // 0x04 // StateOffline signifies that the Hub is offline. StateOffline // 0x08 // Positive States. // StateHasRequiredInfo signifies that the Hub announces the minimum required // information about itself. StateHasRequiredInfo // 0x10 // StateReachable signifies that the Hub is reachable via the network from // the currently connected primary Hub. StateReachable // 0x20 // StateActive signifies that everything seems fine with the Hub and // connections to it should succeed. This is tested by checking if a valid // semi-ephemeral public key is available. StateActive // 0x40 _ // 0x80: Reserved // Trust and Advisory States. // StateTrusted signifies the Hub has the special trusted status. StateTrusted // 0x0100 // StateUsageDiscouraged signifies that usage of the Hub is discouraged for any task. StateUsageDiscouraged // 0x0200 // StateUsageAsHomeDiscouraged signifies that usage of the Hub as a Home Hub is discouraged. StateUsageAsHomeDiscouraged // 0x0400 // StateUsageAsDestinationDiscouraged signifies that usage of the Hub as a Destination Hub is discouraged. StateUsageAsDestinationDiscouraged // 0x0800 // Special States. // StateIsHomeHub signifies that the Hub is the current Home Hub. While not // negative in itself, selecting the Home Hub does not make sense in almost // all cases. StateIsHomeHub // 0x1000 // StateConnectivityIssues signifies that the Hub reports connectivity issues. // This might impact all connectivity or just some. // This does not invalidate the Hub for all operations and not in all cases. StateConnectivityIssues // 0x2000 // StateAllowUnencrypted signifies that the Hub is available to handle unencrypted connections. StateAllowUnencrypted // 0x4000 // State Summaries. // StateSummaryRegard summarizes all states that must always be set in order to take a Hub into consideration for any task. // TODO: Add StateHasRequiredInfo when we start enforcing Hub information. StateSummaryRegard = StateReachable | StateActive // StateSummaryDisregard summarizes all states that must not be set in order to take a Hub into consideration for any task. StateSummaryDisregard = StateInvalid | StateSuperseded | StateFailing | StateOffline | StateUsageDiscouraged | StateIsHomeHub // StateSummaryStatusesAppliedFromIntel summarizes all states that are applied from the intel file. StateSummaryStatusesAppliedFromIntel = StateTrusted | StateUsageDiscouraged | StateUsageAsHomeDiscouraged | StateUsageAsDestinationDiscouraged ) var allStates = []PinState{ StateInvalid, StateSuperseded, StateFailing, StateOffline, StateHasRequiredInfo, StateReachable, StateActive, StateTrusted, StateUsageDiscouraged, StateUsageAsHomeDiscouraged, StateUsageAsDestinationDiscouraged, StateIsHomeHub, StateConnectivityIssues, StateAllowUnencrypted, } // Add returns a new PinState with the given states added. func (pinState PinState) Add(states PinState) PinState { // OR: // 0011 // | 0101 // = 0111 return pinState | states } // Remove returns a new PinState with the given states removed. func (pinState PinState) Remove(states PinState) PinState { // AND NOT: // 0011 // &^ 0101 // = 0010 return pinState &^ states } // Has returns whether the state has all of the given states. func (pinState PinState) Has(states PinState) bool { // AND: // 0011 // & 0101 // = 0001 return pinState&states == states } // HasAnyOf returns whether the state has any of the given states. func (pinState PinState) HasAnyOf(states PinState) bool { // AND: // 0011 // & 0101 // = 0001 return (pinState & states) != 0 } // HasNoneOf returns whether the state does not have any of the given states. func (pinState PinState) HasNoneOf(states PinState) bool { // AND: // 0011 // & 0101 // = 0001 return (pinState & states) == 0 } // addStates adds the given states on the Pin. func (pin *Pin) addStates(states PinState) { pin.State = pin.State.Add(states) } // removeStates removes the given states on the Pin. func (pin *Pin) removeStates(states PinState) { pin.State = pin.State.Remove(states) } func (m *Map) updateStateSuperseded(pin *Pin) { pin.removeStates(StateSuperseded) // Update StateSuperseded // Iterate over all Pins in order to find a matching IP address. // In order to prevent false positive matching, we have to go through IPv4 // and IPv6 separately. // TODO: This will not scale well beyond about 1000 Hubs. // IPv4 Loop if pin.Hub.Info.IPv4 != nil { for _, mapPin := range m.all { // Skip Pin itself if mapPin.Hub.ID == pin.Hub.ID { continue } // Check for a matching IPv4 address. if mapPin.Hub.Info.IPv4 != nil && pin.Hub.Info.IPv4.Equal(mapPin.Hub.Info.IPv4) { continueChecking := checkAndHandleSuperseding(pin, mapPin) if !continueChecking { break } } } } // IPv6 Loop if pin.Hub.Info.IPv6 != nil { for _, mapPin := range m.all { // Skip Pin itself if mapPin.Hub.ID == pin.Hub.ID { continue } // Check for a matching IPv6 address. if mapPin.Hub.Info.IPv6 != nil && pin.Hub.Info.IPv6.Equal(mapPin.Hub.Info.IPv6) { continueChecking := checkAndHandleSuperseding(pin, mapPin) if !continueChecking { break } } } } } func checkAndHandleSuperseding(newPin, existingPin *Pin) (continueChecking bool) { const ( supersedeNone = iota supersedeExisting supersedeNew ) var action int switch { case newPin.Hub.ID == existingPin.Hub.ID: // Cannot supersede same Hub. // Continue checking. action = supersedeNone // Step 1: Check if only one is active. case newPin.State.Has(StateActive) && existingPin.State.HasNoneOf(StateActive): // If only the new Hub is active, supersede the existing one. action = supersedeExisting case newPin.State.HasNoneOf(StateActive) && existingPin.State.Has(StateActive): // If only the existing Hub is active, supersede the new one. action = supersedeNew // Step 2: Check if only one is reachable. case newPin.State.Has(StateReachable) && existingPin.State.HasNoneOf(StateReachable): // If only the new Hub is reachable, supersede the existing one. action = supersedeExisting case newPin.State.HasNoneOf(StateReachable) && existingPin.State.Has(StateReachable): // If only the existing Hub is reachable, supersede the new one. action = supersedeNew // Step 3: Check which one has been seen first. case newPin.Hub.FirstSeen.After(existingPin.Hub.FirstSeen): // If the new Hub has been first seen later, supersede the existing one. action = supersedeExisting default: // If the existing Hub has been first seen later, supersede the new one. action = supersedeNew } switch action { case supersedeExisting: existingPin.addStates(StateSuperseded) existingPin.pushChanges.Set() // Continue checking, as there might be other Hubs to be superseded. return true case supersedeNew: newPin.addStates(StateSuperseded) newPin.pushChanges.Set() // If the new pin is superseded, do _not_ continue, as this will lead to an incorrect state. return false case supersedeNone: fallthrough default: // Do nothing, continue checking. return true } } func (pin *Pin) updateStateHasRequiredInfo() { pin.removeStates(StateHasRequiredInfo) // Check for required Hub Information. switch { case len(pin.Hub.Info.Name) == 0: case len(pin.Hub.Info.Group) == 0: case len(pin.Hub.Info.ContactAddress) == 0: case len(pin.Hub.Info.ContactService) == 0: case len(pin.Hub.Info.Hosters) == 0: case len(pin.Hub.Info.Hosters[0]) == 0: case len(pin.Hub.Info.Datacenter) == 0: default: pin.addStates(StateHasRequiredInfo) } } func (m *Map) updateActiveHubs() { now := time.Now().Unix() for _, pin := range m.all { pin.updateStateActive(now) } } func (pin *Pin) updateStateActive(now int64) { pin.removeStates(StateActive) // Check for active key. for _, key := range pin.Hub.Status.Keys { if now < key.Expires { pin.addStates(StateActive) return } } } func (m *Map) recalculateReachableHubs() error { if m.home == nil { return ErrHomeHubUnset } // reset for _, pin := range m.all { pin.removeStates(StateReachable) pin.HopDistance = 0 pin.pushChanges.Set() } // find all connected Hubs m.home.markReachable(1) return nil } func (pin *Pin) markReachable(hopDistance int) { switch { case !pin.State.Has(StateReachable): // Pin wasn't reachable before. case hopDistance < pin.HopDistance: // New path has a shorter distance. case pin.State.HasAnyOf(StateSummaryDisregard): //nolint:staticcheck // Ignore disregarded pins for reachability calculation. return default: // Pin is already reachable at same or better distance. return } // Update reachability. pin.addStates(StateReachable) pin.HopDistance = hopDistance pin.pushChanges.Set() // Propagate to connected Pins. hopDistance++ for _, lane := range pin.ConnectedTo { lane.Pin.markReachable(hopDistance) } } // Export returns a list of all state names. func (pinState PinState) Export() []string { // Check if there are no states. if pinState == StateNone { return nil } // Collect state names. var stateNames []string for _, state := range allStates { if pinState.Has(state) { stateNames = append(stateNames, state.Name()) } } return stateNames } // String returns the states as a human readable string. func (pinState PinState) String() string { stateNames := pinState.Export() if len(stateNames) == 0 { return "None" } return strings.Join(stateNames, ", ") } // Name returns the name of a single state flag. func (pinState PinState) Name() string { switch pinState { case StateNone: return "None" case StateInvalid: return "Invalid" case StateSuperseded: return "Superseded" case StateFailing: return "Failing" case StateOffline: return "Offline" case StateHasRequiredInfo: return "HasRequiredInfo" case StateReachable: return "Reachable" case StateActive: return "Active" case StateTrusted: return "Trusted" case StateUsageDiscouraged: return "UsageDiscouraged" case StateUsageAsHomeDiscouraged: return "UsageAsHomeDiscouraged" case StateUsageAsDestinationDiscouraged: return "UsageAsDestinationDiscouraged" case StateIsHomeHub: return "IsHomeHub" case StateConnectivityIssues: return "ConnectivityIssues" case StateAllowUnencrypted: return "AllowUnencrypted" case StateSummaryRegard, StateSummaryDisregard: // Satisfy exhaustive linter. fallthrough default: return "Unknown" } } ================================================ FILE: spn/navigator/state_test.go ================================================ package navigator import ( "testing" "github.com/stretchr/testify/assert" ) func TestStates(t *testing.T) { t.Parallel() p := &Pin{} p.addStates(StateInvalid | StateFailing | StateSuperseded) assert.Equal(t, StateInvalid|StateFailing|StateSuperseded, p.State) p.removeStates(StateFailing | StateSuperseded) assert.Equal(t, StateInvalid, p.State) p.addStates(StateTrusted | StateActive) assert.True(t, p.State.Has(StateInvalid|StateTrusted)) assert.False(t, p.State.Has(StateInvalid|StateSuperseded)) assert.True(t, p.State.HasAnyOf(StateInvalid|StateTrusted)) assert.True(t, p.State.HasAnyOf(StateInvalid|StateSuperseded)) assert.False(t, p.State.HasAnyOf(StateSuperseded|StateFailing)) assert.False(t, p.State.Has(StateSummaryRegard)) assert.False(t, p.State.Has(StateSummaryDisregard)) assert.True(t, p.State.HasAnyOf(StateSummaryRegard)) assert.True(t, p.State.HasAnyOf(StateSummaryDisregard)) } ================================================ FILE: spn/navigator/update.go ================================================ package navigator import ( "context" "fmt" "path" "strings" "time" "github.com/tevino/abool" "golang.org/x/exp/slices" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/intel/geoip" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/service/profile" "github.com/safing/portmaster/spn/hub" ) var db = database.NewInterface(&database.Options{ Local: true, Internal: true, }) // InitializeFromDatabase loads all Hubs from the given database prefix and adds them to the Map. func (m *Map) InitializeFromDatabase() error { m.Lock() defer m.Unlock() // start query for Hubs iter, err := db.Query(query.New(hub.MakeHubDBKey(m.Name, ""))) if err != nil { return fmt.Errorf("failed to start query for initialization feed of %s map: %w", m.Name, err) } // update navigator var hubCount int log.Tracef("spn/navigator: starting to initialize %s map from database", m.Name) for r := range iter.Next { h, err := hub.EnsureHub(r) if err != nil { log.Warningf("spn/navigator: could not parse hub %q while initializing %s map: %s", r.Key(), m.Name, err) continue } hubCount++ m.updateHub(h, false, true) } switch { case iter.Err() != nil: return fmt.Errorf("failed to (fully) initialize %s map: %w", m.Name, iter.Err()) case hubCount == 0: log.Warningf("spn/navigator: no hubs available for %s map - this is normal on first start", m.Name) default: log.Infof("spn/navigator: added %d hubs from database to %s map", hubCount, m.Name) } return nil } // UpdateHook updates the a map from database changes. type UpdateHook struct { database.HookBase m *Map } // UsesPrePut implements the Hook interface. func (hook *UpdateHook) UsesPrePut() bool { return true } // PrePut implements the Hook interface. func (hook *UpdateHook) PrePut(r record.Record) (record.Record, error) { // Remove deleted hubs from the map. if r.Meta().IsDeleted() { hook.m.RemoveHub(path.Base(r.Key())) return r, nil } // Ensure we have a hub and update it in navigation map. h, err := hub.EnsureHub(r) if err != nil { log.Debugf("spn/navigator: record %s is not a hub", r.Key()) } else { hook.m.updateHub(h, true, false) } return r, nil } // RegisterHubUpdateHook registers a database pre-put hook that updates all // Hubs saved at the given database prefix. func (m *Map) RegisterHubUpdateHook() (err error) { m.hubUpdateHook, err = database.RegisterHook( query.New(hub.MakeHubDBKey(m.Name, "")), &UpdateHook{m: m}, ) return err } // CancelHubUpdateHook cancels the map's update hook. func (m *Map) CancelHubUpdateHook() { if m.hubUpdateHook != nil { if err := m.hubUpdateHook.Cancel(); err != nil { log.Warningf("spn/navigator: failed to cancel update hook for map %s: %s", m.Name, err) } } } // RemoveHub removes a Hub from the Map. func (m *Map) RemoveHub(id string) { m.Lock() defer m.Unlock() // Get pin and remove it from the map, if it exists. pin, ok := m.all[id] if !ok { return } delete(m.all, id) // Remove lanes from removed Pin. for id := range pin.ConnectedTo { // Remove Lane from peer. peer, ok := m.all[id] if ok { delete(peer.ConnectedTo, pin.Hub.ID) peer.pushChanges.Set() } } // Push update to subscriptions. export := pin.Export() export.Meta().Delete() mapDBController.PushUpdate(export) // Push lane changes. m.PushPinChanges() } // UpdateHub updates a Hub on the Map. func (m *Map) UpdateHub(h *hub.Hub) { m.updateHub(h, true, true) } func (m *Map) updateHub(h *hub.Hub, lockMap, lockHub bool) { if lockMap { m.Lock() defer m.Unlock() } if lockHub { h.Lock() defer h.Unlock() } // Hub requires both Info and Status to be added to the Map. if h.Info == nil || h.Status == nil { return } // Create or update Pin. pin, ok := m.all[h.ID] if ok { pin.Hub = h } else { pin = &Pin{ Hub: h, ConnectedTo: make(map[string]*Lane), stateIntelApplied: abool.New(), pushChanges: abool.New(), } m.all[h.ID] = pin } pin.pushChanges.Set() // 1. Update Pin Data. // Add/Update location data from IP addresses. pin.updateLocationData() // Override Pin Data. m.updateInfoOverrides(pin) // Update Hub cost. pin.Cost = CalculateHubCost(pin.Hub.Status.Load) // Ensure measurements are set when enabled. if m.measuringEnabled && pin.measurements == nil { // Get shared measurements. pin.measurements = pin.Hub.GetMeasurementsWithLockedHub() // Update cost calculation. latency, _ := pin.measurements.GetLatency() capacity, _ := pin.measurements.GetCapacity() pin.measurements.SetCalculatedCost(CalculateLaneCost(latency, capacity)) // Update geo proximity. // Get own location. var myLocation *geoip.Location switch { case m.home != nil && m.home.LocationV4 != nil: myLocation = m.home.LocationV4 case m.home != nil && m.home.LocationV6 != nil: myLocation = m.home.LocationV6 default: locations, ok := netenv.GetInternetLocation() if ok { myLocation = locations.Best().LocationOrNil() } } // Calculate proximity with available location. if myLocation != nil { switch { case pin.LocationV4 != nil: pin.measurements.SetGeoProximity( myLocation.EstimateNetworkProximity(pin.LocationV4), ) case pin.LocationV6 != nil: pin.measurements.SetGeoProximity( myLocation.EstimateNetworkProximity(pin.LocationV6), ) } } } // 2. Update Pin States. // Update the invalid status of the Pin. if pin.Hub.InvalidInfo || pin.Hub.InvalidStatus { pin.addStates(StateInvalid) } else { pin.removeStates(StateInvalid) } // Update online status of the Pin. if pin.Hub.HasFlag(hub.FlagOffline) || pin.Hub.Status.Version == hub.VersionOffline { pin.addStates(StateOffline) } else { pin.removeStates(StateOffline) } // Update online status of the Pin. if pin.Hub.HasFlag(hub.FlagAllowUnencrypted) { pin.addStates(StateAllowUnencrypted) } else { pin.removeStates(StateAllowUnencrypted) } // Update from status flags. if pin.Hub.HasFlag(hub.FlagNetError) { pin.addStates(StateConnectivityIssues) } else { pin.removeStates(StateConnectivityIssues) } // Update Trust and Advisory Statuses. m.updateIntelStatuses(pin, cfgOptionTrustNodeNodes()) // Update Statuses derived from Hub. pin.updateStateHasRequiredInfo() pin.updateStateActive(time.Now().Unix()) // 3. Update Lanes. // Mark all existing Lanes as inactive. for _, lane := range pin.ConnectedTo { lane.active = false } // Update Lanes (connections to other Hubs) from the Status. for _, lane := range pin.Hub.Status.Lanes { // Check if this is a Lane to itself. if lane.ID == pin.Hub.ID { continue } // First, get the Lane peer. peer, ok := m.all[lane.ID] if !ok { // We need to wait for peer to be added to the Map. continue } m.updateHubLane(pin, lane, peer) } // Remove all inactive/abandoned Lanes from both Pins. var removedLanes bool for id, lane := range pin.ConnectedTo { if !lane.active { // Remove Lane from this Pin. delete(pin.ConnectedTo, id) pin.pushChanges.Set() removedLanes = true // Remove Lane from peer. peer, ok := m.all[id] if ok { delete(peer.ConnectedTo, pin.Hub.ID) peer.pushChanges.Set() } } } // Fully recalculate reachability if any Lanes were removed. if removedLanes { err := m.recalculateReachableHubs() if err != nil { log.Warningf("spn/navigator: failed to recalculate reachable Hubs: %s", err) } } // 4. Update states that depend on other information. // Check if hub is superseded or if it supersedes another hub. m.updateStateSuperseded(pin) // Push updates. m.PushPinChanges() } const ( minUnconfirmedLatency = 10 * time.Millisecond maxUnconfirmedCapacity = 100000000 // 100Mbit/s cap1Mbit float32 = 1000000 cap10Mbit float32 = 10000000 cap100Mbit float32 = 100000000 cap1Gbit float32 = 1000000000 cap10Gbit float32 = 10000000000 ) // updateHubLane updates a lane between two Hubs on the Map. // pin must already be locked, lane belongs to pin. // peer will be locked by this function. func (m *Map) updateHubLane(pin *Pin, lane *hub.Lane, peer *Pin) { peer.Hub.Lock() defer peer.Hub.Unlock() // Then get the corresponding Lane from that peer, if it exists. var peerLane *hub.Lane for _, possiblePeerLane := range peer.Hub.Status.Lanes { if possiblePeerLane.ID == pin.Hub.ID { peerLane = possiblePeerLane // We have found the corresponding peerLane, break the loop. break } } if peerLane == nil { // The peer obviously does not advertise a Lane to this Hub. // Maybe this is a fresh Lane, and the message has not yet reached us. // Alternatively, the Lane could have been recently removed. // Abandon this Lane for now. delete(pin.ConnectedTo, peer.Hub.ID) return } // Calculate combined latency, use the greater value. combinedLatency := lane.Latency if peerLane.Latency > combinedLatency { combinedLatency = peerLane.Latency } // Enforce minimum value if at least one side has no data. if (lane.Latency == 0 || peerLane.Latency == 0) && combinedLatency < minUnconfirmedLatency { combinedLatency = minUnconfirmedLatency } // Calculate combined capacity, use the lesser existing value. combinedCapacity := lane.Capacity if combinedCapacity == 0 || (peerLane.Capacity > 0 && peerLane.Capacity < combinedCapacity) { combinedCapacity = peerLane.Capacity } // Enforce maximum value if at least one side has no data. if (lane.Capacity == 0 || peerLane.Capacity == 0) && combinedCapacity > maxUnconfirmedCapacity { combinedCapacity = maxUnconfirmedCapacity } // Calculate lane cost. laneCost := CalculateLaneCost(combinedLatency, combinedCapacity) // Add Lane to both Pins and override old values in the process. pin.ConnectedTo[peer.Hub.ID] = &Lane{ Pin: peer, Capacity: combinedCapacity, Latency: combinedLatency, Cost: laneCost, active: true, } peer.ConnectedTo[pin.Hub.ID] = &Lane{ Pin: pin, Capacity: combinedCapacity, Latency: combinedLatency, Cost: laneCost, active: true, } peer.pushChanges.Set() // Check for reachability. if pin.State.Has(StateReachable) { peer.markReachable(pin.HopDistance + 1) } if peer.State.Has(StateReachable) { pin.markReachable(peer.HopDistance + 1) } } // ResetFailingStates resets the failing state on all pins. func (m *Map) ResetFailingStates() { m.Lock() defer m.Unlock() for _, pin := range m.all { pin.ResetFailingState() } m.PushPinChanges() } func (m *Map) updateFailingStates(ctx *mgr.WorkerCtx) error { m.Lock() defer m.Unlock() for _, pin := range m.all { if pin.State.Has(StateFailing) && !pin.IsFailing() { pin.removeStates(StateFailing) } } return nil } func (m *Map) updateStates(ctx *mgr.WorkerCtx) error { var toDelete []string m.Lock() defer m.Unlock() pinLoop: for _, pin := range m.all { // Check for discontinued Hubs. if m.intel != nil { hubIntel, ok := m.intel.Hubs[pin.Hub.ID] if ok && hubIntel.Discontinued { toDelete = append(toDelete, pin.Hub.ID) log.Infof("spn/navigator: deleting discontinued %s", pin.Hub) continue pinLoop } } // Check for obsoleted Hubs. if pin.State.HasNoneOf(StateActive) && pin.Hub.Obsolete() { toDelete = append(toDelete, pin.Hub.ID) log.Infof("spn/navigator: deleting obsolete %s", pin.Hub) } // Delete hubs async, as deleting triggers a couple hooks that lock the map. if len(toDelete) > 0 { module.mgr.Go("delete hubs", func(_ *mgr.WorkerCtx) error { for _, idToDelete := range toDelete { err := hub.RemoveHubAndMsgs(m.Name, idToDelete) if err != nil { log.Warningf("spn/navigator: failed to delete Hub %s: %s", idToDelete, err) } } return nil }) } } // Update StateActive. m.updateActiveHubs() // Update StateReachable. return m.recalculateReachableHubs() } // AddBootstrapHubs adds the given bootstrap hubs to the map. func (m *Map) AddBootstrapHubs(bootstrapTransports []string) error { m.Lock() defer m.Unlock() return m.addBootstrapHubs(bootstrapTransports) } func (m *Map) addBootstrapHubs(bootstrapTransports []string) error { var anyAdded bool var lastErr error var failed int for _, bootstrapTransport := range bootstrapTransports { err := m.addBootstrapHub(bootstrapTransport) if err != nil { log.Warningf("spn/navigator: failed to add bootstrap hub %q to map %s: %s", bootstrapTransport, m.Name, err) lastErr = err failed++ } else { anyAdded = true } } if lastErr != nil && !anyAdded { return lastErr } return nil } func (m *Map) addBootstrapHub(bootstrapTransport string) error { // Parse bootstrap hub. transport, hubID, hubIP, err := hub.ParseBootstrapHub(bootstrapTransport) if err != nil { return fmt.Errorf("invalid bootstrap hub: %w", err) } // Check if hub already exists. var h *hub.Hub pin, ok := m.all[hubID] if ok { h = pin.Hub } else { h = &hub.Hub{ ID: hubID, Map: m.Name, Info: &hub.Announcement{ ID: hubID, }, Status: &hub.Status{}, FirstSeen: time.Now(), // Do not garbage collect bootstrap hubs. } } // Add IP if it does not yet exist. if hubIP4 := hubIP.To4(); hubIP4 != nil { if h.Info.IPv4 == nil { h.Info.IPv4 = hubIP4 } else if !h.Info.IPv4.Equal(hubIP4) { return fmt.Errorf("additional bootstrap entry with same ID but mismatching IP address: %s", hubIP) } } else { if h.Info.IPv6 == nil { h.Info.IPv6 = hubIP } else if !h.Info.IPv6.Equal(hubIP) { return fmt.Errorf("additional bootstrap entry with same ID but mismatching IP address: %s", hubIP) } } // Add transport if it does not yet exist. t := transport.String() if !utils.StringInSlice(h.Info.Transports, t) { h.Info.Transports = append(h.Info.Transports, t) } // Add/update to map for bootstrapping. m.updateHub(h, false, false) log.Infof("spn/navigator: added/updated bootstrap %s to map %s", h, m.Name) return nil } // UpdateConfigQuickSettings updates config quick settings with available countries. func (m *Map) UpdateConfigQuickSettings(wc *mgr.WorkerCtx) error { ctx, tracer := log.AddTracer(wc.Ctx()) tracer.Trace("navigator: updating SPN rules country quick settings") defer tracer.Submit() opts := m.DefaultOptions() opts.Home = &HomeHubOptions{ Regard: StateTrusted, } opts.Destination = &DestinationHubOptions{ Regard: StateTrusted, Disregard: StateIsHomeHub, } // Home Policy. if err := m.updateQuickSettingExcludeCountryList(ctx, "spn/homePolicy", opts, HomeHub); err != nil { return err } // Transit Policy. if err := m.updateQuickSettingExcludeCountryList(ctx, profile.CfgOptionTransitHubPolicyKey, opts, TransitHub); err != nil { return err } // Exit Policy. if err := m.updateSelectRuleCountryList(ctx, profile.CfgOptionExitHubPolicyKey, opts, DestinationHub); err != nil { return err } // DNS Exit Policy. if err := m.updateSelectRuleCountryList(ctx, "spn/dnsExitPolicy", opts, DestinationHub); err != nil { return err } // Trust Nodes. if err := m.updateQuickSettingVerifiedOwnerList(ctx, "spn/trustNodes"); err != nil { return err } tracer.Trace("navigator: finished updating SPN rules country quick settings") return nil } func (m *Map) updateQuickSettingExcludeCountryList(ctx context.Context, configKey string, opts *Options, matchFor HubType) error { // Get config option. cfgOption, err := config.GetOption(configKey) if err != nil { return fmt.Errorf("failed to get config option %s: %w", configKey, err) } // Get list of countries for this config option. countries := m.GetAvailableCountries(opts, matchFor) // Convert to list. countryList := make([]*geoip.CountryInfo, 0, len(countries)) for _, country := range countries { countryList = append(countryList, country) } // Sort list. slices.SortFunc[[]*geoip.CountryInfo, *geoip.CountryInfo](countryList, func(a, b *geoip.CountryInfo) int { return strings.Compare(a.Name, b.Name) }) // Compile list of quick settings. quickSettings := make([]config.QuickSetting, 0, len(countries)) for _, country := range countryList { quickSettings = append(quickSettings, config.QuickSetting{ Name: fmt.Sprintf("Exclude %s (%s)", country.Name, country.Code), Value: []string{"- " + country.Code}, Action: config.QuickMergeTop, }) } // Lock config option and set new quick settings. cfgOption.Lock() defer cfgOption.Unlock() cfgOption.Annotations[config.QuickSettingsAnnotation] = quickSettings log.Tracer(ctx).Debugf("navigator: updated %d countries in quick settings for %s", len(quickSettings), configKey) return nil } type selectCountry struct { config.QuickSetting FlagID string } func (m *Map) updateSelectRuleCountryList(ctx context.Context, configKey string, opts *Options, matchFor HubType) error { // Get config option. cfgOption, err := config.GetOption(configKey) if err != nil { return fmt.Errorf("failed to get config option %s: %w", configKey, err) } // Get list of countries for this config option. countries := m.GetAvailableCountries(opts, matchFor) // Convert to list. countryList := make([]*geoip.CountryInfo, 0, len(countries)) for _, country := range countries { countryList = append(countryList, country) } // Sort list. slices.SortFunc[[]*geoip.CountryInfo, *geoip.CountryInfo](countryList, func(a, b *geoip.CountryInfo) int { return strings.Compare(a.Name, b.Name) }) // Get continents from countries. continents := make(map[string]*geoip.ContinentInfo) for _, country := range countryList { continents[country.Continent.Code] = &country.Continent } // Convert to list. continentList := make([]*geoip.ContinentInfo, 0, len(continents)) for _, continent := range continents { continentList = append(continentList, continent) } // Sort list. slices.SortFunc[[]*geoip.ContinentInfo, *geoip.ContinentInfo](continentList, func(a, b *geoip.ContinentInfo) int { return strings.Compare(a.Name, b.Name) }) // Start compiling all options. selections := make([]selectCountry, 0, len(continents)+len(countries)+2) // Add EU as special region. selections = append(selections, selectCountry{ QuickSetting: config.QuickSetting{ Name: "European Union", Value: []string{"+ AT", "+ BE", "+ BG", "+ CY", "+ CZ", "+ DE", "+ DK", "+ EE", "+ ES", "+ FI", "+ FR", "+ GR", "+ HR", "+ HU", "+ IE", "+ IT", "+ LT", "+ LU", "+ LV", "+ MT", "+ NL", "+ PL", "+ PT", "+ RO", "+ SE", "+ SI", "+ SK", "- *"}, Action: config.QuickReplace, }, FlagID: "EU", }) selections = append(selections, selectCountry{ QuickSetting: config.QuickSetting{ Name: "US and Canada", Value: []string{"+ US", "+ CA", "- *"}, Action: config.QuickReplace, }, }) // Add countries to quick settings. for _, country := range countryList { selections = append(selections, selectCountry{ QuickSetting: config.QuickSetting{ Name: fmt.Sprintf("%s (%s)", country.Name, country.Code), Value: []string{"+ " + country.Code, "- *"}, Action: config.QuickReplace, }, FlagID: country.Code, }) } // Add continents to quick settings. for _, continent := range continentList { selections = append(selections, selectCountry{ QuickSetting: config.QuickSetting{ Name: fmt.Sprintf("%s (C:%s)", continent.Name, continent.Code), Value: []string{"+ C:" + continent.Code, "- *"}, Action: config.QuickReplace, }, }) } // Lock config option and set new quick settings. cfgOption.Lock() defer cfgOption.Unlock() cfgOption.Annotations[config.QuickSettingsAnnotation] = selections log.Tracer(ctx).Debugf("navigator: updated %d countries in quick settings for %s", len(selections), configKey) return nil } func (m *Map) updateQuickSettingVerifiedOwnerList(ctx context.Context, configKey string) error { // Get config option. cfgOption, err := config.GetOption(configKey) if err != nil { return fmt.Errorf("failed to get config option %s: %w", configKey, err) } pins := m.pinList(true) verifiedOwners := make([]string, 0, len(pins)/5) // Capacity is an estimation. for _, pin := range pins { pin.Lock() vo := pin.VerifiedOwner pin.Unlock() // Skip invalid/unneeded values. switch vo { case "", "Safing": continue } // Add to list, if not yet in there. if !slices.Contains[[]string, string](verifiedOwners, vo) { verifiedOwners = append(verifiedOwners, vo) } } // Sort list. slices.Sort[[]string](verifiedOwners) // Compile list of quick settings. quickSettings := make([]config.QuickSetting, 0, len(verifiedOwners)) for _, vo := range verifiedOwners { quickSettings = append(quickSettings, config.QuickSetting{ Name: fmt.Sprintf("Trust %s", vo), Value: []string{vo}, Action: config.QuickMergeBottom, }) } // Lock config option and set new quick settings. cfgOption.Lock() defer cfgOption.Unlock() cfgOption.Annotations[config.QuickSettingsAnnotation] = quickSettings log.Tracer(ctx).Debugf("navigator: updated %d verified owners in quick settings for %s", len(quickSettings), configKey) return nil } ================================================ FILE: spn/patrol/domains.go ================================================ package patrol import ( "math/rand" "time" ) // getRandomTestDomain returns a random test domain from the test domain list. // Not cryptographically secure random, though. func getRandomTestDomain() string { rng := rand.New(rand.NewSource(time.Now().UnixNano())) //nolint:gosec return testDomains[rng.Intn(len(testDomains)-1)] //nolint:gosec // Weak randomness is not an issue here. } // testDomains is a list of domains to check if they respond successfully to a HTTP GET request. // They are sourced from tranco - trimmed, checked, and cleaned. // Use TestCleanDomains to clean a new/updated list. // Treat as a constant. var testDomains = []string{ "about.com", "addtoany.com", "adobe.com", "aliyun.com", "ampproject.org", "android.com", "apache.org", "apple.com", "apple.news", "appspot.com", "arnebrachhold.de", "avast.com", "bbc.co.uk", "bbc.com", "bing.com", "blogger.com", "blogspot.com", "branch.io", "calendly.com", "cam.ac.uk", "canonical.com", "canva.com", "cisco.com", "cloudflare.com", "cloudns.net", "cnblogs.com", "cnn.com", "creativecommons.org", "criteo.com", "cupfox.app", "dailymail.co.uk", "ddnss.de", "debian.org", "digitalocean.com", "doi.org", "domainmarket.com", "doubleclick.net", "dreamhost.com", "dropbox.com", "dynect.net", "ed.gov", "elegantthemes.com", "elpais.com", "epa.gov", "eporner.com", "espn.com", "europa.eu", "example.com", "facebook.com", "fb.com", "fb.me", "fb.watch", "fbcdn.net", "feedburner.com", "free.fr", "ftc.gov", "g.page", "getbootstrap.com", "gitlab.com", "gmail.com", "gnu.org", "goo.gl", "google-analytics.com", "google.ca", "google.co.in", "google.co.jp", "google.co.th", "google.co.uk", "google.com.au", "google.com.br", "google.com.hk", "google.com.mx", "google.com.tr", "google.com.tw", "google.com", "google.de", "google.es", "google.fr", "google.it", "googledomains.com", "googlesyndication.com", "gstatic.com", "harvard.edu", "hitomi.la", "hubspot.com", "hugedomains.com", "ibm.com", "icloud.com", "ikea.com", "ilovepdf.com", "indiatimes.com", "instagram.com", "investing.com", "investopedia.com", "irs.gov", "kickstarter.com", "launchpad.net", "lencr.org", "lijit.com", "linkedin.com", "linode.com", "mashable.com", "medium.com", "mega.co.nz", "mega.nz", "merriam-webster.com", "mit.edu", "netflix.com", "nginx.org", "nist.gov", "notion.so", "nsone.net", "office.com", "onetrust.com", "openstreetmap.org", "patreon.com", "pexels.com", "photobucket.com", "php.net", "pki.goog", "plos.org", "ps.kz", "readthedocs.io", "redd.it", "reddit.com", "remove.bg", "rfc-editor.org", "savefrom.net", "sedo.com", "so-net.ne.jp", "sourceforge.net", "spamhaus.org", "speedtest.net", "spotify.com", "stanford.edu", "state.gov", "substack.com", "t.me", "taboola.com", "techcrunch.com", "telegram.me", "telegram.org", "threema.ch", "tinyurl.com", "ubuntu.com", "ui.com", "umich.edu", "uol.com.br", "upenn.edu", "usgs.gov", "utexas.edu", "va.gov", "verisign.com", "vmware.com", "w3.org", "wa.me", "webs.com", "whatsapp.com", "whatsapp.net", "whitehouse.gov", "wikimedia.org", "wikipedia.org", "wiktionary.org", "www.aliyundrive.com", "www.amazon.ca", "www.amazon.co.jp", "www.amazon.co.uk", "www.amazon.com", "www.amazon.de", "www.amazon.es", "www.amazon.fr", "www.amazon.in", "www.amazon.it", "www.aol.com", "www.appsflyer.com", "www.att.com", "www.business.site", "www.ca.gov", "www.canada.ca", "www.cctv.com", "www.cdc.gov", "www.chinaz.com", "www.cloud.com", "www.cnet.com", "www.comcast.com", "www.comcast.net", "www.cornell.edu", "www.crashlytics.com", "www.datadoghq.com", "www.db.com", "www.deloitte.com", "www.dw.com", "www.engadget.com", "www.eset.com", "www.fao.org", "www.fedex.com", "www.flickr.com", "www.force.com", "www.ford.com", "www.frontiersin.org", "www.geeksforgeeks.org", "www.gene.com", "www.genius.com", "www.github.io", "www.gov.uk", "www.gravatar.com", "www.healthline.com", "www.hhs.gov", "www.hichina.com", "www.hinet.net", "www.house.gov", "www.hp.com", "www.huawei.com", "www.hupu.com", "www.ietf.org", "www.immunet.com", "www.independent.co.uk", "www.intel.com", "www.jotform.com", "www.klaviyo.com", "www.launchdarkly.com", "www.live.com", "www.macromedia.com", "www.medallia.com", "www.mediatek.com", "www.medicalnewstoday.com", "www.microsoft.com", "www.mongodb.com", "www.mysql.com", "www.namu.wiki", "www.nasa.gov", "www.nba.com", "www.nbcnews.com", "www.nih.gov", "www.noaa.gov", "www.npr.org", "www.nps.gov", "www.ny.gov", "www.okta.com", "www.openai.com", "www.optimizely.com", "www.oracle.com", "www.outlook.com", "www.paloaltonetworks.com", "www.pbs.org", "www.pixabay.com", "www.plala.or.jp", "www.playstation.com", "www.plesk.com", "www.princeton.edu", "www.prnewswire.com", "www.psu.edu", "www.python.org", "www.qq.com", "www.quantserve.com", "www.quillbot.com", "www.rackspace.com", "www.redhat.com", "www.researchgate.net", "www.roku.com", "www.salesforce.com", "www.skype.com", "www.sun.com", "www.teamviewer.com", "www.ted.com", "www.tesla.com", "www.theguardian.com", "www.typeform.com", "www.uchicago.edu", "www.ucla.edu", "www.usda.gov", "www.usps.com", "www.utorrent.com", "www.warnerbros.com", "www.webex.com", "www.who.int", "www.worldbank.org", "www.xbox.com", "www.xerox.com", "www.youdao.com", "www.zdnet.com", "www.zebra.com", "yahoo.com", "yale.edu", "yandex.com", "yandex.net", "youku.com", "youtu.be", "youtube.com", "zemanta.com", "zoro.to", } ================================================ FILE: spn/patrol/domains_test.go ================================================ package patrol import ( "context" "fmt" "sort" "testing" ) var enableDomainTools = "no" // change to "yes" to enable // TestCleanDomains checks, cleans and prints an improved domain list. // Run with: // go test -run ^TestCleanDomains$ github.com/safing/portmaster/spn/patrol -ldflags "-X github.com/safing/portmaster/spn/patrol.enableDomainTools=yes" -timeout 3h -v // This is provided as a test for easier maintenance and ops. func TestCleanDomains(t *testing.T) { //nolint:paralleltest if enableDomainTools != "yes" { t.Skip() return } // Setup context. ctx := context.Background() // Go through all domains and check if they are reachable. goodDomains := make([]string, 0, len(testDomains)) for _, domain := range testDomains { // Check if domain is reachable. code, err := domainIsUsable(ctx, domain) if err != nil { fmt.Printf("FAIL: %s: %s\n", domain, err) } else { fmt.Printf("OK: %s [%d]\n", domain, code) goodDomains = append(goodDomains, domain) continue } // If failed, try again with a www. prefix wwwDomain := "www." + domain code, err = domainIsUsable(ctx, wwwDomain) if err != nil { fmt.Printf("FAIL: %s: %s\n", wwwDomain, err) } else { fmt.Printf("OK: %s [%d]\n", wwwDomain, code) goodDomains = append(goodDomains, wwwDomain) } } sort.Strings(goodDomains) fmt.Println("printing good domains:") for _, domain := range goodDomains { fmt.Printf("%q,\n", domain) } fmt.Println("IMPORTANT: do not forget to go through list and check if everything looks good") } func domainIsUsable(ctx context.Context, domain string) (statusCode int, err error) { // Try IPv6 first as it is way more likely to fail. statusCode, err = CheckHTTPSConnection(ctx, "tcp6", domain) if err != nil { return } return CheckHTTPSConnection(ctx, "tcp4", domain) } ================================================ FILE: spn/patrol/http.go ================================================ package patrol import ( "context" "fmt" "net" "net/http" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" ) var httpsConnectivityConfirmed = abool.NewBool(true) // HTTPSConnectivityConfirmed returns whether the last HTTPS connectivity check succeeded. // Is "true" before first test. func HTTPSConnectivityConfirmed() bool { return httpsConnectivityConfirmed.IsSet() } func connectivityCheckTask(wc *mgr.WorkerCtx) error { // Start tracing logs. ctx, tracer := log.AddTracer(wc.Ctx()) defer tracer.Submit() // Run checks and report status. success := runConnectivityChecks(ctx) if success { tracer.Info("spn/patrol: all connectivity checks succeeded") if httpsConnectivityConfirmed.SetToIf(false, true) { module.EventChangeSignal.Submit(struct{}{}) } return nil } tracer.Errorf("spn/patrol: connectivity check failed") if httpsConnectivityConfirmed.SetToIf(true, false) { module.EventChangeSignal.Submit(struct{}{}) } return nil } func runConnectivityChecks(ctx context.Context) (ok bool) { switch { case conf.HubHasIPv4() && !runHTTPSConnectivityChecks(ctx, "tcp4"): return false case conf.HubHasIPv6() && !runHTTPSConnectivityChecks(ctx, "tcp6"): return false default: // All checks passed. return true } } func runHTTPSConnectivityChecks(ctx context.Context, network string) (ok bool) { // Step 1: Check 1 domain, require 100% if checkHTTPSConnectivity(ctx, network, 1, 1) { return true } // Step 2: Check 5 domains, require 80% if checkHTTPSConnectivity(ctx, network, 5, 0.8) { return true } // Step 3: Check 20 domains, require 70% if checkHTTPSConnectivity(ctx, network, 20, 0.7) { return true } return false } func checkHTTPSConnectivity(ctx context.Context, network string, checks int, requiredSuccessFraction float32) (ok bool) { log.Tracer(ctx).Tracef( "spn/patrol: testing connectivity via https (%d checks; %.0f%% required)", checks, requiredSuccessFraction*100, ) // Run tests. var succeeded int for range checks { if checkHTTPSConnection(ctx, network) { succeeded++ } } // Check success. successFraction := float32(succeeded) / float32(checks) if successFraction < requiredSuccessFraction { log.Tracer(ctx).Warningf( "spn/patrol: https/%s connectivity check failed: %d/%d (%.0f%%)", network, succeeded, checks, successFraction*100, ) return false } log.Tracer(ctx).Debugf( "spn/patrol: https/%s connectivity check succeeded: %d/%d (%.0f%%)", network, succeeded, checks, successFraction*100, ) return true } func checkHTTPSConnection(ctx context.Context, network string) (ok bool) { testDomain := getRandomTestDomain() code, err := CheckHTTPSConnection(ctx, network, testDomain) if err != nil { log.Tracer(ctx).Debugf("spn/patrol: https/%s connect check failed: %s: %s", network, testDomain, err) return false } log.Tracer(ctx).Tracef("spn/patrol: https/%s connect check succeeded: %s [%d]", network, testDomain, code) return true } // CheckHTTPSConnection checks if a HTTPS connection to the given domain can be established. func CheckHTTPSConnection(ctx context.Context, network, domain string) (statusCode int, err error) { // Check network parameter. switch network { case "tcp4": case "tcp6": default: return 0, fmt.Errorf("provided unsupported network: %s", network) } // Build URL. // Use HTTPS to ensure that we have really communicated with the desired // server and not with an intermediate. url := fmt.Sprintf("https://%s/", domain) // Prepare all parts of the request. // TODO: Evaluate if we want to change the User-Agent. req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return 0, err } dialer := &net.Dialer{ Timeout: 15 * time.Second, LocalAddr: conf.GetBindAddr(network), FallbackDelay: -1, // Disables Fast Fallback from IPv6 to IPv4. KeepAlive: -1, // Disable keep-alive. } dialWithNet := func(ctx context.Context, _, addr string) (net.Conn, error) { // Ignore network by http client. // Instead, force either tcp4 or tcp6. return dialer.DialContext(ctx, network, addr) } client := &http.Client{ Transport: &http.Transport{ DialContext: dialWithNet, DisableKeepAlives: true, DisableCompression: true, TLSHandshakeTimeout: 15 * time.Second, }, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, Timeout: 30 * time.Second, } // Make request to server. resp, err := client.Do(req) if err != nil { return 0, fmt.Errorf("failed to send http request: %w", err) } defer func() { _ = resp.Body.Close() }() if resp.StatusCode < 200 || resp.StatusCode >= 400 { return resp.StatusCode, fmt.Errorf("unexpected status code: %s", resp.Status) } return resp.StatusCode, nil } ================================================ FILE: spn/patrol/module.go ================================================ package patrol import ( "errors" "sync/atomic" "time" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" ) // ChangeSignalEventName is the name of the event that signals any change in the patrol system. const ChangeSignalEventName = "change signal" type Patrol struct { mgr *mgr.Manager instance instance EventChangeSignal *mgr.EventMgr[struct{}] } func (p *Patrol) Manager() *mgr.Manager { return p.mgr } func (p *Patrol) Start() error { if conf.PublicHub() { p.mgr.Repeat("connectivity test", 5*time.Minute, connectivityCheckTask) } return nil } func (p *Patrol) Stop() error { return nil } var ( module *Patrol shimLoaded atomic.Bool ) // New returns a new Config module. func New(instance instance) (*Patrol, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Patrol") module = &Patrol{ mgr: m, instance: instance, EventChangeSignal: mgr.NewEventMgr[struct{}](ChangeSignalEventName, m), } return module, nil } type instance interface{} ================================================ FILE: spn/ships/connection_test.go ================================================ package ships import ( "context" "fmt" "net" "sync" "testing" "github.com/stretchr/testify/assert" "github.com/safing/portmaster/spn/hub" ) var ( testPort uint16 = 65000 testData = []byte("The quick brown fox jumps over the lazy dog") localhost = net.IPv4(127, 0, 0, 1) ) func getTestPort() uint16 { testPort++ return testPort } func getTestBuf() []byte { return make([]byte, len(testData)) } func TestConnections(t *testing.T) { t.Parallel() registryLock.Lock() t.Cleanup(func() { registryLock.Unlock() }) for protocol, builder := range registry { t.Run(protocol, func(t *testing.T) { t.Parallel() var wg sync.WaitGroup ctx, cancelCtx := context.WithCancel(context.Background()) // docking requests dockingRequests := make(chan Ship, 1) transport := &hub.Transport{ Protocol: protocol, Port: getTestPort(), } // create listener pier, err := builder.EstablishPier(transport, dockingRequests) if err != nil { t.Fatal(err) } // connect to listener ship, err := builder.LaunchShip(ctx, transport, localhost) if err != nil { t.Fatal(err) } // client send err = ship.Load(testData) if err != nil { t.Fatalf("%s failed: %s", ship, err) } // dock client srvShip := <-dockingRequests if srvShip == nil { t.Fatalf("%s failed to dock", pier) } // server recv buf := getTestBuf() _, err = srvShip.UnloadTo(buf) if err != nil { t.Fatalf("%s failed: %s", ship, err) } // check data assert.Equal(t, testData, buf, "should match") fmt.Print(".") for range 100 { // server send err = srvShip.Load(testData) if err != nil { t.Fatalf("%s failed: %s", ship, err) } // client recv buf = getTestBuf() _, err = ship.UnloadTo(buf) if err != nil { t.Fatalf("%s failed: %s", ship, err) } // check data assert.Equal(t, testData, buf, "should match") fmt.Print(".") // client send err = ship.Load(testData) if err != nil { t.Fatalf("%s failed: %s", ship, err) } // server recv buf = getTestBuf() _, err = srvShip.UnloadTo(buf) if err != nil { t.Fatalf("%s failed: %s", ship, err) } // check data assert.Equal(t, testData, buf, "should match") fmt.Print(".") } ship.Sink() srvShip.Sink() pier.Abolish() cancelCtx() wg.Wait() // wait for docking procedure to end }) } } ================================================ FILE: spn/ships/http.go ================================================ package ships import ( "bufio" "context" "fmt" "io" "net" "net/http" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/hub" ) // HTTPShip is a ship that uses HTTP. type HTTPShip struct { ShipBase } // HTTPPier is a pier that uses HTTP. type HTTPPier struct { PierBase newDockings chan net.Conn } func init() { Register("http", &Builder{ LaunchShip: launchHTTPShip, EstablishPier: establishHTTPPier, }) } /* HTTP Transport Variants: 1. Hijack connection and switch to raw SPN protocol: Request: GET HTTP/1.1 Connection: Upgrade Upgrade: SPN Response: HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: SPN */ func launchHTTPShip(ctx context.Context, transport *hub.Transport, ip net.IP) (Ship, error) { // Default to root path. path := transport.Path if path == "" { path = "/" } // Build request for Variant 1. variant := 1 request, err := http.NewRequest(http.MethodGet, path, nil) if err != nil { return nil, fmt.Errorf("failed to build HTTP request: %w", err) } request.Header.Set("Connection", "Upgrade") request.Header.Set("Upgrade", "SPN") // Create connection. var dialNet string if ip4 := ip.To4(); ip4 != nil { dialNet = "tcp4" } else { dialNet = "tcp6" } dialer := &net.Dialer{ Timeout: 30 * time.Second, LocalAddr: conf.GetBindAddr(dialNet), FallbackDelay: -1, // Disables Fast Fallback from IPv6 to IPv4. KeepAlive: -1, // Disable keep-alive. } conn, err := dialer.DialContext(ctx, dialNet, net.JoinHostPort(ip.String(), portToA(transport.Port))) if err != nil { return nil, fmt.Errorf("failed to connect: %w", err) } // Send HTTP request. err = request.Write(conn) if err != nil { return nil, fmt.Errorf("failed to send HTTP request: %w", err) } // Receive HTTP response. response, err := http.ReadResponse(bufio.NewReader(conn), request) if err != nil { return nil, fmt.Errorf("failed to read HTTP response: %w", err) } defer response.Body.Close() //nolint:errcheck,gosec // Handle response according to variant. switch variant { case 1: if response.StatusCode == http.StatusSwitchingProtocols && response.Header.Get("Connection") == "Upgrade" && response.Header.Get("Upgrade") == "SPN" { // Continue } else { return nil, fmt.Errorf("received unexpected response for variant 1: %s", response.Status) } default: return nil, fmt.Errorf("internal error: unsupported http transport variant: %d", variant) } // Create ship. ship := &HTTPShip{ ShipBase: ShipBase{ conn: conn, transport: transport, mine: true, secure: false, }, } // Init and return. ship.calculateLoadSize(ip, nil, TCPHeaderMTUSize) ship.initBase() return ship, nil } func (pier *HTTPPier) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch { case r.Method == http.MethodGet && r.Header.Get("Connection") == "Upgrade" && r.Header.Get("Upgrade") == "SPN": // Request for Variant 1. // Hijack connection. var conn net.Conn if hijacker, ok := w.(http.Hijacker); ok { // Empty body, so the hijacked connection starts with a clean buffer. _, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "", http.StatusInternalServerError) log.Warningf("ships: failed to empty body for hijack for %s: %s", r.RemoteAddr, err) return } _ = r.Body.Close() // Reply with upgrade confirmation. w.Header().Set("Connection", "Upgrade") w.Header().Set("Upgrade", "SPN") w.WriteHeader(http.StatusSwitchingProtocols) // Get connection. conn, _, err = hijacker.Hijack() if err != nil { log.Warningf("ships: failed to hijack http connection from %s: %s", r.RemoteAddr, err) return } } else { http.Error(w, "", http.StatusInternalServerError) log.Warningf("ships: connection from %s cannot be hijacked", r.RemoteAddr) return } // Create new ship. ship := &HTTPShip{ ShipBase: ShipBase{ transport: pier.transport, conn: conn, mine: false, secure: false, }, } ship.calculateLoadSize(nil, conn.RemoteAddr(), TCPHeaderMTUSize) ship.initBase() // Submit new docking request. select { case pier.dockingRequests <- ship: case <-r.Context().Done(): return } default: // Reply with info page if no variant matches the request. ServeInfoPage(w, r) } } func establishHTTPPier(transport *hub.Transport, dockingRequests chan Ship) (Pier, error) { // Default to root path. path := transport.Path if path == "" { path = "/" } // Create pier. pier := &HTTPPier{ newDockings: make(chan net.Conn), PierBase: PierBase{ transport: transport, dockingRequests: dockingRequests, }, } pier.initBase() // Register handler. err := addHTTPHandler(transport.Port, path, pier.ServeHTTP) if err != nil { return nil, fmt.Errorf("failed to add HTTP handler: %w", err) } return pier, nil } // Abolish closes the underlying listener and cleans up any related resources. func (pier *HTTPPier) Abolish() { // Only abolish once. if !pier.abolishing.SetToIf(false, true) { return } // Do not close the listener, as it is shared. // Instead, remove the HTTP handler and the shared server will shutdown itself when needed. _ = removeHTTPHandler(pier.transport.Port, pier.transport.Path) } ================================================ FILE: spn/ships/http_info.go ================================================ package ships import ( "bytes" _ "embed" "html/template" "net/http" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/info" "github.com/safing/portmaster/base/log" ) var ( //go:embed http_info_page.html.tmpl infoPageData string infoPageTemplate *template.Template // DisplayHubID holds the Hub ID for displaying it on the info page. DisplayHubID string ) type infoPageInput struct { Version string Info *info.Info ID string Name string Group string ContactAddress string ContactService string } var ( pageInputName config.StringOption pageInputGroup config.StringOption pageInputContactAddress config.StringOption pageInputContactService config.StringOption ) func initPageInput() { infoPageTemplate = template.Must(template.New("info-page").Parse(infoPageData)) pageInputName = config.Concurrent.GetAsString("spn/publicHub/name", "") pageInputGroup = config.Concurrent.GetAsString("spn/publicHub/group", "") pageInputContactAddress = config.Concurrent.GetAsString("spn/publicHub/contactAddress", "") pageInputContactService = config.Concurrent.GetAsString("spn/publicHub/contactService", "") } // ServeInfoPage serves the info page for the given request. func ServeInfoPage(w http.ResponseWriter, r *http.Request) { pageData, err := renderInfoPage() if err != nil { log.Warningf("ships: failed to render SPN info page: %s", err) http.Error(w, "", http.StatusInternalServerError) return } _, err = w.Write(pageData) if err != nil { log.Warningf("ships: failed to write info page: %s", err) } } func renderInfoPage() ([]byte, error) { input := &infoPageInput{ Version: info.Version(), Info: info.GetInfo(), ID: DisplayHubID, Name: pageInputName(), Group: pageInputGroup(), ContactAddress: pageInputContactAddress(), ContactService: pageInputContactService(), } buf := &bytes.Buffer{} err := infoPageTemplate.ExecuteTemplate(buf, "info-page", input) if err != nil { return nil, err } return buf.Bytes(), nil } ================================================ FILE: spn/ships/http_info_page.html.tmpl ================================================ SPN Node

You Have Reached an SPN Node

The server, or at least the exact URL you have accessed, leads to an SPN Node.

What is SPN?

SPN stands for "Safing Privacy Network" and is a network of servers that offers high privacy protection of Internet traffic and activity. It was built to replace VPNs for their Internet privacy use case.

More Information

You can find out more about SPN here:

Contact the Operator of This SPN Node

{{ if .ContactAddress }} You can reach the operator of this SPN Node here: {{ .ContactAddress }} {{ if .ContactService }} via {{ .ContactService }} {{ end }} {{ else }} The operator of this SPN Node has not configured any contact data.
Please contact the operator using the usual methods via the hosting provider. {{ end }}

Are You Tracing Bad Activity?

We are sorry there is an incident involving this server. We condemn any disruptive or illegal activity.

Please note that servers are not only operated by Safing (the company behind SPN), but also by third parties.

The SPN works very similar to Tor. Its primary goal is to provide people more privacy on the Internet. We also provide our services to people behind censoring firewalls in oppressive regimes.

This server does not host any content (as part of its role in the SPN network). Rather, it is part of the network where nodes on the Internet simply pass packets among themselves before sending them to their destinations, just as any Internet intermediary does.

Please understand that the SPN makes it technically impossible to single out individual users. We are also legally bound to respective privacy rights.

We can offer to block specific destination IPs and ports, but the abuser doesn't use this server specifically; instead, they will just be routed through a different exit node outside of our control.

SPN Node Info

  • Name: {{ .Name }}
  • Group: {{ .Group }}
  • ContactAddress: {{ .ContactAddress }}
  • ContactService: {{ .ContactService }}
  • Version: {{ .Version }}
  • ID: {{ .ID }}
  • Build:
    • Commit: {{ .Info.Commit }}
    • At: {{ .Info.CommitTime }}
    • From: {{ .Info.Source }}

================================================ FILE: spn/ships/http_info_test.go ================================================ package ships import ( "html/template" "testing" "github.com/safing/portmaster/base/config" ) func TestInfoPageTemplate(t *testing.T) { t.Parallel() infoPageTemplate = template.Must(template.New("info-page").Parse(infoPageData)) pageInputName = config.Concurrent.GetAsString("spn/publicHub/name", "node-name") pageInputGroup = config.Concurrent.GetAsString("spn/publicHub/group", "node-group") pageInputContactAddress = config.Concurrent.GetAsString("spn/publicHub/contactAddress", "john@doe.com") pageInputContactService = config.Concurrent.GetAsString("spn/publicHub/contactService", "email") pageData, err := renderInfoPage() if err != nil { t.Fatal(err) } _ = pageData // t.Log(string(pageData)) } ================================================ FILE: spn/ships/http_shared.go ================================================ package ships import ( "context" "errors" "fmt" "net" "net/http" "sync" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" ) type sharedServer struct { server *http.Server handlers map[string]http.HandlerFunc handlersLock sync.RWMutex } // ServeHTTP forwards requests to registered handler or uses defaults. func (shared *sharedServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { shared.handlersLock.Lock() defer shared.handlersLock.Unlock() // Get and forward to registered handler. handler, ok := shared.handlers[r.URL.Path] if ok { handler(w, r) return } // If there is registered handler and path is "/", respond with info page. if r.Method == http.MethodGet && r.URL.Path == "/" { ServeInfoPage(w, r) return } // Otherwise, respond with error. http.Error(w, "", http.StatusNotFound) } var ( sharedHTTPServers = make(map[uint16]*sharedServer) sharedHTTPServersLock sync.Mutex ) func addHTTPHandler(port uint16, path string, handler http.HandlerFunc) error { // Check params. if port == 0 { return errors.New("cannot listen on port 0") } // Default to root path. if path == "" { path = "/" } sharedHTTPServersLock.Lock() defer sharedHTTPServersLock.Unlock() // Get http server of the port. shared, ok := sharedHTTPServers[port] if ok { // Set path to handler. shared.handlersLock.Lock() defer shared.handlersLock.Unlock() // Check if path is already registered. _, ok := shared.handlers[path] if ok { return errors.New("path already registered") } // Else, register handler at path. shared.handlers[path] = handler return nil } // Shared server does not exist - create one. shared = &sharedServer{ handlers: make(map[string]http.HandlerFunc), } // Add first handler. shared.handlers[path] = handler // Define new server. server := &http.Server{ Addr: fmt.Sprintf(":%d", port), Handler: shared, ReadTimeout: 1 * time.Minute, ReadHeaderTimeout: 10 * time.Second, WriteTimeout: 1 * time.Minute, IdleTimeout: 1 * time.Minute, MaxHeaderBytes: 4096, BaseContext: func(net.Listener) context.Context { return module.mgr.Ctx() }, } shared.server = server // Start listeners. bindIPs := conf.GetBindIPs() listeners := make([]net.Listener, 0, len(bindIPs)) for _, bindIP := range bindIPs { listener, err := net.ListenTCP("tcp", &net.TCPAddr{ IP: bindIP, Port: int(port), }) if err != nil { return fmt.Errorf("failed to listen: %w", err) } listeners = append(listeners, listener) log.Infof("spn/ships: http transport pier established on %s", listener.Addr()) } // Add shared http server to list. sharedHTTPServers[port] = shared // Start servers in service workers. for _, serviceListener := range listeners { module.mgr.Go( fmt.Sprintf("shared http server listener on %s", serviceListener.Addr()), func(_ *mgr.WorkerCtx) error { err := shared.server.Serve(serviceListener) if !errors.Is(http.ErrServerClosed, err) { return err } return nil }, ) } return nil } func removeHTTPHandler(port uint16, path string) error { // Check params. if port == 0 { return nil } // Default to root path. if path == "" { path = "/" } sharedHTTPServersLock.Lock() defer sharedHTTPServersLock.Unlock() // Get http server of the port. shared, ok := sharedHTTPServers[port] if !ok { return nil } // Set path to handler. shared.handlersLock.Lock() defer shared.handlersLock.Unlock() // Check if path is registered. _, ok = shared.handlers[path] if !ok { return nil } // Remove path from handler. delete(shared.handlers, path) // Shutdown shared HTTP server if no more handlers are registered. if len(shared.handlers) == 0 { ctx, cancel := context.WithTimeout( context.Background(), 10*time.Second, ) defer cancel() return shared.server.Shutdown(ctx) } // Remove shared HTTP server from map. delete(sharedHTTPServers, port) return nil } ================================================ FILE: spn/ships/http_shared_test.go ================================================ package ships import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestSharedHTTP(t *testing.T) { //nolint:paralleltest // Test checks global state. _, err := New(struct{}{}) if err != nil { t.Errorf("failed to create module ships: %s", err) } const testPort = 65100 // Register multiple handlers. err = addHTTPHandler(testPort, "", ServeInfoPage) require.NoError(t, err, "should be able to share http listener") err = addHTTPHandler(testPort, "/test", ServeInfoPage) require.NoError(t, err, "should be able to share http listener") err = addHTTPHandler(testPort, "/test2", ServeInfoPage) require.NoError(t, err, "should be able to share http listener") err = addHTTPHandler(testPort, "/", ServeInfoPage) require.Error(t, err, "should fail to register path twice") // Unregister require.NoError(t, removeHTTPHandler(testPort, "")) require.NoError(t, removeHTTPHandler(testPort, "/test")) require.NoError(t, removeHTTPHandler(testPort, "/not-registered")) // removing unregistered handler does not error require.NoError(t, removeHTTPHandler(testPort, "/test2")) require.NoError(t, removeHTTPHandler(testPort, "/not-registered")) // removing unregistered handler does not error // Check if all handlers are gone again. sharedHTTPServersLock.Lock() defer sharedHTTPServersLock.Unlock() assert.Empty(t, sharedHTTPServers, "shared http handlers should be back to zero") } ================================================ FILE: spn/ships/kcp.go ================================================ package ships // KCPShip is a ship that uses KCP. type KCPShip struct { ShipBase } // KCPPier is a pier that uses KCP. type KCPPier struct { PierBase } // TODO: Find a replacement for kcp, which turned out to not fit our use case. /* func init() { Register("kcp", &Builder{ LaunchShip: launchKCPShip, EstablishPier: establishKCPPier, }) } func launchKCPShip(ctx context.Context, transport *hub.Transport, ip net.IP) (Ship, error) { conn, err := kcp.Dial(net.JoinHostPort(ip.String(), portToA(transport.Port))) if err != nil { return nil, err } ship := &KCPShip{ ShipBase: ShipBase{ conn: conn, transport: transport, mine: true, secure: false, // Calculate KCP's MSS. loadSize: kcp.IKCP_MTU_DEF - kcp.IKCP_OVERHEAD, }, } ship.initBase() return ship, nil } func establishKCPPier(transport *hub.Transport, dockingRequests chan *DockingRequest) (Pier, error) { listener, err := kcp.Listen(net.JoinHostPort("", portToA(transport.Port))) if err != nil { return nil, err } pier := &KCPPier{ PierBase: PierBase{ transport: transport, listener: listener, dockingRequests: dockingRequests, }, } pier.PierBase.dockShip = pier.dockShip pier.initBase() return pier, nil } func (pier *KCPPier) dockShip() (Ship, error) { conn, err := pier.listener.Accept() if err != nil { return nil, err } ship := &KCPShip{ ShipBase: ShipBase{ conn: conn, transport: pier.transport, mine: false, secure: false, // Calculate KCP's MSS. loadSize: kcp.IKCP_MTU_DEF - kcp.IKCP_OVERHEAD, }, } ship.initBase() return ship, nil } */ ================================================ FILE: spn/ships/launch.go ================================================ package ships import ( "context" "fmt" "net" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/spn/hub" ) // Launch launches a new ship to the given Hub. func Launch(ctx context.Context, h *hub.Hub, transport *hub.Transport, ip net.IP) (Ship, error) { var transports []*hub.Transport var ips []net.IP // choose transports if transport != nil { transports = []*hub.Transport{transport} } else { if h.Info == nil { return nil, hub.ErrMissingInfo } transports = h.Info.ParsedTransports() // If there are no transports, check if they were parsed. if len(transports) == 0 && len(h.Info.Transports) > 0 { log.Errorf("ships: %s has no parsed transports, but transports are %v", h, h.Info.Transports) // Attempt to parse transports now. transports, _ = hub.ParseTransports(h.Info.Transports) } // Fail if there are not transports. if len(transports) == 0 { return nil, hub.ErrMissingTransports } } // choose IPs if ip != nil { ips = []net.IP{ip} } else { if h.Info == nil { return nil, hub.ErrMissingInfo } ips = make([]net.IP, 0, 3) // If IPs have been verified, check if we can use a virtual network address. var vnetForced bool if h.VerifiedIPs { vnet := GetVirtualNetworkConfig() if vnet != nil { virtIP := vnet.Mapping[h.ID] if virtIP != nil { ips = append(ips, virtIP) if vnet.Force { vnetForced = true log.Infof("spn/ships: forcing virtual network address %s for %s", virtIP, h) } else { log.Infof("spn/ships: using virtual network address %s for %s", virtIP, h) } } } } // Add Hub's IPs if no virtual address was forced. if !vnetForced { // prioritize IPv4 if h.Info.IPv4 != nil { ips = append(ips, h.Info.IPv4) } if h.Info.IPv6 != nil && netenv.IPv6Enabled() { ips = append(ips, h.Info.IPv6) } } if len(ips) == 0 { return nil, hub.ErrMissingIPs } } // connect var firstErr error for _, ip := range ips { for _, tr := range transports { ship, err := connectTo(ctx, h, tr, ip) if err == nil { return ship, nil // return on success } // Check if context is canceled. if ctx.Err() != nil { return nil, ctx.Err() } // Save first error. if firstErr == nil { firstErr = err } } } return nil, firstErr } func connectTo(ctx context.Context, h *hub.Hub, transport *hub.Transport, ip net.IP) (Ship, error) { builder := GetBuilder(transport.Protocol) if builder == nil { return nil, fmt.Errorf("protocol %s not supported", transport.Protocol) } ship, err := builder.LaunchShip(ctx, transport, ip) if err != nil { return nil, fmt.Errorf("failed to connect to %s using %s (%s): %w", h, transport, ip, err) } return ship, nil } ================================================ FILE: spn/ships/masking.go ================================================ package ships import ( "crypto/sha1" "net" "github.com/mr-tron/base58" "github.com/tevino/abool" ) var ( maskingEnabled = abool.New() maskingActive = abool.New() maskingBytes []byte ) // EnableMasking enables masking with the given salt. func EnableMasking(salt []byte) { if maskingEnabled.SetToIf(false, true) { maskingBytes = salt maskingActive.Set() } } // MaskAddress masks the given address if masking is enabled and the ship is // not public. func (ship *ShipBase) MaskAddress(addr net.Addr) string { // Return in plain if masking is not enabled or if ship is public. if maskingActive.IsNotSet() || ship.Public() { return addr.String() } switch typedAddr := addr.(type) { case *net.TCPAddr: return ship.MaskIP(typedAddr.IP) case *net.UDPAddr: return ship.MaskIP(typedAddr.IP) default: return ship.Mask([]byte(addr.String())) } } // MaskIP masks the given IP if masking is enabled and the ship is not public. func (ship *ShipBase) MaskIP(ip net.IP) string { // Return in plain if masking is not enabled or if ship is public. if maskingActive.IsNotSet() || ship.Public() { return ip.String() } return ship.Mask(ip) } // Mask masks the given value. func (ship *ShipBase) Mask(value []byte) string { // Hash the IP with masking bytes. hasher := sha1.New() //nolint:gosec // Not used for cryptography. hasher.Write(maskingBytes) hasher.Write(value) masked := hasher.Sum(nil) // Return first 8 characters from the base58-encoded hash. return "masked:" + base58.Encode(masked)[:8] } ================================================ FILE: spn/ships/module.go ================================================ package ships import ( "errors" "sync/atomic" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" ) type Ships struct { mgr *mgr.Manager instance instance } func (s *Ships) Manager() *mgr.Manager { return s.mgr } func (s *Ships) Start() error { if conf.PublicHub() { initPageInput() } return nil } func (s *Ships) Stop() error { return nil } var ( module *Ships shimLoaded atomic.Bool ) // New returns a new Config module. func New(instance instance) (*Ships, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("Ships") module = &Ships{ mgr: m, instance: instance, } return module, nil } type instance interface{} ================================================ FILE: spn/ships/mtu.go ================================================ package ships import "net" // MTU Calculation Configuration. const ( BaseMTU = 1460 // 1500 with 40 bytes extra space for special cases. IPv4HeaderMTUSize = 20 // Without options, as not common. IPv6HeaderMTUSize = 40 // Without options, as not common. TCPHeaderMTUSize = 60 // Maximum size with options. UDPHeaderMTUSize = 8 // Has no options. ) func (ship *ShipBase) calculateLoadSize(ip net.IP, addr net.Addr, subtract ...int) { ship.loadSize = BaseMTU // Convert addr to IP if needed. if ip == nil && addr != nil { switch v := addr.(type) { case *net.TCPAddr: ip = v.IP case *net.UDPAddr: ip = v.IP case *net.IPAddr: ip = v.IP } } // Subtract IP Header, if IP is available. if ip != nil { if ip4 := ip.To4(); ip4 != nil { ship.loadSize -= IPv4HeaderMTUSize } else { ship.loadSize -= IPv6HeaderMTUSize } } // Subtract others. for sub := range subtract { ship.loadSize -= sub } // Raise buf size to at least load size. if ship.bufSize < ship.loadSize { ship.bufSize = ship.loadSize } } ================================================ FILE: spn/ships/pier.go ================================================ package ships import ( "fmt" "net" "github.com/tevino/abool" "github.com/safing/portmaster/spn/hub" ) // Pier represents a network connection listener. type Pier interface { // String returns a human readable informational summary about the ship. String() string // Transport returns the transport used for this ship. Transport() *hub.Transport // Abolish closes the underlying listener and cleans up any related resources. Abolish() } // DockingRequest is a uniform request that Piers emit when a new ship arrives. type DockingRequest struct { Pier Pier Ship Ship Err error } // EstablishPier is shorthand function to get the transport's builder and establish a pier. func EstablishPier(transport *hub.Transport, dockingRequests chan Ship) (Pier, error) { builder := GetBuilder(transport.Protocol) if builder == nil { return nil, fmt.Errorf("protocol %s not supported", transport.Protocol) } pier, err := builder.EstablishPier(transport, dockingRequests) if err != nil { return nil, fmt.Errorf("failed to establish pier on %s: %w", transport, err) } return pier, nil } // PierBase implements common functions to comply with the Pier interface. type PierBase struct { // transport holds the transport definition of the pier. transport *hub.Transport // listeners holds the actual underlying listeners. listeners []net.Listener // dockingRequests is used to report new connections to the higher layer. dockingRequests chan Ship // abolishing specifies if the pier and listener is being closed. abolishing *abool.AtomicBool } func (pier *PierBase) initBase() { // init pier.abolishing = abool.New() } // String returns a human readable informational summary about the ship. func (pier *PierBase) String() string { return fmt.Sprintf("", pier.transport) } // Transport returns the transport used for this ship. func (pier *PierBase) Transport() *hub.Transport { return pier.transport } // Abolish closes the underlying listener and cleans up any related resources. func (pier *PierBase) Abolish() { if pier.abolishing.SetToIf(false, true) { for _, listener := range pier.listeners { _ = listener.Close() } } } ================================================ FILE: spn/ships/registry.go ================================================ package ships import ( "context" "net" "strconv" "sync" "github.com/safing/portmaster/spn/hub" ) // Builder is a factory that can build ships and piers of it's protocol. type Builder struct { LaunchShip func(ctx context.Context, transport *hub.Transport, ip net.IP) (Ship, error) EstablishPier func(transport *hub.Transport, dockingRequests chan Ship) (Pier, error) } var ( registry = make(map[string]*Builder) allProtocols []string registryLock sync.Mutex ) // Register registers a new builder for a protocol. func Register(protocol string, builder *Builder) { registryLock.Lock() defer registryLock.Unlock() registry[protocol] = builder } // GetBuilder returns the builder for the given protocol, or nil if it does not exist. func GetBuilder(protocol string) *Builder { registryLock.Lock() defer registryLock.Unlock() builder, ok := registry[protocol] if !ok { return nil } return builder } // Protocols returns a slice with all registered protocol names. The return slice must not be edited. func Protocols() []string { registryLock.Lock() defer registryLock.Unlock() return allProtocols } // portToA transforms the given port into a string. func portToA(port uint16) string { return strconv.FormatUint(uint64(port), 10) } ================================================ FILE: spn/ships/ship.go ================================================ package ships import ( "errors" "fmt" "net" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/hub" ) const ( defaultLoadSize = 4096 ) // ErrSunk is returned when a ship sunk, ie. the connection was lost. var ErrSunk = errors.New("ship sunk") // Ship represents a network layer connection. type Ship interface { // String returns a human readable informational summary about the ship. String() string // Transport returns the transport used for this ship. Transport() *hub.Transport // IsMine returns whether the ship was launched from here. IsMine() bool // IsSecure returns whether the ship provides transport security. IsSecure() bool // Public returns whether the ship is marked as public. Public() bool // MarkPublic marks the ship as public. MarkPublic() // LoadSize returns the recommended data size that should be handed to Load(). // This value will be most likely somehow related to the connection's MTU. // Alternatively, using a multiple of LoadSize is also recommended. LoadSize() int // Load loads data into the ship - ie. sends the data via the connection. // Returns ErrSunk if the ship has already sunk earlier. Load(data []byte) error // UnloadTo unloads data from the ship - ie. receives data from the // connection - puts it into the buf. It returns the amount of data // written and an optional error. // Returns ErrSunk if the ship has already sunk earlier. UnloadTo(buf []byte) (n int, err error) // LocalAddr returns the underlying local net.Addr of the connection. LocalAddr() net.Addr // RemoteAddr returns the underlying remote net.Addr of the connection. RemoteAddr() net.Addr // Sink closes the underlying connection and cleans up any related resources. Sink() // MaskAddress masks the address, if enabled. MaskAddress(addr net.Addr) string // MaskIP masks an IP, if enabled. MaskIP(ip net.IP) string // Mask masks a value. Mask(value []byte) string } // ShipBase implements common functions to comply with the Ship interface. type ShipBase struct { // conn is the actual underlying connection. conn net.Conn // transport holds the transport definition of the ship. transport *hub.Transport // mine specifies whether the ship was launched from here. mine bool // secure specifies whether the ship provides transport security. secure bool // public specifies whether the ship is public. public *abool.AtomicBool // bufSize specifies the size of the receive buffer. bufSize int // loadSize specifies the recommended data size that should be handed to Load(). loadSize int // initial holds initial data from setting up the ship. initial []byte // sinking specifies if the connection is being closed. sinking *abool.AtomicBool } func (ship *ShipBase) initBase() { // init ship.sinking = abool.New() ship.public = abool.New() // set default if ship.loadSize == 0 { ship.loadSize = defaultLoadSize } if ship.bufSize == 0 { ship.bufSize = ship.loadSize } } // String returns a human readable informational summary about the ship. func (ship *ShipBase) String() string { if ship.mine { return fmt.Sprintf("", ship.MaskAddress(ship.RemoteAddr()), ship.transport) } return fmt.Sprintf("", ship.MaskAddress(ship.RemoteAddr()), ship.transport) } // Transport returns the transport used for this ship. func (ship *ShipBase) Transport() *hub.Transport { return ship.transport } // IsMine returns whether the ship was launched from here. func (ship *ShipBase) IsMine() bool { return ship.mine } // IsSecure returns whether the ship provides transport security. func (ship *ShipBase) IsSecure() bool { return ship.secure } // Public returns whether the ship is marked as public. func (ship *ShipBase) Public() bool { return ship.public.IsSet() } // MarkPublic marks the ship as public. func (ship *ShipBase) MarkPublic() { ship.public.Set() } // LoadSize returns the recommended data size that should be handed to Load(). // This value will be most likely somehow related to the connection's MTU. // Alternatively, using a multiple of LoadSize is also recommended. func (ship *ShipBase) LoadSize() int { return ship.loadSize } // Load loads data into the ship - ie. sends the data via the connection. // Returns ErrSunk if the ship has already sunk earlier. func (ship *ShipBase) Load(data []byte) error { // Empty load is used as a signal to cease operaetion. if len(data) == 0 { if ship.sinking.SetToIf(false, true) { _ = ship.conn.Close() } return nil } // Send all given data. n, err := ship.conn.Write(data) switch { case err != nil: return err case n == 0: return errors.New("loaded 0 bytes") case n < len(data): // If not all data was sent, try again. log.Debugf("spn/ships: %s only loaded %d/%d bytes", ship, n, len(data)) data = data[n:] return ship.Load(data) } return nil } // UnloadTo unloads data from the ship - ie. receives data from the // connection - puts it into the buf. It returns the amount of data // written and an optional error. // Returns ErrSunk if the ship has already sunk earlier. func (ship *ShipBase) UnloadTo(buf []byte) (n int, err error) { // Process initial data, if there is any. if ship.initial != nil { // Copy as much data as possible. copy(buf, ship.initial) // If buf was too small, skip the copied section. if len(buf) < len(ship.initial) { ship.initial = ship.initial[len(buf):] return len(buf), nil } // If everything was copied, unset the initial data. n := len(ship.initial) ship.initial = nil return n, nil } // Receive data. return ship.conn.Read(buf) } // LocalAddr returns the underlying local net.Addr of the connection. func (ship *ShipBase) LocalAddr() net.Addr { return ship.conn.LocalAddr() } // RemoteAddr returns the underlying remote net.Addr of the connection. func (ship *ShipBase) RemoteAddr() net.Addr { return ship.conn.RemoteAddr() } // Sink closes the underlying connection and cleans up any related resources. func (ship *ShipBase) Sink() { if ship.sinking.SetToIf(false, true) { _ = ship.conn.Close() } } ================================================ FILE: spn/ships/tcp.go ================================================ package ships import ( "context" "fmt" "net" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/hub" ) // TCPShip is a ship that uses TCP. type TCPShip struct { ShipBase } // TCPPier is a pier that uses TCP. type TCPPier struct { PierBase ctx context.Context cancelCtx context.CancelFunc } func init() { Register("tcp", &Builder{ LaunchShip: launchTCPShip, EstablishPier: establishTCPPier, }) } func launchTCPShip(ctx context.Context, transport *hub.Transport, ip net.IP) (Ship, error) { var dialNet string if ip4 := ip.To4(); ip4 != nil { dialNet = "tcp4" } else { dialNet = "tcp6" } dialer := &net.Dialer{ Timeout: 30 * time.Second, LocalAddr: conf.GetBindAddr(dialNet), FallbackDelay: -1, // Disables Fast Fallback from IPv6 to IPv4. KeepAlive: -1, // Disable keep-alive. } conn, err := dialer.DialContext(ctx, dialNet, net.JoinHostPort(ip.String(), portToA(transport.Port))) if err != nil { return nil, fmt.Errorf("failed to connect: %w", err) } ship := &TCPShip{ ShipBase: ShipBase{ conn: conn, transport: transport, mine: true, secure: false, }, } ship.calculateLoadSize(ip, nil, TCPHeaderMTUSize) ship.initBase() return ship, nil } func establishTCPPier(transport *hub.Transport, dockingRequests chan Ship) (Pier, error) { // Start listeners. bindIPs := conf.GetBindIPs() listeners := make([]net.Listener, 0, len(bindIPs)) for _, bindIP := range bindIPs { listener, err := net.ListenTCP("tcp", &net.TCPAddr{ IP: bindIP, Port: int(transport.Port), }) if err != nil { return nil, fmt.Errorf("failed to listen: %w", err) } listeners = append(listeners, listener) log.Infof("spn/ships: tcp transport pier established on %s", listener.Addr()) } // Create new pier. pierCtx, cancelCtx := context.WithCancel(module.mgr.Ctx()) pier := &TCPPier{ PierBase: PierBase{ transport: transport, listeners: listeners, dockingRequests: dockingRequests, }, ctx: pierCtx, cancelCtx: cancelCtx, } pier.initBase() // Start workers. for _, listener := range pier.listeners { module.mgr.Go("accept TCP docking requests", func(wc *mgr.WorkerCtx) error { return pier.dockingWorker(wc.Ctx(), listener) }) } return pier, nil } func (pier *TCPPier) dockingWorker(_ context.Context, listener net.Listener) error { for { // Block until something happens. conn, err := listener.Accept() // Check for errors. switch { case pier.ctx.Err() != nil: return pier.ctx.Err() case err != nil: return err } // Create new ship. ship := &TCPShip{ ShipBase: ShipBase{ transport: pier.transport, conn: conn, mine: false, secure: false, }, } ship.calculateLoadSize(nil, conn.RemoteAddr(), TCPHeaderMTUSize) ship.initBase() // Submit new docking request. select { case pier.dockingRequests <- ship: case <-pier.ctx.Done(): return pier.ctx.Err() } } } // Abolish closes the underlying listener and cleans up any related resources. func (pier *TCPPier) Abolish() { pier.cancelCtx() pier.PierBase.Abolish() } ================================================ FILE: spn/ships/testship.go ================================================ package ships import ( "net" "github.com/mr-tron/base58" "github.com/tevino/abool" "github.com/safing/portmaster/spn/hub" ) // TestShip is a simulated ship that is used for testing higher level components. type TestShip struct { mine bool secure bool loadSize int forward chan []byte backward chan []byte unloadTmp []byte sinking *abool.AtomicBool } // NewTestShip returns a new TestShip for simulation. func NewTestShip(secure bool, loadSize int) *TestShip { return &TestShip{ mine: true, secure: secure, loadSize: loadSize, forward: make(chan []byte, 100), backward: make(chan []byte, 100), sinking: abool.NewBool(false), } } // String returns a human readable informational summary about the ship. func (ship *TestShip) String() string { if ship.mine { return "" } return "" } // Transport returns the transport used for this ship. func (ship *TestShip) Transport() *hub.Transport { return &hub.Transport{ Protocol: "dummy", } } // IsMine returns whether the ship was launched from here. func (ship *TestShip) IsMine() bool { return ship.mine } // IsSecure returns whether the ship provides transport security. func (ship *TestShip) IsSecure() bool { return ship.secure } // LoadSize returns the recommended data size that should be handed to Load(). // This value will be most likely somehow related to the connection's MTU. // Alternatively, using a multiple of LoadSize is also recommended. func (ship *TestShip) LoadSize() int { return ship.loadSize } // Reverse creates a connected TestShip. This is used to simulate a connection instead of using a Pier. func (ship *TestShip) Reverse() *TestShip { return &TestShip{ mine: !ship.mine, secure: ship.secure, loadSize: ship.loadSize, forward: ship.backward, backward: ship.forward, sinking: abool.NewBool(false), } } // Load loads data into the ship - ie. sends the data via the connection. // Returns ErrSunk if the ship has already sunk earlier. func (ship *TestShip) Load(data []byte) error { // Debugging: // log.Debugf("spn/ship: loading %s", spew.Sdump(data)) // Check if ship is alive. if ship.sinking.IsSet() { return ErrSunk } // Empty load is used as a signal to cease operaetion. if len(data) == 0 { ship.Sink() return nil } // Send all given data. ship.forward <- data return nil } // UnloadTo unloads data from the ship - ie. receives data from the // connection - puts it into the buf. It returns the amount of data // written and an optional error. // Returns ErrSunk if the ship has already sunk earlier. func (ship *TestShip) UnloadTo(buf []byte) (n int, err error) { // Process unload tmp data, if there is any. if ship.unloadTmp != nil { // Copy as much data as possible. copy(buf, ship.unloadTmp) // If buf was too small, skip the copied section. if len(buf) < len(ship.unloadTmp) { ship.unloadTmp = ship.unloadTmp[len(buf):] return len(buf), nil } // If everything was copied, unset the unloadTmp data. n := len(ship.unloadTmp) ship.unloadTmp = nil return n, nil } // Receive data. data := <-ship.backward if len(data) == 0 { return 0, ErrSunk } // Copy data, possibly save remainder for later. copy(buf, data) if len(buf) < len(data) { ship.unloadTmp = data[len(buf):] return len(buf), nil } return len(data), nil } // Sink closes the underlying connection and cleans up any related resources. func (ship *TestShip) Sink() { if ship.sinking.SetToIf(false, true) { close(ship.forward) } } // Dummy methods to conform to interface for testing. func (ship *TestShip) LocalAddr() net.Addr { return nil } //nolint:golint func (ship *TestShip) RemoteAddr() net.Addr { return nil } //nolint:golint func (ship *TestShip) Public() bool { return true } //nolint:golint func (ship *TestShip) MarkPublic() {} //nolint:golint func (ship *TestShip) MaskAddress(addr net.Addr) string { return addr.String() } //nolint:golint func (ship *TestShip) MaskIP(ip net.IP) string { return ip.String() } //nolint:golint func (ship *TestShip) Mask(value []byte) string { return base58.Encode(value) } //nolint:golint ================================================ FILE: spn/ships/testship_test.go ================================================ package ships import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestTestShip(t *testing.T) { t.Parallel() tShip := NewTestShip(true, 100) // interface conformance test var ship Ship = tShip srvShip := tShip.Reverse() for range 100 { // client send err := ship.Load(testData) if err != nil { t.Fatalf("%s failed: %s", ship, err) } // server recv buf := getTestBuf() _, err = srvShip.UnloadTo(buf) if err != nil { t.Fatalf("%s failed: %s", ship, err) } // check data assert.Equal(t, testData, buf, "should match") fmt.Print(".") // server send err = srvShip.Load(testData) if err != nil { t.Fatalf("%s failed: %s", ship, err) } // client recv buf = getTestBuf() _, err = ship.UnloadTo(buf) if err != nil { t.Fatalf("%s failed: %s", ship, err) } // check data assert.Equal(t, testData, buf, "should match") fmt.Print(".") } ship.Sink() srvShip.Sink() } ================================================ FILE: spn/ships/virtual_network.go ================================================ package ships import ( "net" "sync" "github.com/safing/portmaster/spn/hub" ) var ( virtNetLock sync.Mutex virtNetConfig *hub.VirtualNetworkConfig ) // SetVirtualNetworkConfig sets the virtual networking config. func SetVirtualNetworkConfig(config *hub.VirtualNetworkConfig) { virtNetLock.Lock() defer virtNetLock.Unlock() virtNetConfig = config } // GetVirtualNetworkConfig returns the virtual networking config. func GetVirtualNetworkConfig() *hub.VirtualNetworkConfig { virtNetLock.Lock() defer virtNetLock.Unlock() return virtNetConfig } // GetVirtualNetworkAddress returns the virtual network IP for the given Hub. func GetVirtualNetworkAddress(dstHubID string) net.IP { virtNetLock.Lock() defer virtNetLock.Unlock() // Check if we have a virtual network config. if virtNetConfig == nil { return nil } // Return mapping for given Hub ID. return virtNetConfig.Mapping[dstHubID] } ================================================ FILE: spn/sluice/module.go ================================================ package sluice import ( "errors" "sync/atomic" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" "github.com/safing/portmaster/spn/conf" ) type SluiceModule struct { mgr *mgr.Manager instance instance } func (s *SluiceModule) Manager() *mgr.Manager { return s.mgr } func (s *SluiceModule) Start() error { return start() } func (s *SluiceModule) Stop() error { return stop() } var ( entrypointInfoMsg = []byte("You have reached the local SPN entry port, but your connection could not be matched to an SPN tunnel.\n") // EnableListener indicates if it should start the sluice listeners. Must be set at startup. EnableListener bool = true ) func start() error { // TODO: // Listening on all interfaces for now, as we need this for Windows. // Handle similarly to the nameserver listener. if conf.Integrated() && EnableListener { StartSluice("tcp4", "0.0.0.0:717") StartSluice("udp4", "0.0.0.0:717") if netenv.IPv6Enabled() { StartSluice("tcp6", "[::]:717") StartSluice("udp6", "[::]:717") } else { log.Warningf("spn/sluice: no IPv6 stack detected, disabling IPv6 SPN entry endpoints") } } return nil } func stop() error { stopAllSluices() return nil } var ( module *SluiceModule shimLoaded atomic.Bool ) // New returns a new Config module. func New(instance instance) (*SluiceModule, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("SluiceModule") module = &SluiceModule{ mgr: m, instance: instance, } return module, nil } type instance interface{} ================================================ FILE: spn/sluice/packet_listener.go ================================================ package sluice import ( "io" "net" "sync" "sync/atomic" "time" "github.com/tevino/abool" "github.com/safing/portmaster/service/mgr" ) // PacketListener is a listener for packet based protocols. type PacketListener struct { sock net.PacketConn closed *abool.AtomicBool newConns chan *PacketConn lock sync.Mutex conns map[string]*PacketConn err error } // ListenPacket creates a packet listener. func ListenPacket(network, address string) (net.Listener, error) { // Create a new listening packet socket. sock, err := net.ListenPacket(network, address) if err != nil { return nil, err } // Create listener and start workers. ln := &PacketListener{ sock: sock, closed: abool.New(), newConns: make(chan *PacketConn), conns: make(map[string]*PacketConn), } module.mgr.Go("packet listener reader", ln.reader) module.mgr.Go("packet listener cleaner", ln.cleaner) return ln, nil } // Accept waits for and returns the next connection to the listener. func (ln *PacketListener) Accept() (net.Conn, error) { conn := <-ln.newConns if conn != nil { return conn, nil } // Check if there is a socket error. ln.lock.Lock() defer ln.lock.Unlock() if ln.err != nil { return nil, ln.err } return nil, io.EOF } // Close closes the listener. // Any blocked Accept operations will be unblocked and return errors. func (ln *PacketListener) Close() error { if !ln.closed.SetToIf(false, true) { return nil } // Close all channels. close(ln.newConns) ln.lock.Lock() defer ln.lock.Unlock() for _, conn := range ln.conns { close(conn.in) } // Close socket. return ln.sock.Close() } // Addr returns the listener's network address. func (ln *PacketListener) Addr() net.Addr { return ln.sock.LocalAddr() } func (ln *PacketListener) getConn(remoteAddr string) (conn *PacketConn, ok bool) { ln.lock.Lock() defer ln.lock.Unlock() conn, ok = ln.conns[remoteAddr] return } func (ln *PacketListener) setConn(conn *PacketConn) { ln.lock.Lock() defer ln.lock.Unlock() ln.conns[conn.addr.String()] = conn } func (ln *PacketListener) reader(_ *mgr.WorkerCtx) error { for { // Read data from connection. buf := make([]byte, 512) n, addr, err := ln.sock.ReadFrom(buf) if err != nil { // Set socket error. ln.lock.Lock() ln.err = err ln.lock.Unlock() // Close and return _ = ln.Close() return nil //nolint:nilerr } buf = buf[:n] // Get connection and supply data. conn, ok := ln.getConn(addr.String()) if ok { // Ignore if conn is closed. if conn.closed.IsSet() { continue } select { case conn.in <- buf: default: } continue } // Or create a new connection. conn = &PacketConn{ ln: ln, addr: addr, closed: abool.New(), closing: make(chan struct{}), buf: buf, in: make(chan []byte, 1), inactivityCnt: new(uint32), } ln.setConn(conn) ln.newConns <- conn } } func (ln *PacketListener) cleaner(ctx *mgr.WorkerCtx) error { for { select { case <-time.After(1 * time.Minute): // Check if listener has died. if ln.closed.IsSet() { return nil } // Clean connections. ln.cleanInactiveConns(10) case <-ctx.Done(): // Exit with module stop. return nil } } } func (ln *PacketListener) cleanInactiveConns(overInactivityCnt uint32) { ln.lock.Lock() defer ln.lock.Unlock() for k, conn := range ln.conns { cnt := atomic.AddUint32(conn.inactivityCnt, 1) switch { case cnt > overInactivityCnt*2: delete(ln.conns, k) case cnt > overInactivityCnt: _ = conn.Close() } } } // PacketConn simulates a connection for a stateless protocol. type PacketConn struct { ln *PacketListener addr net.Addr closed *abool.AtomicBool closing chan struct{} buf []byte in chan []byte inactivityCnt *uint32 } // Read reads data from the connection. // Read can be made to time out and return an error after a fixed // time limit; see SetDeadline and SetReadDeadline. func (conn *PacketConn) Read(b []byte) (n int, err error) { // Check if connection is closed. if conn.closed.IsSet() { return 0, io.EOF } // Mark as active. atomic.StoreUint32(conn.inactivityCnt, 0) // Get new buffer. if conn.buf == nil { select { case conn.buf = <-conn.in: if conn.buf == nil { return 0, io.EOF } case <-conn.closing: return 0, io.EOF } } // Serve from buffer. copy(b, conn.buf) if len(b) >= len(conn.buf) { copied := len(conn.buf) conn.buf = nil return copied, nil } copied := len(b) conn.buf = conn.buf[copied:] return copied, nil } // Write writes data to the connection. // Write can be made to time out and return an error after a fixed // time limit; see SetDeadline and SetWriteDeadline. func (conn *PacketConn) Write(b []byte) (n int, err error) { // Check if connection is closed. if conn.closed.IsSet() { return 0, io.EOF } // Mark as active. atomic.StoreUint32(conn.inactivityCnt, 0) return conn.ln.sock.WriteTo(b, conn.addr) } // Close is a no-op as UDP connections share a single socket. Just stop sending // packets without closing. func (conn *PacketConn) Close() error { if conn.closed.SetToIf(false, true) { close(conn.closing) } return nil } // LocalAddr returns the local network address. func (conn *PacketConn) LocalAddr() net.Addr { return conn.ln.sock.LocalAddr() } // RemoteAddr returns the remote network address. func (conn *PacketConn) RemoteAddr() net.Addr { return conn.addr } // SetDeadline is a no-op as UDP connections share a single socket. func (conn *PacketConn) SetDeadline(t time.Time) error { return nil } // SetReadDeadline is a no-op as UDP connections share a single socket. func (conn *PacketConn) SetReadDeadline(t time.Time) error { return nil } // SetWriteDeadline is a no-op as UDP connections share a single socket. func (conn *PacketConn) SetWriteDeadline(t time.Time) error { return nil } ================================================ FILE: spn/sluice/request.go ================================================ package sluice import ( "errors" "fmt" "net" "time" "github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network/packet" ) const ( defaultSluiceTTL = 30 * time.Second ) var ( // ErrUnsupported is returned when a protocol is not supported. ErrUnsupported = errors.New("unsupported protocol") // ErrSluiceOffline is returned when the sluice for a network is offline. ErrSluiceOffline = errors.New("is offline") ) // Request holds request data for a sluice entry. type Request struct { ConnInfo *network.Connection CallbackFn RequestCallbackFunc Expires time.Time } // RequestCallbackFunc is called for taking a over handling connection that arrived at the sluice. type RequestCallbackFunc func(connInfo *network.Connection, conn net.Conn) // AwaitRequest pre-registers a connection at the sluice for initializing it when it arrives. func AwaitRequest(connInfo *network.Connection, callbackFn RequestCallbackFunc) error { network := getNetworkFromConnInfo(connInfo) if network == "" { return ErrUnsupported } sluice, ok := getSluice(network) if !ok { return fmt.Errorf("sluice for network %s %w", network, ErrSluiceOffline) } return sluice.AwaitRequest(&Request{ ConnInfo: connInfo, CallbackFn: callbackFn, Expires: time.Now().Add(defaultSluiceTTL), }) } func getNetworkFromConnInfo(connInfo *network.Connection) string { var network string // protocol switch connInfo.IPProtocol { //nolint:exhaustive // Looking for specific values. case packet.TCP: network = "tcp" case packet.UDP: network = "udp" default: return "" } // IP version switch connInfo.IPVersion { case packet.IPv4: network += "4" case packet.IPv6: network += "6" default: return "" } return network } ================================================ FILE: spn/sluice/sluice.go ================================================ package sluice import ( "fmt" "net" "strconv" "sync" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/netenv" ) // Sluice is a tunnel entry listener. type Sluice struct { network string address string createListener ListenerFactory lock sync.Mutex listener net.Listener pendingRequests map[string]*Request abandoned bool } // ListenerFactory defines a function to create a listener. type ListenerFactory func(network, address string) (net.Listener, error) // StartSluice starts a sluice listener at the given address. func StartSluice(network, address string) { s := &Sluice{ network: network, address: address, pendingRequests: make(map[string]*Request), } switch s.network { case "tcp4", "tcp6": s.createListener = net.Listen case "udp4", "udp6": s.createListener = ListenUDP default: log.Errorf("spn/sluice: cannot start sluice for %s: unsupported network", network) return } // Start service worker. module.mgr.Go( s.network+" sluice listener", s.listenHandler, ) } // AwaitRequest pre-registers a connection. func (s *Sluice) AwaitRequest(r *Request) error { // Set default expiry. if r.Expires.IsZero() { r.Expires = time.Now().Add(defaultSluiceTTL) } s.lock.Lock() defer s.lock.Unlock() // Check if a pending request already exists for this local address. key := net.JoinHostPort(r.ConnInfo.LocalIP.String(), strconv.Itoa(int(r.ConnInfo.LocalPort))) _, exists := s.pendingRequests[key] if exists { return fmt.Errorf("a pending request for %s already exists", key) } // Add to pending requests. s.pendingRequests[key] = r return nil } func (s *Sluice) getRequest(address string) (r *Request, ok bool) { s.lock.Lock() defer s.lock.Unlock() r, ok = s.pendingRequests[address] if ok { delete(s.pendingRequests, address) } return } func (s *Sluice) init() error { s.lock.Lock() defer s.lock.Unlock() s.abandoned = false // start listening s.listener = nil ln, err := s.createListener(s.network, s.address) if err != nil { return fmt.Errorf("failed to listen: %w", err) } s.listener = ln // Add to registry. addSluice(s) return nil } func (s *Sluice) abandon() { s.lock.Lock() defer s.lock.Unlock() if s.abandoned { return } s.abandoned = true // Remove from registry. removeSluice(s.network) // Close listener. if s.listener != nil { _ = s.listener.Close() } // Notify pending requests. for i, r := range s.pendingRequests { r.CallbackFn(r.ConnInfo, nil) delete(s.pendingRequests, i) } } func (s *Sluice) handleConnection(conn net.Conn) { // Close the connection if handling is not successful. success := false defer func() { if !success { _ = conn.Close() } }() // Get IP address. var remoteIP net.IP switch typedAddr := conn.RemoteAddr().(type) { case *net.TCPAddr: remoteIP = typedAddr.IP case *net.UDPAddr: remoteIP = typedAddr.IP default: log.Warningf("spn/sluice: cannot handle connection for unsupported network %s", conn.RemoteAddr().Network()) return } // Check if the request is local. local, err := netenv.IsMyIP(remoteIP) if err != nil { log.Warningf("spn/sluice: failed to check if request from %s is local: %s", remoteIP, err) return } if !local { log.Warningf("spn/sluice: received external request from %s, ignoring", remoteIP) // TODO: // Do not allow this to be spammed. // Only allow one trigger per second. // Do not trigger by same "remote IP" in a row. netenv.TriggerNetworkChangeCheck() return } // Get waiting request. r, ok := s.getRequest(conn.RemoteAddr().String()) if !ok { _, err := conn.Write(entrypointInfoMsg) if err != nil { log.Warningf("spn/sluice: new %s request from %s without pending request, but failed to reply with info msg: %s", s.network, conn.RemoteAddr(), err) } else { log.Debugf("spn/sluice: new %s request from %s without pending request, replied with info msg", s.network, conn.RemoteAddr()) } return } // Hand over to callback. log.Tracef( "spn/sluice: new %s request from %s for %s (%s:%d)", s.network, conn.RemoteAddr(), r.ConnInfo.Entity.Domain, r.ConnInfo.Entity.IP, r.ConnInfo.Entity.Port, ) r.CallbackFn(r.ConnInfo, conn) success = true } func (s *Sluice) listenHandler(_ *mgr.WorkerCtx) error { defer s.abandon() err := s.init() if err != nil { return err } // Handle new connections. log.Infof("spn/sluice: started listening for %s requests on %s", s.network, s.listener.Addr()) for { conn, err := s.listener.Accept() if err != nil { if module.mgr.IsDone() { return nil } return fmt.Errorf("failed to accept connection: %w", err) } // Handle accepted connection. s.handleConnection(conn) // Clean up old leftovers. s.cleanConnections() } } func (s *Sluice) cleanConnections() { s.lock.Lock() defer s.lock.Unlock() now := time.Now() for address, request := range s.pendingRequests { if now.After(request.Expires) { delete(s.pendingRequests, address) log.Debugf("spn/sluice: removed expired pending %s connection %s", s.network, request.ConnInfo) } } } ================================================ FILE: spn/sluice/sluices.go ================================================ package sluice import "sync" var ( sluices = make(map[string]*Sluice) sluicesLock sync.RWMutex ) func getSluice(network string) (s *Sluice, ok bool) { sluicesLock.RLock() defer sluicesLock.RUnlock() s, ok = sluices[network] return } func addSluice(s *Sluice) { sluicesLock.Lock() defer sluicesLock.Unlock() sluices[s.network] = s } func removeSluice(network string) { sluicesLock.Lock() defer sluicesLock.Unlock() delete(sluices, network) } func copySluices() map[string]*Sluice { sluicesLock.Lock() defer sluicesLock.Unlock() copied := make(map[string]*Sluice, len(sluices)) for k, v := range sluices { copied[k] = v } return copied } func stopAllSluices() { for _, sluice := range copySluices() { sluice.abandon() } } ================================================ FILE: spn/sluice/udp_listener.go ================================================ package sluice import ( "io" "net" "runtime" "sync" "sync/atomic" "time" "github.com/tevino/abool" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" "github.com/safing/portmaster/service/mgr" ) const onWindows = runtime.GOOS == "windows" // UDPListener is a listener for UDP. type UDPListener struct { sock *net.UDPConn closed *abool.AtomicBool newConns chan *UDPConn oobSize int lock sync.Mutex conns map[string]*UDPConn err error } // ListenUDP creates a packet listener. func ListenUDP(network, address string) (net.Listener, error) { // Parse address. udpAddr, err := net.ResolveUDPAddr(network, address) if err != nil { return nil, err } // Determine oob data size. oobSize := 40 // IPv6 (measured) if udpAddr.IP.To4() != nil { oobSize = 32 // IPv4 (measured) } // Create a new listening UDP socket. sock, err := net.ListenUDP(network, udpAddr) if err != nil { return nil, err } // Create listener. ln := &UDPListener{ sock: sock, closed: abool.New(), newConns: make(chan *UDPConn), oobSize: oobSize, conns: make(map[string]*UDPConn), } // Set socket options on listener. err = ln.setSocketOptions() if err != nil { return nil, err } // Start workers. module.mgr.Go("udp listener reader", ln.reader) module.mgr.Go("udp listener cleaner", ln.cleaner) return ln, nil } // Accept waits for and returns the next connection to the listener. func (ln *UDPListener) Accept() (net.Conn, error) { conn := <-ln.newConns if conn != nil { return conn, nil } // Check if there is a socket error. ln.lock.Lock() defer ln.lock.Unlock() if ln.err != nil { return nil, ln.err } return nil, io.EOF } // Close closes the listener. // Any blocked Accept operations will be unblocked and return errors. func (ln *UDPListener) Close() error { if !ln.closed.SetToIf(false, true) { return nil } // Close all channels. close(ln.newConns) ln.lock.Lock() defer ln.lock.Unlock() for _, conn := range ln.conns { close(conn.in) } // Close socket. return ln.sock.Close() } // Addr returns the listener's network address. func (ln *UDPListener) Addr() net.Addr { return ln.sock.LocalAddr() } func (ln *UDPListener) getConn(remoteAddr string) (conn *UDPConn, ok bool) { ln.lock.Lock() defer ln.lock.Unlock() conn, ok = ln.conns[remoteAddr] return } func (ln *UDPListener) setConn(conn *UDPConn) { ln.lock.Lock() defer ln.lock.Unlock() ln.conns[conn.addr.String()] = conn } func (ln *UDPListener) reader(_ *mgr.WorkerCtx) error { for { // TODO: Find good buf size. // With a buf size of 512 we have seen this error on Windows: // wsarecvmsg: A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself. // UDP is not (yet) heavily used, so we can go for the 1500 bytes size for now. // Read data from connection. buf := make([]byte, 1500) // TODO: see comment above. oob := make([]byte, ln.oobSize) n, oobn, _, addr, err := ln.sock.ReadMsgUDP(buf, oob) if err != nil { // Set socket error. ln.lock.Lock() ln.err = err ln.lock.Unlock() // Close and return _ = ln.Close() return nil //nolint:nilerr } buf = buf[:n] oob = oob[:oobn] // Get connection and supply data. conn, ok := ln.getConn(addr.String()) if ok { // Ignore if conn is closed. if conn.closed.IsSet() { continue } select { case conn.in <- buf: default: } continue } // Or create a new connection. conn = &UDPConn{ ln: ln, addr: addr, oob: oob, closed: abool.New(), closing: make(chan struct{}), buf: buf, in: make(chan []byte, 1), inactivityCnt: new(uint32), } ln.setConn(conn) ln.newConns <- conn } } func (ln *UDPListener) cleaner(ctx *mgr.WorkerCtx) error { for { select { case <-time.After(1 * time.Minute): // Check if listener has died. if ln.closed.IsSet() { return nil } // Clean connections. ln.cleanInactiveConns(10) case <-ctx.Done(): // Exit with module stop. return nil } } } func (ln *UDPListener) cleanInactiveConns(overInactivityCnt uint32) { ln.lock.Lock() defer ln.lock.Unlock() for k, conn := range ln.conns { cnt := atomic.AddUint32(conn.inactivityCnt, 1) switch { case cnt > overInactivityCnt*2: delete(ln.conns, k) case cnt > overInactivityCnt: _ = conn.Close() } } } // setUDPSocketOptions sets socket options so that the source address for // replies is correct. func (ln *UDPListener) setSocketOptions() error { // Setting socket options is not supported on windows. if onWindows { return nil } // As we might be listening on an interface that supports both IPv4 and IPv6, // try to set the socket options on both. // Only report an error if it fails on both. err4 := ipv4.NewPacketConn(ln.sock).SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true) err6 := ipv6.NewPacketConn(ln.sock).SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true) if err4 != nil && err6 != nil { return err4 } return nil } // UDPConn simulates a connection for a stateless protocol. type UDPConn struct { ln *UDPListener addr *net.UDPAddr oob []byte closed *abool.AtomicBool closing chan struct{} buf []byte in chan []byte inactivityCnt *uint32 } // Read reads data from the connection. // Read can be made to time out and return an error after a fixed // time limit; see SetDeadline and SetReadDeadline. func (conn *UDPConn) Read(b []byte) (n int, err error) { // Check if connection is closed. if conn.closed.IsSet() { return 0, io.EOF } // Mark as active. atomic.StoreUint32(conn.inactivityCnt, 0) // Get new buffer. if conn.buf == nil { select { case conn.buf = <-conn.in: if conn.buf == nil { return 0, io.EOF } case <-conn.closing: return 0, io.EOF } } // Serve from buffer. copy(b, conn.buf) if len(b) >= len(conn.buf) { copied := len(conn.buf) conn.buf = nil return copied, nil } copied := len(b) conn.buf = conn.buf[copied:] return copied, nil } // Write writes data to the connection. // Write can be made to time out and return an error after a fixed // time limit; see SetDeadline and SetWriteDeadline. func (conn *UDPConn) Write(b []byte) (n int, err error) { // Check if connection is closed. if conn.closed.IsSet() { return 0, io.EOF } // Mark as active. atomic.StoreUint32(conn.inactivityCnt, 0) n, _, err = conn.ln.sock.WriteMsgUDP(b, conn.oob, conn.addr) return n, err } // Close is a no-op as UDP connections share a single socket. Just stop sending // packets without closing. func (conn *UDPConn) Close() error { if conn.closed.SetToIf(false, true) { close(conn.closing) } return nil } // LocalAddr returns the local network address. func (conn *UDPConn) LocalAddr() net.Addr { return conn.ln.sock.LocalAddr() } // RemoteAddr returns the remote network address. func (conn *UDPConn) RemoteAddr() net.Addr { return conn.addr } // SetDeadline is a no-op as UDP connections share a single socket. func (conn *UDPConn) SetDeadline(t time.Time) error { return nil } // SetReadDeadline is a no-op as UDP connections share a single socket. func (conn *UDPConn) SetReadDeadline(t time.Time) error { return nil } // SetWriteDeadline is a no-op as UDP connections share a single socket. func (conn *UDPConn) SetWriteDeadline(t time.Time) error { return nil } ================================================ FILE: spn/spn.go ================================================ package spn ================================================ FILE: spn/terminal/control_flow.go ================================================ package terminal import ( "context" "fmt" "sync" "sync/atomic" "time" "github.com/safing/portmaster/service/mgr" "github.com/safing/structures/varint" ) // FlowControl defines the flow control interface. type FlowControl interface { Deliver(msg *Msg) *Error Receive() <-chan *Msg Send(msg *Msg, timeout time.Duration) *Error ReadyToSend() <-chan struct{} Flush(timeout time.Duration) StartWorkers(m *mgr.Manager, terminalName string) RecvQueueLen() int SendQueueLen() int } // FlowControlType represents a flow control type. type FlowControlType uint8 // Flow Control Types. const ( FlowControlDefault FlowControlType = 0 FlowControlDFQ FlowControlType = 1 FlowControlNone FlowControlType = 2 defaultFlowControl = FlowControlDFQ ) // DefaultSize returns the default flow control size. func (fct FlowControlType) DefaultSize() uint32 { if fct == FlowControlDefault { fct = defaultFlowControl } switch fct { case FlowControlDFQ: return 50000 case FlowControlNone: return 10000 case FlowControlDefault: fallthrough default: return 0 } } // Flow Queue Configuration. const ( DefaultQueueSize = 50000 MaxQueueSize = 1000000 forceReportBelowPercent = 0.75 ) // DuplexFlowQueue is a duplex flow control mechanism using queues. type DuplexFlowQueue struct { // ti is the Terminal that is using the DFQ. ctx context.Context // submitUpstream is used to submit messages to the upstream channel. submitUpstream func(msg *Msg, timeout time.Duration) // sendQueue holds the messages that are waiting to be sent. sendQueue chan *Msg // prioMsgs holds the number of messages to send with high priority. prioMsgs *int32 // sendSpace indicates the amount free slots in the recvQueue on the other end. sendSpace *int32 // readyToSend is used to notify sending components that there is free space. readyToSend chan struct{} // wakeSender is used to wake a sender in case the sendSpace was zero and the // sender is waiting for available space. wakeSender chan struct{} // recvQueue holds the messages that are waiting to be processed. recvQueue chan *Msg // reportedSpace indicates the amount of free slots that the other end knows // about. reportedSpace *int32 // spaceReportLock locks the calculation of space to report. spaceReportLock sync.Mutex // forceSpaceReport forces the sender to send a space report. forceSpaceReport chan struct{} // flush is used to send a finish function to the handler, which will write // all pending messages and then call the received function. flush chan func() } // NewDuplexFlowQueue returns a new duplex flow queue. func NewDuplexFlowQueue( ctx context.Context, queueSize uint32, submitUpstream func(msg *Msg, timeout time.Duration), ) *DuplexFlowQueue { dfq := &DuplexFlowQueue{ ctx: ctx, submitUpstream: submitUpstream, sendQueue: make(chan *Msg, queueSize), prioMsgs: new(int32), sendSpace: new(int32), readyToSend: make(chan struct{}), wakeSender: make(chan struct{}, 1), recvQueue: make(chan *Msg, queueSize), reportedSpace: new(int32), forceSpaceReport: make(chan struct{}, 1), flush: make(chan func()), } atomic.StoreInt32(dfq.sendSpace, int32(queueSize)) atomic.StoreInt32(dfq.reportedSpace, int32(queueSize)) return dfq } // StartWorkers starts the necessary workers to operate the flow queue. func (dfq *DuplexFlowQueue) StartWorkers(m *mgr.Manager, terminalName string) { m.Go(terminalName+" flow queue", dfq.FlowHandler) } // shouldReportRecvSpace returns whether the receive space should be reported. func (dfq *DuplexFlowQueue) shouldReportRecvSpace() bool { return atomic.LoadInt32(dfq.reportedSpace) < int32(float32(cap(dfq.recvQueue))*forceReportBelowPercent) } // decrementReportedRecvSpace decreases the reported recv space by 1 and // returns if the receive space should be reported. func (dfq *DuplexFlowQueue) decrementReportedRecvSpace() (shouldReportRecvSpace bool) { return atomic.AddInt32(dfq.reportedSpace, -1) < int32(float32(cap(dfq.recvQueue))*forceReportBelowPercent) } // getSendSpace returns the current send space. func (dfq *DuplexFlowQueue) getSendSpace() int32 { return atomic.LoadInt32(dfq.sendSpace) } // decrementSendSpace decreases the send space by 1 and returns it. func (dfq *DuplexFlowQueue) decrementSendSpace() int32 { return atomic.AddInt32(dfq.sendSpace, -1) } func (dfq *DuplexFlowQueue) addToSendSpace(n int32) { // Add new space to send space and check if it was zero. atomic.AddInt32(dfq.sendSpace, n) // Wake the sender in case it is waiting. select { case dfq.wakeSender <- struct{}{}: default: } } // reportableRecvSpace returns how much free space can be reported to the other // end. The returned number must be communicated to the other end and must not // be ignored. func (dfq *DuplexFlowQueue) reportableRecvSpace() int32 { // Changes to the recvQueue during calculation are no problem. // We don't want to report space twice though! dfq.spaceReportLock.Lock() defer dfq.spaceReportLock.Unlock() // Calculate reportable receive space and add it to the reported space. reportedSpace := atomic.LoadInt32(dfq.reportedSpace) toReport := int32(cap(dfq.recvQueue)-len(dfq.recvQueue)) - reportedSpace // Never report values below zero. // This can happen, as dfq.reportedSpace is decreased after a container is // submitted to dfq.recvQueue by dfq.Deliver(). This race condition can only // lower the space to report, not increase it. A simple check here solved // this problem and keeps performance high. // Also, don't report values of 1, as the benefit is minimal and this might // be commonly triggered due to the buffer of the force report channel. if toReport <= 1 { return 0 } // Add space to report to dfq.reportedSpace and return it. atomic.AddInt32(dfq.reportedSpace, toReport) return toReport } // FlowHandler handles all flow queue internals and must be started as a worker // in the module where it is used. func (dfq *DuplexFlowQueue) FlowHandler(_ *mgr.WorkerCtx) error { // The upstreamSender is started by the terminal module, but is tied to the // flow owner instead. Make sure that the flow owner's module depends on the // terminal module so that it is shut down earlier. var sendSpaceDepleted bool var flushFinished func() // Drain all queues when shutting down. defer func() { for { select { case msg := <-dfq.sendQueue: msg.Finish() case msg := <-dfq.recvQueue: msg.Finish() default: return } } }() sending: for { // If the send queue is depleted, wait to be woken. if sendSpaceDepleted { select { case <-dfq.wakeSender: if dfq.getSendSpace() > 0 { sendSpaceDepleted = false } else { continue sending } case <-dfq.forceSpaceReport: // Forced reporting of space. // We do not need to check if there is enough sending space, as there is // no data included. spaceToReport := dfq.reportableRecvSpace() if spaceToReport > 0 { msg := NewMsg(varint.Pack64(uint64(spaceToReport))) dfq.submitUpstream(msg, 0) } continue sending case <-dfq.ctx.Done(): return nil } } // Get message from send queue. select { case dfq.readyToSend <- struct{}{}: // Notify that we are ready to send. case msg := <-dfq.sendQueue: // Send message from queue. // If nil, the queue is being shut down. if msg == nil { return nil } // Check if we are handling a high priority message or waiting for one. // Mark any msgs as high priority, when there is one in the pipeline. remainingPrioMsgs := atomic.AddInt32(dfq.prioMsgs, -1) switch { case remainingPrioMsgs >= 0: msg.Unit.MakeHighPriority() case remainingPrioMsgs < -30_000: // Prevent wrap to positive. // Compatible with int16 or bigger. atomic.StoreInt32(dfq.prioMsgs, 0) } // Wait for processing slot. msg.Unit.WaitForSlot() // Prepend available receiving space. msg.Data.Prepend(varint.Pack64(uint64(dfq.reportableRecvSpace()))) // Submit for sending upstream. dfq.submitUpstream(msg, 0) // Decrease the send space and set flag if depleted. if dfq.decrementSendSpace() <= 0 { sendSpaceDepleted = true } // Check if the send queue is empty now and signal flushers. if flushFinished != nil && len(dfq.sendQueue) == 0 { flushFinished() flushFinished = nil } case <-dfq.forceSpaceReport: // Forced reporting of space. // We do not need to check if there is enough sending space, as there is // no data included. spaceToReport := dfq.reportableRecvSpace() if spaceToReport > 0 { msg := NewMsg(varint.Pack64(uint64(spaceToReport))) dfq.submitUpstream(msg, 0) } case newFlushFinishedFn := <-dfq.flush: // Signal immediately if send queue is empty. if len(dfq.sendQueue) == 0 { newFlushFinishedFn() } else { // If there already is a flush finished function, stack them. if flushFinished != nil { stackedFlushFinishFn := flushFinished flushFinished = func() { stackedFlushFinishFn() newFlushFinishedFn() } } else { flushFinished = newFlushFinishedFn } } case <-dfq.ctx.Done(): return nil } } } // Flush waits for all waiting data to be sent. func (dfq *DuplexFlowQueue) Flush(timeout time.Duration) { // Create channel and function for notifying. wait := make(chan struct{}) finished := func() { close(wait) } // Request flush and return when stopping. select { case dfq.flush <- finished: case <-dfq.ctx.Done(): return case <-TimedOut(timeout): return } // Wait for flush to finish and return when stopping. select { case <-wait: case <-dfq.ctx.Done(): case <-TimedOut(timeout): } } var ready = make(chan struct{}) func init() { close(ready) } // ReadyToSend returns a channel that can be read when data can be sent. func (dfq *DuplexFlowQueue) ReadyToSend() <-chan struct{} { if atomic.LoadInt32(dfq.sendSpace) > 0 { return ready } return dfq.readyToSend } // Send adds the given container to the send queue. func (dfq *DuplexFlowQueue) Send(msg *Msg, timeout time.Duration) *Error { select { case dfq.sendQueue <- msg: if msg.Unit.IsHighPriority() { // Reset prioMsgs to the current queue size, so that all waiting and the // message we just added are all handled as high priority. atomic.StoreInt32(dfq.prioMsgs, int32(len(dfq.sendQueue))) } return nil case <-TimedOut(timeout): msg.Finish() return ErrTimeout case <-dfq.ctx.Done(): msg.Finish() return ErrStopping } } // Receive receives a container from the recv queue. func (dfq *DuplexFlowQueue) Receive() <-chan *Msg { // If the reported recv space is nearing its end, force a report. if dfq.shouldReportRecvSpace() { select { case dfq.forceSpaceReport <- struct{}{}: default: } } return dfq.recvQueue } // Deliver submits a container for receiving from upstream. func (dfq *DuplexFlowQueue) Deliver(msg *Msg) *Error { // Ignore nil containers. if msg == nil || msg.Data == nil { msg.Finish() return ErrMalformedData.With("no data") } // Get and add new reported space. addSpace, err := msg.Data.GetNextN16() if err != nil { msg.Finish() return ErrMalformedData.With("failed to parse reported space: %w", err) } if addSpace > 0 { dfq.addToSendSpace(int32(addSpace)) } // Abort processing if the container only contained a space update. if !msg.Data.HoldsData() { msg.Finish() return nil } select { case dfq.recvQueue <- msg: // If the recv queue accepted the Container, decrement the recv space. shouldReportRecvSpace := dfq.decrementReportedRecvSpace() // If the reported recv space is nearing its end, force a report, if the // sender worker is idle. if shouldReportRecvSpace { select { case dfq.forceSpaceReport <- struct{}{}: default: } } return nil default: // If the recv queue is full, return an error. // The whole point of the flow queue is to guarantee that this never happens. msg.Finish() return ErrQueueOverflow } } // FlowStats returns a k=v formatted string of internal stats. func (dfq *DuplexFlowQueue) FlowStats() string { return fmt.Sprintf( "sq=%d rq=%d sends=%d reps=%d", len(dfq.sendQueue), len(dfq.recvQueue), atomic.LoadInt32(dfq.sendSpace), atomic.LoadInt32(dfq.reportedSpace), ) } // RecvQueueLen returns the current length of the receive queue. func (dfq *DuplexFlowQueue) RecvQueueLen() int { return len(dfq.recvQueue) } // SendQueueLen returns the current length of the send queue. func (dfq *DuplexFlowQueue) SendQueueLen() int { return len(dfq.sendQueue) } ================================================ FILE: spn/terminal/defaults.go ================================================ package terminal const ( // UsePriorityDataMsgs defines whether priority data messages should be used. UsePriorityDataMsgs = true ) // DefaultCraneControllerOpts returns the default terminal options for a crane // controller terminal. func DefaultCraneControllerOpts() *TerminalOpts { return &TerminalOpts{ Padding: 0, // Crane already applies padding. FlowControl: FlowControlNone, UsePriorityDataMsgs: UsePriorityDataMsgs, } } // DefaultHomeHubTerminalOpts returns the default terminal options for a crane // terminal used for the home hub. func DefaultHomeHubTerminalOpts() *TerminalOpts { return &TerminalOpts{ Padding: 0, // Crane already applies padding. FlowControl: FlowControlDFQ, UsePriorityDataMsgs: UsePriorityDataMsgs, } } // DefaultExpansionTerminalOpts returns the default terminal options for an // expansion terminal. func DefaultExpansionTerminalOpts() *TerminalOpts { return &TerminalOpts{ Padding: 8, FlowControl: FlowControlDFQ, UsePriorityDataMsgs: UsePriorityDataMsgs, } } ================================================ FILE: spn/terminal/errors.go ================================================ package terminal import ( "context" "errors" "fmt" "github.com/safing/structures/varint" ) // Error is a terminal error. type Error struct { // id holds the internal error ID. id uint8 // external signifies if the error was received from the outside. external bool // err holds the wrapped error or the default error message. err error } // ID returns the internal ID of the error. func (e *Error) ID() uint8 { return e.id } // Error returns the human readable format of the error. func (e *Error) Error() string { if e.external { return "[ext] " + e.err.Error() } return e.err.Error() } // IsExternal returns whether the error occurred externally. func (e *Error) IsExternal() bool { if e == nil { return false } return e.external } // Is returns whether the given error is of the same type. func (e *Error) Is(target error) bool { if e == nil || target == nil { return false } t, ok := target.(*Error) //nolint:errorlint // Error implementation, not usage. if !ok { return false } return e.id == t.id } // Unwrap returns the wrapped error. func (e *Error) Unwrap() error { if e == nil || e.err == nil { return nil } return e.err } // With adds context and details where the error occurred. The provided // message is appended to the error. // A new error with the same ID is returned and must be compared with // errors.Is(). func (e *Error) With(format string, a ...interface{}) *Error { // Return nil if error is nil. if e == nil { return nil } return &Error{ id: e.id, err: fmt.Errorf(e.Error()+": "+format, a...), } } // Wrap adds context higher up in the call chain. The provided message is // prepended to the error. // A new error with the same ID is returned and must be compared with // errors.Is(). func (e *Error) Wrap(format string, a ...interface{}) *Error { // Return nil if error is nil. if e == nil { return nil } return &Error{ id: e.id, err: fmt.Errorf(format+": "+e.Error(), a...), } } // AsExternal creates and returns an external version of the error. func (e *Error) AsExternal() *Error { // Return nil if error is nil. if e == nil { return nil } return &Error{ id: e.id, err: e.err, external: true, } } // Pack returns the serialized internal error ID. The additional message is // lost and is replaced with the default message upon parsing. func (e *Error) Pack() []byte { // Return nil slice if error is nil. if e == nil { return nil } return varint.Pack8(e.id) } // ParseExternalError parses an external error. func ParseExternalError(id []byte) (*Error, error) { // Return nil for an empty error. if len(id) == 0 { return ErrStopping.AsExternal(), nil } parsedID, _, err := varint.Unpack8(id) if err != nil { return nil, fmt.Errorf("failed to unpack error ID: %w", err) } return NewExternalError(parsedID), nil } // NewExternalError creates an external error based on the given ID. func NewExternalError(id uint8) *Error { err, ok := errorRegistry[id] if ok { return err.AsExternal() } return ErrUnknownError.AsExternal() } var errorRegistry = make(map[uint8]*Error) func registerError(id uint8, err error) *Error { // Check for duplicate. _, ok := errorRegistry[id] if ok { panic(fmt.Sprintf("error with id %d already registered", id)) } newErr := &Error{ id: id, err: err, } errorRegistry[id] = newErr return newErr } // func (e *Error) IsSpecial() bool { // if e == nil { // return false // } // return e.id > 0 && e.id < 8 // } // IsOK returns if the error represents a "OK" or success status. func (e *Error) IsOK() bool { return !e.IsError() } // IsError returns if the error represents an erronous condition. func (e *Error) IsError() bool { if e == nil || e.err == nil { return false } if e.id == 0 || e.id >= 8 { return true } return false } // Terminal Errors. var ( // ErrUnknownError is the default error. ErrUnknownError = registerError(0, errors.New("unknown error")) // Error IDs 1-7 are reserved for special "OK" values. ErrStopping = registerError(2, errors.New("stopping")) ErrExplicitAck = registerError(3, errors.New("explicit ack")) ErrNoActivity = registerError(4, errors.New("no activity")) // Errors IDs 8 and up are for regular errors. ErrInternalError = registerError(8, errors.New("internal error")) ErrMalformedData = registerError(9, errors.New("malformed data")) ErrUnexpectedMsgType = registerError(10, errors.New("unexpected message type")) ErrUnknownOperationType = registerError(11, errors.New("unknown operation type")) ErrUnknownOperationID = registerError(12, errors.New("unknown operation id")) ErrPermissionDenied = registerError(13, errors.New("permission denied")) ErrIntegrity = registerError(14, errors.New("integrity violated")) ErrInvalidOptions = registerError(15, errors.New("invalid options")) ErrHubNotReady = registerError(16, errors.New("hub not ready")) ErrRateLimited = registerError(24, errors.New("rate limited")) ErrIncorrectUsage = registerError(22, errors.New("incorrect usage")) ErrTimeout = registerError(62, errors.New("timed out")) ErrUnsupportedVersion = registerError(93, errors.New("unsupported version")) ErrHubUnavailable = registerError(101, errors.New("hub unavailable")) ErrAbandonedTerminal = registerError(102, errors.New("terminal is being abandoned")) ErrShipSunk = registerError(108, errors.New("ship sunk")) ErrDestinationUnavailable = registerError(113, errors.New("destination unavailable")) ErrTryAgainLater = registerError(114, errors.New("try again later")) ErrConnectionError = registerError(121, errors.New("connection error")) ErrQueueOverflow = registerError(122, errors.New("queue overflowed")) ErrCanceled = registerError(125, context.Canceled) ) ================================================ FILE: spn/terminal/fmt.go ================================================ package terminal import "fmt" // CustomTerminalIDFormatting defines an interface for terminal to define their custom ID format. type CustomTerminalIDFormatting interface { CustomIDFormat() string } // FmtID formats the terminal ID together with the parent's ID. func (t *TerminalBase) FmtID() string { if t.ext != nil { if customFormatting, ok := t.ext.(CustomTerminalIDFormatting); ok { return customFormatting.CustomIDFormat() } } return fmtTerminalID(t.parentID, t.id) } func fmtTerminalID(craneID string, terminalID uint32) string { return fmt.Sprintf("%s#%d", craneID, terminalID) } func fmtOperationID(craneID string, terminalID, operationID uint32) string { return fmt.Sprintf("%s#%d>%d", craneID, terminalID, operationID) } ================================================ FILE: spn/terminal/init.go ================================================ package terminal import ( "context" "github.com/safing/jess" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/hub" "github.com/safing/structures/container" "github.com/safing/structures/dsd" "github.com/safing/structures/varint" ) /* Terminal Init Message Format: - Version [varint] - Data Block [bytes; not blocked] - TerminalOpts as DSD */ const ( minSupportedTerminalVersion = 1 maxSupportedTerminalVersion = 1 ) // TerminalOpts holds configuration for the terminal. type TerminalOpts struct { //nolint:golint,maligned // TODO: Rename. Version uint8 `json:"-"` Encrypt bool `json:"e,omitempty"` Padding uint16 `json:"p,omitempty"` FlowControl FlowControlType `json:"fc,omitempty"` FlowControlSize uint32 `json:"qs,omitempty"` // Previously was "QueueSize". UsePriorityDataMsgs bool `json:"pr,omitempty"` } // ParseTerminalOpts parses terminal options from the container and checks if // they are valid. func ParseTerminalOpts(c *container.Container) (*TerminalOpts, *Error) { // Parse and check version. version, err := c.GetNextN8() if err != nil { return nil, ErrMalformedData.With("failed to parse version: %w", err) } if version < minSupportedTerminalVersion || version > maxSupportedTerminalVersion { return nil, ErrUnsupportedVersion.With("requested terminal version %d", version) } // Parse init message. initMsg := &TerminalOpts{} _, err = dsd.Load(c.CompileData(), initMsg) if err != nil { return nil, ErrMalformedData.With("failed to parse init message: %w", err) } initMsg.Version = version // Check if options are valid. tErr := initMsg.Check(false) if tErr != nil { return nil, tErr } return initMsg, nil } // Pack serialized the terminal options and checks if they are valid. func (opts *TerminalOpts) Pack() (*container.Container, *Error) { // Check if options are valid. tErr := opts.Check(true) if tErr != nil { return nil, tErr } // Pack init message. optsData, err := dsd.Dump(opts, dsd.CBOR) if err != nil { return nil, ErrInternalError.With("failed to pack init message: %w", err) } // Compile init message. return container.New( varint.Pack8(opts.Version), optsData, ), nil } // Check checks if terminal options are valid. func (opts *TerminalOpts) Check(useDefaultsForRequired bool) *Error { // Version is required - use default when permitted. if opts.Version == 0 && useDefaultsForRequired { opts.Version = 1 } if opts.Version < minSupportedTerminalVersion || opts.Version > maxSupportedTerminalVersion { return ErrInvalidOptions.With("unsupported terminal version %d", opts.Version) } // FlowControl is optional. switch opts.FlowControl { case FlowControlDefault: // Set to default flow control. opts.FlowControl = defaultFlowControl case FlowControlNone, FlowControlDFQ: // Ok. default: return ErrInvalidOptions.With("unknown flow control type: %d", opts.FlowControl) } // FlowControlSize is required as it needs to be same on both sides. // Use default when permitted. if opts.FlowControlSize == 0 && useDefaultsForRequired { opts.FlowControlSize = opts.FlowControl.DefaultSize() } if opts.FlowControlSize <= 0 || opts.FlowControlSize > MaxQueueSize { return ErrInvalidOptions.With("invalid flow control size of %d", opts.FlowControlSize) } return nil } // NewLocalBaseTerminal creates a new local terminal base for use with inheriting terminals. func NewLocalBaseTerminal( ctx context.Context, id uint32, parentID string, remoteHub *hub.Hub, initMsg *TerminalOpts, upstream Upstream, ) ( t *TerminalBase, initData *container.Container, err *Error, ) { // Pack, check and add defaults to init message. initData, err = initMsg.Pack() if err != nil { return nil, nil, err } // Create baseline. t, err = createTerminalBase(ctx, id, parentID, false, initMsg, upstream) if err != nil { return nil, nil, err } // Setup encryption if enabled. if remoteHub != nil { initMsg.Encrypt = true // Select signet (public key) of remote Hub to use. s := remoteHub.SelectSignet() if s == nil { return nil, nil, ErrHubNotReady.With("failed to select signet of remote hub") } // Create new session. env := jess.NewUnconfiguredEnvelope() env.SuiteID = jess.SuiteWireV1 env.Recipients = []*jess.Signet{s} jession, err := env.WireCorrespondence(nil) if err != nil { return nil, nil, ErrIntegrity.With("failed to initialize encryption: %w", err) } t.jession = jession // Encryption is ready for sending. close(t.encryptionReady) } return t, initData, nil } // NewRemoteBaseTerminal creates a new remote terminal base for use with inheriting terminals. func NewRemoteBaseTerminal( ctx context.Context, id uint32, parentID string, identity *cabin.Identity, initData *container.Container, upstream Upstream, ) ( t *TerminalBase, initMsg *TerminalOpts, err *Error, ) { // Parse init message. initMsg, err = ParseTerminalOpts(initData) if err != nil { return nil, nil, err } // Create baseline. t, err = createTerminalBase(ctx, id, parentID, true, initMsg, upstream) if err != nil { return nil, nil, err } // Setup encryption if enabled. if initMsg.Encrypt { if identity == nil { return nil, nil, ErrInternalError.With("missing identity for setting up incoming encryption") } t.identity = identity } return t, initMsg, nil } ================================================ FILE: spn/terminal/metrics.go ================================================ package terminal import ( "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/metrics" ) var metricsRegistered = abool.New() func registerMetrics() (err error) { // Only register metrics once. if !metricsRegistered.SetToIf(false, true) { return nil } // Get scheduler config and calculat scaling. schedulerConfig := getSchedulerConfig() scaleSlotToSecondsFactor := float64(time.Second / schedulerConfig.SlotDuration) // Register metrics from scheduler stats. _, err = metrics.NewGauge( "spn/scheduling/unit/slotpace/max", nil, metricFromInt(scheduler.GetMaxSlotPace, scaleSlotToSecondsFactor), &metrics.Options{ Name: "SPN Scheduling Max Slot Pace (scaled to per second)", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/scheduling/unit/slotpace/leveled/max", nil, metricFromInt(scheduler.GetMaxLeveledSlotPace, scaleSlotToSecondsFactor), &metrics.Options{ Name: "SPN Scheduling Max Leveled Slot Pace (scaled to per second)", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/scheduling/unit/slotpace/avg", nil, metricFromInt(scheduler.GetAvgSlotPace, scaleSlotToSecondsFactor), &metrics.Options{ Name: "SPN Scheduling Avg Slot Pace (scaled to per second)", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/scheduling/unit/life/avg/seconds", nil, metricFromNanoseconds(scheduler.GetAvgUnitLife), &metrics.Options{ Name: "SPN Scheduling Avg Unit Life", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/scheduling/unit/workslot/avg/seconds", nil, metricFromNanoseconds(scheduler.GetAvgWorkSlotDuration), &metrics.Options{ Name: "SPN Scheduling Avg Work Slot Duration", Permission: api.PermitUser, }, ) if err != nil { return err } _, err = metrics.NewGauge( "spn/scheduling/unit/catchupslot/avg/seconds", nil, metricFromNanoseconds(scheduler.GetAvgCatchUpSlotDuration), &metrics.Options{ Name: "SPN Scheduling Avg Catch-Up Slot Duration", Permission: api.PermitUser, }, ) if err != nil { return err } return nil } func metricFromInt(fn func() int64, scaleFactor float64) func() float64 { return func() float64 { return float64(fn()) * scaleFactor } } func metricFromNanoseconds(fn func() int64) func() float64 { return func() float64 { return float64(fn()) / float64(time.Second) } } ================================================ FILE: spn/terminal/module.go ================================================ package terminal import ( "errors" "flag" "sync/atomic" "time" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/conf" "github.com/safing/portmaster/spn/unit" ) // TerminalModule is the command multiplexing module. type TerminalModule struct { //nolint:golint mgr *mgr.Manager instance instance } // Manager returns the module manager. func (s *TerminalModule) Manager() *mgr.Manager { return s.mgr } // Start starts the module. func (s *TerminalModule) Start() error { return start() } // Stop stops the module. func (s *TerminalModule) Stop() error { return nil } var ( rngFeeder *rng.Feeder = nil scheduler *unit.Scheduler debugUnitScheduling bool ) func init() { flag.BoolVar(&debugUnitScheduling, "debug-unit-scheduling", false, "enable debug logs of the SPN unit scheduler") } func start() error { rngFeeder = rng.NewFeeder() scheduler = unit.NewScheduler(getSchedulerConfig()) if debugUnitScheduling { // Debug unit leaks. scheduler.StartDebugLog() } module.mgr.Go("msg unit scheduler", scheduler.SlotScheduler) lockOpRegistry() return registerMetrics() } var waitForever chan time.Time // TimedOut returns a channel that triggers when the timeout is reached. func TimedOut(timeout time.Duration) <-chan time.Time { if timeout == 0 { return waitForever } return time.After(timeout) } // StopScheduler stops the unit scheduler. func StopScheduler() { if scheduler != nil { scheduler.Stop() } } func getSchedulerConfig() *unit.SchedulerConfig { // Client Scheduler Config. if conf.Client() { return &unit.SchedulerConfig{ SlotDuration: 10 * time.Millisecond, // 100 slots per second MinSlotPace: 10, // 1000pps - Small starting pace for low end devices. WorkSlotPercentage: 0.9, // 90% SlotChangeRatePerStreak: 0.1, // 10% - Increase/Decrease quickly. StatCycleDuration: 1 * time.Minute, // Match metrics report cycle. } } // Server Scheduler Config. return &unit.SchedulerConfig{ SlotDuration: 10 * time.Millisecond, // 100 slots per second MinSlotPace: 100, // 10000pps - Every server should be able to handle this. WorkSlotPercentage: 0.7, // 70% SlotChangeRatePerStreak: 0.05, // 5% StatCycleDuration: 1 * time.Minute, // Match metrics report cycle. } } var ( module *TerminalModule shimLoaded atomic.Bool ) // New returns a new Config module. func New(instance instance) (*TerminalModule, error) { if !shimLoaded.CompareAndSwap(false, true) { return nil, errors.New("only one instance allowed") } m := mgr.New("TerminalModule") module = &TerminalModule{ mgr: m, instance: instance, } return module, nil } type instance interface{} ================================================ FILE: spn/terminal/module_test.go ================================================ package terminal import ( "fmt" "os" "testing" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" "github.com/safing/portmaster/base/metrics" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/core/base" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/conf" ) type testInstance struct { db *dbmodule.DBModule config *config.Config metrics *metrics.Metrics rng *rng.Rng base *base.Base cabin *cabin.Cabin dataDir string } func (stub *testInstance) DataDir() string { if len(stub.dataDir) == 0 { var err error stub.dataDir, err = os.MkdirTemp("", "") if err != nil { panic(fmt.Sprintf("failed to create temp dir: %v", err)) } } return stub.dataDir } func (stub *testInstance) Config() *config.Config { return stub.config } func (stub *testInstance) Metrics() *metrics.Metrics { return stub.metrics } func (stub *testInstance) SPNGroup() *mgr.ExtendedGroup { return nil } func (stub *testInstance) Stopping() bool { return false } func (stub *testInstance) SetCmdLineOperation(f func() error) {} func runTest(m *testing.M) error { var err error conf.EnablePublicHub(true) // Make hub config available. instance := &testInstance{} instance.db, err = dbmodule.New(instance) if err != nil { return fmt.Errorf("failed to create database module: %w", err) } instance.config, err = config.New(instance) if err != nil { return fmt.Errorf("failed to create config module: %w", err) } instance.metrics, err = metrics.New(instance) if err != nil { return fmt.Errorf("failed to create metrics module: %w", err) } instance.rng, err = rng.New(instance) if err != nil { return fmt.Errorf("failed to create rng module: %w", err) } instance.base, err = base.New(instance) if err != nil { return fmt.Errorf("failed to create base module: %w", err) } instance.cabin, err = cabin.New(instance) if err != nil { return fmt.Errorf("failed to create cabin module: %w", err) } _, err = New(instance) if err != nil { return fmt.Errorf("failed to create module: %w", err) } // Start err = instance.db.Start() if err != nil { return fmt.Errorf("failed to start db module: %w", err) } err = instance.config.Start() if err != nil { return fmt.Errorf("failed to start config module: %w", err) } err = instance.metrics.Start() if err != nil { return fmt.Errorf("failed to start metrics module: %w", err) } err = instance.rng.Start() if err != nil { return fmt.Errorf("failed to start rng module: %w", err) } err = instance.base.Start() if err != nil { return fmt.Errorf("failed to start base module: %w", err) } err = instance.cabin.Start() if err != nil { return fmt.Errorf("failed to start cabin module: %w", err) } err = module.Start() if err != nil { return fmt.Errorf("failed to start docks module: %w", err) } m.Run() return nil } func TestMain(m *testing.M) { if err := runTest(m); err != nil { os.Exit(1) } } ================================================ FILE: spn/terminal/msg.go ================================================ package terminal import ( "fmt" "runtime" "github.com/safing/portmaster/spn/unit" "github.com/safing/structures/container" ) // Msg is a message within the SPN network stack. // It includes metadata and unit scheduling. type Msg struct { FlowID uint32 Type MsgType Data *container.Container // Unit scheduling. // Note: With just 100B per packet, a uint64 (the Unit ID) is enough for // over 1800 Exabyte. No need for overflow support. Unit *unit.Unit } // NewMsg returns a new msg. // The FlowID is unset. // The Type is Data. func NewMsg(data []byte) *Msg { msg := &Msg{ Type: MsgTypeData, Data: container.New(data), Unit: scheduler.NewUnit(), } // Debug unit leaks. msg.debugWithCaller(2) return msg } // NewEmptyMsg returns a new empty msg with an initialized Unit. // The FlowID is unset. // The Type is Data. // The Data is unset. func NewEmptyMsg() *Msg { msg := &Msg{ Type: MsgTypeData, Unit: scheduler.NewUnit(), } // Debug unit leaks. msg.debugWithCaller(2) return msg } // Pack prepends the message header (Length and ID+Type) to the data. func (msg *Msg) Pack() { MakeMsg(msg.Data, msg.FlowID, msg.Type) } // Consume adds another Message to itself. // The given Msg is packed before adding it to the data. // The data is moved - not copied! // High priority mark is inherited. func (msg *Msg) Consume(other *Msg) { // Pack message to be added. other.Pack() // Move data. msg.Data.AppendContainer(other.Data) // Inherit high priority. if other.Unit.IsHighPriority() { msg.Unit.MakeHighPriority() } // Finish other unit. other.Finish() } // Finish signals the unit scheduler that this unit has finished processing. // Will no-op if called on a nil Msg. func (msg *Msg) Finish() { // Proxying is necessary, as a nil msg still panics. if msg == nil { return } msg.Unit.Finish() } // Debug registers the unit for debug output with the given source. // Additional calls on the same unit update the unit source. // StartDebugLog() must be called before calling DebugUnit(). func (msg *Msg) Debug() { msg.debugWithCaller(2) } func (msg *Msg) debugWithCaller(skip int) { //nolint:unparam if !debugUnitScheduling || msg == nil { return } _, file, line, ok := runtime.Caller(skip) if ok { scheduler.DebugUnit(msg.Unit, fmt.Sprintf("%s:%d", file, line)) } } ================================================ FILE: spn/terminal/msgtypes.go ================================================ package terminal import ( "github.com/safing/structures/container" "github.com/safing/structures/varint" ) /* Terminal and Operation Message Format: - Length [varint] - If Length is 0, the remainder of given data is padding. - IDType [varint] - Type [uses least two significant bits] - One of Init, Data, Stop - ID [uses all other bits] - The ID is currently not adapted in order to make reading raw message easier. This means that IDs are currently always a multiple of 4. - Data [bytes; format depends on msg type] - MsgTypeInit: - Data [bytes] - MsgTypeData: - AddAvailableSpace [varint, if Flow Queue is used] - (Encrypted) Data [bytes] - MsgTypeStop: - Error Code [varint] */ // MsgType is the message type for both terminals and operations. type MsgType uint8 const ( // MsgTypeInit is used to establish a new terminal or run a new operation. MsgTypeInit MsgType = 1 // MsgTypeData is used to send data to a terminal or operation. MsgTypeData MsgType = 2 // MsgTypePriorityData is used to send prioritized data to a terminal or operation. MsgTypePriorityData MsgType = 0 // MsgTypeStop is used to abandon a terminal or end an operation, with an optional error. MsgTypeStop MsgType = 3 ) // AddIDType prepends the ID and Type header to the message. func AddIDType(c *container.Container, id uint32, msgType MsgType) { c.Prepend(varint.Pack32(id | uint32(msgType))) } // MakeMsg prepends the message header (Length and ID+Type) to the data. func MakeMsg(c *container.Container, id uint32, msgType MsgType) { AddIDType(c, id, msgType) c.PrependLength() } // ParseIDType parses the combined message ID and type. func ParseIDType(c *container.Container) (id uint32, msgType MsgType, err error) { idType, err := c.GetNextN32() if err != nil { return 0, 0, err } msgType = MsgType(idType % 4) return idType - uint32(msgType), msgType, nil } ================================================ FILE: spn/terminal/operation.go ================================================ package terminal import ( "sync" "sync/atomic" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils" "github.com/safing/portmaster/service/mgr" "github.com/safing/structures/container" ) // Operation is an interface for all operations. type Operation interface { // InitOperationBase initialize the operation with the ID and attached terminal. // Should not be overridden by implementations. InitOperationBase(t Terminal, opID uint32) // ID returns the ID of the operation. // Should not be overridden by implementations. ID() uint32 // Type returns the operation's type ID. // Should be overridden by implementations to return correct type ID. Type() string // Deliver delivers a message to the operation. // Meant to be overridden by implementations. Deliver(msg *Msg) *Error // NewMsg creates a new message from this operation. // Should not be overridden by implementations. NewMsg(data []byte) *Msg // Send sends a message to the other side. // Should not be overridden by implementations. Send(msg *Msg, timeout time.Duration) *Error // Flush sends all messages waiting in the terminal. // Should not be overridden by implementations. Flush(timeout time.Duration) // Stopped returns whether the operation has stopped. // Should not be overridden by implementations. Stopped() bool // markStopped marks the operation as stopped. // It returns whether the stop flag was set. markStopped() bool // Stop stops the operation by unregistering it from the terminal and calling HandleStop(). // Should not be overridden by implementations. Stop(self Operation, err *Error) // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. // Meant to be overridden by implementations. HandleStop(err *Error) (errorToSend *Error) // Terminal returns the terminal the operation is linked to. // Should not be overridden by implementations. Terminal() Terminal } // OperationFactory defines an operation factory. type OperationFactory struct { // Type is the type id of an operation. Type string // Requires defines the required permissions to run an operation. Requires Permission // Start is the function that starts a new operation. Start OperationStarter } // OperationStarter is used to initialize operations remotely. type OperationStarter func(attachedTerminal Terminal, opID uint32, initData *container.Container) (Operation, *Error) var ( opRegistry = make(map[string]*OperationFactory) opRegistryLock sync.Mutex opRegistryLocked = abool.New() ) // RegisterOpType registers a new operation type and may only be called during // Go's init and a module's prep phase. func RegisterOpType(factory OperationFactory) { // Check if we can still register an operation type. if opRegistryLocked.IsSet() { log.Errorf("spn/terminal: failed to register operation %s: operation registry is already locked", factory.Type) return } opRegistryLock.Lock() defer opRegistryLock.Unlock() // Check if the operation type was already registered. if _, ok := opRegistry[factory.Type]; ok { log.Errorf("spn/terminal: failed to register operation type %s: type already registered", factory.Type) return } // Save to registry. opRegistry[factory.Type] = &factory } func lockOpRegistry() { opRegistryLocked.Set() } func (t *TerminalBase) handleOperationStart(opID uint32, initData *container.Container) { // Check if the terminal is being abandoned. if t.Abandoning.IsSet() { t.StopOperation(newUnknownOp(opID, ""), ErrAbandonedTerminal) return } // Extract the requested operation name. opType, err := initData.GetNextBlock() if err != nil { t.StopOperation(newUnknownOp(opID, ""), ErrMalformedData.With("failed to get init data: %w", err)) return } // Get the operation factory from the registry. factory, ok := opRegistry[string(opType)] if !ok { t.StopOperation(newUnknownOp(opID, ""), ErrUnknownOperationType.With(utils.SafeFirst16Bytes(opType))) return } // Check if the Terminal has the required permission to run the operation. if !t.HasPermission(factory.Requires) { t.StopOperation(newUnknownOp(opID, factory.Type), ErrPermissionDenied) return } // Get terminal to attach to. attachToTerminal := t.ext if attachToTerminal == nil { attachToTerminal = t } // Run the operation. op, opErr := factory.Start(attachToTerminal, opID, initData) switch { case opErr != nil: // Something went wrong. t.StopOperation(newUnknownOp(opID, factory.Type), opErr) case op == nil: // The Operation was successful and is done already. log.Debugf("spn/terminal: operation %s %s executed", factory.Type, fmtOperationID(t.parentID, t.id, opID)) t.StopOperation(newUnknownOp(opID, factory.Type), nil) default: // The operation started successfully and requires persistence. t.SetActiveOp(opID, op) log.Debugf("spn/terminal: operation %s %s started", factory.Type, fmtOperationID(t.parentID, t.id, opID)) } } // StartOperation starts the given operation by assigning it an ID and sending the given operation initialization data. func (t *TerminalBase) StartOperation(op Operation, initData *container.Container, timeout time.Duration) *Error { // Get terminal to attach to. attachToTerminal := t.ext if attachToTerminal == nil { attachToTerminal = t } // Get the next operation ID and set it on the operation with the terminal. op.InitOperationBase(attachToTerminal, atomic.AddUint32(t.nextOpID, 8)) // Always add operation to the active operations, as we need to receive a // reply in any case. t.SetActiveOp(op.ID(), op) log.Debugf("spn/terminal: operation %s %s started", op.Type(), fmtOperationID(t.parentID, t.id, op.ID())) // Add or create the operation type block. if initData == nil { initData = container.New() initData.AppendAsBlock([]byte(op.Type())) } else { initData.PrependAsBlock([]byte(op.Type())) } // Create init msg. msg := NewEmptyMsg() msg.FlowID = op.ID() msg.Type = MsgTypeInit msg.Data = initData msg.Unit.MakeHighPriority() // Send init msg. err := op.Send(msg, timeout) if err != nil { msg.Finish() } return err } // Send sends data via this terminal. // If a timeout is set, sending will fail after the given timeout passed. func (t *TerminalBase) Send(msg *Msg, timeout time.Duration) *Error { // Wait for processing slot. msg.Unit.WaitForSlot() // Check if the send queue has available space. select { case t.sendQueue <- msg: return nil default: } // Submit message to buffer, if space is available. select { case t.sendQueue <- msg: return nil case <-TimedOut(timeout): msg.Finish() return ErrTimeout.With("sending via terminal") case <-t.Ctx().Done(): msg.Finish() return ErrStopping } } // StopOperation sends the end signal with an optional error and then deletes // the operation from the Terminal state and calls HandleStop() on the Operation. func (t *TerminalBase) StopOperation(op Operation, err *Error) { // Check if the operation has already stopped. if !op.markStopped() { return } // Log reason the Operation is ending. Override stopping error with nil. switch { case err == nil: log.Debugf("spn/terminal: operation %s %s stopped", op.Type(), fmtOperationID(t.parentID, t.id, op.ID())) case err.IsOK(), err.Is(ErrTryAgainLater), err.Is(ErrRateLimited): log.Debugf("spn/terminal: operation %s %s stopped: %s", op.Type(), fmtOperationID(t.parentID, t.id, op.ID()), err) default: log.Warningf("spn/terminal: operation %s %s failed: %s", op.Type(), fmtOperationID(t.parentID, t.id, op.ID()), err) } module.mgr.Go("stop operation", func(_ *mgr.WorkerCtx) error { // Call operation stop handle function for proper shutdown cleaning up. err = op.HandleStop(err) // Send error to the connected Operation, if the error is internal. if !err.IsExternal() { if err == nil { err = ErrStopping } msg := NewMsg(err.Pack()) msg.FlowID = op.ID() msg.Type = MsgTypeStop tErr := t.Send(msg, 10*time.Second) if tErr != nil { msg.Finish() log.Warningf("spn/terminal: failed to send stop msg: %s", tErr) } } // Remove operation from terminal. t.DeleteActiveOp(op.ID()) return nil }) } // GetActiveOp returns the active operation with the given ID from the // Terminal state. func (t *TerminalBase) GetActiveOp(opID uint32) (op Operation, ok bool) { t.lock.RLock() defer t.lock.RUnlock() op, ok = t.operations[opID] return } // SetActiveOp saves an active operation to the Terminal state. func (t *TerminalBase) SetActiveOp(opID uint32, op Operation) { t.lock.Lock() defer t.lock.Unlock() t.operations[opID] = op } // DeleteActiveOp deletes an active operation from the Terminal state. func (t *TerminalBase) DeleteActiveOp(opID uint32) { t.lock.Lock() defer t.lock.Unlock() delete(t.operations, opID) } // GetActiveOpCount returns the amount of active operations. func (t *TerminalBase) GetActiveOpCount() int { t.lock.RLock() defer t.lock.RUnlock() return len(t.operations) } func newUnknownOp(id uint32, typeID string) *unknownOp { op := &unknownOp{ typeID: typeID, } op.id = id return op } type unknownOp struct { OperationBase typeID string } func (op *unknownOp) Type() string { if op.typeID != "" { return op.typeID } return "unknown" } func (op *unknownOp) Deliver(msg *Msg) *Error { return ErrIncorrectUsage.With("unknown op shim cannot receive") } ================================================ FILE: spn/terminal/operation_base.go ================================================ package terminal import ( "time" "github.com/tevino/abool" ) // OperationBase provides the basic operation functionality. type OperationBase struct { terminal Terminal id uint32 stopped abool.AtomicBool } // InitOperationBase initialize the operation with the ID and attached terminal. // Should not be overridden by implementations. func (op *OperationBase) InitOperationBase(t Terminal, opID uint32) { op.id = opID op.terminal = t } // ID returns the ID of the operation. // Should not be overridden by implementations. func (op *OperationBase) ID() uint32 { return op.id } // Type returns the operation's type ID. // Should be overridden by implementations to return correct type ID. func (op *OperationBase) Type() string { return "unknown" } // Deliver delivers a message to the operation. // Meant to be overridden by implementations. func (op *OperationBase) Deliver(_ *Msg) *Error { return ErrIncorrectUsage.With("Deliver not implemented for this operation") } // NewMsg creates a new message from this operation. // Should not be overridden by implementations. func (op *OperationBase) NewMsg(data []byte) *Msg { msg := NewMsg(data) msg.FlowID = op.id msg.Type = MsgTypeData // Debug unit leaks. msg.debugWithCaller(2) return msg } // NewEmptyMsg creates a new empty message from this operation. // Should not be overridden by implementations. func (op *OperationBase) NewEmptyMsg() *Msg { msg := NewEmptyMsg() msg.FlowID = op.id msg.Type = MsgTypeData // Debug unit leaks. msg.debugWithCaller(2) return msg } // Send sends a message to the other side. // Should not be overridden by implementations. func (op *OperationBase) Send(msg *Msg, timeout time.Duration) *Error { // Add and update metadata. msg.FlowID = op.id if msg.Type == MsgTypeData && msg.Unit.IsHighPriority() && UsePriorityDataMsgs { msg.Type = MsgTypePriorityData } // Wait for processing slot. msg.Unit.WaitForSlot() // Send message. tErr := op.terminal.Send(msg, timeout) if tErr != nil { // Finish message unit on failure. msg.Finish() } return tErr } // Flush sends all messages waiting in the terminal. // Meant to be overridden by implementations. func (op *OperationBase) Flush(timeout time.Duration) { op.terminal.Flush(timeout) } // Stopped returns whether the operation has stopped. // Should not be overridden by implementations. func (op *OperationBase) Stopped() bool { return op.stopped.IsSet() } // markStopped marks the operation as stopped. // It returns whether the stop flag was set. func (op *OperationBase) markStopped() bool { return op.stopped.SetToIf(false, true) } // Stop stops the operation by unregistering it from the terminal and calling HandleStop(). // Should not be overridden by implementations. func (op *OperationBase) Stop(self Operation, err *Error) { // Stop operation from terminal. op.terminal.StopOperation(self, err) } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. // Meant to be overridden by implementations. func (op *OperationBase) HandleStop(err *Error) (errorToSend *Error) { return err } // Terminal returns the terminal the operation is linked to. // Should not be overridden by implementations. func (op *OperationBase) Terminal() Terminal { return op.terminal } // OneOffOperationBase is an operation base for operations that just have one // message and a error return. type OneOffOperationBase struct { OperationBase Result chan *Error } // Init initializes the single operation base. func (op *OneOffOperationBase) Init() { op.Result = make(chan *Error, 1) } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. func (op *OneOffOperationBase) HandleStop(err *Error) (errorToSend *Error) { select { case op.Result <- err: default: } return err } // MessageStreamOperationBase is an operation base for receiving a message stream. // Every received message must be finished by the implementing operation. type MessageStreamOperationBase struct { OperationBase Delivered chan *Msg Ended chan *Error } // Init initializes the operation base. func (op *MessageStreamOperationBase) Init(deliverQueueSize int) { op.Delivered = make(chan *Msg, deliverQueueSize) op.Ended = make(chan *Error, 1) } // Deliver delivers data to the operation. func (op *MessageStreamOperationBase) Deliver(msg *Msg) *Error { select { case op.Delivered <- msg: return nil default: return ErrIncorrectUsage.With("request was not waiting for data") } } // HandleStop gives the operation the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Stop() instead. func (op *MessageStreamOperationBase) HandleStop(err *Error) (errorToSend *Error) { select { case op.Ended <- err: default: } return err } ================================================ FILE: spn/terminal/operation_counter.go ================================================ package terminal import ( "fmt" "sync" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service/mgr" "github.com/safing/structures/container" "github.com/safing/structures/dsd" "github.com/safing/structures/varint" ) // CounterOpType is the type ID for the Counter Operation. const CounterOpType string = "debug/count" // CounterOp sends increasing numbers on both sides. type CounterOp struct { //nolint:maligned OperationBase wg sync.WaitGroup server bool opts *CounterOpts counterLock sync.Mutex ClientCounter uint64 ServerCounter uint64 Error error } // CounterOpts holds the options for CounterOp. type CounterOpts struct { ClientCountTo uint64 ServerCountTo uint64 Wait time.Duration Flush bool suppressWorker bool } func init() { RegisterOpType(OperationFactory{ Type: CounterOpType, Start: startCounterOp, }) } // NewCounterOp returns a new CounterOp. func NewCounterOp(t Terminal, opts CounterOpts) (*CounterOp, *Error) { // Create operation. op := &CounterOp{ opts: &opts, } op.wg.Add(1) // Create argument container. data, err := dsd.Dump(op.opts, dsd.JSON) if err != nil { return nil, ErrInternalError.With("failed to pack options: %w", err) } // Initialize operation. tErr := t.StartOperation(op, container.New(data), 3*time.Second) if tErr != nil { return nil, tErr } // Start worker if needed. if op.getRemoteCounterTarget() > 0 && !op.opts.suppressWorker { module.mgr.Go("counter sender", op.CounterWorker) } return op, nil } func startCounterOp(t Terminal, opID uint32, data *container.Container) (Operation, *Error) { // Create operation. op := &CounterOp{ server: true, } op.InitOperationBase(t, opID) op.wg.Add(1) // Parse arguments. opts := &CounterOpts{} _, err := dsd.Load(data.CompileData(), opts) if err != nil { return nil, ErrInternalError.With("failed to unpack options: %w", err) } op.opts = opts // Start worker if needed. if op.getRemoteCounterTarget() > 0 { module.mgr.Go("counter sender", op.CounterWorker) } return op, nil } // Type returns the operation's type ID. func (op *CounterOp) Type() string { return CounterOpType } func (op *CounterOp) getCounter(sending, increase bool) uint64 { op.counterLock.Lock() defer op.counterLock.Unlock() // Use server counter, when op is server or for sending, but not when both. if op.server != sending { if increase { op.ServerCounter++ } return op.ServerCounter } if increase { op.ClientCounter++ } return op.ClientCounter } func (op *CounterOp) getRemoteCounterTarget() uint64 { if op.server { return op.opts.ClientCountTo } return op.opts.ServerCountTo } func (op *CounterOp) isDone() bool { op.counterLock.Lock() defer op.counterLock.Unlock() return op.ClientCounter >= op.opts.ClientCountTo && op.ServerCounter >= op.opts.ServerCountTo } // Deliver delivers data to the operation. func (op *CounterOp) Deliver(msg *Msg) *Error { defer msg.Finish() nextStep, err := msg.Data.GetNextN64() if err != nil { op.Stop(op, ErrMalformedData.With("failed to parse next number: %w", err)) return nil } // Count and compare. counter := op.getCounter(false, true) // Debugging: // if counter < 100 || // counter < 1000 && counter%100 == 0 || // counter < 10000 && counter%1000 == 0 || // counter < 100000 && counter%10000 == 0 || // counter < 1000000 && counter%100000 == 0 { // log.Errorf("spn/terminal: counter %s>%d recvd, now at %d", op.t.FmtID(), op.id, counter) // } if counter != nextStep { log.Warningf( "terminal: integrity of counter op violated: received %d, expected %d", nextStep, counter, ) op.Stop(op, ErrIntegrity.With("counters mismatched")) return nil } // Check if we are done. if op.isDone() { op.Stop(op, nil) } return nil } // HandleStop handles stopping the operation. func (op *CounterOp) HandleStop(err *Error) (errorToSend *Error) { // Check if counting finished. if !op.isDone() { err := fmt.Errorf( "counter op %d: did not finish counting (%d<-%d %d->%d)", op.id, op.opts.ClientCountTo, op.ClientCounter, op.ServerCounter, op.opts.ServerCountTo, ) op.Error = err } op.wg.Done() return err } // SendCounter sends the next counter. func (op *CounterOp) SendCounter() *Error { if op.Stopped() { return ErrStopping } // Increase sending counter. counter := op.getCounter(true, true) // Debugging: // if counter < 100 || // counter < 1000 && counter%100 == 0 || // counter < 10000 && counter%1000 == 0 || // counter < 100000 && counter%10000 == 0 || // counter < 1000000 && counter%100000 == 0 { // defer log.Errorf("spn/terminal: counter %s>%d sent, now at %d", op.t.FmtID(), op.id, counter) // } return op.Send(op.NewMsg(varint.Pack64(counter)), 3*time.Second) } // Wait waits for the Counter Op to finish. func (op *CounterOp) Wait() { op.wg.Wait() } // CounterWorker is a worker that sends counters. func (op *CounterOp) CounterWorker(ctx *mgr.WorkerCtx) error { for { // Send counter msg. err := op.SendCounter() switch err { case nil: // All good, continue. case ErrStopping: // Done! return nil default: // Something went wrong. err := fmt.Errorf("counter op %d: failed to send counter: %w", op.id, err) op.Error = err op.Stop(op, ErrInternalError.With(err.Error())) return nil } // Maybe flush message. if op.opts.Flush { op.terminal.Flush(1 * time.Second) } // Check if we are done with sending. if op.getCounter(true, false) >= op.getRemoteCounterTarget() { return nil } // Maybe wait a little. if op.opts.Wait > 0 { time.Sleep(op.opts.Wait) } } } ================================================ FILE: spn/terminal/permission.go ================================================ package terminal // Permission is a bit-map of granted permissions. type Permission uint16 // Permissions. const ( NoPermission Permission = 0x0 MayExpand Permission = 0x1 MayConnect Permission = 0x2 IsHubOwner Permission = 0x100 IsHubAdvisor Permission = 0x200 IsCraneController Permission = 0x8000 ) // AuthorizingTerminal is an interface for terminals that support authorization. type AuthorizingTerminal interface { GrantPermission(grant Permission) HasPermission(required Permission) bool } // GrantPermission grants the specified permissions to the Terminal. func (t *TerminalBase) GrantPermission(grant Permission) { t.lock.Lock() defer t.lock.Unlock() t.permission |= grant } // HasPermission returns if the Terminal has the specified permission. func (t *TerminalBase) HasPermission(required Permission) bool { t.lock.RLock() defer t.lock.RUnlock() return t.permission.Has(required) } // Has returns if the permission includes the specified permission. func (p Permission) Has(required Permission) bool { return p&required == required } // AddPermissions combines multiple permissions. func AddPermissions(perms ...Permission) Permission { var all Permission for _, p := range perms { all |= p } return all } ================================================ FILE: spn/terminal/rate_limit.go ================================================ package terminal import "time" // RateLimiter is a data flow rate limiter. type RateLimiter struct { maxBytesPerSlot uint64 slotBytes uint64 slotStarted time.Time } // NewRateLimiter returns a new rate limiter. // The given MBit/s are transformed to bytes, so giving a multiple of 8 is // advised for accurate results. func NewRateLimiter(mbits uint64) *RateLimiter { return &RateLimiter{ maxBytesPerSlot: (mbits / 8) * 1_000_000, slotStarted: time.Now(), } } // Limit is given the current transferred bytes and blocks until they may be sent. func (rl *RateLimiter) Limit(xferBytes uint64) { // Check if we need to limit transfer if we go over to max bytes per slot. if rl.slotBytes > rl.maxBytesPerSlot { // Wait if we are still within the slot. sinceSlotStart := time.Since(rl.slotStarted) if sinceSlotStart < time.Second { time.Sleep(time.Second - sinceSlotStart) } // Reset state for next slot. rl.slotBytes = 0 rl.slotStarted = time.Now() } // Add new bytes after checking, as first step over the limit is fully using the limit. rl.slotBytes += xferBytes } ================================================ FILE: spn/terminal/session.go ================================================ package terminal import ( "context" "fmt" "sync" "sync/atomic" "time" "github.com/safing/portmaster/base/log" ) const ( rateLimitMinOps = 250 rateLimitMaxOpsPerSecond = 5 rateLimitMinSuspicion = 25 rateLimitMinPermaSuspicion = rateLimitMinSuspicion * 100 rateLimitMaxSuspicionPerSecond = 1 // Make this big enough to trigger suspicion limit in first blast. concurrencyPoolSize = 30 ) // Session holds terminal metadata for operations. type Session struct { sync.RWMutex // Rate Limiting. // started holds the unix timestamp in seconds when the session was started. // It is set when the Session is created and may be treated as a constant. started int64 // opCount is the amount of operations started (and not rate limited by suspicion). opCount atomic.Int64 // suspicionScore holds a score of suspicious activity. // Every suspicious operations is counted as at least 1. // Rate limited operations because of suspicion are also counted as 1. suspicionScore atomic.Int64 concurrencyPool chan struct{} } // SessionTerminal is an interface for terminals that support authorization. type SessionTerminal interface { GetSession() *Session } // SessionAddOn can be inherited by terminals to add support for sessions. type SessionAddOn struct { lock sync.Mutex // session holds the terminal session. session *Session } // GetSession returns the terminal's session. func (t *SessionAddOn) GetSession() *Session { t.lock.Lock() defer t.lock.Unlock() // Create session if it does not exist. if t.session == nil { t.session = NewSession() } return t.session } // NewSession returns a new session. func NewSession() *Session { return &Session{ started: time.Now().Unix() - 1, // Ensure a 1 second difference to current time. concurrencyPool: make(chan struct{}, concurrencyPoolSize), } } // RateLimitInfo returns some basic information about the status of the rate limiter. func (s *Session) RateLimitInfo() string { secondsActive := time.Now().Unix() - s.started return fmt.Sprintf( "%do/s %ds/s %ds", s.opCount.Load()/secondsActive, s.suspicionScore.Load()/secondsActive, secondsActive, ) } // RateLimit enforces a rate and suspicion limit. func (s *Session) RateLimit() *Error { secondsActive := time.Now().Unix() - s.started // Check the suspicion limit. score := s.suspicionScore.Load() if score > rateLimitMinSuspicion { scorePerSecond := score / secondsActive if scorePerSecond >= rateLimitMaxSuspicionPerSecond { // Add current try to suspicion score. s.suspicionScore.Add(1) return ErrRateLimited } // Permanently rate limit if suspicion goes over the perma min limit and // the suspicion score is greater than 80% of the operation count. if score > rateLimitMinPermaSuspicion && score*5 > s.opCount.Load()*4 { // Think: 80*5 == 100*4 return ErrRateLimited } } // Check the rate limit. count := s.opCount.Add(1) if count > rateLimitMinOps { opsPerSecond := count / secondsActive if opsPerSecond >= rateLimitMaxOpsPerSecond { return ErrRateLimited } } return nil } // Suspicion Factors. const ( SusFactorCommon = 1 SusFactorWeirdButOK = 5 SusFactorQuiteUnusual = 10 SusFactorMustBeMalicious = 100 ) // ReportSuspiciousActivity reports suspicious activity of the terminal. func (s *Session) ReportSuspiciousActivity(factor int64) { s.suspicionScore.Add(factor) } // LimitConcurrency limits concurrent executions. // If over the limit, waiting goroutines are selected randomly. // It returns the context error if it was canceled. func (s *Session) LimitConcurrency(ctx context.Context, f func()) error { // Wait for place in pool. select { case <-ctx.Done(): return ctx.Err() case s.concurrencyPool <- struct{}{}: // We added our entry to the pool, continue with execution. } // Drain own spot if pool after execution. defer func() { select { case <-s.concurrencyPool: // Own entry drained. default: // This should never happen, but let's play safe and not deadlock when pool is empty. log.Warningf("spn/session: failed to drain own entry from concurrency pool") } }() // Execute and return. f() return nil } ================================================ FILE: spn/terminal/session_test.go ================================================ package terminal import ( "context" "sync" "testing" "time" "github.com/stretchr/testify/assert" ) func TestRateLimit(t *testing.T) { t.Parallel() var tErr *Error s := NewSession() // Everything should be okay within the min limit. for range rateLimitMinOps { tErr = s.RateLimit() if tErr != nil { t.Error("should not rate limit within min limit") } } // Somewhere here we should rate limiting. for range rateLimitMaxOpsPerSecond { tErr = s.RateLimit() } assert.ErrorIs(t, tErr, ErrRateLimited, "should rate limit") } func TestSuspicionLimit(t *testing.T) { t.Parallel() var tErr *Error s := NewSession() // Everything should be okay within the min limit. for range rateLimitMinSuspicion { tErr = s.RateLimit() if tErr != nil { t.Error("should not rate limit within min limit") } s.ReportSuspiciousActivity(SusFactorCommon) } // Somewhere here we should rate limiting. for range rateLimitMaxSuspicionPerSecond { s.ReportSuspiciousActivity(SusFactorCommon) tErr = s.RateLimit() } if tErr == nil { t.Error("should rate limit") } } func TestConcurrencyLimit(t *testing.T) { t.Parallel() s := NewSession() started := time.Now() wg := sync.WaitGroup{} workTime := 1 * time.Millisecond workers := concurrencyPoolSize * 10 // Start many workers to test concurrency. wg.Add(workers) for workerNum := range workers { go func() { defer func() { _ = recover() }() _ = s.LimitConcurrency(context.Background(), func() { time.Sleep(workTime) wg.Done() // Panic sometimes. if workerNum%concurrencyPoolSize == 0 { panic("test") } }) }() } // Wait and check time needed. wg.Wait() if time.Since(started) < (time.Duration(workers) * workTime / concurrencyPoolSize) { t.Errorf("workers were too quick - only took %s", time.Since(started)) } else { t.Logf("workers were correctly limited - took %s", time.Since(started)) } } ================================================ FILE: spn/terminal/terminal.go ================================================ package terminal import ( "context" "sync" "sync/atomic" "time" "github.com/tevino/abool" "github.com/safing/jess" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/rng" "github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/conf" "github.com/safing/structures/container" ) const ( timeoutTicks = 5 clientTerminalAbandonTimeout = 15 * time.Second serverTerminalAbandonTimeout = 5 * time.Minute ) // Terminal represents a terminal. type Terminal interface { //nolint:golint // Being explicit is helpful here. // ID returns the terminal ID. ID() uint32 // Ctx returns the terminal context. Ctx() context.Context // Deliver delivers a message to the terminal. // Should not be overridden by implementations. Deliver(msg *Msg) *Error // Send is used by others to send a message through the terminal. // Should not be overridden by implementations. Send(msg *Msg, timeout time.Duration) *Error // Flush sends all messages waiting in the terminal. // Should not be overridden by implementations. Flush(timeout time.Duration) // StartOperation starts the given operation by assigning it an ID and sending the given operation initialization data. // Should not be overridden by implementations. StartOperation(op Operation, initData *container.Container, timeout time.Duration) *Error // StopOperation stops the given operation. // Should not be overridden by implementations. StopOperation(op Operation, err *Error) // Abandon shuts down the terminal unregistering it from upstream and calling HandleAbandon(). // Should not be overridden by implementations. Abandon(err *Error) // HandleAbandon gives the terminal the ability to cleanly shut down. // The terminal is still fully functional at this point. // The returned error is the error to send to the other side. // Should never be called directly. Call Abandon() instead. // Meant to be overridden by implementations. HandleAbandon(err *Error) (errorToSend *Error) // HandleDestruction gives the terminal the ability to clean up. // The terminal has already fully shut down at this point. // Should never be called directly. Call Abandon() instead. // Meant to be overridden by implementations. HandleDestruction(err *Error) // FmtID formats the terminal ID (including parent IDs). // May be overridden by implementations. FmtID() string } // TerminalBase contains the basic functions of a terminal. type TerminalBase struct { //nolint:golint,maligned // Being explicit is helpful here. // TODO: Fix maligned. Terminal // Interface check. lock sync.RWMutex // id is the underlying id of the Terminal. id uint32 // parentID is the id of the parent component. parentID string // ext holds the extended terminal so that the base terminal can access custom functions. ext Terminal // sendQueue holds message to be sent. sendQueue chan *Msg // flowControl holds the flow control system. flowControl FlowControl // upstream represents the upstream (parent) terminal. upstream Upstream // deliverProxy is populated with the configured deliver function deliverProxy func(msg *Msg) *Error // recvProxy is populated with the configured recv function recvProxy func() <-chan *Msg // ctx is the context of the Terminal. ctx context.Context // cancelCtx cancels ctx. cancelCtx context.CancelFunc // waitForFlush signifies if sending should be delayed until the next call // to Flush() waitForFlush *abool.AtomicBool // flush is used to send a finish function to the handler, which will write // all pending messages and then call the received function. flush chan func() // idleTicker ticks for increasing and checking the idle counter. idleTicker *time.Ticker // idleCounter counts the ticks the terminal has been idle. idleCounter *uint32 // jession is the jess session used for encryption. jession *jess.Session // jessionLock locks jession. jessionLock sync.Mutex // encryptionReady is set when the encryption is ready for sending messages. encryptionReady chan struct{} // identity is the identity used by a remote Terminal. identity *cabin.Identity // operations holds references to all active operations that require persistence. operations map[uint32]Operation // nextOpID holds the next operation ID. nextOpID *uint32 // permission holds the permissions of the terminal. permission Permission // opts holds the terminal options. It must not be modified after the terminal // has started. opts *TerminalOpts // lastUnknownOpID holds the operation ID of the last data message received // for an unknown operation ID. lastUnknownOpID uint32 // lastUnknownOpMsgs holds the amount of continuous data messages received // for the operation ID in lastUnknownOpID. lastUnknownOpMsgs uint32 // Abandoning indicates if the Terminal is being abandoned. The main handlers // will keep running until the context has been canceled by the abandon // procedure. // No new operations should be started. // Whoever initiates the abandoning must also start the abandon procedure. Abandoning *abool.AtomicBool } func createTerminalBase( ctx context.Context, id uint32, parentID string, remote bool, initMsg *TerminalOpts, upstream Upstream, ) (*TerminalBase, *Error) { t := &TerminalBase{ id: id, parentID: parentID, sendQueue: make(chan *Msg), upstream: upstream, waitForFlush: abool.New(), flush: make(chan func()), idleTicker: time.NewTicker(time.Minute), idleCounter: new(uint32), encryptionReady: make(chan struct{}), operations: make(map[uint32]Operation), nextOpID: new(uint32), opts: initMsg, Abandoning: abool.New(), } // Stop ticking to disable timeout. t.idleTicker.Stop() // Shift next operation ID if remote. if remote { atomic.AddUint32(t.nextOpID, 4) } // Create context. t.ctx, t.cancelCtx = context.WithCancel(ctx) // Create flow control. switch initMsg.FlowControl { case FlowControlDFQ: t.flowControl = NewDuplexFlowQueue(t.Ctx(), initMsg.FlowControlSize, t.submitToUpstream) t.deliverProxy = t.flowControl.Deliver t.recvProxy = t.flowControl.Receive case FlowControlNone: deliver := make(chan *Msg, initMsg.FlowControlSize) t.deliverProxy = MakeDirectDeliveryDeliverFunc(ctx, deliver) t.recvProxy = MakeDirectDeliveryRecvFunc(deliver) case FlowControlDefault: fallthrough default: return nil, ErrInternalError.With("unknown flow control type %d", initMsg.FlowControl) } return t, nil } // ID returns the Terminal's ID. func (t *TerminalBase) ID() uint32 { return t.id } // Ctx returns the Terminal's context. func (t *TerminalBase) Ctx() context.Context { return t.ctx } // SetTerminalExtension sets the Terminal's extension. This function is not // guarded and may only be used during initialization. func (t *TerminalBase) SetTerminalExtension(ext Terminal) { t.ext = ext } // SetTimeout sets the Terminal's idle timeout duration. // It is broken down into slots internally. func (t *TerminalBase) SetTimeout(d time.Duration) { t.idleTicker.Reset(d / timeoutTicks) } // Deliver on TerminalBase only exists to conform to the interface. It must be // overridden by an actual implementation. func (t *TerminalBase) Deliver(msg *Msg) *Error { // Deliver via configured proxy. err := t.deliverProxy(msg) if err != nil { msg.Finish() } return err } // StartWorkers starts the necessary workers to operate the Terminal. func (t *TerminalBase) StartWorkers(m *mgr.Manager, terminalName string) { // Start terminal workers. m.Go(terminalName+" handler", t.Handler) m.Go(terminalName+" sender", t.Sender) // Start any flow control workers. if t.flowControl != nil { t.flowControl.StartWorkers(m, terminalName) } } const ( sendThresholdLength = 100 // bytes sendMaxLength = 4000 // bytes sendThresholdMaxWait = 20 * time.Millisecond ) // Handler receives and handles messages and must be started as a worker in the // module where the Terminal is used. func (t *TerminalBase) Handler(_ *mgr.WorkerCtx) error { defer t.Abandon(ErrInternalError.With("handler died")) var msg *Msg defer msg.Finish() for { select { case <-t.ctx.Done(): // Call Abandon just in case. // Normally, only the StopProcedure function should cancel the context. t.Abandon(nil) return nil // Controlled worker exit. case <-t.idleTicker.C: // If nothing happens for a while, end the session. if atomic.AddUint32(t.idleCounter, 1) > timeoutTicks { // Abandon the terminal and reset the counter. t.Abandon(ErrNoActivity) atomic.StoreUint32(t.idleCounter, 0) } case msg = <-t.recvProxy(): err := t.handleReceive(msg) if err != nil { t.Abandon(err.Wrap("failed to handle")) return nil } // Register activity. atomic.StoreUint32(t.idleCounter, 0) } } } // submit is used to send message from the terminal to upstream, including // going through flow control, if configured. // This function should be used to send message from the terminal to upstream. func (t *TerminalBase) submit(msg *Msg, timeout time.Duration) { // Submit directly if no flow control is configured. if t.flowControl == nil { t.submitToUpstream(msg, timeout) return } // Hand over to flow control. err := t.flowControl.Send(msg, timeout) if err != nil { msg.Finish() t.Abandon(err.Wrap("failed to submit to flow control")) } } // submitToUpstream is used to directly submit messages to upstream. // This function should only be used by the flow control or submit function. func (t *TerminalBase) submitToUpstream(msg *Msg, timeout time.Duration) { // Add terminal ID as flow ID. msg.FlowID = t.ID() // Debug unit leaks. msg.debugWithCaller(2) // Submit to upstream. err := t.upstream.Send(msg, timeout) if err != nil { msg.Finish() t.Abandon(err.Wrap("failed to submit to upstream")) } } // Sender handles sending messages and must be started as a worker in the // module where the Terminal is used. func (t *TerminalBase) Sender(_ *mgr.WorkerCtx) error { // Don't send messages, if the encryption is net yet set up. // The server encryption session is only initialized with the first // operative message, not on Terminal creation. if t.opts.Encrypt { select { case <-t.ctx.Done(): // Call Abandon just in case. // Normally, the only the StopProcedure function should cancel the context. t.Abandon(nil) return nil // Controlled worker exit. case <-t.encryptionReady: } } // Be sure to call Stop even in case of sudden death. defer t.Abandon(ErrInternalError.With("sender died")) var msgBufferMsg *Msg var msgBufferLen int var msgBufferLimitReached bool var sendMsgs bool var sendMaxWait *time.Timer var flushFinished func() // Finish any current unit when returning. defer msgBufferMsg.Finish() // Only receive message when not sending the current msg buffer. sendQueueOpMsgs := func() <-chan *Msg { // Don't handle more messages, if the buffer is full. if msgBufferLimitReached { return nil } return t.sendQueue } // Only wait for sending slot when the current msg buffer is ready to be sent. readyToSend := func() <-chan struct{} { switch { case !sendMsgs: // Wait until there is something to send. return nil case t.flowControl != nil: // Let flow control decide when we are ready. return t.flowControl.ReadyToSend() default: // Always ready. return ready } } // Calculate current max wait time to send the msg buffer. getSendMaxWait := func() <-chan time.Time { if sendMaxWait != nil { return sendMaxWait.C } return nil } handling: for { select { case <-t.ctx.Done(): // Call Stop just in case. // Normally, the only the StopProcedure function should cancel the context. t.Abandon(nil) return nil // Controlled worker exit. case <-t.idleTicker.C: // If nothing happens for a while, end the session. if atomic.AddUint32(t.idleCounter, 1) > timeoutTicks { // Abandon the terminal and reset the counter. t.Abandon(ErrNoActivity) atomic.StoreUint32(t.idleCounter, 0) } case msg := <-sendQueueOpMsgs(): if msg == nil { continue handling } // Add unit to buffer unit, or use it as new buffer. if msgBufferMsg != nil { // Pack, append and finish additional message. msgBufferMsg.Consume(msg) } else { // Pack operation message. msg.Pack() // Convert to message of terminal. msgBufferMsg = msg msgBufferMsg.FlowID = t.ID() msgBufferMsg.Type = MsgTypeData } msgBufferLen += msg.Data.Length() // Check if there is enough data to hit the sending threshold. if msgBufferLen >= sendThresholdLength { sendMsgs = true } else if sendMaxWait == nil && t.waitForFlush.IsNotSet() { sendMaxWait = time.NewTimer(sendThresholdMaxWait) } // Check if we have reached the maximum buffer size. if msgBufferLen >= sendMaxLength { msgBufferLimitReached = true } // Register activity. atomic.StoreUint32(t.idleCounter, 0) case <-getSendMaxWait(): // The timer for waiting for more data has ended. // Send all available data if not forced to wait for a flush. if t.waitForFlush.IsNotSet() { sendMsgs = true } case newFlushFinishedFn := <-t.flush: // We are flushing - stop waiting. t.waitForFlush.UnSet() // Signal immediately if msg buffer is empty. if msgBufferLen == 0 { newFlushFinishedFn() } else { // If there already is a flush finished function, stack them. if flushFinished != nil { stackedFlushFinishFn := flushFinished flushFinished = func() { stackedFlushFinishFn() newFlushFinishedFn() } } else { flushFinished = newFlushFinishedFn } } // Force sending data now. sendMsgs = true case <-readyToSend(): // Reset sending flags. sendMsgs = false msgBufferLimitReached = false // Send if there is anything to send. var err *Error if msgBufferLen > 0 { // Update message type to include priority. if msgBufferMsg.Type == MsgTypeData && msgBufferMsg.Unit.IsHighPriority() && t.opts.UsePriorityDataMsgs { msgBufferMsg.Type = MsgTypePriorityData } // Wait for clearance on initial msg only. msgBufferMsg.Unit.WaitForSlot() err = t.sendOpMsgs(msgBufferMsg) } // Reset buffer. msgBufferMsg = nil msgBufferLen = 0 // Reset send wait timer. if sendMaxWait != nil { sendMaxWait.Stop() sendMaxWait = nil } // Check if we are flushing and need to notify. if flushFinished != nil { flushFinished() flushFinished = nil } // Handle error after state updates. if err != nil { t.Abandon(err.With("failed to send")) continue handling } } } } // WaitForFlush makes the terminal pause all sending until the next call to // Flush(). func (t *TerminalBase) WaitForFlush() { t.waitForFlush.Set() } // Flush sends all data waiting to be sent. func (t *TerminalBase) Flush(timeout time.Duration) { // Create channel and function for notifying. wait := make(chan struct{}) finished := func() { close(wait) } // Request flush and return when stopping. select { case t.flush <- finished: case <-t.Ctx().Done(): return case <-TimedOut(timeout): return } // Wait for flush to finish and return when stopping. select { case <-wait: case <-t.Ctx().Done(): return case <-TimedOut(timeout): return } // Flush flow control, if configured. if t.flowControl != nil { t.flowControl.Flush(timeout) } } func (t *TerminalBase) encrypt(c *container.Container) (*container.Container, *Error) { if !t.opts.Encrypt { return c, nil } t.jessionLock.Lock() defer t.jessionLock.Unlock() letter, err := t.jession.Close(c.CompileData()) if err != nil { return nil, ErrIntegrity.With("failed to encrypt: %w", err) } encryptedData, err := letter.ToWire() if err != nil { return nil, ErrInternalError.With("failed to pack letter: %w", err) } return encryptedData, nil } func (t *TerminalBase) decrypt(c *container.Container) (*container.Container, *Error) { if !t.opts.Encrypt { return c, nil } t.jessionLock.Lock() defer t.jessionLock.Unlock() letter, err := jess.LetterFromWire(c) if err != nil { return nil, ErrMalformedData.With("failed to parse letter: %w", err) } // Setup encryption if not yet done. if t.jession == nil { if t.identity == nil { return nil, ErrInternalError.With("missing identity for setting up incoming encryption") } // Create jess session. t.jession, err = letter.WireCorrespondence(t.identity) if err != nil { return nil, ErrIntegrity.With("failed to initialize incoming encryption: %w", err) } // Don't need that anymore. t.identity = nil // Encryption is ready for sending. close(t.encryptionReady) } decryptedData, err := t.jession.Open(letter) if err != nil { return nil, ErrIntegrity.With("failed to decrypt: %w", err) } return container.New(decryptedData), nil } func (t *TerminalBase) handleReceive(msg *Msg) *Error { msg.Unit.WaitForSlot() defer msg.Finish() // Debugging: // log.Errorf("spn/terminal %s handling tmsg: %s", t.FmtID(), spew.Sdump(c.CompileData())) // Check if message is empty. This will be the case if a message was only // for updated the available space of the flow queue. if !msg.Data.HoldsData() { return nil } // Decrypt if enabled. var tErr *Error msg.Data, tErr = t.decrypt(msg.Data) if tErr != nil { return tErr } // Handle operation messages. for msg.Data.HoldsData() { // Get next message length. msgLength, err := msg.Data.GetNextN32() if err != nil { return ErrMalformedData.With("failed to get operation msg length: %w", err) } if msgLength == 0 { // Remainder is padding. // Padding can only be at the end of the segment. t.handlePaddingMsg(msg.Data) return nil } // Get op msg data. msgData, err := msg.Data.GetAsContainer(int(msgLength)) if err != nil { return ErrMalformedData.With("failed to get operation msg data (%d/%d bytes): %w", msg.Data.Length(), msgLength, err) } // Handle op msg. if handleErr := t.handleOpMsg(msgData); handleErr != nil { return handleErr } } return nil } func (t *TerminalBase) handleOpMsg(data *container.Container) *Error { // Debugging: // log.Errorf("spn/terminal %s handling opmsg: %s", t.FmtID(), spew.Sdump(data.CompileData())) // Parse message operation id, type. opID, msgType, err := ParseIDType(data) if err != nil { return ErrMalformedData.With("failed to parse operation msg id/type: %w", err) } switch msgType { case MsgTypeInit: t.handleOperationStart(opID, data) case MsgTypeData, MsgTypePriorityData: op, ok := t.GetActiveOp(opID) if ok && !op.Stopped() { // Create message from data. msg := NewEmptyMsg() msg.FlowID = opID msg.Type = msgType msg.Data = data if msg.Type == MsgTypePriorityData { msg.Unit.MakeHighPriority() } // Deliver message to operation. tErr := op.Deliver(msg) if tErr != nil { // Also stop on "success" errors! msg.Finish() t.StopOperation(op, tErr) } return nil } // If an active op is not found, this is likely just left-overs from a // stopped or failed operation. // log.Tracef("spn/terminal: %s received data msg for unknown op %d", fmtTerminalID(t.parentID, t.id), opID) // Send a stop error if this happens too often. if opID == t.lastUnknownOpID { // OpID is the same as last time. t.lastUnknownOpMsgs++ // Log an warning (via StopOperation) and send a stop message every thousand. if t.lastUnknownOpMsgs%1000 == 0 { t.StopOperation(newUnknownOp(opID, ""), ErrUnknownOperationID.With("received %d unsolicited data msgs", t.lastUnknownOpMsgs)) } // TODO: Abandon terminal at over 10000? } else { // OpID changed, set new ID and reset counter. t.lastUnknownOpID = opID t.lastUnknownOpMsgs = 1 } case MsgTypeStop: // Parse received error. opErr, parseErr := ParseExternalError(data.CompileData()) if parseErr != nil { log.Warningf("spn/terminal: %s failed to parse stop error: %s", fmtTerminalID(t.parentID, t.id), parseErr) opErr = ErrUnknownError.AsExternal() } // End operation. op, ok := t.GetActiveOp(opID) if ok { t.StopOperation(op, opErr) } else { log.Tracef("spn/terminal: %s received stop msg for unknown op %d", fmtTerminalID(t.parentID, t.id), opID) } default: log.Warningf("spn/terminal: %s received unexpected message type: %d", t.FmtID(), msgType) return ErrUnexpectedMsgType } return nil } func (t *TerminalBase) handlePaddingMsg(c *container.Container) { padding := c.GetAll() if len(padding) > 0 { rngFeeder.SupplyEntropyIfNeeded(padding, len(padding)) } } func (t *TerminalBase) sendOpMsgs(msg *Msg) *Error { msg.Unit.WaitForSlot() // Add Padding if needed. if t.opts.Padding > 0 { paddingNeeded := (int(t.opts.Padding) - msg.Data.Length()) % int(t.opts.Padding) if paddingNeeded > 0 { // Add padding message header. msg.Data.Append([]byte{0}) paddingNeeded-- // Add needed padding data. if paddingNeeded > 0 { padding, err := rng.Bytes(paddingNeeded) if err != nil { log.Debugf("spn/terminal: %s failed to get random data, using zeros instead", t.FmtID()) padding = make([]byte, paddingNeeded) } msg.Data.Append(padding) } } } // Encrypt operative data. var tErr *Error msg.Data, tErr = t.encrypt(msg.Data) if tErr != nil { return tErr } // Send data. t.submit(msg, 0) return nil } // Abandon shuts down the terminal unregistering it from upstream and calling HandleAbandon(). // Should not be overridden by implementations. func (t *TerminalBase) Abandon(err *Error) { if t.Abandoning.SetToIf(false, true) { module.mgr.Go("terminal abandon procedure", func(_ *mgr.WorkerCtx) error { t.handleAbandonProcedure(err) return nil }) } } // HandleAbandon gives the terminal the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Abandon() instead. // Meant to be overridden by implementations. func (t *TerminalBase) HandleAbandon(err *Error) (errorToSend *Error) { return err } // HandleDestruction gives the terminal the ability to clean up. // The terminal has already fully shut down at this point. // Should never be called directly. Call Abandon() instead. // Meant to be overridden by implementations. func (t *TerminalBase) HandleDestruction(err *Error) {} func (t *TerminalBase) handleAbandonProcedure(err *Error) { // End all operations. for _, op := range t.allOps() { t.StopOperation(op, nil) } // Prepare timeouts for waiting for ops. timeout := clientTerminalAbandonTimeout if conf.PublicHub() { timeout = serverTerminalAbandonTimeout } checkTicker := time.NewTicker(50 * time.Millisecond) defer checkTicker.Stop() abortWaiting := time.After(timeout) // Wait for all operations to end. waitForOps: for { select { case <-checkTicker.C: if t.GetActiveOpCount() <= 0 { break waitForOps } case <-abortWaiting: log.Warningf( "spn/terminal: terminal %s is continuing shutdown with %d active operations", t.FmtID(), t.GetActiveOpCount(), ) break waitForOps } } // Call operation stop handle function for proper shutdown cleaning up. if t.ext != nil { err = t.ext.HandleAbandon(err) } // Send error to the connected Operation, if the error is internal. if !err.IsExternal() { if err == nil { err = ErrStopping } msg := NewMsg(err.Pack()) msg.FlowID = t.ID() msg.Type = MsgTypeStop t.submit(msg, 1*time.Second) } // If terminal was ended locally, send all data before abandoning. // If terminal was ended remotely, don't bother sending remaining data. if !err.IsExternal() { // Flushing could mean sending a full buffer of 50000 packets. t.Flush(5 * time.Minute) } // Stop all other connected workers. t.cancelCtx() t.idleTicker.Stop() // Call operation destruction handle function for proper shutdown cleaning up. if t.ext != nil { t.ext.HandleDestruction(err) } } func (t *TerminalBase) allOps() []Operation { t.lock.Lock() defer t.lock.Unlock() ops := make([]Operation, 0, len(t.operations)) for _, op := range t.operations { ops = append(ops, op) } return ops } // MakeDirectDeliveryDeliverFunc creates a submit upstream function with the // given delivery channel. func MakeDirectDeliveryDeliverFunc( ctx context.Context, deliver chan *Msg, ) func(c *Msg) *Error { return func(c *Msg) *Error { select { case deliver <- c: return nil case <-ctx.Done(): return ErrStopping } } } // MakeDirectDeliveryRecvFunc makes a delivery receive function with the given // delivery channel. func MakeDirectDeliveryRecvFunc( deliver chan *Msg, ) func() <-chan *Msg { return func() <-chan *Msg { return deliver } } ================================================ FILE: spn/terminal/terminal_test.go ================================================ package terminal import ( "fmt" "os" "runtime/pprof" "sync/atomic" "testing" "time" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/hub" "github.com/safing/structures/container" ) func TestTerminals(t *testing.T) { t.Parallel() identity, erro := cabin.CreateIdentity(module.mgr.Ctx(), "test") if erro != nil { t.Fatalf("failed to create identity: %s", erro) } // Test without and with encryption. for _, encrypt := range []bool{false, true} { // Test with different flow controls. for _, fc := range []struct { flowControl FlowControlType flowControlSize uint32 }{ { flowControl: FlowControlNone, flowControlSize: 5, }, { flowControl: FlowControlDFQ, flowControlSize: defaultTestQueueSize, }, } { // Run tests with combined options. testTerminals(t, identity, &TerminalOpts{ Encrypt: encrypt, Padding: defaultTestPadding, FlowControl: fc.flowControl, FlowControlSize: fc.flowControlSize, }) } } } func testTerminals(t *testing.T, identity *cabin.Identity, terminalOpts *TerminalOpts) { t.Helper() // Prepare encryption. var dstHub *hub.Hub if terminalOpts.Encrypt { dstHub = identity.Hub } else { identity = nil } // Create test terminals. var term1 *TestTerminal var term2 *TestTerminal var initData *container.Container var err *Error term1, initData, err = NewLocalTestTerminal( module.mgr.Ctx(), 127, "c1", dstHub, terminalOpts, createForwardingUpstream( t, "c1", "c2", func(msg *Msg) *Error { return term2.Deliver(msg) }, ), ) if err != nil { t.Fatalf("failed to create local terminal: %s", err) } term2, _, err = NewRemoteTestTerminal( module.mgr.Ctx(), 127, "c2", identity, initData, createForwardingUpstream( t, "c2", "c1", func(msg *Msg) *Error { return term1.Deliver(msg) }, ), ) if err != nil { t.Fatalf("failed to create remote terminal: %s", err) } // Start testing with counters. countToQueueSize := uint64(terminalOpts.FlowControlSize) optionsSuffix := fmt.Sprintf( "encrypt=%v,flowType=%d", terminalOpts.Encrypt, terminalOpts.FlowControl, ) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "onlyup-flushing-waiting:" + optionsSuffix, flush: true, serverCountTo: countToQueueSize * 2, waitBetweenMsgs: sendThresholdMaxWait * 2, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "onlyup-waiting:" + optionsSuffix, serverCountTo: 10, waitBetweenMsgs: sendThresholdMaxWait * 2, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "onlyup-flushing:" + optionsSuffix, flush: true, serverCountTo: countToQueueSize * 2, waitBetweenMsgs: time.Millisecond, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "onlyup:" + optionsSuffix, serverCountTo: countToQueueSize * 2, waitBetweenMsgs: time.Millisecond, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "onlydown-flushing-waiting:" + optionsSuffix, flush: true, clientCountTo: countToQueueSize * 2, waitBetweenMsgs: sendThresholdMaxWait * 2, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "onlydown-waiting:" + optionsSuffix, clientCountTo: 10, waitBetweenMsgs: sendThresholdMaxWait * 2, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "onlydown-flushing:" + optionsSuffix, flush: true, clientCountTo: countToQueueSize * 2, waitBetweenMsgs: time.Millisecond, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "onlydown:" + optionsSuffix, clientCountTo: countToQueueSize * 2, waitBetweenMsgs: time.Millisecond, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "twoway-flushing-waiting:" + optionsSuffix, flush: true, clientCountTo: countToQueueSize * 2, serverCountTo: countToQueueSize * 2, waitBetweenMsgs: sendThresholdMaxWait * 2, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "twoway-waiting:" + optionsSuffix, flush: true, clientCountTo: 10, serverCountTo: 10, waitBetweenMsgs: sendThresholdMaxWait * 2, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "twoway-flushing:" + optionsSuffix, flush: true, clientCountTo: countToQueueSize * 2, serverCountTo: countToQueueSize * 2, waitBetweenMsgs: time.Millisecond, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "twoway:" + optionsSuffix, clientCountTo: countToQueueSize * 2, serverCountTo: countToQueueSize * 2, waitBetweenMsgs: time.Millisecond, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "stresstest-down:" + optionsSuffix, clientCountTo: countToQueueSize * 1000, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "stresstest-up:" + optionsSuffix, serverCountTo: countToQueueSize * 1000, }) testTerminalWithCounters(t, term1, term2, &testWithCounterOpts{ testName: "stresstest-duplex:" + optionsSuffix, clientCountTo: countToQueueSize * 1000, serverCountTo: countToQueueSize * 1000, }) // Clean up. term1.Abandon(nil) term2.Abandon(nil) // Give some time for the last log messages and clean up. time.Sleep(100 * time.Millisecond) } func createForwardingUpstream(t *testing.T, srcName, dstName string, deliverFunc func(*Msg) *Error) Upstream { t.Helper() return UpstreamSendFunc(func(msg *Msg, _ time.Duration) *Error { // Fast track nil containers. if msg == nil { dErr := deliverFunc(msg) if dErr != nil { t.Errorf("%s>%s: failed to deliver nil msg to terminal: %s", srcName, dstName, dErr) return dErr.With("failed to deliver nil msg to terminal") } return nil } // Log messages. if logTestCraneMsgs { t.Logf("%s>%s: %v\n", srcName, dstName, msg.Data.CompileData()) } // Deliver to other terminal. dErr := deliverFunc(msg) if dErr != nil { t.Errorf("%s>%s: failed to deliver to terminal: %s", srcName, dstName, dErr) return dErr.With("failed to deliver to terminal") } return nil }) } type testWithCounterOpts struct { testName string flush bool clientCountTo uint64 serverCountTo uint64 waitBetweenMsgs time.Duration } func testTerminalWithCounters(t *testing.T, term1, term2 *TestTerminal, opts *testWithCounterOpts) { t.Helper() // Wait async for test to complete, print stack after timeout. finished := make(chan struct{}) maxTestDuration := 60 * time.Second go func() { select { case <-finished: case <-time.After(maxTestDuration): fmt.Printf("terminal test %s is taking more than %s, printing stack:\n", opts.testName, maxTestDuration) _ = pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) os.Exit(1) } }() t.Logf("starting terminal counter test %s", opts.testName) defer t.Logf("stopping terminal counter test %s", opts.testName) // Start counters. counter, tErr := NewCounterOp(term1, CounterOpts{ ClientCountTo: opts.clientCountTo, ServerCountTo: opts.serverCountTo, Flush: opts.flush, Wait: opts.waitBetweenMsgs, }) if tErr != nil { t.Fatalf("terminal test %s failed to start counter: %s", opts.testName, tErr) } // Wait until counters are done. counter.Wait() close(finished) // Check for error. if counter.Error != nil { t.Fatalf("terminal test %s failed to count: %s", opts.testName, counter.Error) } // Log stats. printCTStats(t, opts.testName, "term1", term1) printCTStats(t, opts.testName, "term2", term2) // Check if stats match, if DFQ is used on both sides. dfq1, ok1 := term1.flowControl.(*DuplexFlowQueue) dfq2, ok2 := term2.flowControl.(*DuplexFlowQueue) if ok1 && ok2 && (atomic.LoadInt32(dfq1.sendSpace) != atomic.LoadInt32(dfq2.reportedSpace) || atomic.LoadInt32(dfq2.sendSpace) != atomic.LoadInt32(dfq1.reportedSpace)) { t.Fatalf("terminal test %s has non-matching space counters", opts.testName) } } func printCTStats(t *testing.T, testName, name string, term *TestTerminal) { t.Helper() dfq, ok := term.flowControl.(*DuplexFlowQueue) if !ok { return } t.Logf( "%s: %s: sq=%d rq=%d sends=%d reps=%d", testName, name, len(dfq.sendQueue), len(dfq.recvQueue), atomic.LoadInt32(dfq.sendSpace), atomic.LoadInt32(dfq.reportedSpace), ) } ================================================ FILE: spn/terminal/testing.go ================================================ package terminal import ( "context" "time" "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/hub" "github.com/safing/structures/container" ) const ( defaultTestQueueSize = 16 defaultTestPadding = 8 logTestCraneMsgs = false ) // TestTerminal is a terminal for running tests. type TestTerminal struct { *TerminalBase } // NewLocalTestTerminal returns a new local test terminal. func NewLocalTestTerminal( ctx context.Context, id uint32, parentID string, remoteHub *hub.Hub, initMsg *TerminalOpts, upstream Upstream, ) (*TestTerminal, *container.Container, *Error) { // Create Terminal Base. t, initData, err := NewLocalBaseTerminal(ctx, id, parentID, remoteHub, initMsg, upstream) if err != nil { return nil, nil, err } t.StartWorkers(module.mgr, "test terminal") return &TestTerminal{t}, initData, nil } // NewRemoteTestTerminal returns a new remote test terminal. func NewRemoteTestTerminal( ctx context.Context, id uint32, parentID string, identity *cabin.Identity, initData *container.Container, upstream Upstream, ) (*TestTerminal, *TerminalOpts, *Error) { // Create Terminal Base. t, initMsg, err := NewRemoteBaseTerminal(ctx, id, parentID, identity, initData, upstream) if err != nil { return nil, nil, err } t.StartWorkers(module.mgr, "test terminal") return &TestTerminal{t}, initMsg, nil } type delayedMsg struct { msg *Msg timeout time.Duration delayUntil time.Time } func createDelayingTestForwardingFunc( srcName, dstName string, delay time.Duration, delayQueueSize int, deliverFunc func(msg *Msg, timeout time.Duration) *Error, ) func(msg *Msg, timeout time.Duration) *Error { // Return simple forward func if no delay is given. if delay == 0 { return func(msg *Msg, timeout time.Duration) *Error { // Deliver to other terminal. dErr := deliverFunc(msg, timeout) if dErr != nil { log.Errorf("spn/testing: %s>%s: failed to deliver to terminal: %s", srcName, dstName, dErr) return dErr } return nil } } // If there is delay, create a delaying channel and handler. delayedMsgs := make(chan *delayedMsg, delayQueueSize) go func() { for { // Read from chan msg := <-delayedMsgs if msg == nil { return } // Check if we need to wait. waitFor := time.Until(msg.delayUntil) if waitFor > 0 { time.Sleep(waitFor) } // Deliver to other terminal. dErr := deliverFunc(msg.msg, msg.timeout) if dErr != nil { log.Errorf("spn/testing: %s>%s: failed to deliver to terminal: %s", srcName, dstName, dErr) } } }() return func(msg *Msg, timeout time.Duration) *Error { // Add msg to delaying msg channel. delayedMsgs <- &delayedMsg{ msg: msg, timeout: timeout, delayUntil: time.Now().Add(delay), } return nil } } // HandleAbandon gives the terminal the ability to cleanly shut down. // The returned error is the error to send to the other side. // Should never be called directly. Call Abandon() instead. func (t *TestTerminal) HandleAbandon(err *Error) (errorToSend *Error) { switch err { case nil: // nil means that the Terminal is being shutdown by the owner. log.Tracef("spn/terminal: %s is closing", fmtTerminalID(t.parentID, t.id)) default: // All other errors are faults. log.Warningf("spn/terminal: %s: %s", fmtTerminalID(t.parentID, t.id), err) } return } // NewSimpleTestTerminalPair provides a simple connected terminal pair for tests. func NewSimpleTestTerminalPair(delay time.Duration, delayQueueSize int, opts *TerminalOpts) (a, b *TestTerminal, err error) { if opts == nil { opts = &TerminalOpts{ Padding: defaultTestPadding, FlowControl: FlowControlDFQ, FlowControlSize: defaultTestQueueSize, } } var initData *container.Container var tErr *Error a, initData, tErr = NewLocalTestTerminal( module.mgr.Ctx(), 127, "a", nil, opts, UpstreamSendFunc(createDelayingTestForwardingFunc( "a", "b", delay, delayQueueSize, func(msg *Msg, timeout time.Duration) *Error { return b.Deliver(msg) }, )), ) if tErr != nil { return nil, nil, tErr.Wrap("failed to create local test terminal") } b, _, tErr = NewRemoteTestTerminal( module.mgr.Ctx(), 127, "b", nil, initData, UpstreamSendFunc(createDelayingTestForwardingFunc( "b", "a", delay, delayQueueSize, func(msg *Msg, timeout time.Duration) *Error { return a.Deliver(msg) }, )), ) if tErr != nil { return nil, nil, tErr.Wrap("failed to create remote test terminal") } return a, b, nil } // BareTerminal is a bare terminal that just returns errors for testing. type BareTerminal struct{} var ( _ Terminal = &BareTerminal{} errNotImplementedByBareTerminal = ErrInternalError.With("not implemented by bare terminal") ) // ID returns the terminal ID. func (t *BareTerminal) ID() uint32 { return 0 } // Ctx returns the terminal context. func (t *BareTerminal) Ctx() context.Context { return context.Background() } // Deliver delivers a message to the terminal. // Should not be overridden by implementations. func (t *BareTerminal) Deliver(msg *Msg) *Error { return errNotImplementedByBareTerminal } // Send is used by others to send a message through the terminal. // Should not be overridden by implementations. func (t *BareTerminal) Send(msg *Msg, timeout time.Duration) *Error { return errNotImplementedByBareTerminal } // Flush sends all messages waiting in the terminal. // Should not be overridden by implementations. func (t *BareTerminal) Flush(timeout time.Duration) {} // StartOperation starts the given operation by assigning it an ID and sending the given operation initialization data. // Should not be overridden by implementations. func (t *BareTerminal) StartOperation(op Operation, initData *container.Container, timeout time.Duration) *Error { return errNotImplementedByBareTerminal } // StopOperation stops the given operation. // Should not be overridden by implementations. func (t *BareTerminal) StopOperation(op Operation, err *Error) {} // Abandon shuts down the terminal unregistering it from upstream and calling HandleAbandon(). // Should not be overridden by implementations. func (t *BareTerminal) Abandon(err *Error) {} // HandleAbandon gives the terminal the ability to cleanly shut down. // The terminal is still fully functional at this point. // The returned error is the error to send to the other side. // Should never be called directly. Call Abandon() instead. // Meant to be overridden by implementations. func (t *BareTerminal) HandleAbandon(err *Error) (errorToSend *Error) { return err } // HandleDestruction gives the terminal the ability to clean up. // The terminal has already fully shut down at this point. // Should never be called directly. Call Abandon() instead. // Meant to be overridden by implementations. func (t *BareTerminal) HandleDestruction(err *Error) {} // FmtID formats the terminal ID (including parent IDs). // May be overridden by implementations. func (t *BareTerminal) FmtID() string { return "bare" } ================================================ FILE: spn/terminal/upstream.go ================================================ package terminal import "time" // Upstream defines the interface for upstream (parent) components. type Upstream interface { Send(msg *Msg, timeout time.Duration) *Error } // UpstreamSendFunc is a helper to be able to satisfy the Upstream interface. type UpstreamSendFunc func(msg *Msg, timeout time.Duration) *Error // Send is used to send a message through this upstream. func (fn UpstreamSendFunc) Send(msg *Msg, timeout time.Duration) *Error { return fn(msg, timeout) } ================================================ FILE: spn/test ================================================ #!/bin/bash warnings=0 errors=0 scripted=0 goUp="\\e[1A" fullTestFlags="-short" install=0 testonly=0 function help { echo "usage: $0 [command] [options]" echo "" echo "commands:" echo " run baseline tests" echo " full run full tests (ie. not short)" echo " install install deps for running tests" echo "" echo "options:" echo " --scripted don't jump console lines (still use colors)" echo " --test-only run tests only, no linters" echo " [package] run only on this package" } function run { if [[ $scripted -eq 0 ]]; then echo "[......] $*" fi # create tmpfile tmpfile=$(mktemp) # execute $* >$tmpfile 2>&1 rc=$? output=$(cat $tmpfile) # check return code if [[ $rc -eq 0 ]]; then if [[ $output == *"[no test files]"* ]]; then echo -e "${goUp}[\e[01;33mNOTEST\e[00m] $*" warnings=$((warnings+1)) else echo -ne "${goUp}[\e[01;32m OK \e[00m] " if [[ $2 == "test" ]]; then echo -n $* echo -n ": " echo $output | cut -f "3-" -d " " else echo $* fi fi else if [[ $output == *"build constraints exclude all Go files"* ]]; then echo -e "${goUp}[ !=OS ] $*" else echo -e "${goUp}[\e[01;31m FAIL \e[00m] $*" cat $tmpfile errors=$((errors+1)) fi fi rm -f $tmpfile } # get and switch to script dir baseDir="$( cd "$(dirname "$0")" && pwd )" cd "$baseDir" # args while true; do case "$1" in "-h"|"help"|"--help") help exit 0 ;; "--scripted") scripted=1 goUp="" shift 1 ;; "--test-only") testonly=1 shift 1 ;; "install") install=1 shift 1 ;; "full") fullTestFlags="" shift 1 ;; *) break ;; esac done # check if $GOPATH/bin is in $PATH if [[ $PATH != *"$GOPATH/bin"* ]]; then export PATH=$GOPATH/bin:$PATH fi # install if [[ $install -eq 1 ]]; then echo "installing dependencies..." # TODO: update golangci-lint version regularly echo "$ curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.44.0" curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.44.0 exit 0 fi # check dependencies if [[ $(which go) == "" ]]; then echo "go command not found" exit 1 fi if [[ $testonly -eq 0 ]]; then if [[ $(which gofmt) == "" ]]; then echo "gofmt command not found" exit 1 fi if [[ $(which golangci-lint) == "" ]]; then echo "golangci-lint command not found" echo "install with: curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin vX.Y.Z" echo "don't forget to specify the version you want" echo "or run: ./test install" echo "" echo "alternatively, install the current dev version with: go get -u github.com/golangci/golangci-lint/cmd/golangci-lint" exit 1 fi fi # target selection if [[ "$1" == "" ]]; then # get all packages packages=$(go list -e ./...) else # single package testing packages=$(go list -e)/$1 echo "note: only running tests for package $packages" fi # platform info platformInfo=$(go env GOOS GOARCH) echo "running tests for ${platformInfo//$'\n'/ }:" # run vet/test on packages for package in $packages; do packagename=${package#github.com/safing/spn} #TODO: could be queried with `go list .` packagename=${packagename#/} echo "" echo $package if [[ $testonly -eq 0 ]]; then run go vet $package run golangci-lint run $packagename fi run go test -cover $fullTestFlags $package done echo "" if [[ $errors -gt 0 ]]; then echo "failed with $errors errors and $warnings warnings" exit 1 else echo "succeeded with $warnings warnings" exit 0 fi ================================================ FILE: spn/testing/README.md ================================================ # Testing Port17 ## Simple Docker Setup Run `run.sh` to start the docker compose test network. Then, connect to the test network, by starting the core with the "test" spn map and the correct bootstrap file. Run `stop.sh` to remove all docker resources again. Setup Guide can be found in the directory. ## Advanced Setup with Shadow For advanced testing we use [shadow](https://github.com/shadow/shadow). The following section will help you set up shadow and will guide you how to test Port17 in a local Shadow environment. ### Setting up Download the docker version from here: [https://security.cs.georgetown.edu/shadow-docker-images/shadow-standalone.tar.gz](https://security.cs.georgetown.edu/shadow-docker-images/shadow-standalone.tar.gz) Then import the image into docker with `gunzip -c shadow-standalone.tar.gz | sudo docker load`. ### Running Execute `sudo docker run -t -i -u shadow shadow-standalone /bin/bash` to start an interactive container with shadow. ================================================ FILE: spn/testing/simple/README.md ================================================ # Setup Guide 1. Build SPN Hub ``` cd ../../../cmds/hub/ ./build ``` 2. Reset any previous state (for a fresh test) ``` ./reset-databases.sh ``` 3. Change compose file and config template as required Files: - `docker-compose.yml` - `config-template.json` 4. Start test network ``` ./run.sh ``` 5. Option 1: Join as Hub For testing just one Hub with a different build or config, you can simply use `./join.sh` to join the network with the most recently build hub binary. 6. Option 2: Join as Portmaster For connecting to the SPN test network with Portmaster, execute portmaster like this: ``` sudo ../../../cmds/portmaster-core/portmaster-core --disable-shutdown-event --devmode --log debug --data /opt/safing/portmaster --spn-map test --bootstrap-file ./testdata/shared/bootstrap.dsd ``` Note: This uses the same portmaster data and config as your installed version. As the SPN Test net operates under a different ID ("test" instead of "main"), this will not pollute the SPN state of your installed Portmaster. 7. Stop the test net This is important, as just stopping the `./run.sh` script will leave you with interfaces with public IPs! ``` ./stop.sh ``` ================================================ FILE: spn/testing/simple/clientsim.sh ================================================ #!/bin/bash cd "$( dirname "${BASH_SOURCE[0]}" )" realpath() { path=`eval echo "$1"` folder=$(dirname "$path") echo $(cd "$folder"; pwd)/$(basename "$path"); } if [[ ! -f "../../client" ]]; then echo "please compile client.go in main directory (output: client)" exit 1 fi bin_path="$(realpath ../../client)" data_path="$(realpath ./testdata)" if [[ ! -d "$data_path" ]]; then mkdir "$data_path" fi shared_path="$(realpath ./testdata/shared)" if [[ ! -d "$shared_path" ]]; then mkdir "$shared_path" fi docker network ls | grep spn-simpletest-network >/dev/null 2>&1 if [[ $? -ne 0 ]]; then docker network create spn-simpletest-network --subnet 6.0.0.0/24 fi docker run -ti --rm \ --name spn-simpletest-clientsim \ --network spn-simpletest-network \ -v $bin_path:/opt/client:ro \ -v $data_path/clientsim:/opt/data \ -v $shared_path:/opt/shared \ --entrypoint /opt/client \ toolset.safing.network/dev \ --data /opt/data \ --bootstrap-file /opt/shared/bootstrap.dsd \ --log trace $* ================================================ FILE: spn/testing/simple/config-template.json ================================================ { "core": { "log": { "level": "trace" }, "metrics": { "instance": "test_$HUBNAME", "push": "" } }, "spn": { "publicHub": { "name": "test-$HUBNAME", "transports": ["http:80", "http:8080", "tcp:17"], "allowUnencrypted": true, "bindToAdvertised": true } } } ================================================ FILE: spn/testing/simple/docker-compose.yml ================================================ version: "2.4" networks: default: ipam: driver: default config: - subnet: 6.0.0.0/24 services: hub1: container_name: spn-test-simple-hub1 hostname: hub1 image: toolset.safing.network/dev entrypoint: "/opt/shared/entrypoint.sh" volumes: - ${SPN_TEST_BIN}:/opt/hub1:ro - ${SPN_TEST_DATA_DIR}/hub1:/opt/data - ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared networks: default: ipv4_address: 6.0.0.11 hub2: container_name: spn-test-simple-hub2 hostname: hub2 image: alpine entrypoint: "/opt/shared/entrypoint.sh" volumes: - ${SPN_TEST_BIN}:/opt/hub2:ro - ${SPN_TEST_DATA_DIR}/hub2:/opt/data - ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared networks: default: ipv4_address: 6.0.0.12 hub3: container_name: spn-test-simple-hub3 hostname: hub3 image: toolset.safing.network/dev entrypoint: "/opt/shared/entrypoint.sh" volumes: - ${SPN_TEST_BIN}:/opt/hub3:ro - ${SPN_TEST_DATA_DIR}/hub3:/opt/data - ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared networks: default: ipv4_address: 6.0.0.13 hub4: container_name: spn-test-simple-hub4 hostname: hub4 image: toolset.safing.network/dev entrypoint: "/opt/shared/entrypoint.sh" volumes: - ${SPN_TEST_BIN}:/opt/hub4:ro - ${SPN_TEST_DATA_DIR}/hub4:/opt/data - ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared networks: default: ipv4_address: 6.0.0.14 hub5: container_name: spn-test-simple-hub5 hostname: hub5 image: toolset.safing.network/dev entrypoint: "/opt/shared/entrypoint.sh" volumes: - ${SPN_TEST_BIN}:/opt/hub5:ro - ${SPN_TEST_DATA_DIR}/hub5:/opt/data - ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared networks: default: ipv4_address: 6.0.0.15 hub6: container_name: spn-test-simple-hub6 hostname: hub6 image: toolset.safing.network/dev entrypoint: "/opt/shared/entrypoint.sh" volumes: - ${SPN_TEST_OLD_BIN}:/opt/hub6:ro - ${SPN_TEST_DATA_DIR}/hub6:/opt/data - ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared networks: default: ipv4_address: 6.0.0.16 hub7: container_name: spn-test-simple-hub7 hostname: hub7 image: toolset.safing.network/dev entrypoint: "/opt/shared/entrypoint.sh" volumes: - ${SPN_TEST_OLD_BIN}:/opt/hub7:ro - ${SPN_TEST_DATA_DIR}/hub7:/opt/data - ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared networks: default: ipv4_address: 6.0.0.17 hub8: container_name: spn-test-simple-hub8 hostname: hub8 image: toolset.safing.network/dev entrypoint: "/opt/shared/entrypoint.sh" volumes: - ${SPN_TEST_OLD_BIN}:/opt/hub8:ro - ${SPN_TEST_DATA_DIR}/hub8:/opt/data - ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared networks: default: ipv4_address: 6.0.0.18 hub9: container_name: spn-test-simple-hub9 hostname: hub9 image: toolset.safing.network/dev entrypoint: "/opt/shared/entrypoint.sh" volumes: - ${SPN_TEST_OLD_BIN}:/opt/hub9:ro - ${SPN_TEST_DATA_DIR}/hub9:/opt/data - ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared networks: default: ipv4_address: 6.0.0.19 hub10: container_name: spn-test-simple-hub10 hostname: hub10 image: toolset.safing.network/dev entrypoint: "/opt/shared/entrypoint.sh" volumes: - ${SPN_TEST_OLD_BIN}:/opt/hub10:ro - ${SPN_TEST_DATA_DIR}/hub10:/opt/data - ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared networks: default: ipv4_address: 6.0.0.20 ================================================ FILE: spn/testing/simple/entrypoint.sh ================================================ #!/bin/sh # Get hostname. HUBNAME=$HOSTNAME if [ "$HUBNAME" = "" ]; then HUBNAME=$(cat /etc/hostname) fi export HUBNAME # Read, process and write config. cat /opt/shared/config-template.json | sed "s/\$HUBNAME/$HUBNAME/g" > /opt/data/config.json # Get binary to start. BIN=$(ls /opt/ | grep hub) # Start Hub. /opt/$BIN --log trace --spn-map test --bootstrap-file /opt/shared/bootstrap.dsd --api-address 0.0.0.0:817 --devmode ================================================ FILE: spn/testing/simple/inject-intel.sh ================================================ #!/bin/bash cd "$( dirname "${BASH_SOURCE[0]}" )" MAIN_INTEL_FILE="intel-testnet.json" if [[ ! -f $MAIN_INTEL_FILE ]]; then echo "missing $MAIN_INTEL_FILE" exit 1 fi echo "if the containing directory cannot be created, you might need to adjust permissions, as nodes are run with root in test containers..." echo "$ sudo chmod -R 777 data/hub*/updates" echo "starting to update..." for hubDir in data/hub*; do # Build destination path hubIntelFile="${hubDir}/updates/all/intel/spn/main-intel_v0-0-0.dsd" # Copy file mkdir -p "${hubDir}/updates/all/intel/spn" echo -n "J" > "$hubIntelFile" cat $MAIN_INTEL_FILE >> "$hubIntelFile" echo "updated $hubIntelFile" done if [[ -d /var/lib/portmaster ]]; then echo "updating intel for local portmaster installation..." portmasterSPNIntelFile="/var/lib/portmaster/updates/all/intel/spn/main-intel_v0-0-0.dsd" echo -n "J" > "$portmasterSPNIntelFile" cat $MAIN_INTEL_FILE >> "$portmasterSPNIntelFile" echo "updated $portmasterSPNIntelFile" fi ================================================ FILE: spn/testing/simple/intel-client.yaml ================================================ # Get current list of IDs from test net: # curl http://127.0.0.1:817/api/v1/spn/map/test/pins | jq ".[] | .ID" # Then import into test client with: # curl -X POST --upload-file intel-client.yaml http://127.0.0.1:817/api/v1/spn/map/test/intel/update Hubs: Zwm48YWWFGdwXjhE1MyEkWfqxPr9DiUBoXpusTZ1FMQnuK: Trusted: true Zwu5LkkbfCbAcYxWG3vtWF1VvWjgWpc1GJfkwRdLFNtytV: Trusted: true ZwuQpz5CqYmYoLnt9KXQ8oxnmosBzfrCYwCGhxT4fsG1Dz: Trusted: true ZwwmC3dHzr7J6XW9mc2KD6FDNuXwPVJUFi9dLnDSNMyjLk: Trusted: true ZwxSBdvqtJyz8zRWKZe6QyK51KH9av6VFay2GQvpFrWKHq: Trusted: true ZwxnuL6zMLj4AxJX8Bj369w2tNrVtYxzffVcXZuMxdxbGj: Trusted: true ZwyXdnC8JkC7m796skGD7QWGoYycByR3KVntkXMY8CxRZQ: Trusted: true Zwz7AHiH1EevD9eYFqvQQPbVWyBBcksTRxxafbRx5Cvc4F: Trusted: true ZwzMtc65t9XBMwmLm2xNSL69FvqHGPLiqeNBZ3eEN5a9sS: Trusted: true ZwzjnCUNGsuWnkYmN3QEj8JPLxU6V1QQFk9b47AigmPKiH: Trusted: true ================================================ FILE: spn/testing/simple/intel-testnet.json ================================================ { "BootstrapHubs": [ ], "TrustedHubs": [ "ZwrY9G9HDo1J3qQrrQs8VF2KD99bj7KyWesJ5kWFUDBU6r", "Zwj56ZFXrsud8gc1Rw3zuxRwMLhGkwvtvnTxCVtJ8EWLhQ", "ZwpdW87ityD9i3N9x8oweCJnbZEqo346VBg4mCsCvTr1Zo", "ZwpJ6ebddk1sccUVpo92JUqicBfKzBN2w4pEGoEY7UsNhX", "Zwte3Jffp9PWmeWfrn8RyGuvZZFCg3v7XR3tpQjdo9TpVt", "ZwrTcdiPF5zR5h9q9EdjHCrrXzYVBdQe5HmEYUWXdLkke3", "Zwv7tSn5iU6bYKn53NaGCxPtL8vSxSK7F9VdQezDaDCLBt", "Zwvtdq3K9knP9iNaRS1Ju8CETWTqy7oRwbScjBtJGBpqhB" ], "AdviseOnlyTrustedHubs": true, "AdviseOnlyTrustedHomeHubs": true, "AdviseOnlyTrustedDestinationHubs": true } ================================================ FILE: spn/testing/simple/join.sh ================================================ #!/bin/bash cd "$( dirname "${BASH_SOURCE[0]}" )" realpath() { path=`eval echo "$1"` folder=$(dirname "$path") echo $(cd "$folder"; pwd)/$(basename "$path"); } leftover=$(docker ps -a | grep spn-test-simple-me | cut -d" " -f1) if [[ $leftover != "" ]]; then docker stop $leftover docker rm $leftover fi if [[ ! -f "../../../cmds/hub/hub" ]]; then echo "please build the hub cmd using cmds/hub/build" exit 1 fi SPN_TEST_BIN="$(realpath ../../../cmds/hub/hub)" SPN_TEST_DATA_DIR="$(realpath ./testdata)" if [[ ! -d "$SPN_TEST_DATA_DIR" ]]; then mkdir "$SPN_TEST_DATA_DIR" fi SPN_TEST_SHARED_DATA_DIR="$(realpath ./testdata/shared)" if [[ ! -d "$SPN_TEST_SHARED_DATA_DIR" ]]; then mkdir "$SPN_TEST_SHARED_DATA_DIR" fi docker run -ti \ --name spn-test-simple-me \ --hostname me \ --network spn-test-simple_default \ -v $SPN_TEST_BIN:/opt/hub_me:ro \ -v $SPN_TEST_DATA_DIR/me:/opt/data \ -v $SPN_TEST_SHARED_DATA_DIR:/opt/shared \ --entrypoint /opt/hub_me \ toolset.safing.network/dev \ --devmode --api-address 0.0.0.0:8081 \ --data /opt/data -log trace --spn-map test --bootstrap-file /opt/shared/bootstrap.dsd ================================================ FILE: spn/testing/simple/reset-databases.sh ================================================ #!/bin/bash cd "$( dirname "${BASH_SOURCE[0]}" )" rm -rf testdata/me/* rm -rf testdata/shared/* rm -rf testdata/hub*/databases ================================================ FILE: spn/testing/simple/run.sh ================================================ #!/bin/bash cd "$( dirname "${BASH_SOURCE[0]}" )" realpath() { path=`eval echo "$1"` folder=$(dirname "$path") echo $(cd "$folder"; pwd)/$(basename "$path"); } leftovers=$(docker ps -a | grep spn-test-simple | cut -d" " -f1) if [[ $leftovers != "" ]]; then docker stop $leftovers docker rm $leftovers fi if [[ ! -f "../../../cmds/hub/hub" ]]; then echo "please build the hub cmd using cmds/hub/build" exit 1 fi # Create variables. SPN_TEST_BIN="$(realpath ../../../cmds/hub/hub)" SPN_TEST_DATA_DIR="$(realpath ./testdata)" if [[ ! -d "$SPN_TEST_DATA_DIR" ]]; then mkdir "$SPN_TEST_DATA_DIR" fi SPN_TEST_SHARED_DATA_DIR="$(realpath ./testdata/shared)" if [[ ! -d "$SPN_TEST_SHARED_DATA_DIR" ]]; then mkdir "$SPN_TEST_SHARED_DATA_DIR" fi # Check if there is an old binary for testing. SPN_TEST_OLD_BIN=$SPN_TEST_BIN if [[ -f "./testdata/old-hub" ]]; then SPN_TEST_OLD_BIN="$(realpath ./testdata/old-hub)" echo "WARNING: running in hybrid mode with old version at $SPN_TEST_OLD_BIN" fi # Export variables export SPN_TEST_BIN export SPN_TEST_OLD_BIN export SPN_TEST_DATA_DIR export SPN_TEST_SHARED_DATA_DIR # Copy files. cp config-template.json ./testdata/shared/config-template.json cp entrypoint.sh ./testdata/shared/entrypoint.sh chmod 555 ./testdata/shared/entrypoint.sh # Run! docker compose -p spn-test-simple up --remove-orphans ================================================ FILE: spn/testing/simple/stop.sh ================================================ #!/bin/bash cd "$( dirname "${BASH_SOURCE[0]}" )" docker compose -p spn-test-simple stop docker compose -p spn-test-simple rm oldnet=$(docker network ls | grep spn-test-simple | cut -d" " -f1) if [[ $oldnet != "" ]]; then docker network rm $oldnet fi if [[ -d "data/shared" ]]; then rm -r "data/shared" fi ================================================ FILE: spn/tools/Dockerfile ================================================ FROM alpine as builder # Ensure ca-certficates are up to date # RUN update-ca-certificates # Download and verify spn-hub binary. RUN mkdir /init RUN wget https://updates.safing.io/latest/linux_amd64/hub/spn-hub -O /init/spn-hub COPY start-checksum.txt /init/start-checksum RUN cd /init && sha256sum -c /init/start-checksum RUN chmod 555 /init/spn-hub # Use minimal image as base. FROM alpine # Copy the static executable. COPY --from=builder /init/spn-hub /init/spn-hub # Copy the init script COPY container-init.sh /init.sh # Run the hub. ENTRYPOINT ["/init.sh"] ================================================ FILE: spn/tools/container-init.sh ================================================ #!/bin/sh DATA="/data" START="/data/spn-hub" INIT_START="/init/spn-hub" # Set safe shell options. set -euf -o pipefail # Check if data dir is mounted. if [ ! -d $DATA ]; then echo "Nothing mounted at $DATA, aborting." exit 1 fi # Copy init start to correct location, if not available. if [ ! -f $START ]; then cp $INIT_START $START fi # Remove PID file, which could have been left after a crash. rm -f $DATA/hub-lock.pid # Always start the SPN Hub with the updated main start binary. echo "running: $START" $START -- $@ ================================================ FILE: spn/tools/install.sh ================================================ #!/bin/sh # # This script should be run via curl as root: # sudo sh -c "$(curl -fsSL https://raw.githubusercontent.com/safing/portmaster/master/spn/tools/install-spn.sh)" # or wget # sudo sh -c "$(wget -qO- https://raw.githubusercontent.com/safing/portmaster/master/spn/tools/install-spn.sh)" # # As an alternative, you can first download the install script and run it afterwards: # wget https://raw.githubusercontent.com/safing/portmaster/master/spn/tools/install-spn.sh # sudo sh ./install.sh # # set -e ARCH= INSTALLDIR= PMSTART= ENABLENOW= INSTALLSYSTEMD= SYSTEMDINSTALLPATH= apply_defaults() { ARCH=${ARCH:-amd64} INSTALLDIR=${INSTALLDIR:-/opt/safing/spn} PMSTART=${PMSTART:-https://updates.safing.io/latest/linux_${ARCH}/start/portmaster-start} SYSTEMDINSTALLPATH=${SYSTEMDINSTALLPATH:-/etc/systemd/system/spn.service} if command_exists systemctl; then INSTALLSYSTEMD=${INSTALLSYSTEMD:-yes} ENABLENOW=${ENABLENOW:-yes} else INSTALLSYSTEMD=${INSTALLSYSTEMD:-no} ENABLENOW=${ENABLENOW:-no} fi # The hostname may be freshly set, ensure the ENV variable is correct. export HOSTNAME=$(hostname) } command_exists() { command -v "$@" >/dev/null 2>&1 } setup_tty() { if [ -t 0 ]; then interactive=yes fi if [ -t 1 ]; then RED=$(printf '\033[31m') GREEN=$(printf '\033[32m') YELLOW=$(printf '\033[33m') BLUE=$(printf '\033[34m') BOLD=$(printf '\033[1m') RESET=$(printf '\033[m') else RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET="" fi } log() { echo ${GREEN}${BOLD}"-> "${RESET}"$@" >&2 } error() { echo ${RED}"Error: $@"${RESET} >&2 } warn() { echo ${YELLOW}"warn: $@"${RESET} >&2 } run_systemctl() { systemctl $@ >/dev/null 2>&1 } download_file() { local src=$1 local dest=$2 if command_exists curl; then curl --silent --fail --show-error --location --output $dest $src elif command_exists wget; then wget --quiet -O $dest $src else error "No suitable download command found, either curl or wget must be installed" exit 1 fi } ensure_install_dir() { log "Creating ${INSTALLDIR}" mkdir -p ${INSTALLDIR} } download_pmstart() { log "Downloading portmaster-start ..." local dest="${INSTALLDIR}/portmaster-start" if [ -f "${dest}" ]; then warn "Overwriting existing portmaster-start at ${dest}" fi download_file ${PMSTART} ${dest} log "Changing permissions" chmod a+x ${dest} } download_updates() { log "Downloading updates ..." ${INSTALLDIR}/portmaster-start --data=${INSTALLDIR} update } setup_systemd() { log "Installing systemd service unit ..." if [ ! "${INSTALLSYSTEMD}" = "yes" ]; then warn "Skipping setup of systemd service unit" echo "To launch the hub, execute the following as root:" echo "" echo "${INSTALLDIR}/portmaster-start --data ${INSTALLDIR} hub" echo "" return fi if [ -f "${SYSTEMDINSTALLPATH}" ]; then warn "Overwriting existing unit path" fi cat >${SYSTEMDINSTALLPATH} < " HOSTNAME fi if [ "${METRICS_COMMENT}" = "" ]; then log "Please enter metrics comment:" read -p "> " METRICS_COMMENT fi } write_config_file() { cat >${1} </dev/null 2>&1 } setup_tty() { if [ -t 0 ]; then interactive=yes fi if [ -t 1 ]; then RED=$(printf '\033[31m') GREEN=$(printf '\033[32m') YELLOW=$(printf '\033[33m') BLUE=$(printf '\033[34m') BOLD=$(printf '\033[1m') RESET=$(printf '\033[m') else RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET="" fi } log() { echo ${GREEN}${BOLD}"-> "${RESET}"$@" >&2 } error() { echo ${RED}"Error: $@"${RESET} >&2 } warn() { echo ${YELLOW}"warn: $@"${RESET} >&2 } run_systemctl() { systemctl $@ >/dev/null 2>&1 } download_file() { local src=$1 local dest=$2 if command_exists curl; then curl --silent --fail --show-error --location --output $dest $src elif command_exists wget; then wget --quiet -O $dest $src else error "No suitable download command found, either curl or wget must be installed" exit 1 fi } ensure_install_dir() { log "Creating ${INSTALLDIR}" mkdir -p ${INSTALLDIR} } download_spnbinary() { log "Downloading SPN binary ..." local dest="${INSTALLDIR}/hub" if [ -f "${dest}" ]; then warn "Overwriting existing hub at ${dest}" fi download_file ${SPNBINARY} ${dest} log "Changing permissions" chmod a+x ${dest} } setup_systemd() { log "Installing systemd service unit ..." if [ ! "${INSTALLSYSTEMD}" = "yes" ]; then warn "Skipping setup of systemd service unit" echo "To launch the hub, execute the following as root:" echo "" echo "${INSTALLDIR}/hub --data-dir ${INSTALLDIR}" echo "" return fi if [ -f "${SYSTEMDINSTALLPATH}" ]; then warn "Overwriting existing unit path" fi cat >${SYSTEMDINSTALLPATH} < " HOSTNAME fi if [ "${METRICS_COMMENT}" = "" ]; then log "Please enter metrics comment:" read -p "> " METRICS_COMMENT fi } write_config_file() { cat >${1} < /etc/sysctl.d/9999-spn-network-optimizing.conf # cat /etc/sysctl.d/9999-spn-network-optimizing.conf # sysctl -p /etc/sysctl.d/9999-spn-network-optimizing.conf # Provide adequate buffer memory. # net.ipv4.tcp_mem is in 4096-byte pages. net.core.rmem_max = 1073741824 net.core.wmem_max = 1073741824 net.core.rmem_default = 16777216 net.core.wmem_default = 16777216 net.ipv4.tcp_rmem = 4096 16777216 1073741824 net.ipv4.tcp_wmem = 4096 16777216 1073741824 net.ipv4.tcp_mem = 4194304 8388608 16777216 net.ipv4.udp_rmem_min = 16777216 net.ipv4.udp_wmem_min = 16777216 # Enable TCP window scaling. net.ipv4.tcp_window_scaling = 1 # Increase the length of the processor input queue net.core.netdev_max_backlog = 100000 net.core.netdev_budget = 1000 net.core.netdev_budget_usecs = 10000 # Set better congestion control. net.ipv4.tcp_congestion_control = htcp # Turn off fancy stuff for more stability. net.ipv4.tcp_sack = 0 net.ipv4.tcp_dsack = 0 net.ipv4.tcp_fack = 0 net.ipv4.tcp_timestamps = 0 # Max reorders before slow start. net.ipv4.tcp_reordering = 3 # Prefer low latency to higher throughput. # Disables IPv4 TCP prequeue processing. net.ipv4.tcp_low_latency = 1 # Don't start slow. net.ipv4.tcp_slow_start_after_idle = 0 ================================================ FILE: spn/unit/doc.go ================================================ // Package unit provides a "work unit" scheduling system for handling data sets that traverse multiple workers / goroutines. // The aim is to bind priority to a data set instead of a goroutine and split resources fairly among requests. // // Every "work" Unit is assigned an ever increasing ID and can be marked as "paused" or "high priority". // The Scheduler always gives a clearance up to a certain ID. All units below this ID may be processed. // High priority Units may always be processed. // // The Scheduler works with short slots and measures how many Units were finished in a slot. // The "slot pace" holds an indication of the current Unit finishing speed per slot. It is only changed slowly (but boosts if too far away) in order to keep stabilize the system. // The Scheduler then calculates the next unit ID limit to give clearance to for the next slot: // // "finished units" + "slot pace" + "paused units" - "fraction of high priority units" package unit ================================================ FILE: spn/unit/scheduler.go ================================================ package unit import ( "errors" "math" "sync" "sync/atomic" "time" "github.com/tevino/abool" "github.com/safing/portmaster/service/mgr" ) const ( defaultSlotDuration = 10 * time.Millisecond // 100 slots per second defaultMinSlotPace = 100 // 10 000 pps defaultWorkSlotPercentage = 0.7 // 70% defaultSlotChangeRatePerStreak = 0.02 // 2% defaultStatCycleDuration = 1 * time.Minute ) // Scheduler creates and schedules units. // Must be created using NewScheduler(). type Scheduler struct { //nolint:maligned // Configuration. config SchedulerConfig // Units IDs Limit / Thresholds. // currentUnitID holds the last assigned Unit ID. currentUnitID atomic.Int64 // clearanceUpTo holds the current threshold up to which Unit ID Units may be processed. clearanceUpTo atomic.Int64 // slotPace holds the current pace. This is the base value for clearance // calculation, not the value of the current cleared Units itself. slotPace atomic.Int64 // finished holds the amount of units that were finished within the current slot. finished atomic.Int64 // Slot management. slotSignalA chan struct{} slotSignalB chan struct{} slotSignalSwitch bool slotSignalsLock sync.RWMutex stopping abool.AtomicBool unitDebugger *UnitDebugger // Stats. stats struct { // Working Values. progress struct { maxPace atomic.Int64 maxLeveledPace atomic.Int64 avgPaceSum atomic.Int64 avgPaceCnt atomic.Int64 avgUnitLifeSum atomic.Int64 avgUnitLifeCnt atomic.Int64 avgWorkSlotSum atomic.Int64 avgWorkSlotCnt atomic.Int64 avgCatchUpSlotSum atomic.Int64 avgCatchUpSlotCnt atomic.Int64 } // Calculated Values. current struct { maxPace atomic.Int64 maxLeveledPace atomic.Int64 avgPace atomic.Int64 avgUnitLife atomic.Int64 avgWorkSlot atomic.Int64 avgCatchUpSlot atomic.Int64 } } } // SchedulerConfig holds scheduler configuration. type SchedulerConfig struct { // SlotDuration defines the duration of one slot. SlotDuration time.Duration // MinSlotPace defines the minimum slot pace. // The slot pace will never fall below this value. MinSlotPace int64 // WorkSlotPercentage defines the how much of a slot should be scheduled with work. // The remainder is for catching up and breathing room for other tasks. // Must be between 55% (0.55) and 95% (0.95). // The default value is 0.7 (70%). WorkSlotPercentage float64 // SlotChangeRatePerStreak defines how many percent (0-1) the slot pace // should change per streak. // Is enforced to be able to change the minimum slot pace by at least 1. // The default value is 0.02 (2%). SlotChangeRatePerStreak float64 // StatCycleDuration defines how often stats are calculated. // The default value is 1 minute. StatCycleDuration time.Duration } // NewScheduler returns a new scheduler. func NewScheduler(config *SchedulerConfig) *Scheduler { // Fallback to empty config if none is given. if config == nil { config = &SchedulerConfig{} } // Create new scheduler. s := &Scheduler{ config: *config, slotSignalA: make(chan struct{}), slotSignalB: make(chan struct{}), } // Fill in defaults. if s.config.SlotDuration == 0 { s.config.SlotDuration = defaultSlotDuration } if s.config.MinSlotPace == 0 { s.config.MinSlotPace = defaultMinSlotPace } if s.config.WorkSlotPercentage == 0 { s.config.WorkSlotPercentage = defaultWorkSlotPercentage } if s.config.SlotChangeRatePerStreak == 0 { s.config.SlotChangeRatePerStreak = defaultSlotChangeRatePerStreak } if s.config.StatCycleDuration == 0 { s.config.StatCycleDuration = defaultStatCycleDuration } // Check boundaries of WorkSlotPercentage. switch { case s.config.WorkSlotPercentage < 0.55: s.config.WorkSlotPercentage = 0.55 case s.config.WorkSlotPercentage > 0.95: s.config.WorkSlotPercentage = 0.95 } // The slot change rate must be able to change the slot pace by at least 1. if s.config.SlotChangeRatePerStreak < (1 / float64(s.config.MinSlotPace)) { s.config.SlotChangeRatePerStreak = (1 / float64(s.config.MinSlotPace)) // Debug logging: // fmt.Printf("--- increased SlotChangeRatePerStreak to %f\n", s.config.SlotChangeRatePerStreak) } // Initialize scheduler fields. s.clearanceUpTo.Store(s.config.MinSlotPace) s.slotPace.Store(s.config.MinSlotPace) return s } func (s *Scheduler) nextSlotSignal() chan struct{} { s.slotSignalsLock.RLock() defer s.slotSignalsLock.RUnlock() if s.slotSignalSwitch { return s.slotSignalA } return s.slotSignalB } func (s *Scheduler) announceNextSlot() { s.slotSignalsLock.Lock() defer s.slotSignalsLock.Unlock() // Close new slot signal and refresh previous one. if s.slotSignalSwitch { close(s.slotSignalA) s.slotSignalB = make(chan struct{}) } else { close(s.slotSignalB) s.slotSignalA = make(chan struct{}) } // Switch to next slot. s.slotSignalSwitch = !s.slotSignalSwitch } // SlotScheduler manages the slot and schedules units. // Must only be started once. func (s *Scheduler) SlotScheduler(ctx *mgr.WorkerCtx) error { // Start slot ticker. ticker := time.NewTicker(s.config.SlotDuration / 2) defer ticker.Stop() // Give clearance to all when stopping. defer s.clearanceUpTo.Store(math.MaxInt64 - math.MaxInt32) var ( halfSlotID uint64 halfSlotStartedAt = time.Now() halfSlotEndedAt time.Time halfSlotDuration = float64(s.config.SlotDuration / 2) increaseStreak float64 decreaseStreak float64 oneStreaks int cycleStatsAt = uint64(s.config.StatCycleDuration / (s.config.SlotDuration / 2)) ) for range ticker.C { halfSlotEndedAt = time.Now() switch { case halfSlotID%2 == 0: // First Half-Slot: Work Slot // Calculate time taken in previous slot. catchUpSlotDuration := halfSlotEndedAt.Sub(halfSlotStartedAt).Nanoseconds() // Add current slot duration to avg calculation. s.stats.progress.avgCatchUpSlotCnt.Add(1) if s.stats.progress.avgCatchUpSlotSum.Add(catchUpSlotDuration) < 0 { // Reset if we wrap. s.stats.progress.avgCatchUpSlotCnt.Store(1) s.stats.progress.avgCatchUpSlotSum.Store(catchUpSlotDuration) } // Reset slot counters. s.finished.Store(0) // Raise clearance according s.clearanceUpTo.Store( s.currentUnitID.Load() + int64( float64(s.slotPace.Load())*s.config.WorkSlotPercentage, ), ) // Announce start of new slot. s.announceNextSlot() default: // Second Half-Slot: Catch-Up Slot // Calculate time taken in previous slot. workSlotDuration := halfSlotEndedAt.Sub(halfSlotStartedAt).Nanoseconds() // Add current slot duration to avg calculation. s.stats.progress.avgWorkSlotCnt.Add(1) if s.stats.progress.avgWorkSlotSum.Add(workSlotDuration) < 0 { // Reset if we wrap. s.stats.progress.avgWorkSlotCnt.Store(1) s.stats.progress.avgWorkSlotSum.Store(workSlotDuration) } // Calculate slot duration skew correction, as slots will not run in the // exact specified duration. slotDurationSkewCorrection := halfSlotDuration / float64(workSlotDuration) // Calculate slot pace with performance of first half-slot. // Get current slot pace as float64. currentSlotPace := float64(s.slotPace.Load()) // Calculate current raw slot pace. newRawSlotPace := float64(s.finished.Load()*2) * slotDurationSkewCorrection // Move slot pace in the trending direction. if newRawSlotPace >= currentSlotPace { // Adjust based on streak. increaseStreak++ decreaseStreak = 0 s.slotPace.Add(int64( currentSlotPace * s.config.SlotChangeRatePerStreak * increaseStreak, )) // Count one-streaks. if increaseStreak == 1 { oneStreaks++ } else { oneStreaks = 0 } // Debug logging: // fmt.Printf("+++ slot pace: %.0f (current raw pace: %.0f, increaseStreak: %.0f, clearanceUpTo: %d)\n", currentSlotPace, newRawSlotPace, increaseStreak, s.clearanceUpTo.Load()) } else { // Adjust based on streak. decreaseStreak++ increaseStreak = 0 s.slotPace.Add(int64( -currentSlotPace * s.config.SlotChangeRatePerStreak * decreaseStreak, )) // Enforce minimum. if s.slotPace.Load() < s.config.MinSlotPace { s.slotPace.Store(s.config.MinSlotPace) decreaseStreak = 0 } // Count one-streaks. if decreaseStreak == 1 { oneStreaks++ } else { oneStreaks = 0 } // Debug logging: // fmt.Printf("--- slot pace: %.0f (current raw pace: %.0f, decreaseStreak: %.0f, clearanceUpTo: %d)\n", currentSlotPace, newRawSlotPace, decreaseStreak, s.clearanceUpTo.Load()) } // Record Stats // Add current pace to avg calculation. s.stats.progress.avgPaceCnt.Add(1) if s.stats.progress.avgPaceSum.Add(s.slotPace.Load()) < 0 { // Reset if we wrap. s.stats.progress.avgPaceCnt.Store(1) s.stats.progress.avgPaceSum.Store(s.slotPace.Load()) } // Check if current pace is new max. if s.slotPace.Load() > s.stats.progress.maxPace.Load() { s.stats.progress.maxPace.Store(s.slotPace.Load()) } // Check if current pace is new leveled max if oneStreaks >= 3 && s.slotPace.Load() > s.stats.progress.maxLeveledPace.Load() { s.stats.progress.maxLeveledPace.Store(s.slotPace.Load()) } } // Switch to other slot-half. halfSlotID++ halfSlotStartedAt = halfSlotEndedAt // Cycle stats after defined time period. if halfSlotID%cycleStatsAt == 0 { s.cycleStats() } // Check if we are stopping. select { case <-ctx.Done(): return nil default: } if s.stopping.IsSet() { return nil } } // We should never get here. // If we do, trigger a worker restart via the service worker. return errors.New("unexpected end of scheduler") } // Stop stops the scheduler and gives clearance to all units. func (s *Scheduler) Stop() { s.stopping.Set() } ================================================ FILE: spn/unit/scheduler_stats.go ================================================ package unit // Stats are somewhat racy, as one value of sum or count might already be // updated with the latest slot data, while the other has been not. // This is not so much of a problem, as slots are really short and the impact // is very low. // cycleStats calculates the new values and cycles the current values. func (s *Scheduler) cycleStats() { // Get and reset max pace. s.stats.current.maxPace.Store(s.stats.progress.maxPace.Load()) s.stats.progress.maxPace.Store(0) // Get and reset max leveled pace. s.stats.current.maxLeveledPace.Store(s.stats.progress.maxLeveledPace.Load()) s.stats.progress.maxLeveledPace.Store(0) // Get and reset avg slot pace. avgPaceCnt := s.stats.progress.avgPaceCnt.Load() if avgPaceCnt > 0 { s.stats.current.avgPace.Store(s.stats.progress.avgPaceSum.Load() / avgPaceCnt) } else { s.stats.current.avgPace.Store(0) } s.stats.progress.avgPaceCnt.Store(0) s.stats.progress.avgPaceSum.Store(0) // Get and reset avg unit life. avgUnitLifeCnt := s.stats.progress.avgUnitLifeCnt.Load() if avgUnitLifeCnt > 0 { s.stats.current.avgUnitLife.Store(s.stats.progress.avgUnitLifeSum.Load() / avgUnitLifeCnt) } else { s.stats.current.avgUnitLife.Store(0) } s.stats.progress.avgUnitLifeCnt.Store(0) s.stats.progress.avgUnitLifeSum.Store(0) // Get and reset avg work slot duration. avgWorkSlotCnt := s.stats.progress.avgWorkSlotCnt.Load() if avgWorkSlotCnt > 0 { s.stats.current.avgWorkSlot.Store(s.stats.progress.avgWorkSlotSum.Load() / avgWorkSlotCnt) } else { s.stats.current.avgWorkSlot.Store(0) } s.stats.progress.avgWorkSlotCnt.Store(0) s.stats.progress.avgWorkSlotSum.Store(0) // Get and reset avg catch up slot duration. avgCatchUpSlotCnt := s.stats.progress.avgCatchUpSlotCnt.Load() if avgCatchUpSlotCnt > 0 { s.stats.current.avgCatchUpSlot.Store(s.stats.progress.avgCatchUpSlotSum.Load() / avgCatchUpSlotCnt) } else { s.stats.current.avgCatchUpSlot.Store(0) } s.stats.progress.avgCatchUpSlotCnt.Store(0) s.stats.progress.avgCatchUpSlotSum.Store(0) } // GetMaxSlotPace returns the current maximum slot pace. func (s *Scheduler) GetMaxSlotPace() int64 { return s.stats.current.maxPace.Load() } // GetMaxLeveledSlotPace returns the current maximum leveled slot pace. func (s *Scheduler) GetMaxLeveledSlotPace() int64 { return s.stats.current.maxLeveledPace.Load() } // GetAvgSlotPace returns the current average slot pace. func (s *Scheduler) GetAvgSlotPace() int64 { return s.stats.current.avgPace.Load() } // GetAvgUnitLife returns the current average unit lifetime until it is finished. func (s *Scheduler) GetAvgUnitLife() int64 { return s.stats.current.avgUnitLife.Load() } // GetAvgWorkSlotDuration returns the current average work slot duration. func (s *Scheduler) GetAvgWorkSlotDuration() int64 { return s.stats.current.avgWorkSlot.Load() } // GetAvgCatchUpSlotDuration returns the current average catch up slot duration. func (s *Scheduler) GetAvgCatchUpSlotDuration() int64 { return s.stats.current.avgCatchUpSlot.Load() } ================================================ FILE: spn/unit/scheduler_test.go ================================================ package unit import ( "testing" "github.com/safing/portmaster/service/mgr" ) func BenchmarkScheduler(b *testing.B) { workers := 10 // Create and start scheduler. s := NewScheduler(&SchedulerConfig{}) m := mgr.New("unit-test") m.Go("test", func(ctx *mgr.WorkerCtx) error { err := s.SlotScheduler(ctx) if err != nil { panic(err) } return nil }) defer m.Cancel() // Init control structures. done := make(chan struct{}) finishedCh := make(chan struct{}) // Start workers. for range workers { go func() { for { u := s.NewUnit() u.WaitForSlot() u.Finish() select { case finishedCh <- struct{}{}: case <-done: return } } }() } // Start benchmark. b.ResetTimer() for range b.N { <-finishedCh } b.StopTimer() // Cleanup. close(done) } ================================================ FILE: spn/unit/unit.go ================================================ package unit import ( "time" "github.com/tevino/abool" ) // Unit describes a "work unit" and is meant to be embedded into another struct // used for passing data moving through multiple processing steps. type Unit struct { id int64 scheduler *Scheduler created time.Time finished abool.AtomicBool highPriority abool.AtomicBool } // NewUnit returns a new unit within the scheduler. func (s *Scheduler) NewUnit() *Unit { return &Unit{ id: s.currentUnitID.Add(1), scheduler: s, created: time.Now(), } } // ReUse re-initialized the unit to be able to reuse already allocated structs. func (u *Unit) ReUse() { // Finish previous unit. u.Finish() // Get new ID and unset finish flag. u.id = u.scheduler.currentUnitID.Add(1) u.finished.UnSet() } // WaitForSlot blocks until the unit may be processed. func (u *Unit) WaitForSlot() { // High priority units may always process. if u.highPriority.IsSet() { return } for { // Check if we are allowed to process in the current slot. if u.id <= u.scheduler.clearanceUpTo.Load() { return } // Debug logging: // fmt.Printf("unit %d waiting for clearance at %d\n", u.id, u.scheduler.clearanceUpTo.Load()) // Wait for next slot. <-u.scheduler.nextSlotSignal() } } // Finish signals the unit scheduler that this unit has finished processing. // Will no-op if called on a nil Unit. func (u *Unit) Finish() { if u == nil { return } // Always increase finished, even if the unit is from a previous epoch. if u.finished.SetToIf(false, true) { u.scheduler.finished.Add(1) // Record the time this unit took from creation to finish. timeTaken := time.Since(u.created).Nanoseconds() u.scheduler.stats.progress.avgUnitLifeCnt.Add(1) if u.scheduler.stats.progress.avgUnitLifeSum.Add(timeTaken) < 0 { // Reset if we wrap. u.scheduler.stats.progress.avgUnitLifeCnt.Store(1) u.scheduler.stats.progress.avgUnitLifeSum.Store(timeTaken) } } } // MakeHighPriority marks the unit as high priority. func (u *Unit) MakeHighPriority() { switch { case u.finished.IsSet(): // Unit is already finished. case !u.highPriority.SetToIf(false, true): // Unit is already set to high priority. // Else: High Priority set. case u.id > u.scheduler.clearanceUpTo.Load(): // Unit is outside current clearance, reduce clearance by one. u.scheduler.clearanceUpTo.Add(-1) } } // IsHighPriority returns whether the unit has high priority. func (u *Unit) IsHighPriority() bool { return u.highPriority.IsSet() } // RemovePriority removes the high priority mark. func (u *Unit) RemovePriority() { u.highPriority.UnSet() } ================================================ FILE: spn/unit/unit_debug.go ================================================ package unit import ( "sync" "time" "github.com/safing/portmaster/base/log" ) // UnitDebugger is used to debug unit leaks. type UnitDebugger struct { //nolint:golint units map[int64]*UnitDebugData unitsLock sync.Mutex } // UnitDebugData represents a unit that is being debugged. type UnitDebugData struct { //nolint:golint unit *Unit unitSource string } // DebugUnit registers the given unit for debug output with the given source. // Additional calls on the same unit update the unit source. // StartDebugLog() must be called before calling DebugUnit(). func (s *Scheduler) DebugUnit(u *Unit, unitSource string) { // Check if scheduler and unit debugger are created. if s == nil || s.unitDebugger == nil { return } s.unitDebugger.unitsLock.Lock() defer s.unitDebugger.unitsLock.Unlock() s.unitDebugger.units[u.id] = &UnitDebugData{ unit: u, unitSource: unitSource, } } // StartDebugLog logs the scheduler state every second. func (s *Scheduler) StartDebugLog() { s.unitDebugger = &UnitDebugger{ units: make(map[int64]*UnitDebugData), } // Force StatCycleDuration to match the debug log output. s.config.StatCycleDuration = time.Second go func() { for { s.debugStep() time.Sleep(time.Second) } }() } func (s *Scheduler) debugStep() { s.unitDebugger.unitsLock.Lock() defer s.unitDebugger.unitsLock.Unlock() // Go through debugging units and clear finished ones, count sources. sources := make(map[string]int) for id, debugUnit := range s.unitDebugger.units { if debugUnit.unit.finished.IsSet() { delete(s.unitDebugger.units, id) } else { cnt := sources[debugUnit.unitSource] sources[debugUnit.unitSource] = cnt + 1 } } // Print current state. log.Debugf( `scheduler: state: slotPace=%d avgPace=%d maxPace=%d maxLeveledPace=%d currentUnitID=%d clearanceUpTo=%d unitLife=%s slotDurations=%s/%s`, s.slotPace.Load(), s.GetAvgSlotPace(), s.GetMaxSlotPace(), s.GetMaxLeveledSlotPace(), s.currentUnitID.Load(), s.clearanceUpTo.Load(), time.Duration(s.GetAvgUnitLife()).Round(10*time.Microsecond), time.Duration(s.GetAvgWorkSlotDuration()).Round(10*time.Microsecond), time.Duration(s.GetAvgCatchUpSlotDuration()).Round(10*time.Microsecond), ) log.Debugf("scheduler: unit sources: %+v", sources) } ================================================ FILE: spn/unit/unit_test.go ================================================ package unit import ( "fmt" "math" "math/rand" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/safing/portmaster/service/mgr" ) func TestUnit(t *testing.T) { //nolint:paralleltest // Ignore deprectation, as the given alternative is not safe for concurrent use. // The global rand methods use a locked seed, which is not available from outside. rand.Seed(time.Now().UnixNano()) //nolint size := 1000000 workers := 100 m := mgr.New("unit-test") // Create and start scheduler. s := NewScheduler(&SchedulerConfig{}) s.StartDebugLog() // ctx, cancel := context.WithCancel(context.Background()) m.Go("test", func(w *mgr.WorkerCtx) error { err := s.SlotScheduler(w) if err != nil { panic(err) } return nil }) defer m.Cancel() // Create 10 workers. var wg sync.WaitGroup wg.Add(workers) sizePerWorker := size / workers for range workers { go func() { for range sizePerWorker { u := s.NewUnit() // Make 1% high priority. if rand.Int()%100 == 0 { //nolint:gosec // This is a test. u.MakeHighPriority() } u.WaitForSlot() time.Sleep(10 * time.Microsecond) u.Finish() } wg.Done() }() } // Wait for workers to finish. wg.Wait() // Wait for two slot durations for values to update. time.Sleep(s.config.SlotDuration * 2) // Print current state. s.cycleStats() fmt.Printf(`scheduler state: currentUnitID = %d slotPace = %d clearanceUpTo = %d finished = %d maxPace = %d maxLeveledPace = %d avgPace = %d avgUnitLife = %s avgWorkSlot = %s avgCatchUpSlot = %s `, s.currentUnitID.Load(), s.slotPace.Load(), s.clearanceUpTo.Load(), s.finished.Load(), s.GetMaxSlotPace(), s.GetMaxLeveledSlotPace(), s.GetAvgSlotPace(), time.Duration(s.GetAvgUnitLife()), time.Duration(s.GetAvgWorkSlotDuration()), time.Duration(s.GetAvgCatchUpSlotDuration()), ) // Check if everything seems good. assert.Equal(t, size, int(s.currentUnitID.Load()), "currentUnitID must match size") assert.GreaterOrEqual( t, int(s.clearanceUpTo.Load()), size+int(float64(s.config.MinSlotPace)*s.config.SlotChangeRatePerStreak), "clearanceUpTo must be at least size+minSlotPace", ) // Shutdown m.Cancel() time.Sleep(s.config.SlotDuration * 10) // Check if scheduler shut down correctly. assert.Equal(t, math.MaxInt64-math.MaxInt32, int(s.clearanceUpTo.Load()), "clearance must be near MaxInt64") } ================================================ FILE: windows_core_dll/build.ps1 ================================================ msbuild .\windows_core_dll.sln /p:Configuration=Release ls .\x64\Release\portmaster-core.dll ================================================ FILE: windows_core_dll/dllmain.cpp ================================================ // dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" #pragma comment(lib, "tdh.lib") // GUID of the DNS log provider static const GUID DNS_CLIENT_PROVIDER_GUID = { 0x1C95126E, 0x7EEA, 0x49A9, {0xA3, 0xFE, 0xA3, 0x78, 0xB0, 0x3D, 0xDB, 0x4D} }; // GUID of the event session. This should be unique for the application. static const GUID PORTMASTER_ETW_SESSION_GUID = { 0x0211d070, 0xc3b2, 0x4609, {0x92, 0xf5, 0x28, 0xe7, 0x18, 0xb2, 0x3b, 0x18} }; // Name of the session. This is visble when user queries all ETW sessions. // (example `logman query -ets`) #define LOGSESSION_NAME L"PortmasterDNSEventListener" // Fuction type of the callback that will be called on each event. typedef uint64_t(*GoEventRecordCallback)(wchar_t* domain, uint32_t pid, wchar_t* result); // Holds the state of the ETW Session. struct ETWSessionState { TRACEHANDLE SessionTraceHandle; EVENT_TRACE_PROPERTIES* SessionProperties; TRACEHANDLE sessionHandle; GoEventRecordCallback callback; }; // getPropertyValue reads a property from the event. static bool getPropertyValue(PEVENT_RECORD evt, LPWSTR prop, PBYTE* pData) { // Describe the data that needs to be retrieved from the event. PROPERTY_DATA_DESCRIPTOR DataDescriptor; ZeroMemory(&DataDescriptor, sizeof(DataDescriptor)); DataDescriptor.PropertyName = (ULONGLONG)(prop); DataDescriptor.ArrayIndex = 0; DWORD PropertySize = 0; // Check if the data is available and what is the size of it. DWORD status = TdhGetPropertySize(evt, 0, NULL, 1, &DataDescriptor, &PropertySize); if (ERROR_SUCCESS != status) { return false; } // Allocate memory for the data. *pData = (PBYTE)malloc(PropertySize); if (NULL == *pData) { return false; } // Get the data. status = TdhGetProperty(evt, 0, NULL, 1, &DataDescriptor, PropertySize, *pData); if (ERROR_SUCCESS != status) { if (*pData) { free(*pData); *pData = NULL; } return false; } return true; } // EventRecordCallback is a callback called on each event. static void WINAPI EventRecordCallback(PEVENT_RECORD eventRecord) { PBYTE resultValue = NULL; PBYTE domainValue = NULL; getPropertyValue(eventRecord, (LPWSTR)L"QueryResults", &resultValue); getPropertyValue(eventRecord, (LPWSTR)L"QueryName", &domainValue); ETWSessionState* state = (ETWSessionState*)eventRecord->UserContext; if (resultValue != NULL && domainValue != NULL) { state->callback((wchar_t*)domainValue, eventRecord->EventHeader.ProcessId, (wchar_t*)resultValue); } free(resultValue); free(domainValue); } extern "C" { // PM_ETWCreateState allocates memory for the state and initializes the config for the session. PM_ETWDestroySession must be called to avoid leaks. // callback must be set to a valid function pointer. __declspec(dllexport) ETWSessionState* PM_ETWCreateState(GoEventRecordCallback callback) { // Create trace session properties. ULONG BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME); EVENT_TRACE_PROPERTIES* SessionProperties = (EVENT_TRACE_PROPERTIES*)calloc(1, BufferSize); SessionProperties->Wnode.BufferSize = BufferSize; SessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; SessionProperties->Wnode.ClientContext = 1; // QPC clock resolution SessionProperties->Wnode.Guid = PORTMASTER_ETW_SESSION_GUID; SessionProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; SessionProperties->MaximumFileSize = 1; // MB SessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); // Create state ETWSessionState* state = (ETWSessionState*)calloc(1, sizeof(ETWSessionState)); state->SessionProperties = SessionProperties; state->callback = callback; return state; } // PM_ETWInitializeSession initializes the session. __declspec(dllexport) uint32_t PM_ETWInitializeSession(ETWSessionState* state) { return StartTrace(&state->SessionTraceHandle, LOGSESSION_NAME, state->SessionProperties); } // PM_ETWStartTrace subscribes to the dns events and start listening. The function blocks while the listener is running. // Call PM_ETWStopTrace to stop the listener. __declspec(dllexport) uint32_t PM_ETWStartTrace(ETWSessionState* state) { ULONG status = EnableTraceEx2(state->SessionTraceHandle, (LPCGUID)&DNS_CLIENT_PROVIDER_GUID, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_INFORMATION, 0, 0, 0, NULL); if (status != ERROR_SUCCESS) { return status; } EVENT_TRACE_LOGFILE trace = { 0 }; trace.LoggerName = (LPWSTR)(LOGSESSION_NAME); trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD; trace.EventRecordCallback = EventRecordCallback; trace.Context = state; state->sessionHandle = OpenTrace(&trace); if (state->sessionHandle == INVALID_PROCESSTRACE_HANDLE) { return 1; } status = ProcessTrace(&state->sessionHandle, 1, NULL, NULL); if (status != ERROR_SUCCESS) { return 1; } return ERROR_SUCCESS; } // PM_ETWFlushTrace flushes the event buffer. __declspec(dllexport) uint32_t PM_ETWFlushTrace(ETWSessionState* state) { return ControlTrace(state->SessionTraceHandle, LOGSESSION_NAME, state->SessionProperties, EVENT_TRACE_CONTROL_FLUSH); } // PM_ETWFlushTrace stops the listener. __declspec(dllexport) uint32_t PM_ETWStopTrace(ETWSessionState* state) { return ControlTrace(state->SessionTraceHandle, LOGSESSION_NAME, state->SessionProperties, EVENT_TRACE_CONTROL_STOP); } // PM_ETWFlushTrace Closes the session and frees recourses. __declspec(dllexport) uint32_t PM_ETWDestroySession(ETWSessionState* state) { if (state == NULL) { return 1; } uint32_t status = CloseTrace(state->sessionHandle); // Free memory. free(state->SessionProperties); free(state); return status; } // PM_ETWStopOldSession removes old session with the same name if they exist. // It returns success(0) only if its able to delete the old session. __declspec(dllexport) ULONG PM_ETWStopOldSession() { ULONG status = ERROR_SUCCESS; TRACEHANDLE sessionHandle = 0; // Create trace session properties size_t bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME); EVENT_TRACE_PROPERTIES* sessionProperties = (EVENT_TRACE_PROPERTIES*)calloc(1, bufferSize); sessionProperties->Wnode.BufferSize = (ULONG)bufferSize; sessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; sessionProperties->Wnode.ClientContext = 1; // QPC clock resolution sessionProperties->Wnode.Guid = PORTMASTER_ETW_SESSION_GUID; sessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); // Use Control trace will stop the session which will trigger a delete. status = ControlTrace(NULL, LOGSESSION_NAME, sessionProperties, EVENT_TRACE_CONTROL_STOP); free(sessionProperties); return status; } } ================================================ FILE: windows_core_dll/framework.h ================================================ #pragma once #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files #include ================================================ FILE: windows_core_dll/pch.cpp ================================================ // pch.cpp: source file corresponding to the pre-compiled header #include "pch.h" // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. ================================================ FILE: windows_core_dll/pch.h ================================================ // pch.h: This is a precompiled header file. // Files listed below are compiled only once, improving build performance for future builds. // This also affects IntelliSense performance, including code completion and many code browsing features. // However, files listed here are ALL re-compiled if any one of them is updated between builds. // Do not add files here that you will be updating frequently as this negates the performance advantage. #ifndef PCH_H #define PCH_H // add headers that you want to pre-compile here #include "framework.h" #include #include #include #include #include #include #include #endif //PCH_H ================================================ FILE: windows_core_dll/windows_core_dll.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.11.35222.181 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "windows_core_dll", "windows_core_dll.vcxproj", "{6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Debug|x64.ActiveCfg = Debug|x64 {6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Debug|x64.Build.0 = Debug|x64 {6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Debug|x86.ActiveCfg = Debug|Win32 {6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Debug|x86.Build.0 = Debug|Win32 {6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Release|x64.ActiveCfg = Release|x64 {6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Release|x64.Build.0 = Release|x64 {6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Release|x86.ActiveCfg = Release|Win32 {6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8E60106D-49DF-49C7-AC08-02775342FEAE} EndGlobalSection EndGlobal ================================================ FILE: windows_core_dll/windows_core_dll.vcxproj ================================================ Debug Win32 Release Win32 Debug x64 Release x64 17.0 Win32Proj {6f3c7eaf-8511-4822-aaf0-1086d27e4da9} windowscoredll 10.0 portmaster-core DynamicLibrary true v143 Unicode DynamicLibrary false v143 true Unicode DynamicLibrary true v143 Unicode DynamicLibrary false v143 true Unicode Level3 true WIN32;_DEBUG;WINDOWSCOREDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h Windows true false Level3 true true true WIN32;NDEBUG;WINDOWSCOREDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h Windows true true true false Level3 true _DEBUG;WINDOWSCOREDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h Windows true false Level3 true true true NDEBUG;WINDOWSCOREDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h Windows true true true false Create Create Create Create ================================================ FILE: windows_core_dll/windows_core_dll.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Source Files Source Files ================================================ FILE: windows_core_dll/windows_core_dll.vcxproj.user ================================================  ================================================ FILE: windows_kext/.gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Mono auto generated files mono_crash.* # Build results [Dd]ebug/ [Dd]ebugPublic/ */**/[Rr]elease/ */**/[Rr]eleases/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ [Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUnit *.VisualState.xml TestResult.xml nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ # ASP.NET Scaffolding ScaffoldingReadMe.txt # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *_wpftmp.csproj *.log *.tlog *.vspscc *.vssscc .builds *.pidb *.svclog *.scc *.exe # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Coverlet is a free, cross platform Code Coverage Tool coverage*.json coverage*.xml coverage*.info # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # NuGet Symbol Packages *.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx *.appxbundle *.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !?*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser *- [Bb]ackup.rdl *- [Bb]ackup ([0-9]).rdl *- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio 6 auto-generated project file (contains which files were open etc.) *.vbp # Visual Studio 6 workspace and project file (working project files containing files to include in project) *.dsw *.dsp # Visual Studio 6 technical files *.ncb *.aps # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # CodeRush personal settings .cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ # Local History for Visual Studio .localhistory/ # Visual Studio History (VSHistory) files .vshistory/ # BeatPulse healthcheck temp database healthchecksdb # Backup folder for Package Reference Convert tool in Visual Studio 2017 MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ # Fody - auto-generated XML schema FodyWeavers.xsd # VS Code files for those working on multiple tools .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json *.code-workspace # Local History for Visual Studio Code .history/ # Windows Installer files from build outputs *.cab *.msi *.msix *.msm *.msp # JetBrains Rider *.sln.iml pm_kext/RCa04400 pm_kext/RCa05788 pm_kext/RCa06452 target kext.sys release/build ================================================ FILE: windows_kext/PacketFlow.md ================================================ # There and back again, a packets tale. An explanation on the complete path of the packet from entering to the exit of the kernel extension. ## Entry The packet entry point depends on the packet and the internal windows filter state: - First packet of outbound connection -> AleAuthConnect Layer - First packet of inbound connection -> InboundIppacket Layer ## ALE layer Each defined ALE layer has a filter linked to it. This filter has a state. When a decision is made to block or permit a connection it will be saved to the filter state. The only way to update the decision in a filter is to clear the whole state and apply the decision for the next packet of each connection. ### First packet For outgoing connections this logic fallows: - Packet enters in one of the ALE layer - Packet is TCP or UDP 1. Save and absorb packet. 2. Send an event to Portmaster. 2. Create a cache entry. - If Packet is not TCP/UDP forward to packet layer For incoming connection this logic fallow: - Packet enter in one of the Packet layer: 1. Save packet and absorb. 2. Send an event to Portmaster. 2. Create a cache entry if the protocol is TCP or UDP. 3. Wait for Portmasters decision. If more packets arrive before Portmaster returns a decision, packet will be absorbed and another event will be sent. For Outgoing connection this will happen in ALE layer. For Incoming connection this will happen in Packet layer. ### Pormtaster returns a verdict for the connection Connection cache will be updated and the packet will be injected. The next steps depend of the direction of the packet and the verdict * Permanent Verdict / Outgoing connection - Allow / Block / Drop directly in the ALE layer. For Block and Drop packet layer will not see the rest of the packet in the connection. * Temporary Verdict / Outgoing connection - Always Allow - this connections are solely handled by the packet layer. (This is true only for outgoing connections) * Permanent or Temporary Verdict / Incoming connection - Allow / Block / Drop. Handled by the Packet layer > There is no defined ALE layers for inbound connection. Inbound packets are handed compactly by the packet layer Fallowing specifics apply to the ALE layer: 1. Connections with flag `reauthorize == false` are special. When the flag is `false` that means that a applications is calling a function `connect()` or `accept()` for a connection. This is a special case because we control the result of the function, telling the application that it's allowed or not allowed to continue with the connection. Since we are making request to Portmaster we need to take longer time. This is done with pending the packet. This allows the kernel extension to pause the event and continue when it has the verdict. See `ale_callouts.rs -> save_packet()` function. 2. If packet payload is present it is from the transport layer. ## Packet layer The logic for the packet is split in two: ### TCP or UDP protocols The packet layer will not process packets that miss a cache entry: - Incoming packet: it will forward it to the ALE layer. - Outgoing packet: this is treated as invalid state since ALE should be the entry for the packets. If it happens the packet layer will create a request to Portmaster for it. For packets with a cache entry: - Permanent Verdict: apply the verdict. - Redirect Verdict: copy the packet, modify and inject. Drop the original packet. - Temporary verdict: send request to Portmaster. After portmaster returns the verdict for the packet. If its allowed it will be modified (if needed) and injected everything else will be dropped. The packet layer will permit all injected packets. ### Not TCP or UDP protocols -> ICMP, IGMP ... Does packets are treated as with temporary verdict. There will be no cache entry for them. Every packet will be send to Portmaster for a decision and re-injected if allowed. ## Connection Cache It holds information for all TCP and UDP connections. Local and destination ip addresses and ports, verdict, protocol, process id It also holds last active time and end time. Cache entry is removed automatically 1 minute after an end state has been set or after 10 minutes of inactivity. End stat is set by Endpoint layers or Resource release layers. ================================================ FILE: windows_kext/PortmasterKext64.inf ================================================ ;/*++ ; ;Copyright (c) Safing ICS Technologies GmbH. ; ; This program is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program. If not, see . ; ;--*/ [Version] Signature = "$Windows NT$" Class = WFPCALLOUTS ClassGuid = {57465043-616C-6C6F-7574-5F636C617373} Provider = %Provider% CatalogFile = PortmasterKext64.Cat DriverVer = 01/01/2019,1.0.11.0 [SourceDisksNames] 1 = %DiskName% [SourceDisksFiles] PortmasterKext64.sys = 1 [DestinationDirs] DefaultDestDir = 12 ; %windir%\system32\drivers PortmasterKext.DriverFiles = 12 ; %windir%\system32\drivers [DefaultInstall] OptionDesc = %Description% CopyFiles = PortmasterKext.DriverFiles [DefaultInstall.Services] AddService = %ServiceName%,,PortmasterKext.Service [DefaultUninstall] DelFiles = PortmasterKext.DriverFiles [DefaultUninstall.Services] DelService = PortmasterKext,0x200 ; SPSVCINST_STOPSERVICE [PortmasterKext.DriverFiles] PortmasterKext64.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY [PortmasterKext.Service] DisplayName = %ServiceName% Description = %ServiceDesc% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 0 ; SERVICE_BOOT_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %12%\PortmasterKext64.sys [Strings] Provider = "Safing ICS Technologies GmbH" DiskName = "PortmasterKext Installation Disk" Description = "PortmasterKext Driver" ServiceName = "PortmasterKext" ServiceDesc = "PortmasterKext Driver" ================================================ FILE: windows_kext/README.md ================================================ # Portmaster Windows kext Implementation of Safing's Portmaster Windows kernel extension in Rust. ### Documentation - [Driver](driver/README.md) -> entry point. - [WDK](wdk/README.md) -> Windows Driver Kit interface. - [Packet Path](PacketFlow.md) -> Detailed documentation of what happens to a packet when it enters the kernel extension. - [Release](release/README.md) -> Guide how to do a release build. - [Windows Filtering Platform - MS](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/roadmap-for-developing-wfp-callout-drivers) -> The driver is build on top of WFP. ### Building (For release) Please refer to [release/README.md](release/README.md) for details about the release procedure. ### Building (For testing and development) The Windows Portmaster Kernel Extension is currently only developed and tested for the amd64 (64-bit) architecture. __Prerequirements:__ - Visual Studio 2022 - Install C++ and Windows 11 SDK (22H2) components - Add `link.exe` and `signtool` in the PATH - Windows Driver Kit - https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk - Rust (Can be separate machine) - https://www.rust-lang.org/tools/install __Setup Test Signing:__ > Not recommended for a work machine. Usually done on virtual machine dedicated for testing. In order to test the driver on your machine, you will have to sign it (starting with Windows 10). Create a new certificate for test signing: ```ps1 # Open a *x64 Free Build Environment* console as Administrator. # Run the MakeCert.exe tool to create a test certificate: MakeCert -r -pe -ss PrivateCertStore -n "CN=DriverCertificate" DriverCertificate.cer # Install the test certificate with CertMgr.exe: CertMgr /add DriverCertificate.cer /s /r localMachine root ``` Enable Test Signing on the dev machine: ```ps1 # Before you can load test-signed drivers, you must enable Windows test mode. To do this, run this command: Bcdedit.exe -set TESTSIGNING ON # Then, restart Windows. For more information, see The TESTSIGNING Boot Configuration Option. ``` __Build driver:__ ```sh cd driver cargo build --release ``` > Build also works on linux __Link and sign:__ On a windows machine copy `driver.lib` from the project target directory (`driver/target/x86_64-pc-windows-msvc/release/driver.lib`) in the same folder as `link-dev.ps1`. Run `link-dev.ps1`. `driver.sys` should appear in the folder. Sign the driver with the test certificate: ``` SignTool sign /v /s TestCertStoreName /n TestCertName driver.sys ``` Load and use the driver. ================================================ FILE: windows_kext/c_helper/c_helper.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files ================================================ FILE: windows_kext/c_helper/c_helper.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.5.33502.453 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "c_helper", "c_helper.vcxproj", "{39A5E911-A716-4708-8B88-3895183C6372}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|ARM = Release|ARM Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {39A5E911-A716-4708-8B88-3895183C6372}.Debug|ARM.ActiveCfg = Debug|ARM {39A5E911-A716-4708-8B88-3895183C6372}.Debug|ARM.Build.0 = Debug|ARM {39A5E911-A716-4708-8B88-3895183C6372}.Debug|ARM.Deploy.0 = Debug|ARM {39A5E911-A716-4708-8B88-3895183C6372}.Debug|ARM64.ActiveCfg = Debug|ARM64 {39A5E911-A716-4708-8B88-3895183C6372}.Debug|ARM64.Build.0 = Debug|ARM64 {39A5E911-A716-4708-8B88-3895183C6372}.Debug|ARM64.Deploy.0 = Debug|ARM64 {39A5E911-A716-4708-8B88-3895183C6372}.Debug|x64.ActiveCfg = Debug|x64 {39A5E911-A716-4708-8B88-3895183C6372}.Debug|x64.Build.0 = Debug|x64 {39A5E911-A716-4708-8B88-3895183C6372}.Debug|x64.Deploy.0 = Debug|x64 {39A5E911-A716-4708-8B88-3895183C6372}.Debug|x86.ActiveCfg = Debug|Win32 {39A5E911-A716-4708-8B88-3895183C6372}.Debug|x86.Build.0 = Debug|Win32 {39A5E911-A716-4708-8B88-3895183C6372}.Debug|x86.Deploy.0 = Debug|Win32 {39A5E911-A716-4708-8B88-3895183C6372}.Release|ARM.ActiveCfg = Release|ARM {39A5E911-A716-4708-8B88-3895183C6372}.Release|ARM.Build.0 = Release|ARM {39A5E911-A716-4708-8B88-3895183C6372}.Release|ARM.Deploy.0 = Release|ARM {39A5E911-A716-4708-8B88-3895183C6372}.Release|ARM64.ActiveCfg = Release|ARM64 {39A5E911-A716-4708-8B88-3895183C6372}.Release|ARM64.Build.0 = Release|ARM64 {39A5E911-A716-4708-8B88-3895183C6372}.Release|ARM64.Deploy.0 = Release|ARM64 {39A5E911-A716-4708-8B88-3895183C6372}.Release|x64.ActiveCfg = Release|x64 {39A5E911-A716-4708-8B88-3895183C6372}.Release|x64.Build.0 = Release|x64 {39A5E911-A716-4708-8B88-3895183C6372}.Release|x64.Deploy.0 = Release|x64 {39A5E911-A716-4708-8B88-3895183C6372}.Release|x86.ActiveCfg = Release|Win32 {39A5E911-A716-4708-8B88-3895183C6372}.Release|x86.Build.0 = Release|Win32 {39A5E911-A716-4708-8B88-3895183C6372}.Release|x86.Deploy.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {91E52350-EBB9-4B0F-9C28-61C0BBAEDC6A} EndGlobalSection EndGlobal ================================================ FILE: windows_kext/c_helper/c_helper.vcxproj ================================================  Debug Win32 Release Win32 Debug x64 Release x64 Debug ARM Release ARM Debug ARM64 Release ARM64 {39A5E911-A716-4708-8B88-3895183C6372} {0a049372-4c4d-4ea0-a64e-dc6ad88ceca1} v4.5 12.0 Debug Win32 c_helper KMDF $(LatestTargetPlatformVersion) c_helper Windows10 true WindowsKernelModeDriver10.0 StaticLibrary Universal Unicode Windows10 false WindowsKernelModeDriver10.0 StaticLibrary Universal Unicode Windows10 true WindowsKernelModeDriver10.0 StaticLibrary Universal Unicode false Windows10 false WindowsKernelModeDriver10.0 StaticLibrary Universal Unicode false Windows10 true WindowsKernelModeDriver10.0 StaticLibrary Unicode Windows10 false WindowsKernelModeDriver10.0 StaticLibrary Universal Unicode Windows10 true WindowsKernelModeDriver10.0 StaticLibrary Universal Unicode Windows10 false WindowsKernelModeDriver10.0 StaticLibrary Universal Unicode false $(SolutionDir)$(Platform) $(SolutionDir)$(Platform) $(SolutionDir)$(Platform) $(Platform)\$(ConfigurationName)\ $(TargetName.Replace(' ','')) _DEBUG;WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) MultiThreadedDebugDLL WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) _DEBUG;WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) MultiThreadedDebugDLL false WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) false _DEBUG;WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) MultiThreadedDebugDLL WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) _DEBUG;WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) MultiThreadedDebugDLL WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) Default Default ================================================ FILE: windows_kext/c_helper/helper.c ================================================ /* * Name: helper.c */ #include #include #define NDIS640 1 // Windows 8 and Windows Server 2012 #include "Ntifs.h" #include // Windows Driver Development Kit #include // Windows Driver Foundation #pragma warning(push) #pragma warning(disable: 4201) // Disable "Nameless struct/union" compiler warning for fwpsk.h only! #include // Functions and enumerated types used to implement callouts in kernel mode #pragma warning(pop) // Re-enable "Nameless struct/union" compiler warning #include // Functions used for managing IKE and AuthIP main mode (MM) policy and security associations #include // Mappings of OS specific function versions (i.e. fn's that end in 0 or 1) #include // Used to define GUID's #include // Used to define GUID's #include "devguid.h" #include #include #include EVT_WDF_DRIVER_UNLOAD emptyEventUnload; NTSTATUS pm_InitDriverObject(DRIVER_OBJECT * driverObject, UNICODE_STRING * registryPath, WDFDRIVER * driver, WDFDEVICE * device, wchar_t *win_device_name, wchar_t *dos_device_name, WDF_OBJECT_ATTRIBUTES * objectAttributes, void (*wdfEventUnload)(WDFDRIVER)) { UNICODE_STRING deviceName = { 0 }; RtlInitUnicodeString(&deviceName, win_device_name); UNICODE_STRING deviceSymlink = { 0 }; RtlInitUnicodeString(&deviceSymlink, dos_device_name); // Create a WDFDRIVER for this driver WDF_DRIVER_CONFIG config = { 0 }; WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); config.DriverInitFlags = WdfDriverInitNonPnpDriver; config.EvtDriverUnload = wdfEventUnload; // <-- Necessary for this driver to unload correctly NTSTATUS status = WdfDriverCreate(driverObject, registryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, driver); if (!NT_SUCCESS(status)) { return status; } // Create a WDFDEVICE for this driver PWDFDEVICE_INIT deviceInit = WdfControlDeviceInitAllocate(*driver, &SDDL_DEVOBJ_SYS_ALL_ADM_ALL); // only admins and kernel can access device if (!deviceInit) { return STATUS_INSUFFICIENT_RESOURCES; } // Configure the WDFDEVICE_INIT with a name to allow for access from user mode WdfDeviceInitSetDeviceType(deviceInit, FILE_DEVICE_NETWORK); WdfDeviceInitSetCharacteristics(deviceInit, FILE_DEVICE_SECURE_OPEN, false); (void) WdfDeviceInitAssignName(deviceInit, &deviceName); (void) WdfPdoInitAssignRawDevice(deviceInit, &GUID_DEVCLASS_NET); WdfDeviceInitSetDeviceClass(deviceInit, &GUID_DEVCLASS_NET); status = WdfDeviceCreate(&deviceInit, objectAttributes, device); if (!NT_SUCCESS(status)) { WdfDeviceInitFree(deviceInit); return status; } status = WdfDeviceCreateSymbolicLink(*device, &deviceSymlink); if (!NT_SUCCESS(status)) { return status; } // The system will not send I/O requests or Windows Management Instrumentation (WMI) requests to a control device object unless the driver has called WdfControlFinishInitializing. WdfControlFinishInitializing(*device); return STATUS_SUCCESS; } void* pm_WdfObjectGetTypedContextWorker(WDFOBJECT wdfObject, PCWDF_OBJECT_CONTEXT_TYPE_INFO typeInfo) { return WdfObjectGetTypedContextWorker(wdfObject, typeInfo->UniqueType); } DEVICE_OBJECT* pm_GetDeviceObject(WDFDEVICE device) { return WdfDeviceWdmGetDeviceObject(device); } UINT64 pm_QuerySystemTime() { UINT64 timestamp = 0; KeQuerySystemTime(×tamp); return timestamp; } ================================================ FILE: windows_kext/driver/.cargo/config.toml ================================================ [build] target = "x86_64-pc-windows-msvc" rustflags = ["-C", "panic=abort"] ================================================ FILE: windows_kext/driver/Cargo.toml ================================================ [package] name = "driver" version = "0.0.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] name = "driver" path = "src/lib.rs" crate-type = ["staticlib"] [dependencies] wdk = { path = "../wdk" } protocol = { path = "../protocol" } num = { version = "0.4", default-features = false } num-derive = { version = "0.4", default-features = false } num-traits = { version = "0.2", default-features = false } smoltcp = { version = "0.10", default-features = false, features = ["proto-ipv4", "proto-ipv6"] } # WARNING: Do not update. The version was choosen for a reason. See wdk/README.md for more detiels. [dependencies.windows-sys] git = "https://github.com/microsoft/windows-rs" rev = "dffa8b03dc4987c278d82e88015ffe96aa8ac317" features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Wdk_System_SystemServices", "Win32_Foundation", "Win32_Security", "Win32_System_IO", "Win32_System_Kernel", "Win32_System_Power", "Win32_System_WindowsProgramming", "Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_NetworkManagement_WindowsFilteringPlatform"] ================================================ FILE: windows_kext/driver/Makefile.toml ================================================ [env.development] TARGET_PATH = "target/x86_64-pc-windows-msvc/debug" [env.production] TARGET_PATH = "target/x86_64-pc-windows-msvc/release" BUILD_FLAGS = "--release" [tasks.build-driver] script = [ "cargo build $BUILD_FLAGS", ] [tasks.upload] dependencies = ["build-driver"] script = [ "scp $TARGET_PATH/driver.lib windows:'C:/Dev/'", ] ================================================ FILE: windows_kext/driver/README.md ================================================ # Driver This is the entry point of the Kernel extension. ## Quick overview `entry.rs`: This file contains the entry point and calling all the needed initialization code. - Setting up the driver object - Allocating global state `fn driver_entry()` -> entry pointer of the driver. `device.rs`: Holds the global state of the driver. Initialization: Setting up global state, Filter engine and callouts. Portmaster communication: The communication happens concurrently with the File read/write API. That means when Pormtaster sends a command the kernel extension will start to process it and queue the result in the `IOQueue`. `fn read()` -> called on read request from Portmaster - `IOQueue` holds all the events queued for Portmaster. Blocks until there is a element that can be poped or shutdown request is sent from Portmaster. If there is more then one event in the queue it will write as much as it can in the supplied buffer. `fn write()` -> called on write request from Portmaster. Used when Portmaster wants to send a command to kernel extension. Verdict Response, GetLogs ... (see `protocol` for list of all the commands) ## Callouts `callouts.rs` -> defines the list of all used callouts in the kernel extension. ALE (Application Layer Enforcement) https://learn.microsoft.com/en-us/windows/win32/fwp/application-layer-enforcement--ale- ### ALE Auth Connection level filtering. It will make a decision based on the first packet of a connection. Works together with the packet layer to provide firewall functionality. - **AleLayerOutboundV4** - **AleLayerInboundV4** - **AleLayerOutboundV6** - **AleLayerInboundV6** ### ALE endpoint / resource assignment and release Used to listen for event when connection has ended. Does no filtering. - **AleEndpointClosureV4, AleEndpointClosureV6** - Triggered when connection to an endpoint has ended. Usually only TCP is triggered. The triggered connection will be marked for deletion. - **AleResourceAssignmentV4, AleResourceAssignmentV6** -> only for logging (not used) - AleResourceReleaseV4, AleResourceReleaseV6 -> Triggered when port is release from an application. The triggered connection/s will be marked for deletion. ### Stream layer This layer works on the application OSI layer. Meaning that only the payload of the TCP/UDP connection will be available. It is used for bandwidth monitoring. This functionality is completely separate from the rest of the system so it can be disabled or enabled without affect anything else. - **StreamLayerV4, StreamLayerV6** -> For TCP connections - **DatagramDataLayerV4, DatagramDataLayerV6** -> For UDP connections ### Packet layer This layer handled each packet on the network OSI layer. Works together with ALE Auth layer to provide firewall functionality. - **IPPacketOutboundV4, IPPacketOutboundV6** -> Triggered on every outbound packet. - **IPPacketInboundV4, IPPacketInboundV6** -> Triggered on every inbound packet. ================================================ FILE: windows_kext/driver/rust-toolchain ================================================ stable ================================================ FILE: windows_kext/driver/src/ale_callouts.rs ================================================ use crate::connection::{Connection, ConnectionV4, ConnectionV6, Direction, Verdict}; use crate::connection_map::Key; use crate::device::{Device, Packet}; use crate::info; use smoltcp::wire::{ IpAddress, IpProtocol, Ipv4Address, Ipv6Address, IPV4_HEADER_LEN, IPV6_HEADER_LEN, }; use wdk::filter_engine::callout_data::CalloutData; use wdk::filter_engine::layer::{self, FieldsAleAuthConnectV4, FieldsAleAuthConnectV6, ValueType}; use wdk::filter_engine::net_buffer::NetBufferList; use wdk::filter_engine::packet::{Injector, TransportPacketList}; // ALE Layers #[derive(Debug)] #[allow(dead_code)] struct AleLayerData { is_ipv6: bool, reauthorize: bool, process_id: u64, protocol: IpProtocol, direction: Direction, local_ip: IpAddress, local_port: u16, remote_ip: IpAddress, remote_port: u16, interface_index: u32, sub_interface_index: u32, } impl AleLayerData { fn as_key(&self) -> Key { let mut local_port = 0; let mut remote_port = 0; match self.protocol { IpProtocol::Tcp | IpProtocol::Udp => { local_port = self.local_port; remote_port = self.remote_port; } _ => {} } Key { protocol: self.protocol, local_address: self.local_ip, local_port, remote_address: self.remote_ip, remote_port, } } } fn get_protocol(data: &CalloutData, index: usize) -> IpProtocol { IpProtocol::from(data.get_value_u8(index)) } fn get_ipv4_address(data: &CalloutData, index: usize) -> IpAddress { IpAddress::Ipv4(Ipv4Address::from_bytes( &data.get_value_u32(index).to_be_bytes(), )) } fn get_ipv6_address(data: &CalloutData, index: usize) -> IpAddress { IpAddress::Ipv6(Ipv6Address::from_bytes(data.get_value_byte_array16(index))) } pub fn ale_layer_connect_v4(data: CalloutData) { type Fields = FieldsAleAuthConnectV4; let ale_data = AleLayerData { is_ipv6: false, reauthorize: data.is_reauthorize(Fields::Flags as usize), process_id: data.get_process_id().unwrap_or(0), protocol: get_protocol(&data, Fields::IpProtocol as usize), direction: Direction::Outbound, local_ip: get_ipv4_address(&data, Fields::IpLocalAddress as usize), local_port: data.get_value_u16(Fields::IpLocalPort as usize), remote_ip: get_ipv4_address(&data, Fields::IpRemoteAddress as usize), remote_port: data.get_value_u16(Fields::IpRemotePort as usize), interface_index: 0, sub_interface_index: 0, }; ale_layer_auth(data, ale_data); } pub fn ale_layer_connect_v6(data: CalloutData) { type Fields = FieldsAleAuthConnectV6; let ale_data = AleLayerData { is_ipv6: true, reauthorize: data.is_reauthorize(Fields::Flags as usize), process_id: data.get_process_id().unwrap_or(0), protocol: get_protocol(&data, Fields::IpProtocol as usize), direction: Direction::Outbound, local_ip: get_ipv6_address(&data, Fields::IpLocalAddress as usize), local_port: data.get_value_u16(Fields::IpLocalPort as usize), remote_ip: get_ipv6_address(&data, Fields::IpRemoteAddress as usize), remote_port: data.get_value_u16(Fields::IpRemotePort as usize), interface_index: data.get_value_u32(Fields::InterfaceIndex as usize), sub_interface_index: data.get_value_u32(Fields::SubInterfaceIndex as usize), }; ale_layer_auth(data, ale_data); } fn ale_layer_auth(mut data: CalloutData, ale_data: AleLayerData) { // Make the default path as drop. data.block_and_absorb(); let Some(device) = crate::entry::get_device() else { return; }; // Check if packet was previously injected from the packet layer. if device .injector .was_network_packet_injected_by_self(data.get_layer_data() as _, ale_data.is_ipv6) { data.action_permit(); return; } match ale_data.protocol { IpProtocol::Tcp | IpProtocol::Udp => { // Only TCP and UDP make sense to be supported in the ALE layer. // Everything else is not associated with a connection and will be handled in the packet layer. } _ => { // Outbound: Will be handled by packet layer next. // Inbound: Was already handled by the packet layer. data.action_permit(); return; } } let key = ale_data.as_key(); // Check if connection is already in cache. let verdict = if ale_data.is_ipv6 { device .connection_cache .read_connection_v6(&key, |conn| -> Option { // Function is behind spin lock, just copy and return. Some(conn.verdict) }) } else { device .connection_cache .read_connection_v4(&ale_data.as_key(), |conn| -> Option { // Function is behind spin lock, just copy and return. Some(conn.verdict) }) }; // Connection already in cache. if let Some(verdict) = verdict { crate::dbg!("processing existing connection: {} {}", key, verdict); match verdict { // No verdict yet Verdict::Undecided => { crate::dbg!("saving packet: {}", key); // Connection is already pended. Save packet and wait for verdict. match save_packet(device, &mut data, &ale_data, false) { Ok(packet) => { let info = device.packet_cache.push( (key, packet), ale_data.process_id, ale_data.direction, true, ); if let Some(info) = info { let _ = device.event_queue.push(info); } } Err(err) => { crate::err!("failed to pend packet: {}", err); } }; data.block_and_absorb(); } // There is a verdict Verdict::PermanentAccept | Verdict::Accept | Verdict::RedirectNameServer | Verdict::RedirectTunnel | Verdict::RedirectSplitTunnel => { // Continue to packet layer. data.action_permit(); if device.is_owner_pid(ale_data.process_id as u32) && matches!(ale_data.direction, Direction::Outbound) { // If this is Portmaster's own outbound connection, clear the write flag // to prevent subsequent filters in the chain from overriding the permit action. // This prevents other firewall applications from blocking Portmaster's own connections. data.clear_write_flag(); } } Verdict::PermanentBlock | Verdict::Undeterminable | Verdict::Failed => { // Packet layer will not see this connection. crate::dbg!("permanent block {}", key); data.action_block_hard(); } Verdict::PermanentDrop => { // Packet layer will not see this connection. crate::dbg!("permanent drop {}", key); data.block_and_absorb(); } Verdict::Block => { if let Direction::Outbound = ale_data.direction { // Handled by packet layer. data.action_permit(); } else { // packet layer will still see the packets. data.action_block_hard(); } } Verdict::Drop => { if let Direction::Outbound = ale_data.direction { // Handled by packet layer. data.action_permit(); } else { // packet layer will still see the packets. data.block_and_absorb(); } } } } else { crate::dbg!("pending connection: {} {}", key, ale_data.direction); // Only first packet of a connection can be pended: reauthorize == false let can_pend_connection = !ale_data.reauthorize; match save_packet(device, &mut data, &ale_data, can_pend_connection) { Ok(packet) => { let info = device.packet_cache.push( (key, packet), ale_data.process_id, ale_data.direction, true, ); if let Some(info) = info { let _ = device.event_queue.push(info); } } Err(err) => { crate::err!("failed to pend packet: {}", err); } }; // Connection is not in cache, add it. crate::dbg!( "ale layer adding connection: {} PID: {}", key, ale_data.process_id ); if ale_data.is_ipv6 { let conn = ConnectionV6::from_key(&key, ale_data.process_id, ale_data.direction).unwrap(); device.connection_cache.add_connection_v6(conn); } else { let conn = ConnectionV4::from_key(&key, ale_data.process_id, ale_data.direction).unwrap(); device.connection_cache.add_connection_v4(conn); } // Drop packet. It will be re-injected after Portmaster returns a verdict. data.block_and_absorb(); } } fn save_packet( device: &Device, callout_data: &mut CalloutData, ale_data: &AleLayerData, pend: bool, ) -> Result { let mut packet_list = None; let mut save_packet_list = true; if ale_data.protocol == IpProtocol::Tcp { if let Direction::Outbound = ale_data.direction { // Only time a packet data is missing is during connect state of outbound TCP connection. // Don't save packet list only if connection is outbound, reauthorize is false and the protocol is TCP. save_packet_list = ale_data.reauthorize; } }; if save_packet_list { packet_list = create_packet_list(device, callout_data, ale_data); } if pend && matches!(ale_data.protocol, IpProtocol::Tcp | IpProtocol::Udp) { match callout_data.pend_operation(packet_list) { Ok(classify_defer) => Ok(Packet::AleLayer(classify_defer)), Err(err) => Err(alloc::format!("failed to defer connection: {}", err)), } } else { Ok(Packet::AleLayer(callout_data.pend_filter_rest(packet_list))) } } fn create_packet_list( device: &Device, callout_data: &mut CalloutData, ale_data: &AleLayerData, ) -> Option { let mut nbl = NetBufferList::new(callout_data.get_layer_data() as _); let mut inbound = false; if let Direction::Inbound = ale_data.direction { if ale_data.is_ipv6 { nbl.retreat(IPV6_HEADER_LEN as u32, true); } else { nbl.retreat(IPV4_HEADER_LEN as u32, true); } inbound = true; } let address: &[u8] = match &ale_data.remote_ip { IpAddress::Ipv4(address) => &address.0, IpAddress::Ipv6(address) => &address.0, }; if let Ok(clone) = nbl.clone(&device.network_allocator) { return Some(Injector::from_ale_callout( ale_data.is_ipv6, callout_data, clone, address, inbound, ale_data.interface_index, ale_data.sub_interface_index, )); } return None; } pub fn endpoint_closure_v4(data: CalloutData) { type Fields = layer::FieldsAleEndpointClosureV4; let Some(device) = crate::entry::get_device() else { return; }; let ip_address_type = data.get_value_type(Fields::IpLocalAddress as usize); if let ValueType::FwpUint32 = ip_address_type { let key = Key { protocol: get_protocol(&data, Fields::IpProtocol as usize), local_address: get_ipv4_address(&data, Fields::IpLocalAddress as usize), local_port: data.get_value_u16(Fields::IpLocalPort as usize), remote_address: get_ipv4_address(&data, Fields::IpRemoteAddress as usize), remote_port: data.get_value_u16(Fields::IpRemotePort as usize), }; let conn = device.connection_cache.end_connection_v4(key); if let Some(conn) = conn { let info = protocol::info::connection_end_event_v4_info( data.get_process_id().unwrap_or(0), conn.get_direction() as u8, u8::from(get_protocol(&data, Fields::IpProtocol as usize)), conn.local_address.0, conn.remote_address.0, conn.local_port, conn.remote_port, ); let _ = device.event_queue.push(info); } } else { // Invalid ip address type. Just ignore the error. // err!( // device.logger, // "unknown ipv4 address type: {:?}", // ip_address_type // ); } } pub fn endpoint_closure_v6(data: CalloutData) { type Fields = layer::FieldsAleEndpointClosureV6; let Some(device) = crate::entry::get_device() else { return; }; let local_ip_address_type = data.get_value_type(Fields::IpLocalAddress as usize); let remote_ip_address_type = data.get_value_type(Fields::IpRemoteAddress as usize); if let ValueType::FwpByteArray16Type = local_ip_address_type { if let ValueType::FwpByteArray16Type = remote_ip_address_type { let key = Key { protocol: get_protocol(&data, Fields::IpProtocol as usize), local_address: get_ipv6_address(&data, Fields::IpLocalAddress as usize), local_port: data.get_value_u16(Fields::IpLocalPort as usize), remote_address: get_ipv6_address(&data, Fields::IpRemoteAddress as usize), remote_port: data.get_value_u16(Fields::IpRemotePort as usize), }; let conn = device.connection_cache.end_connection_v6(key); if let Some(conn) = conn { let info = protocol::info::connection_end_event_v6_info( data.get_process_id().unwrap_or(0), conn.get_direction() as u8, u8::from(get_protocol(&data, Fields::IpProtocol as usize)), conn.local_address.0, conn.remote_address.0, conn.local_port, conn.remote_port, ); let _ = device.event_queue.push(info); } } } } pub fn ale_resource_monitor(data: CalloutData) { let Some(device) = crate::entry::get_device() else { return; }; match data.layer { layer::Layer::AleResourceAssignmentV4Discard => { type Fields = layer::FieldsAleResourceAssignmentV4; if let Some(conns) = device.connection_cache.end_all_on_port_v4(( get_protocol(&data, Fields::IpProtocol as usize), data.get_value_u16(Fields::IpLocalPort as usize), )) { let process_id = data.get_process_id().unwrap_or(0); info!( "Port {}/{} Ipv4 assign request discarded pid={}", data.get_value_u16(Fields::IpLocalPort as usize), get_protocol(&data, Fields::IpProtocol as usize), process_id, ); for conn in conns { let info = protocol::info::connection_end_event_v4_info( process_id, conn.get_direction() as u8, data.get_value_u8(Fields::IpProtocol as usize), conn.local_address.0, conn.remote_address.0, conn.local_port, conn.remote_port, ); let _ = device.event_queue.push(info); } } } layer::Layer::AleResourceAssignmentV6Discard => { type Fields = layer::FieldsAleResourceAssignmentV6; if let Some(conns) = device.connection_cache.end_all_on_port_v6(( get_protocol(&data, Fields::IpProtocol as usize), data.get_value_u16(Fields::IpLocalPort as usize), )) { let process_id = data.get_process_id().unwrap_or(0); info!( "Port {}/{} Ipv6 assign request discarded pid={}", data.get_value_u16(Fields::IpLocalPort as usize), get_protocol(&data, Fields::IpProtocol as usize), process_id, ); for conn in conns { let info = protocol::info::connection_end_event_v6_info( process_id, conn.get_direction() as u8, data.get_value_u8(Fields::IpProtocol as usize), conn.local_address.0, conn.remote_address.0, conn.local_port, conn.remote_port, ); let _ = device.event_queue.push(info); } } } layer::Layer::AleResourceReleaseV4 => { type Fields = layer::FieldsAleResourceReleaseV4; if let Some(conns) = device.connection_cache.end_all_on_port_v4(( get_protocol(&data, Fields::IpProtocol as usize), data.get_value_u16(Fields::IpLocalPort as usize), )) { let process_id = data.get_process_id().unwrap_or(0); info!( "Port {}/{} released pid={}", data.get_value_u16(Fields::IpLocalPort as usize), get_protocol(&data, Fields::IpProtocol as usize), process_id, ); for conn in conns { let info = protocol::info::connection_end_event_v4_info( process_id, conn.get_direction() as u8, data.get_value_u8(Fields::IpProtocol as usize), conn.local_address.0, conn.remote_address.0, conn.local_port, conn.remote_port, ); let _ = device.event_queue.push(info); } } } layer::Layer::AleResourceReleaseV6 => { type Fields = layer::FieldsAleResourceReleaseV6; if let Some(conns) = device.connection_cache.end_all_on_port_v6(( get_protocol(&data, Fields::IpProtocol as usize), data.get_value_u16(Fields::IpLocalPort as usize), )) { let process_id = data.get_process_id().unwrap_or(0); info!( "Port {}/{} released pid={}", data.get_value_u16(Fields::IpLocalPort as usize), get_protocol(&data, Fields::IpProtocol as usize), process_id, ); for conn in conns { let info = protocol::info::connection_end_event_v6_info( process_id, conn.get_direction() as u8, data.get_value_u8(Fields::IpProtocol as usize), conn.local_address.0, conn.remote_address.0, conn.local_port, conn.remote_port, ); let _ = device.event_queue.push(info); } } } _ => {} } } ================================================ FILE: windows_kext/driver/src/array_holder.rs ================================================ use core::cell::RefCell; use alloc::vec::Vec; pub struct ArrayHolder(RefCell>>); unsafe impl Sync for ArrayHolder {} impl ArrayHolder { pub const fn default() -> Self { Self(RefCell::new(None)) } pub fn save(&self, data: &[u8]) { if let Ok(mut opt) = self.0.try_borrow_mut() { opt.replace(data.to_vec()); } } pub fn load(&self) -> Option> { if let Ok(mut opt) = self.0.try_borrow_mut() { return opt.take(); } None } } ================================================ FILE: windows_kext/driver/src/bandwidth.rs ================================================ use alloc::collections::BTreeMap; use protocol::info::{BandwidthValueV4, BandwidthValueV6, Info}; use smoltcp::wire::{IpProtocol, Ipv4Address, Ipv6Address}; use wdk::rw_spin_lock::RwSpinLock; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] pub struct Key { pub local_ip: Address, pub local_port: u16, pub remote_ip: Address, pub remote_port: u16, } struct Value { received_bytes: usize, transmitted_bytes: usize, } enum Direction { Tx(usize), Rx(usize), } pub struct Bandwidth { stats_tcp_v4: BTreeMap, Value>, stats_tcp_v4_lock: RwSpinLock, stats_tcp_v6: BTreeMap, Value>, stats_tcp_v6_lock: RwSpinLock, stats_udp_v4: BTreeMap, Value>, stats_udp_v4_lock: RwSpinLock, stats_udp_v6: BTreeMap, Value>, stats_udp_v6_lock: RwSpinLock, } impl Bandwidth { pub fn new() -> Self { Self { stats_tcp_v4: BTreeMap::new(), stats_tcp_v4_lock: RwSpinLock::default(), stats_tcp_v6: BTreeMap::new(), stats_tcp_v6_lock: RwSpinLock::default(), stats_udp_v4: BTreeMap::new(), stats_udp_v4_lock: RwSpinLock::default(), stats_udp_v6: BTreeMap::new(), stats_udp_v6_lock: RwSpinLock::default(), } } pub fn get_all_updates_tcp_v4(&mut self) -> Option { let stats_map; { let _guard = self.stats_tcp_v4_lock.write_lock(); if self.stats_tcp_v4.is_empty() { return None; } stats_map = core::mem::replace(&mut self.stats_tcp_v4, BTreeMap::new()); } let mut values = alloc::vec::Vec::with_capacity(stats_map.len()); for (key, value) in stats_map.iter() { values.push(BandwidthValueV4 { local_ip: key.local_ip.0, local_port: key.local_port, remote_ip: key.remote_ip.0, remote_port: key.remote_port, transmitted_bytes: value.transmitted_bytes as u64, received_bytes: value.received_bytes as u64, }); } Some(protocol::info::bandiwth_stats_array_v4( u8::from(IpProtocol::Tcp), values, )) } pub fn get_all_updates_tcp_v6(&mut self) -> Option { let stats_map; { let _guard = self.stats_tcp_v6_lock.write_lock(); if self.stats_tcp_v6.is_empty() { return None; } stats_map = core::mem::replace(&mut self.stats_tcp_v6, BTreeMap::new()); } let mut values = alloc::vec::Vec::with_capacity(stats_map.len()); for (key, value) in stats_map.iter() { values.push(BandwidthValueV6 { local_ip: key.local_ip.0, local_port: key.local_port, remote_ip: key.remote_ip.0, remote_port: key.remote_port, transmitted_bytes: value.transmitted_bytes as u64, received_bytes: value.received_bytes as u64, }); } Some(protocol::info::bandiwth_stats_array_v6( u8::from(IpProtocol::Tcp), values, )) } pub fn get_all_updates_udp_v4(&mut self) -> Option { let stats_map; { let _guard = self.stats_udp_v4_lock.write_lock(); if self.stats_udp_v4.is_empty() { return None; } stats_map = core::mem::replace(&mut self.stats_udp_v4, BTreeMap::new()); } let mut values = alloc::vec::Vec::with_capacity(stats_map.len()); for (key, value) in stats_map.iter() { values.push(BandwidthValueV4 { local_ip: key.local_ip.0, local_port: key.local_port, remote_ip: key.remote_ip.0, remote_port: key.remote_port, transmitted_bytes: value.transmitted_bytes as u64, received_bytes: value.received_bytes as u64, }); } Some(protocol::info::bandiwth_stats_array_v4( u8::from(IpProtocol::Udp), values, )) } pub fn get_all_updates_udp_v6(&mut self) -> Option { let stats_map; { let _guard = self.stats_udp_v6_lock.write_lock(); if self.stats_udp_v6.is_empty() { return None; } stats_map = core::mem::replace(&mut self.stats_udp_v6, BTreeMap::new()); } let mut values = alloc::vec::Vec::with_capacity(stats_map.len()); for (key, value) in stats_map.iter() { values.push(BandwidthValueV6 { local_ip: key.local_ip.0, local_port: key.local_port, remote_ip: key.remote_ip.0, remote_port: key.remote_port, transmitted_bytes: value.transmitted_bytes as u64, received_bytes: value.received_bytes as u64, }); } Some(protocol::info::bandiwth_stats_array_v6( u8::from(IpProtocol::Udp), values, )) } pub fn update_tcp_v4_tx(&mut self, key: Key, tx_bytes: usize) { Self::update( &mut self.stats_tcp_v4, &mut self.stats_tcp_v4_lock, key, Direction::Tx(tx_bytes), ); } pub fn update_tcp_v4_rx(&mut self, key: Key, rx_bytes: usize) { Self::update( &mut self.stats_tcp_v4, &mut self.stats_tcp_v4_lock, key, Direction::Rx(rx_bytes), ); } pub fn update_tcp_v6_tx(&mut self, key: Key, tx_bytes: usize) { Self::update( &mut self.stats_tcp_v6, &mut self.stats_tcp_v6_lock, key, Direction::Tx(tx_bytes), ); } pub fn update_tcp_v6_rx(&mut self, key: Key, rx_bytes: usize) { Self::update( &mut self.stats_tcp_v6, &mut self.stats_tcp_v6_lock, key, Direction::Rx(rx_bytes), ); } pub fn update_udp_v4_tx(&mut self, key: Key, tx_bytes: usize) { Self::update( &mut self.stats_udp_v4, &mut self.stats_udp_v4_lock, key, Direction::Tx(tx_bytes), ); } pub fn update_udp_v4_rx(&mut self, key: Key, rx_bytes: usize) { Self::update( &mut self.stats_udp_v4, &mut self.stats_udp_v4_lock, key, Direction::Rx(rx_bytes), ); } pub fn update_udp_v6_tx(&mut self, key: Key, tx_bytes: usize) { Self::update( &mut self.stats_udp_v6, &mut self.stats_udp_v6_lock, key, Direction::Tx(tx_bytes), ); } pub fn update_udp_v6_rx(&mut self, key: Key, rx_bytes: usize) { Self::update( &mut self.stats_udp_v6, &mut self.stats_udp_v6_lock, key, Direction::Rx(rx_bytes), ); } fn update( map: &mut BTreeMap, Value>, lock: &mut RwSpinLock, key: Key
, bytes: Direction, ) { let _guard = lock.write_lock(); if let Some(value) = map.get_mut(&key) { match bytes { Direction::Tx(bytes_count) => value.transmitted_bytes += bytes_count, Direction::Rx(bytes_count) => value.received_bytes += bytes_count, } } else { let mut received_bytes = 0; let mut transmitted_bytes = 0; match bytes { Direction::Tx(bytes_count) => transmitted_bytes += bytes_count, Direction::Rx(bytes_count) => received_bytes += bytes_count, } map.insert( key, Value { received_bytes, transmitted_bytes, }, ); } } #[allow(dead_code)] pub fn get_entries_count(&self) -> usize { let mut size = 0; { let values = &self.stats_tcp_v4.values(); let _guard = self.stats_tcp_v4_lock.read_lock(); size += values.len(); } { let values = &self.stats_tcp_v6.values(); let _guard = self.stats_tcp_v6_lock.read_lock(); size += values.len(); } { let values = &self.stats_udp_v4.values(); let _guard = self.stats_udp_v4_lock.read_lock(); size += values.len(); } { let values = &self.stats_udp_v6.values(); let _guard = self.stats_udp_v6_lock.read_lock(); size += values.len(); } return size; } } ================================================ FILE: windows_kext/driver/src/callouts.rs ================================================ use alloc::vec::Vec; use wdk::filter_engine::callout::FilterType; use wdk::{ consts, filter_engine::{callout::Callout, layer::Layer}, }; use crate::{ale_callouts, packet_callouts, stream_callouts}; pub fn get_callout_vec() -> Vec { alloc::vec![ // ----------------------------------------- // ALE Auth layers Callout::new( "Portmaster ALE Outbound IPv4", "Portmaster uses this layer to block/permit outgoing ipv4 connections", 0x58545073_f893_454c_bbea_a57bc964f46d, Layer::AleAuthConnectV4, consts::FWP_ACTION_CALLOUT_TERMINATING, FilterType::Resettable, ale_callouts::ale_layer_connect_v4, ), Callout::new( "Portmaster ALE Outbound IPv6", "Portmaster uses this layer to block/permit outgoing ipv6 connections", 0x4bd2a080_2585_478d_977c_7f340c6bc3d4, Layer::AleAuthConnectV6, consts::FWP_ACTION_CALLOUT_TERMINATING, FilterType::Resettable, ale_callouts::ale_layer_connect_v6, ), // ----------------------------------------- // ALE connection end layers Callout::new( "Portmaster Endpoint Closure IPv4", "Portmaster uses this layer to detect when a IPv4 connection has ended", 0x58f02845_ace9_4455_ac80_8a84b86fe566, Layer::AleEndpointClosureV4, consts::FWP_ACTION_CALLOUT_INSPECTION, FilterType::NonResettable, ale_callouts::endpoint_closure_v4, ), Callout::new( "Portmaster Endpoint Closure IPv6", "Portmaster uses this layer to detect when a IPv6 connection has ended", 0x2bc82359_9dc5_4315_9c93_c89467e283ce, Layer::AleEndpointClosureV6, consts::FWP_ACTION_CALLOUT_INSPECTION, FilterType::NonResettable, ale_callouts::endpoint_closure_v6, ), // ----------------------------------------- // ALE resource assignment and release. // Callout::new( // "AleResourceAssignmentV4", // "Ipv4 Port assignment monitoring", // 0x6b9d1985_6f75_4d05_b9b5_1607e187906f, // Layer::AleResourceAssignmentV4Discard, // consts::FWP_ACTION_CALLOUT_INSPECTION, // FilterType::NonResettable, // ale_callouts::ale_resource_monitor, // ), Callout::new( "Portmaster resource release IPv4", "Portmaster uses this layer to detect when a IPv4 port has been released", 0x7b513bb3_a0be_4f77_a4bc_03c052abe8d7, Layer::AleResourceReleaseV4, consts::FWP_ACTION_CALLOUT_INSPECTION, FilterType::NonResettable, ale_callouts::ale_resource_monitor, ), // Callout::new( // "AleResourceAssignmentV6", // "Ipv4 Port assignment monitor", // 0xb0d02299_3d3e_437d_916a_f0e96a60cc18, // Layer::AleResourceAssignmentV6Discard, // consts::FWP_ACTION_CALLOUT_INSPECTION, // FilterType::NonResettable, // ale_callouts::ale_resource_monitor, // ), Callout::new( "Portmaster resource release IPv6", "Portmaster uses this layer to detect when a IPv6 port has been released", 0x6cf36e04_e656_42c3_8cac_a1ce05328bd1, Layer::AleResourceReleaseV6, consts::FWP_ACTION_CALLOUT_INSPECTION, FilterType::NonResettable, ale_callouts::ale_resource_monitor, ), // ----------------------------------------- // Stream layer Callout::new( "Portmaster Stream IPv4", "Portmaster uses this layer for bandwidth statistics of IPv4 TCP connections", 0xe2ca13bf_9710_4caa_a45c_e8c78b5ac780, Layer::StreamV4, consts::FWP_ACTION_CALLOUT_INSPECTION, FilterType::NonResettable, stream_callouts::stream_layer_tcp_v4, ), Callout::new( "Portmaster Stream IPv6", "Portmaster uses this layer for bandwidth statistics of IPv6 TCP connections", 0x66c549b3_11e2_4b27_8f73_856e6fd82baa, Layer::StreamV6, consts::FWP_ACTION_CALLOUT_INSPECTION, FilterType::NonResettable, stream_callouts::stream_layer_tcp_v6, ), Callout::new( "Portmaster Datagram IPv4", "Portmaster uses this layer for bandwidth statistics of IPv4 UDP connections", 0xe7eeeaba_168a_45bb_8747_e1a702feb2c5, Layer::DatagramDataV4, consts::FWP_ACTION_CALLOUT_INSPECTION, FilterType::NonResettable, stream_callouts::stream_layer_udp_v4, ), Callout::new( "Portmaster Datagram IPv6", "Portmaster uses this layer for bandwidth statistics of IPv6 UDP connections", 0xb25862cd_f744_4452_b14a_d0c1e5a25b30, Layer::DatagramDataV6, consts::FWP_ACTION_CALLOUT_INSPECTION, FilterType::NonResettable, stream_callouts::stream_layer_udp_v6, ), // ----------------------------------------- // Packet layers Callout::new( "Portmaster Packet Outbound IPv4", "Portmaster uses this layer to redirect/block/permit outgoing ipv4 packets", 0xf3183afe_dc35_49f1_8ea2_b16b5666dd36, Layer::OutboundIppacketV4, consts::FWP_ACTION_CALLOUT_TERMINATING, FilterType::NonResettable, packet_callouts::ip_packet_layer_outbound_v4, ), Callout::new( "Portmaster Packet Inbound IPv4", "Portmaster uses this layer to redirect/block/permit inbound ipv4 packets", 0xf0369374_203d_4bf0_83d2_b2ad3cc17a50, Layer::InboundIppacketV4, consts::FWP_ACTION_CALLOUT_TERMINATING, FilterType::NonResettable, packet_callouts::ip_packet_layer_inbound_v4, ), Callout::new( "Portmaster Packet Outbound IPv6", "Portmaster uses this layer to redirect/block/permit outgoing ipv6 packets", 0x91daf8bc_0908_4bf8_9f81_2c538ab8f25a, Layer::OutboundIppacketV6, consts::FWP_ACTION_CALLOUT_TERMINATING, FilterType::NonResettable, packet_callouts::ip_packet_layer_outbound_v6, ), Callout::new( "Portmaster Packet Inbound IPv6", "Portmaster uses this layer to redirect/block/permit inbound ipv6 packets", 0xfe9faf5f_ceb2_4cd9_9995_f2f2b4f5fcc0, Layer::InboundIppacketV6, consts::FWP_ACTION_CALLOUT_TERMINATING, FilterType::NonResettable, packet_callouts::ip_packet_layer_inbound_v6, ) ] } ================================================ FILE: windows_kext/driver/src/common.rs ================================================ #![allow(dead_code)] use core::fmt::Display; use num_derive::{FromPrimitive, ToPrimitive}; pub const ICMPV4_CODE_DESTINATION_UNREACHABLE: u32 = 3; pub const ICMPV4_CODE_DU_PORT_UNREACHABLE: u32 = 3; // Destination Unreachable (Port unreachable) ; pub const ICMPV4_CODE_DU_ADMINISTRATIVELY_PROHIBITED: u32 = 13; // Destination Unreachable (Communication Administratively Prohibited) ; pub const ICMPV6_CODE_DESTINATION_UNREACHABLE: u32 = 1; pub const ICMPV6_CODE_DU_PORT_UNREACHABLE: u32 = 4; // Destination Unreachable (Port unreachable) ; enum Direction { Outbound = 0, Inbound = 1, } const SIOCTL_TYPE: u32 = 40000; macro_rules! ctl_code { ($device_type:expr, $function:expr, $method:expr, $access:expr) => { ($device_type << 16) | ($access << 14) | ($function << 2) | $method }; } pub const METHOD_BUFFERED: u32 = 0; pub const METHOD_IN_DIRECT: u32 = 1; pub const METHOD_OUT_DIRECT: u32 = 2; pub const METHOD_NEITHER: u32 = 3; pub const FILE_READ_DATA: u32 = 0x0001; // file & pipe pub const FILE_WRITE_DATA: u32 = 0x0002; // file & pipe #[repr(u32)] #[derive(FromPrimitive, ToPrimitive)] pub enum ControlCode { Version = ctl_code!( SIOCTL_TYPE, 0x800, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA ), ShutdownRequest = ctl_code!( SIOCTL_TYPE, 0x801, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA ), } impl Display for ControlCode { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { ControlCode::Version => _ = write!(f, "Version"), ControlCode::ShutdownRequest => _ = write!(f, "Shutdown"), }; return Ok(()); } } ================================================ FILE: windows_kext/driver/src/connection.rs ================================================ use alloc::{ boxed::Box, string::{String, ToString}, }; use core::{ fmt::{Debug, Display}, sync::atomic::{AtomicU64, Ordering}, }; use num_derive::FromPrimitive; use smoltcp::wire::{IpAddress, IpProtocol, Ipv4Address, Ipv6Address}; use crate::connection_map::Key; pub static PM_DNS_PORT: u16 = 53; pub static PM_SPN_PORT: u16 = 717; pub static PM_SPLIT_TUN_PORT: u16 = 719; // Make sure this in sync with the Go version #[derive(Copy, Clone, FromPrimitive)] #[repr(u8)] #[rustfmt::skip] pub enum Verdict { Undecided = 0, // Undecided is the default status of new connections. Undeterminable = 1, Accept = 2, PermanentAccept = 3, Block = 4, PermanentBlock = 5, Drop = 6, PermanentDrop = 7, RedirectNameServer = 8, // redirect to PM_DNS_PORT port RedirectTunnel = 9, // redirect to PM_SPN_PORT port Failed = 10, RedirectSplitTunnel= 11, // redirect to PM_SPLIT_TUN_PORT port // RedirectSplitTunnel must stay last: older Portmaster versions only know verdicts 0–10 and would never send this value. } impl Display for Verdict { #[rustfmt::skip] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Verdict::Undecided => write!(f, "Undecided"), Verdict::Undeterminable => write!(f, "Undeterminable"), Verdict::Accept => write!(f, "Accept"), Verdict::PermanentAccept => write!(f, "PermanentAccept"), Verdict::Block => write!(f, "Block"), Verdict::PermanentBlock => write!(f, "PermanentBlock"), Verdict::Drop => write!(f, "Drop"), Verdict::PermanentDrop => write!(f, "PermanentDrop"), Verdict::RedirectNameServer => write!(f, "RedirectNameServer"), Verdict::RedirectTunnel => write!(f, "RedirectTunnel"), Verdict::RedirectSplitTunnel=> write!(f, "RedirectSplitTunnel"), Verdict::Failed => write!(f, "Failed"), } } } /// Direction of the connection. #[derive(Copy, Clone, FromPrimitive)] #[repr(u8)] pub enum Direction { Outbound = 0, Inbound = 1, } impl Display for Direction { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Direction::Outbound => write!(f, "Outbound"), Direction::Inbound => write!(f, "Inbound"), } } } impl Debug for Direction { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{}", self) } } #[derive(Clone)] pub struct ConnectionExtra { pub(crate) end_timestamp: u64, pub(crate) direction: Direction, } pub trait Connection { fn redirect_info(&self) -> Option { let redirect_address = if self.is_ipv6() { IpAddress::Ipv6(Ipv6Address::LOOPBACK) } else { IpAddress::Ipv4(Ipv4Address::new(127, 0, 0, 1)) }; match self.get_verdict() { Verdict::RedirectNameServer => Some(RedirectInfo { local_address: self.get_local_address(), remote_address: self.get_remote_address(), remote_port: self.get_remote_port(), redirect_port: PM_DNS_PORT, unify: false, redirect_address, }), Verdict::RedirectTunnel => Some(RedirectInfo { local_address: self.get_local_address(), remote_address: self.get_remote_address(), remote_port: self.get_remote_port(), redirect_port: PM_SPN_PORT, unify: true, redirect_address, }), Verdict::RedirectSplitTunnel => Some(RedirectInfo { local_address: self.get_local_address(), remote_address: self.get_remote_address(), remote_port: self.get_remote_port(), redirect_port: PM_SPLIT_TUN_PORT, unify: true, redirect_address, }), _ => None, } } /// Returns the key of the connection. fn get_key(&self) -> Key { Key { protocol: self.get_protocol(), local_address: self.get_local_address(), local_port: self.get_local_port(), remote_address: self.get_remote_address(), remote_port: self.get_remote_port(), } } /// Returns true if the connection is equal to the given key. The key is considered equal if the remote port and address are equal. fn remote_equals(&self, key: &Key) -> bool; /// Returns true if the connection is equal to the given key for redirecting. The key is considered equal if the remote port and address are equal. fn redirect_equals(&self, key: &Key) -> bool; /// Returns the protocol of the connection. fn get_protocol(&self) -> IpProtocol; /// Returns the verdict of the connection. fn get_verdict(&self) -> Verdict; /// Returns the local address of the connection. fn get_local_address(&self) -> IpAddress; /// Returns the local port of the connection. fn get_local_port(&self) -> u16; /// Returns the remote address of the connection. fn get_remote_address(&self) -> IpAddress; /// Returns the remote port of the connection. fn get_remote_port(&self) -> u16; /// Returns true if the connection is an IPv6 connection. fn is_ipv6(&self) -> bool; /// Returns the direction of the connection. fn get_direction(&self) -> Direction; // Returns the process id of the connection. fn get_process_id(&self) -> u64; /// Ends the connection. fn end(&mut self, timestamp: u64); /// Returns true if the connection has ended. fn has_ended(&self) -> bool { self.get_end_time() > 0 } /// Returns the timestamp when the connection ended. fn get_end_time(&self) -> u64; /// Returns the timestamp when the connection was last accessed. fn get_last_accessed_time(&self) -> u64; /// Sets the timestamp when the connection was last accessed. fn set_last_accessed_time(&self, timestamp: u64); } pub struct ConnectionV4 { pub(crate) protocol: IpProtocol, pub(crate) local_address: Ipv4Address, pub(crate) local_port: u16, pub(crate) remote_address: Ipv4Address, pub(crate) remote_port: u16, pub(crate) verdict: Verdict, pub(crate) process_id: u64, pub(crate) last_accessed_timestamp: AtomicU64, pub(crate) extra: Box, } pub struct ConnectionV6 { pub(crate) protocol: IpProtocol, pub(crate) local_address: Ipv6Address, pub(crate) local_port: u16, pub(crate) remote_address: Ipv6Address, pub(crate) remote_port: u16, pub(crate) verdict: Verdict, pub(crate) process_id: u64, pub(crate) last_accessed_timestamp: AtomicU64, pub(crate) extra: Box, } #[derive(Debug)] pub struct RedirectInfo { pub(crate) local_address: IpAddress, pub(crate) remote_address: IpAddress, pub(crate) remote_port: u16, pub(crate) redirect_port: u16, pub(crate) unify: bool, pub(crate) redirect_address: IpAddress, } impl ConnectionV4 { /// Creates a new ipv4 connection from the given key. pub fn from_key(key: &Key, process_id: u64, direction: Direction) -> Result { let IpAddress::Ipv4(local_address) = key.local_address else { return Err("wrong ip address version".to_string()); }; let IpAddress::Ipv4(remote_address) = key.remote_address else { return Err("wrong ip address version".to_string()); }; let timestamp = wdk::utils::get_system_timestamp_ms(); Ok(Self { protocol: key.protocol, local_address, local_port: key.local_port, remote_address, remote_port: key.remote_port, verdict: Verdict::Undecided, process_id, last_accessed_timestamp: AtomicU64::new(timestamp), extra: Box::new(ConnectionExtra { direction, end_timestamp: 0, }), }) } } impl Connection for ConnectionV4 { fn remote_equals(&self, key: &Key) -> bool { if self.remote_port != key.remote_port { return false; } if let IpAddress::Ipv4(remote_address) = &key.remote_address { return self.remote_address.eq(remote_address); } false } fn get_key(&self) -> Key { Key { protocol: self.protocol, local_address: IpAddress::Ipv4(self.local_address), local_port: self.local_port, remote_address: IpAddress::Ipv4(self.remote_address), remote_port: self.remote_port, } } fn redirect_equals(&self, key: &Key) -> bool { match self.verdict { Verdict::RedirectNameServer => { if key.remote_port != PM_DNS_PORT { return false; } match key.remote_address { IpAddress::Ipv4(a) => a.is_loopback(), IpAddress::Ipv6(_) => false, } } Verdict::RedirectTunnel => { if key.remote_port != PM_SPN_PORT { return false; } key.local_address.eq(&key.remote_address) } Verdict::RedirectSplitTunnel => { if key.remote_port != PM_SPLIT_TUN_PORT { return false; } key.local_address.eq(&key.remote_address) } _ => false, } } fn get_protocol(&self) -> IpProtocol { self.protocol } fn get_verdict(&self) -> Verdict { self.verdict } fn get_local_address(&self) -> IpAddress { IpAddress::Ipv4(self.local_address) } fn get_local_port(&self) -> u16 { self.local_port } fn get_remote_address(&self) -> IpAddress { IpAddress::Ipv4(self.remote_address) } fn get_remote_port(&self) -> u16 { self.remote_port } fn is_ipv6(&self) -> bool { false } fn get_process_id(&self) -> u64 { self.process_id } fn get_direction(&self) -> Direction { self.extra.direction } fn end(&mut self, timestamp: u64) { self.extra.end_timestamp = timestamp; } fn get_end_time(&self) -> u64 { self.extra.end_timestamp } fn get_last_accessed_time(&self) -> u64 { self.last_accessed_timestamp.load(Ordering::Relaxed) } fn set_last_accessed_time(&self, timestamp: u64) { self.last_accessed_timestamp .store(timestamp, Ordering::Relaxed); } } impl Clone for ConnectionV4 { fn clone(&self) -> Self { Self { protocol: self.protocol, local_address: self.local_address, local_port: self.local_port, remote_address: self.remote_address, remote_port: self.remote_port, verdict: self.verdict, process_id: self.process_id, last_accessed_timestamp: AtomicU64::new( self.last_accessed_timestamp.load(Ordering::Relaxed), ), extra: self.extra.clone(), } } } impl ConnectionV6 { /// Creates a new ipv6 connection from the given key. pub fn from_key(key: &Key, process_id: u64, direction: Direction) -> Result { let IpAddress::Ipv6(local_address) = key.local_address else { return Err("wrong ip address version".to_string()); }; let IpAddress::Ipv6(remote_address) = key.remote_address else { return Err("wrong ip address version".to_string()); }; let timestamp = wdk::utils::get_system_timestamp_ms(); Ok(Self { protocol: key.protocol, local_address, local_port: key.local_port, remote_address, remote_port: key.remote_port, verdict: Verdict::Undecided, process_id, last_accessed_timestamp: AtomicU64::new(timestamp), extra: Box::new(ConnectionExtra { direction, end_timestamp: 0, }), }) } } impl Connection for ConnectionV6 { fn remote_equals(&self, key: &Key) -> bool { if self.remote_port != key.remote_port { return false; } if let IpAddress::Ipv6(remote_address) = &key.remote_address { return self.remote_address.eq(remote_address); } false } fn get_key(&self) -> Key { Key { protocol: self.protocol, local_address: IpAddress::Ipv6(self.local_address), local_port: self.local_port, remote_address: IpAddress::Ipv6(self.remote_address), remote_port: self.remote_port, } } fn redirect_equals(&self, key: &Key) -> bool { match self.verdict { Verdict::RedirectNameServer => { if key.remote_port != PM_DNS_PORT { return false; } match key.remote_address { IpAddress::Ipv4(_) => false, IpAddress::Ipv6(a) => a.is_loopback(), } } Verdict::RedirectTunnel => { if key.remote_port != PM_SPN_PORT { return false; } key.local_address.eq(&key.remote_address) } Verdict::RedirectSplitTunnel => { if key.remote_port != PM_SPLIT_TUN_PORT { return false; } key.local_address.eq(&key.remote_address) } _ => false, } } fn get_protocol(&self) -> IpProtocol { self.protocol } fn get_verdict(&self) -> Verdict { self.verdict } fn get_local_address(&self) -> IpAddress { IpAddress::Ipv6(self.local_address) } fn get_local_port(&self) -> u16 { self.local_port } fn get_remote_address(&self) -> IpAddress { IpAddress::Ipv6(self.remote_address) } fn get_remote_port(&self) -> u16 { self.remote_port } fn is_ipv6(&self) -> bool { true } fn get_process_id(&self) -> u64 { self.process_id } fn get_direction(&self) -> Direction { self.extra.direction } fn end(&mut self, timestamp: u64) { self.extra.end_timestamp = timestamp; } fn get_end_time(&self) -> u64 { self.extra.end_timestamp } fn get_last_accessed_time(&self) -> u64 { self.last_accessed_timestamp.load(Ordering::Relaxed) } fn set_last_accessed_time(&self, timestamp: u64) { self.last_accessed_timestamp .store(timestamp, Ordering::Relaxed); } } impl Clone for ConnectionV6 { fn clone(&self) -> Self { Self { protocol: self.protocol, local_address: self.local_address, local_port: self.local_port, remote_address: self.remote_address, remote_port: self.remote_port, verdict: self.verdict, process_id: self.process_id, last_accessed_timestamp: AtomicU64::new( self.last_accessed_timestamp.load(Ordering::Relaxed), ), extra: self.extra.clone(), } } } ================================================ FILE: windows_kext/driver/src/connection_cache.rs ================================================ use crate::{ connection::{Connection, ConnectionV4, ConnectionV6, RedirectInfo, Verdict}, connection_map::{ConnectionMap, Key}, }; use alloc::vec::Vec; use smoltcp::wire::IpProtocol; use wdk::rw_spin_lock::RwSpinLock; pub struct ConnectionCache { connections_v4: ConnectionMap, connections_v6: ConnectionMap, lock_v4: RwSpinLock, lock_v6: RwSpinLock, } impl ConnectionCache { pub fn new() -> Self { Self { connections_v4: ConnectionMap::new(), connections_v6: ConnectionMap::new(), lock_v4: RwSpinLock::default(), lock_v6: RwSpinLock::default(), } } pub fn add_connection_v4(&mut self, connection: ConnectionV4) { let _guard = self.lock_v4.write_lock(); self.connections_v4.add(connection); } pub fn add_connection_v6(&mut self, connection: ConnectionV6) { let _guard = self.lock_v6.write_lock(); self.connections_v6.add(connection); } pub fn update_connection(&mut self, key: Key, verdict: Verdict) -> Option { if key.is_ipv6() { let _guard = self.lock_v6.write_lock(); if let Some(conn) = self.connections_v6.get_mut(&key) { conn.verdict = verdict; return conn.redirect_info(); } } else { let _guard = self.lock_v4.write_lock(); if let Some(conn) = self.connections_v4.get_mut(&key) { conn.verdict = verdict; return conn.redirect_info(); } } None } pub fn read_connection_v4( &self, key: &Key, process_connection: fn(&ConnectionV4) -> Option, ) -> Option { let _guard = self.lock_v4.read_lock(); self.connections_v4.read(key, process_connection) } pub fn read_connection_v6( &self, key: &Key, process_connection: fn(&ConnectionV6) -> Option, ) -> Option { let _guard = self.lock_v6.read_lock(); self.connections_v6.read(key, process_connection) } pub fn end_connection_v4(&mut self, key: Key) -> Option { let _guard = self.lock_v4.write_lock(); self.connections_v4.end(key) } pub fn end_connection_v6(&mut self, key: Key) -> Option { let _guard = self.lock_v6.write_lock(); self.connections_v6.end(key) } pub fn end_all_on_port_v4(&mut self, key: (IpProtocol, u16)) -> Option> { let _guard = self.lock_v4.write_lock(); self.connections_v4.end_all_on_port(key) } pub fn end_all_on_port_v6(&mut self, key: (IpProtocol, u16)) -> Option> { let _guard = self.lock_v6.write_lock(); self.connections_v6.end_all_on_port(key) } pub fn clean_ended_connections(&mut self) { { let _guard = self.lock_v4.write_lock(); self.connections_v4.clean_ended_connections(); } { let _guard = self.lock_v6.write_lock(); self.connections_v6.clean_ended_connections(); } } pub fn clear(&mut self) { { let _guard = self.lock_v4.write_lock(); self.connections_v4.clear(); } { let _guard = self.lock_v6.write_lock(); self.connections_v6.clear(); } } #[allow(dead_code)] pub fn get_entries_count(&self) -> usize { let mut size = 0; { let _guard = self.lock_v4.read_lock(); size += self.connections_v4.get_count(); } { let _guard = self.lock_v6.read_lock(); size += self.connections_v6.get_count(); } return size; } } ================================================ FILE: windows_kext/driver/src/connection_map.rs ================================================ use core::{fmt::Display, time::Duration}; use crate::connection::Connection; use alloc::{collections::BTreeMap, vec::Vec}; use smoltcp::wire::{IpAddress, IpProtocol}; #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] pub struct Key { pub(crate) protocol: IpProtocol, pub(crate) local_address: IpAddress, pub(crate) local_port: u16, pub(crate) remote_address: IpAddress, pub(crate) remote_port: u16, } impl Display for Key { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!( f, "p: {} l: {}:{} r: {}:{}", self.protocol, self.local_address, self.local_port, self.remote_address, self.remote_port ) } } impl Key { /// Returns the protocol and port as a tuple. pub fn small(&self) -> (IpProtocol, u16) { (self.protocol, self.local_port) } /// Returns true if the local address is an IPv4 address. pub fn is_ipv6(&self) -> bool { match self.local_address { IpAddress::Ipv4(_) => false, IpAddress::Ipv6(_) => true, } } /// Returns true if the local address is a loopback address. pub fn is_loopback(&self) -> bool { match self.local_address { IpAddress::Ipv4(ip) => ip.is_loopback(), IpAddress::Ipv6(ip) => ip.is_loopback(), } } /// Returns a new key with the local and remote addresses and ports reversed. #[allow(dead_code)] pub fn reverse(&self) -> Key { Key { protocol: self.protocol, local_address: self.remote_address, local_port: self.remote_port, remote_address: self.local_address, remote_port: self.local_port, } } } pub struct ConnectionMap(BTreeMap<(IpProtocol, u16), Vec>); impl ConnectionMap { pub fn new() -> Self { Self(BTreeMap::new()) } pub fn add(&mut self, conn: T) { let key = conn.get_key().small(); if let Some(connections) = self.0.get_mut(&key) { connections.push(conn); } else { self.0.insert(key, alloc::vec![conn]); } } pub fn get_mut(&mut self, key: &Key) -> Option<&mut T> { if let Some(connections) = self.0.get_mut(&key.small()) { for conn in connections { if conn.remote_equals(key) { conn.set_last_accessed_time(wdk::utils::get_system_timestamp_ms()); return Some(conn); } } } None } pub fn read(&self, key: &Key, read_connection: fn(&T) -> Option) -> Option { if let Some(connections) = self.0.get(&key.small()) { for conn in connections { if conn.remote_equals(key) { conn.set_last_accessed_time(wdk::utils::get_system_timestamp_ms()); return read_connection(conn); } if conn.redirect_equals(key) { conn.set_last_accessed_time(wdk::utils::get_system_timestamp_ms()); return read_connection(conn); } } } None } pub fn end(&mut self, key: Key) -> Option { if let Some(connections) = self.0.get_mut(&key.small()) { for conn in connections.iter_mut() { if conn.remote_equals(&key) { conn.end(wdk::utils::get_system_timestamp_ms()); return Some(conn.clone()); } } } return None; } pub fn end_all_on_port(&mut self, key: (IpProtocol, u16)) -> Option> { if let Some(connections) = self.0.get_mut(&key) { let mut vec = Vec::with_capacity(connections.len()); for conn in connections.iter_mut() { if !conn.has_ended() { conn.end(wdk::utils::get_system_timestamp_ms()); vec.push(conn.clone()); } } return Some(vec); } return None; } pub fn clear(&mut self) { self.0.clear(); } pub fn clean_ended_connections(&mut self) { let now = wdk::utils::get_system_timestamp_ms(); const TEN_MINUETS: u64 = Duration::from_secs(60 * 10).as_millis() as u64; let before_ten_minutes = now - TEN_MINUETS; let before_one_minute = now - Duration::from_secs(60).as_millis() as u64; for (_, connections) in self.0.iter_mut() { connections.retain(|c| { if c.has_ended() && c.get_end_time() < before_one_minute { // Ended more than 1 minute ago return false; } if c.get_last_accessed_time() < before_ten_minutes { // Last active more than 10 minutes ago return false; } // Keep return true; }); } self.0.retain(|_, v| !v.is_empty()); } pub fn get_count(&self) -> usize { let mut count = 0; for conn in self.0.values() { count += conn.len(); } return count; } } ================================================ FILE: windows_kext/driver/src/device.rs ================================================ use alloc::string::String; use core::sync::atomic::{AtomicU32, Ordering}; use num_traits::FromPrimitive; use protocol::{command::CommandType, info::Info}; use smoltcp::wire::{IpAddress, IpProtocol, Ipv4Address, Ipv6Address}; use wdk::{ driver::Driver, filter_engine::{ callout_data::ClassifyDefer, net_buffer::{NetBufferList, NetworkAllocator}, packet::{InjectInfo, Injector}, FilterEngine, }, ioqueue::{self, IOQueue}, irp_helpers::{ReadRequest, WriteRequest}, }; use crate::{ array_holder::ArrayHolder, bandwidth::Bandwidth, callouts, connection_cache::ConnectionCache, connection_map::Key, dbg, err, id_cache::IdCache, logger, packet_util::Redirect, }; pub enum Packet { PacketLayer(NetBufferList, InjectInfo), AleLayer(ClassifyDefer), } // Device Context pub struct Device { pub(crate) filter_engine: FilterEngine, pub(crate) read_leftover: ArrayHolder, pub(crate) event_queue: IOQueue, // Queue for events to user-space pub(crate) packet_cache: IdCache, // Cache of pending packets waiting for verdict pub(crate) connection_cache: ConnectionCache, // Cache of connections and their verdicts pub(crate) injector: Injector, pub(crate) network_allocator: NetworkAllocator, pub(crate) bandwidth_stats: Bandwidth, /// PID of the user-space process that currently holds the device handle open. /// Written once on IRP_MJ_CREATE, cleared on IRP_MJ_CLEANUP. /// AtomicU32 gives lock-free reads in callouts with zero overhead. pub(crate) owner_pid: AtomicU32, } impl Device { /// Initialize all members of the device. Memory is handled by windows. /// Make sure everything is initialized here. pub fn new(driver: &Driver) -> Result { let mut filter_engine = match FilterEngine::new(driver, 0x7dab1057_8e2b_40c4_9b85_693e381d7896) { Ok(fe) => fe, Err(err) => return Err(alloc::format!("filter engine error: {}", err)), }; filter_engine.commit(callouts::get_callout_vec())?; Ok(Self { filter_engine, read_leftover: ArrayHolder::default(), event_queue: IOQueue::new(), packet_cache: IdCache::new(), connection_cache: ConnectionCache::new(), injector: Injector::new(), network_allocator: NetworkAllocator::new(), bandwidth_stats: Bandwidth::new(), owner_pid: AtomicU32::new(0), }) } /// Returns the PID of the process that currently has the device handle open, or 0 if none. pub fn is_owner_pid(&self, pid: u32) -> bool { let p = self.owner_pid.load(Ordering::Acquire); p != 0 && p == pid } /// Cleanup is called just before drop. // pub fn cleanup(&mut self) {} fn write_buffer(&mut self, read_request: &mut ReadRequest, info: Info) { let bytes = info.as_bytes(); let count = read_request.write(bytes); // Check if the full buffer was written. if count < bytes.len() { // Save the leftovers for later. self.read_leftover.save(&bytes[count..]); } } /// Called when handle. Read is called from user-space. pub fn read(&mut self, read_request: &mut ReadRequest) { if let Some(data) = self.read_leftover.load() { // There are leftovers from previous request. let count = read_request.write(&data); // Check if full command was written. if count < data.len() { // Save the leftovers for later. self.read_leftover.save(&data[count..]); } } else { // Noting left from before. Wait for next commands. match self.event_queue.wait_and_pop() { Ok(info) => { self.write_buffer(read_request, info); } Err(ioqueue::Status::Timeout) => { // Timeout. This will only trigger if pop function is called with timeout. read_request.timeout(); return; } Err(err) => { // Queue failed. Send EOF, to notify user-space. Usually happens on rundown. err!("failed to pop value: {}", err); read_request.end_of_file(); return; } } } // Check if we have more space. InfoType + data_size == 5 bytes while read_request.free_space() > 5 { match self.event_queue.pop() { Ok(info) => { self.write_buffer(read_request, info); } Err(_) => { break; } } } read_request.complete(); } // Called when handle.Write is called from user-space. pub fn write(&mut self, write_request: &mut WriteRequest) { // Try parsing the command. let mut buffer = write_request.get_buffer(); let command = protocol::command::parse_type(buffer); let Some(command) = command else { err!("Unknown command number: {}", buffer[0]); return; }; buffer = &buffer[1..]; let mut _classify_defer = None; match command { CommandType::Shutdown => { wdk::dbg!("Shutdown command"); self.shutdown(); } CommandType::Verdict => { let verdict = protocol::command::parse_verdict(buffer); wdk::dbg!("Verdict command"); // Received verdict decision for a specific connection. if let Some((key, mut packet)) = self.packet_cache.pop_id(verdict.id) { if let Some(verdict) = FromPrimitive::from_u8(verdict.verdict) { dbg!("Verdict received {}: {}", key, verdict); // Add verdict in the cache. let redirect_info = self.connection_cache.update_connection(key, verdict); // if verdict.is_permanent() { // dbg!(self.logger, "resetting filters {}: {}", key, verdict); // _ = self.filter_engine.reset_all_filters(); // } match verdict { crate::connection::Verdict::Accept | crate::connection::Verdict::PermanentAccept => { if let Err(err) = self.inject_packet(packet, false) { err!("failed to inject packet: {}", err); } else { dbg!("packet injected: {}", key); } } crate::connection::Verdict::RedirectNameServer | crate::connection::Verdict::RedirectTunnel | crate::connection::Verdict::RedirectSplitTunnel => { if let Some(redirect_info) = redirect_info { // Will not redirect packets from ALE layer if let Err(err) = packet.redirect(redirect_info) { err!("failed to redirect packet: {}", err); } if let Err(err) = self.inject_packet(packet, false) { err!("failed to inject packet: {}", err); } } } _ => { // Inject only ALE layer. This will trigger proper block/drop. // Packet layer just drop the packet. if let Err(err) = self.inject_packet(packet, true) { err!("failed to inject packet: {}", err); } } } }; } else { // Id was not in the packet cache. let id = verdict.id; err!("Verdict invalid id: {}", id); } } CommandType::UpdateV4 => { let update = protocol::command::parse_update_v4(buffer); // Build the new action. if let Some(verdict) = FromPrimitive::from_u8(update.verdict) { // Update with new action. dbg!("Verdict update received {:?}: {}", update, verdict); _classify_defer = self.connection_cache.update_connection( Key { protocol: IpProtocol::from(update.protocol), local_address: IpAddress::Ipv4(Ipv4Address::from_bytes( &update.local_address, )), local_port: update.local_port, remote_address: IpAddress::Ipv4(Ipv4Address::from_bytes( &update.remote_address, )), remote_port: update.remote_port, }, verdict, ); } else { err!("invalid verdict value: {}", update.verdict); } } CommandType::UpdateV6 => { let update = protocol::command::parse_update_v6(buffer); // Build the new action. if let Some(verdict) = FromPrimitive::from_u8(update.verdict) { // Update with new action. dbg!("Verdict update received {:?}: {}", update, verdict); _classify_defer = self.connection_cache.update_connection( Key { protocol: IpProtocol::from(update.protocol), local_address: IpAddress::Ipv6(Ipv6Address::from_bytes( &update.local_address, )), local_port: update.local_port, remote_address: IpAddress::Ipv6(Ipv6Address::from_bytes( &update.remote_address, )), remote_port: update.remote_port, }, verdict, ); } else { err!("invalid verdict value: {}", update.verdict); } } CommandType::ClearCache => { wdk::dbg!("ClearCache command"); self.connection_cache.clear(); if let Err(err) = self.filter_engine.reset_all_filters() { err!("failed to reset filters: {}", err); } } CommandType::GetLogs => { wdk::dbg!("GetLogs command"); let lines_vec = logger::flush(); for line in lines_vec { let _ = self.event_queue.push(line); } } CommandType::GetBandwidthStats => { wdk::dbg!("GetBandwidthStats command"); let stats = self.bandwidth_stats.get_all_updates_tcp_v4(); if let Some(stats) = stats { _ = self.event_queue.push(stats); } let stats = self.bandwidth_stats.get_all_updates_tcp_v6(); if let Some(stats) = stats { _ = self.event_queue.push(stats); } let stats = self.bandwidth_stats.get_all_updates_udp_v4(); if let Some(stats) = stats { _ = self.event_queue.push(stats); } let stats = self.bandwidth_stats.get_all_updates_udp_v6(); if let Some(stats) = stats { _ = self.event_queue.push(stats); } } CommandType::PrintMemoryStats => { // Getting the information takes a long time and interferes with the callouts causing the device to crash. // TODO(vladimir): Make more optimized version // info!( // "Packet cache: {} entries", // self.packet_cache.get_entries_count() // ); // info!( // "BandwidthStats cache: {} entries", // self.bandwidth_stats.get_entries_count() // ); // info!( // "Connection cache: {} entries\n {}", // self.connection_cache.get_entries_count(), // self.connection_cache.get_full_cache_info() // ); } CommandType::CleanEndedConnections => { wdk::dbg!("CleanEndedConnections command"); self.connection_cache.clean_ended_connections(); } } } pub fn shutdown(&mut self) { // End blocking operations from the queue. This will end pending read requests. self.event_queue.rundown(); // Resolve all pending packets. This is important for proper driver unload. let pending_packets = self.packet_cache.pop_all(); for el in pending_packets { let key = el.value.0; let packet = el.value.1; // Set any verdict. Driver will unload after that and the filter will not be active. _ = self .connection_cache .update_connection(key, crate::connection::Verdict::PermanentBlock); _ = self.inject_packet(packet, true); // Blocked must be set, so it only handles the ALE layer. } } pub fn inject_packet(&mut self, packet: Packet, blocked: bool) -> Result<(), String> { match packet { Packet::PacketLayer(nbl, inject_info) => { if !blocked { self.injector.inject_net_buffer_list(nbl, inject_info) } else { Ok(()) } } Packet::AleLayer(defer) => { let packet_list = defer.complete(&mut self.filter_engine)?; if let Some(packet_list) = packet_list { self.injector.inject_packet_list_transport(packet_list)?; } Ok(()) } } } } impl Drop for Device { fn drop(&mut self) { _ = logger::flush(); // dbg!("Device Context drop called."); } } ================================================ FILE: windows_kext/driver/src/entry.rs ================================================ use crate::common::ControlCode; use crate::device; use alloc::boxed::Box; use core::sync::atomic::{AtomicPtr, Ordering}; use num_traits::FromPrimitive; use wdk::irp_helpers::{CleanupRequest, CreateRequest, DeviceControlRequest, ReadRequest, WriteRequest}; use wdk::{err, info, interface}; use windows_sys::Wdk::Foundation::{DEVICE_OBJECT, DRIVER_OBJECT, IRP}; use windows_sys::Win32::Foundation::{NTSTATUS, STATUS_SUCCESS}; static VERSION: [u8; 4] = include!("../../kextinterface/version.txt"); /// Global device pointer. /// /// We use `AtomicPtr` to ensure thread safety. /// - **Safety**: Prevents data races and acts as a compiler barrier against dangerous optimizations /// (e.g., load hoisting), ensuring concurrent callouts see a valid, up-to-date pointer. /// - **Performance**: Negligible overhead. On x64, `Acquire` is free (same as a normal load). /// On ARM64, it uses efficient hardware-supported load-acquire instructions. static DEVICE: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); pub fn get_device() -> Option<&'static mut device::Device> { // Acquire pairs with the Release store in driver_entry and the AcqRel swap in driver_unload. unsafe { DEVICE.load(Ordering::Acquire).as_mut() } } // DriverEntry is the entry point of the driver (main function). Will be called when driver is loaded. // Name should not be changed #[export_name = "DriverEntry"] pub extern "system" fn driver_entry( driver_object: *mut windows_sys::Wdk::Foundation::DRIVER_OBJECT, registry_path: *mut windows_sys::Win32::Foundation::UNICODE_STRING, ) -> windows_sys::Win32::Foundation::NTSTATUS { info!("Starting initialization..."); // Initialize driver object. let mut driver = match interface::init_driver_object( driver_object, registry_path, "PortmasterKext", core::ptr::null_mut(), ) { Ok(driver) => driver, Err(status) => { err!("driver_entry: failed to initialize driver: {}", status); return windows_sys::Win32::Foundation::STATUS_FAILED_DRIVER_ENTRY; } }; // Set driver functions. driver.set_driver_unload(Some(driver_unload)); driver.set_create_fn(Some(driver_create)); driver.set_cleanup_fn(Some(driver_cleanup)); driver.set_read_fn(Some(driver_read)); driver.set_write_fn(Some(driver_write)); driver.set_device_control_fn(Some(device_control)); // Initialize device. let device = match device::Device::new(&driver) { Ok(device) => Box::new(device), Err(err) => { wdk::err!("filed to initialize device: {}", err); return -1; } }; // Release: makes the fully-constructed Device visible to all cores that subsequently // perform an Acquire load. DEVICE.store(Box::into_raw(device), Ordering::Release); STATUS_SUCCESS } // driver_unload function is called when service delete is called from user-space. unsafe extern "system" fn driver_unload(_object: *const DRIVER_OBJECT) { info!("Unloading complete"); // Atomically null the pointer before freeing. Any core that performs an Acquire load // *after* this swap will see null and bail out safely. Any core that already loaded a // non-null pointer before this swap is protected by the OS-level serialisation: // - WFP callouts: FilterEngine::drop() (field declared first in Device) calls the WFP // unregister APIs which block until every in-flight classify callback has returned, // so no callout thread holds a live reference by the time the memory is freed. // - IRP dispatch (read/write/ioctl): the I/O Manager guarantees no dispatch routine // is executing when driver_unload is called. // The swap is executed exactly once, on the unload path. let ptr = DEVICE.swap(core::ptr::null_mut(), Ordering::AcqRel); if !ptr.is_null() { unsafe { drop(Box::from_raw(ptr)); } } } /// driver_create is triggered when user-space opens a handle to the device (CreateFile). unsafe extern "system" fn driver_create( _device_object: *const DEVICE_OBJECT, irp: *mut IRP, ) -> NTSTATUS { let mut create_request = CreateRequest::new(irp.as_mut().unwrap()); if let Some(device) = get_device() { let pid = create_request.get_requestor_pid(); device.owner_pid.store(pid, core::sync::atomic::Ordering::Release); info!("Device opened by PID {}", pid); } create_request.complete(); create_request.get_status() } /// driver_cleanup is triggered when user-space closes the last handle to the device. unsafe extern "system" fn driver_cleanup( _device_object: *const DEVICE_OBJECT, irp: *mut IRP, ) -> NTSTATUS { let mut cleanup_request = CleanupRequest::new(irp.as_mut().unwrap()); if let Some(device) = get_device() { let old_pid = device.owner_pid.swap(0, core::sync::atomic::Ordering::Release); info!("Device closed by PID {}", old_pid); } cleanup_request.complete(); cleanup_request.get_status() } // driver_read event triggered from user-space on file.Read. unsafe extern "system" fn driver_read( _device_object: *const DEVICE_OBJECT, irp: *mut IRP, ) -> NTSTATUS { let mut read_request = ReadRequest::new(irp.as_mut().unwrap()); let Some(device) = get_device() else { read_request.complete(); return read_request.get_status(); }; device.read(&mut read_request); read_request.get_status() } /// driver_write event triggered from user-space on file.Write. unsafe extern "system" fn driver_write( _device_object: *const DEVICE_OBJECT, irp: *mut IRP, ) -> NTSTATUS { let mut write_request = WriteRequest::new(irp.as_mut().unwrap()); let Some(device) = get_device() else { write_request.complete(); return write_request.get_status(); }; device.write(&mut write_request); write_request.mark_all_as_read(); write_request.complete(); write_request.get_status() } /// device_control event triggered from user-space on file.deviceIOControl. unsafe extern "system" fn device_control( _device_object: *const DEVICE_OBJECT, irp: *mut IRP, ) -> NTSTATUS { let mut control_request = DeviceControlRequest::new(irp.as_mut().unwrap()); let Some(device) = get_device() else { control_request.complete(); return control_request.get_status(); }; let Some(control_code): Option = FromPrimitive::from_u32(control_request.get_control_code()) else { wdk::info!("Unknown IOCTL code: {}", control_request.get_control_code()); control_request.not_implemented(); return control_request.get_status(); }; wdk::info!("IOCTL: {}", control_code); match control_code { ControlCode::Version => { control_request.write(&VERSION); } ControlCode::ShutdownRequest => device.shutdown(), }; control_request.complete(); control_request.get_status() } ================================================ FILE: windows_kext/driver/src/id_cache.rs ================================================ use core::mem; use alloc::collections::VecDeque; use protocol::info::Info; use smoltcp::wire::{IpAddress, IpProtocol}; use wdk::rw_spin_lock::RwSpinLock; use crate::{connection::Direction, connection_map::Key, device::Packet}; pub struct Entry { pub value: T, id: u64, } pub struct IdCache { values: VecDeque>, lock: RwSpinLock, next_id: u64, } impl IdCache { pub fn new() -> Self { Self { values: VecDeque::with_capacity(1000), lock: RwSpinLock::default(), next_id: 1, // 0 is invalid id } } pub fn push( &mut self, value: (Key, Packet), process_id: u64, direction: Direction, ale_layer: bool, ) -> Option { let _guard = self.lock.write_lock(); let id = self.next_id; let info = build_info(&value.0, id, process_id, direction, &value.1, ale_layer); self.values.push_back(Entry { value, id }); self.next_id = self.next_id.wrapping_add(1); // Assuming this will not overflow. return info; } pub fn pop_id(&mut self, id: u64) -> Option<(Key, Packet)> { let _guard = self.lock.write_lock(); if let Ok(index) = self.values.binary_search_by_key(&id, |val| val.id) { return Some(self.values.remove(index).unwrap().value); } None } #[allow(dead_code)] pub fn get_entries_count(&self) -> usize { let _guard = self.lock.read_lock(); return self.values.len(); } pub fn pop_all(&mut self) -> VecDeque> { let mut values = VecDeque::with_capacity(1); let _guard = self.lock.write_lock(); mem::swap(&mut self.values, &mut values); return values; } } fn get_payload(packet: &Packet) -> Option<&[u8]> { match packet { Packet::PacketLayer(nbl, _) => nbl.get_data(), Packet::AleLayer(defer) => { let p = match defer { wdk::filter_engine::callout_data::ClassifyDefer::Initial(_, p) => p, wdk::filter_engine::callout_data::ClassifyDefer::Reauthorization(_, p) => p, }; if let Some(tpl) = p { tpl.net_buffer_list.get_data() } else { None } } } } fn build_info( key: &Key, packet_id: u64, process_id: u64, direction: Direction, packet: &Packet, ale_layer: bool, ) -> Option { let (local_port, remote_port) = match key.protocol { IpProtocol::Tcp | IpProtocol::Udp => (key.local_port, key.remote_port), _ => (0, 0), }; let payload_layer = if ale_layer { 4 // Transport layer } else { 3 // Network layer }; let mut payload = &[][..]; if let Some(p) = get_payload(packet) { payload = p; } match (key.local_address, key.remote_address) { (IpAddress::Ipv6(local_ip), IpAddress::Ipv6(remote_ip)) if key.is_ipv6() => { Some(protocol::info::connection_info_v6( packet_id, process_id, direction as u8, u8::from(key.protocol), local_ip.0, remote_ip.0, local_port, remote_port, payload_layer, payload, )) } (IpAddress::Ipv4(local_ip), IpAddress::Ipv4(remote_ip)) => { Some(protocol::info::connection_info_v4( packet_id, process_id, direction as u8, u8::from(key.protocol), local_ip.0, remote_ip.0, local_port, remote_port, payload_layer, payload, )) } _ => None, } } ================================================ FILE: windows_kext/driver/src/lib.rs ================================================ #![cfg_attr(not(test), no_std)] #![no_main] #![allow(clippy::needless_return)] extern crate alloc; mod ale_callouts; mod array_holder; mod bandwidth; mod callouts; mod common; mod connection; mod connection_cache; mod connection_map; mod device; mod entry; mod id_cache; pub mod logger; mod packet_callouts; mod packet_util; mod stream_callouts; use wdk::allocator::WindowsAllocator; // For consistent behavior during development and production only release mode should be used. // Certain behavior of the compiler will change and this can result in errors and different behavior in debug and release mode. #[cfg(debug_assertions)] compile_error!("Must be built in release mode to ensure consistent behavior and prevent optimization-related issues. Use `cargo build --release`."); #[cfg(not(test))] use core::panic::PanicInfo; // Declaration of the global memory allocator #[global_allocator] static HEAP: WindowsAllocator = WindowsAllocator {}; #[no_mangle] pub extern "system" fn _DllMainCRTStartup() {} #[cfg(not(test))] #[panic_handler] fn panic(info: &PanicInfo) -> ! { use wdk::err; err!("{}", info); loop {} } ================================================ FILE: windows_kext/driver/src/logger.rs ================================================ use alloc::boxed::Box; use alloc::vec::Vec; use core::{ mem::MaybeUninit, sync::atomic::{AtomicPtr, AtomicUsize, Ordering}, }; use protocol::info::{Info, Severity}; pub const LOG_LEVEL: u8 = Severity::Warning as u8; // pub const LOG_LEVEL: u8 = Severity::Trace as u8; pub const MAX_LOG_LINE_SIZE: usize = 150; const SIZE_OF_LOG_LINE_BUFFER: usize = 1024; static mut LOG_LINES: [AtomicPtr; SIZE_OF_LOG_LINE_BUFFER] = unsafe { MaybeUninit::zeroed().assume_init() }; static START_INDEX: AtomicUsize = unsafe { MaybeUninit::zeroed().assume_init() }; static END_INDEX: AtomicUsize = unsafe { MaybeUninit::zeroed().assume_init() }; pub fn add_line(log_line: Info) { let mut index = END_INDEX.fetch_add(1, Ordering::Acquire); unsafe { index %= SIZE_OF_LOG_LINE_BUFFER; let ptr = &mut LOG_LINES[index]; let line = Box::new(log_line); let old = ptr.swap(Box::into_raw(line), Ordering::SeqCst); if !old.is_null() { _ = Box::from_raw(old); } } } pub fn flush() -> Vec { let mut vec = Vec::new(); let end_index = END_INDEX.load(Ordering::Acquire); let start_index = START_INDEX.load(Ordering::Acquire); if end_index <= start_index { return vec; } unsafe { let count = end_index - start_index; for i in start_index..start_index + count { let index = i % SIZE_OF_LOG_LINE_BUFFER; let ptr = LOG_LINES[index].swap(core::ptr::null_mut(), Ordering::SeqCst); if !ptr.is_null() { vec.push(*Box::from_raw(ptr)); } } } START_INDEX.store(end_index, Ordering::Release); vec } #[macro_export] macro_rules! log_internal { ($log_line:expr, $($arg:tt)*) => ({ use core::fmt::Write; _ = write!($log_line, "{}:{} ", file!(), line!()); _ = write!($log_line, $($arg)*); $crate::logger::add_line($log_line); }); } #[macro_export] macro_rules! crit { ($($arg:tt)*) => ({ if protocol::info::Severity::Critical as u8 >= $crate::logger::LOG_LEVEL { let message = alloc::format!($($arg)*); $crate::logger::add_line(protocol::info::Severity::Critical, alloc::format!("{}:{} ", file!(), line!()), message) } }); } #[macro_export] macro_rules! err { ($($arg:tt)*) => ({ if protocol::info::Severity::Error as u8 >= $crate::logger::LOG_LEVEL { let mut log_line = protocol::info::log_line(protocol::info::Severity::Error, $crate::logger::MAX_LOG_LINE_SIZE); $crate::log_internal!(log_line, $($arg)*); } }); } #[macro_export] macro_rules! warn { ($($arg:tt)*) => ({ if protocol::info::Severity::Warning as u8 >= $crate::logger::LOG_LEVEL { let mut log_line = protocol::info::log_line(protocol::info::Severity::Warning, $crate::logger::MAX_LOG_LINE_SIZE); $crate::log_internal!(log_line, $($arg)*); } }); } #[macro_export] macro_rules! dbg { ($($arg:tt)*) => ({ if protocol::info::Severity::Debug as u8 >= $crate::logger::LOG_LEVEL { let mut log_line = protocol::info::log_line(protocol::info::Severity::Debug, $crate::logger::MAX_LOG_LINE_SIZE); $crate::log_internal!(log_line, $($arg)*); } }); } #[macro_export] macro_rules! info { ($($arg:tt)*) => ({ if protocol::info::Severity::Info as u8 >= $crate::logger::LOG_LEVEL { let mut log_line = protocol::info::log_line(protocol::info::Severity::Info, $crate::logger::MAX_LOG_LINE_SIZE); $crate::log_internal!(log_line, $($arg)*); } }); } ================================================ FILE: windows_kext/driver/src/packet_callouts.rs ================================================ use alloc::string::String; use smoltcp::wire::{IPV4_HEADER_LEN, IPV6_HEADER_LEN}; use wdk::filter_engine::callout_data::CalloutData; use wdk::filter_engine::layer; use wdk::filter_engine::net_buffer::{NetBufferList, NetBufferListIter}; use wdk::filter_engine::packet::InjectInfo; use crate::connection::{ Connection, ConnectionV4, ConnectionV6, Direction, RedirectInfo, Verdict, PM_DNS_PORT, PM_SPN_PORT, }; use crate::connection_cache::ConnectionCache; use crate::connection_map::Key; use crate::device::{Device, Packet}; use crate::packet_util::{ get_key_from_nbl_v4, get_key_from_nbl_v6, recalc_header_checksums, Redirect, }; // IP packet layers pub fn ip_packet_layer_outbound_v4(data: CalloutData) { type Fields = layer::FieldsOutboundIppacketV4; let interface_index = data.get_value_u32(Fields::InterfaceIndex as usize); let sub_interface_index = data.get_value_u32(Fields::SubInterfaceIndex as usize); ip_packet_layer( data, false, Direction::Outbound, interface_index, sub_interface_index, ); } pub fn ip_packet_layer_inbound_v4(data: CalloutData) { type Fields = layer::FieldsInboundIppacketV4; let interface_index = data.get_value_u32(Fields::InterfaceIndex as usize); let sub_interface_index = data.get_value_u32(Fields::SubInterfaceIndex as usize); ip_packet_layer( data, false, Direction::Inbound, interface_index, sub_interface_index, ); } pub fn ip_packet_layer_outbound_v6(data: CalloutData) { type Fields = layer::FieldsOutboundIppacketV6; let interface_index = data.get_value_u32(Fields::InterfaceIndex as usize); let sub_interface_index = data.get_value_u32(Fields::SubInterfaceIndex as usize); ip_packet_layer( data, true, Direction::Outbound, interface_index, sub_interface_index, ); } pub fn ip_packet_layer_inbound_v6(data: CalloutData) { type Fields = layer::FieldsInboundIppacketV6; let interface_index = data.get_value_u32(Fields::InterfaceIndex as usize); let sub_interface_index = data.get_value_u32(Fields::SubInterfaceIndex as usize); ip_packet_layer( data, true, Direction::Inbound, interface_index, sub_interface_index, ); } struct ConnectionInfo { verdict: Verdict, process_id: u64, redirect_info: Option, } impl ConnectionInfo { fn from_connection(conn: &T) -> Self { ConnectionInfo { verdict: conn.get_verdict(), process_id: conn.get_process_id(), redirect_info: conn.redirect_info(), } } } fn fast_track_pm_packets(key: &Key, direction: Direction) -> bool { match direction { Direction::Outbound => { if key.local_port == PM_DNS_PORT || key.local_port == PM_SPN_PORT { return key.local_address == key.remote_address; } } Direction::Inbound => { if key.local_port == PM_DNS_PORT || key.local_port == PM_SPN_PORT { return key.local_address == key.remote_address; } } } return false; } fn ip_packet_layer( mut data: CalloutData, ipv6: bool, direction: Direction, interface_index: u32, sub_interface_index: u32, ) { // Make the default path as drop. data.block_and_absorb(); // Block all fragment data. No easy way to keep track of the origin and they are rarely used. if data.is_fragment_data() { data.block_and_absorb(); crate::err!("blocked fragment packet"); return; } let Some(device) = crate::entry::get_device() else { return; }; if device .injector .was_network_packet_injected_by_self(data.get_layer_data() as _, ipv6) { data.action_permit(); return; } for mut nbl in NetBufferListIter::new(data.get_layer_data() as _) { if let Direction::Inbound = direction { // The header is not part of the NBL for incoming packets. Move the beginning of the buffer back so we get access to it. // The NBL will auto advance after it loses scope. if ipv6 { nbl.retreat(IPV6_HEADER_LEN as u32, true); } else { nbl.retreat(IPV4_HEADER_LEN as u32, true); } } // Get key from packet. let key = match if ipv6 { get_key_from_nbl_v6(&nbl, direction) } else { get_key_from_nbl_v4(&nbl, direction) } { Ok(key) => key, Err(err) => { crate::err!("failed to get key from nbl: {}", err); return; } }; if fast_track_pm_packets(&key, direction) { data.action_permit(); return; } let mut send_request_to_portmaster = true; let mut process_id = 0; if matches!( key.protocol, smoltcp::wire::IpProtocol::Tcp | smoltcp::wire::IpProtocol::Udp ) { if let Some(mut conn_info) = get_connection_info(&mut device.connection_cache, &key, ipv6) { process_id = conn_info.process_id; // Check if there is action for this connection. match conn_info.verdict { Verdict::Undecided | Verdict::Accept | Verdict::Block | Verdict::Drop => {} Verdict::PermanentAccept => { send_request_to_portmaster = false; data.action_permit(); } Verdict::PermanentBlock => { send_request_to_portmaster = false; data.action_block_hard(); } Verdict::Undeterminable | Verdict::PermanentDrop | Verdict::Failed => { send_request_to_portmaster = false; data.block_and_absorb(); } Verdict::RedirectNameServer | Verdict::RedirectTunnel | Verdict::RedirectSplitTunnel => { if let Some(redirect_info) = conn_info.redirect_info.take() { match clone_packet( device, nbl, direction, ipv6, key.is_loopback(), interface_index, sub_interface_index, ) { Ok(mut packet) => { let _ = packet.redirect(redirect_info); if let Err(err) = device.inject_packet(packet, false) { crate::err!("failed to inject packet: {}", err); } } Err(err) => crate::err!("failed to clone packet: {}", err), } } // This will block the original packet. Even if injection failed. data.block_and_absorb(); continue; } } } else { // Connections is not in the cache. crate::dbg!("packet layer adding connection: {} PID: 0", key); if ipv6 { let conn = ConnectionV6::from_key(&key, 0, direction).unwrap(); device.connection_cache.add_connection_v6(conn); } else { let conn = ConnectionV4::from_key(&key, 0, direction).unwrap(); device.connection_cache.add_connection_v4(conn); } } } // Clone packet and send to Portmaster. if send_request_to_portmaster { let packet = match clone_packet( device, nbl, direction, ipv6, key.is_loopback(), interface_index, sub_interface_index, ) { Ok(p) => p, Err(err) => { crate::err!("failed to clone packet: {}", err); return; } }; let info = device .packet_cache .push((key, packet), process_id, direction, false); // Send to Portmaster if let Some(info) = info { let _ = device.event_queue.push(info); } data.block_and_absorb(); } } } fn clone_packet( device: &mut Device, nbl: NetBufferList, direction: Direction, ipv6: bool, loopback: bool, interface_index: u32, sub_interface_index: u32, ) -> Result { let mut clone = nbl.clone(&device.network_allocator)?; let inbound = match direction { Direction::Outbound => false, Direction::Inbound => true, }; if let Some(data) = clone.get_data_mut() { // Outbound packets intercepted at the IP layer may carry only a partial // pseudo-header checksum because the TCP/IP stack relies on NIC hardware // checksum offload to fill in the real value before transmission. // When this clone is later re-injected via FwpsInjectNetwork*Async (on // Accept/PermanentAccept verdict), it bypasses the NIC entirely, so offload // never runs. We must compute the full software checksum here. recalc_header_checksums(data, ipv6); } Ok(Packet::PacketLayer( clone, InjectInfo { ipv6, inbound, loopback, interface_index, sub_interface_index, }, )) } fn get_connection_info( connection_cache: &mut ConnectionCache, key: &Key, ipv6: bool, ) -> Option { if ipv6 { let conn_info = connection_cache.read_connection_v6( key, |conn: &ConnectionV6| -> Option { // Function is is behind spin lock. Just copy and return. Some(ConnectionInfo::from_connection(conn)) }, ); return conn_info; } else { let conn_info = connection_cache.read_connection_v4( key, |conn: &ConnectionV4| -> Option { // Function is is behind spin lock. Just copy and return. Some(ConnectionInfo::from_connection(conn)) }, ); return conn_info; } } ================================================ FILE: windows_kext/driver/src/packet_util.rs ================================================ use alloc::string::{String, ToString}; use smoltcp::wire::{ IpAddress, IpProtocol, Ipv4Address, Ipv4Packet, Ipv6Address, Ipv6Packet, TcpPacket, UdpPacket, }; use wdk::filter_engine::net_buffer::NetBufferList; use crate::connection_map::Key; use crate::device::Packet; use crate::{ connection::{Direction, RedirectInfo}, dbg, err, }; /// `Redirect` is a trait that defines a method for redirecting network packets. /// /// This trait is used to implement different strategies for redirecting packets, /// depending on the specific requirements of the application. pub trait Redirect { /// Redirects a network packet based on the provided `RedirectInfo`. /// /// # Arguments /// /// * `redirect_info` - A struct containing information about how to redirect the packet. /// /// # Returns /// /// * `Ok(())` if the packet was successfully redirected. /// * `Err(String)` if there was an error redirecting the packet. fn redirect(&mut self, redirect_info: RedirectInfo) -> Result<(), String>; } impl Redirect for Packet { fn redirect(&mut self, redirect_info: RedirectInfo) -> Result<(), String> { if let Packet::PacketLayer(nbl, inject_info) = self { let Some(data) = nbl.get_data_mut() else { return Err("trying to redirect immutable NBL".to_string()); }; if inject_info.inbound { redirect_inbound_packet( data, redirect_info.local_address, redirect_info.remote_address, redirect_info.remote_port, ) } else { redirect_outbound_packet( data, redirect_info.redirect_address, redirect_info.redirect_port, redirect_info.unify, ) } return Ok(()); } // return Err("can't redirect from non packet layer".to_string()); return Ok(()); } } /// Redirects an outbound packet to a specified remote address and port. /// /// # Arguments /// /// * `packet` - A mutable reference to the packet data. /// * `remote_address` - The IP address to redirect the packet to. /// * `remote_port` - The port to redirect the packet to. /// * `unify` - If true, the source and destination addresses of the packet will be set to the same value. /// /// This function modifies the packet in-place to change its destination address and port. /// It also updates the checksums for the IP and transport layer headers. /// If the `unify` parameter is true, it sets the source and destination addresses to be the same. /// If the remote address is a loopback address, it sets the source address to the loopback address. fn redirect_outbound_packet( packet: &mut [u8], remote_address: IpAddress, remote_port: u16, unify: bool, ) { match remote_address { IpAddress::Ipv4(remote_address) => { if let Ok(mut ip_packet) = Ipv4Packet::new_checked(packet) { if unify { ip_packet.set_dst_addr(ip_packet.src_addr()); } else { ip_packet.set_dst_addr(remote_address); if remote_address.is_loopback() { ip_packet.set_src_addr(Ipv4Address::new(127, 0, 0, 1)); } } ip_packet.fill_checksum(); let src_addr = ip_packet.src_addr(); let dst_addr = ip_packet.dst_addr(); if ip_packet.next_header() == IpProtocol::Udp { if let Ok(mut udp_packet) = UdpPacket::new_checked(ip_packet.payload_mut()) { udp_packet.set_dst_port(remote_port); udp_packet .fill_checksum(&IpAddress::Ipv4(src_addr), &IpAddress::Ipv4(dst_addr)); } } if ip_packet.next_header() == IpProtocol::Tcp { if let Ok(mut tcp_packet) = TcpPacket::new_checked(ip_packet.payload_mut()) { tcp_packet.set_dst_port(remote_port); tcp_packet .fill_checksum(&IpAddress::Ipv4(src_addr), &IpAddress::Ipv4(dst_addr)); } } } } IpAddress::Ipv6(remote_address) => { if let Ok(mut ip_packet) = Ipv6Packet::new_checked(packet) { ip_packet.set_dst_addr(remote_address); if unify { ip_packet.set_dst_addr(ip_packet.src_addr()); } else { ip_packet.set_dst_addr(remote_address); if remote_address.is_loopback() { ip_packet.set_src_addr(Ipv6Address::LOOPBACK); } } let src_addr = ip_packet.src_addr(); let dst_addr = ip_packet.dst_addr(); if ip_packet.next_header() == IpProtocol::Udp { if let Ok(mut udp_packet) = UdpPacket::new_checked(ip_packet.payload_mut()) { udp_packet.set_dst_port(remote_port); udp_packet .fill_checksum(&IpAddress::Ipv6(src_addr), &IpAddress::Ipv6(dst_addr)); } } if ip_packet.next_header() == IpProtocol::Tcp { if let Ok(mut tcp_packet) = TcpPacket::new_checked(ip_packet.payload_mut()) { tcp_packet.set_dst_port(remote_port); tcp_packet .fill_checksum(&IpAddress::Ipv6(src_addr), &IpAddress::Ipv6(dst_addr)); } } } } } } /// Redirects an inbound packet to a local address. /// /// This function takes a mutable reference to a packet and modifies it in place. /// It changes the destination address to the provided local address and the source address /// to the original remote address. It also sets the source port to the original remote port. /// The function handles both IPv4 and IPv6 addresses. /// /// # Arguments /// /// * `packet` - A mutable reference to the packet data. /// * `local_address` - The local IP address to redirect the packet to. /// * `original_remote_address` - The original remote IP address of the packet. /// * `original_remote_port` - The original remote port of the packet. /// fn redirect_inbound_packet( packet: &mut [u8], local_address: IpAddress, original_remote_address: IpAddress, original_remote_port: u16, ) { match local_address { IpAddress::Ipv4(local_address) => { let IpAddress::Ipv4(original_remote_address) = original_remote_address else { return; }; if let Ok(mut ip_packet) = Ipv4Packet::new_checked(packet) { ip_packet.set_dst_addr(local_address); ip_packet.set_src_addr(original_remote_address); ip_packet.fill_checksum(); let src_addr = ip_packet.src_addr(); let dst_addr = ip_packet.dst_addr(); if ip_packet.next_header() == IpProtocol::Udp { if let Ok(mut udp_packet) = UdpPacket::new_checked(ip_packet.payload_mut()) { udp_packet.set_src_port(original_remote_port); udp_packet .fill_checksum(&IpAddress::Ipv4(src_addr), &IpAddress::Ipv4(dst_addr)); } } if ip_packet.next_header() == IpProtocol::Tcp { if let Ok(mut tcp_packet) = TcpPacket::new_checked(ip_packet.payload_mut()) { tcp_packet.set_src_port(original_remote_port); tcp_packet .fill_checksum(&IpAddress::Ipv4(src_addr), &IpAddress::Ipv4(dst_addr)); } } } } IpAddress::Ipv6(local_address) => { if let Ok(mut ip_packet) = Ipv6Packet::new_checked(packet) { let IpAddress::Ipv6(original_remote_address) = original_remote_address else { return; }; ip_packet.set_dst_addr(local_address); ip_packet.set_src_addr(original_remote_address); let src_addr = ip_packet.src_addr(); let dst_addr = ip_packet.dst_addr(); if ip_packet.next_header() == IpProtocol::Udp { if let Ok(mut udp_packet) = UdpPacket::new_checked(ip_packet.payload_mut()) { udp_packet.set_src_port(original_remote_port); udp_packet .fill_checksum(&IpAddress::Ipv6(src_addr), &IpAddress::Ipv6(dst_addr)); } } if ip_packet.next_header() == IpProtocol::Tcp { if let Ok(mut tcp_packet) = TcpPacket::new_checked(ip_packet.payload_mut()) { tcp_packet.set_src_port(original_remote_port); tcp_packet .fill_checksum(&IpAddress::Ipv6(src_addr), &IpAddress::Ipv6(dst_addr)); } } } } } } pub fn recalc_header_checksums(packet: &mut [u8], ipv6: bool) { if ipv6 { if let Ok(mut ip_packet) = Ipv6Packet::new_checked(packet) { let src_addr = ip_packet.src_addr(); let dst_addr = ip_packet.dst_addr(); if ip_packet.next_header() == IpProtocol::Udp { if let Ok(mut udp_packet) = UdpPacket::new_checked(ip_packet.payload_mut()) { udp_packet .fill_checksum(&IpAddress::Ipv6(src_addr), &IpAddress::Ipv6(dst_addr)); } } if ip_packet.next_header() == IpProtocol::Tcp { if let Ok(mut tcp_packet) = TcpPacket::new_checked(ip_packet.payload_mut()) { tcp_packet .fill_checksum(&IpAddress::Ipv6(src_addr), &IpAddress::Ipv6(dst_addr)); } } } } else { if let Ok(mut ip_packet) = Ipv4Packet::new_checked(packet) { ip_packet.fill_checksum(); let src_addr = ip_packet.src_addr(); let dst_addr = ip_packet.dst_addr(); if ip_packet.next_header() == IpProtocol::Udp { if let Ok(mut udp_packet) = UdpPacket::new_checked(ip_packet.payload_mut()) { udp_packet .fill_checksum(&IpAddress::Ipv4(src_addr), &IpAddress::Ipv4(dst_addr)); } } if ip_packet.next_header() == IpProtocol::Tcp { if let Ok(mut tcp_packet) = TcpPacket::new_checked(ip_packet.payload_mut()) { tcp_packet .fill_checksum(&IpAddress::Ipv4(src_addr), &IpAddress::Ipv4(dst_addr)); } } } } } #[allow(dead_code)] fn print_packet(packet: &[u8]) { if let Ok(ip_packet) = Ipv4Packet::new_checked(packet) { if ip_packet.next_header() == IpProtocol::Udp { if let Ok(udp_packet) = UdpPacket::new_checked(ip_packet.payload()) { dbg!("packet {} {}", ip_packet, udp_packet); } } if ip_packet.next_header() == IpProtocol::Tcp { if let Ok(tcp_packet) = TcpPacket::new_checked(ip_packet.payload()) { dbg!("packet {} {}", ip_packet, tcp_packet); } } } else { err!("failed to print packet: invalid ip header: {:?}", packet); } } /// This function extracts a key from a given IPv4 network buffer list (NBL). /// The key contains the protocol, local and remote addresses and ports. /// /// # Arguments /// /// * `nbl` - A reference to the network buffer list from which the key will be extracted. /// * `direction` - The direction of the packet (Inbound or Outbound). /// /// # Returns /// /// * `Ok(Key)` - A key containing the protocol, local and remote addresses and ports. /// * `Err(String)` - An error message if the function fails to get net_buffer data. fn get_ports(packet: &[u8], protocol: smoltcp::wire::IpProtocol) -> (u16, u16) { match protocol { smoltcp::wire::IpProtocol::Tcp => { let tcp_packet = TcpPacket::new_unchecked(packet); (tcp_packet.src_port(), tcp_packet.dst_port()) } smoltcp::wire::IpProtocol::Udp => { let udp_packet = UdpPacket::new_unchecked(packet); (udp_packet.src_port(), udp_packet.dst_port()) } _ => (0, 0), // No ports for other protocols } } pub fn get_key_from_nbl_v4(nbl: &NetBufferList, direction: Direction) -> Result { // Get first bytes of the packet. IP header + src port (2 bytes) + dst port (2 bytes) let mut headers = [0; smoltcp::wire::IPV4_HEADER_LEN + 4]; if nbl.read_bytes(&mut headers).is_err() { return Err("failed to get net_buffer data".to_string()); } // This will panic in debug mode, probably because of runtime checks. // Parse packet let ip_packet = Ipv4Packet::new_unchecked(&headers); let (src_port, dst_port) = get_ports( &headers[smoltcp::wire::IPV4_HEADER_LEN..], ip_packet.next_header(), ); // Build key match direction { Direction::Outbound => Ok(Key { protocol: ip_packet.next_header(), local_address: IpAddress::Ipv4(ip_packet.src_addr()), local_port: src_port, remote_address: IpAddress::Ipv4(ip_packet.dst_addr()), remote_port: dst_port, }), Direction::Inbound => Ok(Key { protocol: ip_packet.next_header(), local_address: IpAddress::Ipv4(ip_packet.dst_addr()), local_port: dst_port, remote_address: IpAddress::Ipv4(ip_packet.src_addr()), remote_port: src_port, }), } } /// This function extracts a key from a given IPv6 network buffer list (NBL). /// The key contains the protocol, local and remote addresses and ports. /// /// # Arguments /// /// * `nbl` - A reference to the network buffer list from which the key will be extracted. /// * `direction` - The direction of the packet (Inbound or Outbound). /// /// # Returns /// /// * `Ok(Key)` - A key containing the protocol, local and remote addresses and ports. /// * `Err(String)` - An error message if the function fails to get net_buffer data. pub fn get_key_from_nbl_v6(nbl: &NetBufferList, direction: Direction) -> Result { // Get first bytes of the packet. IP header + src port (2 bytes) + dst port (2 bytes) let mut headers = [0; smoltcp::wire::IPV6_HEADER_LEN + 4]; let Ok(()) = nbl.read_bytes(&mut headers) else { return Err("failed to get net_buffer data".to_string()); }; // This will panic in debug mode, probably because of runtime checks. // Parse packet let ip_packet = Ipv6Packet::new_unchecked(&headers); let (src_port, dst_port) = get_ports( &headers[smoltcp::wire::IPV6_HEADER_LEN..], ip_packet.next_header(), ); // Build key match direction { Direction::Outbound => Ok(Key { protocol: ip_packet.next_header(), local_address: IpAddress::Ipv6(ip_packet.src_addr()), local_port: src_port, remote_address: IpAddress::Ipv6(ip_packet.dst_addr()), remote_port: dst_port, }), Direction::Inbound => Ok(Key { protocol: ip_packet.next_header(), local_address: IpAddress::Ipv6(ip_packet.dst_addr()), local_port: dst_port, remote_address: IpAddress::Ipv6(ip_packet.src_addr()), remote_port: src_port, }), } } // Converts a given key into connection information. // // This function takes a key, packet id, process id, and direction as input. // It then uses these to create a new `ConnectionInfoV6` or `ConnectionInfoV4` object, // depending on whether the IP addresses in the key are IPv6 or IPv4 respectively. // // # Arguments // // * `key` - A reference to the key object containing the connection details. // * `packet_id` - The id of the packet. // * `process_id` - The id of the process. // * `direction` - The direction of the connection. // // # Returns // // * `Some(Box)` - A boxed `Info` trait object if the key contains valid IPv4 or IPv6 addresses. // * `None` - If the key does not contain valid IPv4 or IPv6 addresses. // pub fn key_to_connection_info( // key: &Key, // packet_id: u64, // process_id: u64, // direction: Direction, // payload: &[u8], // ) -> Option { // let (local_port, remote_port) = match key.protocol { // IpProtocol::Tcp | IpProtocol::Udp => (key.local_port, key.remote_port), // _ => (0, 0), // }; // match (key.local_address, key.remote_address) { // (IpAddress::Ipv6(local_ip), IpAddress::Ipv6(remote_ip)) if key.is_ipv6() => { // Some(protocol::info::connection_info_v6( // packet_id, // process_id, // direction as u8, // u8::from(key.protocol), // local_ip.0, // remote_ip.0, // local_port, // remote_port, // payload, // )) // } // (IpAddress::Ipv4(local_ip), IpAddress::Ipv4(remote_ip)) => { // Some(protocol::info::connection_info_v4( // packet_id, // process_id, // direction as u8, // u8::from(key.protocol), // local_ip.0, // remote_ip.0, // local_port, // remote_port, // payload, // )) // } // _ => None, // } // } ================================================ FILE: windows_kext/driver/src/stream_callouts.rs ================================================ use smoltcp::wire::{Ipv4Address, Ipv6Address}; use wdk::filter_engine::{callout_data::CalloutData, layer, net_buffer::NetBufferListIter}; use crate::{bandwidth, connection::Direction}; pub fn stream_layer_tcp_v4(data: CalloutData) { type Fields = layer::FieldsStreamV4; let Some(device) = crate::entry::get_device() else { return; }; let mut direction = Direction::Outbound; let data_length = if let Some(packet) = data.get_stream_callout_packet() { if packet.is_receive() { direction = Direction::Inbound; } packet.get_data_len() } else { return; }; let local_ip = Ipv4Address::from_bytes( &data .get_value_u32(Fields::IpLocalAddress as usize) .to_be_bytes(), ); let local_port = data.get_value_u16(Fields::IpLocalPort as usize); let remote_ip = Ipv4Address::from_bytes( &data .get_value_u32(Fields::IpRemoteAddress as usize) .to_be_bytes(), ); let remote_port = data.get_value_u16(Fields::IpRemotePort as usize); match direction { Direction::Outbound => { device.bandwidth_stats.update_tcp_v4_tx( bandwidth::Key { local_ip, local_port, remote_ip, remote_port, }, data_length, ); } Direction::Inbound => { device.bandwidth_stats.update_tcp_v4_rx( bandwidth::Key { local_ip, local_port, remote_ip, remote_port, }, data_length, ); } } } pub fn stream_layer_tcp_v6(data: CalloutData) { type Fields = layer::FieldsStreamV6; let Some(device) = crate::entry::get_device() else { return; }; let mut direction = Direction::Outbound; let data_length = if let Some(packet) = data.get_stream_callout_packet() { if packet.is_receive() { direction = Direction::Inbound; } packet.get_data_len() } else { return; }; if data_length == 0 { return; } let local_ip = Ipv6Address::from_bytes(data.get_value_byte_array16(Fields::IpLocalAddress as usize)); let local_port = data.get_value_u16(Fields::IpLocalPort as usize); let remote_ip = Ipv6Address::from_bytes(data.get_value_byte_array16(Fields::IpRemoteAddress as usize)); let remote_port = data.get_value_u16(Fields::IpRemotePort as usize); match direction { Direction::Outbound => { device.bandwidth_stats.update_tcp_v6_tx( bandwidth::Key { local_ip, local_port, remote_ip, remote_port, }, data_length, ); } Direction::Inbound => { device.bandwidth_stats.update_tcp_v6_rx( bandwidth::Key { local_ip, local_port, remote_ip, remote_port, }, data_length, ); } } } pub fn stream_layer_udp_v4(data: CalloutData) { type Fields = layer::FieldsDatagramDataV4; let Some(device) = crate::entry::get_device() else { return; }; let mut data_length: usize = 0; for nbl in NetBufferListIter::new(data.get_layer_data() as _) { data_length += nbl.get_data_length() as usize; } let mut direction = Direction::Inbound; if data.get_value_u8(Fields::Direction as usize) == 0 { direction = Direction::Outbound; } let local_ip = Ipv4Address::from_bytes( &data .get_value_u32(Fields::IpLocalAddress as usize) .to_be_bytes(), ); let local_port = data.get_value_u16(Fields::IpLocalPort as usize); let remote_ip = Ipv4Address::from_bytes( &data .get_value_u32(Fields::IpRemoteAddress as usize) .to_be_bytes(), ); let remote_port = data.get_value_u16(Fields::IpRemotePort as usize); match direction { Direction::Outbound => { device.bandwidth_stats.update_udp_v4_tx( bandwidth::Key { local_ip, local_port, remote_ip, remote_port, }, data_length, ); } Direction::Inbound => { device.bandwidth_stats.update_udp_v4_rx( bandwidth::Key { local_ip, local_port, remote_ip, remote_port, }, data_length, ); } } } pub fn stream_layer_udp_v6(data: CalloutData) { type Fields = layer::FieldsDatagramDataV6; let Some(device) = crate::entry::get_device() else { return; }; let mut data_length: usize = 0; for nbl in NetBufferListIter::new(data.get_layer_data() as _) { data_length += nbl.get_data_length() as usize; } let mut direction = Direction::Inbound; if data.get_value_u8(Fields::Direction as usize) == 0 { direction = Direction::Outbound; } let local_ip = Ipv6Address::from_bytes(data.get_value_byte_array16(Fields::IpLocalAddress as usize)); let local_port = data.get_value_u16(Fields::IpLocalPort as usize); let remote_ip = Ipv6Address::from_bytes(data.get_value_byte_array16(Fields::IpRemoteAddress as usize)); let remote_port = data.get_value_u16(Fields::IpRemotePort as usize); match direction { Direction::Outbound => { device.bandwidth_stats.update_udp_v6_tx( bandwidth::Key { local_ip, local_port, remote_ip, remote_port, }, data_length, ); } Direction::Inbound => { device.bandwidth_stats.update_udp_v6_rx( bandwidth::Key { local_ip, local_port, remote_ip, remote_port, }, data_length, ); } } } ================================================ FILE: windows_kext/kextinterface/command.go ================================================ package kextinterface import ( "encoding/binary" "io" ) // Command IDs. const ( CommandShutdown = 0 CommandVerdict = 1 CommandUpdateV4 = 2 CommandUpdateV6 = 3 CommandClearCache = 4 CommandGetLogs = 5 CommandBandwidthStats = 6 CommandPrintMemoryStats = 7 CommandCleanEndedConnections = 8 ) // KextVerdict is the verdict ID used to with the kext. type KextVerdict uint8 // Kext Verdicts. // Make sure this is in sync with the Rust version. const ( // VerdictUndecided is the default status of new connections. VerdictUndecided KextVerdict = 0 VerdictUndeterminable KextVerdict = 1 VerdictAccept KextVerdict = 2 VerdictPermanentAccept KextVerdict = 3 VerdictBlock KextVerdict = 4 VerdictPermanentBlock KextVerdict = 5 VerdictDrop KextVerdict = 6 VerdictPermanentDrop KextVerdict = 7 VerdictRerouteToNameserver KextVerdict = 8 VerdictRerouteToTunnel KextVerdict = 9 VerdictFailed KextVerdict = 10 ) type Verdict struct { command uint8 ID uint64 Verdict uint8 } type UpdateV4 struct { command uint8 Protocol uint8 LocalAddress [4]byte LocalPort uint16 RemoteAddress [4]byte RemotePort uint16 Verdict uint8 } type UpdateV6 struct { command uint8 Protocol uint8 LocalAddress [16]byte LocalPort uint16 RemoteAddress [16]byte RemotePort uint16 Verdict uint8 } // SendShutdownCommand sends a Shutdown command to the kext. func SendShutdownCommand(writer io.Writer) error { _, err := writer.Write([]byte{CommandShutdown}) return err } // SendVerdictCommand sends a Verdict command to the kext. func SendVerdictCommand(writer io.Writer, verdict Verdict) error { verdict.command = CommandVerdict return binary.Write(writer, binary.LittleEndian, verdict) } // SendUpdateV4Command sends a UpdateV4 command to the kext. func SendUpdateV4Command(writer io.Writer, update UpdateV4) error { update.command = CommandUpdateV4 return binary.Write(writer, binary.LittleEndian, update) } // SendUpdateV6Command sends a UpdateV6 command to the kext. func SendUpdateV6Command(writer io.Writer, update UpdateV6) error { update.command = CommandUpdateV6 return binary.Write(writer, binary.LittleEndian, update) } // SendClearCacheCommand sends a ClearCache command to the kext. func SendClearCacheCommand(writer io.Writer) error { _, err := writer.Write([]byte{CommandClearCache}) return err } // SendGetLogsCommand sends a GetLogs command to the kext. func SendGetLogsCommand(writer io.Writer) error { _, err := writer.Write([]byte{CommandGetLogs}) return err } // SendGetBandwidthStatsCommand sends a GetBandwidthStats command to the kext. func SendGetBandwidthStatsCommand(writer io.Writer) error { _, err := writer.Write([]byte{CommandBandwidthStats}) return err } // SendPrintMemoryStatsCommand sends a PrintMemoryStats command to the kext. func SendPrintMemoryStatsCommand(writer io.Writer) error { _, err := writer.Write([]byte{CommandPrintMemoryStats}) return err } // SendCleanEndedConnectionsCommand sends a CleanEndedConnections command to the kext. func SendCleanEndedConnectionsCommand(writer io.Writer) error { _, err := writer.Write([]byte{CommandCleanEndedConnections}) return err } ================================================ FILE: windows_kext/kextinterface/info.go ================================================ package kextinterface import ( "encoding/binary" "errors" "fmt" "io" ) const ( InfoLogLine = 0 InfoConnectionIpv4 = 1 InfoConnectionIpv6 = 2 InfoConnectionEndEventV4 = 3 InfoConnectionEndEventV6 = 4 InfoBandwidthStatsV4 = 5 InfoBandwidthStatsV6 = 6 ) var ( ErrUnknownInfoType = errors.New("unknown info type") ErrUnexpectedInfoSize = errors.New("unexpected info size") ErrUnexpectedReadError = errors.New("unexpected read error") ) type connectionV4Internal struct { ID uint64 ProcessID uint64 Direction byte Protocol byte LocalIP [4]byte RemoteIP [4]byte LocalPort uint16 RemotePort uint16 PayloadLayer uint8 } type ConnectionV4 struct { connectionV4Internal Payload []byte } func (c *ConnectionV4) Compare(other *ConnectionV4) bool { return c.ID == other.ID && c.ProcessID == other.ProcessID && c.Direction == other.Direction && c.Protocol == other.Protocol && c.LocalIP == other.LocalIP && c.RemoteIP == other.RemoteIP && c.LocalPort == other.LocalPort && c.RemotePort == other.RemotePort } type connectionV6Internal struct { ID uint64 ProcessID uint64 Direction byte Protocol byte LocalIP [16]byte RemoteIP [16]byte LocalPort uint16 RemotePort uint16 PayloadLayer uint8 } type ConnectionV6 struct { connectionV6Internal Payload []byte } func (c ConnectionV6) Compare(other *ConnectionV6) bool { return c.ID == other.ID && c.ProcessID == other.ProcessID && c.Direction == other.Direction && c.Protocol == other.Protocol && c.LocalIP == other.LocalIP && c.RemoteIP == other.RemoteIP && c.LocalPort == other.LocalPort && c.RemotePort == other.RemotePort } type ConnectionEndV4 struct { ProcessID uint64 Direction byte Protocol byte LocalIP [4]byte RemoteIP [4]byte LocalPort uint16 RemotePort uint16 } type ConnectionEndV6 struct { ProcessID uint64 Direction byte Protocol byte LocalIP [16]byte RemoteIP [16]byte LocalPort uint16 RemotePort uint16 } type LogLine struct { Severity byte Line string } type BandwidthValueV4 struct { LocalIP [4]byte LocalPort uint16 RemoteIP [4]byte RemotePort uint16 TransmittedBytes uint64 ReceivedBytes uint64 } type BandwidthValueV6 struct { LocalIP [16]byte LocalPort uint16 RemoteIP [16]byte RemotePort uint16 TransmittedBytes uint64 ReceivedBytes uint64 } type BandwidthStatsArray struct { Protocol uint8 ValuesV4 []BandwidthValueV4 ValuesV6 []BandwidthValueV6 } type Info struct { ConnectionV4 *ConnectionV4 ConnectionV6 *ConnectionV6 ConnectionEndV4 *ConnectionEndV4 ConnectionEndV6 *ConnectionEndV6 LogLine *LogLine BandwidthStats *BandwidthStatsArray } type readHelper struct { infoType byte commandSize uint32 readSize int reader io.Reader } func newReadHelper(reader io.Reader) (*readHelper, error) { helper := &readHelper{reader: reader} err := binary.Read(reader, binary.LittleEndian, &helper.infoType) if err != nil { return nil, err } err = binary.Read(reader, binary.LittleEndian, &helper.commandSize) if err != nil { return nil, err } return helper, nil } func (r *readHelper) ReadData(data any) error { err := binary.Read(r, binary.LittleEndian, data) if err != nil { return errors.Join(ErrUnexpectedReadError, err) } if err := r.checkOverRead(); err != nil { return err } return nil } // Passing size = 0 will read the rest of the command. func (r *readHelper) ReadBytes(size uint32) ([]byte, error) { if uint32(r.readSize) >= r.commandSize { return nil, errors.Join(fmt.Errorf("cannot read more bytes than the command size: %d >= %d", r.readSize, r.commandSize), ErrUnexpectedReadError) } if size == 0 { size = r.commandSize - uint32(r.readSize) } if r.commandSize < uint32(r.readSize)+size { return nil, ErrUnexpectedInfoSize } bytes := make([]byte, size) err := binary.Read(r, binary.LittleEndian, bytes) if err != nil { return nil, errors.Join(ErrUnexpectedReadError, err) } return bytes, nil } func (r *readHelper) ReadUntilTheEnd() { _, _ = r.ReadBytes(0) } func (r *readHelper) checkOverRead() error { if uint32(r.readSize) > r.commandSize { return ErrUnexpectedInfoSize } return nil } func (r *readHelper) Read(p []byte) (n int, err error) { n, err = r.reader.Read(p) r.readSize += n return } func RecvInfo(reader io.Reader) (*Info, error) { helper, err := newReadHelper(reader) if err != nil { return nil, err } // Make sure the whole command is read before return. defer helper.ReadUntilTheEnd() // Read data switch helper.infoType { case InfoConnectionIpv4: { parseError := fmt.Errorf("failed to parse InfoConnectionIpv4") newInfo := ConnectionV4{} var fixedSizeValues connectionV4Internal // Read fixed size values. err = helper.ReadData(&fixedSizeValues) if err != nil { return nil, errors.Join(parseError, err, fmt.Errorf("fixed")) } newInfo.connectionV4Internal = fixedSizeValues // Read size of payload. var payloadSize uint32 err = helper.ReadData(&payloadSize) if err != nil { return nil, errors.Join(parseError, err, fmt.Errorf("payloadsize")) } // Check if there is payload. if payloadSize > 0 { // Read payload. newInfo.Payload, err = helper.ReadBytes(payloadSize) if err != nil { return nil, errors.Join(parseError, err, fmt.Errorf("payload")) } } return &Info{ConnectionV4: &newInfo}, nil } case InfoConnectionIpv6: { parseError := fmt.Errorf("failed to parse InfoConnectionIpv6") newInfo := ConnectionV6{} // Read fixed size values. err = helper.ReadData(&newInfo.connectionV6Internal) if err != nil { return nil, errors.Join(parseError, err) } // Read size of payload. var payloadSize uint32 err = helper.ReadData(&payloadSize) if err != nil { return nil, errors.Join(parseError, err) } // Check if there is payload. if payloadSize > 0 { // Read payload. newInfo.Payload, err = helper.ReadBytes(payloadSize) if err != nil { return nil, errors.Join(parseError, err) } } return &Info{ConnectionV6: &newInfo}, nil } case InfoConnectionEndEventV4: { parseError := fmt.Errorf("failed to parse InfoConnectionEndEventV4") var connectionEnd ConnectionEndV4 // Read fixed size values. err = helper.ReadData(&connectionEnd) if err != nil { return nil, errors.Join(parseError, err) } return &Info{ConnectionEndV4: &connectionEnd}, nil } case InfoConnectionEndEventV6: { parseError := fmt.Errorf("failed to parse InfoConnectionEndEventV6") var connectionEnd ConnectionEndV6 // Read fixed size values. err = helper.ReadData(&connectionEnd) if err != nil { return nil, errors.Join(parseError, err) } return &Info{ConnectionEndV6: &connectionEnd}, nil } case InfoLogLine: { parseError := fmt.Errorf("failed to parse InfoLogLine") logLine := LogLine{} // Read severity err = helper.ReadData(&logLine.Severity) if err != nil { return nil, errors.Join(parseError, err) } // Read string bytes, err := helper.ReadBytes(0) if err != nil { return nil, errors.Join(parseError, err) } logLine.Line = string(bytes) return &Info{LogLine: &logLine}, nil } case InfoBandwidthStatsV4: { parseError := fmt.Errorf("failed to parse InfoBandwidthStatsV4") // Read Protocol var protocol uint8 err = helper.ReadData(&protocol) if err != nil { return nil, errors.Join(parseError, err) } // Read size of array var size uint32 err = helper.ReadData(&size) if err != nil { return nil, errors.Join(parseError, err) } // Read array statsArray := make([]BandwidthValueV4, size) for i := range int(size) { err = helper.ReadData(&statsArray[i]) if err != nil { return nil, errors.Join(parseError, err) } } return &Info{BandwidthStats: &BandwidthStatsArray{Protocol: protocol, ValuesV4: statsArray}}, nil } case InfoBandwidthStatsV6: { parseError := fmt.Errorf("failed to parse InfoBandwidthStatsV6") // Read Protocol var protocol uint8 err = helper.ReadData(&protocol) if err != nil { return nil, errors.Join(parseError, err) } // Read size of array var size uint32 err = helper.ReadData(&size) if err != nil { return nil, errors.Join(parseError, err) } // Read array statsArray := make([]BandwidthValueV6, size) for i := range int(size) { err = helper.ReadData(&statsArray[i]) if err != nil { return nil, errors.Join(parseError, err) } } return &Info{BandwidthStats: &BandwidthStatsArray{Protocol: protocol, ValuesV6: statsArray}}, nil } } return nil, ErrUnknownInfoType } ================================================ FILE: windows_kext/kextinterface/ioctl.go ================================================ //go:build windows // +build windows package kextinterface import ( "golang.org/x/sys/windows" ) const ( METHOD_BUFFERED = 0 METHOD_IN_DIRECT = 1 METHOD_OUT_DIRECT = 2 METHOD_NEITHER = 3 SIOCTL_TYPE = 40000 ) func ctlCode(device_type, function, method, access uint32) uint32 { return (device_type << 16) | (access << 14) | (function << 2) | method } var ( IOCTL_VERSION = ctlCode(SIOCTL_TYPE, 0x800, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) IOCTL_SHUTDOWN_REQUEST = ctlCode(SIOCTL_TYPE, 0x801, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) ) func ReadVersion(file *KextFile) ([]uint8, error) { data := make([]uint8, 4) _, err := file.deviceIOControl(IOCTL_VERSION, nil, data) if err != nil { return nil, err } return data, nil } ================================================ FILE: windows_kext/kextinterface/kext.go ================================================ //go:build windows // +build windows package kextinterface import ( _ "embed" "fmt" "strconv" "strings" "syscall" "time" "github.com/safing/portmaster/base/log" "golang.org/x/sys/windows" ) var ( //go:embed version.txt versionTxt string // 4 byte version of the Kext interface InterfaceVersion = func() (v [4]byte) { // Parse version from file "version.txt". Expected format: [0, 1, 2, 3] s := strings.TrimSpace(versionTxt) s = strings.TrimPrefix(s, "[") s = strings.TrimSuffix(s, "]") str_ver := strings.Split(s, ",") for i := range v { n, err := strconv.Atoi(strings.TrimSpace(str_ver[i])) if err != nil { panic(err) } v[i] = byte(n) } return }() ) const ( winInvalidHandleValue = windows.InvalidHandle stopServiceTimeoutDuration = time.Duration(30 * time.Second) ) type KextService struct { handle windows.Handle driverName string } func (s *KextService) isValid() bool { return s != nil && s.handle != windows.InvalidHandle && s.handle != 0 } func (s *KextService) isRunning() (bool, error) { if !s.isValid() { return false, fmt.Errorf("kext service not initialized") } var status windows.SERVICE_STATUS err := windows.QueryServiceStatus(s.handle, &status) if err != nil { return false, err } return status.CurrentState == windows.SERVICE_RUNNING, nil } func (s *KextService) waitForServiceStatus(neededStatus uint32, timeLimit time.Duration) (bool, error) { var status windows.SERVICE_STATUS status.CurrentState = windows.SERVICE_NO_CHANGE start := time.Now() for status.CurrentState != neededStatus { err := windows.QueryServiceStatus(s.handle, &status) if err != nil { return false, fmt.Errorf("failed while waiting for service to start: %w", err) } if time.Since(start) > timeLimit { return false, fmt.Errorf("time limit reached") } // Sleep for 1/10 of the wait hint, recommended time from microsoft time.Sleep(time.Duration((status.WaitHint / 10)) * time.Millisecond) } return true, nil } func (s *KextService) Start(wait bool) error { if !s.isValid() { return fmt.Errorf("kext service not initialized") } // Start the service: err := windows.StartService(s.handle, 0, nil) if err != nil { err = windows.GetLastError() if err != windows.ERROR_SERVICE_ALREADY_RUNNING { // Failed to start service; clean-up: var status windows.SERVICE_STATUS _ = windows.ControlService(s.handle, windows.SERVICE_CONTROL_STOP, &status) _ = windows.DeleteService(s.handle) _ = windows.CloseServiceHandle(s.handle) s.handle = windows.InvalidHandle return err } } // Wait for service to start if wait { success, err := s.waitForServiceStatus(windows.SERVICE_RUNNING, stopServiceTimeoutDuration) if err != nil || !success { return fmt.Errorf("service did not start: %w", err) } } return nil } func (s *KextService) GetHandle() windows.Handle { return s.handle } func (s *KextService) Stop(wait bool) error { if !s.isValid() { return fmt.Errorf("kext service not initialized") } // Stop the service var status windows.SERVICE_STATUS err := windows.ControlService(s.handle, windows.SERVICE_CONTROL_STOP, &status) if err != nil { return fmt.Errorf("service failed to stop: %w", err) } // Wait for service to stop if wait { success, err := s.waitForServiceStatus(windows.SERVICE_STOPPED, time.Duration(10*time.Second)) if err != nil || !success { return fmt.Errorf("service did not stop: %w", err) } } return nil } func (s *KextService) Delete() error { if !s.isValid() { return fmt.Errorf("kext service not initialized") } err := windows.DeleteService(s.handle) if err != nil { return fmt.Errorf("failed to delete service: %s", err) } // Service wont be deleted until all handles are closed. err = windows.CloseServiceHandle(s.handle) if err != nil { return fmt.Errorf("failed to close service handle: %s", err) } s.handle = windows.InvalidHandle return nil } func (s *KextService) WaitUntilDeleted(serviceManager windows.Handle) error { driverNameU16, err := syscall.UTF16FromString(s.driverName) if err != nil { return fmt.Errorf("failed to convert driver name to UTF16 string: %w", err) } // Wait until we can no longer open the old service. // Not very efficient but NotifyServiceStatusChange cannot be used with driver service. start := time.Now() timeLimit := time.Duration(30 * time.Second) for { handle, err := windows.OpenService(serviceManager, &driverNameU16[0], windows.SERVICE_ALL_ACCESS) if err != nil { break } _ = windows.CloseServiceHandle(handle) if time.Since(start) > timeLimit { return fmt.Errorf("time limit reached") } time.Sleep(100 * time.Millisecond) } return nil } func (s *KextService) OpenFile(readBufferSize int) (*KextFile, error) { if !s.isValid() { return nil, fmt.Errorf("invalid kext object") } driverNameU16, err := syscall.UTF16FromString(`\\.\` + s.driverName) if err != nil { return nil, fmt.Errorf("failed to convert driver driverName to UTF16 string %w", err) } handle, err := windows.CreateFile(&driverNameU16[0], windows.GENERIC_READ|windows.GENERIC_WRITE, 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_OVERLAPPED, 0) if err != nil { return nil, err } return &KextFile{handle: handle, buffer: make([]byte, readBufferSize)}, nil } func CreateKextService(driverName string, driverPath string) (*KextService, error) { // Open the service manager: manager, err := windows.OpenSCManager(nil, nil, windows.SC_MANAGER_ALL_ACCESS) if err != nil { return nil, fmt.Errorf("failed to open service manager: %d", err) } defer windows.CloseServiceHandle(manager) driverNameU16, err := syscall.UTF16FromString(driverName) if err != nil { return nil, fmt.Errorf("failed to convert driver name to UTF16 string: %w", err) } // Check if there is an old service. service, err := windows.OpenService(manager, &driverNameU16[0], windows.SERVICE_ALL_ACCESS) if err == nil { log.Warning("kext: old driver service was found") oldService := &KextService{handle: service, driverName: driverName} oldService.Stop(true) err = oldService.Delete() if err != nil { return nil, err } err := oldService.WaitUntilDeleted(manager) if err != nil { return nil, err } service = windows.InvalidHandle log.Warning("kext: old driver service was deleted successfully") } driverPathU16, err := syscall.UTF16FromString(driverPath) // Create the service service, err = windows.CreateService(manager, &driverNameU16[0], &driverNameU16[0], windows.SERVICE_ALL_ACCESS, windows.SERVICE_KERNEL_DRIVER, windows.SERVICE_DEMAND_START, windows.SERVICE_ERROR_NORMAL, &driverPathU16[0], nil, nil, nil, nil, nil) if err != nil { return nil, err } return &KextService{handle: service, driverName: driverName}, nil } ================================================ FILE: windows_kext/kextinterface/kext_file.go ================================================ //go:build windows // +build windows package kextinterface import ( "fmt" "golang.org/x/sys/windows" ) type KextFile struct { handle windows.Handle buffer []byte read_slice []byte } // Read tries to read the supplied buffer length from the driver. // The data from the driver is read in chunks `len(f.buffer)` and the extra data is cached for the next call. // The performance penalty of calling the function with small buffers is very small. // The function will block until the next info packet is received from the kext. func (f *KextFile) Read(buffer []byte) (int, error) { if err := f.IsValid(); err != nil { return 0, fmt.Errorf("failed to read: %w", err) } // If no data is available from previous calls, read from kext. if f.read_slice == nil || len(f.read_slice) == 0 { err := f.refill_read_buffer() if err != nil { return 0, err } } if len(f.read_slice) >= len(buffer) { // There is enough data to fill the requested buffer. copy(buffer, f.read_slice[0:len(buffer)]) // Move the slice to contain the remaining data. f.read_slice = f.read_slice[len(buffer):] } else { // There is not enough data to fill the requested buffer. // Write everything available. copy(buffer[0:len(f.read_slice)], f.read_slice) copiedBytes := len(f.read_slice) f.read_slice = nil // Read again. _, err := f.Read(buffer[copiedBytes:]) if err != nil { return 0, err } } return len(buffer), nil } func (f *KextFile) refill_read_buffer() error { var count uint32 = 0 overlapped := &windows.Overlapped{} err := windows.ReadFile(f.handle, f.buffer[:], &count, overlapped) if err != nil { return err } f.read_slice = f.buffer[0:count] return nil } // Write sends the buffer bytes to the kext. The function will block until the whole buffer is written to the kext. func (f *KextFile) Write(buffer []byte) (int, error) { if err := f.IsValid(); err != nil { return 0, fmt.Errorf("failed to write: %w", err) } var count uint32 = 0 overlapped := &windows.Overlapped{} err := windows.WriteFile(f.handle, buffer, &count, overlapped) return int(count), err } // Close closes the handle to the kext. This will cancel all active Reads and Writes. func (f *KextFile) Close() error { if err := f.IsValid(); err != nil { return fmt.Errorf("failed to close: %w", err) } err := windows.CloseHandle(f.handle) f.handle = windows.InvalidHandle return err } // deviceIOControl exists for compatibility with the old kext. func (f *KextFile) deviceIOControl(code uint32, inData []byte, outData []byte) (*windows.Overlapped, error) { if err := f.IsValid(); err != nil { return nil, fmt.Errorf("failed to send io control: %w", err) } // Prepare the input data var inDataPtr *byte = nil var inDataSize uint32 = 0 if inData != nil { inDataPtr = &inData[0] inDataSize = uint32(len(inData)) } // Prepare the output data var outDataPtr *byte = nil var outDataSize uint32 = 0 if outData != nil { outDataPtr = &outData[0] outDataSize = uint32(len(outData)) } // Make the request to the kext. overlapped := &windows.Overlapped{} err := windows.DeviceIoControl(f.handle, code, inDataPtr, inDataSize, outDataPtr, outDataSize, nil, overlapped) if err != nil { return nil, err } return overlapped, nil } // GetHandle returns the handle of the kext. func (f *KextFile) GetHandle() windows.Handle { return f.handle } // IsValid checks if kext file holds a valid handle to the kext driver. func (f *KextFile) IsValid() error { if f == nil { return fmt.Errorf("nil kext file") } if f.handle == windows.Handle(0) || f.handle == windows.InvalidHandle { return fmt.Errorf("invalid handle") } return nil } ================================================ FILE: windows_kext/kextinterface/kext_file_test.go ================================================ //go:build linux // +build linux package kextinterface type KextFile struct{} func (f *KextFile) Read(buffer []byte) (int, error) { return 0, nil } // func (f *KextFile) flushBuffer() {} ================================================ FILE: windows_kext/kextinterface/protocol_test.go ================================================ package kextinterface import ( "bytes" "errors" "math/rand" "os" "testing" ) func TestRustInfoFile(t *testing.T) { t.Parallel() file, err := os.Open("testdata/rust_info_test.bin") if err != nil { panic(err) } defer func() { _ = file.Close() }() first := true for { info, err := RecvInfo(file) // First info should be with invalid size. // This tests if invalid info data is handled properly. if first { if !errors.Is(err, ErrUnexpectedInfoSize) { t.Errorf("unexpected error: %s\n", err) } first = false continue } if err != nil { if errors.Is(err, ErrUnexpectedReadError) { t.Errorf("unexpected error: %s\n", err) } return } switch { case info.LogLine != nil: if info.LogLine.Severity != 1 { t.Errorf("unexpected Log severity: %d\n", info.LogLine.Severity) } if info.LogLine.Line != "prefix: test log" { t.Errorf("unexpected Log line: %s\n", info.LogLine.Line) } case info.ConnectionV4 != nil: conn := info.ConnectionV4 expected := connectionV4Internal{ ID: 1, ProcessID: 2, Direction: 3, Protocol: 4, LocalIP: [4]byte{1, 2, 3, 4}, RemoteIP: [4]byte{2, 3, 4, 5}, LocalPort: 5, RemotePort: 6, PayloadLayer: 7, } if conn.connectionV4Internal != expected { t.Errorf("unexpected ConnectionV4: %+v\n", conn) } if !bytes.Equal(conn.Payload, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { t.Errorf("unexpected ConnectionV4 payload: %+v\n", conn.Payload) } case info.ConnectionV6 != nil: conn := info.ConnectionV6 expected := connectionV6Internal{ ID: 1, ProcessID: 2, Direction: 3, Protocol: 4, LocalIP: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, RemoteIP: [16]byte{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, LocalPort: 5, RemotePort: 6, PayloadLayer: 7, } if conn.connectionV6Internal != expected { t.Errorf("unexpected ConnectionV6: %+v\n", conn) } if !bytes.Equal(conn.Payload, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { t.Errorf("unexpected ConnectionV6 payload: %+v\n", conn.Payload) } case info.ConnectionEndV4 != nil: endEvent := info.ConnectionEndV4 expected := ConnectionEndV4{ ProcessID: 1, Direction: 2, Protocol: 3, LocalIP: [4]byte{1, 2, 3, 4}, RemoteIP: [4]byte{2, 3, 4, 5}, LocalPort: 4, RemotePort: 5, } if *endEvent != expected { t.Errorf("unexpected ConnectionEndV4: %+v\n", endEvent) } case info.ConnectionEndV6 != nil: endEvent := info.ConnectionEndV6 expected := ConnectionEndV6{ ProcessID: 1, Direction: 2, Protocol: 3, LocalIP: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, RemoteIP: [16]byte{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, LocalPort: 4, RemotePort: 5, } if *endEvent != expected { t.Errorf("unexpected ConnectionEndV6: %+v\n", endEvent) } case info.BandwidthStats != nil: stats := info.BandwidthStats if stats.Protocol != 1 { t.Errorf("unexpected Bandwidth stats protocol: %d\n", stats.Protocol) } if stats.ValuesV4 != nil { if len(stats.ValuesV4) != 2 { t.Errorf("unexpected Bandwidth stats value length: %d\n", len(stats.ValuesV4)) } expected1 := BandwidthValueV4{ LocalIP: [4]byte{1, 2, 3, 4}, LocalPort: 1, RemoteIP: [4]byte{2, 3, 4, 5}, RemotePort: 2, TransmittedBytes: 3, ReceivedBytes: 4, } if stats.ValuesV4[0] != expected1 { t.Errorf("unexpected Bandwidth stats value: %+v expected: %+v\n", stats.ValuesV4[0], expected1) } expected2 := BandwidthValueV4{ LocalIP: [4]byte{1, 2, 3, 4}, LocalPort: 5, RemoteIP: [4]byte{2, 3, 4, 5}, RemotePort: 6, TransmittedBytes: 7, ReceivedBytes: 8, } if stats.ValuesV4[1] != expected2 { t.Errorf("unexpected Bandwidth stats value: %+v expected: %+v\n", stats.ValuesV4[1], expected2) } } else if stats.ValuesV6 != nil { if len(stats.ValuesV6) != 2 { t.Errorf("unexpected Bandwidth stats value length: %d\n", len(stats.ValuesV6)) } expected1 := BandwidthValueV6{ LocalIP: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, LocalPort: 1, RemoteIP: [16]byte{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, RemotePort: 2, TransmittedBytes: 3, ReceivedBytes: 4, } if stats.ValuesV6[0] != expected1 { t.Errorf("unexpected Bandwidth stats value: %+v expected: %+v\n", stats.ValuesV6[0], expected1) } expected2 := BandwidthValueV6{ LocalIP: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, LocalPort: 5, RemoteIP: [16]byte{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, RemotePort: 6, TransmittedBytes: 7, ReceivedBytes: 8, } if stats.ValuesV6[1] != expected2 { t.Errorf("unexpected Bandwidth stats value: %+v expected: %+v\n", stats.ValuesV6[1], expected2) } } } } } func TestGenerateCommandFile(t *testing.T) { t.Parallel() file, err := os.Create("../protocol/testdata/go_command_test.bin") if err != nil { t.Errorf("failed to create file: %s", err) } defer func() { _ = file.Close() }() enums := []byte{ CommandShutdown, CommandVerdict, CommandUpdateV4, CommandUpdateV6, CommandClearCache, CommandGetLogs, CommandBandwidthStats, CommandCleanEndedConnections, } selected := make([]byte, 5000) for i := range selected { selected[i] = enums[rand.Intn(len(enums))] //nolint:gosec } for _, value := range selected { switch value { case CommandShutdown: err := SendShutdownCommand(file) if err != nil { t.Fatal(err) } case CommandVerdict: err := SendVerdictCommand(file, Verdict{ ID: 1, Verdict: 2, }) if err != nil { t.Fatal(err) } case CommandUpdateV4: err := SendUpdateV4Command(file, UpdateV4{ Protocol: 1, LocalAddress: [4]byte{1, 2, 3, 4}, LocalPort: 2, RemoteAddress: [4]byte{2, 3, 4, 5}, RemotePort: 3, Verdict: 4, }) if err != nil { t.Fatal(err) } case CommandUpdateV6: err := SendUpdateV6Command(file, UpdateV6{ Protocol: 1, LocalAddress: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, LocalPort: 2, RemoteAddress: [16]byte{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, RemotePort: 3, Verdict: 4, }) if err != nil { t.Fatal(err) } case CommandClearCache: err := SendClearCacheCommand(file) if err != nil { t.Fatal(err) } case CommandGetLogs: err := SendGetLogsCommand(file) if err != nil { t.Fatal(err) } case CommandBandwidthStats: err := SendGetBandwidthStatsCommand(file) if err != nil { t.Fatal(err) } case CommandPrintMemoryStats: err := SendPrintMemoryStatsCommand(file) if err != nil { t.Fatal(err) } case CommandCleanEndedConnections: err := SendCleanEndedConnectionsCommand(file) if err != nil { t.Fatal(err) } } } } ================================================ FILE: windows_kext/kextinterface/version.txt ================================================ [2, 1, 0, 0] ================================================ FILE: windows_kext/link-dev.ps1 ================================================ # Example script for creating debug builds. Libraries may change depending on the version of the WDK that is installed. $SDK_Version = "10.0.26100.0" link.exe /OUT:driver.sys ` /MANIFEST:NO /PROFILE /Driver ` "C:\Program Files (x86)\Windows Kits\10\lib\$SDK_Version\km\x64\wdmsec.lib" ` "C:\Program Files (x86)\Windows Kits\10\lib\$SDK_Version\km\x64\ndis.lib" ` "C:\Program Files (x86)\Windows Kits\10\lib\$SDK_Version\km\x64\fwpkclnt.lib" ` "C:\Program Files (x86)\Windows Kits\10\lib\$SDK_Version\km\x64\BufferOverflowK.lib" ` "C:\Program Files (x86)\Windows Kits\10\lib\$SDK_Version\km\x64\ntoskrnl.lib" ` "C:\Program Files (x86)\Windows Kits\10\lib\$SDK_Version\km\x64\hal.lib" ` "C:\Program Files (x86)\Windows Kits\10\lib\$SDK_Version\km\x64\wmilib.lib" ` "C:\Program Files (x86)\Windows Kits\10\lib\wdf\kmdf\x64\1.15\WdfLdr.lib" ` "C:\Program Files (x86)\Windows Kits\10\lib\wdf\kmdf\x64\1.15\WdfDriverEntry.lib" ` "driver.lib" ` /RELEASE /VERSION:"10.0" /DEBUG /MACHINE:X64 /ENTRY:"FxDriverEntry" /OPT:REF /INCREMENTAL:NO /SUBSYSTEM:NATIVE",6.01" /OPT:ICF /ERRORREPORT:PROMPT /MERGE:"_TEXT=.text;_PAGE=PAGE" /NOLOGO /NODEFAULTLIB /SECTION:"INIT,d" if(!$?) { Exit $LASTEXITCODE } ================================================ FILE: windows_kext/protocol/Cargo.toml ================================================ [package] name = "protocol" version = "0.0.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] num = { version = "0.4", default-features = false } num-derive = { version = "0.4", default-features = false } num-traits = { version = "0.2", default-features = false } [dev-dependencies] rand = "0.8.5" ================================================ FILE: windows_kext/protocol/README.md ================================================ # Protocol Defines protocol that communicates with `kextinterface` / Portmaster. The crate implements simple binary protocol. The communications is designed to be concurrent stream of packets. Input and output work independent of each other. - Pormtaster can read multiple info packets from the queue with single read request. - Portmaster can write one command packet to the kernel extension with single write request. ## Info: Kext -> Portmaster Info is a packet that sends information/events from the kernel extension to portmaster. For example: `new connection`, `end of connection`, `bandwidth stats` ... check `info.rs` for full list. The Info packet contains a header that is 5 bytes ``` 0 1 2 3 4 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Info Type | Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ``` > Note that one tick mark represents one bit position. The header is followed by the info data. ## Command: Portmaster -> Kext Command is a packet that portmaster sends to the kernel extension. For example: `verdict response`, `shutdown`, `get logs` ... check `command.rs` for full list. The header of the command packet is 1 byte ``` 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ | Command Type | +-+-+-+-+-+-+-+-+ ``` > Note that one tick mark represents one bit position. Rest of the packet will be the payload of the command (some commands don't contain payload just the command type). ================================================ FILE: windows_kext/protocol/src/command.rs ================================================ // Commands from user space use num_derive::FromPrimitive; use num_traits::FromPrimitive; #[repr(u8)] #[derive(Clone, Copy, FromPrimitive)] #[rustfmt::skip] pub enum CommandType { Shutdown = 0, Verdict = 1, UpdateV4 = 2, UpdateV6 = 3, ClearCache = 4, GetLogs = 5, GetBandwidthStats = 6, PrintMemoryStats = 7, CleanEndedConnections = 8, } #[repr(C, packed)] pub struct Command { pub command_type: CommandType, value: [u8; 0], } #[repr(C, packed)] #[derive(Debug, PartialEq, Eq)] pub struct Verdict { pub id: u64, pub verdict: u8, } #[repr(C, packed)] #[derive(Debug, PartialEq, Eq)] pub struct UpdateV4 { pub protocol: u8, pub local_address: [u8; 4], pub local_port: u16, pub remote_address: [u8; 4], pub remote_port: u16, pub verdict: u8, } #[repr(C, packed)] #[derive(Debug, PartialEq, Eq)] pub struct UpdateV6 { pub protocol: u8, pub local_address: [u8; 16], pub local_port: u16, pub remote_address: [u8; 16], pub remote_port: u16, pub verdict: u8, } pub fn parse_type(bytes: &[u8]) -> Option { FromPrimitive::from_u8(bytes[0]) } pub fn parse_verdict(bytes: &[u8]) -> &Verdict { as_type(bytes) } pub fn parse_update_v4(bytes: &[u8]) -> &UpdateV4 { as_type(bytes) } pub fn parse_update_v6(bytes: &[u8]) -> &UpdateV6 { as_type(bytes) } fn as_type(bytes: &[u8]) -> &T { let ptr: *const u8 = &bytes[0]; let t_ptr: *const T = ptr as _; unsafe { t_ptr.as_ref().unwrap() } } #[cfg(test)] use std::fs::File; #[cfg(test)] use std::io::Read; #[cfg(test)] use std::mem::size_of; #[cfg(test)] use std::panic; #[test] fn test_go_command_file() { let mut file = File::open("testdata/go_command_test.bin").unwrap(); loop { let mut command: [u8; 1] = [0]; let bytes_count = file.read(&mut command).unwrap(); if bytes_count == 0 { return; } if let Some(command) = parse_type(&command) { match command { CommandType::Shutdown => {} CommandType::Verdict => { let mut buf = [0; size_of::()]; let bytes_count = file.read(&mut buf).unwrap(); if bytes_count != size_of::() { panic!("unexpected bytes count") } assert_eq!(parse_verdict(&buf), &Verdict { id: 1, verdict: 2 }) } CommandType::UpdateV4 => { let mut buf = [0; size_of::()]; let bytes_count = file.read(&mut buf).unwrap(); if bytes_count != size_of::() { panic!("unexpected bytes count") } assert_eq!( parse_update_v4(&buf), &UpdateV4 { protocol: 1, local_address: [1, 2, 3, 4], local_port: 2, remote_address: [2, 3, 4, 5], remote_port: 3, verdict: 4 } ) } CommandType::UpdateV6 => { let mut buf = [0; size_of::()]; let bytes_count = file.read(&mut buf).unwrap(); if bytes_count != size_of::() { panic!("unexpected bytes count") } assert_eq!( parse_update_v6(&buf), &UpdateV6 { protocol: 1, local_address: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], local_port: 2, remote_address: [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 ], remote_port: 3, verdict: 4 } ) } CommandType::ClearCache => {} CommandType::GetLogs => {} CommandType::GetBandwidthStats => {} CommandType::PrintMemoryStats => {} CommandType::CleanEndedConnections => {} } } else { panic!("Unknown command: {}", command[0]); } } } ================================================ FILE: windows_kext/protocol/src/info.rs ================================================ use alloc::vec::Vec; #[repr(u8)] #[derive(Clone, Copy)] enum InfoType { LogLine = 0, ConnectionIpv4 = 1, ConnectionIpv6 = 2, ConnectionEndEventV4 = 3, ConnectionEndEventV6 = 4, BandwidthStatsV4 = 5, BandwidthStatsV6 = 6, } // Fallow this pattern when adding new packets: [InfoType: u8, data_size_in_bytes: u32, data: ...] trait PushBytes { fn push(self, vec: &mut Vec); } impl PushBytes for u8 { fn push(self, vec: &mut Vec) { vec.push(self); } } impl PushBytes for InfoType { fn push(self, vec: &mut Vec) { vec.push(self as u8); } } impl PushBytes for u16 { fn push(self, vec: &mut Vec) { vec.extend_from_slice(&u16::to_le_bytes(self)); } } impl PushBytes for u32 { fn push(self, vec: &mut Vec) { vec.extend_from_slice(&u32::to_le_bytes(self)); } } impl PushBytes for u64 { fn push(self, vec: &mut Vec) { vec.extend_from_slice(&u64::to_le_bytes(self)); } } impl PushBytes for usize { fn push(self, vec: &mut Vec) { vec.extend_from_slice(&usize::to_le_bytes(self)); } } impl PushBytes for [u8; 4] { fn push(self, vec: &mut Vec) { vec.extend_from_slice(&self); } } impl PushBytes for [u8; 16] { fn push(self, vec: &mut Vec) { vec.extend_from_slice(&self); } } impl PushBytes for &[u8] { fn push(self, vec: &mut Vec) { vec.extend_from_slice(self); } } macro_rules! push_bytes { ($vec:expr,$value:expr) => { PushBytes::push($value, $vec); }; } macro_rules! get_combined_size{ ($($a:expr),*)=>{{0 $(+core::mem::size_of_val(&$a))*}} } pub struct Info(Vec); impl Info { fn new(info_type: InfoType, size: usize) -> Self { let mut vec = Vec::with_capacity(size + 5); // +1 for the info type +4 for the size. push_bytes!(&mut vec, info_type); push_bytes!(&mut vec, size as u32); Self(vec) } fn with_capacity(info_type: InfoType, capacity: usize) -> Self { let mut vec = Vec::with_capacity(capacity + 5); // +1 for the info type + 4 for the size. push_bytes!(&mut vec, info_type); push_bytes!(&mut vec, 0 as u32); Self(vec) } #[cfg(test)] fn assert_size(&self) { let size = u32::from_le_bytes([self.0[1], self.0[2], self.0[3], self.0[4]]) as usize; assert_eq!(size, self.0.len() - 5); } fn update_size(&mut self) { let size = self.0.len() - 5; let bytes = &mut self.0; bytes[1] = size as u8; bytes[2] = (size >> 8) as u8; bytes[3] = (size >> 16) as u8; bytes[4] = (size >> 24) as u8; } pub fn as_bytes(&self) -> &[u8] { return self.0.as_slice(); } } impl core::fmt::Write for Info { fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { const MAX_CAPACITY: usize = 500; let space_left = self.0.capacity() - self.0.len(); if s.len() > space_left { if self.0.capacity() < MAX_CAPACITY { self.0.reserve(MAX_CAPACITY); } else { return Ok(()); } } self.0.extend_from_slice(s.as_bytes()); self.update_size(); Ok(()) } } pub fn connection_info_v4( id: u64, process_id: u64, direction: u8, protocol: u8, local_ip: [u8; 4], remote_ip: [u8; 4], local_port: u16, remote_port: u16, payload_layer: u8, payload: &[u8], ) -> Info { let mut size = get_combined_size!( id, process_id, direction, protocol, local_ip, remote_ip, local_port, remote_port, payload_layer, payload.len() as u32 ); size += payload.len(); let mut info = Info::new(InfoType::ConnectionIpv4, size); let vec = &mut info.0; push_bytes!(vec, id); push_bytes!(vec, process_id); push_bytes!(vec, direction); push_bytes!(vec, protocol); push_bytes!(vec, local_ip); push_bytes!(vec, remote_ip); push_bytes!(vec, local_port); push_bytes!(vec, remote_port); push_bytes!(vec, payload_layer); push_bytes!(vec, payload.len() as u32); push_bytes!(vec, payload); info } pub fn connection_info_v6( id: u64, process_id: u64, direction: u8, protocol: u8, local_ip: [u8; 16], remote_ip: [u8; 16], local_port: u16, remote_port: u16, payload_layer: u8, payload: &[u8], ) -> Info { let mut size = get_combined_size!( id, process_id, direction, protocol, local_ip, remote_ip, local_port, remote_port, payload_layer, payload.len() as u32 ); size += payload.len(); let mut info = Info::new(InfoType::ConnectionIpv6, size); let vec = &mut info.0; push_bytes!(vec, id); push_bytes!(vec, process_id); push_bytes!(vec, direction); push_bytes!(vec, protocol); push_bytes!(vec, local_ip); push_bytes!(vec, remote_ip); push_bytes!(vec, local_port); push_bytes!(vec, remote_port); push_bytes!(vec, payload_layer); push_bytes!(vec, payload.len() as u32); if !payload.is_empty() { push_bytes!(vec, payload); } info } pub fn connection_end_event_v4_info( process_id: u64, direction: u8, protocol: u8, local_ip: [u8; 4], remote_ip: [u8; 4], local_port: u16, remote_port: u16, ) -> Info { let size = get_combined_size!( process_id, direction, protocol, local_ip, remote_ip, local_port, remote_port ); let mut info = Info::new(InfoType::ConnectionEndEventV4, size); let vec = &mut info.0; push_bytes!(vec, process_id); push_bytes!(vec, direction); push_bytes!(vec, protocol); push_bytes!(vec, local_ip); push_bytes!(vec, remote_ip); push_bytes!(vec, local_port); push_bytes!(vec, remote_port); info } pub fn connection_end_event_v6_info( process_id: u64, direction: u8, protocol: u8, local_ip: [u8; 16], remote_ip: [u8; 16], local_port: u16, remote_port: u16, ) -> Info { let size = get_combined_size!( process_id, direction, protocol, local_ip, remote_ip, local_port, remote_port ); let mut info = Info::new(InfoType::ConnectionEndEventV6, size); let vec = &mut info.0; push_bytes!(vec, process_id); push_bytes!(vec, direction); push_bytes!(vec, protocol); push_bytes!(vec, local_ip); push_bytes!(vec, remote_ip); push_bytes!(vec, local_port); push_bytes!(vec, remote_port); info } #[repr(u8)] #[derive(Clone, Copy)] pub enum Severity { Trace = 1, Debug = 2, Info = 3, Warning = 4, Error = 5, Critical = 6, Disabled = 7, } // pub fn log_line(severity: Severity, prefix: String, line: String) -> Info { // let mut size = get_combined_size!(severity); // size += prefix.len() + line.len(); // let mut info = Info::new(InfoType::LogLine, size); // let vec = &mut info.0; // push_bytes!(vec, severity as u8); // push_bytes!(vec, prefix.as_bytes()); // push_bytes!(vec, line.as_bytes()); // info // } pub fn log_line(severity: Severity, capacity: usize) -> Info { let mut info = Info::with_capacity(InfoType::LogLine, capacity); let vec = &mut info.0; push_bytes!(vec, severity as u8); info } // Special struct for Bandwidth stats pub struct BandwidthValueV4 { pub local_ip: [u8; 4], pub local_port: u16, pub remote_ip: [u8; 4], pub remote_port: u16, pub transmitted_bytes: u64, pub received_bytes: u64, } impl BandwidthValueV4 { fn get_size(&self) -> usize { get_combined_size!( self.local_ip, self.local_port, self.remote_ip, self.remote_port, self.transmitted_bytes, self.received_bytes ) } } impl PushBytes for BandwidthValueV4 { fn push(self, vec: &mut Vec) { push_bytes!(vec, self.local_ip); push_bytes!(vec, self.local_port); push_bytes!(vec, self.remote_ip); push_bytes!(vec, self.remote_port); push_bytes!(vec, self.transmitted_bytes); push_bytes!(vec, self.received_bytes); } } pub struct BandwidthValueV6 { pub local_ip: [u8; 16], pub local_port: u16, pub remote_ip: [u8; 16], pub remote_port: u16, pub transmitted_bytes: u64, pub received_bytes: u64, } impl BandwidthValueV6 { fn get_size(&self) -> usize { get_combined_size!( self.local_ip, self.local_port, self.remote_ip, self.remote_port, self.transmitted_bytes, self.received_bytes ) } } impl PushBytes for BandwidthValueV6 { fn push(self, vec: &mut Vec) { push_bytes!(vec, self.local_ip); push_bytes!(vec, self.local_port); push_bytes!(vec, self.remote_ip); push_bytes!(vec, self.remote_port); push_bytes!(vec, self.transmitted_bytes); push_bytes!(vec, self.received_bytes); } } pub fn bandiwth_stats_array_v4(protocol: u8, values: Vec) -> Info { let mut size = get_combined_size!(protocol, values.len() as u32); if !values.is_empty() { size += values[0].get_size() * values.len(); } let mut info = Info::new(InfoType::BandwidthStatsV4, size); let vec = &mut info.0; push_bytes!(vec, protocol); push_bytes!(vec, values.len() as u32); for v in values { push_bytes!(vec, v); } info } pub fn bandiwth_stats_array_v6(protocol: u8, values: Vec) -> Info { let mut size = get_combined_size!(protocol, values.len() as u32); if !values.is_empty() { size += values[0].get_size() * values.len(); } let mut info = Info::new(InfoType::BandwidthStatsV6, size); let vec = &mut info.0; push_bytes!(vec, protocol); push_bytes!(vec, values.len() as u32); for v in values { push_bytes!(vec, v); } info } #[cfg(test)] use std::fs::File; #[cfg(test)] use std::io::Write; #[cfg(test)] use rand::seq::SliceRandom; #[test] fn generate_test_info_file() -> Result<(), std::io::Error> { let mut file = File::create("../kextinterface/testdata/rust_info_test.bin")?; let enums = [ InfoType::LogLine, InfoType::ConnectionIpv4, InfoType::ConnectionIpv6, InfoType::ConnectionEndEventV4, InfoType::ConnectionEndEventV6, InfoType::BandwidthStatsV4, InfoType::BandwidthStatsV6, ]; let mut selected: Vec = Vec::with_capacity(1000); let mut rng = rand::thread_rng(); for _ in 0..selected.capacity() { selected.push(enums.choose(&mut rng).unwrap().clone()); } // Write wrong size data. To make sure that mismatches between kext and portmaster are handled properly. let mut info = connection_info_v6( 1, 2, 3, 4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], 5, 6, 7, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ); info.assert_size(); info.0[0] = InfoType::ConnectionIpv4 as u8; file.write_all(&info.0)?; for value in selected { file.write_all(&match value { InfoType::LogLine => { let mut info = log_line(Severity::Trace, 5); use std::fmt::Write; _ = write!(info, "prefix: test log"); info.assert_size(); info.0 } InfoType::ConnectionIpv4 => { let info = connection_info_v4( 1, 2, 3, 4, [1, 2, 3, 4], [2, 3, 4, 5], 5, 6, 7, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ); info.assert_size(); info.0 } InfoType::ConnectionIpv6 => { let info = connection_info_v6( 1, 2, 3, 4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], 5, 6, 7, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ); info.assert_size(); info.0 } InfoType::ConnectionEndEventV4 => { let info = connection_end_event_v4_info(1, 2, 3, [1, 2, 3, 4], [2, 3, 4, 5], 4, 5); info.assert_size(); info.0 } InfoType::ConnectionEndEventV6 => { let info = connection_end_event_v6_info( 1, 2, 3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], 4, 5, ); info.assert_size(); info.0 } InfoType::BandwidthStatsV4 => { let mut vec = Vec::new(); vec.push(BandwidthValueV4 { local_ip: [1, 2, 3, 4], local_port: 1, remote_ip: [2, 3, 4, 5], remote_port: 2, transmitted_bytes: 3, received_bytes: 4, }); vec.push(BandwidthValueV4 { local_ip: [1, 2, 3, 4], local_port: 5, remote_ip: [2, 3, 4, 5], remote_port: 6, transmitted_bytes: 7, received_bytes: 8, }); let info = bandiwth_stats_array_v4(1, vec); info.assert_size(); info.0 } InfoType::BandwidthStatsV6 => { let mut vec = Vec::new(); vec.push(BandwidthValueV6 { local_ip: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], local_port: 1, remote_ip: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], remote_port: 2, transmitted_bytes: 3, received_bytes: 4, }); vec.push(BandwidthValueV6 { local_ip: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], local_port: 5, remote_ip: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], remote_port: 6, transmitted_bytes: 7, received_bytes: 8, }); let info = bandiwth_stats_array_v6(1, vec); info.assert_size(); info.0 } })?; } return Ok(()); } ================================================ FILE: windows_kext/protocol/src/lib.rs ================================================ #![cfg_attr(not(test), no_std)] extern crate alloc; pub mod command; pub mod info; ================================================ FILE: windows_kext/test/BUILD_DEBUG.md ================================================ # Building and Running Driver with Debug Logging ## Driver Signing Requirement Windows requires **all kernel drivers to be signed**. Test signing provides a free alternative to expensive production code signing certificates for development and testing purposes. ## Important: Debug Builds Are Disabled ⚠️ **The driver cannot be compiled in debug mode.** The code contains a compile-time check (`compile_error!`) that prevents debug builds due to potential optimization-related issues and inconsistent compiler behavior between debug and release modes. However, you can still enable verbose logging in release builds by changing the log level. ## Prerequisites Already documented in [main README](../README.md), but quick recap: 1. **Visual Studio 2022** with C++ and Windows SDK 2. **Windows Driver Kit (WDK)** installed 3. **Rust toolchain** installed 4. **Test signing enabled** (see below) ## Step 1: Enable Test Signing (One-time Setup) ⚠️ **SECURITY WARNING**: Test signing reduces system security by allowing any locally-generated test certificate to load kernel drivers. **Strongly recommended to use a VM or dedicated test machine**. See "Disabling Test Signing" section below to restore security when done testing. ### Create Test Certificate Open **PowerShell as Administrator**: ```powershell # Create a self-signed certificate for driver testing MakeCert -r -pe -ss PrivateCertStore -n "CN=DriverTestCert" DriverTestCert.cer # Install the certificate to Trusted Root CertMgr /add DriverTestCert.cer /s /r localMachine root # Install to Trusted Publishers (needed for driver installation) CertMgr /add DriverTestCert.cer /s /r localMachine trustedpublisher ``` ### Enable Test Signing Mode ```powershell # Enable test signing Bcdedit.exe -set TESTSIGNING ON # Restart required! Restart-Computer ``` After restart, you should see **"Test Mode"** watermark in the corner of your screen. ### Verify Test Signing is Enabled ```powershell bcdedit /enum | Select-String testsigning # Should show: testsigning Yes ``` ## Step 2: Enable Debug Logging in Driver To see verbose logs from the driver, edit the log level before building. **Edit `driver/src/logger.rs`:** ```rust // Change line 8 from: pub const LOG_LEVEL: u8 = Severity::Warning as u8; // To one of: pub const LOG_LEVEL: u8 = Severity::Debug as u8; // Recommended for testing // pub const LOG_LEVEL: u8 = Severity::Info as u8; // Less verbose // pub const LOG_LEVEL: u8 = Severity::Trace as u8; // Most verbose ``` For testing, `Debug` level is recommended. ## Step 3: Build Driver in Release Mode Navigate to the driver directory: ```powershell cd D:\Projects\Portmaster\portmaster\windows_kext\driver # Build in release mode (only mode supported) cargo build --release --target x86_64-pc-windows-msvc # Output: driver/target/x86_64-pc-windows-msvc/release/driver.lib ``` **Note:** Debug builds (`cargo build` without `--release`) will fail with a compile error by design. ## Step 4: Link the Driver Copy the `.lib` file to the root directory: ```powershell cd D:\Projects\Portmaster\portmaster\windows_kext Copy-Item driver/target/x86_64-pc-windows-msvc/release/driver.lib . -Force ``` Run the linker script: ```powershell .\link-dev.ps1 ``` This creates `driver.sys` in the current directory. ## Step 5: Sign the Driver ## Step 5: Sign the Driver ```powershell cd D:\Projects\Portmaster\portmaster\windows_kext # Sign the driver SignTool sign /v /s PrivateCertStore /n DriverTestCert driver.sys ``` Verify signature: ```powershell SignTool verify /v /pa driver.sys ``` You should see: **"Successfully verified: driver.sys"** ## Step 6: View Driver Logs ### Ring Buffer Logs (Recommended) These logs come through the `GetLogs` command. ### Kernel Debugger Output (Not Available in Release) The `wdk::dbg!()`, `wdk::info!()`, and `wdk::err!()` macros only work in debug builds, which are disabled for this driver. These would output to tools like DebugView via `DbgPrint`, but since debug builds are not allowed, this logging path is not available. **Use the ring buffer logs** (captured by `dbg!`, `info!`, `warn!`, `err!` macros) for all debugging. ## Common Issues ### "The hash for the file is not present in the specified catalog file" **Solution**: Your driver isn't signed or the certificate isn't trusted. ```powershell # Re-sign the driver SignTool sign /v /s PrivateCertStore /n DriverTestCert driver.sys ``` ### "Windows cannot verify the digital signature" **Solution**: Test signing not enabled or certificate not in Trusted Root. ```powershell # Check test signing bcdedit /enum | Select-String testsigning # Reinstall certificate if needed CertMgr /add DriverTestCert.cer /s /r localMachine root ``` ### "Service marked for deletion" **Solution**: Manually clean up: ```powershell sc stop PortmasterKext sc delete PortmasterKext # Wait a few seconds # Then try starting again ``` ### "Access is denied" when creating service **Solution**: Run as Administrator. ### No debug output (`GetLogs` command) **Solution**: 1. Make sure you edited `driver/src/logger.rs` to set `LOG_LEVEL = Severity::Debug` 2. Rebuild the driver in **release mode** (`cargo build --release`) 3. The driver must be actively running and processing connections to generate logs 4. Default log level (`Warning`) only shows errors, not normal operations ## Quick Build & Test Cycle ```powershell # 1. (Optional) Enable debug logging - edit driver/src/logger.rs first # 2. Build driver in release mode cd D:\Projects\Portmaster\portmaster\windows_kext\driver cargo build --release # 3. Link and sign cd .. Copy-Item driver/target/x86_64-pc-windows-msvc/release/driver.lib . -Force .\link-dev.ps1 SignTool sign /v /s PrivateCertStore /n DriverTestCert driver.sys # 4. Test (in playground, as Administrator) ``` ## Disabling Test Signing (When Done Testing) ⚠️ **IMPORTANT**: When finished testing, disable test signing to restore system security. ```powershell # Run as Administrator Bcdedit.exe -set TESTSIGNING OFF # Restart required for changes to take effect Restart-Computer ``` After restart, the "Test Mode" watermark will disappear and the system will no longer accept test-signed drivers. This restores normal kernel driver security enforcement.Production vs Test Signing ================================================ FILE: windows_kext/test/README.md ================================================ # Test Directory > ⚠️ **Notice**: This folder and its contents were primarily generated with the assistance of AI and may contain errors or inaccuracies. They are intended solely for local testing and development and must not be used in production. ## Contents - `build_test.ps1` - Script to build the test-signed driver - `_out/` - Output directory for built test driver - `_testcert/` - Test certificates for driver signing ## Purpose This directory contains tools and utilities for testing the Portmaster Windows kernel driver during development. These are developer tools only and are not part of the production build or release process. ================================================ FILE: windows_kext/test/build_test.ps1 ================================================ # Build and Sign Test Driver Script # Must be run from Developer PowerShell for Visual Studio $ErrorActionPreference = "Stop" # Get script directory and set paths $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $rootDir = Split-Path -Parent $scriptDir $driverDir = Join-Path $rootDir "driver" $certPath = Join-Path $rootDir "test\_testcert\DriverTestCert.cer" $outDir = Join-Path $scriptDir "_out" # Create output directory if it doesn't exist if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir -Force | Out-Null } Write-Host "=================================================" -ForegroundColor Cyan Write-Host " Building and Signing Test Driver" -ForegroundColor Cyan Write-Host "=================================================" -ForegroundColor Cyan Write-Host "" # Verify we are in the correct directory if (-not (Test-Path $driverDir)) { Write-Host "ERROR: Driver directory not found at: $driverDir" -ForegroundColor Red Write-Host "Please run this script from the windows_kext root directory or ensure paths are correct." -ForegroundColor Red exit 1 } # Verify certificate exists if (-not (Test-Path $certPath)) { Write-Host "ERROR: Certificate not found at: $certPath" -ForegroundColor Red Write-Host "Please create the test certificate first." -ForegroundColor Red exit 1 } # # Step 1: Build Driver in Release Mode # Write-Host "[1/3] Building driver in release mode..." -ForegroundColor Yellow Push-Location $driverDir try { cargo build --release --target x86_64-pc-windows-msvc if ($LASTEXITCODE -ne 0) { Write-Host "ERROR: Cargo build failed with exit code $LASTEXITCODE" -ForegroundColor Red exit $LASTEXITCODE } Write-Host " Driver built successfully" -ForegroundColor Green } finally { Pop-Location } # # Step 2: Link the Driver # Write-Host "[2/3] Linking driver..." -ForegroundColor Yellow Push-Location $outDir try { # Copy the .lib file to output directory $libSource = Join-Path $driverDir "target\x86_64-pc-windows-msvc\release\driver.lib" $libDest = Join-Path $outDir "driver.lib" if (-not (Test-Path $libSource)) { Write-Host "ERROR: Built driver.lib not found at: $libSource" -ForegroundColor Red exit 1 } Copy-Item $libSource $libDest -Force Write-Host " Copied driver.lib" -ForegroundColor Green # Run linker script (from output directory so files are created here) $linkScript = Join-Path $rootDir "link-dev.ps1" if (-not (Test-Path $linkScript)) { Write-Host "ERROR: link-dev.ps1 not found at: $linkScript" -ForegroundColor Red exit 1 } & $linkScript if ($LASTEXITCODE -ne 0) { Write-Host "ERROR: Linking failed with exit code $LASTEXITCODE" -ForegroundColor Red exit $LASTEXITCODE } # Rename driver.sys to test name $sysFile = Join-Path $outDir "driver.sys" if (-not (Test-Path $sysFile)) { Write-Host "ERROR: driver.sys was not created" -ForegroundColor Red exit 1 } $testSysFile = Join-Path $outDir "PortmasterKext_test.sys" Move-Item $sysFile $testSysFile -Force Write-Host " Driver linked successfully (PortmasterKext_test.sys)" -ForegroundColor Green } finally { Pop-Location } # # Step 3: Sign the Driver # Write-Host "[3/3] Signing driver..." -ForegroundColor Yellow Push-Location $outDir try { $sysFile = Join-Path $outDir "PortmasterKext_test.sys" # Sign the driver SignTool sign /v /fd SHA256 /s PrivateCertStore /n DriverTestCert $sysFile if ($LASTEXITCODE -ne 0) { Write-Host "ERROR: Signing failed with exit code $LASTEXITCODE" -ForegroundColor Red Write-Host "Make sure the certificate is installed in PrivateCertStore" -ForegroundColor Yellow exit $LASTEXITCODE } Write-Host " Driver signed successfully" -ForegroundColor Green # Verify signature Write-Host "" Write-Host "Verifying signature..." -ForegroundColor Yellow SignTool verify /v /pa $sysFile if ($LASTEXITCODE -ne 0) { Write-Host "WARNING: Signature verification failed" -ForegroundColor Yellow } else { Write-Host " Signature verified" -ForegroundColor Green } } finally { Pop-Location } Write-Host "" Write-Host "=================================================" -ForegroundColor Cyan Write-Host " Build Complete!" -ForegroundColor Green Write-Host "=================================================" -ForegroundColor Cyan Write-Host "" Write-Host "Output directory: $outDir" -ForegroundColor White Write-Host "Driver file: PortmasterKext_test.sys" -ForegroundColor White Write-Host "" Write-Host "Next steps:" -ForegroundColor Yellow Write-Host " 1. Run playground as Administrator" -ForegroundColor White Write-Host " 2. Use start command to load the driver" -ForegroundColor White Write-Host "" ================================================ FILE: windows_kext/test_protocol.sh ================================================ #!/bin/sh echo Running tests echo ======================== cd protocol cargo test cd ../kextinterface go test -v . ================================================ FILE: windows_kext/wdk/.cargo/config.toml ================================================ [build] target = "x86_64-pc-windows-msvc" ================================================ FILE: windows_kext/wdk/Cargo.toml ================================================ [package] name = "wdk" version = "0.0.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] ntstatus = { version = "0.1.2", default-features = false } [dependencies.widestring] version = "1.0.2" default-features = false features = ["alloc"] # WARNING: Do not update. The version was choosen for a reason. See wdk/README.md for more detiels. [dependencies.windows-sys] git = "https://github.com/microsoft/windows-rs" rev = "dffa8b03dc4987c278d82e88015ffe96aa8ac317" features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Wdk_System_SystemServices", "Win32_Foundation", "Win32_Security", "Win32_System_IO", "Win32_System_Kernel", "Win32_System_Power", "Win32_System_WindowsProgramming", "Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_NetworkManagement_WindowsFilteringPlatform", "Win32_System_Rpc"] ================================================ FILE: windows_kext/wdk/README.md ================================================ # WDK (Windows Driver Kit) A library that interfaces with the windows kernel. The crate has extensive use of **unsafe** rust, be more causes when making changes. Do not update `windows-sys` dependency. The version contains bugs that have specific workarounds in this crate. Updating without reviewing the new version can result in broken build or undefined behavior. see: `wdk/src/driver.rs` see: `wdk/src/irp_helper.rs` Open issues need to be resolved: https://github.com/microsoft/windows-rs/issues/2805 Resolved: https://github.com/microsoft/wdkmetadata/issues/59 ================================================ FILE: windows_kext/wdk/build.rs ================================================ #[cfg(target_arch = "x86_64")] fn main() { // C Helper println!("cargo:rerun-if-changed=../c_helper/x64/c_helper.lib"); println!("cargo:rustc-link-search=native=../c_helper/x64"); } #[cfg(target_arch = "aarch64")] fn main() { // C Helper println!("cargo:rerun-if-changed=../c_helper/ARM64/c_helper.lib"); println!("cargo:rustc-link-search=native=../c_helper/ARM64"); } ================================================ FILE: windows_kext/wdk/rust-analyzer.cargo.target ================================================ x86_64-pc-windows-msvc ================================================ FILE: windows_kext/wdk/rust-toolchain ================================================ stable ================================================ FILE: windows_kext/wdk/src/allocator.rs ================================================ extern crate alloc; use core::alloc::{GlobalAlloc, Layout}; use alloc::alloc::handle_alloc_error; use windows_sys::Wdk::System::SystemServices::{ExAllocatePool2, ExFreePoolWithTag}; // For reference: https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/pool_flags #[allow(dead_code)] #[repr(u64)] enum PoolType { RequiredStartUseQuota = 0x0000000000000001, Uninitialized = 0x0000000000000002, // Don't zero-initialize allocation Session = 0x0000000000000004, // Use session specific pool CacheAligned = 0x0000000000000008, // Cache aligned allocation RaiseOnFailure = 0x0000000000000020, // Raise exception on failure NonPaged = 0x0000000000000040, // Non paged pool NX NonPagedExecute = 0x0000000000000080, // Non paged pool executable Paged = 0x0000000000000100, // Paged pool RequiredEnd = 0x0000000080000000, OptionalStart = 0x0000000100000000, OptionalEnd = 0x8000000000000000, } pub struct WindowsAllocator {} unsafe impl Sync for WindowsAllocator {} pub(crate) const POOL_TAG: u32 = u32::from_ne_bytes(*b"PMrs"); unsafe impl GlobalAlloc for WindowsAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { let pool = ExAllocatePool2(PoolType::NonPaged as u64, layout.size(), POOL_TAG); if pool.is_null() { handle_alloc_error(layout); } pool as *mut u8 } unsafe fn dealloc(&self, ptr: *mut u8, _: Layout) { ExFreePoolWithTag(ptr as _, POOL_TAG); } unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { self.alloc(layout) } unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { // SAFETY: the caller must ensure that the `new_size` does not overflow. // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid. let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; // SAFETY: the caller must ensure that `new_layout` is greater than zero. let new_ptr = unsafe { self.alloc(new_layout) }; if !new_ptr.is_null() { // SAFETY: the previously allocated block cannot overlap the newly allocated block. // The safety contract for `dealloc` must be upheld by the caller. unsafe { core::ptr::copy_nonoverlapping( ptr, new_ptr, core::cmp::min(layout.size(), new_size), ); self.dealloc(ptr, layout); } } new_ptr } } ================================================ FILE: windows_kext/wdk/src/attributes.rs ================================================ extern crate proc_macro; use proc_macro::TokenStream; use quote::quote; // using proc_macro_attribute to declare an attribute like procedural macro #[proc_macro_attribute] // _metadata is argument provided to macro call and _input is code to which attribute like macro attaches pub fn my_custom_attribute(_metadata: TokenStream, _input: TokenStream) -> TokenStream { // returning a simple TokenStream for Struct TokenStream::from(quote! {struct H{}}) } ================================================ FILE: windows_kext/wdk/src/consts.rs ================================================ // Actions pub const FWP_ACTION_FLAG_TERMINATING: u32 = 0x00001000; pub const FWP_ACTION_FLAG_NON_TERMINATING: u32 = 0x00002000; pub const FWP_ACTION_FLAG_CALLOUT: u32 = 0x00004000; pub const FWP_ACTION_BLOCK: u32 = 0x00000001 | FWP_ACTION_FLAG_TERMINATING; pub const FWP_ACTION_PERMIT: u32 = 0x00000002 | FWP_ACTION_FLAG_TERMINATING; pub const FWP_ACTION_CALLOUT_TERMINATING: u32 = 0x00000003 | FWP_ACTION_FLAG_CALLOUT | FWP_ACTION_FLAG_TERMINATING; pub const FWP_ACTION_CALLOUT_INSPECTION: u32 = 0x00000004 | FWP_ACTION_FLAG_CALLOUT | FWP_ACTION_FLAG_NON_TERMINATING; pub const FWP_ACTION_CALLOUT_UNKNOWN: u32 = 0x00000005 | FWP_ACTION_FLAG_CALLOUT; pub const FWP_ACTION_CONTINUE: u32 = 0x00000006 | FWP_ACTION_FLAG_NON_TERMINATING; pub const FWP_ACTION_NONE: u32 = 0x00000007; pub const FWP_ACTION_NONE_NO_MATCH: u32 = 0x00000008; pub const FWP_CONDITION_FLAG_IS_LOOPBACK: u32 = 0x00000001; pub const FWP_CONDITION_FLAG_IS_IPSEC_SECURED: u32 = 0x00000002; pub const FWP_CONDITION_FLAG_IS_REAUTHORIZE: u32 = 0x00000004; pub const FWP_CONDITION_FLAG_IS_WILDCARD_BIND: u32 = 0x00000008; pub const FWP_CONDITION_FLAG_IS_RAW_ENDPOINT: u32 = 0x00000010; pub const FWP_CONDITION_FLAG_IS_FRAGMENT: u32 = 0x00000020; pub const FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP: u32 = 0x00000040; pub const FWP_CONDITION_FLAG_IS_IPSEC_NATT_RECLASSIFY: u32 = 0x00000080; pub const FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY: u32 = 0x00000100; pub const FWP_CONDITION_FLAG_IS_IMPLICIT_BIND: u32 = 0x00000200; pub const FWP_CONDITION_FLAG_IS_REASSEMBLED: u32 = 0x00000400; pub const FWP_CONDITION_FLAG_IS_NAME_APP_SPECIFIED: u32 = 0x00004000; pub const FWP_CONDITION_FLAG_IS_PROMISCUOUS: u32 = 0x00008000; pub const FWP_CONDITION_FLAG_IS_AUTH_FW: u32 = 0x00010000; pub const FWP_CONDITION_FLAG_IS_RECLASSIFY: u32 = 0x00020000; pub const FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU: u32 = 0x00040000; pub const FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU: u32 = 0x00080000; pub const FWP_CONDITION_FLAG_IS_CONNECTION_REDIRECTED: u32 = 0x00100000; // Driver pub const METHOD_BUFFERED: u32 = 0; pub const METHOD_IN_DIRECT: u32 = 1; pub const METHOD_OUT_DIRECT: u32 = 2; pub const METHOD_NEITHER: u32 = 3; pub const SIOCTL_TYPE: u32 = 40000; pub const FILE_READ_DATA: u32 = 0x00000001; pub const FILE_READ_ATTRIBUTES: u32 = 0x00000080; pub const FILE_READ_EA: u32 = 0x00000008; pub const FILE_WRITE_DATA: u32 = 0x00000002; pub const FILE_WRITE_ATTRIBUTES: u32 = 0x00000100; pub const FILE_WRITE_EA: u32 = 0x00000010; pub const FILE_APPEND_DATA: u32 = 0x00000004; pub const FILE_EXECUTE: u32 = 0x00000020; ================================================ FILE: windows_kext/wdk/src/debug.rs ================================================ #[cfg(debug_assertions)] #[macro_export] macro_rules! log { ($level:expr, $($arg:tt)*) => ({ let message = alloc::format!($($arg)*); $crate::interface::dbg_print(alloc::format!("{} {}: {}", $level, core::module_path!(), message)); }); } #[cfg(not(debug_assertions))] #[macro_export] macro_rules! log { ($($arg:expr),*) => {{ $( _ = $arg; )* }}; } #[macro_export] macro_rules! err { ($($arg:tt)*) => ($crate::log!("ERROR", $($arg)*)); } #[macro_export] macro_rules! dbg { ($($arg:tt)*) => ($crate::log!("DEBUG", $($arg)*)); } #[macro_export] macro_rules! info { ($($arg:tt)*) => ($crate::log!("INFO", $($arg)*)); } ================================================ FILE: windows_kext/wdk/src/driver.rs ================================================ use windows_sys::{ Wdk::Foundation::{DEVICE_OBJECT, DRIVER_DISPATCH, DRIVER_OBJECT, DRIVER_UNLOAD}, Win32::Foundation::HANDLE, }; use crate::{ interface, irp_helpers::{ReadRequest, WriteRequest}, }; pub trait Device { fn new(driver: &Driver) -> Self; fn cleanup(&mut self); fn read(&mut self, read_request: &mut ReadRequest); fn write(&mut self, write_request: &mut WriteRequest); fn shutdown(&mut self); } pub struct Driver { _device_handle: HANDLE, driver_object: *mut DRIVER_OBJECT, device_object: *mut DEVICE_OBJECT, } unsafe impl Sync for Driver {} impl Driver { pub(crate) fn new( driver_object: *mut DRIVER_OBJECT, _driver_handle: HANDLE, device_handle: HANDLE, ) -> Driver { return Driver { // driver_handle, _device_handle: device_handle, driver_object, device_object: interface::wdf_device_wdm_get_device_object(device_handle), }; } pub fn get_device_object(&self) -> *mut DEVICE_OBJECT { return self.device_object; } pub fn get_device_object_ref(&self) -> Option<&mut DEVICE_OBJECT> { return unsafe { self.device_object.as_mut() }; } pub fn set_driver_unload(&mut self, driver_unload: DRIVER_UNLOAD) { if let Some(driver) = unsafe { self.driver_object.as_mut() } { driver.DriverUnload = driver_unload } } pub fn set_read_fn(&mut self, mj_fn: DRIVER_DISPATCH) { self.set_major_fn(windows_sys::Wdk::System::SystemServices::IRP_MJ_READ, mj_fn); } pub fn set_write_fn(&mut self, mj_fn: DRIVER_DISPATCH) { self.set_major_fn( windows_sys::Wdk::System::SystemServices::IRP_MJ_WRITE, mj_fn, ); } pub fn set_create_fn(&mut self, mj_fn: DRIVER_DISPATCH) { self.set_major_fn( windows_sys::Wdk::System::SystemServices::IRP_MJ_CREATE, mj_fn, ); } pub fn set_device_control_fn(&mut self, mj_fn: DRIVER_DISPATCH) { self.set_major_fn( windows_sys::Wdk::System::SystemServices::IRP_MJ_DEVICE_CONTROL, mj_fn, ); } pub fn set_close_fn(&mut self, mj_fn: DRIVER_DISPATCH) { self.set_major_fn( windows_sys::Wdk::System::SystemServices::IRP_MJ_CLOSE, mj_fn, ); } pub fn set_cleanup_fn(&mut self, mj_fn: DRIVER_DISPATCH) { self.set_major_fn( windows_sys::Wdk::System::SystemServices::IRP_MJ_CLEANUP, mj_fn, ); } fn set_major_fn(&mut self, fn_index: u32, mj_fn: DRIVER_DISPATCH) { if let Some(driver) = unsafe { self.driver_object.as_mut() } { driver.MajorFunction[fn_index as usize] = mj_fn } } } ================================================ FILE: windows_kext/wdk/src/error.rs ================================================ // use anyhow::anyhow; // pub fn anyhow_ntstatus(status: i32) -> anyhow::Error { // if let Some(value) = ntstatus::ntstatus::NtStatus::from_u32(status as u32) { // return anyhow!(value); // } // return anyhow!("UNKNOWN_NTSTATUS_CODE"); // } ================================================ FILE: windows_kext/wdk/src/fast_mutex.rs ================================================ use alloc::boxed::Box; use core::{ cell::UnsafeCell, ops::{Deref, DerefMut}, }; use windows_sys::{ Wdk::{ Foundation::{FAST_MUTEX, KEVENT}, System::SystemServices::FM_LOCK_BIT, }, Win32::System::Kernel::{SynchronizationEvent, EVENT_TYPE}, }; // #[link(name = "NtosKrnl", kind = "static")] extern "C" { fn KeInitializeEvent(event: *mut KEVENT, event_type: EVENT_TYPE, state: bool); /// The ExAcquireFastMutex routine acquires the given fast mutex with APCs to the current thread disabled. fn ExAcquireFastMutex(kmutex: *mut FAST_MUTEX); /// The ExTryToAcquireFastMutex routine acquires the given fast mutex, if possible, with APCs to the current thread disabled. fn ExTryToAcquireFastMutex(kmutex: *mut FAST_MUTEX) -> bool; // The ExReleaseFastMutex routine releases ownership of a fast mutex that was acquired with ExAcquireFastMutex or ExTryToAcquireFastMutex. fn ExReleaseFastMutex(kmutex: *mut FAST_MUTEX); } /// The ExInitializeFastMutex routine initializes a fast mutex variable, used to synchronize mutually exclusive access by a set of threads to a shared resource. /// ExInitializeFastMutex must be called before any calls to other ExXxxFastMutex routines occur. #[allow(non_snake_case)] unsafe fn ExInitializeFastMutex(kmutex: *mut FAST_MUTEX) { core::ptr::write_volatile(&mut (*kmutex).Count, FM_LOCK_BIT as i32); // (*kmutex).Count = FM_LOCK_BIT as i32; (*kmutex).Owner = core::ptr::null_mut(); (*kmutex).Contention = 0; KeInitializeEvent(&mut (*kmutex).Event, SynchronizationEvent, false) } pub struct FastMutex { kmutex: UnsafeCell>, val: UnsafeCell, } impl FastMutex { pub const fn default(val: T) -> Self { Self { kmutex: UnsafeCell::new(None), val: UnsafeCell::new(val), } } pub fn init(&self) { let mutex = Box::into_raw(Box::new(unsafe { MaybeUninit::zeroed().assume_init(); })); unsafe { ExInitializeFastMutex(mutex); *self.kmutex.get() = Some(mutex); } } pub fn deinit(&self) { unsafe { let opt = &mut (*self.kmutex.get()); if let Some(mutex) = opt { _ = Box::from_raw(mutex); } opt.take(); } } pub fn lock(&self) -> Result, ()> { unsafe { if let Some(mutex) = *self.kmutex.get() { ExAcquireFastMutex(mutex); return Ok(LockGuard::new(self)); } } return Err(()); } pub fn try_lock(&self) -> Option> { unsafe { if let Some(mutex) = *self.kmutex.get() { ExTryToAcquireFastMutex(mutex); return Some(LockGuard::new(self)); } } return None; } fn get<'a>(&self) -> *mut T { self.val.get() } fn unlock(&self) { unsafe { if let Some(mutex) = *self.kmutex.get() { ExReleaseFastMutex(mutex); } else { panic!("Mutex not initialized"); } } } } impl Drop for FastMutex { fn drop(&mut self) { self.deinit(); } } pub struct LockGuard<'a, T> { mutex: &'a FastMutex, } impl<'a, T> LockGuard<'a, T> { fn new(mutex: &'a FastMutex) -> Self { return LockGuard { mutex }; } } impl<'a, T> Drop for LockGuard<'a, T> { fn drop(&mut self) { self.mutex.unlock(); } } impl<'a, T> Deref for LockGuard<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { unsafe { &*self.mutex.get() } } } impl<'a, T> DerefMut for LockGuard<'a, T> { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { &mut *self.mutex.get() } } } ================================================ FILE: windows_kext/wdk/src/ffi.rs ================================================ #![allow(non_snake_case)] use core::ffi::c_void; use windows_sys::{ core::{GUID, PCWSTR}, Wdk::Foundation::{DEVICE_OBJECT, DRIVER_OBJECT, MDL}, Win32::{ Foundation::{HANDLE, NTSTATUS, UNICODE_STRING}, NetworkManagement::WindowsFilteringPlatform::{ FWPM_PROVIDER_CONTEXT2, FWP_CONDITION_VALUE0, FWP_MATCH_TYPE, FWP_VALUE0, }, Networking::WinSock::{ADDRESS_FAMILY, SCOPE_ID}, System::Kernel::COMPARTMENT_ID, }, }; use crate::filter_engine::{ classify::ClassifyOut, layer::IncomingValues, metadata::FwpsIncomingMetadataValues, }; pub(crate) type FwpsCalloutClassifyFn = unsafe extern "C" fn( inFixedValues: *const IncomingValues, inMetaValues: *const FwpsIncomingMetadataValues, layerData: *mut c_void, classifyContext: *mut c_void, filter: *const FWPS_FILTER2, flowContext: u64, classifyOut: *mut ClassifyOut, ); pub(crate) type FwpsCalloutNotifyFn = unsafe extern "C" fn( notifyType: u32, filterKey: *const GUID, filter: *mut FWPS_FILTER2, ) -> NTSTATUS; pub(crate) type FwpsCalloutFlowDeleteNotifyFn = unsafe extern "C" fn(layerId: u16, calloutId: u32, flowContext: u64); /// The FWPS_ACTION0 structure specifies the run-time action that the filter engine takes if all of the filter's filtering conditions are true. #[allow(non_camel_case_types, non_snake_case)] #[repr(C)] pub(crate) struct FWPS_ACTION0 { r#type: u32, calloutId: u32, } /// The FWPS_FILTER_CONDITION0 structure defines a run-time filtering condition for a filter. #[allow(non_camel_case_types, non_snake_case)] #[repr(C)] pub(crate) struct FWPS_FILTER_CONDITION0 { fieldId: u16, reserved: u16, matchType: FWP_MATCH_TYPE, conditionValue: FWP_CONDITION_VALUE0, } /// The WdfExecutionLevel enumeration type specifies the maximum IRQL at which the framework will call the event callback functions that a driver has supplied for a framework object. #[repr(C)] enum WdfExecutionLevel { Invalid = 0, InheritFromParent, Passive, Dispatch, } /// The WDF_SYNCHRONIZATION_SCOPE enumeration type specifies how the framework will synchronize execution of an object's event callback functions. #[repr(C)] enum WdfSynchronizationScope { Invalid = 0x00, InheritFromParent, Device, Queue, None, } unsafe impl Sync for WdfObjectContextTypeInfo {} /// The FWPS_FILTER2 structure defines a run-time filter in the filter engine. #[allow(non_camel_case_types, non_snake_case)] #[repr(C)] pub(crate) struct FWPS_FILTER2 { pub(crate) filterId: u64, pub(crate) weight: FWP_VALUE0, pub(crate) subLayerWeight: u16, pub(crate) flags: u16, pub(crate) numFilterConditions: u32, pub(crate) filterCondition: *mut FWPS_FILTER_CONDITION0, pub(crate) action: FWPS_ACTION0, pub(crate) context: u64, pub(crate) providerContext: *mut FWPM_PROVIDER_CONTEXT2, } /// The FWPS_CALLOUT3 structure defines the data that is required for a callout driver to register a callout with the filter engine. #[allow(non_camel_case_types, non_snake_case)] #[repr(C)] pub(crate) struct FWPS_CALLOUT3 { pub(crate) calloutKey: GUID, pub(crate) flags: u32, pub(crate) classifyFn: Option, pub(crate) notifyFn: Option, pub(crate) flowDeleteFn: Option, } /// The filter engine calls a callout's completionFn callout function whenever packet data, described by the netBufferList parameter in one of the packet injection functions, has been injected into the network stack. #[allow(non_camel_case_types)] type FWPS_INJECT_COMPLETE0 = unsafe extern "C" fn( context: *mut c_void, net_buffer_list: *mut NET_BUFFER_LIST, dispatch_level: bool, ); /// The FWPS_TRANSPORT_SEND_PARAMS1 structure defines properties of an outbound transport layer packet. #[allow(non_camel_case_types)] #[repr(C)] pub(crate) struct FWPS_TRANSPORT_SEND_PARAMS1 { pub(crate) remote_address: *const u8, pub(crate) remote_scope_id: SCOPE_ID, pub(crate) control_data: *mut c_void, //WSACMSGHDR, pub(crate) control_data_length: u32, pub(crate) header_include_header: *mut u8, pub(crate) header_include_header_length: u32, } /// The FWPS_PACKET_INJECTION_STATE enumeration type specifies the injection state of a network buffer list. #[allow(non_camel_case_types)] #[repr(C)] pub(crate) enum FWPS_PACKET_INJECTION_STATE { FWPS_PACKET_NOT_INJECTED, FWPS_PACKET_INJECTED_BY_SELF, FWPS_PACKET_INJECTED_BY_OTHER, FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF, FWPS_PACKET_INJECTION_STATE_MAX, } pub(crate) const FWPS_INJECTION_TYPE_STREAM: u32 = 0x00000001; pub(crate) const FWPS_INJECTION_TYPE_TRANSPORT: u32 = 0x00000002; pub(crate) const FWPS_INJECTION_TYPE_NETWORK: u32 = 0x00000004; pub(crate) const FWPS_INJECTION_TYPE_FORWARD: u32 = 0x00000008; pub(crate) const FWPS_INJECTION_TYPE_L2: u32 = 0x00000010; pub(crate) const FWPS_INJECTION_TYPE_VSWITCH_TRANSPORT: u32 = 0x00000020; pub(crate) const NDIS_OBJECT_TYPE_DEFAULT: u8 = 0x80; // used when object type is implicit in the API call pub(crate) const NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1: u8 = 1; /// The NBListHeader is the header of NET_BUFFER_LIST struct. #[repr(C)] pub(crate) struct NBListHeader { pub(crate) next: *mut NET_BUFFER_LIST, pub(crate) first_net_buffer: *mut NET_BUFFER, } /// The NET_BUFFER_LIST structure specifies a linked list of NET_BUFFER structures. /// This is internal struct should never be allocated from the driver. Use provided functions by microsoft. #[allow(non_camel_case_types, non_snake_case)] #[repr(C)] pub struct NET_BUFFER_LIST { pub(crate) Header: NBListHeader, pub(crate) Context: *mut c_void, pub(crate) ParentNetBufferList: *mut NET_BUFFER_LIST, pub(crate) NdisPoolHandle: NDIS_HANDLE, pub(crate) NdisReserved: [*mut c_void; 2], pub(crate) ProtocolReserved: [*mut c_void; 4], pub(crate) MiniportReserved: [*mut c_void; 2], pub(crate) Scratch: *mut c_void, pub(crate) SourceHandle: NDIS_HANDLE, pub(crate) NblFlags: u32, pub(crate) ChildRefCount: i32, pub(crate) Flags: u32, pub(crate) Status: NDIS_STATUS, pub(crate) NetBufferListInfo: [*mut c_void; 20], // Extra data at the end of the struct. The size of the array is not fixed. } #[allow(non_camel_case_types, non_snake_case)] #[repr(C)] pub union NBSize { pub DataLength: u32, pub stDataLength: u64, } /// This is internal struct should never be allocated from the driver. Use provided functions by microsoft. /// The NET_BUFFER structure specifies data that is transmitted or received over the network. #[allow(non_camel_case_types, non_snake_case)] #[repr(C)] pub struct NET_BUFFER { pub(crate) Next: *mut NET_BUFFER, pub(crate) CurrentMdl: *mut MDL, pub(crate) CurrentMdlOffset: u32, pub(crate) nbSize: NBSize, pub(crate) MdlChain: *mut MDL, pub(crate) DataOffset: u32, pub(crate) ChecksumBias: u16, pub(crate) Reserved: u16, pub(crate) NdisPoolHandle: NDIS_HANDLE, pub(crate) NdisReserved: [*mut c_void; 2], pub(crate) ProtocolReserved: [*mut c_void; 6], pub(crate) MiniportReserved: [*mut c_void; 4], pub(crate) DataPhysicalAddress: u64, pub(crate) SharedMemoryInfo: *mut c_void, } /// This data type is used as the generic handle type in NDIS function calls. #[allow(non_camel_case_types)] pub type NDIS_HANDLE = *mut c_void; /// This data type is used to indicate success and error states in numerous functions and object identifiers. #[allow(non_camel_case_types)] pub type NDIS_STATUS = i32; /// The NDIS_OBJECT_HEADER structure packages the object type, version, and size information that is required in many NDIS 6.0 structures. #[allow(non_camel_case_types, non_snake_case)] #[repr(C)] pub(crate) struct NDIS_OBJECT_HEADER { pub(crate) Type: u8, pub(crate) Revision: u8, pub(crate) Size: u16, } /// The NET_BUFFER_LIST_POOL_PARAMETERS structure defines the parameters for a pool of NET_BUFFER_LIST structures. #[allow(non_camel_case_types, non_snake_case)] #[repr(C)] pub(crate) struct NET_BUFFER_LIST_POOL_PARAMETERS { pub(crate) Header: NDIS_OBJECT_HEADER, pub(crate) ProtocolId: u8, pub(crate) fAllocateNetBuffer: bool, pub(crate) ContextSize: u16, pub(crate) PoolTag: u32, pub(crate) DataSize: u32, pub(crate) Flags: u32, } /// WdfObjectContextTypeInfo is a description of the device context. #[repr(C)] pub struct WdfObjectContextTypeInfo { size: u32, context_name: *const u8, context_size: usize, unique_type: *const WdfObjectContextTypeInfo, _evt_driver_get_unique_context_type: *const c_void, // Internal use } impl WdfObjectContextTypeInfo { pub const fn default(null_terminated_name: &'static str) -> Self { Self { size: core::mem::size_of::() as u32, context_name: null_terminated_name.as_ptr(), context_size: 0, unique_type: core::ptr::null(), _evt_driver_get_unique_context_type: core::ptr::null(), } } } /// WdfObjectAttributes contains attributes for the device context. #[repr(C)] pub struct WdfObjectAttributes { size: u32, evt_cleanup_callback: Option, evt_destroy_callback: Option, execution_level: WdfExecutionLevel, synchronization_scope: WdfSynchronizationScope, parent_object: HANDLE, context_size_override: usize, context_type_info: *const WdfObjectContextTypeInfo, } impl WdfObjectAttributes { pub fn new() -> Self { Self { size: core::mem::size_of::() as u32, evt_cleanup_callback: None, evt_destroy_callback: None, execution_level: WdfExecutionLevel::InheritFromParent, synchronization_scope: WdfSynchronizationScope::InheritFromParent, parent_object: core::ptr::null_mut(), context_size_override: 0, context_type_info: core::ptr::null(), } } pub fn add_context(&mut self, context_info: &'static mut WdfObjectContextTypeInfo) { context_info.context_size = core::mem::size_of::(); context_info.unique_type = context_info; self.context_size_override = 0; self.context_type_info = context_info.unique_type; } pub fn set_cleanup_fn(&mut self, callback: extern "system" fn(wdf_object: HANDLE)) { self.evt_cleanup_callback = Some(callback); } pub fn set_destroy_fn(&mut self, callback: extern "system" fn(wdf_object: HANDLE)) { self.evt_destroy_callback = Some(callback); } } // #[link(name = "Fwpkclnt", kind = "static")] // #[link(name = "Fwpuclnt", kind = "static")] // #[link(name = "WdfDriverEntry", kind = "static")] // #[link(name = "WdfLdr", kind = "static")] // #[link(name = "BufferOverflowK", kind = "static")] // #[link(name = "uuid", kind = "static")] // #[link(name = "wdmsec", kind = "static")] // #[link(name = "wmilib", kind = "static")] // #[link(name = "NtosKrnl", kind = "static")] // #[link(name = "ndis", kind = "static")] #[link(name = "c_helper", kind = "static")] extern "C" { /// The FwpsCalloutUnregisterById0 function unregisters a callout from the filter engine. pub(crate) fn FwpsCalloutUnregisterById0(id: u32) -> NTSTATUS; /// The FwpsCalloutRegister3 function registers a callout with the filter engine. pub(crate) fn FwpsCalloutRegister3( deviceObject: *mut c_void, callout: *const FWPS_CALLOUT3, calloutId: *mut u32, ) -> NTSTATUS; /// The FwpsPendOperation0 function is called by a callout to suspend packet processing pending completion of another operation. pub(crate) fn FwpsPendOperation0( completionHandle: HANDLE, completionContext: *mut HANDLE, ) -> NTSTATUS; /// The FwpsCompleteOperation0 function is called by a callout to resume packet processing that was suspended pending completion of another operation. pub(crate) fn FwpsCompleteOperation0(completionContext: HANDLE, netBufferList: *mut c_void); /// The FwpsAcquireClassifyHandle0 function generates a classification handle that is used to identify asynchronous classification operations and requests for writable layer data. pub(crate) fn FwpsAcquireClassifyHandle0( classify_context: *mut c_void, reserved: u32, // Must be zero. classify_handle: *mut u64, ) -> NTSTATUS; /// A callout driver calls FwpsReleaseClassifyHandle0 to release a classification handle that was previously acquired through a call to FwpsAcquireClassifyHandle0. pub(crate) fn FwpsReleaseClassifyHandle0(classify_handle: u64); /// A callout's classifyFn function calls FwpsPendClassify0 to pend the current classify request. After the request is pended, the callout driver must complete the processing of the classify request asynchronously by calling FwpsCompleteClassify0. pub(crate) fn FwpsPendClassify0( classify_handle: u64, filterId: u64, flags: u32, // Must be zero. classifyOut: *const ClassifyOut, ) -> NTSTATUS; /// A callout driver calls FwpsCompleteClassify0 to asynchronously complete a pended classify request. The callout driver's classifyFn function must have previously called FwpsPendClassify0 to pend the classify request. pub(crate) fn FwpsCompleteClassify0( classify_handle: u64, flags: u32, // Must be zero. classifyOut: *const ClassifyOut, ); /// The FwpsAcquireWritableLayerDataPointer0 function returns layer-specific data that can be inspected and changed. pub(crate) fn FwpsAcquireWritableLayerDataPointer0( classify_handle: u64, filter_id: u64, flags: u32, writable_layer_data: *mut c_void, classify_out: *mut ClassifyOut, ) -> NTSTATUS; /// The FwpsApplyModifiedLayerData0 function applies changes to layer-specific data made after a call to FwpsAcquireWritableLayerDataPointer0. pub(crate) fn FwpsApplyModifiedLayerData0( classifyHandle: u64, modifiedLayerData: *mut *mut c_void, flags: u32, ); /// pm_InitDriverObject initialize driver object. This function initializes requerd memory for the device context. pub(crate) fn pm_InitDriverObject( driver_object: *mut DRIVER_OBJECT, registry_path: *mut UNICODE_STRING, wdf_driver: *mut HANDLE, wdf_device: *mut HANDLE, win_driver_path: PCWSTR, dos_driver_path: PCWSTR, object_attributes: *mut WdfObjectAttributes, wdf_driver_unload: extern "C" fn(HANDLE), ) -> NTSTATUS; /// pm_WdfObjectGetTypedContextWerker 1to1 reference to the WdfObjectGetTypedContextWorker macro. The WdfObjectGetTypedContext macro returns a pointer to an object's context space. pub(crate) fn pm_WdfObjectGetTypedContextWorker( wdf_object: HANDLE, type_info: *const WdfObjectContextTypeInfo, ) -> *mut c_void; /// WdfObjectGetTypedContext 1to1 reference to WdfDeviceWdmGetDeviceObject. The WdfDeviceWdmGetDeviceObject method returns the Windows Driver Model (WDM) device object that is associated with a specified framework device object. pub(crate) fn pm_GetDeviceObject(wdf_device: HANDLE) -> *mut DEVICE_OBJECT; /// The FwpsInjectNetworkSendAsync0 function injects packet data into the send data path. pub(crate) fn FwpsInjectNetworkSendAsync0( injectionHandle: HANDLE, injectionContext: HANDLE, flags: u32, compartmentId: COMPARTMENT_ID, netBufferList: *mut NET_BUFFER_LIST, completionFn: FWPS_INJECT_COMPLETE0, completionContext: *mut c_void, ) -> NTSTATUS; /// The FwpsInjectNetworkReceiveAsync0 function injects packet data into the receive data path. pub(crate) fn FwpsInjectNetworkReceiveAsync0( injectionHandle: HANDLE, injectionContext: HANDLE, flags: u32, compartmentId: COMPARTMENT_ID, interfaceIndex: u32, subInterfaceIndex: u32, netBufferList: *mut NET_BUFFER_LIST, completionFn: FWPS_INJECT_COMPLETE0, completionContext: *mut c_void, ) -> NTSTATUS; /// The FwpsInjectTransportSendAsync1 function injects packet data from the transport, datagram data, or ICMP error layers into the send data path. This function differs from the previous version (FwpsInjectTransportSendAsync0) in that it takes an updated parameters structure as an argument. pub(crate) fn FwpsInjectTransportSendAsync1( injectionHandle: HANDLE, injectionContext: HANDLE, endpointHandle: u64, flags: u32, sendArgs: *mut FWPS_TRANSPORT_SEND_PARAMS1, addressFamily: ADDRESS_FAMILY, compartmentId: COMPARTMENT_ID, netBufferList: *mut NET_BUFFER_LIST, completionFn: FWPS_INJECT_COMPLETE0, completionContext: *mut c_void, ) -> NTSTATUS; /// The FwpsInjectTransportReceiveAsync0 function injects packet data from the transport, datagram data, or ICMP error layers into the receive data path. pub(crate) fn FwpsInjectTransportReceiveAsync0( injectionHandle: HANDLE, injectionContext: HANDLE, reserved: *const c_void, flags: u32, addressFamily: ADDRESS_FAMILY, compartmentId: COMPARTMENT_ID, interfaceIndex: u32, subInterfaceIndex: u32, netBufferList: *mut NET_BUFFER_LIST, completionFn: FWPS_INJECT_COMPLETE0, completionContext: *mut c_void, ) -> NTSTATUS; /// The FwpsInjectionHandleCreate0 function creates a handle that can be used by packet injection functions to inject packet or stream data into the TCP/IP network stack and by the FwpsQueryPacketInjectionState0 function to query the packet injection state. pub(crate) fn FwpsInjectionHandleCreate0( addressFamily: ADDRESS_FAMILY, flags: u32, injectionHandle: &mut HANDLE, ) -> NTSTATUS; /// The FwpsQueryPacketInjectionState0 function is called by a callout to query the injection state of packet data. pub(crate) fn FwpsQueryPacketInjectionState0( injectionHandle: HANDLE, netBufferList: *const NET_BUFFER_LIST, injectionContext: *mut HANDLE, ) -> FWPS_PACKET_INJECTION_STATE; /// The FwpsInjectionHandleDestroy0 function destroys an injection handle that was previously created by calling the FwpsInjectionHandleCreate0 function. pub(crate) fn FwpsInjectionHandleDestroy0(injectionHandle: HANDLE) -> NTSTATUS; /// The FwpsReferenceNetBufferList0 function increments the reference count for a NET_BUFFER_LIST structure. pub(crate) fn FwpsReferenceNetBufferList0( netBufferList: *mut NET_BUFFER_LIST, intendToModify: bool, ); /// The FwpsDereferenceNetBufferList0 function decrements the reference count for a NET_BUFFER_LIST structure that a callout driver had acquired earlier using the FwpsReferenceNetBufferList0 function. pub(crate) fn FwpsDereferenceNetBufferList0( netBufferList: *mut NET_BUFFER_LIST, dispatchLevel: bool, ); /// Call the NdisGetDataBuffer function to gain access to a contiguous block of data from a NET_BUFFER structure. pub(crate) fn NdisGetDataBuffer( NetBuffer: *const NET_BUFFER, BytesNeeded: u32, Storage: *mut u8, AlignMultiple: u32, AlignOffset: u32, ) -> *mut u8; /// Call the NdisAllocateCloneNetBufferList function to create a new clone NET_BUFFER_LIST structure. pub(crate) fn NdisAllocateCloneNetBufferList( OriginalNetBufferList: *mut NET_BUFFER_LIST, NetBufferListPoolHandle: NDIS_HANDLE, NetBufferPoolHandle: NDIS_HANDLE, AllocateCloneFlag: u32, ) -> *mut NET_BUFFER_LIST; /// Call the NdisFreeCloneNetBufferList function to free a NET_BUFFER_LIST structure and all associated NET_BUFFER structures and MDL chains that were previously allocated by calling the NdisAllocateCloneNetBufferList function. pub(crate) fn NdisFreeCloneNetBufferList( CloneNetBufferList: *mut NET_BUFFER_LIST, FreeCloneFlags: u32, ); /// The FwpsAllocateNetBufferAndNetBufferList0 function allocates a new NET_BUFFER_LIST structure. pub(crate) fn FwpsAllocateNetBufferAndNetBufferList0( poolHandle: NDIS_HANDLE, contextSize: u16, contextBackFill: u16, mdlChain: *mut MDL, dataOffset: u32, dataLength: u64, netBufferList: *mut *mut NET_BUFFER_LIST, ) -> NTSTATUS; /// The FwpsFreeNetBufferList0 function frees a NET_BUFFER_LIST structure that was previously allocated by a call to the FwpsAllocateNetBufferAndNetBufferList0 function. pub(crate) fn FwpsFreeNetBufferList0(netBufferList: *mut NET_BUFFER_LIST); /// Call the NdisAllocateNetBufferListPool function to allocate a pool of NET_BUFFER_LIST structures. pub(crate) fn NdisAllocateNetBufferListPool( NdisHandle: NDIS_HANDLE, Parameters: *const NET_BUFFER_LIST_POOL_PARAMETERS, ) -> NDIS_HANDLE; /// Call the NdisFreeNetBufferListPool function to free a NET_BUFFER_LIST structure pool. pub(crate) fn NdisFreeNetBufferListPool(PoolHandle: NDIS_HANDLE); /// Call the NdisRetreatNetBufferDataStart function to access more used data space in the MDL chain of a NET_BUFFER structure. pub(crate) fn NdisRetreatNetBufferDataStart( NetBuffer: *mut NET_BUFFER, DataOffsetDelta: u32, DataBackFill: u32, AllocateMdlHandler: *mut c_void, ) -> NDIS_STATUS; /// Call the NdisAdvanceNetBufferDataStart function to release the used data space that was added with the NdisRetreatNetBufferDataStart function. pub(crate) fn NdisAdvanceNetBufferDataStart( NetBuffer: *mut NET_BUFFER, DataOffsetDelta: u32, FreeMdl: bool, FreeMdlHandler: *mut c_void, ); /// The KeQuerySystemTime routine obtains the current system time. /// System time is a count of 100-nanosecond intervals since January 1, 1601. System time is typically updated approximately every ten milliseconds. This value is computed for the GMT time zone. pub(crate) fn pm_QuerySystemTime() -> u64; /// Returns the process identifier of the current process. /// This is safe to call from IRP_MJ_CREATE handlers, which always execute in the context of the initiating user-space process. pub(crate) fn PsGetCurrentProcessId() -> HANDLE; } ================================================ FILE: windows_kext/wdk/src/filter_engine/callout.rs ================================================ use super::{callout_data::CalloutData, ffi, layer::Layer}; use crate::ffi::FwpsCalloutClassifyFn; use alloc::{borrow::ToOwned, format, string::String}; use windows_sys::{Wdk::Foundation::DEVICE_OBJECT, Win32::Foundation::HANDLE}; pub enum FilterType { Resettable, NonResettable, } pub struct Callout { pub(crate) id: u32, pub(super) address: u64, pub(crate) name: String, pub(crate) description: String, pub(crate) guid: u128, pub(crate) layer: Layer, pub(crate) action: u32, pub(crate) registered: bool, pub(crate) filter_type: FilterType, pub(crate) filter_id: u64, pub(crate) callout_fn: fn(CalloutData), } impl Callout { pub fn new( name: &str, description: &str, guid: u128, layer: Layer, action: u32, filter_type: FilterType, callout_fn: fn(CalloutData), ) -> Self { Self { id: 0, address: 0, name: name.to_owned(), description: description.to_owned(), guid, layer, action, registered: false, filter_type, filter_id: 0, callout_fn, } } pub fn register_filter( &mut self, filter_engine_handle: HANDLE, sublayer_guid: u128, ) -> Result<(), String> { match ffi::register_filter( filter_engine_handle, sublayer_guid, &self.name, &self.description, self.guid, self.layer, self.action, self.address, // The address of the callout is passed as context. ) { Ok(id) => { self.filter_id = id; } Err(error) => { return Err(format!("failed to register filter: {}", error)); } }; return Ok(()); } pub(crate) fn register_callout( &mut self, filter_engine_handle: HANDLE, device_object: *mut DEVICE_OBJECT, callout_fn: FwpsCalloutClassifyFn, ) -> Result<(), String> { match ffi::register_callout( device_object, filter_engine_handle, &self.name, &self.description, self.guid, self.layer, callout_fn, ) { Ok(id) => { self.registered = true; self.id = id; } Err(code) => { return Err(format!("failed to register callout: {}", code)); } }; return Ok(()); } } ================================================ FILE: windows_kext/wdk/src/filter_engine/callout_data.rs ================================================ use crate::{ ffi::{FwpsCompleteOperation0, FwpsPendOperation0}, utils::check_ntstatus, }; use super::{ classify::ClassifyOut, layer::{Layer, Value, ValueType}, metadata::FwpsIncomingMetadataValues, packet::TransportPacketList, stream_data::StreamCalloutIoPacket, FilterEngine, }; use alloc::string::{String, ToString}; use core::{ffi::c_void, ptr::NonNull}; use windows_sys::Win32::{ Foundation::HANDLE, NetworkManagement::WindowsFilteringPlatform::FWP_CONDITION_FLAG_IS_REAUTHORIZE, Networking::WinSock::SCOPE_ID, }; pub enum ClassifyDefer { Initial(HANDLE, Option), Reauthorization(usize, Option), } impl ClassifyDefer { pub fn complete( self, filter_engine: &mut FilterEngine, ) -> Result, String> { unsafe { match self { ClassifyDefer::Initial(context, packet_list) => { FwpsCompleteOperation0(context, core::ptr::null_mut()); return Ok(packet_list); } ClassifyDefer::Reauthorization(_callout_id, packet_list) => { // There is no way to reset single filter. If another request for filter reset is trigger at the same time it will fail. // // Resetting all filters forces WFP to re-evaluate (reauthorize) all existing connections // using the updated verdict cache. // If STATUS_FWP_TXN_IN_PROGRESS is returned, another reset_all_filters() call is // already running concurrently, which will trigger the same WFP reauthorization. // It is safe to ignore this specific error and proceed with injecting the packet: // the verdict for this connection is already in the connection_cache, so the callout // will apply the correct verdict when the injected packet passes through. match filter_engine.reset_all_filters() { Ok(_) => {} Err(err) if err.contains("STATUS_FWP_TXN_IN_PROGRESS") => { // Another transaction is already in progress and will handle reauthorization. } Err(err) => return Err(err), } return Ok(packet_list); } } } } // pub fn add_net_buffer(&mut self, nbl: NetBufferList) { // if let Some(packet_list) = match self { // ClassifyDefer::Initial(_, packet_list) => packet_list, // ClassifyDefer::Reauthorization(_, packet_list) => packet_list, // } { // packet_list.net_buffer_list_queue.push(nbl); // } // } } pub struct CalloutData<'a> { pub layer: Layer, pub(crate) callout_id: usize, pub(crate) values: &'a [Value], pub(crate) metadata: *const FwpsIncomingMetadataValues, pub(crate) classify_out: *mut ClassifyOut, pub(crate) layer_data: *mut c_void, } impl<'a> CalloutData<'a> { pub fn get_value_type(&self, index: usize) -> ValueType { self.values[index].value_type } pub fn get_value_u8(&'a self, index: usize) -> u8 { unsafe { return self.values[index].value.uint8; }; } pub fn get_value_u16(&'a self, index: usize) -> u16 { unsafe { return self.values[index].value.uint16; }; } pub fn get_value_u32(&'a self, index: usize) -> u32 { unsafe { return self.values[index].value.uint32; }; } pub fn get_value_byte_array16(&'a self, index: usize) -> &'a [u8; 16] { unsafe { return self.values[index].value.byte_array16.as_ref().unwrap(); }; } pub fn get_process_id(&self) -> Option { unsafe { (*self.metadata).get_process_id() } } pub fn get_process_path(&self) -> Option { unsafe { return (*self.metadata).get_process_path(); } } pub fn get_transport_endpoint_handle(&self) -> Option { unsafe { return (*self.metadata).get_transport_endpoint_handle(); } } pub fn get_remote_scope_id(&self) -> Option { unsafe { return (*self.metadata).get_remote_scope_id(); } } pub fn get_control_data(&self) -> Option> { unsafe { return (*self.metadata).get_control_data(); } } pub fn get_layer_data(&self) -> *mut c_void { return self.layer_data; } pub fn get_stream_callout_packet(&self) -> Option<&mut StreamCalloutIoPacket> { match self.layer { Layer::StreamV4 | Layer::StreamV4Discard | Layer::StreamV6 | Layer::StreamV6Discard => unsafe { (self.layer_data as *mut StreamCalloutIoPacket).as_mut() }, _ => None, } } pub fn is_fragment_data(&self) -> bool { unsafe { (*self.metadata).is_fragment_data() } } pub fn pend_operation( &mut self, packet_list: Option, ) -> Result { unsafe { let mut completion_context: HANDLE = core::ptr::null_mut(); if let Some(completion_handle) = (*self.metadata).get_completion_handle() { let status = FwpsPendOperation0(completion_handle, &mut completion_context); check_ntstatus(status)?; return Ok(ClassifyDefer::Initial(completion_context, packet_list)); } Err("callout not supported".to_string()) } } pub fn pend_filter_rest(&mut self, packet_list: Option) -> ClassifyDefer { ClassifyDefer::Reauthorization(self.callout_id, packet_list) } pub fn action_permit(&mut self) { unsafe { (*self.classify_out).action_permit(); (*self.classify_out).clear_absorb_flag(); } } pub fn action_continue(&mut self) { unsafe { (*self.classify_out).action_continue(); (*self.classify_out).clear_absorb_flag(); } } // Block action and clear the write flag. // This will block the packet and prevent next filter in the chain to change the action. pub fn action_block_hard(&mut self) { unsafe { (*self.classify_out).action_block(); (*self.classify_out).clear_absorb_flag(); // Next filter in the chain will not change the action. (*self.classify_out).clear_write_flag(); } } pub fn action_none(&mut self) { unsafe { (*self.classify_out).set_none(); (*self.classify_out).clear_absorb_flag(); } } pub fn block_and_absorb(&mut self) { unsafe { (*self.classify_out).action_block(); (*self.classify_out).set_absorb(); } } pub fn clear_write_flag(&mut self) { unsafe { (*self.classify_out).clear_write_flag(); } } pub fn is_reauthorize(&self, flags_index: usize) -> bool { self.get_value_u32(flags_index) & FWP_CONDITION_FLAG_IS_REAUTHORIZE > 0 } pub fn get_callout_id(&self) -> usize { self.callout_id } } ================================================ FILE: windows_kext/wdk/src/filter_engine/classify.rs ================================================ #![allow(dead_code)] use windows_sys::Win32::NetworkManagement::WindowsFilteringPlatform::FWPS_CLASSIFY_OUT_FLAG_ABSORB; const FWP_ACTION_FLAG_TERMINATING: u32 = 0x00001000; const FWP_ACTION_FLAG_NON_TERMINATING: u32 = 0x00002000; const FWP_ACTION_FLAG_CALLOUT: u32 = 0x00004000; const FWP_ACTION_BLOCK: u32 = 0x00000001 | FWP_ACTION_FLAG_TERMINATING; const FWP_ACTION_PERMIT: u32 = 0x00000002 | FWP_ACTION_FLAG_TERMINATING; const FWP_ACTION_CALLOUT_TERMINATING: u32 = 0x00000003 | FWP_ACTION_FLAG_CALLOUT | FWP_ACTION_FLAG_TERMINATING; const FWP_ACTION_CALLOUT_INSPECTION: u32 = 0x00000004 | FWP_ACTION_FLAG_CALLOUT | FWP_ACTION_FLAG_NON_TERMINATING; const FWP_ACTION_CALLOUT_UNKNOWN: u32 = 0x00000005 | FWP_ACTION_FLAG_CALLOUT; const FWP_ACTION_CONTINUE: u32 = 0x00000006 | FWP_ACTION_FLAG_NON_TERMINATING; const FWP_ACTION_NONE: u32 = 0x00000007; const FWP_ACTION_NONE_NO_MATCH: u32 = 0x00000008; const FWP_CONDITION_FLAG_IS_LOOPBACK: u32 = 0x00000001; const FWP_CONDITION_FLAG_IS_IPSEC_SECURED: u32 = 0x00000002; const FWP_CONDITION_FLAG_IS_REAUTHORIZE: u32 = 0x00000004; const FWP_CONDITION_FLAG_IS_WILDCARD_BIND: u32 = 0x00000008; const FWP_CONDITION_FLAG_IS_RAW_ENDPOINT: u32 = 0x00000010; const FWP_CONDITION_FLAG_IS_FRAGMENT: u32 = 0x00000020; const FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP: u32 = 0x00000040; const FWP_CONDITION_FLAG_IS_IPSEC_NATT_RECLASSIFY: u32 = 0x00000080; const FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY: u32 = 0x00000100; const FWP_CONDITION_FLAG_IS_IMPLICIT_BIND: u32 = 0x00000200; const FWP_CONDITION_FLAG_IS_REASSEMBLED: u32 = 0x00000400; const FWP_CONDITION_FLAG_IS_NAME_APP_SPECIFIED: u32 = 0x00004000; const FWP_CONDITION_FLAG_IS_PROMISCUOUS: u32 = 0x00008000; const FWP_CONDITION_FLAG_IS_AUTH_FW: u32 = 0x00010000; const FWP_CONDITION_FLAG_IS_RECLASSIFY: u32 = 0x00020000; const FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU: u32 = 0x00040000; const FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU: u32 = 0x00080000; const FWP_CONDITION_FLAG_IS_CONNECTION_REDIRECTED: u32 = 0x00100000; const FWPS_RIGHT_ACTION_WRITE: u32 = 0x00000001; #[repr(C)] #[derive(Clone, Copy)] pub struct ClassifyOut { action_type: u32, _out_context: u64, // System use _filter_id: u64, // System use rights: u32, flags: u32, reserved: u32, } impl ClassifyOut { // Checks if write action flag is set. Indicates if the callout can change the action. pub fn can_set_action(&self) -> bool { self.rights & FWPS_RIGHT_ACTION_WRITE > 0 } /// Set block action. Write flag should be cleared, after this. pub fn action_block(&mut self) { self.action_type = FWP_ACTION_BLOCK; } /// Set permit action. pub fn action_permit(&mut self) { self.action_type = FWP_ACTION_PERMIT; } // Set continue action. pub fn action_continue(&mut self) { self.action_type = FWP_ACTION_CONTINUE; } // Set none action. pub fn set_none(&mut self) { self.action_type = FWP_ACTION_NONE; } // Set absorb flag. This will drop the packet. Used when the packets will be reinjected in the future. pub fn set_absorb(&mut self) { self.flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; } // Removes the absorb flag. pub fn clear_absorb_flag(&mut self) { self.flags &= !FWPS_CLASSIFY_OUT_FLAG_ABSORB; } // Clear the write flag permission. Next filter in the chain will not change the action. pub fn clear_write_flag(&mut self) { self.rights &= !FWPS_RIGHT_ACTION_WRITE; } } ================================================ FILE: windows_kext/wdk/src/filter_engine/connect_request.rs ================================================ use core::ffi::c_void; use windows_sys::Win32::{ Foundation::HANDLE, Networking::WinSock::{AF_INET, AF_INET6}, }; use crate::info; #[repr(C)] pub(crate) struct FwpsConnectRequest0 { pub(crate) local_address_and_port: [u8; 128], pub(crate) remote_address_and_port: [u8; 128], pub(crate) port_reservation_token: u64, pub(crate) local_redirect_target_pid: u32, pub(crate) previous_version: *const FwpsConnectRequest0, pub(crate) modifier_filter_id: u64, pub(crate) local_redirect_handle: HANDLE, pub(crate) local_redirect_context: *mut c_void, pub(crate) local_redirect_context_size: usize, } #[repr(C)] struct SocketAddressGeneric { family: u16, padding: [u8; 128 - 2], } #[repr(C)] struct SocketAddressIPv4 { family: u16, port: u16, addr: [u8; 4], zero: [u8; 8], padding: [u8; 128 - 2 - 2 - 4 - 8], } #[repr(C)] struct SocketAddressIPv6 { family: u16, port: u16, flowinfo: u16, addr: [u8; 16], scope_id: u32, padding: [u8; 128 - 2 - 2 - 2 - 16 - 4], } impl FwpsConnectRequest0 { pub(crate) fn set_remote(&mut self, ip: &[u8], port: u16) { info!("local: {:?}", self.local_address_and_port); info!("remote: {:?}", self.remote_address_and_port); unsafe { let generic_socket: &mut SocketAddressGeneric = core::mem::transmute(&mut self.remote_address_and_port); match generic_socket.family { AF_INET => { info!("Socket type AF_INET"); let socket_ipv4: &mut SocketAddressIPv4 = core::mem::transmute(generic_socket); for i in 0..4 { socket_ipv4.addr[i] = ip[i]; } socket_ipv4.port = u16::to_be(port); } AF_INET6 => { info!("Socket type AF_INET6"); let socket_ipv6: &mut SocketAddressIPv6 = core::mem::transmute(generic_socket); for i in 0..16 { socket_ipv6.addr[i] = ip[i]; } socket_ipv6.port = u16::to_be(port); } _ => { info!("Unsupported socket type: {}", generic_socket.family); } } } info!("after: {:?}", self.remote_address_and_port); } } ================================================ FILE: windows_kext/wdk/src/filter_engine/ffi.rs ================================================ use crate::alloc::borrow::ToOwned; use crate::ffi::FwpsCalloutClassifyFn; use crate::ffi::{FwpsCalloutRegister3, FwpsCalloutUnregisterById0, FWPS_CALLOUT3, FWPS_FILTER2}; use crate::utils::check_ntstatus; use alloc::string::String; use core::mem::MaybeUninit; use core::ptr; use widestring::U16CString; use windows_sys::Wdk::Foundation::DEVICE_OBJECT; use windows_sys::Win32::Foundation::{NTSTATUS, STATUS_SUCCESS}; use windows_sys::Win32::NetworkManagement::WindowsFilteringPlatform::{ FwpmCalloutAdd0, FwpmEngineClose0, FwpmEngineOpen0, FwpmFilterAdd0, FwpmFilterDeleteById0, FwpmSubLayerAdd0, FwpmSubLayerDeleteByKey0, FwpmTransactionAbort0, FwpmTransactionBegin0, FwpmTransactionCommit0, FWPM_CALLOUT0, FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT, FWPM_DISPLAY_DATA0, FWPM_FILTER0, FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT, FWPM_SESSION0, FWPM_SESSION_FLAG_DYNAMIC, FWPM_SUBLAYER0, FWP_UINT8, }; use windows_sys::Win32::System::Rpc::RPC_C_AUTHN_WINNT; use windows_sys::{ core::GUID, Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE}, }; use super::layer::Layer; pub(crate) fn create_filter_engine() -> Result { unsafe { let mut handle: HANDLE = INVALID_HANDLE_VALUE; let mut wdf_session: FWPM_SESSION0 = MaybeUninit::zeroed().assume_init(); wdf_session.flags = FWPM_SESSION_FLAG_DYNAMIC; let status = FwpmEngineOpen0( core::ptr::null(), RPC_C_AUTHN_WINNT, core::ptr::null_mut(), &wdf_session, &mut handle, ); check_ntstatus(status as i32)?; return Ok(handle); } } pub(crate) fn register_sublayer( filter_engine_handle: HANDLE, name: &str, description: &str, guid: u128, ) -> Result<(), String> { let Ok(name) = U16CString::from_str(name) else { return Err("invalid argument name".to_owned()); }; let Ok(description) = U16CString::from_str(description) else { return Err("invalid argument description".to_owned()); }; unsafe { let mut sublayer: FWPM_SUBLAYER0 = MaybeUninit::zeroed().assume_init(); sublayer.subLayerKey = GUID::from_u128(guid); sublayer.displayData.name = name.as_ptr() as _; sublayer.displayData.description = description.as_ptr() as _; sublayer.flags = 0; sublayer.weight = 0xFFFF; // Set to Max value. Weight compared to other sublayers. let status = FwpmSubLayerAdd0(filter_engine_handle, &sublayer, core::ptr::null_mut()); check_ntstatus(status as i32)?; return Ok(()); } } pub(crate) fn unregister_sublayer(filter_engine_handle: HANDLE, guid: u128) -> Result<(), String> { let guid = GUID::from_u128(guid); unsafe { let status = FwpmSubLayerDeleteByKey0(filter_engine_handle, ptr::addr_of!(guid)); check_ntstatus(status as i32)?; return Ok(()); } } unsafe extern "C" fn generic_notify( _notify_type: u32, _filter_key: *const GUID, _filter: *mut FWPS_FILTER2, ) -> NTSTATUS { return STATUS_SUCCESS; } unsafe extern "C" fn generic_delete_notify(_layer_id: u16, _callout_id: u32, _flow_context: u64) {} pub(crate) fn register_callout( device_object: *mut DEVICE_OBJECT, filter_engine_handle: HANDLE, name: &str, description: &str, guid: u128, layer: Layer, callout_fn: FwpsCalloutClassifyFn, ) -> Result { let s_callout = FWPS_CALLOUT3 { calloutKey: GUID::from_u128(guid), flags: 0, classifyFn: Some(callout_fn), notifyFn: Some(generic_notify), flowDeleteFn: Some(generic_delete_notify), }; unsafe { let mut callout_id: u32 = 0; let status = FwpsCalloutRegister3(device_object as _, &s_callout, &mut callout_id); check_ntstatus(status)?; callout_add(filter_engine_handle, guid, layer, name, description)?; return Ok(callout_id); } } fn callout_add( filter_engine_handle: HANDLE, guid: u128, layer: Layer, name: &str, description: &str, ) -> Result<(), String> { let Ok(name) = U16CString::from_str(name) else { return Err("invalid argument name".to_owned()); }; let Ok(description) = U16CString::from_str(description) else { return Err("invalid argument description".to_owned()); }; let display_data = FWPM_DISPLAY_DATA0 { name: name.as_ptr() as _, description: description.as_ptr() as _, }; unsafe { let mut callout: FWPM_CALLOUT0 = MaybeUninit::zeroed().assume_init(); callout.calloutKey = GUID::from_u128(guid); callout.displayData = display_data; callout.applicableLayer = layer.get_guid(); callout.flags = FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; let status = FwpmCalloutAdd0( filter_engine_handle, &callout, core::ptr::null_mut(), core::ptr::null_mut(), ); check_ntstatus(status as i32)?; }; return Ok(()); } pub(crate) fn unregister_callout(callout_id: u32) -> Result<(), String> { unsafe { let status = FwpsCalloutUnregisterById0(callout_id); check_ntstatus(status as i32)?; return Ok(()); } } pub(crate) fn register_filter( filter_engine_handle: HANDLE, sublayer_guid: u128, name: &str, description: &str, callout_guid: u128, layer: Layer, action: u32, context: u64, ) -> Result { let Ok(name) = U16CString::from_str(name) else { return Err("invalid argument name".to_owned()); }; let Ok(description) = U16CString::from_str(description) else { return Err("invalid argument description".to_owned()); }; let mut filter_id: u64 = 0; unsafe { let mut filter: FWPM_FILTER0 = MaybeUninit::zeroed().assume_init(); filter.displayData.name = name.as_ptr() as _; filter.displayData.description = description.as_ptr() as _; filter.action.r#type = action; // Says this filter's callout MUST make a block/permit decision. Also see doc excerpts below. filter.subLayerKey = GUID::from_u128(sublayer_guid); filter.weight.r#type = FWP_UINT8; filter.weight.Anonymous.uint8 = 15; // The weight of this filter within its sublayer filter.flags = FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT; filter.numFilterConditions = 0; // If you specify 0, this filter invokes its callout for all traffic in its layer filter.layerKey = layer.get_guid(); // This layer must match the layer that ExampleCallout is registered to filter.action.Anonymous.calloutKey = GUID::from_u128(callout_guid); filter.Anonymous.rawContext = context; let status = FwpmFilterAdd0( filter_engine_handle, &filter, core::ptr::null_mut(), &mut filter_id, ); check_ntstatus(status as i32)?; return Ok(filter_id); } } pub(crate) fn unregister_filter( filter_engine_handle: HANDLE, filter_id: u64, ) -> Result<(), String> { unsafe { let status = FwpmFilterDeleteById0(filter_engine_handle, filter_id); check_ntstatus(status as i32)?; return Ok(()); } } pub(crate) fn filter_engine_close(filter_engine_handle: HANDLE) -> Result<(), String> { unsafe { let status = FwpmEngineClose0(filter_engine_handle); check_ntstatus(status as i32)?; return Ok(()); } } pub(crate) fn filter_engine_transaction_begin( filter_engine_handle: HANDLE, flags: u32, ) -> Result<(), String> { unsafe { let status = FwpmTransactionBegin0(filter_engine_handle, flags); check_ntstatus(status as i32)?; return Ok(()); } } pub(crate) fn filter_engine_transaction_commit(filter_engine_handle: HANDLE) -> Result<(), String> { unsafe { let status = FwpmTransactionCommit0(filter_engine_handle); check_ntstatus(status as i32)?; return Ok(()); } } pub(crate) fn filter_engine_transaction_abort(filter_engine_handle: HANDLE) -> Result<(), String> { unsafe { let status = FwpmTransactionAbort0(filter_engine_handle); check_ntstatus(status as i32)?; return Ok(()); } } ================================================ FILE: windows_kext/wdk/src/filter_engine/layer.rs ================================================ use core::fmt::Debug; use windows_sys::{ core::GUID, Win32::NetworkManagement::WindowsFilteringPlatform::{ FWPM_LAYER_ALE_AUTH_CONNECT_V4, FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD, FWPM_LAYER_ALE_AUTH_CONNECT_V6, FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD, FWPM_LAYER_ALE_AUTH_LISTEN_V4, FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD, FWPM_LAYER_ALE_AUTH_LISTEN_V6, FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD, FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD, FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD, FWPM_LAYER_ALE_BIND_REDIRECT_V4, FWPM_LAYER_ALE_BIND_REDIRECT_V6, FWPM_LAYER_ALE_CONNECT_REDIRECT_V4, FWPM_LAYER_ALE_CONNECT_REDIRECT_V6, FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4, FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6, FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4, FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD, FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6, FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD, FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4, FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD, FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6, FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD, FWPM_LAYER_ALE_RESOURCE_RELEASE_V4, FWPM_LAYER_ALE_RESOURCE_RELEASE_V6, FWPM_LAYER_DATAGRAM_DATA_V4, FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD, FWPM_LAYER_DATAGRAM_DATA_V6, FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD, FWPM_LAYER_INBOUND_ICMP_ERROR_V4, FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD, FWPM_LAYER_INBOUND_ICMP_ERROR_V6, FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD, FWPM_LAYER_INBOUND_IPPACKET_V4, FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD, FWPM_LAYER_INBOUND_IPPACKET_V6, FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD, FWPM_LAYER_INBOUND_TRANSPORT_V4, FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD, FWPM_LAYER_INBOUND_TRANSPORT_V6, FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD, FWPM_LAYER_IPFORWARD_V4, FWPM_LAYER_IPFORWARD_V4_DISCARD, FWPM_LAYER_IPFORWARD_V6, FWPM_LAYER_IPFORWARD_V6_DISCARD, FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4, FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD, FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6, FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD, FWPM_LAYER_OUTBOUND_IPPACKET_V4, FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD, FWPM_LAYER_OUTBOUND_IPPACKET_V6, FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD, FWPM_LAYER_OUTBOUND_TRANSPORT_V4, FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD, FWPM_LAYER_OUTBOUND_TRANSPORT_V6, FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD, FWPM_LAYER_STREAM_V4, FWPM_LAYER_STREAM_V4_DISCARD, FWPM_LAYER_STREAM_V6, FWPM_LAYER_STREAM_V6_DISCARD, }, }; #[repr(C)] pub(crate) struct Value { pub(crate) value_type: ValueType, pub(crate) value: ValueData, } #[repr(C)] pub(crate) struct IncomingValues { pub(crate) layer_id: u16, pub(crate) value_count: u32, pub(crate) incoming_value_array: *const Value, } #[repr(C)] pub(crate) union ValueData { pub(crate) uint8: u8, pub(crate) uint16: u16, pub(crate) uint32: u32, pub(crate) uint64: *const u64, pub(crate) byte_array16: *const [u8; 16], // TODO: add the rest of possible values. } #[repr(C)] #[derive(Copy, Clone, Debug)] pub enum ValueType { FwpEmpty = 0, FwpUint8 = 1, FwpUint16 = 2, FwpUint32 = 3, FwpUint64 = 4, FwpInt8 = 5, FwpInt16 = 6, FwpInt32 = 7, FwpInt64 = 8, FwpFloat = 9, FwpDouble = 10, FwpByteArray16Type = 11, FwpByteBlobType = 12, FwpSid = 13, FwpSecurityDescriptorType = 14, FwpTokenInformationType = 15, FwpTokenAccessInformationType = 16, FwpUnicodeStringType = 17, FwpByteArray6Type = 18, FwpSingleDataTypeMax = 0xff, FwpV4AddrMask = 0xff + 1, FwpV6AddrMask = 0xff + 2, FwpRangeType = 0xff + 3, FwpDataTypeMax = 0xff + 4, } #[derive(Copy, Clone, Debug)] pub enum Layer { InboundIppacketV4, InboundIppacketV4Discard, InboundIppacketV6, InboundIppacketV6Discard, OutboundIppacketV4, OutboundIppacketV4Discard, OutboundIppacketV6, OutboundIppacketV6Discard, IpforwardV4, IpforwardV4Discard, IpforwardV6, IpforwardV6Discard, InboundTransportV4, InboundTransportV4Discard, InboundTransportV6, InboundTransportV6Discard, OutboundTransportV4, OutboundTransportV4Discard, OutboundTransportV6, OutboundTransportV6Discard, StreamV4, StreamV4Discard, StreamV6, StreamV6Discard, DatagramDataV4, DatagramDataV4Discard, DatagramDataV6, DatagramDataV6Discard, InboundIcmpErrorV4, InboundIcmpErrorV4Discard, InboundIcmpErrorV6, InboundIcmpErrorV6Discard, OutboundIcmpErrorV4, OutboundIcmpErrorV4Discard, OutboundIcmpErrorV6, OutboundIcmpErrorV6Discard, AleResourceAssignmentV4, AleResourceAssignmentV4Discard, AleResourceAssignmentV6, AleResourceAssignmentV6Discard, AleAuthListenV4, AleAuthListenV4Discard, AleAuthListenV6, AleAuthListenV6Discard, AleAuthRecvAcceptV4, AleAuthRecvAcceptV4Discard, AleAuthRecvAcceptV6, AleAuthRecvAcceptV6Discard, AleAuthConnectV4, AleAuthConnectV4Discard, AleAuthConnectV6, AleAuthConnectV6Discard, AleFlowEstablishedV4, AleFlowEstablishedV4Discard, AleFlowEstablishedV6, AleFlowEstablishedV6Discard, AleConnectRedirectV4, AleConnectRedirectV6, AleBindRedirectV4, AleBindRedirectV6, AleResourceReleaseV4, AleResourceReleaseV6, AleEndpointClosureV4, AleEndpointClosureV6, } impl Layer { pub fn get_guid(&self) -> GUID { match self { Layer::InboundIppacketV4 => FWPM_LAYER_INBOUND_IPPACKET_V4, Layer::InboundIppacketV4Discard => FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD, Layer::InboundIppacketV6 => FWPM_LAYER_INBOUND_IPPACKET_V6, Layer::InboundIppacketV6Discard => FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD, Layer::OutboundIppacketV4 => FWPM_LAYER_OUTBOUND_IPPACKET_V4, Layer::OutboundIppacketV4Discard => FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD, Layer::OutboundIppacketV6 => FWPM_LAYER_OUTBOUND_IPPACKET_V6, Layer::OutboundIppacketV6Discard => FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD, Layer::IpforwardV4 => FWPM_LAYER_IPFORWARD_V4, Layer::IpforwardV4Discard => FWPM_LAYER_IPFORWARD_V4_DISCARD, Layer::IpforwardV6 => FWPM_LAYER_IPFORWARD_V6, Layer::IpforwardV6Discard => FWPM_LAYER_IPFORWARD_V6_DISCARD, Layer::InboundTransportV4 => FWPM_LAYER_INBOUND_TRANSPORT_V4, Layer::InboundTransportV4Discard => FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD, Layer::InboundTransportV6 => FWPM_LAYER_INBOUND_TRANSPORT_V6, Layer::InboundTransportV6Discard => FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD, Layer::OutboundTransportV4 => FWPM_LAYER_OUTBOUND_TRANSPORT_V4, Layer::OutboundTransportV4Discard => FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD, Layer::OutboundTransportV6 => FWPM_LAYER_OUTBOUND_TRANSPORT_V6, Layer::OutboundTransportV6Discard => FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD, Layer::StreamV4 => FWPM_LAYER_STREAM_V4, Layer::StreamV4Discard => FWPM_LAYER_STREAM_V4_DISCARD, Layer::StreamV6 => FWPM_LAYER_STREAM_V6, Layer::StreamV6Discard => FWPM_LAYER_STREAM_V6_DISCARD, Layer::DatagramDataV4 => FWPM_LAYER_DATAGRAM_DATA_V4, Layer::DatagramDataV4Discard => FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD, Layer::DatagramDataV6 => FWPM_LAYER_DATAGRAM_DATA_V6, Layer::DatagramDataV6Discard => FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD, Layer::InboundIcmpErrorV4 => FWPM_LAYER_INBOUND_ICMP_ERROR_V4, Layer::InboundIcmpErrorV4Discard => FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD, Layer::InboundIcmpErrorV6 => FWPM_LAYER_INBOUND_ICMP_ERROR_V6, Layer::InboundIcmpErrorV6Discard => FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD, Layer::OutboundIcmpErrorV4 => FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4, Layer::OutboundIcmpErrorV4Discard => FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD, Layer::OutboundIcmpErrorV6 => FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6, Layer::OutboundIcmpErrorV6Discard => FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD, Layer::AleResourceAssignmentV4 => FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4, Layer::AleResourceAssignmentV4Discard => FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD, Layer::AleResourceAssignmentV6 => FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6, Layer::AleResourceAssignmentV6Discard => FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD, Layer::AleAuthListenV4 => FWPM_LAYER_ALE_AUTH_LISTEN_V4, Layer::AleAuthListenV4Discard => FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD, Layer::AleAuthListenV6 => FWPM_LAYER_ALE_AUTH_LISTEN_V6, Layer::AleAuthListenV6Discard => FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD, Layer::AleAuthRecvAcceptV4 => FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, Layer::AleAuthRecvAcceptV4Discard => FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD, Layer::AleAuthRecvAcceptV6 => FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, Layer::AleAuthRecvAcceptV6Discard => FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD, Layer::AleAuthConnectV4 => FWPM_LAYER_ALE_AUTH_CONNECT_V4, Layer::AleAuthConnectV4Discard => FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD, Layer::AleAuthConnectV6 => FWPM_LAYER_ALE_AUTH_CONNECT_V6, Layer::AleAuthConnectV6Discard => FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD, Layer::AleFlowEstablishedV4 => FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4, Layer::AleFlowEstablishedV4Discard => FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD, Layer::AleFlowEstablishedV6 => FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6, Layer::AleFlowEstablishedV6Discard => FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD, Layer::AleConnectRedirectV4 => FWPM_LAYER_ALE_CONNECT_REDIRECT_V4, Layer::AleConnectRedirectV6 => FWPM_LAYER_ALE_CONNECT_REDIRECT_V6, Layer::AleBindRedirectV4 => FWPM_LAYER_ALE_BIND_REDIRECT_V4, Layer::AleBindRedirectV6 => FWPM_LAYER_ALE_BIND_REDIRECT_V6, Layer::AleResourceReleaseV4 => FWPM_LAYER_ALE_RESOURCE_RELEASE_V4, Layer::AleResourceReleaseV6 => FWPM_LAYER_ALE_RESOURCE_RELEASE_V6, Layer::AleEndpointClosureV4 => FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4, Layer::AleEndpointClosureV6 => FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6, } } } #[repr(usize)] pub enum FieldsInboundIppacketV4 { IpLocalAddress, IpRemoteAddress, IpLocalAddressType, IpLocalInterface, InterfaceIndex, SubInterfaceIndex, Flags, InterfaceType, TunnelType, CompartmentId, Max, } #[repr(usize)] pub enum FieldsInboundIppacketV6 { IpLocalAddress, IpRemoteAddress, IpLocalAddressType, IpLocalInterface, InterfaceIndex, SubInterfaceIndex, Flags, InterfaceType, TunnelType, CompartmentId, Max, } #[repr(usize)] pub enum FieldsOutboundIppacketV4 { IpLocalAddress, IpLocalAddressType, IpRemoteAddress, IpLocalInterface, InterfaceIndex, SubInterfaceIndex, Flags, InterfaceType, TunnelType, CompartmentId, Max, } #[repr(usize)] pub enum FieldsOutboundIppacketV6 { IpLocalAddress, IpLocalAddressType, IpRemoteAddress, IpLocalInterface, InterfaceIndex, SubInterfaceIndex, Flags, InterfaceType, TunnelType, CompartmentId, Max, } #[repr(usize)] pub enum FieldsIpforwardV4 { IpSourceAddress, IpDestinationAddress, IpDestinationAddressType, IpLocalInterface, IpForwardInterface, SourceInterfaceIndex, SourceSubInterfaceIndex, DestinationInterfaceIndex, DestinationSubInterfaceIndex, Flags, IpPhysicalArrivalInterface, ArrivalInterfaceProfileId, IpPhysicalNexthopInterface, NexthopInterfaceProfileId, CompartmentId, Max, } #[repr(usize)] pub enum FieldsIpforwardV6 { IpSourceAddress, IpDestinationAddress, IpDestinationAddressType, IpLocalInterface, IpForwardInterface, SourceInterfaceIndex, SourceSubInterfaceIndex, DestinationInterfaceIndex, DestinationSubInterfaceIndex, Flags, IpPhysicalArrivalInterface, ArrivalInterfaceProfileId, IpPhysicalNexthopInterface, NexthopInterfaceProfileId, CompartmentId, Max, } #[repr(usize)] pub enum FieldsInboundTransportV4 { IpProtocol, IpLocalAddress, IpRemoteAddress, IpLocalAddressType, IpLocalPort, IpRemotePort, IpLocalInterface, InterfaceIndex, SubInterfaceIndex, Flags, InterfaceType, TunnelType, ProfileId, IpsecSecurityRealmId, CompartmentId, Max, } #[repr(usize)] pub enum FieldsInboundTransportFas { FieldInboundTransportFastMax, } #[repr(usize)] pub enum FieldsOutboundTransportFas { FieldOutboundTransportFastMax, } #[repr(usize)] pub enum FieldsInboundTransportV6 { IpProtocol, IpLocalAddress, IpRemoteAddress, IpLocalAddressType, IpLocalPort, IpRemotePort, IpLocalInterface, InterfaceIndex, SubInterfaceIndex, Flags, InterfaceType, TunnelType, ProfileId, IpsecSecurityRealmId, CompartmentId, Max, } #[repr(usize)] pub enum FieldsOutboundTransportV4 { IpProtocol, IpLocalAddress, IpLocalAddressType, IpRemoteAddress, IpLocalPort, IpRemotePort, IpLocalInterface, InterfaceIndex, SubInterfaceIndex, IpDestinationAddressType, Flags, InterfaceType, TunnelType, ProfileId, IpsecSecurityRealmId, CompartmentId, Max, } #[repr(usize)] pub enum FieldsOutboundTransportV6 { IpProtocol, IpLocalAddress, IpLocalAddressType, IpRemoteAddress, IpLocalPort, IpRemotePort, IpLocalInterface, InterfaceIndex, SubInterfaceIndex, IpDestinationAddressType, Flags, InterfaceType, TunnelType, ProfileId, IpsecSecurityRealmId, CompartmentId, Max, } #[repr(usize)] pub enum FieldsStreamV4 { IpLocalAddress, IpLocalAddressType, IpRemoteAddress, IpLocalPort, IpRemotePort, Direction, Flags, CompartmentId, Max, } #[repr(usize)] pub enum FieldsStreamV6 { IpLocalAddress, IpLocalAddressType, IpRemoteAddress, IpLocalPort, IpRemotePort, Direction, Flags, CompartmentId, Max, } #[repr(usize)] pub enum FieldsDatagramDataV4 { IpProtocol, IpLocalAddress, IpRemoteAddress, IpLocalAddressType, IpLocalPort, IpRemotePort, IpLocalInterface, InterfaceIndex, SubInterfaceIndex, Direction, Flags, InterfaceType, TunnelType, CompartmentId, Max, } #[repr(usize)] pub enum FieldsDatagramDataV6 { IpProtocol, IpLocalAddress, IpRemoteAddress, IpLocalAddressType, IpLocalPort, IpRemotePort, IpLocalInterface, InterfaceIndex, SubInterfaceIndex, Direction, Flags, InterfaceType, TunnelType, CompartmentId, Max, } #[repr(usize)] pub enum FieldsStreamPacketV4 { IpLocalAddress, IpRemoteAddress, IpLocalPort, IpRemotePort, IpLocalInterface, InterfaceIndex, SubInterfaceIndex, Direction, Flags, InterfaceType, TunnelType, CompartmentId, Max, } #[repr(usize)] pub enum FieldsStreamPacketV6 { IpLocalAddress, IpRemoteAddress, IpLocalPort, IpRemotePort, IpLocalInterface, InterfaceIndex, SubInterfaceIndex, Direction, Flags, InterfaceType, TunnelType, CompartmentId, Max, } #[repr(usize)] pub enum FieldsInboundIcmpErrorV4 { EmbeddedProtocol, IpLocalAddress, IpRemoteAddress, EmbeddedRemoteAddress, EmbeddedLocalAddressType, EmbeddedLocalPort, EmbeddedRemotePort, IpLocalInterface, IcmpType, IcmpCode, InterfaceIndex, // of local/delivery interface SubInterfaceIndex, // of arrival interface InterfaceType, // of local/delivery interface TunnelType, // of local/delivery interface IpArrivalInterface, ArrivalInterfaceIndex, ArrivalInterfaceType, ArrivalTunnelType, Flags, ArrivalInterfaceProfileId, InterfaceQuarantineEpoch, CompartmentId, Max, } #[repr(usize)] pub enum FieldsInboundIcmpErrorV6 { EmbeddedProtocol, IpLocalAddress, IpRemoteAddress, EmbeddedRemoteAddress, EmbeddedLocalAddressType, EmbeddedLocalPort, EmbeddedRemotePort, IpLocalInterface, IcmpType, IcmpCode, InterfaceIndex, // of local/delivery interface SubInterfaceIndex, // of arrival interface InterfaceType, // of local/delivery interface TunnelType, // of local/delivery interface IpArrivalInterface, ArrivalInterfaceIndex, ArrivalInterfaceType, ArrivalTunnelType, Flags, ArrivalInterfaceProfileId, InterfaceQuarantineEpoch, CompartmentId, Max, } #[repr(usize)] pub enum FieldsOutboundIcmpErrorV4 { IpLocalAddress, IpRemoteAddress, IpLocalAddressType, IpLocalInterface, IcmpType, IcmpCode, InterfaceIndex, SubInterfaceIndex, InterfaceType, TunnelType, Flags, NexthopInterfaceProfileId, InterfaceQuarantineEpoch, CompartmentId, Max, } #[repr(usize)] pub enum FieldsOutboundIcmpErrorV6 { IpLocalAddress, IpRemoteAddress, IpLocalAddressType, IpLocalInterface, IpLocalPort, IpRemotePort, InterfaceIndex, SubInterfaceIndex, InterfaceType, TunnelType, Flags, NexthopInterfaceProfileId, InterfaceQuarantineEpoch, CompartmentId, Max, } #[repr(usize)] pub enum FieldsAleResourceAssignmentV4 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, AlePromiscuousMode, IpLocalInterface, Flags, InterfaceType, TunnelType, LocalInterfaceProfileId, SioFirewallSocketProperty, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, // // These reserved fields MUST be in this order. DO NOT change their order // Reserved0, Reserved1, Max, } #[repr(usize)] pub enum FieldsAleResourceAssignmentV6 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, AlePromiscuousMode, IpLocalInterface, Flags, InterfaceType, TunnelType, LocalInterfaceProfileId, SioFirewallSocketProperty, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, // // These reserved fields MUST be in this order. DO NOT change their order // Reserved0, Reserved1, Max, } #[repr(usize)] pub enum FieldsAleResourceReleaseV4 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpLocalInterface, Flags, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, Max, } #[repr(usize)] pub enum FieldsAleResourceReleaseV6 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpLocalInterface, Flags, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, Max, } #[repr(usize)] pub enum FieldsAleEndpointClosureV4 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpRemoteAddress, IpRemotePort, IpLocalInterface, Flags, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, Max, } #[repr(usize)] pub enum FieldsAleEndpointClosureV6 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpRemoteAddress, IpRemotePort, IpLocalInterface, Flags, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, Max, } #[repr(usize)] pub enum FieldsAleAuthListenV4 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpLocalInterface, Flags, InterfaceType, TunnelType, LocalInterfaceProfileId, SioFirewallSocketProperty, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, Max, } #[repr(usize)] pub enum FieldsAleAuthListenV6 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpLocalInterface, Flags, InterfaceType, TunnelType, LocalInterfaceProfileId, SioFirewallSocketProperty, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, Max, } #[repr(usize)] pub enum FieldsAleAuthRecvAcceptV4 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpRemoteAddress, IpRemotePort, AleRemoteUserId, AleRemoteMachineId, IpLocalInterface, Flags, SioFirewallSystemPort, NapContext, InterfaceType, // of local/delivery interface TunnelType, // of local/delivery interface InterfaceIndex, // of local/delivery interface SubInterfaceIndex, // of arrival interface IpArrivalInterface, ArrivalInterfaceType, ArrivalTunnelType, ArrivalInterfaceIndex, NexthopSubInterfaceIndex, IpNexthopInterface, NexthopInterfaceType, NexthopTunnelType, NexthopInterfaceIndex, OriginalProfileId, CurrentProfileId, ReauthorizeReason, OriginalIcmpType, InterfaceQuarantineEpoch, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, // // These reserved fields MUST be in this order. DO NOT change their order // Reserved0, Reserved1, Reserved2, Reserved3, Max, } #[repr(usize)] pub enum FieldsAleAuthRecvAcceptV6 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpRemoteAddress, IpRemotePort, AleRemoteUserId, AleRemoteMachineId, IpLocalInterface, Flags, SioFirewallSystemPort, NapContext, InterfaceType, // of local/delivery interface TunnelType, // of local/delivery interface InterfaceIndex, // of local/delivery interface SubInterfaceIndex, // of arrival interface IpArrivalInterface, ArrivalInterfaceType, ArrivalTunnelType, ArrivalInterfaceIndex, NexthopSubInterfaceIndex, IpNexthopInterface, NexthopInterfaceType, NexthopTunnelType, NexthopInterfaceIndex, OriginalProfileId, CurrentProfileId, ReauthorizeReason, OriginalIcmpType, InterfaceQuarantineEpoch, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, // // These reserved fields MUST be in this order. DO NOT change their order // Reserved0, Reserved1, Reserved2, Reserved3, Max, } #[repr(usize)] pub enum FieldsAleBindRedirectV4 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, Flags, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, Max, } #[repr(usize)] pub enum FieldsAleBindRedirectV6 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, Flags, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, Max, } #[repr(usize)] pub enum FieldsAleConnectRedirectV4 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpRemoteAddress, IpDestinationAddressType, IpRemotePort, Flags, AleOriginalAppId, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, Max, } #[repr(usize)] pub enum FieldsAleConnectRedirectV6 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpRemoteAddress, IpDestinationAddressType, IpRemotePort, Flags, AleOriginalAppId, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, Max, } #[repr(usize)] pub enum FieldsAleAuthConnectV4 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpRemoteAddress, IpRemotePort, AleRemoteUserId, AleRemoteMachineId, IpDestinationAddressType, IpLocalInterface, Flags, InterfaceType, TunnelType, InterfaceIndex, SubInterfaceIndex, IpArrivalInterface, ArrivalInterfaceType, ArrivalTunnelType, ArrivalInterfaceIndex, NexthopSubInterfaceIndex, IpNexthopInterface, NexthopInterfaceType, NexthopTunnelType, NexthopInterfaceIndex, OriginalProfileId, CurrentProfileId, ReauthorizeReason, PeerName, OriginalIcmpType, InterfaceQuarantineEpoch, AleOriginalAppId, AlePackageId, AleSecurityAttributeFqbnValue, AleEffectiveName, CompartmentId, // // These reserved fields MUST be in this order. DO NOT change their order // Reserved0, Reserved1, Reserved2, Reserved3, Max, } #[repr(usize)] pub enum FieldsAleAuthConnectV6 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpRemoteAddress, IpRemotePort, AleRemoteUserId, AleRemoteMachineId, IpDestinationAddressType, IpLocalInterface, Flags, InterfaceType, TunnelType, InterfaceIndex, SubInterfaceIndex, IpArrivalInterface, ArrivalInterfaceType, ArrivalTunnelType, ArrivalInterfaceIndex, NexthopSubInterfaceIndex, IpNexthopInterface, NexthopInterfaceType, NexthopTunnelType, NexthopInterfaceIndex, OriginalProfileId, CurrentProfileId, ReauthorizeReason, PeerName, OriginalIcmpType, InterfaceQuarantineEpoch, AleOriginalAppId, AlePackageId, AleSecurityAttributeFqbnValue, AleEffectiveName, CompartmentId, // // These reserved fields MUST be in this order. DO NOT change their order // Reserved0, Reserved1, Reserved2, Reserved3, Max, } #[repr(usize)] pub enum FieldsAleFlowEstablishedV4 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpRemoteAddress, IpRemotePort, AleRemoteUserId, AleRemoteMachineId, IpDestinationAddressType, IpLocalInterface, Direction, InterfaceType, TunnelType, Flags, AleOriginalAppId, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, // // These reserved fields MUST be in this order. DO NOT change their order // Reserved0, Reserved1, Reserved2, Reserved3, Max, } #[repr(usize)] pub enum FieldsAleFlowEstablishedV6 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpRemoteAddress, IpRemotePort, AleRemoteUserId, AleRemoteMachineId, IpDestinationAddressType, IpLocalInterface, Direction, InterfaceType, TunnelType, Flags, AleOriginalAppId, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, // // These reserved fields MUST be in this order. DO NOT change their order // Reserved0, Reserved1, Reserved2, Reserved3, Max, } #[repr(usize)] pub enum FieldsNameResolutionCacheV4 { AleUserId, AleAppId, IpRemoteAddress, PeerName, CompartmentId, Max, } #[repr(usize)] pub enum FieldsNameResolutionCacheV6 { AleUserId, AleAppId, IpRemoteAddress, PeerName, CompartmentId, Max, } #[repr(usize)] pub enum FieldsInboundMacFrameEthernet { InterfaceMacAddress, MacLocalAddress, MacRemoteAddress, MacLocalAddressType, MacRemoteAddressType, EtherType, VlanId, Interface, InterfaceIndex, NdisPort, L2Flags, CompartmentId, Max, } #[repr(usize)] pub enum FieldsOutboundMacFrameEthernet { InterfaceMacAddress, MacLocalAddress, MacRemoteAddress, MacLocalAddressType, MacRemoteAddressType, EtherType, VlanId, Interface, InterfaceIndex, NdisPort, L2Flags, CompartmentId, Max, } #[repr(usize)] pub enum FieldsInboundMacFrameNative { NdisMediaType, NdisPhysicalMediaType, Interface, InterfaceType, InterfaceIndex, NdisPort, L2Flags, CompartmentId, Max, } #[repr(usize)] pub enum FieldsInboundMacFrameNativeFast { FastMax, } #[repr(usize)] pub enum FieldsOutboundMacFrameNative { NdisMediaType, NdisPhysicalMediaType, Interface, InterfaceType, InterfaceIndex, NdisPort, L2Flags, CompartmentId, Max, } #[repr(usize)] pub enum FieldsOutboundMacFrameNativeFast { Max, } #[repr(usize)] pub enum FieldsIngressVswitchEthernet { MacSourceAddress, MacSourceAddressType, MacDestinationAddress, MacDestinationAddressType, EtherType, VlanId, VswitchTenantNetworkId, VswitchId, VswitchNetworkType, VswitchSourceInterfaceId, VswitchSourceInterfaceType, VswitchSourceVmId, L2Flags, CompartmentId, Max, } #[repr(usize)] pub enum FieldsEgressVswitchEthernet { MacSourceAddress, MacSourceAddressType, MacDestinationAddress, MacDestinationAddressType, EtherType, VlanId, VswitchTenantNetworkId, VswitchId, VswitchNetworkType, VswitchSourceInterfaceId, VswitchSourceInterfaceType, VswitchSourceVmId, VswitchDestinationInterfaceId, VswitchDestinationInterfaceType, VswitchDestinationVmId, L2Flags, CompartmentId, Max, } #[repr(usize)] pub enum FieldsIngressVswitchTransportV4 { IpSourceAddress, IpDestinationAddress, IpProtocol, IpSourcePort, IpDestinationPort, VlanId, VswitchTenantNetworkId, VswitchId, VswitchNetworkType, VswitchSourceInterfaceId, VswitchSourceInterfaceType, VswitchSourceVmId, L2Flags, CompartmentId, Max, } #[repr(usize)] pub enum FieldsIngressVswitchTransportV6 { IpSourceAddress, IpDestinationAddress, IpProtocol, IpSourcePort, IpDestinationPort, VlanId, VswitchTenantNetworkId, VswitchId, VswitchNetworkType, VswitchSourceInterfaceId, VswitchSourceInterfaceType, VswitchSourceVmId, L2Flags, CompartmentId, Max, } #[repr(usize)] pub enum FieldsEgressVswitchTransportV4 { IpSourceAddress, IpDestinationAddress, IpProtocol, IpSourcePort, IpDestinationPort, VlanId, VswitchTenantNetworkId, VswitchId, VswitchNetworkType, VswitchSourceInterfaceId, VswitchSourceInterfaceType, VswitchSourceVmId, VswitchDestinationInterfaceId, VswitchDestinationInterfaceType, VswitchDestinationVmId, L2Flags, CompartmentId, Max, } #[repr(usize)] pub enum FieldsEgressVswitchTransportV6 { IpSourceAddress, IpDestinationAddress, IpProtocol, IpSourcePort, IpDestinationPort, VlanId, VswitchTenantNetworkId, VswitchId, VswitchNetworkType, VswitchSourceInterfaceId, VswitchSourceInterfaceType, VswitchSourceVmId, VswitchDestinationInterfaceId, VswitchDestinationInterfaceType, VswitchDestinationVmId, L2Flags, CompartmentId, Max, } #[repr(usize)] pub enum FieldsIpsecKmDemuxV4 { IpLocalAddress, IpRemoteAddress, QmMode, IpLocalInterface, CurrentProfileId, IpsecSecurityRealmId, Max, } #[repr(usize)] pub enum FieldsIpsecKmDemuxV6 { IpLocalAddress, IpRemoteAddress, QmMode, IpLocalInterface, CurrentProfileId, IpsecSecurityRealmId, Max, } #[repr(usize)] pub enum FieldsIpsecV4 { IpProtocol, IpLocalAddress, IpRemoteAddress, IpLocalPort, IpRemotePort, IpLocalInterface, ProfileId, IpsecSecurityRealmId, Max, } #[repr(usize)] pub enum FieldsIpsecV6 { IpProtocol, IpLocalAddress, IpRemoteAddress, IpLocalPort, IpRemotePort, IpLocalInterface, ProfileId, IpsecSecurityRealmId, Max, } #[repr(usize)] pub enum FieldsIkeextV4 { IpLocalAddress, IpRemoteAddress, IpLocalInterface, ProfileId, IpsecSecurityRealmId, Max, } #[repr(usize)] pub enum FieldsIkeextV6 { IpLocalAddress, IpRemoteAddress, IpLocalInterface, ProfileId, IpsecSecurityRealmId, Max, } #[repr(usize)] pub enum FieldsRpcUm { RemoteUserToken, AuthLevel, AuthType, DcomAppId, IfFlag, IfUuid, IfVersion, ImageName, LocalAddrV4, LocalAddrV6, LocalPort, Max, Pipe, Protocol, RemoteAddrV4, RemoteAddrV6, SecEncryptAlgorithm, SecKeySize, } #[repr(usize)] pub enum FieldsRpcEpmap { RemoteUserToken, IfUuid, IfVersion, Protocol, AuthType, AuthLevel, SecEncryptAlgorithm, SecKeySize, LocalAddrV4, LocalAddrV6, LocalPort, Pipe, RemoteAddrV4, RemoteAddrV6, Max, } #[repr(usize)] pub enum FieldsRpcEpAdd { ProcessWithRpcIfUuid, Protocol, EpValue, EpFlags, Max, } #[repr(usize)] pub enum FieldsRpcProxyConn { ClientToken, ServerName, ServerPort, ProxyAuthType, ClientCertKeyLength, ClientCertOid, Max, } #[repr(usize)] pub enum FieldsRpcProxyIf { ClientToken, IfUuid, IfVersion, ServerName, ServerPort, ProxyAuthType, ClientCertKeyLength, ClientCertOid, Max, } #[repr(usize)] pub enum FieldsKmAuthorization { RemoteId, AuthenticationType, KmType, Direction, KmMode, IpsecPolicyKey, NapContext, Max, } #[repr(usize)] pub enum FieldsInboundReserved2 { Reserved0, Reserved1, Reserved2, Reserved3, Reserved4, Reserved5, Reserved6, Reserved7, Reserved8, Reserved9, Reserved10, Reserved11, Reserved12, Reserved13, Reserved14, Reserved15, Max, } #[repr(usize)] pub enum FieldsOutboundNetworkConnectionPolicyV4 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpRemoteAddress, IpDestinationAddressType, IpRemotePort, Flags, AleOriginalAppId, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, Max, } #[repr(usize)] pub enum FieldsOutboundNetworkConnectionPolicyV6 { AleAppId, AleUserId, IpLocalAddress, IpLocalAddressType, IpLocalPort, IpProtocol, IpRemoteAddress, IpDestinationAddressType, IpRemotePort, Flags, AleOriginalAppId, AlePackageId, AleSecurityAttributeFqbnValue, CompartmentId, Max, } ================================================ FILE: windows_kext/wdk/src/filter_engine/metadata.rs ================================================ use core::{ffi::c_void, ptr::NonNull}; use alloc::string::String; use widestring::U16CString; use windows_sys::Win32::{ Foundation::HANDLE, NetworkManagement::{ IpHelper::IP_ADDRESS_PREFIX, WindowsFilteringPlatform::{ FWPS_METADATA_FIELD_COMPLETION_HANDLE, FWPS_METADATA_FIELD_FRAGMENT_DATA, FWPS_METADATA_FIELD_PROCESS_ID, FWPS_METADATA_FIELD_PROCESS_PATH, FWPS_METADATA_FIELD_REMOTE_SCOPE_ID, FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA, FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE, FWP_BYTE_BLOB, FWP_DIRECTION, }, }, Networking::WinSock::SCOPE_ID, }; #[repr(C)] pub(crate) struct FwpsIncomingMetadataValues { /// Bitmask representing which values are set. current_metadata_values: u32, /// Internal flags; flags: u32, /// Reserved for system use. reserved: u64, /// Discard module and reason. discard_metadata: FwpsDiscardMetadata0, /// Flow Handle. flow_handle: u64, /// IP Header size. ip_header_size: u32, /// Transport Header size transport_header_size: u32, /// Process Path. process_path: *const FWP_BYTE_BLOB, /// Token used for authorization. token: u64, /// Process Id. process_id: u64, /// Source and Destination interface indices for discard indications. source_interface_index: u32, destination_interface_index: u32, /// Compartment Id for injection APIs. compartment_id: u32, /// Fragment data for inbound fragments. fragment_metadata: FwpsInboundFragmentMetadata0, /// Path MTU for outbound packets (to enable calculation of fragments). path_mtu: u32, /// Completion handle (required in order to be able to pend at this layer). completion_handle: HANDLE, /// Endpoint handle for use in outbound transport layer injection. transport_endpoint_handle: u64, /// Remote scope id for use in outbound transport layer injection. remote_scope_id: SCOPE_ID, /// Socket control data (and length) for use in outbound transport layer injection. control_data: *const u8, control_data_length: u32, /// Direction for the current packet. Only specified for ALE re-authorization. packet_direction: FWP_DIRECTION, /// Raw IP header (and length) if the packet is sent with IP header from a RAW socket. header_include_header: *mut c_void, header_include_header_length: u32, destination_prefix: IP_ADDRESS_PREFIX, frame_length: u16, parent_endpoint_handle: u64, icmp_id_and_sequence: u32, /// PID of the process that will be accepting the redirected connection local_redirect_target_pid: u64, /// original destination of a redirected connection original_destination: *mut c_void, redirect_records: HANDLE, /// Bitmask representing which L2 values are set. current_l2_metadata_values: u32, /// L2 layer Flags; l2_flags: u32, ethernet_mac_header_size: u32, wifi_operation_mode: u32, padding0: u32, padding1: u16, padding2: u32, v_switch_packet_context: HANDLE, sub_process_tag: *mut c_void, // Reserved for system use. reserved1: u64, } impl FwpsIncomingMetadataValues { pub(crate) fn has_field(&self, field: u32) -> bool { self.current_metadata_values & field > 0 } pub(crate) fn get_process_id(&self) -> Option { if self.has_field(FWPS_METADATA_FIELD_PROCESS_ID) { return Some(self.process_id); } None } pub(crate) unsafe fn get_process_path(&self) -> Option { if self.has_field(FWPS_METADATA_FIELD_PROCESS_PATH) { if let Ok(path16) = U16CString::from_ptr( core::mem::transmute((*self.process_path).data), (*self.process_path).size as usize / 2, ) { if let Ok(path) = path16.to_string() { return Some(path); } } } None } pub(crate) fn get_completion_handle(&self) -> Option { if self.has_field(FWPS_METADATA_FIELD_COMPLETION_HANDLE) { return Some(self.completion_handle); } None } pub(crate) fn get_transport_endpoint_handle(&self) -> Option { if self.has_field(FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE) { return Some(self.transport_endpoint_handle); } None } pub(crate) fn get_remote_scope_id(&self) -> Option { if self.has_field(FWPS_METADATA_FIELD_REMOTE_SCOPE_ID) { return Some(self.remote_scope_id); } None } pub(crate) fn is_fragment_data(&self) -> bool { if self.has_field(FWPS_METADATA_FIELD_FRAGMENT_DATA) { return self.fragment_metadata.fragment_offset != 0; } false } pub(crate) unsafe fn get_control_data(&self) -> Option> { if self.has_field(FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA) { if self.control_data.is_null() || self.control_data_length == 0 { return None; } let ptr = NonNull::new(self.control_data as *mut u8).unwrap(); let slice = NonNull::slice_from_raw_parts(ptr, self.control_data_length as usize); return Some(slice); } None } } #[allow(dead_code)] #[repr(C)] enum FwpsDiscardModule0 { Network = 0, Transport = 1, General = 2, Max = 3, } #[repr(C)] struct FwpsDiscardMetadata0 { discard_module: FwpsDiscardModule0, discard_reason: u32, filter_id: u64, } #[repr(C)] struct FwpsInboundFragmentMetadata0 { fragment_identification: u32, fragment_offset: u16, fragment_length: u32, } ================================================ FILE: windows_kext/wdk/src/filter_engine/mod.rs ================================================ use core::ffi::c_void; use crate::alloc::borrow::ToOwned; use crate::driver::Driver; use crate::ffi::FWPS_FILTER2; use crate::filter_engine::transaction::Transaction; use crate::{dbg, info}; use alloc::boxed::Box; use alloc::string::String; use alloc::{format, vec::Vec}; use windows_sys::Wdk::Foundation::DEVICE_OBJECT; use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE}; use self::callout::{Callout, FilterType}; use self::callout_data::CalloutData; use self::classify::ClassifyOut; use self::layer::IncomingValues; use self::metadata::FwpsIncomingMetadataValues; pub mod callout; pub mod callout_data; pub(crate) mod classify; #[allow(dead_code)] pub mod ffi; pub mod layer; pub(crate) mod metadata; pub mod net_buffer; pub mod packet; pub mod stream_data; pub mod transaction; // Helper functions for ALE Readirect layers. Not needed for the current implementation. // pub mod connect_request; pub struct FilterEngine { device_object: *mut DEVICE_OBJECT, handle: HANDLE, sublayer_guid: u128, committed: bool, callouts: Option>>, } impl FilterEngine { pub fn new(driver: &Driver, layer_guid: u128) -> Result { let filter_engine_handle: HANDLE; match ffi::create_filter_engine() { Ok(handle) => { filter_engine_handle = handle; } Err(code) => { return Err(format!("failed to initialize filter engine {}", code).to_owned()); } } Ok(Self { device_object: driver.get_device_object(), handle: filter_engine_handle, sublayer_guid: layer_guid, committed: false, callouts: None, }) } pub fn commit(&mut self, callouts: Vec) -> Result<(), String> { { // Begin write transaction. This is also a lock guard. let mut filter_engine = match Transaction::begin_write(self) { Ok(transaction) => transaction, Err(err) => { return Err(err); } }; if let Err(err) = filter_engine.register_sublayer() { return Err(format!("filter_engine: {}", err)); } dbg!("Callouts count: {}", callouts.len()); let mut boxed_callouts = Vec::new(); // Register all callouts for callout in callouts { let mut callout = Box::new(callout); callout.address = callout.as_ref() as *const Callout as u64; if let Err(err) = callout.register_callout( filter_engine.handle, filter_engine.device_object, catch_all_callout, ) { // This will destroy the callout structs. return Err(err); } if let Err(err) = callout.register_filter(filter_engine.handle, filter_engine.sublayer_guid) { // This will destroy the callout structs. return Err(err); } dbg!( "registering callout: {} -> {}", callout.name, callout.filter_id ); boxed_callouts.push(callout) } if let Some(callouts) = &mut filter_engine.callouts { callouts.append(&mut boxed_callouts); } else { filter_engine.callouts = Some(boxed_callouts); } filter_engine.commit()? } self.committed = true; info!("transaction committed"); return Ok(()); } pub fn reset_all_filters(&mut self) -> Result<(), String> { // Begin to write transaction. This is also a lock guard. It will abort if transaction is not committed. let mut filter_engine = match Transaction::begin_write(self) { Ok(transaction) => transaction, Err(err) => { return Err(err); } }; let filter_engine_handle = filter_engine.handle; let sublayer_guid = filter_engine.sublayer_guid; if let Some(callouts) = &mut filter_engine.callouts { for callout in callouts { if let FilterType::Resettable = callout.filter_type { if callout.filter_id != 0 { // Remove old filter. if let Err(err) = ffi::unregister_filter(filter_engine_handle, callout.filter_id) { return Err(format!("filter_engine: {}", err)); } callout.filter_id = 0; } // Create new filter. if let Err(err) = callout.register_filter(filter_engine_handle, sublayer_guid) { return Err(format!("filter_engine: {}", err)); } } } } // Commit transaction. filter_engine.commit()?; return Ok(()); } fn register_sublayer(&self) -> Result<(), String> { let result = ffi::register_sublayer( self.handle, "PortmasterSublayer", "The Portmaster sublayer holds all it's filters.", self.sublayer_guid, ); if let Err(code) = result { return Err(format!("failed to register sublayer: {}", code)); } return Ok(()); } } impl Drop for FilterEngine { fn drop(&mut self) { dbg!("Unregistering callouts"); if let Some(callouts) = &self.callouts { for callout in callouts { if callout.registered { if let Err(code) = ffi::unregister_callout(callout.id) { dbg!("failed to unregister callout: {}", code); } if callout.filter_id != 0 { if let Err(code) = ffi::unregister_filter(self.handle, callout.filter_id) { dbg!("failed to unregister filter: {}", code) } } } } } if self.committed { if let Err(code) = ffi::unregister_sublayer(self.handle, self.sublayer_guid) { dbg!("Failed to unregister sublayer: {}", code); } } if !self.handle.is_null() && self.handle != INVALID_HANDLE_VALUE { _ = ffi::filter_engine_close(self.handle); } } } #[no_mangle] unsafe extern "C" fn catch_all_callout( fixed_values: *const IncomingValues, meta_values: *const FwpsIncomingMetadataValues, layer_data: *mut c_void, _context: *mut c_void, filter: *const FWPS_FILTER2, _flow_context: u64, classify_out: *mut ClassifyOut, ) { let filter = &(*filter); // Filter context is the address of the callout. let callout = filter.context as *mut Callout; if let Some(callout) = callout.as_ref() { // Setup callout data. let array = core::slice::from_raw_parts( (*fixed_values).incoming_value_array, (*fixed_values).value_count as usize, ); let data = CalloutData { layer: callout.layer, callout_id: filter.context as usize, values: array, metadata: meta_values, classify_out, layer_data, }; // Call the defined function. (callout.callout_fn)(data); } } ================================================ FILE: windows_kext/wdk/src/filter_engine/net_buffer.rs ================================================ use core::mem::MaybeUninit; use alloc::{ string::{String, ToString}, vec::Vec, }; use windows_sys::Wdk::System::SystemServices::{ IoAllocateMdl, IoFreeMdl, MmBuildMdlForNonPagedPool, }; use crate::{ allocator::POOL_TAG, ffi::{ FwpsAllocateNetBufferAndNetBufferList0, FwpsFreeNetBufferList0, NdisAdvanceNetBufferDataStart, NdisAllocateNetBufferListPool, NdisFreeNetBufferListPool, NdisGetDataBuffer, NdisRetreatNetBufferDataStart, NDIS_HANDLE, NDIS_OBJECT_TYPE_DEFAULT, NET_BUFFER_LIST, NET_BUFFER_LIST_POOL_PARAMETERS, NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1, }, utils::check_ntstatus, }; pub struct NetBufferList { pub(crate) nbl: *mut NET_BUFFER_LIST, data: Option>, advance_on_drop: Option, } impl NetBufferList { pub fn new(nbl: *mut NET_BUFFER_LIST) -> NetBufferList { NetBufferList { nbl, data: None, advance_on_drop: None, } } pub fn iter(&self) -> NetBufferListIter { NetBufferListIter(self.nbl) } pub fn read_bytes(&self, buffer: &mut [u8]) -> Result<(), ()> { unsafe { let Some(nbl) = self.nbl.as_ref() else { return Err(()); }; let nb = nbl.Header.first_net_buffer; if let Some(nb) = nb.as_ref() { let data_length = nb.nbSize.DataLength; if data_length == 0 { return Err(()); } if buffer.len() > data_length as usize { return Err(()); } let mut ptr = NdisGetDataBuffer(nb, buffer.len() as u32, core::ptr::null_mut(), 1, 0); if !ptr.is_null() { buffer.copy_from_slice(core::slice::from_raw_parts(ptr, buffer.len())); return Ok(()); } ptr = NdisGetDataBuffer(nb, buffer.len() as u32, buffer.as_mut_ptr(), 1, 0); if !ptr.is_null() { return Ok(()); } } } return Err(()); } pub fn clone(&self, net_allocator: &NetworkAllocator) -> Result { unsafe { let Some(nbl) = self.nbl.as_ref() else { return Err("net buffer list is null".to_string()); }; let nb = nbl.Header.first_net_buffer; if let Some(nb) = nb.as_ref() { let data_length = nb.nbSize.DataLength; if data_length == 0 { return Err("can't clone empty packet".to_string()); } // Allocate space in buffer, if buffer is too small. let mut buffer = alloc::vec![0_u8; data_length as usize]; let buffer_ptr = buffer.as_mut_ptr(); // Two options returns a pointer to the raw packet buffer, // or copies the data to the supplied buffer // and returns a pointer to the supplied buffer. let ptr = NdisGetDataBuffer(nb, data_length, buffer_ptr, 1, 0); if ptr.is_null() { return Err("failed to copy packet buffer".to_string()); } // If the pointers differ the data is not in the correct place. if ptr != buffer_ptr { buffer.copy_from_slice(core::slice::from_raw_parts(ptr, data_length as usize)); } let new_nbl = net_allocator.wrap_packet_in_nbl(&buffer)?; return Ok(NetBufferList { nbl: new_nbl, data: Some(buffer), advance_on_drop: None, }); } else { return Err("net buffer is null".to_string()); } } } pub fn get_data_mut(&mut self) -> Option<&mut [u8]> { if let Some(data) = &mut self.data { return Some(data.as_mut_slice()); } return None; } pub fn get_data(&self) -> Option<&[u8]> { if let Some(data) = &self.data { return Some(data.as_slice()); } return None; } pub fn get_data_length(&self) -> u32 { unsafe { if let Some(nbl) = self.nbl.as_ref() { let mut nb = nbl.Header.first_net_buffer; let mut data_length = 0; while !nb.is_null() { let mut next = core::ptr::null_mut(); if let Some(nb) = nb.as_ref() { data_length += nb.nbSize.DataLength; next = nb.Next; } nb = next; } data_length } else { 0 } } } /// Retreats the mnl of the buffer. Does not auto advance multiple retreats. pub fn retreat(&mut self, size: u32, auto_advance: bool) { unsafe { if let Some(nbl) = self.nbl.as_mut() { if let Some(nb) = nbl.Header.first_net_buffer.as_mut() { NdisRetreatNetBufferDataStart(nb as _, size, 0, core::ptr::null_mut()); if auto_advance { self.advance_on_drop = Some(size); } } } } } /// Advances the MDL of the buffer. pub fn advance(&mut self, size: u32) { unsafe { if let Some(nbl) = self.nbl.as_mut() { if let Some(nb) = nbl.Header.first_net_buffer.as_mut() { NdisAdvanceNetBufferDataStart(nb as _, size, false, core::ptr::null_mut()); } } } } } impl Drop for NetBufferList { fn drop(&mut self) { if let Some(advance_amount) = self.advance_on_drop { self.advance(advance_amount); } if self.data.is_some() { NetworkAllocator::free_net_buffer(self.nbl); } } } pub struct NetBufferListIter(*mut NET_BUFFER_LIST); impl NetBufferListIter { pub fn new(nbl: *mut NET_BUFFER_LIST) -> Self { Self(nbl) } } impl Iterator for NetBufferListIter { type Item = NetBufferList; fn next(&mut self) -> Option { unsafe { if let Some(nbl) = self.0.as_mut() { self.0 = nbl.Header.next as _; return Some(NetBufferList { nbl, data: None, advance_on_drop: None, }); } None } } } pub fn read_packet_partial(nbl: *mut NET_BUFFER_LIST, buffer: &mut [u8]) -> Result<(), ()> { unsafe { let Some(nbl) = nbl.as_ref() else { return Err(()); }; let nb = nbl.Header.first_net_buffer; if let Some(nb) = nb.as_ref() { let data_length = nb.nbSize.DataLength; if data_length == 0 { return Err(()); } if buffer.len() > data_length as usize { return Err(()); } let ptr = NdisGetDataBuffer(nb, buffer.len() as u32, buffer.as_mut_ptr(), 1, 0); if !ptr.is_null() { return Ok(()); } } } return Err(()); } pub struct RetreatGuard { size: u32, nbl: *mut NET_BUFFER_LIST, } impl Drop for RetreatGuard { fn drop(&mut self) { NetworkAllocator::advance_net_buffer(self.nbl, self.size); } } pub struct NetworkAllocator { pool_handle: NDIS_HANDLE, } impl NetworkAllocator { pub fn new() -> Self { unsafe { let mut params: NET_BUFFER_LIST_POOL_PARAMETERS = MaybeUninit::zeroed().assume_init(); params.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; params.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; params.Header.Size = core::mem::size_of::() as u16; params.fAllocateNetBuffer = true; params.PoolTag = POOL_TAG; params.DataSize = 0; let pool_handle = NdisAllocateNetBufferListPool(core::ptr::null_mut(), ¶ms); Self { pool_handle } } } pub fn wrap_packet_in_nbl(&self, packet_data: &[u8]) -> Result<*mut NET_BUFFER_LIST, String> { if self.pool_handle.is_null() { return Err("allocator not initialized".to_string()); } unsafe { // Create MDL struct that will hold the buffer. let mdl = IoAllocateMdl( packet_data.as_ptr() as _, packet_data.len() as u32, 0, 0, core::ptr::null_mut(), ); if mdl.is_null() { return Err("failed to allocate mdl".to_string()); } // Build mdl with packet_data buffer. MmBuildMdlForNonPagedPool(mdl); // Initialize NBL structure. let mut nbl = core::ptr::null_mut(); let status = FwpsAllocateNetBufferAndNetBufferList0( self.pool_handle, 0, 0, mdl, 0, packet_data.len() as u64, &mut nbl, ); if let Err(err) = check_ntstatus(status) { IoFreeMdl(mdl); return Err(err); } return Ok(nbl); } } pub fn free_net_buffer(nbl: *mut NET_BUFFER_LIST) { NetBufferListIter::new(nbl).for_each(|nbl| unsafe { if let Some(nbl) = nbl.nbl.as_mut() { if let Some(nb) = nbl.Header.first_net_buffer.as_mut() { IoFreeMdl(nb.MdlChain); } FwpsFreeNetBufferList0(nbl); } }); } pub fn retreat_net_buffer( nbl: *mut NET_BUFFER_LIST, size: u32, auto_advance: bool, ) -> Option { unsafe { if let Some(nbl) = nbl.as_mut() { if let Some(nb) = nbl.Header.first_net_buffer.as_mut() { NdisRetreatNetBufferDataStart(nb as _, size, 0, core::ptr::null_mut()); if auto_advance { return Some(RetreatGuard { size, nbl }); } } } } return None; } pub fn advance_net_buffer(nbl: *mut NET_BUFFER_LIST, size: u32) { unsafe { if let Some(nbl) = nbl.as_mut() { if let Some(nb) = nbl.Header.first_net_buffer.as_mut() { NdisAdvanceNetBufferDataStart(nb as _, size, false, core::ptr::null_mut()); } } } } } impl Drop for NetworkAllocator { fn drop(&mut self) { unsafe { if !self.pool_handle.is_null() { NdisFreeNetBufferListPool(self.pool_handle); } } } } ================================================ FILE: windows_kext/wdk/src/filter_engine/packet.rs ================================================ use alloc::{ boxed::Box, string::{String, ToString}, }; use core::{ffi::c_void, mem::MaybeUninit}; use windows_sys::Win32::{ Foundation::{HANDLE, INVALID_HANDLE_VALUE}, Networking::WinSock::{AF_INET, AF_INET6, AF_UNSPEC, SCOPE_ID}, System::Kernel::UNSPECIFIED_COMPARTMENT_ID, }; use crate::{ ffi::{ FwpsInjectNetworkReceiveAsync0, FwpsInjectNetworkSendAsync0, FwpsInjectTransportReceiveAsync0, FwpsInjectTransportSendAsync1, FwpsInjectionHandleCreate0, FwpsInjectionHandleDestroy0, FwpsQueryPacketInjectionState0, FWPS_INJECTION_TYPE_NETWORK, FWPS_INJECTION_TYPE_TRANSPORT, FWPS_PACKET_INJECTION_STATE, FWPS_TRANSPORT_SEND_PARAMS1, NET_BUFFER_LIST, }, utils::check_ntstatus, }; use super::{callout_data::CalloutData, net_buffer::NetBufferList}; pub struct TransportPacketList { ipv6: bool, pub net_buffer_list: NetBufferList, remote_ip: [u8; 16], endpoint_handle: u64, remote_scope_id: SCOPE_ID, // Owned copy of the WFP control data. The original WFP pointer is only // valid during the ALE classify callback; the bytes are copied here so they outlive it. control_data: Option>, inbound: bool, interface_index: u32, sub_interface_index: u32, // send_params and remote_ip must outlive inject_packet_list_transport // because FwpsInjectTransportSendAsync1 may read them after the function returns. // Storing send_params here ensures it lives on the heap inside Box // until the WFP completion callback (free_transport_packet) drops it. send_params: FWPS_TRANSPORT_SEND_PARAMS1, } pub struct InjectInfo { pub ipv6: bool, pub inbound: bool, pub loopback: bool, pub interface_index: u32, pub sub_interface_index: u32, } pub struct Injector { transport_inject_handle: HANDLE, packet_inject_handle_v4: HANDLE, packet_inject_handle_v6: HANDLE, } // TODO: Implement custom allocator for the packet buffers for reusing memory and reducing allocations. This should improve latency. impl Injector { pub fn new() -> Self { let mut transport_inject_handle: HANDLE = INVALID_HANDLE_VALUE; let mut packet_inject_handle_v4: HANDLE = INVALID_HANDLE_VALUE; let mut packet_inject_handle_v6: HANDLE = INVALID_HANDLE_VALUE; unsafe { let status = FwpsInjectionHandleCreate0( AF_UNSPEC, FWPS_INJECTION_TYPE_TRANSPORT, &mut transport_inject_handle, ); if let Err(err) = check_ntstatus(status) { crate::err!("error allocating transport inject handle: {}", err); } let status = FwpsInjectionHandleCreate0( AF_INET, FWPS_INJECTION_TYPE_NETWORK, &mut packet_inject_handle_v4, ); if let Err(err) = check_ntstatus(status) { crate::err!("error allocating network inject handle: {}", err); } let status = FwpsInjectionHandleCreate0( AF_INET6, FWPS_INJECTION_TYPE_NETWORK, &mut packet_inject_handle_v6, ); if let Err(err) = check_ntstatus(status) { crate::err!("error allocating network inject handle: {}", err); } } Self { transport_inject_handle, packet_inject_handle_v4, packet_inject_handle_v6, } } // TODO: pick a better name pub fn from_ale_callout( ipv6: bool, callout_data: &CalloutData, net_buffer_list: NetBufferList, remote_ip_slice: &[u8], inbound: bool, interface_index: u32, sub_interface_index: u32, ) -> TransportPacketList { let mut control_data: Option> = None; if let Some(cd) = callout_data.get_control_data() { // Copy the bytes while the WFP pointer is still valid (we are inside the callout). control_data = Some(unsafe { cd.as_ref() }.to_vec().into_boxed_slice()); } let mut remote_ip: [u8; 16] = [0; 16]; if ipv6 { remote_ip[0..16].copy_from_slice(remote_ip_slice); } else { remote_ip[0..4].copy_from_slice(remote_ip_slice); } TransportPacketList { ipv6, net_buffer_list, remote_ip, endpoint_handle: callout_data.get_transport_endpoint_handle().unwrap_or(0), remote_scope_id: callout_data .get_remote_scope_id() .unwrap_or(unsafe { MaybeUninit::zeroed().assume_init() }), control_data, inbound, interface_index, sub_interface_index, // Populated with valid pointers in inject_packet_list_transport after boxing. send_params: unsafe { MaybeUninit::zeroed().assume_init() }, } } // TODO: pick a better name. This is not transport pub fn inject_packet_list_transport( &self, packet_list: TransportPacketList, ) -> Result<(), String> { if self.transport_inject_handle == INVALID_HANDLE_VALUE { return Err("failed to inject packet: invalid handle value".to_string()); } // Box the entire packet_list so that remote_ip and send_params // are heap-allocated. Their addresses remain stable until free_transport_packet // drops the Box after WFP calls the completion callback. let mut boxed = Box::new(packet_list); let raw_nbl = boxed.net_buffer_list.nbl; unsafe { // Populate send_params with pointers into the boxed struct. // These addresses are stable because the Box will not move until freed. let mut control_data_length = 0; let control_data: *mut c_void = match &boxed.control_data { Some(cd) => { control_data_length = cd.len(); cd.as_ptr() as *mut c_void } None => core::ptr::null_mut(), }; boxed.send_params = FWPS_TRANSPORT_SEND_PARAMS1 { remote_address: boxed.remote_ip.as_ptr(), remote_scope_id: boxed.remote_scope_id, control_data, control_data_length: control_data_length as u32, header_include_header: core::ptr::null_mut(), header_include_header_length: 0, }; let address_family = if boxed.ipv6 { AF_INET6 } else { AF_INET }; let raw_ptr = Box::into_raw(boxed); // Inject. Context is *mut TransportPacketList; freed by free_transport_packet. let status = if (*raw_ptr).inbound { FwpsInjectTransportReceiveAsync0( self.transport_inject_handle, core::ptr::null_mut(), core::ptr::null_mut(), 0, address_family, UNSPECIFIED_COMPARTMENT_ID, (*raw_ptr).interface_index, (*raw_ptr).sub_interface_index, raw_nbl, free_transport_packet, raw_ptr as _, ) } else { FwpsInjectTransportSendAsync1( self.transport_inject_handle, core::ptr::null_mut(), (*raw_ptr).endpoint_handle, 0, &mut (*raw_ptr).send_params, address_family, UNSPECIFIED_COMPARTMENT_ID, raw_nbl, free_transport_packet, raw_ptr as _, ) }; // Check for success if let Err(err) = check_ntstatus(status) { _ = Box::from_raw(raw_ptr); return Err(err); } } return Ok(()); } pub fn inject_net_buffer_list( &self, net_buffer_list: NetBufferList, inject_info: InjectInfo, ) -> Result<(), String> { if self.packet_inject_handle_v4 == INVALID_HANDLE_VALUE { return Err("failed to inject packet: invalid handle value".to_string()); } // Escape the stack, so the data can be freed after inject is complete. let packet_boxed = Box::new(net_buffer_list); let nbl = packet_boxed.nbl; let packet_pointer = Box::into_raw(packet_boxed); let inject_handle = if inject_info.ipv6 { self.packet_inject_handle_v6 } else { self.packet_inject_handle_v4 }; let status = if inject_info.inbound && !inject_info.loopback { // Inject inbound. unsafe { FwpsInjectNetworkReceiveAsync0( inject_handle, core::ptr::null_mut(), 0, UNSPECIFIED_COMPARTMENT_ID, inject_info.interface_index, inject_info.sub_interface_index, nbl, free_packet, (packet_pointer as *mut NetBufferList) as _, ) } } else { // Inject outbound. unsafe { FwpsInjectNetworkSendAsync0( inject_handle, core::ptr::null_mut(), 0, UNSPECIFIED_COMPARTMENT_ID, nbl, free_packet, (packet_pointer as *mut NetBufferList) as _, ) } }; // Check for error. if let Err(err) = check_ntstatus(status) { unsafe { // Get back ownership for data. _ = Box::from_raw(packet_pointer); } return Err(err); } return Ok(()); } pub fn was_network_packet_injected_by_self( &self, nbl: *const NET_BUFFER_LIST, ipv6: bool, ) -> bool { let inject_handle = if ipv6 { self.packet_inject_handle_v6 } else { self.packet_inject_handle_v4 }; if inject_handle == INVALID_HANDLE_VALUE || inject_handle.is_null() { return false; } unsafe { let state = FwpsQueryPacketInjectionState0(inject_handle, nbl, core::ptr::null_mut()); match state { FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_NOT_INJECTED => false, FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_INJECTED_BY_SELF => true, FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_INJECTED_BY_OTHER => false, FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF => true, FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_INJECTION_STATE_MAX => false, } } } pub fn was_network_packet_injected_by_self_ale(&self, nbl: *const NET_BUFFER_LIST) -> bool { unsafe { let state = FwpsQueryPacketInjectionState0( self.transport_inject_handle, nbl, core::ptr::null_mut(), ); match state { FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_NOT_INJECTED => false, FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_INJECTED_BY_SELF => true, FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_INJECTED_BY_OTHER => false, FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF => true, FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_INJECTION_STATE_MAX => false, } } } } impl Drop for Injector { fn drop(&mut self) { unsafe { if self.transport_inject_handle != INVALID_HANDLE_VALUE && !self.transport_inject_handle.is_null() { FwpsInjectionHandleDestroy0(self.transport_inject_handle); self.transport_inject_handle = INVALID_HANDLE_VALUE; } if self.packet_inject_handle_v4 != INVALID_HANDLE_VALUE && !self.packet_inject_handle_v4.is_null() { FwpsInjectionHandleDestroy0(self.packet_inject_handle_v4); self.packet_inject_handle_v4 = INVALID_HANDLE_VALUE; } if self.packet_inject_handle_v6 != INVALID_HANDLE_VALUE && !self.packet_inject_handle_v6.is_null() { FwpsInjectionHandleDestroy0(self.packet_inject_handle_v6); self.packet_inject_handle_v6 = INVALID_HANDLE_VALUE; } } } } unsafe extern "C" fn free_packet( context: *mut c_void, net_buffer_list: *mut NET_BUFFER_LIST, _dispatch_level: bool, ) { if let Some(nbl) = net_buffer_list.as_ref() { if let Err(err) = check_ntstatus(nbl.Status) { crate::err!("inject status: {}", err); } else { crate::dbg!("inject status: Ok"); } } _ = Box::from_raw(context as *mut NetBufferList); } /// Completion callback for transport inject paths (both inbound and outbound). /// The context is a `Box` cast to `*mut c_void`. /// Dropping it also correctly drops the inner `NetBufferList`. unsafe extern "C" fn free_transport_packet( context: *mut c_void, net_buffer_list: *mut NET_BUFFER_LIST, _dispatch_level: bool, ) { if let Some(nbl) = net_buffer_list.as_ref() { if let Err(err) = check_ntstatus(nbl.Status) { crate::err!("inject status: {}", err); } else { crate::dbg!("inject status: Ok"); } } _ = Box::from_raw(context as *mut TransportPacketList); } ================================================ FILE: windows_kext/wdk/src/filter_engine/stream_data.rs ================================================ use crate::ffi::{NET_BUFFER, NET_BUFFER_LIST}; use windows_sys::Wdk::Foundation::MDL; const FWPS_STREAM_FLAG_RECEIVE: u32 = 0x00000001; #[repr(C)] pub enum StreamActionType { None, NeedMoreData, DropConnection, Defer, AllowConnection, TypeMax, } #[repr(C)] pub struct StreamCalloutIoPacket { stream_data: *mut StreamData, missed_bytes: usize, count_bytes_required: usize, count_bytes_enforced: usize, stream_action: StreamActionType, } #[repr(C)] pub struct StreamDataOffset { // NET_BUFFER_LIST in which offset lies. net_buffer_list: *mut NET_BUFFER_LIST, // NET_BUFFER in which offset lies. net_buffer: *mut NET_BUFFER, // MDL in which offset lies. mdl: *mut MDL, // Byte offset from the beginning of the MDL in which data lies. mdl_offset: u32, // Offset relative to the DataOffset of the NET_BUFFER. net_buffer_offset: u32, // Offset from the beginning of the entire stream buffer. stream_data_offset: usize, } #[repr(C)] pub struct StreamData { flags: u32, data_offset: StreamDataOffset, data_length: usize, net_buffer_list_chain: *mut NET_BUFFER_LIST, } impl StreamCalloutIoPacket { pub fn get_data_len(&self) -> usize { unsafe { if let Some(stream_data) = self.stream_data.as_ref() { return stream_data.data_length; } } return 0; } pub fn is_receive(&self) -> bool { unsafe { if let Some(stream_data) = self.stream_data.as_ref() { return stream_data.flags & FWPS_STREAM_FLAG_RECEIVE > 0; } } return false; } } ================================================ FILE: windows_kext/wdk/src/filter_engine/transaction.rs ================================================ use core::ops::{Deref, DerefMut}; use super::{ffi, FilterEngine}; use alloc::{format, string::String}; use windows_sys::Win32::NetworkManagement::WindowsFilteringPlatform::FWPM_TXN_READ_ONLY; /// Transaction guard for Filter Engine. Internally useses a lock. DO NOT USE WITH OTHER LOCKS. pub(super) struct Transaction<'a> { filter_engine: &'a mut FilterEngine, committed: bool, } impl<'a> Transaction<'a> { fn begin(filter_engine: &'a mut FilterEngine, flags: u32) -> Result { if let Err(code) = ffi::filter_engine_transaction_begin(filter_engine.handle, flags) { return Err(format!( "filter-engine: failed to begin transaction: {}", code )); } Ok(Self { filter_engine, committed: false, }) } /// Creates a read only guard for filter engine transaction. #[allow(dead_code)] pub(super) fn begin_read(filter_engine: &'a mut FilterEngine) -> Result { return Self::begin(filter_engine, FWPM_TXN_READ_ONLY); } /// Creates a read/write guard for filter engine transaction. /// Note! If another transaction is already in progress, it will return an error STATUS_FWP_TXN_IN_PROGRESS. pub(super) fn begin_write(filter_engine: &'a mut FilterEngine) -> Result { return Self::begin(filter_engine, 0); } /// Applying all the changes and releases the lock. pub(super) fn commit(&mut self) -> Result<(), String> { if let Err(code) = ffi::filter_engine_transaction_commit(self.filter_engine.handle) { return Err(format!( "filter-engine: failed to commit transaction: {}", code )); } self.committed = true; Ok(()) } } impl<'a> Deref for Transaction<'a> { type Target = FilterEngine; fn deref(&self) -> &Self::Target { self.filter_engine } } impl<'a> DerefMut for Transaction<'a> { fn deref_mut(&mut self) -> &mut Self::Target { self.filter_engine } } impl<'a> Drop for Transaction<'a> { /// Releases the lock of transaction was not committed. fn drop(&mut self) { if !self.committed { _ = ffi::filter_engine_transaction_abort(self.filter_engine.handle); } } } ================================================ FILE: windows_kext/wdk/src/interface.rs ================================================ use crate::{ alloc::borrow::ToOwned, driver::Driver, ffi::{ pm_GetDeviceObject, pm_InitDriverObject, pm_WdfObjectGetTypedContextWorker, WdfObjectAttributes, WdfObjectContextTypeInfo, }, utils::check_ntstatus, }; use alloc::ffi::CString; use alloc::format; use alloc::string::String; use widestring::U16CString; use windows_sys::{ Wdk::{ Foundation::{DEVICE_OBJECT, DRIVER_OBJECT}, System::SystemServices::DbgPrint, }, Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE, UNICODE_STRING}, }; // Debug pub fn dbg_print(str: String) { if let Ok(c_str) = CString::new(str) { unsafe { DbgPrint(c_str.as_ptr() as _); } } } pub fn init_driver_object( driver_object: *mut DRIVER_OBJECT, registry_path: *mut UNICODE_STRING, driver_name: &str, object_attributes: *mut WdfObjectAttributes, ) -> Result { let win_driver_path = format!("\\Device\\{}", driver_name); let dos_driver_path = format!("\\??\\{}", driver_name); let mut wdf_driver_handle = INVALID_HANDLE_VALUE; let mut wdf_device_handle = INVALID_HANDLE_VALUE; let Ok(win_driver) = U16CString::from_str(win_driver_path) else { return Err("Invalid argument win_driver_path".to_owned()); }; let Ok(dos_driver) = U16CString::from_str(dos_driver_path) else { return Err("Invalid argument dos_driver_path".to_owned()); }; unsafe { let status = pm_InitDriverObject( driver_object, registry_path, &mut wdf_driver_handle, &mut wdf_device_handle, win_driver.as_ptr(), dos_driver.as_ptr(), object_attributes, empty_wdf_driver_unload, ); check_ntstatus(status)?; return Ok(Driver::new( driver_object, wdf_driver_handle, wdf_device_handle, )); } } pub fn get_device_context_from_wdf_device( wdf_device: HANDLE, type_info: &'static WdfObjectContextTypeInfo, ) -> *mut T { unsafe { return core::mem::transmute(pm_WdfObjectGetTypedContextWorker(wdf_device, type_info)); } } pub(crate) fn wdf_device_wdm_get_device_object(wdf_device: HANDLE) -> *mut DEVICE_OBJECT { unsafe { return pm_GetDeviceObject(wdf_device); } } pub fn get_device_context_from_device_object<'a, T>( device_object: &mut DEVICE_OBJECT, ) -> Result<&'a mut T, ()> { unsafe { if let Some(context) = device_object.DeviceExtension.as_mut() { return Ok(core::mem::transmute(context)); } } return Err(()); } /// Empty unload event extern "C" fn empty_wdf_driver_unload(_driver: HANDLE) {} ================================================ FILE: windows_kext/wdk/src/ioqueue.rs ================================================ use core::{ cell::UnsafeCell, ffi::c_void, fmt::Display, marker::PhantomData, mem::MaybeUninit, pin::Pin, sync::atomic::{AtomicBool, Ordering}, }; use crate::dbg; use alloc::boxed::Box; use ntstatus::ntstatus::NtStatus; use windows_sys::{Wdk::Foundation::KQUEUE, Win32::System::Kernel::LIST_ENTRY}; #[derive(Debug)] pub enum Status { Uninitialized, Timeout, UserAPC, Abandoned, } impl Display for Status { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Status::Uninitialized => write!(f, "Uninitialized"), Status::Timeout => write!(f, "Timeout"), Status::UserAPC => write!(f, "UserAPC"), Status::Abandoned => write!(f, "Abandoned"), } } } #[repr(i8)] pub enum KprocessorMode { KernelMode = 0, UserMode = 1, } // #[link(name = "NtosKrnl", kind = "static")] extern "C" { /* KeInitializeQueue [out] Queue Pointer to a KQUEUE structure for which the caller must provide resident storage in nonpaged pool. This structure is defined as follows: [in] Count The maximum number of threads for which the waits on the queue object can be satisfied concurrently. If this parameter is not supplied, the number of processors in the machine is used. */ fn KeInitializeQueue(queue: *mut KQUEUE, count: u64); /* KeInsertQueue returns the previous signal state of the given Queue. If it was set to zero (that is, not signaled) before KeInsertQueue was called, KeInsertQueue returns zero, meaning that no entries were queued. If it was nonzero (signaled), KeInsertQueue returns the number of entries that were queued before KeInsertQueue was called. */ fn KeInsertQueue(queue: *mut KQUEUE, list_entry: *mut c_void) -> i32; /* KeRemoveQueue returns one of the following: A pointer to a dequeued entry from the given queue object, if one is available STATUS_TIMEOUT, if the given Timeout interval expired before an entry became available STATUS_USER_APC, if a user-mode APC was delivered in the context of the calling thread STATUS_ABANDONED, if the queue has been run down */ fn KeRemoveQueue( queue: *mut KQUEUE, waitmode: KprocessorMode, timeout: *const i64, ) -> *mut LIST_ENTRY; // If the queue is empty, KeRundownQueue returns NULL; otherwise, it returns the address of the first entry in the queue. fn KeRundownQueue(queue: *mut KQUEUE) -> *mut LIST_ENTRY; } #[repr(C)] struct Entry { list: LIST_ENTRY, // Internal use entry: T, } pub struct IOQueue { // The address of the value should not change. kernel_queue: Pin>>, initialized: AtomicBool, _type: PhantomData, // 0 size variable. Required for the generic to work properly. Compiler limitation. } unsafe impl Sync for IOQueue {} impl IOQueue { /// Make sure `rundown` is called on exit, if `drop()` is not called for queue. pub fn new() -> Self { unsafe { let kernel_queue = Box::pin(UnsafeCell::new(MaybeUninit::zeroed().assume_init())); KeInitializeQueue(kernel_queue.get(), 1); Self { kernel_queue, initialized: AtomicBool::new(true), _type: PhantomData, } } } /// Pushes new entry of any type. pub fn push(&self, entry: T) -> Result<(), Status> { let kqueue = self.kernel_queue.get(); // Allocate entry. let list_entry = Box::new(Entry { list: LIST_ENTRY { Flink: core::ptr::null_mut(), Blink: core::ptr::null_mut(), }, entry, }); let raw_ptr = Box::into_raw(list_entry); // Check if initialized. let result = if self.initialized.load(Ordering::Acquire) { unsafe { KeInsertQueue(kqueue, raw_ptr as *mut c_void) } } else { -1 }; // There is no documentation that rundown queue will return error. This is here just for good measures. // It is unlikely to happen and not critical. if result >= 0 { return Ok(()); } _ = unsafe { Box::from_raw(raw_ptr) }; return Err(Status::Uninitialized); } /// Returns an Element or a status. fn pop_internal(&self, timeout: *const i64) -> Result { unsafe { let kqueue = self.kernel_queue.get(); // Check if initialized. if self.initialized.load(Ordering::Acquire) { // Pop and check the return value. let list_entry = KeRemoveQueue(kqueue, KprocessorMode::KernelMode, timeout) as *mut Entry; let error_code = NtStatus::try_from(list_entry as u32); match error_code { Ok(NtStatus::STATUS_TIMEOUT) => return Err(Status::Timeout), Ok(NtStatus::STATUS_USER_APC) => return Err(Status::UserAPC), Ok(NtStatus::STATUS_ABANDONED) => return Err(Status::Abandoned), _ => { // The return value is a pointer. let list_entry = Box::from_raw(list_entry); let entry = list_entry.entry; return Ok(entry); } } } } Err(Status::Uninitialized) } /// Returns element or a status. Waits until element is pushed or the queue is interrupted. pub fn wait_and_pop(&self) -> Result { // No timeout. self.pop_internal(core::ptr::null()) } /// Returns element or a status. Does not wait. pub fn pop(&self) -> Result { let timeout: i64 = 0; self.pop_internal(&timeout) } /// Returns element or a status. Waits the specified timeout. pub fn pop_timeout(&self, timeout: i64) -> Result { let timeout_ptr: i64 = timeout * -10000; self.pop_internal(&timeout_ptr) } /// Removes all elements and frees all the memory. The object can't be used after this function is called. pub fn rundown(&self) { unsafe { let kqueue = self.kernel_queue.get(); if kqueue.is_null() { return; } // Check if initialized. if self.initialized.swap(false, Ordering::Acquire) { // Remove and free all elements from the queue. let list_entries: *mut LIST_ENTRY = KeRundownQueue(kqueue); if !list_entries.is_null() { let mut entry = list_entries; while !core::ptr::eq((*entry).Flink, list_entries) { let next = (*entry).Flink; dbg!("discarding entry"); let _ = Box::from_raw(entry as *mut Entry); entry = next; } dbg!("discarding last entry"); let _ = Box::from_raw(entry as *mut Entry); } } } } } impl Drop for IOQueue { fn drop(&mut self) { // Reinitialize queue. self.rundown(); unsafe { let ptr = self.kernel_queue.get(); if !ptr.is_null() { *ptr = MaybeUninit::zeroed().assume_init(); } } } } ================================================ FILE: windows_kext/wdk/src/irp_helpers.rs ================================================ use windows_sys::{ Wdk::{ Foundation::{IO_STACK_LOCATION_0_4, IRP}, Storage::FileSystem::IO_NO_INCREMENT, System::SystemServices::IofCompleteRequest, }, Win32::Foundation::{ NTSTATUS, STATUS_END_OF_FILE, STATUS_NOT_IMPLEMENTED, STATUS_SUCCESS, STATUS_TIMEOUT, }, }; /// Wraps an IRP_MJ_CREATE request (triggered when user-space opens the device handle). pub struct CreateRequest<'a> { irp: &'a mut IRP, } impl CreateRequest<'_> { pub fn new(irp: &mut IRP) -> CreateRequest<'_> { CreateRequest { irp } } /// Returns the PID of the process that opened the device handle. /// Safe to call here because IRP_MJ_CREATE always runs in the context /// of the initiating process, never in an arbitrary system thread. pub fn get_requestor_pid(&self) -> u32 { unsafe { crate::ffi::PsGetCurrentProcessId() as u32 } } pub fn complete(&mut self) { // FILE_OPENED (1): indicates the device was opened (not created/superseded). const FILE_OPENED: usize = 1; self.irp.IoStatus.Information = FILE_OPENED; self.irp.IoStatus.Anonymous.Status = STATUS_SUCCESS; unsafe { IofCompleteRequest(self.irp, IO_NO_INCREMENT as i8) }; } pub fn get_status(&self) -> NTSTATUS { unsafe { self.irp.IoStatus.Anonymous.Status } } } /// Wraps an IRP_MJ_CLEANUP request (triggered when user-space closes the last handle). pub struct CleanupRequest<'a> { irp: &'a mut IRP, } impl CleanupRequest<'_> { pub fn new(irp: &mut IRP) -> CleanupRequest<'_> { CleanupRequest { irp } } pub fn complete(&mut self) { self.irp.IoStatus.Information = 0; self.irp.IoStatus.Anonymous.Status = STATUS_SUCCESS; unsafe { IofCompleteRequest(self.irp, IO_NO_INCREMENT as i8) }; } pub fn get_status(&self) -> NTSTATUS { unsafe { self.irp.IoStatus.Anonymous.Status } } } pub struct ReadRequest<'a> { irp: &'a mut IRP, buffer: &'a mut [u8], fill_index: usize, } impl ReadRequest<'_> { pub fn new(irp: &mut IRP) -> ReadRequest<'_> { unsafe { let irp_sp = irp.Tail.Overlay.Anonymous2.Anonymous.CurrentStackLocation; let device_io = (*irp_sp).Parameters.Read; let system_buffer = irp.AssociatedIrp.SystemBuffer; let buffer = core::slice::from_raw_parts_mut( system_buffer as *mut u8, device_io.Length as usize, ); ReadRequest { irp, buffer, fill_index: 0, } } } pub fn free_space(&self) -> usize { self.buffer.len() - self.fill_index } pub fn complete(&mut self) { self.irp.IoStatus.Information = self.fill_index; self.irp.IoStatus.Anonymous.Status = STATUS_SUCCESS; } pub fn end_of_file(&mut self) { self.irp.IoStatus.Information = self.fill_index; self.irp.IoStatus.Anonymous.Status = STATUS_END_OF_FILE; } pub fn timeout(&mut self) { self.irp.IoStatus.Anonymous.Status = STATUS_TIMEOUT; } pub fn get_status(&self) -> NTSTATUS { unsafe { self.irp.IoStatus.Anonymous.Status } } pub fn write(&mut self, bytes: &[u8]) -> usize { let mut bytes_to_write: usize = bytes.len(); // Check if we have enough space if bytes_to_write > self.free_space() { bytes_to_write = self.free_space(); } for i in 0..bytes_to_write { self.buffer[self.fill_index + i] = bytes[i]; } self.fill_index += bytes_to_write; bytes_to_write } } pub struct WriteRequest<'a> { irp: &'a mut IRP, buffer: &'a mut [u8], } impl WriteRequest<'_> { pub fn new(irp: &mut IRP) -> WriteRequest<'_> { unsafe { let irp_sp = irp.Tail.Overlay.Anonymous2.Anonymous.CurrentStackLocation; let device_io = (*irp_sp).Parameters.Write; let system_buffer = irp.AssociatedIrp.SystemBuffer; let buffer = core::slice::from_raw_parts_mut( system_buffer as *mut u8, device_io.Length as usize, ); WriteRequest { irp, buffer } } } pub fn get_buffer(&self) -> &[u8] { self.buffer } pub fn mark_all_as_read(&mut self) { self.irp.IoStatus.Information = self.buffer.len(); } pub fn complete(&mut self) { self.irp.IoStatus.Anonymous.Status = STATUS_SUCCESS; } pub fn get_status(&self) -> NTSTATUS { unsafe { self.irp.IoStatus.Anonymous.Status } } } pub struct DeviceControlRequest<'a> { irp: &'a mut IRP, buffer: &'a mut [u8], fill_index: usize, control_code: u32, } // Windows-rs version of the struct is incorrect (18.01.2024). // TODO: Use the official version when fixed. // For future reference: https://github.com/microsoft/windows-rs/issues/2805 #[repr(C)] struct DeviceIOControlParams { output_buffer_length: u32, _padding1: u32, input_buffer_length: u32, _padding2: u32, io_control_code: u32, _padding3: u32, } impl DeviceControlRequest<'_> { pub fn new(irp: &mut IRP) -> DeviceControlRequest<'_> { unsafe { let irp_sp = irp.Tail.Overlay.Anonymous2.Anonymous.CurrentStackLocation; // Use the struct directly when replaced with proper version. let device_io: DeviceIOControlParams = core::mem::transmute::( (*irp_sp).Parameters.DeviceIoControl, ); let system_buffer = irp.AssociatedIrp.SystemBuffer; let buffer = core::slice::from_raw_parts_mut( system_buffer as *mut u8, device_io.output_buffer_length as usize, ); DeviceControlRequest { irp, buffer, fill_index: 0, control_code: device_io.io_control_code, } } } pub fn get_buffer(&self) -> &[u8] { self.buffer } pub fn write(&mut self, bytes: &[u8]) -> usize { let mut bytes_to_write: usize = bytes.len(); // Check if we have enough space if bytes_to_write > self.free_space() { bytes_to_write = self.free_space(); } for i in 0..bytes_to_write { self.buffer[self.fill_index + i] = bytes[i]; } self.fill_index += bytes_to_write; bytes_to_write } pub fn complete(&mut self) { self.irp.IoStatus.Information = self.buffer.len(); self.irp.IoStatus.Anonymous.Status = STATUS_SUCCESS; unsafe { IofCompleteRequest(self.irp, IO_NO_INCREMENT as i8) }; } pub fn not_implemented(&mut self) { self.irp.IoStatus.Anonymous.Status = STATUS_NOT_IMPLEMENTED; unsafe { IofCompleteRequest(self.irp, IO_NO_INCREMENT as i8) }; } pub fn get_status(&self) -> NTSTATUS { unsafe { self.irp.IoStatus.Anonymous.Status } } pub fn get_control_code(&self) -> u32 { self.control_code } pub fn free_space(&self) -> usize { self.buffer.len() - self.fill_index } } ================================================ FILE: windows_kext/wdk/src/lib.rs ================================================ #![cfg_attr(not(test), no_std)] #![allow(clippy::needless_return)] extern crate alloc; pub mod allocator; pub mod consts; pub mod debug; pub mod driver; pub mod error; pub mod filter_engine; pub mod interface; pub mod ioqueue; pub mod irp_helpers; pub mod rw_spin_lock; pub mod utils; #[allow(dead_code)] pub mod ffi; // Needed by the linker for legacy reasons. Not important for rust. #[cfg(not(test))] #[export_name = "_fltused"] static _FLTUSED: i32 = 0; // Needed by the compiler but not used. #[cfg(not(test))] #[no_mangle] pub extern "system" fn __CxxFrameHandler3(_: *mut u8, _: *mut u8, _: *mut u8, _: *mut u8) -> i32 { 0 } ================================================ FILE: windows_kext/wdk/src/rw_spin_lock.rs ================================================ use core::cell::UnsafeCell; use windows_sys::Wdk::System::SystemServices::{ ExAcquireSpinLockExclusive, ExAcquireSpinLockShared, ExReleaseSpinLockExclusive, ExReleaseSpinLockShared, }; /// A reader-writer spin lock implementation. /// /// This lock allows multiple readers to access the data simultaneously, /// but only one writer can access the data at a time. It uses a spin loop /// to wait for the lock to become available. pub struct RwSpinLock { data: UnsafeCell, } impl RwSpinLock { /// Creates a new `RwSpinLock` with the default initial value. pub const fn default() -> Self { Self { data: UnsafeCell::new(0), } } /// Acquires a read lock on the `RwSpinLock`. /// /// This method blocks until a read lock can be acquired. /// Returns a `RwLockGuard` that represents the acquired read lock. pub fn read_lock(&self) -> RwLockGuard<'_> { let irq = unsafe { ExAcquireSpinLockShared(self.data.get()) }; RwLockGuard { data: &self.data, exclusive: false, old_irq: irq, } } /// Acquires a write lock on the `RwSpinLock`. /// /// This method blocks until a write lock can be acquired. /// Returns a `RwLockGuard` that represents the acquired write lock. pub fn write_lock(&self) -> RwLockGuard<'_> { let irq = unsafe { ExAcquireSpinLockExclusive(self.data.get()) }; RwLockGuard { data: &self.data, exclusive: true, old_irq: irq, } } } /// Represents a guard for a read-write lock. pub struct RwLockGuard<'a> { data: &'a UnsafeCell, exclusive: bool, old_irq: u8, } impl<'a> Drop for RwLockGuard<'a> { /// Releases the acquired spin lock when the `RwLockGuard` goes out of scope. /// /// If the lock was acquired exclusively, it releases the spin lock using `ExReleaseSpinLockExclusive`. /// If the lock was acquired shared, it releases the spin lock using `ExReleaseSpinLockShared`. fn drop(&mut self) { unsafe { if self.exclusive { ExReleaseSpinLockExclusive(self.data.get(), self.old_irq); } else { ExReleaseSpinLockShared(self.data.get(), self.old_irq); } } } } ================================================ FILE: windows_kext/wdk/src/spin_lock.rs ================================================ use core::{ffi::c_void, mem::MaybeUninit, ptr}; use windows_sys::Wdk::System::SystemServices::{ KeAcquireInStackQueuedSpinLock, KeInitializeSpinLock, KeReleaseInStackQueuedSpinLock, KLOCK_QUEUE_HANDLE, }; // Copy of KSPIN_LOCK_QUEUE WDK C struct #[repr(C)] #[allow(dead_code)] struct KSpinLockQueue { next: *mut c_void, // struct _KSPIN_LOCK_QUEUE * volatile Next; lock: *mut c_void, // PKSPIN_LOCK volatile Lock; } // Copy of KLOCK_QUEUE_HANDLE WDK C struct pub struct KLockQueueHandle { lock: KLOCK_QUEUE_HANDLE, } // Copy of KSpinLock WDK C struct #[repr(C)] pub struct KSpinLock { ptr: *mut usize, } impl KSpinLock { pub fn create() -> Self { unsafe { let p: KSpinLock = KSpinLock { ptr: ptr::null_mut(), }; KeInitializeSpinLock(p.ptr); return p; } } pub fn lock(&mut self) -> KLockQueueHandle { unsafe { let mut handle = MaybeUninit::zeroed().assume_init(); KeAcquireInStackQueuedSpinLock(self.ptr, &mut handle); KLockQueueHandle { lock: handle } } } } impl Drop for KLockQueueHandle { fn drop(&mut self) { unsafe { KeReleaseInStackQueuedSpinLock(&mut self.lock); } } } ================================================ FILE: windows_kext/wdk/src/utils.rs ================================================ use alloc::string::{String, ToString}; use ntstatus::ntstatus::NtStatus; use windows_sys::Win32::Foundation::STATUS_SUCCESS; use crate::ffi; pub fn check_ntstatus(status: i32) -> Result<(), String> { if status == STATUS_SUCCESS { return Ok(()); } let Some(status) = NtStatus::from_u32(status as u32) else { return Err("UNKNOWN_ERROR_CODE".to_string()); }; return Err(status.to_string()); } pub fn get_system_timestamp_ms() -> u64 { // 100 nano seconds units -> device by 10 -> micro seconds -> divide by 1000 -> milliseconds unsafe { ffi::pm_QuerySystemTime() / 10_000 } }